Ext.define('IW.controller.Pages', { extend: 'Ext.app.Controller', models: ['Page', 'ContentField', 'Attachment', 'Lock'], stores: ['Pages', 'ContentFields', 'Attachments', 'Locks'], views: ['page.PageTree','page.PageWindow','page.TreeWindow','page.Access'], requires: ['IW.Utilities'], init: function() { this.control({ 'pagetree': { itemdblclick: this.openPage, select: this.selectPage, deselect: this.deselectPage }, 'pagewindow': { itemeditbuttonclick: this.editPage, itemsavebuttonclick: this.savePage, resize: this.resizeWindow, itemrefreshbuttonclick: this.refreshPage, itemcancelbuttonclick: this.cancelEdit, close: this.closeWindow, pagelinkclick: this.openPageLink }, 'treewindow': { addpagebuttonclick: this.addPage, deletepagebuttonclick: this.deletePageConfirm, refreshtree: this.refreshTree, pageaccessrightsbuttonclick: this.showacldialog }, 'pageaccess': { searchSelect: this.searchSelect, saveACL: this.saveACL } }); }, saveACL: function (win) { var rhidden = Ext.getCmp('page-hidden-read'); win.record.set('Readacl', rhidden.value); var whidden = Ext.getCmp('page-hidden-write'); win.record.set('Writeacl', whidden.value); var ahidden = Ext.getCmp('page-hidden-admin'); win.record.set('Adminacl', ahidden.value); var inherit = Ext.getCmp('stop-acl-inheritation'); win.record.set('Stopinheritation', inherit.value); for (index in win.record.stores) { var store = win.record.stores[index]; store.sync(); } win.destroy(); }, searchSelect: function(combo, record, hidden, panel) { this.getController('Wikis').searchSelect(combo, record, hidden, panel); }, showacldialog: function(event, target, owner, tool) { var rec = owner.up('window').down('panel').getSelectionModel().getSelection()[0]; var dialog = Ext.create('IW.view.page.Access', { title: 'Edit access for '+rec.data.Title, record: rec }); dialog.show(); var rhidden = Ext.getCmp('page-hidden-read'); rhidden.value = rec.data.Readacl; var rpanel = Ext.getCmp('page-acl-read'); this.getController('Wikis').buildACLEditor(rhidden, rpanel); var whidden = Ext.getCmp('page-hidden-write'); whidden.value = rec.data.Writeacl; var wpanel = Ext.getCmp('page-acl-write'); this.getController('Wikis').buildACLEditor(whidden, wpanel); var ahidden = Ext.getCmp('page-hidden-admin'); ahidden.value = rec.data.Adminacl; var apanel = Ext.getCmp('page-acl-admin'); this.getController('Wikis').buildACLEditor(ahidden, apanel); var inherit = Ext.getCmp('stop-acl-inheritation'); inherit.setValue(rec.data.Stopinheritation); }, getAllChildren: function (node) { var me = this; var all = new Array(); if(!Ext.value(node,false)) { return []; } if(!node.hasChildNodes()) { return node; } else { all.push(node); node.eachChild(function (Mynode) { all = all.concat(me.getAllChildren(Mynode)); }); } return all; }, refreshTree: function(event,target,owner,tool) { var tree = owner.up('window').down('treepanel'); tree.getStore().reload( { callback: function(records, options, success) { // Nothing, see savePage() } } ); }, deletePage: function (record, store) { // This is slightly bad, but TreeStore doesn't have method for deleting records. // It has to be done via model, which has to get the Proxy first from somewhere. // .... // Note that autoSync is not required, sync will occur in any case. record.setProxy(store.getProxy()); var children = this.getAllChildren(record); Ext.Array.each(children, function(item, index, allchildren) { item.setProxy(store.getProxy()); }); record.destroy(false); }, deletePageConfirm: function (event, target, owner, tool) { var rec = owner.up('window').down('panel').getSelectionModel().getSelection()[0]; var st = owner.up('window').down('panel').store; var me = this; Ext.Msg.confirm('Delete page?', 'You are about the delete the page '+rec.data.Title+'. Are you sure you want to do this?', function(button) { if (button === 'yes') { me.deletePage(rec, st); me.closeAllChildren(rec.data.Path); } }); }, closeAllChildren: function(path) { // Closes page and its children - using path Ext.WindowManager.each(function (item) { if (item.record && item.record.data.Page_id && item.record.data.Path) { if (item.record.data.Path.indexOf(path) == 0) { item.close(); } } }); }, selectPage: function (panel, record, index, eOpts ) { var minus = panel.view.up('window').tools.Minus; var plus = panel.view.up('window').tools.Plus; var key = panel.view.up('window').tools.Key; if (minus.hidden && IW.Utilities.canwrite(record)) { minus.show(); } if (key.hidden && IW.Utilities.canadmin(record)) { key.show(); } if (IW.Utilities.canwrite(record)) { plus.show(); } else { plus.hide(); } }, deselectPage: function (panel, record, index, eOpts ) { var minus = panel.view.up('window').tools.Minus; var plus = panel.view.up('window').tools.Plus; var key = panel.view.up('window').tools.Key; var wiki = panel.view.up('window').record; if (!minus.hidden) { minus.hide(); } if (!key.hidden) { key.hide(); } if (IW.Utilities.canwrite(wiki)) { plus.show(); } else { plus.hide(); } }, openPageLink: function(win, element) { // Fake something that will work with openPage, reusing the same store all over again //var store = win.store; var wiki_id = element.id.split('/')[0] var parent_id = element.id.split('/')[1] var page_id = element.id.split('/')[2] var treeStore = Ext.create('IW.store.Pages'); if (parent_id != "") { treeStore.setRootNode({ expanded: true, Wiki_id: wiki_id, Page_id: parent_id }); } else { treeStore.setRootNode({ expanded: true, Wiki_id: wiki_id }); } treeStore.load({ scope: this, callback: function(records, operation, success) { var pagerecord; var root = treeStore.getRootNode(); // treeStore doesn't have proper search method, yet // cascadeBy walks the tree until we get match root.cascadeBy(function (childNode) { if (childNode.get('Page_id') == page_id) { pagerecord = childNode; } }, this); // If we found one, open it! if (pagerecord) { var panel = { store: treeStore }; this.getController('Pages').openPage(panel, pagerecord, null, null, null, null); } } }); }, addPageLinkEvents: function(win) { var ow = win; /* Ext.util.Observable.capture(ow, function(){ console.log(arguments); }); */ ow.down('panel').body.on('click', function (evt, el, o) { ow.fireEvent('pagelinkclick', ow, el); }, this, { delegate : '.extpagelink' }); }, fixPageAnchorLinks: function(win) { // First we will add the TOC links of window a random prefix, then the targets. This will keep // the anchors of pages separated. However, this will have to be noted when adding "open & edit" // links or similar to the subchapters - there will be extra nonsense on the ids. var randomCode = Math.random().toString(36).substring(7); var linkElems = win.down('panel').getEl().select("a.extpageanchor").elements; if (linkElems && linkElems.length > 0) { Ext.Array.forEach(linkElems, function (item, index, allItems) { var link = item.href.split('#'); item.href = '#' + randomCode + '+' + link[1]; }); // Now change the titles from below... var titleElems = win.down('panel').getEl().select('.extpagetarget').elements; if (titleElems && titleElems.length > 0) { Ext.Array.forEach(titleElems, function (item, index, allItems) { item.id = randomCode + '+' + item.id; }); } } }, openPage: function(panel, record, item, index, e, eOpts ) { console.log("openPage"); var pw = Ext.widget('pagewindow', { title: record.data.Title, store: panel.store, record: record }); pw.show(); this.refreshPage(null, null, pw, null); }, refreshPage: function(event, target, owner, tool) { var win; var me = this; if (owner.xtype == 'pagewindow' ) { win = owner; } else { win = owner.up('window'); } var record = win.record; if (record) { // Reload and update var ContentField = Ext.ModelManager.getModel('IW.model.ContentField'); var fieldId = record.data.Wiki_id.toString() + '/' + record.data.Page_id.toString(); ContentField.load(fieldId, { success: function(field) { win.down('panel').update(field.data.Contentwithmacros); win.contentfield = field; me.addPageLinkEvents (win); me.fixPageAnchorLinks (win); }, failure: function(field) { console.log('Unable to load the content field'); } }); } else { console.log('Refresh requested but record was undefined!'); } }, cancelEdit: function(event, target, owner, tool, blockrefresh) { var panel = owner.up('window').down('panel'); var editor = CKEDITOR.instances[panel.body.down('div').id]; var record = owner.up('window').record; // Same... owner.up('window').tools.Refresh.show(); owner.up('window').tools.Edit.show(); owner.up('window').tools.Save.hide(); owner.up('window').tools.Cancel.hide(); editor.destroy(); owner.up('window').setAutoScroll(true); panel.editorenabled = false; // Reset content field var me = this; setTimeout(function() { // If we reload the content faster than it has been saved, there will be problems... // This causes flicker, but the content shows more reliably. Should take an other look // later. me.refreshPage(null, null, owner.up('window'), null); }, 1000); // Restore title owner.up('window').getHeader().remove(0); owner.up('window').setTitle(record.data.Title); // Remove listener that can prevent dragging owner.up('window').dd.removeListener('beforedragstart'); // Remove the locks this.unlockPage(record.data.Page_id, record.data.Wiki_id); }, editPage: function(event, target, owner, tool) { // Used to check for locks var me = this; var wind = owner.up('window'); var page_id = wind.record.data.Page_id; var wiki_id = wind.record.data.Wiki_id; var Lock = Ext.ModelManager.getModel('IW.model.Lock'); Lock.load(wiki_id+'/'+page_id, { success: function(lock) { console.log(lock); if (lock.data.Target_id != '') { Ext.Msg.alert('Potential edit conflict!', 'User '+ lock.data.Realname + " started editing this content at " + lock.data.Modified + ". You may continue, but saving might cause conflicts.", function() { // Open for editing me.editPageProceed(event, target, owner, tool); }, this ); } else { // There is probably no lock, so open the page for editing and lock me.lockPage(page_id, wiki_id); me.editPageProceed(event, target, owner, tool); } }, failure: function(lock) { // There is probably no lock, so open the page for editing and lock me.lockPage(page_id, wiki_id); me.editPageProceed(event, target, owner, tool); } }); }, lockPage: function(page_id, wiki_id) { var lock = Ext.create('IW.model.Lock'); lock.data.Wiki_id = wiki_id; lock.data.Target_id = page_id; lock.data.id = wiki_id+'/'+page_id; lock.save(); }, unlockPage: function(page_id, wiki_id) { var Lock = Ext.ModelManager.getModel('IW.model.Lock'); Lock.load(wiki_id+'/'+page_id, { success: function (lock) { if (lock.get('Wiki_id') != "") { // Destroy the lock lock.destroy(); } }, failure: function(lock) { // Either not locked or error } }); }, editPageProceed: function(event, target, owner, tool) { var window = owner.up('window'); // Change the content to version without macros window.down('panel').update(window.contentfield.data.Content); // window.down('panel').update(window.record.data.content); // Change the header to edit mode //------------------------------------- var killDrag = false; // window.dd is an Ext.util.ComponentDragger var dragEvent = window.dd.on({ // beforedragstart event can cancel the drag beforedragstart: function(dd, e) { if (killDrag) { return false; } } }); var header = window.getHeader(); var origTitle = header.title; header.setTitle(''); // Get rid of the original for now var field = Ext.create('Ext.form.field.Text', { name: 'Title', allowBlank: false, cls: 'pagetitleinput', value: origTitle, listeners : { el : { delegate : 'input', mouseout: function() { killDrag = false; }, mouseover: function() { killDrag = true; } } } }); header.insert(0, field); // First, before the tools (several buttons there) // Change to content area to edit mode // ----------------------------------- var panel = owner.up('window').down('panel'); var contentdiv = panel.body.down('div'); // Remove scrollbars, editor will handle this thing owner.up('window').setAutoScroll(false); // Boolean variable for other functions panel.editorenabled = true; // Enable the editor var newID = 'panel-'+owner.up('window').record.data.Page_id+'-innerCt'; // Generate ID that will be always the same for editing the same content contentdiv.dom.id = newID; contentdiv.id = newID; var editor = CKEDITOR.replace( contentdiv.id , { baseFloatZIndex: 99999, on: { // Resize once to fit the panel instanceReady: function(evt) { var editor = evt.editor; var width = panel.getWidth(); var height = panel.getHeight(); editor.resize(width,height); editor.wiki = window.record.data.Wiki_id; } } }); // Change the tools owner.up('window').tools.Refresh.hide(); owner.up('window').tools.Edit.hide(); owner.up('window').tools.Save.show(); owner.up('window').tools.Cancel.show(); }, savePage: function(event, target, owner, tool) { var panel = owner.up('window').down('panel'); var editor = CKEDITOR.instances[panel.body.down('div').id]; var record = owner.up('window').record; var contentfield = owner.up('window').contentfield; var newtitle = owner.up('window').getHeader().getComponent(0).value; // editor.checkDirty() failed for unknown reason, always reported "false" after setData() // Handle the content area if (contentfield.data.Content!=editor.getData()) { contentfield.data.Content=editor.getData(); var fieldId = record.data.Wiki_id.toString() + '/' + record.data.Page_id.toString(); contentfield.setId(fieldId); contentfield.save({ success: function(record, operation) { if (newtitle==record.data.Title) { // Nothing so far } }, failure: function(record, operation) { console.log('Unable to save content field!'); } }); } // Handle the title if (newtitle!=record.data.Title) { var store = owner.up('window').store; //var target = store.getById(record.data.internalId); //console.log(target); //record.data.title = newtitle; record.set('Title', newtitle); owner.up('window').record = record; // Save the title // The trick we did with separate stores will cause the following to be necessary. // There might be a cleaner solution to this though, have to investigate it later. var me = this; for (index in record.stores) { st = record.stores[index]; // This is a bit messy, but we have to asynchronously sync (to save changes), // reload the item back from store (to get macros calculated), and set the content // to the reloaded... st.sync({ success: function() { // Reload var newstore = null; // No idea why, but sometimes it's tree.treeStore, and sometimes treeStore if (st.treeStore) { newstore = st.treeStore; } else { newstore = st.tree.treeStore; } newstore.reload({ callback: function(records, options, success) { //target = st.getById(record.data.id); // Set the content item to record //console.log(target); //owner.up('window').record = target; // Practically the same after everything has been saved successfully... me.cancelEdit(event, target, owner, tool); } }); }, failure: function() { console.log('Unable to save edited data!'); }, scope: this }); } } else { this.cancelEdit(event, target, owner, tool); } }, closeWindow: function(window, eOpts) { // Remove locks var record = window.record; this.unlockPage(record.data.Page_id, record.data.Wiki_id); // Destroy the editors var panel = window.down('panel'); if (panel.editorenabled) { var editor = CKEDITOR.instances[panel.body.down('div').id]; editor.destroy(); } window.down('panel').destroy(); window.destroy(); }, resizeWindow: function ( window, width, height, oldWidth, oldHeight, eOpts ) { var panel = window.down('panel'); if (panel.editorenabled) { var editor = CKEDITOR.instances[panel.body.down('div').id]; editor.resize(panel.getWidth(),panel.getHeight()); } }, addPage: function (event, target, owner, tool) { var window = owner.up('window'); var store = window.down('panel').getStore(); var rootNode = store.getRootNode(); // Determine the parent path. If nothing is selected, use rootNode. var parent = window.down('panel').getStore().getRootNode(); var selected = window.down('panel').getSelectionModel().getSelection(); if (selected.length > 0 ) { parent = selected[0]; } var getPath = function(r,u,w) { if (r.data.Path && r.data.Path.length > 0) { return r.data.Path + '/' + u; } else { return u; } }; // Create new model var uuidstr = Ext.data.IdGenerator.get('uuid').generate(); var now = new Date(); var newPage = Ext.create('IW.model.Page', { Page_id: uuidstr, Wiki_id: window.record.data.Wiki_id, Path: getPath(parent, uuidstr, window), Title: 'New Page', Create_user: '', Content: 'Insert content here', ContentWithMacros: 'Insert content here', Readacl: '', Writeacl: '', Adminacl: '', Modified: now.toJSON(), Stopinheritation: false, ChildNodes: [], loaded: true, MatchedPermissions: ['admin'], parentId: parent.internalId, root: rootNode, }); // Add to ui parent.appendChild(newPage, false, true); parent.expand(); window.down('panel').getSelectionModel().select(newPage); // Save the new page to store and open the window newPage.setProxy(store.getProxy()); var me = this; newPage.save({ success: function (record, operation) { // Open the page me.openPage (window.down('panel'), newPage); }, failure: function (record, operation) { console.log('Unable to save new page!'); } }); } });