1 var REST = library.REST.REST();
  2 
  3 /**
  4  * Gets a the container for the field. Either an Empty container or the container already saved to the Resource
  5  * @param theFile the file we are dialing with
  6  * @param theResourceWrapper where we are pulling the current data from
  7  * @param theProperty the field we are working with
  8  * @param theForcedType the type to force upon the field if the field is empty
  9  * @returns SeqWrapper | BagWrapper | AltWrapper | String
 10  */
 11 function getFieldWrapper(
 12   theFile,
 13   theResourceWrapper,
 14   theProperty,
 15   theForcedType
 16 ) {
 17   var aFieldValue = theResourceWrapper.getField(theProperty);
 18 
 19   //Create a new Resource wrapper
 20   var aFieldWrapper;
 21   if (!!aFieldValue) {
 22     //If the field already has a type, maintain it
 23     aFieldWrapper = aFieldValue;
 24   } else if (!!theForcedType) {
 25     switch (theForcedType) {
 26       case "Bag":
 27         aFieldWrapper = theFile.xmp.newBag();
 28         break;
 29       case "Alt":
 30         aFieldWrapper = theFile.xmp.newAlt();
 31         break;
 32       case "Seq":
 33       case "RDB":
 34         aFieldWrapper = theFile.xmp.newSeq();
 35         break;
 36       default:
 37         aFieldWrapper = "";
 38     }
 39   } else if (
 40     theProperty.getFieldType() !== null &&
 41     theProperty.getFieldType().indexOf("list") >= 0
 42   ) {
 43     aFieldWrapper = theFile.xmp.newSeq();
 44   } else {
 45     aFieldWrapper = "";
 46   }
 47 
 48   return aFieldWrapper;
 49 }
 50 
 51 /**
 52  * Determines if the JSON field value has the required structure for an RDB field
 53  * @param theJSONValue
 54  * @returns {boolean}
 55  */
 56 function isRDB(theJSONValue, theProperty) {
 57   var aFieldType = theProperty.getFieldType();
 58   if (!!aFieldType) {
 59     return (
 60       aFieldType.toLowerCase() === "rdb" &&
 61       theJSONValue instanceof Array &&
 62       theJSONValue.length > 0 &&
 63       theJSONValue[0] instanceof Array &&
 64       theJSONValue[0].length > 0 &&
 65       !!theJSONValue[0][0].fieldId
 66     );
 67   } else {
 68     return false;
 69   }
 70 }
 71 
 72 /**
 73  * Gets a value to write to the file for the field defined in theFieldJSON
 74  * @param theFile the asset we are currently writing to
 75  * @param theResourceWrapper the resouce wrapper we should be pulling the current data from
 76  * @param theFieldJSON defines the field and what we are writing
 77  {
 78 	   "fieldId": "http://purl.org/dc/elements/1.1/ title",
 79 	   "value": "A new value for Dublin Core title",
 80 	   "append": true
 81 	}
 82 
 83  Normal container field
 84  {
 85 	   "fieldId": "http://purl.org/dc/elements/1.1/ title",
 86 	   "value": ["A new value for Dublin Core title","value2"],
 87 	   "append": true
 88 	}
 89 
 90  Repeating data block container field
 91  {
 92 		"fieldId": "http://purl.org/dc/elements/1.1/ repeating",
 93 		"append": true,
 94 		"value":
 95 		[
 96 			[
 97 				{
 98 				   "fieldId": "http://purl.org/dc/elements/1.1/ blockField1",
 99 				   "value": "Field1 in block"
100 				},
101 				{
102 				   "fieldId": "http://purl.org/dc/elements/1.1/ blockField2",
103 				   "value": "Field2 in block"
104 				}
105 			]
106 		]
107 	 }
108 
109  * @returns {*}
110  */
111 function getNewFieldValue(theFile, theResourceWrapper, theFieldJSON) {
112   var aValue = theFieldJSON.value;
113   var anAppend = theFieldJSON.append;
114   var anAllowDuplicates = !!theFieldJSON.allowDuplicates;
115   var aProperty = REST.getProperty(theFieldJSON.fieldId);
116 
117   if (isRDB(aValue, aProperty)) {
118     //Repeating Data Block!!!
119     var aWrapper = getFieldWrapper(theFile, theFile.xmp.meta, aProperty, "RDB");
120     if (!theFieldJSON.append) {
121       aWrapper.clear();
122     }
123 
124     for (var i in aValue) {
125       var aBlockFields = aValue[i];
126       var aBlock = theFile.xmp.newStructure();
127       for (var j in aBlockFields) {
128         var aRDBField = aBlockFields[j];
129         var aBlockField = REST.getProperty(aRDBField.fieldId);
130         var aFieldValue = getNewFieldValue(theFile, aBlock, aRDBField);
131         aBlock.addField(aBlockField, aFieldValue);
132       }
133       aWrapper = aWrapper.addItem(aBlock);
134     }
135     return aWrapper;
136   } else {
137     var aFieldWrapper = getFieldWrapper(theFile, theResourceWrapper, aProperty);
138 
139     if (typeof aFieldWrapper == "string") {
140       if (aValue instanceof Array) {
141         var aStringValue = "";
142         for (var i in aValue) {
143           var anObject = aValue[i];
144           if (typeof anObject == "object") {
145             aStringValue += JSON.stringify(anObject);
146           } else {
147             aStringValue += anObject;
148           }
149           if (i != aValue.length - 1) {
150             aStringValue += ",";
151           }
152         }
153         aValue = aStringValue;
154       }
155       return anAppend ? aFieldWrapper + aValue : aValue;
156     }
157 
158     //Deal with container types
159     aValue = aValue instanceof Array ? aValue : [aValue];
160     if (!anAppend) {
161       aFieldWrapper.clear();
162     }
163     if (!anAllowDuplicates) {
164       var aValues = {};
165       var aFiltered = aFieldWrapper.filter(function (theValue) {
166         if (!aValues[theValue]) {
167           aValues[theValue] = true;
168           return true;
169         }
170         return false;
171       });
172       aFieldWrapper.clear();
173       for (var i = 0; i < aFiltered.length; i++) {
174         aFieldWrapper.addItem(aFiltered[i]);
175       }
176     }
177     for (var j = 0; j < aValue.length; j++) {
178       var aSubValue = aValue[j];
179       if (typeof aSubValue == "object") {
180         //The request is malformed or the user is trying to write a data block to a non data block field
181         aSubValue = JSON.stringify(aSubValue);
182       }
183       if (!anAllowDuplicates && aFieldWrapper.contains(aSubValue)) {
184         continue;
185       }
186       aFieldWrapper.addItem(aSubValue);
187     }
188     return aFieldWrapper;
189   }
190 }
191 
192 /**
193  *  writes field values for the asset
194  *
195  * @param theFieldData
196  *          {
197  *              "id":123456,
198  *              "fields:
199  *              [
200  *                  {
201  *                      "fieldId": "http://purl.org/dc/elements/1.1/ title",
202  *                      "value": "A new value for Dublin Core title",
203  *                      "append": true
204  *                  },...
205  *              ]
206  *          }
207  */
208 function setFields(theFieldData) {
209   var aChangeList = {};
210 
211   var aFile = fileManager.getFileObjectById(theFieldData.id);
212   if (
213     aFile == null ||
214     searchManager.filterByACL([theFieldData.id]).length == 0
215   ) {
216     REST.pushError(
217       REST.errors.e404,
218       "The file for this id is not found: " + theFieldData.id
219     );
220     return;
221   }
222   var aFields = theFieldData.fields;
223   for (var i in aFields) {
224     var aField = aFields[i];
225     if (
226       !fieldManager.canEdit(aField.fieldId, context.getUser().getEditLevel())
227     ) {
228       logger.warning("Cannot edit field due to field level: " + aField.fieldId);
229       REST.pushError(
230         REST.errors.e403,
231         "Cannot edit field due to field level: " + aField.fieldId
232       );
233       continue;
234     }
235     var aProperty = REST.getProperty(aField.fieldId);
236     var aFieldValue = getNewFieldValue(aFile, aFile.xmp.meta, aField);
237     var aOldFieldValue = getFieldWrapper(aFile, aFile.xmp.meta, aProperty);
238     aFile.xmp.meta.addField(aProperty, aFieldValue);
239 
240     // If we have a workflow to run after we should add the metadata change to the trigger event.
241     aChangeList = REST.addToChangeList(
242       aChangeList,
243       aProperty,
244       aOldFieldValue,
245       aFieldValue
246     );
247   }
248   var anIsModified = aFile.isXmpModified();
249   if (!aFile.writeXmp()) {
250     REST.pushError(
251       REST.errors.e500,
252       "Failed to write to the asset: " +
253         theFieldData.id +
254         ". Ensure that the field values are allowed by the field structure"
255     );
256     return;
257   }
258   if (!anIsModified) {
259     return;
260   }
261   var aJsonStub = {};
262   var aTriggerResponse = REST.triggerAssetBasedWorkflows(
263     REST.AssetTriggers.Modified,
264     aFile,
265     aChangeList
266   );
267   if (aTriggerResponse) {
268     // If we have a workflow that we want to trigger post-file writing do it here.
269     aJsonStub.triggerAssetModified = aTriggerResponse.ok;
270   }
271 
272   REST.push(aFile, aJsonStub);
273 }
274 
275 /**
276  * @name SetFields
277  * @class Writes a values to assets
278  * @description For each asset in  "data", this endpoint writes the defined field values to the asset.
279  * @param data an array of objects with an asset id and the fields to set.
280  * <pre><code>
281  *     [
282  *          {
283  *              "id":123456,
284  *              "fields":
285  *              [
286  *                  {
287  *                      "fieldId": "http://purl.org/dc/elements/1.1/ title",
288  *                      "value": "A new value for Dublin Core title",
289  *                      "append": true
290  *                  },...
291  *              ]
292  *          },...
293  *      ]
294  * </code></pre>
295  * @param [verbose=false] Setting this to true will collect a variety of default values for each asset.
296  * @param [fields] An array of field id's to collect the values for each asset
297  * @param [triggerAssetBasedWorkflow=false] Set to false to avoid chaining other metadata edited workflows
298  * @returns [{assetInfo}, ... ]
299  *
300  * @example /wf/restapi/v2/setFields
301  *
302  * Parameters:
303  * data=
304  [
305  {
306 	   "id": 201629375,
307 	   "fields": [
308 		 {
309 		   "fieldId": "http://purl.org/dc/elements/1.1/ title",
310 		   "value": "A new value for Dublin Core title",
311 		   "append": false
312 		 }
313 	   ]
314 	 },
315  {
316 	   "id": 201628784,
317 	   "fields": [
318 		 {
319 		   "fieldId": "http://purl.org/dc/elements/1.1/ title",
320 		   "value": "A new value for Dublin Core title",
321 		   "append": false
322 		 }
323 	   ]
324 	 }
325  ]
326  * fields=["http://purl.org/dc/elements/1.1/ title"]
327  * triggerAssetModified=true
328  *
329  * Response:
330  [
331  {
332 	   "id": 201629375,
333 	   "fields": {
334 		 "http://purl.org/dc/elements/1.1/ title": "A new value for Dublin Core title"
335 	   },
336 	   "triggerAssetModified": {
337 	     "ok": true,
338 	     "workflowTriggered": "Renditions/Workflows/moveAsset.xmpwf"
339 	   }
340 	 },
341  {
342 	   "id": 201628784,
343 	   "fields": {
344 		 "http://purl.org/dc/elements/1.1/ title": "A new value for Dublin Core title"
345 	   },
346 	   "triggerAssetModified": {
347 	     "ok": true,
348 	     "workflowTriggered": "Renditions/Workflows/moveAsset.xmpwf"
349 	   }
350 	 }
351  ]
352 
353  * @example /wf/restapi/v2/setFields
354  *
355  * Parameters:
356  * data=
357  [
358  {
359 	   "id": 201629375,
360 	   "fields": [
361 		 {
362 		   "fieldId": "http://purl.org/dc/elements/1.1/ title",
363 		   "value": " append this ",
364 		   "append": true
365 		 }
366 	   ]
367 	 },
368  {
369 	   "id": 201628784,
370 	   "fields": [
371 		 {
372 		   "fieldId": "http://purl.org/dc/elements/1.1/ title",
373 		   "value": " append that",
374 		   "append": true
375 		 }
376 	   ]
377 	 }
378  ]
379  * fields=["http://purl.org/dc/elements/1.1/ title"]
380  *
381  * Response:
382  [
383  {
384 	   "id": 201629375,
385 	   "fields": {
386 		 "http://purl.org/dc/elements/1.1/ title": "A new value for Dublin Core title append this "
387 	   }
388 	 },
389  {
390 	   "id": 201628784,
391 	   "fields": {
392 		 "http://purl.org/dc/elements/1.1/ title": "A new value for Dublin Core title append that"
393 	   }
394 	 }
395  ]
396 
397  * @example /wf/restapi/v2/setFields
398  *
399  * Parameters:
400  * data=
401  [
402  {
403 	   "id": 201629375,
404 	   "fields": [
405 		 {
406 		   "fieldId": "http://purl.org/dc/elements/1.1/ title",
407 		   "value": "DC Title",
408 		   "append": false
409 		 },
410 		 {
411 		   "fieldId": "http://purl.org/dc/elements/1.1/ subject",
412 		   "value": ["DC Keywords"],
413 		   "append": false
414 		 }
415 	   ]
416 	 }
417  ]
418  * fields=["http://purl.org/dc/elements/1.1/ title", "http://purl.org/dc/elements/1.1/ subject"]
419  *
420  * Response:
421  [
422  {
423 	   "id": 201629375,
424 	   "fields": {
425 		 "http://purl.org/dc/elements/1.1/ title": "DC Title",
426 		 "http://purl.org/dc/elements/1.1/ subject": [
427 		   "DC Keywords"
428 		 ]
429 	   }
430 	 }
431  ]
432 
433  * @example Setting multiple values in a container(CSV) field
434  * /wf/restapi/v2/setFields
435  *
436  * Parameters:
437  * data=
438  [
439  {
440 	   "id": 201629375,
441 	   "fields": [
442 		 {
443 		   "fieldId": "http://purl.org/dc/elements/1.1/ subject",
444 		   "value": ["value1","value2"],
445 		   "append": false
446 		 }
447 	   ]
448 	 }
449  ]
450  * fields=["http://purl.org/dc/elements/1.1/ subject"]
451  *
452  * Response:
453  [
454  {
455 	   "id": 201629375,
456 	   "fields": {
457 		 "http://purl.org/dc/elements/1.1/ subject": [
458 		   "value1",
459 		   "value2"
460 		 ]
461 	   }
462 	 }
463  ]
464 
465 
466  * @example Writing to data block fields
467  * /wf/restapi/v2/setFields
468  *
469  * Parameters:
470  * data=
471  [
472  {
473    "id": 201629375,
474    "fields": [
475 	 {
476 	   "fieldId": "http://ns.mediabeacon.com/REST_v2_testing/container/en/1.0/ datablock",
477 	   "value": [
478 		 [
479 		   {
480 			 "fieldId": "http://purl.org/dc/elements/1.1/ title",
481 			 "value": "textField"
482 		   },
483 		   {
484 			 "fieldId": "http://purl.org/dc/elements/1.1/ subject",
485 			 "value": [
486 			   "value1",
487 			   "value2"
488 			 ]
489 		   }
490 		 ]
491 	   ],
492 	   "append": false
493 	 }
494    ]
495  }
496  ]
497  * fields=["http://ns.mediabeacon.com/REST_v2_testing/container/en/1.0/ datablock"]
498  *
499  * Response:
500  [
501  {
502    "id": 201631907,
503    "fields": {
504 	 "http://ns.mediabeacon.com/REST_v2_testing/container/en/1.0/ datablock": [
505 	   {
506 		 "http://purl.org/dc/elements/1.1/ title": "textField",
507 		 "http://purl.org/dc/elements/1.1/ subject": [
508 		   "value1",
509 		   "value2"
510 		 ]
511 	   }
512 	 ]
513    }
514  }
515  ]
516 
517  * @example Writing multiple data blocks
518  * /wf/restapi/v2/setFields
519  *
520  * Parameters:
521  * data=
522  [
523  {
524    "id": 201629375,
525    "fields": [
526 	 {
527 	   "fieldId": "http://ns.mediabeacon.com/REST_v2_testing/container/en/1.0/ datablock",
528 	   "value": [
529 		 [
530 		   {
531 			 "fieldId": "http://purl.org/dc/elements/1.1/ title",
532 			 "value": "textField"
533 		   },
534 		   {
535 			 "fieldId": "http://purl.org/dc/elements/1.1/ subject",
536 			 "value": [
537 			   "value1",
538 			   "value2"
539 			 ]
540 		   }
541 		 ],
542 		 [
543 		   {
544 			 "fieldId": "http://purl.org/dc/elements/1.1/ title",
545 			 "value": "block 2"
546 		   },
547 		   {
548 			 "fieldId": "http://purl.org/dc/elements/1.1/ subject",
549 			 "value": [
550 			   "block 2"
551 			 ]
552 		   }
553 		 ]
554 	   ],
555 	   "append": false
556 	 }
557    ]
558  }
559  ]
560  * fields=["http://ns.mediabeacon.com/REST_v2_testing/container/en/1.0/ datablock"]
561  *
562  * Response:
563  [
564  {
565    "id": 201631907,
566    "fields": {
567 	 "http://ns.mediabeacon.com/REST_v2_testing/container/en/1.0/ datablock": [
568 	   {
569 		 "http://purl.org/dc/elements/1.1/ title": "textField",
570 		 "http://purl.org/dc/elements/1.1/ subject": [
571 		   "value1",
572 		   "value2"
573 		 ]
574 	   },
575 	   {
576 		 "http://purl.org/dc/elements/1.1/ title": "block 2",
577 		 "http://purl.org/dc/elements/1.1/ subject": [
578 		   "block 2"
579 		 ]
580 	   }
581 	 ]
582    }
583  }
584  ]
585 
586  */
587 function main() {
588   if (!context.getUser().isPermissionEnabled("editMetaData")) {
589     REST.pushError(REST.errors.e403, "editMetadata permission is not enabled");
590     return REST.execute();
591   }
592   REST.setCallback(setFields);
593   return REST.execute("data");
594 }
595 main();
596