/* * Copyright 2007-2017 Charles du Jeu - Abstrium SAS * This file is part of Pydio. * * Pydio is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Pydio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * * The latest code can be found at . */ import Observable from '../lang/Observable' import Logger from '../lang/Logger' import AjxpNode from './AjxpNode' import LangUtils from '../util/LangUtils' import PathUtils from '../util/PathUtils' import PydioApi from '../http/PydioApi' /** * Full container of the data tree. Contains the SelectionModel as well. */ export default class PydioDataModel extends Observable{ /** * Constructor * > Warning, events are now LOCAL by default */ constructor(localEvents=true){ super(); this._currentRep = '/'; this._selectedNodes = []; this._bEmpty = true; this._globalEvents = !localEvents; this._bFile= false; this._bDir= false; this._isRecycle= false; this._pendingSelection=null; this._selectionSource = {}; this._rootNode = null; } static RemoteDataModelFactory(providerProperties, rootLabel=''){ let dataModel = new PydioDataModel(true); let rNodeProvider = new RemoteNodeProvider(providerProperties); dataModel.setAjxpNodeProvider(rNodeProvider); const rootNode = new AjxpNode("/", false, rootLabel, '', rNodeProvider); dataModel.setRootNode(rootNode); return dataModel; } /** * Sets the data source that will feed the nodes with children. * @param iAjxpNodeProvider IAjxpNodeProvider */ setAjxpNodeProvider (iAjxpNodeProvider){ this._iAjxpNodeProvider = iAjxpNodeProvider; } /** * Return the current data source provider * @return IAjxpNodeProvider */ getAjxpNodeProvider (){ return this._iAjxpNodeProvider; } /** * Changes the current context node. * @param ajxpNode AjxpNode Target node, either an existing one or a fake one containing the target part. * @param forceReload Boolean If set to true, the node will be reloaded even if already loaded. */ requireContextChange (ajxpNode, forceReload=false){ if(ajxpNode == null) return; this.setSelectedNodes([]); const path = ajxpNode.getPath(); if((path == "" || path == "/") && ajxpNode != this._rootNode){ ajxpNode = this._rootNode; } let paginationPage = null; if(ajxpNode.getMetadata().has('paginationData') && ajxpNode.getMetadata().get('paginationData').has('new_page') && ajxpNode.getMetadata().get('paginationData').get('new_page') != ajxpNode.getMetadata().get('paginationData').get('current')){ paginationPage = ajxpNode.getMetadata().get('paginationData').get('new_page'); forceReload = true; } if(ajxpNode != this._rootNode && (!ajxpNode.getParent() || ajxpNode.fake)){ // Find in arbo or build fake arbo let fakeNodes = []; ajxpNode = ajxpNode.findInArbo(this._rootNode, fakeNodes); if(fakeNodes.length){ const firstFake = fakeNodes.shift(); firstFake.observeOnce("first_load", function(e){ this.requireContextChange(ajxpNode); }.bind(this)); firstFake.observeOnce("error", function(message){ Logger.error(message); firstFake.notify("node_removed"); const parent = firstFake.getParent(); parent.removeChild(firstFake); //delete(firstFake); this.requireContextChange(parent); }.bind(this) ); this.publish("context_loading"); firstFake.load(this._iAjxpNodeProvider); return; } } ajxpNode.observeOnce("loaded", function(){ this.setContextNode(ajxpNode, true); this.publish("context_loaded"); if(this.getPendingSelection()){ const selPath = ajxpNode.getPath() + (ajxpNode.getPath() == "/" ? "" : "/" ) +this.getPendingSelection(); const selNode = ajxpNode.findChildByPath(selPath); if(selNode) { this.setSelectedNodes([selNode], this); }else{ if(ajxpNode.getMetadata().get("paginationData") && arguments.length < 3){ let newPage; let currentPage = ajxpNode.getMetadata().get("paginationData").get("current"); this.loadPathInfoSync(selPath, function(foundNode){ newPage = foundNode.getMetadata().get("page_position"); }, {page_position:'true'}); if(newPage && newPage !== currentPage){ ajxpNode.getMetadata().get("paginationData").set("new_page", newPage); this.requireContextChange(ajxpNode, true, true); return; } } } this.clearPendingSelection(); } }.bind(this)); ajxpNode.observeOnce("error", function(message){ Logger.error(message); this.publish("context_loaded"); }.bind(this)); this.publish("context_loading"); try{ if(forceReload){ if(paginationPage){ ajxpNode.getMetadata().get('paginationData').set('current', paginationPage); } ajxpNode.reload(this._iAjxpNodeProvider); }else{ ajxpNode.load(this._iAjxpNodeProvider); } }catch(e){ this.publish("context_loaded"); } } requireNodeReload(nodeOrPath, completeCallback){ if(nodeOrPath instanceof String){ nodeOrPath = new AjxpNode(nodeOrPath); } let onComplete = null; if(this._selectedNodes.length) { let found = -1; this._selectedNodes.map(function(node, key){ if(node.getPath() == nodeOrPath.getPath()) found = key; }); if(found !== -1){ // MAKE SURE SELECTION IS OK AFTER RELOAD this._selectedNodes = LangUtils.arrayWithout(this._selectedNodes, found); this.publish("selection_changed", this); onComplete = function(newNode){ this._selectedNodes.push(newNode); this._selectionSource = {}; this.publish("selection_changed", this); if(completeCallback) completeCallback(newNode); }.bind(this); } } this._iAjxpNodeProvider.refreshNodeAndReplace(nodeOrPath, onComplete); } loadPathInfoSync (path, callback, additionalParameters = {}){ this._iAjxpNodeProvider.loadLeafNodeSync(new AjxpNode(path), callback, false, additionalParameters); } loadPathInfoAsync (path, callback){ this._iAjxpNodeProvider.loadLeafNodeSync(new AjxpNode(path), callback, true); } /** * Sets the root of the data store * @param ajxpRootNode AjxpNode The parent node */ setRootNode (ajxpRootNode){ this._rootNode = ajxpRootNode; this._rootNode.setRoot(); this._rootNode.observe("child_added", function(c){ //console.log(c); }); this.publish("root_node_changed", this._rootNode); this.setContextNode(this._rootNode); } /** * Gets the current root node * @returns AjxpNode */ getRootNode (){ return this._rootNode; } /** * Sets the current context node * @param ajxpDataNode AjxpNode * @param forceEvent Boolean If set to true, event will be triggered even if the current node is already the same. */ setContextNode (ajxpDataNode, forceEvent){ if(this._contextNode && this._contextNode == ajxpDataNode && this._currentRep == ajxpDataNode.getPath() && !forceEvent){ return; // No changes } if(!ajxpDataNode) return; if(this._contextNodeReplacedObserver && this._contextNode){ this._contextNode.stopObserving("node_replaced", this._contextNodeReplacedObserver); } this._contextNode = ajxpDataNode; this._currentRep = ajxpDataNode.getPath(); this.publish("context_changed", ajxpDataNode); if(!this._contextNodeReplacedObserver) this._contextNodeReplacedObserver = this.contextNodeReplaced.bind(this); ajxpDataNode.observe("node_replaced", this._contextNodeReplacedObserver); } contextNodeReplaced(newNode){ this.setContextNode(newNode); } /** * */ publish(eventName, optionalData){ let args = []; if(this._globalEvents){ if(window.pydio){ args.push(eventName); if(optionalData) args.push(optionalData); window.pydio.fire.apply(window.pydio,args); }else if(document.fire) { args.push("ajaxplorer:"+eventName); if(optionalData) args.push(optionalData); document.fire.apply(document, args); } if(optionalData){ args = [eventName, {memo:optionalData}]; }else{ args = [eventName]; } this.notify.apply(this,args); }else{ if(optionalData){ args = [eventName, {memo:optionalData}]; }else{ args = [eventName]; } this.notify.apply(this,args); } } /** * Get the current context node * @returns AjxpNode */ getContextNode (){ return this._contextNode; } /** * After a copy or move operation, many nodes may have to be reloaded * This function tries to reload them in the right order and if necessary. * @param nodes AjxpNodes[] An array of nodes */ multipleNodesReload (nodes){ for(var i=0;i= imTime){ return false; } n.getParent().removeChild(n); return true; } return false; } /** * Update a node somewhere in the datamodel * @param node AjxpNode * @param setSelectedAfterUpdate bool */ updateNode(node, setSelectedAfterUpdate=false){ var original = node.getMetadata().get("original_path"); var fake, n; if(original && original != node.getPath() && PathUtils.getDirname(original) != PathUtils.getDirname(node.getPath())){ // Node was really moved to another folder fake = new AjxpNode(original); n = fake.findInArbo(this.getRootNode(), undefined); if(n){ n.getParent().removeChild(n); } var parentFake = new AjxpNode(PathUtils.getDirname(node.getPath())); var parent = parentFake.findInArbo(this.getRootNode(), undefined); if(!parent && PathUtils.getDirname(node.getPath()) == "") parent = this.getRootNode(); if(parent){ node.getMetadata().set("original_path", undefined); parent.addChild(node); } }else{ if(node.getMetadata().get("original_path") === "/" && node.getPath() === "/"){ n = this.getRootNode(); n.replaceMetadata(node.getMetadata()); if(setSelectedAfterUpdate && this.getContextNode() == n) { this.setSelectedNodes([n], {}); } return; } fake = new AjxpNode(original); n = fake.findInArbo(this.getRootNode(), undefined); if(n && !n.isMoreRecentThan(node)){ node._isLoaded = n._isLoaded; n.replaceBy(node, "override"); if(setSelectedAfterUpdate && this.getContextNode() == n.getParent()) { this.setSelectedNodes([n], {}); } } } } /** * Sets an array of nodes to be selected after the context is (re)loaded * @param selection AjxpNode[] */ setPendingSelection (selection){ this._pendingSelection = selection; } /** * Gets the array of nodes to be selected after the context is (re)loaded * @returns AjxpNode[] */ getPendingSelection (){ return this._pendingSelection; } /** * Clears the nodes to be selected */ clearPendingSelection (){ this._pendingSelection = null; } /** * Set an array of nodes as the current selection * @param ajxpDataNodes AjxpNode[] The nodes to select * @param source String The source of this selection action */ setSelectedNodes (ajxpDataNodes, source){ if(this._selectedNodes.length == ajxpDataNodes.length){ if(ajxpDataNodes.length === 0) { return; } var equal = true; for(var k=0;k 1; } /** * Whether the selection has a file with one of the mimes * @param mimeTypes Array Array of mime types * @returns Boolean */ hasMime (mimeTypes){ if(mimeTypes.length==1 && mimeTypes[0] == "*") return true; var has = false; mimeTypes.map(function(mime){ if(has) return; for(let i=0; i