help = File.join(File.dirname(__FILE__), 'myextensions', 'help.html')
UI.openURL("file:///#{help}")
This function has worked for years. Now I have a MAC user who says it’s broken in SketchUp 2019. No error message, just nothing happens when he clicks on my Help icon.
He reports another extension that has a function to open Finder that is also broken.
I’m a windows developer so cannot test. Has anyone else seen this?
As of SU 2019.3, OpenURL no longer encodes its argument for you. In particular if the URL string contains any spaces you must replace them with %20, the URL code for space. Other special characters could also need encoding, so print out the string to check.
require 'uri'
# Default replacement:
path = URI.encode("file:///C:/Program Files/SketchUp/Some Subfolder/@!help.html")
#=> file:///C:/Program%20Files/SketchUp/Some%20Subfolder/@!help.html
# Specify a character set to replace as 2nd argument:
path = URI.encode(
"file:///C:/Program Files/SketchUp/Some Subfolder/@!help.html",
" !@" # ie: space, exclamation point, asterisk
)
#=> file:///C:/Program%20Files/SketchUp/Some%20Subfolder/%40%21help.html
NOTE: These methods have a note that they are obsolete and that there are alternatives like CGI.escape and URI.encode_www_form_component, but they both want to change spaces to “+” which might not work well. (I’ve no Mac so Steve’ll need to weight in.)
They fixed it because of a reason. The Release Notes say simply …
Fixed UI.openURL to not perform URL encoding on Mac for cross platform consistency.
… but there was a problem where encoded URLs could not find valid resources on the Mac.
(Might have been something that began happening with the Catalina release. :shrug: )
I tend to always use system commands to open files (or folders)
# OSX vs WINDOWS
PLATFORM_IS_OSX = ((Object::RUBY_PLATFORM =~ /darwin/i) ? true : false).freeze()
PLATFORM_IS_WINDOWS = (!PLATFORM_IS_OSX).freeze()
# get folder path and do repl
folder_path = "path/to/the/folder"
folder_path = folder_path.gsub("/", "\\\\" if PLATFORM_IS_WINDOWS
# construct the command to open Explorer on Windows or Finder on OsX
command = PLATFORM_IS_WINDOWS ? "start \"\" \"#{folder_path}\"" : "open \"#{folder_path}\""
Could this fix for cross-platform inconsistencies with URLs have introduced a platform difference for local paths? I’ve used openURL quite a few times to open local documents.
The URL specs say either %20 or + may substitute for space.
Edit: After some more research, it seems that there has been confusion about this for some time. The established practice when encoding the request part (after the ?) in an HTML GET or POST request was to use +. So, using + instead of %20 is still generally allowed in that context. But for the path part of the URL it seems to be preferred to use %20 for consistency with how other illegal characters are encoded.
The change in behavior in UI.openURL jumped out on Mac because the normal path for extension files is
The “PLATFORM_IS” approach is extremely problematic to any developer who does have both platforms to test. You make the change, assemble all the code, upload it to the warehouse, thousands of users download it, then you see typo that you couldn’t test.
Opening a 30 page user manual in the default browser is much better than using HTML dialog. The help file can contain links to other websites, and the user has browser support for search within the document.
Before Trimble allowed such a change, they should have performed a global search for OpenURL in all ruby code in the warehouse (I expect that they keep the unencrypted ruby) and send a red alert email to every developer. Another indication that adult supervision is lacking.
Why?
Just create a method that does this good for once and for all and just call that method from everywhere in your code…
# somewhere in constants.rb f.e.
# OSX vs WINDOWS
PLATFORM_IS_OSX = ((Object::RUBY_PLATFORM =~ /darwin/i) ? true : false).freeze()
PLATFORM_IS_WINDOWS = (!PLATFORM_IS_OSX).freeze()
# somewhere in utils.rb f.e.
def open_external(path)
# do replace on windows
path= path.gsub("/", "\\\\") if PLATFORM_IS_WINDOWS
# construct the command to open Explorer on Windows or Finder on OsX
command = PLATFORM_IS_WINDOWS ? "start \"\" \"#{path}\"" : "open \"#{path}\""
system(command)
end #def
# somewhere in your code
open_external("C:/users/") # this should work both for folders and files
It means being aware of the big picture. “Solutions” like yours are what the API is for.
It maybe it should have a method “OpenURLfromSU” well tested by Trimble on both platforms to do thus.
It should not have to be reinvented and propagated by every developer.
if you check the API or Julia’s link. you’ll see that SU does have a platform method…
Sketchup.platform == :platform_win
# or
Sketchup.platform == :platform_osx
and as you don’t even need a url to open a html file in a browser or any in Finder on a mac, you set yourself up for a fall…
help = File.join(File.dirname(__FILE__), 'myextensions', 'help.html')
if Sketchup.platform == :platform_win
UI.openURL("file:///#{help}")
else # use one of the incredibly common, standard Ruby methods
# all these work
`open "#{help}"`
# or
%x[ open "#{help}" ]
# or
system("open #{help.inspect}")
# there are even more variants, this opens any file type in Finder...
%x[ open -R #{help.inspect}]
end
there is no need to reinvent the wheel if carry out some ‘due diligence’ before publishing…
Never noticed it. Got added in 2014 it seems. The check I use has been stuck on top of my constants.rb file for ages I believe.
Will change my starter kit to use the sketchup specific method. Thanks.
It’s totally unnecessary to replace forward unix-like slashes with double backslashes on Windows. Windows NT has always AFAIK accepted pathnames with forward slashes. I usually do the opposite, replacing backslashes with forward slashes (there are some Sketchup module methods that return double backslashed pathnames on Windows, but shouldn’t have been written this way.) …
path = path.gsub(/(\\\\|\\)/,'/') if PLATFORM_IS_WIN
I think the main exception is registry hive paths.
RUBY_PLATFORM being a constant defined in Object, is global.
The class qualification is not necessary.
=~ does not return a boolean true || false so you use a tertiary IF, but instead it’s better (more elegant) to do the opposite and use !~ method which does directly return true || false so no tertiary IF is needed (ie., it’s faster.) Ex …
The Sketchup.platform method came out for SU2014 but for backward compatibility you can use a rescue clause when the Sketchup module doesn’t respond to a ::platform() call. IE …
Freezing / Constants are not really constant. Ie …
EDIT (ADD): Trying to freeze an immediate singleton object like true or false is a frivolous endeavor.
PLATFORM_IS_OSX = !PLATFORM_IS_WIN.freeze()
Then do this …
PLATFORM_IS_OSX = "I've been changed!"
You get a warning that the constant was previously set, but it is still reassigned to point at the new object (in this case a String.) The global = reference assignment function is an interpreter function, not a Ruby method, so it can reassign references that point at frozen objects so that they point at other objects (frozen or not.)
So to truly freeze your constants, you must wrap them within a nested mixin module, freeze that module, and then mixin (via include) this module wherever you need to access these constants.
module Author
module SomePlugin
module Constants
PLATFORM_IS_WIN =(
Sketchup.platform == :platform_win rescue RUBY_PLATFORM !~ /darwin/i
)
PLATFORM_IS_OSX = !PLATFORM_IS_WIN
end
Constants.freeze
include Constants
# Plugin module code here can access Constants.constants
end
end
Or perhaps the constants are in a separate file oustide the current plugin module. Ex …
# plugin_main.rb
require 'constants.rb'
module Author
module SomePlugin
include Author::Constants
# Plugin module code here can access Author::Constants.constants
end
end