Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions etc/agrammon.yaml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,31 @@ Model:
technical: technical.cfg
variant: SHL
version: "6.0 - #REV#"

# Sibling model versions reachable from this deployment.
# Each entry: { label, url, version, title }
# - label short string shown in the dropdown
# - url where to navigate when this version is picked
# - version matches the Model.version of the sibling instance;
# the entry whose version equals THIS process's
# Model.version is the active one. Switching to a strictly
# older version triggers a confirm dialog in the GUI.
# - title per-locale title shown in the GUI header for THIS version.
# The active entry's title overrides GUI.title.
# Order should be newest first.
# Leave the block out (or empty) to hide the dropdown.
Versions:
- label: '7.0'
url: /single/v7
version: '7.0.0'
title:
de: AGRAMMON 7.0 Einzelbetriebsmodell
en: AGRAMMON 7.0 Single Farm Model
fr: AGRAMMON 7.0 modèle Exploitation individuelle
- label: '6.6'
url: /single/v6
version: '6.6.0'
title:
de: AGRAMMON 6.6 Einzelbetriebsmodell
en: AGRAMMON 6.6 Single Farm Model
fr: AGRAMMON 6.6 modèle Exploitation individuelle
5 changes: 5 additions & 0 deletions frontend/source/class/agrammon/Application.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,18 @@ qx.Class.define('agrammon.Application', {

navbar.setVariant(data.modelVariant);
agrammon.module.dataset.DatasetTable.getInstance().setVariant(data.variant);
agrammon.module.dataset.DatasetTable.getInstance().setActiveVersion(data.version);
mainMenu.setTitle(data.title, data.version);
// setVersions overrides the title with the entry that
// matches data.version (if a Versions block is configured).
mainMenu.setVersions(data.versions, data.version);
let info = agrammon.Info.getInstance();
info.setVersion(data.version);
info.setVariant(data.variant);
info.setGuiVariant(data.guiVariant);
info.setModelVariant(data.modelVariant);
info.setTitle(data.title);
info.setVersions(data.versions);
info.setSubmissionAddresses(data.submission);
qx.event.message.Bus.dispatchByName('agrammon.Info.setModelVariant', data.modelVariant);

Expand Down
5 changes: 4 additions & 1 deletion frontend/source/class/agrammon/Info.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ qx.Class.define( 'agrammon.Info',
},
submissionAddresses: {
nullable: true
}
},
versions: { init: null,
nullable: true
}
},

