commit 3477e8e87e7776cd6086b08a8093828f427633da
parent 302831192e05cd04396eb7bba0d8868f3f70138a
Author: Felicitus <felicitus@felicitus.org>
Date: Tue, 6 Sep 2011 09:22:50 +0200
Added user preferences menu item + password change function
Diffstat:
9 files changed, 360 insertions(+), 1 deletion(-)
diff --git a/frontend/css/PartKeepr.css b/frontend/css/PartKeepr.css
@@ -106,6 +106,9 @@ td.o2 {
background-image: url(../resources/fugue-icons/icons/ruler.png) !important;
}
+.icon-gear {
+ background-image: url(../resources/fugue-icons/icons/gear.png) !important;
+}
diff --git a/frontend/js/Components/MenuBar.js b/frontend/js/Components/MenuBar.js
@@ -46,8 +46,25 @@ Ext.define('PartKeepr.MenuBar', {
}]
});
+ this.systemMenu = Ext.create('Ext.menu.Menu', {
+ items: [
+ {
+ text: i18n('Disconnect'),
+ icon: 'resources/silkicons/disconnect.png',
+ handler: this.disconnect
+ },{
+ text: i18n("User Preferences"),
+ icon: 'resources/fugue-icons/icons/gear.png',
+ handler: this.showUserPreferences
+ }
+ ]
+ });
+
this.items = [{
- text: 'Menu',
+ text: i18n("System"),
+ menu: this.systemMenu
+ },{
+ text: i18n('Menu'),
menu: this.menu
},
'->',
@@ -62,6 +79,18 @@ Ext.define('PartKeepr.MenuBar', {
this.callParent();
},
+ showUserPreferences: function () {
+ var j = new PartKeepr.UserPreferencePanel({
+ iconCls: 'icon-gear',
+ closable: true
+ });
+
+ PartKeepr.getApplication().addItem(j);
+ j.show();
+ },
+ disconnect: function () {
+ PartKeepr.getApplication().logout();
+ },
/**
* Shows the system information window
*/
diff --git a/frontend/js/Components/User/UserPasswordChangePanel.js b/frontend/js/Components/User/UserPasswordChangePanel.js
@@ -0,0 +1,75 @@
+Ext.define('PartKeepr.UserPasswordChangePanel', {
+ extend: 'Ext.form.FormPanel',
+ title: i18n("Change Password"),
+ bodyStyle: 'background:#DBDBDB;padding: 10px;',
+ initComponent: function () {
+
+ this.oldPassword = Ext.create("Ext.form.field.Text", {
+ inputType: "password",
+ name: 'password',
+ labelWidth: 150,
+ style: 'border-bottom: 1px solid grey; padding-bottom: 10px;',
+ width: 300,
+ fieldLabel: i18n("Current Password")
+ });
+
+ this.newPassword = Ext.create("Ext.form.field.Text", {
+ style: 'margin-top: 10px',
+ inputType: "password",
+ name: 'password',
+ labelWidth: 150,
+ width: 300,
+ fieldLabel: i18n("New Password")
+ });
+
+ this.newPasswordConfirm = Ext.create("Ext.form.field.Text", {
+ inputType: "password",
+ name: 'password',
+ labelWidth: 150,
+ width: 300,
+ validator: Ext.bind(this.validatePassword, this),
+ fieldLabel: i18n("New Password (Confirm)")
+ });
+
+ this.items = [
+ this.oldPassword,
+ this.newPassword,
+ this.newPasswordConfirm,
+ {
+ xtype: 'fieldcontainer',
+ hideEmptyLabel: false,
+ width: 300,
+ labelWidth: 150,
+ items: {
+ xtype: 'button',
+ handler: this.onChangePassword,
+ scope: this,
+ width: 145,
+ icon: 'resources/silkicons/accept.png',
+ text: i18n("Change Password")
+ }
+ }];
+
+ this.callParent();
+ },
+ onChangePassword: function () {
+ if (this.getForm().isValid()) {
+
+ var call = new PartKeepr.ServiceCall("UserPreference", "changePassword");
+ call.setParameter("oldpassword", md5(this.oldPassword.getValue()));
+ call.setParameter("newpassword", md5(this.newPassword.getValue()));
+ call.setHandler(Ext.bind(this.onAfterPasswordChange, this));
+ call.doCall();
+ }
+ },
+ onAfterPasswordChange: function (data) {
+ Ext.Msg.alert(data.data, data.data);
+ },
+ validatePassword: function () {
+ if (this.newPassword.getValue() != this.newPasswordConfirm.getValue()) {
+ return i18n("Passwords don't match");
+ }
+
+ return true;
+ }
+});+
\ No newline at end of file
diff --git a/frontend/js/Components/User/UserPreferences.js b/frontend/js/Components/User/UserPreferences.js
@@ -0,0 +1,13 @@
+Ext.define('PartKeepr.UserPreferencePanel', {
+ extend: 'Ext.tab.Panel',
+ title: i18n("User Preferences"),
+ tabPosition: 'bottom',
+ //bodyStyle: 'background:#DBDBDB;padding: 10px;',
+ initComponent: function () {
+
+ this.passwordChangePanel = Ext.create("PartKeepr.UserPasswordChangePanel");
+
+ this.items = [ this.passwordChangePanel ];
+ this.callParent();
+ }
+});+
\ No newline at end of file
diff --git a/frontend/js/Components/Widgets/ResistorCalculator.js b/frontend/js/Components/Widgets/ResistorCalculator.js
@@ -0,0 +1,29 @@
+Ext.define("PartKeepr.ResistorCalculator",{
+ extend:"Ext.window.Window",
+ alias: 'widget.ResistorCalculator',
+
+ width: 300,
+ height: 300,
+ layout: 'fit',
+ initComponent: function () {
+
+ //this.resistorValueField = Ext.create("Ext.form.field.Number");
+ this.resistorDisplay = Ext.create("PartKeepr.ResistorDisplay", {
+ viewBox: false
+ });
+
+ /*this.resistorDisplay = Ext.create('Ext.draw.Component', {
+ viewBox: false,
+ items: [{
+ type: 'circle',
+ fill: '#79BB3F',
+ radius: 100,
+ x: 100,
+ y: 100
+ }]
+ });*/
+
+ this.items = [ this.resistorDisplay ];
+ this.callParent();
+ }
+});+
\ No newline at end of file
diff --git a/frontend/js/Components/Widgets/ResistorDisplay.js b/frontend/js/Components/Widgets/ResistorDisplay.js
@@ -0,0 +1,24 @@
+Ext.define("PartKeepr.ResistorDisplay",{
+ extend:"Ext.draw.Component",
+ alias: 'widget.ResistorDisplay',
+
+ viewBox: false,
+ initComponent: function () {
+
+ this.circle = Ext.create("Ext.draw.Sprite", {
+ type: 'circle',
+ fill: '#79BB3F',
+ radius: 100,
+ x: 100,
+ y: 100
+ });
+
+ this.items = [ this.circle ];
+
+ this.callParent();
+
+ this.circle = this.surface;
+
+
+ }
+});+
\ No newline at end of file
diff --git a/frontend/js/Components/Widgets/UserPreferenceGrid.js b/frontend/js/Components/Widgets/UserPreferenceGrid.js
@@ -0,0 +1,71 @@
+Ext.define('PartKeepr.UserPreferenceGrid', {
+ extend: 'Ext.grid.Panel',
+
+ columnLines: true,
+
+ columns: [{
+ header: i18n("Key"),
+ dataIndex: 'key',
+ flex: 0.3,
+ minWidth: 200,
+ renderer: Ext.util.Format.htmlEncode
+ },{
+ header: i18n("Value"),
+ dataIndex: 'value',
+ flex: 0.7,
+ minWidth: 200,
+ renderer: Ext.util.Format.htmlEncode
+ }],
+ userId: null,
+
+ initComponent: function () {
+ this.deleteButton = Ext.create("Ext.button.Button", {
+ text: i18n('Delete'),
+ disabled: true,
+ itemId: 'delete',
+ scope: this,
+ icon: 'resources/silkicons/delete.png',
+ handler: this.onDeleteClick
+ });
+
+ this.dockedItems = [{
+ xtype: 'toolbar',
+ items: [
+ this.deleteButton
+ ]
+ }];
+ this.store = Ext.create("Ext.data.Store", {
+ model: 'PartKeepr.UserPreference',
+ pageSize: -1
+ });
+
+ this.callParent();
+
+ this.getSelectionModel().on('selectionchange', this.onSelectChange, this);
+ },
+ onDeleteClick: function () {
+ var selection = this.getView().getSelectionModel().getSelection()[0];
+ if (selection) {
+ // Set phantom to false because ExtJS has problems with PK-less thingies
+ selection.phantom = false;
+ this.store.remove(selection);
+ }
+ },
+ onSelectChange: function(selModel, selections){
+ this.deleteButton.setDisabled(selections.length === 0);
+ },
+ syncPreferences: function () {
+ /* Iterate through all removed records and issue an AJAX
+ * call. This is necessary because the server side doesn't suport string
+ * keys and ExtJS can't handle composite keys.
+ */
+ for (var j=0;j<this.store.removed.length;j++) {
+ var call = new PartKeepr.ServiceCall("UserPreference", "destroy");
+ call.setParameter("key", this.store.removed[j].get("key"));
+ call.setParameter("user_id", this.store.removed[j].get("user_id"));
+ call.doCall();
+ }
+
+ this.store.removed = [];
+ }
+});
diff --git a/frontend/js/bugfixes.js b/frontend/js/bugfixes.js
@@ -19,6 +19,108 @@ Ext.override(Ext.panel.Table, {
scrollDelta: 100
});
+Ext.panel.Table.override({
+ determineScrollbars: function() {
+ var me = this,
+ box,
+ tableEl,
+ scrollWidth,
+ clientWidth,
+ scrollHeight,
+ clientHeight,
+ verticalScroller = me.verticalScroller,
+ horizontalScroller = me.horizontalScroller,
+ curScrollbars = (verticalScroller && verticalScroller.ownerCt === me ? 1 : 0) |
+ (horizontalScroller && horizontalScroller.ownerCt === me ? 2 : 0),
+ reqScrollbars = 0; // 1 = vertical, 2 = horizontal, 3 = both
+
+
+ // If we are not collapsed, and the view has been rendered AND filled, then we can determine scrollbars
+ if (!me.collapsed && me.view && me.view.el && me.view.el.dom.firstChild && !me.changingScrollBars) {
+
+
+ // Calculate maximum, *scrollbarless* space which the view has available.
+ // It will be the Fit Layout's calculated size, plus the widths of any currently shown scrollbars
+ //####### THIS IS ONLY CHANGE I HAVE MADE, USE VIEW SIZE RATHER THAN FULL PANEL SIZE #######
+ box = me.view.getSize();
+
+
+ clientWidth = box.width + ((curScrollbars & 1) ? verticalScroller.width : 0);
+ clientHeight = box.height + ((curScrollbars & 2) ? horizontalScroller.height : 0);
+
+
+ // Calculate the width of the scrolling block
+ // There will never be a horizontal scrollbar if all columns are flexed.
+
+
+ scrollWidth = (me.headerCt.query('[flex]').length && !me.headerCt.layout.tooNarrow) ? 0 : me.headerCt.getFullWidth();
+
+
+ // Calculate the height of the scrolling block
+ if (verticalScroller && verticalScroller.el) {
+ scrollHeight = verticalScroller.getSizeCalculation().height;
+ } else {
+ tableEl = me.view.el.child('table', true);
+ scrollHeight = tableEl ? tableEl.offsetHeight : 0;
+ }
+
+
+ // View is too high.
+ // Definitely need a vertical scrollbar
+ if (scrollHeight > clientHeight) {
+ reqScrollbars = 1;
+
+
+ // But if scrollable block width goes into the zone required by the vertical scrollbar, we'll also need a horizontal
+ if (horizontalScroller && ((clientWidth - scrollWidth) < verticalScroller.width)) {
+ reqScrollbars = 3;
+ }
+ }
+
+
+ // View height fits. But we stil may need a horizontal scrollbar, and this might necessitate a vertical one.
+ else {
+ // View is too wide.
+ // Definitely need a horizontal scrollbar
+ if (scrollWidth > clientWidth) {
+ reqScrollbars = 2;
+
+
+ // But if scrollable block height goes into the zone required by the horizontal scrollbar, we'll also need a vertical
+ if (verticalScroller && ((clientHeight - scrollHeight) < horizontalScroller.height)) {
+ reqScrollbars = 3;
+ }
+ }
+ }
+
+
+ // If scrollbar requirements have changed, change 'em...
+ if (reqScrollbars !== curScrollbars) {
+
+
+ // Suspend component layout while we add/remove the docked scrollers
+ me.suspendLayout = true;
+ if (reqScrollbars & 1) {
+ me.showVerticalScroller();
+ } else {
+ me.hideVerticalScroller();
+ }
+ if (reqScrollbars & 2) {
+ me.showHorizontalScroller();
+ } else {
+ me.hideHorizontalScroller();
+ }
+ me.suspendLayout = false;
+ }
+ // Lay out the Component.
+ // Set a flag so that afterComponentLayout does not recurse back into here.
+ me.changingScrollBars = true;
+ me.doComponentLayout();
+ me.changingScrollBars = false;
+ }
+ }
+});
+
Ext.override(Ext.data.Connection, {
/**
* Inject session header. I haven't found a better way to do
diff --git a/src/de/RaumZeitLabor/PartKeepr/UserPreference/UserPreferenceService.php b/src/de/RaumZeitLabor/PartKeepr/UserPreference/UserPreferenceService.php
@@ -88,7 +88,16 @@ class UserPreferenceService extends Service implements RestfulService {
} else {
UserPreference::deletePreference($this->getUser(), $this->getParameter("key"));
}
+ }
+
+ public function changePassword () {
+ if (!$this->getUser()->compareHashedPassword($this->getParameter("oldpassword"))) {
+ throw new \Exception("Invalid Password");
+ } else {
+ $this->getUser()->setHashedPassword($this->getParameter("newpassword"));
+ }
+ return array("data" => PartKeepr::i18n("Password changed successfully"));
}
}
\ No newline at end of file