diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/Info.lua b/Info.lua new file mode 100644 index 0000000..c786ed1 --- /dev/null +++ b/Info.lua @@ -0,0 +1,16 @@ +return { + LrSdkVersion = 4.0, + LrSdkMinimumVersion = 3.0, -- minimum SDK version required by this plug-in + + LrToolkitIdentifier = 'com.adobe.lightroom.export.stipple', + LrPluginName = LOC "$$$/Stipple/PluginName=Stipple", + + LrExportServiceProvider = { + title = LOC "$$$/Stipple/Stipple-title=Stipple", + file = 'StippleExportServiceProvider.lua', + }, + + LrMetadataProvider = 'StippleMetadataDefinition.lua', + + VERSION = { major=0, minor=1, revision=1, build=1, }, +} \ No newline at end of file diff --git a/StippleAPI.lua b/StippleAPI.lua new file mode 100644 index 0000000..d69a312 --- /dev/null +++ b/StippleAPI.lua @@ -0,0 +1,407 @@ + -- Lightroom SDK +local LrBinding = import 'LrBinding' +local LrDate = import 'LrDate' +local LrDialogs = import 'LrDialogs' +local LrErrors = import 'LrErrors' +local LrFunctionContext = import 'LrFunctionContext' +local LrHttp = import 'LrHttp' +local LrMD5 = import 'LrMD5' +local LrPathUtils = import 'LrPathUtils' +local LrView = import 'LrView' +local LrXml = import 'LrXml' + +local prefs = import 'LrPrefs'.prefsForPlugin() + +local bind = LrView.bind +local share = LrView.share + +local logger = import 'LrLogger'( 'StippleAPI' ) +logger:enable('print') + +local JSON = require 'json' +local urlBase = 'https://stipple.com' + +--============================================================================-- + +StippleAPI = {} + +-------------------------------------------------------------------------------- + +local appearsAlive + +-------------------------------------------------------------------------------- + +local function formatError( nativeErrorCode ) + return LOC "$$$/Stipple/Error/NetworkFailure=Could not contact the Stipple web service. Please check your Internet connection." +end + +-------------------------------------------------------------------------------- + +local function trim( s ) + return string.gsub( s, "^%s*(.-)%s*$", "%1" ) +end + +-------------------------------------------------------------------------------- + +function StippleAPI.showApiKeyDialog( message ) + LrFunctionContext.callWithContext( 'StippleAPI.showApiKeyDialog', function( context ) + local f = LrView.osFactory() + local properties = LrBinding.makePropertyTable( context ) + + properties.apiKey = prefs.apiKey + + local contents = f:column { + bind_to_object = properties, + spacing = f:control_spacing(), + fill = 1, + + f:static_text { + title = LOC "$$$/Stipple/ApiKeyDialog/Message=In order to use the Stipple plug-in, you must obtain an API key from Stipple.com. Sign on to Stipple and register for a key.", + fill_horizontal = 1, + width_in_chars = 55, + height_in_lines = 2, + size = 'small', + }, + + message and f:static_text { + title = message, + fill_horizontal = 1, + width_in_chars = 55, + height_in_lines = 2, + size = 'small', + text_color = import 'LrColor'( 1, 0, 0 ), + } or 'skipped item', + f:row { + spacing = f:label_spacing(), + + f:static_text { + title = LOC "$$$/Stipple/ApiKeyDialog/Key=API Key:", + alignment = 'right', + width = share 'title_width', + }, + + f:edit_field { + fill_horizonal = 1, + width_in_chars = 35, + value = bind 'apiKey', + }, + }, + } + + local result = LrDialogs.presentModalDialog { + title = LOC "$$$/Stipple/ApiKeyDialog/Title=Enter Your Stipple API Key", + contents = contents, + accessoryView = f:push_button { + title = LOC "$$$/Stipple/ApiKeyDialog/GoToStipple=Get Stipple API Key...", + action = function() + LrHttp.openUrlInBrowser( urlBase .. "/api_docs/v1" ) + end + }, + } + + if result == 'ok' then + prefs.apiKey = trim ( properties.apiKey ) + else + LrErrors.throwCanceled() + end + end ) +end + +-------------------------------------------------------------------------------- + +function StippleAPI.getApiKeyAndSecret() + local apiKey = prefs.apiKey + + while not(type( apiKey ) == 'string' and #apiKey > 10) do + local message + + if apiKey then + message = LOC "$$$/Stipple/ApiKeyDialog/Invalid=The key below is not valid." + end + + StippleAPI.showApiKeyDialog( message ) + apiKey = prefs.apiKey + end + + return apiKey +end + +-------------------------------------------------------------------------------- + +function StippleAPI.makeApiSignature( params ) + local apiKey = StippleAPI.getApiKeyAndSecret() + + if not params.api_key then + params.api_key = apiKey + end + + -- Get list of arguments in sorted order. + local argNames = {} + for name in pairs( params ) do + table.insert( argNames, name ) + end + + table.sort( argNames ) + + -- Build the secret string to be MD5 hashed. + local allArgs = sharedSecret + for _, name in ipairs( argNames ) do + if params[ name ] then -- might be false + allArgs = string.format( '%s%s%s', allArgs, name, params[ name ] ) + end + end + + return LrMD5.digest( allArgs ) +end + +-------------------------------------------------------------------------------- + +function StippleAPI.callRestMethod( propertyTable, params ) + local apiKey = StippleAPI.getApiKeyAndSecret() + + if not params.api_key then + params.api_key = apiKey + end + + local suppressError = params.suppressError + local suppressErrorCodes = params.suppressErrorCodes + local skipAuthToken = params.skipAuthToken + + params.suppressError = nil + params.suppressErrorCodes = nil + params.skipAuthToken = nil + + local url = string.format( urlBase .. '/api/v1/%s', assert( params.url ) ) + + for name, value in pairs( params ) do + local query_seperator = '?' + + if name ~= 'url' and value then + local gsubString = '([^0-9A-Za-z])' + + value = tostring( value ) + + if name ~= 'tag_id' then + value = string.gsub( value, gsubString, function( c ) return string.format( '%%%02X', string.byte( c ) ) end ) + end + + value = string.gsub( value, ' ', '+' ) + params[ name ] = value + + url = string.format( '%s%s%s=%s', url, query_seperator, name, value ) + query_seperator = '&' + end + end + + logger:info( 'calling Stipple API via URL:', url ) + local response, hdrs = LrHttp.get( url ) + logger:info( 'Stipple response:', response ) + + if not response then + appearsAlive = false + + if suppressError then + return { stat = "noresponse" } + else + if hdrs and hdrs.error then + LrErrors.throwUserError( formatError( hdrs.error.nativeCode ) ) + end + end + end + + -- Mac has different implementation with that on Windows when the server refuses the request. + if hdrs.status ~= 200 then + LrErrors.throwUserError( formatError( hdrs.status ) ) + end + + appearsAlive = true + + local json = JSON:decode(response) + + if suppressErrorCodes then + local errorCode = simpleXml and simpleXml.err and tonumber( simpleXml.err.code ) + + if errorCode and suppressErrorCodes[ errorCode ] then + suppressError = true + end + end + + if tonumber(json.status) == 200 or suppressError then + logger:info( 'Stipple API returned status ' .. json.status ) + + return json, response + else + logger:warn( 'Stipple API returned error', tostring(json.status) ) + LrErrors.throwUserError( LOC( "$$$/Stipple/Error/API=Stipple API returned an error message (function ^1, status ^2, error ^3)", + tostring(params.url), tostring(json.status), tostring(json.error))) + end +end + +-------------------------------------------------------------------------------- + +function StippleAPI.uploadPhoto( propertyTable, params ) + assert( type( params ) == 'table', 'StippleAPI.uploadPhoto: params must be a table' ) + + local apiKey = StippleAPI.getApiKeyAndSecret() + local postUrl = params.id and urlBase .. '/api/v1/photos/update' or urlBase .. '/api/v1/photos/upload/' + local originalParams = params.id and table.shallowcopy( params ) + + logger:info( 'uploading photo', params.filePath ) + + local filePath = assert( params.filePath ) + params.filePath = nil + + local fileName = LrPathUtils.leafName( filePath ) + local mimeChunks = {} + + for argName, argValue in pairs( params ) do + if argName ~= 'api_key' and argName ~= 'photo' and argValue then + mimeChunks[ #mimeChunks + 1 ] = { name = argName, value = argValue } + end + end + + mimeChunks[ #mimeChunks + 1 ] = { name = 'api_key', value = apiKey } + + if params.photo.caption then + mimeChunks[ #mimeChunks + 1 ] = { name = 'photo[caption]', value = params.photo.caption } + end + + mimeChunks[ #mimeChunks + 1 ] = { name = 'photo[source_page]', value = params.photo.source_page } + mimeChunks[ #mimeChunks + 1 ] = { name = 'file', fileName = fileName, filePath = filePath, contentType = 'application/octet-stream' } + + local response, hdrs = LrHttp.postMultipart( postUrl, mimeChunks ) -- Post it and wait for confirmation. + + if not response then + if hdrs and hdrs.error then + LrErrors.throwUserError( formatError( hdrs.error.nativeCode ) ) + end + end + + -- Parse Stipple response for photo ID. + local json = JSON:decode(response) + + if tonumber(json.status) == 200 then + return json.data.photo.id + elseif params.id and json.error and tonumber(hdrs.error.nativeCode) == 422 then + -- Photo is missing. Most likely, the user deleted it outside of Lightroom. Just repost it. + +-- originalParams.id = nil +-- return StippleAPI.uploadPhoto( propertyTable, originalParams ) + LrErrors.throwUserError( LOC( "$$$/Stipple/Error/API/Upload=Stipple API Falling into the elseif case")) + else + logger:info( 'uploading photo', json.status ) + + LrErrors.throwUserError( LOC( "$$$/Stipple/Error/API/Upload=Stipple API returned an error message (function supload, message ^1)", + tostring( json.status ))) + end +end + +-------------------------------------------------------------------------------- + +function StippleAPI.openAuthUrl() + local response = StippleAPI.callRestMethod( nil, { url = 'users/me' } ) + + return response.data +end + +-------------------------------------------------------------------------------- + +local function getPhotoInfo( propertyTable, params ) + return nil, nil +end + +-------------------------------------------------------------------------------- + +function StippleAPI.constructPhotoURL( propertyTable, params ) + return urlBase .. '/photos/' .. params.id +end + +-------------------------------------------------------------------------------- + +function StippleAPI.constructPhotosetURL( propertyTable, photosetId ) + return urlBase .. "/photos/" .. propertyTable.nsid .. "/sets/" .. photosetId +end + +-------------------------------------------------------------------------------- + +function StippleAPI.constructPhotostreamURL( propertyTable ) + return urlBase .. "/a#library/untagged" +end + +------------------------------------------------------------------------------- + +local function traversePhotosetsForTitle( node, title ) + return '' +end + +-------------------------------------------------------------------------------- + +function StippleAPI.createOrUpdatePhotoset( propertyTable, params ) + return true +end + +-------------------------------------------------------------------------------- + +function StippleAPI.listPhotosFromPhotoset( propertyTable, params ) + return nil +end + +-------------------------------------------------------------------------------- + +function StippleAPI.setPhotosetSequence( propertyTable, params ) + return true +end + +-------------------------------------------------------------------------------- + +function StippleAPI.addPhotosToSet( propertyTable, params ) + return true +end + +-------------------------------------------------------------------------------- + +function StippleAPI.deletePhoto( propertyTable, params ) + return true +end + +-------------------------------------------------------------------------------- + +function StippleAPI.deletePhotoset( propertyTable, params ) + return true +end + +-------------------------------------------------------------------------------- + +local function removePhotoTags( propertyTable, node, previous_tag ) + return false +end + +-------------------------------------------------------------------------------- + +function StippleAPI.setImageTags( propertyTable, params ) + return true +end + +-------------------------------------------------------------------------------- + +function StippleAPI.getUserInfo( propertyTable, params ) + return { } +end + +-------------------------------------------------------------------------------- + +function StippleAPI.getComments( propertyTable, params ) + return nil +end + +-------------------------------------------------------------------------------- + +function StippleAPI.addComment( propertyTable, params ) + return +end + +-------------------------------------------------------------------------------- + +function StippleAPI.testStippleConnection( propertyTable ) + return true +end \ No newline at end of file diff --git a/StippleExportDialogSections.lua b/StippleExportDialogSections.lua new file mode 100644 index 0000000..e69de29 diff --git a/StippleExportServiceProvider.lua b/StippleExportServiceProvider.lua new file mode 100644 index 0000000..f41ac11 --- /dev/null +++ b/StippleExportServiceProvider.lua @@ -0,0 +1,688 @@ + +-- Lightroom SDK +local LrBinding = import 'LrBinding' +local LrDialogs = import 'LrDialogs' +local LrErrors = import 'LrErrors' +local LrFileUtils = import 'LrFileUtils' +local LrPathUtils = import 'LrPathUtils' +local LrView = import 'LrView' + +local logger = import 'LrLogger'( 'StippleAPI' ) +logger:enable('print') + +-- Common shortcuts +local bind = LrView.bind +local share = LrView.share + +-- JSON Reading/Writing +local JSON = require 'json' + +-- Stipple plug-in +require 'StippleAPI' +require 'StipplePublishSupport' + +local exportServiceProvider = {} + +-- A typical service provider would probably roll all of this into one file, but +-- this approach allows us to document the publish-specific hooks separately. +for name, value in pairs( StipplePublishSupport ) do + exportServiceProvider[ name ] = value +end + +exportServiceProvider.supportsIncrementalPublish = 'only' + +-------------------------------------------------------------------------------- +--- (optional) Plug-in defined value declares which fields in your property table should + -- be saved as part of an export preset or a publish service connection. If present, + -- should contain an array of items with key and default values. For example: + --
+        -- exportPresetFields = {
+ --     { key = 'username', default = "" },
+ --     { key = 'fullname', default = "" },
+ --     { key = 'nsid', default = "" },
+ --     { key = 'privacy', default = 'public' },
+ --     { key = 'privacy_family', default = false },
+ --     { key = 'privacy_friends', default = false },
+ -- }
+ --
+ --

The key item should match the values used by your user interface + -- controls.

+ --

The default item is the value to the first time + -- your plug-in is selected in the Export or Publish dialog. On second and subsequent + -- activations, the values chosen by the user in the previous session are used.

+ --

First supported in version 1.3 of the Lightroom SDK.

+ -- @name exportServiceProvider.exportPresetFields + -- @class property + +exportServiceProvider.exportPresetFields = { + { key = 'username', default = "" }, + { key = 'fullname', default = "" }, + { key = 'nsid', default = "" }, +-- { key = 'isUserPro', default = false }, +-- { key = 'auth_token', default = '' }, +-- { key = 'privacy', default = 'public' }, +-- { key = 'privacy_family', default = false }, +-- { key = 'privacy_friends', default = false }, +-- { key = 'safety', default = 'safe' }, +-- { key = 'hideFromPublic', default = false }, + { key = 'type', default = 'photo' }, + { key = 'addToPhotoset', default = false }, + { key = 'photoset', default = '' }, + { key = 'titleFirstChoice', default = 'title' }, + { key = 'titleSecondChoice', default = 'filename' }, + { key = 'titleRepublishBehavior', default = 'replace' }, +} + +-------------------------------------------------------------------------------- +--- (optional) Plug-in defined value restricts the display of sections in the Export + -- or Publish dialog to those named. You can use either hideSections or + -- showSections, but not both. If present, this should be an array + -- containing one or more of the following strings: + -- + --

You cannot suppress display of the "Connection Name" section in the Publish Manager dialog.

+ --

If you suppress the "exportLocation" section, the files are rendered into + -- a temporary folder which is deleted immediately after the Export operation + -- completes.

+ --

First supported in version 1.3 of the Lightroom SDK.

+ -- @name exportServiceProvider.showSections + -- @class property + +--exportServiceProvider.showSections = { 'fileNaming', 'fileSettings', etc... } -- not used for Stipple plug-in + +-------------------------------------------------------------------------------- +--- (optional) Plug-in defined value suppresses the display of the named sections in + -- the Export or Publish dialogs. You can use either hideSections or + -- showSections, but not both. If present, this should be an array + -- containing one or more of the following strings: + -- + --

You cannot suppress display of the "Connection Name" section in the Publish Manager dialog.

+ --

If you suppress the "exportLocation" section, the files are rendered into + -- a temporary folder which is deleted immediately after the Export operation + -- completes.

+ --

First supported in version 1.3 of the Lightroom SDK.

+ -- @name exportServiceProvider.hideSections + -- @class property + +exportServiceProvider.hideSections = {'exportLocation', 'fileNaming', 'video'} + +-------------------------------------------------------------------------------- +--- (optional, Boolean) If your plug-in allows the display of the exportLocation section, + -- this property controls whether the item "Temporary folder" is available. + -- If the user selects this option, the files are rendered into a temporary location + -- on the hard drive, which is deleted when the export finished. + --

If your plug-in hides the exportLocation section, this temporary + -- location behavior is always used.

+ -- @name exportServiceProvider.canExportToTemporaryLocation + -- @class property + +-- exportServiceProvider.canExportToTemporaryLocation = true -- not used for Stipple plug-in + +-------------------------------------------------------------------------------- +--- (optional) Plug-in defined value restricts the available file format choices in the + -- Export or Publish dialogs to those named. You can use either allowFileFormats or + -- disallowFileFormats, but not both. If present, this should be an array + -- containing one or more of the following strings: + -- + --

This property affects the output of still photo files only; + -- it does not affect the output of video files. + -- See canExportVideo.)