members :
Expand Down
31 changes: 27 additions & 4 deletions frontend/source/class/agrammon/module/dataset/DatasetTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,12 @@ qx.Class.define('agrammon.module.dataset.DatasetTable', {
{
variant: { init: null,
check: "String"
}
},
activeVersion: { init: null,
nullable: true,
check: "String",
apply: "_applyActiveVersion"
}
},

members :
Expand All @@ -235,11 +240,19 @@ qx.Class.define('agrammon.module.dataset.DatasetTable', {
__btnClearReference: null,

__table: null,
__rowRenderer: null,
__searchTimer: null,
__searchFilter: null,
__filterHash: null,
__datasetStore: null,

_applyActiveVersion: function(value) {
if (this.__rowRenderer) {
this.__rowRenderer.setActiveVersion(value || '');
if (this.__table) this.__table.updateContent();
}
},

__searchTimeout: 250, // timeout after which SearchAsYouType view is updated
__searchColumn: 0, // Dataset name
__buttonRow: null,
Expand Down Expand Up @@ -558,7 +571,15 @@ qx.Class.define('agrammon.module.dataset.DatasetTable', {
padding: 0,
showCellFocusIndicator: false
});
table.getDataRowRenderer().setHighlightFocusRow(false);

// Custom row renderer fades rows whose dataset version doesn't
// match the active model version. Column 4 holds the version.
this.__rowRenderer = new agrammon.ui.table.rowrenderer.DatasetVersion(4);
this.__rowRenderer.setHighlightFocusRow(false);
if (this.getActiveVersion()) {
this.__rowRenderer.setActiveVersion(this.getActiveVersion());
}
table.setDataRowRenderer(this.__rowRenderer);

table.getSelectionModel().setSelectionMode(qx.ui.table.selection.Model.MULTIPLE_INTERVAL_SELECTION);
var tcm = table.getTableColumnModel();
Expand All @@ -572,8 +593,10 @@ qx.Class.define('agrammon.module.dataset.DatasetTable', {
tcmb.setWidth(this.__commentColumn,70);

tcm.setColumnVisible(3,true);
tcm.setColumnVisible(4,false);
tcm.setColumnVisible(4,false);
// Version column is visible so users can see which model
// version each dataset belongs to (the row is faded if it
// doesn't match the active version).
tcm.setColumnVisible(4,true);
// FIX ME: Column 5 must not be made visible, because it
// has an array as value. Needs a specific cell renderer!
tcm.setColumnVisible(5,false);
Expand Down
24 changes: 24 additions & 0 deletions frontend/source/class/agrammon/ui/menu/MainMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ qx.Class.define('agrammon.ui.menu.MainMenu', {
editButton.setMenu(editMenu);

var optionMenu = new agrammon.ui.menu.OptionMenu();
this.__optionMenu = optionMenu;
var optionButton = new qx.ui.menubar.Button(this.tr("Options"));
optionButton.setMenu(optionMenu);

Expand Down Expand Up @@ -68,6 +69,7 @@ qx.Class.define('agrammon.ui.menu.MainMenu', {
this.addSpacer();
this.add(title);
this.addSpacer();

this.add(info);

return;
Expand All @@ -79,6 +81,7 @@ qx.Class.define('agrammon.ui.menu.MainMenu', {
__adminMenu: null,
__adminButton: null,
__editButton: null,
__optionMenu: null,
__title: null, // the Label widget
__titles: null, // multi-lingual hash of title values
__tooltip: null,
Expand Down Expand Up @@ -106,6 +109,27 @@ qx.Class.define('agrammon.ui.menu.MainMenu', {

},

/**
* Forward the Versions block to the OptionMenu (which owns the
* "Set model version ..." submenu) and override the page title
* from the matching entry, if any.
*
* @param versions {Array} list of { label, url, version, title }
* @param activeVersion {String} Model.version string of THIS process
*/
setVersions: function(versions, activeVersion) {
this.__optionMenu.setVersions(versions, activeVersion);

if (versions && versions.length) {
for (var i = 0; i < versions.length; i++) {
if (versions[i].version === activeVersion && versions[i].title) {
this.setTitle(versions[i].title, activeVersion);
break;
}
}
}
},

__enableEdit: function(msg) {
this.__editButton.setEnabled(msg.getData());
},
Expand Down
103 changes: 103 additions & 0 deletions frontend/source/class/agrammon/ui/menu/OptionMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ qx.Class.define('agrammon.ui.menu.OptionMenu', {
var langButton = new qx.ui.menu.Button(this.tr("Set language ..."),
null, null, langMenu);

// Model-version submenu. Hidden until setVersions() is called with
// a non-empty Versions block; populated lazily so labels match the
// current config and the active entry is disabled.
var versionMenu = new qx.ui.menu.Menu;
var versionButton = new qx.ui.menu.Button(this.tr("Set model version ..."),
null, null, versionMenu);
versionButton.exclude();
this.__versionMenu = versionMenu;
this.__versionButton = versionButton;

var passwordDialog =
new qx.ui.window.Window(this.tr("Changing password for ")
+ username,
Expand Down Expand Up @@ -146,6 +156,7 @@ qx.Class.define('agrammon.ui.menu.OptionMenu', {
var passwordButton = new qx.ui.menu.Button(this.tr("Change password"),
null, passwordCommand);
this.add(langButton);
this.add(versionButton);
this.add(passwordButton);

return;
Expand All @@ -156,6 +167,98 @@ qx.Class.define('agrammon.ui.menu.OptionMenu', {
{
__rpc: null,
__info: null,
__versionMenu: null,
__versionButton: null,
__activeVersion: null,

/**
* Populate the model-version submenu.
*
* @param versions {Array} list of { label, url, version, title }
* @param activeVersion {String} Model.version string of THIS process
*/
setVersions: function(versions, activeVersion) {
this.__activeVersion = activeVersion;
this.__versionMenu.removeAll();

if (!versions || !versions.length) {
this.__versionButton.exclude();
return;
}

for (var i = 0; i < versions.length; i++) {
var v = versions[i];
var btn = new qx.ui.menu.Button(v.label);
btn.setUserData('versionEntry', v);
if (v.version === activeVersion) {
// Can't switch to ourselves — make it visually obvious
// and unclickable.
btn.setEnabled(false);
}
else {
btn.addListener('execute', this.__onVersionPick, this);
}
this.__versionMenu.add(btn);
}
this.__versionButton.show();
},

__onVersionPick: function(e) {
var entry = e.getTarget().getUserData('versionEntry');
if (!entry) return;

if (this.__isOlder(entry.version, this.__activeVersion)) {
var prevActive = this.__activeVersion;
var dialog = new agrammon.ui.dialog.Confirm(
this.tr("Switch to older model version?"),
this.tr("You are about to switch from version %1 to the older version %2. Older model versions may not understand datasets created in a newer one. Continue?",
prevActive, entry.version),
function () {
dialog.close();
window.location.assign(entry.url);
},
this,
false
);
dialog.open();
}
else {
window.location.assign(entry.url);
}
},

/**
* true iff `a` is strictly older than `b`.
*
* Only pure dotted-numeric versions (e.g. "6.6.0") are accepted.
* Pre-release tags (-rc1, -alpha), build metadata (+build),
* "v" prefixes and the like are not parsed: callers get a console
* warning and the version is treated as not-older, so navigation
* proceeds without the downgrade-confirm dialog.
*/
__isOlder: function(a, b) {
if (!a || !b) return false;
var pa = this.__parseVersion(a);
var pb = this.__parseVersion(b);
if (pa === null || pb === null) return false;
var n = Math.max(pa.length, pb.length);
for (var i = 0; i < n; i++) {
var x = pa[i] || 0, y = pb[i] || 0;
if (x < y) return true;
if (x > y) return false;
}
return false;
},

/** Returns Array<Number> for "1.2.3", or null (and warns) otherwise. */
__parseVersion: function(v) {
var s = String(v);
if (!/^\d+(\.\d+)*$/.test(s)) {
this.warn("OptionMenu: version string '" + s + "' is not pure dotted-numeric; downgrade comparison disabled for it.");
return null;
}
return s.split('.').map(Number);
},

__changePassword: function(data, exc, id) {
if (exc == null && ! data.error) {
Expand Down
Loading
Loading