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