+ --

First supported in version 1.3 of the Lightroom SDK.

+ -- @name exportServiceProvider.allowFileFormats + -- @class property + +exportServiceProvider.allowFileFormats = {'JPEG'} + +-------------------------------------------------------------------------------- +--- (optional) Plug-in defined value suppresses the named file formats from the list + -- of available file format choices in the Export or Publish dialogs. + -- You can use either allowFileFormats or + -- disallowFileFormats, but not both. If present, + -- this should be an array containing one or more of the following strings: + -- + --

Affects the output of still photo files only, not video files. + -- See canExportVideo.

+ --

First supported in version 1.3 of the Lightroom SDK.

+ -- @name exportServiceProvider.disallowFileFormats + -- @class property + +--exportServiceProvider.disallowFileFormats = { 'PSD', 'TIFF', 'DNG', 'ORIGINAL' } -- not used for Stipple plug-in + +-------------------------------------------------------------------------------- +--- (optional) Plug-in defined value restricts the available color space choices in the + -- Export or Publish dialogs to those named. You can use either allowColorSpaces or + -- disallowColorSpaces, but not both. If present, this should be an array + -- containing one or more of the following strings: + -- + --

Affects the output of still photo files only, not video files. + -- See canExportVideo.

+ --

First supported in version 1.3 of the Lightroom SDK.

