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