Skip to content

Commit

Permalink
Merge pull request #1692 from girder/app-buttons
Browse files Browse the repository at this point in the history
Add and support app buttons
  • Loading branch information
manthey authored Oct 17, 2024
2 parents 91dcdb7 + 07abdb5 commit e0b10dc
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## 1.30.1

### Improvements

- Support generalized application buttons ([#1692](../../pull/1692))

### Bug Fixes

- Don't use a default for yaml config files except .large_image_config.yaml ([#1685](../../pull/1685))
Expand Down
4 changes: 4 additions & 0 deletions girder/girder_large_image/rest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ def _groupingPipeline(initialPipeline, cbase, grouping, sort=None):
initialPipeline.append({'$set': {'firstOrder': {
'$mergeObjects': ['$firstOrder', centry]}}})
initialPipeline.append({'$replaceRoot': {'newRoot': '$firstOrder'}})
initialPipeline.append({'$set': {'meta._grouping': {
'keys': grouping['keys'],
'values': [f'${key}' for key in grouping['keys']],
}}})


def _itemFindRecursive( # noqa
Expand Down
7 changes: 6 additions & 1 deletion girder/girder_large_image/web_client/templates/itemList.pug
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ ul.g-item-list.li-item-list(layout_mode=(itemList.layout || {}).mode || '', meta
skip = true;
}
});
#{divtype}.li-item-list-cell(class=classes.join(' '), g-item-cid=item.cid, href=item._href ? item._href : `#item/${item.id}`, title=colNames[colidx])
#{divtype}.li-item-list-cell(class=classes.join(' '), g-item-cid=item.cid, href=item._href ? item._href : `#item/${item.id}`, title=colNames[colidx], target=item._href && item._hrefTarget ? item._hrefTarget : undefined)
if !skip && column.label
span.g-item-list-label
= column.label
Expand All @@ -62,6 +62,11 @@ ul.g-item-list.li-item-list(layout_mode=(itemList.layout || {}).mode || '', meta
a.g-view-inline(title="View in browser", target="_blank", rel="noopener noreferrer",
href=item.downloadUrl({contentDisposition: 'inline'}))
i.icon-eye
if availableApps && availableApps.items[item.id]
- const apps = Object.entries(availableApps.items[item.id]).sort(([name1, app1], [name2, app2]) => { let diff = (app1.priority || 0) - (app2.priority || 0); return diff ? diff : (registeredApps[name1].name.toLowerCase() > registeredApps[name2].name.toLowerCase() ? 1 : -1); })
for app in apps
a.g-hui-open-link(title="Open in " + registeredApps[app[0]].name, href=app[1].url, target="_blank")
i.icon-link-ext
else if column.value === 'size'
.g-item-size= formatSize(item.get('size'))
else if column.value === 'description'
Expand Down
84 changes: 74 additions & 10 deletions girder/girder_large_image/web_client/views/itemList.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ wrap(HierarchyWidget, 'render', function (render) {
} else {
this.$('.li-flatten-item-list').removeClass('hidden');
}

const updateChecked = () => {
// const resources = this._getCheckedResourceParam();
// TODO: handle checked resources for apps
};

this.listenTo(this.itemListView, 'g:checkboxesChanged', updateChecked);
this.listenTo(this.folderListView, 'g:checkboxesChanged', updateChecked);
});

wrap(FolderListWidget, 'checkAll', function (checkAll, checked) {
Expand Down Expand Up @@ -277,13 +285,10 @@ wrap(ItemListWidget, 'render', function (render) {
return true;
}
if (nav.type === 'open') {
// TODO: handle open type
// we probably need to get all the grouped items to pass them to
// the .open-in-volview button via that _getCheckedResourceParam
// call OR modify the volview plugin to have an open item with less
// context. The current folder context would ideally be the
// deepest common parent rather than our current folder. Where
// does volview store its zip file?
if (item._href) {
window.open(item._href, '_blank');
return true;
}
}
return false;
};
Expand Down Expand Up @@ -447,13 +452,38 @@ wrap(ItemListWidget, 'render', function (render) {
}
};

this.checkApps = (resources) => {
const items = this.collection.models;
const folders = [this.parentView.parentModel];
const canHandle = {items: {}, folders: {}};
// TODO: handle checked resources
Object.entries(ItemListWidget.registeredApplications).forEach(([appname, app]) => {
items.forEach((item) => {
const check = app.check('item', item, this.parentView.parentModel);
if (check) {
canHandle.items[item.id] = canHandle.items[item.id] || {};
canHandle.items[item.id][appname] = check;
}
});
folders.forEach((folder) => {
const check = app.check('item', folder, this.parentView.parentModel);
if (check) {
canHandle.folders[folder.id] = canHandle.folders[folder.id] || {};
canHandle.folders[folder.id][appname] = check;
}
});
});
return canHandle;
};

/**
* For each item in the collection, if we are navigating to something other
* than the item, set an href property.
*/
function adjustItemHref() {
function adjustItemHref(availableApps) {
this.collection.forEach((item) => {
item._href = undefined;
item._hrefTarget = undefined;
});
const list = this._confList();
const nav = (list || {}).navigate;
Expand Down Expand Up @@ -490,8 +520,23 @@ wrap(ItemListWidget, 'render', function (render) {
item._href += '&filter=' + encodeURIComponent(filter);
}
});
} else if (nav.type === 'open') {
this.collection.forEach((item) => {
let apps = availableApps.items[item.id];
let app;
if (nav.name && apps[nav.name]) {
app = apps[nav.name];
}
if (!app) {
apps = Object.entries(apps).sort(([name1, app1], [name2, app2]) => { const diff = (app1.priority || 0) - (app2.priority || 0); return diff || (ItemListWidget.registeredApplications[name1].name.toLowerCase() > ItemListWidget.registeredApplications[name2].name.toLowerCase() ? 1 : -1); });
app = apps[0][1];
}
if (app.url && app.url !== true) {
item._href = app.url;
item._hrefTarget = '_blank';
}
});
}
// TODO: handle nav.type open
}

function itemListRender() {
Expand Down Expand Up @@ -537,7 +582,8 @@ wrap(ItemListWidget, 'render', function (render) {
this._setSort();
return;
}
adjustItemHref.call(this);
const availableApps = this.checkApps();
adjustItemHref.call(this, availableApps);
this.$el.html(ItemListTemplate({
items: this.collection.toArray(),
isParentPublic: this.public,
Expand All @@ -556,6 +602,8 @@ wrap(ItemListWidget, 'render', function (render) {
sort: this._lastSort,
MetadatumWidget: MetadatumWidget,
accessLevel: this.accessLevel,
registeredApps: ItemListWidget.registeredApplications,
availableApps: availableApps,
parentView: this,
AccessType: AccessType
}));
Expand Down Expand Up @@ -694,4 +742,20 @@ function itemListMetadataEdit(evt) {
return false;
}

/**
* This is a dictionary where the key is the unique application identified.
* Each dictionary contains
* name: the display name
* icon: an optional url to an icon to display
* check: a method that takes (modelType, model, currentFolder) where
* modelType is either 'item', 'folder', or 'resource' and model is a
* bootstrap model or a resource dictionary of models. The function
* returns an object with {url: <url>, priority: <integer>, open:
* <function>} where url is the url to open if possible, the open function
* is a method to call to open the model. Priority affects the order that
* the open calls are listed in (lower is earlier). Return undefined or
* false if the application cannot open this model.
*/
ItemListWidget.registeredApplications = {};

export default ItemListWidget;

0 comments on commit e0b10dc

Please sign in to comment.