+ -- @name exportServiceProvider.allowColorSpaces + -- @class property + +exportServiceProvider.allowColorSpaces = {'sRGB'} + +-------------------------------------------------------------------------------- +--- (optional) Plug-in defined value suppresses the named color spaces from the list + -- of available color space choices in the Export or Publish dialogs. You can use either allowColorSpaces or + -- disallowColorSpaces, but not both. If present, this should be an array + -- containing one or more of the following strings: + -- + --

Affects the output of still photo files only, not video files. + -- See canExportVideo.

+ --

First supported in version 1.3 of the Lightroom SDK.

+ -- @name exportServiceProvider.disallowColorSpaces + -- @class property + + +--exportServiceProvider.disallowColorSpaces = { 'AdobeRGB', 'ProPhotoRGB' } -- not used for Stipple plug-in + +-------------------------------------------------------------------------------- +--- (optional, Boolean) Plug-in defined value is true to hide print resolution controls + -- in the Image Sizing section of the Export or Publish dialog. + -- (Recommended when uploading to most web services.) + --

First supported in version 1.3 of the Lightroom SDK.

+ -- @name exportServiceProvider.hidePrintResolution + -- @class property + +exportServiceProvider.hidePrintResolution = true + +-------------------------------------------------------------------------------- +--- (optional, Boolean) When plug-in defined value istrue, both video and + -- still photos can be exported through this plug-in. If not present or set to false, + -- video files cannot be exported through this plug-in. If set to the string "only", + -- video files can be exported, but not still photos. + --

