getting XMP File does not have a constructor error through ExtendScript - debugging

I am using In Design CC 2019, on my Mac OS. When I am trying to get XMP data for my .indd (InDesign document) using ExtendScript.
I am currently getting the error like this:
XMPFile Does not have a constructor.
Below is my script.
// load XMP Library
function loadXMPLibrary(){
if ( ExternalObject.AdobeXMPScript){
try{ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');}
catch (e){alert('Unable to load the AdobeXMPScript library!'); return false;}
}
return true;
}
var myFile= app.activeDocument.fullName;
// check library and file
if(loadXMPLibrary() && myFile != null){
xmpFile = new XMPFile(myFile.fsName, XMPConst.FILE_INDESIGN, XMPConst.OPEN_FOR_UPDATE);
var myXmp = xmpFile.getXMP();
}
if(myXmp){
$.writeln ('sucess')
}

There's an issue with your codes logic, you need to make the following change:
Add the Logical NOT operator (i.e. !) to the condition specified for your if statement in the body of your loadXMPLibrary function.
function loadXMPLibrary(){
if (!ExternalObject.AdobeXMPScript) { // <--- Change to this
// ^
try {ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');}
catch (e){alert('Unable to load the AdobeXMPScript library!'); return false;}
}
return true;
}
You need to add this because currently your if statement checks whether the condition is truthy, i.e. it checks whether ExternalObject.AdobeXMPScript is true. This will always remain false, until the AdobeXMPScript library has been loaded, therefore you're code that actually loads the library never gets executed.
Revised script:
For clarity here is the complete revised script:
// load XMP Library
function loadXMPLibrary() {
if (!ExternalObject.AdobeXMPScript) {
try{ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');}
catch (e){alert('Unable to load the AdobeXMPScript library!'); return false;}
}
return true;
}
var myFile= app.activeDocument.fullName;
// check library and file
if (loadXMPLibrary() && myFile !== null) {
xmpFile = new XMPFile(myFile.fsName, XMPConst.FILE_INDESIGN, XMPConst.OPEN_FOR_UPDATE);
var myXmp = xmpFile.getXMP();
}
if (myXmp){
$.writeln ('success')
}

Related

Is there any way to get passed empty folders when drag dropping folder structure using dropzone.js?

I'm using dropzone.js to be able to easily upload files and folders. I do not get an notification for empty folders, is there any way to have an event fired for empty folders?
Thank you.
So this is what I came up with, not the most elegant solution, but does what I need to be able to handle emptyfolders. Would be great if dropzone.js had an option for enabling emptyfolders to be returned.
I made the following changes to the dropzone.js
At the very top I added
var passedFolders = [];
// used to store the names of fodlers that were passed, with either files or without
// the key is the path name, the value is true if empty, false if it had contents
Then under the section
var readEntries = function readEntries() {
return dirReader.readEntries(function (entries) {
change it to
var readEntries = function readEntries() {
return dirReader.readEntries(function (entries) {
if (entries.length > 0) {
var _iterator7 = dropzone_createForOfIteratorHelper(entries, true),
_step7;
try {
for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
var entry = _step7.value;
if (entry.isFile) {
//There is a a file in the folder, so we can set it's value to false
passedFolders[entry.fullPath.substring(0, entry.fullPath.lastIndexOf(entry.name)-1)] = false //-1 to remove the last slash
entry.file(function (file) {
if (_this5.options.ignoreHiddenFiles && file.name.substring(0, 1) === ".") {
return;
}
file.fullPath = "".concat(path, "/").concat(file.name);
return _this5.addFile(file);
});
} else if (entry.isDirectory) {
//This is just a folder, not a file. Store the folder path information set to true, it will remain true if the folder remains empty
passedFolders[entry.fullPath] = true;
_this5._addFilesFromDirectory(entry, "".concat(path, "/").concat(entry.name));
}
} // Recursively call readEntries() again, since browser only handle
// the first 100 entries.
// See: https://developer.mozilla.org/en-US/docs/Web/API/DirectoryReader#readEntries
} catch (err) {
_iterator7.e(err);
} finally {
_iterator7.f();
}
readEntries();
}
return null;
}, errorHandler);
};
Now passedFolders will contain an associate array, where the key is the pathname and the value is true if it is empty, false if it contained files.
Then under the dropzone init, when the file uploads are complete you can choose what to do with empty folders
this.on('queuecomplete', function (file, json) {
alert("Uploads complete, now we can deal with the empty folders");
for (var key in passedFolders) {
if (passedFolders[key]) {
alert(key);
}
}
});

Photoshop CC2019 auto-update all linked smart objects including nested ones

I have Photoshop CC2019 PSD document containing several smart objects that contains other smart objects that contains other smart objects. Some of these have linked layers. Normally, such images are not updated automatically (which is extremely annoying, Adobe!) but you have to manually update each of them once the linked image content has changed.
There is a .jsx script file named "Update All Modified Content.jsx" which auto-updates linked layers (PNG image in my case) but only if the smart object is in the top most document - that is no nested smart objects with linked layers are updated automatically.
My question is: does anyone know how to update the content of the above mentioned .jsx file so that it would auto-update all linked images across all the smart objects in PSD document including nested ones?
For those who care or would be willing to help updating the code here it is:
// Update all modified content
var idplacedLayerUpdateAllModified = stringIDToTypeID( "placedLayerUpdateAllModified" );
executeAction( idplacedLayerUpdateAllModified, undefined, DialogModes.NO );
So, after spending half a day with it I finally solved it myself. Here is the code:
#target photoshop
// SET INITIAL ACTIVE DOCUMENT
var mainDocument = app.activeDocument;
// SAVE THE DOCUMENT NAME FOR FUTURE USE
var mainDocName = mainDocument.name;
// RUN THE MAIN UPDATE FUNCTION
mainDocument.suspendHistory("processAllSmartObjects", "autoupdateAllSmartObjects(mainDocument, 0)");
// FINALLY SAVE THE MAIN DOCUMENT
mainDocument.save();
function autoupdateAllSmartObjects(theParent, prevVal) {
// FUNCTION TO TEST IF SMARTOBJECT IS LINKED
function isLinkedSO(obj) {
var localFilePath = "";
var ref = new ActionReference();
ref.putIdentifier(charIDToTypeID('Lyr '), obj.id);
var desc = executeActionGet(ref);
var smObj = desc.getObjectValue(stringIDToTypeID('smartObject'));
var isLinked = false;
// TEST IF IT HAS LINKED FILE
try {
var localFilePath = smObj.getPath(stringIDToTypeID('link'));
isLinked = true;
} catch(e) {
//
}
return isLinked;
}
// FUNCTION TO UPDATE LINKED SMART OBJECT
function doTheUpdate(LYR, stackNr) {
// SET ACTIVE LAYER TO ACTUALY ITERATED ONE
app.activeDocument.activeLayer = LYR;
// RUN IN "SILENT" MODE
app.displayDialogs = DialogModes.NO;
var layer = app.activeDocument.activeLayer;
// IF ACTIVE LAYER IS SMARTOBJECT
if (layer.kind == "LayerKind.SMARTOBJECT") {
//alert(layer);
// OPEN THE SMARTOBJECT
app.runMenuItem(stringIDToTypeID('placedLayerEditContents'));
// DO THE ACTUAL FILE UPDATE
var idplacedLayerUpdateAllModified = stringIDToTypeID( "placedLayerUpdateAllModified" );
executeAction( idplacedLayerUpdateAllModified, undefined, DialogModes.NO);
// IF IT IS NOT THE "CORE/MAIN" DOCUMENT
if(stackNr > 0) {
// SAVE CHANGES (UPDATE) AND CLOSE IT
app.activeDocument.close(SaveOptions.SAVECHANGES);
}
// CONTINUE INSIDE THIS ACTIVE SMARTOBJECT
autoupdateAllSmartObjects(app.activeDocument, stackNr);
}
return;
}
// FUNCTION TO PARSE GROUPS
function parseGroup(LYR) {
var groupLayers = LYR.layers;
// IF GROUP IS NOT EMPTY
if(groupLayers.length > 0) {
// PARSE ALL LAYERS IN THE GROUP
for (var i = groupLayers.length - 1; i >= 0; i--) {
var lyr = groupLayers[i];
// IF NOT LOCKED = NOT EDITABL:E
if(!lyr.allLocked) {
// YET ANOTHER GROUP?
if (lyr.typename == "LayerSet") {
// IF IT IS NOT EMPTY
if (lyr.layers.length > 0) {
// RE-RUN THE SCRIPT ANEW WITH THE SELECTED GROUP AS LAYERS SOURCE
autoupdateAllSmartObjects(lyr, 0);
}
// LAYERS
} else if (lyr.typename == "ArtLayer") {
// IF THE LAYER IS SMARTOBJECT
if (lyr.kind == LayerKind.SMARTOBJECT) {
// IF THE LAYER IS SET TO "visible" (THAT IS: NOT DISABLED)
if(lyr.visible){
// TEST IF THE SMARTOBJECT IS ACTUALLY LINKED
if(!isLinkedSO(lyr)) {
// RUN THE UPDATE SUB-FUNCTION
doTheUpdate(lyr, i);
}
}
}
}
}
}
}
}
// PARSE ALL THE LAYERS
for (var i = theParent.layers.length - 1 - prevVal; i >= 0; i--) {
var theLayer = theParent.layers[i];
// ONLY ArtLayers
if (theLayer.typename == "ArtLayer") {
// IF THE LAYER IS SMARTOBJECT
if (theLayer.kind == LayerKind.SMARTOBJECT) {
// IF THE LAYER IS SET TO "visible" (THAT IS: NOT DISABLED)
if(theLayer.visible){
// TEST IF THE SMARTOBJECT IS ACTUALLY LINKED
if(!isLinkedSO(theLayer)){
// RUN THE UPDATE SUB-FUNCTION
doTheUpdate(theLayer, i);
// IF WE ARE AT THE LAST LAYER IN THE STACK AND IT IS NOT OUR MAIN DOCUMENT
if(i == 0 && app.activeDocument.name !== mainDocName) {
// SAVE CHANGES (UPDATE) AND CLOSE IT
app.activeDocument.close(SaveOptions.SAVECHANGES);
}
}
}
}
// ONLY Groups
} else if (theLayer.typename == "LayerSet") {
// RUN SUB-FUNCTION FOR GROUP PARSING
parseGroup(theLayer);
// ANYTHING ELSE
} else {
autoupdateAllSmartObjects(theLayer, m);
}
}
return;
};
OP's script works!! It was going in loops for me too but after some trial and error, I realised that my smart objects (SO) that are linked across artboards ( - e.g. if you change one SO, it changes on various artboards) - were the issue. I hid all such SO and it works.
so basically, it only works for Smart Objects + copies made via 'New smart object via copy' NOT 'duplicate layer' / copypaste smart objects. If your work contains a 'duplicate layer' SO - it will break the script. You need to hide these objects ( or not work like that all together) before running the script

How to tell if a cell value has passed validation

I am familiar with the Google Apps script DataValidation object. To get and set validation criteria. But how to tell programatically if a cell value is actually valid. So I can see the little red validation fail message in the spreadsheet but can the fact the cell is currently failing validation be picked up thru code?
I have tried to see if there is a cell property that tells you this but there is not. Also I looked for some sort of DataValidation "validate" method - i.e. test a value against validation rules, but nothing there either
Any ideas? Is this possible??
Specific answer to your question, there is no method within Google Apps Script that will return the validity of a Range such as .isValid(). As you state, you could reverse engineer a programatic one using Range.getDataValidations() and then parsing the results of that in order to validate again the values of a Range.getValues() call.
It's a good suggestion. I've added a feature request to the issue tracker -> Add a Star to vote it up.
I've created a workaround for this issue that works in a very ugly -technically said- and slightly undetermined way.
About the workaround:
It works based on the experience that the web browser implementation of catch() function allows to access thrown errors from the Google's JS code parts.
In case an invalid input into a cell is rejected by a validation rule then the system will display an error message that is catchable by the user written GAS. In order to make it work first the reject value has to be set on the specified cell then its vale has to be re-entered (modified) then -right after this- calling the getDataValidation() built in function allows the user to catch the necessary error.
Only single cells can be tested with this method as setCellValues() ignores any data validation restriction (as of today).
Disadvantages:
The validity won't be necessarily re-checked for this function:
it calls a cell validation function right after the value is inserted into the cell.
Therefore the result of this function might be faulty.
The code messes up the history as cells will be changed - in case they are
valid.
I've tested it successfully on both Firefox and Chromium.
function getCellValidity(cell) {
var origValidRule = cell.getDataValidation();
if (origValidRule == null || ! (cell.getNumRows() == cell.getNumColumns() == 1)) {
return null;
}
var cell_value = cell.getValue();
if (cell_value === '') return true; // empty cell is always valid
var is_valid = true;
var cell_formula = cell.getFormula();
// Storing and checking if cell validation is set to allow invalid input with a warning or reject it
var reject_invalid = ! origValidRule.getAllowInvalid();
// If invalid value is allowed (just warning), then changing validation to reject it
// IMPORTANT: this will not throw an error!
if (! reject_invalid) {
var rejectValidRule = origValidRule.copy().setAllowInvalid(false).build();
cell.setDataValidation(rejectValidRule);
}
// Re-entering value or formula into the cell itself
var cell_formula = cell.getFormula();
if (cell_formula !== '') {
cell.setFormula(cell_formula);
} else {
cell.setValue(cell_value);
}
try {
var tempValidRule = cell.getDataValidation();
} catch(e) {
// Exception: The data that you entered in cell XY violates the data validation rules set on this cell.
// where XY is the A1 style address of the cell
is_valid = false;
}
// Restoring original rule
if (rejectValidRule != null) {
cell.setDataValidation(origValidRule.copy().setAllowInvalid(true).build());
}
return is_valid;
}
I still recommend starring the above Google bug report opened by Jonathon.
I'm using this solution. Simple to learn and fast to use! You may need to adapt this code for your needs. Hope you enjoy
function test_corr(link,name) {
var ss = SpreadsheetApp.openByUrl(link).getSheetByName(name);
var values = ss.getRange(2,3,200,1).getValues();
var types = ss.getRange(2,3,200,1).getDataValidations()
var ans
for (var i = 0; i < types.length; i++) {
if (types[i][0] != null){
var type = types[i][0].getCriteriaType()
var dval_values = types[i][0].getCriteriaValues()
ans = false
if (type == "VALUE_IN_LIST") {
for (var j = 0; j < dval_values[0].length; j++) {
if (dval_values[0][j] == values[i][0]) { ans = true }
}
} else if (type == "NUMBER_BETWEEN") {
if (values[i][0] >= dval_values[0] && values[i][0] <= dval_values[1]) { ans = true }
} else if (type == "CHECKBOX") {
if (values[i][0] == "Да" || values[i][0] == "Нет") { ans = true }
}
if (!ans) { return false }
}
}
return true;
}

d3.js return value of function always undefined

I am calling a function from my index.html file. The function is defined in a javascript file which i have referred to in the html. However the return value is always undefined. When i debug i could see the value in the return string.
Follwing is the code in index.html
<script type="text/javascript">
function readQueryStringparam(name)
{
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
var regexS = "[\\?&]" + name + "=([^&#]*)";
var regex = new RegExp(regexS);
var results = regex.exec(window.location.href);
if (results == null)
return "";
else
return results[1];
}
function getDiDataUrlPrefix()
{
diDataUrlPrefix = diGlobal.instanceInfo.getDiDataUrlPrefix();
//alert(diDataUrlPrefix);
sbu = readQueryStringparam('sbu');
appid = readQueryStringparam('appid');
if (sbu.length > 0)
{
sbu = sbu.trim();
CreateChart(diDataUrlPrefix,sbu,0,appid);
}
else if (appid.length > 0)
{
sbu = GetSBUForApplication(appid);
CreateChart(diDataUrlPrefix,0,0,appid);
}
}
</script>
I get the value for the parameters supplied in the url as well as diDataUrlPrefix.
Following is the code in the javascript file:
function GetSBUForApplication(appid)
{
setTimeout(function() { }, 10000);
var string;
var file = diDataUrlPrefix + "/oss/csvs/Consolidated_RAG.csv";
d3.text(file, function(datasetText)
{
parsedCSVapp = d3.csv.parseRows(datasetText);
if (appid >0)
{
parsedCSVapp = parsedCSVapp.filter(function(row)
{
//alert(parsedCSVapp);
return row[0] == appid
})//parsed fileter ends here
returnstring = parsedCSVapp[0][4];
}
})
return returnstring;
}
However the value of sbu is always undefined.However i can see the values in parsedCSVapp. The csv file looks like this:
Application_Id,Application Name,Status,Name,Business Unit
200039,DEALING,RED,Marc Begun,Financial&Risk
200070,NGTX,RED,Marc Begun,Financial&Risk
200097,WORLD-CHECK,RED,Graham Fisher,Financial&Risk
200009,BOARDLINK,RED,Jennifer Simon,Financial&Risk
200088,THOMSON ONE,RED,Jonathan Weinberg,Financial&Risk
200037,DATASTREAM,RED,Ian Brocklehurst,Financial&Risk
200044,EIKON,RED,Olivier Martin,Financial&Risk
200011,COLLABORATION,RED,Frank Tarsillo,Financial&Risk
d3.text (and d3.csv, d3.json and similar) make asynchronous calls. That is, when you run the code, the call is made and execution resumes without waiting for the call to return.
The second argument to those functions is a function that gets executed when the call returns -- the callback.
This function will not be executed at the same time as you run d3.text, but later. You cannot determine at what time exactly it will be run. Any code that you want to call as a result of one of those calls needs to be run as part of the callback function, or called from there.

Restrictions in file types in FineUploader 3.7.0

I am using the option "allowedExtensions" without any problem but there is a situation where I have to permit any type of extension but two.
Is there a simple way to do that? I didn't find an option like 'restrictedExtensions' to do that in the code.
Thanks
From the docs:
The validate and validateBatch events are thrown/called before the default Fine Uploader validators (defined in the options) execute.
Also, if your validation event handler returns false, then Fine Uploader will register that file as invalid and not submit it.
Here's some code you could try in your validate event handler. It has not been tested yet so YMMV.
var notAllowedExts = ['pptx', 'xlsx', 'docx'];
/* ... */
onValidate: function (fileOrBlobData) {
var valid = true;
var fileName = fileOrBlobData.name || '';
qq.each(notAllowedExts, function(idx, notAllowedExt) {
var extRegex = new RegExp('\\.' + notAllowedExt + "$", 'i');
if (fileName.match(extRegex) != null) {
valid = false;
return false;
}
});
return valid;
}
/* ... */

Resources