PartKeepr.js (23617B)
1 Ext.namespace('PartKeepr'); 2 3 PartKeepr.application = null; 4 5 Ext.application({ 6 name: 'PartKeepr', 7 loginManager: null, 8 9 init: function () 10 { 11 12 13 }, 14 launch: function () 15 { 16 Ext.setGlyphFontFamily('FontAwesome'); 17 Ext.get("loader-wrapper").hide(); 18 Ext.setLocale('en_US'); 19 20 this.createLayout(); 21 22 PartKeepr.application = this; 23 24 // Set static data of the server 25 PartKeepr.setMaxUploadSize(window.parameters.maxUploadSize); 26 PartKeepr.setAvailableImageFormats(window.parameters.availableImageFormats); 27 PartKeepr.setOctoPartAvailable(window.parameters.isOctoPartAvailable); 28 29 var authenticationProvider = Ext.create(window.parameters.authentication_provider); 30 PartKeepr.Auth.AuthenticationProvider.setAuthenticationProvider(authenticationProvider); 31 32 this.control({ 33 'MenuBar menuitem': { 34 click: this.onAppMenuClick, 35 scope: this 36 } 37 }); 38 39 var config = {}; 40 41 if (window.parameters.autoLoginUsername) { 42 config.autoLogin = true; 43 config.autoLoginUsername = window.parameters.autoLoginUsername; 44 config.autoLoginPassword = window.parameters.autoLoginPassword; 45 } 46 47 this.loginManager = Ext.create("PartKeepr.Auth.LoginManager", config); 48 this.loginManager.on("login", this.onLogin, this); 49 this.loginManager.on("logout", this.onLogout, this); 50 this.loginManager.login(); 51 }, 52 onAppMenuClick: function (item) 53 { 54 if (typeof item.target === "function") { 55 this.openAppItem(item.target["$className"]); 56 } 57 58 if (typeof(item.theme) === "string") { 59 this.switchTheme(item.theme); 60 } 61 }, 62 openAppItem: function (target) 63 { 64 targetClass = Ext.ClassManager.get(target); 65 66 var config = { 67 title: targetClass.title, 68 closable: targetClass.closable, 69 iconCls: targetClass.iconCls 70 }; 71 72 var j = Ext.create(target, config); 73 74 if (targetClass.superclass["$className"] == "PartKeepr.Actions.BaseAction") { 75 j.execute(); 76 } else { 77 PartKeepr.getApplication().addItem(j); 78 j.show(); 79 } 80 }, 81 getParameter: function (parameter) 82 { 83 if (window.parameters[parameter] !== undefined) { 84 return window.parameters[parameter]; 85 } 86 }, 87 getLoginManager: function () 88 { 89 return this.loginManager; 90 }, 91 getPartManager: function () 92 { 93 return this.partManager; 94 }, 95 /** 96 * Handles the login function. Initializes the part manager window, 97 * enables the menu bar and creates the stores+loads them. 98 */ 99 onLogin: function () 100 { 101 this.createGlobalStores(); 102 103 var initialUserPreferences = Ext.decode(this.getLoginManager().getUser().get("initialUserPreferences")); 104 105 var records = this.getUserPreferenceStore().getProxy().getReader().read(initialUserPreferences); 106 107 this.getUserPreferenceStore().loadRecords(records.records); 108 109 var preferredTheme = this.getUserPreference("partkeepr.user.theme", null); 110 111 this.createPartManager(); 112 113 this.menuBar.enable(); 114 115 this.doSystemStatusCheck(); 116 117 this.unacknowledgedNoticesTask = Ext.TaskManager.start({ 118 run: this.doUnacknowledgedNoticesCheck, 119 scope: this, 120 interval: 100000 121 }); 122 123 this.displayTipWindowTask = new Ext.util.DelayedTask(this.displayTipOfTheDayWindow, this); 124 this.displayTipWindowTask.delay(100); 125 126 if (window.parameters.motd) { 127 this.displayMOTD(); 128 } 129 130 this.getStatusbar().setConnected(); 131 132 if (preferredTheme !== null && preferredTheme !== window.theme) { 133 this.switchTheme(preferredTheme); 134 } 135 136 }, 137 onLogout: function () 138 { 139 this.menuBar.disable(); 140 this.centerPanel.removeAll(true); 141 this.getStatusbar().setDisconnected(); 142 143 Ext.TaskManager.stop(this.unacknowledgedNoticesTask); 144 }, 145 /** 146 * Re-creates the part manager. This is usually called when the "compactLayout" configuration option has been 147 * changed. 148 * 149 * @param none 150 * @return nothing 151 */ 152 recreatePartManager: function () 153 { 154 this.centerPanel.remove(this.partManager); 155 this.getPartManager().destroy(); 156 157 this.createPartManager(); 158 }, 159 /** 160 * Creates the part manager. While this is usually only done after login, it can also happen when the user changes 161 * the "compact" preference. 162 */ 163 createPartManager: function () 164 { 165 this.partManager = Ext.create("PartKeepr.PartManager", { 166 title: i18n("Part Manager"), 167 compactLayout: PartKeepr.getApplication().getUserPreference("partkeepr.partmanager.compactlayout", false), 168 iconCls: 'web-icon brick', 169 closable: false 170 }); 171 172 this.centerPanel.insert(0, this.partManager); 173 }, 174 /** 175 * Sets the initial user preferences, which are applied into the userPreferenceStore after login. 176 */ 177 setInitialUserPreferences: function (obj) 178 { 179 PartKeepr.initialUserPreferences = obj; 180 }, 181 /** 182 * Displays the tip of the day window. 183 * 184 * This method checks if the user has disabled tips, and if so, this method 185 * avoids showing the window. 186 */ 187 displayTipOfTheDayWindow: function () 188 { 189 if (!Ext.data.StoreManager.lookup('TipOfTheDayStore') || !Ext.data.StoreManager.lookup( 190 'TipOfTheDayStore').isLoaded() || !Ext.data.StoreManager.lookup( 191 'TipOfTheDayHistoryStore') || !Ext.data.StoreManager.lookup( 192 'TipOfTheDayHistoryStore').isLoaded() || !this.getUserPreferenceStore().isLoaded() 193 ) { 194 this.displayTipWindowTask.delay(100); 195 return; 196 } 197 198 if (PartKeepr.getApplication().getUserPreference("partkeepr.tipoftheday.showtips") !== false) { 199 var j = Ext.create("PartKeepr.TipOfTheDayWindow"); 200 201 if (j.hasTips()) { 202 j.show(); 203 } 204 } 205 }, 206 /** 207 * Displays a message-of-the-day 208 */ 209 displayMOTD: function () 210 { 211 Ext.MessageBox.alert(i18n("Message of the day"), window.parameters.motd); 212 }, 213 /** 214 * Does a schema status call against the PartKeepr installation, in order to verify if the schema is up-to-date. 215 * 216 * @param none 217 * @return nothing 218 */ 219 doSystemStatusCheck: function () 220 { 221 var call = new PartKeepr.ServiceCall("api", "system_status"); 222 call.setHandler(Ext.bind(this.onSystemStatusCheck, this)); 223 call.doCall(); 224 }, 225 /** 226 * Handler for the schema check 227 * @param data The data returned from the server 228 */ 229 onSystemStatusCheck: function (data) 230 { 231 if (data.schemaStatus !== "complete") { 232 alert(i18n("Your database schema is not up-to-date! Please re-run setup immediately!")); 233 } 234 235 if (data.inactiveCronjobCount > 0) { 236 alert(i18n("The following cronjobs aren't running:") + "\n\n" + data.inactiveCronjobs.join("\n")); 237 } 238 }, 239 /* 240 * Checks for unacknowledged system notices. Triggers a service call against the server. 241 * 242 * Checks if a session is active; otherwise, nothing will happen. 243 * 244 * @param none 245 * @return nothing 246 */ 247 doUnacknowledgedNoticesCheck: function () 248 { 249 this.systemNoticeStore.load({ 250 scope: this, 251 callback: this.onUnacknowledgedNoticesCheck 252 }); 253 }, 254 /** 255 * Handler for the unacknowledged system notices check 256 * @param data The data returned from the server 257 */ 258 onUnacknowledgedNoticesCheck: function () 259 { 260 if (this.systemNoticeStore.count() > 0) { 261 this.statusBar.systemNoticeButton.show(); 262 } else { 263 this.statusBar.systemNoticeButton.hide(); 264 } 265 }, 266 createGlobalStores: function () 267 { 268 this.footprintStore = Ext.create("Ext.data.Store", 269 { 270 model: 'PartKeepr.FootprintBundle.Entity.Footprint', 271 pageSize: 99999999, 272 autoLoad: true 273 }); 274 275 this.siPrefixStore = Ext.create("Ext.data.Store", 276 { 277 model: 'PartKeepr.SiPrefixBundle.Entity.SiPrefix', 278 pageSize: 99999999, 279 autoLoad: true 280 }); 281 282 this.currencyStore = Ext.create("PartKeepr.Data.Store.CurrencyStore", { 283 autoLoad: true 284 }); 285 286 this.distributorStore = Ext.create("Ext.data.Store", 287 { 288 model: 'PartKeepr.DistributorBundle.Entity.Distributor', 289 pageSize: 99999999, 290 autoLoad: true 291 }); 292 293 this.manufacturerStore = Ext.create("Ext.data.Store", 294 { 295 model: 'PartKeepr.ManufacturerBundle.Entity.Manufacturer', 296 pageSize: 99999999, 297 autoLoad: true 298 }); 299 300 this.partUnitStore = Ext.create("Ext.data.Store", 301 { 302 model: 'PartKeepr.PartBundle.Entity.PartMeasurementUnit', 303 pageSize: 99999999, 304 autoLoad: true 305 }); 306 307 this.unitStore = Ext.create("Ext.data.Store", 308 { 309 model: 'PartKeepr.UnitBundle.Entity.Unit', 310 pageSize: 99999999, 311 autoLoad: true 312 }); 313 314 this.userStore = Ext.create("Ext.data.Store", 315 { 316 model: 'PartKeepr.AuthBundle.Entity.User', 317 pageSize: 99999999, 318 autoLoad: true 319 }); 320 321 this.userPreferenceStore = Ext.create("PartKeepr.data.store.UserPreferenceStore", 322 { 323 model: 'PartKeepr.AuthBundle.Entity.UserPreference', 324 autoLoad: false 325 }); 326 327 this.barcodeScannerManager = Ext.create("PartKeepr.BarcodeScanner.Manager"); 328 329 this.systemPreferenceStore = Ext.create("PartKeepr.data.store.SystemPreferenceStore", 330 { 331 model: 'PartKeepr.SystemPreferenceBundle.Entity.SystemPreference', 332 autoLoad: true, 333 listeners: { 334 scope: this, 335 "load": function () 336 { 337 this.barcodeScannerManager.registerBarcodeScannerHotkey(); 338 } 339 } 340 }); 341 342 this.tipOfTheDayStore = Ext.create("PartKeepr.data.store.TipOfTheDayStore"); 343 this.tipOfTheDayHistoryStore = Ext.create("PartKeepr.data.store.TipOfTheDayHistoryStore"); 344 this.systemNoticeStore = Ext.create("PartKeepr.data.store.SystemNoticeStore"); 345 346 }, 347 getBarcodeScannerManager: function () 348 { 349 return this.barcodeScannerManager; 350 351 }, 352 storeLoaded: function (store) 353 { 354 store._loaded = true; 355 }, 356 setAdmin: function (admin) 357 { 358 this.admin = admin; 359 }, 360 isAdmin: function () 361 { 362 return this.admin; 363 }, 364 getSystemPreferenceStore: function () 365 { 366 return this.systemPreferenceStore; 367 }, 368 /** 369 * Queries for a specific system preference. Returns either the value or a default value if 370 * the preference was not found. 371 * @param key The key to query 372 * @param defaultValue A default value to return (optional) 373 * @returns the key value, or defaultValue if preference key was not found 374 */ 375 getSystemPreference: function (key, defaultValue) 376 { 377 if (this.systemPreferenceStore === undefined) { 378 return defaultValue; 379 } 380 var record = this.systemPreferenceStore.findRecord("preferenceKey", key); 381 382 if (record) { 383 var value = record.get("preferenceValue"); 384 var decodedValue = Ext.decode(value, true); 385 386 if (decodedValue === null) { 387 return value; 388 } else { 389 return decodedValue; 390 } 391 } else { 392 return (typeof defaultValue == "undefined") ? null : defaultValue; 393 } 394 }, 395 /** 396 * Sets a specific system preference. Directly commits the change to the server. 397 * 398 * @param key The key to set 399 * @param value The value to set 400 */ 401 setSystemPreference: function (key, value) 402 { 403 var record = this.systemPreferenceStore.findRecord("preferenceKey", key); 404 value = Ext.encode(value); 405 406 if (record) { 407 if (record.get("preferenceValue") != value) { 408 record.set("preferenceValue", value); 409 record.save(); 410 } 411 } else { 412 var j = new PartKeepr.SystemPreferenceBundle.Entity.SystemPreference(); 413 j.set("preferenceKey", key); 414 j.set("preferenceValue", value); 415 j.save(); 416 this.systemPreferenceStore.add(j); 417 } 418 }, 419 /** 420 * Queries for a specific user preference. Returns either the value or a default value if 421 * the preference was not found. 422 * @param key The key to query 423 * @param defaultValue A default value to return (optional) 424 * @returns the key value, or defaultValue if preference key was not found 425 */ 426 getUserPreference: function (key, defaultValue) 427 { 428 var record = this.userPreferenceStore.findRecord("preferenceKey", key); 429 430 if (record) { 431 var value = record.get("preferenceValue"); 432 var decodedValue = Ext.decode(value, true); 433 434 if (decodedValue === null) { 435 return value; 436 } else { 437 return decodedValue; 438 } 439 } else { 440 return (typeof defaultValue == "undefined") ? null : defaultValue; 441 } 442 }, 443 /** 444 * Sets a specific user preference. Directly commits the change to the server. 445 * 446 * @param key The key to set 447 * @param value The value to set 448 */ 449 setUserPreference: function (key, value) 450 { 451 var record = this.userPreferenceStore.findRecord("preferenceKey", key); 452 value = Ext.encode(value); 453 454 if (record) { 455 if (record.get("preferenceValue") != value) { 456 record.set("preferenceValue", value); 457 record.save(); 458 } 459 } else { 460 var j = new PartKeepr.AuthBundle.Entity.UserPreference(); 461 j.set("preferenceKey", key); 462 j.set("preferenceValue", value); 463 j.save(); 464 this.userPreferenceStore.add(j); 465 } 466 }, 467 getUserPreferenceStore: function () 468 { 469 return this.userPreferenceStore; 470 }, 471 getUnitStore: function () 472 { 473 return this.unitStore; 474 }, 475 getPartUnitStore: function () 476 { 477 return this.partUnitStore; 478 }, 479 getFootprintStore: function () 480 { 481 return this.footprintStore; 482 }, 483 getManufacturerStore: function () 484 { 485 return this.manufacturerStore; 486 }, 487 getDistributorStore: function () 488 { 489 return this.distributorStore; 490 }, 491 getCurrencyStore: function () 492 { 493 return this.currencyStore; 494 }, 495 getDefaultPartUnit: function () 496 { 497 return this.partUnitStore.findRecord("default", true); 498 }, 499 getUserStore: function () 500 { 501 return this.userStore; 502 }, 503 getSiPrefixStore: function () 504 { 505 return this.siPrefixStore; 506 }, 507 uploadFileFromURL: function (url, description, callback, scope) 508 { 509 var uploadURL = PartKeepr.getBasePath() + "/api/temp_uploaded_files/upload"; 510 511 var options = { 512 url: uploadURL, 513 method: 'POST', 514 params: { 515 description: description, 516 url: url 517 }, 518 callback: callback, 519 scope: scope 520 }; 521 522 Ext.Ajax.request(options); 523 }, 524 /** 525 * Converts the Character "micro" (µ, available on german keyboards via AltGr+m) to the Character "Mu" (μ). 526 * 527 * The standard for Si-Prefixes defines that the "Mu"-character should be used instead of the "micro" character. 528 * 529 * Wikipedia Entry for the "Micro" Si Prefix: http://en.wikipedia.org/wiki/Micro- 530 * 531 */ 532 convertMicroToMu: function (value) 533 { 534 /** 535 * Since the Si-Prefix for "micro" is μ, but keyboard have "µ" on it 536 * (note: both chars might look identical, depending on your font), we need 537 * to convert "µ" (on the keyboard, Unicode U+00B5) to the Mu (U+03BC). 538 */ 539 540 return str_replace("µ", "μ", value); 541 }, 542 /** 543 * Creates the main view of PartKeepr. 544 */ 545 createLayout: function () 546 { 547 this.statusBar = Ext.create("PartKeepr.Statusbar"); 548 549 this.messageLog = this.createMessageLog(); 550 551 this.centerPanel = Ext.create("Ext.tab.Panel", { 552 xtype: 'tabpanel', 553 border: false, 554 region: 'center', 555 bodyStyle: 'background:#DBDBDB', 556 plugins: Ext.create('Ext.ux.TabCloseMenu') 557 558 }); 559 560 561 this.menuBar = Ext.create("PartKeepr.MenuBar"); 562 563 this.menuBar.disable(); 564 this.viewPort = Ext.create('Ext.container.Viewport', { 565 layout: 'fit', 566 items: [ 567 { 568 xtype: 'panel', 569 border: false, 570 layout: 'border', 571 items: [ 572 this.centerPanel, 573 this.messageLog 574 ], 575 bbar: this.statusBar, 576 tbar: this.menuBar 577 } 578 ] 579 580 }); 581 }, 582 addItem: function (item) 583 { 584 this.centerPanel.add(item); 585 }, 586 createMessageLog: function () 587 { 588 return Ext.create("PartKeepr.MessageLog", { 589 height: 200, 590 hidden: true, 591 split: true, 592 title: i18n("Message Log"), 593 titleCollapse: true, 594 collapsible: true, 595 region: 'south', 596 listeners: { 597 beforecollapse: Ext.bind( 598 function (obj) 599 { 600 this.hideMessageLog(); 601 return false; 602 }, 603 this) 604 } 605 }); 606 }, 607 log: function (message) 608 { 609 this.logMessage(message, "none"); 610 }, 611 logMessage: function (message, severity) 612 { 613 if (message != i18n("Ready.")) { 614 var r = Ext.ModelManager.create({ 615 message: message, 616 severity: severity, 617 date: new Date() 618 }, 'PartKeepr.Message'); 619 620 this.messageLog.getStore().add(r); 621 } 622 }, 623 hideMessageLog: function () 624 { 625 this.messageLog.hide(); 626 }, 627 showMessageLog: function () 628 { 629 this.messageLog.show(); 630 }, 631 toggleMessageLog: function () 632 { 633 if (this.messageLog.isHidden()) { 634 this.showMessageLog(); 635 } else { 636 this.hideMessageLog(); 637 } 638 639 }, 640 getStatusbar: function () 641 { 642 return this.statusBar; 643 }, 644 /** 645 * Sets the username. This should only be called from the login dialog. 646 * 647 * Also updates the statusbar to reflect the username. 648 * 649 * @param {string} username The username to set 650 */ 651 setUsername: function (username) 652 { 653 this.username = username; 654 this.getStatusbar().setCurrentUser(username); 655 }, 656 /** 657 * Returns the current username 658 * @returns {string} 659 */ 660 getUsername: function () 661 { 662 return this.username; 663 }, 664 formatCurrency: function (value, code) 665 { 666 var format = Ext.util.Format; 667 format.currencyPrecision = PartKeepr.getApplication().getUserPreference( 668 "partkeepr.formatting.currency.numdecimals", 2); 669 670 format.currencySign = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.symbol", "€"); 671 672 if (code !== null) { 673 var currency = PartKeepr.getApplication().getCurrencyStore().findRecord("code", code, 0, false, false, 674 true); 675 676 if (currency !== null) { 677 format.currencySign = currency.get("symbol"); 678 } 679 } 680 681 format.currencyAtEnd = PartKeepr.getApplication().getUserPreference( 682 "partkeepr.formatting.currency.currencySymbolAtEnd", true); 683 684 if (PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.thousandsSeparator", 685 true) === true) { 686 // @todo This is hard-coded for now 687 format.thousandSeparator = ","; 688 } else { 689 format.thousandSeparator = ""; 690 } 691 692 return format.currency(value); 693 }, 694 switchTheme: function (theme) 695 { 696 if (window.themes[theme]) { 697 window.theme = theme; 698 this.setUserPreference("partkeepr.user.theme", theme); 699 this.menuBar.selectTheme(theme); 700 Ext.util.CSS.swapStyleSheet("theme", window.themes[theme].themeUri); 701 Ext.util.CSS.swapStyleSheet("themeUx", window.themes[theme].themeUxUri); 702 703 Ext.get("loader-wrapper").show(); 704 Ext.get("loader-message").setHtml(i18n("Applying theme…")); 705 706 707 Ext.defer(this.updateThemeLayout, 1000, this); 708 } 709 }, 710 updateThemeLayout: function () 711 { 712 Ext.get("loader-wrapper").hide(); 713 714 Ext.defer(this.refreshLayout, 100, this); 715 }, 716 refreshLayout: function () { 717 this.viewPort.updateLayout(); 718 } 719 }); 720 721 PartKeepr.getSession = function () 722 { 723 alert("This should not be called."); 724 return "hli2ong0ktnise68p9f5nu6nk1"; 725 }; 726 727 PartKeepr.log = function (message) 728 { 729 PartKeepr.getApplication().log(message); 730 }; 731 732 /** 733 * <p>This static method returns the instance of the application.</p> 734 * @return {PartKeepr} The application 735 */ 736 PartKeepr.getApplication = function () 737 { 738 return PartKeepr.application; 739 }; 740 741 PartKeepr.getBasePath = function () 742 { 743 var href = document.getElementsByTagName('base')[0].href; 744 745 746 if (href.substr(-2) === '//') { 747 return href.substr(0, href.length - 2); 748 } 749 750 if (href.substr(-1) === '/') { 751 return href.substr(0, href.length - 1); 752 } 753 754 return href; 755 }; 756 757 PartKeepr.getImagePath = function () 758 { 759 return "image.php"; 760 }; 761 762 PartKeepr.setMaxUploadSize = function (size) 763 { 764 PartKeepr.maxUploadSize = size; 765 }; 766 767 PartKeepr.getMaxUploadSize = function () 768 { 769 return PartKeepr.maxUploadSize; 770 }; 771 772 PartKeepr.setOctoPartAvailable = function (octoPartAvailable) 773 { 774 PartKeepr.octoPartAvailable = octoPartAvailable; 775 }; 776 777 PartKeepr.isOctoPartAvailable = function () 778 { 779 return PartKeepr.octoPartAvailable; 780 }; 781 782 PartKeepr.bytesToSize = function (bytes) 783 { 784 var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; 785 if (bytes === 0) { 786 return '0 Bytes'; 787 } 788 var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10); 789 return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]; 790 }; 791 792 PartKeepr.setAvailableImageFormats = function (formats) 793 { 794 PartKeepr.imageFormats = formats; 795 }; 796 797 PartKeepr.getAvailableImageFormats = function () 798 { 799 return PartKeepr.imageFormats; 800 }; 801 802 PartKeepr.serializeRecords = function (records) 803 { 804 var finalData = []; 805 806 for (var i = 0; i < records.length; i++) { 807 finalData.push(records[i].data); 808 } 809 810 return finalData; 811 };