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.ContentObjectAPI, GCN.TagContainerAPI ],
 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 IS_NOT_FIRST = false;
506 
507 			var restURL = GCN.settings.BACKEND_PATH + '/rest/file/create.json?sid=' + 
508 							GCN.sid;
509 
510 			if (typeof applyContentWrapperFilter !== 'undefined') {
511 				if (jQuery.type(applyContentWrapperFilter) === "boolean") {
512 					restURL += '&content-wrapper-filter=' + applyContentWrapperFilter;
513 				} else {
514 					GCN.error('INVALID_ARGUMENTS', 'the `multipartUploadURL()\' method ' +
515 					'only accepts boolean values for the `applyContentWrapperFilter\' parameter');
516 				}
517 				
518 			}
519 			
520 			if (filterContentType) {
521 				restURL += '&filter-contenttype=' + filterContentType;
522 			}
523 			
524 			return restURL;
525 		},
526 
527 		/**
528 		 * This method will inspect the json and decide whether the onSuccess
529 		 * or onError should be called. A file or image api object will be
530 		 * passed to the success handler.
531 		 * @TODO(petro): The success callback should not receive a second
532 		 *               argument containing messages.  It is not consitanct
533 		 *               with out API.
534 		 *
535 		 * @public
536 		 * @name handleUploadResponse
537 		 * @memberOf FolderAPI
538 		 * @param {object} response The REST-API reponse object that was given
539 		 *                          in response to the upload request.
540 		 * @param {function(FileAPI, Array.string)=} success Optional callback
541 		 *                                                   that will receive
542 		 *                                                   as its first
543 		 *                                                   argument, a
544 		 *                                                   {@link FileAPI}
545 		 *                                                   object of the
546 		 *                                                   uploaded file. The
547 		 *                                                   second argument is
548 		 *                                                   an array of
549 		 *                                                   message strings
550 		 *                                                   returned in
551 		 *                                                   response to the
552 		 *                                                   upload request. 
553 		 * @param {function(GCNError):boolean=} error Optional custom error
554 		 *                                            handler.
555 		 */
556 		'!handleUploadResponse': function (response, success, error) {
557 			if (GCN.getResponseCode(response) === 'OK') {
558 				if (success) {
559 					var that = this;
560 					GCN.file(response.file, function (file) {
561 						that._invoke(success, [file, response.messages]);
562 					}, error);
563 				}
564 			} else {
565 				GCN.handleResponseError(response, error);
566 			}
567 		},
568 
569 		/**
570 		 * Fetch items inside this folder.
571 		 *
572 		 * @param {string} type One of: "file"
573 		 *                              "folder"
574 		 *                              "page"
575 		 *                              "image"
576 		 *                              "template"
577 		 * @param {function(Array.<ContentObjectAPI>)} success Callback that
578 		 *                                              will receive an array
579 		 *                                              of the requested items.
580 		 * @param {function(GCNError):boolean=} success Custom error handler.
581 		 */
582 		'!_getItems': function (type, success, error) {
583 			var that = this;
584 
585 			if (!this._fetched) {
586 				this._read(function () {
587 					that._getItems(type, success, error);
588 				}, error);
589 
590 				return;
591 			}
592 
593 			var api;
594 			var url = GCN.settings.BACKEND_PATH + '/rest/' + this._type
595 				    + '/getItems/' + this.id() + '?type=' + type
596 					+ GCN._getChannelParameter(that, '&');
597 
598 			switch (type) {
599 			case 'page':
600 				api = GCN.PageAPI;
601 				break;
602 			case 'file':
603 				api = GCN.FileAPI;
604 				break;
605 			case 'image':
606 				api = GCN.ImageAPI;
607 				break;
608 			case 'folder':
609 				api = GCN.FolderAPI;
610 				url = GCN.settings.BACKEND_PATH + '/rest/' + this._type
611 					+ '/getFolders/' + this.id()
612 					+ GCN._getChannelParameter(that);
613 				break;
614 			default:
615 				var err = GCN.createError('UNEXPECTED_TYPE',
616 					'Unknown object type ' + type, this);
617 
618 				GCN.handleError(err, error);
619 				return;
620 			}
621 
622 			this._authAjax({
623 				url     : url,
624 				type    : 'GET',
625 				error   : error,
626 				success : function (response) {
627 					var items = [];
628 					var i;
629 					var j = response.numItems;
630 
631 					for (i = 0; i < j; i++) {
632 						items.push(that._continue(api,
633 							(type === 'folder') ? response.folders[i] :
634 							                      response.items[i],
635 							null, error));
636 					}
637 
638 					that._invoke(success, [items]);
639 				}
640 			});
641 		}
642 
643 	});
644 
645 	GCN.folder = GCN.exposeAPI(FolderAPI);
646 	GCN.FolderAPI = FolderAPI;
647 
648 }(GCN));
649