From bd2aef2e663c5abee59489c42316e5554eed2771 Mon Sep 17 00:00:00 2001 From: Erik Amaru Ortiz Date: Thu, 29 Mar 2012 16:42:09 -0400 Subject: [PATCH] BUG 8875 "ExtJS based Login & Main ProcessMaker interface" (1st commit) --- gulliver/js/ext/pmos-common.js | 86 +- gulliver/js/ext/ux/portal.js | 230 ++++ gulliver/js/ext/ux/ux.menu.js | 13 + gulliver/js/ext/ux/virtualkeyboard.js | 812 +++++++++++++ gulliver/system/class.g.php | 261 +++- gulliver/system/class.headPublisher.php | 48 +- gulliver/system/class.rbac.php | 26 +- workflow/engine/classes/class.processMap.php | 14 +- workflow/engine/classes/class.system.php | 129 ++ .../engine/classes/model/UsersProperties.php | 16 +- workflow/engine/config/paths.php | 51 +- workflow/engine/controllers/admin.php | 38 + workflow/engine/controllers/adminProxy.php | 74 ++ workflow/engine/controllers/installer.php | 1057 +++++++++++++++++ workflow/engine/controllers/main.php | 1050 ++++++++++++++++ workflow/engine/menus/processmaker.php | 29 +- workflow/engine/menus/setup.php | 1 + .../engine/methods/cases/casesListExtJs.php | 1 - .../engine/methods/cases/casesStartPage.php | 5 - .../methods/cases/cases_Scheduler_Log.php | 1 - workflow/engine/methods/cases/main_init.php | 20 +- workflow/engine/methods/cases/open.php | 2 +- .../methods/departments/departments.php | 1 - .../engine/methods/groups/groupsMembers.php | 1 - .../engine/methods/login/authentication.php | 8 +- workflow/engine/methods/roles/roles_List.php | 1 - .../engine/methods/setup/appCacheViewConf.php | 1 - workflow/engine/methods/setup/languages.php | 1 - .../engine/methods/setup/loginSettings.php | 8 +- .../methods/setup/loginSettingsAjax.php | 25 +- workflow/engine/methods/setup/skin_Ajax.php | 42 +- .../skinEngine/base/css/pmos-xtheme-gray.css | 367 ++++++ .../skinEngine/base/layout-submenu.html | 15 +- workflow/engine/skinEngine/skinEngine.php | 13 +- workflow/engine/templates/admin/system.js | 185 +++ workflow/engine/templates/cases/main.js | 45 +- workflow/engine/templates/index.html.tpl | 8 + workflow/engine/templates/installer/Card.js | 147 +++ .../engine/templates/installer/CardLayout.js | 56 + workflow/engine/templates/installer/Header.js | 175 +++ workflow/engine/templates/installer/Wizard.js | 551 +++++++++ .../engine/templates/installer/databases.tpl | 30 + workflow/engine/templates/installer/main.html | 41 + workflow/engine/templates/installer/main.js | 1020 ++++++++++++++++ .../engine/templates/installer/newSite.html | 41 + .../engine/templates/installer/newSite.js | 535 +++++++++ .../engine/templates/main/forgotPassword.tpl | 26 + workflow/engine/templates/main/index.html | 30 + workflow/engine/templates/main/index.js | 244 ++++ workflow/engine/templates/main/login.html | 97 ++ workflow/engine/templates/main/login.js | 480 ++++++++ workflow/engine/templates/main/sysLogin.html | 97 ++ workflow/engine/templates/main/sysLogin.js | 323 +++++ workflow/engine/templates/main/systemInfo.js | 36 + .../engine/templates/setup/loginSettings.js | 53 +- workflow/engine/templates/setup/main.js | 9 +- .../public_html/images/canvastile_bg1.jpg | Bin 0 -> 517 bytes .../public_html/images/canvastile_bg2.jpg | Bin 0 -> 603 bytes workflow/public_html/images/close.png | Bin 0 -> 832 bytes workflow/public_html/images/dashboard.png | Bin 0 -> 940 bytes workflow/public_html/images/designerIcon.png | Bin 0 -> 1128 bytes workflow/public_html/images/error.png | Bin 0 -> 2361 bytes .../images/ext-ux-wiz-stepIndicator.png | Bin 0 -> 356 bytes workflow/public_html/images/header_bg.jpg | Bin 0 -> 361 bytes workflow/public_html/images/homeIcon.png | Bin 0 -> 1743 bytes workflow/public_html/images/icon-logout.png | Bin 0 -> 903 bytes .../public_html/images/icon-user-arrow.png | Bin 0 -> 370 bytes workflow/public_html/images/icon-user-sep.png | Bin 0 -> 231 bytes workflow/public_html/images/icon-user.png | Bin 0 -> 941 bytes workflow/public_html/images/info.png | Bin 0 -> 2242 bytes workflow/public_html/images/login-loader.gif | Bin 0 -> 723 bytes .../public_html/images/logo_processmaker2.gif | Bin 0 -> 1519 bytes .../public_html/images/logo_processmaker2.xcf | Bin 0 -> 9869 bytes workflow/public_html/images/logout.gif | Bin 0 -> 389 bytes .../public_html/images/processmaker.logo.png | Bin 0 -> 9029 bytes .../public_html/images/processmaker2.logo.png | Bin 0 -> 7997 bytes .../images/processmaker2.logo2.png | Bin 0 -> 21345 bytes .../public_html/images/profile-icontab.png | Bin 0 -> 1003 bytes .../public_html/images/profile-picture.png | Bin 0 -> 566 bytes workflow/public_html/images/profile.gif | Bin 0 -> 932 bytes workflow/public_html/images/setup-gears.gif | Bin 0 -> 640 bytes workflow/public_html/images/setupIcon.png | Bin 0 -> 2070 bytes workflow/public_html/images/start.png | Bin 0 -> 4173 bytes workflow/public_html/images/sys-info-icon.png | Bin 0 -> 1246 bytes workflow/public_html/images/x-icon-user.png | Bin 0 -> 1280 bytes workflow/public_html/index.html | 2 +- workflow/public_html/sysGeneric.php | 141 ++- 87 files changed, 8549 insertions(+), 270 deletions(-) create mode 100644 gulliver/js/ext/ux/portal.js create mode 100644 gulliver/js/ext/ux/ux.menu.js create mode 100644 gulliver/js/ext/ux/virtualkeyboard.js create mode 100644 workflow/engine/controllers/installer.php create mode 100644 workflow/engine/controllers/main.php create mode 100644 workflow/engine/templates/admin/system.js create mode 100644 workflow/engine/templates/index.html.tpl create mode 100644 workflow/engine/templates/installer/Card.js create mode 100644 workflow/engine/templates/installer/CardLayout.js create mode 100644 workflow/engine/templates/installer/Header.js create mode 100644 workflow/engine/templates/installer/Wizard.js create mode 100644 workflow/engine/templates/installer/databases.tpl create mode 100644 workflow/engine/templates/installer/main.html create mode 100644 workflow/engine/templates/installer/main.js create mode 100644 workflow/engine/templates/installer/newSite.html create mode 100644 workflow/engine/templates/installer/newSite.js create mode 100644 workflow/engine/templates/main/forgotPassword.tpl create mode 100644 workflow/engine/templates/main/index.html create mode 100644 workflow/engine/templates/main/index.js create mode 100644 workflow/engine/templates/main/login.html create mode 100644 workflow/engine/templates/main/login.js create mode 100644 workflow/engine/templates/main/sysLogin.html create mode 100644 workflow/engine/templates/main/sysLogin.js create mode 100644 workflow/engine/templates/main/systemInfo.js create mode 100644 workflow/public_html/images/canvastile_bg1.jpg create mode 100644 workflow/public_html/images/canvastile_bg2.jpg create mode 100644 workflow/public_html/images/close.png create mode 100644 workflow/public_html/images/dashboard.png create mode 100644 workflow/public_html/images/designerIcon.png create mode 100644 workflow/public_html/images/error.png create mode 100644 workflow/public_html/images/ext-ux-wiz-stepIndicator.png create mode 100644 workflow/public_html/images/header_bg.jpg create mode 100644 workflow/public_html/images/homeIcon.png create mode 100644 workflow/public_html/images/icon-logout.png create mode 100644 workflow/public_html/images/icon-user-arrow.png create mode 100644 workflow/public_html/images/icon-user-sep.png create mode 100644 workflow/public_html/images/icon-user.png create mode 100644 workflow/public_html/images/info.png create mode 100644 workflow/public_html/images/login-loader.gif create mode 100644 workflow/public_html/images/logo_processmaker2.gif create mode 100644 workflow/public_html/images/logo_processmaker2.xcf create mode 100644 workflow/public_html/images/logout.gif create mode 100644 workflow/public_html/images/processmaker.logo.png create mode 100644 workflow/public_html/images/processmaker2.logo.png create mode 100644 workflow/public_html/images/processmaker2.logo2.png create mode 100644 workflow/public_html/images/profile-icontab.png create mode 100644 workflow/public_html/images/profile-picture.png create mode 100644 workflow/public_html/images/profile.gif create mode 100644 workflow/public_html/images/setup-gears.gif create mode 100644 workflow/public_html/images/setupIcon.png create mode 100644 workflow/public_html/images/start.png create mode 100644 workflow/public_html/images/sys-info-icon.png create mode 100644 workflow/public_html/images/x-icon-user.png diff --git a/gulliver/js/ext/pmos-common.js b/gulliver/js/ext/pmos-common.js index 311f0323f..59529bfc2 100755 --- a/gulliver/js/ext/pmos-common.js +++ b/gulliver/js/ext/pmos-common.js @@ -28,7 +28,10 @@ PMExtJSCommon = function() { msg: msg, buttons: Ext.MessageBox.YESNO, animEl: 'mb9', - fn: fn != undefined ? fn: function(){}, + fn: function(btn, text){ + if ( btn == 'yes' ) + setTimeout(fn, 0); + }, icon: Ext.MessageBox.QUESTION }); } @@ -132,7 +135,34 @@ PMExtJSCommon = function() { }), loadMask: true }); - } + } + + this.cookie = { + create: function(name, value, days) { + if (days) { + var date = new Date(); + date.setTime(date.getTime()+(days*24*60*60*1000)); + var expires = "; expires="+date.toGMTString(); + }else var expires = ""; + document.cookie = name+"="+value+expires+"; path=/"; + }, + + read: function(name) { + var nameEQ = name + "="; + var ca = document.cookie.split(';'); + for(var i=0;i < ca.length;i++) { + var c = ca[i]; + while (c.charAt(0)==' ') c = c.substring(1,c.length); + if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); + } + return null; + }, + + erase: function(name) { + Tools.createCookie(name,"",-1); + } + } + } var PMExt = new PMExtJSCommon(); @@ -159,7 +189,7 @@ Ext.msgBoxSlider = function(){ msgCt = Ext.DomHelper.insertFirst(document.body, {id:'msg-div', style:'position:absolute'}, true); } //msgCt.alignTo(document, 'br-br'); - msgCt.alignTo(document, "br-br", [ -20, -20]); + //msgCt.alignTo(document, "br-br", [-20, -20]); var s = String.format.apply(String, Array.prototype.slice.call(arguments, 1)); var m = Ext.DomHelper.append(msgCt, {html:createBox(title, s)}, true); @@ -171,7 +201,57 @@ Ext.msgBoxSlider = function(){ Ext.get('x-box-mc-inner' ).setStyle('background-repeat', 'no-repeat'); Ext.get('x-box-mc-inner' ).setStyle('padding-left', '35px'); m.slideIn('t').pause(3).ghost("t", {remove:true}); + }, + + msgTopCenter : function(type, title, format, time) { + if (typeof remove == 'undefined') + remove : true; + + if (typeof time == 'undefined') + time = 3; + + if( ! msgCt ) { + msgCt = Ext.DomHelper.insertFirst(document.body, {id:'msg-div', style:'position:absolute'}, true); + } + + var s = String.format.apply(String, Array.prototype.slice.call(arguments, 2)); + var m = Ext.DomHelper.append(msgCt, {html:createBox(title, s)}, true); + m.setWidth(400 ); + m.position(null, 5000 ); + m.alignTo(document, 't-t'); + + switch(type) { + case 'alert': + case 'warning': + case 'tmp-warning': + image = '/images/alert.gif'; + break; + case 'error': + case 'tmp-error': + image = '/images/error.png'; + break; + case 'tmp-info': + case 'info': + image = '/images/info.png'; + break; + case 'success': + case 'ok': + image = '/images/select-icon.png'; + break; + default: + image = ''; + } + + if (image != '') { + Ext.get('x-box-mc-inner' ).setStyle('background-image', 'url("'+image+'")'); + } + Ext.get('x-box-mc-inner' ).setStyle('background-position', '5px 10px'); + Ext.get('x-box-mc-inner' ).setStyle('background-repeat', 'no-repeat'); + Ext.get('x-box-mc-inner' ).setStyle('padding-left', '45px'); + + m.slideIn('t').pause(time).ghost("t", {remove:true}); } + }; }(); diff --git a/gulliver/js/ext/ux/portal.js b/gulliver/js/ext/ux/portal.js new file mode 100644 index 000000000..de5035b89 --- /dev/null +++ b/gulliver/js/ext/ux/portal.js @@ -0,0 +1,230 @@ +/*! + * Ext JS Library 3.2.1 + * Copyright(c) 2006-2010 Ext JS, Inc. + * licensing@extjs.com + * http://www.extjs.com/license + */ + +/** Portal.js */ + +Ext.ux.Portal = Ext.extend(Ext.Panel, { + layout : 'column', + autoScroll : true, + cls : 'x-portal', + defaultType : 'portalcolumn', + + initComponent : function(){ + Ext.ux.Portal.superclass.initComponent.call(this); + this.addEvents({ + validatedrop:true, + beforedragover:true, + dragover:true, + beforedrop:true, + drop:true + }); + }, + + initEvents : function(){ + Ext.ux.Portal.superclass.initEvents.call(this); + this.dd = new Ext.ux.Portal.DropZone(this, this.dropConfig); + }, + + beforeDestroy : function() { + if(this.dd){ + this.dd.unreg(); + } + Ext.ux.Portal.superclass.beforeDestroy.call(this); + } +}); + +Ext.reg('portal', Ext.ux.Portal); + +Ext.ux.Portal.DropZone = Ext.extend(Ext.dd.DropTarget, { + + constructor : function(portal, cfg){ + this.portal = portal; + Ext.dd.ScrollManager.register(portal.body); + Ext.ux.Portal.DropZone.superclass.constructor.call(this, portal.bwrap.dom, cfg); + portal.body.ddScrollConfig = this.ddScrollConfig; + }, + + ddScrollConfig : { + vthresh: 50, + hthresh: -1, + animate: true, + increment: 200 + }, + + createEvent : function(dd, e, data, col, c, pos){ + return { + portal: this.portal, + panel: data.panel, + columnIndex: col, + column: c, + position: pos, + data: data, + source: dd, + rawEvent: e, + status: this.dropAllowed + }; + }, + + notifyOver : function(dd, e, data){ + var xy = e.getXY(), portal = this.portal, px = dd.proxy; + + // case column widths + if(!this.grid){ + this.grid = this.getGrid(); + } + + // handle case scroll where scrollbars appear during drag + var cw = portal.body.dom.clientWidth; + if(!this.lastCW){ + this.lastCW = cw; + }else if(this.lastCW != cw){ + this.lastCW = cw; + portal.doLayout(); + this.grid = this.getGrid(); + } + + // determine column + var col = 0, xs = this.grid.columnX, cmatch = false; + for(var len = xs.length; col < len; col++){ + if(xy[0] < (xs[col].x + xs[col].w)){ + cmatch = true; + break; + } + } + // no match, fix last index + if(!cmatch){ + col--; + } + + // find insert position + var p, match = false, pos = 0, + c = portal.items.itemAt(col), + items = c.items.items, overSelf = false; + + for(var len = items.length; pos < len; pos++){ + p = items[pos]; + var h = p.el.getHeight(); + if(h === 0){ + overSelf = true; + } + else if((p.el.getY()+(h/2)) > xy[1]){ + match = true; + break; + } + } + + pos = (match && p ? pos : c.items.getCount()) + (overSelf ? -1 : 0); + var overEvent = this.createEvent(dd, e, data, col, c, pos); + + if(portal.fireEvent('validatedrop', overEvent) !== false && + portal.fireEvent('beforedragover', overEvent) !== false){ + + // make sure proxy width is fluid + px.getProxy().setWidth('auto'); + + if(p){ + px.moveProxy(p.el.dom.parentNode, match ? p.el.dom : null); + }else{ + px.moveProxy(c.el.dom, null); + } + + this.lastPos = {c: c, col: col, p: overSelf || (match && p) ? pos : false}; + this.scrollPos = portal.body.getScroll(); + + portal.fireEvent('dragover', overEvent); + + return overEvent.status; + }else{ + return overEvent.status; + } + + }, + + notifyOut : function(){ + delete this.grid; + }, + + notifyDrop : function(dd, e, data){ + delete this.grid; + if(!this.lastPos){ + return; + } + var c = this.lastPos.c, + col = this.lastPos.col, + pos = this.lastPos.p, + panel = dd.panel, + dropEvent = this.createEvent(dd, e, data, col, c, + pos !== false ? pos : c.items.getCount()); + + if(this.portal.fireEvent('validatedrop', dropEvent) !== false && + this.portal.fireEvent('beforedrop', dropEvent) !== false){ + + dd.proxy.getProxy().remove(); + panel.el.dom.parentNode.removeChild(dd.panel.el.dom); + + if(pos !== false){ + c.insert(pos, panel); + }else{ + c.add(panel); + } + + c.doLayout(); + + this.portal.fireEvent('drop', dropEvent); + + // scroll position is lost on drop, fix it + var st = this.scrollPos.top; + if(st){ + var d = this.portal.body.dom; + setTimeout(function(){ + d.scrollTop = st; + }, 10); + } + + } + delete this.lastPos; + }, + + // internal cache of body and column coords + getGrid : function(){ + var box = this.portal.bwrap.getBox(); + box.columnX = []; + this.portal.items.each(function(c){ + box.columnX.push({x: c.el.getX(), w: c.el.getWidth()}); + }); + return box; + }, + + // unregister the dropzone from ScrollManager + unreg: function() { + Ext.dd.ScrollManager.unregister(this.portal.body); + Ext.ux.Portal.DropZone.superclass.unreg.call(this); + } +}); + +/** PortalColumn.js */ + +Ext.ux.PortalColumn = Ext.extend(Ext.Container, { + layout : 'anchor', + //autoEl : 'div',//already defined by Ext.Component + defaultType : 'portlet', + cls : 'x-portal-column' +}); + +Ext.reg('portalcolumn', Ext.ux.PortalColumn); + +/** Portlet.js */ + +Ext.ux.Portlet = Ext.extend(Ext.Panel, { + anchor : '100%', + frame : true, + collapsible : false, + draggable : true, + cls : 'x-portlet' +}); + +Ext.reg('portlet', Ext.ux.Portlet); diff --git a/gulliver/js/ext/ux/ux.menu.js b/gulliver/js/ext/ux/ux.menu.js new file mode 100644 index 000000000..1be27d143 --- /dev/null +++ b/gulliver/js/ext/ux/ux.menu.js @@ -0,0 +1,13 @@ +/* + * Ext.ux.menu.StoreMenu Addon + * + * @author Marco Wienkoop (wm003/lubber) + * @copyright (c) 2009, Marco Wienkoop (marco.wienkoop@lubber.de) http://www.lubber.de +*/ + +Ext.namespace('Ext.ux.menu');Ext.ux.menu.StoreMenu=function(config){Ext.ux.menu.StoreMenu.superclass.constructor.call(this,config);if(!this.store){this.store=new Ext.data.SimpleStore({fields:['config'],url:this.url,baseParams:this.baseParams});} +this.on('show',this.onMenuLoad,this);this.store.on('beforeload',this.onBeforeLoad,this);this.store.on('load',this.onLoad,this);};Ext.extend(Ext.ux.menu.StoreMenu,Ext.menu.Menu,{loadingText:Ext.LoadMask.prototype.msg||'Loading...',loaded:false,onMenuLoad:function(){if(!this.loaded){this.store.load();}},updateMenuItems:function(loadedState,records){this.removeAll();this.el.sync();if(loadedState){for(var i=0,len=records.length;i'+this.loadingText+'');} +this.loaded=loadedState;},onBeforeLoad:function(store){this.store.baseParams=this.baseParams;this.updateMenuItems(false);},onLoad:function(store,records){this.updateMenuItems(true,records);}}); \ No newline at end of file diff --git a/gulliver/js/ext/ux/virtualkeyboard.js b/gulliver/js/ext/ux/virtualkeyboard.js new file mode 100644 index 000000000..a9ad65da6 --- /dev/null +++ b/gulliver/js/ext/ux/virtualkeyboard.js @@ -0,0 +1,812 @@ +Ext.ns('Ext.ux.plugins'); +Ext.ux.plugins.VirtualKeyboard = Ext.extend ( Ext.util.Observable, { + events: {}, + /** + * Init of plugin + * @param {Ext.Component} field + */ + init: function(field) { + this.addEvents({ + 'expand': true, + 'collapse': true + }); + + if(field.getXType() != 'textfield' && field.getXType() != 'textarea') + return; + + this.component = field; + field.virtualKeyboardPlugin = this; + + field.enableKeyEvents = true; + field.on({ + 'destroy': this.destroy, + 'focus': function(f){ + this.activeTarget = f; + }, + 'keypress': function(field, e){ + if(e.getKey() == e.ESC) + field.collapseKeyboard(); + }, + scope: this + }); + + Ext.apply(field, { + onRender: field.onRender.createSequence(function(ct, position) { + this.wrap = this.el.wrap({cls: "x-form-field-wrap"}); + if(this.fieldLabel && this.keyboardConfig.showIcon) { + var label = this.el.findParent('.x-form-element', 5, true) || this.el.findParent('.x-form-field-wrap', 5, true); + + this.virtualKeyboardIcon = label.createChild({ + cls:(this.keyboardIconCls || 'ux-virtualkeyboard-icon'), + style:'width:16px; height:18px; position:absolute; left:0; top:0; display:block; background:transparent no-repeat scroll 0 2px;' + }); + + this.alignKeyboardIcon = function(){ + var el = this.el; + this.virtualKeyboardIcon.alignTo(el, 'tl-tr', [2, 0]); + } + //Redefine alignErrorIcon to move the errorIcon (if it exist) to the right of helpIcon + if(this.alignErrorIcon) { + this.alignErrorIcon = function() { + this.errorIcon.alignTo(this.virtualKeyboardIcon, 'tl-tr', [2, 0]); + } + } + + this.on('resize', this.alignKeyboardIcon, this); + + this.virtualKeyboardIcon.on('click', function(e){ + if(this.disabled){ + return; + } + this.expandKeyboard(); + this.el.focus(); + }, this); + + } + }), //end of onRender + + initKeyboard: function(){ + var cls = 'x-keyboard-container'; + + this.keyboardContainer = new Ext.Layer({ + shadow: true, + cls: [cls, this.virtualKeyboardClass].join(' '), + constrain: false + }); + + this.keyboardContainer.setWidth(370); + + var config = this.keyboardConfig || {}; + config['showCloseButton'] = true; + config['keyboardTarget'] = this; + + this.keyboard = new Ext.ux.VirtualKeyboard(config); + this.keyboard.render(this.keyboardContainer); + + this.keyboard.getTBar().add('-', new Ext.Button({ + text: this.keyboardConfig.closeButtonText || 'Close', + listeners: { + 'click': this.collapseKeyboard, + scope: this + }, + scope: this + })); + }, + + collapseKeyboardIf : function(e){ + if( !e.within(this.wrap) && !e.within(this.keyboardContainer) && !this.keyboard.selectingLanguage ){ + this.collapseKeyboard(); + } + }, + + expandKeyboard : function(){ + if(this.isKeyboardExpanded() /*|| !this.hasFocus*/){ + return; + } + if(!this.keyboard) + this.initKeyboard(); + this.keyboardContainer.alignTo(this.wrap, this.keyboardAlign || 'tl-bl?'); + this.keyboardContainer.show(); + Ext.getDoc().on('mousewheel', this.collapseKeyboardIf, this); + Ext.getDoc().on('mousedown', this.collapseKeyboardIf, this); + this.virtualKeyboardPlugin.fireEvent('expand', this); + }, + + collapseKeyboard : function(){ + if(!this.isKeyboardExpanded()){ + return; + } + this.keyboardContainer.hide(); + Ext.getDoc().un('mousewheel', this.collapseKeyboardIf, this); + Ext.getDoc().un('mousedown', this.collapseKeyboardIf, this); + this.virtualKeyboardPlugin.fireEvent('collapse', this); + }, + + + isKeyboardExpanded : function(){ + return this.keyboardContainer && this.keyboardContainer.isVisible(); + } + + }); //end of Ext.apply + }, // end of function init + + destroy: function(component){ + if(component){ + if(component.keyboard){ + component.keyboard.keyboard.remove(); + delete component.keyboard; + } + if(component.keyboardContainer){ + component.keyboardContainer.remove(); + delete component.keyboardContainer; + } + } + }, + + expand: function(){ + if(this.activeTarget){ + this.activeTarget.expandKeyboard(); + } + } + +}); // end of extend + +Ext.ns('Ext.ux'); + +/** + * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3 + * + * Inspired from: HTML Virtual Keyboard Interface Script - v1.11 + * http://www.greywyvern.com/code/js/keyboard.html + * Copyright (c) 2008 - GreyWyvern + * Licenced for free distribution under the BSDL + * http://www.opensource.org/licenses/bsd-license.php + + * + * @author Edouard Fattal + * @url http://efattal.fr/extjs/examples/virtualkeyboard + */ + +/** + * @class Ext.ux.VirtualKeyboard + * @extends Ext.Component + */ +Ext.ux.VirtualKeyboard = Ext.extend(Ext.Component, { + /* ***** Create keyboards **************************************** */ + keyboardTarget: null, + languageSelection: false, + numpad: true, + keyCenter: 3, + layoutDDK: {}, + shift: false, + capslock: false, + alternate: false, + dead: false, + deadKeysOn: false, + Languages: { + Arabic: [ // Arabic Keyboard + [["\u0630", "\u0651 "], ["1", "!", "\u00a1", "\u00b9"], ["2", "@", "\u00b2"], ["3", "#", "\u00b3"], ["4", "$", "\u00a4", "\u00a3"], ["5", "%", "\u20ac"], ["6", "^", "\u00bc"], ["7", "&", "\u00bd"], ["8", "*", "\u00be"], ["9", "(", "\u2018"], ["0", ")", "\u2019"], ["-", "_", "\u00a5"], ["=", "+", "\u00d7", "\u00f7"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["\u0636", "\u064e"], ["\u0635", "\u064b"], ["\u062b", "\u064f"], ["\u0642", "\u064c"], ["\u0641", "\u0644"], ["\u063a", "\u0625"], ["\u0639", "\u2018"], ["\u0647", "\u00f7"], ["\u062e", "\u00d7"], ["\u062d", "\u061b"], ["\u062c", "\u003c"], ["\u062f", "\u003e"], ["\u005c", "\u007c"]], + [["Caps", "Caps"], ["\u0634", "\u0650"], ["\u0633", "\u064d"], ["\u064a", "\u005d"], ["\u0628", "\u005b"], ["\u0644", "\u0644"], ["\u0627", "\u0623"], ["\u062a", "\u0640"], ["\u0646", "\u060c"], ["\u0645", "\u002f"], ["\u0643", "\u003a"], ["\u0637", "\u0022"], ["Enter", "Enter"]], + [["Shift", "Shift"], ["\u0626", "\u007e"], ["\u0621", "\u0652"], ["\u0624", "\u007d"], ["\u0631", "\u007b"], ["\u0644", "\u0644"], ["\u0649", "\u0622"], ["\u0629", "\u2019"], ["\u0648", "\u002c"], ["\u0632", "\u002e"], ["\u0638", "\u061f"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["Alt", "Alt"]] + ], + Belgian: [ // Belgian Standard Keyboard + [["\u00b2", "\u00b3"], ["&", "1", "|"], ["\u00e9", "2", "@"], ['"', "3", "#"], ["'", "4"], ["(", "5"], ["\u00a7", "6", "^"], ["\u00e8", "7"], ["!", "8"], ["\u00e7", "9", "{"], ["\u00e0", "0", "}"], [")", "\u00b0"], ["-", "_"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["a", "A"], ["z", "Z"], ["e", "E", "\u20ac"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["\u005e", "\u00a8", "["], ["$", "*", "]"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["q", "Q"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], ["m", "M"], ["\u00f9", "%", "\u00b4"], ["\u03bc", "\u00a3", "`"]], + [["Shift", "Shift"], ["<", ">", "\\"], ["w", "W"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], [",", "?"], [";", "."], [":", "/"], ["=", "+", "~"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + /*Burmese: [ // Burmese Keyboard + [["\u1039`", "~"], ["\u1041", "\u100D"], ["\u1042", "\u100E"], ["\u1043", "\u100B"], ["\u1044", "\u1000\u103B\u1015\u103A"], ["\u1045", "%"], ["\u1046", "\u002F"], ["\u1047", "\u101B"], ["\u1048", "\u1002"], ["\u1049", "("], ["\u1040", ")"], ["-", "_"], ["=", "+"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["\u1006", "\u1029"], ["\u1010", "\u1040"], ["\u1014", "\u103F"], ["\u1019", "\u1023"], ["\u1021", "\u1024"], ["\u1015", "\u104C"], ["\u1000", "\u1009"], ["\u1004", "\u104D"], ["\u101E", "\u1025"], ["\u1005", "\u100F"], ["\u101F", "\u1027"], ["\u2018", "\u2019"], ["\u104F", "\u100B\u1039\u100C"]], + [["Caps", "Caps"], ["\u200B\u1031", "\u1017"], ["\u200B\u103B", "\u200B\u103E"], ["\u200B\u102D", "\u200B\u102E"], ["\u200B\u103A","\u1004\u103A\u1039\u200B"], ["\u200B\u102B", "\u200B\u103D"], ["\u200B\u1037", "\u200B\u1036"], ["\u200B\u103C", "\u200B\u1032"], ["\u200B\u102F", "\u200B\u102F"], ["\u200B\u1030", "\u200B\u1030"], ["\u200B\u1038", "\u200B\u102B\u103A"], ["\u1012", "\u1013"], ["Enter", "Enter"]], + [["Shift", "Shift"], ["\u1016", "\u1007"], ["\u1011", "\u100C"], ["\u1001", "\u1003"], ["\u101C", "\u1020"], ["\u1018", "\u1026"], ["\u100A", "\u1008"], ["\u200B\u102C", "\u102A"], ["\u101A", "\u101B"], ["\u002E", "\u101B"], ["\u104B", "\u104A"], ["Shift", "Shift"]], + [[" ", " "]] + ],*/ + Dutch: [ // Dutch Standard Keyboard + [["@", "\u00a7", "\u00ac"], ["1", "!", "\u00b9"], ["2", '"', "\u00b2"], ["3", "#", "\u00b3"], ["4", "$", "\u00bc"], ["5", "%", "\u00bd"], ["6", "&", "\u00be"], ["7", "_", "\u00a3"], ["8", "(", "{"], ["9", ")", "}"], ["0", "'"], ["/", "?", "\\"], ["\u00b0", "~", "\u00b8"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q"], ["w", "W"], ["e", "E", "\u20ac"], ["r", "R", "\u00b6"], ["t", "T"], ["y", "Y"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["\u00a8", "^"], ["*", "|"], ["<", ">"]], + [["Caps", "Caps"], ["a", "A"], ["s", "S", "\u00df"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], ["+", "\u00b1"], ["\u00b4", "\u0060"], ["Enter", "Enter"]], + [["Shift", "Shift"], ["]", "[", "\u00a6"], ["z", "Z", "\u00ab"], ["x", "X", "\u00bb"], ["c", "C", "\u00a2"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M", "\u00b5"], [",", ";"], [".", ":", "\u00b7"], ["-", "="], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + Dvorak: [ // Dvorak Keyboard + [["`", "~"], ["1", "!"], ["2", "@"], ["3", "#"], ["4", "$"], ["5", "%"], ["6", "^"], ["7", "&"], ["8", "*"], ["9", "("], ["0", ")"], ["[", "{"], ["]", "}"], ["Bksp", "Bksp"]], + [["Tab", "Tab"],["'", '"'], [",", "<"], [".", ">"], ["p", "P"], ["y", "Y"], ["f", "F"], ["g", "G"], ["c", "C"], ["r", "R"], ["l", "L"], ["/", "?"], ["=", "+"], ["\\", "|"]], + [["Caps", "Caps"], ["a", "A"], ["o", "O"], ["e", "E"], ["u", "U"], ["i", "I"], ["d", "D"], ["h", "H"], ["t", "T"], ["n", "N"], ["s", "S"], ["-", "_"], ["Enter", "Enter"]], + [["Shift", "Shift"], [";", ":"], ["q", "Q"], ["j", "J"], ["k", "K"], ["x", "X"], ["b", "B"], ["m", "M"], ["w", "W"], ["v", "V"], ["z", "Z"], ["Shift", "Shift"]], + [[" ", " "]] + ], + French: [ // French Standard Keyboard + [["\u00b2", "\u00b3"], ["&", "1"], ["\u00e9", "2", "~"], ['"', "3", "#"], ["'", "4", "{"], ["(", "5", "["], ["-", "6", "|"], ["\u00e8", "7", "\u0060"], ["_", "8", "\\"], ["\u00e7", "9", "\u005e"], ["\u00e0", "0", "\u0040"], [")", "\u00b0", "]"], ["=", "+", "}"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["a", "A"], ["z", "Z"], ["e", "E", "\u20ac"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["^", "\u00a8"], ["$", "\u00a3", "\u00a4"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["q", "Q"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], ["m", "M"], ["\u00f9", "%"], ["*", "\u03bc"]], + [["Shift", "Shift"], ["<", ">"], ["w", "W"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], [",", "?"], [";", "."], [":", "/"], ["!", "\u00a7"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + German: [ // German Standard Keyboard + [["\u005e", "\u00b0"], ["1", "!"], ["2", '"', "\u00b2"], ["3", "\u00a7", "\u00b3"], ["4", "$"], ["5", "%"], ["6", "&"], ["7", "/", "{"], ["8", "(", "["], ["9", ")", "]"], ["0", "=", "}"], ["\u00df", "?", "\\"], ["\u00b4", "\u0060"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q", "\u0040"], ["w", "W"], ["e", "E", "\u20ac"], ["r", "R"], ["t", "T"], ["z", "Z"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["\u00fc", "\u00dc"], ["+", "*", "~"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["a", "A"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], ["\u00f6", "\u00d6"], ["\u00e4", "\u00c4"], ["#", "'"]], + [["Shift", "Shift"], ["<", ">", "\u00a6"], ["y", "Y"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M", "\u00b5"], [",", ";"], [".", ":"], ["-", "_"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + Greek: [ // Greek Standard Keyboard + [["`", "~"], ["1", "!"], ["2", "@", "\u00b2"], ["3", "#", "\u00b3"], ["4", "$", "\u00a3"], ["5", "%", "\u00a7"], ["6", "^", "\u00b6"], ["7", "&"], ["8", "*", "\u00a4"], ["9", "(", "\u00a6"], ["0", ")", "\u00ba"], ["-", "_", "\u00b1"], ["=", "+", "\u00bd"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], [";", ":"], ["\u03c2", "^"], ["\u03b5", "\u0395"], ["\u03c1", "\u03a1"], ["\u03c4", "\u03a4"], ["\u03c5", "\u03a5"], ["\u03b8", "\u0398"], ["\u03b9", "\u0399"], ["\u03bf", "\u039f"], ["\u03c0", "\u03a0"], ["[", "{", "\u201c"], ["]", "}", "\u201d"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["\u03b1", "\u0391"], ["\u03c3", "\u03a3"], ["\u03b4", "\u0394"], ["\u03c6", "\u03a6"], ["\u03b3", "\u0393"], ["\u03b7", "\u0397"], ["\u03be", "\u039e"], ["\u03ba", "\u039a"], ["\u03bb", "\u039b"], ["\u0384", "\u00a8", "\u0385"], ["'", '"'], ["\\", "|", "\u00ac"]], + [["Shift", "Shift"], ["<", ">"], ["\u03b6", "\u0396"], ["\u03c7", "\u03a7"], ["\u03c8", "\u03a8"], ["\u03c9", "\u03a9"], ["\u03b2", "\u0392"], ["\u03bd", "\u039d"], ["\u03bc", "\u039c"], [",", "<"], [".", ">"], ["/", "?"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + Hebrew: [ // Hebrew Standard Keyboard + [["~", "`"], ["1", "!"], ["2", "@"], ["3", "#"], ["4" , "$", "\u20aa"], ["5" , "%"], ["6", "^"], ["7", "&"], ["8", "*"], ["9", ")"], ["0", "("], ["-", "_"], ["=", "+"], ["\\", "|"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["/", "Q"], ["'", "W"], ["\u05e7", "E", "\u20ac"], ["\u05e8", "R"], ["\u05d0", "T"], ["\u05d8", "Y"], ["\u05d5", "U", "\u05f0"], ["\u05df", "I"], ["\u05dd", "O"], ["\u05e4", "P"], ["]", "}"], ["[", "{"]], + [["Caps", "Caps"], ["\u05e9", "A"], ["\u05d3", "S"], ["\u05d2", "D"], ["\u05db", "F"], ["\u05e2", "G"], ["\u05d9", "H", "\u05f2"], ["\u05d7", "J", "\u05f1"], ["\u05dc", "K"], ["\u05da", "L"], ["\u05e3", ":"], ["," , '"'], ["Enter", "Enter"]], + [["Shift", "Shift"], ["\u05d6", "Z"], ["\u05e1", "X"], ["\u05d1", "C"], ["\u05d4", "V"], ["\u05e0", "B"], ["\u05de", "N"], ["\u05e6", "M"], ["\u05ea", ">"], ["\u05e5", "<"], [".", "?"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + Hungarian: [ // Hungarian Standard Keyboard + [["0", "\u00a7"], ["1", "'", "\u007e"], ["2", '"', "\u02c7"], ["3", "+", "\u02c6"], ["4", "!", "\u02d8"], ["5", "%", "\u00b0"], ["6", "/", "\u02db"], ["7", "=", "\u0060"], ["8", "(", "\u02d9"], ["9", ")", "\u00b4"], ["\u00f6", "\u00d6", "\u02dd"], ["\u00fc", "\u00dc", "\u00a8"], ["\u00f3", "\u00d3", "\u00b8"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q", "\u005c"], ["w", "W", "\u007c"], ["e", "E", "\u00c4"], ["r", "R"], ["t", "T"], ["z", "Z"], ["u", "U", "\u20ac"], ["i", "I", "\u00cd"], ["o", "O"], ["p", "P"], ["\u0151", "\u0150", "\u00f7"], ["\u00fa", "\u00da", "\u00d7"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["a", "A", "\u00e4"], ["s", "S","\u0111"], ["d", "D","\u0110"], ["f", "F","\u005b"], ["g", "G","\u005d"], ["h", "H"], ["j", "J","\u00ed"], ["k", "K","\u0141"], ["l", "L","\u0142"], ["\u00e9", "\u00c9","\u0024"], ["\u00e1", "\u00c1","\u00df"], ["\u0171", "\u0170","\u00a4"]], + [["Shift", "Shift"], ["\u00ed", "\u00cd","\u003c"], ["y", "Y","\u003e"], ["x", "X","\u0023"], ["c", "C","\u0026"], ["v", "V","\u0040"], ["b", "B","\u007b"], ["n", "N","\u007d"], ["m", "M","\u003c"], [",", "?","\u003b"], [".", ":","\u003e"], ["-", "_","\u002a"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + Italian: [ // Italian Standard Keyboard + [["\u005c", "\u007c"], ["1", "!"], ["2", '"'], ["3", "\u00a3"], ["4", "$", "\u20ac"], ["5", "%"], ["6", "&"], ["7", "/"], ["8", "("], ["9", ")"], ["0", "="], ["'", "?"], ["\u00ec", "\u005e"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q"], ["w", "W"], ["e", "E", "\u20ac"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["\u00e8", "\u00e9", "[", "{"], ["+", "*", "]", "}"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["a", "A"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], ["\u00f2", "\u00e7", "@"], ["\u00e0", "\u00b0", "#"], ["\u00f9", "\u00a7"]], + [["Shift", "Shift"], ["<", ">"], ["z", "Z"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M"], [",", ";"], [".", ":"], ["-", "_"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + Lithuanian: [ // Lithuanian Standard Keyboard + [["`", "~"], ["\u0105", "\u0104"], ["\u010D", "\u010C"], ["\u0119", "\u0118"], ["\u0117", "\u0116"], ["\u012F", "\u012E"], ["\u0161", "\u0160"], ["\u0173", "\u0172"], ["\u016B", "\u016A"], ["\u201E", "("], ["\u201C", ")"], ["-", "_"], ["\u017E", "\u017D"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q"], ["w", "W"], ["e", "E"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["[", "{"], ["]", "}"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["a", "A"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], [";", ":"], ["'", '"'], ["\\", "|"]], + [["Shift", "Shift"], ["\u2013", "\u20AC"], ["z", "Z"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M"], [",", "<"], [".", ">"], ["/", "?"], ["Shift", "Shift"]], + [[" ", " "]] + ], + Norwegian: [ // Norwegian Standard Keyboard + [["|", "\u00a7"], ["1", "!"], ["2", '"', "@"], ["3", "#", "\u00a3"], ["4", "\u00a4", "$"], ["5", "%"], ["6", "&"], ["7", "/", "{"], ["8", "(", "["], ["9", ")", "]"], ["0", "=", "}"], ["+", "?"], ["\\", "`", "\u00b4"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q"], ["w", "W"], ["e", "E", "\u20ac"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["\u00e5", "\u00c5"], ["\u00a8", "^", "~"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["a", "A"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], ["\u00f8", "\u00d8"], ["\u00e6", "\u00c6"], ["'", "*"]], + [["Shift", "Shift"], ["<", ">"], ["z", "Z"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M", "\u03bc", "\u039c"], [",", ";"], [".", ":"], ["-", "_"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + /*Numpad: [ // Number pad + [["$"], ["\u00a3"], ["\u20ac"], ["\u00a5"], ["/"], ["^"], ["Bksp", "Bksp"]], + [["."], ["7"], ["8"], ["9"], ["*"], ["<"], ["("], ["["]], + [["="], ["4"], ["5"], ["6"], ["-"], [">"], [")"], ["]"]], + [["0"], ["1"], ["2"], ["3"], ["+"], ["Enter", "Enter"]], + [[" "]] + ], + "Polish Prog": [ // Polish Programmers Keyboard + [["`", "~"], ["1", "!"], ["2", "@"], ["3", "#"], ["4", "$"], ["5", "%"], ["6", "^"], ["7", "&"], ["8", "*"], ["9", "("], ["0", ")"], ["-", "_"], ["=", "+"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q"], ["w", "W"], ["e", "E", "\u0119", "\u0118"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U"], ["i", "I"], ["o", "O", "\u00f3", "\u00d3"], ["p", "P"], ["[", "{"], ["]", "}"], ["\\", "|"]], + [["Caps", "Caps"], ["a", "A", "\u0105", "\u0104"], ["s", "S", "\u015b", "\u015a"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L", "\u0142", "\u0141"], [";", ":"], ["'", '"'], ["Enter", "Enter"]], + [["Shift", "Shift"], ["z", "Z", "\u017c", "\u017b"], ["x", "X", "\u017a", "\u0179"], ["c", "C", "\u0107", "\u0106"], ["v", "V"], ["b", "B"], ["n", "N", "\u0144", "\u0143"], ["m", "M"], [",", "<"], [".", ">"], ["/", "?"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["Alt", "Alt"]] + ],*/ + Portuguese: [ // Portuguese Standard Keyboard + [["`", "\u00ac", "\u00a6"], ["1", "!"], ["2", '"'], ["3", "\u00a3"], ["4", "$", "\u20ac"], ["5", "%"], ["6", "^"], ["7", "&"], ["8", "*"], ["9", "("], ["0", ")"], ["-", "_"], ["=", "+"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q"], ["w", "W"], ["e", "E", "\u00e9", "\u00c9"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U", "\u00fa", "\u00da"], ["i", "I", "\u00ed", "\u00cd"], ["o", "O", "\u00f3", "\u00d3"], ["p", "P"], ["[", "{"], ["]", "}"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["a", "A", "\u00e1", "\u00c1"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], ["\u00e7", "\u00c7"], [";", ":"], ["'", "@"], ["#", "~"]], + [["Shift", "Shift"], ["\\", "|"], ["z", "Z"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M"], [",", "<"], [".", ">"], ["/", "?"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + Russian: [ // Russian Standard Keyboard + [["\u0451", "\u0401"], ["1", "!"], ["2", '"'], ["3", "\u2116"], ["4", ";"], ["5", "%"], ["6", ":"], ["7", "?"], ["8", "*"], ["9", "("], ["0", ")"], ["-", "_"], ["=", "+"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["\u0439", "\u0419"], ["\u0446", "\u0426"], ["\u0443", "\u0423"], ["\u043A", "\u041A"], ["\u0435", "\u0415"], ["\u043D", "\u041D"], ["\u0433", "\u0413"], ["\u0448", "\u0428"], ["\u0449", "\u0429"], ["\u0437", "\u0417"], ["\u0445", "\u0425"], ["\u044A", "\u042A"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["\u0444", "\u0424"], ["\u044B", "\u042B"], ["\u0432", "\u0412"], ["\u0430", "\u0410"], ["\u043F", "\u041F"], ["\u0440", "\u0420"], ["\u043E", "\u041E"], ["\u043B", "\u041B"], ["\u0434", "\u0414"], ["\u0436", "\u0416"], ["\u044D", "\u042D"], ["\\", "/"]], + [["Shift", "Shift"], ["/", "|"], ["\u044F", "\u042F"], ["\u0447", "\u0427"], ["\u0441", "\u0421"], ["\u043C", "\u041C"], ["\u0438", "\u0418"], ["\u0442", "\u0422"], ["\u044C", "\u042C"], ["\u0431", "\u0411"], ["\u044E", "\u042E"], [".", ","], ["Shift", "Shift"]], + [[" ", " "]] + ], + Slovenian: [ // Slovenian Standard Keyboard + [["\u00a8", "\u00a8", "\u00b8"], ["1", "!", "~"], ["2", '"', "\u02c7"], ["3", "#", "^"], ["4", "$", "\u02d8"], ["5", "%", "\u00b0"], ["6", "&", "\u02db"], ["7", "/", "\u0060"], ["8", "(", "\u00B7"], ["9", ")", "\u00b4"], ["0", "=", "\u2033"], ["'", "?", "\u00a8"], ["+", "*", "\u00b8"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q", "\\"], ["w", "W","|"], ["e", "E", "\u20ac"], ["r", "R"], ["t", "T"], ["z", "Z"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["\u0161", "\u0160", "\u00f7"], ["\u0111", "\u0110", "\u00d7"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["a", "A"], ["s", "S"], ["d", "D"], ["f", "F", "["], ["g", "G", "]"], ["h", "H"], ["j", "J"], ["k", "K", "\u0142"], ["l", "L", "\u0141"], ["\u010D", "\u010C"], ["\u0107", "\u0106", "\u00df"], ["\u017E", "\u017D", "\u00a4"]], + [["Shift", "Shift"], ["<", ">"], ["y", "Y"], ["x", "X"], ["c", "C"], ["v", "V", "@"], ["b", "B", "{",], ["n", "N", "}"], ["m", "M", "\u00a7"], [",", ";"], [".", ":"], ["-", "_"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + "Spanish-SP": [ // Spanish (Spain) Standard Keyboard + [["\u00ba", "\u00aa", "\\"], ["1", "!", "|"], ["2", '"', "@"], ["3", "'", "#"], ["4", "$", "~"], ["5", "%", "\u20ac"], ["6", "&","\u00ac"], ["7", "/"], ["8", "("], ["9", ")"], ["0", "="], ["'", "?"], ["\u00a1", "\u00bf"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q"], ["w", "W"], ["e", "E"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["\u0060", "^", "["], ["\u002b", "\u002a", "]"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["a", "A"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], ["\u00f1", "\u00d1"], ["\u00b4", "\u00a8", "{"], ["\u00e7", "\u00c7", "}"]], + [["Shift", "Shift"], ["<", ">"], ["z", "Z"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M"], [",", ";"], [".", ":"], ["-", "_"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + "Turkish-F": [ // Turkish F Keyboard Layout + [['+', "*", "\u00ac"], ["1", "!", "\u00b9", "\u00a1"], ["2", '"', "\u00b2"], ["3", "^", "#", "\u00b3"], ["4", "$", "\u00bc", "\u00a4"], ["5", "%", "\u00bd"], ["6", "&", "\u00be"], ["7", "'", "{"], ["8", "(", '['], ["9", ")", ']'], ["0", "=", "}"], ["/", "?", "\\", "\u00bf"], ["-", "_", "|"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["f", "F", "@"], ["g", "G"], ["\u011f", "\u011e"], ["\u0131", "\u0049", "\u00b6", "\u00ae"], ["o", "O"], ["d", "D", "\u00a5"], ["r", "R"], ["n", "N"], ["h", "H", "\u00f8", "\u00d8"], ["p", "P", "\u00a3"], ["q", "Q", "\u00a8"], ["w", "W", "~"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["u", "U", "\u00e6", "\u00c6"], ["i", "\u0130", "\u00df", "\u00a7"], ["e", "E", "\u20ac"], ["a", "A", " ", "\u00aa"], ["\u00fc", "\u00dc"], ["t", "T"], ["k", "K"], ["m", "M"], ["l", "L"], ["y", "Y", "\u00b4"], ["\u015f", "\u015e"], ["x", "X", "`"]], + [["Shift", "Shift"], ["<", ">", "|", "\u00a6"], ["j", "J", "\u00ab", "<"], ["\u00f6", "\u00d6", "\u00bb", ">"], ["v", "V", "\u00a2", "\u00a9"], ["c", "C"], ["\u00e7", "\u00c7"], ["z", "Z"], ["s", "S", "\u00b5", "\u00ba"], ["b", "B", "\u00d7"], [".", ":", "\u00f7"], [",", ";", "-"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + "Turkish-Q": [ // Turkish Q Keyboard Layout + [['"', "\u00e9", "<"], ["1", "!", ">"], ["2", "'", "\u00a3"], ["3", "^", "#"], ["4", "+", "$"], ["5", "%", "\u00bd"], ["6", "&"], ["7", "/", "{"], ["8", "(", '['], ["9", ")", ']'], ["0", "=", "}"], ["*", "?", "\\"], ["-", "_", "|"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q", "@"], ["w", "W"], ["e", "E", "\u20ac"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U"], ["\u0131", "\u0049", "\u0069", "\u0130"], ["o", "O"], ["p", "P"], ["\u011f", "\u011e", "\u00a8"], ["\u00fc", "\u00dc", "~"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["a", "A", "\u00e6", "\u00c6"], ["s", "S", "\u00df"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], ["\u015f", "\u015e", "\u00b4"], ["\u0069", "\u0130"], [",", ";", "`"]], + [["Shift", "Shift"], ["<", ">", "|"], ["z", "Z"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M"], ["\u00f6", "\u00d6"], ["\u00e7", "\u00c7"], [".", ":"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + UK: [ // UK Standard Keyboard + [["`", "\u00ac", "\u00a6"], ["1", "!"], ["2", '"'], ["3", "\u00a3"], ["4", "$", "\u20ac"], ["5", "%"], ["6", "^"], ["7", "&"], ["8", "*"], ["9", "("], ["0", ")"], ["-", "_"], ["=", "+"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q"], ["w", "W"], ["e", "E", "\u00e9", "\u00c9"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U", "\u00fa", "\u00da"], ["i", "I", "\u00ed", "\u00cd"], ["o", "O", "\u00f3", "\u00d3"], ["p", "P"], ["[", "{"], ["]", "}"], ["Enter", "Enter"]], + [["Caps", "Caps"], ["a", "A", "\u00e1", "\u00c1"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], [";", ":"], ["'", "@"], ["#", "~"]], + [["Shift", "Shift"], ["\\", "|"], ["z", "Z"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M"], [",", "<"], [".", ">"], ["/", "?"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["AltGr", "AltGr"]] + ], + US: [ // US Standard Keyboard + [["`", "~"], ["1", "!"], ["2", "@"], ["3", "#"], ["4", "$"], ["5", "%"], ["6", "^"], ["7", "&"], ["8", "*"], ["9", "("], ["0", ")"], ["-", "_"], ["=", "+"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q"], ["w", "W"], ["e", "E"], ["r", "R"], ["t", "T"], ["y", "Y"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["[", "{"], ["]", "}"], ["\\", "|"]], + [["Caps", "Caps"], ["a", "A"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], [";", ":"], ["'", '"'], ["Enter", "Enter"]], + [["Shift", "Shift"], ["z", "Z"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M"], [",", "<"], [".", ">"], ["/", "?"], ["Shift", "Shift"]], + [[" ", " "]] + ], + "US Int'l": [ // US International Keyboard + [["`", "~"], ["1", "!", "\u00a1", "\u00b9"], ["2", "@", "\u00b2"], ["3", "#", "\u00b3"], ["4", "$", "\u00a4", "\u00a3"], ["5", "%", "\u20ac"], ["6", "^", "\u00bc"], ["7", "&", "\u00bd"], ["8", "*", "\u00be"], ["9", "(", "\u2018"], ["0", ")", "\u2019"], ["-", "_", "\u00a5"], ["=", "+", "\u00d7", "\u00f7"], ["Bksp", "Bksp"]], + [["Tab", "Tab"], ["q", "Q", "\u00e4", "\u00c4"], ["w", "W", "\u00e5", "\u00c5"], ["e", "E", "\u00e9", "\u00c9"], ["r", "R", "\u00ae"], ["t", "T", "\u00fe", "\u00de"], ["y", "Y", "\u00fc", "\u00dc"], ["u", "U", "\u00fa", "\u00da"], ["i", "I", "\u00ed", "\u00cd"], ["o", "O", "\u00f3", "\u00d3"], ["p", "P", "\u00f6", "\u00d6"], ["[", "{", "\u00ab"], ["]", "}", "\u00bb"], ["\\", "|", "\u00ac", "\u00a6"]], + [["Caps", "Caps"], ["a", "A", "\u00e1", "\u00c1"], ["s", "S", "\u00df", "\u00a7"], ["d", "D", "\u00f0", "\u00d0"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L", "\u00f8", "\u00d8"], [";", ":", "\u00b6", "\u00b0"], ["'", '"', "\u00b4", "\u00a8"], ["Enter", "Enter"]], + [["Shift", "Shift"], ["z", "Z", "\u00e6", "\u00c6"], ["x", "X"], ["c", "C", "\u00a9", "\u00a2"], ["v", "V"], ["b", "B"], ["n", "N", "\u00f1", "\u00d1"], ["m", "M", "\u00b5"], [",", "<", "\u00e7", "\u00c7"], [".", ">"], ["/", "?", "\u00bf"], ["Shift", "Shift"]], + [[" ", " ", " ", " "], ["Alt", "Alt"]] + ] + }, + deadKey: { + '"': [ // Umlaut / Diaeresis / Greek Dialytika + ["a", "\u00e4"], ["e", "\u00eb"], ["i", "\u00ef"], ["o", "\u00f6"], ["u", "\u00fc"], ["y", "\u00ff"], ["\u03b9", "\u03ca"], ["\u03c5", "\u03cb"], + ["A", "\u00c4"], ["E", "\u00cb"], ["I", "\u00cf"], ["O", "\u00d6"], ["U", "\u00dc"], ["Y", "\u0178"], ["\u0399", "\u03aa"], ["\u03a5", "\u03ab"] + ], + '\u00a8': [ // Umlaut / Diaeresis / Greek Dialytika + ["a", "\u00e4"], ["e", "\u00eb"], ["i", "\u00ef"], ["o", "\u00f6"], ["u", "\u00fc"], ["y", "\u00ff"], ["\u03b9", "\u03ca"], ["\u03c5", "\u03cb"], + ["A", "\u00c4"], ["E", "\u00cb"], ["I", "\u00cf"], ["O", "\u00d6"], ["U", "\u00dc"], ["Y", "\u0178"], ["\u0399", "\u03aa"], ["\u03a5", "\u03ab"] + ], + '~': [ // Tilde + ["a", "\u00e3"], ["o", "\u00f5"], ["n", "\u00f1"], + ["A", "\u00c3"], ["O", "\u00d5"], ["N", "\u00d1"] + ], + '^': [ // Circumflex + ["a", "\u00e2"], ["e", "\u00ea"], ["i", "\u00ee"], ["o", "\u00f4"], ["u", "\u00fb"], ["w", "\u0175"], ["y", "\u0177"], + ["A", "\u00c2"], ["E", "\u00ca"], ["I", "\u00ce"], ["O", "\u00d4"], ["U", "\u00db"], ["W", "\u0174"], ["Y", "\u0176"] + ], + '\u02c7': [ // Baltic caron + ["c", "\u010D"], ["s", "\u0161"], ["z", "\u017E"], ["r", "\u0159"], ["d", "\u010f"], ["t", "\u0165"], ["n", "\u0148"], ["l", "\u013e"], ["e", "\u011b"], + ["C", "\u010C"], ["S", "\u0160"], ["Z", "\u017D"], ["R", "\u0158"], ["D", "\u010e"], ["T", "\u0164"], ["N", "\u0147"], ["L", "\u013d"], ["E", "\u011a"] + ], + '\u02d8': [ // Romanian and Turkish breve + ["a", "\u0103"], ["g", "\u011f"], + ["A", "\u0102"], ["G", "\u011e"] + ], + '`': [ // Grave + ["a", "\u00e0"], ["e", "\u00e8"], ["i", "\u00ec"], ["o", "\u00f2"], ["u", "\u00f9"], + ["A", "\u00c0"], ["E", "\u00c8"], ["I", "\u00cc"], ["O", "\u00d2"], ["U", "\u00d9"] + ], + "'": [ // Acute / Greek Tonos + ["a", "\u00e1"], ["e", "\u00e9"], ["i", "\u00ed"], ["o", "\u00f3"], ["u", "\u00fa"], ["\u03b1", "\u03ac"], ["\u03b5", "\u03ad"], ["\u03b7", "\u03ae"], ["\u03b9", "\u03af"], ["\u03bf", "\u03cc"], ["\u03c5", "\u03cd"], ["\u03c9", "\u03ce"], + ["A", "\u00c1"], ["E", "\u00c9"], ["I", "\u00cd"], ["O", "\u00d3"], ["U", "\u00da"], ["\u0391", "\u0386"], ["\u0395", "\u0388"], ["\u0397", "\u0389"], ["\u0399", "\u038a"], ["\u039f", "\u038c"], ["\u03a5", "\u038e"], ["\u03a9", "\u038f"] + ], + '\u00b4':[ // Acute / Greek Tonos + ["a", "\u00e1"], ["e", "\u00e9"], ["i", "\u00ed"], ["o", "\u00f3"], ["u", "\u00fa"], ["\u03b1", "\u03ac"], ["\u03b5", "\u03ad"], ["\u03b7", "\u03ae"], ["\u03b9", "\u03af"], ["\u03bf", "\u03cc"], ["\u03c5", "\u03cd"], ["\u03c9", "\u03ce"], + ["A", "\u00c1"], ["E", "\u00c9"], ["I", "\u00cd"], ["O", "\u00d3"], ["U", "\u00da"], ["\u0391", "\u0386"], ["\u0395", "\u0388"], ["\u0397", "\u0389"], ["\u0399", "\u038a"], ["\u039f", "\u038c"], ["\u03a5", "\u038e"], ["\u03a9", "\u038f"] + ], + '\u0384': [ // Acute / Greek Tonos + ["a", "\u00e1"], ["e", "\u00e9"], ["i", "\u00ed"], ["o", "\u00f3"], ["u", "\u00fa"], ["\u03b1", "\u03ac"], ["\u03b5", "\u03ad"], ["\u03b7", "\u03ae"], ["\u03b9", "\u03af"], ["\u03bf", "\u03cc"], ["\u03c5", "\u03cd"], ["\u03c9", "\u03ce"], + ["A", "\u00c1"], ["E", "\u00c9"], ["I", "\u00cd"], ["O", "\u00d3"], ["U", "\u00da"], ["\u0391", "\u0386"], ["\u0395", "\u0388"], ["\u0397", "\u0389"], ["\u0399", "\u038a"], ["\u039f", "\u038c"], ["\u03a5", "\u038e"], ["\u03a9", "\u038f"] + ], + '\u02dd': [ // Hungarian Double Acute Accent + ["o", "\u0151"], ["u", "\u0171"], + ["O", "\u0150"], ["U", "\u0170"] + ], + '\u0385': [ // Greek Dialytika + Tonos + ["\u03b9", "\u0390"], ["\u03c5", "\u03b0"] + ], + '\u00b0': [ // Ring + ["a", "\u00e5"], + ["A", "\u00c5"] + ], + '\u00ba': [ // Ring + ["a", "\u00e5"], + ["A", "\u00c5"] + ] + }, + + initComponent : function(options){ + Ext.ux.VirtualKeyboard.superclass.initComponent.call(this); + + Ext.apply(this, { + language: this.language || 'US', + deadKeysButtonText: this.deadKeysButtonText || 'Type accented letters', + //deadKeysButtonTip: this.deadKeysButtonTip || 'Dead keys are used to generate accented letters', + autoDestroy: true + }); + + Ext.apply(this, options); + + this.addEvents( + 'keypress' + ); + + /*this.on('beforedestroy', function(){ + this.keyboard.removeAllListeners(); + delete this.keyboard; + }, this);*/ + + }, + + onRender: function(ct, position){ + this.initKeyboard(ct); + Ext.ux.VirtualKeyboard.superclass.onRender.call(this, ct, position); + }, + + initKeyboard : function(ct){ + this.keyboardTarget.el.on('click', this.IESel, this); + this.keyboardTarget.el.on('keyup', this.IESel, this); + this.keyboardTarget.el.on('select', this.IESel, this); + + this.keyboard = ct.createChild({ + tag: 'div', + cls: 'x-keyboard x-panel' + }); + + this.keyboard.setStyle({width: this.width || 370}); + + var layouts = 0; + for (lang in this.Languages) if (typeof this.Languages[lang] == "object") layouts++; + + var dh = Ext.DomHelper; + + var ktbarItems = []; + + if (this.languageSelection) { + + var values = []; + for (ktype in this.Languages) { + if (typeof this.Languages[ktype] == "object") { + values.push([ktype, ktype]); + } + } + + this.languageSelector = new Ext.form.ComboBox({ + store: values, + forceSelection: true, + triggerAction: 'all', + editable: false, + readOnly: true, + height: 15, + width: 100, + value: this.language, + listeners: { + 'select': function(combo, record) { + this.language = record.data.value; + this.buildKeys(); + }, + expand: function(){ + this.selectingLanguage = true; + }, + collapse: function(){ + this.selectingLanguage = false; + }, + scope: this + } + }); + ktbarItems.push(this.languageSelector); + } + else{ + ktbarItems.push(this.language); + } + ktbarItems.push('-', { + text: this.deadKeysButtonText, + iconCls: 'ux-accented-icon', + //tooltip: this.deadKeysButtonTip, + enableToggle: true, + listeners: { + toggle: function(btn, pressed){ + this.deadKeysOn = pressed; + this.keyModify(""); + }, + scope: this + } + }); + + this.ktbar = new Ext.Toolbar({ + renderTo: this.keyboard, + items: ktbarItems + }); + + if (!this.languageSelection){ + Ext.fly(this.ktbar.items.items[0].getEl()).setStyle({fontWeight: 'bold'}); + } + + var wrap = this.keyboard.createChild({ + tag: 'div', + cls: 'x-panel-bwrap' + }); + + var mc = wrap.createChild({ + tag: 'div', + cls: 'x-panel-body' + }); + + this.keysContainer = mc.createChild({ + tag: 'div' + }); + + this.buildKeys(); + }, + + getTBar: function(){ + return this.ktbar; + }, + + buildKeys: function() { + this.shift = this.capslock = this.alternate = this.dead = false; + //this.deadKeysOn = (this.layoutDDK[this.language]) ? false : this.deadCheckbox.checked; + + this.keysContainer.update(''); + + var dh = Ext.DomHelper; + + for (var x = 0, hasdeadKey = false, lyt; lyt = this.Languages[this.language][x++];) { + var table = dh.append(this.keysContainer, { + tag: 'table', + cellSpacing: '1', + cellPadding:'0', + border: '0', + cls: (lyt.length <= this.keyCenter ? 'keyboardInputCenter' : '') + ' keys', + align: (lyt.length <= this.keyCenter ? 'center' : ''), + html: '' + }); + + if(Ext.isIE && table.firstChild){ + table.removeChild(table.firstChild); + + } + var tbody = dh.append(table, {tag: 'tbody'}); + var tr = dh.append(tbody, {tag: 'tr'}); + + for (var y = 0, lkey; lkey = lyt[y++];) { + if (!this.layoutDDK[this.language] && !hasdeadKey){ + for (var z = 0; z < lkey.length; z++){ + if (this.deadKey[lkey[z]]){ + hasdeadKey = true; + break; + } + } + } + + var alive = false; + if (this.deadKeysOn){ + for (key in this.deadKey){ + if (key === lkey[0]){ + alive = true; + } + } + } + //var cls = ['x-btn', 'x-btn-center']; + var cls = []; + if(alive) + cls.push('alive'); + if (lyt.length > this.keyCenter && y == lyt.length) + cls.push('last'); + + var td = dh.append(tr, { + tag: 'td', + cls: cls.join(' '), + html: lkey[0] == " " ? " " : lkey[0] + }); + + if (lkey[0] == " ") + td.id = "spacebar"; + + Ext.fly(td).on('mouseover', function(event, target) { if (!Ext.fly(target).hasClass('dead') && target.firstChild.nodeValue != "\xa0") Ext.fly(target).addClass('hover'); }); + Ext.fly(td).on('mouseout', function(event, target) { if (!Ext.fly(target).hasClass('dead')) Ext.fly(target).removeClass(['hover', 'pressed']); }); + Ext.fly(td).on('mousedown', function(event, target) { if (!Ext.fly(target).hasClass('dead')) Ext.fly(target).addClass('pressed'); }); + Ext.fly(td).on('mouseup', function(event, target) { if (!Ext.fly(target).hasClass('dead') && target.firstChild.nodeValue != "\xa0") Ext.fly(target).removeClass('pressed'); }); + Ext.fly(td). swallowEvent('dblclick', true); + + td.modifier = lkey[1]; + + switch (lkey[1]) { + case "Caps": + case "Shift": + case "Alt": + case "AltGr": + Ext.fly(td).on('click', function(event, target) { + this.keyModify(target.modifier); + }, this, {stopEvent: true}); + break; + case "Tab": + Ext.fly(td).on('click', function(event, target) { + this.keyInsert("\t"); + }, this, {stopEvent: true}); + break; + case "Bksp": + Ext.fly(td).on('click', function() { + this.keyboardTarget.focus(); + var dom = this.keyboardTarget.el.dom; + if (dom.setSelectionRange) { + var srt = dom.selectionStart; + var len = dom.selectionEnd; + if (srt < len) srt++; + this.keyboardTarget.setValue(dom.value.substr(0, srt - 1) + dom.value.substr(len)); + dom.setSelectionRange(srt - 1, srt - 1); + } else if (dom.createTextRange) { + try { this.range.select(); } catch(e) {} + this.range = document.selection.createRange(); + if (!this.range.text.length) + this.range.moveStart('character', -1); + this.range.text = ""; + } else this.keyboardTarget.setValue(dom.value.substr(0, dom.value.length - 1)); + if (this.shift) this.keyModify("Shift"); + if (this.alternate) this.keyModify("AltGr"); + return true; + }, this); + break; + + case "Enter": + //if (self.keyboardTarget.nodeName == "TEXTAREA") { this.keyInsert("\n"); } else self.VKI_close(); + Ext.fly(td).on('click', function(event, target) { + this.keyInsert("\n"); + }, this, {stopEvent: true}); + break; + + default: + Ext.fly(td).on('click', function(event, target) { + var keyValue = target.firstChild.nodeValue; + if(keyValue == "\xa0"){ + keyValue = " "; + } + if (this.deadKeysOn && this.dead) { + if (this.dead != keyValue) { + for (key in this.deadKey) { + if (key == this.dead) { + if (keyValue != " ") { + for (var z = 0, rezzed = false, dk; dk = this.deadKey[key][z++];) { + if (dk[0] == keyValue) { + this.keyInsert(dk[1]); + rezzed = true; + break; + } + } + } else { + this.keyInsert(this.dead); + rezzed = true; + } + break; + } + } + } else rezzed = true; + } + this.dead = false; + + if (!rezzed && keyValue != "\xa0") { + if (this.deadKeysOn) { + for (key in this.deadKey) { + if (key == keyValue) { + this.dead = key; + this.className = "dead"; + if (this.shift) this.keyModify("Shift"); + if (this.alternate) this.keyModify("AltGr"); + break; + } + } + if (!this.dead) this.keyInsert(keyValue); + } else this.keyInsert(keyValue); + } + this.keyModify(""); + }, this, {stopEvent: true}); + delete td; + } + delete tr + for (var z = lkey.length; z < 4; z++) lkey[z] = "\xa0"; + } + delete table; + delete tbody; + } + }, + + keyModify: function(type){ + switch (type) { + case "Alt": + case "AltGr": this.alternate = !this.alternate; break; + case "Caps": this.capslock = !this.capslock; break; + case "Shift": this.shift = !this.shift; break; + } + var vchar = 0; + if (!this.shift != !this.capslock) vchar += 1; + + var tables = this.keyboard.select('table.keys'); + for (var x = 0; x < tables.getCount(); x++) { + var tds = tables.item(x).select('td'); + for (var y = 0; y < tds.getCount(); y++) { + td = tds.item(y); + var dead = alive = isTarget = false; + if(!this.Languages[this.language][x]){ + //alert('stop'); + } + + var lkey = this.Languages[this.language][x][y]; + + switch (lkey[1]) { + case "Alt": + case "AltGr": + if (this.alternate) dead = true; + break; + case "Shift": + if (this.shift) dead = true; + break; + case "Caps": + if (this.capslock) dead = true; + break; + case "Tab": + case "Enter": + case "Bksp": + break; + default: + var char = lkey[vchar + ((this.alternate && lkey.length == 4) ? 2 : 0)]; + if (type) + td.update(char); + if (this.deadKeysOn) { + if (this.dead) { + if (char == this.dead) + dead = true; + for (var z = 0; z < this.deadKey[this.dead].length; z++){ + if (char == this.deadKey[this.dead][z][0]) { + isTarget = true; + break; + } + } + } + for (key in this.deadKey){ + if (key === char) { + alive = true; + break; + } + } + } + } + td.dom.className = (dead) ? "dead" : ((isTarget) ? "target" : ((alive) ? "alive" : "")); + if (y == tds.getCount() - 1 && tds.getCount() > this.keyCenter) td.addClass('last'); + } + } + }, + + keyInsert: function(keyValue){ + this.fireEvent('keyPress', this, keyValue); + + if(this.keyboardTarget){ + this.keyboardTarget.focus(); + var dom = this.keyboardTarget.el.dom; + if (dom.setSelectionRange) { + var srt = dom.selectionStart; + var len = dom.selectionEnd; + dom.value = dom.value.substr(0, srt) + keyValue + dom.value.substr(len); + if (keyValue == "\n" && window.opera) srt++; + dom.setSelectionRange(srt + keyValue.length, srt + keyValue.length); + } else if (dom.createTextRange) { + try { this.range.select(); } catch(e) {} + this.range = document.selection.createRange(); + this.range.text = keyValue; + this.range.collapse(true); + this.range.select(); + } else + this.keyboardTarget.setValue(this.keyboardTarget.getValue() + keyValue); + if (this.shift) this.keyModify("Shift"); + if (this.alternate) this.keyModify("AltGr"); + this.keyboardTarget.focus(); + } + }, + + getXType: function(){ + return 'virtualkeyboard'; + }, + + IESel: function (event, target){ + if (target.createTextRange){ + this.range = document.selection.createRange(); + } + } +}); + +Ext.reg('virtualkeyboard', Ext.ux.VirtualKeyboard); diff --git a/gulliver/system/class.g.php b/gulliver/system/class.g.php index 90c27c023..76c51c0de 100755 --- a/gulliver/system/class.g.php +++ b/gulliver/system/class.g.php @@ -169,7 +169,7 @@ class G * @access public * @return string */ - /*public static*/ function &getVersion( ) + function &getVersion( ) { //majorVersion.minorVersion-SvnRevision return '3.0-1'; @@ -179,7 +179,7 @@ class G * getIpAddress * @return string $ip */ - /*public static*/ function getIpAddress () + function getIpAddress () { if (getenv('HTTP_CLIENT_IP')) { $ip = getenv('HTTP_CLIENT_IP'); @@ -532,7 +532,7 @@ class G /*************** path functions *****************/ - /*public static*/ function mk_dir( $strPath, $rights = 0777) + function mk_dir( $strPath, $rights = 0777) { $folder_path = array($strPath); $oldumask = umask(0); @@ -588,7 +588,7 @@ class G * @param boolean $createPath if true this function will create the path * @return boolean */ - /*public static*/ function verifyPath( $strPath , $createPath = false ) + function verifyPath( $strPath , $createPath = false ) { $folder_path = strstr($strPath, '.') ? dirname($strPath) : $strPath; @@ -910,15 +910,15 @@ class G * @param string $urlLink * @return string */ - function parseURI( $uri ) + function parseURI($uri, $config = array()) { $aRequestUri = explode('/', $uri ); + if ( substr ( $aRequestUri[1], 0, 3 ) == 'sys' ) { define( 'SYS_TEMP', substr ( $aRequestUri[1], 3 ) ); } else { define("ENABLE_ENCRYPT", 'yes' ); - define( 'SYS_TEMP', $aRequestUri[1] ); $plain = '/sys' . SYS_TEMP; @@ -962,8 +962,9 @@ class G unset($toparse); array_shift($URI_VARS); - define("SYS_LANG", array_shift($URI_VARS)); - define("SYS_SKIN", array_shift($URI_VARS)); + + $SYS_LANG = array_shift($URI_VARS); + $SYS_SKIN = array_shift($URI_VARS); $SYS_COLLECTION = array_shift($URI_VARS); $SYS_TARGET = array_shift($URI_VARS); @@ -973,8 +974,26 @@ class G while ( count ( $URI_VARS ) > 0 && $exit == 0) { $SYS_TARGET .= '/' . array_shift($URI_VARS); } - define('SYS_COLLECTION', $SYS_COLLECTION ); - define('SYS_TARGET', $SYS_TARGET ); + + // if ($SYS_TARGET == 'login') { + // if (isset($config['default_lang']) && !empty($config['default_lang'])) { + // $SYS_LANG = $config['default_lang']; + // } + + // if (isset($config['default_skin']) && !empty($config['default_skin'])) { + // $SYS_SKIN = $config['default_skin']; + // } + // } + + /* Fix to prevent use uxs skin outside siplified interface, because that skin is not compatible with others interfaces*/ + if ($SYS_SKIN == 'uxs' && $SYS_COLLECTION !== 'home') { + $SYS_SKIN = 'classic'; + } + + define("SYS_LANG", $SYS_LANG); + define("SYS_SKIN", $SYS_SKIN); + define('SYS_COLLECTION', $SYS_COLLECTION); + define('SYS_TARGET', $SYS_TARGET); if ( $SYS_COLLECTION == 'js2' ) { print "ERROR"; die; @@ -1148,6 +1167,14 @@ $output = $outputHeader.$output; { header('Content-Type: text/javascript'); + if (!G::LoadTranslationObject($locale)) { + header('Cache-Control: no-cache'); + header('Pragma: no-cache'); + return; + } + + global $translation; + //if userAgent (BROWSER) is MSIE we need special headers to avoid MSIE behaivor. $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']); if ( file_exists($filename) ) @@ -1174,11 +1201,7 @@ $output = $outputHeader.$output; } } - $output = ''; - G::LoadTranslationObject($locale); - global $translation; - $output .= JSMin::minify ( 'var TRANSLATIONS = ' . G::json_encode($translation) . ';' ); - return $output; + return JSMin::minify ( 'var TRANSLATIONS = ' . G::json_encode($translation) . ';' ); } /** @@ -1283,8 +1306,7 @@ $output = $outputHeader.$output; $checksum = G::getCheckSum(array( $pathJs . 'ext/wz_jsgraphics.js', $pathJs . 'ext/mootools.js', - $pathJs . 'ext/moocanvas.js', - $pathJs . 'ext/pmos-common.js' + $pathJs . 'ext/moocanvas.js' )); $cf = $cachePath . "ext-draw2d-cache.$checksum.js"; @@ -1300,7 +1322,6 @@ $output = $outputHeader.$output; $output .= JSMin::minify ( file_get_contents ( $pathJs . 'ext/mootools.js' ) ); $output .= JSMin::minify ( file_get_contents ( $pathJs . 'ext/moocanvas.js' ) ); $output .= file_get_contents ($pathJs . 'ext/draw2d.js'); //already minified - $output .= JSMin::minify ( file_get_contents ( $pathJs . 'ext/pmos-common.js' ) ); file_put_contents($cf, $output); //error_log("draw2d.js writting ".$cf); } @@ -1308,6 +1329,7 @@ $output = $outputHeader.$output; case 'ext-all.js' : $cachePath = PATH_C . 'ExtJs' . PATH_SEP; $checksum = G::getCheckSum(array( + $pathJs . 'ext/pmos-common.js', $pathJs . 'ext/ux/miframe.js', $pathJs . 'ext/ux.locationbar/Ext.ux.LocationBar.js', $pathJs . 'ext/ux.statusbar/ext-statusbar.js', @@ -1324,6 +1346,7 @@ $output = $outputHeader.$output; $output .= file_get_contents ( $pathJs . 'ext/ext-all.js' ); //already minified $output .= file_get_contents ( $pathJs . 'ext/ux/ux-all.js' ); //already minified + $output .= JSMin::minify ( file_get_contents ( $pathJs . 'ext/pmos-common.js' ) ); $output .= JSMin::minify ( file_get_contents ( $pathJs . 'ext/ux/miframe.js' ) ); $output .= JSMin::minify ( file_get_contents ( $pathJs . 'ext/ux.locationbar/Ext.ux.LocationBar.js' ) ); $output .= JSMin::minify ( file_get_contents ( $pathJs . 'ext/ux.statusbar/ext-statusbar.js' ) ); @@ -2192,6 +2215,8 @@ $output = $outputHeader.$output; $translation = $foreignTranslations; else $translation = array_merge($defaultTranslations, $foreignTranslations); + + return true; } /** @@ -3274,6 +3299,138 @@ $output = $outputHeader.$output; } return false; } + + /** + * Send a mail using phpmailer + * this method use the global smtp server connection stored on Configuration table + * this information is retrieved by the PMFunction getEmailConfiguration() + * + * @author Erik Amaru Ortiz + * @param string $from address that is sending the email + * @param string $fromName name of sender + * @param mixed $address the possibles values are: + * string + * array('email1', 'some name ') + * array('to'=>array('email1', 'some name '), 'cc'=>array(...), 'bcc'=>array(...)) + * @param string $subject contains the email subject + * @param string $body contains the email body (text plain or html) + * @return mixed boolean or string : if the email was sent successfully returns true, otherwise returns a string within error message + */ + function sendMail($from, $fromName, $address, $subject, $body) + { + // require_once "classes/class.pmFunctions.php"; + G::LoadClass("pmFunctions"); + G::LoadThirdParty('phpmailer', 'class.phpmailer'); + $setup = getEmailConfiguration(); + + if (count($setup) == 0 || !isset($setup['MESS_ENGINE']) || !isset($setup['MESS_SERVER']) + || !isset($setup['MESS_ENABLED']) || !isset($setup['MESS_RAUTH']) || $setup['MESS_SERVER'] == '') { + return G::LoadTranslation('ID_EMAIL_ENGINE_IS_NOT_CONFIGURED'); + } + + if (!$setup['MESS_ENABLED']) { + return G::LoadTranslation('ID_EMAIL_ENGINE_IS_NOT_ENABLED'); + } + + $mail = new PHPMailer(true); + $mail->From = $from != '' && $from ? $from : $setup['MESS_ACCOUNT']; + $mail->FromName = $fromName; + $mail->Subject = $subject; + $mail->Body = $body; + $mail->IsHTML (true); + $mail->IsSMTP(); + $mail->Host = $setup['MESS_SERVER']; + $mail->Port = $setup['MESS_PORT']; + $mail->SMTPAuth = isset($setup['MESS_RAUTH']) && $setup['MESS_RAUTH'] ? true : false; + $mail->Username = $setup['MESS_ACCOUNT']; + $mail->Password = $setup['MESS_PASSWORD']; + $mail->SMTPSecure = $setup['SMTPSecure']; + + $emailAddressList = G::envelopEmailAddresses($address); + + foreach ($emailAddressList['to'] as $emails) { + $mail->AddAddress($emails[0], $emails[1]); + } + foreach ($emailAddressList['cc'] as $emails) { + $mail->AddCC($emails[0], $emails[1]); + } + foreach ($emailAddressList['bcc'] as $emails) { + $mail->AddBCC($emails[0], $emails[1]); + } + + return $mail->Send() ? true : $mail->ErrorInfo; + } + + /** + * Envelope a emails collection from a string or array + * @author Erik Amaru Ortiz + * @param mixed $address the possibles values are: + * string + * array('email1', 'some name ') + * array('to'=>array('email1', 'some name '), 'cc'=>array(...), 'bcc'=>array(...)) + * @return array contains: + * array( + * 'to' => array('email@host.com', 'some name or empty string', array('email@host.com', '..'), ...), + * 'cc' => array('email@host.com', 'some name or empty string', ...), + * 'bcc' => array('email@host.com', 'some name or empty string', ...) + * ) + */ + function envelopEmailAddresses($address) + { + $emailAddressList = array(); + $emailAddressList['to'] = array(); + $emailAddressList['cc'] = array(); + $emailAddressList['bcc'] = array(); + $ereg = '/([\"\w\W\s]*\s*)?(<([\w\-\.]+@[\.-\w]+\.\w{2,3})+>)/'; + + if (!is_array($address)) { + if (preg_match($ereg, $address, $match)) + $emailAddressList['to'][] = array($match[3], $match[1]); + else + $emailAddressList['to'][] = array($address, ''); + } + else { + foreach ($address as $type => $emails) { + if (!is_array($emails)) { + if (preg_match($ereg, $emails, $match)) + $emailAddressList['to'][] = array($match[3], $match[1]); + else + $emailAddressList['to'][] = array($emails, ''); + } + else { + switch ($type) { + case 'cc': + foreach ($emails as $email) { + if (preg_match($ereg, $email, $match)) + $emailAddressList['cc'][] = array($match[3], $match[1]); + else + $emailAddressList['cc'][] = array($email, ''); + } + break; + case 'bcc': + foreach ($emails as $email) { + if (preg_match($ereg, $email, $match)) + $emailAddressList['bcc'][] = array($match[3], $match[1]); + else + $emailAddressList['bcc'][] = array($email, ''); + } + break; + case 'to': + default: + foreach ($emails as $email) { + if (preg_match($ereg, $email, $match)) + $emailAddressList['to'][] = array($match[3], $match[1]); + else + $emailAddressList['to'][] = array($email, ''); + } + break; + } + } + } + } + + return $emailAddressList; + } /** * Get the type of a variable @@ -4705,6 +4862,74 @@ function getDirectorySize($path,$maxmtime=0) $contents = file_put_contents($inifile, sprintf("\n%s = $s\n", $variable, $value)); } } + + function write_php_ini($file, $array) + { + $res = array(); + foreach($array as $key => $val) + { + if(is_array($val)) + { + $res[] = "[$key]"; + foreach($val as $skey => $sval) $res[] = "$skey = ".(is_numeric($sval) ? $sval : '"'.$sval.'"'); + } + else $res[] = "$key = ".(is_numeric($val) ? $val : '"'.$val.'"'); + } + file_put_contents($file, implode("\r\n", $res)); + } + + + /** + * Update a ini file passing a array values, this finction don not remove the original commets + * @author Erik Amaru Ortiz + */ + function update_php_ini($file, $array) + { + $iniLines = array(); + $iniContent = array(); + + if (file_exists($file)) { + $iniContent = file($file); + } + + foreach ($iniContent as $line) { + $line = trim($line); + $lineParts = explode(';', $line); + $setting = parse_ini_string($lineParts[0]); + + if (is_array($setting) && count($setting) > 0) { + list($key, ) = array_keys($setting); + + if (isset($array[$key])) { + $value = $array[$key]; + $line = "$key = ".(is_numeric($value) ? $value : '"'.$value.'"'); + $line .= isset($lineParts[1]) ? ' ;' . $lineParts[1] : ''; + unset($array[$key]); + + $lastComment = array_pop($iniLines); + if (strpos($lastComment, "Setting $key") === false) { + $iniLines[] = $lastComment; + } + + $iniLines[] = ";Setting $key - Updated by System on " . date('D d M, Y H:i:s'); + } + } + $iniLines[] = $line; + } + + // inserting new values + foreach ($array as $key => $value) { + $line = "$key = ".(is_numeric($value) ? $value : '"'.$value.'"'); + $iniLines[] = ''; + $iniLines[] = ";Setting $key - Created by System on " . date('D d M, Y H:i:s'); + $iniLines[] = $line; + } + + $content = implode("\r\n", $iniLines); + + file_put_contents($file, $content); + } + }; /** diff --git a/gulliver/system/class.headPublisher.php b/gulliver/system/class.headPublisher.php index 8a55de51e..4c4295330 100755 --- a/gulliver/system/class.headPublisher.php +++ b/gulliver/system/class.headPublisher.php @@ -57,6 +57,9 @@ class headPublisher { /* variable array, to store the variables generated in PHP, and used in JavaScript */ var $vars = array (); + /* tplVariable array, to store the variables for template power */ + var $tplVariable = array (); + var $leimnudInitString = ' var leimnud = new maborak(); leimnud.make({ zip:true, @@ -280,23 +283,11 @@ class headPublisher { $head = ''; $head .= " \n"; $head .= " \n"; -// $head .= " \n"; -/* - if (isset ( $this->extJsLibrary ) && is_array ( $this->extJsLibrary )) { - foreach ( $this->extJsLibrary as $file ) { - $head .= " \n"; - } - } - */ + + // enabled for particular use + $head .= $this->getExtJsLibraries(); + $head .= " \n"; - - //$head .= " \n"; - //$head .= " \n"; - //$head .= " \n"; - //$head .= " \n"; - //$head .= " \n"; - //$head .= " \n"; - $head .= " \n"; if (! isset ( $this->extJsSkin ) || $this->extJsSkin == '') { @@ -376,6 +367,17 @@ class headPublisher { return $script; } + function getExtJsLibraries() + { + $script = ''; + if (isset ( $this->extJsLibrary ) && is_array ( $this->extJsLibrary )) { + foreach ( $this->extJsLibrary as $file ) { + $script .= " \n"; + } + } + return $script; + } + /** * add a ExtJS extended library * @@ -468,8 +470,13 @@ class headPublisher { $this->extJsScript [] = '/extjs/' . $cacheName; //hook for registered javascripts from plugins - $oPluginRegistry = & PMPluginRegistry::getSingleton(); - $pluginJavascripts = $oPluginRegistry->getRegisteredJavascriptBy($filename); + if ( class_exists( 'PMPluginRegistry' ) ) { + $oPluginRegistry = & PMPluginRegistry::getSingleton(); + $pluginJavascripts = $oPluginRegistry->getRegisteredJavascriptBy($filename); + } + else + $pluginJavascripts = array(); + if (count($pluginJavascripts) > 0) { if ($debug) { foreach ($pluginJavascripts as $pluginJsFile) { @@ -599,6 +606,11 @@ class headPublisher { $template = new TemplatePower ( $sPath . $file . '.html' ); $template->prepare (); + + foreach ($this->getVars() as $k => $v) { + $template->assign($k, $v); + } + $body .= $template->getOutputContent (); } } diff --git a/gulliver/system/class.rbac.php b/gulliver/system/class.rbac.php index 29a25eed0..9f39409ec 100755 --- a/gulliver/system/class.rbac.php +++ b/gulliver/system/class.rbac.php @@ -155,29 +155,20 @@ class RBAC * @param string $sUser the user * @return $this->aUserInfo[ $sSystem ] */ - function loadUserRolePermission( $sSystem, $sUser, $pathData = null, $sid =null ) { - //if we provide a path data and session we will cache the session Info for this user - if ( $pathData != null && $sid != null ) { - $pathData = $pathData . 'session' . PATH_SEP; - $filePath = $pathData . $sid . '.rbac'; - if ( file_exists ( $filePath ) && filesize($filePath) > 0 ) { - $this->aUserInfo = unserialize( file_get_contents ( $filePath ) ); - return; - } - } + function loadUserRolePermission( $sSystem, $sUser ) { + //in previous versions we provided a path data and session we will cache the session Info for this user + //now this is deprecated, and all the aUserInfo is in the memcache + $this->sSystem = $sSystem; $fieldsSystem = $this->systemObj->loadByCode($sSystem); - $fieldsRoles = $this->usersRolesObj->getRolesBySystem ($fieldsSystem['SYS_UID'], $sUser ); + $fieldsRoles = $this->usersRolesObj->getRolesBySystem ($fieldsSystem['SYS_UID'], $sUser ); $fieldsPermissions = $this->usersRolesObj->getAllPermissions ($fieldsRoles['ROL_UID'], $sUser ); + $this->aUserInfo['USER_INFO'] = $this->userObj->load( $sUser); $this->aUserInfo[ $sSystem ]['SYS_UID'] = $fieldsSystem['SYS_UID']; - $this->aUserInfo[ $sSystem ]['ROLE'] = $fieldsRoles; + $this->aUserInfo[ $sSystem ]['ROLE'] = $fieldsRoles; $this->aUserInfo[ $sSystem ]['PERMISSIONS'] = $fieldsPermissions; - - if ( $pathData != null && $sid != null ) { - G::mk_dir ( $pathData ); - file_put_contents( $filePath, serialize ( $this->aUserInfo ) ); - } } + /** * verification the register automatic * @@ -308,6 +299,7 @@ class RBAC return $res; } else { + $this->userObj->reuseUserFields = true; $res = $this->userObj->VerifyLogin($strUser, $strPass); return $res; } diff --git a/workflow/engine/classes/class.processMap.php b/workflow/engine/classes/class.processMap.php index 4058a4e07..b37c09387 100755 --- a/workflow/engine/classes/class.processMap.php +++ b/workflow/engine/classes/class.processMap.php @@ -3133,6 +3133,7 @@ class processMap { */ function listNoProcessesUser($sProcessUID) { G::LoadSystem('rbac'); + $memcache = & PMmemcached::getSingleton(SYS_SYS); $oCriteria = new Criteria('workflow'); $oCriteria->addSelectColumn(ProcessUserPeer::USR_UID); @@ -3156,7 +3157,11 @@ class processMap { $aUIDS = array(); $oRBAC = RBAC::getSingleton (); while ($aRow = $oDataset->getRow()) { - $oRBAC->loadUserRolePermission($oRBAC->sSystem, $aRow ['USR_UID']); + $memKey = 'rbacSession' . session_id(); + if ( ($oRBAC->aUserInfo = $memcache->get($memKey)) === false ) { + $oRBAC->loadUserRolePermission($oRBAC->sSystem, $aRow ['USR_UID']); + $memcache->set( $memKey, $oRBAC->aUserInfo, PMmemcached::EIGHT_HOURS ); + } $aPermissions = $oRBAC->aUserInfo [$oRBAC->sSystem] ['PERMISSIONS']; $bInclude = false; foreach ($aPermissions as $aPermission) { @@ -5730,6 +5735,7 @@ class processMap { */ function listExtNoProcessesUser($sProcessUID) { G::LoadSystem('rbac'); + $memcache = & PMmemcached::getSingleton(SYS_SYS); $oCriteria = new Criteria('workflow'); $oCriteria->addSelectColumn(ProcessUserPeer::USR_UID); @@ -5753,7 +5759,11 @@ class processMap { $aUIDS = array(); $oRBAC = RBAC::getSingleton (); while ($aRow = $oDataset->getRow()) { - $oRBAC->loadUserRolePermission($oRBAC->sSystem, $aRow ['USR_UID']); + $memKey = 'rbacSession' . session_id(); + if ( ($oRBAC->aUserInfo = $memcache->get($memKey)) === false ) { + $oRBAC->loadUserRolePermission($oRBAC->sSystem, $aRow ['USR_UID']); + $memcache->set( $memKey, $oRBAC->aUserInfo, PMmemcached::EIGHT_HOURS ); + } $aPermissions = $oRBAC->aUserInfo [$oRBAC->sSystem] ['PERMISSIONS']; $bInclude = false; foreach ($aPermissions as $aPermission) { diff --git a/workflow/engine/classes/class.system.php b/workflow/engine/classes/class.system.php index 6e568eed0..2f95a1d3b 100755 --- a/workflow/engine/classes/class.system.php +++ b/workflow/engine/classes/class.system.php @@ -922,5 +922,134 @@ class System { return $config; } + + function getSkingList() + { + //Create Skins custom folder if it doesn't exists + if(!is_dir(PATH_CUSTOM_SKINS)){ + G::verifyPath(PATH_CUSTOM_SKINS, true); + } + + //Get Skin Config files + $skinListArray = array(); + $customSkins = glob(PATH_CUSTOM_SKINS . "*/config.xml"); + $configurationFile = G::ExpandPath("skinEngine") . 'base' . PATH_SEP . 'config.xml'; + array_unshift($customSkins, $configurationFile); + + //Read and parse each Configuration File + foreach ($customSkins as $key => $configInformation) { + $folderId = str_replace(G::ExpandPath("skinEngine") . 'base', "", str_replace(PATH_CUSTOM_SKINS, "", str_replace("/config.xml", "", $configInformation))); + + if ($folderId == "") { + $folderId = "classic"; + } + + $xmlConfiguration = file_get_contents($configInformation); + $xmlConfigurationObj = G::xmlParser($xmlConfiguration); + + if (isset($xmlConfigurationObj->result['skinConfiguration'])) { + $skinInformationArray = $skinFilesArray = $xmlConfigurationObj->result['skinConfiguration']['__CONTENT__']['information']['__CONTENT__']; + $res = array(); + $res['SKIN_FOLDER_ID'] = strtolower($folderId); + + foreach ($skinInformationArray as $keyInfo => $infoValue) { + $res['SKIN_' . strtoupper($keyInfo)] = $infoValue['__VALUE__']; + } + + $skinListArray['skins'][] = $res; + } + } + + $skinListArray['currentSkin'] = SYS_SKIN; + + return $skinListArray; + } + + function getAllTimeZones() + { + $timezones = DateTimeZone::listAbbreviations(); + + $cities = array(); + foreach( $timezones as $key => $zones ) + { + foreach( $zones as $id => $zone ) + { + /** + * Only get timezones explicitely not part of "Others". + * @see http://www.php.net/manual/en/timezones.others.php + */ + if ( preg_match( '/^(America|Antartica|Arctic|Asia|Atlantic|Africa|Europe|Indian|Pacific)\//', $zone['timezone_id'] ) + && $zone['timezone_id']) { + $cities[$zone['timezone_id']][] = $key; + } + } + } + + // For each city, have a comma separated list of all possible timezones for that city. + foreach( $cities as $key => $value ) + $cities[$key] = join( ', ', $value); + + // Only keep one city (the first and also most important) for each set of possibilities. + $cities = array_unique( $cities ); + + // Sort by area/city name. + ksort( $cities ); + + return $cities; + } + + public function getSystemConfiguration($iniFile='') + { + $config = array( + 'debug' => 0, + 'debug_sql' => 0, + 'debug_time' => 0, + 'debug_calendar' => 0, + 'wsdl_cache' => 1, + 'memory_limit' => '100M', + 'time_zone' => 'America/La_Paz', + 'memcached' => 0, + 'memcached_server' =>'', + 'default_skin' => 'classic', + 'default_lang' => 'en' + ); + + if (empty($iniFile) || !file_exists($iniFile)) { + return $config; + } + + /* Read the env.ini */ + $ini_contents = parse_ini_file($iniFile, false); + + if ($ini_contents !== false) { + $config = array_merge($config, $ini_contents); + } + //echo '
'; print_r($config); die;
+    return $config;
+  }
+
+  function updateIndexFile($conf)
+  {
+    if (!defined('PATH_TPL')) {
+      throw new Exception('PATH_TPL constant is not defined.');
+    }
+
+    if (!file_exists(PATH_HTML . 'index.html')) {
+      if (!is_writable(PATH_HTML)) {
+        throw new Exception('The public directory is not writable.');
+      }
+    }
+    else {
+      if (!is_writable(PATH_HTML . 'index.html')) {
+        throw new Exception('The public index file is not writable.');
+      }
+    }
+
+    $content = file_get_contents(PATH_TPL . 'index.html.tpl');
+    $content = str_replace('{lang}', $conf['lang'], $content);
+    $content = str_replace('{skin}', $conf['skin'], $content);
+
+    return (@file_put_contents(PATH_HTML . 'index.html', $content) !== false);
+  }
   
 }// end System class
diff --git a/workflow/engine/classes/model/UsersProperties.php b/workflow/engine/classes/model/UsersProperties.php
index 193efbd95..31e782cad 100755
--- a/workflow/engine/classes/model/UsersProperties.php
+++ b/workflow/engine/classes/model/UsersProperties.php
@@ -18,11 +18,14 @@ require_once 'classes/model/om/BaseUsersProperties.php';
  */
 class UsersProperties extends BaseUsersProperties 
 {
-  function UserPropertyExists($sUserUID) 
-  {
+  var $fields = null;
+	
+  function UserPropertyExists($sUserUID) {
     try {
       $oUserProperty = UsersPropertiesPeer::retrieveByPk($sUserUID);
-      if (is_object($oUserProperty) && get_class($oUserProperty) == 'UsersProperties') {
+      if (!is_null($oUserProperty) && is_object($oUserProperty) && get_class($oUserProperty) == 'UsersProperties') {
+        $this->fields = $oUserProperty->toArray(BasePeer::TYPE_FIELDNAME);
+        $this->fromArray($this->fields, BasePeer::TYPE_FIELDNAME);
         return true;
       }
       else {
@@ -124,7 +127,7 @@ class UsersProperties extends BaseUsersProperties
       $this->create($aUserProperty);
     }
     else {
-      $aUserProperty = $this->load($sUserUID);
+      $aUserProperty = $this->fields;
     }
     return $aUserProperty;
   }
@@ -219,6 +222,11 @@ class UsersProperties extends BaseUsersProperties
     }
     //end plugin
 
+    if (substr(SYS_SKIN, 0, 2) === 'ux' && SYS_SKIN != 'uxs') {
+      return '../main';
+    }
+
+
     /**
      * New feature - User Experience Redirector
      * @author Erik Amaru Ortiz 
diff --git a/workflow/engine/config/paths.php b/workflow/engine/config/paths.php
index 9e58954aa..d14d92ece 100755
--- a/workflow/engine/config/paths.php
+++ b/workflow/engine/config/paths.php
@@ -23,56 +23,6 @@
  *
  */
 
-  /* Default configuration values (do not change these, use env.ini) */
-  $default_config = array(
-    'debug' => 0,
-    'debug_sql' => 0,
-    'debug_time' => 0,
-    'debug_calendar' => 0,
-    'wsdl_cache' => 1,
-    'memory_limit' => '100M',
-    'time_zone' => 'America/La_Paz',
-    'memcached' => 0,
-    'memcached_server' => ''
-  );
-
-  /* Read the env.ini */
-  $env_file = realpath(dirname(__FILE__) . "/env.ini");
-  $config = $default_config;
-  if ($env_file !== false && file_exists($env_file)) {
-    $ini_contents = parse_ini_file($env_file, false);
-    if ($ini_contents !== false)
-      $config = array_merge($default_config, $ini_contents);
-  }
-  else {
-    // if the env.ini file doesn't exist, and the current is a developemnt env, then force enable debug
-    if (!file_exists ( PATH_TRUNK . 'workflow/engine/methods/login/version-pmos.php' )) {
-      $config['debug'] = 1;
-    }
-  }
-
-
-//*** Do not change any of these settings directly, use env.ini instead
-  ini_set('display_errors','On');
-
-  ini_set('short_open_tag', 'on');
-  ini_set('asp_tags', 'on');
-  // The register_globals feature has been DEPRECATED as of PHP 5.3.0. default value Off.
-  // ini_set('register_globals', 'off');
-  ini_set('default_charset', "UTF-8");
-  $e_all = defined('E_DEPRECATED') ? E_ALL ^ E_DEPRECATED : E_ALL;
-  ini_set('error_reporting', ($config['debug'] ? $e_all : $e_all ^ E_NOTICE) );
-  ini_set('memory_limit', $config['memory_limit']);
-  ini_set('soap.wsdl_cache_enabled', $config['wsdl_cache']);
-  
-  define ('DEBUG_SQL_LOG', $config['debug_sql'] );
-  define ('DEBUG_TIME_LOG', $config['debug_time'] );
-  define ('DEBUG_CALENDAR_LOG', $config['debug_calendar'] );
-  define ('MEMCACHED_ENABLED',  $config['memcached']);
-  define ('MEMCACHED_SERVER',   $config['memcached_server']);
-  
-  define ('TIME_ZONE', $config['time_zone']);
-
 //***************** System Directories & Paths **************************
 
 //***************** RBAC Paths **************************
@@ -96,6 +46,7 @@
   define( 'PATH_SKIN_ENGINE', PATH_CORE . 'skinEngine'   . PATH_SEP );
   define( 'PATH_METHODS',     PATH_CORE . 'methods'      . PATH_SEP );
   define( 'PATH_XMLFORM',     PATH_CORE . 'xmlform'      . PATH_SEP );
+  define( 'PATH_CONFIG',      PATH_CORE . 'config'       . PATH_SEP );
   define( 'PATH_PLUGINS',     PATH_CORE . 'plugins'      . PATH_SEP  );
   define( 'PATH_HTMLMAIL',    PATH_CORE . 'html_templates' . PATH_SEP );
   define( 'PATH_TPL',         PATH_CORE . 'templates'    . PATH_SEP );
diff --git a/workflow/engine/controllers/admin.php b/workflow/engine/controllers/admin.php
index 76686bdaf..7bd2dbff4 100644
--- a/workflow/engine/controllers/admin.php
+++ b/workflow/engine/controllers/admin.php
@@ -11,6 +11,44 @@ class Admin extends Controller
   
   public $debug = true;
 
+  public function system()
+  {
+    require_once PATH_CONTROLLERS . 'main.php';
+    G::loadClass('system');
+    $skinsList = System::getSkingList();
+    $skins = array();
+    $timeZonesList = System::getAllTimeZones();
+    $timeZonesList = array_keys($timeZonesList);
+    $mainController = new Main();
+    $languagesList = $mainController->getLanguagesList();
+    $sysConf = System::getSystemConfiguration(PATH_CONFIG . 'env.ini');
+
+    foreach ($skinsList['skins'] as $skin) {
+      $skins[] = array($skin['SKIN_FOLDER_ID'], $skin['SKIN_NAME']);
+    }
+    $skins[] = array('uxmodern', 'uxmodern');
+
+    foreach ($timeZonesList as $tz) {
+      $timeZones[] = array($tz, $tz);
+    }
+    
+    
+
+    $this->includeExtJS('admin/system');
+    //G::LoadClass('configuration');
+
+    // $c = new Configurations();
+    // $configPage = $c->getConfiguration('usersList', 'pageSize','',$_SESSION['USER_LOGGED']);
+    // $Config['pageSize'] = isset($configPage['pageSize']) ? $configPage['pageSize'] : 20;
+
+    $this->setJSVar('skinsList', $skins);
+    $this->setJSVar('languagesList', $languagesList);
+    $this->setJSVar('timeZonesList', $timeZones);
+    $this->setJSVar('sysConf', $sysConf);
+    
+    G::RenderPage('publish', 'extJs');
+  }
+
   public function uxList()
   {
     require_once PATH_CONTROLLERS . 'adminProxy.php';
diff --git a/workflow/engine/controllers/adminProxy.php b/workflow/engine/controllers/adminProxy.php
index decf1d804..7ce4a2352 100644
--- a/workflow/engine/controllers/adminProxy.php
+++ b/workflow/engine/controllers/adminProxy.php
@@ -25,6 +25,80 @@
 
 class adminProxy extends HttpProxyController
 {    
+  public function saveSystemConf($httpData)
+  {
+    G::loadClass('system');
+    $envFile = PATH_CONFIG . 'env.ini';
+    $updateRedirector = false;
+    $restart = false;
+
+    if (!file_exists($envFile) ) { // if ini file doesn't exists
+      if (!is_writable(PATH_CONFIG)) {
+        throw new Exception('The enviroment config directory is not writable. 
Please give write permission to directory: /workflow/engine/config'); + } + $content = ";\r\n"; + $content .= "; ProcessMaker System Bootstrap Configuration\r\n"; + $content .= ";\r\n"; + file_put_contents($envFile, $content); + //@chmod($envFile, 0777); + } + else { + if (!is_writable($envFile)) { + throw new Exception('The enviroment ini file file is not writable.
Please give write permission to file: /workflow/engine/config/env.ini'); + } + } + + $sysConf = System::getSystemConfiguration($envFile); + + $updatedConf = array(); + + if ($sysConf['default_lang'] != $httpData->default_lang) { + $updatedConf['default_lang'] = $sysConf['default_lang'] = $httpData->default_lang; + $updateRedirector = true; + } + + if ($sysConf['default_skin'] != $httpData->default_skin) { + $updatedConf['default_skin'] = $sysConf['default_skin'] = $httpData->default_skin; + $updateRedirector = true; + } + + if ($sysConf['time_zone'] != $httpData->time_zone) { + $updatedConf['time_zone'] = $httpData->time_zone; + } + + $httpData->memory_limit .= 'M'; + if ($sysConf['memory_limit'] != $httpData->memory_limit) { + $updatedConf['memory_limit'] = $httpData->memory_limit; + } + + + if ($updateRedirector) { + if (!file_exists(PATH_HTML . 'index.html')) { + if (!is_writable(PATH_HTML)) { + throw new Exception('The public directory is not writable.
Please give write permission to file: /workflow/public_html'); + } + } + else { + if (!is_writable(PATH_HTML . 'index.html')) { + throw new Exception('The public index file is not writable.
Please give write permission to file: /workflow/public_html/index.html'); + } + } + + System::updateIndexFile(array( + 'lang' => $sysConf['default_lang'], + 'skin' => $sysConf['default_skin'] + )); + + $restart = true; + } + + G::update_php_ini($envFile, $updatedConf); + + $this->success = true; + $this->restart = $restart; + $this->message = 'Saved Successfully'; + } + function uxUserUpdate($httpData) { require_once 'classes/model/Users.php'; diff --git a/workflow/engine/controllers/installer.php b/workflow/engine/controllers/installer.php new file mode 100644 index 000000000..966c5ed85 --- /dev/null +++ b/workflow/engine/controllers/installer.php @@ -0,0 +1,1057 @@ + + */ + +class Installer extends Controller { + var $path_config; + var $path_languages; + var $path_plugins; + var $path_xmlforms; + var $path_shared; + var $path_sep; + + var $link; #resource for database connection + + public function __construct() { + $this->path_config = PATH_CORE.'config/'; + $this->path_languages = PATH_CORE.'content/languages/'; + $this->path_plugins = PATH_CORE.'plugins/'; + $this->path_xmlforms = PATH_CORE.'xmlform/'; + $this->path_public = PATH_HOME.'public_html/'; + $this->path_shared = PATH_TRUNK.'shared/'; + $this->path_sep = PATH_SEP; + } + + public function index($httpData) { + $step1_txt = 'If any of these items is not supported (marked as No) then please take actions to correct them.

' . + 'Failure to do so could lead to your ProcessMaker! installation not functioning correctly.

' . + '(*) MSSQL Support is optional.

' . + '(**) OpenSSL is optional.

' . + '(***) LDAP is optional.'; + + $step2_txt = 'These settings are recommended for PHP in order to ensure full compatibility with ProcessMaker. <> ' . + 'However, ProcessMaker still operate if your settings do not quite match the recommended'; + $step3_txt = 'In order for ProcessMaker to function correctly it needs to be able to access or write to certain files or directories.
' . + 'If you see "unwriteable" you need to change the permissions on the file or directory to allow ProcessMaker to write to it.'; + $step4_txt = 'ProcessMaker stores all of its data in a database. This screen gives the installation program the information needed to create this database.

' . + 'If you are installing ProcessMaker on a remote web server, you will need to get this information from your Database Server.
'; + $step5_txt = 'ProcessMaker uses a workspaces to store data. Please select a valid workspace name and credentials to log in it. '; + $step6_txt = 'xxx'; + + $licenseContent = file_get_contents(PATH_TRUNK . 'LICENSE.txt'); + + $this->includeExtJS('installer/CardLayout', false); + $this->includeExtJS('installer/Wizard', false); + $this->includeExtJS('installer/Header', false); + $this->includeExtJS('installer/Card', false); + $this->includeExtJS('installer/main', false); + + $this->setJSVar('licenseTxt', $licenseContent); + $this->setJSVar('step1_txt', $step1_txt); + $this->setJSVar('step2_txt', $step2_txt); + $this->setJSVar('step3_txt', $step3_txt); + $this->setJSVar('step4_txt', $step4_txt); + $this->setJSVar('step5_txt', $step5_txt); + $this->setJSVar('step6_txt', $step6_txt); + + $this->setJSVar('path_config', $this->path_config ); + $this->setJSVar('path_languages', $this->path_languages ); + $this->setJSVar('path_plugins', $this->path_plugins ); + $this->setJSVar('path_xmlforms', $this->path_xmlforms ); + $this->setJSVar('path_public', $this->path_public ); + $this->setJSVar('path_shared', $this->path_shared ); + $this->setJSVar('path_sep', $this->path_sep ); + + $this->setView('installer/main'); + + G::RenderPage('publish', 'extJs'); + } + + public function newSite() { + $textStep1 = 'ProcessMaker stores all of its data in a database. This screen gives the installation program the information needed to create this database.

' . + 'If you are installing ProcessMaker on a remote web server, you will need to get this information from your Database Server.'; + $textStep2 = 'ProcessMaker uses a workspaces to store data. Please select a valid workspace name and credentials to log in it.'; + + $this->includeExtJS('installer/CardLayout', false); + $this->includeExtJS('installer/Wizard', false); + $this->includeExtJS('installer/Header', false); + $this->includeExtJS('installer/Card', false); + $this->includeExtJS('installer/newSite', false); + + $this->setJSVar('textStep1', $textStep1); + $this->setJSVar('textStep2', $textStep2); + + $this->setJSVar('DB_ADAPTER', DB_ADAPTER); + $aux = explode(':', DB_HOST); + $this->setJSVar('DB_HOST', $aux[0]); + $this->setJSVar('DB_PORT', isset($aux[1]) ? $aux[1] : (DB_ADAPTER == 'mssql' ? '1433' : '3306')); + $this->setJSVar('DB_NAME', 'workflow'); + $this->setJSVar('DB_USER', ''); + $this->setJSVar('DB_PASS', ''); + $this->setJSVar('pathConfig', PATH_CORE . 'config' . PATH_SEP); + $this->setJSVar('pathLanguages', PATH_LANGUAGECONT); + $this->setJSVar('pathPlugins', PATH_PLUGINS); + $this->setJSVar('pathXmlforms', PATH_XMLFORM); + $this->setJSVar('pathShared', PATH_DATA); + + $this->setView('installer/newSite'); + + G::RenderPage('publish', 'extJs'); + } + + public function getSystemInfo() { + $this->setResponseType('json'); + + // PHP info and verification + $phpVer = phpversion(); + preg_match('/[0-9\.]+/', $phpVer, $match); + $phpVerNum = (float) $match[0]; + + $info->php->version = phpversion(); + $info->php->result = $phpVerNum > 5.1 ? true : false; + + // MYSQL info and verification + $info->mysql->result = false; + if ( function_exists ( 'mysql_query' ) ) { + $mysqlVer = mysql_get_client_info(); + preg_match('/[0-9\.]+/', $mysqlVer, $match); + $mysqlNum = (float) $match[0]; + $info->mysql->version = 'Client API version ' . $mysqlVer; + $info->mysql->result = $mysqlNum >= 5.0 ? true : false; + } + + // MSSQL info and verification + $info->mssql->result = false; + $info->mssql->version = 'not enabled'; + if ( function_exists ( 'mssql_query' ) ) { + $info->mssql->result = true; + $info->mssql->version = 'enabled'; + } + + // OpenSSL info + $info->openssl->result = false; + $info->openssl->version = 'not enabled'; + if ( function_exists ( 'openssl_open' ) ) { + $info->openssl->result = true; + $info->openssl->version = 'enabled'; + } + + // Curl info + $info->curl->result = false; + $info->curl->version = 'not enabled'; + if ( function_exists ( 'curl_version' ) ) { + $info->curl->result = true; + $version = curl_version(); + $info->curl->version = 'cURL ' . $version['version']; + $info->openssl->version = $version['ssl_version']; + } + + // DOMDocument info + $info->dom->result = false; + $info->dom->version = 'not enabled'; + if ( class_exists ( 'DOMDocument' ) ) { + $info->dom->result = true; + $info->dom->version = 'enabled'; + } + + // GD info + $info->gd->result = false; + $info->gd->version = 'not enabled'; + if ( function_exists ( 'gd_info' ) ) { + $info->gd->result = true; + $gdinfo = gd_info(); + $info->gd->version = $gdinfo['GD Version'] ; + } + + // Multibyte info + $info->multibyte->result = false; + $info->multibyte->version = 'not enabled'; + if ( function_exists ( 'mb_check_encoding' ) ) { + $info->multibyte->result = true; + $info->multibyte->version = 'enabled'; + } + + // soap info + $info->soap->result = false; + $info->soap->version = 'not enabled'; + if ( class_exists ( 'SoapClient' ) ) { + $info->soap->result = true; + $info->soap->version = 'enabled'; + } + + // ldap info + $info->ldap->result = false; + $info->ldap->version = 'not enabled'; + if ( function_exists ( 'ldap_connect' ) ) { + $info->ldap->result = true; + $info->ldap->version = 'enabled'; + } + + // memory limit verification + $memory = (int)ini_get("memory_limit"); + $info->memory->version = $memory . 'M'; + if ( $memory > 80 ) { + $info->memory->result = true; + } + else { + $info->memory->result = false; + } + + return $info; + } + + public function is_dir_writable( $path ) { + return ( is_dir($path) && is_writable($path) ); + } + + public function getPermissionInfo() { + + $this->setResponseType('json'); + + // pathConfig + $info->pathConfig->message = 'unwriteable'; + $info->pathConfig->result = $this->is_dir_writable($_REQUEST['pathConfig']); + if ( $info->pathConfig->result ) $info->pathConfig->message = 'writeable'; + + $info->pathLanguages->message = 'unwriteable'; + $info->pathLanguages->result = $this->is_dir_writable($_REQUEST['pathLanguages']); + if ( $info->pathLanguages->result ) $info->pathLanguages->message = 'writeable'; + + $info->pathPlugins->message = 'unwriteable'; + $info->pathPlugins->result = $this->is_dir_writable($_REQUEST['pathPlugins']); + if ( $info->pathPlugins->result ) $info->pathPlugins->message = 'writeable'; + + $info->pathXmlforms->message = 'unwriteable'; + $info->pathXmlforms->result = $this->is_dir_writable($_REQUEST['pathXmlforms']); + if ( $info->pathXmlforms->result ) $info->pathXmlforms->message = 'writeable'; + + $info->pathPublic->message = 'unwriteable'; + $info->pathPublic->result = $this->is_dir_writable($_REQUEST['pathPublic']); + if ( $info->pathPublic->result ) $info->pathPublic->message = 'writeable'; + + $info->pathShared->message = 'unwriteable'; + $info->pathShared->result = $this->is_dir_writable($_REQUEST['pathShared']); + if ( $info->pathShared->result ) $info->pathShared->message = 'writeable'; + + if (!$info->pathShared->result) { + G::verifyPath($_REQUEST['pathShared'], true); + $info->pathShared->result = $this->is_dir_writable($_REQUEST['pathShared']); + } + + if ($info->pathShared->result) { + $aux = pathinfo($_REQUEST['pathLogFile']); + G::verifyPath($aux['dirname'], true); + if (is_dir($aux['dirname'])) { + if (!file_exists($_REQUEST['pathLogFile'])) { + @file_put_contents($_REQUEST['pathLogFile'], ''); + } + } + } + $info->pathLogFile->message = 'Could not create the installation log'; + $info->pathLogFile->result = file_exists($_REQUEST['pathLogFile']); + if ( $info->pathLogFile->result ) $info->pathLogFile->message = 'Installation log created'; + + return $info; + } + + public function testConnection () { + $this->setResponseType('json'); + if ($_REQUEST['db_engine'] == 'mysql') { + return $this->testMySQLconnection(); + } + else { + return $this->testMSSQLconnection(); + } + } + + /** + log the queries and other information to install.log, + the install.log files should be placed in shared/logs + for that reason we are using the $_REQUEST of pathShared + */ + public function installLog( $text ) { + $serverAddr = $_SERVER['SERVER_ADDR']; + //if this function is called outside the createWorkspace, just returns and do nothing + if ( !isset( $_REQUEST['pathShared']) ) + return; + + //log file is in shared/logs + $pathShared = trim($_REQUEST['pathShared']); + if ( substr($pathShared,-1) != '/' ) $pathShared .= '/'; + $logFile = $pathShared . 'log/install.log'; + + if ( !is_file($logFile) ) { + G::mk_dir(dirname($pathShared)); + $fpt = fopen ( $logFile, 'w' ); + if ( $fpt !== NULL ) { + fwrite( $fpt, sprintf ( "%s %s\n", date('Y:m:d H:i:s'), '----- starting log file ------' )); + fclose( $fpt); + } + else { + throw ( new Exception ( sprintf ( "File '%s' is not writeable. Please check permission before continue", $logFile ) ) ); + return $false; + } + } + + $fpt = fopen ( $logFile, 'a' ); + fwrite( $fpt, sprintf ( "%s %s\n", date('Y:m:d H:i:s'), trim($text) )); + fclose( $fpt); + return true; + } + + /** + function to create a workspace + in fact this function is calling appropiate functions for mysql and mssql + */ + public function createWorkspace() { + $this->setResponseType('json'); + if ($_REQUEST['db_engine'] == 'mysql') { + $info = $this->createMySQLWorkspace(); + } + else { + $info = $this->createMSSQLWorkspace(); + } + $info->url = '/sys' . $_REQUEST['workspace'] . '/en/classic/main/login'; + $this->forceTogenerateTranslationsFiles($info->url); + return $info; + } + + public function forceTogenerateTranslationsFiles($url) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, (isset($_SERVER['HTTPS']) ? ($_SERVER['HTTPS'] != '' ? 'https://' : 'http://') : 'http://') . $_SERVER['HTTP_HOST'] . '/js/ext/translation.en.js?r=' . rand(1, 10000)); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 60); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20); + curl_exec($ch); + curl_close($ch); + } + + /** + send a query to MySQL and log the query + */ + public function mysqlQuery($sql) { + $this->installLog($sql); + $query = @mysql_query($sql, $this->link); + if (!$query) { + $errorMessage = mysql_error($this->link); + $this->installLog('MySQL error: ' . $errorMessage); + throw new Exception($errorMessage); + return false; + } + @mysql_free_result($query); + return true; + } + + /** + send a query to MSSQL and log the query + */ + public function mssqlQuery( $sql ) { + $this->installLog( $sql ); + $query = @mssql_query($sql, $this->link); + if (!$query) { + $errorMessage = mssql_get_last_message(); + $this->installLog ( 'MSSQL error: ' . $errorMessage ); + throw ( new Exception ( $errorMessage ) ); + return false; + } + @mssql_free_result($query); + return true; + } + + /** + * query_sql_file send many statements to server + * + * @param string $file + * @param string $connection + * @return array $report + */ + public function mysqlFileQuery( $file ) { + if ( !is_file($file) ) { + throw ( new Exception ( sprintf ( "File $file is not a valid sql file", $file ) ) ); + return $false; + } + $this->installLog ( 'Procesing: ' . $file ); + $startTime = microtime(true); + // $content = file_get_contents($file); + // $queries = explode(';', $content); + + // foreach( $queries as $sql) { + // if (trim($sql) != '') { + // $query = @mysql_query($sql, $this->link); + // if (!$query) { + // $errorMessage = mysql_error($this->link); + + // $this->installLog ( sprintf ( 'MySQL error: %s Query: %s ', $errorMessage, $sql ) ); + // throw ( new Exception ( $errorMessage ) ); + // return false; + // } + // } + // } + + //erik: New Update, to support more complex queries + + $lines = file($file); + $previous = NULL; + $errors = ''; + @mysql_query("SET NAMES 'utf8';"); + foreach ($lines as $j => $line) { + $line = trim($line); // Remove comments from the script + + if (strpos($line, "--") === 0) { + $line = substr($line, 0, strpos($line, "--")); + } + + if (empty($line)) { + continue; + } + + if (strpos($line, "#") === 0) { + $line = substr($line, 0, strpos($line, "#")); + } + + if (empty($line)) { + continue; + } + + // Concatenate the previous line, if any, with the current + if ($previous) { + $line = $previous . " " . $line; + } + $previous = NULL; + + // If the current line doesnt end with ; then put this line together + // with the next one, thus supporting multi-line statements. + if (strrpos($line, ";") != strlen($line) - 1) { + $previous = $line; + continue; + } + + $line = substr($line, 0, strrpos($line, ";")); + @mysql_query($line, $this->link); + } + + $endTime = microtime(true); + $this->installLog ( sprintf ('File: %s processed in %3.2f seconds', basename($file) , $endTime - $startTime ) ); + return true; + } + + /** + * query_sql_file send many statements to server + * + * @param string $file + * @param string $connection + * @return array $report + */ + public function mssqlFileQuery( $file ) { + if ( !is_file($file) ) { + throw ( new Exception ( sprintf ( "File $file is not a valid sql file", $file ) ) ); + return $false; + } + $this->installLog ( 'Procesing: ' . $file ); + $startTime = microtime(true); + $content = file_get_contents($file); + $queries = explode(';', $content); + + foreach( $queries as $sql) { + $query = @mssql_query($sql, $this->link); + if (!$query) { + $errorMessage = mssql_get_last_message(); + + $this->installLog ( sprintf ( 'MSSQL error: %s Query: %s ', $errorMessage, $sql ) ); + throw ( new Exception ( $errorMessage ) ); + return false; + } + } + $endTime = microtime(true); + $this->installLog ( sprintf ('File: %s processed in %3.2f seconds', basename($file) , $endTime - $startTime ) ); + return true; + } + + /** + * set Grant Privileges for MySQL + * + * @param string $psUser + * @param string $psPassword + * @param string $psDatabase + * @return void + */ + public function setGrantPrivilegesMySQL($psUser, $psPassword, $psDatabase, $host) { + $host = ($host == 'localhost' || $host == '127.0.0.1' ? 'localhost' : '%'); + $query = sprintf("GRANT ALL PRIVILEGES ON `%s`.* TO %s@'%s' IDENTIFIED BY '%s' WITH GRANT OPTION", $psDatabase, $psUser, $host, $psPassword); + $this->mysqlQuery($query); + } + + /** + * set Grant Privileges for SQLServer + * + * @param string $psUser + * @param string $psPassword + * @param string $psDatabase + * @return void + */ + public function setGrantPrivilegesMSSQL( $psUser, $psPassword, $psDatabase ) { + + $query = sprintf ( "IF EXISTS (SELECT * FROM sys.server_principals WHERE name = N'%s') DROP LOGIN [%s]", $psUser, $psUser ); + $this->mssqlQuery( $query ); + + $query = sprintf ( "CREATE LOGIN [%s] WITH PASSWORD=N'%s', DEFAULT_DATABASE=[%s], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF ", $psUser, $psPassword, $psDatabase ); + $this->mssqlQuery( $query ); + + $query = sprintf ( "USE %s;", $psDatabase ); + $this->mssqlQuery( $query ); + + $query = sprintf ( "IF EXISTS (SELECT * FROM sys.database_principals WHERE name = N'%s') DROP USER [%s]", $psUser, $psUser ); + $this->mssqlQuery( $query ); + + $query = sprintf ( "CREATE USER %s FOR LOGIN %s;", $psUser, $psUser ); + $this->mssqlQuery( $query ); + + $query = sprintf ( "sp_addrolemember 'db_owner', '%s' ", $psUser ); + $this->mssqlQuery( $query ); + + $query = sprintf ( "sp_addrolemember 'db_ddladmin', '%s' ", $psUser ); + $this->mssqlQuery( $query ); + + $query = sprintf ( "sp_addrolemember 'db_accessadmin', '%s' ", $psUser ); + $this->mssqlQuery( $query ); + + $query = sprintf ( "use master " ); + $this->mssqlQuery( $query ); + + return true; + } + + public function createMySQLWorkspace() { + ini_set('max_execution_time', '0'); + $info->result = false; + $info->message = ''; + + $db_hostname = trim($_REQUEST['db_hostname']); + $db_port = trim($_REQUEST['db_port']); + $db_username = trim($_REQUEST['db_username']); + $db_password = trim($_REQUEST['db_password']); + $wf = trim($_REQUEST['wfDatabase']); + $rb = trim($_REQUEST['rbDatabase']); + $rp = trim($_REQUEST['rpDatabase']); + $workspace = trim($_REQUEST['workspace']); + $pathConfig = trim($_REQUEST['pathConfig']); + $pathLanguages = trim($_REQUEST['pathLanguages']); + $pathPlugins = trim($_REQUEST['pathPlugins']); + $pathShared = trim($_REQUEST['pathShared']); + $pathXmlforms = trim($_REQUEST['pathXmlforms']); + $adminPassword = trim($_REQUEST['adminPassword']); + $adminUsername = trim($_REQUEST['adminUsername']); + $deleteDB = ($_REQUEST['deleteDB'] == 'true'); + + if ( substr($pathShared,-1) != '/' ) $pathShared .= '/'; + + $this->installLog ( '-------------------------------------------' ); + $this->installLog ( sprintf ( "Creating workspace '%s' ", $workspace) ); + + try { + $db_host = ($db_port != '' && $db_port != 3306) ? $db_hostname . ':' . $db_port : $db_hostname ; + $this->link = @mysql_connect($db_host, $db_username, $db_password); + $this->installLog (sprintf ( "Connected to server %s:%d using user: '%s' ", $db_hostname, $db_port, $db_username ) ); + + if ($deleteDB) { + $q = sprintf('DROP DATABASE IF EXISTS %s;' , $wf, $wf); + $this->mysqlQuery($q); + + $q = sprintf('DROP DATABASE IF EXISTS %s;' , $rb, $rb); + $this->mysqlQuery($q); + + $q = sprintf('DROP DATABASE IF EXISTS %s;' , $rp, $rp); + $this->mysqlQuery($q); + } + + // CREATE databases wf_workflow, rb_workflow and rp_workflow + $q = sprintf('CREATE DATABASE IF NOT EXISTS %s;' , $wf, $wf); + $this->mysqlQuery($q); + + $q = sprintf('CREATE DATABASE IF NOT EXISTS %s;' , $rb, $rb); + $this->mysqlQuery($q); + + $q = sprintf('CREATE DATABASE IF NOT EXISTS %s;' , $rp, $rp); + $this->mysqlQuery($q); + + // CREATE users and GRANT Privileges + $wfPass = G::generate_password(12); + $rbPass = G::generate_password(12); + $rpPass = G::generate_password(12); + $this->setGrantPrivilegesMySQL($wf, $wfPass, $wf, $db_hostname); + $this->setGrantPrivilegesMySQL($rb, $rbPass, $rb, $db_hostname); + $this->setGrantPrivilegesMySQL($rp, $rpPass, $rp, $db_hostname); + + // Generate the db.php file and folders + $path_site = $pathShared . "/sites/" . $workspace ."/"; + $db_file = $path_site. "db.php"; + @mkdir($path_site, 0777, true); + @mkdir($path_site . "files/", 0777, true); + @mkdir($path_site . "mailTemplates/", 0777, true); + @mkdir($path_site . "public/", 0777, true); + @mkdir($path_site . "reports/", 0777, true); + @mkdir($path_site . "xmlForms", 0777, true); + + $dbText = "installLog("Creating: " . $db_file ); + file_put_contents ( $db_file, $dbText); + + // Generate the databases.php file + $databases_file = $path_site . 'databases.php'; + $dbData = sprintf("\$dbAdapter = '%s';\n", 'mysql'); + $dbData .= sprintf("\$dbHost = '%s';\n", $db_host); + $dbData .= sprintf("\$dbName = '%s';\n", $wf); + $dbData .= sprintf("\$dbUser = '%s';\n", $wf); + $dbData .= sprintf("\$dbPass = '%s';\n", $wfPass); + $dbData .= sprintf("\$dbRbacHost = '%s';\n", $db_host); + $dbData .= sprintf("\$dbRbacName = '%s';\n", $rb); + $dbData .= sprintf("\$dbRbacUser = '%s';\n", $rb); + $dbData .= sprintf("\$dbRbacPass = '%s';\n", $rbPass); + $dbData .= sprintf("\$dbReportHost = '%s';\n", $db_host); + $dbData .= sprintf("\$dbReportName = '%s';\n", $rp); + $dbData .= sprintf("\$dbReportUser = '%s';\n", $rp); + $dbData .= sprintf("\$dbReportPass = '%s';\n", $rpPass); + $databasesText = str_replace('{dbData}', $dbData, @file_get_contents(PATH_HOME . 'engine/templates/installer/databases.tpl')); + + $this->installLog('Creating: ' . $databases_file); + file_put_contents($databases_file, $databasesText); + + + // Execute scripts to create and populates databases + $query = sprintf ( "USE %s;", $rb ); + $this->mysqlQuery( $query ); + + $this->mysqlFileQuery ( PATH_RBAC_HOME . 'engine/data/mysql/schema.sql' ); + $this->mysqlFileQuery ( PATH_RBAC_HOME . 'engine/data/mysql/insert.sql' ); + + $query = sprintf ( "USE %s;", $wf ); + $this->mysqlQuery( $query ); + $this->mysqlFileQuery ( PATH_HOME . 'engine/data/mysql/schema.sql' ); + $this->mysqlFileQuery ( PATH_HOME . 'engine/data/mysql/insert.sql' ); + + // Create the triggers + if (file_exists(PATH_HOME . 'engine/methods/setup/setupSchemas/triggerAppDelegationInsert.sql') && + file_exists(PATH_HOME . 'engine/methods/setup/setupSchemas/triggerAppDelegationUpdate.sql') && + file_exists(PATH_HOME . 'engine/methods/setup/setupSchemas/triggerApplicationUpdate.sql') && + file_exists(PATH_HOME . 'engine/methods/setup/setupSchemas/triggerApplicationDelete.sql')) { + $this->mysqlQuery(@file_get_contents(PATH_HOME . 'engine/methods/setup/setupSchemas/triggerAppDelegationInsert.sql')); + $this->mysqlQuery(@file_get_contents(PATH_HOME . 'engine/methods/setup/setupSchemas/triggerAppDelegationUpdate.sql')); + $this->mysqlQuery(@file_get_contents(PATH_HOME . 'engine/methods/setup/setupSchemas/triggerApplicationUpdate.sql')); + $this->mysqlQuery(@file_get_contents(PATH_HOME . 'engine/methods/setup/setupSchemas/triggerApplicationDelete.sql')); + $this->mysqlQuery("INSERT INTO `CONFIGURATION` ( + `CFG_UID`, + `CFG_VALUE` + ) + VALUES ( + 'APP_CACHE_VIEW_ENGINE', + '" . mysql_real_escape_string(serialize(array('LANG' => 'en', 'STATUS' => 'active'))) . "' + )"); + } + + // Change admin user + $query = sprintf ( "USE %s;", $wf ); + $this->mysqlQuery( $query ); + + $query = sprintf ( "UPDATE USERS SET USR_USERNAME = '%s', USR_PASSWORD = '%s' WHERE USR_UID = '00000000000000000000000000000001' ", $adminUsername, md5($adminPassword) ); + $this->mysqlQuery( $query ); + + $query = sprintf ( "USE %s;", $rb ); + $this->mysqlQuery( $query ); + + $query = sprintf ( "UPDATE USERS SET USR_USERNAME = '%s', USR_PASSWORD = '%s' WHERE USR_UID = '00000000000000000000000000000001' ", $adminUsername, md5($adminPassword) ); + $this->mysqlQuery( $query ); + + // Write the paths_installed.php file (contains all the information configured so far) + if ( !file_exists(FILE_PATHS_INSTALLED) ) { + $sh = md5( filemtime( PATH_GULLIVER . '/class.g.php' ) ); + $h = G::encrypt($db_hostname.$sh.$db_username.$sh.$db_password.'1' , $sh); + $dbText = "installLog("Creating: " . FILE_PATHS_INSTALLED ); + file_put_contents ( FILE_PATHS_INSTALLED, $dbText); + } + + //erik: for new env conf handling + G::loadClass('system'); + $envFile = PATH_CONFIG . 'env.ini'; + + //writting for new installtions to use the new skin 'uxmind' with new Front End ExtJs Based + $updatedConf['default_skin'] = 'uxmodern'; + G::update_php_ini($envFile, $updatedConf); + + // getting configuration from env.ini + $sysConf = System::getSystemConfiguration($envFile); + + // update the main index file + $indexFileUpdated = System::updateIndexFile(array( + 'lang' => 'en', + 'skin' => $updatedConf['default_skin'] + )); + + $this->installLog("Index File updated $indexFileUpdated with lang: {$sysConf['default_lang']}, skin: {$sysConf['default_skin']} " ); + $this->installLog("Install completed Succesfully" ); + + $info->url = '/sys' . $_REQUEST['workspace'] . '/en/uxmodern/main/login'; + $info->uri = '/sys' . $_REQUEST['workspace'] . '/en/uxmodern/main/login'; + $info->result = true; + $info->message = 'Succesfully OK---'; + } + catch (Exception $e) { + $info->result = false; + $info->message = $e->getMessage(); + } + return $info; + } + + public function createMSSQLWorkspace() { + ini_set('max_execution_time', '0'); + $info->result = false; + $info->message = ''; + + $db_hostname = trim($_REQUEST['db_hostname']); + $db_port = trim($_REQUEST['db_port']); + $db_username = trim($_REQUEST['db_username']); + $db_password = trim($_REQUEST['db_password']); + $wf = trim($_REQUEST['wfDatabase']); + $rb = trim($_REQUEST['rbDatabase']); + $rp = trim($_REQUEST['rpDatabase']); + $workspace = trim($_REQUEST['workspace']); + $pathConfig = trim($_REQUEST['pathConfig']); + $pathLanguages = trim($_REQUEST['pathLanguages']); + $pathPlugins = trim($_REQUEST['pathPlugins']); + $pathShared = trim($_REQUEST['pathShared']); + $pathXmlforms = trim($_REQUEST['pathXmlforms']); + $adminPassword = trim($_REQUEST['adminPassword']); + $adminUsername = trim($_REQUEST['adminUsername']); + $deleteDB = ($_REQUEST['deleteDB'] == 'true'); + + if ( substr($pathShared,-1) != '/' ) $pathShared .= '/'; + + $this->installLog ( '-------------------------------------------' ); + $this->installLog ( sprintf ( "Creating workspace '%s' ", $workspace) ); + + try { + $db_host = ($db_port != '' && $db_port != 1433) ? $db_hostname . ':' . $db_port : $db_hostname ; + $this->link = @mssql_connect($db_host, $db_username, $db_password); + $this->installLog (sprintf ( "Connected to server %s:%d using user: '%s' ", $db_hostname, $db_port, $db_username ) ); + + $this->mssqlQuery( 'USE [master]' ); + + // DROP databases wf_workflow, rb_workflow and rp_workflow + if ($deleteDB) { + $q = sprintf ("IF EXISTS (SELECT name FROM sys.databases WHERE name='%s' ) DROP DATABASE %s" , $wf, $wf ); + $this->mssqlQuery( $q); + + $q = sprintf ("IF EXISTS (SELECT name FROM sys.databases WHERE name='%s' ) DROP DATABASE %s" , $rb, $rb ); + $this->mssqlQuery( $q); + + $q = sprintf ("IF EXISTS (SELECT name FROM sys.databases WHERE name='%s' ) DROP DATABASE %s" , $rp, $rp ); + $this->mssqlQuery( $q); + } + + // CREATE databases wf_workflow, rb_workflow and rp_workflow + $q = sprintf ("IF NOT EXISTS (SELECT * FROM sys.databases WHERE name='%s' ) CREATE DATABASE %s" , $wf, $wf ); + $this->mssqlQuery( $q); + + $q = sprintf ("IF NOT EXISTS (SELECT * FROM sys.databases WHERE name='%s' ) CREATE DATABASE %s" , $rb, $rb ); + $this->mssqlQuery( $q); + + $q = sprintf ("IF NOT EXISTS (SELECT * FROM sys.databases WHERE name='%s' ) CREATE DATABASE %s" , $rp, $rp ); + $this->mssqlQuery( $q); + + //CREATE users and GRANT Privileges + $wfPass = G::generate_password(12); + $rbPass = G::generate_password(12); + $rpPass = G::generate_password(12); + $this->setGrantPrivilegesMSSQL($wf, $wfPass, $wf ); + $this->setGrantPrivilegesMSSQL($rb, $rbPass, $rb ); + $this->setGrantPrivilegesMSSQL($rp, $rpPass, $rp ); + + //Generate the db.php file and folders + $path_site = $pathShared . "/sites/" . $workspace ."/"; + $db_file = $path_site. "db.php"; + mkdir($path_site, 0777, true); + @mkdir($path_site . "files/", 0777, true); + @mkdir($path_site . "mailTemplates/", 0777, true); + @mkdir($path_site . "public/", 0777, true); + @mkdir($path_site . "reports/", 0777, true); + @mkdir($path_site . "xmlForms", 0777, true); + + $dbText = "installLog("Creating: " . $db_file ); + file_put_contents ( $db_file, $dbText); + + // Generate the databases.php file + $databases_file = $path_site . 'databases.php'; + $dbData = sprintf("\$dbAdapter = '%s';\n", 'mssql'); + $dbData .= sprintf("\$dbHost = '%s';\n", $db_host); + $dbData .= sprintf("\$dbName = '%s';\n", $wf); + $dbData .= sprintf("\$dbUser = '%s';\n", $wf); + $dbData .= sprintf("\$dbPass = '%s';\n", $wfPass); + $dbData .= sprintf("\$dbRbacHost = '%s';\n", $db_host); + $dbData .= sprintf("\$dbRbacName = '%s';\n", $rb); + $dbData .= sprintf("\$dbRbacUser = '%s';\n", $rb); + $dbData .= sprintf("\$dbRbacPass = '%s';\n", $rbPass); + $dbData .= sprintf("\$dbReportHost = '%s';\n", $db_host); + $dbData .= sprintf("\$dbReportName = '%s';\n", $rp); + $dbData .= sprintf("\$dbReportUser = '%s';\n", $rp); + $dbData .= sprintf("\$dbReportPass = '%s';\n", $rpPass); + $databasesText = str_replace('{dbData}', $dbData, @file_get_contents(PATH_HOME . 'engine/templates/installer/databases.tpl')); + + $this->installLog('Creating: ' . $databases_file); + file_put_contents($databases_file, $databasesText); + + //execute scripts to create and populates databases + $query = sprintf ( "USE %s;", $rb ); + $this->mssqlQuery( $query ); + + $this->mssqlFileQuery ( PATH_RBAC_HOME . 'engine/data/mssql/schema.sql' ); + $this->mssqlFileQuery ( PATH_RBAC_HOME . 'engine/data/mssql/insert.sql' ); + + $query = sprintf ( "USE %s;", $wf ); + $this->mssqlQuery( $query ); + $this->mssqlFileQuery ( PATH_HOME . 'engine/data/mssql/schema.sql' ); + $this->mssqlFileQuery ( PATH_HOME . 'engine/data/mssql/insert.sql' ); + + // Create the triggers + if (file_exists(PATH_HOME . 'engine/plugins/enterprise/data/triggerAppDelegationInsert.sql') && + file_exists(PATH_HOME . 'engine/plugins/enterprise/data/triggerAppDelegationUpdate.sql') && + file_exists(PATH_HOME . 'engine/plugins/enterprise/data/triggerApplicationUpdate.sql') && + file_exists(PATH_HOME . 'engine/plugins/enterprise/data/triggerApplicationDelete.sql')) { + $this->mssqlQuery(@file_get_contents(PATH_HOME . 'engine/plugins/enterprise/data/triggerAppDelegationInsert.sql')); + $this->mssqlQuery(@file_get_contents(PATH_HOME . 'engine/plugins/enterprise/data/triggerAppDelegationUpdate.sql')); + $this->mssqlQuery(@file_get_contents(PATH_HOME . 'engine/plugins/enterprise/data/triggerApplicationUpdate.sql')); + $this->mssqlQuery(@file_get_contents(PATH_HOME . 'engine/plugins/enterprise/data/triggerApplicationDelete.sql')); + $this->mssqlQuery("INSERT INTO CONFIGURATION ( + CFG_UID, + CFG_VALUE + ) + VALUES ( + 'APP_CACHE_VIEW_ENGINE', + '" . addslashes(serialize(array('LANG' => 'en', 'STATUS' => 'active'))) . "' + )"); + } + + //change admin user + $query = sprintf ( "USE %s;", $wf ); + $this->mssqlQuery( $query ); + + $query = sprintf ( "UPDATE USERS SET USR_USERNAME = '%s', USR_PASSWORD = '%s' WHERE USR_UID = '00000000000000000000000000000001' ", $adminUsername, md5($adminPassword) ); + $this->mssqlQuery( $query ); + + $query = sprintf ( "USE %s;", $rb ); + $this->mssqlQuery( $query ); + + $query = sprintf ( "UPDATE USERS SET USR_USERNAME = '%s', USR_PASSWORD = '%s' WHERE USR_UID = '00000000000000000000000000000001' ", $adminUsername, md5($adminPassword) ); + $this->mssqlQuery( $query ); + + // Write the paths_installed.php file (contains all the information configured so far) + if ( !file_exists(FILE_PATHS_INSTALLED) ) { + $sh = md5( filemtime( PATH_GULLIVER . '/class.g.php' ) ); + $h = G::encrypt($db_hostname.$sh.$db_username.$sh.$db_password.'1' , $sh); + $dbText = "installLog("Creating: " . FILE_PATHS_INSTALLED ); + file_put_contents ( FILE_PATHS_INSTALLED, $dbText); + } + $this->installLog("Install completed Succesfully" ); + $info->result = true; + $info->message = 'Succesfully'; + $info->url = '/sys' . $_REQUEST['workspace'] . '/en/classic/main/login'; + } + catch (Exception $e) { + $info->result = false; + $info->message = $e->getMessage(); + } + return $info; + } + + public function getEngines() { + $this->setResponseType('json'); + $engines = array(); + if (function_exists('mysql_query')) { + $engine = new stdclass(); + $engine->id = 'mysql'; + $engine->label = 'MySQL'; + $engines[] = $engine; + } + /** DISABLED TEMPORARELY + if (function_exists('mssql_query')) { + $engine = new stdclass(); + $engine->id = 'mssql'; + $engine->label = 'Microsoft SQL Server'; + $engines[] = $engine; + }*/ + return $engines; + } + + public function checkDatabases() { + $this->setResponseType('json'); + $databasesExists = new stdclass(); + if ($_REQUEST['db_engine'] == 'mysql') { + $link = @mysql_connect($_REQUEST['db_hostname'], $_REQUEST['db_username'], $_REQUEST['db_password']); + $dataset = @mysql_query("show databases like '" . $_REQUEST['wfDatabase'] . "'", $link); + $databasesExists->wfDatabaseExists = (@mysql_num_rows($dataset) > 0); + $dataset = @mysql_query("show databases like '" . $_REQUEST['rbDatabase'] . "'", $link); + $databasesExists->rbDatabaseExists = (@mysql_num_rows($dataset) > 0); + $dataset = @mysql_query("show databases like '" . $_REQUEST['rpDatabase'] . "'", $link); + $databasesExists->rpDatabaseExists = (@mysql_num_rows($dataset) > 0); + } + else { + $link = @mssql_connect($_REQUEST['db_hostname'], $_REQUEST['db_username'], $_REQUEST['db_password']); + $dataset = @mssql_query("select * from sys.databases where name = '" . $_REQUEST['wfDatabase'] . "'", $link); + $databasesExists->wfDatabaseExists = (@mssql_num_rows($dataset) > 0); + $dataset = @mssql_query("select * from sys.databases where name = '" . $_REQUEST['rbDatabase'] . "'", $link); + $databasesExists->rbDatabaseExists = (@mssql_num_rows($dataset) > 0); + $dataset = @mssql_query("select * from sys.databases where name = '" . $_REQUEST['rpDatabase'] . "'", $link); + $databasesExists->rpDatabaseExists = (@mssql_num_rows($dataset) > 0); + } + return $databasesExists; + } + + /** + * Privates functions section, non callable by http request + */ + + private function testMySQLconnection() { + $info->result = false; + $info->message = ''; + if ( !function_exists("mysql_connect") ) { + $info->message = 'php-mysql is Not Installed'; + return $info; + } + $db_hostname = $_REQUEST['db_hostname']; + $db_port = $_REQUEST['db_port']; + $db_username = $_REQUEST['db_username']; + $db_password = $_REQUEST['db_password']; + $fp = @fsockopen($db_hostname, $db_port, $errno, $errstr, 30); + if ( !$fp ) { + $info->message .= "Connection Error: $errstr ($errno)"; + return $info; + } + + $db_host = ($db_port != '' && $db_port != 1433) ? $db_hostname . ':' . $db_port : $db_hostname ; + $link = @mysql_connect($db_host, $db_username, $db_password); + if (!$link) { + $info->message .= "Connection Error: unable to connect to MySQL using provided credentials."; + return $info; + } + $res = @mysql_query("SELECT * FROM `information_schema`.`USER_PRIVILEGES` where (GRANTEE = \"'$db_username'@'$db_hostname'\" OR GRANTEE = \"'$db_username'@'%'\") and PRIVILEGE_TYPE = 'SUPER' ", $link); + $row = @mysql_fetch_array($res); + $hasSuper = is_array($row); + @mysql_free_result($res); + @mysql_close($link); + if (!$hasSuper) { + $info->message .= "Connection Error: User '$db_username' can't create databases and Users
Please provide an user with SUPER privilege."; + return $info; + } + $info->message .= "Succesfully connected to MySQL Server"; + $info->result = true; + return $info; + } + + private function testMSSQLconnection() { + $info->result = false; + $info->message = ''; + if ( !function_exists("mssql_connect") ) { + $info->message = 'php-mssql is Not Installed'; + return $info; + } + + $db_hostname = $_REQUEST['db_hostname']; + $db_port = $_REQUEST['db_port']; + $db_username = $_REQUEST['db_username']; + $db_password = $_REQUEST['db_password']; + + $fp = @fsockopen($db_hostname, $db_port, $errno, $errstr, 30); + if ( !$fp ) { + $info->message .= "Connection Error: $errstr ($errno)"; + return $info; + } + + $db_host = ($db_port != '' && $db_port != 1433) ? $db_hostname . ':' . $db_port : $db_hostname ; + $link = @mssql_connect($db_host, $db_username, $db_password); + if (!$link) { + $info->message .= "Connection Error: unable to connect to MSSQL using provided credentials."; + return $info; + } + + //checking if user has the dbcreator role + $hasDbCreator = false; + $hasSecurityAdmin = false; + $hasSysAdmin = false; + + $res = @mssql_query( "EXEC sp_helpsrvrolemember 'dbcreator' ", $link ); + $row = mssql_fetch_array($res); + while ( is_array( $row ) ) { + if ( $row['MemberName'] == $db_username ) $hasDbCreator = true; + $row = mssql_fetch_array($res); + } + mssql_free_result($res); + + $res = @mssql_query( "EXEC sp_helpsrvrolemember 'sysadmin' ", $link ); + $row = mssql_fetch_array($res); + while ( is_array( $row ) ) { + if ( $row['MemberName'] == $db_username ) $hasSysAdmin = true; + $row = mssql_fetch_array($res); + } + mssql_free_result($res); + + $res = @mssql_query( "EXEC sp_helpsrvrolemember 'SecurityAdmin' ", $link ); + $row = mssql_fetch_array($res); + while ( is_array( $row ) ) { + if ( $row['MemberName'] == $db_username ) $hasSecurityAdmin = true; + $row = mssql_fetch_array($res); + } + mssql_free_result($res); + + if ( ! ( $hasSysAdmin || ( $hasSecurityAdmin && $hasDbCreator) ) ) { + $info->message .= "Connection Error: User '$db_username' can't create databases and Users
Please provide an user with sysadmin role or dbcreator and securityadmin roles."; + return $info; + } + + $info->message .= "Succesfully connected to MSSQL Server"; + $info->result = true; + return $info; + } +} \ No newline at end of file diff --git a/workflow/engine/controllers/main.php b/workflow/engine/controllers/main.php new file mode 100644 index 000000000..a1bbaaa10 --- /dev/null +++ b/workflow/engine/controllers/main.php @@ -0,0 +1,1050 @@ + + */ + +class Main extends Controller +{ + private $memcache; + private $conf; + + public function __construct() + { + G::LoadClass('memcached'); + $this->memcache = & PMmemcached::getSingleton(defined('SYS_SYS') ? SYS_SYS : ''); + + define('ERROR_EXCEPTION' , 1); + define('INFO_EXCEPTION' , 3); + define('WARNING_EXCEPTION', 2); + + //$this->setDebug(true); + } + + function index($httpData) + { + global $RBAC; + $RBAC->requirePermissions('PM_LOGIN'); + + $meta = new stdClass; + $this->includeExtJS('main/index'); + $this->includeExtJSLib('ux/ux.menu'); + $this->setView('main/index'); + + // setting variables for template + $this->setVar('logo_company', $this->getCompanyLogo()); + $this->setVar('userfullname', $this->getUserFullName()); + $this->setVar('user', isset($_SESSION['USR_USERNAME']) ? $_SESSION['USR_USERNAME'] : ''); + $this->setVar('pipe', isset($_SESSION['USR_USERNAME']) ? ' | ' : ''); + $this->setVar('rolename', $this->getUserRole()); + $this->setVar('logout', G::LoadTranslation('ID_LOGOUT')); + $this->setVar('workspace', defined('SYS_SYS')? ucfirst(SYS_SYS): ''); + $this->setVar('user_avatar', 'users/users_ViewPhotoGrid?pUID=' . $_SESSION['USER_LOGGED'].'&h=' . rand()); + $this->setVar('udate', G::getformatedDate(date('Y-m-d'), 'M d, yyyy', SYS_LANG)); + + // setting variables on javascript env. + $this->setJSVar('meta', array('menu'=>$this->getMenu())); + $this->setJSVar('activeTab', $this->getActiveTab()); + $this->setJSVar('urlAddGetParams', $this->getUrlGetParams()); + + $showSystemInfo = $RBAC->userCanAccess('PM_SETUP') == 1; + + $this->setJSVar('showSystemInfo', $showSystemInfo); + + if (($flyNotify = $this->getFlyNotify()) !== false) { + $this->setJSVar('flyNotify', $flyNotify); + } + + G::RenderPage('publish', 'extJs'); + } + + function getSystemInfo() + { + $this->setResponseType('json'); + $infoList = $this->_getSystemInfo(); + $data = array(); + + foreach ($infoList as $row) { + $data[] = array( + 'label' => $row[0], + 'value' => $row[1], + 'section' => $row[2], + ); + } + return $data; + } + + /** + * Login + */ + public function login() + { + require_once 'classes/model/LoginLog.php'; + G::LoadClass('system'); + G::loadClass('configuration'); + $this->conf = new Configurations; + + // getting posibles errors passed by GET method + $this->getInUrlError(); + + if (!isset ($_SESSION ['G_MESSAGE'])) { + $_SESSION['G_MESSAGE'] = ''; + } + if (!isset ($_SESSION ['G_MESSAGE_TYPE'])) { + $_SESSION['G_MESSAGE_TYPE'] = ''; + } + + $msg = $_SESSION['G_MESSAGE']; + $msgType = $_SESSION['G_MESSAGE_TYPE']; + + if (! isset($_SESSION['FAILED_LOGINS'])) { + $_SESSION['FAILED_LOGINS'] = 0; + } + $sFailedLogins = $_SESSION ['FAILED_LOGINS']; + + if ( isset ($_SESSION ['USER_LOGGED']) ) { + //close the session, if the current session_id was used in PM. + $oCriteria = new Criteria ( 'workflow' ); + $oCriteria->add ( LoginLogPeer::LOG_SID, session_id () ); + $oCriteria->add ( LoginLogPeer::USR_UID, isset ( $_SESSION ['USER_LOGGED'] ) ? $_SESSION ['USER_LOGGED'] : '-' ); + $oCriteria->add ( LoginLogPeer::LOG_STATUS, 'ACTIVE' ); + $oCriteria->add ( LoginLogPeer::LOG_END_DATE, NULL, Criteria::ISNULL ); + $oDataset = LoginLogPeer::doSelectRS ( $oCriteria ); + $oDataset->setFetchmode ( ResultSet::FETCHMODE_ASSOC ); + $oDataset->next (); + $aRow = $oDataset->getRow (); + if ($aRow) { + if ($aRow ['LOG_STATUS'] != 'CLOSED' && $aRow ['LOG_END_DATE'] == NULL) { + $weblog = new LoginLog ( ); + $aLog ['LOG_UID'] = $aRow ['LOG_UID']; + $aLog ['LOG_STATUS'] = 'CLOSED'; + $aLog ['LOG_IP'] = $aRow ['LOG_IP']; + $aLog ['LOG_SID'] = session_id (); + $aLog ['LOG_INIT_DATE'] = $aRow ['LOG_INIT_DATE']; + $aLog ['LOG_END_DATE'] = date ( 'Y-m-d H:i:s' ); + $aLog ['LOG_CLIENT_HOSTNAME'] = $aRow ['LOG_CLIENT_HOSTNAME']; + $aLog ['USR_UID'] = $aRow ['USR_UID']; + $weblog->update ( $aLog ); + } + } + //remove memcached session + $this->memcache->delete( 'rbacSession' . session_id() ) ; + } + else { + // Execute SSO trigger + $pluginRegistry =& PMPluginRegistry::getSingleton(); + if (defined('PM_SINGLE_SIGN_ON')) { + if ($pluginRegistry->existsTrigger(PM_SINGLE_SIGN_ON)) { + if ($pluginRegistry->executeTriggers(PM_SINGLE_SIGN_ON, null)) { + // Start new session + @session_destroy(); + session_start(); + session_regenerate_id(); + // Authenticate + $result = $this->authentication(); + if ($result->success) { + // Redirect to landing page for the user + G::header('Location: ' . $result->url); + die(); + } + } + } + } + } + //end log + + //start new session + @session_destroy (); + session_start (); + session_regenerate_id (); + + if (strlen ( $msg ) > 0) { + $_SESSION ['G_MESSAGE'] = $msg; + } + if (strlen ( $msgType ) > 0) { + $_SESSION ['G_MESSAGE_TYPE'] = $msgType; + } + $_SESSION ['FAILED_LOGINS'] = $sFailedLogins; + + $availableLangArray = $this->getLanguagesList(); + + //$G_PUBLISH = new Publisher ( ); + //$G_PUBLISH->AddContent ( 'xmlform', 'xmlform', 'login/login', '', $aFields, SYS_URI . 'login/authentication.php' ); + + G::LoadClass ( 'serverConfiguration' ); + + if ( ($nextBeatDate = $this->memcache->get('nextBeatDate')) === false ) { + //get the serverconf singleton, and check if we can send the heartbeat + $oServerConf = & serverConf::getSingleton (); + + $sflag = $oServerConf->getHeartbeatProperty('HB_OPTION','HEART_BEAT_CONF'); + $sflag = (trim($sflag)!='')?$sflag:'1'; + + //get date of next beat + $nextBeatDate = $oServerConf->getHeartbeatProperty('HB_NEXT_BEAT_DATE','HEART_BEAT_CONF'); + $this->memcache->set( 'nextBeatDate', $nextBeatDate, 1*3600 ); + } + + $sflag = 1; + //TODO analize this & enable to work with this new login + // if flag to send heartbeat is enabled, and it is time to send heartbeat, sent it using asynchronous beat. + // if( ($sflag=="1") && ((strtotime ( "now" ) > $nextBeatDate) || is_null($nextBeatDate) ) ){ + // $oHeadPublisher =& headPublisher::getSingleton(); + // //To do: we need to change to ExtJs + // $oHeadPublisher->addScriptCode( 'var flagHeartBeat = 1; '); + // } + // else + // $oHeadPublisher->addScriptCode( 'var flagHeartBeat = 0; '); + + //check if we show the panel with the getting started info + if ( ($flagGettingStarted = $this->memcache->get('flagGettingStarted')) === false ) { + require_once 'classes/model/Configuration.php'; + $oConfiguration = new Configuration ( ); + $oCriteria = new Criteria ( 'workflow' ); + $oCriteria->add ( ConfigurationPeer::CFG_UID, 'getStarted' ); + $oCriteria->add ( ConfigurationPeer::OBJ_UID, '' ); + $oCriteria->add ( ConfigurationPeer::CFG_VALUE, '1' ); + $oCriteria->add ( ConfigurationPeer::PRO_UID, '' ); + $oCriteria->add ( ConfigurationPeer::USR_UID, '' ); + $oCriteria->add ( ConfigurationPeer::APP_UID, '' ); + $flagGettingStarted = ConfigurationPeer::doCount ( $oCriteria ); + $this->memcache->set( 'flagGettingStarted', $flagGettingStarted, 8*3600 ) ; + } + + // if( $flagGettingStarted == 0 ) { + // $oHeadPublisher->addScriptCode( 'var flagGettingStarted = 1; '); + // } + // else + // $oHeadPublisher->addScriptCode( 'var flagGettingStarted = 0; '); + + G::loadClass('configuration'); + $oConf = new Configurations; + $oConf->loadConfig($obj, 'ENVIRONMENT_SETTINGS',''); + + $flagForgotPassword = isset($oConf->aConfig['login_enableForgotPassword'])? $oConf->aConfig['login_enableForgotPassword']: 'off'; + //$oHeadPublisher->addScriptCode("var flagForgotPassword = '$flagForgotPassword';"); + + $this->includeExtJSLib('ux/virtualkeyboard'); + $this->includeExtJS('main/login'); + $this->setView('main/login'); + + $oConf->loadConfig($obj, 'ENVIRONMENT_SETTINGS',''); + + $forgotPasswd = isset($oConf->aConfig['login_enableForgotPassword'])? $oConf->aConfig['login_enableForgotPassword']: false; + $virtualKeyboad = isset($oConf->aConfig['login_enableVirtualKeyboard'])? $oConf->aConfig['login_enableVirtualKeyboard']: false; + $defaultLanguaje = isset($oConf->aConfig['login_defaultLanguage'])? $oConf->aConfig['login_defaultLanguage']: 'en'; + + + $this->setJSVar('forgotPasswd', $forgotPasswd); + $this->setJSVar('virtualKeyboad', $virtualKeyboad); + + $this->setJSVar('languages', $availableLangArray); + $this->setJSVar('defaultLang', $defaultLanguaje); + + //binding G::SendTemporalMessage() to Ext.msgBoxSlider.msgTopCenter() + if (($flyNotify = $this->getFlyNotify()) !== false) { + $this->setJSVar('flyNotify', $flyNotify); + } + + //binding G::SendTemporalMessage() to Ext.msgBoxSlider.msgTopCenter() + if (isset($_GET['u'])) { + $this->setJSVar('urlRequested', urldecode($_GET['u'])); + } + + $this->setVar('logo_company', $this->getCompanyLogo()); + $this->setVar('pmos_version', System::getVersion()); + + $footerText = 'Copyright © 2003-2011 Colosa, Inc. All rights reserved.'; + $adviseText = 'Supplied free of charge with no support, certification, warranty, + maintenance nor indemnity by Colosa and its Certified Partners. '; + $this->setVar('footer_text', $footerText); + $this->setVar('advise_text', $adviseText); + $loginScript = $this->getHeadPublisher()->getExtJsLibraries(); + $loginScript .= $this->getHeadPublisher()->getExtJsScripts(); + $this->setVar('login_script', $loginScript); + $this->setVar('login_vars', $this->getHeadPublisher()->getExtJsVariablesScript()); + + G::RenderPage('publish', 'plain'); + } + + /** + * SysLogin + */ + public function sysLogin() + { + require_once ("propel/Propel.php"); + require_once ("creole/Creole.php"); + G::LoadClass('system'); + G::LoadThirdParty ( "pake", "pakeColor.class" ); + Propel::init ( PATH_CORE . "config/databases.php" ); + Creole::registerDriver ( 'dbarray', 'creole.contrib.DBArrayConnection' ); + + // getting posibles errors passed by GET method + $this->getInUrlError(); + + $availableWorkspace = $this->getWorkspacesAvailable(); + $availableWorkspaceList = array(); + + foreach ($availableWorkspace as $ws) { + $availableWorkspaceList[] = array($ws, $ws); + } + + $aField ['LOGIN_VERIFY_MSG'] = G::loadTranslation('LOGIN_VERIFY_MSG'); + + //Get Server Configuration + G::LoadClass ( 'serverConfiguration' ); + $oServerConf = & serverConf::getSingleton (); + + $availableLangArray = $this->getLanguagesList(); + + $this->includeExtJSLib('ux/virtualkeyboard'); + $this->includeExtJS('main/sysLogin'); + $this->setView('main/sysLogin'); + + $this->setVar('logo_company', $this->getCompanyLogo()); + $this->setVar('pmos_version', System::getVersion()); + + $footerText = 'Copyright © 2003-2011 Colosa, Inc. All rights reserved.'; + $adviseText = 'Supplied free of charge with no support, certification, warranty, + maintenance nor indemnity by Colosa and its Certified Partners. '; + $this->setVar('footer_text', $footerText); + $this->setVar('advise_text', $adviseText); + + //binding G::SendTemporalMessage() to Ext.msgBoxSlider.msgTopCenter() + if (($flyNotify = $this->getFlyNotify()) !== false) { + $this->setJSVar('flyNotify', $flyNotify); + } + + $this->setJSVar('languages', $availableLangArray); + $this->setJSVar('workspaces', $availableWorkspaceList); + $this->setJSVar('wsPrivate', $oServerConf->getProperty('LOGIN_NO_WS')); + + $this->setJSVar('defaultLang', 'en'); + $this->setJSVar('defaultWS', ''); + + $loginScript = $this->getHeadPublisher()->getExtJsLibraries(); + $loginScript .= $this->getHeadPublisher()->getExtJsScripts(); + $this->setVar('login_script', $loginScript); + $this->setVar('login_vars', $this->getHeadPublisher()->getExtJsVariablesScript()); + + G::RenderPage('publish', 'plain'); + + } + + public function sysLoginVerify() + { + $this->setResponseType('json'); + + try { + if (!defined('SYS_SYS')) { + if (!isset($_POST)) { + throw new Exception('Bad Request!', ERROR_EXCEPTION); + } + + if (!isset($_POST['workspace'])) { + throw new Exception('No workspace specified!', ERROR_EXCEPTION); + } + + session_start(); + $_SESSION['sysLogin'] = $_POST; + + $url = '/sys' . $_POST['workspace'] . '/' . SYS_LANG . '/' . SYS_SKIN . '/main/sysLoginVerify'; + + $result->success = true; + $result->url = $url; + $result->message = G::LoadTranslation('LOGIN_VERIFY_MSG'); + } else { + $_POST = $_SESSION['sysLogin']; + $result = $this->authentication(); + + if ($result->success) { + G::header ( 'location: ' . $result->url); + } else { + G::SendTemporalMessage($result->message, "error", 'string'); + G::header('location: /sys' . $_POST['workspace'] . '/' . SYS_LANG . '/' . SYS_SKIN . '/main/login'); + } + } + } catch ( Exception $e ) { + $result->success = false; + $result->message = $e->getMessage(); + $result->errorCode = $e->getCode(); + } + + return $result; + } + + public function authentication() + { + global $RBAC; + + $this->setResponseType('json'); + + try { + if (!$RBAC->singleSignOn) { + if (!isset($_POST['user']) ) { + throw new Exception(G::LoadTranslation('ID_USER_HAVENT_RIGHTS_SYSTEM')); + } + + $frm = $_POST; + $usr = ''; + $pwd = ''; + $lang = ''; + + if (isset($frm['user'])) { + $usr = strtolower(trim($frm['user'])); + $pwd = $currentPwd = trim($frm['password']); + $lang = trim($frm['lang']) != '' ? $frm['lang'] : SYS_LANG; + } + + $uid = $RBAC->VerifyLogin($usr , $pwd); + + switch ($uid) { + //The user doesn't exist + case -1: + throw new Exception(G::LoadTranslation('ID_USER_NOT_REGISTERED')); + break; + //The password is incorrect + case -2: + throw new Exception(G::LoadTranslation('ID_WRONG_PASS')); + + if(isset($_SESSION['__AUTH_ERROR__'])){ + G::SendMessageText($_SESSION['__AUTH_ERROR__'], "warning"); + unset($_SESSION['__AUTH_ERROR__']); + } + break; + //The user is inactive + case -3: + require_once 'classes/model/Users.php'; + $user = new Users; + $aUser = $user->loadByUsernameInArray($usr); + switch($aUser['USR_STATUS']){ + case 'VACATION': + throw new Exception(G::LoadTranslation('ID_USER_ONVACATION', WARNING_EXCEPTION)); + break; + case 'INACTIVE': + throw new Exception(G::LoadTranslation('ID_USER_INACTIVE', WARNING_EXCEPTION)); + break; + } + break; + //The Due date is finished + case -4: + throw new Exception(G::LoadTranslation('ID_USER_INACTIVE_BY_DATE', WARNING_EXCEPTION)); + break; + case -5: + throw new Exception(G::LoadTranslation('ID_AUTHENTICATION_SOURCE_INVALID', WARNING_EXCEPTION)); + break; + } + + //to avoid empty string in user field. This will avoid a weird message "this row doesn't exist" + if ( !isset($uid) ) { + $uid = -1; + throw new Exception(G::LoadTranslation('ID_USER_NOT_REGISTERED', WARNING_EXCEPTION)); + } + + if ( !isset($uid) || $uid < 0 ) { + if(isset($_SESSION['FAILED_LOGINS'])) + $_SESSION['FAILED_LOGINS']++; + if (!defined('PPP_FAILED_LOGINS')) { + define('PPP_FAILED_LOGINS', 0); + } + if (PPP_FAILED_LOGINS > 0) { + if ($_SESSION['FAILED_LOGINS'] >= PPP_FAILED_LOGINS) { + $oConnection = Propel::getConnection('rbac'); + $oStatement = $oConnection->prepareStatement("SELECT USR_UID FROM USERS WHERE USR_USERNAME = '" . $usr . "'"); + $oDataset = $oStatement->executeQuery(); + if ($oDataset->next()) { + $sUserUID = $oDataset->getString('USR_UID'); + $oConnection = Propel::getConnection('rbac'); + $oStatement = $oConnection->prepareStatement("UPDATE USERS SET USR_STATUS = 0 WHERE USR_UID = '" . $sUserUID . "'"); + $oStatement->executeQuery(); + $oConnection = Propel::getConnection('workflow'); + $oStatement = $oConnection->prepareStatement("UPDATE USERS SET USR_STATUS = 'INACTIVE' WHERE USR_UID = '" . $sUserUID . "'"); + $oStatement->executeQuery(); + unset($_SESSION['FAILED_LOGINS']); + G::SendMessageText(G::LoadTranslation('ID_ACCOUNT') . ' "' . $usr . '" ' . G::LoadTranslation('ID_ACCOUNT_DISABLED_CONTACT_ADMIN'), 'warning'); + } + else { + //Nothing + } + } + } + + //TODO complete this redirection + G::header ("location: login.html"); + die; + } + + //Execute the SSO Script from plugin + $oPluginRegistry =& PMPluginRegistry::getSingleton(); + if ( $oPluginRegistry->existsTrigger ( PM_LOGIN ) ) { + $lSession=""; + $loginInfo = new loginInfo ($usr, $pwd, $lSession ); + $oPluginRegistry->executeTriggers ( PM_LOGIN , $loginInfo ); + } + + $_SESSION['USER_LOGGED'] = $uid; + $_SESSION['USR_USERNAME'] = $usr; + } + else { + $uid = $RBAC->userObj->fields['USR_UID']; + $usr = $RBAC->userObj->fields['USR_USERNAME']; + $_SESSION['USER_LOGGED'] = $uid; + $_SESSION['USR_USERNAME'] = $usr; + } + + if (!isset($_SESSION['WORKSPACE'])) { + $_SESSION['WORKSPACE'] = SYS_SYS; + } + + // Assign the uid of user to userloggedobj + $RBAC->loadUserRolePermission($RBAC->sSystem, $uid); + $res = $RBAC->userCanAccess('PM_LOGIN'); + + if ($res != 1 ) { + if ($res == -2) + throw new Exception(G::LoadTranslation('ID_USER_HAVENT_RIGHTS_SYSTEM', ERROR_EXCEPTION)); + else + throw new Exception(G::LoadTranslation('ID_USER_HAVENT_RIGHTS_PAGE', ERROR_EXCEPTION)); + die; + } + + //using optimization with memcache, the user data will be in memcache 8 hours + $memKey = 'rbacSession' . session_id(); + $this->memcache->set( $memKey, $RBAC->aUserInfo, PMmemcached::EIGHT_HOURS ); + + $_SESSION['USR_FULLNAME'] = $RBAC->aUserInfo['USER_INFO']['USR_FIRSTNAME'] . ' ' . $RBAC->aUserInfo['USER_INFO']['USR_LASTNAME']; + unset($_SESSION['FAILED_LOGINS']); + + //get the default language, language posted in form has preference + if ( isset($frm['USER_LANG']) && $frm['USER_LANG'] != '' ) { + $lang = $frm['USER_LANG']; + } + else { + $lang = (defined('SYS_LANG')) ? SYS_LANG : 'en'; + } + + /**log in table Login**/ + require_once 'classes/model/LoginLog.php'; + $weblog=new LoginLog(); + $aLog['LOG_UID'] = G::generateUniqueID(); + $aLog['LOG_STATUS'] = 'ACTIVE'; + $aLog['LOG_IP'] = $_SERVER['REMOTE_ADDR']; + $aLog['LOG_SID'] = session_id(); + $aLog['LOG_INIT_DATE'] = date('Y-m-d H:i:s'); + $aLog['LOG_CLIENT_HOSTNAME'] = $_SERVER['HTTP_HOST']; + $aLog['USR_UID'] = $_SESSION['USER_LOGGED']; + $aLog['LOG_TYPE'] = (isset($RBAC->aUserInfo['USER_INFO']['USR_AUTH_TYPE']) ? ($RBAC->aUserInfo['USER_INFO']['USR_AUTH_TYPE'] != 'MYSQL' ? $RBAC->aUserInfo['USER_INFO']['USR_AUTH_TYPE'] : 'processmaker') : 'processmaker'); + $aLog['LOG_SSO'] = (int) $RBAC->singleSignOn; + $weblog->create($aLog); + /**end log**/ + + // increment logins in heartbeat + G::LoadClass('serverConfiguration'); + $oServerConf =& serverConf::getSingleton(); + $oServerConf->sucessfulLogin(); + + // disabling this slow process, until revisit this issue + // //************** background processes, here we are putting some back office routines ********** + // $oServerConf->setWsInfo(SYS_SYS,$oServerConf->getWorkspaceInfo(SYS_SYS) ); + // + // + // //**** defining and saving server info, this file has the values of the global array $_SERVER **** + // //this file is useful for command line environment (no Browser), I mean for triggers, crons and other executed over command line + // $_CSERVER = $_SERVER; + // unset($_CSERVER['REQUEST_TIME']); + // unset($_CSERVER['REMOTE_PORT']); + // $cput = serialize($_CSERVER); + // if( !is_file(PATH_DATA_SITE . PATH_SEP . '.server_info') ){ + // file_put_contents(PATH_DATA_SITE . PATH_SEP . '.server_info', $cput); + // } + // else { + // $c = file_get_contents(PATH_DATA_SITE . PATH_SEP . '.server_info'); + // if(md5($c) != md5($cput)){ + // file_put_contents(PATH_DATA_SITE . PATH_SEP . '.server_info', $cput); + // } + // } + // + + + /* Check password using policy - Start */ + require_once 'classes/model/UsersProperties.php'; + $oUserProperty = new UsersProperties(); + if (!$RBAC->singleSignOn) { + $aUserProperty = $oUserProperty->loadOrCreateIfNotExists($_SESSION['USER_LOGGED'], array('USR_PASSWORD_HISTORY' => serialize(array(md5($currentPwd))))); + $aErrors = $oUserProperty->validatePassword($pwd, $aUserProperty['USR_LAST_UPDATE_DATE'], $aUserProperty['USR_LOGGED_NEXT_TIME']); + + if (!empty($aErrors)) { + if (!defined('NO_DISPLAY_USERNAME')) { + define('NO_DISPLAY_USERNAME', 1); + } + $aFields = array(); + $aFields['DESCRIPTION'] = ''; + $aFields['DESCRIPTION'] .= G::LoadTranslation('ID_POLICY_ALERT').':

'; + foreach ($aErrors as $sError) { + switch ($sError) { + case 'ID_PPP_MINIMUM_LENGTH': + $aFields['DESCRIPTION'] .= ' - ' . G::LoadTranslation($sError).': ' . PPP_MINIMUM_LENGTH . '
'; + $aFields[substr($sError, 3)] = PPP_MINIMUM_LENGTH; + break; + case 'ID_PPP_MAXIMUM_LENGTH': + $aFields['DESCRIPTION'] .= ' - ' . G::LoadTranslation($sError).': ' . PPP_MAXIMUM_LENGTH . '
'; + $aFields[substr($sError, 3)] = PPP_MAXIMUM_LENGTH; + break; + case 'ID_PPP_EXPIRATION_IN': + $aFields['DESCRIPTION'] .= ' - ' . G::LoadTranslation($sError).' ' . PPP_EXPIRATION_IN . ' ' . G::LoadTranslation('ID_DAYS') . '
'; + $aFields[substr($sError, 3)] = PPP_EXPIRATION_IN; + break; + default: + $aFields['DESCRIPTION'] .= ' - ' . G::LoadTranslation($sError).'
'; + $aFields[substr($sError, 3)] = 1; + break; + } + } + + //TODO complete this policy section (need update) + $aFields['DESCRIPTION'] .= '
' . G::LoadTranslation('ID_PLEASE_CHANGE_PASSWORD_POLICY') . '

'; + //TODO need migrate to ExtJS + // $G_PUBLISH = new Publisher; + // $G_PUBLISH->AddContent('xmlform', 'xmlform', 'login/changePassword', '', $aFields, 'changePassword'); + // G::RenderPage('publish'); + //die; + } + } + + $sLocation = $oUserProperty->redirectTo($_SESSION['USER_LOGGED'], $lang); + + $result->success = true; + $result->message = 'Logged Successfully'; + + $result->url = $sLocation; + } + catch ( Exception $e ) { + $result->success = false; + $result->message = $e->getMessage(); + $result->errorCode = $e->getCode(); + } + + return $result; + } + + + + public function forgotPassword($httpData) + { + $this->setResponseType('json'); + global $RBAC; + require_once PATH_RBAC."model/RbacUsers.php"; + require_once 'classes/model/Users.php'; + G::LoadClass("system"); + + $rbacUser = new RbacUsers(); + $user = new Users(); + + try { + $userData = $rbacUser->getByUsername($httpData->username); + + if (!$userData) { + $msg = G::LoadTranslation('ID_USER') . ' ' . $httpData->username . ' '. G::LoadTranslation('ID_IS_NOT_REGISTERED'); + throw new Exception($msg); + } + + if (trim($userData['USR_EMAIL']) != trim($httpData->email)) { + $msg = G::LoadTranslation('ID_EMAIL_DOES_NOT_MATCH_FOR_USER') .' '. $httpData->username . ''; + throw new Exception($msg); + } + + $newPass = G::generate_password(); + + $aData['USR_UID'] = $userData['USR_UID']; + $aData['USR_PASSWORD'] = md5($newPass); + + $rbacUser->update($aData); + $user->update($aData); + + $subject = G::loadTranslation('ID_PROCESSMAKER_FORGOT_PASSWORD_SERVICE'); + + $template = new TemplatePower(PATH_TPL . 'main/forgotPassword.tpl'); + $template->prepare(); + $template->assign('server', $_SERVER['SERVER_NAME']); + + $template->assign('serviceMsg', G::loadTranslation('ID_PROCESSMAKER_FORGOT_PASSWORD_SERVICE')); + $template->assign('content', G::loadTranslation('ID_PASSWORD_CHANGED_SUCCESSFULLY')); + $template->assign('passwd', $newPass); + $template->assign('poweredBy', G::loadTranslation('ID_PROCESSMAKER_SLOGAN1')); + $template->assign('versionLabel', G::loadTranslation('ID_VERSION')); + $template->assign('version', System::getVersion()); + $template->assign('visit', G::loadTranslation('ID_VISIT')); + + $template->assign('footer', ''); + $body = $template->getOutputContent(); + + G::sendMail('', 'ProcessMaker Service', $httpData->email, $subject, $body); + + $result->success = true; + $result->message = G::LoadTranslation('ID_NEW_PASSWORD_SENT'); + } catch(Exception $e) { + $result->success = false; + $result->message = $e->getMessage(); + } + return $result; + } + + /** * + * Private Functions * + * **/ + + private function getMenu() + { + global $G_MAIN_MENU; + global $G_SUB_MENU; + global $G_MENU_SELECTED; + global $G_SUB_MENU_SELECTED; + global $G_ID_MENU_SELECTED; + global $G_ID_SUB_MENU_SELECTED; + + $G_MAIN_MENU = 'processmaker'; + $G_SUB_MENU = 'process'; + $G_ID_MENU_SELECTED = 'BPMN'; + + $oMenu = new Menu(); + $menus = $oMenu->generateArrayForTemplate($G_MAIN_MENU, 'SelectedMenu', 'mainMenu', $G_MENU_SELECTED, $G_ID_MENU_SELECTED); + + foreach ($menus as $i => $menu) { + if (strpos($menu['target'], 'cases/main') !== false) + $menus[$i]['target'] = str_replace('cases/main', 'cases/main_init', $menus[$i]['target']); + + if (strpos($menu['target'], 'bpmn/main') !== false) + $menus[$i]['target'] = str_replace('bpmn/main', 'bpmn/mainInit', $menus[$i]['target']); + + if (strpos($menu['target'], 'processes/main') !== false) + $menus[$i]['target'] = str_replace('processes/main', 'processes/mainInit', $menus[$i]['target']); + + if (strpos($menu['target'], 'setup/main') !== false) { + $menus[$i]['target'] = str_replace('setup/main', 'setup/main_init', $menus[$i]['target']); + + // $param = ''; + // if (isset($_GET['i18']) || isset($_GET['i18n'])) { + // $s = strpos($menus[$i]['target'], '?') !== false ? '&' : '?'; + // $menus[$i]['target'] .= $s . 'i18'; + // } + } + + if (strpos($menu['target'], 'dashboard/main') !== false) + $menus[$i]['target'] = str_replace('dashboard/main', 'dashboard', $menus[$i]['target']); + + $menus[$i]['elementclass'] = preg_replace(array('/class=/', '/"/'), array('', ''), $menus[$i]['elementclass']) ; + } + return $menus; + } + + private function getCompanyLogo() + { + $sCompanyLogo = '/images/processmaker2.logo.png'; + //$sCompanyLogo = '/images/logo_processmaker2.gif'; + + if(defined("SYS_SYS")){ + if ( ($aFotoSelect = $this->memcache->get('aFotoSelect')) === false ) { + G::LoadClass( 'replacementLogo' ); + $oLogoR = new replacementLogo(); + $aFotoSelect = $oLogoR->getNameLogo((isset($_SESSION['USER_LOGGED']))?$_SESSION['USER_LOGGED']:''); + $this->memcache->set( 'aFotoSelect', $aFotoSelect, 1*3600 ); + } + + if ( is_array ( $aFotoSelect ) ) { + $sFotoSelect = trim($aFotoSelect['DEFAULT_LOGO_NAME']); + $sWspaceSelect = trim($aFotoSelect['WORKSPACE_LOGO_NAME']); + } + } + + if (class_exists('PMPluginRegistry')) { + $oPluginRegistry = &PMPluginRegistry::getSingleton(); + + if ( isset($sFotoSelect) && $sFotoSelect!='' && !(strcmp($sWspaceSelect,SYS_SYS)) ){ + $sCompanyLogo = $oPluginRegistry->getCompanyLogo($sFotoSelect); + $sCompanyLogo = "/sys".SYS_SYS."/".SYS_LANG."/".SYS_SKIN."/adminProxy/showLogoFile?id=".base64_encode($sCompanyLogo); + } + else { + $sCompanyLogo = $oPluginRegistry->getCompanyLogo($sCompanyLogo); + } + } + + return $sCompanyLogo; + } + + public function getLanguagesList() + { + //translation + $Translations = G::getModel("Translation"); + $translationsTable = $Translations->getTranslationEnvironments(); + + if ( ($languagesList = $this->memcache->get('languagesList')) === false ) { + $languagesList = array (); + + foreach ( $translationsTable as $locale ) { + $LANG_ID = $locale['LOCALE']; + + if( $locale['COUNTRY'] != '.' ) + $LANG_NAME = $locale['LANGUAGE'] . ' (' . (ucwords(strtolower($locale['COUNTRY']))) . ')'; + else + $LANG_NAME = $locale['LANGUAGE']; + + $languagesList [] = array($LANG_ID, $LANG_NAME); + } + $this->memcache->set('languagesList', $languagesList, 1*3600 ); + } + + return $languagesList; + } + + private function getWorkspacesAvailable() { + G::LoadClass ( 'serverConfiguration' ); + $oServerConf = & serverConf::getSingleton (); + $dir = PATH_DB; + $filesArray = array (); + if (file_exists ( $dir )) { + if ($handle = opendir ( $dir )) { + while ( false !== ($file = readdir ( $handle )) ) { + if (($file != ".") && ($file != "..")) { + if (file_exists ( PATH_DB . $file . '/db.php' )) { + if (! $oServerConf->isWSDisabled ( $file )) + $filesArray [] = $file; + } + } + } + closedir ( $handle ); + } + } + sort ( $filesArray, SORT_STRING ); + return $filesArray; + } + + private function getUserRole() + { + global $RBAC; + $rolCode = str_replace('_', ' ', $RBAC->aUserInfo['PROCESSMAKER']['ROLE']['ROL_CODE']); + $rolUid = $RBAC->aUserInfo['PROCESSMAKER']['ROLE']['ROL_UID']; + + $oCriteria1 = new Criteria('workflow'); + $oCriteria1->add(ContentPeer::CON_CATEGORY, 'ROL_NAME'); + $oCriteria1->add(ContentPeer::CON_ID, $rolUid); + $oCriteria1->add(ContentPeer::CON_LANG, SYS_LANG); + $oDataset1 = ContentPeer::doSelectRS($oCriteria1); + $oDataset1->setFetchmode(ResultSet::FETCHMODE_ASSOC); + $oDataset1->next(); + $aRow = $oDataset1->getRow(); + $rolName = $aRow['CON_VALUE']; + + return $rolName ? $rolName : $rolCode; + } + + + /** + * binding G::SendTemporalMessage() to Javascript routine Ext.msgBoxSlider.msgTopCenter() + */ + private function getFlyNotify() + { + if (!isset($_SESSION['G_MESSAGE'])) { + return false; + } + + $flyNotify['title'] = isset($_SESSION['G_MESSAGE_TITLE']) ? $_SESSION['G_MESSAGE_TITLE'] : ''; + $flyNotify['text'] = $_SESSION['G_MESSAGE']; + + unset($_SESSION['G_MESSAGE']); + if (isset($_SESSION['G_MESSAGE_TYPE'])) { + $flyNotify['type'] = $_SESSION['G_MESSAGE_TYPE']; + unset($_SESSION['G_MESSAGE_TYPE']); + } + else { + $flyNotify['type'] = ''; + } + + if ($flyNotify['title'] == '') { + switch($flyNotify['type']) { + case 'alert': case 'warning': case 'tmp-warning': + $flyNotify['title'] = G::loadTranslation('ID_WARNING'); + break; + case 'error': case 'tmp-error': + $flyNotify['title'] = G::loadTranslation('ID_ERROR'); + break; + case 'tmp-info': case 'info': + $flyNotify['title'] = G::loadTranslation('ID_INFO'); + break; + case 'success': case 'ok': + $flyNotify['title'] = G::loadTranslation('ID_SUCCESS'); + break; + } + $flyNotify['title'] = strtoupper($flyNotify['title']); + } + + $flyNotify['time'] = 5; //TODO make dinamic + $this->flyNotify = $flyNotify; + + return $this->flyNotify; + } + + private function setFlyNotify($type, $title, $text, $time = 5) + { + $this->flyNotify = array( + 'type' => $type, + 'title' => $title, + 'text' => $text, + 'time' => $time + ); + + $_SESSION ['G_MESSAGE'] = $text; + $_SESSION ['G_MESSAGE_TYPE'] = $type; + } + + private function getInUrlError() + { + if (isset($_GET['errno'])) { + switch ($_GET['errno']) { + case '1': $trnLabel = 'ID_USER_HAVENT_RIGHTS_PAGE'; break; + case '2': $trnLabel = 'ID_NOT_WORKSPACE'; break; + default : $trnLabel = 'ID_USER_HAVENT_RIGHTS_PAGE'; + } + + $this->setFlyNotify('error', 'ERROR', G::loadTranslation($trnLabel)); + } + } + + private function getActiveTab() + { + $activeTab = ''; + if (isset($_GET['st'])) { + $activeTab = $_GET['st']; + unset($_GET['st']); + if (!is_numeric($activeTab)) { + switch ($activeTab) { + case 'home' : $activeTab = 0; break; + case 'bpmn' : $activeTab = 1; break; + case 'designer': $activeTab = 2; break; + case 'admin' : $activeTab = 3; break; + default: $activeTab = 0; + } + } + else { + $activeTab = $activeTab > -1 && $activeTab < 3 ? (int) $activeTab : ''; + } + } + + return $activeTab; + } + + private function getUrlGetParams() + { + $urlGetParams = ''; + foreach ($_GET as $key => $value) { + $urlGetParams .= $urlGetParams == '' ? $key : "&" . $key; + $urlGetParams .= trim($value) != '' ? '=' . $value : ''; + } + return $urlGetParams; + } + + private function getUserFullName() + { + return isset($_SESSION['USR_FULLNAME']) ? $_SESSION['USR_FULLNAME'] : ''; + } + + private function _getSystemInfo() + { + G::LoadClass("system"); + + if (getenv ( 'HTTP_CLIENT_IP' )) { + $ip = getenv ( 'HTTP_CLIENT_IP' ); + } + else { + if (getenv ( 'HTTP_X_FORWARDED_FOR' )) { + $ip = getenv ( 'HTTP_X_FORWARDED_FOR' ); + } + else { + $ip = getenv ( 'REMOTE_ADDR' ); + } + } + + $redhat = ''; + if (file_exists ( '/etc/redhat-release' )) { + $fnewsize = filesize ( '/etc/redhat-release' ); + $fp = fopen ( '/etc/redhat-release', 'r' ); + $redhat = trim ( fread ( $fp, $fnewsize ) ); + fclose ( $fp ); + } + + $redhat .= " (" . PHP_OS . ")"; + if (defined ( "DB_HOST" )) { + G::LoadClass ( 'net' ); + G::LoadClass ( 'dbConnections' ); + $dbNetView = new NET ( DB_HOST ); + $dbNetView->loginDbServer ( DB_USER, DB_PASS ); + + $dbConns = new dbConnections ( '' ); + $availdb = ''; + foreach ( $dbConns->getDbServicesAvailables () as $key => $val ) { + if ($availdb != '') + $availdb .= ', '; + $availdb .= $val ['name']; + } + + try { + + $sMySQLVersion = $dbNetView->getDbServerVersion ( DB_ADAPTER ); + + } + catch ( Exception $oException ) { + $sMySQLVersion = '?????'; + } + } + + $sysSection = G::loadTranslation('ID_SYSTEM_INFO'); + $pmSection = 'ProcessMaker Information'; + + $properties = array(); + $ee = class_exists('pmLicenseManager') ? " - Enterprise Edition" : ''; + $properties[] = array('ProcessMaker Ver.', System::getVersion() . $ee, $pmSection); + $properties[] = array('Operating System', $redhat, $sysSection); + $properties[] = array('Time Zone', (defined('TIME_ZONE')) ? TIME_ZONE : "Unknown", $sysSection); + $properties[] = array('Web Server', getenv ( 'SERVER_SOFTWARE'), $sysSection); + $properties[] = array('Server Name', getenv ( 'SERVER_NAME'), $pmSection); + $properties[] = array('Server IP Address', $this->lookup ($ip), $sysSection); + $properties[] = array('PHP Version', phpversion(), $sysSection); + + if (defined ( "DB_HOST" )) { + $properties[] = array('Data Base', $dbNetView->dbName(DB_ADAPTER) . ' (Version ' . $sMySQLVersion . ')', $pmSection); + $properties[] = array('Data Base Server', DB_HOST, $pmSection); + $properties[] = array('Data Base Name', DB_NAME, $pmSection); + $properties[] = array('Available DB Engines', $availdb, $sysSection); + } + else { + $properties[] = array('Data Base', "Not defined", $pmSection); + $properties[] = array('Data Base Server', "Not defined", $pmSection); + $properties[] = array('Data Base Name', "Not defined", $pmSection); + $properties[] = array('Available DB Engines', "Not defined", $sysSection); + } + + $properties[] = array('Workspace', defined ( "SYS_SYS" ) ? SYS_SYS : "Not defined", $pmSection); + + + $properties[] = array('Server Protocol', getenv ( 'SERVER_PROTOCOL'), $sysSection); + $properties[] = array('Server Port', getenv ( 'SERVER_PORT'), $sysSection); + //$properties[] = array('Remote Host', getenv ( 'REMOTE_HOST'), $sysSection); + $properties[] = array('Server Addr.', getenv ( 'SERVER_ADDR'), $sysSection); + $properties[] = array('User\'s Browser', getenv ( 'HTTP_USER_AGENT'), $sysSection); + + + return $properties; + } + + private function lookup( $target ) { + global $ntarget; + $msg = $target . ' => '; + //if (eregi ( '[a-zA-Z]', $target )) + if (preg_match( '[a-zA-Z]', $target )) //Made compatible to PHP 5.3 + $ntarget = gethostbyname ( $target ); + else + $ntarget = gethostbyaddr ( $target ); + $msg .= $ntarget; + return ($msg); + } +} \ No newline at end of file diff --git a/workflow/engine/menus/processmaker.php b/workflow/engine/menus/processmaker.php index 2f5648f81..cb7ebf47d 100755 --- a/workflow/engine/menus/processmaker.php +++ b/workflow/engine/menus/processmaker.php @@ -24,40 +24,35 @@ */ -# -# ---= Processmaker main menu=--- -# +/************************************* + * ---= Processmaker main menu=--- + *************************************/ global $G_TMP_MENU; global $RBAC; -if ($RBAC->userCanAccess('PM_DASHBOARD') == 1 ) { - //$G_TMP_MENU->AddIdRawOption('DASHBOARD', 'dashboard/dashboard', G::LoadTranslation('ID_DASHBOARD')); -} - -#CASES MODULE +// HOME MODULE if ($RBAC->userCanAccess('PM_CASES') == 1) { - $G_TMP_MENU->AddIdRawOption('CASES', 'cases/main', G::LoadTranslation('ID_HOME')); + $G_TMP_MENU->AddIdRawOption('CASES', 'cases/main', G::LoadTranslation('ID_HOME'), '', '', '', 'x-pm-home'); } -#PROCESSES MODULE +// DESIGNER MODULE if ($RBAC->userCanAccess('PM_FACTORY') == 1 ) { - $G_TMP_MENU->AddIdRawOption('PROCESSES', 'processes/main', G::LoadTranslation('ID_DESIGNER')); + //$G_TMP_MENU->AddIdRawOption('BPMN', 'bpmn/main', 'BPMN DESIGNER','', '', '', 'x-pm-bpmn'); + $G_TMP_MENU->AddIdRawOption('PROCESSES', 'processes/main', G::LoadTranslation('ID_DESIGNER'), '', '', '', 'x-pm-designer'); } // DASHBOARD MODULE if ($RBAC->userCanAccess('PM_DASHBOARD') == 1) { - $G_TMP_MENU->AddIdRawOption('DASHBOARD', 'dashboard/main', G::LoadTranslation('ID_DASHBOARD')); + $G_TMP_MENU->AddIdRawOption('DASHBOARD', 'dashboard/main', G::LoadTranslation('ID_DASHBOARD'), '', '', '', 'x-pm-dashboard'); } -/*if ($RBAC->userCanAccess('PM_REPORTS') == 1 ) { - $G_TMP_MENU->AddIdRawOption('REPORTS', 'reports/reportsList'); -}*/ - +// ADMIN MODULE if ($RBAC->userCanAccess('PM_SETUP') == 1 || $RBAC->userCanAccess('PM_USERS') == 1) { - $G_TMP_MENU->AddIdRawOption('SETUP', 'setup/main', G::LoadTranslation('ID_SETUP')); + $G_TMP_MENU->AddIdRawOption('SETUP', 'setup/main', G::LoadTranslation('ID_SETUP'), '', '', '', 'x-pm-setup'); } +// PLUGINS MENUS if( file_exists(PATH_CORE . 'menus/plugin.php') ) { require_once(PATH_CORE . 'menus/plugin.php'); } \ No newline at end of file diff --git a/workflow/engine/menus/setup.php b/workflow/engine/menus/setup.php index 6858ab061..55228f116 100755 --- a/workflow/engine/menus/setup.php +++ b/workflow/engine/menus/setup.php @@ -79,6 +79,7 @@ if ($RBAC->userCanAccess('PM_SETUP') == 1 || $RBAC->userCanAccess('PM_USERS') == if ($RBAC->userCanAccess('PM_SETUP_ADVANCE') == 1) { $G_TMP_MENU->AddIdRawOption('AUTHSOURCES', '../authSources/authSources_List', G::LoadTranslation('ID_AUTH_SOURCES'), '', '', 'users'); $G_TMP_MENU->AddIdRawOption('UX', '../admin/uxList', G::LoadTranslation('ID_USER_EXPERIENCE'), '', '', 'users'); + $G_TMP_MENU->AddIdRawOption('SYSTEM', '../admin/system', G::LoadTranslation('ID_SYSTEM'), '', '', 'settings'); } diff --git a/workflow/engine/methods/cases/casesListExtJs.php b/workflow/engine/methods/cases/casesListExtJs.php index 39d5f4069..0a0ef830e 100755 --- a/workflow/engine/methods/cases/casesListExtJs.php +++ b/workflow/engine/methods/cases/casesListExtJs.php @@ -115,7 +115,6 @@ G::LoadClass('configuration'); $c = new Configurations(); - $oHeadPublisher->usingExtJs('ux/GridRowActions'); //$oHeadPublisher->addExtJsScript('cases/caseUtils', true); $oHeadPublisher->addExtJsScript('app/main', true); $oHeadPublisher->addExtJsScript('cases/casesList', false ); //adding a javascript file .js diff --git a/workflow/engine/methods/cases/casesStartPage.php b/workflow/engine/methods/cases/casesStartPage.php index 5dcd078eb..760d016ef 100755 --- a/workflow/engine/methods/cases/casesStartPage.php +++ b/workflow/engine/methods/cases/casesStartPage.php @@ -12,8 +12,6 @@ $oHeadPublisher =& headPublisher::getSingleton(); global $RBAC; switch($page){ case "startCase": - - $oHeadPublisher->usingExtJs('ux.treefilterx/Ext.ux.tree.TreeFilterX'); $oHeadPublisher->addExtJsScript('cases/casesStartCase', true); //adding a javascript file .js $oHeadPublisher->addContent( 'cases/casesStartCase'); //adding a html file .html. @@ -35,9 +33,6 @@ switch($page){ $oHeadPublisher->assign('permitoaddfile' ,$RBAC->userCanAccess('PM_FOLDERS_ADD_FILE') ); $oHeadPublisher->assign('permitoaddfolder',$RBAC->userCanAccess('PM_FOLDERS_ADD_FOLDER')); - $oHeadPublisher->usingExtJs('ux.locationbar/Ext.ux.LocationBar'); - $oHeadPublisher->usingExtJs('ux.statusbar/ext-statusbar'); - $oHeadPublisher->addExtJsScript('cases/casesDocuments', false); //adding a javascript file .js $oHeadPublisher->addContent( 'cases/casesDocuments'); //adding a html file .html. break; diff --git a/workflow/engine/methods/cases/cases_Scheduler_Log.php b/workflow/engine/methods/cases/cases_Scheduler_Log.php index a650e91fc..15a9da6a7 100755 --- a/workflow/engine/methods/cases/cases_Scheduler_Log.php +++ b/workflow/engine/methods/cases/cases_Scheduler_Log.php @@ -33,7 +33,6 @@ $Config['pageSize'] = isset($configPage['pageSize']) ? $configPage['pageSize'] : $oHeadPublisher =& headPublisher::getSingleton(); -//$oHeadPublisher->usingExtJs('ux/Ext.ux.fileUploadField'); $oHeadPublisher->addExtJsScript('cases/casesSchedulerLog', false); //adding a javascript file .js $oHeadPublisher->addContent('cases/casesSchedulerLog'); //adding a html file .html. diff --git a/workflow/engine/methods/cases/main_init.php b/workflow/engine/methods/cases/main_init.php index 97329ce2a..4fcdd7640 100755 --- a/workflow/engine/methods/cases/main_init.php +++ b/workflow/engine/methods/cases/main_init.php @@ -27,12 +27,19 @@ $oHeadPublisher->addExtJsScript('cases/main', false ); //adding a javascript file .js $oHeadPublisher->addContent( 'cases/main'); //adding a html file .html. - G::loadClass('configuration'); - $oConf = new Configurations; - $oConf->loadConfig($x, 'USER_PREFERENCES','','',$_SESSION['USER_LOGGED'],''); + $keyMem = 'USER_PREFERENCES'.$_SESSION['USER_LOGGED']; + $memcache = & PMmemcached::getSingleton(SYS_SYS); + if ( ($arrayConfig = $memcache->get($keyMem)) === false ) { + G::loadClass('configuration'); + $oConf = new Configurations; + $oConf->loadConfig($x, 'USER_PREFERENCES','','',$_SESSION['USER_LOGGED'],''); + $arrayConfig = $oConf->aConfig; + $memcache->set( $keyMem, $arrayConfig, PMmemcached::ONE_HOUR); + } + $confDefaultOption=''; - if( sizeof($oConf->Fields) > 0 && isset($oConf->aConfig['DEFAULT_CASES_MENU']) ){ #this user has a configuration record - $confDefaultOption = $oConf->aConfig['DEFAULT_CASES_MENU']; + if( isset($arrayConfig['DEFAULT_CASES_MENU']) ){ #this user has a configuration record + $confDefaultOption = $arrayConfig['DEFAULT_CASES_MENU']; global $G_TMP_MENU; $oMenu = new Menu(); $oMenu->load('cases'); @@ -45,7 +52,8 @@ } $defaultOption = $defaultOption != '' ? $defaultOption : 'casesListExtJs'; - } else { + } + else { $defaultOption = 'casesListExtJs'; $confDefaultOption = 'CASES_INBOX'; } diff --git a/workflow/engine/methods/cases/open.php b/workflow/engine/methods/cases/open.php index 851317abc..625f4ef8e 100755 --- a/workflow/engine/methods/cases/open.php +++ b/workflow/engine/methods/cases/open.php @@ -54,7 +54,7 @@ $conf = new Configurations; $oHeadPublisher =& headPublisher::getSingleton(); - $oHeadPublisher->usingExtJs('ux/miframe'); + $oHeadPublisher->addExtJsScript('app/main', true); $oHeadPublisher->addExtJsScript('cases/open', true); $oHeadPublisher->assign('FORMATS',$conf->getFormats()); diff --git a/workflow/engine/methods/departments/departments.php b/workflow/engine/methods/departments/departments.php index b6afccc1a..871365bce 100755 --- a/workflow/engine/methods/departments/departments.php +++ b/workflow/engine/methods/departments/departments.php @@ -57,7 +57,6 @@ if (($RBAC_Response=$RBAC->userCanAccess("PM_USERS"))!=1) return $RBAC_Response; $oHeadPublisher =& headPublisher::getSingleton(); -//$oHeadPublisher->usingExtJs('ux/Ext.ux.fileUploadField'); $oHeadPublisher->addExtJsScript('departments/departmentList', false); //adding a javascript file .js $oHeadPublisher->addContent('departments/departmentList'); //adding a html file .html. diff --git a/workflow/engine/methods/groups/groupsMembers.php b/workflow/engine/methods/groups/groupsMembers.php index 0f1e5d84a..fe40d43bc 100755 --- a/workflow/engine/methods/groups/groupsMembers.php +++ b/workflow/engine/methods/groups/groupsMembers.php @@ -57,7 +57,6 @@ $G_PUBLISH = new Publisher; $oHeadPublisher =& headPublisher::getSingleton(); -//$oHeadPublisher->usingExtJs('ux/Ext.ux.fileUploadField'); $oHeadPublisher->addExtJsScript('groups/groupsMembers', false); //adding a javascript file .js $oHeadPublisher->addContent('groups/groupsMembers'); //adding a html file .html. diff --git a/workflow/engine/methods/login/authentication.php b/workflow/engine/methods/login/authentication.php index 65522cdaa..67953a60f 100755 --- a/workflow/engine/methods/login/authentication.php +++ b/workflow/engine/methods/login/authentication.php @@ -124,7 +124,13 @@ try { } else { G::SendTemporalMessage($errLabel, "warning"); - $loginUrl = 'login'; + + if (substr(SYS_SKIN, 0, 2) !== 'ux') { + $loginUrl = 'login'; + } + else { + $loginUrl = '../main/login'; + } } G::header("location: $loginUrl"); diff --git a/workflow/engine/methods/roles/roles_List.php b/workflow/engine/methods/roles/roles_List.php index 798af10ed..6b5c60fea 100755 --- a/workflow/engine/methods/roles/roles_List.php +++ b/workflow/engine/methods/roles/roles_List.php @@ -55,7 +55,6 @@ $Config['pageSize'] = isset($configPage['pageSize']) ? $configPage['pageSize'] : $oHeadPublisher =& headPublisher::getSingleton(); -//$oHeadPublisher->usingExtJs('ux/Ext.ux.fileUploadField'); $oHeadPublisher->addExtJsScript('roles/rolesList', false); //adding a javascript file .js $oHeadPublisher->addContent('roles/rolesList'); //adding a html file .html. $oHeadPublisher->assign('FORMATS',$c->getFormats()); diff --git a/workflow/engine/methods/setup/appCacheViewConf.php b/workflow/engine/methods/setup/appCacheViewConf.php index 6a68d95b7..efb6f0a06 100755 --- a/workflow/engine/methods/setup/appCacheViewConf.php +++ b/workflow/engine/methods/setup/appCacheViewConf.php @@ -4,7 +4,6 @@ $oHeadPublisher =& headPublisher::getSingleton(); //$oHeadPublisher->setExtSkin( 'xtheme-blue'); - //$oHeadPublisher->usingExtJs('ux/Ext.ux.codepress'); $oHeadPublisher->addExtJsScript('setup/appCacheViewConf', false); //adding a javascript file .js $oHeadPublisher->addContent('setup/appCacheViewConf'); //adding a html file .html. diff --git a/workflow/engine/methods/setup/languages.php b/workflow/engine/methods/setup/languages.php index fa8705a84..eb3d2cd4a 100755 --- a/workflow/engine/methods/setup/languages.php +++ b/workflow/engine/methods/setup/languages.php @@ -24,7 +24,6 @@ */ $RBAC->requirePermissions('PM_SETUP_ADVANCE'); -//$oHeadPublisher->usingExtJs('ux/Ext.ux.fileUploadField'); $oHeadPublisher->addExtJsScript('setup/languages', false); //adding a javascript file .js $oHeadPublisher->addContent('setup/languages'); //adding a html file .html. diff --git a/workflow/engine/methods/setup/loginSettings.php b/workflow/engine/methods/setup/loginSettings.php index 0942371ac..72123cfea 100755 --- a/workflow/engine/methods/setup/loginSettings.php +++ b/workflow/engine/methods/setup/loginSettings.php @@ -33,12 +33,14 @@ $oConf->loadConfig($obj, 'ENVIRONMENT_SETTINGS',''); - $defaultOption = isset($oConf->aConfig['login_enableForgotPassword'])? $oConf->aConfig['login_enableForgotPassword']: false; + $forgotPasswd = isset($oConf->aConfig['login_enableForgotPassword'])? $oConf->aConfig['login_enableForgotPassword']: false; + $virtualKeyboad = isset($oConf->aConfig['login_enableVirtualKeyboard'])? $oConf->aConfig['login_enableVirtualKeyboard']: false; $defaultLanguaje = isset($oConf->aConfig['login_defaultLanguage'])? $oConf->aConfig['login_defaultLanguage']: 'en'; - $oHeadPublisher->assign( 'currentLang', $defaultLanguaje); //current language - $oHeadPublisher->assign( 'currentOption', $defaultOption); //current option + $oHeadPublisher->assign('currentLang', $defaultLanguaje); + $oHeadPublisher->assign('forgotPasswd', $forgotPasswd); + $oHeadPublisher->assign('virtualKeyboad',$virtualKeyboad); G::RenderPage('publish', 'extJs'); diff --git a/workflow/engine/methods/setup/loginSettingsAjax.php b/workflow/engine/methods/setup/loginSettingsAjax.php index bea75a980..ed7bd13e2 100755 --- a/workflow/engine/methods/setup/loginSettingsAjax.php +++ b/workflow/engine/methods/setup/loginSettingsAjax.php @@ -1,5 +1,5 @@ loadConfig($obj, 'ENVIRONMENT_SETTINGS',''); - - $conf->aConfig['login_enableForgotPassword'] = isset($_POST['acceptRP']) ? $_POST['acceptRP'] : 'off'; - $conf->aConfig['login_defaultLanguage'] = $_POST['lang']; + + $conf->aConfig['login_enableForgotPassword'] = isset($_REQUEST['forgotPasswd']); + $conf->aConfig['login_enableVirtualKeyboard'] = isset($_REQUEST['virtualKeyboad']); + $conf->aConfig['login_defaultLanguage'] = isset($_REQUEST['lang'])? $_REQUEST['lang'] : 'en'; + $conf->saveConfig('ENVIRONMENT_SETTINGS', ''); - $response->success = true; - if (isset($_POST['acceptRP']) && $_POST['acceptRP']) - $response->enable = true; - else - $response->enable = false; - - echo G::json_encode($response); + //remove from memcache when this value is updated/created + $memcache->delete('flagForgotPassword') ; + $response->success = true; + echo G::json_encode($response); + break; } \ No newline at end of file diff --git a/workflow/engine/methods/setup/skin_Ajax.php b/workflow/engine/methods/setup/skin_Ajax.php index f048ee002..a00e17f75 100755 --- a/workflow/engine/methods/setup/skin_Ajax.php +++ b/workflow/engine/methods/setup/skin_Ajax.php @@ -41,46 +41,10 @@ function updatePageSize() { } function skinList() { - //Create Skins custom folder if it doesn't exists - if(!is_dir(PATH_CUSTOM_SKINS)){ - G::verifyPath(PATH_CUSTOM_SKINS, true); - } + G::loadClass('system'); - //Get Skin Config files - $skinListArray = array(); - $customSkins = glob(PATH_CUSTOM_SKINS . "*/config.xml"); - $configurationFile = G::ExpandPath("skinEngine") . 'base' . PATH_SEP . 'config.xml'; - array_unshift($customSkins, $configurationFile); - - //Read and parse each Configuration File - foreach ($customSkins as $key => $configInformation) { - - $folderId = str_replace(G::ExpandPath("skinEngine") . 'base', "", str_replace(PATH_CUSTOM_SKINS, "", str_replace("/config.xml", "", $configInformation))); - if ($folderId == "") - $folderId = "classic"; - $xmlConfiguration = file_get_contents($configInformation); - $xmlConfigurationObj = G::xmlParser($xmlConfiguration); - if (isset($xmlConfigurationObj->result['skinConfiguration'])) { - $skinInformationArray = $skinFilesArray = $xmlConfigurationObj->result['skinConfiguration']['__CONTENT__']['information']['__CONTENT__']; - - - $menuOption = array(); - - $res = array(); - $res['SKIN_FOLDER_ID'] = strtolower($folderId); - foreach ($skinInformationArray as $keyInfo => $infoValue) { - $res['SKIN_' . strtoupper($keyInfo)] = $infoValue['__VALUE__']; - } - $skinListArray['skins'][] = $res; - $skinMenuArray[] = $menuOption; - } - } - $skinListArray['currentSkin'] = SYS_SKIN; - if ((isset($_REQUEST['type'])) && ($_REQUEST['type'] == "menu")) { - print_r(G::json_encode($skinMenuArray)); - } else { - print_r(G::json_encode($skinListArray)); - } + $skinListArray = System::getSkingList(); + echo G::json_encode($skinListArray); } function newSkin($baseSkin='classic') { diff --git a/workflow/engine/skinEngine/base/css/pmos-xtheme-gray.css b/workflow/engine/skinEngine/base/css/pmos-xtheme-gray.css index 8dfc9bf23..9a0d7e0ed 100755 --- a/workflow/engine/skinEngine/base/css/pmos-xtheme-gray.css +++ b/workflow/engine/skinEngine/base/css/pmos-xtheme-gray.css @@ -834,6 +834,11 @@ antes funcionaba. background-image:url( /images/icons_silk/sprites.png) !important; background-position:0 -8929px !important; } + +.x-headBlock{ + background-color: #EEEEEE; +} + .x-grid-empty{ text-align: center; position: absolute; @@ -928,6 +933,175 @@ antes funcionaba. clear:none; } + +/* default-layout */ + +.x-user-bar { + color:#fff; + font-size:8px; + font-weight: bold; +} + +.x-pm-tabmenu { + color: #000 !important; + padding-left: 32px !important; + padding-right: 15px !important; + padding-top: 5px !important; + padding-bottom: 8px !important; + font: 13px "Lucida Grande",Lucida,Verdana,sans-serif !important; +} + +.x-pm-home { + background-image:url(/images/homeIcon.png) !important; +} + +.x-pm-bpmn { + background-image:url(/images/bpmnIcon.png) !important; +} + +.x-pm-designer { + background-image:url(/images/designerIcon.png) !important; +} + +.x-pm-setup { + background-image:url(/images/setupIcon.png) !important; +} + +.x-pm-profile { + background-image:url(/images/profile-icontab.png) !important; +} + +.x-pm-dashboard { + background-image:url(/images/dashboard.png) !important; +} + +.x-pm-toolbars { + background-color: #F0F0F0; + background-image: url("/images/ext/gray/toolbar/bg.gif"); + border-color: #D0D0D0; +} + +.x-pm-logout-icon { + background-image:url(/images/logout.gif); +} + +.x-pm-headerbar1 { + background-color: #32405a; + background-image: url("/images/header_bg.jpg"); + color: #fff; + padding: 0px; + font: 8pt Tahoma,sans-serif,MiscFixed; + font-size: 12px; +} +.x-pm-headerbar2 { + background-color: #000; + background-image: url("/images/canvastile_bg1.jpg"); + color: #fff; + padding: 5px; + font: 8pt Tahoma,sans-serif,MiscFixed; + font-size: 12px; +} + +.headerRightSection +{ + font: 8pt Tahoma,sans-serif,MiscFixed; + color: #fff; +} + +.headerLeftSection +{ + font: 8pt Tahoma,sans-serif,MiscFixed; + color: #fff; +} + +.headerLeftSideBar a:hover { + color: orange; +} + +.headerLeftSideBar a { + font: bold 8pt Tahoma,sans-serif,MiscFixed; + color: #fff; + text-decoration: none; +} + +.headerRightSideBar a:hover { + color: orange; +} + +a.login { + font: 7pt Tahoma,sans-serif,MiscFixed; + color: #00004e; + text-decoration: none; +} + +a.login:hover { + color: blue; +} + +.headerRightSideBar a { + font: 15px Tahoma,sans-serif,MiscFixed; + color: #fff; + text-decoration: none; + vertical-align: top; +} + +.headerRightSideBar +{ + padding-top: 10px; + padding-bottom: 10px; + padding-left: 0px; + padding-right: 5px; + color: #fff; + + display: table-cell; + vertical-align: top !important; +} +#user-avatar +{ + padding-top: 10px; + padding-bottom: 10px; + padding-left: 0px; + padding-right: 10px; + color: #fff; + display: table-cell; + vertical-align: top !important; +} + +.headerRightSideBar label { + font: 9pt Lucida,Verdana,sans-serif,MiscFixed; + color: #fff; + + display: inline-block; + padding-top: 2px; + padding-bottom: 2px; + padding-left: 3px; + padding-right: 3px; +} +.headerLeftSideBar +{ + padding-top: 0px; + padding-bottom: 17px; + padding-left: 0px; + padding-right: 5px; +} +.companyLogo +{ + padding-top: 0px; + padding-bottom: 0px; + padding-left: 10px; + padding-right: 0px; +} + +.ux-auth-warning { + background:url("../images/ext/default/warning.png") no-repeat center left; + padding: 2px; + padding-left:20px; + font-weight:bold; +} +.ux-auth-header-icon { + background: url("../images/ext/default/locked.png") 0 4px no-repeat !important; +} + /*! * Ext JS Library 3.3.3 * Copyright(c) 2006-2011 Sencha Inc. @@ -1050,6 +1224,141 @@ antes funcionaba. color: #15428B; } +/** Virtual keyboard UX*/ +.ux-virtualkeyboard-icon { + background-image:url(../images/ext/default/keyboard.png) ! important; +} +.ux-accented-icon { + background-image:url(../images/ext/default/accented.png) ! important; +} +.x-keyboard .x-toolbar{ + border-width: 1px; +} +.x-keyboard .x-panel-body{ + padding:5px; +} + +.x-keyboard .x-panel-body tbody tr td { + margin:0px; + padding:0px 6px 3px 6px; +} +.x-keyboard .x-panel-body tbody tr td div { + text-align:center; + position:relative; + height:0px; +} +table.keys { + height:20px; + white-space:nowrap; + width:100%; + border-spacing: 0 2px 2px 0; +} +table.keyboardInputCenter { + width:auto; + margin:0 auto; +} + +#spacebar{width:150px;} + +table.keys tbody tr td { + vertical-align:middle; + text-align: center; + padding:0px 5px; + white-space:pre; + /*font:normal 11px 'Lucida Console',monospace;*/ + font: .69em Arial, sans-serif; + border-top:1px solid #99BBE8; + border-right:1px solid #15428B; + border-bottom:1px solid #15428B; + border-left:1px solid #99BBE8; + background-color:#D0DEF0; + cursor:default; + min-width:0.75em; + -moz-user-select: none; +} +table.keys tbody tr td.last { + width:99%; + margin:10px; +} +table.keys tbody tr td.alive { + background-color:#ccccdd; +} +table.keys tbody tr td.target { + background-color:#ddddcc; +} +table.keys tbody tr td.hover { + border-top:1px solid #99BBE8; + border-right:1px solid #15428B; + border-bottom:1px solid #15428B; + border-left:1px solid #99BBE8; + background-color:#99BBE8; +} +table.keys tbody tr td.pressed, +table.keys tbody tr td.dead { + border-top:1px solid #15428B; + border-right:1px solid #99BBE8; + border-bottom:1px solid #99BBE8; + border-left:1px solid #15428B; + background-color:#99BBE8; +} + +.keyboardInputInitiator { + margin-left:3px; + vertical-align:middle; + cursor:pointer; +} + +/* BPMN Text Rotation */ +.rotateText { + font-family: Arial; + font-size: 10pt; + display: table; + text-align: center; + /* for firefox, safari, chrome, etc. */ + -webkit-transform: rotate(-90deg); + -moz-transform: rotate(-90deg); + -o-transform: rotate(-90deg);/* For Opera*/ + -khtml-transform: rotate(-90deg);/* For Lunix*/ + /* for ie */ + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); + } + .rotateText p { + display: table-cell; + vertical-align: middle; + } + + .rotateTextIE { + font-family: Arial; + font-size: 10pt; + display: table; + text-align: center; + writing-mode: tb-rl; + filter: flipv fliph; + } + + .normalText{ + font-family: Arial; + font-size: 10pt; + display: table; + text-align: center; + } + + .normalText p{ + display: table-cell; + vertical-align: middle; + } + + .normalTopText{ + font-family: Arial; + font-size: 10pt; + display: table; + text-align: center; + } + + .x-padding-left{ + padding-left: 175px; + } + .x-pm-startcase-btn { background-image:url(/images/start.png) !important; color: #000 !important; @@ -1082,6 +1391,61 @@ antes funcionaba. .x-text-plain { font-weight: bold; } +#oAuth { + background-color: #fff; + text-shadow: #fff 0 1px 0; +} + +#oAuth ul { + position: relative; + display: block; + height: auto; + font-size: 85%; +} + +#oAuth ul li img { + margin-bottom: 1px; +} + +#oAuth ul li { + float: left; + padding: 10px 10px; + margin: 5px; +/* margin: 10px 0 0 25px;*/ + text-align: center; + line-height: 1.25em; + color: #333; + font-family: "Helvetica Neue",sans-serif; + height: 35px; + width: 35px; + overflow: hidden; + border-top: 1px solid transparent; + cursor: pointer; +} + +#oAuth ul li.oAuth-hover { + background-color: #eee; +} + +#oAuth ul li.x-view-selected { + background-color: rgba(100, 100, 100, .15); + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + border-top: 1px solid rgba(0, 0, 0, .15); +} + +#oAuth ul li img { +/* display: block;*/ +} + +#oAuth li strong { + color: #000; + display: block; +} + +#oAuth li span { + color: #999; +} /* Case Notes styles */ @@ -1125,3 +1489,6 @@ td.x-cnotes-label { font: 11px arial,tahoma,helvetica,sans-serif; color: #465070; } +.replace { + display:none; +} \ No newline at end of file diff --git a/workflow/engine/skinEngine/base/layout-submenu.html b/workflow/engine/skinEngine/base/layout-submenu.html index eb3f4c8e5..5cb168d24 100755 --- a/workflow/engine/skinEngine/base/layout-submenu.html +++ b/workflow/engine/skinEngine/base/layout-submenu.html @@ -15,7 +15,20 @@ - + + + {if ($exit_editor==1) } + + {/if} +
diff --git a/workflow/engine/skinEngine/skinEngine.php b/workflow/engine/skinEngine/skinEngine.php index 042b4bde9..71a12000a 100644 --- a/workflow/engine/skinEngine/skinEngine.php +++ b/workflow/engine/skinEngine/skinEngine.php @@ -53,7 +53,7 @@ if (strtolower($G_SKIN_MAIN) != "classic") { if (is_dir($skinsBasePath . $G_SKIN_MAIN)) { // check this skin on core skins path $skinObject = $skinsBasePath . $G_SKIN_MAIN; } - else if (is_dir(PATH_CUSTOM_SKINS . $G_SKIN_MAIN)) { // check this skin on user skins path + else if (defined('PATH_CUSTOM_SKINS') && is_dir(PATH_CUSTOM_SKINS . $G_SKIN_MAIN)) { // check this skin on user skins path $skinObject = PATH_CUSTOM_SKINS . $G_SKIN_MAIN; } else { //Skin doesn't exist @@ -203,6 +203,11 @@ switch (strtolower($G_SKIN)) { $menus = $oMenu->generateArrayForTemplate($G_MAIN_MENU, 'SelectedMenu', 'mainMenu', $G_MENU_SELECTED, $G_ID_MENU_SELECTED); $smarty->assign('menus', $menus); + if (substr(SYS_SKIN, 0, 2) == 'ux') { + $smarty->assign('exit_editor', 1); + $smarty->assign('exit_editor_label', G::loadTranslation('ID_CLOSE_EDITOR')); + } + $oSubMenu = new Menu(); $subMenus = $oSubMenu->generateArrayForTemplate($G_SUB_MENU, 'selectedSubMenu', 'subMenu', $G_SUB_MENU_SELECTED, $G_ID_SUB_MENU_SELECTED); $smarty->assign('subMenus', $subMenus); @@ -273,6 +278,12 @@ switch (strtolower($G_SKIN)) { $smarty->display($layoutFileRaw['basename']); break; //end case 'raw' + + case "plain": + $oHeadPublisher = & headPublisher::getSingleton(); + echo $oHeadPublisher->renderExtJs(); + break; + case 'extjs'://This is a special template but need main skin styles G::LoadClass('serverConfiguration'); diff --git a/workflow/engine/templates/admin/system.js b/workflow/engine/templates/admin/system.js new file mode 100644 index 000000000..7ea23407e --- /dev/null +++ b/workflow/engine/templates/admin/system.js @@ -0,0 +1,185 @@ +Ext.onReady(function(){ + + var cmbSkins = new Ext.form.ComboBox({ + fieldLabel : _('ID_DEFAULT_SKIN'), + hiddenName : 'default_skin', + store : new Ext.data.ArrayStore({ + fields: ['ID', 'NAME'], + data : skinsList + }), + mode : 'local', + emptyText : _('ID_SELECT'), + valueField : 'ID', + displayField : 'NAME', + selectOnFocus : true, + editable : true, + triggerAction: 'all', + allowBlank : false, + forceSelection: true, + listeners:{ + select: function(){ + changeSettings(); + }, + afterrender: function(){ + i = cmbSkins.store.findExact('ID', sysConf.default_skin, 0); + if (i == -1) return; + cmbSkins.setValue(cmbSkins.store.getAt(i).data.ID); + cmbSkins.setRawValue(cmbSkins.store.getAt(i).data.NAME); + } + } + }); + + var cmbLang = new Ext.form.ComboBox({ + fieldLabel : _('ID_DEFAULT_LANG'), + hiddenName : 'default_lang', + store : new Ext.data.ArrayStore({ + fields: ['ID', 'NAME'], + data : languagesList + }), + mode : 'local', + emptyText : _('ID_SELECT'), + valueField : 'ID', + displayField : 'NAME', + selectOnFocus : true, + editable : true, + triggerAction: 'all', + forceSelection: true, + allowBlank : false, + listeners:{ + select: function(){ + changeSettings(); + }, + afterrender: function(){ + i = cmbLang.store.findExact('ID', sysConf.default_lang, 0); + if (i == -1) return; + cmbLang.setValue(cmbLang.store.getAt(i).data.ID); + cmbLang.setRawValue(cmbLang.store.getAt(i).data.NAME); + } + } + }); + + var cmbTimeZone = new Ext.form.ComboBox({ + fieldLabel : _('ID_TIME_ZONE'), + hiddenName : 'time_zone', + store : new Ext.data.ArrayStore({ + fields: ['ID', 'NAME'], + data : timeZonesList + }), + mode : 'local', + emptyText : _('ID_SELECT'), + valueField : 'ID', + displayField : 'NAME', + selectOnFocus : true, + editable : true, + triggerAction: 'all', + forceSelection : true, + allowBlank : false, + listeners:{ + select: function(){ + changeSettings(); + } + } + }); + + cmbTimeZone.setValue(sysConf.time_zone); + + saveButton = new Ext.Action({ + text : _('ID_SAVE_SETTINGS'), + disabled : true, + handler : saveSettings + }); + + xfields = new Ext.form.FieldSet({ + title: _('ID_SYSTEM_SETTINGS'), + items : [ + cmbSkins, + cmbLang, + cmbTimeZone, + { + xtype: 'numberfield', + id : 'memory_limit', + name : 'memory_limit', + fieldLabel: _('ID_MEMORY_LIMIT') + '(MB) ', + allowBlank: false, + value: sysConf.memory_limit, + listeners:{ + change: function(){ + changeSettings(); + } + } + }/*, + { + name: 'forgotPasswd', + xtype: 'checkbox', + checked: false, //forgotPasswd, + fieldLabel: _('ID_ENABLE_FOTGOT_PASSWORD'), + listeners:{ + check:function(){ + changeSettings(); + } + } + }*/ + ], + buttons : [saveButton] + }); + + + var frm = new Ext.FormPanel({ + title: ' ', + id:'frm', + labelWidth: 170, + width:460, + labelAlign:'right', + autoScroll: true, + bodyStyle:'padding:2px', + waitMsgTarget : true, + frame: true, + + defaults: { + allowBlank: false, + msgTarget: 'side', + align:'center' + }, + items:[ xfields ] + + }); + //render to process-panel + frm.render(document.body); + +}); //end onready() + +function saveSettings() +{ + Ext.getCmp('frm').getForm().submit( { + url : '../adminProxy/saveSystemConf', + waitMsg : _('ID_SAVING_PROCESS'), + timeout : 36000, + success : function(obj, resp) { + //nothing to do + response = Ext.decode(resp.response.responseText); + parent.PMExt.notify(_('ID_INFO'),_('ID_SAVED_SUCCESSFULLY')); + + if(response.restart) { + PMExt.confirm(_('ID_CONFIRM'), 'To take effect the changes you need re login.
Redirect now?', function(){ + + if (typeof window.parent.parent != 'undefined') + window.parent.parent.location.href = '/'; + if (typeof window.parent != 'undefined') + window.parent.location.href = '/'; + else + window.location.href = '/'; + }); + } + else + saveButton.disable(); + }, + failure: function(obj, resp) { + PMExt.error( _('ID_ERROR'), resp.result.message); + } + }); +} + +changeSettings = function() +{ + saveButton.enable(); +} diff --git a/workflow/engine/templates/cases/main.js b/workflow/engine/templates/cases/main.js index a7ca8f87e..5d46d840a 100755 --- a/workflow/engine/templates/cases/main.js +++ b/workflow/engine/templates/cases/main.js @@ -21,24 +21,24 @@ var NOTIFIER_FLAG = false; var result; var _action = ''; var _CASE_TITLE; - -new Ext.KeyMap(document, { - key: Ext.EventObject.F5, - fn: function(keycode, e) { - if (! e.ctrlKey) { - if (Ext.isIE) { - e.browserEvent.keyCode = 8; - } - e.stopEvent(); - updateCasesTree(); - } - else - Ext.Msg.alert('Refresh', 'You clicked: CTRL-F5'); - } -}); Ext.onReady(function(){ + new Ext.KeyMap(document, { + key: Ext.EventObject.F5, + fn: function(keycode, e) { + if (! e.ctrlKey) { + if (Ext.isIE) { + e.browserEvent.keyCode = 8; + } + e.stopEvent(); + updateCasesTree(); + } + else + Ext.Msg.alert('Refresh', 'You clicked: CTRL-F5'); + } + }); + Ext.QuickTips.init(); Ext.state.Manager.setProvider(new Ext.state.CookieProvider()); var resetGrid = function() { @@ -158,14 +158,17 @@ Ext.onReady(function(){ }); + //center iframe panel centerPanel = { - region: 'center', // a center region is ALWAYS required for border layout - xtype:'panel', - deferredRender: false, - contentEl:'casesSubFrame' + region : 'center', + xtype : 'iframepanel', + frameConfig:{ + name : 'casesSubFrame', + id : 'casesSubFrame' + }, + deferredRender: false } - /** * Menu Panel */ @@ -326,8 +329,6 @@ Ext.onReady(function(){ /** * Triggers Panel */ - Ext.QuickTips.init(); - var xg = Ext.grid; var reader = new Ext.data.JsonReader( diff --git a/workflow/engine/templates/index.html.tpl b/workflow/engine/templates/index.html.tpl new file mode 100644 index 000000000..239bd9aab --- /dev/null +++ b/workflow/engine/templates/index.html.tpl @@ -0,0 +1,8 @@ + + +Redirector + + + + + \ No newline at end of file diff --git a/workflow/engine/templates/installer/Card.js b/workflow/engine/templates/installer/Card.js new file mode 100644 index 000000000..316e69b58 --- /dev/null +++ b/workflow/engine/templates/installer/Card.js @@ -0,0 +1,147 @@ +Ext.namespace('Ext.ux.Wiz'); + +/** + * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3 + * + * @author Thorsten Suckow-Homberg + * @url http://www.siteartwork.de/wizardcomponent + */ + +/** + * @class Ext.ux.Wiz.Card + * @extends Ext.FormPanel + * + * A specific {@link Ext.FormPanel} that can be used as a card in a + * {@link Ext.ux.Wiz}-component. An instance of this card does only work properly + * if used in a panel that uses a {@see Ext.layout.CardLayout}-layout. + * + * @constructor + * @param {Object} config The config object + */ +Ext.ux.Wiz.Card = Ext.extend(Ext.FormPanel, { + + /** + * @cfg {Boolean} header "True" to create the header element. Defaults to + * "false". See {@link Ext.form.FormPanel#header} + */ + header : false, + + /** + * @cfg {Strting} hideMode Hidemode of this component. Defaults to "offsets". + * See {@link Ext.form.FormPanel#hideMode} + */ + hideMode : 'display', + + initComponent : function() + { + this.addEvents( + /** + * @event beforecardhide + * If you want to add additional checks to your card which cannot be easily done + * using default validators of input-fields (or using the monitorValid-config option), + * add your specific listeners to this event. + * This event gets only fired if the activeItem of the ownerCt-component equals to + * this instance of {@see Ext.ux.Wiz.Card}. This is needed since a card layout usually + * hides it's items right after rendering them, involving the beforehide-event. + * If those checks would be attached to the normal beforehide-event, the card-layout + * would never be able to hide this component after rendering it, depending on the + * listeners return value. + * + * @param {Ext.ux.Wiz.Card} card The card that triggered the event + */ + 'beforecardhide' + ); + + + Ext.ux.Wiz.Card.superclass.initComponent.call(this); + + }, + +// -------- helper + isValid : function() + { + if (this.monitorValid) { + return this.bindHandler(); + } + + return true; + }, + +// -------- overrides + + /** + * Overrides parent implementation since we allow to add any element + * in this component which must not be neccessarily be a form-element. + * So before a call to "isValid()" is about to be made, this implementation + * checks first if the specific item sitting in this component has a method "isValid" - if it + * does not exists, it will be added on the fly. + */ + bindHandler : function() + { + this.form.items.each(function(f){ + if(!f.isValid){ + f.isValid = Ext.emptyFn; + } + }); + + Ext.ux.Wiz.Card.superclass.bindHandler.call(this); + }, + + /** + * Overrides parent implementation. This is needed because in case + * this method uses "monitorValid=true", the method "startMonitoring" must + * not be called, until the "show"-event of this card fires. + */ + initEvents : function() + { + var old = this.monitorValid; + this.monitorValid = false; + Ext.ux.Wiz.Card.superclass.initEvents.call(this); + this.monitorValid = old; + + this.on('beforehide', this.bubbleBeforeHideEvent, this); + + this.on('beforecardhide', this.isValid, this); + this.on('show', this.onCardShow, this); + this.on('hide', this.onCardHide, this); + }, + +// -------- listener + /** + * Checks wether the beforecardhide-event may be triggered. + */ + bubbleBeforeHideEvent : function() + { + var ly = this.ownerCt.layout; + var activeItem = ly.activeItem; + + if (activeItem && activeItem.id === this.id) { + return this.fireEvent('beforecardhide', this); + } + + return true; + }, + + /** + * Stops monitoring the form elements in this component when the + * 'hide'-event gets fired. + */ + onCardHide : function() + { + if (this.monitorValid) { + this.stopMonitoring(); + } + }, + + /** + * Starts monitoring the form elements in this component when the + * 'show'-event gets fired. + */ + onCardShow : function() + { + if (this.monitorValid) { + this.startMonitoring(); + } + } + +}); \ No newline at end of file diff --git a/workflow/engine/templates/installer/CardLayout.js b/workflow/engine/templates/installer/CardLayout.js new file mode 100644 index 000000000..fa762a76a --- /dev/null +++ b/workflow/engine/templates/installer/CardLayout.js @@ -0,0 +1,56 @@ +Ext.namespace('Ext.ux.layout'); + +/** + * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3 + * + * @author Thorsten Suckow-Homberg + * @url http://www.siteartwork.de/cardlayout + */ + +/** + * @class Ext.ux.layout.CardLayout + * @extends Ext.layout.CardLayout + * + * A specific {@link Ext.layout.CardLayout} that only sets the active item + * if the 'beforehide'-method of the card to hide did not return false (in this case, + * components usually won't be hidden). + * The original implementation of {@link Ext.layout.CardLayout} does not take + * the return value of the 'beforehide'-method into account. + * + * @constructor + * @param {Object} config The config object + */ +Ext.ux.layout.CardLayout = Ext.extend(Ext.layout.CardLayout, { + + /** + * Sets the active (visible) item in the layout. + * + * If the currently visible item is still visible after calling the 'hide() + * method on it, this implementation assumes that the 'beforehide'-event returned + * false, thus not the item was not allowed to be hidden. The active item will then + * equal to the item that was active, before this method was called. + * + * @param {String/Number} item The string component id or numeric index of the item to activate + */ + setActiveItem : function(item){ + item = this.container.getComponent(item); + if(this.activeItem != item){ + if(this.activeItem){ + this.activeItem.hide(); + } + // check if the beforehide method allowed to + // hide the current item + if (this.activeItem && !this.activeItem.hidden) { + return; + } + var layout = item.doLayout && (this.layoutOnCardChange || !item.rendered); + this.activeItem = item; + item.show(); + this.layout(); + if(layout){ + item.doLayout(); + } + } + } + +}); \ No newline at end of file diff --git a/workflow/engine/templates/installer/Header.js b/workflow/engine/templates/installer/Header.js new file mode 100644 index 000000000..a086d5968 --- /dev/null +++ b/workflow/engine/templates/installer/Header.js @@ -0,0 +1,175 @@ +Ext.namespace('Ext.ux.Wiz'); + +/** + * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3 + * + * @author Thorsten Suckow-Homberg + * @url http://www.siteartwork.de/wizardcomponent + */ + +/** + * @class Ext.ux.Wiz.Header + * @extends Ext.BoxComponent + * + * A specific {@link Ext.BoxComponent} that can be used to show the current process in an + * {@link Ext.ux.Wiz}. + * + * An instance of this class is usually being created by {@link Ext.ux.Wiz#initPanels} using the + * {@link Ext.ux.Wiz#headerConfig}-object. + * + * @private + * @constructor + * @param {Object} config The config object + */ +Ext.ux.Wiz.Header = Ext.extend(Ext.BoxComponent, { + + /** + * @cfg {Number} height The height of this component. Defaults to "55". + */ + height : 55, + + /** + * @cfg {String} region The Region of this component. Since a {@link Ext.ux.Wiz} + * usually uses a {@link Ext.layout.BorderLayout}, this property defaults to + * "north". If you want to change this property, you should also change the appropriate + * css-classes that are used for this component. + */ + region : 'north', + + /** + * @cfg {String} title The title that gets rendered in the head of the component. This + * should be a text describing the purpose of the wizard. + */ + title : 'Wizard', + + /** + * @cfg {Number} steps The overall number of steps the user has to go through + * to finish the wizard. + */ + steps : 0, + + /** + * @cfg {String} stepText The text in the header indicating the current process in the wizard. + * (defaults to "Step {0} of {1}: {2}"). + * {0} is replaced with the index (+1) of the current card, {1} is replaced by the + * total number of cards in the wizard and {2} is replaced with the title-property of the + * {@link Ext.ux.Wiz.Card} + * @type String + */ + stepText : "Step {0} of {1}: {2}", + + /** + * @cfg {Object} autoEl The element markup used to render this component. + */ + autoEl : { + tag : 'div', + cls : 'ext-ux-wiz-Header', + children : [{ + tag : 'div', + cls : 'ext-ux-wiz-Header-title' + }, { + tag : 'div', + children : [{ + tag : 'div', + cls : 'ext-ux-wiz-Header-step' + }, { + tag : 'div', + cls : 'ext-ux-wiz-Header-stepIndicator-container' + }] + }] + }, + + /** + * @param {Ext.Element} + */ + titleEl : null, + + /** + * @param {Ext.Element} + */ + stepEl : null, + + /** + * @param {Ext.Element} + */ + imageContainer : null, + + /** + * @param {Array} + */ + indicators : null, + + /** + * @param {Ext.Template} + */ + stepTemplate : null, + + /** + * @param {Number} lastActiveStep Stores the index of the last active card that + * was shown- + */ + lastActiveStep : -1, + +// -------- helper + /** + * Gets called by {@link Ext.ux.Wiz#onCardShow()} and updates the header + * with the approppriate information, such as the progress of the wizard + * (i.e. which card is being shown etc.) + * + * @param {Number} currentStep The index of the card currently shown in + * the wizard + * @param {String} title The title-property of the {@link Ext.ux.Wiz.Card} + * + * @private + */ + updateStep : function(currentStep, title) + { + var html = this.stepTemplate.apply({ + 0 : currentStep+1, + 1 : this.steps, + 2 : title + }); + + this.stepEl.update(html); + + if (this.lastActiveStep != -1) { + this.indicators[this.lastActiveStep].removeClass('ext-ux-wiz-Header-stepIndicator-active'); + } + + this.indicators[currentStep].addClass('ext-ux-wiz-Header-stepIndicator-active'); + + this.lastActiveStep = currentStep; + }, + + +// -------- listener + /** + * Overrides parent implementation to render this component properly. + */ + onRender : function(ct, position) + { + Ext.ux.Wiz.Header.superclass.onRender.call(this, ct, position); + + this.indicators = []; + this.stepTemplate = new Ext.Template(this.stepText); + this.stepTemplate.compile(); + + var el = this.el.dom.firstChild; + var ns = el.nextSibling; + + this.titleEl = new Ext.Element(el); + this.stepEl = new Ext.Element(ns.firstChild); + this.imageContainer = new Ext.Element(ns.lastChild); + + this.titleEl.update(this.title); + + var image = null; + for (var i = 0, len = this.steps; i < len; i++) { + image = document.createElement('div'); + image.innerHTML = " "; + image.className = 'ext-ux-wiz-Header-stepIndicator'; + this.indicators[i] = new Ext.Element(image); + this.imageContainer.appendChild(image); + } + } +}); \ No newline at end of file diff --git a/workflow/engine/templates/installer/Wizard.js b/workflow/engine/templates/installer/Wizard.js new file mode 100644 index 000000000..e3fae0631 --- /dev/null +++ b/workflow/engine/templates/installer/Wizard.js @@ -0,0 +1,551 @@ +Ext.namespace('Ext.ux'); + +/** + * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3 + * + * @author Thorsten Suckow-Homberg + * @url http://www.siteartwork.de/wizardcomponent + */ + +/** + * @class Ext.ux.Wiz + * @extends Ext.Window + * + * A specific {@link Ext.Window} that models a wizard component. + * A wizard is basically a dialog that guides a user through various steps + * where he has to fill out form-data. + * A {@link Ext.ux.Wiz}-component consists typically of a {@link Ext.ux.Wiz.Header} + * and window-buttons ({@link Ext.Button}) which are linked to the {@link Ext.ux.Wiz.Card}s + * which themself represent the forms the user has to fill out. + * + * In order to switch between the cards in the wizard, you need the {@link Ext.ux.layout.CardLayout}, + * which will check if an active-item can be hidden, before the requested new item will be set to + * 'active', i.e. shown. This is needed since the wizard may not allow a card to be hidden, if + * the input entered by the user was not valid. You can get this custom layout at + * {@link http://www.siteartwork.de/cardlayout}. + * + * Note: + * When data has been collected and teh "onFinish" listener triggers an AJAX-request, + * you should call the "switchDialogState" method so that the the dialog shows a loadmask. + * Once the requests finishes, call "switchDialogState" again, specially before any call + * to the "close" method of this component, otherwise the "closable" property of this + * instance might prevent a "close" operation for this dialog. + * + * + * @constructor + * @param {Object} config The config object + */ +Ext.ux.Wiz = Ext.extend(Ext.Window, { + + /** + * @cfg {Object} An object containing the messages for the {@link Ext.LoadMask} + * covering the card-panel on request, whereas the property identifies the + * msg-text to show, and the value is the message text itself. Defaults to +

+  {
+      default : 'Saving...'
+  }
+       
+ * + * Depending on the contexts the loadMask has to be shown in (using the method + * showLoadMask of this class), the object can be configure to hold + * various messages. +

+  this.loadMaskConfig = {
+      default    : 'Saving...',
+      validating : 'Please wait, validating input...',
+  };
+  // loadMask will be shown, displaying the message 'Please wait, validating input...'
+  this.showLoadMask(true, 'validating');
+  
+ */ + loadMaskConfig : { + 'default' : 'Saving...' + }, + + /** + * @cfg {Number} height The height of the dialog. Defaults to "400". + */ + height : 400, + + /** + * @cfg {Number} width The width of the dialog. Defaults to "540". + */ + width : 540, + + /** + * @cfg {Boolean} closable Wether the dialog is closable. Defaults to "true". + * This property will be changed by the "switchDialogState"-method, which will + * enable/disable controls based on the passed argument. Thus, this config property + * serves two purposes: Tell the init config to render a "close"-tool, and create a + * "beforeclose"-listener which will either return true or false, indicating if the + * dialog may be closed. + */ + closable : true, + + /** + * @cfg {Boolean} resizable Wether the dialog is resizable. Defaults to "false". + */ + resizable : false, + + /** + * @cfg {Boolean} resizable Wether the dialog is modal. Defaults to "true". + */ + modal : true, + + /** + * @cfg {Array} cards A numeric array with the configured {@link Ext.ux.Wiz.Card}s. + * The index of the cards in the array represent the order in which they get displayed + * in the wizard (i.e. card at index 0 gets displayed in the first step, card at index 1 gets + * displayed in the second step and so on). + */ + cards : null, + + /** + * @cfg {String} previousButtonText The text to render the previous-button with. + * Defaults to "< Back" (< Back) + */ + previousButtonText : '< Previous', + + /** + * @cfg {String} nextButtonText The text to render the next-button with. + * Defaults to "Next >" (Next >) + */ + nextButtonText : 'Next >', + + /** + * @cfg {String} cancelButtonText The text to render the cancel-button with. + * Defaults to "Cancel" + */ + cancelButtonText : 'Cancel', + + /** + * @cfg {String} finishButtonText The text to render the next-button with when the last + * step of the wizard is reached. Defaults to "Finish" + */ + finishButtonText : 'Finish', + + /** + * @cfg {Object} headerConfig A config-object to use with {@link Ext.ux.Wiz.Header}. + * If not present, it defaults to an empty object. + */ + headerConfig : {}, + + /** + * @cfg {Object} cardPanelConfig A config-object to use with {@link Ext.Panel}, which + * represents the card-panel in this dialog. + * If not present, it defaults to an empty object + */ + cardPanelConfig : {}, + + /** + * @param {Ext.Button} The window-button for paging to the previous card. + * @private + */ + previousButton : null, + + /** + * @param {Ext.Button} The window-button for paging to the next card. When the + * last card is reached, the event fired by and the text rendered to this button + * will change. + * @private + */ + nextButton : null, + + /** + * @param {Ext.Button} The window-button for canceling the wizard. The event + * fired by this button will usually close the dialog. + * @private + */ + cancelButton : null, + + /** + * @param {Ex.Panel} The card-panel that holds the various wizard cards + * ({@link Ext.ux.Wiz.Card}). The card-panel itself uses the custom + * {@link Ext.ux.layout.CardLayout}, which needs to be accessible by this class. + * You can get it at {@link http://www.siteartwork.de/cardlayout}. + * @private + */ + cardPanel : null, + + /** + * @param {Number} currentCard The current {@link Ext.ux.Wiz.Card} displayed. + * Defaults to -1. + * @private + */ + currentCard : -1, + + /** + * @param {Ext.ux.Wiz.Header} The header-panel of the wizard. + * @private + */ + headPanel : null, + + /** + * @param {Number} cardCount Helper for storing the number of cards used + * by this wizard. Defaults to 0 (inherits "cards.length" later on). + * @private + */ + cardCount : 0, + + /** + * Inits this component with the specified config-properties and automatically + * creates its components. + */ + initComponent : function() + { + this.initButtons(); + this.initPanels(); + + var title = this.title || this.headerConfig.title; + title = title || ""; + + Ext.apply(this, { + title : title, + layout : 'border', + cardCount : this.cards.length, + buttons : [ + this.previousButton, + this.nextButton, + this.cancelButton + ], + items : [ + this.headPanel, + this.cardPanel + ] + }); + + this.addEvents( + /** + * @event cancel + * Fires after the cancel-button has been clicked. + * @param {Ext.ux.Wiz} this + */ + 'cancel', + /** + * @event finish + * Fires after the last card was reached in the wizard and the + * next/finish-button has been clicked. + * @param {Ext.ux.Wiz} this + * @param {Object} data The collected data of the cards, whereas + * the index is the id of the card and the specific values + * are objects with key/value pairs in the form formElementName : value + */ + 'finish' + ); + + Ext.ux.Wiz.superclass.initComponent.call(this); + }, + + // -------- helper + /** + * Returns the form-data of all cards in this wizard. The first index is the + * id of the card in this wizard, + * and the values are objects containing key/value pairs in the form of + * fieldName : fieldValue. + * + * @return {Array} + */ + getWizardData : function() + { + var formValues = {}; + var cards = this.cards; + for (var i = 0, len = cards.length; i < len; i++) { + if (cards[i].form) { + formValues[cards[i].id] = cards[i].form.getValues(false); + } else { + formValues[cards[i].id] = {}; + } + } + + return formValues; + }, + + /** + * Switches the state of this wizard between disabled/enabled. + * A disabled dialog will have a {@link Ext.LoadMask} covering the card-panel + * to prevent user input, and the buttons will be rendered disabled/enabled. + * If the dialog is closable, the close-tool will be masked, too, and the dialog will not + * be closable by clicking the "close" tool. + * + * @param {Boolean} enabled "false" to prevent user input and mask the elements, + * otherwise true. + * @param {String} type The type of msg for the {@Ext.LoadMask} covering + * the cardPanel, as defined in the cfg property "loadMaskConfig" + */ + switchDialogState : function(enabled, type) + { + this.showLoadMask(!enabled, type); + + this.previousButton.setDisabled(!enabled); + this.nextButton.setDisabled(!enabled); + this.cancelButton.setDisabled(!enabled); + + var ct = this.tools['close']; + + if (ct) { + switch (enabled) { + case true: + this.tools['close'].unmask(); + break; + + default: + this.tools['close'].mask(); + break; + } + } + + this.closable = enabled; + }, + + /** + * Shows the load mask for this wizard. By default, the cardPanel's body + * will be masked. + * + * @param {Boolean} show true to show the load mask, otherwise false. + * @param {String} type The type of message for the {@Ext.LoadMask} covering + * the cardPanel, as defined in the cfg property "loadMaskConfig" + */ + showLoadMask : function(show, type) + { + if (!type) { + type = 'default'; + } + + if (show) { + if (this.loadMask == null) { + this.loadMask = new Ext.LoadMask(this.body); + } + this.loadMask.msg = this.loadMaskConfig[type]; + this.loadMask.show(); + } else { + if (this.loadMask) { + this.loadMask.hide(); + } + } + }, + + + /** + * Inits the listener for the various {@link Ext.ux.Wiz.Card}s used + * by this component. + */ + initEvents : function() + { + Ext.ux.Wiz.superclass.initEvents.call(this); + + this.on('beforeclose', this.onBeforeClose, this); + }, + + /** + * Creates the head- and the card-panel. + * Be sure to have the custom {@link Ext.ux.layout.CardLayout} available + * in order to make the card-panel work as expected by this component + * ({@link http://www.siteartwork.de/cardlayout}). + */ + initPanels : function() + { + var cards = this.cards; + var cardPanelConfig = this.cardPanelConfig; + + Ext.apply(this.headerConfig, { + steps : cards.length + }); + + this.headPanel = new Ext.ux.Wiz.Header(this.headerConfig); + + Ext.apply(cardPanelConfig, { + layout : new Ext.ux.layout.CardLayout(), + items : cards + }); + + Ext.applyIf(cardPanelConfig, { + region : 'center', + border : false, + activeItem : 0 + }); + + var cards = this.cards; + + for (var i = 0, len = cards.length; i < len; i++) { + cards[i].on('show', this.onCardShow, this); + cards[i].on('hide', this.onCardHide, this); + cards[i].on('clientvalidation', this.onClientValidation, this); + } + + this.cardPanel = new Ext.Panel(cardPanelConfig); + }, + + /** + * Creates the instances for the the window buttons. + */ + initButtons : function() + { + this.previousButton = new Ext.Button({ + text : this.previousButtonText, + disabled : true, + minWidth : 75, + handler : this.onPreviousClick, + scope : this + }); + + this.nextButton = new Ext.Button({ + text : this.nextButtonText, + minWidth : 75, + handler : this.onNextClick, + scope : this + }); + + this.cancelButton = new Ext.Button({ + text : this.cancelButtonText, + handler : this.onCancelClick, + scope : this, + minWidth : 75 + }); + }, + +// -------- listeners + + /** + * Listener for the beforeclose event. + * This listener will return true or false based on the "closable" + * property by this component. This property will be changed by the "switchDialogState" + * method, indicating if there is currently any process running that should prevent + * this dialog from being closed. + * + * @param {Ext.Panel} panel The panel being closed + * + * @return {Boolean} + */ + onBeforeClose : function(panel) + { + return this.closable; + }, + + /** + * By default, the card firing this event monitors user input in a frequent + * interval and fires the 'clientvalidation'-event along with it. This listener + * will enable/disable the next/finish-button in accordance with it, based upon + * the parameter isValid. isValid" will be set by the form validation and depends + * on the validators you are using for the different input-elemnts in your form. + * If the card does not contain any forms, this listener will never be called by the + * card itself. + * + * @param {Ext.ux.Wiz.Card} The card that triggered the event. + * @param {Boolean} isValid "true", if the user input was valid, otherwise + * "false" + */ + onClientValidation : function(card, isValid) + { + if (!isValid) { + this.nextButton.setDisabled(true); + } else { + this.nextButton.setDisabled(false); + } + }, + + /** + * This will render the "next" button as disabled since the bindHandler's delay + * of the next card to show might be lagging on slower systems + * + */ + onCardHide : function(card) + { + if (this.cardPanel.layout.activeItem.id === card.id) { + this.nextButton.setDisabled(true); + } + }, + + + /** + * Listener for the "show" event of the card that gets shown in the card-panel. + * Renders the next/previous buttons based on the position of the card in the wizard + * and updates the head-panel accordingly. + * + * @param {Ext.ux.Wiz.Card} The card being shown. + */ + onCardShow : function(card) + { + var parent = card.ownerCt; + + var items = parent.items; + + for (var i = 0, len = items.length; i < len; i++) { + if (items.get(i).id == card.id) { + break; + } + } + + this.currentCard = i; + this.headPanel.updateStep(i, ''+card.title+''); + //erik: fix to set on main title the title of the current cardç + //this.setTitle(card.title); + + if (i == len-1) { + this.nextButton.setText(this.finishButtonText); + } else { + this.nextButton.setText(this.nextButtonText); + } + + /*if (card.isValid()) { + this.nextButton.setDisabled(false); + }*/ + //this.nextButton.setDisabled(card.isValid()); + + if (i == 0) { + this.previousButton.setDisabled(true); + } else { + this.previousButton.setDisabled(false); + } + + }, + + + /** + * Fires the 'cancel'-event. Closes this dialog if the return value of the + * listeners does not equal to "false". + */ + onCancelClick : function() + { + if (this.fireEvent('cancel', this) !== false) { + this.close(); + } + }, + + /** + * Fires the 'finish'-event. Closes this dialog if the return value of the + * listeners does not equal to "false". + */ + onFinish : function() + { + if (this.fireEvent('finish', this, this.getWizardData()) !== false) { + this.close(); + } + }, + + /** + * Listener for the previous-button. + * Switches to the previous displayed {@link Ext.ux.Wiz.Card}. + */ + onPreviousClick : function() + { + if (this.currentCard > 0) { + this.cardPanel.getLayout().setActiveItem(this.currentCard - 1); + } + }, + + /** + * Listener for the next-button. Switches to the next {@link Ext.ux.Wiz.Card} + * if the 'beforehide'-method of it did not return false. The functionality + * for this is implemented in {@link Ext.ux.layout.CardLayout}, which is needed + * as the layout for the card-panel of this component. + */ + onNextClick : function() + { + if (this.currentCard == this.cardCount-1) { + this.onFinish(); + } else { + this.cardPanel.getLayout().setActiveItem(this.currentCard+1); + } + } +}); \ No newline at end of file diff --git a/workflow/engine/templates/installer/databases.tpl b/workflow/engine/templates/installer/databases.tpl new file mode 100644 index 000000000..8e714f09b --- /dev/null +++ b/workflow/engine/templates/installer/databases.tpl @@ -0,0 +1,30 @@ + + .ext-ux-wiz-Header { + background-color:white; + border-bottom:1px solid #99BBE8; + background-image:url(/images/processmaker.logo.jpg); + background-repeat: no-repeat; +} + +.ext-ux-wiz-Header-title { + font-weight: bold; + padding: 4px 0px 0px 4px; +} + +.ext-ux-wiz-Header-step { + color:#767676; + text-align:right; + padding:2px 4px 0px 0px; + font-size:13.5px; +} + +.ext-ux-wiz-Header-stepIndicator-container { + float:right; + margin-right:6px; + margin-top:8px; +} + +.ext-ux-wiz-Header-stepIndicator { + margin-left:28px; + float:left; + background-image:url(/images/ext-ux-wiz-stepIndicator.png); + background-position:6px 0px; + background-repeat:repeat-x; + height:6px; + width:6px; +} + +.ext-ux-wiz-Header-stepIndicator-active { + background-position:0px 0px !important; +} + + \ No newline at end of file diff --git a/workflow/engine/templates/installer/main.js b/workflow/engine/templates/installer/main.js new file mode 100644 index 000000000..130190606 --- /dev/null +++ b/workflow/engine/templates/installer/main.js @@ -0,0 +1,1020 @@ +//var licenseTxt; +var wizard; +var theme; + +Ext.onReady(function(){ + Ext.QuickTips.init(); + var steps = new Array(); + + var storeDatabase = new Ext.data.Store({ + proxy: new Ext.data.HttpProxy({url: 'getEngines', method:'POST'}), + reader: new Ext.data.JsonReader({ + fields: [{name: 'id'},{name: 'label'}] + }), + listeners: {load: function() { + Ext.getCmp('db_engine').setValue(Ext.getCmp('db_engine').store.getAt(0).id); + if (Ext.getCmp('db_engine').store.getAt(0).id == 'mysql') { + Ext.getCmp('db_port').setValue('3306'); + Ext.getCmp('db_username').setValue('root'); + } + else { + Ext.getCmp('db_port').setValue('1433'); + Ext.getCmp('db_username').setValue('sa'); + } + testConnection(); + }} + }); + + var store = new Ext.data.ArrayStore({ + fields: ['id', 'label'], + data : [['en', 'English'],['es', 'Spanish']] + }); + + var cmbLanguages = new Ext.form.ComboBox({ + fieldLabel: 'Language', + store: store, + labelWidth: 200, + displayField:'label', + typeAhead: true, + mode: 'local', + forceSelection: true, + triggerAction: 'all', + emptyText:'Select a language...', + selectOnFocus:true + }); + + // getting the system info + function getSystemInfo() { + wizard.showLoadMask(true); + Ext.Ajax.request({ + url: 'getSystemInfo', + success: function(response){ + var response = Ext.util.JSON.decode(response.responseText); + Ext.getCmp('php').setValue (getFieldOutput(response.php.version, response.php.result)); + Ext.getCmp('mysql').setValue (getFieldOutput(response.mysql.version, response.mysql.result)); + //Ext.getCmp('mssql').setValue (getFieldOutput(response.mssql.version, response.mssql.result)); + Ext.getCmp('curl').setValue (getFieldOutput(response.curl.version, response.curl.result)); + Ext.getCmp('openssl').setValue (getFieldOutput(response.openssl.version, response.openssl.result)); + Ext.getCmp('dom').setValue (getFieldOutput(response.dom.version, response.dom.result)); + Ext.getCmp('gd').setValue (getFieldOutput(response.gd.version, response.gd.result)); + Ext.getCmp('multibyte').setValue (getFieldOutput(response.multibyte.version,response.multibyte.result)); + Ext.getCmp('soap').setValue (getFieldOutput(response.soap.version, response.soap.result)); + Ext.getCmp('ldap').setValue (getFieldOutput(response.ldap.version, response.ldap.result)); + Ext.getCmp('memory').setValue (getFieldOutput(response.memory.version, response.memory.result)); + + dbReq = response.mysql.result || response.mssql.result; + phpReq = response.php.result && response.curl.result && response.dom.result && response.gd.result && response.multibyte.result && response.soap.result && response.memory.result; + wizard.onClientValidation(0, dbReq && phpReq); + wizard.showLoadMask(false); + }, + failure: function(){}, + params: {'clientBrowser': PMExt.getBrowser().name} + }); + } + + // getting the system info + function getPermissionInfo() { + wizard.showLoadMask(true); + Ext.Ajax.request({ + url: 'getPermissionInfo', + success: function(response) { + var okImage = ''; + var badImage = ''; + var response = Ext.util.JSON.decode(response.responseText); + Ext.get('pathConfigSpan').dom.innerHTML = (response.pathConfig.result ? okImage : badImage); + Ext.get('pathLanguagesSpan').dom.innerHTML = (response.pathLanguages.result ? okImage : badImage); + Ext.get('pathPluginsSpan').dom.innerHTML = (response.pathPlugins.result ? okImage : badImage); + Ext.get('pathXmlformsSpan').dom.innerHTML = (response.pathXmlforms.result ? okImage : badImage); + Ext.get('pathPublicSpan').dom.innerHTML = (response.pathPublic.result ? okImage : badImage); + Ext.get('pathSharedSpan').dom.innerHTML = (response.pathShared.result ? okImage : badImage); + Ext.get('pathLogFileSpan').dom.innerHTML = (response.pathLogFile.result ? okImage : badImage); + wizard.onClientValidation(1, response.pathConfig.result && response.pathLanguages.result && response.pathPlugins.result && response.pathXmlforms.result && response.pathShared.result && response.pathLogFile.result); + wizard.showLoadMask(false); + }, + failure: function(){}, + params: { + 'pathConfig': Ext.getCmp('pathConfig').getValue(), + 'pathLanguages': Ext.getCmp('pathLanguages').getValue(), + 'pathPlugins': Ext.getCmp('pathPlugins').getValue(), + 'pathXmlforms': Ext.getCmp('pathXmlforms').getValue(), + 'pathShared': Ext.getCmp('pathShared').getValue(), + 'pathLogFile': Ext.getCmp('pathLogFile').getValue(), + 'pathPublic': Ext.getCmp('pathPublic').getValue() + } + }); + } + + function checkLicenseAgree() { + wizard.onClientValidation(2, Ext.getCmp('agreeCheckbox').getValue()); + } + + + function ckeckDBEnginesValuesLoaded() { + wizard.showLoadMask(true); + if (Ext.getCmp('db_engine').store.getCount() == 0) { + Ext.getCmp('db_engine').store.load(); + } + else { + testConnection(); + } + } + + // test database Connection + function testConnection() { + wizard.showLoadMask(true); + if ((Ext.getCmp('db_engine').getValue() == '') || !Ext.getCmp('db_hostname').isValid() || !Ext.getCmp('db_username').isValid()) { + wizard.onClientValidation(3, false); + wizard.showLoadMask(false); + return false; + } + Ext.Ajax.request({ + url: 'testConnection', + success: function(response){ + var response = Ext.util.JSON.decode(response.responseText); + Ext.getCmp('db_message').setValue(getFieldOutput(response.message, response.result)); + wizard.onClientValidation(3, response.result); + wizard.showLoadMask(false); + }, + failure: function(){}, + params: { + 'db_engine' : Ext.getCmp('db_engine').getValue(), + 'db_hostname': Ext.getCmp('db_hostname').getValue(), + 'db_username': Ext.getCmp('db_username').getValue(), + 'db_password': Ext.getCmp('db_password').getValue(), + 'db_port' : Ext.getCmp('db_port').getValue() + } + }); + } + + function checkWorkspaceConfiguration() { + var canInstall = false; + if (!Ext.getCmp('workspace').isValid()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter a valid Workspace Name.', false)); + wizard.onClientValidation(4, false); + return; + } + if (!Ext.getCmp('adminUsername').isValid()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter a valid Admin Username.', false)); + wizard.onClientValidation(4, false); + return; + } + if (Ext.getCmp('adminPassword').getValue() == '') { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter the Admin Password.', false)); + wizard.onClientValidation(4, false); + return; + } + if (Ext.getCmp('adminPassword').getValue() != Ext.getCmp('confirmPassword').getValue()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('The password confirmation is incorrect.', false)); + wizard.onClientValidation(4, false); + return; + } + if (!Ext.getCmp('wfDatabase').isValid()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter the Workflow Database Name.', false)); + wizard.onClientValidation(4, false); + return; + } + if (!Ext.getCmp('rbDatabase').isValid()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter the Rbac Database Name.', false)); + wizard.onClientValidation(4, false); + return; + } + if (!Ext.getCmp('rpDatabase').isValid()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter the Report Database Name.', false)); + wizard.onClientValidation(4, false); + return; + } + checkDatabases(); + } + + function checkDatabases() { + wizard.showLoadMask(true); + Ext.Ajax.request({ + url: 'checkDatabases', + success: function(response){ + var existMsg = '(Exists)'; + var noExistsMsg = '(No exists)'; + var response = Ext.util.JSON.decode(response.responseText); + Ext.get('wfDatabaseSpan').dom.innerHTML = (response.wfDatabaseExists ? existMsg : noExistsMsg); + Ext.get('rbDatabaseSpan').dom.innerHTML = (response.rbDatabaseExists ? existMsg : noExistsMsg); + Ext.get('rpDatabaseSpan').dom.innerHTML = (response.rpDatabaseExists ? existMsg : noExistsMsg); + var dbFlag = ((!response.wfDatabaseExists && !response.rbDatabaseExists && !response.rpDatabaseExists) || Ext.getCmp('deleteDB').getValue()); + wizard.onClientValidation(4, dbFlag); + if (dbFlag) { + Ext.getCmp('finish_message').setValue(getFieldOutput('The data is correct.', true)); + } + else { + Ext.getCmp('finish_message').setValue(getFieldOutput('Rename the databases names or workspace name or check the "Delete Databases if exists" to overwrite the exiting databases.', false)); + } + wizard.showLoadMask(false); + }, + failure: function(){}, + params: { + 'db_engine' : Ext.getCmp('db_engine').getValue(), + 'db_hostname': Ext.getCmp('db_hostname').getValue(), + 'db_username': Ext.getCmp('db_username').getValue(), + 'db_password': Ext.getCmp('db_password').getValue(), + 'db_port' : Ext.getCmp('db_port').getValue(), + 'wfDatabase' : Ext.getCmp('wfDatabase').getValue(), + 'rbDatabase' : Ext.getCmp('rbDatabase').getValue(), + 'rpDatabase' : Ext.getCmp('rpDatabase').getValue() + } + }); + } + + + var setIndex = 0; + // first card with welcome message +/* steps[setIndex++] = new Ext.ux.Wiz.Card({ + title : 'Setting language', + labelAlign: 'right', + labelWidth: 200, + items : [ + { + border : false, + bodyStyle : 'background:none;', + html : 'Welcome to the Setup ProcessMaker wizard
'+ + 'Select your language from the list.

' + }, + { + border : false, + bodyStyle : 'background:none;padding-bottom:10px;', + html : 'ProcessMaker! 2.0.x Stable 18-Apr-2011 23:00 GMT:' + }, + cmbLanguages, + { + border : false, + bodyStyle : 'background:none;padding-top:250px;padding-left:420px;', + html : 'Please click the "next"-button and fill out all form values.'+ + '
' + } + ], + listeners: { + show: function(){ + //alert('s'); + //make akax call here + } + } + }); +*/ + // first card with Pre-Installation Check + steps[setIndex++] = new Ext.ux.Wiz.Card({ + title : 'Pre-installation check', + monitorValid : false, + labelAlign: 'left', + labelWidth: 200, + defaults : { + }, + items : [ + { + border : false, + html : 'Pre-installation check', + bodyStyle : 'background:none;padding-top:0px;padding-bottom:5px;font-weight:bold;font-size:1.3em;' + }, + { + xtype:'panel', + layout:'border', + height: 340, + items:[ + { + region: 'west', + width: 250, + bodyStyle : 'padding:10px;font-size:1.2em;', + html: step1_txt + }, + /*{ + region: 'south', + xtype : 'fieldset', + bodyStyle : 'padding:-3px;font-size:1.2em;', + height : 45, + labelAlign: 'right', + items:[ + new Ext.Button({ + text : 'Check Again', + handler : getSystemInfo, + scope : this + }) + ] + },*/ + { + region: 'center', + xtype : 'fieldset', + labelWidth: 200, + items:[ + { + xtype : 'displayfield', + fieldLabel: 'PHP Version >= 5.1', + id : 'php' + }, + { + xtype : 'displayfield', + fieldLabel: 'MySQL Support', + id : 'mysql' + }/*, + { + xtype : 'displayfield', + fieldLabel: 'MSSQL Support (*)', + id : 'mssql' + }*/, + { + xtype : 'displayfield', + fieldLabel: 'cURL Version', + id : 'curl' + }, + { + xtype : 'displayfield', + fieldLabel: 'OpenSSL Version (**)', + id : 'openssl' + }, + { + xtype : 'displayfield', + fieldLabel: 'DOM/XML Support', + id : 'dom' + }, + { + xtype : 'displayfield', + fieldLabel: 'GD Support', + id : 'gd' + }, + { + xtype : 'displayfield', + fieldLabel: 'Multibyte Strings Support', + id : 'multibyte' + }, + { + xtype : 'displayfield', + fieldLabel: 'Soap Support', + id : 'soap' + }, + { + xtype : 'displayfield', + fieldLabel: 'LDAP Support (***)', + id : 'ldap' + }, + { + xtype : 'displayfield', + fieldLabel: 'Memory Limit >= 80M', + id: 'memory', + value: '5.0 or greater' + }, + new Ext.Button({ + text : 'Check Again', + handler : getSystemInfo, + scope : this + }) + ] + } + ] + } + ], + listeners: { + show: getSystemInfo + } + }); + + // second with Pre-Installation Check + /* + steps[setIndex++] = new Ext.ux.Wiz.Card({ + title : 'Recomended Settings', + monitorValid : true, + labelAlign: 'right', + labelWidth: 200, + defaults : { + //labelStyle : 'font-size:11px' + }, + items : [ + { + border : false, + html:'Recomended Settings', + bodyStyle : 'background:none;padding-top:10px;padding-bottom:5px;font-weight:bold;' + }, + { + xtype:'panel', + layout:'border', + height: 120, + items:[ + { + region: 'west', + bodyStyle : 'padding:10px;font-size:1.2em;', + width: 250, + html: step2_txt + }, + { + region: 'center', + xtype : 'fieldset', + defaultType: 'displayfield', + items:[ + { + fieldLabel: 'PHP Version', + value: '5.1.0 or greater' + }, + { + fieldLabel: 'MySql Version', + value: '5.0 or greater' + } + ] + } + ] + } + ] + } ); + */ + + // third card with Directory File Permission + steps[setIndex++] = new Ext.ux.Wiz.Card({ + title:'Directory File Permission', + monitorValid : false, + labelAlign: 'left', + labelWidth: 200, + defaults : { + //labelStyle : 'font-size:11px' + }, + items : [ + { + border : false, + html:'Directory File Permission', + bodyStyle : 'background:none;padding-top:0px;padding-bottom:5px;font-weight:bold;font-size:1.3em;' + }, + { + xtype:'panel', + layout:'border', + height: 300, + items:[ + { + region: 'north', + height: 55, + bodyStyle : 'padding:10px;font-size:1.2em;', + html: step3_txt + }, + { + region: 'center', + xtype : 'fieldset', + alignField : 'left', + bodyStyle : 'padding-left:40px;font-size:12;', + labelWidth: 180, + items:[ + { + xtype: 'textfield', + fieldLabel: ' Config Directory', + id: 'pathConfig', + width: 430, + value: path_config, + disabled: true + }, + { + xtype: 'textfield', + fieldLabel: ' Language Directory', + id: 'pathLanguages', + width: 430, + value: path_languages, + disabled: true + }, + { + xtype: 'textfield', + fieldLabel: ' Plugins Directory', + id: 'pathPlugins', + width: 430, + value: path_plugins, + disabled: true + }, + { + xtype: 'textfield', + fieldLabel: ' Xmlform Directory Directory', + id: 'pathXmlforms', + width: 430, + value: path_xmlforms, + disabled: true + }, + { + xtype: 'textfield', + fieldLabel: ' Public Directory', + id: 'pathPublic', + width: 430, + value: path_public, + disabled: true + }, + { + xtype: 'textfield', + fieldLabel: ' Workflow Data Directory', + id: 'pathShared', + width: 430, + value: path_shared, + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(2, false); + if (Ext.getCmp('pathShared').getValue().substr(-1, 1) != path_sep) { + Ext.getCmp('pathLogFile').setValue(Ext.getCmp('pathShared').getValue() + path_sep + 'log' + path_sep + 'install.log'); + } + else { + Ext.getCmp('pathLogFile').setValue(Ext.getCmp('pathShared').getValue() + 'log' + path_sep + 'install.log'); + } + }} + }, + { + xtype: 'textfield', + fieldLabel: ' Installation log file', + id: 'pathLogFile', + width: 430, + value: path_shared + 'log' + path_sep + 'install.log', + disabled: true + }, + new Ext.Button({ + text : 'Check Again', + handler : getPermissionInfo, + scope : this + }) + ] + } + ] + } + ], + listeners: { + show: getPermissionInfo + } + + } ); + + // third card with input field email-address + steps[setIndex++] = new Ext.ux.Wiz.Card({ + title:'ProcessMaker Open Source License', + //monitorValid : false, + defaults : { + labelStyle : 'font-size:12px' + }, + items : [ + { + border : false, + html:'ProcessMaker Open Source License', + bodyStyle : 'background:none;padding-top:0px;padding-bottom:5px;font-weight:bold;font-size:1.3em;' + }, + { + xtype:'panel', + layout:'border', + height: 340, + items:[ + { + region: 'center', + xtype : 'fieldset', + items:[ + new Ext.form.TextArea({ + name : 'license', + readOnly : true, + width : 510, + height : 280, + style : 'font-size:13px', + value : licenseTxt + }), + new Ext.form.Checkbox({ + boxLabel : 'I agree', + id : 'agreeCheckbox', + handler: function() { + wizard.onClientValidation(2, this.getValue()); + } + }) + ] + } + ] + } + ], + listeners: { + show: checkLicenseAgree + } + + }); + + // fourth card Database Configuration + steps[setIndex++] = new Ext.ux.Wiz.Card({ + title : 'Database Configuration', + monitorValid : false, + items : [ + { + border : false, + html : 'Database Configuration', + bodyStyle : 'background:none;padding-top:0px;padding-bottom:5px;font-weight:bold;font-size:1.3em;' + }, + { + xtype:'panel', + layout:'border', + height: 360, + items:[ + { + region: 'west', + width: 200, + bodyStyle : 'padding:10px;font-size:1.2em;', + html: step4_txt + }, + { + region: 'center', + xtype : 'panel', + bodyStyle : 'background:none;padding-left:20px;padding-right:20px;padding-top:20px;padding-bottom:20px;font-size:1.2em;', + items:[ + { + xtype:'fieldset', + labelAlign: 'left', + labelWidth: 160, + items:[ + new Ext.form.ComboBox({ + fieldLabel: 'Database Engine', + width : 200, + store : storeDatabase, + displayField : 'label', + valueField : 'id', + mode : 'local', + editable : false, + forceSelection: true, + allowBlank: false, + triggerAction: 'all', + id: 'db_engine', + selectOnFocus : true, + listeners: {select: function() { + if (this.value == 'mysql') { + Ext.getCmp('db_port').setValue('3306'); + Ext.getCmp('db_username').setValue('root'); + } + else { + Ext.getCmp('db_port').setValue('1433'); + Ext.getCmp('db_username').setValue('sa'); + } + wizard.onClientValidation(3, false); + }} + }), + { + xtype : 'textfield', + fieldLabel: 'Host Name', + width : 180, + id: 'db_hostname', + value :'localhost', + allowBlank : false, + validator : function(v){ + var t = /^[0-9\.a-zA-Z_\-]+$/; + return t.test(v); + }, + listeners: {change: function() { + wizard.onClientValidation(3, false); + }} + }, + { + xtype : 'textfield', + fieldLabel: 'Port', + width : 180, + id: 'db_port', + value :'', + allowBlank : false, + validator : function(v){ + var t = /^[0-9]+$/; + return t.test(v); + }, + listeners: {change: function() { + wizard.onClientValidation(3, false); + }} + }, + { + xtype : 'textfield', + fieldLabel: 'Username', + width : 180, + id: 'db_username', + value :'root', + allowBlank : false, + validator : function(v){ + var t = /^[.a-zA-Z_\-]+$/; + return t.test(v); + }, + listeners: {change: function() { + wizard.onClientValidation(3, false); + }} + }, + { + xtype : 'textfield', + fieldLabel: 'Password', + inputType : 'password', + value : '', + width : 180, + id: 'db_password', + allowBlank : true, + listeners: {change: function() { + wizard.onClientValidation(3, false); + }} + }, + { + xtype : 'displayfield', + //fieldLabel: ', + id : 'db_message' + }, + new Ext.Button({ + text : ' Test Connection', + handler : testConnection, + scope : this + }) + ] + }, + ] + } + ] + } + ], + listeners: { + show: ckeckDBEnginesValuesLoaded + } + }); + + + steps[setIndex++] = new Ext.ux.Wiz.Card({ + title : 'Workspace Configuration', + monitorValid : false, + defaults : { + labelStyle : 'font-size:11px' + }, + items : [ + { + border : false, + html : 'Workspace Configuration', + bodyStyle : 'background:none;padding-top:0px;padding-bottom:5px;font-weight:bold;font-size:1.3em;' + }, + { + xtype:'panel', + layout:'border', + height: 360, + items:[ + { + region: 'west', + width: 200, + bodyStyle : 'padding:10px;font-size:1.2em;', + html: step5_txt + }, + { + region: 'center', + xtype : 'panel', + bodyStyle : 'background:none;padding-left:20px;padding-right:20px;padding-top:20px;padding-bottom:20px;font-size:1.2em;', + items:[ + { + xtype:'fieldset', + //labelAlign: 'right', + labelWidth: 210, + items:[ + { + xtype : 'textfield', + fieldLabel: 'Workspace Name', + value :'workflow', + maxLength: 29, + validator : function(v){ + var t = /^[a-zA-Z_0-9]+$/; + return t.test(v); + }, + id : 'workspace', + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(4, false); + if (!Ext.getCmp('changeDBNames').getValue()) { + Ext.getCmp('wfDatabase').setValue('wf_' + this.getValue()); + Ext.getCmp('rbDatabase').setValue('rb_' + this.getValue()); + Ext.getCmp('rpDatabase').setValue('rp_' + this.getValue()); + } + }} + }, + { + xtype : 'textfield', + fieldLabel: 'Admin Username', + value :'admin', + validator : function(v){ + var t = /^[a-zA-Z_0-9.@-]+$/; + return t.test(v); + }, + id : 'adminUsername', + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(4, false); + }} + }, + { + xtype : 'textfield', + fieldLabel: 'Admin Password', + inputType : 'password', + id: 'adminPassword', + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(4, false); + }} + }, + { + xtype : 'textfield', + fieldLabel: 'Confirm Admin Password', + inputType : 'password', + id : 'confirmPassword', + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(4, false); + }} + } + ] + }, + { + xtype : 'fieldset', + labelAlign: 'left', + labelWidth: 210, + //labelWidth: 200, + //title: 'ProcessMaker Databases', + items:[ + new Ext.form.Checkbox({ + boxLabel: 'Change Database names', + id : 'changeDBNames', + handler: function() { + if (this.getValue()) { + Ext.getCmp('wfDatabase').enable(); + Ext.getCmp('rbDatabase').enable(); + Ext.getCmp('rpDatabase').enable(); + Ext.getCmp('wfDatabase').validate(); + Ext.getCmp('rbDatabase').validate(); + Ext.getCmp('rpDatabase').validate(); + } + else { + Ext.getCmp('wfDatabase').setValue('wf_' + Ext.getCmp('workspace').getValue()); + Ext.getCmp('rbDatabase').setValue('rb_' + Ext.getCmp('workspace').getValue()); + Ext.getCmp('rpDatabase').setValue('rp_' + Ext.getCmp('workspace').getValue()); + Ext.getCmp('wfDatabase').disable(); + Ext.getCmp('rbDatabase').disable(); + Ext.getCmp('rpDatabase').disable(); + } + wizard.onClientValidation(4, false); + } + }), + { + xtype : 'textfield', + fieldLabel: 'Workflow Database Name ', + id : 'wfDatabase', + value :'wf_workflow', + allowBlank : false, + maxLength: 32, + validator : function(v){ + var t = /^[a-zA-Z_0-9]+$/; + return t.test(v); + }, + disabled: true, + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(4, false); + }} + }, + { + xtype : 'textfield', + fieldLabel: 'Rbac Database Name ', + id : 'rbDatabase', + value :'rb_workflow', + allowBlank : false, + maxLength: 32, + validator : function(v){ + var t = /^[a-zA-Z_0-9]+$/; + return t.test(v); + }, + disabled: true, + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(4, false); + }} + }, + { + xtype : 'textfield', + fieldLabel: 'Report Database Name ', + id : 'rpDatabase', + value :'rp_workflow', + allowBlank : false, + maxLength: 32, + validator : function(v){ + var t = /^[a-zA-Z_0-9]+$/; + return t.test(v); + }, + disabled: true, + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(4, false); + }} + }, + new Ext.form.Checkbox({ + boxLabel : "Delete Databases if exists", + id : 'deleteDB', + handler: function() { + wizard.onClientValidation(4, false); + } + }), + { + xtype : 'displayfield', + id : 'finish_message' + }, + new Ext.Button({ + id: 'checkWSConfiguration', + text: ' Check Workspace Configuration', + handler: checkWorkspaceConfiguration, + scope: this + }) + ] + } + ] + } + ] + } + ], + listeners: { + show: function() { + checkWorkspaceConfiguration(); + } + } + }); + + wizard = new Ext.ux.Wiz({ + height: 520, + width : 780, + id : 'wizard', + closable: false, + headerConfig : { + title : ' ' + }, + cardPanelConfig : { + defaults : { + bodyStyle : 'padding:20px 10px 10px 20px;background-color:#F6F6F6;', + border : false + } + }, + cards : steps, + loadMaskConfig: { + default: 'Checking...', + finishing: 'Finishing...' + }, + listeners: { + finish: finishInstallation + } + }); + + // show the wizard + wizard.show(); +}); + + +function finishInstallation() +{ + wizard.showLoadMask(true, 'finishing'); + Ext.Ajax.request({ + url: 'createWorkspace', + success: function(response){ + var response = Ext.util.JSON.decode(response.responseText); + Ext.getCmp('finish_message').setValue(getFieldOutput(response.message, response.result)); + wizard.showLoadMask(false); + if (response.result) { + wizard.onClientValidation(4, false); + + //Ext.msgBoxSlider.msgTopCenter( + PMExt.info( + 'ProcessMaker Installation', + 'ProcessMaker was successfully installed
Workspace "' + Ext.getCmp('workspace').getValue() + '" was installed correctly.', + function(){ + _redirect(response.uri); + } + ); + + //setTimeout("_redirect('"+response.url+"')", 3000); + // Ext.Msg.alert( + // 'ProcessMaker was successfully installed', + // 'Workspace "' + Ext.getCmp('workspace').getValue() + '" was installed correctly now you will be redirected to your new workspace.', + // function() {_redirectwindow.location = response.url;} + // ); + } + }, + failure: function(){wizard.showLoadMask(false);}, + params: { + 'db_engine' : Ext.getCmp('db_engine' ).getValue(), + 'db_hostname' : Ext.getCmp('db_hostname' ).getValue(), + 'db_username' : Ext.getCmp('db_username' ).getValue(), + 'db_password' : Ext.getCmp('db_password' ).getValue(), + 'db_port' : Ext.getCmp('db_port' ).getValue(), + 'pathConfig' : Ext.getCmp('pathConfig' ).getValue(), + 'pathLanguages' : Ext.getCmp('pathLanguages').getValue(), + 'pathPlugins' : Ext.getCmp('pathPlugins' ).getValue(), + 'pathXmlforms' : Ext.getCmp('pathXmlforms' ).getValue(), + 'pathShared' : Ext.getCmp('pathShared' ).getValue(), + 'workspace' : Ext.getCmp('workspace' ).getValue(), + 'adminUsername' : Ext.getCmp('adminUsername').getValue(), + 'adminPassword' : Ext.getCmp('adminPassword').getValue(), + 'wfDatabase' : Ext.getCmp('wfDatabase' ).getValue(), + 'rbDatabase' : Ext.getCmp('rbDatabase' ).getValue(), + 'rpDatabase' : Ext.getCmp('rpDatabase' ).getValue(), + 'deleteDB' : Ext.getCmp('deleteDB' ).getValue() + }, + timeout: 180000 + }); +} + +function _redirect(_uri){ + console.log('redirecting:: '+_uri); + window.location = _uri; +} + +function getFieldOutput(txt, assert) +{ + if(assert == true) { + img = 'dialog-ok-apply.png'; + size = 'width=12 height=12'; + color = 'green'; + } else { + img = 'delete.png'; + size = 'width=15 height=15'; + color = 'red'; + } + return ''+txt + ' '; +} \ No newline at end of file diff --git a/workflow/engine/templates/installer/newSite.html b/workflow/engine/templates/installer/newSite.html new file mode 100644 index 000000000..f4469cd8e --- /dev/null +++ b/workflow/engine/templates/installer/newSite.html @@ -0,0 +1,41 @@ + \ No newline at end of file diff --git a/workflow/engine/templates/installer/newSite.js b/workflow/engine/templates/installer/newSite.js new file mode 100644 index 000000000..4a77459c9 --- /dev/null +++ b/workflow/engine/templates/installer/newSite.js @@ -0,0 +1,535 @@ +Ext.onReady(function(){ + var getFieldOutput = function(txt, assert) { + if(assert == true) { + img = 'dialog-ok-apply.png'; + size = 'width=12 height=12'; + color = 'green'; + } else { + img = 'delete.png'; + size = 'width=15 height=15'; + color = 'red'; + } + return '' + txt + ' '; + }; + + var testConnection = function() { + wizard.showLoadMask(true); + if ((Ext.getCmp('db_engine').getValue() == '') || !Ext.getCmp('db_hostname').isValid() || !Ext.getCmp('db_username').isValid()) { + wizard.onClientValidation(1, false); + wizard.showLoadMask(false); + return false; + } + Ext.Ajax.request({ + url: 'newSite', + success: function(response){ + var response = Ext.util.JSON.decode(response.responseText); + Ext.getCmp('db_message').setValue(getFieldOutput(response.message, response.result)); + wizard.onClientValidation(1, response.result); + wizard.showLoadMask(false); + }, + failure: function(){}, + params: { + 'action': 'testConnection', + 'db_engine': Ext.getCmp('db_engine').getValue(), + 'db_hostname': Ext.getCmp('db_hostname').getValue(), + 'db_port': Ext.getCmp('db_port').getValue(), + 'db_username': Ext.getCmp('db_username').getValue(), + 'db_password': Ext.getCmp('db_password').getValue() + } + }); + }; + + var ckeckDBEnginesValuesLoaded = function() { + wizard.showLoadMask(true); + if (Ext.getCmp('db_engine').store.getCount() == 0) { + Ext.getCmp('db_engine').store.load(); + } + else { + testConnection(); + } + }; + + var checkWorkspaceConfiguration = function() { + var canInstall = false; + if (!Ext.getCmp('workspace').isValid()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter a valid Workspace Name.', false)); + wizard.onClientValidation(2, false); + return; + } + if (!Ext.getCmp('adminUsername').isValid()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter a valid Admin Username.', false)); + wizard.onClientValidation(2, false); + return; + } + if (Ext.getCmp('adminPassword').getValue() == '') { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter the Admin Password.', false)); + wizard.onClientValidation(2, false); + return; + } + if (Ext.getCmp('adminPassword').getValue() != Ext.getCmp('confirmPassword').getValue()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('The password confirmation is incorrect.', false)); + wizard.onClientValidation(2, false); + return; + } + if (!Ext.getCmp('wfDatabase').isValid()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter the Workflow Database Name.', false)); + wizard.onClientValidation(2, false); + return; + } + if (!Ext.getCmp('rbDatabase').isValid()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter the Rbac Database Name.', false)); + wizard.onClientValidation(2, false); + return; + } + if (!Ext.getCmp('rpDatabase').isValid()) { + Ext.getCmp('finish_message').setValue(getFieldOutput('Please enter the Report Database Name.', false)); + wizard.onClientValidation(2, false); + return; + } + checkDatabases(); + }; + + var checkDatabases = function() { + wizard.showLoadMask(true); + Ext.Ajax.request({ + url: 'newSite', + success: function(response){ + var existMsg = '(Exists)'; + var noExistsMsg = '(No exists)'; + var response = Ext.util.JSON.decode(response.responseText); + Ext.get('wfDatabaseSpan').dom.innerHTML = (response.wfDatabaseExists ? existMsg : noExistsMsg); + Ext.get('rbDatabaseSpan').dom.innerHTML = (response.rbDatabaseExists ? existMsg : noExistsMsg); + Ext.get('rpDatabaseSpan').dom.innerHTML = (response.rpDatabaseExists ? existMsg : noExistsMsg); + var dbFlag = ((!response.wfDatabaseExists && !response.rbDatabaseExists && !response.rpDatabaseExists) || Ext.getCmp('deleteDB').getValue()); + wizard.onClientValidation(2, dbFlag); + if (dbFlag) { + Ext.getCmp('finish_message').setValue(getFieldOutput('The data is correct.', true)); + } + else { + Ext.getCmp('finish_message').setValue(getFieldOutput('Rename the databases names or workspace name or check the "Delete Databases if exists" to overwrite the exiting databases.', false)); + } + wizard.showLoadMask(false); + }, + failure: function(){}, + params: { + 'action': 'checkDatabases', + 'db_engine': Ext.getCmp('db_engine').getValue(), + 'db_hostname': Ext.getCmp('db_hostname').getValue(), + 'db_username': Ext.getCmp('db_username').getValue(), + 'db_password': Ext.getCmp('db_password').getValue(), + 'db_port': Ext.getCmp('db_port').getValue(), + 'wfDatabase': Ext.getCmp('wfDatabase').getValue(), + 'rbDatabase': Ext.getCmp('rbDatabase').getValue(), + 'rpDatabase': Ext.getCmp('rpDatabase').getValue() + } + }); + }; + + var steps = []; + var setIndex = 0; + var storeDatabase = new Ext.data.Store({ + proxy: new Ext.data.HttpProxy({url: 'newSite?action=getEngines', method: 'POST'}), + reader: new Ext.data.JsonReader({ + fields: [{name: 'id'},{name: 'label'}] + }), + listeners: {load: function() { + Ext.getCmp('db_engine').setValue(DB_ADAPTER); + testConnection(); + }} + }); + + steps[setIndex++] = new Ext.ux.Wiz.Card({ + title: 'Database Configuration', + monitorValid: false, + items: [ + { + border: false, + html: 'Database Configuration', + bodyStyle: 'background:none;padding-top:0px;padding-bottom:5px;font-weight:bold;font-size:1.3em;' + }, + { + xtype:'panel', + layout:'border', + height: 360, + items: [ + { + region: 'west', + width: 200, + bodyStyle: 'padding:10px;font-size:1.2em;', + html: textStep1 + }, + { + region: 'center', + xtype: 'panel', + bodyStyle: 'background:none;padding-left:20px;padding-right:20px;padding-top:20px;padding-bottom:20px;font-size:1.2em;', + items:[ + { + xtype:'fieldset', + labelAlign: 'left', + labelWidth: 160, + items: [ + new Ext.form.ComboBox({ + fieldLabel: 'Database Engine', + width: 200, + store: storeDatabase, + displayField: 'label', + valueField: 'id', + mode: 'local', + editable: false, + forceSelection: true, + allowBlank: false, + triggerAction: 'all', + id: 'db_engine', + selectOnFocus: true, + listeners: {select: function() { + if (this.value == 'mysql') { + Ext.getCmp('db_port').setValue('3306'); + Ext.getCmp('db_username').setValue('root'); + } + else { + Ext.getCmp('db_port').setValue('1433'); + Ext.getCmp('db_username').setValue('sa'); + } + wizard.onClientValidation(1, false); + }} + }), + { + xtype: 'textfield', + fieldLabel: 'Host Name', + width: 180, + id: 'db_hostname', + value: DB_HOST, + allowBlank: false, + validator: function(v){ + var t = /^[0-9\.a-zA-Z_\-]+$/; + return t.test(v); + }, + listeners: {change: function() { + wizard.onClientValidation(1, false); + }} + }, + { + xtype: 'textfield', + fieldLabel: 'Port', + width: 180, + id: 'db_port', + value: DB_PORT, + allowBlank: false, + validator: function(v){ + var t = /^[0-9]+$/; + return t.test(v); + }, + listeners: {change: function() { + wizard.onClientValidation(1, false); + }} + }, + { + xtype: 'textfield', + fieldLabel: 'Username', + width: 180, + id: 'db_username', + value: DB_USER, + allowBlank: false, + validator: function(v){ + var t = /^[.a-zA-Z_\-]+$/; + return t.test(v); + }, + listeners: {change: function() { + wizard.onClientValidation(1, false); + }} + }, + { + xtype: 'textfield', + fieldLabel: 'Password', + inputType: 'password', + width: 180, + id: 'db_password', + value: DB_PASS, + allowBlank: true, + listeners: {change: function() { + wizard.onClientValidation(1, false); + }} + }, + { + xtype: 'displayfield', + id: 'db_message' + }, + new Ext.Button({ + text: ' Test Connection', + handler: testConnection, + scope: this + }) + ] + }, + ] + } + ] + } + ], + listeners: { + show: ckeckDBEnginesValuesLoaded + } + }); + + steps[setIndex++] = new Ext.ux.Wiz.Card({ + title: 'Workspace Configuration', + monitorValid: false, + defaults: { + labelStyle: 'font-size:11px' + }, + items: [ + { + border: false, + html: 'Workspace Configuration', + bodyStyle: 'background:none;padding-top:0px;padding-bottom:5px;font-weight:bold;font-size:1.3em;' + }, + { + xtype:'panel', + layout:'border', + height: 360, + items:[ + { + region: 'west', + width: 200, + bodyStyle: 'padding:10px;font-size:1.2em;', + html: textStep2 + }, + { + region: 'center', + xtype: 'panel', + bodyStyle: 'background:none;padding-left:20px;padding-right:20px;padding-top:20px;padding-bottom:20px;font-size:1.2em;', + items: [ + { + xtype:'fieldset', + //labelAlign: 'right', + labelWidth: 210, + items:[ + { + xtype: 'textfield', + fieldLabel: 'Workspace Name', + value:'workflow', + maxLength: 29, + validator: function(v){ + var t = /^[a-zA-Z_0-9]+$/; + return t.test(v); + }, + id: 'workspace', + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(2, false); + if (!Ext.getCmp('changeDBNames').getValue()) { + Ext.getCmp('wfDatabase').setValue('wf_' + this.getValue()); + Ext.getCmp('rbDatabase').setValue('rb_' + this.getValue()); + Ext.getCmp('rpDatabase').setValue('rp_' + this.getValue()); + } + }} + }, + { + xtype: 'textfield', + fieldLabel: 'Admin Username', + value:'admin', + validator: function(v){ + var t = /^[a-zA-Z_0-9.@-]+$/; + return t.test(v); + }, + id: 'adminUsername', + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(2, false); + }} + }, + { + xtype: 'textfield', + fieldLabel: 'Admin Password', + inputType: 'password', + id: 'adminPassword', + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(2, false); + }} + }, + { + xtype: 'textfield', + fieldLabel: 'Confirm Admin Password', + inputType: 'password', + id: 'confirmPassword', + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(2, false); + }} + } + ] + }, + { + xtype: 'fieldset', + labelAlign: 'left', + labelWidth: 210, + //labelWidth: 200, + //title: 'ProcessMaker Databases', + items:[ + new Ext.form.Checkbox({ + boxLabel: 'Change Database names', + id: 'changeDBNames', + handler: function() { + if (this.getValue()) { + Ext.getCmp('wfDatabase').enable(); + Ext.getCmp('rbDatabase').enable(); + Ext.getCmp('rpDatabase').enable(); + Ext.getCmp('wfDatabase').validate(); + Ext.getCmp('rbDatabase').validate(); + Ext.getCmp('rpDatabase').validate(); + } + else { + Ext.getCmp('wfDatabase').setValue('wf_' + Ext.getCmp('workspace').getValue()); + Ext.getCmp('rbDatabase').setValue('rb_' + Ext.getCmp('workspace').getValue()); + Ext.getCmp('rpDatabase').setValue('rp_' + Ext.getCmp('workspace').getValue()); + Ext.getCmp('wfDatabase').disable(); + Ext.getCmp('rbDatabase').disable(); + Ext.getCmp('rpDatabase').disable(); + } + wizard.onClientValidation(2, false); + } + }), + { + xtype: 'textfield', + fieldLabel: 'Workflow Database Name ', + id: 'wfDatabase', + value:'wf_workflow', + allowBlank: false, + maxLength: 32, + validator: function(v){ + var t = /^[a-zA-Z_0-9]+$/; + return t.test(v); + }, + disabled: true, + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(2, false); + }} + }, + { + xtype: 'textfield', + fieldLabel: 'Rbac Database Name ', + id: 'rbDatabase', + value:'rb_workflow', + allowBlank: false, + maxLength: 32, + validator: function(v){ + var t = /^[a-zA-Z_0-9]+$/; + return t.test(v); + }, + disabled: true, + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(2, false); + }} + }, + { + xtype: 'textfield', + fieldLabel: 'Report Database Name ', + id: 'rpDatabase', + value:'rp_workflow', + allowBlank: false, + maxLength: 32, + validator: function(v){ + var t = /^[a-zA-Z_0-9]+$/; + return t.test(v); + }, + disabled: true, + enableKeyEvents: true, + listeners: {keyup: function() { + wizard.onClientValidation(2, false); + }} + }, + new Ext.form.Checkbox({ + boxLabel: "Delete Databases if exists", + id: 'deleteDB', + handler: function() { + wizard.onClientValidation(2, false); + } + }), + { + xtype: 'displayfield', + id: 'finish_message' + }, + new Ext.Button({ + id: 'checkWSConfiguration', + text: ' Check Workspace Configuration', + handler: checkWorkspaceConfiguration, + scope: this + }) + ] + } + ] + } + ] + } + ], + listeners: { + show: function() { + checkWorkspaceConfiguration(); + } + } + }); + + var wizard = new Ext.ux.Wiz({ + height: 520, + width: 780, + id: 'wizard', + closable: false, + headerConfig: { + title: ' ' + }, + cardPanelConfig: { + defaults: { + bodyStyle: 'padding:20px 10px 10px 20px;background-color:#F6F6F6;', + border: false + } + }, + cards: steps, + loadMaskConfig: { + default: 'Checking...', + finishing: 'Finishing...' + }, + listeners: { + finish: function(){ + wizard.showLoadMask(true, 'finishing'); + Ext.Ajax.request({ + url: 'newSite', + success: function(response){ + var response = Ext.util.JSON.decode(response.responseText); + Ext.getCmp('finish_message').setValue(getFieldOutput(response.message, response.result)); + wizard.showLoadMask(false); + if (response.result) { + Ext.Msg.alert('ProcessMaker was successfully installed', 'Workspace "' + Ext.getCmp('workspace').getValue() + '" was installed correctly now you will be redirected to your new workspace.', function() {window.location = response.url;}); + } + }, + failure: function(){wizard.showLoadMask(false);}, + params: { + 'action': 'createWorkspace', + 'db_engine': Ext.getCmp('db_engine').getValue(), + 'db_hostname': Ext.getCmp('db_hostname').getValue(), + 'db_username': Ext.getCmp('db_username').getValue(), + 'db_password': Ext.getCmp('db_password').getValue(), + 'db_port': Ext.getCmp('db_port').getValue(), + 'pathConfig': pathConfig, + 'pathLanguages': pathLanguages, + 'pathPlugins': pathPlugins, + 'pathXmlforms': pathXmlforms, + 'pathShared': pathShared, + 'workspace': Ext.getCmp('workspace').getValue(), + 'adminUsername': Ext.getCmp('adminUsername').getValue(), + 'adminPassword': Ext.getCmp('adminPassword').getValue(), + 'wfDatabase': Ext.getCmp('wfDatabase').getValue(), + 'rbDatabase': Ext.getCmp('rbDatabase').getValue(), + 'rpDatabase': Ext.getCmp('rpDatabase').getValue(), + 'deleteDB': Ext.getCmp('deleteDB').getValue() + }, + timeout: 180000 + }); + } + } + }); + + wizard.show(); +}); \ No newline at end of file diff --git a/workflow/engine/templates/main/forgotPassword.tpl b/workflow/engine/templates/main/forgotPassword.tpl new file mode 100644 index 000000000..da21504d6 --- /dev/null +++ b/workflow/engine/templates/main/forgotPassword.tpl @@ -0,0 +1,26 @@ + + + + + + + + + + + + +
+ +
+

{serviceMsg}

+ {content} {passwd} +
+
+
+ {poweredBy} {versionLabel} {version} +
+ {visit} http://www.processmaker.com +
+
\ No newline at end of file diff --git a/workflow/engine/templates/main/index.html b/workflow/engine/templates/main/index.html new file mode 100644 index 000000000..eb8c31270 --- /dev/null +++ b/workflow/engine/templates/main/index.html @@ -0,0 +1,30 @@ +
+ + + + + + + + +
+ + + + + + + +
+ +
+ {userfullname} 
+ {rolename}  +
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/workflow/engine/templates/main/index.js b/workflow/engine/templates/main/index.js new file mode 100644 index 000000000..8468d5f17 --- /dev/null +++ b/workflow/engine/templates/main/index.js @@ -0,0 +1,244 @@ +/** + * Main Controller for processMaker v2.x + * @date Jul 17, 2011 + * @author Erik Amaru Ortiz + */ + +var Main = function() { + return { + /** properties */ + panels : new Array(), + configuration: {}, + viewport : null, + systemInfoWindow : null, + + /** init method */ + init : function() { + + Ext.QuickTips.init(); + Ext.form.Field.prototype.msgTarget = 'side'; + + this.configureComponents(); + this.buildComponents(); + + this.viewport = new Ext.Viewport({ + layout: 'border', + items: [this.panels] + }); + + Ext.getCmp('eastPanel').hide(); + Ext.getCmp('westPanel').hide(); + Ext.getCmp('southPanel').hide(); + + Ext.getCmp('centerPanel').ownerCt.doLayout(); + + if (typeof flyNotify != 'undefined') { + Ext.msgBoxSlider.msgTopCenter(flyNotify.type, flyNotify.title, flyNotify.text, flyNotify.time); + } + } + } +}(); + +Main.configureComponents = function() +{ + this.configuration.eastPanel = { + id:'eastPanel', + region: 'east', + width: 200, + height: 500, + minSize: 175, + maxSize: 400, + split: true, + collapsible: true, + items: [] + }; + + this.configuration.centerPanel = { + id:'centerPanel', + region: 'center', + layout: 'fit', + width: 200, + margins: '0 0 0 0' // top right botom left + }; + + this.configuration.centerPanel.items = new Array(); + this.configuration.centerPanel.items.push({ + xtype:"tabpanel", + id: 'mainTabPanel', + defaultType:"iframepanel", + activeTab: activeTab != '' ? activeTab : 0 + }); + + this.configuration.westPanel = { + id:'westPanel', + title: '', + region: 'west', + width: 200, + split: true, + collapsible: true, + items: [] + }; + + this.configuration.northPanel = { + id:'northPanel', + region: 'north', + height: 40, + applyTo: 'panel-header', + margins: '0 0 0 0', // top right botom left + items: [] + }; + + this.configuration.southPanel = { + id:'southPanel', + region: 'south', + height: 68, + margins: '0 0 0 0', // top right botom left + items: [] + }; + + this.configuration.userMenu = {} + this.configuration.userMenu.items = new Array(); + this.configuration.userMenu.items.push({ + text : _("ID_VIEW_EDIT_PROFILE"), + icon: '/images/profile-picture.png', + handler: function() { + Main._addTab('profile', 'Profile', 'users/usersInit'); + } + }); + + /*this.configuration.userMenu.items.push({ + id:'skinMenu', + text : _("ID_SKINS"), + icon: '/images/icon-pmskins.png' + });*/ + + if (showSystemInfo) { + this.configuration.userMenu.items.push({ + text : _('ID_SYSTEM_INFO'), + icon: '/images/sys-info-icon.png', + handler: systemInfo + }); + } + + this.configuration.userMenu.items.push({ + text : _('ID_LOGOUT'), + icon: '/images/logout.gif', + handler: function() { + location.href = 'main/login'; + } + }); +}; + +Main.buildComponents = function() +{ + var centerTabPanelItems = new Array(); + for (var i=0; i + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + +{login_script} +{login_vars} + + \ No newline at end of file diff --git a/workflow/engine/templates/main/login.js b/workflow/engine/templates/main/login.js new file mode 100644 index 000000000..5a8a81331 --- /dev/null +++ b/workflow/engine/templates/main/login.js @@ -0,0 +1,480 @@ +/* + * ProcessMaker Login + * Created on date Jul 15, 2011 + * + * @author Erik Amaru Ortiz + */ + +var loadMask = function(){ + return { + init: function() { + var loading = Ext.get('loading'); + var mask = Ext.get('loading-mask'); + mask.setOpacity(0.8); + mask.shift({ + xy: loading.getXY(), + width: loading.getWidth(), + height: loading.getHeight(), + remove: true, + duration: 1, + opacity: 0.3, + easing: 'bounceOut', + callback: function(){ + loading.fadeOut({ + duration: 0.2, + remove: true + }); + document.getElementById('loginLogo').style.display = 'block'; + } + }); + }}; +}(); + +Ext.onReady(loadMask.init, loadMask, true); + +var Login = function() { + return { + /** Properties */ + form : null, + window : null, + enableVirtualKeyboard : false, + enableForgotPassword : false, + fieldsWidth : 200, + + /** Init method */ + init : function() { + new Ext.KeyMap(document, { + key: [10, 13], + fn: function(keycode, e) { + Login.submit(); + } + }); + + Ext.QuickTips.init(); + Ext.form.Field.prototype.msgTarget = 'side'; + + this.enableVirtualKeyboard = virtualKeyboad; + this.enableForgotPassword = forgotPasswd; + + this.initComponents(); + + this.window.show(); + Ext.getCmp('userTxt').focus(true, 1000); + + if (typeof errMsg != 'undefined') { + Ext.msgBoxSlider.msgTopCenter('alert', 'ERROR', errMsg, 10); + } + + if (typeof flyNotify != 'undefined') { + Ext.msgBoxSlider.msgTopCenter(flyNotify.type, flyNotify.title, flyNotify.text, flyNotify.time); + } + } + } +}(); + +Login.initComponents = function() +{ + var userTxt = { + id : 'userTxt', + name : 'form[USR_USERNAME]', + fieldLabel: _('ID_USER'), + allowBlank: false + } + + var usernameTxt = { + id : 'usernameTxt', + name : 'username', + fieldLabel: _('ID_USER'), + allowBlank: false + } + + var emailTxt = { + id : 'emailTxt', + name : 'email', + fieldLabel: _('ID_EMAIL'), + allowBlank: false + } + + var passwordTxt = { + fieldLabel: _('ID_PASSWORD'), + name : 'form[USR_PASSWORD]', + inputType : 'password', + allowBlank: false, + + validationEvent : this.enableVirtualKeyboard == true ? 'blur' : 'keyup', + enableKeyEvents : true, + width: this.fieldsWidth, //this.enableVirtualKeyboard == true ? 183 : this.fieldsWidth, + keyboardConfig: { + showIcon: true, + languageSelection: true + }, + plugins: this.enableVirtualKeyboard == true ? new Ext.ux.plugins.VirtualKeyboard() : null, + + listeners: { + render: function() { + this.capsWarningTooltip = new Ext.ToolTip({ + target: this.id, + anchor: 'top', + width: 305, + html: '
'+_('ID_CAPS_LOCK_IS_ON')+'

' + + '
'+_('ID_CAPS_LOCK_ALERT1')+'

' + + '
'+_('ID_CAPS_LOCK_ALERT2')+'
' + }); + this.capsWarningTooltip.disable(); + this.capsWarningTooltip.on('enable', function() { + this.disable(); + }); + }, + + keypress: { + fn: function(field, e) { + if(this.forceVirtualKeyboard) { + field.plugins.expand(); + e.stopEvent(); + } + else { + var charCode = e.getCharCode(); + if((e.shiftKey && charCode >= 97 && charCode <= 122) || + (!e.shiftKey && charCode >= 65 && charCode <= 90)) { + + field.capsWarningTooltip.show(); + } + else { + if(field.capsWarningTooltip.hidden == false) { + field.capsWarningTooltip.hide(); + } + } + } + }, + scope: this + }, + blur: function(field) { + if(this.capsWarningTooltip.hidden == false) { + this.capsWarningTooltip.hide(); + } + } + } + + /*, + listeners : { + specialkey: function(f,e){ + if (e.getKey() == e.ENTER) { + Login.submit(); + } + } + }*/ + } + + var forgotPasswordLink = { + xtype: 'box', + autoEl: { + html: '' + } + }; + var forgotPasswordBox = { + xtype: 'box', + autoEl: 'div', + height: 4 + } + + var languagesCmb = new Ext.form.ComboBox({ + id : 'language', + fieldLabel : _('ID_LAN_LANGUAGE'), + name : 'form[USER_LANG]', + displayField: 'name', + typeAhead : true, + mode : 'local', + emptyText : _('ID_SELECT'), + allowBlank : false, + valueField : 'id', + editable : true, + selectOnFocus : true, + forceSelection: true, + triggerAction : 'all', + store : new Ext.data.ArrayStore({ + fields: ['id', 'name'], + data : languages + }), + listeners : { + afterrender : function(){ + var store = languagesCmb.getStore(); + var i = store.findExact('id', defaultLang, 0); + if (i > -1){ + Ext.getCmp('language').setValue(store.getAt(i).data.id); + Ext.getCmp('language').setRawValue(store.getAt(i).data.name); + } + } + } + }); + + + var formConfig = { + id : 'login-form', + labelWidth: 80, + labelAlign: 'right', + bodyStyle : "padding: 10px;", + url : "authentication", + closeAction: 'hide', + frame : true, + width : 230, + padding : 10, + defaultType : 'textfield', + monitorValid: true, + defaults : { + width : this.fieldsWidth + }, + buttons: [{ + id: 'submit-btn', + text: _('LOGIN'), + formBind: true, + handler: Login.submit + }] + }; + + formConfig.items = new Array(); + + if (this.enableForgotPassword) { + formConfig.items.push(forgotPasswordLink); + } + formConfig.items.push(userTxt); + formConfig.items.push(passwordTxt); + formConfig.items.push(languagesCmb); + + + this.form = new Ext.FormPanel(formConfig); + + this.forgotPasswordForm = new Ext.FormPanel({ + id : 'fp-form', + labelWidth: 80, + labelAlign: 'right', + bodyStyle : "padding: 10px;", + url : "forgotPassword", + closeAction: 'hide', + frame : true, + width : 230, + padding : 10, + defaultType : 'textfield', + monitorValid: true, + defaults : { + width : this.fieldsWidth + }, + items: [ + usernameTxt, + emailTxt + ], + buttons: [{ + id: 'send-btn', + text: _('ID_SEND'), + formBind: true, + handler: Login.sendFpRequest + }, { + id: 'cancel-btn', + text: _('ID_CANCEL'), + handler: Login.restore + }] + }); + + this.forgotPasswordForm = new Ext.FormPanel({ + id : 'fp-form', + labelWidth: 80, + labelAlign: 'right', + bodyStyle : "padding: 10px;", + url : "forgotPassword", + closeAction: 'hide', + frame : true, + width : 230, + padding : 10, + defaultType : 'textfield', + monitorValid: true, + defaults : { + width : this.fieldsWidth + }, + items: [ + usernameTxt, + emailTxt + ], + buttons: [{ + id: 'send-btn', + text: _('ID_SEND'), + formBind: true, + handler: Login.sendFpRequest + }, { + id: 'cancel-btn', + text: _('ID_CANCEL'), + handler: Login.restore + }] + }); + + this.window = new Ext.Window({ + layout: 'fit', + title: _('LOGIN'), + width: 380, + height: 194, //180, + iconCls: 'ux-auth-header-icon', + closable: false, + resizable: false, + plain: true, + draggable: false, + items: [this.form], + bbar: new Ext.ux.StatusBar({ + defaultText: '', + id: 'login-statusbar', + statusAlign: 'right', // the magic config + items: [] + }) + }); + + this.fpWindow = new Ext.Window({ + layout: 'fit', + title: _('ID_FORGOT_PASSWORD'), + width: 380, + height: 150, //180, + //iconCls: 'ux-auth-header-icon', + closable: false, + resizable: false, + plain: true, + draggable: false, + items: [this.forgotPasswordForm], + bbar: new Ext.ux.StatusBar({ + defaultText: '', + id: 'login-statusbar2', + statusAlign: 'right' + }) + }); + + //Ext.getCmp('login-form').hide(); +} + +Login.forgotPassword = function() +{ + this.window.hide(); + this.fpWindow.show(); +} +Login.restore = function() +{ + Login.window.show(); + Login.fpWindow.hide(); +} + + +Login.sendFpRequest = function() +{ + Ext.getCmp('login-statusbar2').showBusy(); + + if (!Login.forgotPasswordForm.getForm().isValid()) { + Ext.getCmp('login-statusbar2').setStatus({ + text: _('ID_VALIDATION_ERRORS'), + iconCls: 'x-status-error', + clear: true + }); + return; + } + + Login.forgotPasswordForm.getForm().submit({ + method: 'POST', + waitTitle: '', + waitMsg: 'Sending Request...', + success: function(form, action) + { + serverResponse = Ext.util.JSON.decode(action.response.responseText); + Ext.getCmp('login-statusbar2').setStatus({ + text: _('ID_SUCCESS'), + iconCls: 'x-status-valid', + clear: true // auto-clear after a set interval + }); + Ext.msgBoxSlider.msgTopCenter('info', _('ID_INFO'), serverResponse.message, 10); + setTimeout('Login.restore()', 4000); + }, + failure: function(form, action) + { + + if (action.failureType == 'server') { + serverResponse = Ext.util.JSON.decode(action.response.responseText); + Ext.getCmp('login-statusbar2').setStatus({ + text: serverResponse.message, + iconCls: 'x-status-error', + clear: true // auto-clear after a set interval + }); + Login.submiting = false; + //Ext.msgBoxSlider.msgTopCenter('alert', 'LOGIN ERROR', serverResponse.message, 10); + } + else { + Ext.Msg.alert('ERROR', _('ID_SERVER_PROBLEM') + ' ' + action.response.responseText); + } + //Login.form.getForm().reset(); + } + }); + +} + + +Login.submiting = false; +Login.submit = function() +{ + if (Login.submiting) { + return false; + } + + Login.submiting = true; + + document.forms[0].action = '../login/authentication'; + document.forms[0].submit(); + return; + + + Ext.getCmp('login-statusbar').showBusy(); + + if (!Login.form.getForm().isValid()) { + Ext.getCmp('login-statusbar').setStatus({ + text: _('ID_VALIDATION_ERRORS'), + iconCls: 'x-status-error', + clear: true + }); + return; + } + + Login.form.getForm().submit({ + method: 'POST', + //waitTitle: '', + //waitMsg: 'Verifying User...', + success: function(form, action) + { + serverResponse = Ext.util.JSON.decode(action.response.responseText); + Ext.getCmp('login-statusbar').setStatus({ + text: serverResponse.message, + iconCls: 'x-status-valid', + clear: true // auto-clear after a set interval + }); + + if (typeof urlRequested != 'undefined') { + window.location = urlRequested; + } + else { + window.location = serverResponse.url; + } + }, + failure: function(form, action) + { + + if (action.failureType == 'server') { + serverResponse = Ext.util.JSON.decode(action.response.responseText); + Ext.getCmp('login-statusbar').setStatus({ + text: serverResponse.message, + iconCls: 'x-status-error', + clear: true // auto-clear after a set interval + }); + Login.submiting = false; + //Ext.msgBoxSlider.msgTopCenter('alert', 'LOGIN ERROR', serverResponse.message, 10); + } + else { + Ext.Msg.alert('ERROR', _('ID_SERVER_PROBLEM') + ' ' + action.response.responseText); + } + //Login.form.getForm().reset(); + } + }); +} + +Ext.onReady(Login.init, Login, true); + diff --git a/workflow/engine/templates/main/sysLogin.html b/workflow/engine/templates/main/sysLogin.html new file mode 100644 index 000000000..d17590cb6 --- /dev/null +++ b/workflow/engine/templates/main/sysLogin.html @@ -0,0 +1,97 @@ + + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + +{login_script} +{login_vars} + + \ No newline at end of file diff --git a/workflow/engine/templates/main/sysLogin.js b/workflow/engine/templates/main/sysLogin.js new file mode 100644 index 000000000..82725c692 --- /dev/null +++ b/workflow/engine/templates/main/sysLogin.js @@ -0,0 +1,323 @@ +/* + * ProcessMaker Login + * Created on date Jul 15, 2011 + * + * @author Erik Amaru Ortiz + */ + +var loadMask = function(){ + return { + init: function() { + var loading = Ext.get('loading'); + var mask = Ext.get('loading-mask'); + mask.setOpacity(0.8); + mask.shift({ + xy: loading.getXY(), + width: loading.getWidth(), + height: loading.getHeight(), + remove: true, + duration: 1, + opacity: 0.3, + easing: 'bounceOut', + callback: function(){ + loading.fadeOut({ + duration: 0.2, + remove: true + }); + document.getElementById('loginLogo').style.display = 'block'; + } + }); + }}; +}(); + +Ext.onReady(loadMask.init, loadMask, true); + +var Login = function() { + return { + /** Properties */ + form : null, + window : null, + enableVirtualKeyboard : true, + fieldsWidth : 200, + + /** Init method */ + init : function() { + new Ext.KeyMap(document, { + key: [10, 13], + fn: function(keycode, e) { + Login.submit(); + } + }); + + Ext.QuickTips.init(); + Ext.form.Field.prototype.msgTarget = 'side'; + + this.initComponents(); + + Ext.getCmp('workspace').setValue(PMExt.cookie.read('x-pm-ws')); + + this.window.show(); + Ext.getCmp('userTxt').focus(true, 1000); + + if (typeof flyNotify != 'undefined') { + Ext.msgBoxSlider.msgTopCenter(flyNotify.type, flyNotify.title, flyNotify.text, flyNotify.time); + } + } + } +}(); + +Login.initComponents = function() +{ + var userTxt = { + id : 'userTxt', + name : 'form[USR_USERNAME]', + fieldLabel: _('ID_USER'), + allowBlank: false + } + + var passwordTxt = { + fieldLabel: _('ID_PASSWORD'), + name : 'form[USR_PASSWORD]', + inputType : 'password', + allowBlank: false, + + validationEvent : this.enableVirtualKeyboard == true ? 'blur' : 'keyup', + enableKeyEvents : true, + width: this.fieldsWidth, //this.enableVirtualKeyboard == true ? 183 : this.fieldsWidth, + keyboardConfig: { + showIcon: true, + languageSelection: true + }, + plugins: this.enableVirtualKeyboard == true ? new Ext.ux.plugins.VirtualKeyboard() : null, + + listeners: { + render: function() { + this.capsWarningTooltip = new Ext.ToolTip({ + target: this.id, + anchor: 'top', + width: 305, + html: '
'+_('ID_CAPS_LOCK_IS_ON')+'

' + + '
'+_('ID_CAPS_LOCK_ALERT1')+'

' + + '
'+_('ID_CAPS_LOCK_ALERT2')+'
' + }); + this.capsWarningTooltip.disable(); + this.capsWarningTooltip.on('enable', function() { + this.disable(); + }); + }, + + keypress: { + fn: function(field, e) { + if(this.forceVirtualKeyboard) { + field.plugins.expand(); + e.stopEvent(); + } + else { + var charCode = e.getCharCode(); + if((e.shiftKey && charCode >= 97 && charCode <= 122) || + (!e.shiftKey && charCode >= 65 && charCode <= 90)) { + + field.capsWarningTooltip.show(); + } + else { + if(field.capsWarningTooltip.hidden == false) { + field.capsWarningTooltip.hide(); + } + } + } + }, + scope: this + }, + blur: function(field) { + if(this.capsWarningTooltip.hidden == false) { + this.capsWarningTooltip.hide(); + } + } + } + } + + var workspaceField; + + if (wsPrivate) { + workspaceField = { + id: 'workspace', + name: 'form[USER_ENV]', + fieldLabel: _('ID_WORKSPACE'), + allowBlank: false + } + } else { + workspaceField = new Ext.form.ComboBox({ + id: 'workspace', + fieldLabel: _('ID_WORKSPACE'), + name : 'form[USER_ENV]', + store: new Ext.data.ArrayStore({ + fields: ['id', 'name'], + data : workspaces + }), + displayField:'name', + typeAhead : true, + mode : 'local', + forceSelection: true, + triggerAction: 'all', + emptyText : _('ID_SELECT_WORKSPACE'), + allowBlank : false, + selectOnFocus: true, + valueField : 'id', + editable : true, + listeners: { + afterrender: function(){ + var store = workspaceField.getStore(); + var i = store.findExact('id', defaultWS, 0); + if (i > -1){ + Ext.getCmp('workspace').setValue(store.getAt(i).data.id); + Ext.getCmp('workspace').setRawValue(store.getAt(i).data.name); + } + } + } + }); + } + + var languagesCmb = new Ext.form.ComboBox({ + id : 'language', + fieldLabel : _('ID_LAN_LANGUAGE'), + name : 'form[USER_LANG]', + displayField: 'name', + typeAhead : true, + mode : 'local', + emptyText : _('ID_SELECT'), + allowBlank : false, + valueField : 'id', + editable : true, + selectOnFocus : true, + forceSelection: true, + triggerAction : 'all', + store : new Ext.data.ArrayStore({ + fields: ['id', 'name'], + data : languages + }), + listeners : { + afterrender : function(){ + var store = languagesCmb.getStore(); + var i = store.findExact('id', defaultLang, 0); + if (i > -1){ + Ext.getCmp('language').setValue(store.getAt(i).data.id); + Ext.getCmp('language').setRawValue(store.getAt(i).data.name); + } + } + } + }); + + this.form = new Ext.FormPanel({ + id : 'login-form', + name : 'login_form', + labelWidth: 80, + labelAlign: 'right', + url : "../main/sysLoginVerify", + frame : true, + width : 230, + padding : 10, + defaultType : 'textfield', + monitorValid: true, + defaults : { + width:200 + }, + items: [ + userTxt, + passwordTxt, + workspaceField, + languagesCmb + ], + buttons: [{ + text: _('LOGIN'), + formBind: true, + handler: Login.submit + }] + }); + + this.window = new Ext.Window({ + layout: 'fit', + width: 380, + height: 210, + title : _('LOGIN'), + iconCls: 'ux-auth-header-icon', + closable: false, + resizable: false, + plain: true, + draggable: false, + items: [this.form], + bbar: new Ext.ux.StatusBar({ + defaultText: '', + id: 'login-statusbar', + statusAlign: 'right', // the magic config + items: [] + }) + }); + +} + +Login.submiting = false; + +Login.submit = function() +{ + if (Login.submiting) { + return false; + } + + Ext.getCmp('login-statusbar').showBusy(); + + if (!Login.form.getForm().isValid()) { + Ext.getCmp('login-statusbar').setStatus({ + text: _('ID_VALIDATION_ERRORS'), + iconCls: 'x-status-error', + clear: true + }); + return; + } + + Login.submiting = true; + + document.forms[0].action = '../login/sysLoginVerify'; + document.forms[0].submit(); + return; + + + Login.form.getForm().submit({ + method: 'POST', + //waitTitle: '', + //waitMsg: 'Verifying User...', + success: function(form, action) + { + // persistene on cookie + PMExt.cookie.create('x-pm-ws', Ext.getCmp('workspace').getValue(), 30); + + + serverResponse = Ext.util.JSON.decode(action.response.responseText); + Ext.getCmp('login-statusbar').setStatus({ + text: serverResponse.message, + iconCls: 'x-status-valid', + clear: true // auto-clear after a set interval + }); + window.location = serverResponse.url; + }, + failure: function(form, action) + { + Login.submiting = false; + if (action.failureType == 'server') { + serverResponse = Ext.util.JSON.decode(action.response.responseText); + Ext.getCmp('login-statusbar').setStatus({ + text: serverResponse.message, + iconCls: 'x-status-error', + clear: true // auto-clear after a set interval + }); + //Ext.msgBoxSlider.msgTopCenter('alert', 'LOGIN ERROR', serverResponse.message, 10); + } + else { + Ext.Msg.alert('ERROR', _('ID_SERVER_PROBLEM') + ' ' + action.response.responseText); + } + //Login.form.getForm().reset(); + } + }); +} + +Ext.onReady(Login.init, Login, true); + diff --git a/workflow/engine/templates/main/systemInfo.js b/workflow/engine/templates/main/systemInfo.js new file mode 100644 index 000000000..e13e8671e --- /dev/null +++ b/workflow/engine/templates/main/systemInfo.js @@ -0,0 +1,36 @@ +Ext.onReady(function(){ + + var store = new Ext.data.ArrayStore({ + fields: ['name', 'value'], + idIndex: 0 + }); + var propsGrid = new Ext.grid.GridPanel({ + store : store, + columns : [{ + id : 'name', + header : '', + width : 150, + sortable : false, + dataIndex : 'name', + renderer: function(v){return ''+v+''}, + align: 'right' + }, + { + header : '', + width : 350, + sortable : false, + dataIndex : 'value' + }], + stripeRows : true, + autoHeight : true, + width : 480, + columnLines: true, + enableColumnHide: false, + enableColumnResize: false, + enableHdMenu: false + }); + + store.loadData(properties); + propsGrid.render(document.body); + +}); \ No newline at end of file diff --git a/workflow/engine/templates/setup/loginSettings.js b/workflow/engine/templates/setup/loginSettings.js index 3a627c120..403c10d91 100755 --- a/workflow/engine/templates/setup/loginSettings.js +++ b/workflow/engine/templates/setup/loginSettings.js @@ -1,7 +1,7 @@ Ext.onReady(function(){ var cmbLanguages = new Ext.form.ComboBox({ - fieldLabel : TRANSLATIONS.ID_CACHE_LANGUAGE, // 'Language' + fieldLabel : _('ID_DEFAULT_LANGUAGE'), hiddenName : 'lang', store : new Ext.data.Store( { proxy : new Ext.data.HttpProxy( { @@ -21,7 +21,9 @@ Ext.onReady(function(){ editable : false, allowBlank : false, listeners:{ - select: function(){ChangeSettings('1');} + select: function(){ + changeSettings(); + } } }); @@ -33,31 +35,42 @@ Ext.onReady(function(){ handler : saveSettings }); - loginFields = new Ext.form.FieldSet({ + loginFields = new Ext.form.FieldSet({ title: _('ID_LOGIN_SETTINGS'), items : [ - cmbLanguages, - { + cmbLanguages, + { + name: 'forgotPasswd', xtype: 'checkbox', - checked: currentOption, - name: 'acceptRP', + checked: forgotPasswd, fieldLabel: _('ID_ENABLE_FOTGOT_PASSWORD'), - id: 'ch_ii', listeners:{ - check:function(){ChangeSettings('2');} + check:function(){ + changeSettings(); + } + } + }, + { + name: 'virtualKeyboad', + xtype: 'checkbox', + checked: virtualKeyboad, + fieldLabel: _('ID_ENABLE_VIRTUAL_KEYBOARD'), + listeners:{ + check:function(){ + changeSettings(); + } } - } ], - buttons : [saveButton] + buttons : [saveButton] }); - var frm = new Ext.FormPanel( { + var frm = new Ext.FormPanel({ title: ' ', id:'frm', labelWidth: 150, - width:400, + width:460, labelAlign:'right', autoScroll: true, bodyStyle:'padding:2px', @@ -74,9 +87,11 @@ Ext.onReady(function(){ }); //render to process-panel frm.render(document.body); -}); -function saveSettings() { +}); //end onready() + +function saveSettings() +{ Ext.getCmp('frm').getForm().submit( { url : 'loginSettingsAjax?request=saveSettings', waitMsg : _('ID_SAVING_PROCESS'), @@ -84,10 +99,7 @@ function saveSettings() { success : function(obj, resp) { //nothing to do response = Ext.decode(resp.response.responseText); - if (response.enable) - parent.PMExt.notify(_('ID_LOGIN_SETTINGS'),_('ID_ENABLE_FORGOT_PASSWORD')); - else - parent.PMExt.notify(_('ID_LOGIN_SETTINGS'),_('ID_DISABLE_FORGOT_PASSWORD')); + parent.PMExt.notify(_('ID_INFO'),_('ID_SAVED_SUCCESSFULLY')); saveButton.disable(); }, failure: function(obj, resp) { @@ -96,6 +108,7 @@ function saveSettings() { }); } -ChangeSettings = function(iType){ +changeSettings = function() +{ saveButton.enable(); } diff --git a/workflow/engine/templates/setup/main.js b/workflow/engine/templates/setup/main.js index 90b4c2d55..4cb7c6635 100755 --- a/workflow/engine/templates/setup/main.js +++ b/workflow/engine/templates/setup/main.js @@ -118,8 +118,13 @@ var main = function(){ items: items }), { - region: 'center', // a center region is ALWAYS required for border - contentEl: 'setup-frame' + region: 'center', + xtype : 'iframepanel', + frameConfig:{ + name : 'setup-frame', + id : 'setup-frame' + }, + deferredRender: false } ] }); diff --git a/workflow/public_html/images/canvastile_bg1.jpg b/workflow/public_html/images/canvastile_bg1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..290685e78d66ea1d4f6bd1c6ecc704cf2a88e8aa GIT binary patch literal 517 zcmex=jH%)-h70<3JTV8F)4&d$Nk z&c?~f#l^|Vg9<=~FfubUv$C*qvaxaUa&vO?^78=!KR;L@l;VN#Q1!xj{|_(-axl$c zU}R<#WMC3xWEN!ne}q925v*PC|1Aa{W}s<;%z_N|3_t6tuFD>%4q`iSH>Yvy1>sL0 zc-J&sWl(kC4Dbk$EMZ;2+ZMM+v1O%>sRPd@CjB@8S%X`mDM#lTa4}RhNT#s+C>WJ6 zFINcI?_lbX6R<;SVcmiFK3k?KH`UsiS`J8BF*brld_~|!$brK;tzrvU8K*f!HJtEjxU`I6YJ;waG@~{rN5EbGC9TX2|8D{S D%%gDG literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/canvastile_bg2.jpg b/workflow/public_html/images/canvastile_bg2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0ab19e7e2391da1c7fb7413056c4e40188583292 GIT binary patch literal 603 zcmY+8PiPZC6vp41*_mXr8`G_@#(vqL*#a ztp^WPvGlmq97050qy-Toixx!bAqs+jK&oDXM`=-{uuh^@^LsFF9^d;up7+*Uhn`I8 zL<$fQ(z3w&0AoP5q4Gx+`BH@`Z5U@AIQRMdJ|7n%5D-G^rX7NmQl>D$xd^I44Tg3| zLZQy$pQinuy1mDOc4d(dPa(Wwr{;oH{c{bOjl#pnpTyvi`&5__YQ_F!0O zD*1%jwWT#b8@KzTgEjT;(fZBZhl`EA;(_GMl$oA5zh~}>HTTpxbSeF)e9j`TOgA6a z2v}L?g+aE~gMPJ+<&scoxe_TjMos;0CZ?1-uPc*wAMZ#%KAQG`F%&Ui-Z5`{ra5Su zg=uDyv4C}TrutxmTPM}ii@gmzdyOGnoM3OjarhF(-Ha2dWtR^QB$j!de02-9F-)Gh zE4PwOciv6okuN_TqapXs+{0LFms)LQ9?s^?l~mSo7M_=97aCeI5$I14b2Q$-ikq<) jTREt2D4W*<2#@U%HePMsF6EL>reDT~OQS>KkoWEvI=z$# literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/close.png b/workflow/public_html/images/close.png new file mode 100644 index 0000000000000000000000000000000000000000..5a1226f2b940f6e7747559d3625d9af9b3a94b79 GIT binary patch literal 832 zcmV-G1Hb%Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf6 z6FCo@ng4kJ00O{CL_t(I%cYb(ODkIxhM&D>GRcrHVnBieen7w?rJV$VMMOkmWg|BJ zExUB7#v))NM8P73kjgGfEQ|v}WQG`%VZKh0J9EuFT+W5NV4BUIXT58^Yi;rJ@)Ges zBZ?wC&qIx(B}-LR#^W)L;~=H{BOnYzip3&^Vc>b*k`aSOqY;K-P^;Alg5ZxZMN#

GgUzjsrjt1jG!f zD9VcT$z;NCIHce2)9rR~9EUIraU6$kw@bg@XE+=(nM{&@gb*l-vNRwL&+{-0gNus` z^7%ZbX`*Qw&1RF^+gnW2$w3eV%;$4*xf~}aCuo{RyWK`r zRod+~XJ=;|9v+g*<@o;oP7L^~U#M}JLI~#bc`|2028UER)HwSS)yW zc;M#dhU4R7>h(IV>vDa4jqAEJ8Vxd;%nIzco~2SLEX(5V?vBUDNA~ykX*Qc=vss$W zCX2;_`}=!lvl#~m2W)O`CMF0W{@%@>Kwa0VR;#pHEeeGKzVA~k7HPFwe0_bP>pH1a z>bH$h<0+4ORW6rlG#boiGm6DxlKCevo6S-xm6G1ZW~c!9d>-4j+1S`X*L9?ntgo-* z`#$M(dL{eL&JIBkkWQzOQexXSg+hTSik7h5-CdrZo(RJb!!VK^{pWW4d@-byyuH2g z`ua+(R>O5&#CSZ8;$A&JKQoz3{uS~wf*_z=E>o>m@jQ<(4EY6Rn?aIQ#k$u30000< KMNUMnLSTaYtAPjr literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/dashboard.png b/workflow/public_html/images/dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..1a35ff7a7eb6424f94ab0651d93c2a4be1f44d97 GIT binary patch literal 940 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 Awg3PC literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/header_bg.jpg b/workflow/public_html/images/header_bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ec7eee1f2aac029dcf45b53666ec3cdeb8788184 GIT binary patch literal 361 zcmex=jH%)-h70<3JTV8F)4&d$Nk z&c?~f#l^|Vg9<=~FfubUv$C*qvaxaUa&vO?^78=!KR;L@l;VN#Q1!xj{|_(-axkPY zFfua=GB61;G7B>PKf)jeb}9oBU}NP3ipvWyFflT-G9pPZiZBTW3W^3LEi42okOX>> zk%1XWo(X6)D;vm6L68&*5LEnsi-Cuk5oj^9AcH-_8Si@ksF&B27i%eZY~XPfn8)mM xNcut3jdPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipe@ z6D1@pLd0+Y00vJ0#6;Mm6lsJkMq*g7F8|AGMn;Hv5u(6$m z81LHawf8+U_i_4TZ7_8b(JLKk?$y2LoAaITelx)Q=3{R$tq8leDxD`NZ7xzYDqPsjg%^_G15(T61!^ z$Th{(b!!M)TN`m|yn>^Hb9^@M(>=F@cuz-+wyX*(8}hevrP{BO$;1QzipBK8kbG9-i;db9T-nI2K;cR_V-Sm7m8ogayC` zh!Do|RXXt6Rn8Qp^=L=p!B{ksEEEb){h{xzH{-F`5QZXz>rsUzNHzxUK|C&U$q`wx8h7a%Ay7~U!o%|=oV=>I+^Vh*f zM@MnszyScjpI&-3(A>PD3$|!hT7#8hCZ+O?E8Bf>AZh; zG#YyLk-7O^FWrBk_1b9 zoQ{?V%vVf*IXi8NRbM=}W6d46Cqus(eCEeaZ`WPNiHNUl9v>Tb;_;@v;c&x~@1`q} zeJ96wV4?y9i9QAB8ujye7z{va+}j?*FCS>5JC-|zQl<8T(eV$D-`&}vDVO{&H&+&wPC?-PzLM7K_Clp-`w#C?%bs6L>6B z^6X##W6Yetm=_QLlx8SRP#QN8i}x`srniQ&^ozfZnoLoK9LMR&WHJqQCX*pM5KvYy zigmX(Qm!UBTlFzf^oh+)*0rcUL7-I3;be0R?ad8D1bCjubAE; z-b#0>|SsNK18F@T6Gc#;| zYxiyj(0Ag*iHEv6H+*AdEU`P@U^kLd3}py{>hElrf}R5C20@VT*npt=nqd$GCmJkT z84-ozZ2s842L_LKcklWS0A6qqE>UDqDfN1}>OH8HY=o8s%>WZ%46RLu`GGIC5)myB zOU%Hl{U7nAnJPgDKr=vslv1d;epxDWs=IsFnI(3WTE5J-0|AS{#M;a^+W-~;6^U=} zT90pTT4j_k%i8FgWmzFR5LnjI6r}w}2Y^Tb&1~bFJ zEOmXx^$pj3&;XXGSIGtp7Q(WY0f1%OjSMC>^X9AxP+T`_zLtv@VcqzN0oCa=N&!X# z#xRHhqe&}0UwEAsHU`Xq)*3JhFc#P4&!m+C0|XKzNGt`jgn$7<2(VEG{=^)^Q&bm- z&kZ&ah)F5M%#5=`LxR8v2>>7E8Q~y78C1hT7R)4w;YtbDbC-f0J$6L@aPRkWD_g?V zZ5^v%Y970;1QEdiyz%yC(n{lliysVc*zg6s)Z2R@nM_jGXPfbz-JMKaXQeUX@Rs#0 z#Ksh|xm;$)_WSe_uoqu=fw$kcZP4}nW22CXncJ9A^?mG z4_|C*YWn%=)vFIZ_Hai-y$b|}p-QQgzjP`6`_Zwn3!nPKbNKLcq15Wsx@0nWr)Ak; z0Hn39Tu!G?7mLN=tC@^EdgPCI=#hu9CY1`rWAWQsTiZ605RC+|RH`^Nl^r;D?%YVv l(@*;VaPZJUJn@q!@Lv;N($m(!Naz3n002ovPDHLkV1hreIEw%P literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/icon-logout.png b/workflow/public_html/images/icon-logout.png new file mode 100644 index 0000000000000000000000000000000000000000..5dbec9f55daebe05ed9d14ec5b833180bfefd598 GIT binary patch literal 903 zcmV;219<$2P)Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RX0~!|)2#j2mNdN!?8A(JzR5;6x zRLf55Kn!(eCQUl2ARwhs5E6wQj~y)Nl3(D9_yxWK7Hr`QSg=4Us{#V4LV(Ik2ncQS z7|&gp>pQgfO4o}h@i=FE>|=YaR!azhh@R)cPqWsJI6cp^){gtFwREb+kHatw!w~+@ zK0ZFmvJ^sm!%<#@XpGTXudc2xEG*FRzfdTNAP5c*4+n#R*4kQ&h-xIRTCFZFE-o!C z<#~>XLWprt%0NmvIXQWBboBQ2#-&h*NNCUPKq)RLj1^%yu_M=#(Tm6=otA0dHDeRAqA1)LdSMcrU|E)m%MSoj z%6`9pdV1RFbZ&2Nvn)fzjg5`V%gbJ`N8CwbOvPGDtfiCypt!&UIv5PDuC5T#_kC)2 z9LL+++wFF{+wBgAL$_F56CMmjz>P-3ofV$2EX%?$Op;`Cb2Eyf>+9?1=VvLU`E5K= z^r2$i?E^ENe6abX9R!W&9$-m%e_y8b^qP4ZP zFbqFGKe?e4L#?ycDgemyyxDBB`dkx2^m@IouP*@5T0cEKot&JcX-Z|~@)=`Dj7kWR z=lSmLE)S!-W70I;+1aVr>ssr_$H%?By*Q3ZH!Li75ELBGZArBXRKI55VH&o7g8mHT6630i9%$8n`n;hCk&M}S&)b90oPi_TALZM9l$G#WEAGqqZcmMHnX zm6a8~jo`G_zVG+@{VdDe?dWD21VI!k44ofy`glX(f`a29w(7Bet# z3xhBt!>lSzd4XdguXKMB{K6u>|Dl1!ihV!&U z1C#k?`HJ^@4@A1xq*u9YFlAVi+?JX)m8qkngTX=RdHdYSn|28cDoZiG+;H>Y+Ev#} z&vs=Ct-pS{`u*!&?|$q3fBX68mTNvPQQ6Nc&u#o{6P?ZAru@BD{&=8a)=8h*{r5w+ z=yb2zcFn?_!H-eo*WbFC;mHQC((+us+~YoTW{zRkLSezJ>W}#>S`0Ne8x~Y;Rtef5 zDp+V^rhc_iV!P$~h3AevOWnc0WRdjMv}2~{kN*n=xtY*{?-ud*>)l5;++j^Ir1y>}z)!Opd(sTeM2*qrTPoy7i1F Z<)_}7VB)5;@&M2g44$rjF6*2UngBoMPU-*v literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/icon-user.png b/workflow/public_html/images/icon-user.png new file mode 100644 index 0000000000000000000000000000000000000000..8af87d088936f9c072888d4ebaeabb2c29f2efd7 GIT binary patch literal 941 zcmV;e15*5nP)Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RX0~!|_5eviRLI3~*KS@MER7l6Q zmd#2dSro_rw^B(eO$-x7F?8vokWoikA`FoUs3WKk;8qNRFA+p=A#Q>TeFB?afJ->U-Js(Ac5cpBTIfo=k z0061cP51ZrTDe>Xr4*D>FvjZcu^|9J8u94t?94MhKK{9@s|&$k5CD+PW|7Hc5Q#*v zxVWfAqtU+zAvJB(1(ixA%+Jq@QmKU5+1Y1nx~?OUNT9#J|L@h+Rkgjno!9iPUbt3P zR>aB431((yz!-a`-QC@Y$K%j-J(SDk-a3Y{PK^J&yu8HJ)KnG3R!!3o3WcES`UfXl zveTlXC}?VGg4gSHy!^JdHe6p{|NcE5fl`WxhliRTF~&eCg`y~qcP$GcP%B$dBUR0{$C!C;V0Pf!19 z7)GT~ztvnWhfpYlv9YnAo$9D=XXx$i{Ue=DqfjXPFDR8tp`)V%EiEkO7K`AV&9Qp#>NIxsTAC9H-?6W5Rb#+}+)6 z86F-Ejf{-A0)fDb2cBUV*xK5{(a{kmCMJ+fCJC{NF&qwySS$vg&-ZE*)-+96mW73d z1tJ50EX$B22_BE9p~=~(c)eb@-EI&#!e>o*q-raJTsolIlB+z z@g>0lB&5D{Nk{uUthN4s{cG*L&w>B3i7)1Ef{lNID{rY3l{m0kEC?Z)%cPi{O%XYl zMsY}t^IswX`<-y#ZfI^T#9g?x!0p=Mb-QjXDsZgQG}R#ugH$R#8&9U9vx&K(>Di<| zIXl-UrxQJlMuNBs)A&CGy1yU+=eMAKE5s8nv$y!}b+wg0Y}{14wQg;dqrzK^r@%t7 zO~I)NOz32@Sz@zE!qFJP@Kok}|L9nA_od_hz}Vl6iJ2p4<|G}@eog`nKZWy0VEeak zzM-b#@m)JM?`pbblU7+;OtDqqwMmMs0?o8?1*wcqcW{i?k9NjiKicu;uz&Pd*t7u2 zG_*dow5eCw_Fur|amCcQZO>O5H~#TQdz!ZGyt&@wE4EQvD5!P`3M>LuSq>h!G{py} zhbbwt;C1vE9(XzQvgqxxLnf&!B0Mgz|P0urklos<>?eMS#3Y!dN`bqtBk!*F==!5t0jzEXim z&1P9sYNyC?n8_N~0_WFc zBExT9KFi1FMk%ZCq7@dhuGXDX$XJ|_e5?c%15ivTim>L?mxh9#BDDX<*eu#=ht-T> z7|asf+#^U`J|WU^4V(Z>sqHL+oR88}>&7Mbz5?PTv<_A!IdOwknOzB(< zPB9TSQRH)?YUbsGD;Kw-qQF2X!l~BoPA;{!%gp%vNhl4b&c97|B6Kbg8k?R=XNVZJm=rwT0&I5c!twjg(N_8N z?_M2k{@4CPbG;utPjc)XAhH6^iUe})&{Kml>TeGZbdEN+bt%W%E^6m{hs~Y-kkUUq zrcBPnF`G=-Y&HyX(jPf_A#?hzKR?4r^9vZ6Rv>X5jO!8r(19^Bu?{2ZKNs`2b_}(j z>F;Ve5okGeEOe%8SR0FFT$N=dqNu0<1PorDFj@k!mz2%-ost8GjnBycnFR8d0!BdZ zAv@bba^eKB(D8KmVs{`EvG$H6D%@_jR#om{dNyG?-7(neaym~XVv{g)`AZ?NRFOF# z28^Sp`()JLJ`uVUYa5JIOs7QAc zJxW2TMV}5mPRaTXI{yB-g1;=Y09Etr;Ji}*x6icy0_X2g%#`4NK>z>%E_zg0bYpU5 zVRU6JWMOn=004N}Gcqu+&^55sHL?gXG_x|Xv@$i+Hn6ZVFaQ8T;sqxn1gEJ0001s} zR9JLvZ)9m^c`amNbY%blc-k{EFtE@yu+TL!2r)FXGP1NXHPAM&ure?J075ba8}9!} Q`~Uy|07*qoM6N<$f?M-G_W%F@ literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/login-loader.gif b/workflow/public_html/images/login-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..f9bc34d6643eed5193a2f7dfa5a3279352e554ce GIT binary patch literal 723 zcmZ?wbhEHb6kyA{`ThGZko)${_j6}o-@5f-=Z?o8 zKmL65==14QuWsIWzhm3u5AT2c`SbVTgHJ1$-(9!%{*pzvU%vddcJ;lbi*N7V`Q-4S z=UX;CT(;y60}Lqs=k{|A33hf2a5d61U}gl0EB=#mE=o--Nlj5G&n(GMNX$yC$jMJk z(aX$B%V$vh$->DERHXwl0pv&qR$T>!zLd;)OBQfFI>ofkL7>^U+0$%;#f& zBG|s=k;4fdz2bG(xLnsxbHZkVN|y!qofNBuT~d;wzhe;q#@H&#hQ^bOelv#{qf!{#vSL%xvdo*UiPJM4FWQznE0#~;&EKZ& ziGmN9^``r(^v^J1iq~Z@GdsOt--93yY);aLnDqqco)w$kttygH4cfx{z;ylZ?J@<^ hxxaNCnN`>-^D9o5!Gz5@a{Usl0YtJH)}SN;1_0%$7?uD4 literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/logo_processmaker2.gif b/workflow/public_html/images/logo_processmaker2.gif new file mode 100644 index 0000000000000000000000000000000000000000..93bc6ab078e39d4b1730c46e7a9b00373a5ca59e GIT binary patch literal 1519 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 D#mQy2 literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/logout.gif b/workflow/public_html/images/logout.gif new file mode 100644 index 0000000000000000000000000000000000000000..59588a45a49b0f71f4ee7a13a072bc4e0c97fbb3 GIT binary patch literal 389 zcmZ?wbhEHb6krfwSSru(=g;HktHPG0ah~hodVg}l%_-_{UtPR6%jDU!v(MKHyxdj( zaH-ex4I2O7-2QTFRS7Xye4XcCz84>yR!@3qF?YwR*mHbJyu`awG zjO>{#wK>ksJ;GB(McJ8HCpL=;atre`vqZAGu!!<=^UAY@adHaG5^P?@Eh^8mYr`gH z_5|*dHM{pPF|%f^VdLG!vti@06A66mQ-#k9>=p95<8|{sGm{+Clc&#~$vHAu0{}eT B-n{?- literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/processmaker.logo.png b/workflow/public_html/images/processmaker.logo.png new file mode 100644 index 0000000000000000000000000000000000000000..808819cdc36beadd4a2d77e4691cd86e5fc98f4a GIT binary patch literal 9029 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 literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/processmaker2.logo.png b/workflow/public_html/images/processmaker2.logo.png new file mode 100644 index 0000000000000000000000000000000000000000..22102fdfb02e33ce17a405542f4265be3573d99f GIT binary patch literal 7997 zcmV-DAHv{?P)-Q00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01m_e01m_fl`9S#00007bV*G`2iyZ1 z79=+J)es^803N+bL_t(|+U!Y!k*d5x<_M? z=adpj9+fq+ktldT0VOE`P@ES_7tJ8~UP;FdzrmA}logx)-XghI(q@v60eH;gHGf$) z-3v@f8ZYB=n8Ltl2uY^Hh)z%xMQZo|s&P|FwP2KK0m%C|!cYT{_f75gFQrrq*A@yF zmyJCD0Z2#72!x1@757!UHO|+JMJA4>BZ*_TyN{%TWRf%oz?G6t0?s z4)q|gdf!J;Ud_0kw!BqsrwZY6!nU;Q4K z7LY|GX9G9}z%)sxkz52|5r9uy^9K&q16Ae$LpRoVS;CYOe^Fj~yrYg$o}-X-Ey)i7 zs9OQ5);BBQ0Lcdd{29r8*{g=v1WA_x_$YwxY}Bj9^J@pmZv*&-3#ss(suoy;EyEz1<5Z+`XR~n^){$%%8LY`uPSVo)aON82jC?Dp9V0Eq}c$@0`UC+{*17~7LE)=fMm)KmLDYl-|vZe zUXqlGqG*LMu^oVr$0`ga3x8_eaiBe z_gi8Astl#V-14|A-7gxtVW2F^p6_3T+#kk|&?dTR#r?C#ErQH4Jj*aBC@F%YDgWR1 zuX~?g2Cx*s9mXg&0ay*-cO9MdNSZ?OMF6Tt?@|IM5jUbr=p8OJ0qi09kfifS_DWhv za=N4eFJzL`3_{RNvNvp2Go{pD6h+Hp)&TT+tX=~y**Xo7+;2nD>VybNs{1~@0DAp< z0x&44-(bREJ7r2K(TX$yKo5X!dpJ2C>jTj5F*+f@6MX+3NnPIMrY&oKN@;&l6wNl* z*M0B&99{N!4c~vjYu9L(zlORhaWD#&l_V?e5|R^AN(26XUD9L#D@o3Z<5q?20q0;S zX$`N1pT8GCkHh*o0Om~#+{FRf)6$e@|!Im=KyWY{EPw9?aJm7@ zy(BkD+D) z;{nWc{=LS+JH=qxc>orX+zjCV0NCI~nC#764&YLfr#e5ELyA?B?j-qyr0oFqksL-# zZ5g`&Oad?uzzP7DkX$CI*PFJLz;{XBnH9?S{v?360+?x|`%YN$&^nfakvuzy{+}0|35c2iL5RV%;=aEz{Pn2fzY* zjQs%iN;;n8XRJ)CCH*PM^^)E}@@fDJ>}ekbaF?WwB&PtlMAG|7p6@(u&_;mvkQW-3 zzg^NdNZxB@l*9R&q?sfyl=ME5Z?sYj8lkUQsV+C<|1|(N+uAlWlIX?0isbtwooNd? z;I9dimXo}a!JXyy9>+za4;0Jtlqw9`3d zk3*~vkbE0}Irip#9%mwe3nX3d9QUgLek|!#hmIpc-WVSHZv*xo(NP!9zmAvmqJ^&u zz$|<0VX=sugK!VFanwCFXrHpOAYd~2P4y6l7QXI803Qc%J;^Uh`m)z=I7KVx><2Om z769f-dXtTL3S%b&X{$)?^3T^9Azqq8`w*_zJzg{aZYT97NpA*lKgrJl_^CnP;~i;C z<$=0xp3_M-m?v6Bnjyr_Si`vhzD)960B!{EBO8Y|+1YIX@QtD@{ z*)QoXlJ^7H>d^3VEA$xv{(xkkq}u@Oa(F%wz;BcMO)K700PdFbjMrwKODkvC;LIl3 zE$LQCeUc`S{20kMJJoo`oBEs;;Y0wJ_!%#7=v9|=lcasYD>GIhvcgE(L2`cZ7+E>G z032`GNdEmqr%u^ZXOB4O_Z0^fun#DG-Ou`O0lXzd0(JktWy7+|7tQrJMaX~W0C)$< z)slXY|5u7W7#)l~>RH|&l=5ae+`SvXS^(#eydJ>GIm+<7lb>T9$#s^yxp}ON_eeU& zaff1 zHInvuF{VlSG07VMyv235>qu^tw8ry0!=CI+Kj@7BzU*+i&(Cxl$@fTlH^~zKybZuw z*I7yO0!hC`@+8mgZvlK0z>Ch`dP&|!^7jB-?$GDMBww&`s*yZT(zyU;N_qsq=Se<5 za=X`LBFXOoco%@TJCd15vdfXq3VYt^Bp;Ua=Qe&jy!Mkxe%JNZs~ky8wvrT)T3Zor zCz3my3r~y4ti8nw0N-;o(c1Wk6uXn8Oi@B zX$gQ2`?(dAuW9zv1tf0?p{pv=2n1z28^Gz7!y-fz1%T&B{#4R-lJB&mNHNE?$6n*| z&%FTN@BNxt%FSDn76Vx7(oq$Lkd;&}H~piei%DK=XU9@>Q6o7605jN|F^G2b(pZ{` zmMfPcmRia7hkMnX3!FjnQUGso7zL8gO8RS(t8L}7<(lEVYnzqp&m`@%#{}_;CWmB7J!CynNoxg5aj?Flwm_)fDC8zlJ?=la`8 z?zbQg+PM4?fEP(VE$LB`{SN702VjCzj9URbEoqlQmkhw|1MpdU>-zvaO>!^EMoOu- z1F540V3+4NEqgXQ+%7-+WZ!#&(~??_&N4OBp1I@{Sq9&_ER@IO2q3&iP91g{Q)$@f zUQoeO>vnomHVE;)*I0L(F<-Q|09ti04K$_WwKd>_ec9fl_74pTC!>xFF^ z;9BLp?{;rsqcn-8q(=e#wWKW;+Lo=&e1{Yh0Bi?vk8{zz7)d5?NooOj&=z{JL&DR& zX^SMSFk5Daq=zivkxrOA#(L-6&q{jAphK6v>}iswlH5-6M=sXykEw=@%p(RSme_-? zl=P4{ezM2Q-l_0rwStvyI7k1oQS*j{p(ku&A!%|}plqeB%-JCPrSCD=p-dIaaxJGE zEr;3>xT*nIVs_uFl8$kd6G5+*pK+Z-;C93j1S)X;IhF%c%#&^OXO@*cZEZ zV*@*Ef5O~00=OH%3!Tb>!Duy%M7JzIF|uH(2Hp-d&Z$YiWwQ^!OU7{y=^;l|<`O_c z|F0)lHrPY%w^!QfGQt`F4>+fMF0*h-U`x~9=ev@gizE~qgqZ-Ac|%`u$UE49T(C>h zGXOSP`4$*dI4OHnNozfygDjfMUxNnT?zE8A9bsfNw}TZRFlifr>|^ZUWc>;Acj1YoTN{==Tpg zRd}1EH|8~oUaHg-(uq(4=jihdgvL3g47!qh%IkX&B3|MzWVs*A9==ouFQ*OVlI}1< zU#|0)>d~n3_=l^^3a-6nAnOMJe&Tv2?a}%z4E+xKc97g<1#OfU-Q(@H2fM4}C1DRg z(VXz674*eX=G`Q@LDG6jCy<fmZVcien!%z zPE#IrLrcG;MjIMw6s8mjFIJX5l4}9HO7fVnXEjGiX99T0b4H2Wv76)*0Ct8PyeQ?H z2=(|UNPafg>;2woPKPdYnesA+<3+`DNNqH^WVA8~vT(aFJ3e&N3Vgq$e|CPAtw{>`1S*IcEawSE zPVaTTpfWScpi9l6%-&GGsVtWn(AZ6Kmkm%a$w@|lC;Hxd9m=o+pE|O9XvLjkw7u!t z?s6+vE9W$xLuTqsus6z#-HNYX2gbOvnpw z+%{d(8w0^cF*y$&IYo9?MJ>K;c*=^bpfjClvSU-`ik{mR$pKq`m04EpPHZTli-l{? z+j1y5N{%w3B-YLnXQL8xmyj<-njpv0s8BLW`8jKuffnkNk*1fyn7#Jg+x@Ifd);S^ zykF-s(qiYLuamTrKOGfVHah`NZ=Uo8Kcgb$LbJ*oJdiOYj`l(44`^x*$ z#^SdCY;?GMrVUvZ)SAQV=}vzleRI$RiKh+TjOI=C=*T-Pxh6TT@$fH@z(jui%avhF^D%g(fUP3upiVzOxRRi!KfFC-tNUnX4Fc86@ zw5-g}*fTecDmO^}jim3BTxK@eN|MX$ZLe~u`40g8yQJqN?U&RliegVnY0!+Jf3(uA zH1faLSix12{uO}lk^C=`);W^EC?Q>rzV4NDb$qT|26Tu$83+1h%tj(&oM|VkhT7fz@;{S&bqP0TUw83Si=d$otg|4NXtZdx}ZF$#7dY0sE zt|`9`z?J@*Y46=n@=KC77Dch&*EjsN&!FCq0W5Io>l#UKa*A=aq*qA(Hr0ItBV^y#ZE|1sJ#Hd88Nig1*H4mOHJ!E<*S!=W z#EL@GdHCNrNt#>+F0%PTHU#l|$8PxV`%W*Gs_Q9=1AoWcn2OboT`<=Wi_ zcadxcu-Ks3j1CPDIV?w@`3)F}-eW&q-f2gCT2)0Re?Jo=DYXp7MrEe`ip-rx=)c`^;| zWxr3xn^qQce4xp$!wx!&U>pKsAlrXGS{hRW9Tr$%`P>y9M+y_ zB*nChDj^xnsrcBlk0z@lx^eBCj?s9I}3Owj`xoiI3 z9r9#^xy5|68ChK&^5M$#EOH3EMymjT- zMQNZR**D$q0% zF6V1|ap2^QDg#@Tlzb4tZH`J(hnCl-!}pt=3LTNr4{}mgNS2IGxn5h@mRqU|(>#Qy z?fr7uHS)qwHOSZ10bgOHP?k{Uh|3`M>_;>^+%p2Z8cmlD=T(g6RmTWIRiWXPXmu4J zMsh2LI#0H`P4PlW*BXSWX9bir!9AwelDyOeil-b_Hcf=M&tYf{z?JS;nr4Njz1&0$ z9iVW%q!qTPP1kO>db~|0vAo^m%#zfbQp%@XbQ##WM$(4>e30Y^ogbbE;CBIhT+$x` zcq_?yBqs!;(&e7nCFEebxZkOndAcv2@v!pX%DUFx0H@LJkYt zdAR8P9O7;Db*vJ?fCOh&UF2VD6nUvshRmUs)MakKIrf&Plq0#&$gk)zhYQdnl{=R^ z*I|g}+AO7dd7M*xYDcd@rooav(|N)*x&0Al&z(u$Pxpfk&l^i-Pq zczNbrD;wpo^n*@`@JjS!=;+X|ll+wV&L8n6e@)U$wx*}KCGpJ|;tX#BaI2&z9QN%q zpmRHb6MSUwhcVP!{SO%8!uA5V!mWoZFw{tLlceuEUrv&4C3!Z6S|Arn`uFb5{-(QU z63J6sHu?>dO6roHuxHFP*De!AK53@Z*CqYjMraa-nnm7=Axdh~T~`||42An#X8`yN zhIs2YN_x@?I0Zwz^4~IkFevFx8-qqT1Yv{%2SNlw*H0gYS~yRRf>~k&4mdyG{M}L4UEu##Vowp@g$Sr0BA2-ukgfW6grpoR=fPRuq1a0LtSQP0Qj+y z^i7V!;&UWPivawNq{~R2W5+P!k#i~0=iUuec<4$}3dZ@M!zyLY8<@s&qYl=DfkudS zCfta2znlI4$%=QK^O=tTc#nmX;p7nj*eL1mFmxKjP8>LiaU+HfJD6gxd?J8PSgCXG z=mZOU(}$sb$)HZZjnf86pYwQESr|Sd=>wrAn3-#ZbIGre{FZsY1n?(T$hVrA_HRgD z6GGye*US5S%^UPpyS zIBjDOfbWz1SZS|fYO>!v^=7CCql#r!3X2E)OdHH(i=9Xxb^s~8V>+)S?%90m`sh2OUvL%qj8FhOW3Mz&Yd_{_zUE^=B_3o@UdWe~u(jLP?S zsOF5|ag5htmF*Z{`<-Vr3<7N&g}C3YB-JoL^R&akwE(sS9&x2YJNNx7lItwQi`?Ve zYXxW+X?_mCKau=_q#Z?341{MGG{ff$Zb!S;R=C?hj2u>N18|2u_EP}%2jsY!Qd&>) zPchU8ewn0s264KBux$05|DNQX0A4AIqR;balKf9L62I=c@?;!nx=3cbZ2@pM$^U6G z#$MwOO_G1?y7gNmEd?-Nn88L#Pa3CK zk#pTr_+0mySNUxA-3UkR`sc#Y(SXkhv_&;%bX8Ru_4%Pwd#=4HFvLmUAn9qBlA7`Gc&8h63;1k%rTG}@Z|-t){3aWw zXU()5zTUZA-NH224J5}JiLE&j*(zzRjZp(YBMXOnqE}+*z=wEBrYvkf1@MHVhWBH# zq$~WKnVl86`fCCF&|Y7rv7X}bC*nX4aSk6}HTLk3_i2(zC}(5nSg7u7(0t0wBkmnu z<~nn|gapfDK0tDd!NyY@3YYWOhXLH@xxU_DX-_$du(I9e)>HYMujP_Xw^7OHv`Pm( z^a0p`p=Ov>E`{}ny7R@7PPOr61@7=elCPlD95dd^oT+rf`A}%NJJejd2qzupj*;P0 zEIV1HTuXLoq!xsviZRp=u`S)NtSgkv*A&<9X&YujA>JDPk#s@0`yCi@;g@yrp^uwnDQ+ z!}CBco#DW_T}25wA0}snDuTREHHX&Sj#T3z`*q*1fdikjHsX}mEFW#kQ4DxQ8m*3e zsAsNs{B*@b`2e`r;sf3eXHGc4uhnaH|MCIn`AAi`H~VnIt3&zhEE}PvZb;c;BQU`R zVOOB6_T*!R+BE{;K*PNx%>Zy6$y0!%V;*!uNU5Zw zVED5U?s8<*^g0YWCCrC77S8P%%5!SR)CEOS>h~%xL?_UylhTfLE$j@Aimd-%syGSds1hRe00000NkvXXu0mjf=!IY{ literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/processmaker2.logo2.png b/workflow/public_html/images/processmaker2.logo2.png new file mode 100644 index 0000000000000000000000000000000000000000..10c3820213eb2a4f36c81a88a9aeaca128c8afd5 GIT binary patch literal 21345 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 Ef?~w?9{>OV literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/profile.gif b/workflow/public_html/images/profile.gif new file mode 100644 index 0000000000000000000000000000000000000000..64df9df2187dc3798aef00871f9297dcf2d06296 GIT binary patch literal 932 zcmd^;+e=e%0LNE!F=7~&6ciLaL>Dt73L~f&b45^u6$F~7T~sK{%R?)t%UIcknlrJR zsex%LovG>6v^ASf&zYLGyKYW*J#+VS`JLNZe@CB(@5>iH-={C{bb9L1oDG}}9A`U+ zWut5~>I#y1@??gMIs&9SNYM<7gebe8^oA*lW*2>=FG7yA)Lkl zg966346I%-j{pk-bPm+$BIZ1V!9|$dgwBcUop`Q1`LVjV)7at)67vXdTgG)x6eTEs zm<;2wIi>~@7+n}a(j-M&5ZvU(%6{^4 zv8;E~59MaLANRl1lv5N{3s*G%c_f|?{Pv9gvZ=}xyledjZw{S!J9Ez)0#f~ZEut$J zO!cC^62-|5+S9B@9!TF5D*v`7#0>59UUDT8ygoFmzbEC znwpxHmX@8JotKwaP*6}*R8&$@QeIwOQBhG@Sy@|KTUS?CUtizU)YRPE+}hgO-rnBP z(b3)A-P6<4*Vi{`(xl0gCr_C&W$M(a)2C0LF=NKenKNh2nl*d&?74I2&YL%H{`~n1 z7A#n}aN(jwixw|lykyCeWy_W=U%q_BiWMtYu3WWh)#}x&*Q{BycJ11A>(;Gbzkb7p z4VyM?+Pr!5)~#E&Z{NOS$BvylckbG?Yu~uyLRpR_3Jlo+_-u3=Iz_J@7%d_@7}!!4<0;x z`0&xAN6()>fA#9s+qZAuy?ghcmO$~JpmR}bVo7R>LV0FMhJw4NZvcbhPZmZlhI$4a z1|R@M6a)K-hWe)FdU=JGjwUnx`tX=~NypB5k%V}iut<%_IExOgdb@z2;Eyh II51cP0M@lw6aWAK literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/setupIcon.png b/workflow/public_html/images/setupIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..e05d786959f30e2bf189ae813567206c6d32b8e9 GIT binary patch literal 2070 zcmV+x2Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipe@ z6DJbU&NFKO00)*yL_t(Y$BkB9Y*g0~{^tJe8n3;J&3b=uu-67d47M@0Q%D0wYSIK1 zR8>L|X+qi*8mTW;`q-!TDQZYlQK=Gt4Mb`Pp(?7HxR1kdz;aGGn$z*a|U<>Q&Uq9$tO>q1OOa3aDWWMc)_x)_bkhL z&oGSV_wCz90C4WyxraE>X!KFHAp8&Lx*l*i9Q^>A7-JD4+T?UP->g=vAR=g*hH|<5 zYXJY2BxxmpglU??_4V}$d|RYasV7X+yeRI2b8a!l3VJ>-!ee86Y;26{x-J-F1!ViDS&$F{M1)i- z)#7rwek&`=k7QYPTc#<#xPFbQ)hdJlEX#tTD3GKDKIa^a*-#V}M1aHL#QF`rLQ^%e z87p$ePU*V-8=ud22LL1@LL?IP)YjG>(lqU->15KKN+!WM2j>EoWrHyrl0;z4Mx|0l zrBViCHi#B2k!{=HoTF4Kp|!Pjqr>6o z1n@8A{!?}~G&E>Nu?W+wLXsqyX7!*iNt_Ul)d-o^+e17@TVZ$)CHF~{)bUF=( z!+~O<2qHo@lL6kbttzV9FbqhNgv$MLTb2dJ7&zxbRaKH@8OcO4B^^I;{F8iMpXncX3eRlW zjE*&HP_9(K7{h!fgMi>xGp9vI$7-4wzlB^j2Y^tmR zuU@^n_wb*Oj1mBR@#U9mgTdg(xokFc_VlN)?S-jdm;#y8#j+es{}3J?)pyl?@jWD=4jfiVC?fDo{4hH!Wk&prRb3Qcn?2e1$* zk(itFc-%8UC z3rqFuh{vbR=H~d{dV70!yuABWL?aOlJoO#qbse>?T49+B{Mse8$m=@oe(g1k3=g9< z6hd9S8`rK~|LdJQ)8?ao+H3bi$sN+t67 z{j_;-kaS(2ymv1*R07*qoM6N<$f~O?Z AO8@`> literal 0 HcmV?d00001 diff --git a/workflow/public_html/images/start.png b/workflow/public_html/images/start.png new file mode 100644 index 0000000000000000000000000000000000000000..b542706d5464cd1fde45e7269beea9fd8d41fc96 GIT binary patch literal 4173 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 literal 0 HcmV?d00001 diff --git a/workflow/public_html/index.html b/workflow/public_html/index.html index d1a08e840..057cd02f8 100755 --- a/workflow/public_html/index.html +++ b/workflow/public_html/index.html @@ -3,6 +3,6 @@ Redirector - + \ No newline at end of file diff --git a/workflow/public_html/sysGeneric.php b/workflow/public_html/sysGeneric.php index 3ea6b5493..2a8a288ad 100755 --- a/workflow/public_html/sysGeneric.php +++ b/workflow/public_html/sysGeneric.php @@ -58,6 +58,56 @@ define('PATH_TRUNK', $pathTrunk ); define('PATH_OUTTRUNK', $pathOutTrunk ); +//////////////////////////// start, from paths.php + + /* Default configuration values (do not change these, use env.ini) */ + // $default_config = array( + // 'debug' => 0, + // 'debug_sql' => 0, + // 'debug_time' => 0, + // 'debug_calendar' => 0, + // 'wsdl_cache' => 1, + // 'memory_limit' => '100M', + // 'time_zone' => 'America/La_Paz', + // 'memcached' => 0, + // 'memcached_server' => '' + // ); + + // /* Read the env.ini */ + // $env_file = realpath($pathhome . PATH_SEP . 'engine' . PATH_SEP . 'config' . PATH_SEP . 'env.ini'); + // $config = $default_config; + + // if ($env_file !== false && file_exists($env_file)) { + // $ini_contents = parse_ini_file($env_file, false); + // if ($ini_contents !== false) + // $config = array_merge($default_config, $ini_contents); + // } + //var_dump($pathhome . 'engine' . PATH_SEP . 'classes' . PATH_SEP . 'class.system.php'); die; + require_once $pathhome . 'engine' . PATH_SEP . 'classes' . PATH_SEP . 'class.system.php'; + $config = System::getSystemConfiguration($pathhome . 'engine' . PATH_SEP . 'config' . PATH_SEP . 'env.ini'); + +//*** Do not change any of these settings directly, use env.ini instead + ini_set('display_errors','On'); + + ini_set('short_open_tag', 'on'); + ini_set('asp_tags', 'on'); + // The register_globals feature has been DEPRECATED as of PHP 5.3.0. default value Off. + // ini_set('register_globals', 'off'); + ini_set('default_charset', "UTF-8"); + $e_all = defined('E_DEPRECATED') ? E_ALL ^ E_DEPRECATED : E_ALL; + ini_set('error_reporting', ($config['debug'] ? $e_all : $e_all ^ E_NOTICE) ); + ini_set('memory_limit', $config['memory_limit']); + ini_set('soap.wsdl_cache_enabled', $config['wsdl_cache']); + + define ('DEBUG_SQL_LOG', $config['debug_sql'] ); + define ('DEBUG_TIME_LOG', $config['debug_time'] ); + define ('DEBUG_CALENDAR_LOG', $config['debug_calendar'] ); + define ('MEMCACHED_ENABLED', $config['memcached']); + define ('MEMCACHED_SERVER', $config['memcached_server']); + + define ('TIME_ZONE', $config['time_zone']); +//////////////////////////// end, from paths.php + //************* Including these files we get the PM paths and definitions (that should be just one file *********** require_once ( $pathhome . PATH_SEP . 'engine' . PATH_SEP . 'config' . PATH_SEP . 'paths.php' ); @@ -104,6 +154,13 @@ $oHeadPublisher->addMaborakFile( PATH_CORE . 'js' . PATH_SEP . 'appFolder/core/appFolderList.js', true ); $oHeadPublisher->addMaborakFile( PATH_THIRDPARTY . 'htmlarea/editor.js', true ); + //erik: if it is a installation instance + if(!defined('PATH_C')) { + $tmpDir = G::getSysTemDir(); + define('PATH_C', $tmpDir . ((substr($tmpDir, -1) == PATH_SEP)? '': PATH_SEP)); + define('PATH_LANGUAGECONT', PATH_HOME . 'engine/content/languages/' ); + } + //************ defining Virtual URLs ****************/ $virtualURITable = array(); $virtualURITable['/plugin/(*)'] = 'plugin'; @@ -214,7 +271,7 @@ die; break; case 'errorFile': - header ("location: /errors/error404.php"); + header ("location: /errors/error404.php?url=" . urlencode($_SERVER['REQUEST_URI'])); if ( DEBUG_TIME_LOG ) logTimeByPage(); //log this page die; break; @@ -228,7 +285,8 @@ //************** the request correspond to valid php page, now parse the URI ************** - G::parseURI ( getenv( "REQUEST_URI" ) ); + G::parseURI(getenv("REQUEST_URI" ), $config); + $oHeadPublisher->addMaborakFile( PATH_GULLIVER_HOME . 'js' . PATH_SEP . "widgets/jscalendar/lang/calendar-" . SYS_LANG . ".js"); define( 'SYS_URI' , '/sys' . SYS_TEMP . '/' . SYS_LANG . '/' . SYS_SKIN . '/' ); @@ -265,16 +323,32 @@ //************** Installer, redirect to install if we don't have a valid shared data folder ***************/ if ( !defined('PATH_DATA') || !file_exists(PATH_DATA)) { - if ( (SYS_TARGET==='installServer')) { - $phpFile = G::ExpandPath('methods') ."install/installServer.php"; - require_once($phpFile); - die(); + + /*new installer, extjs based*/ + + define('PATH_DATA', PATH_C); + require_once ( PATH_CONTROLLERS . 'installer.php' ); + $controller = 'Installer'; + + //if the method name is empty set default to index method + if (strpos(SYS_TARGET, '/') !== false) + list($controller, $controllerAction) = explode('/', SYS_TARGET); + else + $controllerAction = SYS_TARGET; + + $controllerAction = ($controllerAction != '' && $controllerAction != 'login')? $controllerAction: 'index'; + + //create the installer controller and call its method + if( is_callable(Array('Installer', $controllerAction)) ) { + $installer = new $controller(); + $installer->setHttpRequestData($_REQUEST); + $installer->call($controllerAction); } else { - $phpFile = G::ExpandPath('methods') ."install/install.php"; - require_once($phpFile); - die(); + $_SESSION['phpFileNotFound'] = $_SERVER['REQUEST_URI']; + header ("location: /errors/error404.php?url=" . urlencode($_SERVER['REQUEST_URI'])); } + die; } // ************* Load Language Translation ***************** @@ -303,15 +377,13 @@ set_include_path(get_include_path() . PATH_SEPARATOR . PATH_WORKSPACE); } else { - $aMessage['MESSAGE'] = G::LoadTranslation ('ID_NOT_WORKSPACE'); - $G_PUBLISH = new Publisher; - $G_PUBLISH->AddContent('xmlform', 'xmlform', 'login/showMessage', '', $aMessage ); - G::RenderPage( 'publish' ); + G::SendTemporalMessage ('ID_NOT_WORKSPACE', "error"); + G::header('location: /sys/' . SYS_LANG . '/' . SYS_SKIN . '/main/sysLogin?errno=2'); die; } } else { //when we are in global pages, outside any valid workspace - if ((SYS_TARGET==='sysLoginVerify') || (SYS_TARGET==='sysLogin') || (SYS_TARGET==='newSite')) { + if (SYS_TARGET==='newSite') { $phpFile = G::ExpandPath('methods') . SYS_COLLECTION . "/" . SYS_TARGET.'.php'; require_once($phpFile); die(); @@ -321,7 +393,22 @@ require_once( PATH_METHODS . "login/dbInfo.php" ) ; } else{ - require_once( PATH_METHODS . "login/sysLogin.php" ) ; + + if (substr(SYS_SKIN, 0, 2) === 'ux' && SYS_TARGET != 'sysLoginVerify') { // new ux sysLogin - extjs based form + require_once PATH_CONTROLLERS . 'main.php'; + $controllerClass = 'Main'; + $controllerAction = SYS_TARGET == 'sysLoginVerify' ? SYS_TARGET : 'sysLogin'; + //if the method exists + if( is_callable(Array($controllerClass, $controllerAction)) ) { + $controller = new $controllerClass(); + $controller->setHttpRequestData($_REQUEST); + $controller->call($controllerAction); + } + } + else { // classic sysLogin interface + require_once( PATH_METHODS . "login/sysLogin.php" ) ; + die(); + } } if ( DEBUG_TIME_LOG ) logTimeByPage(); //log this page die(); @@ -486,7 +573,7 @@ if ( ! $isControllerCall && ! file_exists( $phpFile ) ) { $_SESSION['phpFileNotFound'] = $_SERVER['REQUEST_URI']; print $phpFile; - header ("location: /errors/error404.php"); + header ("location: /errors/error404.php?url=" . urlencode($_SERVER['REQUEST_URI'])); die; } } @@ -494,8 +581,9 @@ //redirect to login, if user changed the workspace in the URL if( ! $avoidChangedWorkspaceValidation && isset( $_SESSION['WORKSPACE'] ) && $_SESSION['WORKSPACE'] != SYS_SYS) { $_SESSION['WORKSPACE'] = SYS_SYS; + G::SendTemporalMessage ('ID_USER_HAVENT_RIGHTS_SYSTEM', "error"); - header ( 'Location: /sys' . SYS_SYS . '/' . SYS_LANG . '/' . SYS_SKIN . '/login/login' ); + header ( 'Location: /sys' . SYS_SYS . '/' . SYS_LANG . '/' . SYS_SKIN . '/main/login' ); die; } @@ -516,7 +604,12 @@ if((isset( $_SESSION['USER_LOGGED'] ))&&(!(isset($_GET['sid'])))) { $RBAC->initRBAC(); - $RBAC->loadUserRolePermission( $RBAC->sSystem, $_SESSION['USER_LOGGED'] , PATH_DATA, session_id()); + //using optimization with memcache, the user data will be in memcache 8 hours, or until session id goes invalid + $memKey = 'rbacSession' . session_id(); + if ( ($RBAC->aUserInfo = $memcache->get($memKey)) === false ) { + $RBAC->loadUserRolePermission( $RBAC->sSystem, $_SESSION['USER_LOGGED'] ); + $memcache->set( $memKey, $RBAC->aUserInfo, PMmemcached::EIGHT_HOURS ); + } } else { // this is the blank list to allow execute scripts with no login (without session started) @@ -539,6 +632,7 @@ $noLoginFolders[] = 'services'; $noLoginFolders[] = 'tracker'; + $noLoginFolders[] = 'installer'; //This sentence is used when you lost the Session if ( !in_array(SYS_TARGET, $noLoginFiles) && !in_array(SYS_COLLECTION, $noLoginFolders) && $bWE != true && $collectionPlugin != 'services') { @@ -556,6 +650,8 @@ $bRedirect = false; $RBAC->initRBAC(); $RBAC->loadUserRolePermission( $RBAC->sSystem, $_SESSION['USER_LOGGED'] ); + $memKey = 'rbacSession' . session_id(); + $memcache->set( $memKey, $RBAC->aUserInfo, PMmemcached::EIGHT_HOURS ); } } if ($bRedirect) { @@ -564,14 +660,19 @@ $loginUrl = 'home/login'; } else { - $loginUrl = 'login/login'; + $loginUrl = 'main/login'; } if (empty($_POST)) { header('location: ' . SYS_URI . $loginUrl . '?u=' . urlencode($_SERVER['REQUEST_URI'])); } else { - header('location: ' . SYS_URI . $loginUrl); + if ( $isControllerCall ) { + header("HTTP/1.0 302 session lost in controller"); + } + else { + header('location: ' . SYS_URI . $loginUrl); + } } die(); }