No conversions are available for video files. They are simply + -- copied in the same format that was originally imported into Lightroom.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name exportServiceProvider.canExportVideo + -- @class property + +exportServiceProvider.canExportVideo = false -- video is not supported through this sample plug-in + +-------------------------------------------------------------------------------- +-- FLICKR SPECIFIC: Helper functions and tables. + +local function updateCantExportBecause( propertyTable ) + if not propertyTable.validAccount then + propertyTable.LR_cantExportBecause = LOC "$$$/Stipple/ExportDialog/NoLogin=You haven't logged in to Stipple yet." + return + end + + propertyTable.LR_cantExportBecause = nil +end + +local displayNameForTitleChoice = { + filename = LOC "$$$/Stipple/ExportDialog/Title/Filename=Filename", + title = LOC "$$$/Stipple/ExportDialog/Title/Title=IPTC Title", + empty = LOC "$$$/Stipple/ExportDialog/Title/Empty=Leave Blank", +} + +--local kSafetyTitles = { +-- safe = LOC "$$$/Stipple/ExportDialog/Safety/Safe=Safe", +-- moderate = LOC "$$$/Stipple/ExportDialog/Safety/Moderate=Moderate", +-- restricted = LOC "$$$/Stipple/ExportDialog/Safety/Restricted=Restricted", +--} + +local function booleanToNumber( value ) + return value and 1 or 0 +end + +local privacyToNumber = { + private = 0, + public = 1, +} + +local safetyToNumber = { + safe = 1, + moderate = 2, + restricted = 3, +} + +local contentTypeToNumber = { + photo = 1, + screenshot = 2, + other = 3, +} + +local function getStippleTitle( photo, exportSettings, pathOrMessage ) + local title + + -- Get title according to the options in Stipple Title section. + if exportSettings.titleFirstChoice == 'filename' then + title = LrPathUtils.leafName( pathOrMessage ) + elseif exportSettings.titleFirstChoice == 'title' then + title = photo:getFormattedMetadata 'title' + + if ( not title or #title == 0 ) and exportSettings.titleSecondChoice == 'filename' then + title = LrPathUtils.leafName( pathOrMessage ) + end + end + + return title +end + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the + -- user chooses this export service provider in the Export or Publish dialog, + -- or when the destination is already selected when the dialog is invoked, + -- (remembered from the previous export operation). + --

This is a blocking call. If you need to start a long-running task (such as + -- network access), create a task using the LrTasks + -- namespace.

+ --

First supported in version 1.3 of the Lightroom SDK.

+ -- @param propertyTable (table) An observable table that contains the most + -- recent settings for your export or publish plug-in, including both + -- settings that you have defined and Lightroom-defined export settings + -- @name exportServiceProvider.startDialog + -- @class function + +function exportServiceProvider.startDialog( propertyTable ) + -- Clear login if it's a new connection. + if not propertyTable.LR_editingExistingPublishConnection then + propertyTable.username = nil + propertyTable.nsid = nil + end + + -- Can't export until we've validated the login. + propertyTable:addObserver( 'validAccount', function() updateCantExportBecause( propertyTable ) end ) + updateCantExportBecause( propertyTable ) + + -- Make sure we're logged in. + require 'StippleUser' + StippleUser.verifyLogin( propertyTable ) +end + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- chooses a different export service provider in the Export or Publish dialog + -- or closes the dialog. + --

This is a blocking call. If you need to start a long-running task (such as + -- network access), create a task using the LrTasks + -- namespace.

+ --

First supported in version 1.3 of the Lightroom SDK.

+ -- @param propertyTable (table) An observable table that contains the most + -- recent settings for your export or publish plug-in, including both + -- settings that you have defined and Lightroom-defined export settings + -- @param why (string) The reason this function was called. One of + -- 'ok', 'cancel', or 'changedServiceProvider' + -- @name exportServiceProvider.endDialog + -- @class function + +--function exportServiceProvider.endDialog( propertyTable ) + -- not used for Stipple plug-in +--end + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- chooses this export service provider in the Export or Publish dialog. + -- It can create new sections that appear above all of the built-in sections + -- in the dialog (except for the Publish Service section in the Publish dialog, + -- which always appears at the very top). + --

Your plug-in's startDialog + -- function, if any, is called before this function is called.

+ --

This is a blocking call. If you need to start a long-running task (such as + -- network access), create a task using the LrTasks + -- namespace.

+ --

First supported in version 1.3 of the Lightroom SDK.

+ -- @param f (LrView.osFactory object) + -- A view factory object. + -- @param propertyTable (table) An observable table that contains the most + -- recent settings for your export or publish plug-in, including both + -- settings that you have defined and Lightroom-defined export settings + -- @return (table) An array of dialog sections (see example code for details) + -- @name exportServiceProvider.sectionsForTopOfDialog + -- @class function + +function exportServiceProvider.sectionsForTopOfDialog( f, propertyTable ) + return { + { + title = LOC "$$$/Stipple/ExportDialog/Account=Stipple Account", + synopsis = bind 'accountStatus', + + f:row { + spacing = f:control_spacing(), + + f:static_text { + title = bind 'accountStatus', + alignment = 'right', + fill_horizontal = 1, + }, + + f:push_button { + width = tonumber( LOC "$$$/locale_metric/Stipple/ExportDialog/LoginButton/Width=90" ), + title = bind 'loginButtonTitle', + enabled = bind 'loginButtonEnabled', + action = function() + require 'StippleUser' + StippleUser.login(propertyTable) + end, + }, + }, + },{ + title = LOC "$$$/Stipple/ExportDialog/Title=Stipple Title", + synopsis = function(props) + if props.titleFirstChoice == 'title' then + return LOC("$$$/Stipple/ExportDialog/Synopsis/TitleWithFallback=IPTC Title or ^1", displayNameForTitleChoice[ props.titleSecondChoice ]) + else + return props.titleFirstChoice and displayNameForTitleChoice[ props.titleFirstChoice ] or '' + end + end, + + f:column { + spacing = f:control_spacing(), + + f:row { + spacing = f:label_spacing(), + + f:static_text { + title = LOC "$$$/Stipple/ExportDialog/ChooseTitleBy=Set Stipple Title Using:", + alignment = 'right', + width = share 'stippleTitleSectionLabel', + }, + + f:popup_menu { + value = bind 'titleFirstChoice', + width = share 'stippleTitleLeftPopup', + items = { + { value = 'filename', title = displayNameForTitleChoice.filename }, + { value = 'title', title = displayNameForTitleChoice.title }, + { value = 'empty', title = displayNameForTitleChoice.empty }, + }, + }, + + f:spacer { width = 20 }, + + f:static_text { + title = LOC "$$$/Stipple/ExportDialog/ChooseTitleBySecondChoice=If Empty, Use:", + enabled = LrBinding.keyEquals( 'titleFirstChoice', 'title', propertyTable ), + }, + + f:popup_menu { + value = bind 'titleSecondChoice', + enabled = LrBinding.keyEquals( 'titleFirstChoice', 'title', propertyTable ), + items = { + { value = 'filename', title = displayNameForTitleChoice.filename }, + { value = 'empty', title = displayNameForTitleChoice.empty }, + }, + }, + }, + + f:row { + spacing = f:label_spacing(), + + f:static_text { + title = LOC "$$$/Stipple/ExportDialog/OnUpdate=When Updating Photos:", + alignment = 'right', + width = share 'stippleTitleSectionLabel', + }, + + f:popup_menu { + value = bind 'titleRepublishBehavior', + width = share 'stippleTitleLeftPopup', + items = { + { value = 'replace', title = LOC "$$$/Stipple/ExportDialog/ReplaceExistingTitle=Replace Existing Title" }, + { value = 'leaveAsIs', title = LOC "$$$/Stipple/ExportDialog/LeaveAsIs=Leave Existing Title" }, + }, + }, + }, + }, + }, + } +end + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- chooses this export service provider in the Export or Publish dialog. + -- It can create new sections that appear below all of the built-in sections in the dialog. + --

Your plug-in's startDialog + -- function, if any, is called before this function is called.

+ --

This is a blocking call. If you need to start a long-running task (such as + -- network access), create a task using the LrTasks + -- namespace.

+ --

First supported in version 1.3 of the Lightroom SDK.

+ -- @param f (LrView.osFactory object) + -- A view factory object + -- @param propertyTable (table) An observable table that contains the most + -- recent settings for your export or publish plug-in, including both + -- settings that you have defined and Lightroom-defined export settings + -- @return (table) An array of dialog sections (see example code for details) + -- @name exportServiceProvider.sectionsForBottomOfDialog + -- @class function + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called at the beginning + -- of each export and publish session before the rendition objects are generated. + -- It provides an opportunity for your plug-in to modify the export settings. + --

First supported in version 2.0 of the Lightroom SDK.

+ -- @param exportSettings (table) The current export settings. + -- @name exportServiceProvider.updateExportSettings + -- @class function + +--function exportServiceProvider.updateExportSettings( exportSettings ) -- not used for the Stipple sample plug-in +-- exportSettings.LR_format = 'JPEG' +-- exportSettings.LR_jpeg_quality = 100 +-- end + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called for each exported photo + -- after it is rendered by Lightroom and after all post-process actions have been + -- applied to it. This function is responsible for transferring the image file + -- to its destination, as defined by your plug-in. The function that + -- you define is launched within a cooperative task that Lightroom provides. You + -- do not need to start your own task to run this function; and in general, you + -- should not need to start another task from within your processing function. + --

First supported in version 1.3 of the Lightroom SDK.

+ -- @param functionContext (
LrFunctionContext) + -- function context that you can use to attach clean-up behaviors to this + -- process; this function context terminates as soon as your function exits. + -- @param exportContext (LrExportContext) + -- Information about your export settings and the photos to be published. + +function exportServiceProvider.processRenderedPhotos( functionContext, exportContext ) + local exportSession = exportContext.exportSession -- Make a local reference to the export parameters. + local exportSettings = assert( exportContext.propertyTable ) + local nPhotos = exportSession:countRenditions() -- Get the # of photos. + + -- Set progress title. + local progressScope = exportContext:configureProgress { + title = nPhotos > 1 + and LOC( "$$$/Stipple/Publish/Progress=Publishing ^1 photos to Stipple", nPhotos ) + or LOC "$$$/Stipple/Publish/Progress/One=Publishing one photo to Stipple", + } + + local uploadedPhotoIds = {} -- Save off uploaded photo IDs so we can take user to those photos later. + local publishedCollectionInfo = exportContext.publishedCollectionInfo + local isDefaultCollection = publishedCollectionInfo.isDefaultCollection + + -- Look for a photoset id for this collection. + local photosetId = publishedCollectionInfo.remoteId + + -- Get a list of photos already in this photoset so we know which ones we can replace and which have + -- to be re-uploaded entirely. + local photosetPhotoIds = photosetId and StippleAPI.listPhotosFromPhotoset( exportSettings, { photosetId = photosetId } ) + + local photosetPhotosSet = {} -- Turn it into a set for quicker access later. + + if photosetPhotoIds then + for _, id in ipairs( photosetPhotoIds ) do + photosetPhotosSet[ id ] = true + end + end + + local couldNotPublishBecauseFreeAccount = {} + local stipplePhotoIdsForRenditions = {} + local photosetUrl + + for i, rendition in exportContext:renditions { stopIfCanceled = true } do + progressScope:setPortionComplete( ( i - 1 ) / nPhotos ) -- Update progress scope. + + local photo = rendition.photo -- Get next photo. + local stipplePhotoId = stipplePhotoIdsForRenditions[rendition] -- See if we previously uploaded this photo. + + if not rendition.wasSkipped then + local success, pathOrMessage = rendition:waitForRender() -- Update progress scope again once we've got rendered photo. + + progressScope:setPortionComplete( ( i - 0.5 ) / nPhotos ) -- Check for cancellation again after photo has been rendered. + + if progressScope:isCanceled() then break end + + if success then + local title = getStippleTitle( photo, exportSettings, pathOrMessage ) -- Build up common metadata for this photo. + local description = photo:getFormattedMetadata( 'caption' ) + local keywordTags = photo:getFormattedMetadata( 'keywordTagsForExport' ) + local tags + + if keywordTags then + tags = {} + local keywordIter = string.gfind( keywordTags, "[^,]+" ) + + for keyword in keywordIter do + if string.sub( keyword, 1, 1 ) == ' ' then + keyword = string.sub( keyword, 2, -1 ) + end + + if string.find( keyword, ' ' ) ~= nil then + keyword = '"' .. keyword .. '"' + end + + tags[ #tags + 1 ] = keyword + end + end + + local content_type = contentTypeToNumber[ exportSettings.type ] + local previous_tags = photo:getPropertyForPlugin( _PLUGIN, 'previous_tags' ) + local didReplace = not not stipplePhotoId + + stipplePhotoId = StippleAPI.uploadPhoto(exportSettings, { + id = stipplePhotoId, + filePath = pathOrMessage, + photo = { + caption = description, source_page = "Stipple Lightroom Plugin" }, + claim = 1, -- always claim the photo + } + ) + + if didReplace then + -- The replace call used by StippleAPI.uploadPhoto ignores all of the metadata that is passed + -- in above. We have to manually upload that info after the fact in this case. + if exportSettings.titleRepublishBehavior == 'replace' then + --StippleAPI.callRestMethod( exportSettings, { + --method = 'stipple.photos.setMeta', + --photo_id = stipplePhotoId, + --title = title or '', + --description = description or '', + --}) + end + end + + -- When done with photo, delete temp file. There is a cleanup step that happens later, + -- but this will help manage space in the event of a large upload. + LrFileUtils.delete( pathOrMessage ) + + -- Remember this in the list of photos we uploaded. + uploadedPhotoIds[ #uploadedPhotoIds + 1 ] = stipplePhotoId + + -- If this isn't the Photostream, set up the photoset. + if not photosetUrl then + if not isDefaultCollection then + -- Create or update this photoset. + photosetUrl = 'https://stipple.com' + + --photosetId, photosetUrl = StippleAPI.createOrUpdatePhotoset(exportSettings, { + --photosetId = photosetId, + --title = publishedCollectionInfo.name, + --description = ??, + --primary_photo_id = uploadedPhotoIds[ 1 ], + --}) + else + photosetUrl = StippleAPI.constructPhotostreamURL( exportSettings ) -- Photostream: find the URL. + end + end + + rendition:recordPublishedPhotoId(stipplePhotoId) -- Record this Stipple ID with the photo so we know to replace instead of upload. + + local photoUrl + + if (not isDefaultCollection) then + photoUrl = StippleAPI.constructPhotoURL(exportSettings, { + id = stipplePhotoId, + photosetId = photosetId, + }) + + -- Add the uploaded photos to the correct photoset. + -- StippleAPI.addPhotosToSet(exportSettings, { + -- photoId = stipplePhotoId, + -- photosetId = photosetId, + -- }) + else + photoUrl = StippleAPI.constructPhotoURL(exportSettings, { + id = stipplePhotoId, + }) + end + + rendition:recordPublishedPhotoUrl( photoUrl ) + + -- Because it is common for Stipple users (even viewers) to add additional tags + -- via the Stipple web site, so we can avoid removing those user-added tags that + -- were never in Lightroom to begin with. See earlier comment. + photo.catalog:withPrivateWriteAccessDo(function() + photo:setPropertyForPlugin( _PLUGIN, 'previous_tags', table.concat( tags, ',' ) ) + end ) + end + else + -- To get the skipped photo out of the to-republish bin. + rendition:recordPublishedPhotoId(rendition.publishedPhotoId) + end + end + + if #uploadedPhotoIds > 0 then + if (not isDefaultCollection) then + --exportSession:recordRemoteCollectionId( photosetId ) + end + + -- Set up some additional metadata for this collection. + exportSession:recordRemoteCollectionUrl( photosetUrl ) + end + + progressScope:done() +end + +-------------------------------------------------------------------------------- + +return exportServiceProvider \ No newline at end of file diff --git a/StippleLogo.png b/StippleLogo.png new file mode 100644 index 0000000..f4a2fd3 Binary files /dev/null and b/StippleLogo.png differ diff --git a/StippleMetadataDefinition.lua b/StippleMetadataDefinition.lua new file mode 100644 index 0000000..493043b --- /dev/null +++ b/StippleMetadataDefinition.lua @@ -0,0 +1,10 @@ +return { + metadataFieldsForPhotos = { + { + id = 'previous_tags', + dataType = 'string', + }, + }, + + schemaVersion = 2, -- must be a number, preferably a positive integer +} diff --git a/StipplePublishSupport.lua b/StipplePublishSupport.lua new file mode 100644 index 0000000..3fab2a1 --- /dev/null +++ b/StipplePublishSupport.lua @@ -0,0 +1,1480 @@ +-- Lightroom SDK +local LrDialogs = import 'LrDialogs' + +-- Stipple plug-in +require 'StippleAPI' + +--[[ +--- The service definition script for a publish service provider associates + -- the code and hooks that extend the behavior of Lightroom's Publish features + -- with their implementation for your plug-in. The plug-in's Info.lua file + -- identifies this script in the LrExportServiceProvider entry. The script + -- must define the needed callback functions and properties (with the required + -- names and syntax) and assign them to members of the table that it returns. + --

The StipplePublishSupport.lua file of the Stipple sample plug-in provides + -- examples of and documentation for the hooks that a plug-in must provide in order to + -- define a publish service. Because much of the functionality of a publish service + -- is the same as that of an export service, this example builds upon that defined in the + -- StippleExportServiceProvider.lua file.

+ --

The service definition script for a publish service should return a table that contains: + --

+ --

Most of these functions are the same as those defined for an export service provider. + -- Publish services, unlike export services, cannot create presets. (You could think of the + -- publish service itself as an export preset.) The settings tables passed + -- to these callback functions contain only Lightroom-defined settings, and settings that + -- have been explicitly declared in the exportPresetFields list of the publish service. + -- A callback function that you define for a publish service cannot make any changes to the + -- settings table passed to it.

+ -- @module_type Plug-in provided + + module 'SDK - Publish service provider' -- not actually executed, but suffices to trick LuaDocs + +--]] + + +--============================================================================-- + +local publishServiceProvider = {} + +-------------------------------------------------------------------------------- +--- (string) Plug-in defined value is the filename of the icon to be displayed + -- for this publish service provider, in the Publish Services panel, the Publish + -- Manager dialog, and in the header shown when a published collection is selected. + -- The icon must be in PNG format and no more than 24 pixels wide or 19 pixels tall. + --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.small_icon + -- @class property + +publishServiceProvider.small_icon = 'StippleLogo.png' + +-------------------------------------------------------------------------------- +--- (optional, string) Plug-in defined value customizes the behavior of the + -- Description entry in the Publish Manager dialog. If the user does not provide + -- an explicit name choice, Lightroom can provide one based on another entry + -- in the publishSettings property table. This entry contains the name of the + -- property that should be used in this case. + -- @name publishServiceProvider.publish_fallbackNameBinding + -- @class property + +publishServiceProvider.publish_fallbackNameBinding = 'fullname' + +-------------------------------------------------------------------------------- +--- (optional, string) Plug-in defined value customizes the name of a published + -- collection to match the terminology used on the service you are targeting. + --

