1 /** 2 * Copyright (C) 2014 KO GmbH <copyright@kogmbh.com> 3 * 4 * @licstart 5 * This file is part of WebODF. 6 * 7 * WebODF is free software: you can redistribute it and/or modify it 8 * under the terms of the GNU Affero General Public License (GNU AGPL) 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * WebODF is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Affero General Public License for more details. 16 * 17 * You should have received a copy of the GNU Affero General Public License 18 * along with WebODF. If not, see <http://www.gnu.org/licenses/>. 19 * @licend 20 * 21 * @source: http://www.webodf.org/ 22 * @source: https://github.com/kogmbh/WebODF/ 23 */ 24 25 /*global gui, runtime, core, ops, odf*/ 26 27 /** 28 * @constructor 29 * @implements {core.Destroyable} 30 * @param {!ops.Session} session 31 * @param {!string} inputMemberId 32 */ 33 gui.MetadataController = function MetadataController(session, inputMemberId) { 34 "use strict"; 35 36 var odtDocument = session.getOdtDocument(), 37 eventNotifier = new core.EventNotifier([gui.MetadataController.signalMetadataChanged]), 38 /** @const @type {!Array.<!string>} */ 39 readonlyProperties = [ 40 "dc:creator", 41 "dc:date", 42 "meta:editing-cycles", 43 "meta:editing-duration", 44 "meta:document-statistic" 45 ]; 46 47 /** 48 * @param {!Object} changes 49 * @return {undefined} 50 */ 51 function onMetadataUpdated(changes) { 52 eventNotifier.emit(gui.MetadataController.signalMetadataChanged, changes); 53 } 54 55 /** 56 * @param {!string} property 57 * @return {!boolean} 58 */ 59 function isWriteableMetadata(property) { 60 var isWriteable = (readonlyProperties.indexOf(property) === -1); 61 if (! isWriteable) { 62 runtime.log("Setting " + property + " is restricted."); 63 } 64 return isWriteable; 65 } 66 67 /** 68 * Sets the metadata fields from the given properties map. 69 * Avoid setting certain fields since they are automatically set: 70 * dc:creator 71 * dc:date 72 * meta:editing-cycles 73 * If you do wish to externally set these fields, try getting 74 * the master session to inject operations into the timeline 75 * with the relevant properties. 76 * 77 * The following properties are never used and will be removed for semantic 78 * consistency from the document: 79 * meta:editing-duration 80 * meta:document-statistic 81 * 82 * Setting any of the above mentioned fields using this method will have no effect. 83 * 84 * @param {?Object.<!string, !string>} setProperties A flat object that is a string->string map of field name -> value. 85 * @param {?Array.<!string>|undefined=} removedProperties An array of metadata field names (prefixed). 86 * @return {undefined} 87 */ 88 this.setMetadata = function (setProperties, removedProperties) { 89 var /** @type {!Object.<!string,!string>} */ 90 filteredSetProperties = {}, 91 /** @type {!string} */ 92 filteredRemovedProperties = "", 93 op; 94 95 if (setProperties) { 96 Object.keys(setProperties).filter(isWriteableMetadata).forEach(function (property) { 97 filteredSetProperties[property] = setProperties[property]; 98 }); 99 } 100 if (removedProperties) { 101 filteredRemovedProperties = removedProperties.filter(isWriteableMetadata).join(","); 102 } 103 104 if (filteredRemovedProperties.length > 0 105 || Object.keys(filteredSetProperties).length > 0) { 106 op = new ops.OpUpdateMetadata(); 107 op.init({ 108 memberid: inputMemberId, 109 setProperties: filteredSetProperties, 110 removedProperties: filteredRemovedProperties.length > 0 ? { attributes: filteredRemovedProperties } : null 111 }); 112 session.enqueue([op]); 113 } 114 }; 115 116 /** 117 * Returns the value of the requested document metadata field 118 * @param {!string} property A namespace-prefixed field name, for example 119 * dc:creator 120 * @return {?string} 121 */ 122 this.getMetadata = function (property) { 123 var namespaceUri, parts; 124 125 runtime.assert(typeof property === "string", "Property must be a string"); 126 parts = property.split(':'); 127 runtime.assert(parts.length === 2, "Property must be a namespace-prefixed string"); 128 namespaceUri = odf.Namespaces.lookupNamespaceURI(parts[0]); 129 // TODO: support other namespaces 130 runtime.assert(Boolean(namespaceUri), "Prefix must be for an ODF namespace."); 131 return odtDocument.getOdfCanvas().odfContainer().getMetadata(/**@type{!string}*/(namespaceUri), parts[1]); 132 }; 133 134 /** 135 * @param {!string} eventid 136 * @param {!Function} cb 137 * @return {undefined} 138 */ 139 this.subscribe = function (eventid, cb) { 140 eventNotifier.subscribe(eventid, cb); 141 }; 142 143 /** 144 * @param {!string} eventid 145 * @param {!Function} cb 146 * @return {undefined} 147 */ 148 this.unsubscribe = function (eventid, cb) { 149 eventNotifier.unsubscribe(eventid, cb); 150 }; 151 152 /** 153 * @param {!function(!Error=):undefined} callback, passing an error object in case of error 154 * @return {undefined} 155 */ 156 this.destroy = function(callback) { 157 odtDocument.unsubscribe(ops.OdtDocument.signalMetadataUpdated, onMetadataUpdated); 158 callback(); 159 }; 160 161 function init() { 162 odtDocument.subscribe(ops.OdtDocument.signalMetadataUpdated, onMetadataUpdated); 163 } 164 165 init(); 166 }; 167 168 /**@const*/gui.MetadataController.signalMetadataChanged = "metadata/changed"; 169