/* Copyright (c) 2006-2007, ePages GmbH All Rights Reserved. epages.cartridges.de_epages.presentation.widget.Objectstore $Revision: 1.40 $ */ dojo.provide('epages.cartridges.de_epages.presentation.widget.Objectstore'); dojo.require('epages.lang.hash'); dojo.require('epages.io.json'); dojo.require('epages.widget'); dojo.require('dojo.data.api.Identity'); dojo.require('dojo.data.util.simpleFetch'); dojo.require('epages.cartridges.de_epages.presentation.icon'); dojo.require('epages.io.translation'); dojo.declare( 'epages.cartridges.de_epages.presentation.widget.Objectstore', [dojo.data.api.Identity], { /** * public properties */ actionChildren : "JSONChildren", // server ViewAction to get children information url : '?', // request url like '/epages/DemoShop.admin/?' queryClasses : undefined, // request class filter titleAttribute : 'NameOrAlias', // attribute used to show name at tree undoWidgetId : 'undoWidget', _htmlUndo : undefined, // html undo object to register changes undoWidget : undefined, id : 'objectStore', ignoreSubproducts : undefined, /** * private properties */ _info : undefined, // epages.lang.Hash values including information about objectid _JsonIo : new epages.io.Json(), // epages.io.Json() JSON communication channel to server _serverAttributes : '', // String, list of attributes for server _translation : new epages.io.Translation(dojo.moduleUrl('epages.cartridges.de_epages.presentation.widget', 'templates/translation'), 'auto'), // translation object registerUndo: function () { this.undoWidget = $$(this.undoWidgetId); if (this.undoWidget === undefined) { //console.warn(this.id+' : undo widget not found in '+this.declaredClass); } else { this._htmlUndo = this.undoWidget.getUndoObject(); } }, constructor: function(keywordParameters){ dojo.mixin(this,keywordParameters); this._info = $H(); //find undo-Widget if (this._htmlUndo === undefined) { this.undoWidget = $$(this.undoWidgetId); if (this.undoWidget === undefined) { var me = this; dojo.addOnLoad(function() { me.registerUndo(); }); } else this._htmlUndo = this.undoWidget.getUndoObject(); } //subscribe to undobuttons save dojo.subscribe('undobuttons/saveFinished',this, "_reloadStore"); }, _correctMissingPositions : function (children) { //find objects with position 0 and correct them var missingPositionIds = []; var firstPosition = undefined; dojo.forEach(children, function (el) { if(this._info.get(el).isProduct == false){ if(this._info.get(el).position == 0) missingPositionIds.push(el); else if(firstPosition == undefined || this._info.get(el).position < firstPosition) firstPosition = this._info.get(el).position; } }, this); if(missingPositionIds.length != 0){ //if undoWidget is not defined do correction at dojo.addOnLoad because then the undo Widget is defined if (this.undoWidget === undefined) { var me = this; dojo.addOnLoad(function() { me._setMissingPositions(missingPositionIds,firstPosition); }); } else{ this._setMissingPositions(missingPositionIds,firstPosition); } } }, _loadElement: function(el) { var info = { alias : el.Alias, nameOrAlias: el.NameOrAlias, title : el.title === undefined ? el[this.titleAttribute] : el.title, objectId : el.ObjectID, position : el.Position, parentId : el.ParentID, isProduct : el._isProduct ? true : false, isDeleted : false, children : el.hasChildren == 1 ? []: undefined, isCategory : el._isCategory ? true : false, IsVisibleContentView : el.IsVisibleContentView === '' ? 0 : el.IsVisibleContentView, ClassAlias : el.ClassAlias, webUrl : el.WebUrl, webUrlSSL : el.WebUrlSSL, path : el.Path, classId : el.ClassID, VisibleInNavigation : el.VisibleInNavigation, ReadPermissionFor : el.ReadPermissionFor, canonicalUrl : el.CanonicalURL ? el.CanonicalURL : '', seoQuality : el.SEOQuality, canDoSEOCheck : el.CanDoSEOCheck, isAppointment : el._isAppointment ? true : false }; if(el.IsVisible){ info.IsVisibleContentView = el.IsVisible; } //removeChildren for ImageGallery/GuestBook/Forum/Blog if(el.ClassAlias == 'ImageGallery' || el.ClassAlias == 'GuestBook' || el.ClassAlias == 'CustomForm' || el.ClassAlias == 'Forum'|| el.ClassAlias == 'BlogPost'){ el.children = undefined; info.children = undefined; } if(el.error){ info.error = el.error; } if(el.children !== undefined) { dojo.forEach(el.children, function (child) { this._loadElement(child); info.children.push(child.ObjectID); }, this); } this._info.set(el.ObjectID, info); }, // loads the child objects from the server _loadList: function(objectId, reload) { // exists object var info = this._info.get(objectId); if(info === undefined) { info = { objectId : objectId, children : [] }; this._info.set(objectId, info); reload = true; } var children = info.children; if(!reload && (children === undefined || children.length != 0)) { return objectId; } // not loaded or reload var attr = { 'ViewAction' : this.actionChildren, 'ObjectID' : objectId, 'Attributes' : this._serverAttributes, 'WithPath' : reload ? 1 : 0 }; if(this.queryClasses !== undefined){ attr["QueryClasses"] = this.queryClasses; } if(this.ignoreSubproducts !== undefined){ attr["IgnoreSubproducts"] = this.ignoreSubproducts; } var jsonData = this._JsonIo.loadSync(this.url, attr); if (jsonData != null && !jsonData.error) { // save data at internal structure if(jsonData.data.path !== undefined) { for(var i=0, iLength=jsonData.data.path.length; i<iLength; i++){ var elm = jsonData.data.path[i]; this._loadElement(elm); } } return objectId; } return undefined; }, _fetch: function (objectId) { var childrenIds = this._info.get(objectId).children; var items = []; var i; dojo.forEach(childrenIds, function(el) { i = this._info.get(el); if (! i.isDeleted) items.push(i); }, this); return items; }, _fetchItems: function(keywordArgs, findCallback, errorCallback){ if(this._loadList(keywordArgs.query.id) && typeof findCallback == 'function') { findCallback(this._fetch(keywordArgs.query.id), keywordArgs); } else { console.warn('error while _fetch %d', keywordArgs.query.id, 'in '+this.declaredClass); //errorCallback(keywordArgs); } }, fetchItemByIdentity: function(objectId, reload){ reload = (reload === undefined)? false : reload; if (this._loadList(objectId, reload)) return this._info.get(objectId); return undefined; }, hasAttribute: function(node, attributeName){ return node[attributeName] !== undefined; }, getLabel: function(item){ return item.title; }, getIdentity: function(item){ return item.objectId; }, getValue: function(item, attribute){ console.debug('not implemented - getValue %s', attribute); }, getValues: function(item, attribute){ // set in Objecttree.js indentCurrent or outdenCurrent, deleted in ToolMovePage _onOutdent or _onIndent var reload = (this.indentNode)? false : true; if (attribute == 'children' && this._loadList(item.objectId, reload)) { return this._fetch(item.objectId); } else{ console.debug('getValues() unknown attribute', attribute); } }, isItemLoaded: function(something){ //console.debug('isItemLoaded'); return true; }, deleteItem: function(item) { var objectId = item.objectId; this._info.get(objectId).isDeleted = true; if (this._htmlUndo !== undefined) { var inputs = this._createChangeNodes(objectId); var parentInput = inputs[1]; parentInput.value = 'Delete'; epages.event.fire(parentInput, 'change'); this._htmlUndo.addInput(parentInput); } return true; }, _createChangeNodes: function (objectId) { var positionInput = $('positionInput_' + objectId); if (positionInput == null) { positionInput = document.createElement("input"); positionInput.type = 'hidden'; positionInput.id = 'positionInput_' + objectId; positionInput.name = objectId + ':Position'; positionInput.value = this._info.get(objectId).position; this.undoWidget.domNode.appendChild(positionInput); this._htmlUndo.setDefaultValue(positionInput); dojo.connect(positionInput, "onchange", this, 'onChangePosition'); } var parentInput = $('parentInput_' + objectId); if (parentInput == null) { parentInput = document.createElement("input"); parentInput.type = 'hidden'; parentInput.id = 'parentInput_' + objectId; parentInput.name = objectId + ':Parent'; parentInput.value = this._info.get(objectId).parentId; this.undoWidget.domNode.appendChild(parentInput); this._htmlUndo.setDefaultValue(parentInput); dojo.connect(parentInput, "onchange", this, 'onChangeParent'); } return [positionInput, parentInput]; //Array }, canMoveItem: function(step, item) { if (item.parentId === undefined || item.isProduct || this._info.get(item.parentId) == undefined || (item.ClassAlias && item.ClassAlias == 'BlogPost')) { return false; } var childrenIds = this._info.get(item.parentId).children; var content = $A(childrenIds).grep(function (el) { return !this._info.get(el).isProduct}, this); var found = $A(content).find(item.objectId); // not found if (found == null) { return false; } var newPos = found + step; if (newPos < 0 || content.length <= newPos) { return false; } return true; }, moveItem: function (step,item) { // find old position var parentId = item.parentId; var childrenIds = this._info.get(parentId).children; var content = $A(childrenIds).grep(function (el) { return !this._info.get(el).isProduct}, this); var found = $A(content).find(item.objectId); // not found if (found == null) return; var newPos = found + step; // outside (can not move up first or down last) if (newPos < 0 || content.length <= newPos) { return; } // set for undo if (this._htmlUndo !== undefined) { var indexHigh = found < newPos ? newPos : found; var indexLow = found < newPos ? found : newPos; var lowObjectId = content[indexLow]; var highObjectId = content[indexHigh]; var lowInfo = this._info.get(lowObjectId); var highInfo = this._info.get(highObjectId); if (indexHigh == content.length - 1) { this.setNodePosition(lowInfo, parseInt(highInfo.position) + 10); } else { // get positions around, to find the larger gap var posSmall = indexLow == 0 ? 0 : parseInt(this._info.get(content[indexLow - 1]).position); var posLow = parseInt(lowInfo.position); var posHigh = parseInt(highInfo.position); var posLarge = parseInt(this._info.get(content[indexHigh + 1]).position); if (posLow - posSmall == 1 && posLarge - posHigh == 1) { this.setNodePosition(lowInfo, posHigh); this.setNodePosition(highInfo, posLow); } else if (posLow - posSmall <= posLarge - posHigh) { var d = parseInt((posLarge - posHigh) / 2); if (!d) d = 1; this.setNodePosition(lowInfo, posHigh + d); } else { d = parseInt((posLow - posSmall) / 2); if (!d) d = 1; this.setNodePosition(highInfo, posLow - d); } } } childrenIds[found] = childrenIds[newPos]; childrenIds[newPos] = item.objectId; return true; }, setNodePosition: function (info, position) { var inputs = this._createChangeNodes(info.objectId); info.position = position; inputs[0].value = info.position; this._htmlUndo.addInput(inputs[0]); }, _setMissingPositions:function(objectIds,firstPosition){ for(var i=0, iLength=objectIds.length; i<iLength; i++){ var info = this._info.get(objectIds[i]); var newPosition = firstPosition-1-i; this.setNodePosition(info,(newPosition)); } }, onChangePosition: function (evt) { dojo.stopEvent(evt); var positionNode = evt.currentTarget; // correct internal structure var position = parseInt(positionNode.value); var objectId = parseInt(positionNode.id.replace(/^positionInput_/,'')); var info = this._info.get(objectId); var old = info.position; info.position = position; var childrenIds = this._info.get(info.parentId).children; var waArray = $A(childrenIds); var oldPosition = waArray.find(objectId); waArray.remove(oldPosition); // find right place var found = 0; waArray.each(function (el) { var el_info = this._info.get(el); if (!el_info.isProduct && el_info.position <= position) found++; }, this); // insert on place waArray.insertAt(found, objectId); dojo.publish(this.id+'/changePosition',[{item:info, oldPosition:old}]); }, onChangeParent: function (evt) { dojo.stopEvent(evt); var parentNode = evt.currentTarget; // correct internal structure var parentObjectId = parentNode.value == 'Delete' ? 'Delete' : parseInt(parentNode.value); var objectId = parseInt(parentNode.id.replace(/^parentInput_/,'')); var info = this._info.get(objectId); if(parentObjectId == 'Delete'){ dojo.publish(this.id + '/changeParent', [{ item: info, 'delete': true }]); } else{ this._info.get(objectId).isDeleted = false; var oldParentObjectId = info.parentId; var newParentObjectId = parentObjectId; if($A(this._info.get(oldParentObjectId).children).exists(newParentObjectId.toString())){ this.indentItem(info); } if($A(this._info.get(newParentObjectId).children).exists(oldParentObjectId.toString())){ this.outdentItem(info); } dojo.publish(this.id+'/changeParent',[{item:info, oldParent:oldParentObjectId}]); } }, canIndentItem: function(item) { if (item.parentId === undefined || item.isProduct || this._info.get(item.parentId) == undefined) { return false; } var children = this._info.get(item.parentId).children; var found = $A(children).find(item.objectId); var newParent = this._info.get(children[found-1]); if (newParent && newParent.isCategory) { return true; } return false; }, indentItem: function (item) { var objectId = item.objectId; var info = this._info.get(item.objectId); var parent = this._info.get(item.parentId); var childrenIds = parent.children; var found = $A(childrenIds).find(item.objectId); // cant not indent first element if (found != null && 0 < found) { $A(childrenIds).remove(found); var newParentObjectId = childrenIds[found - 1]; if (this._loadList(newParentObjectId)) { if(this._info.get(newParentObjectId).isCategory == false) return; if(this._info.get(newParentObjectId).children === undefined) this._info.get(newParentObjectId).children = []; var children = this._info.get(newParentObjectId).children; var content = $A(children).grep(function (el) { return !this._info.get(el).isProduct}, this); var newIndex = content.length; // products are appended var lastPosition = content.length == 0 ? 0 : this._info.get(content[content.length - 1]).position; //change Alias if child with same alias already exists for(var i=0, iLength=children.length; i<iLength; i++){ var child = this._info.get(children[i]); if(child.alias == info.alias){ info.alias += new Date().getTime(); var aliasInput = document.createElement("input"); aliasInput.type = 'hidden'; //aliasInput.id = 'positionInput_' + objectId; aliasInput.name = objectId + ':Alias'; aliasInput.value = this._info.get(objectId).alias; if(this.undoWidget){ this.undoWidget.domNode.appendChild(aliasInput); this._htmlUndo.addInput(aliasInput); } break; } } $A(children).insertAt(newIndex,objectId); // set for undo if (this._htmlUndo !== undefined) { var inputs = this._createChangeNodes(item.objectId); if (info.position <= lastPosition) { info.position = parseInt(lastPosition) + 10; inputs[0].value = info.position; this._htmlUndo.addInput(inputs[0]); // positionInput } info.parentId = newParentObjectId; item.parentId = newParentObjectId; inputs[1].value = newParentObjectId; this._htmlUndo.addInput(inputs[1]); // parentInput } return newParentObjectId; } } }, canOutdentItem: function(item) { if (item.parentId === undefined || item.isProduct || this._info.get(item.parentId) == undefined) { return false; } var newParentObjectId = this._info.get(item.parentId).parentId; if (newParentObjectId === undefined || this._info.get(newParentObjectId) == undefined) { return false; } return true; }, outdentItem: function (item) { var objectId = item.objectId; var info = this._info.get(item.objectId); var newParentObjectId = this._info.get(item.parentId).parentId; //remove at old Parents childlist var oldParent = item.parentId; var oldChildPosition = $A(this._info.get(oldParent).children).find(objectId); $A(this._info.get(oldParent).children).remove(oldChildPosition); if(this._info.get(oldParent).children.length == 0){ this._info.get(oldParent).children = undefined; }; //insert as child of new Parent behind old Parent var content = $A(this._info.get(newParentObjectId).children).grep(function (el) { return !this._info.get(el).isProduct}, this); var found = $A(content).find(item.parentId); if (found != null) { //change Alias if child with same alias already exists for(var i=0, iLength=content.length; i<iLength; i++){ var child = this._info.get(content[i]); if(child.alias == info.alias){ info.alias += new Date().getTime(); var aliasInput = document.createElement("input"); aliasInput.type = 'hidden'; //aliasInput.id = 'positionInput_' + objectId; aliasInput.name = objectId + ':Alias'; aliasInput.value = this._info.get(objectId).alias; if(this.undoWidget){ this.undoWidget.domNode.appendChild(aliasInput); this._htmlUndo.addInput(aliasInput); } break; } } $A(this._info.get(newParentObjectId).children).insertAt(found+1,objectId); if (this._htmlUndo !== undefined) { var inputs = this._createChangeNodes(item.objectId); var before = this._info.get(content[found]); var info = this._info.get(item.objectId); var deltaPosition = 0; if (found == content.length - 1) { // last position if (info.position <= before.position) { deltaPosition = 10; } } else { // inner position var behind = this._info.get(content[found + 1]); if (before.position < info.position && info.position < behind.position) { // do nothing position is good } else if (before.position + 1 < behind.position) { // there is space deltaPosition = (parseInt(before.position) + parseInt( behind.position))/2 - parseInt(info.position); } else { // no space //console.debug('sorry no space'); deltaPosition = parseInt(behind.position) - parseInt(info.position); } } if (deltaPosition != 0) { info.position = parseInt(info.position) + parseInt(deltaPosition); inputs[0].value = info.position; this._htmlUndo.addInput(inputs[0]); // positionInput } info.parentId = newParentObjectId; item.parentId = newParentObjectId; inputs[1].value = newParentObjectId; this._htmlUndo.addInput(inputs[1]); // parentInput } return newParentObjectId; } }, createObject: function (objectId, classId, name, isVisible) { var children = this._info.get(objectId).children; var content = $A(children).grep(function (el) { return !this._info.get(el).isProduct}, this); var position = 10; if (content.length) { var posLastItem = parseInt(this._info.get(content[content.length-1]).position); position += (posLastItem !== isNaN) ? posLastItem : 0; } var data = { 'ViewAction': 'JSONGet', 'Attributes': 'ObjectID,ClassAlias=Class.Alias,IsVisibleContentView,Position,Name=NameOrAlias,Alias', 'ObjectID': objectId, 'ChangeAction': 'NewObject', 'ClassID': classId, 'Name': name, 'Position': position }; if(isVisible){ data['IsVisibleContentView'] = 1; } var result = this._JsonIo.loadSync(this.url,data); if (result.error != null) { var error = result.error.data.Errors[0]; var expectedErrors = { // try to translate a feature specific language tag, otherwise use default FeatureLimitExceeded 'FeatureLimitExceeded': function(error) { if (this._translation.exists('FeatureLimitExceeded' + error.Feature)) return 'FeatureLimitExceeded' + error.Feature; return 'FeatureLimitExceeded'; } }; var message = this._translation.get(expectedErrors[error.Reason] ? expectedErrors[error.Reason].call(this, error) : 'Error (Code: ' + error.Reason + ')', error.Vars); dojo.publish("uimessage/show", [ this._translation.get("CantCreatePage"), message, "Dialog", { titleBar: this._translation.get("Notification"), typeClass:"Warning", sizeClass:"Large" } ]); return; } else { var data = result.data; var newObjectId = result.data.ObjectID; if(this._loadList(objectId,true) != undefined){ if (this._htmlUndo !== undefined) { this._info.get(newObjectId).parentId = 'Delete'; var inputs = this._createChangeNodes(newObjectId); inputs[1].value = objectId; this._info.get(newObjectId).parentId = objectId; this._htmlUndo.addInput(inputs[1]); } return this._info.get(newObjectId); } else{ return; } } }, onChangeVisibility:function(evt){ dojo.stopEvent(evt); var node = evt.currentTarget; var objectId = parseInt(node.id.replace(/^IsVisibleContentViewInput_/,'')); var info = this._info.get(objectId); info.IsVisibleContentView = node.value; dojo.publish(this.id+'/changeVisibility',[{item:info}]); }, changeVisibility: function(item){ var objectId = item.objectId; //new Visibility is opposite of the old var newVisibility = item.IsVisibleContentView == 0 ? 1 : 0; var visibilityInput = $('IsVisibleContentViewInput_' + objectId); if (visibilityInput == null) { visibilityInput = document.createElement("input"); visibilityInput.type = 'hidden'; visibilityInput.id = 'IsVisibleContentViewInput_' + objectId; visibilityInput.name = objectId + ':IsVisibleContentView'; visibilityInput.value = this._info.get(objectId).IsVisibleContentView; this.undoWidget.domNode.appendChild(visibilityInput); this._htmlUndo.setDefaultValue(visibilityInput); dojo.connect(visibilityInput, "onchange", this, 'onChangeVisibility'); } this._info.get(objectId).IsVisibleContentView = newVisibility; visibilityInput.value = newVisibility; this._htmlUndo.addInput(visibilityInput); return newVisibility; }, changeAttribute: function(attribute){ if(attribute.languageId){ var attributeInputId = attribute.name +'Input_' + attribute.objectId+'_'+attribute.languageId; var attributeName = attribute.objectId + ':'+ attribute.name +':'+attribute.languageId; } else { var attributeInputId = attribute.name +'Input_' + attribute.objectId; var attributeName = attribute.objectId + ':'+ attribute.name; } if ($(attributeInputId) == null) { var attributeInput = document.createElement("input"); attributeInput.type = 'hidden'; attributeInput.id = attributeInputId; attributeInput.name = attributeName; if(attribute.oldValue){ var oldValue = attribute.oldValue; } else{ var oldValue = this._info.get(attribute.objectId); oldValue = oldValue[attribute.name]; } attributeInput.value = oldValue; this.undoWidget.domNode.appendChild(attributeInput); this._htmlUndo.setDefaultValue(attributeInput); } var attributeInput = $(attributeInputId); if(!attribute.oldValue){ this._info.get(attribute.objectId)[attribute.name] = attribute.value; } attributeInput.value = attribute.value; this._htmlUndo.addInput(attributeInput); }, _reloadStore:function(){ if(this.undoWidget && this.undoWidget.id == "undoWidget" && this.undoWidget.existsChanges() == false){ this._info = $H(); var childNodes = this.undoWidget.domNode.childNodes; for(var i=0, iLength=childNodes.length; i<iLength; i++){ // remove hidden inputs if(childNodes[i] && childNodes[i].value && childNodes[i].value == 'Delete'){ this.undoWidget.domNode.removeChild(childNodes[i]); } } dojo.publish(this.id+'/reloadStore'); } } }); dojo.extend(epages.cartridges.de_epages.presentation.widget.Objectstore,dojo.data.util.simpleFetch);