This string is typically used in combination with verbs that take action on + -- the published collection, such as "Create ^1" or "Rename ^1".

+ --

If not provided, Lightroom uses the default name, "Published Collection."

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.titleForPublishedCollection + -- @class property + +publishServiceProvider.titleForPublishedCollection = LOC "$$$/Stipple/TitleForPublishedCollection=Photoset" + +-------------------------------------------------------------------------------- +--- (optional, string) Plug-in defined value customizes the name of a published + -- collection to match the terminology used on the service you are targeting. + --

Unlike titleForPublishedCollection, this string is typically + -- used by itself. In English, these strings nay be the same, but in + -- other languages (notably German), you may have to use a different form + -- of the name to be gramatically correct. If you are localizing your plug-in, + -- use a separate translation key to make this possible.

+ --

If not provided, Lightroom uses the value of + -- titleForPublishedCollection instead.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.titleForPublishedCollection_standalone + -- @class property + +publishServiceProvider.titleForPublishedCollection_standalone = LOC "$$$/Stipple/TitleForPublishedCollection/Standalone=Photoset" + +-------------------------------------------------------------------------------- +--- (optional, string) Plug-in defined value customizes the name of a published + -- collection set to match the terminoy used on the service you are targeting. + --

This string is typically used in combination with verbs that take action on + -- the published collection set, such as "Create ^1" or "Rename ^1".

