diff --git a/src/auth.js b/src/auth.js index 05c7fd4b3e2d776190d4ed95354e44284e7a7330..e9d3e447036fe3fc06650942826f33ff833fbdb5 100644 --- a/src/auth.js +++ b/src/auth.js @@ -102,7 +102,12 @@ export class ResourceHandler { checkAuthenticated(); } - // definitions of query parameters in addition to API go here + /* + * query is a dictionary of different queries + * Additional to anything specified from eve + * (http://python-eve.org/features.html#filtering) + * we support the key `search`, which is translated into a `where` filter + */ buildQuerystring(query) { const queryKeys = Object.keys(query); @@ -112,10 +117,15 @@ export class ResourceHandler { if ('search' in query && query.search.length > 0) { // translate search into where, we just look if any field contains search + // The search-string may match any of the keys in the object specified in the + // constructor const searchQuery = { $or: this.searchKeys.map((key) => { const fieldQuery = {}; - fieldQuery[key] = query.search; + fieldQuery[key] = { + $regex: `${query.search}`, + $options: 'i' + }; return fieldQuery; }), }; diff --git a/src/groups/editGroup.js b/src/groups/editGroup.js index 30a8d674ff427f405c1befa6c0ebff2ab116b3cc..d6d4317db931f83cc5c5bcd48c27570d1be0b3fa 100644 --- a/src/groups/editGroup.js +++ b/src/groups/editGroup.js @@ -30,6 +30,10 @@ export default class NewGroup extends EditView { type: 'checkbox', label: 'the group can be seen by all users and they can subscribe themselves', }, + requires_storage: { + type: 'checkbox', + label: "the group shares a folder with it's members in the AMIV Cloud", + } }), m('div', { style: { display: 'flex' } }, [ m(TextField, { label: 'Group Moderator: ', disabled: true, style: { width: '160px' } }), diff --git a/src/groups/viewGroup.js b/src/groups/viewGroup.js index d41ac4b40c29320c375833c4c9ae9858eb495e38..f95ef8f1c688464e51e123faa3df871cc32c9348 100644 --- a/src/groups/viewGroup.js +++ b/src/groups/viewGroup.js @@ -1,5 +1,14 @@ import m from 'mithril'; -import { Button, RaisedButton, Card, Toolbar, ToolbarTitle, TextField, Icon } from 'polythene-mithril'; +import { + Button, + RaisedButton, + Card, + Toolbar, + ToolbarTitle, + TextField, + Icon, + IconButton +} from 'polythene-mithril'; import { icons, Property } from '../views/elements'; import ItemView from '../views/itemView'; import TableView from '../views/tableView'; @@ -66,13 +75,21 @@ class MembersTable { onCancel: () => { this.addmode = false; m.redraw(); }, selectedText: user => `${user.firstname} ${user.lastname}`, }) : '', + m(Toolbar, { compact: true }, [ + m(ToolbarTitle, { text: 'Members' }), + m(Button, { + className: 'blue-button', + borders: true, + label: 'add', + events: { onclick: () => { this.addmode = true; } }, + }), + ]), m(TableView, { - tableHeight: '420px', + tableHeight: '375px', controller: this.ctrl, keys: ['user.lastname', 'user.firstname', 'user.email'], tileContent: data => this.itemRow(data), clickOnRows: false, - onAdd: () => { this.addmode = true; }, titles: [ { text: 'Name', width: '18em' }, { text: 'Email', width: '9em' }, @@ -173,13 +190,17 @@ export default class viewGroup extends ItemView { title: 'Moderator', onclick: () => { m.route.set(`/users/${this.data.moderator._id}`); }, }, `${this.data.moderator.firstname} ${this.data.moderator.lastname}`) : '', + this.data.requires_storage ? m(IconButton, { + label: 'has a folder on the AMIV Cloud', + style: { color: '#ffffff', backgroundColor: 'orange' }, + icon: { svg: { content: m.trust(icons.cloud) } }, + inactive: true, + compact: true, + }): '', ]), m('div.viewcontainer', [ // now-column layout: This first column are the members - m('div.viewcontainercolumn', [ - m('h4', 'Members'), - m(MembersTable, { group: this.data._id }), - ]), + m('div.viewcontainercolumn', m(MembersTable, { group: this.data._id })), // the second column contains receive_from and forward_to emails m('div.viewcontainercolumn', [ m(EmailTable, { diff --git a/src/layout.js b/src/layout.js index 40c9b7f0f97c4803875eae8ac178e2a4ed08c91d..e2859a16691e2ed6f39916012d56e239a5b26208 100644 --- a/src/layout.js +++ b/src/layout.js @@ -47,14 +47,8 @@ styler.add('layout', layoutStyle); class Menupoint { view({ attrs: { title, href, icon = null } }) { return m(ListTile, { - url: { - href, - oncreate: m.route.link, - }, - front: icon ? m(Icon, { - avatar: true, - svg: m.trust(icon), - }) : '', + url: { href, oncreate: m.route.link }, + front: icon ? m(Icon, { svg: m.trust(icon) }) : '', title, }); } diff --git a/src/listcontroller.js b/src/listcontroller.js index 5968d46ee5fe600bbfc518e2edd8994b773d8c3b..0fd7fb942f4fc9444715ed82c777d0c63cc54954 100644 --- a/src/listcontroller.js +++ b/src/listcontroller.js @@ -51,6 +51,7 @@ export default class DatalistController { // embedded keys like user.firstname if (!this.onlineSearch && this.clientSearchKeys.length > 0 && this.search) { const response = []; + const searchRegex = new RegExp(this.search, "i") // We go through all response items and will add them to response if // they match the query. data._items.forEach((item) => { @@ -63,13 +64,13 @@ export default class DatalistController { key.split('.').forEach((subKey) => { intermediateObject = intermediateObject[subKey]; }); - if (intermediateObject.includes(this.search)) { + if (intermediateObject.match(searchRegex)) { response.push(item); // return true to end the search of this object, it is already // matched return true; } - } else if (item[key] && item[key].includes(this.search)) { + } else if (item[key] && item[key].match(searchRegex)) { response.push(item); // return true to end the search of this object, it is already // matched diff --git a/src/views/editView.js b/src/views/editView.js index 9c05ed2dd72135034678c0b3fa546102ff2cf4ff..22bd0ec61108b5439b5ee6c377359a4d4d44b6e8 100644 --- a/src/views/editView.js +++ b/src/views/editView.js @@ -16,16 +16,19 @@ const objectNameForResource = { }; export default class EditView extends ItemView { - /* Extension of ItemView to edit a data item + /** + * Extension of ItemView to edit a data item * * Requires: * - call constructor with vnode, resource, (valid, true by default) * - vnode.attrs.onfinish has to be a callback function that is called after * the edit is finished - * - * Provides Methods: - * - bind(attrs): binds a form-field against this.data - * - submit + * @param {object} vnode [as provided by mithril] + * @param {string} resource [the API resource of this view, e.g. 'events'] + * @param {object} embedded [any embedding query that should be added + * to API requests for this resource] + * @param {Boolean} valid [whether the view should be valid before the + * first validation] */ constructor(vnode, valid = true) { super(vnode); @@ -66,7 +69,15 @@ export default class EditView extends ItemView { }).catch((error) => { console.log(error); }); } - // bind form-fields to the object data and validation + /** + * bind form-fields to the object data and validation + * + * A binded form-field automatically updates this.data and calls validation + * on the current data state with every change. + * + * @param {object} options for the input element + * @return {object} modified options passed to the input element + */ bind(attrs) { // initialize error-list for every bound field if (!this.errors[attrs.name]) this.errors[attrs.name] = []; @@ -114,6 +125,17 @@ export default class EditView extends ItemView { m.redraw(); } + /** + * Rendering Function to make form descriptions shorter + * + * @param {object} Collection of descriptions for input form fields + * {key: description} + * with key matching the field in this.data + * description containing type in ['text', 'number', + * 'checkbox', 'datetime'] and any attributes passed to the + * input element + * @return {string} mithril rendered output + */ renderPage(page) { return Object.keys(page).map((key) => { const field = page[key]; @@ -143,6 +165,13 @@ export default class EditView extends ItemView { }); } + /** + * Submit the changed version of this.data + * + * @param {Boolean} true if the data should be send as FormData instead of + * JSON. Necessary in cases where files are included in the + * changes. + */ submit(formData = false) { if (Object.keys(this.data).length > 0) { let request; diff --git a/src/views/elements.js b/src/views/elements.js index db238d05c97c7d777f853de5ef08ef496125aa7f..c5117518db44c75f9670fa8f48b653e7e241ce25 100644 --- a/src/views/elements.js +++ b/src/views/elements.js @@ -19,6 +19,7 @@ export const icons = { ArrowLeft: '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"/><path d="M0-.5h24v24H0z" fill="none"/></svg>', checked: '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>', group: '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>', + cloud: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"/></svg>', }; export class textInput { diff --git a/src/views/tableView.js b/src/views/tableView.js index 2b6805149a96a9bb14e3bfa3d4c0f0b1f696b595..5536126eb9d12fbc5d1616cc0a8b2d108708d3a5 100644 --- a/src/views/tableView.js +++ b/src/views/tableView.js @@ -82,7 +82,7 @@ export default class TableView { attrs: { controller, titles, - onAdd = () => {}, + onAdd = false, tableHeight = false, }, }) { @@ -98,12 +98,12 @@ export default class TableView { }, fullWidth: false, }), - m(Button, { + onAdd ? m(Button, { className: 'blue-button', borders: true, label: 'Add', events: { onclick: () => { onAdd(); } }, - }), + }) : '', ], }), m(List, {