1 (function (GCN) { 2 3 'use strict'; 4 5 /** 6 * @private 7 * @const 8 * @type {number} 9 */ 10 //var TYPE_ID = 10002; 11 12 /** 13 * @const 14 * @private 15 * @type {object<string, *>} Default folder settings. 16 */ 17 var DEFAULT_SETTINGS = { 18 // Load folder privileges as well 19 privileges: true, 20 update: true 21 }; 22 23 /** 24 * @class 25 * @name FolderAPI 26 * @extends ContentObjectAPI 27 * @extends TagContainerAPI 28 */ 29 var FolderAPI = GCN.defineChainback({ 30 /** @lends FolderAPI */ 31 32 __chainbacktype__: 'FolderAPI', 33 _extends: [ GCN.TagContainerAPI, GCN.ContentObjectAPI ], 34 _type: 'folder', 35 36 /** 37 * @public 38 * @type {Array.<string>} Writable properties for the folder object. 39 */ 40 WRITEABLE_PROPS: [ 'description', 41 'motherId', 42 'name', 43 'publishDir' ], 44 45 /** 46 * Persist changes made to the page object in Gentics Content.Node . 47 * 48 * @public 49 * @param {function(FolderAPI)=} success Optional callback that will 50 * receive this object as its 51 * only argument. 52 * @param {function(GCNError):boolean} error Optional custom error 53 * handler. 54 */ 55 save: function (success, error) { 56 this._save(null, success, error); 57 }, 58 59 /** 60 * Removes the folder and all its parent objects 61 * 62 * @public 63 * @param {function(FolderAPI)=} success Optional callback that will 64 * receive this object as its 65 * only argument. 66 * @param {function(GCNError):boolean} error Optional custom error 67 * handler. 68 */ 69 remove: function (success, error) { 70 this._remove(success, error); 71 }, 72 73 /** 74 * Gets this folder's parent folder. If this folder does not have a 75 * parent, then the returned object will be an API to an object that 76 * does not exists. Only when attempting to perform read/write 77 * operations on this object on the server will a `NOTFOUND' error be 78 * encountered. We recognize that this is relatively late for the use 79 * to find out that this folder has no parent; if the use need to 80 * guarentee that a parent folder exists before further operations, 81 * they are simply to pass a callback into this function. 82 * 83 * @name parent 84 * @memberOf FolderAPI 85 * @public 86 * @param {function(FolderAPI)=} success Optional callback that will 87 * receive this object as its 88 * only argument. 89 * @param {function(GCNError):boolean} error Optional custom error 90 * handler. 91 * @return {FolderAPI} The parent folder 92 */ 93 '!parent': function (success, error) { 94 this._continue(GCN.FolderAPI, this.id(), success, error); 95 }, 96 97 /** 98 * Check if a given permission is available for a folder. If no 99 * name is provided an array of available permissions is returned. 100 * 101 * @name perm 102 * @memberOf FolderAPI 103 * @public 104 * @param {name} optional Privilege name to be checked. possible 105 * values are: 106 * "viewfolder" 107 * "createfolder" 108 * "updatefolder" 109 * "deletefolder" 110 * "viewpage" 111 * "createpage" 112 * "updatepage" 113 * "deletepage" 114 * "publishpage" 115 * "viewtemplate" 116 * "createtemplate" 117 * "linktemplate" 118 * "updatetemplate" 119 * "deletetemplate" 120 * @return {boolean|Array.<string>} Permission value for the given name 121 * or an array of permissions 122 */ 123 '!perm': function (name) { 124 var i; 125 126 if (!name) { 127 return this._data.privileges; 128 } 129 130 for (i in this._data.privileges) { 131 if (this._data.privileges.hasOwnProperty(i) && 132 this._data.privileges[i] === name) { 133 return true; 134 } 135 } 136 137 return false; 138 }, 139 140 /** 141 * Get this content object's node. 142 * 143 * @override 144 * @param {funtion(NodeAPI)=} success Optional callback to receive a 145 * {@link NodeAPI} object as the 146 * only argument. 147 * @param {function(GCNError):boolean=} error Optional custom error 148 * handler. 149 * @return {NodeAPI} This object's node. 150 */ 151 '!node': function (success, error) { 152 return this._continue(GCN.NodeAPI, null, success, error); 153 }, 154 155 // ==================================================================== 156 // Pages 157 // ==================================================================== 158 159 /** 160 * Returns page of the given id which resides in this folder. 161 * 162 * @public 163 * @name page 164 * @memberOf FolderAPI 165 * @param {number} id 166 * @param {function(PageAPI)=} success Optional callback that will 167 * receive a {@link PageAPI} object 168 * as its only argument. 169 * @param {function(GCNError):boolean=} error Optional custom error 170 * handler. 171 * @return {PageAPI} 172 */ 173 '!page': function (id, success, error) { 174 return this._continue(GCN.PageAPI, id, success, error); 175 }, 176 177 /** 178 * Retreive a list of all pages this folder. 179 * 180 * @param {function(Array.PageAPI)=} success Optional callback that 181 * will receive an array of 182 * {@link PageAPI} objects as 183 * its only argument. 184 * @param {function(GCNError):boolean=} error Optional custom error 185 * handler. 186 */ 187 pages: function (success, error) { 188 this._getItems('page', success, error); 189 }, 190 191 /** 192 * Creates a new page inside this folder. 193 * 194 * @param {number} templateId The id of the template to be used for 195 * the page. 196 * @param {object} options Set all the options to create a page the 197 * following options are allowed: 198 * <pre> 199 * GCN.folder(4711).createPage(13, { 200 * // set a language code for the new page like 'en', 'de', ... 201 * // if you don't supply a language code the page will have 202 * // no language assigned 203 * language: 'en', 204 * // id of the page this page should be a variant of 205 * variantId: 42 206 * }); 207 * </pre> 208 * @param {function(PageAPI)=} success Optional callback that will 209 * receive a {@link PageAPI} object 210 * as its only argument. 211 * @param {function(GCNError):boolean=} error Optional custom error 212 * handler. 213 * @return {PageAPI} The newly created page. 214 */ 215 createPage: function () { 216 var args = Array.prototype.slice.call(arguments); 217 var templateId = args[0]; 218 var options; 219 var success; 220 var error; 221 var j = args.length; 222 var i; 223 224 // Determine `options', `success', `error' 225 for (i = 1; i < j; ++i) { 226 switch (jQuery.type(args[i])) { 227 case 'function': 228 if (success) { 229 error = args[i]; 230 } else { 231 success = args[i]; 232 } 233 break; 234 case 'object': 235 options = args[i]; 236 break; 237 } 238 } 239 240 var that = this; 241 var page = that._continue(GCN.PageAPI)._procure(); 242 243 this._read(function () { 244 if (!options) { 245 options = {}; 246 } 247 248 // default settings 249 if (that.nodeId()) { 250 options.nodeId = that.nodeId(); 251 } 252 options.folderId = that.id(); 253 options.templateId = templateId; 254 255 that._authAjax({ 256 url : GCN.settings.BACKEND_PATH + '/rest/page/create/', 257 type : 'POST', 258 json : options, 259 error : function (xhr, status, msg) { 260 GCN.handleHttpError(xhr, msg, error); 261 }, 262 success : function (response) { 263 if (GCN.getResponseCode(response) === 'OK') { 264 var data = response.page; 265 page._data = data; 266 page._fetched = true; 267 if (success) { 268 that._invoke(success, [page]); 269 } 270 } else { 271 page._die(GCN.getResponseCode(response)); 272 GCN.handleResponseError(response, error); 273 } 274 275 // Halt the call chain until this object has been fully 276 // realized. 277 page._vacate(); 278 } 279 }, error); 280 }, error); 281 }, 282 283 // ==================================================================== 284 // Templates 285 // ==================================================================== 286 287 // '!template': function (id, success, error) { 288 // return this._continue(GCN.TemplateAPI, id, success, error); 289 // }, 290 // 291 // '!templates': function (ids, success, error) { 292 // //FIXME: Not implemented 293 // }, 294 // 295 // createTemplate: function (settings, success, error) { 296 // //FIXME: Not implemented 297 // }, 298 299 /** 300 * Retreive a list of all files in this folder. 301 * 302 * @param {function(Array.FileAPI)=} success Optional callback that 303 * will receive an array of 304 * {@link FileAPI} objects as 305 * its only argument. 306 * @param {function(GCNError):boolean=} error Optional custom error 307 * handler. 308 */ 309 files: function (success, error) { 310 this._getItems('file', success, error); 311 }, 312 313 /** 314 * Retreive a list of all images in this folder. 315 * 316 * @param {function(Array.ImageAPI)=} success Optional callback that 317 * will receive an array of 318 * {@link ImageAPI} objects 319 * as its only argument. 320 * @param {function(GCNError):boolean=} error Optional custom error 321 * handler. 322 */ 323 images: function (success, error) { 324 this._getItems('image', success, error); 325 }, 326 327 // ==================================================================== 328 // Folders 329 // ==================================================================== 330 331 /** 332 * @override 333 * @see ContentObjectAPI._loadParams 334 */ 335 '!_loadParams': function () { 336 return jQuery.extend(DEFAULT_SETTINGS, this._settings); 337 }, 338 339 /** 340 * @FIXME(petro) Why on do we need this method inside FolderAPI? 341 */ 342 '!folder': function (id, success, error) { 343 return this._continue(GCN.FolderAPI, id, success, error); 344 }, 345 346 /** 347 * Retreive a list of all sub folders of this folder. 348 * 349 * @param {function(Array.FolderAPI)=} success Optional callback that 350 * will receive an array of 351 * {@link FolderAPI} 352 * objects as its only 353 * argument. 354 * @param {function(GCNError):boolean=} error Optional custom error 355 * handler. 356 */ 357 folders: function (success, error) { 358 this._getItems('folder', success, error); 359 }, 360 361 /** 362 * Create a sub folder within this folder, with the option of also 363 * automatically creating a startpage for this folder. 364 * 365 * @param {string} name the folder name 366 * @param {object} settings pass in an optional settings object 367 * possible options are: 368 * <pre> 369 * { 370 * // optional description for the folder 371 * description: 'this is my folder', 372 * // set a publish directory for the folder 373 * publishDir: '/this/is/my/folder/', 374 * // adding a template id will automatically create a new 375 * // startpage for the folder 376 * templateId: 5, 377 * // provide a language code for the start page. optional. 378 * language: 'en', 379 * // when true creating the folder will fail if a folder with 380 * // that name exists. otherwise conflicting names will be 381 * // postfixed with an increasing number. defaults to false. 382 * failOnDuplicate: false 383 * } 384 * </pre> 385 * @param {function(FolderAPI)=} success Optional callback that 386 * will receive a 387 * {@link FolderAPI} object as 388 * its only argument. 389 * @param {function(GCNError):boolean=} error Optional custom error 390 * handler. 391 * @throws UNKNOWN_ARGUMENT Thrown when unexpected arguments are 392 * provided. 393 */ 394 createFolder: function () { 395 var that = this; 396 var success; 397 var error; 398 var settings; 399 var name; 400 var i; 401 var j = arguments.length; 402 403 // parse arguments 404 for (i = 0; i < j; ++i) { 405 switch (jQuery.type(arguments[i])) { 406 case 'function': 407 if (!success) { 408 success = arguments[i]; 409 } else if (success && !error) { 410 error = arguments[i]; 411 } else { 412 GCN.error('UNKNOWN_ARGUMENT', 413 'success and error handler already set. Don\'t ' + 414 'know what to do with arguments[' + i + ']'); 415 } 416 break; 417 case 'object': 418 if (!settings) { 419 settings = arguments[i]; 420 } else { 421 GCN.error('UNKNOWN_ARGUMENT', 422 'settings already set. Don\'t know what to do ' + 423 'with arguments[' + i + '] value ' + arguments[i]); 424 } 425 break; 426 case 'string': 427 if (!name) { 428 name = arguments[i]; 429 } else { 430 GCN.error('UNKNOWN_ARGUMENT', 431 'name already set. Don\'t know what to do with ' + 432 'arguments[' + i + '] value ' + arguments[i]); 433 } 434 break; 435 default: 436 GCN.error('UNKNOWN_ARGUMENT', 437 'Don\'t know what to do with arguments[' + i + '] ' + 438 'value ' + arguments[i]); 439 } 440 } 441 442 // initialize basic settings object 443 if (!settings) { 444 settings = {}; 445 } 446 447 // set default parameters 448 settings.name = name; 449 settings.motherId = this.id(); 450 if (this.nodeId()) { 451 settings.nodeId = this.nodeId(); 452 } 453 454 // automatically enable startpage generation if a template is set 455 if (settings.templateId) { 456 settings.startpage = true; 457 } 458 459 this._authAjax({ 460 url : GCN.settings.BACKEND_PATH + '/rest/folder/create/', 461 type : 'POST', 462 error : error, 463 json : settings, 464 success : function (response) { 465 that._continue(GCN.FolderAPI, response.folder, success, 466 error); 467 } 468 }); 469 }, 470 471 /** 472 * Get a URL for uploading files into this folder. 473 * 474 * @public 475 * @name uploadURL 476 * @memberOf FolderAPI 477 * @return {string} Rest API url for file uploading. 478 */ 479 '!uploadURL': function () { 480 return ( 481 GCN.settings.BACKEND_PATH 482 + '/rest/file/createSimple.json?sid=' + GCN.sid 483 + '&folderId=' + this.id() 484 + GCN._getChannelParameter(this, '&') 485 ); 486 }, 487 488 /** 489 * Get the upload URL that is capable of dealing with multipart form 490 * data. 491 * 492 * @public 493 * @name uploadURL 494 * @memberOf FolderAPI 495 * @param {boolean} applyContentWrapperFilter When true the reponse will 496 * be wrapped so that it can be interpreted by various 497 * upload implementations. 498 * @param {string} filterContentType Define a custom content type that 499 * should be used for the server side implementation. 500 * The defined content type will modify the response 501 * 'Content-Type' header value. 502 * @return {string} Rest API url for multipart file upload. 503 */ 504 '!multipartUploadURL' : function (applyContentWrapperFilter, filterContentType) { 505 var restURL = GCN.settings.BACKEND_PATH 506 + '/rest/file/create.json?sid=' 507 + GCN.sid; 508 509 if (typeof applyContentWrapperFilter !== 'undefined') { 510 if (jQuery.type(applyContentWrapperFilter) === 'boolean') { 511 restURL += '&content-wrapper-filter=' 512 + applyContentWrapperFilter; 513 } else { 514 GCN.error('INVALID_ARGUMENTS', 'the `multipartUploadURL()\' method ' + 515 'only accepts boolean values for the `applyContentWrapperFilter\' parameter'); 516 } 517 } 518 519 if (filterContentType) { 520 restURL += '&filter-contenttype=' + filterContentType; 521 } 522 523 return restURL; 524 }, 525 526 /** 527 * This method will inspect the json and decide whether the onSuccess 528 * or onError should be called. A file or image api object will be 529 * passed to the success handler. 530 * @TODO(petro): The success callback should not receive a second 531 * argument containing messages. It is not consitanct 532 * with out API. 533 * 534 * @public 535 * @name handleUploadResponse 536 * @memberOf FolderAPI 537 * @param {object} response The REST-API reponse object that was given 538 * in response to the upload request. 539 * @param {function(FileAPI, Array.string)=} success Optional callback 540 * that will receive 541 * as its first 542 * argument, a 543 * {@link FileAPI} 544 * object of the 545 * uploaded file. The 546 * second argument is 547 * an array of 548 * message strings 549 * returned in 550 * response to the 551 * upload request. 552 * @param {function(GCNError):boolean=} error Optional custom error 553 * handler. 554 */ 555 '!handleUploadResponse': function (response, success, error) { 556 if (GCN.getResponseCode(response) === 'OK') { 557 if (success) { 558 var that = this; 559 GCN.file(response.file, function (file) { 560 that._invoke(success, [file, response.messages]); 561 }, error); 562 } 563 } else { 564 GCN.handleResponseError(response, error); 565 } 566 }, 567 568 /** 569 * Fetch items inside this folder. 570 * 571 * @param {string} type One of: "file" 572 * "folder" 573 * "page" 574 * "image" 575 * "template" 576 * @param {function(Array.<ContentObjectAPI>)} success Callback that 577 * will receive an array 578 * of the requested items. 579 * @param {function(GCNError):boolean=} success Custom error handler. 580 */ 581 '!_getItems': function (type, success, error) { 582 var that = this; 583 584 if (!this._fetched) { 585 this._read(function () { 586 that._getItems(type, success, error); 587 }, error); 588 589 return; 590 } 591 592 var api; 593 var url = GCN.settings.BACKEND_PATH + '/rest/' + this._type 594 + '/getItems/' + this.id() + '?type=' + type 595 + GCN._getChannelParameter(that, '&'); 596 597 switch (type) { 598 case 'page': 599 api = GCN.PageAPI; 600 break; 601 case 'file': 602 api = GCN.FileAPI; 603 break; 604 case 'image': 605 api = GCN.ImageAPI; 606 break; 607 case 'folder': 608 api = GCN.FolderAPI; 609 url = GCN.settings.BACKEND_PATH + '/rest/' + this._type 610 + '/getFolders/' + this.id() 611 + GCN._getChannelParameter(that); 612 break; 613 default: 614 var err = GCN.createError('UNEXPECTED_TYPE', 615 'Unknown object type ' + type, this); 616 617 GCN.handleError(err, error); 618 return; 619 } 620 621 this._authAjax({ 622 url : url, 623 type : 'GET', 624 error : error, 625 success : function (response) { 626 var items = []; 627 var i; 628 var j = response.numItems; 629 630 for (i = 0; i < j; i++) { 631 items.push(that._continue(api, 632 (type === 'folder') ? response.folders[i] : 633 response.items[i], 634 null, error)); 635 } 636 637 that._invoke(success, [items]); 638 } 639 }); 640 } 641 642 }); 643 644 GCN.folder = GCN.exposeAPI(FolderAPI); 645 GCN.FolderAPI = FolderAPI; 646 647 }(GCN)); 648