+ --

If not provided, Lightroom uses the default name, "Published Collection Set."

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.titleForPublishedCollectionSet + -- @class property + +-- publishServiceProvider.titleForPublishedCollectionSet = "(something)" -- not used for Stipple plug-in + +-------------------------------------------------------------------------------- +--- (optional, string) Plug-in defined value customizes the name of a published + -- collection to match the terminology used on the service you are targeting. + --

Unlike titleForPublishedCollectionSet, this string is typically + -- used by itself. In English, these strings may be the same, but in + -- other languages (notably German), you may have to use a different form + -- of the name to be gramatically correct. If you are localizing your plug-in, + -- use a separate translation key to make this possible.

+ --

If not provided, Lightroom uses the value of + -- titleForPublishedCollectionSet instead.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.titleForPublishedCollectionSet_standalone + -- @class property + +--publishServiceProvider.titleForPublishedCollectionSet_standalone = "(something)" -- not used for Stipple plug-in + +-------------------------------------------------------------------------------- +--- (optional, string) Plug-in defined value customizes the name of a published + -- smart collection to match the terminology used on the service you are targeting. + --

This string is typically used in combination with verbs that take action on + -- the published smart collection, such as "Create ^1" or "Rename ^1".

+ --

If not provided, Lightroom uses the default name, "Published Smart Collection."

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.titleForPublishedSmartCollection + -- @class property + +publishServiceProvider.titleForPublishedSmartCollection = LOC "$$$/Stipple/TitleForPublishedSmartCollection=Smart Photoset" + +-------------------------------------------------------------------------------- +--- (optional, string) Plug-in defined value customizes the name of a published + -- smart collection to match the terminology used on the service you are targeting. + --

Unlike titleForPublishedSmartCollection, this string is typically + -- used by itself. In English, these strings may be the same, but in + -- other languages (notably German), you may have to use a different form + -- of the name to be gramatically correct. If you are localizing your plug-in, + -- use a separate translation key to make this possible.

+ --

