@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]