sketchucation logo sketchucation
    • Login
    ℹ️ Licensed Extensions | FredoBatch, ElevationProfile, FredoSketch, LayOps, MatSim and Pic2Shape will require license from Sept 1st More Info

    Instructor content

    Scheduled Pinned Locked Moved Developers' Forum
    84 Posts 8 Posters 7.9k Views 8 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Chris FullmerC Offline
      Chris Fullmer
      last edited by

      Sorry Dan, you lost me at "APPDATA". I never knew there was an alternate folder schema somewhere. I'm really not sure what appdata is.

      Lately you've been tan, suspicious for the winter.
      All my Plugins I've written

      1 Reply Last reply Reply Quote 0
      • B Offline
        bentleykfrog
        last edited by

        @chris fullmer said:

        Sorry Dan, you lost me at "APPDATA". I never knew there was an alternate folder schema somewhere. I'm really not sure what appdata is.

        +1 on this, sorry for the confusion dan 😳

        Time to make a fool out of myself for a second time:

        @thomthom said:

        @chris fullmer said:

        That worked for me, are you guys seeing differently?

        The snippet I posted work for me when I added it to Bezier Tools as a working example. I generate a relative path from Sketchup.get_resource_path( 'helpcontent' )

        In the snippet you posted earlier, doesn't parts = origin.split( File::SEPERATOR ).size include the separator in the drive name (ie. C:/), so you get x+1 slashes to the root C:/ from helpcontent instead of x, which negates the /tool folder?

        Also, just to double-check, is there meant to be an escaping character for w in /^w:/ (ie. /^\w:/) or is something weird going on?

        sorry for the noob questions, I really appreciate the responses.

        1 Reply Last reply Reply Quote 0
        • thomthomT Offline
          thomthom
          last edited by

          @bentleykfrog said:

          In the snippet you posted earlier, doesn't parts = origin.split( File::SEPERATOR ).size include the separator in the drive name (ie. C:/), so you get x+1 slashes to the root C:/ from helpcontent instead of x, which negates the /tool folder?

          That is a possibility... I might very well be wrong about where it base the path from. But the positive side-effect of my little bug is that it works. 😄

          @bentleykfrog said:

          Also, just to double-check, is there meant to be an escaping character for w in /^w:/ (ie. /^\w:/) or is something weird going on?

          You're right. And it's there in my original code - it seems that the forum ate the escape character! 😮

          Lemme try again:

          Without PHP formatting:

          
            # Get Instructor Path
            #
            # Tool.getInstructorContentDirectory expects a path relative to SketchUp's
            # Resource/<locale>/helpcontent/ folder, despite the documentations use an
            # absolute path.
            #
            # This method is a wrapper that generates a path to the actual help content
            # which SketchUp can use.
            #
            # The given path must be under the same drive as SketchUp's help content.
            #
            # This quick exist in all current SketchUp versions.
            # Current; SketchUp 8 M1
            def self.get_instructor_path( path )
              path = File.expand_path( path )
              origin = Sketchup.get_resource_path( 'helpcontent' )
              # Check if drive matches
              origin_drive = origin.match( /^(\w);/ )
              if origin_drive
                origin_drive = origin_drive[1].downcase
              end
              path_drive = path.match( /^(\w);/ )
              if path_drive
                path_drive = path_drive[1].downcase
                path = path[2...path.size] # Trim drive letter
              end
              if path_drive && origin_drive
                return nil unless origin_drive == path_drive
              end
              # Build relative path
              parts = origin.split( File;;SEPARATOR ).size
              path_to_root = "..#{File;;SEPARATOR}" * parts
              relative_path = File.join( path_to_root, path )
              return relative_path
            end
          
          

          With PHP formatting:

          <span class="syntaxdefault"><br />&nbsp;&nbsp;</span><span class="syntaxcomment">#&nbsp;Get&nbsp;Instructor&nbsp;Path<br />&nbsp;&nbsp;#<br />&nbsp;&nbsp;#&nbsp;Tool.getInstructorContentDirectory&nbsp;expects&nbsp;a&nbsp;path&nbsp;relative&nbsp;to&nbsp;SketchUp's<br />&nbsp;&nbsp;#&nbsp;Resource/<locale>/helpcontent/&nbsp;folder,&nbsp;despite&nbsp;the&nbsp;documentations&nbsp;use&nbsp;an<br />&nbsp;&nbsp;#&nbsp;absolute&nbsp;path.<br />&nbsp;&nbsp;#<br />&nbsp;&nbsp;#&nbsp;This&nbsp;method&nbsp;is&nbsp;a&nbsp;wrapper&nbsp;that&nbsp;generates&nbsp;a&nbsp;path&nbsp;to&nbsp;the&nbsp;actual&nbsp;help&nbsp;content<br />&nbsp;&nbsp;#&nbsp;which&nbsp;SketchUp&nbsp;can&nbsp;use.<br />&nbsp;&nbsp;#<br />&nbsp;&nbsp;#&nbsp;The&nbsp;given&nbsp;path&nbsp;must&nbsp;be&nbsp;under&nbsp;the&nbsp;same&nbsp;drive&nbsp;as&nbsp;SketchUp's&nbsp;help&nbsp;content.<br />&nbsp;&nbsp;#<br />&nbsp;&nbsp;#&nbsp;This&nbsp;quick&nbsp;exist&nbsp;in&nbsp;all&nbsp;current&nbsp;SketchUp&nbsp;versions.<br />&nbsp;&nbsp;#&nbsp;Current;&nbsp;SketchUp&nbsp;8&nbsp;M1<br />&nbsp;&nbsp;</span><span class="syntaxdefault">def&nbsp;self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">get_instructor_path</span><span class="syntaxkeyword">(&nbsp;</span><span class="syntaxdefault">path&nbsp;</span><span class="syntaxkeyword">)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="syntaxdefault">path&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">File</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">expand_path</span><span class="syntaxkeyword">(&nbsp;</span><span class="syntaxdefault">path&nbsp;</span><span class="syntaxkeyword">)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="syntaxdefault">origin&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">Sketchup</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">get_resource_path</span><span class="syntaxkeyword">(&nbsp;</span><span class="syntaxstring">'helpcontent'&nbsp;</span><span class="syntaxkeyword">)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="syntaxcomment">#&nbsp;Check&nbsp;if&nbsp;drive&nbsp;matches<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="syntaxdefault">origin_drive&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">origin</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">match</span><span class="syntaxkeyword">(&nbsp;/^(</span><span class="syntaxdefault">w</span><span class="syntaxkeyword">);/&nbsp;)<br />&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;</span><span class="syntaxdefault">origin_drive<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;origin_drive&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">origin_drive</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">].</span><span class="syntaxdefault">downcase<br />&nbsp;&nbsp;&nbsp;&nbsp;end<br />&nbsp;&nbsp;&nbsp;&nbsp;path_drive&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">match</span><span class="syntaxkeyword">(&nbsp;/^(</span><span class="syntaxdefault">w</span><span class="syntaxkeyword">);/&nbsp;)<br />&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;</span><span class="syntaxdefault">path_drive<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;path_drive&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">path_drive</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">].</span><span class="syntaxdefault">downcase<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;path&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">2.</span><span class="syntaxkeyword">..</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">size</span><span class="syntaxkeyword">]&nbsp;</span><span class="syntaxcomment">#&nbsp;Trim&nbsp;drive&nbsp;letter<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="syntaxdefault">end<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="syntaxkeyword">if&nbsp;</span><span class="syntaxdefault">path_drive&nbsp;</span><span class="syntaxkeyword">&&&nbsp;</span><span class="syntaxdefault">origin_drive<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="syntaxkeyword">return&nbsp;</span><span class="syntaxdefault">nil&nbsp;unless&nbsp;origin_drive&nbsp;</span><span class="syntaxkeyword">==&nbsp;</span><span class="syntaxdefault">path_drive<br />&nbsp;&nbsp;&nbsp;&nbsp;end<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="syntaxcomment">#&nbsp;Build&nbsp;relative&nbsp;path<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="syntaxdefault">parts&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">origin</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">split</span><span class="syntaxkeyword">(&nbsp;</span><span class="syntaxdefault">File</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">SEPARATOR&nbsp;</span><span class="syntaxkeyword">).</span><span class="syntaxdefault">size<br />&nbsp;&nbsp;&nbsp;&nbsp;path_to_root&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxstring">"..#{File;;SEPARATOR}"&nbsp;</span><span class="syntaxkeyword">*&nbsp;</span><span class="syntaxdefault">parts<br />&nbsp;&nbsp;&nbsp;&nbsp;relative_path&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">File</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">join</span><span class="syntaxkeyword">(&nbsp;</span><span class="syntaxdefault">path_to_root</span><span class="syntaxkeyword">,&nbsp;</span><span class="syntaxdefault">path&nbsp;</span><span class="syntaxkeyword">)<br />&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;</span><span class="syntaxdefault">relative_path<br />&nbsp;&nbsp;end<br /></span>
          

          Thomas Thomassen — SketchUp Monkey & Coding addict
          List of my plugins and link to the CookieWare fund

          1 Reply Last reply Reply Quote 0
          • thomthomT Offline
            thomthom
            last edited by

            grrr.... the PHP formatter broke the code!

            Thomas Thomassen — SketchUp Monkey & Coding addict
            List of my plugins and link to the CookieWare fund

            1 Reply Last reply Reply Quote 0
            • B Offline
              bentleykfrog
              last edited by

              @thomthom said:

              @bentleykfrog said:

              In the snippet you posted earlier, doesn't parts = origin.split( File::SEPERATOR ).size include the separator in the drive name (ie. C:/), so you get x+1 slashes to the root C:/ from helpcontent instead of x, which negates the /tool folder?

              That is a possibility... I might very well be wrong about where it base the path from. But the positive side-effect of my little bug is that it works. 😄

              Its definitely a positive side effect, and consistent across OS's as macs start with '/' instead of 'C:/'

              @thomthom said:

              With PHP formatting:

              PHP & escape characters == no love (not even with double escaping), which sucks with Regex. From my experience there's no telling how many processes a php string has been through on a forum post (ie. as written in your own code or copy-and-paste from another website/forum) so I think the php code goes on the singular escape and doesn't bother to check for doubles, which is wrong even if you're formatting php code. I suppose since sketchucation is in php, they've at least got to make the code mute to prevent some pretty easy hacking, by formatting php code as a string. (I'll stop here since its way off topic)

              1 Reply Last reply Reply Quote 0
              • B Offline
                bentleykfrog
                last edited by

                Initial testing on the mac works too, though there's no need to copy the instructor files to the temp directory on a mac 🎉

                1 Reply Last reply Reply Quote 0
                • Dan RathbunD Offline
                  Dan Rathbun
                  last edited by

                  @thomthom said:

                  grrr.... the PHP formatter broke the code!

                  Good catch!

                  We best be careful from now on using the [ code = php ] bbtag.

                  I'm not here much anymore.

                  1 Reply Last reply Reply Quote 0
                  • Dan RathbunD Offline
                    Dan Rathbun
                    last edited by

                    @chris fullmer said:

                    Sorry Dan, you lost me at "APPDATA". I never knew there was an alternate folder schema somewhere. I'm really not sure what appdata is.

                    It's a path where applications keep user specific data files.

                    And there are two (2) AppData paths (on PC at least.)
                    One is "Local" AppData, that is used only on that specific computer.
                    The other is "Roaming" AppData, which is used for a specific user, no matter what workstation they log onto within a given network.

                    It looks like Sketchup does not use the Local AppData path at all (although GoogleEarth and GoogleUpdater do.)

                    Also.. if you have your system set to "hide system folders" you may not see either of the AppData folders at all. (They are defined as "hidden system" folders on XP.)

                    To see what the paths are, open a cmd shell, and type set to view all the Environment variables.
                    check the values for:
                    APPDATA
                    LOCALAPPDATA

                    To make the situation a bit more confusing.. there is also a generic APPDATA path for All Users on a PC computer, kept under the "All Users" profile. This is the profile that is normally considered to be the "Shared" profile. It's where the "Shared Documents", "Shared Music", "Shared Pictures" etc. are kept. (And also where the SU Pro license file is kept.)
                    So normally we would think it better to keep "Shared Plugins" under the "All Users" profile, instead of beneath the Program Files application folder. (In the past, there was not much difference, but on Windows versions 6+, there is a security hiccup when trying to use a Plugins folder beneath the Program Files path. It may be easier, if Google changes the Sketchup PC edition, to use the "All Users" profile for the "Shared Plugins" and "Shared Tools".)

                    I'm not here much anymore.

                    1 Reply Last reply Reply Quote 0
                    • Dan RathbunD Offline
                      Dan Rathbun
                      last edited by

                      @bentleykfrog said:

                      @bentleykfrog said:

                      Its a long way around but I'm thinking that it would be possible to write the required files and folders to the /temp/ folder on the C:/ drive before the tool loads, ...

                      .... So far, it works quite well. I'm just rolling over testing on the mac and will update this if there needs to be changes.

                      First of all. ALWAYS copying files to the TEMP folder seems like extra work that may never be needed, if the user does not have the instructor toolwindow open. (Note that the SUAPI is lacking a UI.inspector_visible?() method, which we could write for PC, IF we know the window names for all language builds.)

                      Also.. there is no reason why the tool's getInstructorContentDirectory() callback could not be the 'trigger' that copies the files to TEMP, rather than doing it everytime. This is something to think about for the future.
                      (Issue) Does Sketchup always call the getInstructorContentDirectory() callback regardless of whether the Instructor window is open or closed?

                      Playing 'devil's advocate', let's say we get Google to provide a cross-platform, locale independant query method to return visibilty state for inspector window, likely named: UI.inspector_visible?('*caption*') (where caption is one of the strings returned by the existing UI.inspector_names() method.)
                      Then the getInstructorContentDirectory() callback would look like:

                      
                      class MyTool
                      
                        def getInstructorContentDirectory() # callback
                          if UI.inspector_visible?('Instructor')
                            temp =( MAC ? "#{ENV['TMPDIR']}" ; "#{ENV['TEMP']}" )
                            return copy_help_files( temp, @tool_help_subdir ) unless @help_files_copied
                          else
                            return ""
                          end
                        end
                      
                        def copy_help_files( temp, tool_help_subdir )
                      
                          # call Niall's library method here
                          # passing args  temp and tool_help_subdir 
                          # Niall's method should return path to temp folder
                          # with the "path to root" prepended and the
                          # tool_help_subdir appeneded
                          
                          @help_files_copied = true
                      
                        end
                      
                        def deactivate()
                          @help_files_copied = false
                          # other code here
                        end
                      
                        def suspend()
                          @help_files_copied = false
                          # other code here
                        end
                      
                      end # class
                      

                      This is just an idea, but actually Niall it would be better if your code was implemented as a Mixin Module (not a class,) and then people who need it actually "mix" it into their Tool class using include() (That likely means you need to rename the initialize method, as Tool coders often need to use that method to init their tool instance. how about help_setup() instead, and authors can insert a call to help_setup() within their initialize() method.)
                      This way all your methods become private methods of the Tool class instance.
                      Wrap your code in module Mixin::LoadInstructorContent then to mix it in:

                      class MyTool
                      
                        require('Mixin/load_instructor.rb')
                        include( Mixin;;LoadInstructorContent )
                      
                        # now all Niall's methods are instance methods
                        # of THIS specific tool class 'MyTool'
                        # a coder need only insert the help_setup() call;
                        def initialize()
                          help_setup()
                          # other tool instance init code
                        end
                      
                      end #class
                      

                      SO.. and this is important your Mixin module, can actually define the getInstructorContentDirectory() callback method for the tool author, so he/she does not need to do it.

                      I'm not here much anymore.

                      1 Reply Last reply Reply Quote 0
                      • Chris FullmerC Offline
                        Chris Fullmer
                        last edited by

                        @dan rathbun said:

                        (Issue) Does Sketchup always call the getInstructorContentDirectory() callback regardless of whether the Instructor window is open or closed?

                        I'm pretty sure it does not. If you put a UI.messagebox into the getInstructorContentDirectory() callback method, it does not pop up unless the instructor is actually open.

                        Lately you've been tan, suspicious for the winter.
                        All my Plugins I've written

                        1 Reply Last reply Reply Quote 0
                        • Dan RathbunD Offline
                          Dan Rathbun
                          last edited by

                          @chris fullmer said:

                          @dan rathbun said:

                          (Issue) Does Sketchup always call the getInstructorContentDirectory() callback regardless of whether the Instructor window is open or closed?

                          I'm pretty sure it does not. If you put a UI.messagebox into the getInstructorContentDirectory() callback method, it does not pop up unless the instructor is actually open.

                          Well cool! That in and of itself can be a test, and perhaps a means of defining a query method via an empty tool. More testing required.

                          I'm not here much anymore.

                          1 Reply Last reply Reply Quote 0
                          • Dan RathbunD Offline
                            Dan Rathbun
                            last edited by

                            @Niall

                            This is safer:
                            temp =( MAC ? "#{ENV['TMPDIR']}" : "#{ENV['TEMP']}" )

                            as there is more than one compiler for the PC, not just 'mswin' (there is also 'mingw')

                            So test for 'darwin' instead.

                            For the global constants:

                            <span class="syntaxdefault">  unless defined</span><span class="syntaxkeyword">?(</span><span class="syntaxdefault">Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">MAC</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> <br />    Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">MAC </span><span class="syntaxkeyword">=(</span><span class="syntaxdefault"> Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">RUBY_PLATFORM </span><span class="syntaxkeyword">=~</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">/</span><span class="syntaxdefault">darwin</span><span class="syntaxkeyword">/</span><span class="syntaxdefault">i </span><span class="syntaxkeyword">?</span><span class="syntaxdefault"> true </span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> false </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">  end<br />  unless defined</span><span class="syntaxkeyword">?(</span><span class="syntaxdefault">Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">WIN</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> <br />    Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">WIN </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> not Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">MAC </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">  end<br />  Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">OSX </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">MAC unless defined</span><span class="syntaxkeyword">?(</span><span class="syntaxdefault">Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">OSX</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">  Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">PC  </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">WIN unless defined</span><span class="syntaxkeyword">?(</span><span class="syntaxdefault">Object</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">PC</span><span class="syntaxkeyword">)</span><span class="syntaxdefault">  </span>
                            

                            Now they can be accessed by their simple names, ie MAC or WIN, etc.

                            Note that using the old deprecated PLATFORM constant directly may cause problems in future Ruby versions, when they stop defining it. Use RUBY_PLATFORM instead.

                            • Note there may still be a poor example on the Sketchup FAQ webpage, that suggests users set a constant, and this example uses the identifier PLATFORM, which may break your code.
                              see question: How can I detect if my plugin is running on the Mac vs. PC?
                              on: Google SketchUp Ruby API FAQs
                              (I entered a 'doc bug report', and suggested the code snippet above be added to the sketchup.rb file instead.)

                            I'm not here much anymore.

                            1 Reply Last reply Reply Quote 0
                            • Dan RathbunD Offline
                              Dan Rathbun
                              last edited by


                              Here's another way to get around the drive letters on PC


                              On my machine I have logical drives 😄(XP SYSTEM), E:(DATA) and F:(TEMP)
                              My machine is named 'JEDI'

                              Environment vars:
                              ENV['COMPUTERNAME'] = 'JEDI'
                              ENV['LOGONSERVER'] = '\JEDI'

                              I went to drive F:, and made a dir 'Shared', went into that dir, and made a subdir 'Plugins'
                              Then I right-clicked that 'Plugins' subdir, and chose "Sharing and Security..." from the context menu.
                              Under "Network sharing and security" I checked the box "Share this folder on the network"
                              In the Sharename textbox I typed 'NetPlugins'...
                              .. below that I checked the box "Allow network users to change my files" *
                              (*If you don't at least temporarily, you can't copy files into that folder.)
                              and clicked the 'OK' button.

                              The folder: 'F:\Shared\Plugins'
                              is now shared as: '\JEDI\NetPlugins'

                              or generically as the Ruby string:
                              "#{File.expand_path(ENV['LOGONSERVER'])}/NetPlugins"

                              I copied a 'TestScript.rb' to the shared folder, and tested it at the console:
                              load "#{ENV['LOGONSERVER']}/NetPlugins/TestScript.rb"
                              .. no problem it loaded the file.

                              I tested: File.expand_path("#{ENV['LOGONSERVER']}/NetPlugins/TestScript.rb")

                              //JEDI/NetPlugins/TestScript.rb
                              .. so expanding the path will not break code by reverting to a drive letter prepended absolute path.

                              Next I made a little file called 'zz.rb' (so it would load after all other plugins,) and put it in the normal Program Files Plugins folder. It has 2 lines:

                              server = ENV['LOGONSERVER'].gsub(/\\/,'/')
                              require_all("#{server}/NetPlugins")
                              

                              server = File.expand_path(ENV['LOGONSERVER']) also works, if you dont like gsub.

                              .. I then started Sketchup 8, and bingo! the TestScript.rb file loaded. Here's the test script:

                              puts "I am a test script loaded from #{File.expand_path(ENV['LOGONSERVER'])},\n and my path is; #{__FILE__}\n"
                              

                              💭

                              I'm not here much anymore.

                              1 Reply Last reply Reply Quote 0
                              • Dan RathbunD Offline
                                Dan Rathbun
                                last edited by

                                We need to think about two plugin install locations:

                                COMMOM_PLUGINS and USER_PLUGINS

                                Both platforms:
                                COMMOM_PLUGINS = Sketchup.find_support_file('Plugins')

                                Mac:
                                [normal case]
                                USER_PLUGINS = "/Users/#{ENV['USER']}" + COMMOM_PLUGINS
                                [if the user sets a custom HOME folder]
                                USER_PLUGINS = "#{ENV['HOME']}" + COMMOM_PLUGINS

                                PC:
                                VER = Sketchup.version.to_i USER_PLUGINS = File.expand_path(ENV['APPDATA']+'\Google\Google SketchUp '+VER+'\SketchUp\Plugins')

                                Note that on Mac, the loading of scripts in the user plugins (on Sketchup startup,) is automatic.
                                Not so on PC. A user must create the appdata dir, AND use the require_all() method from the sketchup.rb file to autoload scripts from the appdata path. The require_all() method will also push the appdata path onto the $LOAD_PATH array, so that subsequently, normal require() or load() can find rubies beneath that path.
                                %APPDATA%
                                On XP the appdata path is: "C:/Documents and Settings/#{ENV['USERNAME']/Application Data/" ...
                                On Windows 6.0+, it's "C:/Users/#{ENV['USERNAME']/AppData/Roaming" ...

                                IN ADDITION, some people, myself included, wish to maintain a common cross version plugins folder that the Google installers will not touch. Some will wish to have this on another drive (one person here at SCF, said he uses a "thumbdrive", so he can take his plugins from machine to machine easily.)
                                Others may wish to create a folder under the appdata path, like:
                                USER_PLUGINS_ALL = File.expand_path(ENV['APPDATA']+'\Google\Google SketchUp All\Plugins')
                                And as TIG said, others may wish to use a network drive.

                                I'm not here much anymore.

                                1 Reply Last reply Reply Quote 0
                                • P Offline
                                  Pout
                                  last edited by

                                  tried this to before
                                  after some testing i just created a tutorial in a webdialog

                                  1 Reply Last reply Reply Quote 0
                                  • B Offline
                                    bentleykfrog
                                    last edited by

                                    @dan rathbun said:

                                    ...I tested: File.expand_path("#{ENV['LOGONSERVER']}/NetPlugins/TestScript.rb")

                                    //JEDI/NetPlugins/TestScript.rb
                                    .. so expanding the path will not break code by reverting to a drive letter prepended absolute path...

                                    Nice find Dan, and this should (hopefully:untested:willcheck) work with the temp copy script, as it compares the array[0] of somepath.split("/") with the same of the temp directory to start the copy to the temp folder. So on pc this would be like comparing 'C:' with '' (before this there's a short-circuit for mac). Only question now is can sketchup run from a '\Server' path?? as this would ruin the comparison. 😕

                                    1 Reply Last reply Reply Quote 0
                                    • B Offline
                                      bentleykfrog
                                      last edited by

                                      @bentleykfrog said:

                                      Only question now is can sketchup run from a '\Server' path?? as this would ruin the comparison. 😕

                                      Actually that should read "can the default temp folder run from a '\Server' path?? as this would ruin the comparison"

                                      1 Reply Last reply Reply Quote 0
                                      • B Offline
                                        bentleykfrog
                                        last edited by

                                        @chris fullmer said:

                                        @dan rathbun said:

                                        (Issue) Does Sketchup always call the getInstructorContentDirectory() callback regardless of whether the Instructor window is open or closed?

                                        I'm pretty sure it does not. If you put a UI.messagebox into the getInstructorContentDirectory() callback method, it does not pop up unless the instructor is actually open.

                                        I get the same results on mac. Also, I notice that it calls getInstructorContentDirectory() onMouseOver and onMouseOut of the instructor dialog. Not sure if its a webkit quirk or if its intended, will check on pc.

                                        1 Reply Last reply Reply Quote 0
                                        • B Offline
                                          bentleykfrog
                                          last edited by

                                          I've updated Dan's edit of the load_instructor script to address some issues:

                                          • script now contains getInstructorContentDirectory method as per Dan's suggestion
                                          • script methods converted from camelCase
                                          • added prefix 'lins_' to each method to reduce chance of method name conflicts
                                          • removed returns when not needed
                                          • made the temp directory creation more consistent
                                          • modified the precedence of mac over temp file setting
                                          • added private method calls to relative_instructor_path & temp_instructor_folder methods
                                          • added plugin folder name attribute to lins_setup() to resolve FILE issue when load_instructor.rb installed into [any $LOAD_PATH]/mixin/load_instructor.rb and default values of instructor folder and temp folder used
                                          • added $LOAD_PATH check for plugins instructor folder if no instructor_folder attribute is set to lins_setup
                                          • since we're specifying a folder name even on default, unique temp folder name creation was removed

                                          Read the definition of use in the code to get a better understanding of how to use Load Instructor.

                                          Issues I'm unsure of:

                                          • I removed the @@tool_filepath setting as I'm not sure what to do with it. If the scripts are intended to be truly portable, then how does an .rbs file require() other files? ThomThom's vertex tools method suggests that their code will already have a variable showing the scripts location, with which you could set the instructor folder location from, and use the location folder attribute of lins_setup().
                                          • Sketchup loaded from a server location? (ie. portable versions of Sketchup?)
                                          • Attaching an AppObserver for onQuit to delete the temp file? (I wouldn't have a clue about this: I'm concerned that it might overwrite other plugins' onQuit method, and I'm guessing that onQuit won't be called when there's a bugsplat, so maybe its better to have consistent temp folder directories as in this update? and rely on the system/users preference for temp folder clearing?)

                                          Thanks to Dan for all the tips/advice/&code, you've been more than helpful.


                                          Load Instructor [Updated 13-03-2011:7.12am]

                                          1 Reply Last reply Reply Quote 0
                                          • B Offline
                                            bentleykfrog
                                            last edited by

                                            @bentleykfrog said:

                                            Its a long way around but I'm thinking that it would be possible to write the required files and folders to the /temp/ folder on the C:/ drive before the tool loads, then use a relative path to the /temp/instructor/ directory in getInstructorContentDirectory. This would avoid the volume traversal problem but I think it would be a bit of a headache.

                                            Script Updated on 2011-03-13: See http://forums.sketchucation.com/viewtopic.php?f=180&t=31060&p=315364#p315364

                                            Here's a very long post and a very-rough draft of the Instructor-copy-to-temp-folder script. I've tested this on Windows 7 with Sketchup 8 and I used [url=http://forums.sketchucation.com/viewtopic.php?t=17660:2t71i41l]Alex's Plugin Loader[/url:2t71i41l] for testing on other drives. So far, it works quite well. I'm just rolling over testing on the mac and will update this if there needs to be changes.

                                            I'm not sure about alot of things, to say the least;

                                            • file chmod permissions on close? (ie. Security, should we go for a File.chmod(0644, temp_abs_path+entry) before close?)
                                            • file encoding copy? I'm using the texture writer to supplement the simplicity of my File.write code but I'm thinking that if encoding is preserved the script might not have the issue of corrupt images on copy (can we use require "fileutils", I've got no experience with ruby extensions)
                                            • block devices and character devices? 😲 (this flies way over my head)
                                            • And add more to that 'fifo', 'link', 'socket'
                                            • File.executable? checks? (probably not needed as sketchup loads the plugin script unless the plugin and instructor content are in completely foreign folders (thomthom?))
                                            • Temp directory clearing? is this necessary on tool close or would it be better to store a default with the script and maintain the files on exit, and check if the files exist and are consistent on next load? (probably better to clear and load for each sketchup session?)
                                            • Not sure about flash/movie/audio/etc.. files, (my target.write code doesn't like anything more complex than a text file, there's probably some encoding settings that I'm missing out on as in my first point that is corrupting the file on write. If fileutils is not possible then maybe encoding settings might solve this)
                                            • How about server locations on windows like "\someserver" instead of "C:\" 😕

                                            Also, please forgive the amateur coding, its my first try at something that is cut-and-paste portable. But I'm open for critique/suggestions for improvement (though its a little off topic, best to PM)

                                            # ##-------------------------##
                                            # ##----DEFINITION OF USE----##
                                            # ##-------------------------##
                                            # #first add the load instructor to your module
                                            # #or whatever module you'd like to add it to
                                            # #in this example, I've added it to MyModule
                                            
                                            # #calling the load instructor
                                            # #specifying no attributes should return the base temp and 
                                            # #construct a instructor path by; [ruby_script_name]/instructor
                                            # instructor_copy = MyModule;;LoadInstructor.new
                                            # #after calling the load instructor, you can set the
                                            # #instructor_folder & temp_folder if you need to adjust
                                            # #the default values
                                            # instructor_copy.instructor_folder = [custom instructor folder]
                                            # instructor_copy.temp_folder = [custom temp folder]
                                            # #or you can call the load instructor with custom directories like this
                                            # temp_folder = ([PLATFORM].grep(/mswin/)==[PLATFORM]) ? ENV["TEMP"] ; ENV["TMPDIR"]
                                            # instructor_folder = File.join(File.join(File.basename( __FILE__ ),"floating_camera"), "instructor")
                                            # instructor_copy = MyModule;;LoadInstructor.new( instructor_folder , temp_folder )
                                            # #we need to copy the files to the temp directory
                                            # #if needed by this method
                                            # instructor_copy.copyInstructor
                                            # #any errors in the copy will be reported to the ruby console
                                            
                                            # #after this has completed, you can return the instructor temp 
                                            # #folder path (relative to the temp folder) by;
                                            # instructor_temp_folder = instructor_copy.returnTempInstructorFolderName
                                            # #this will be helpful if you're storing the instructor temp 
                                            # #folder location in a class variable to check if you need 
                                            # #to run the script per sketchup session (returns false if 
                                            # #no temp folder created)
                                            
                                            # #finally, you can get the relative path from the sketchup 
                                            # #tools folder to the instructor folder (whether it was 
                                            # #copied to the temp folder or not) by calling
                                            # @@relative_instructor_folder = instructor_copy.returnRelativePath
                                            
                                            # #as an example, you can put these calls inside your tools 
                                            # #initialize method, like the example below. Initialize 
                                            # #will be called before getInstructorContentDirectory
                                            # def initialize
                                            	# instructor = CControl;;LoadInstructor.new
                                            	# instructor.copyInstructor
                                            	# @@relative_instructor_path = instructor.returnRelativePath
                                            # end #def
                                            
                                            # #and then add this def to your tool to give sketchup the
                                            # #relative path
                                            # def getInstructorContentDirectory
                                            	# return @@relative_instructor_path
                                            # end #def
                                            
                                            # #now you should have a consistent Instructor Directory
                                            # #across drives
                                            
                                            	#need to check if paths are relative or absolute
                                            	class LoadInstructor
                                            		attr_accessor ;instructor_folder, ;temp_folder
                                            		def initialize(instructor_folder=false, temp_folder=false)
                                            			if instructor_folder
                                            				@instructor_folder = instructor_folder
                                            			else
                                            				@instructor_folder = self.getDefaultInstructorFolder
                                            			end
                                            			if temp_folder
                                            				@temp_folder = temp_folder
                                            			elsif [PLATFORM].grep(/mswin/)!=[PLATFORM]
                                            				@temp_folder = @instructor_folder
                                            			else
                                            				@temp_folder = self.getDefaultTempFolder
                                            			end
                                            		end #def
                                            		
                                            		def getDefaultInstructorFolder
                                            			dir = File.expand_path(File.dirname( __FILE__ ))
                                            			file_ext = File.extname( __FILE__)
                                            			file_basename = File.basename( __FILE__ , file_ext)
                                            			dir = dir + "/" + file_basename + "/instructor"
                                            			return dir
                                            		end #def
                                            		
                                            		def getDefaultTempFolder
                                            			temp_folder = File.expand_path(ENV["TEMP"])
                                            			return temp_folder
                                            		end #def
                                            		
                                            		# adapted from http://boonedocks.net/mike/archives/162-Determining-Image-File-Types-in-Ruby.html
                                            		# checks for jpg,png,tif,tga & bmp and variations in their data formats
                                            		def getImageType(file)
                                            		
                                            			filestart = IO.read(file,30)
                                            			case filestart
                                            				when /^BM/
                                            					#puts "BMP; "+file
                                            					return 'bmp'
                                            				when /^GIF8/
                                            					#puts "GIF; "+file
                                            					return 'gif'
                                            				when /^MM\x00\*(\x00){3}/
                                            					#when /^MM.\*.{3}/
                                            					#puts "TIF MAC; "+file
                                            					return 'tif'
                                            				when /^II\*\x00\x08(\x00){3}\x15/
                                            					#when /^II\*./
                                            					#puts "TIF PC; "+file
                                            					return 'tif'
                                            				when /^\x89PNG/
                                            					#puts "PNG; "+file
                                            					return 'png'
                                            				when /^\xff\xd8\xff\xe0\x00\x10JFIF/
                                            					#puts "JPG; "+file
                                            					return 'jpg'
                                            				when /^\xff\xd8\xff\xe0/
                                            					#puts "JPG; "+file
                                            					return 'jpg'
                                            				when /^\xff\xd8\xff\xe1(.*){2}Exif/
                                            					#puts "JPG; "+file
                                            					return 'jpg'
                                            				when /^\x00\x00\x0A\x00/
                                            					#puts "TGA Compressed; "+file
                                            					return 'tga'
                                            				when /^\x00\x00\x02\x00/
                                            					#puts "TGA; "+file
                                            					return 'tga'
                                            				else
                                            					#check for tga files by extension
                                            					fileext = File.extname(file)
                                            					case fileext
                                            						when /^\.(?i)(tga|vda|icb|vst)/
                                            							#puts "TGA by extension; "+file
                                            							return 'tga'
                                            						when /^\.(?i)(tif|tiff)/
                                            							#puts "TIF by extension; "+file
                                            							return 'tif'
                                            					end #case
                                            					puts "unknown; "+IO.read(file,20).dump
                                            					return 'unknown'
                                            			end
                                            		end
                                            		
                                            		def checkValidity(filename)
                                            			filetype = File.ftype(filename) #grabs the file type; 'file','directory','characterSpecial','blockSpecial',
                                            																			#'fifo','link','socket','unknown'
                                            			case filetype
                                            				when "file"
                                            					if (!File.readable?(filename))
                                            						puts "file is not readable; "+filename
                                            						return "unreadable"
                                            					end #if
                                            					if (!File.size?(filename))
                                            						puts "file exists but has 0 size; "+filename
                                            						return "invalidsize"
                                            					end #if
                                            					image_type = self.getImageType(filename)
                                            					if(image_type == 'unknown')
                                            						puts "file exists but is unknown (assume text file); "+filename
                                            						return "unknowntype"
                                            					end #if
                                            					#puts "found image file; "+filename
                                            					return "imgfile"
                                            				when "directory"
                                            					#puts "found directory; "+filename
                                            					return "directory"
                                            			end #case
                                            		end #def
                                            		
                                            		#call like self.copyInstructorFiles([base_dir_of_instructor_folder],[base_dir_of_instructor_folder])
                                            		#dir changes on recursion through the child directories and basedir stays constant to
                                            		#maintain the folder/file structure in the temp folder
                                            		def copyInstructorFiles(temp_folder, dir, basedir)
                                            			entries = Dir.entries(dir)
                                            			if (dir != basedir)
                                            				temp_path = dir.dup
                                            				temp_path.slice!(basedir)
                                            				temp_folder_abs_path = temp_folder+temp_path
                                            				Dir.delete(temp_folder_abs_path) if(File.exists?(temp_folder_abs_path))
                                            				Dir.mkdir(temp_folder_abs_path, 0700)
                                            			else
                                            				temp_folder_abs_path = temp_folder
                                            			end #if
                                            			entries.delete(".")
                                            			entries.delete("..")
                                            			
                                            			#create a group and a texture writer object for copying imgfiles
                                            			entries.each {|entry|
                                            				entry_absolute_path = File.join(dir, entry)
                                            				file_validity = self.checkValidity(entry_absolute_path)
                                            				case file_validity
                                            					when "directory"
                                            						#recurse through the child directories and copy the files
                                            						self.copyInstructorFiles(temp_folder,entry_absolute_path,basedir)
                                            					when "imgfile"
                                            						grp = Sketchup.active_model.entities.add_group
                                            						tw  = Sketchup.create_texture_writer
                                            						#so create a material name
                                            						filename = File.basename(entry_absolute_path)
                                            						fileext  = File.extname(filename)
                                            						filebase = File.basename(entry_absolute_path, fileext)
                                            						matname = "instructor_"+filebase + rand(1000).to_s
                                            						
                                            						#load the image into a material
                                            						mat = Sketchup.active_model.materials.add(matname)
                                            						mat.texture = entry_absolute_path
                                            						mat.texture.size = [mat.texture.image_height,mat.texture.image_width]
                                            						
                                            						#apply the material to a group and export to temp directory
                                            						grp.material = mat
                                            						tw.load(grp)
                                            						result = tw.write(grp, File.join(temp_folder_abs_path,entry))
                                            						
                                            						case result
                                            							when 0
                                            								#puts "file written; "+temp_folder_abs_path+"/"+filename
                                            							when 1; puts "file failed (invalid tiff); "+temp_folder_abs_path+"/"+filename
                                            							when 2; puts "file failed (unknown); "+temp_folder_abs_path+"/"+filename
                                            						end #case
                                            						
                                            						#delete the material
                                            						grp.material = nil
                                            						Sketchup.active_model.materials.remove mat
                                            						grp.erase! if grp.valid?
                                            					when "unknowntype"
                                            						source = File.open(entry_absolute_path)
                                            						target = File.open(File.join(temp_folder_abs_path,entry), "w")
                                            						
                                            						target.write( source.read(64) ) while not source.eof?
                                            						
                                            						source.close
                                            						target.close
                                            				end #case
                                            			}
                                            		end #def
                                            		
                                            		def copyInstructor
                                            			@instructor_folder = File.expand_path(@instructor_folder)
                                            			@temp_folder       = File.expand_path(@temp_folder)		
                                            			#on mac there's no need to copy, just to generate a correct relative path
                                            			#as other volumes can be accessed through the /volumes/ folder
                                            			#so @instructor_folder == @temp_folder only on mac
                                            			if @instructor_folder != @temp_folder
                                            				#check if paths are on the same drive in windows, if they are
                                            				#there's no need to copy as they can be accessed through relative paths
                                            				instructor_path_array = @instructor_folder.split("/")
                                            				temp_path_array = @temp_folder.split("/")
                                            				if instructor_path_array[0] != temp_path_array[0]
                                            					#check if the temp path is equal to ENV["TEMP"]
                                            					#if so we need to generate a unique instructor folder id
                                            					if @temp_folder == File.expand_path( ENV["TEMP"] )
                                            						temp_instructor_folder = File.join(@temp_folder, "instructor-"+Time.now.to_f.round.to_s)
                                            					elsif (temp_path_array[temp_path_length-1] != "instructor")
                                            						temp_instructor_folder = File.join(@temp_folder, "instructor")
                                            					else
                                            						temp_instructor_folder = @temp_folder
                                            					end #if
                                            					Dir.delete(temp_instructor_folder) if(File.exists?(temp_instructor_folder))
                                            					Dir.mkdir(temp_instructor_folder, 0700)
                                            					self.copyInstructorFiles(temp_instructor_folder,@instructor_folder,@instructor_folder)
                                            					@temp_folder = temp_instructor_folder
                                            				else
                                            					puts "on same drive"
                                            				end
                                            			else
                                            				puts "on a mac"
                                            			end #if
                                            		end #def
                                            		
                                            		def returnTempInstructorFolderName
                                            			#return the relative path from ENV["TEMP"] so user can store this
                                            			#in a default and use it for consistency between plugins and sessions
                                            			instructor_path_array = @instructor_folder.split("/")
                                            			temp_path_array = @temp_folder.split("/")
                                            			if (@instructor_folder != @temp_folder) && (instructor_path_array[0] != temp_path_array[0])
                                            				temp_instructor_folder_name = @temp_folder.dup
                                            				temp_instructor_folder_name.slice!(File.expand_path( ENV["TEMP"] ))
                                            				return temp_instructor_folder_name[1,temp_instructor_folder_name.length]
                                            			else
                                            				return false
                                            			end
                                            		end #def
                                            		
                                            		def returnRelativePath
                                            			tool_path = Sketchup.get_resource_path( 'helpcontent/tool' )
                                            			temp_path = @temp_folder
                                            			#need to reverse loop through tool path by the separator until
                                            			#we get a match starting at the first line of the temp path
                                            			relativity_index = 0
                                            			common_directory_path = temp_path
                                            			tool_path_array = File.expand_path(tool_path).split('/')
                                            			0.upto(tool_path_array.length-1) {|i|
                                            				common_directory = tool_path_array[0, tool_path_array.length - i]
                                            				if [PLATFORM].grep(/mswin/)==[PLATFORM]
                                            					common_directory_path = common_directory.join('/')
                                            				else
                                            					common_directory_path = '/'+common_directory.join('/')
                                            				end
                                            				if common_directory_path == temp_path[0,common_directory_path.length]
                                            					break
                                            				end
                                            				relativity_index = relativity_index.next
                                            			}
                                            			
                                            			temp_path = temp_path[common_directory_path.length,temp_path.length]
                                            			path_to_common = "..#{File;;SEPARATOR}" * relativity_index
                                            			relative_path = File.join( path_to_common, temp_path)
                                            			return relative_path
                                            		end #def
                                            	
                                            	end #class
                                            

                                            Script Updated on 2011-03-13: See http://forums.sketchucation.com/viewtopic.php?f=180&t=31060&p=315364#p315364


                                            Load Instructor [Updated 10-03-2011:3.20am]

                                            1 Reply Last reply Reply Quote 0
                                            • 1
                                            • 2
                                            • 3
                                            • 4
                                            • 5
                                            • 3 / 5
                                            • First post
                                              Last post
                                            Buy SketchPlus
                                            Buy SUbD
                                            Buy WrapR
                                            Buy eBook
                                            Buy Modelur
                                            Buy Vertex Tools
                                            Buy SketchCuisine
                                            Buy FormFonts

                                            Advertisement