If not provided, Lightroom uses the value of + -- titleForPublishedSmartCollectionSet instead.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.titleForPublishedSmartCollection_standalone + -- @class property + +publishServiceProvider.titleForPublishedSmartCollection_standalone = LOC "$$$/Stipple/TitleForPublishedSmartCollection/Standalone=Smart Photoset" + +-------------------------------------------------------------------------------- +--- (optional) If you provide this plug-in defined callback function, Lightroom calls it to + -- retrieve the default collection behavior for this publish service, then use that information to create + -- a built-in default collection for this service (if one does not yet exist). + -- This special collection is marked in italics and always listed at the top of the list of published collections. + --

This callback should return a table that configures the default collection. The + -- elements of the configuration table are optional, and default as shown.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @return (table) A table with the following fields: + -- + -- @name publishServiceProvider.getCollectionBehaviorInfo + -- @class function + +function publishServiceProvider.getCollectionBehaviorInfo( publishSettings ) + return { + defaultCollectionName = LOC "$$$/Stipple/DefaultCollectionName/Photostream=Photostream", + defaultCollectionCanBeDeleted = false, + canAddCollection = true, + maxCollectionSetDepth = 0, -- Collection sets are not supported through the Stipple sample plug-in. + } +end + +-------------------------------------------------------------------------------- +--- When set to the string "disable", the "Go to Published Collection" context-menu item + -- is disabled (dimmed) for this publish service. + --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.titleForGoToPublishedCollection + -- @class property + +publishServiceProvider.titleForGoToPublishedCollection = LOC "$$$/Stipple/TitleForGoToPublishedCollection=Show in Stipple" + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user chooses + -- the "Go to Published Collection" context-menu item. + --

If this function is not provided, Lightroom uses the URL recorded for the published collection via + -- exportSession:recordRemoteCollectionUrl.

+ --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.goToPublishedCollection + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + +--[[ Not used for Stipple plug-in. + +function publishServiceProvider.goToPublishedCollection( publishSettings, info ) +end + +--]] + +-------------------------------------------------------------------------------- +--- (optional, string) Plug-in defined value overrides the label for the + -- "Go to Published Photo" context-menu item, allowing you to use something more appropriate to + -- your service. Set to the special value "disable" to disable (dim) the menu item for this service. + --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.titleForGoToPublishedPhoto + -- @class property + +publishServiceProvider.titleForGoToPublishedPhoto = LOC "$$$/Stipple/TitleForGoToPublishedCollection=Show in Stipple" + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user chooses the + -- "Go to Published Photo" context-menu item. + --

If this function is not provided, Lightroom invokes the URL recorded for the published photo via + -- exportRendition:recordPublishedPhotoUrl.

+ --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.goToPublishedPhoto + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + +--[[ Not used for Stipple plug-in. + +function publishServiceProvider.goToPublishedPhoto( publishSettings, info ) +end + +]]-- + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user creates + -- a new publish service via the Publish Manager dialog. It allows your plug-in + -- to perform additional initialization. + --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.didCreateNewPublishService + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + +--[[ Not used for Stipple plug-in. + +function publishServiceProvider.didCreateNewPublishService( publishSettings, info ) +end + +--]] + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user creates + -- a new publish service via the Publish Manager dialog. It allows your plug-in + -- to perform additional initialization. + --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.didUpdatePublishService + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + +--[[ Not used for Stipple plug-in. + +function publishServiceProvider.didUpdatePublishService( publishSettings, info ) +end + +]]-- + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- has attempted to delete the publish service from Lightroom. + -- It provides an opportunity for you to customize the confirmation dialog. + --

