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