Do not use this hook to actually tear down the service. Instead, use + -- willDeletePublishService + -- for that purpose. + --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.shouldDeletePublishService + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + -- @return (string) 'cancel', 'delete', or nil (to allow Lightroom's default + -- dialog to be shown instead) + +--[[ Not used for Stipple plug-in. + +function publishServiceProvider.shouldDeletePublishService( publishSettings, info ) +end + +]]-- + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- has confirmed the deletion of the publish service from Lightroom. + -- It provides a final opportunity for you to remove private data + -- immediately before the publish service is removed from the Lightroom catalog. + --

Do not use this hook to present user interface (aside from progress, + -- if the operation will take a long time). Instead, use + -- shouldDeletePublishService + -- for that purpose. + --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.willDeletePublishService + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + +--[[ Not used for Stipple plug-in. + +function publishServiceProvider.willDeletePublishService( publishSettings, info ) +end + +--]] + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- has attempted to delete one or more published collections defined by your + -- plug-in from Lightroom. It provides an opportunity for you to customize the + -- confirmation dialog. + --

Do not use this hook to actually tear down the collection(s). Instead, use + -- deletePublishedCollection + -- for that purpose. + --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.shouldDeletePublishedCollection + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + -- @return (string) "ignore", "cancel", "delete", or nil + -- (If you return nil, Lightroom's default dialog will be displayed.) + +--[[ Not used for Stipple plug-in. + +function publishServiceProvider.shouldDeletePublishedCollection( publishSettings, info ) +end + +]]-- + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- has attempted to delete one or more photos from the Lightroom catalog that are + -- published through your service. It provides an opportunity for you to customize + -- the confirmation dialog. + --

Do not use this hook to actually delete photo(s). Instead, if the user + -- confirms the deletion for all relevant services. Lightroom will call + -- deletePhotosFromPublishedCollection + -- for that purpose. + --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.shouldDeletePhotosFromServiceOnDeleteFromCatalog + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param nPhotos (number) The number of photos that are being deleted. At least + -- one of these photos is published through this service; some may only be published + -- on other services or not published at all. + -- @return (string) What action should Lightroom take? + -- + +--[[ Not used for Stipple plug-in. + +function publishServiceProvider.shouldDeletePhotosFromServiceOnDeleteFromCatalog( publishSettings, nPhotos ) +end + +]]-- + +-------------------------------------------------------------------------------- +--- This plug-in defined callback function is called when one or more photos + -- have been removed from a published collection and need to be removed from + -- the service. If the service you are supporting allows photos to be deleted + -- via its API, you should do that from this function. + --

As each photo is deleted, you should call the deletedCallback + -- function to inform Lightroom that the deletion was successful. This will cause + -- Lightroom to remove the photo from the "Delete Photos to Remove" group in the + -- Library grid.

+ --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.deletePhotosFromPublishedCollection + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param arrayOfPhotoIds (table) The remote photo IDs that were declared by this plug-in + -- when they were published. + -- @param deletedCallback (function) This function must be called for each photo ID + -- as soon as the deletion is confirmed by the remote service. It takes a single + -- argument: the photo ID from the arrayOfPhotoIds array. + -- @param localCollectionId (number) The local identifier for the collection for which + -- photos are being removed. + +function publishServiceProvider.deletePhotosFromPublishedCollection( publishSettings, arrayOfPhotoIds, deletedCallback ) + for i, photoId in ipairs( arrayOfPhotoIds ) do + StippleAPI.deletePhoto( publishSettings, { photoId = photoId, suppressErrorCodes = { [ 1 ] = true } } ) -- If Stipple says photo not found, ignore that. + deletedCallback( photoId ) + end +end + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called whenever a new + -- publish service is created and whenever the settings for a publish service + -- are changed. It allows the plug-in to specify which metadata should be + -- considered when Lightroom determines whether an existing photo should be + -- moved to the "Modified Photos to Re-Publish" status. + --

This is a blocking call.

+ -- @name publishServiceProvider.metadataThatTriggersRepublish + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @return (table) A table containing one or more of the following elements + -- as key, Boolean true or false as a value, where true means that a change + -- to the value does trigger republish status, and false means changes to the + -- value are ignored: + -- + +function publishServiceProvider.metadataThatTriggersRepublish( publishSettings ) + return { + default = false, + title = true, + caption = true, + keywords = true, + gps = true, + dateCreated = true, + } +end + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- creates a new published collection or edits an existing one. It can add + -- additional controls to the dialog box for editing this collection. These controls + -- can be used to configure behaviors specific to this collection (such as + -- privacy or appearance on a web service). + --

This is a blocking call. If you need to start a long-running task (such as + -- network access), create a task using the LrTasks + -- namespace.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.viewForCollectionSettings + -- @class function + -- @param f (LrView.osFactory object) + -- A view factory object. + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + -- @return (table) A single view description created from one of the methods in + -- the view factory. (We recommend that f:groupBox be the outermost view.) + +--[[ Not used for Stipple plug-in. This is an example of how this function might work. + +function publishServiceProvider.viewForCollectionSettings( f, publishSettings, info ) + + local collectionSettings = assert( info.collectionSettings ) + + -- Fill in default parameters. This code sample targets a hypothetical service + -- that allows users to enable or disable ratings and comments on a per-collection + -- basis. + + if collectionSettings.enableRating == nil then + collectionSettings.enableRating = false + end + + if collectionSettings.enableComments == nil then + collectionSettings.enableComments = false + end + + local bind = import 'LrView'.bind + + return f:group_box { + title = "Sample Plug-in Collection Settings", -- this should be localized via LOC + size = 'small', + fill_horizontal = 1, + bind_to_object = assert( collectionSettings ), + + f:column { + fill_horizontal = 1, + spacing = f:label_spacing(), + + f:checkbox { + title = "Enable Rating", -- this should be localized via LOC + value = bind 'enableRating', + }, + + f:checkbox { + title = "Enable Comments", -- this should be localized via LOC + value = bind 'enableComments', + }, + }, + + } + +end +--]] + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- creates a new published collection set or edits an existing one. It can add + -- additional controls to the dialog box for editing this collection set. These controls + -- can be used to configure behaviors specific to this collection set (such as + -- privacy or appearance on a web service). + --

This is a blocking call. If you need to start a long-running task (such as + -- network access), create a task using the LrTasks + -- namespace.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.viewForCollectionSetSettings + -- @class function + -- @param f (LrView.osFactory object) + -- A view factory object. + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + -- @return (table) A single view description created from one of the methods in + -- the view factory. (We recommend that f:groupBox be the outermost view.) + +--[[ Not used for Stipple plug-in. + +function publishServiceProvider.viewForCollectionSetSettings( f, publishSettings, info ) + -- See viewForCollectionSettings example above. +end + +--]] + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- closes the dialog for creating a new published collection or editing an existing + -- one. It is only called if you have also provided the viewForCollectionSettings + -- callback, and is your opportunity to clean up any tasks or processes you may + -- have started while the dialog was running. + --

This is a blocking call. If you need to start a long-running task (such as + -- network access), create a task using the LrTasks + -- namespace.

+ --

Your code should not update the server from here. That should be done + -- via the updateCollectionSettings callback. (If, for instance, the + -- settings changes are later undone; this callback is not called again, but + -- updateCollectionSettings is.)

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.endDialogForCollectionSettings + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + +--[[ Not used for Stipple plug-in. This is an example of how this function might work. + +function publishServiceProvider.endDialogForCollectionSettings( publishSettings, info ) + -- not used for Stipple plug-in +end + +--]] + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- closes the dialog for creating a new published collection set or editing an existing + -- one. It is only called if you have also provided the viewForCollectionSetSettings + -- callback, and is your opportunity to clean up any tasks or processes you may + -- have started while the dialog was running. + --

This is a blocking call. If you need to start a long-running task (such as + -- network access), create a task using the LrTasks + -- namespace.

+ --

Your code should not update the server from here. That should be done + -- via the updateCollectionSetSettings callback. (If, for instance, the + -- settings changes are later undone; this callback will not be called again; + -- updateCollectionSetSettings will be.)

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.endDialogForCollectionSetSettings + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + +--[[ Not used for Stipple plug-in. This is an example of how this function might work. + +function publishServiceProvider.endDialogForCollectionSetSettings( publishSettings, info ) + -- not used for Stipple plug-in +end + +--]] + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- has changed the per-collection settings defined via the viewForCollectionSettings + -- callback. It is your opportunity to update settings on your web service to + -- match the new settings. + --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ --

Your code should not use this callback function to clean up from the + -- dialog. This callback is not be called if the user cancels the dialog.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.updateCollectionSettings + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + +--[[ Not used for Stipple plug-in. + +function publishServiceProvider.updateCollectionSettings( publishSettings, info ) +end + +--]] + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when the user + -- has changed the per-collection set settings defined via the viewForCollectionSetSettings + -- callback. It is your opportunity to update settings on your web service to + -- match the new settings. + --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ --

Your code should not use this callback function to clean up from the + -- dialog. This callback will not be called if the user cancels the dialog.

+ --

First supported in version 3.0 of the Lightroom SDK.

+ -- @name publishServiceProvider.updateCollectionSetSettings + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + -- + +--[[ Not used for Stipple plug-in. + +function publishServiceProvider.updateCollectionSetSettings( publishSettings, info ) +end + +--]] + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called when new or updated + -- photos are about to be published to the service. It allows you to specify whether + -- the user-specified sort order should be followed as-is or reversed. The Stipple + -- sample plug-in uses this to reverse the order on the Photostream so that photos + -- appear in the Stipple web interface in the same sequence as they are shown in the + -- library grid. + --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ -- @param collectionInfo + -- @name publishServiceProvider.shouldReverseSequenceForPublishedCollection + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param publishedCollectionInfo (LrPublishedCollectionInfo) an object containing publication information for this published collection. + -- @return (boolean) true to reverse the sequence when publishing new photos + +function publishServiceProvider.shouldReverseSequenceForPublishedCollection( publishSettings, collectionInfo ) + return false +end + +-------------------------------------------------------------------------------- +--- (Boolean) If this plug-in defined property is set to true, Lightroom will + -- enable collections from this service to be sorted manually and will call + -- the imposeSortOrderOnPublishedCollection + -- callback to cause photos to be sorted on the service after each Publish + -- cycle. + -- @name publishServiceProvider.supportsCustomSortOrder + -- @class property + +publishServiceProvider.supportsCustomSortOrder = true + +-------------------------------------------------------------------------------- +--- (optional) This plug-in defined callback function is called after each time + -- that photos are published via this service assuming the published collection + -- is set to "User Order." Your plug-in should ensure that the photos are displayed + -- in the designated sequence on the service. + --

This is not a blocking call. It is called from within a task created + -- using the LrTasks namespace. In most + -- cases, you should not need to start your own task within this function.

+ -- @name publishServiceProvider.imposeSortOrderOnPublishedCollection + -- @class function + -- @param publishSettings (table) The settings for this publish service, as specified + -- by the user in the Publish Manager dialog. Any changes that you make in + -- this table do not persist beyond the scope of this function call. + -- @param info (table) A table with these fields: + --