diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000000000000000000000000000000000..b86372955e42a15f043d2ff55340d6475c713bfe --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,12 @@ +module.exports = { + globals: { + __webpack_nonce__: true, + __webpack_public_path__: true, + _: true, + $: true, + moment: true, + escapeHTML: true, + oc_userconfig: true + }, + extends: ['nextcloud'] +} diff --git a/Makefile b/Makefile index 6707760263499ea4bb64c3c23550c79bd3ae53e6..a3e1d4eab6221da6109a2c0ea10b6a2650b26a5d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ all: clean dev-setup build-js-production -dev-setup: clean-dev npm-init +# Dev env management +dev-setup: clean clean-dev npm-init npm-init: npm install @@ -8,6 +9,7 @@ npm-init: npm-update: npm update +# Building build-js: npm run dev @@ -17,9 +19,14 @@ build-js-production: watch-js: npm run watch -clean-dev: - rm -rf node_modules +# Linting +lint-fix: + npm run lint:fix + +lint-fix-watch: + npm run lint:fix-watch +# Cleaning clean: rm -rf apps/accessibility/js/ rm -rf apps/comments/js/ @@ -27,12 +34,15 @@ clean: rm -rf apps/files_trashbin/js/ rm -rf apps/files_versions/js/ rm -rf apps/oauth2/js/ + rm -rf apps/settings/js/vue-* rm -rf apps/systemtags/js/systemtags.* rm -rf apps/twofactor_backupcodes/js rm -rf apps/updatenotification/js/updatenotification.* rm -rf apps/workflowengine/js/ rm -rf core/js/dist - rm -rf settings/js/vue-* + +clean-dev: + rm -rf node_modules clean-git: clean git checkout -- apps/accessibility/js/ @@ -41,9 +51,9 @@ clean-git: clean git checkout -- apps/files_trashbin/js/ git checkout -- apps/files_versions/js/ git checkout -- apps/oauth2/js/ + git checkout -- apps/settings/js/vue-* git checkout -- apps/systemtags/js/systemtags.* git checkout -- apps/twofactor_backupcodes/js git checkout -- apps/updatenotification/js/updatenotification.* git checkout -- apps/workflowengine/js/ git checkout -- core/js/dist - git checkout -- settings/js/vue-* diff --git a/apps/accessibility/.eslintrc.js b/apps/accessibility/.eslintrc.js deleted file mode 100644 index 2cc3899a0c6b96ae6d220510427928005e3b759b..0000000000000000000000000000000000000000 --- a/apps/accessibility/.eslintrc.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - env: { - browser: true, - es6: true - }, - extends: 'eslint:recommended', - parserOptions: { - sourceType: 'module' - }, - rules: { - indent: ['error', 'tab'], - 'linebreak-style': ['error', 'unix'], - quotes: ['error', 'single'], - semi: ['error', 'always'] - } -}; diff --git a/apps/accessibility/js/accessibility.js b/apps/accessibility/js/accessibility.js index 6e0b9c3958d033ee8a1db39993595e7fde1ff40e..af132e1a3c62a082b742fcd68345f62698cf0bf0 100644 Binary files a/apps/accessibility/js/accessibility.js and b/apps/accessibility/js/accessibility.js differ diff --git a/apps/accessibility/js/accessibility.js.map b/apps/accessibility/js/accessibility.js.map index cafc3dd859483568f14294f923b58816ac69736a..51fc6eb16d2a8bd982e014884efd51e41602edac 100644 Binary files a/apps/accessibility/js/accessibility.js.map and b/apps/accessibility/js/accessibility.js.map differ diff --git a/apps/accessibility/src/App.vue b/apps/accessibility/src/Accessibility.vue similarity index 60% rename from apps/accessibility/src/App.vue rename to apps/accessibility/src/Accessibility.vue index 7fdc54af664bc86fc88014bfcdac832e74453598..56592103d564c0201906aa553e5a5e94fc29f480 100644 --- a/apps/accessibility/src/App.vue +++ b/apps/accessibility/src/Accessibility.vue @@ -1,60 +1,56 @@ <template> <div id="accessibility" class="section"> - <h2>{{t('accessibility', 'Accessibility')}}</h2> + <h2>{{ t('accessibility', 'Accessibility') }}</h2> <p v-html="description" /> <p v-html="descriptionDetail" /> <div class="preview-list"> - <preview :preview="highcontrast" - :key="highcontrast.id" :selected="selected.highcontrast" - v-on:select="selectHighContrast"></preview> - <preview v-for="preview in themes" :preview="preview" - :key="preview.id" :selected="selected.theme" - v-on:select="selectTheme"></preview> - <preview v-for="preview in fonts" :preview="preview" - :key="preview.id" :selected="selected.font" - v-on:select="selectFont"></preview> + <ItemPreview :key="highcontrast.id" + :preview="highcontrast" + :selected="selected.highcontrast" + @select="selectHighContrast" /> + <ItemPreview v-for="preview in themes" + :key="preview.id" + :preview="preview" + :selected="selected.theme" + @select="selectTheme" /> + <ItemPreview v-for="preview in fonts" + :key="preview.id" + :preview="preview" + :selected="selected.font" + @select="selectFont" /> </div> </div> </template> <script> -import preview from './components/itemPreview'; -import axios from 'nextcloud-axios'; +import ItemPreview from './components/ItemPreview' +import axios from 'nextcloud-axios' export default { name: 'Accessibility', - components: { preview }, - beforeMount() { - // importing server data into the app - const serverDataElmt = document.getElementById('serverData'); - if (serverDataElmt !== null) { - this.serverData = JSON.parse( - document.getElementById('serverData').dataset.server - ); - } - }, + components: { ItemPreview }, data() { return { serverData: [] - }; + } }, computed: { themes() { - return this.serverData.themes; + return this.serverData.themes }, highcontrast() { - return this.serverData.highcontrast; + return this.serverData.highcontrast }, fonts() { - return this.serverData.fonts; + return this.serverData.fonts }, selected() { return { theme: this.serverData.selected.theme, highcontrast: this.serverData.selected.highcontrast, font: this.serverData.selected.font - }; + } }, description() { // using the `t` replace method escape html, we have to do it manually :/ @@ -66,7 +62,7 @@ export default { We aim to be compliant with the {guidelines} 2.1 on AA level, with the high contrast theme even on AAA level.` ) - .replace('{guidelines}', this.guidelinesLink) + .replace('{guidelines}', this.guidelinesLink) }, guidelinesLink() { return `<a target="_blank" href="https://www.w3.org/WAI/standards-guidelines/wcag/" rel="noreferrer nofollow">${t('accessibility', 'Web Content Accessibility Guidelines')}</a>` @@ -77,8 +73,8 @@ export default { `If you find any issues, don’t hesitate to report them on {issuetracker}. And if you want to get involved, come join {designteam}!` ) - .replace('{issuetracker}', this.issuetrackerLink) - .replace('{designteam}', this.designteamLink) + .replace('{issuetracker}', this.issuetrackerLink) + .replace('{designteam}', this.designteamLink) }, issuetrackerLink() { return `<a target="_blank" href="https://github.com/nextcloud/server/issues/" rel="noreferrer nofollow">${t('accessibility', 'our issue tracker')}</a>` @@ -87,19 +83,28 @@ export default { return `<a target="_blank" href="https://nextcloud.com/design" rel="noreferrer nofollow">${t('accessibility', 'our design team')}</a>` } }, + beforeMount() { + // importing server data into the app + const serverDataElmt = document.getElementById('serverData') + if (serverDataElmt !== null) { + this.serverData = JSON.parse( + document.getElementById('serverData').dataset.server + ) + } + }, methods: { selectHighContrast(id) { - this.selectItem('highcontrast', id); + this.selectItem('highcontrast', id) }, selectTheme(id, idSelectedBefore) { - this.selectItem('theme', id); - document.body.classList.remove(idSelectedBefore); + this.selectItem('theme', id) + document.body.classList.remove(idSelectedBefore) if (id) { - document.body.classList.add(id); + document.body.classList.add(id) } }, selectFont(id) { - this.selectItem('font', id); + this.selectItem('font', id) }, /** @@ -110,43 +115,40 @@ export default { * @param {string} id the data of the change */ selectItem(type, id) { - axios.post( - OC.linkToOCS('apps/accessibility/api/v1/config', 2) + type, - { value: id } - ) + axios.post(OC.linkToOCS('apps/accessibility/api/v1/config', 2) + type, { value: id }) .then(response => { - this.serverData.selected[type] = id; + this.serverData.selected[type] = id // Remove old link - let link = document.querySelector('link[rel=stylesheet][href*=accessibility][href*=user-]'); + let link = document.querySelector('link[rel=stylesheet][href*=accessibility][href*=user-]') if (!link) { // insert new css - let link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = OC.generateUrl('/apps/accessibility/css/user-style.css') + '?v=' + new Date().getTime(); - document.head.appendChild(link); + let link = document.createElement('link') + link.rel = 'stylesheet' + link.href = OC.generateUrl('/apps/accessibility/css/user-style.css') + '?v=' + new Date().getTime() + document.head.appendChild(link) } else { // compare arrays if ( - JSON.stringify(Object.values(this.selected)) === - JSON.stringify([false, false]) + JSON.stringify(Object.values(this.selected)) + === JSON.stringify([false, false]) ) { // if nothing is selected, blindly remove the css - link.remove(); + link.remove() } else { // force update - link.href = - link.href.split('?')[0] + - '?v=' + - new Date().getTime(); + link.href + = link.href.split('?')[0] + + '?v=' + + new Date().getTime() } } }) .catch(err => { - console.log(err, err.response); - OC.Notification.showTemporary(t('accessibility', err.response.data.ocs.meta.message + '. Unable to apply the setting.')); - }); + console.error(err, err.response) + OC.Notification.showTemporary(t('accessibility', err.response.data.ocs.meta.message + '. Unable to apply the setting.')) + }) } } -}; +} </script> diff --git a/apps/accessibility/src/components/ItemPreview.vue b/apps/accessibility/src/components/ItemPreview.vue new file mode 100644 index 0000000000000000000000000000000000000000..66f751b37d75d45cb12d1a64ffd1238fe46da0a8 --- /dev/null +++ b/apps/accessibility/src/components/ItemPreview.vue @@ -0,0 +1,40 @@ +<template> + <div :class="{preview: true}"> + <div class="preview-image" :style="{backgroundImage: 'url(' + preview.img + ')'}" /> + <div class="preview-description"> + <h3>{{ preview.title }}</h3> + <p>{{ preview.text }}</p> + <input :id="'accessibility-' + preview.id" + v-model="checked" + type="checkbox" + class="checkbox"> + <label :for="'accessibility-' + preview.id">{{ t('accessibility', 'Enable') }} {{ preview.title.toLowerCase() }}</label> + </div> + </div> +</template> + +<script> +export default { + name: 'ItemPreview', + props: { + preview: { + type: Object, + required: true + }, + selected: { + type: String, + default: null + } + }, + computed: { + checked: { + get() { + return this.selected === this.preview.id + }, + set(checked) { + this.$emit('select', checked ? this.preview.id : false, this.selected) + } + } + } +} +</script> diff --git a/apps/accessibility/src/components/itemPreview.vue b/apps/accessibility/src/components/itemPreview.vue deleted file mode 100644 index 7f4e17b16f1b72fe40b7ad15e746c225ef77f629..0000000000000000000000000000000000000000 --- a/apps/accessibility/src/components/itemPreview.vue +++ /dev/null @@ -1,28 +0,0 @@ -<template> - <div :class="{preview: true}"> - <div class="preview-image" :style="{backgroundImage: 'url(' + preview.img + ')'}"></div> - <div class="preview-description"> - <h3>{{preview.title}}</h3> - <p>{{preview.text}}</p> - <input type="checkbox" class="checkbox" :id="'accessibility-' + preview.id" v-model="checked" /> - <label :for="'accessibility-' + preview.id">{{t('accessibility', 'Enable')}} {{preview.title.toLowerCase()}}</label> - </div> - </div> -</template> - -<script> -export default { - name: 'itemPreview', - props: ['preview', 'selected'], - computed: { - checked: { - get() { - return this.selected === this.preview.id; - }, - set(checked) { - this.$emit('select', checked ? this.preview.id : false, this.selected); - } - } - }, -}; -</script> diff --git a/apps/accessibility/src/main.js b/apps/accessibility/src/main.js index 5d9b2a2d0a8ae4c0bb8f2a8649aed9282f306188..618f4a7355f7ae363af086a9b1a935f356fec358 100644 --- a/apps/accessibility/src/main.js +++ b/apps/accessibility/src/main.js @@ -1,12 +1,12 @@ -import Vue from 'vue'; -import App from './App.vue'; +import Vue from 'vue' +import App from './Accessibility.vue' /* global t */ // bind to window -Vue.prototype.OC = OC; -Vue.prototype.t = t; +Vue.prototype.OC = OC +Vue.prototype.t = t -new Vue({ +export default new Vue({ el: '#accessibility', render: h => h(App) -}); +}) diff --git a/apps/comments/js/comments.js b/apps/comments/js/comments.js index 179f31d95fbdedc45ca0ed3807672a2a4dc27502..0af7b54d60a803d47b9d6eb66e189f7a4304c4ef 100644 Binary files a/apps/comments/js/comments.js and b/apps/comments/js/comments.js differ diff --git a/apps/comments/js/comments.js.map b/apps/comments/js/comments.js.map index f14874b52f40d8b6a51742e7046b8f5ada7a246c..bbd2e6954c199f1d9ce630748ea77ee9520e3ea3 100644 Binary files a/apps/comments/js/comments.js.map and b/apps/comments/js/comments.js.map differ diff --git a/apps/comments/src/activitytabviewplugin.js b/apps/comments/src/activitytabviewplugin.js index b6195b80c457ea2bcbf57a7deaa5236efcd8619b..2afee4c2be53d63062fe9a36549bd56b09a508de 100644 --- a/apps/comments/src/activitytabviewplugin.js +++ b/apps/comments/src/activitytabviewplugin.js @@ -1,4 +1,4 @@ -/* +/** * @author Joas Schilling <coding@schilljs.com> * Copyright (c) 2016 * @@ -18,18 +18,18 @@ * @param {jQuery} $el jQuery handle for this activity * @param {string} view The view that displayes this activity */ - prepareModelForDisplay: function (model, $el, view) { + prepareModelForDisplay: function(model, $el, view) { if (model.get('app') !== 'comments' || model.get('type') !== 'comments') { - return; + return } if (view === 'ActivityTabView') { - $el.addClass('comment'); + $el.addClass('comment') if (model.get('message') && this._isLong(model.get('message'))) { - $el.addClass('collapsed'); - var $overlay = $('<div>').addClass('message-overlay'); - $el.find('.activitymessage').after($overlay); - $el.on('click', this._onClickCollapsedComment); + $el.addClass('collapsed') + var $overlay = $('<div>').addClass('message-overlay') + $el.find('.activitymessage').after($overlay) + $el.on('click', this._onClickCollapsedComment) } } }, @@ -38,22 +38,21 @@ * Copy of CommentsTabView._onClickComment() */ _onClickCollapsedComment: function(ev) { - var $row = $(ev.target); + var $row = $(ev.target) if (!$row.is('.comment')) { - $row = $row.closest('.comment'); + $row = $row.closest('.comment') } - $row.removeClass('collapsed'); + $row.removeClass('collapsed') }, /* * Copy of CommentsTabView._isLong() */ _isLong: function(message) { - return message.length > 250 || (message.match(/\n/g) || []).length > 1; + return message.length > 250 || (message.match(/\n/g) || []).length > 1 } - }; + } +})() -})(); - -OC.Plugins.register('OCA.Activity.RenderingPlugins', OCA.Comments.ActivityTabViewPlugin); +OC.Plugins.register('OCA.Activity.RenderingPlugins', OCA.Comments.ActivityTabViewPlugin) diff --git a/apps/comments/src/app.js b/apps/comments/src/app.js index 547059393a525c399d10cda22992092d7cb952d1..626d7703a3e94746724f55b0935bf47cfde592a8 100644 --- a/apps/comments/src/app.js +++ b/apps/comments/src/app.js @@ -13,8 +13,7 @@ /** * @namespace */ - OCA.Comments = {}; + OCA.Comments = {} } -})(); - +})() diff --git a/apps/comments/src/commentcollection.js b/apps/comments/src/commentcollection.js index a15039cf48475e0838dbd542fb8c73687d98a93e..8e7f9e37d56957d3c2a0e1045082a2ecb47b2a24 100644 --- a/apps/comments/src/commentcollection.js +++ b/apps/comments/src/commentcollection.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2016 * @@ -20,148 +21,147 @@ var CommentCollection = OC.Backbone.Collection.extend( /** @lends OCA.Comments.CommentCollection.prototype */ { - sync: OC.Backbone.davSync, + sync: OC.Backbone.davSync, - model: OCA.Comments.CommentModel, + model: OCA.Comments.CommentModel, - /** + /** * Object type * * @type string */ - _objectType: 'files', + _objectType: 'files', - /** + /** * Object id * * @type string */ - _objectId: null, + _objectId: null, - /** + /** * True if there are no more page results left to fetch * * @type bool */ - _endReached: false, + _endReached: false, - /** + /** * Number of comments to fetch per page * * @type int */ - _limit : 20, + _limit: 20, - /** + /** * Initializes the collection * * @param {string} [options.objectType] object type * @param {string} [options.objectId] object id */ - initialize: function(models, options) { - options = options || {}; - if (options.objectType) { - this._objectType = options.objectType; - } - if (options.objectId) { - this._objectId = options.objectId; - } - }, + initialize: function(models, options) { + options = options || {} + if (options.objectType) { + this._objectType = options.objectType + } + if (options.objectId) { + this._objectId = options.objectId + } + }, - url: function() { - return OC.linkToRemote('dav') + '/comments/' + - encodeURIComponent(this._objectType) + '/' + - encodeURIComponent(this._objectId) + '/'; - }, + url: function() { + return OC.linkToRemote('dav') + '/comments/' + + encodeURIComponent(this._objectType) + '/' + + encodeURIComponent(this._objectId) + '/' + }, - setObjectId: function(objectId) { - this._objectId = objectId; - }, + setObjectId: function(objectId) { + this._objectId = objectId + }, - hasMoreResults: function() { - return !this._endReached; - }, + hasMoreResults: function() { + return !this._endReached + }, - reset: function() { - this._endReached = false; - this._summaryModel = null; - return OC.Backbone.Collection.prototype.reset.apply(this, arguments); - }, + reset: function() { + this._endReached = false + this._summaryModel = null + return OC.Backbone.Collection.prototype.reset.apply(this, arguments) + }, - /** + /** * Fetch the next set of results */ - fetchNext: function(options) { - var self = this; - if (!this.hasMoreResults()) { - return null; - } + fetchNext: function(options) { + var self = this + if (!this.hasMoreResults()) { + return null + } - var body = '<?xml version="1.0" encoding="utf-8" ?>\n' + - '<oc:filter-comments xmlns:D="DAV:" xmlns:oc="http://owncloud.org/ns">\n' + + var body = '<?xml version="1.0" encoding="utf-8" ?>\n' + + '<oc:filter-comments xmlns:D="DAV:" xmlns:oc="http://owncloud.org/ns">\n' // load one more so we know there is more - ' <oc:limit>' + (this._limit + 1) + '</oc:limit>\n' + - ' <oc:offset>' + this.length + '</oc:offset>\n' + - '</oc:filter-comments>\n'; - - options = options || {}; - var success = options.success; - options = _.extend({ - remove: false, - parse: true, - data: body, - davProperties: CommentCollection.prototype.model.prototype.davProperties, - success: function(resp) { - if (resp.length <= self._limit) { + + ' <oc:limit>' + (this._limit + 1) + '</oc:limit>\n' + + ' <oc:offset>' + this.length + '</oc:offset>\n' + + '</oc:filter-comments>\n' + + options = options || {} + var success = options.success + options = _.extend({ + remove: false, + parse: true, + data: body, + davProperties: CommentCollection.prototype.model.prototype.davProperties, + success: function(resp) { + if (resp.length <= self._limit) { // no new entries, end reached - self._endReached = true; - } else { + self._endReached = true + } else { // remove last entry, for next page load - resp = _.initial(resp); - } - if (!self.set(resp, options)) { - return false; + resp = _.initial(resp) + } + if (!self.set(resp, options)) { + return false + } + if (success) { + success.apply(null, arguments) + } + self.trigger('sync', 'REPORT', self, options) } - if (success) { - success.apply(null, arguments); - } - self.trigger('sync', 'REPORT', self, options); - } - }, options); + }, options) - return this.sync('REPORT', this, options); - }, + return this.sync('REPORT', this, options) + }, - /** + /** * Returns the matching summary model * - * @return {OCA.Comments.CommentSummaryModel} summary model + * @returns {OCA.Comments.CommentSummaryModel} summary model */ - getSummaryModel: function() { - if (!this._summaryModel) { - this._summaryModel = new OCA.Comments.CommentSummaryModel({ - id: this._objectId, - objectType: this._objectType - }); - } - return this._summaryModel; - }, + getSummaryModel: function() { + if (!this._summaryModel) { + this._summaryModel = new OCA.Comments.CommentSummaryModel({ + id: this._objectId, + objectType: this._objectType + }) + } + return this._summaryModel + }, - /** + /** * Updates the read marker for this comment thread * * @param {Date} [date] optional date, defaults to now * @param {Object} [options] backbone options */ - updateReadMarker: function(date, options) { - options = options || {}; - - return this.getSummaryModel().save({ - readMarker: (date || new Date()).toUTCString() - }, options); - } - }); + updateReadMarker: function(date, options) { + options = options || {} - OCA.Comments.CommentCollection = CommentCollection; -})(OC, OCA); + return this.getSummaryModel().save({ + readMarker: (date || new Date()).toUTCString() + }, options) + } + }) + OCA.Comments.CommentCollection = CommentCollection +})(OC, OCA) diff --git a/apps/comments/src/commentmodel.js b/apps/comments/src/commentmodel.js index 3711e53c9f3b18137806dea11fd67a9c0f7cc66e..804fbdfd3cb2145a105c1e3c65ceff68a8e68689 100644 --- a/apps/comments/src/commentmodel.js +++ b/apps/comments/src/commentmodel.js @@ -12,7 +12,7 @@ _.extend(OC.Files.Client, { PROPERTY_FILEID: '{' + OC.Files.Client.NS_OWNCLOUD + '}id', - PROPERTY_MESSAGE: '{' + OC.Files.Client.NS_OWNCLOUD + '}message', + PROPERTY_MESSAGE: '{' + OC.Files.Client.NS_OWNCLOUD + '}message', PROPERTY_ACTORTYPE: '{' + OC.Files.Client.NS_OWNCLOUD + '}actorType', PROPERTY_ACTORID: '{' + OC.Files.Client.NS_OWNCLOUD + '}actorId', PROPERTY_ISUNREAD: '{' + OC.Files.Client.NS_OWNCLOUD + '}isUnread', @@ -21,7 +21,7 @@ PROPERTY_ACTORDISPLAYNAME: '{' + OC.Files.Client.NS_OWNCLOUD + '}actorDisplayName', PROPERTY_CREATIONDATETIME: '{' + OC.Files.Client.NS_OWNCLOUD + '}creationDateTime', PROPERTY_MENTIONS: '{' + OC.Files.Client.NS_OWNCLOUD + '}mentions' - }); + }) /** * @class OCA.Comments.CommentModel @@ -32,62 +32,62 @@ */ var CommentModel = OC.Backbone.Model.extend( /** @lends OCA.Comments.CommentModel.prototype */ { - sync: OC.Backbone.davSync, + sync: OC.Backbone.davSync, - defaults: { - actorType: 'users', - objectType: 'files' - }, + defaults: { + actorType: 'users', + objectType: 'files' + }, - davProperties: { - 'id': OC.Files.Client.PROPERTY_FILEID, - 'message': OC.Files.Client.PROPERTY_MESSAGE, - 'actorType': OC.Files.Client.PROPERTY_ACTORTYPE, - 'actorId': OC.Files.Client.PROPERTY_ACTORID, - 'actorDisplayName': OC.Files.Client.PROPERTY_ACTORDISPLAYNAME, - 'creationDateTime': OC.Files.Client.PROPERTY_CREATIONDATETIME, - 'objectType': OC.Files.Client.PROPERTY_OBJECTTYPE, - 'objectId': OC.Files.Client.PROPERTY_OBJECTID, - 'isUnread': OC.Files.Client.PROPERTY_ISUNREAD, - 'mentions': OC.Files.Client.PROPERTY_MENTIONS - }, + davProperties: { + 'id': OC.Files.Client.PROPERTY_FILEID, + 'message': OC.Files.Client.PROPERTY_MESSAGE, + 'actorType': OC.Files.Client.PROPERTY_ACTORTYPE, + 'actorId': OC.Files.Client.PROPERTY_ACTORID, + 'actorDisplayName': OC.Files.Client.PROPERTY_ACTORDISPLAYNAME, + 'creationDateTime': OC.Files.Client.PROPERTY_CREATIONDATETIME, + 'objectType': OC.Files.Client.PROPERTY_OBJECTTYPE, + 'objectId': OC.Files.Client.PROPERTY_OBJECTID, + 'isUnread': OC.Files.Client.PROPERTY_ISUNREAD, + 'mentions': OC.Files.Client.PROPERTY_MENTIONS + }, - parse: function(data) { - return { - id: data.id, - message: data.message, - actorType: data.actorType, - actorId: data.actorId, - actorDisplayName: data.actorDisplayName, - creationDateTime: data.creationDateTime, - objectType: data.objectType, - objectId: data.objectId, - isUnread: (data.isUnread === 'true'), - mentions: this._parseMentions(data.mentions) - }; - }, + parse: function(data) { + return { + id: data.id, + message: data.message, + actorType: data.actorType, + actorId: data.actorId, + actorDisplayName: data.actorDisplayName, + creationDateTime: data.creationDateTime, + objectType: data.objectType, + objectId: data.objectId, + isUnread: (data.isUnread === 'true'), + mentions: this._parseMentions(data.mentions) + } + }, - _parseMentions: function(mentions) { - if(_.isUndefined(mentions)) { - return {}; - } - var result = {}; - for(var i in mentions) { - var mention = mentions[i]; - if(_.isUndefined(mention.localName) || mention.localName !== 'mention') { - continue; + _parseMentions: function(mentions) { + if (_.isUndefined(mentions)) { + return {} } - result[i] = {}; - for (var child = mention.firstChild; child; child = child.nextSibling) { - if(_.isUndefined(child.localName) || !child.localName.startsWith('mention')) { - continue; + var result = {} + for (var i in mentions) { + var mention = mentions[i] + if (_.isUndefined(mention.localName) || mention.localName !== 'mention') { + continue + } + result[i] = {} + for (var child = mention.firstChild; child; child = child.nextSibling) { + if (_.isUndefined(child.localName) || !child.localName.startsWith('mention')) { + continue + } + result[i][child.localName] = child.textContent } - result[i][child.localName] = child.textContent; } + return result } - return result; - } - }); + }) - OCA.Comments.CommentModel = CommentModel; -})(OC, OCA); + OCA.Comments.CommentModel = CommentModel +})(OC, OCA) diff --git a/apps/comments/src/comments.js b/apps/comments/src/comments.js index f0d4433674b930e25cfedfb0104204fa452aef8d..e632936c770833575ed30bdabd026616ed9a5277 100644 --- a/apps/comments/src/comments.js +++ b/apps/comments/src/comments.js @@ -15,4 +15,4 @@ import './vendor/At.js/dist/js/jquery.atwho.min' import './style/autocomplete.scss' import './style/comments.scss' -window.OCA.Comments = OCA.Comments; +window.OCA.Comments = OCA.Comments diff --git a/apps/comments/src/commentsmodifymenu.js b/apps/comments/src/commentsmodifymenu.js index 2640dcf4201eb666b2bae2278bc681695997f119..7c9470f13b2f6f9007df44dfd5d6d001f9e94a5b 100644 --- a/apps/comments/src/commentsmodifymenu.js +++ b/apps/comments/src/commentsmodifymenu.js @@ -8,7 +8,6 @@ * */ -/* global Handlebars */ (function() { /** @@ -23,7 +22,7 @@ _scopes: [ { name: 'edit', - displayName: t('comments', 'Edit comment'), + displayName: t('comments', 'Edit comment'), iconClass: 'icon-rename' }, { @@ -45,14 +44,14 @@ * @param {Object} event event object */ _onClickAction: function(event) { - var $target = $(event.currentTarget); + var $target = $(event.currentTarget) if (!$target.hasClass('menuitem')) { - $target = $target.closest('.menuitem'); + $target = $target.closest('.menuitem') } - OC.hideMenus(); + OC.hideMenus() - this.trigger('select:menu-item-clicked', event, $target.data('action')); + this.trigger('select:menu-item-clicked', event, $target.data('action')) }, /** @@ -61,49 +60,49 @@ render: function() { this.$el.html(OCA.Comments.Templates['commentsmodifymenu']({ items: this._scopes - })); + })) }, /** * Displays the menu + * @param {Event} context the click event */ show: function(context) { - this._context = context; + this._context = context - for(var i in this._scopes) { - this._scopes[i].active = false; + for (var i in this._scopes) { + this._scopes[i].active = false } - - var $el = $(context.target); - var offsetIcon = $el.offset(); - var offsetContainer = $el.closest('.authorRow').offset(); + var $el = $(context.target) + var offsetIcon = $el.offset() + var offsetContainer = $el.closest('.authorRow').offset() // adding some extra top offset to push the menu below the button. var position = { top: offsetIcon.top - offsetContainer.top + 48, left: '', right: '' - }; + } - position.left = offsetIcon.left - offsetContainer.left; + position.left = offsetIcon.left - offsetContainer.left if (position.left > 200) { // we need to position the menu to the right. - position.left = ''; - position.right = this.$el.closest('.comment').find('.date').width(); - this.$el.removeClass('menu-left').addClass('menu-right'); + position.left = '' + position.right = this.$el.closest('.comment').find('.date').width() + this.$el.removeClass('menu-left').addClass('menu-right') } else { - this.$el.removeClass('menu-right').addClass('menu-left'); + this.$el.removeClass('menu-right').addClass('menu-left') } - this.$el.css(position); - this.render(); - this.$el.removeClass('hidden'); + this.$el.css(position) + this.render() + this.$el.removeClass('hidden') - OC.showMenu(null, this.$el); + OC.showMenu(null, this.$el) } - }); + }) - OCA.Comments = OCA.Comments || {}; - OCA.Comments.CommentsModifyMenu = CommentsModifyMenu; -})(OC, OCA); + OCA.Comments = OCA.Comments || {} + OCA.Comments.CommentsModifyMenu = CommentsModifyMenu +})(OC, OCA) diff --git a/apps/comments/src/commentstabview.js b/apps/comments/src/commentstabview.js index 8b71fc1f87b612a1231f8394e2904ef0eac08f7e..a638f46a2c6ee364d34f64f9f4c6dda60986f3a6 100644 --- a/apps/comments/src/commentstabview.js +++ b/apps/comments/src/commentstabview.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2016 * @@ -17,252 +18,252 @@ */ var CommentsTabView = OCA.Files.DetailTabView.extend( /** @lends OCA.Comments.CommentsTabView.prototype */ { - id: 'commentsTabView', - className: 'tab commentsTabView', - _autoCompleteData: undefined, - _commentsModifyMenu: undefined, - - events: { - 'submit .newCommentForm': '_onSubmitComment', - 'click .showMore': '_onClickShowMore', - 'click .cancel': '_onClickCloseComment', - 'click .comment': '_onClickComment', - 'keyup div.message': '_onTextChange', - 'change div.message': '_onTextChange', - 'input div.message': '_onTextChange', - 'paste div.message': '_onPaste' - }, - - _commentMaxLength: 1000, - - initialize: function() { - OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments); - this.collection = new OCA.Comments.CommentCollection(); - this.collection.on('request', this._onRequest, this); - this.collection.on('sync', this._onEndRequest, this); - this.collection.on('add', this._onAddModel, this); - this.collection.on('change:message', this._onChangeModel, this); - - this._commentMaxThreshold = this._commentMaxLength * 0.9; - - // TODO: error handling - _.bindAll(this, '_onTypeComment', '_initAutoComplete', '_onAutoComplete'); - }, - - template: function(params) { - var currentUser = OC.getCurrentUser(); - return OCA.Comments.Templates['view'](_.extend({ - actorId: currentUser.uid, - actorDisplayName: currentUser.displayName - }, params)); - }, - - editCommentTemplate: function(params) { - var currentUser = OC.getCurrentUser(); - return OCA.Comments.Templates['edit_comment'](_.extend({ - actorId: currentUser.uid, - actorDisplayName: currentUser.displayName, - newMessagePlaceholder: t('comments', 'New comment …'), - submitText: t('comments', 'Post'), - cancelText: t('comments', 'Cancel'), - tag: 'li' - }, params)); - }, - - commentTemplate: function(params) { - params = _.extend({ - editTooltip: t('comments', 'Edit comment'), - isUserAuthor: OC.getCurrentUser().uid === params.actorId, - isLong: this._isLong(params.message) - }, params); - - if (params.actorType === 'deleted_users') { + id: 'commentsTabView', + className: 'tab commentsTabView', + _autoCompleteData: undefined, + _commentsModifyMenu: undefined, + + events: { + 'submit .newCommentForm': '_onSubmitComment', + 'click .showMore': '_onClickShowMore', + 'click .cancel': '_onClickCloseComment', + 'click .comment': '_onClickComment', + 'keyup div.message': '_onTextChange', + 'change div.message': '_onTextChange', + 'input div.message': '_onTextChange', + 'paste div.message': '_onPaste' + }, + + _commentMaxLength: 1000, + + initialize: function() { + OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments) + this.collection = new OCA.Comments.CommentCollection() + this.collection.on('request', this._onRequest, this) + this.collection.on('sync', this._onEndRequest, this) + this.collection.on('add', this._onAddModel, this) + this.collection.on('change:message', this._onChangeModel, this) + + this._commentMaxThreshold = this._commentMaxLength * 0.9 + + // TODO: error handling + _.bindAll(this, '_onTypeComment', '_initAutoComplete', '_onAutoComplete') + }, + + template: function(params) { + var currentUser = OC.getCurrentUser() + return OCA.Comments.Templates['view'](_.extend({ + actorId: currentUser.uid, + actorDisplayName: currentUser.displayName + }, params)) + }, + + editCommentTemplate: function(params) { + var currentUser = OC.getCurrentUser() + return OCA.Comments.Templates['edit_comment'](_.extend({ + actorId: currentUser.uid, + actorDisplayName: currentUser.displayName, + newMessagePlaceholder: t('comments', 'New comment …'), + submitText: t('comments', 'Post'), + cancelText: t('comments', 'Cancel'), + tag: 'li' + }, params)) + }, + + commentTemplate: function(params) { + params = _.extend({ + editTooltip: t('comments', 'Edit comment'), + isUserAuthor: OC.getCurrentUser().uid === params.actorId, + isLong: this._isLong(params.message) + }, params) + + if (params.actorType === 'deleted_users') { // makes the avatar a X - params.actorId = null; - params.actorDisplayName = t('comments', '[Deleted user]'); - } + params.actorId = null + params.actorDisplayName = t('comments', '[Deleted user]') + } - return OCA.Comments.Templates['comment'](params); - }, - - getLabel: function() { - return t('comments', 'Comments'); - }, - - getIcon: function() { - return 'icon-comment'; - }, - - setFileInfo: function(fileInfo) { - if (fileInfo) { - this.model = fileInfo; - - this.render(); - this._initAutoComplete($('#commentsTabView').find('.newCommentForm .message')); - this.collection.setObjectId(this.model.id); - // reset to first page - this.collection.reset([], {silent: true}); - this.nextPage(); - } else { - this.model = null; - this.render(); - this.collection.reset(); - } - }, - - render: function() { - this.$el.html(this.template({ - emptyResultLabel: t('comments', 'No comments yet, start the conversation!'), - moreLabel: t('comments', 'More comments …') - })); - this.$el.find('.comments').before(this.editCommentTemplate({ tag: 'div'})); - this.$el.find('.has-tooltip').tooltip(); - this.$container = this.$el.find('ul.comments'); - this.$el.find('.avatar').avatar(OC.getCurrentUser().uid, 32); - this.delegateEvents(); - this.$el.find('.message').on('keydown input change', this._onTypeComment); - - autosize(this.$el.find('.newCommentRow .message')) - this.$el.find('.newCommentForm .message').focus(); - }, - - _initAutoComplete: function($target) { - var s = this; - var limit = 10; - if(!_.isUndefined(OC.appConfig.comments)) { - limit = OC.appConfig.comments.maxAutoCompleteResults; - } - $target.atwho({ - at: '@', - limit: limit, - callbacks: { - remoteFilter: s._onAutoComplete, - highlighter: function (li) { + return OCA.Comments.Templates['comment'](params) + }, + + getLabel: function() { + return t('comments', 'Comments') + }, + + getIcon: function() { + return 'icon-comment' + }, + + setFileInfo: function(fileInfo) { + if (fileInfo) { + this.model = fileInfo + + this.render() + this._initAutoComplete($('#commentsTabView').find('.newCommentForm .message')) + this.collection.setObjectId(this.model.id) + // reset to first page + this.collection.reset([], { silent: true }) + this.nextPage() + } else { + this.model = null + this.render() + this.collection.reset() + } + }, + + render: function() { + this.$el.html(this.template({ + emptyResultLabel: t('comments', 'No comments yet, start the conversation!'), + moreLabel: t('comments', 'More comments …') + })) + this.$el.find('.comments').before(this.editCommentTemplate({ tag: 'div' })) + this.$el.find('.has-tooltip').tooltip() + this.$container = this.$el.find('ul.comments') + this.$el.find('.avatar').avatar(OC.getCurrentUser().uid, 32) + this.delegateEvents() + this.$el.find('.message').on('keydown input change', this._onTypeComment) + + autosize(this.$el.find('.newCommentRow .message')) + this.$el.find('.newCommentForm .message').focus() + }, + + _initAutoComplete: function($target) { + var s = this + var limit = 10 + if (!_.isUndefined(OC.appConfig.comments)) { + limit = OC.appConfig.comments.maxAutoCompleteResults + } + $target.atwho({ + at: '@', + limit: limit, + callbacks: { + remoteFilter: s._onAutoComplete, + highlighter: function(li) { // misuse the highlighter callback to instead of // highlighting loads the avatars. - var $li = $(li); - $li.find('.avatar').avatar(undefined, 32); - return $li; + var $li = $(li) + $li.find('.avatar').avatar(undefined, 32) + return $li + }, + sorter: function(q, items) { return items } + }, + displayTpl: function(item) { + return '<li>' + + '<span class="avatar-name-wrapper">' + + '<span class="avatar" ' + + 'data-username="' + escapeHTML(item.id) + '" ' // for avatars + + 'data-user="' + escapeHTML(item.id) + '" ' // for contactsmenu + + 'data-user-display-name="' + escapeHTML(item.label) + '">' + + '</span>' + + '<strong>' + escapeHTML(item.label) + '</strong>' + + '</span></li>' }, - sorter: function (q, items) { return items; } - }, - displayTpl: function (item) { - return '<li>' + - '<span class="avatar-name-wrapper">' + - '<span class="avatar" ' + - 'data-username="' + escapeHTML(item.id) + '" ' + // for avatars - 'data-user="' + escapeHTML(item.id) + '" ' + // for contactsmenu - 'data-user-display-name="' + escapeHTML(item.label) + '">' + - '</span>' + - '<strong>' + escapeHTML(item.label) + '</strong>' + - '</span></li>'; - }, - insertTpl: function (item) { - return '' + - '<span class="avatar-name-wrapper">' + - '<span class="avatar" ' + - 'data-username="' + escapeHTML(item.id) + '" ' + // for avatars - 'data-user="' + escapeHTML(item.id) + '" ' + // for contactsmenu - 'data-user-display-name="' + escapeHTML(item.label) + '">' + - '</span>' + - '<strong>' + escapeHTML(item.label) + '</strong>' + - '</span>'; - }, - searchKey: "label" - }); - $target.on('inserted.atwho', function (je, $el) { - var editionMode = true; - s._postRenderItem( + insertTpl: function(item) { + return '' + + '<span class="avatar-name-wrapper">' + + '<span class="avatar" ' + + 'data-username="' + escapeHTML(item.id) + '" ' // for avatars + + 'data-user="' + escapeHTML(item.id) + '" ' // for contactsmenu + + 'data-user-display-name="' + escapeHTML(item.label) + '">' + + '</span>' + + '<strong>' + escapeHTML(item.label) + '</strong>' + + '</span>' + }, + searchKey: 'label' + }) + $target.on('inserted.atwho', function(je, $el) { + var editionMode = true + s._postRenderItem( // we need to pass the parent of the inserted element // passing the whole comments form would re-apply and request // avatars from the server - $(je.target).find( - 'span[data-username="' + $el.find('[data-username]').data('username') + '"]' - ).parent(), - editionMode - ); - }); - }, - - _onAutoComplete: function(query, callback) { - var s = this; - if(!_.isUndefined(this._autoCompleteRequestTimer)) { - clearTimeout(this._autoCompleteRequestTimer); - } - this._autoCompleteRequestTimer = _.delay(function() { - if(!_.isUndefined(this._autoCompleteRequestCall)) { - this._autoCompleteRequestCall.abort(); + $(je.target).find( + 'span[data-username="' + $el.find('[data-username]').data('username') + '"]' + ).parent(), + editionMode + ) + }) + }, + + _onAutoComplete: function(query, callback) { + var s = this + if (!_.isUndefined(this._autoCompleteRequestTimer)) { + clearTimeout(this._autoCompleteRequestTimer) } - this._autoCompleteRequestCall = $.ajax({ - url: OC.linkToOCS('core', 2) + 'autocomplete/get', - data: { - search: query, - itemType: 'files', - itemId: s.model.get('id'), - sorter: 'commenters|share-recipients', - limit: OC.appConfig.comments.maxAutoCompleteResults - }, - beforeSend: function (request) { - request.setRequestHeader('Accept', 'application/json'); - }, - success: function (result) { - callback(result.ocs.data); + this._autoCompleteRequestTimer = _.delay(function() { + if (!_.isUndefined(this._autoCompleteRequestCall)) { + this._autoCompleteRequestCall.abort() } - }); - }, 400); - }, - - _formatItem: function(commentModel) { - var timestamp = new Date(commentModel.get('creationDateTime')).getTime(); - var data = _.extend({ - timestamp: timestamp, - date: OC.Util.relativeModifiedDate(timestamp), - altDate: OC.Util.formatDate(timestamp), - formattedMessage: this._formatMessage(commentModel.get('message'), commentModel.get('mentions')) - }, commentModel.attributes); - return data; - }, - - _toggleLoading: function(state) { - this._loading = state; - this.$el.find('.loading').toggleClass('hidden', !state); - }, - - _onRequest: function(type) { - if (type === 'REPORT') { - this._toggleLoading(true); - this.$el.find('.showMore').addClass('hidden'); - } - }, + this._autoCompleteRequestCall = $.ajax({ + url: OC.linkToOCS('core', 2) + 'autocomplete/get', + data: { + search: query, + itemType: 'files', + itemId: s.model.get('id'), + sorter: 'commenters|share-recipients', + limit: OC.appConfig.comments.maxAutoCompleteResults + }, + beforeSend: function(request) { + request.setRequestHeader('Accept', 'application/json') + }, + success: function(result) { + callback(result.ocs.data) + } + }) + }, 400) + }, + + _formatItem: function(commentModel) { + var timestamp = new Date(commentModel.get('creationDateTime')).getTime() + var data = _.extend({ + timestamp: timestamp, + date: OC.Util.relativeModifiedDate(timestamp), + altDate: OC.Util.formatDate(timestamp), + formattedMessage: this._formatMessage(commentModel.get('message'), commentModel.get('mentions')) + }, commentModel.attributes) + return data + }, + + _toggleLoading: function(state) { + this._loading = state + this.$el.find('.loading').toggleClass('hidden', !state) + }, + + _onRequest: function(type) { + if (type === 'REPORT') { + this._toggleLoading(true) + this.$el.find('.showMore').addClass('hidden') + } + }, - _onEndRequest: function(type) { - var fileInfoModel = this.model; - this._toggleLoading(false); - this.$el.find('.emptycontent').toggleClass('hidden', !!this.collection.length); - this.$el.find('.showMore').toggleClass('hidden', !this.collection.hasMoreResults()); + _onEndRequest: function(type) { + var fileInfoModel = this.model + this._toggleLoading(false) + this.$el.find('.emptycontent').toggleClass('hidden', !!this.collection.length) + this.$el.find('.showMore').toggleClass('hidden', !this.collection.hasMoreResults()) - if (type !== 'REPORT') { - return; - } + if (type !== 'REPORT') { + return + } - // find first unread comment - var firstUnreadComment = this.collection.findWhere({isUnread: true}); - if (firstUnreadComment) { + // find first unread comment + var firstUnreadComment = this.collection.findWhere({ isUnread: true }) + if (firstUnreadComment) { // update read marker - this.collection.updateReadMarker( - null, - { - success: function() { - fileInfoModel.set('commentsUnread', 0); + this.collection.updateReadMarker( + null, + { + success: function() { + fileInfoModel.set('commentsUnread', 0) + } } - } - ); - } - this.$el.find('.newCommentForm .message').focus(); - - }, + ) + } + this.$el.find('.newCommentForm .message').focus() + + }, - /** + /** * takes care of post-rendering after a new comment was added to the * collection * @@ -271,322 +272,321 @@ * @param options * @private */ - _onAddModel: function(model, collection, options) { + _onAddModel: function(model, collection, options) { // we need to render it immediately, to ensure that the right // order of comments is kept on opening comments tab - var $comment = $(this.commentTemplate(this._formatItem(model))); - if (!_.isUndefined(options.at) && collection.length > 1) { - this.$container.find('li').eq(options.at).before($comment); - } else { - this.$container.append($comment); - } - this._postRenderItem($comment); - $('#commentsTabView').find('.newCommentForm div.message').text('').prop('contenteditable', true); - - // we need to update the model, because it consists of client data - // only, but the server might add meta data, e.g. about mentions - var oldMentions = model.get('mentions'); - var self = this; - model.fetch({ - success: function (model) { - if(_.isEqual(oldMentions, model.get('mentions'))) { + var $comment = $(this.commentTemplate(this._formatItem(model))) + if (!_.isUndefined(options.at) && collection.length > 1) { + this.$container.find('li').eq(options.at).before($comment) + } else { + this.$container.append($comment) + } + this._postRenderItem($comment) + $('#commentsTabView').find('.newCommentForm div.message').text('').prop('contenteditable', true) + + // we need to update the model, because it consists of client data + // only, but the server might add meta data, e.g. about mentions + var oldMentions = model.get('mentions') + var self = this + model.fetch({ + success: function(model) { + if (_.isEqual(oldMentions, model.get('mentions'))) { // don't attempt to render if unnecessary, avoids flickering - return; + return + } + var $updated = $(self.commentTemplate(self._formatItem(model))) + $comment.html($updated.html()) + self._postRenderItem($comment) } - var $updated = $(self.commentTemplate(self._formatItem(model))); - $comment.html($updated.html()); - self._postRenderItem($comment); - } - }) + }) - }, + }, - /** + /** * takes care of post-rendering after a new comment was edited * * @param model * @private */ - _onChangeModel: function (model) { - if(model.get('message').trim() === model.previous('message').trim()) { - return; - } + _onChangeModel: function(model) { + if (model.get('message').trim() === model.previous('message').trim()) { + return + } - var $form = this.$container.find('.comment[data-id="' + model.id + '"] form'); - var $row = $form.closest('.comment'); - var $target = $row.data('commentEl'); - if(_.isUndefined($target)) { + var $form = this.$container.find('.comment[data-id="' + model.id + '"] form') + var $row = $form.closest('.comment') + var $target = $row.data('commentEl') + if (_.isUndefined($target)) { // ignore noise – this is only set after editing a comment and hitting post - return; - } - var self = this; - - // we need to update the model, because it consists of client data - // only, but the server might add meta data, e.g. about mentions - model.fetch({ - success: function (model) { - $target.removeClass('hidden'); - $row.remove(); - - var $message = $target.find('.message'); - $message - .html(self._formatMessage(model.get('message'), model.get('mentions'))) - .find('.avatar') - .each(function () { $(this).avatar(); }); - self._postRenderItem($message); + return } - }); - }, - - _postRenderItem: function($el, editionMode) { - $el.find('.has-tooltip').tooltip(); - var inlineAvatars = $el.find('.message .avatar'); - if ($($el.context).hasClass('message')) { - inlineAvatars = $el.find('.avatar'); - } - inlineAvatars.each(function () { - var $this = $(this); - $this.avatar($this.attr('data-username'), 16); - }); - $el.find('.authorRow .avatar').each(function () { - var $this = $(this); - $this.avatar($this.attr('data-username'), 32); - }); - - var username = $el.find('.avatar').data('username'); - if (username !== OC.getCurrentUser().uid) { - $el.find('.authorRow .avatar, .authorRow .author').contactsMenu( - username, 0, $el.find('.authorRow')); - } + var self = this - var $message = $el.find('.message'); - if($message.length === 0) { - // it is the case when writing a comment and mentioning a person - $message = $el; - } - - - if (!editionMode) { - var self = this; - // add the dropdown menu to display the edit and delete option - var modifyCommentMenu = new OCA.Comments.CommentsModifyMenu(); - $el.find('.authorRow').append(modifyCommentMenu.$el); - $el.find('.more').on('click', _.bind(modifyCommentMenu.show, modifyCommentMenu)); - - self.listenTo(modifyCommentMenu, 'select:menu-item-clicked', function(ev, action) { - if (action === 'edit') { - self._onClickEditComment(ev); - } else if (action === 'delete') { - self._onClickDeleteComment(ev); + // we need to update the model, because it consists of client data + // only, but the server might add meta data, e.g. about mentions + model.fetch({ + success: function(model) { + $target.removeClass('hidden') + $row.remove() + + var $message = $target.find('.message') + $message + .html(self._formatMessage(model.get('message'), model.get('mentions'))) + .find('.avatar') + .each(function() { $(this).avatar() }) + self._postRenderItem($message) } - }); - } + }) + }, + + _postRenderItem: function($el, editionMode) { + $el.find('.has-tooltip').tooltip() + var inlineAvatars = $el.find('.message .avatar') + if ($($el.context).hasClass('message')) { + inlineAvatars = $el.find('.avatar') + } + inlineAvatars.each(function() { + var $this = $(this) + $this.avatar($this.attr('data-username'), 16) + }) + $el.find('.authorRow .avatar').each(function() { + var $this = $(this) + $this.avatar($this.attr('data-username'), 32) + }) + + var username = $el.find('.avatar').data('username') + if (username !== OC.getCurrentUser().uid) { + $el.find('.authorRow .avatar, .authorRow .author').contactsMenu( + username, 0, $el.find('.authorRow')) + } - this._postRenderMessage($message, editionMode); - }, + var $message = $el.find('.message') + if ($message.length === 0) { + // it is the case when writing a comment and mentioning a person + $message = $el + } - _postRenderMessage: function($el, editionMode) { - if (editionMode) { - return; - } + if (!editionMode) { + var self = this + // add the dropdown menu to display the edit and delete option + var modifyCommentMenu = new OCA.Comments.CommentsModifyMenu() + $el.find('.authorRow').append(modifyCommentMenu.$el) + $el.find('.more').on('click', _.bind(modifyCommentMenu.show, modifyCommentMenu)) + + self.listenTo(modifyCommentMenu, 'select:menu-item-clicked', function(ev, action) { + if (action === 'edit') { + self._onClickEditComment(ev) + } else if (action === 'delete') { + self._onClickDeleteComment(ev) + } + }) + } - $el.find('.avatar-name-wrapper').each(function() { - var $this = $(this); - var $avatar = $this.find('.avatar'); + this._postRenderMessage($message, editionMode) + }, - var user = $avatar.data('user'); - if (user !== OC.getCurrentUser().uid) { - $this.contactsMenu(user, 0, $this); + _postRenderMessage: function($el, editionMode) { + if (editionMode) { + return } - }); - }, - /** + $el.find('.avatar-name-wrapper').each(function() { + var $this = $(this) + var $avatar = $this.find('.avatar') + + var user = $avatar.data('user') + if (user !== OC.getCurrentUser().uid) { + $this.contactsMenu(user, 0, $this) + } + }) + }, + + /** * Convert a message to be displayed in HTML, * converts newlines to <br> tags. */ - _formatMessage: function(message, mentions, editMode) { - message = escapeHTML(message).replace(/\n/g, '<br/>'); + _formatMessage: function(message, mentions, editMode) { + message = escapeHTML(message).replace(/\n/g, '<br/>') - for(var i in mentions) { - if(!mentions.hasOwnProperty(i)) { - return; - } - var mention = '@' + mentions[i].mentionId; - if (mentions[i].mentionId.indexOf(' ') !== -1) { - mention = _.escape('@"' + mentions[i].mentionId + '"'); - } + for (var i in mentions) { + if (!mentions.hasOwnProperty(i)) { + return + } + var mention = '@' + mentions[i].mentionId + if (mentions[i].mentionId.indexOf(' ') !== -1) { + mention = _.escape('@"' + mentions[i].mentionId + '"') + } - // escape possible regex characters in the name - mention = mention.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - var regex = new RegExp("(^|\\s)(" + mention + ")\\b", 'g'); - if (mentions[i].mentionId.indexOf(' ') !== -1) { - regex = new RegExp("(^|\\s)(" + mention + ")", 'g'); - } + // escape possible regex characters in the name + mention = mention.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + var regex = new RegExp('(^|\\s)(' + mention + ')\\b', 'g') + if (mentions[i].mentionId.indexOf(' ') !== -1) { + regex = new RegExp('(^|\\s)(' + mention + ')', 'g') + } - var displayName = this._composeHTMLMention(mentions[i].mentionId, mentions[i].mentionDisplayName); + var displayName = this._composeHTMLMention(mentions[i].mentionId, mentions[i].mentionDisplayName) - // replace every mention either at the start of the input or after a whitespace - // followed by a non-word character. - message = message.replace(regex, - function(match, p1) { + // replace every mention either at the start of the input or after a whitespace + // followed by a non-word character. + message = message.replace(regex, + function(match, p1) { // to get number of whitespaces (0 vs 1) right - return p1+displayName; - } - ); - } - if(editMode !== true) { - message = OCP.Comments.plainToRich(message); - } - return message; - }, - - _composeHTMLMention: function(uid, displayName) { - var avatar = '' + - '<span class="avatar" ' + - 'data-username="' + _.escape(uid) + '" ' + - 'data-user="' + _.escape(uid) + '" ' + - 'data-user-display-name="' + _.escape(displayName) + '">' + - '</span>'; - - var isCurrentUser = (uid === OC.getCurrentUser().uid); - - return '' + - '<span class="atwho-inserted" contenteditable="false">' + - '<span class="avatar-name-wrapper' + (isCurrentUser ? ' currentUser' : '') + '">' + - avatar + - '<strong>' + _.escape(displayName) + '</strong>' + - '</span>' + - '</span>'; - }, - - nextPage: function() { - if (this._loading || !this.collection.hasMoreResults()) { - return; - } + return p1 + displayName + } + ) + } + if (editMode !== true) { + message = OCP.Comments.plainToRich(message) + } + return message + }, + + _composeHTMLMention: function(uid, displayName) { + var avatar = '' + + '<span class="avatar" ' + + 'data-username="' + _.escape(uid) + '" ' + + 'data-user="' + _.escape(uid) + '" ' + + 'data-user-display-name="' + _.escape(displayName) + '">' + + '</span>' + + var isCurrentUser = (uid === OC.getCurrentUser().uid) + + return '' + + '<span class="atwho-inserted" contenteditable="false">' + + '<span class="avatar-name-wrapper' + (isCurrentUser ? ' currentUser' : '') + '">' + + avatar + + '<strong>' + _.escape(displayName) + '</strong>' + + '</span>' + + '</span>' + }, + + nextPage: function() { + if (this._loading || !this.collection.hasMoreResults()) { + return + } - this.collection.fetchNext(); - }, - - _onClickEditComment: function(ev) { - ev.preventDefault(); - var $comment = $(ev.target).closest('.comment'); - var commentId = $comment.data('id'); - var commentToEdit = this.collection.get(commentId); - var $formRow = $(this.editCommentTemplate(_.extend({ - isEditMode: true, - submitText: t('comments', 'Save') - }, commentToEdit.attributes))); - - $comment.addClass('hidden').removeClass('collapsed'); - // spawn form - $comment.after($formRow); - $formRow.data('commentEl', $comment); - $formRow.find('.message').on('keydown input change', this._onTypeComment); - - // copy avatar element from original to avoid flickering - $formRow.find('.avatar:first').replaceWith($comment.find('.avatar:first').clone()); - $formRow.find('.has-tooltip').tooltip(); - - var $message = $formRow.find('.message'); - $message - .html(this._formatMessage(commentToEdit.get('message'), commentToEdit.get('mentions'), true)) - .find('.avatar') - .each(function () { $(this).avatar(); }); - var editionMode = true; - this._postRenderItem($message, editionMode); - - // Enable autosize - autosize($formRow.find('.message')); - - // enable autocomplete - this._initAutoComplete($formRow.find('.message')); - - return false; - }, - - _onTypeComment: function(ev) { - var $field = $(ev.target); - var len = $field.text().length; - var $submitButton = $field.data('submitButtonEl'); - if (!$submitButton) { - $submitButton = $field.closest('form').find('.submit'); - $field.data('submitButtonEl', $submitButton); - } - $field.tooltip('hide'); - if (len > this._commentMaxThreshold) { - $field.attr('data-original-title', t('comments', 'Allowed characters {count} of {max}', {count: len, max: this._commentMaxLength})); - $field.tooltip({trigger: 'manual'}); - $field.tooltip('show'); - $field.addClass('error'); - } + this.collection.fetchNext() + }, + + _onClickEditComment: function(ev) { + ev.preventDefault() + var $comment = $(ev.target).closest('.comment') + var commentId = $comment.data('id') + var commentToEdit = this.collection.get(commentId) + var $formRow = $(this.editCommentTemplate(_.extend({ + isEditMode: true, + submitText: t('comments', 'Save') + }, commentToEdit.attributes))) + + $comment.addClass('hidden').removeClass('collapsed') + // spawn form + $comment.after($formRow) + $formRow.data('commentEl', $comment) + $formRow.find('.message').on('keydown input change', this._onTypeComment) + + // copy avatar element from original to avoid flickering + $formRow.find('.avatar:first').replaceWith($comment.find('.avatar:first').clone()) + $formRow.find('.has-tooltip').tooltip() + + var $message = $formRow.find('.message') + $message + .html(this._formatMessage(commentToEdit.get('message'), commentToEdit.get('mentions'), true)) + .find('.avatar') + .each(function() { $(this).avatar() }) + var editionMode = true + this._postRenderItem($message, editionMode) + + // Enable autosize + autosize($formRow.find('.message')) + + // enable autocomplete + this._initAutoComplete($formRow.find('.message')) + + return false + }, + + _onTypeComment: function(ev) { + var $field = $(ev.target) + var len = $field.text().length + var $submitButton = $field.data('submitButtonEl') + if (!$submitButton) { + $submitButton = $field.closest('form').find('.submit') + $field.data('submitButtonEl', $submitButton) + } + $field.tooltip('hide') + if (len > this._commentMaxThreshold) { + $field.attr('data-original-title', t('comments', 'Allowed characters {count} of {max}', { count: len, max: this._commentMaxLength })) + $field.tooltip({ trigger: 'manual' }) + $field.tooltip('show') + $field.addClass('error') + } - var limitExceeded = (len > this._commentMaxLength); - $field.toggleClass('error', limitExceeded); - $submitButton.prop('disabled', limitExceeded); - - // Submits form with Enter, but Shift+Enter is a new line. If the - // autocomplete popover is being shown Enter does not submit the - // form either; it will be handled by At.js which will add the - // currently selected item to the message. - if (ev.keyCode === 13 && !ev.shiftKey && !$field.atwho('isSelecting')) { - $submitButton.click(); - ev.preventDefault(); - } - }, + var limitExceeded = (len > this._commentMaxLength) + $field.toggleClass('error', limitExceeded) + $submitButton.prop('disabled', limitExceeded) + + // Submits form with Enter, but Shift+Enter is a new line. If the + // autocomplete popover is being shown Enter does not submit the + // form either; it will be handled by At.js which will add the + // currently selected item to the message. + if (ev.keyCode === 13 && !ev.shiftKey && !$field.atwho('isSelecting')) { + $submitButton.click() + ev.preventDefault() + } + }, - _onClickComment: function(ev) { - var $row = $(ev.target); - if (!$row.is('.comment')) { - $row = $row.closest('.comment'); - } - $row.removeClass('collapsed'); - }, - - _onClickCloseComment: function(ev) { - ev.preventDefault(); - var $row = $(ev.target).closest('.comment'); - $row.data('commentEl').removeClass('hidden'); - $row.remove(); - return false; - }, - - _onClickDeleteComment: function(ev) { - ev.preventDefault(); - var $comment = $(ev.target).closest('.comment'); - var commentId = $comment.data('id'); - var $loading = $comment.find('.deleteLoading'); - var $moreIcon = $comment.find('.more'); - - $comment.addClass('disabled'); - $loading.removeClass('hidden'); - $moreIcon.addClass('hidden'); - - $comment.data('commentEl', $comment); - - this.collection.get(commentId).destroy({ - success: function() { - $comment.data('commentEl').remove(); - $comment.remove(); - }, - error: function() { - $loading.addClass('hidden'); - $moreIcon.removeClass('hidden'); - $comment.removeClass('disabled'); - - OC.Notification.showTemporary(t('comments', 'Error occurred while retrieving comment with ID {id}', {id: commentId})); + _onClickComment: function(ev) { + var $row = $(ev.target) + if (!$row.is('.comment')) { + $row = $row.closest('.comment') } - }); + $row.removeClass('collapsed') + }, + + _onClickCloseComment: function(ev) { + ev.preventDefault() + var $row = $(ev.target).closest('.comment') + $row.data('commentEl').removeClass('hidden') + $row.remove() + return false + }, + + _onClickDeleteComment: function(ev) { + ev.preventDefault() + var $comment = $(ev.target).closest('.comment') + var commentId = $comment.data('id') + var $loading = $comment.find('.deleteLoading') + var $moreIcon = $comment.find('.more') + + $comment.addClass('disabled') + $loading.removeClass('hidden') + $moreIcon.addClass('hidden') + + $comment.data('commentEl', $comment) + + this.collection.get(commentId).destroy({ + success: function() { + $comment.data('commentEl').remove() + $comment.remove() + }, + error: function() { + $loading.addClass('hidden') + $moreIcon.removeClass('hidden') + $comment.removeClass('disabled') + + OC.Notification.showTemporary(t('comments', 'Error occurred while retrieving comment with ID {id}', { id: commentId })) + } + }) - return false; - }, + return false + }, - _onClickShowMore: function(ev) { - ev.preventDefault(); - this.nextPage(); - }, + _onClickShowMore: function(ev) { + ev.preventDefault() + this.nextPage() + }, - /** + /** * takes care of updating comment element states after submit (either new * comment or edit). * @@ -594,106 +594,106 @@ * @param {jQuery} $form * @private */ - _onSubmitSuccess: function(model, $form) { - var $submit = $form.find('.submit'); - var $loading = $form.find('.submitLoading'); - - $submit.removeClass('hidden'); - $loading.addClass('hidden'); - }, - - _commentBodyHTML2Plain: function($el) { - var $comment = $el.clone(); - - $comment.find('.avatar-name-wrapper').each(function () { - var $this = $(this), - $inserted = $this.parent(), - userId = $this.find('.avatar').data('username'); - if (userId.indexOf(' ') !== -1) { - $inserted.html('@"' + userId + '"'); - } else { - $inserted.html('@' + userId); - } - }); + _onSubmitSuccess: function(model, $form) { + var $submit = $form.find('.submit') + var $loading = $form.find('.submitLoading') + + $submit.removeClass('hidden') + $loading.addClass('hidden') + }, + + _commentBodyHTML2Plain: function($el) { + var $comment = $el.clone() + + $comment.find('.avatar-name-wrapper').each(function() { + var $this = $(this) + var $inserted = $this.parent() + var userId = $this.find('.avatar').data('username') + if (userId.indexOf(' ') !== -1) { + $inserted.html('@"' + userId + '"') + } else { + $inserted.html('@' + userId) + } + }) - $comment.html(OCP.Comments.richToPlain($comment.html())); + $comment.html(OCP.Comments.richToPlain($comment.html())) - var oldHtml; - var html = $comment.html(); - do { + var oldHtml + var html = $comment.html() + do { // replace works one by one - oldHtml = html; - html = oldHtml.replace("<br>", "\n"); // preserve line breaks - } while(oldHtml !== html); - $comment.html(html); - - return $comment.text(); - }, - - _onSubmitComment: function(e) { - var self = this; - var $form = $(e.target); - var commentId = $form.closest('.comment').data('id'); - var currentUser = OC.getCurrentUser(); - var $submit = $form.find('.submit'); - var $loading = $form.find('.submitLoading'); - var $commentField = $form.find('.message'); - var message = $commentField.text().trim(); - e.preventDefault(); - - if (!message.length || message.length > this._commentMaxLength) { - return; - } + oldHtml = html + html = oldHtml.replace('<br>', '\n') // preserve line breaks + } while (oldHtml !== html) + $comment.html(html) + + return $comment.text() + }, + + _onSubmitComment: function(e) { + var self = this + var $form = $(e.target) + var commentId = $form.closest('.comment').data('id') + var currentUser = OC.getCurrentUser() + var $submit = $form.find('.submit') + var $loading = $form.find('.submitLoading') + var $commentField = $form.find('.message') + var message = $commentField.text().trim() + e.preventDefault() + + if (!message.length || message.length > this._commentMaxLength) { + return + } - $commentField.prop('contenteditable', false); - $submit.addClass('hidden'); - $loading.removeClass('hidden'); + $commentField.prop('contenteditable', false) + $submit.addClass('hidden') + $loading.removeClass('hidden') - message = this._commentBodyHTML2Plain($commentField); - if (commentId) { + message = this._commentBodyHTML2Plain($commentField) + if (commentId) { // edit mode - var comment = this.collection.get(commentId); - comment.save({ - message: message - }, { - success: function(model) { - self._onSubmitSuccess(model, $form); - if(model.get('message').trim() === model.previous('message').trim()) { + var comment = this.collection.get(commentId) + comment.save({ + message: message + }, { + success: function(model) { + self._onSubmitSuccess(model, $form) + if (model.get('message').trim() === model.previous('message').trim()) { // model change event doesn't trigger, manually remove the row. - var $row = $form.closest('.comment'); - $row.data('commentEl').removeClass('hidden'); - $row.remove(); + var $row = $form.closest('.comment') + $row.data('commentEl').removeClass('hidden') + $row.remove() + } + }, + error: function() { + self._onSubmitError($form, commentId) } - }, - error: function() { - self._onSubmitError($form, commentId); - } - }); - } else { - this.collection.create({ - actorId: currentUser.uid, - actorDisplayName: currentUser.displayName, - actorType: 'users', - verb: 'comment', - message: message, - creationDateTime: (new Date()).toUTCString() - }, { - at: 0, - // wait for real creation before adding - wait: true, - success: function(model) { - self._onSubmitSuccess(model, $form); - }, - error: function() { - self._onSubmitError($form, undefined); - } - }); - } + }) + } else { + this.collection.create({ + actorId: currentUser.uid, + actorDisplayName: currentUser.displayName, + actorType: 'users', + verb: 'comment', + message: message, + creationDateTime: (new Date()).toUTCString() + }, { + at: 0, + // wait for real creation before adding + wait: true, + success: function(model) { + self._onSubmitSuccess(model, $form) + }, + error: function() { + self._onSubmitError($form, undefined) + } + }) + } - return false; - }, + return false + }, - /** + /** * takes care of updating the UI after an error on submit (either new * comment or edit). * @@ -701,51 +701,51 @@ * @param {string|undefined} commentId * @private */ - _onSubmitError: function($form, commentId) { - $form.find('.submit').removeClass('hidden'); - $form.find('.submitLoading').addClass('hidden'); - $form.find('.message').prop('contenteditable', true); - - if(!_.isUndefined(commentId)) { - OC.Notification.show(t('comments', 'Error occurred while updating comment with id {id}', {id: commentId}), {type: 'error'}); - } else { - OC.Notification.show(t('comments', 'Error occurred while posting comment'), {type: 'error'}); - } - }, + _onSubmitError: function($form, commentId) { + $form.find('.submit').removeClass('hidden') + $form.find('.submitLoading').addClass('hidden') + $form.find('.message').prop('contenteditable', true) - /** + if (!_.isUndefined(commentId)) { + OC.Notification.show(t('comments', 'Error occurred while updating comment with id {id}', { id: commentId }), { type: 'error' }) + } else { + OC.Notification.show(t('comments', 'Error occurred while posting comment'), { type: 'error' }) + } + }, + + /** * ensures the contenteditable div is really empty, when user removed * all input, so that the placeholder will be shown again * * @private */ - _onTextChange: function() { - var $message = $('#commentsTabView').find('.newCommentForm div.message'); - if(!$message.text().trim().length) { - $message.empty(); - } - }, + _onTextChange: function() { + var $message = $('#commentsTabView').find('.newCommentForm div.message') + if (!$message.text().trim().length) { + $message.empty() + } + }, - /** + /** * Limit pasting to plain text * * @param e * @private */ - _onPaste: function (e) { - e.preventDefault(); - var text = e.originalEvent.clipboardData.getData("text/plain"); - document.execCommand('insertText', false, text); - }, + _onPaste: function(e) { + e.preventDefault() + var text = e.originalEvent.clipboardData.getData('text/plain') + document.execCommand('insertText', false, text) + }, - /** + /** * Returns whether the given message is long and needs * collapsing */ - _isLong: function(message) { - return message.length > 250 || (message.match(/\n/g) || []).length > 1; - } - }); + _isLong: function(message) { + return message.length > 250 || (message.match(/\n/g) || []).length > 1 + } + }) - OCA.Comments.CommentsTabView = CommentsTabView; -})(OC, OCA); + OCA.Comments.CommentsTabView = CommentsTabView +})(OC, OCA) diff --git a/apps/comments/src/commentsummarymodel.js b/apps/comments/src/commentsummarymodel.js index ffabbc30fb4ebac5ac6bd2a9c7f4563d63261629..1a3002a9f78d64bb4f29f608f2d1598147bea5b7 100644 --- a/apps/comments/src/commentsummarymodel.js +++ b/apps/comments/src/commentsummarymodel.js @@ -12,7 +12,7 @@ _.extend(OC.Files.Client, { PROPERTY_READMARKER: '{' + OC.Files.Client.NS_OWNCLOUD + '}readMarker' - }); + }) /** * @class OCA.Comments.CommentSummaryModel @@ -24,45 +24,47 @@ */ var CommentSummaryModel = OC.Backbone.Model.extend( /** @lends OCA.Comments.CommentSummaryModel.prototype */ { - sync: OC.Backbone.davSync, + sync: OC.Backbone.davSync, - /** + /** * Object type * * @type string */ - _objectType: 'files', + _objectType: 'files', - /** + /** * Object id * * @type string */ - _objectId: null, + _objectId: null, - davProperties: { - 'readMarker': OC.Files.Client.PROPERTY_READMARKER - }, + davProperties: { + 'readMarker': OC.Files.Client.PROPERTY_READMARKER + }, - /** - * Initializes the summary model - * - * @param {string} [options.objectType] object type - * @param {string} [options.objectId] object id - */ - initialize: function(attrs, options) { - options = options || {}; - if (options.objectType) { - this._objectType = options.objectType; - } - }, + /** + * Initializes the summary model + * + * @param {any} [attrs] ignored + * @param {Object} [options] destructuring object + * @param {string} [options.objectType] object type + * @param {string} [options.objectId] object id + */ + initialize: function(attrs, options) { + options = options || {} + if (options.objectType) { + this._objectType = options.objectType + } + }, - url: function() { - return OC.linkToRemote('dav') + '/comments/' + - encodeURIComponent(this._objectType) + '/' + - encodeURIComponent(this.id) + '/'; - } - }); + url: function() { + return OC.linkToRemote('dav') + '/comments/' + + encodeURIComponent(this._objectType) + '/' + + encodeURIComponent(this.id) + '/' + } + }) - OCA.Comments.CommentSummaryModel = CommentSummaryModel; -})(OC, OCA); + OCA.Comments.CommentSummaryModel = CommentSummaryModel +})(OC, OCA) diff --git a/apps/comments/src/filesplugin.js b/apps/comments/src/filesplugin.js index 2073be09b69fa5dbef240224a5c6b4d354a689ae..e315dd2fef850b2d9ff74cd1fc88599cdfb7353d 100644 --- a/apps/comments/src/filesplugin.js +++ b/apps/comments/src/filesplugin.js @@ -8,20 +8,18 @@ * */ -/* global Handlebars */ - (function() { _.extend(OC.Files.Client, { PROPERTY_COMMENTS_UNREAD: '{' + OC.Files.Client.NS_OWNCLOUD + '}comments-unread' - }); + }) - OCA.Comments = _.extend({}, OCA.Comments); + OCA.Comments = _.extend({}, OCA.Comments) if (!OCA.Comments) { /** * @namespace */ - OCA.Comments = {}; + OCA.Comments = {} } /** @@ -38,43 +36,43 @@ count: count, countMessage: n('comments', '%n unread comment', '%n unread comments', count), iconUrl: OC.imagePath('core', 'actions/comment') - }); + }) }, attach: function(fileList) { - var self = this; + var self = this if (this.ignoreLists.indexOf(fileList.id) >= 0) { - return; + return } - fileList.registerTabView(new OCA.Comments.CommentsTabView('commentsTabView')); + fileList.registerTabView(new OCA.Comments.CommentsTabView('commentsTabView')) - var oldGetWebdavProperties = fileList._getWebdavProperties; + var oldGetWebdavProperties = fileList._getWebdavProperties fileList._getWebdavProperties = function() { - var props = oldGetWebdavProperties.apply(this, arguments); - props.push(OC.Files.Client.PROPERTY_COMMENTS_UNREAD); - return props; - }; + var props = oldGetWebdavProperties.apply(this, arguments) + props.push(OC.Files.Client.PROPERTY_COMMENTS_UNREAD) + return props + } fileList.filesClient.addFileInfoParser(function(response) { - var data = {}; - var props = response.propStat[0].properties; - var commentsUnread = props[OC.Files.Client.PROPERTY_COMMENTS_UNREAD]; + var data = {} + var props = response.propStat[0].properties + var commentsUnread = props[OC.Files.Client.PROPERTY_COMMENTS_UNREAD] if (!_.isUndefined(commentsUnread) && commentsUnread !== '') { - data.commentsUnread = parseInt(commentsUnread, 10); + data.commentsUnread = parseInt(commentsUnread, 10) } - return data; - }); + return data + }) - fileList.$el.addClass('has-comments'); - var oldCreateRow = fileList._createRow; + fileList.$el.addClass('has-comments') + var oldCreateRow = fileList._createRow fileList._createRow = function(fileData) { - var $tr = oldCreateRow.apply(this, arguments); + var $tr = oldCreateRow.apply(this, arguments) if (fileData.commentsUnread) { - $tr.attr('data-comments-unread', fileData.commentsUnread); + $tr.attr('data-comments-unread', fileData.commentsUnread) } - return $tr; - }; + return $tr + } // register "comment" action for reading comments fileList.fileActions.registerAction({ @@ -94,35 +92,35 @@ permissions: OC.PERMISSION_READ, type: OCA.Files.FileActions.TYPE_INLINE, render: function(actionSpec, isDefault, context) { - var $file = context.$file; - var unreadComments = $file.data('comments-unread'); + var $file = context.$file + var unreadComments = $file.data('comments-unread') if (unreadComments) { - var $actionLink = $(self._formatCommentCount(unreadComments)); - context.$file.find('a.name>span.fileactions').append($actionLink); - return $actionLink; + var $actionLink = $(self._formatCommentCount(unreadComments)) + context.$file.find('a.name>span.fileactions').append($actionLink) + return $actionLink } - return ''; + return '' }, actionHandler: function(fileName, context) { - context.$file.find('.action-comment').tooltip('hide'); + context.$file.find('.action-comment').tooltip('hide') // open sidebar in comments section - context.fileList.showDetailsView(fileName, 'commentsTabView'); + context.fileList.showDetailsView(fileName, 'commentsTabView') } - }); + }) // add attribute to "elementToFile" - var oldElementToFile = fileList.elementToFile; + var oldElementToFile = fileList.elementToFile fileList.elementToFile = function($el) { - var fileInfo = oldElementToFile.apply(this, arguments); - var commentsUnread = $el.data('comments-unread'); + var fileInfo = oldElementToFile.apply(this, arguments) + var commentsUnread = $el.data('comments-unread') if (commentsUnread) { - fileInfo.commentsUnread = commentsUnread; + fileInfo.commentsUnread = commentsUnread } - return fileInfo; - }; + return fileInfo + } } - }; + } -})(); +})() -OC.Plugins.register('OCA.Files.FileList', OCA.Comments.FilesPlugin); +OC.Plugins.register('OCA.Files.FileList', OCA.Comments.FilesPlugin) diff --git a/apps/comments/src/search.js b/apps/comments/src/search.js index 8e0a35ff6edd9174c33c3b5c04c21d32c9bb9dc1..c62726d90827a83cc4d393532cf9af3677c3e81b 100644 --- a/apps/comments/src/search.js +++ b/apps/comments/src/search.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2014 * @@ -8,15 +9,15 @@ * */ (function(OC, OCA, $) { - "use strict"; + 'use strict' /** * Construct a new FileActions instance * @constructs Files */ var Comment = function() { - this.initialize(); - }; + this.initialize() + } Comment.prototype = { @@ -27,25 +28,25 @@ */ initialize: function() { - var self = this; + var self = this this.fileAppLoaded = function() { - return !!OCA.Files && !!OCA.Files.App; - }; + return !!OCA.Files && !!OCA.Files.App + } function inFileList($row, result) { - return false; + return false - if (! self.fileAppLoaded()) { - return false; + if (!self.fileAppLoaded()) { + return false } - var dir = self.fileList.getCurrentDirectory().replace(/\/+$/,''); - var resultDir = OC.dirname(result.path); - return dir === resultDir && self.fileList.inList(result.name); + var dir = self.fileList.getCurrentDirectory().replace(/\/+$/, '') + var resultDir = OC.dirname(result.path) + return dir === resultDir && self.fileList.inList(result.name) } function hideNoFilterResults() { - var $nofilterresults = $('.nofilterresults'); - if ( ! $nofilterresults.hasClass('hidden') ) { - $nofilterresults.addClass('hidden'); + var $nofilterresults = $('.nofilterresults') + if (!$nofilterresults.hasClass('hidden')) { + $nofilterresults.addClass('hidden') } } @@ -64,73 +65,73 @@ */ this.renderCommentResult = function($row, result) { if (inFileList($row, result)) { - return null; + return null } - hideNoFilterResults(); - /*render preview icon, show path beneath filename, + hideNoFilterResults() + /* render preview icon, show path beneath filename, show size and last modified date on the right */ - this.updateLegacyMimetype(result); + this.updateLegacyMimetype(result) - var $pathDiv = $('<div>').addClass('path').text(result.path); + var $pathDiv = $('<div>').addClass('path').text(result.path) - var $avatar = $('<div>'); + var $avatar = $('<div>') $avatar.addClass('avatar') .css('display', 'inline-block') .css('vertical-align', 'middle') - .css('margin', '0 5px 2px 3px'); + .css('margin', '0 5px 2px 3px') if (result.authorName) { - $avatar.avatar(result.authorId, 21, undefined, false, undefined, result.authorName); + $avatar.avatar(result.authorId, 21, undefined, false, undefined, result.authorName) } else { - $avatar.avatar(result.authorId, 21); + $avatar.avatar(result.authorId, 21) } - $row.find('td.info div.name').after($pathDiv).text(result.comment).prepend($('<span>').addClass('path').css('margin-right', '5px').text(result.authorName)).prepend($avatar); - $row.find('td.result a').attr('href', result.link); + $row.find('td.info div.name').after($pathDiv).text(result.comment).prepend($('<span>').addClass('path').css('margin-right', '5px').text(result.authorName)).prepend($avatar) + $row.find('td.result a').attr('href', result.link) $row.find('td.icon') .css('background-image', 'url(' + OC.imagePath('core', 'actions/comment') + ')') - .css('opacity', '.4'); - var dir = OC.dirname(result.path); + .css('opacity', '.4') + var dir = OC.dirname(result.path) // "result.path" does not include a leading "/", so "OC.dirname" // returns the path itself for files or folders in the root. if (dir === result.path) { - dir = '/'; + dir = '/' } $row.find('td.info a').attr('href', - OC.generateUrl('/apps/files/?dir={dir}&scrollto={scrollto}', {dir: dir, scrollto: result.fileName}) - ); + OC.generateUrl('/apps/files/?dir={dir}&scrollto={scrollto}', { dir: dir, scrollto: result.fileName }) + ) - return $row; - }; + return $row + } this.handleCommentClick = function($row, result, event) { if (self.fileAppLoaded() && self.fileList.id === 'files') { - self.fileList.changeDirectory(OC.dirname(result.path)); - self.fileList.scrollTo(result.name); - return false; + self.fileList.changeDirectory(OC.dirname(result.path)) + self.fileList.scrollTo(result.name) + return false } else { - return true; + return true } - }; + } - this.updateLegacyMimetype = function (result) { + this.updateLegacyMimetype = function(result) { // backward compatibility: if (!result.mime && result.mime_type) { - result.mime = result.mime_type; + result.mime = result.mime_type } - }; - this.setFileList = function (fileList) { - this.fileList = fileList; - }; + } + this.setFileList = function(fileList) { + this.fileList = fileList + } - OC.Plugins.register('OCA.Search.Core', this); + OC.Plugins.register('OCA.Search.Core', this) }, attach: function(search) { - search.setRenderer('comment', this.renderCommentResult.bind(this)); - search.setHandler('comment', this.handleCommentClick.bind(this)); + search.setRenderer('comment', this.renderCommentResult.bind(this)) + search.setHandler('comment', this.handleCommentClick.bind(this)) } - }; + } - OCA.Search.comment = new Comment(); -})(OC, OCA, $); + OCA.Search.comment = new Comment() +})(OC, OCA, $) diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 8db23fca3b5e983d002525bbd60f753d306c342d..c63c1e31522d18d647aad667e7bf7928939868fd 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -727,11 +727,11 @@ OC.Uploader.prototype = _.extend({ * * @param {array} selection of files to upload * @param {object} callbacks - object with several callback methods - * @param {function} callbacks.onNoConflicts - * @param {function} callbacks.onSkipConflicts - * @param {function} callbacks.onReplaceConflicts - * @param {function} callbacks.onChooseConflicts - * @param {function} callbacks.onCancel + * @param {Function} callbacks.onNoConflicts + * @param {Function} callbacks.onSkipConflicts + * @param {Function} callbacks.onReplaceConflicts + * @param {Function} callbacks.onChooseConflicts + * @param {Function} callbacks.onCancel */ checkExistingFiles: function (selection, callbacks) { var fileList = this.fileList; diff --git a/apps/files/js/files.js b/apps/files/js/files.js index b46aeb26eee66948d1dc0756d957ef301d280c2d..395b2a53c1000b7fc5e8d9506ec7a1b47adeb23d 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -336,7 +336,7 @@ * - JS periodically checks for this cookie and then knows when the download has started to call the callback * * @param {string} url download URL - * @param {function} callback function to call once the download has started + * @param {Function} callback function to call once the download has started */ handleDownload: function(url, callback) { var randomToken = Math.random().toString(36).substring(2), diff --git a/apps/files_external/js/statusmanager.js b/apps/files_external/js/statusmanager.js index 0c0c8b36c74df96b6399b81ce2726af9cf52b868..ebedd91f2acec8c0e2539d3b999941422469cc84 100644 --- a/apps/files_external/js/statusmanager.js +++ b/apps/files_external/js/statusmanager.js @@ -125,7 +125,7 @@ OCA.Files_External.StatusManager = { /** * Function to get external mount point list from the files_external API - * @param {function} afterCallback function to be executed + * @param {Function} afterCallback function to be executed */ getMountPointList: function (afterCallback) { diff --git a/apps/files_sharing/.eslintrc.js b/apps/files_sharing/.eslintrc.js deleted file mode 100644 index 214cccecd84b0e0da0cd17e02242e269492155d6..0000000000000000000000000000000000000000 --- a/apps/files_sharing/.eslintrc.js +++ /dev/null @@ -1,22 +0,0 @@ -module.exports = { - env: { - browser: true, - es6: true - }, - globals: { - t: true, - n: true, - OC: true, - OCA: true - }, - extends: 'eslint:recommended', - parserOptions: { - sourceType: 'module' - }, - rules: { - indent: ['error', 'tab'], - 'linebreak-style': ['error', 'unix'], - quotes: ['error', 'single'], - semi: ['error', 'always'] - } -}; diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js index b6ca71e15d11164ed8f52a050d67279d977d1377..29cd3385c3ae18275d1c8ab8e86d9f4589b9b221 100644 --- a/apps/files_sharing/js/app.js +++ b/apps/files_sharing/js/app.js @@ -12,7 +12,7 @@ if (!OCA.Sharing) { /** * @namespace OCA.Sharing */ - OCA.Sharing = {}; + OCA.Sharing = {} } /** * @namespace @@ -25,7 +25,7 @@ OCA.Sharing.App = { initSharingIn: function($el) { if (this._inFileList) { - return this._inFileList; + return this._inFileList } this._inFileList = new OCA.Sharing.FileList( @@ -40,19 +40,19 @@ OCA.Sharing.App = { // if handling the event with the file list already created. shown: true } - ); + ) - this._extendFileList(this._inFileList); - this._inFileList.appName = t('files_sharing', 'Shared with you'); - this._inFileList.$el.find('#emptycontent').html('<div class="icon-shared"></div>' + - '<h2>' + t('files_sharing', 'Nothing shared with you yet') + '</h2>' + - '<p>' + t('files_sharing', 'Files and folders others share with you will show up here') + '</p>'); - return this._inFileList; + this._extendFileList(this._inFileList) + this._inFileList.appName = t('files_sharing', 'Shared with you') + this._inFileList.$el.find('#emptycontent').html('<div class="icon-shared"></div>' + + '<h2>' + t('files_sharing', 'Nothing shared with you yet') + '</h2>' + + '<p>' + t('files_sharing', 'Files and folders others share with you will show up here') + '</p>') + return this._inFileList }, initSharingOut: function($el) { if (this._outFileList) { - return this._outFileList; + return this._outFileList } this._outFileList = new OCA.Sharing.FileList( $el, @@ -66,19 +66,19 @@ OCA.Sharing.App = { // if handling the event with the file list already created. shown: true } - ); + ) - this._extendFileList(this._outFileList); - this._outFileList.appName = t('files_sharing', 'Shared with others'); - this._outFileList.$el.find('#emptycontent').html('<div class="icon-shared"></div>' + - '<h2>' + t('files_sharing', 'Nothing shared yet') + '</h2>' + - '<p>' + t('files_sharing', 'Files and folders you share will show up here') + '</p>'); - return this._outFileList; + this._extendFileList(this._outFileList) + this._outFileList.appName = t('files_sharing', 'Shared with others') + this._outFileList.$el.find('#emptycontent').html('<div class="icon-shared"></div>' + + '<h2>' + t('files_sharing', 'Nothing shared yet') + '</h2>' + + '<p>' + t('files_sharing', 'Files and folders you share will show up here') + '</p>') + return this._outFileList }, initSharingLinks: function($el) { if (this._linkFileList) { - return this._linkFileList; + return this._linkFileList } this._linkFileList = new OCA.Sharing.FileList( $el, @@ -92,19 +92,19 @@ OCA.Sharing.App = { // if handling the event with the file list already created. shown: true } - ); + ) - this._extendFileList(this._linkFileList); - this._linkFileList.appName = t('files_sharing', 'Shared by link'); - this._linkFileList.$el.find('#emptycontent').html('<div class="icon-public"></div>' + - '<h2>' + t('files_sharing', 'No shared links') + '</h2>' + - '<p>' + t('files_sharing', 'Files and folders you share by link will show up here') + '</p>'); - return this._linkFileList; + this._extendFileList(this._linkFileList) + this._linkFileList.appName = t('files_sharing', 'Shared by link') + this._linkFileList.$el.find('#emptycontent').html('<div class="icon-public"></div>' + + '<h2>' + t('files_sharing', 'No shared links') + '</h2>' + + '<p>' + t('files_sharing', 'Files and folders you share by link will show up here') + '</p>') + return this._linkFileList }, initSharingDeleted: function($el) { if (this._deletedFileList) { - return this._deletedFileList; + return this._deletedFileList } this._deletedFileList = new OCA.Sharing.FileList( $el, @@ -119,19 +119,19 @@ OCA.Sharing.App = { // if handling the event with the file list already created. shown: true } - ); + ) - this._extendFileList(this._deletedFileList); - this._deletedFileList.appName = t('files_sharing', 'Deleted shares'); - this._deletedFileList.$el.find('#emptycontent').html('<div class="icon-share"></div>' + - '<h2>' + t('files_sharing', 'No deleted shares') + '</h2>' + - '<p>' + t('files_sharing', 'Shares you deleted will show up here') + '</p>'); - return this._deletedFileList; + this._extendFileList(this._deletedFileList) + this._deletedFileList.appName = t('files_sharing', 'Deleted shares') + this._deletedFileList.$el.find('#emptycontent').html('<div class="icon-share"></div>' + + '<h2>' + t('files_sharing', 'No deleted shares') + '</h2>' + + '<p>' + t('files_sharing', 'Shares you deleted will show up here') + '</p>') + return this._deletedFileList }, initShareingOverview: function($el) { if (this._overviewFileList) { - return this._overviewFileList; + return this._overviewFileList } this._overviewFileList = new OCA.Sharing.FileList( $el, @@ -144,43 +144,43 @@ OCA.Sharing.App = { // if handling the event with the file list already created. shown: true } - ); + ) - this._extendFileList(this._overviewFileList); - this._overviewFileList.appName = t('files_sharing', 'Shares'); - this._overviewFileList.$el.find('#emptycontent').html('<div class="icon-share"></div>' + - '<h2>' + t('files_sharing', 'No shares') + '</h2>' + - '<p>' + t('files_sharing', 'Shares will show up here') + '</p>'); - return this._overviewFileList; + this._extendFileList(this._overviewFileList) + this._overviewFileList.appName = t('files_sharing', 'Shares') + this._overviewFileList.$el.find('#emptycontent').html('<div class="icon-share"></div>' + + '<h2>' + t('files_sharing', 'No shares') + '</h2>' + + '<p>' + t('files_sharing', 'Shares will show up here') + '</p>') + return this._overviewFileList }, removeSharingIn: function() { if (this._inFileList) { - this._inFileList.$fileList.empty(); + this._inFileList.$fileList.empty() } }, removeSharingOut: function() { if (this._outFileList) { - this._outFileList.$fileList.empty(); + this._outFileList.$fileList.empty() } }, removeSharingLinks: function() { if (this._linkFileList) { - this._linkFileList.$fileList.empty(); + this._linkFileList.$fileList.empty() } }, removeSharingDeleted: function() { if (this._deletedFileList) { - this._deletedFileList.$fileList.empty(); + this._deletedFileList.$fileList.empty() } }, removeSharingOverview: function() { if (this._overviewFileList) { - this._overviewFileList.$fileList.empty(); + this._overviewFileList.$fileList.empty() } }, @@ -188,46 +188,46 @@ OCA.Sharing.App = { * Destroy the app */ destroy: function() { - OCA.Files.fileActions.off('setDefault.app-sharing', this._onActionsUpdated); - OCA.Files.fileActions.off('registerAction.app-sharing', this._onActionsUpdated); - this.removeSharingIn(); - this.removeSharingOut(); - this.removeSharingLinks(); - this._inFileList = null; - this._outFileList = null; - this._linkFileList = null; - this._overviewFileList = null; - delete this._globalActionsInitialized; + OCA.Files.fileActions.off('setDefault.app-sharing', this._onActionsUpdated) + OCA.Files.fileActions.off('registerAction.app-sharing', this._onActionsUpdated) + this.removeSharingIn() + this.removeSharingOut() + this.removeSharingLinks() + this._inFileList = null + this._outFileList = null + this._linkFileList = null + this._overviewFileList = null + delete this._globalActionsInitialized }, _createFileActions: function() { // inherit file actions from the files app - var fileActions = new OCA.Files.FileActions(); + var fileActions = new OCA.Files.FileActions() // note: not merging the legacy actions because legacy apps are not // compatible with the sharing overview and need to be adapted first - fileActions.registerDefaultActions(); - fileActions.merge(OCA.Files.fileActions); + fileActions.registerDefaultActions() + fileActions.merge(OCA.Files.fileActions) if (!this._globalActionsInitialized) { // in case actions are registered later - this._onActionsUpdated = _.bind(this._onActionsUpdated, this); - OCA.Files.fileActions.on('setDefault.app-sharing', this._onActionsUpdated); - OCA.Files.fileActions.on('registerAction.app-sharing', this._onActionsUpdated); - this._globalActionsInitialized = true; + this._onActionsUpdated = _.bind(this._onActionsUpdated, this) + OCA.Files.fileActions.on('setDefault.app-sharing', this._onActionsUpdated) + OCA.Files.fileActions.on('registerAction.app-sharing', this._onActionsUpdated) + this._globalActionsInitialized = true } // when the user clicks on a folder, redirect to the corresponding // folder in the files app instead of opening it directly - fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { - OCA.Files.App.setActiveView('files', {silent: true}); - OCA.Files.App.fileList.changeDirectory(OC.joinPaths(context.$file.attr('data-path'), filename), true, true); - }); - fileActions.setDefault('dir', 'Open'); - return fileActions; + fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function(filename, context) { + OCA.Files.App.setActiveView('files', { silent: true }) + OCA.Files.App.fileList.changeDirectory(OC.joinPaths(context.$file.attr('data-path'), filename), true, true) + }) + fileActions.setDefault('dir', 'Open') + return fileActions }, _restoreShareAction: function() { - var fileActions = new OCA.Files.FileActions(); + var fileActions = new OCA.Files.FileActions() fileActions.registerAction({ name: 'Restore', displayName: '', @@ -237,70 +237,70 @@ OCA.Sharing.App = { iconClass: 'icon-history', type: OCA.Files.FileActions.TYPE_INLINE, actionHandler: function(fileName, context) { - var shareId = context.$file.data('shareId'); + var shareId = context.$file.data('shareId') $.post(OC.linkToOCS('apps/files_sharing/api/v1/deletedshares', 2) + shareId) - .success(function(result) { - context.fileList.remove(context.fileInfoModel.attributes.name); - }).fail(function() { - OC.Notification.showTemporary(t('files_sharing', 'Something happened. Unable to restore the share.')); - }); + .success(function(result) { + context.fileList.remove(context.fileInfoModel.attributes.name) + }).fail(function() { + OC.Notification.showTemporary(t('files_sharing', 'Something happened. Unable to restore the share.')) + }) } - }); - return fileActions; + }) + return fileActions }, _onActionsUpdated: function(ev) { _.each([this._inFileList, this._outFileList, this._linkFileList], function(list) { if (!list) { - return; + return } if (ev.action) { - list.fileActions.registerAction(ev.action); + list.fileActions.registerAction(ev.action) } else if (ev.defaultAction) { list.fileActions.setDefault( ev.defaultAction.mime, ev.defaultAction.name - ); + ) } - }); + }) }, _extendFileList: function(fileList) { // remove size column from summary - fileList.fileSummary.$el.find('.filesize').remove(); + fileList.fileSummary.$el.find('.filesize').remove() } -}; +} $(document).ready(function() { $('#app-content-sharingin').on('show', function(e) { - OCA.Sharing.App.initSharingIn($(e.target)); - }); + OCA.Sharing.App.initSharingIn($(e.target)) + }) $('#app-content-sharingin').on('hide', function() { - OCA.Sharing.App.removeSharingIn(); - }); + OCA.Sharing.App.removeSharingIn() + }) $('#app-content-sharingout').on('show', function(e) { - OCA.Sharing.App.initSharingOut($(e.target)); - }); + OCA.Sharing.App.initSharingOut($(e.target)) + }) $('#app-content-sharingout').on('hide', function() { - OCA.Sharing.App.removeSharingOut(); - }); + OCA.Sharing.App.removeSharingOut() + }) $('#app-content-sharinglinks').on('show', function(e) { - OCA.Sharing.App.initSharingLinks($(e.target)); - }); + OCA.Sharing.App.initSharingLinks($(e.target)) + }) $('#app-content-sharinglinks').on('hide', function() { - OCA.Sharing.App.removeSharingLinks(); - }); + OCA.Sharing.App.removeSharingLinks() + }) $('#app-content-deletedshares').on('show', function(e) { - OCA.Sharing.App.initSharingDeleted($(e.target)); - }); + OCA.Sharing.App.initSharingDeleted($(e.target)) + }) $('#app-content-deletedshares').on('hide', function() { - OCA.Sharing.App.removeSharingDeleted(); - }); + OCA.Sharing.App.removeSharingDeleted() + }) $('#app-content-shareoverview').on('show', function(e) { - OCA.Sharing.App.initShareingOverview($(e.target)); - }); + OCA.Sharing.App.initShareingOverview($(e.target)) + }) $('#app-content-shareoverview').on('hide', function() { - OCA.Sharing.App.removeSharingOverview(); - }); -}); + OCA.Sharing.App.removeSharingOverview() + }) +}) diff --git a/apps/files_sharing/js/dist/additionalScripts.js b/apps/files_sharing/js/dist/additionalScripts.js index 1f93f3d0954b631550763d68074fff27fb9ecba9..79143ae714b0cb190afea02cf3a42ed082d13ebb 100644 Binary files a/apps/files_sharing/js/dist/additionalScripts.js and b/apps/files_sharing/js/dist/additionalScripts.js differ diff --git a/apps/files_sharing/js/dist/additionalScripts.js.map b/apps/files_sharing/js/dist/additionalScripts.js.map index 9db2068bf98ddc4a3e9a6cab1df5f0bb4307f3de..143949c1e3e34685528234e8f6c322e47e3a2279 100644 Binary files a/apps/files_sharing/js/dist/additionalScripts.js.map and b/apps/files_sharing/js/dist/additionalScripts.js.map differ diff --git a/apps/files_sharing/js/dist/collaboration.js b/apps/files_sharing/js/dist/collaboration.js index 22509085e98564766b9758c33e8f7d46a9872b80..5dd0e126a1fa26366de1da39d8447ca6394ce65b 100644 Binary files a/apps/files_sharing/js/dist/collaboration.js and b/apps/files_sharing/js/dist/collaboration.js differ diff --git a/apps/files_sharing/js/dist/collaboration.js.map b/apps/files_sharing/js/dist/collaboration.js.map index 0b92d9f393df09145c3dde24d7f3a00bdf105c71..ddca7ef495979be88095f7258a346311f1f95184 100644 Binary files a/apps/files_sharing/js/dist/collaboration.js.map and b/apps/files_sharing/js/dist/collaboration.js.map differ diff --git a/apps/files_sharing/js/dist/files_sharing.4.js b/apps/files_sharing/js/dist/files_sharing.4.js index 8ac4caf8c2b4d2be3be5c739cc2f9b195f05e923..7929a7a595104c8d29e0e7d7e5800c19553387f6 100644 Binary files a/apps/files_sharing/js/dist/files_sharing.4.js and b/apps/files_sharing/js/dist/files_sharing.4.js differ diff --git a/apps/files_sharing/js/dist/files_sharing.4.js.map b/apps/files_sharing/js/dist/files_sharing.4.js.map index 8e2fb4898732c743a1d3e910caa59dad4edcd7a0..7ebc04b8ccc7300d72d69e7df6417841eecd2175 100644 Binary files a/apps/files_sharing/js/dist/files_sharing.4.js.map and b/apps/files_sharing/js/dist/files_sharing.4.js.map differ diff --git a/apps/files_sharing/js/dist/files_sharing.js.map b/apps/files_sharing/js/dist/files_sharing.js.map index 8e33e4111a6be22ecfe743864d2c10b4878e3a58..1cb98bff5e4d2ce60f42727decb2ac2a4b6afe00 100644 Binary files a/apps/files_sharing/js/dist/files_sharing.js.map and b/apps/files_sharing/js/dist/files_sharing.js.map differ diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index 7fa38a58c5997c9eb2d711c46e00fa8c3919f72f..5b198300589301205d9b23a7da4f32693e6d0173 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> * @@ -25,428 +26,423 @@ * @param {boolean} [options.linksOnly] true to return only link shares */ var FileList = function($el, options) { - this.initialize($el, options); - }; + this.initialize($el, options) + } FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, /** @lends OCA.Sharing.FileList.prototype */ { - appName: 'Shares', + appName: 'Shares', - /** + /** * Whether the list shows the files shared with the user (true) or * the files that the user shared with others (false). */ - _sharedWithUser: false, - _linksOnly: false, - _showDeleted: false, - _clientSideSort: true, - _allowSelection: false, - _isOverview: false, - - /** + _sharedWithUser: false, + _linksOnly: false, + _showDeleted: false, + _clientSideSort: true, + _allowSelection: false, + _isOverview: false, + + /** * @private */ - initialize: function($el, options) { - OCA.Files.FileList.prototype.initialize.apply(this, arguments); - if (this.initialized) { - return; - } + initialize: function($el, options) { + OCA.Files.FileList.prototype.initialize.apply(this, arguments) + if (this.initialized) { + return + } - // TODO: consolidate both options - if (options && options.sharedWithUser) { - this._sharedWithUser = true; - } - if (options && options.linksOnly) { - this._linksOnly = true; - } - if (options && options.showDeleted) { - this._showDeleted = true; - } - if (options && options.isOverview) { - this._isOverview = true; - } - }, + // TODO: consolidate both options + if (options && options.sharedWithUser) { + this._sharedWithUser = true + } + if (options && options.linksOnly) { + this._linksOnly = true + } + if (options && options.showDeleted) { + this._showDeleted = true + } + if (options && options.isOverview) { + this._isOverview = true + } + }, - _renderRow: function() { + _renderRow: function() { // HACK: needed to call the overridden _renderRow // this is because at the time this class is created // the overriding hasn't been done yet... - return OCA.Files.FileList.prototype._renderRow.apply(this, arguments); - }, + return OCA.Files.FileList.prototype._renderRow.apply(this, arguments) + }, - _createRow: function(fileData) { + _createRow: function(fileData) { // TODO: hook earlier and render the whole row here - var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments); - $tr.find('.filesize').remove(); - $tr.find('td.date').before($tr.children('td:first')); - $tr.find('td.filename input:checkbox').remove(); - $tr.attr('data-share-id', _.pluck(fileData.shares, 'id').join(',')); - if (this._sharedWithUser) { - $tr.attr('data-share-owner', fileData.shareOwner); - $tr.attr('data-mounttype', 'shared-root'); - var permission = parseInt($tr.attr('data-permissions')) | OC.PERMISSION_DELETE; - $tr.attr('data-permissions', permission); - } - if (this._showDeleted) { - var permission = fileData.permissions; - $tr.attr('data-share-permissions', permission); - } - - // add row with expiration date for link only shares - influenced by _createRow of filelist - if (this._linksOnly) { - var expirationTimestamp = 0; - if(fileData.shares && fileData.shares[0].expiration !== null) { - expirationTimestamp = moment(fileData.shares[0].expiration).valueOf(); + var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments) + $tr.find('.filesize').remove() + $tr.find('td.date').before($tr.children('td:first')) + $tr.find('td.filename input:checkbox').remove() + $tr.attr('data-share-id', _.pluck(fileData.shares, 'id').join(',')) + if (this._sharedWithUser) { + $tr.attr('data-share-owner', fileData.shareOwner) + $tr.attr('data-mounttype', 'shared-root') + var permission = parseInt($tr.attr('data-permissions')) | OC.PERMISSION_DELETE + $tr.attr('data-permissions', permission) } - $tr.attr('data-expiration', expirationTimestamp); - - // date column (1000 milliseconds to seconds, 60 seconds, 60 minutes, 24 hours) - // difference in days multiplied by 5 - brightest shade for expiry dates in more than 32 days (160/5) - var modifiedColor = Math.round((expirationTimestamp - (new Date()).getTime()) / 1000 / 60 / 60 / 24 * 5); - // ensure that the brightest color is still readable - if (modifiedColor >= 160) { - modifiedColor = 160; + if (this._showDeleted) { + var permission = fileData.permissions + $tr.attr('data-share-permissions', permission) } - var formatted; - var text; - if (expirationTimestamp > 0) { - formatted = OC.Util.formatDate(expirationTimestamp); - text = OC.Util.relativeModifiedDate(expirationTimestamp); - } else { - formatted = t('files_sharing', 'No expiration date set'); - text = ''; - modifiedColor = 160; - } - td = $('<td></td>').attr({"class": "date"}); - td.append($('<span></span>').attr({ - "class": "modified", - "title": formatted, - "style": 'color:rgb(' + modifiedColor + ',' + modifiedColor + ',' + modifiedColor + ')' + // add row with expiration date for link only shares - influenced by _createRow of filelist + if (this._linksOnly) { + var expirationTimestamp = 0 + if (fileData.shares && fileData.shares[0].expiration !== null) { + expirationTimestamp = moment(fileData.shares[0].expiration).valueOf() + } + $tr.attr('data-expiration', expirationTimestamp) + + // date column (1000 milliseconds to seconds, 60 seconds, 60 minutes, 24 hours) + // difference in days multiplied by 5 - brightest shade for expiry dates in more than 32 days (160/5) + var modifiedColor = Math.round((expirationTimestamp - (new Date()).getTime()) / 1000 / 60 / 60 / 24 * 5) + // ensure that the brightest color is still readable + if (modifiedColor >= 160) { + modifiedColor = 160 + } + + var formatted + var text + if (expirationTimestamp > 0) { + formatted = OC.Util.formatDate(expirationTimestamp) + text = OC.Util.relativeModifiedDate(expirationTimestamp) + } else { + formatted = t('files_sharing', 'No expiration date set') + text = '' + modifiedColor = 160 + } + td = $('<td></td>').attr({ 'class': 'date' }) + td.append($('<span></span>').attr({ + 'class': 'modified', + 'title': formatted, + 'style': 'color:rgb(' + modifiedColor + ',' + modifiedColor + ',' + modifiedColor + ')' }).text(text) - .tooltip({placement: 'top'}) - ); + .tooltip({ placement: 'top' }) + ) - $tr.append(td); - } - return $tr; - }, + $tr.append(td) + } + return $tr + }, - /** + /** * Set whether the list should contain outgoing shares * or incoming shares. * * @param state true for incoming shares, false otherwise */ - setSharedWithUser: function(state) { - this._sharedWithUser = !!state; - }, + setSharedWithUser: function(state) { + this._sharedWithUser = !!state + }, - updateEmptyContent: function() { - var dir = this.getCurrentDirectory(); - if (dir === '/') { + updateEmptyContent: function() { + var dir = this.getCurrentDirectory() + if (dir === '/') { // root has special permissions - this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty); - this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty); + this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty) + this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty) - // hide expiration date header for non link only shares - if (!this._linksOnly) { - this.$el.find('th.column-expiration').addClass('hidden'); + // hide expiration date header for non link only shares + if (!this._linksOnly) { + this.$el.find('th.column-expiration').addClass('hidden') + } + } else { + OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments) } - } - else { - OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments); - } - }, + }, - getDirectoryPermissions: function() { - return OC.PERMISSION_READ | OC.PERMISSION_DELETE; - }, + getDirectoryPermissions: function() { + return OC.PERMISSION_READ | OC.PERMISSION_DELETE + }, - updateStorageStatistics: function() { + updateStorageStatistics: function() { // no op because it doesn't have // storage info like free space / used space - }, + }, - updateRow: function($tr, fileInfo, options) { + updateRow: function($tr, fileInfo, options) { // no-op, suppress re-rendering - return $tr; - }, + return $tr + }, - reload: function() { - this.showMask(); - if (this._reloadCall) { - this._reloadCall.abort(); - } + reload: function() { + this.showMask() + if (this._reloadCall) { + this._reloadCall.abort() + } - // there is only root - this._setCurrentDir('/', false); - - var promises = []; - - var deletedShares = { - url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'deletedshares', - /* jshint camelcase: false */ - data: { - format: 'json', - include_tags: true - }, - type: 'GET', - beforeSend: function (xhr) { - xhr.setRequestHeader('OCS-APIREQUEST', 'true'); - }, - }; - - var shares = { - url: OC.linkToOCS('apps/files_sharing/api/v1') + 'shares', - /* jshint camelcase: false */ - data: { - format: 'json', - shared_with_me: this._sharedWithUser !== false, - include_tags: true - }, - type: 'GET', - beforeSend: function (xhr) { - xhr.setRequestHeader('OCS-APIREQUEST', 'true'); - }, - }; - - var remoteShares = { - url: OC.linkToOCS('apps/files_sharing/api/v1') + 'remote_shares', - /* jshint camelcase: false */ - data: { - format: 'json', - include_tags: true - }, - type: 'GET', - beforeSend: function (xhr) { - xhr.setRequestHeader('OCS-APIREQUEST', 'true'); - }, - }; - - // Add the proper ajax requests to the list and run them - // and make sure we have 2 promises - if (this._showDeleted) { - promises.push($.ajax(deletedShares)); - } else { - promises.push($.ajax(shares)); - - if (this._sharedWithUser !== false || this._isOverview) { - promises.push($.ajax(remoteShares)); + // there is only root + this._setCurrentDir('/', false) + + var promises = [] + + var deletedShares = { + url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'deletedshares', + /* jshint camelcase: false */ + data: { + format: 'json', + include_tags: true + }, + type: 'GET', + beforeSend: function(xhr) { + xhr.setRequestHeader('OCS-APIREQUEST', 'true') + } } - if (this._isOverview) { - shares.data.shared_with_me = !shares.data.shared_with_me; - promises.push($.ajax(shares)); + + var shares = { + url: OC.linkToOCS('apps/files_sharing/api/v1') + 'shares', + /* jshint camelcase: false */ + data: { + format: 'json', + shared_with_me: this._sharedWithUser !== false, + include_tags: true + }, + type: 'GET', + beforeSend: function(xhr) { + xhr.setRequestHeader('OCS-APIREQUEST', 'true') + } + } + + var remoteShares = { + url: OC.linkToOCS('apps/files_sharing/api/v1') + 'remote_shares', + /* jshint camelcase: false */ + data: { + format: 'json', + include_tags: true + }, + type: 'GET', + beforeSend: function(xhr) { + xhr.setRequestHeader('OCS-APIREQUEST', 'true') + } } - } - this._reloadCall = $.when.apply($, promises); - var callBack = this.reloadCallback.bind(this); - return this._reloadCall.then(callBack, callBack); - }, + // Add the proper ajax requests to the list and run them + // and make sure we have 2 promises + if (this._showDeleted) { + promises.push($.ajax(deletedShares)) + } else { + promises.push($.ajax(shares)) - reloadCallback: function(shares, remoteShares, additionalShares) { - delete this._reloadCall; - this.hideMask(); + if (this._sharedWithUser !== false || this._isOverview) { + promises.push($.ajax(remoteShares)) + } + if (this._isOverview) { + shares.data.shared_with_me = !shares.data.shared_with_me + promises.push($.ajax(shares)) + } + } - this.$el.find('#headerSharedWith').text( - t('files_sharing', this._sharedWithUser ? 'Shared by' : 'Shared with') - ); + this._reloadCall = $.when.apply($, promises) + var callBack = this.reloadCallback.bind(this) + return this._reloadCall.then(callBack, callBack) + }, - var files = []; + reloadCallback: function(shares, remoteShares, additionalShares) { + delete this._reloadCall + this.hideMask() - // make sure to use the same format - if (shares[0] && shares[0].ocs) { - shares = shares[0]; - } - if (remoteShares && remoteShares[0] && remoteShares[0].ocs) { - remoteShares = remoteShares[0]; - } - if (additionalShares && additionalShares[0] && additionalShares[0].ocs) { - additionalShares = additionalShares[0]; - } + this.$el.find('#headerSharedWith').text( + t('files_sharing', this._sharedWithUser ? 'Shared by' : 'Shared with') + ) - if (shares.ocs && shares.ocs.data) { - files = files.concat(this._makeFilesFromShares(shares.ocs.data, this._sharedWithUser)); - } + var files = [] - if (remoteShares && remoteShares.ocs && remoteShares.ocs.data) { - files = files.concat(this._makeFilesFromRemoteShares(remoteShares.ocs.data)); - } + // make sure to use the same format + if (shares[0] && shares[0].ocs) { + shares = shares[0] + } + if (remoteShares && remoteShares[0] && remoteShares[0].ocs) { + remoteShares = remoteShares[0] + } + if (additionalShares && additionalShares[0] && additionalShares[0].ocs) { + additionalShares = additionalShares[0] + } - if (additionalShares && additionalShares.ocs && additionalShares.ocs.data) { - files = files.concat(this._makeFilesFromShares(additionalShares.ocs.data, !this._sharedWithUser)); - } + if (shares.ocs && shares.ocs.data) { + files = files.concat(this._makeFilesFromShares(shares.ocs.data, this._sharedWithUser)) + } + + if (remoteShares && remoteShares.ocs && remoteShares.ocs.data) { + files = files.concat(this._makeFilesFromRemoteShares(remoteShares.ocs.data)) + } + if (additionalShares && additionalShares.ocs && additionalShares.ocs.data) { + files = files.concat(this._makeFilesFromShares(additionalShares.ocs.data, !this._sharedWithUser)) + } - this.setFiles(files); - return true; - }, + this.setFiles(files) + return true + }, - _makeFilesFromRemoteShares: function(data) { - var files = data; + _makeFilesFromRemoteShares: function(data) { + var files = data - files = _.chain(files) + files = _.chain(files) // convert share data to file data - .map(function(share) { - var file = { - shareOwner: share.owner + '@' + share.remote.replace(/.*?:\/\//g, ""), - name: OC.basename(share.mountpoint), - mtime: share.mtime * 1000, - mimetype: share.mimetype, - type: share.type, - id: share.file_id, - path: OC.dirname(share.mountpoint), - permissions: share.permissions, - tags: share.tags || [] - }; - - file.shares = [{ - id: share.id, - type: OC.Share.SHARE_TYPE_REMOTE - }]; - return file; - }) - .value(); - return files; - }, - - /** + .map(function(share) { + var file = { + shareOwner: share.owner + '@' + share.remote.replace(/.*?:\/\//g, ''), + name: OC.basename(share.mountpoint), + mtime: share.mtime * 1000, + mimetype: share.mimetype, + type: share.type, + id: share.file_id, + path: OC.dirname(share.mountpoint), + permissions: share.permissions, + tags: share.tags || [] + } + + file.shares = [{ + id: share.id, + type: OC.Share.SHARE_TYPE_REMOTE + }] + return file + }) + .value() + return files + }, + + /** * Converts the OCS API share response data to a file info * list * @param {Array} data OCS API share array * @param {bool} sharedWithUser - * @return {Array.<OCA.Sharing.SharedFileInfo>} array of shared file info + * @returns {Array.<OCA.Sharing.SharedFileInfo>} array of shared file info */ - _makeFilesFromShares: function(data, sharedWithUser) { + _makeFilesFromShares: function(data, sharedWithUser) { /* jshint camelcase: false */ - var files = data; + var files = data - if (this._linksOnly) { - files = _.filter(data, function(share) { - return share.share_type === OC.Share.SHARE_TYPE_LINK; - }); - } + if (this._linksOnly) { + files = _.filter(data, function(share) { + return share.share_type === OC.Share.SHARE_TYPE_LINK + }) + } - // OCS API uses non-camelcased names - files = _.chain(files) + // OCS API uses non-camelcased names + files = _.chain(files) // convert share data to file data - .map(function(share) { + .map(function(share) { // TODO: use OC.Files.FileInfo - var file = { - id: share.file_source, - icon: OC.MimeType.getIconUrl(share.mimetype), - mimetype: share.mimetype, - tags: share.tags || [] - }; - if (share.item_type === 'folder') { - file.type = 'dir'; - file.mimetype = 'httpd/unix-directory'; - } - else { - file.type = 'file'; - } - file.share = { - id: share.id, - type: share.share_type, - target: share.share_with, - stime: share.stime * 1000, - expiration: share.expiration, - }; - if (sharedWithUser) { - file.shareOwner = share.displayname_owner; - file.shareOwnerId = share.uid_owner; - file.name = OC.basename(share.file_target); - file.path = OC.dirname(share.file_target); - file.permissions = share.permissions; - if (file.path) { - file.extraData = share.file_target; + var file = { + id: share.file_source, + icon: OC.MimeType.getIconUrl(share.mimetype), + mimetype: share.mimetype, + tags: share.tags || [] } - } - else { - if (share.share_type !== OC.Share.SHARE_TYPE_LINK) { - file.share.targetDisplayName = share.share_with_displayname; - file.share.targetShareWithId = share.share_with; + if (share.item_type === 'folder') { + file.type = 'dir' + file.mimetype = 'httpd/unix-directory' + } else { + file.type = 'file' } - file.name = OC.basename(share.path); - file.path = OC.dirname(share.path); - file.permissions = OC.PERMISSION_ALL; - if (file.path) { - file.extraData = share.path; + file.share = { + id: share.id, + type: share.share_type, + target: share.share_with, + stime: share.stime * 1000, + expiration: share.expiration } - } - return file; - }) + if (sharedWithUser) { + file.shareOwner = share.displayname_owner + file.shareOwnerId = share.uid_owner + file.name = OC.basename(share.file_target) + file.path = OC.dirname(share.file_target) + file.permissions = share.permissions + if (file.path) { + file.extraData = share.file_target + } + } else { + if (share.share_type !== OC.Share.SHARE_TYPE_LINK) { + file.share.targetDisplayName = share.share_with_displayname + file.share.targetShareWithId = share.share_with + } + file.name = OC.basename(share.path) + file.path = OC.dirname(share.path) + file.permissions = OC.PERMISSION_ALL + if (file.path) { + file.extraData = share.path + } + } + return file + }) // Group all files and have a "shares" array with // the share info for each file. // // This uses a hash memo to cumulate share information // inside the same file object (by file id). - .reduce(function(memo, file) { - var data = memo[file.id]; - var recipient = file.share.targetDisplayName; - var recipientId = file.share.targetShareWithId; - if (!data) { - data = memo[file.id] = file; - data.shares = [file.share]; - // using a hash to make them unique, - // this is only a list to be displayed - data.recipients = {}; - data.recipientData = {}; - // share types - data.shareTypes = {}; - // counter is cheaper than calling _.keys().length - data.recipientsCount = 0; - data.mtime = file.share.stime; - } - else { + .reduce(function(memo, file) { + var data = memo[file.id] + var recipient = file.share.targetDisplayName + var recipientId = file.share.targetShareWithId + if (!data) { + data = memo[file.id] = file + data.shares = [file.share] + // using a hash to make them unique, + // this is only a list to be displayed + data.recipients = {} + data.recipientData = {} + // share types + data.shareTypes = {} + // counter is cheaper than calling _.keys().length + data.recipientsCount = 0 + data.mtime = file.share.stime + } else { // always take the most recent stime - if (file.share.stime > data.mtime) { - data.mtime = file.share.stime; + if (file.share.stime > data.mtime) { + data.mtime = file.share.stime + } + data.shares.push(file.share) } - data.shares.push(file.share); - } - if (recipient) { + if (recipient) { // limit counterparts for output - if (data.recipientsCount < 4) { + if (data.recipientsCount < 4) { // only store the first ones, they will be the only ones // displayed - data.recipients[recipient] = true; - data.recipientData[data.recipientsCount] = { - 'shareWith': recipientId, - 'shareWithDisplayName': recipient - }; + data.recipients[recipient] = true + data.recipientData[data.recipientsCount] = { + 'shareWith': recipientId, + 'shareWithDisplayName': recipient + } + } + data.recipientsCount++ } - data.recipientsCount++; - } - data.shareTypes[file.share.type] = true; + data.shareTypes[file.share.type] = true - delete file.share; - return memo; - }, {}) + delete file.share + return memo + }, {}) // Retrieve only the values of the returned hash - .values() + .values() // Clean up - .each(function(data) { + .each(function(data) { // convert the recipients map to a flat // array of sorted names - data.mountType = 'shared'; - delete data.recipientsCount; - if (sharedWithUser) { + data.mountType = 'shared' + delete data.recipientsCount + if (sharedWithUser) { // only for outgoing shares - delete data.shareTypes; - } else { - data.shareTypes = _.keys(data.shareTypes); - } - }) + delete data.shareTypes + } else { + data.shareTypes = _.keys(data.shareTypes) + } + }) // Finish the chain by getting the result - .value(); + .value() - // Sort by expected sort comparator - return files.sort(this._sortComparator); - }, - }); + // Sort by expected sort comparator + return files.sort(this._sortComparator) + } + }) /** * Share info attributes. @@ -486,5 +482,5 @@ * passing to HTML data attributes with jQuery) */ - OCA.Sharing.FileList = FileList; -})(); + OCA.Sharing.FileList = FileList +})() diff --git a/apps/files_sharing/src/additionalScripts.js b/apps/files_sharing/src/additionalScripts.js index b0525a64a120c48dc38d475c5354621120b235a7..db65a2d2085e23a8d4b6907e7ebe2ab03e693d19 100644 --- a/apps/files_sharing/src/additionalScripts.js +++ b/apps/files_sharing/src/additionalScripts.js @@ -1,6 +1,3 @@ -__webpack_public_path__ = OC.linkTo('files_sharing', 'js/dist/'); -__webpack_nonce__ = btoa(OC.requestToken); - import './share' import './sharetabview' import './sharebreadcrumbview' @@ -10,4 +7,9 @@ import './style/sharebreadcrumb.scss' import './collaborationresourceshandler.js' -window.OCA.Sharing = OCA.Sharing; +// eslint-disable-next-line camelcase +__webpack_public_path__ = OC.linkTo('files_sharing', 'js/dist/') +// eslint-disable-next-line camelcase +__webpack_nonce__ = btoa(OC.requestToken) + +window.OCA.Sharing = OCA.Sharing diff --git a/apps/files_sharing/src/collaborationresources.js b/apps/files_sharing/src/collaborationresources.js index 27f268be852eff2d1a54cd2fef0eb9463ec7bfef..9c31f21bee5699455dc003461cb1f0818e988c0b 100644 --- a/apps/files_sharing/src/collaborationresources.js +++ b/apps/files_sharing/src/collaborationresources.js @@ -1,4 +1,4 @@ -/* +/** * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> * * @author Julius Härtl <jus@bitgrid.net> @@ -20,21 +20,23 @@ * */ -import Vue from 'vue'; -import Vuex from 'vuex'; -import { Tooltip, PopoverMenu } from 'nextcloud-vue'; -import ClickOutside from 'vue-click-outside'; +import Vue from 'vue' +import Vuex from 'vuex' +import { Tooltip, PopoverMenu } from 'nextcloud-vue' +import ClickOutside from 'vue-click-outside' -Vue.prototype.t = t; -Vue.component('PopoverMenu', PopoverMenu); -Vue.directive('ClickOutside', ClickOutside); +import View from './views/CollaborationView' + +Vue.prototype.t = t Tooltip.options.defaultHtml = false -Vue.directive('Tooltip', Tooltip); -Vue.use(Vuex); -import View from './views/CollaborationView'; +// eslint-disable-next-line vue/match-component-file-name +Vue.component('PopoverMenu', PopoverMenu) +Vue.directive('ClickOutside', ClickOutside) +Vue.directive('Tooltip', Tooltip) +Vue.use(Vuex) export { Vue, View -}; +} diff --git a/apps/files_sharing/src/collaborationresourceshandler.js b/apps/files_sharing/src/collaborationresourceshandler.js index 32e9f17ad770615f3127d3619a89fa3a20c74468..1e1ebe5b541a581f43c603161fdd9a0e799303a6 100644 --- a/apps/files_sharing/src/collaborationresourceshandler.js +++ b/apps/files_sharing/src/collaborationresourceshandler.js @@ -1,19 +1,21 @@ -__webpack_public_path__ = OC.linkTo('files_sharing', 'js/dist/'); -__webpack_nonce__ = btoa(OC.requestToken); +// eslint-disable-next-line camelcase +__webpack_public_path__ = OC.linkTo('files_sharing', 'js/dist/') +// eslint-disable-next-line camelcase +__webpack_nonce__ = btoa(OC.requestToken) window.OCP.Collaboration.registerType('file', { action: () => { return new Promise((resolve, reject) => { - OC.dialogs.filepicker(t('files_sharing', 'Link to a file'), function (f) { - const client = OC.Files.getClient(); + OC.dialogs.filepicker(t('files_sharing', 'Link to a file'), function(f) { + const client = OC.Files.getClient() client.getFileInfo(f).then((status, fileInfo) => { - resolve(fileInfo.id); + resolve(fileInfo.id) }).fail(() => { - reject(); - }); - }, false, null, false, OC.dialogs.FILEPICKER_TYPE_CHOOSE, '', { allowDirectoryChooser: true }); - }); + reject(new Error('Cannot get fileinfo')) + }) + }, false, null, false, OC.dialogs.FILEPICKER_TYPE_CHOOSE, '', { allowDirectoryChooser: true }) + }) }, typeString: t('files_sharing', 'Link to a file'), typeIconClass: 'icon-files-dark' -}); +}) diff --git a/apps/files_sharing/src/files_sharing.js b/apps/files_sharing/src/files_sharing.js index 0bf695a72c9ba69dd8126db90da28bf78ae7fba2..c521bb1141458e92d8d42d7ef1094ed0ebe8399b 100644 --- a/apps/files_sharing/src/files_sharing.js +++ b/apps/files_sharing/src/files_sharing.js @@ -1,5 +1,7 @@ -__webpack_nonce__ = btoa(OC.requestToken); -__webpack_public_path__ = OC.linkTo('files_sharing', 'js/dist/'); +import '../js/app' +import '../js/sharedfilelist' -import '../js/app'; -import '../js/sharedfilelist'; +// eslint-disable-next-line camelcase +__webpack_nonce__ = btoa(OC.requestToken) +// eslint-disable-next-line camelcase +__webpack_public_path__ = OC.linkTo('files_sharing', 'js/dist/') diff --git a/apps/files_sharing/src/share.js b/apps/files_sharing/src/share.js index 32ca20f3e624305994e5d9e20cf97a8a4c25a36b..a66f166759f5d07b101d853fa0bc088421da9cfe 100644 --- a/apps/files_sharing/src/share.js +++ b/apps/files_sharing/src/share.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2014 * @@ -14,10 +15,10 @@ PROPERTY_SHARE_TYPES: '{' + OC.Files.Client.NS_OWNCLOUD + '}share-types', PROPERTY_OWNER_ID: '{' + OC.Files.Client.NS_OWNCLOUD + '}owner-id', PROPERTY_OWNER_DISPLAY_NAME: '{' + OC.Files.Client.NS_OWNCLOUD + '}owner-display-name' - }); + }) if (!OCA.Sharing) { - OCA.Sharing = {}; + OCA.Sharing = {} } /** * @namespace @@ -34,131 +35,130 @@ attach: function(fileList) { // core sharing is disabled/not loaded if (!OC.Share) { - return; + return } if (fileList.id === 'trashbin' || fileList.id === 'files.public') { - return; + return } - var fileActions = fileList.fileActions; - var oldCreateRow = fileList._createRow; + var fileActions = fileList.fileActions + var oldCreateRow = fileList._createRow fileList._createRow = function(fileData) { - var tr = oldCreateRow.apply(this, arguments); - var sharePermissions = OCA.Sharing.Util.getSharePermissions(fileData); - + var tr = oldCreateRow.apply(this, arguments) + var sharePermissions = OCA.Sharing.Util.getSharePermissions(fileData) + if (fileData.permissions === 0) { // no permission, disabling sidebar - delete fileActions.actions.all.Comment; - delete fileActions.actions.all.Details; - delete fileActions.actions.all.Goto; + delete fileActions.actions.all.Comment + delete fileActions.actions.all.Details + delete fileActions.actions.all.Goto } - tr.attr('data-share-permissions', sharePermissions); + tr.attr('data-share-permissions', sharePermissions) if (fileData.shareOwner) { - tr.attr('data-share-owner', fileData.shareOwner); - tr.attr('data-share-owner-id', fileData.shareOwnerId); + tr.attr('data-share-owner', fileData.shareOwner) + tr.attr('data-share-owner-id', fileData.shareOwnerId) // user should always be able to rename a mount point if (fileData.mountType === 'shared-root') { - tr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE); + tr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE) } } if (fileData.recipientData && !_.isEmpty(fileData.recipientData)) { - tr.attr('data-share-recipient-data', JSON.stringify(fileData.recipientData)); + tr.attr('data-share-recipient-data', JSON.stringify(fileData.recipientData)) } if (fileData.shareTypes) { - tr.attr('data-share-types', fileData.shareTypes.join(',')); + tr.attr('data-share-types', fileData.shareTypes.join(',')) } - return tr; - }; + return tr + } - var oldElementToFile = fileList.elementToFile; + var oldElementToFile = fileList.elementToFile fileList.elementToFile = function($el) { - var fileInfo = oldElementToFile.apply(this, arguments); - fileInfo.sharePermissions = $el.attr('data-share-permissions') || undefined; - fileInfo.shareOwner = $el.attr('data-share-owner') || undefined; - fileInfo.shareOwnerId = $el.attr('data-share-owner-id') || undefined; + var fileInfo = oldElementToFile.apply(this, arguments) + fileInfo.sharePermissions = $el.attr('data-share-permissions') || undefined + fileInfo.shareOwner = $el.attr('data-share-owner') || undefined + fileInfo.shareOwnerId = $el.attr('data-share-owner-id') || undefined - if( $el.attr('data-share-types')){ - fileInfo.shareTypes = $el.attr('data-share-types').split(','); + if ($el.attr('data-share-types')) { + fileInfo.shareTypes = $el.attr('data-share-types').split(',') } - if( $el.attr('data-expiration')){ - var expirationTimestamp = parseInt($el.attr('data-expiration')); - fileInfo.shares = []; - fileInfo.shares.push({expiration: expirationTimestamp}); + if ($el.attr('data-expiration')) { + var expirationTimestamp = parseInt($el.attr('data-expiration')) + fileInfo.shares = [] + fileInfo.shares.push({ expiration: expirationTimestamp }) } - return fileInfo; - }; + return fileInfo + } - var oldGetWebdavProperties = fileList._getWebdavProperties; + var oldGetWebdavProperties = fileList._getWebdavProperties fileList._getWebdavProperties = function() { - var props = oldGetWebdavProperties.apply(this, arguments); - props.push(OC.Files.Client.PROPERTY_OWNER_ID); - props.push(OC.Files.Client.PROPERTY_OWNER_DISPLAY_NAME); - props.push(OC.Files.Client.PROPERTY_SHARE_TYPES); - return props; - }; + var props = oldGetWebdavProperties.apply(this, arguments) + props.push(OC.Files.Client.PROPERTY_OWNER_ID) + props.push(OC.Files.Client.PROPERTY_OWNER_DISPLAY_NAME) + props.push(OC.Files.Client.PROPERTY_SHARE_TYPES) + return props + } fileList.filesClient.addFileInfoParser(function(response) { - var data = {}; - var props = response.propStat[0].properties; - var permissionsProp = props[OC.Files.Client.PROPERTY_PERMISSIONS]; + var data = {} + var props = response.propStat[0].properties + var permissionsProp = props[OC.Files.Client.PROPERTY_PERMISSIONS] if (permissionsProp && permissionsProp.indexOf('S') >= 0) { - data.shareOwner = props[OC.Files.Client.PROPERTY_OWNER_DISPLAY_NAME]; - data.shareOwnerId = props[OC.Files.Client.PROPERTY_OWNER_ID]; + data.shareOwner = props[OC.Files.Client.PROPERTY_OWNER_DISPLAY_NAME] + data.shareOwnerId = props[OC.Files.Client.PROPERTY_OWNER_ID] } - var shareTypesProp = props[OC.Files.Client.PROPERTY_SHARE_TYPES]; + var shareTypesProp = props[OC.Files.Client.PROPERTY_SHARE_TYPES] if (shareTypesProp) { data.shareTypes = _.chain(shareTypesProp).filter(function(xmlvalue) { - return (xmlvalue.namespaceURI === OC.Files.Client.NS_OWNCLOUD && xmlvalue.nodeName.split(':')[1] === 'share-type'); + return (xmlvalue.namespaceURI === OC.Files.Client.NS_OWNCLOUD && xmlvalue.nodeName.split(':')[1] === 'share-type') }).map(function(xmlvalue) { - return parseInt(xmlvalue.textContent || xmlvalue.text, 10); - }).value(); + return parseInt(xmlvalue.textContent || xmlvalue.text, 10) + }).value() } - return data; - }); + return data + }) // use delegate to catch the case with multiple file lists - fileList.$el.on('fileActionsReady', function(ev){ - var $files = ev.$files; + fileList.$el.on('fileActionsReady', function(ev) { + var $files = ev.$files _.each($files, function(file) { - var $tr = $(file); - var shareTypes = $tr.attr('data-share-types') || ''; - var shareOwner = $tr.attr('data-share-owner'); + var $tr = $(file) + var shareTypes = $tr.attr('data-share-types') || '' + var shareOwner = $tr.attr('data-share-owner') if (shareTypes || shareOwner) { - var hasLink = false; - var hasShares = false; + var hasLink = false + var hasShares = false _.each(shareTypes.split(',') || [], function(shareType) { - shareType = parseInt(shareType, 10); + shareType = parseInt(shareType, 10) if (shareType === OC.Share.SHARE_TYPE_LINK) { - hasLink = true; + hasLink = true } else if (shareType === OC.Share.SHARE_TYPE_EMAIL) { - hasLink = true; + hasLink = true } else if (shareType === OC.Share.SHARE_TYPE_USER) { - hasShares = true; + hasShares = true } else if (shareType === OC.Share.SHARE_TYPE_GROUP) { - hasShares = true; + hasShares = true } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) { - hasShares = true; + hasShares = true } else if (shareType === OC.Share.SHARE_TYPE_CIRCLE) { - hasShares = true; + hasShares = true } else if (shareType === OC.Share.SHARE_TYPE_ROOM) { - hasShares = true; + hasShares = true } - }); - OCA.Sharing.Util._updateFileActionIcon($tr, hasShares, hasLink); + }) + OCA.Sharing.Util._updateFileActionIcon($tr, hasShares, hasLink) } - }); - }); - + }) + }) fileList.$el.on('changeDirectory', function() { - OCA.Sharing.sharesLoaded = false; - }); + OCA.Sharing.sharesLoaded = false + }) fileActions.registerAction({ name: 'Share', @@ -193,40 +193,40 @@ type: OCA.Files.FileActions.TYPE_INLINE, actionHandler: function(fileName, context) { // do not open sidebar if permission is set and equal to 0 - var permissions = parseInt(context.$file.data('share-permissions'), 10); + var permissions = parseInt(context.$file.data('share-permissions'), 10) if (isNaN(permissions) || permissions > 0) { - fileList.showDetailsView(fileName, 'shareTabView'); + fileList.showDetailsView(fileName, 'shareTabView') } }, render: function(actionSpec, isDefault, context) { - var permissions = parseInt(context.$file.data('permissions'), 10); + var permissions = parseInt(context.$file.data('permissions'), 10) // if no share permissions but share owner exists, still show the link if ((permissions & OC.PERMISSION_SHARE) !== 0 || context.$file.attr('data-share-owner')) { - return fileActions._defaultRenderAction.call(fileActions, actionSpec, isDefault, context); + return fileActions._defaultRenderAction.call(fileActions, actionSpec, isDefault, context) } // don't render anything - return null; + return null } - }); + }) - var shareTab = new OCA.Sharing.ShareTabView('shareTabView', {order: -20}); + var shareTab = new OCA.Sharing.ShareTabView('shareTabView', { order: -20 }) // detect changes and change the matching list entry shareTab.on('sharesChanged', function(shareModel) { - var fileInfoModel = shareModel.fileInfoModel; - var $tr = fileList.findFileEl(fileInfoModel.get('name')); + var fileInfoModel = shareModel.fileInfoModel + var $tr = fileList.findFileEl(fileInfoModel.get('name')) // We count email shares as link share - var hasLinkShares = shareModel.hasLinkShares(); - shareModel.get('shares').forEach(function (share) { + var hasLinkShares = shareModel.hasLinkShares() + shareModel.get('shares').forEach(function(share) { if (share.share_type === OC.Share.SHARE_TYPE_EMAIL) { - hasLinkShares = true; + hasLinkShares = true } - }); + }) - OCA.Sharing.Util._updateFileListDataAttributes(fileList, $tr, shareModel); + OCA.Sharing.Util._updateFileListDataAttributes(fileList, $tr, shareModel) if (!OCA.Sharing.Util._updateFileActionIcon($tr, shareModel.hasUserShares(), hasLinkShares)) { // remove icon, if applicable - OC.Share.markFileAsShared($tr, false, false); + OC.Share.markFileAsShared($tr, false, false) } // FIXME: this is too convoluted. We need to get rid of the above updates @@ -237,12 +237,12 @@ // we need to modify the model // (FIXME: yes, this is hacky) icon: $tr.attr('data-icon') - }); - }); - fileList.registerTabView(shareTab); + }) + }) + fileList.registerTabView(shareTab) - var breadCrumbSharingDetailView = new OCA.Sharing.ShareBreadCrumbView({shareTab: shareTab}); - fileList.registerBreadCrumbDetailView(breadCrumbSharingDetailView); + var breadCrumbSharingDetailView = new OCA.Sharing.ShareBreadCrumbView({ shareTab: shareTab }) + fileList.registerBreadCrumbDetailView(breadCrumbSharingDetailView) }, /** @@ -252,18 +252,17 @@ // files app current cannot show recipients on load, so we don't update the // icon when changed for consistency if (fileList.id === 'files') { - return; + return } - var recipients = _.pluck(shareModel.get('shares'), 'share_with_displayname'); + var recipients = _.pluck(shareModel.get('shares'), 'share_with_displayname') // note: we only update the data attribute because updateIcon() if (recipients.length) { - var recipientData = _.mapObject(shareModel.get('shares'), function (share) { - return {shareWith: share.share_with, shareWithDisplayName: share.share_with_displayname}; - }); - $tr.attr('data-share-recipient-data', JSON.stringify(recipientData)); - } - else { - $tr.removeAttr('data-share-recipient-data'); + var recipientData = _.mapObject(shareModel.get('shares'), function(share) { + return { shareWith: share.share_with, shareWithDisplayName: share.share_with_displayname } + }) + $tr.attr('data-share-recipient-data', JSON.stringify(recipientData)) + } else { + $tr.removeAttr('data-share-recipient-data') } }, @@ -274,16 +273,16 @@ * @param {boolean} hasUserShares true if a user share exists * @param {boolean} hasLinkShares true if a link share exists * - * @return {boolean} true if the icon was set, false otherwise + * @returns {boolean} true if the icon was set, false otherwise */ _updateFileActionIcon: function($tr, hasUserShares, hasLinkShares) { // if the statuses are loaded already, use them for the icon // (needed when scrolling to the next page) if (hasUserShares || hasLinkShares || $tr.attr('data-share-recipient-data') || $tr.attr('data-share-owner')) { - OC.Share.markFileAsShared($tr, true, hasLinkShares); - return true; + OC.Share.markFileAsShared($tr, true, hasLinkShares) + return true } - return false; + return false }, /** @@ -291,9 +290,9 @@ * @returns {String} */ getSharePermissions: function(fileData) { - return fileData.sharePermissions; + return fileData.sharePermissions } - }; -})(); + } +})() -OC.Plugins.register('OCA.Files.FileList', OCA.Sharing.Util); +OC.Plugins.register('OCA.Files.FileList', OCA.Sharing.Util) diff --git a/apps/files_sharing/src/sharebreadcrumbview.js b/apps/files_sharing/src/sharebreadcrumbview.js index 17e3eae0bf5a6f1ab7af2ce34babcc3fc7bf54da..a90c94b6d7dc5b1c45ebc3fe18af647f06bd42d9 100644 --- a/apps/files_sharing/src/sharebreadcrumbview.js +++ b/apps/files_sharing/src/sharebreadcrumbview.js @@ -1,5 +1,3 @@ -/* global Handlebars, OC */ - /** * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * @@ -23,7 +21,7 @@ */ (function() { - 'use strict'; + 'use strict' var BreadCrumbView = OC.Backbone.View.extend({ tagName: 'span', @@ -36,68 +34,68 @@ _shareTab: undefined, initialize: function(options) { - this._shareTab = options.shareTab; + this._shareTab = options.shareTab }, render: function(data) { - this._dirInfo = data.dirInfo || null; + this._dirInfo = data.dirInfo || null if (this._dirInfo !== null && (this._dirInfo.path !== '/' || this._dirInfo.name !== '')) { - var isShared = data.dirInfo && data.dirInfo.shareTypes && data.dirInfo.shareTypes.length > 0; - this.$el.removeClass('shared icon-public icon-shared'); + var isShared = data.dirInfo && data.dirInfo.shareTypes && data.dirInfo.shareTypes.length > 0 + this.$el.removeClass('shared icon-public icon-shared') if (isShared) { - this.$el.addClass('shared'); + this.$el.addClass('shared') if (data.dirInfo.shareTypes.indexOf(OC.Share.SHARE_TYPE_LINK) !== -1) { - this.$el.addClass('icon-public'); + this.$el.addClass('icon-public') } else { - this.$el.addClass('icon-shared'); + this.$el.addClass('icon-shared') } } else { - this.$el.addClass('icon-shared'); + this.$el.addClass('icon-shared') } - this.$el.show(); - this.delegateEvents(); + this.$el.show() + this.delegateEvents() } else { - this.$el.removeClass('shared icon-public icon-shared'); - this.$el.hide(); + this.$el.removeClass('shared icon-public icon-shared') + this.$el.hide() } - return this; + return this }, _onClick: function(e) { - e.preventDefault(); + e.preventDefault() - var fileInfoModel = new OCA.Files.FileInfoModel(this._dirInfo); - var self = this; + var fileInfoModel = new OCA.Files.FileInfoModel(this._dirInfo) + var self = this fileInfoModel.on('change', function() { self.render({ dirInfo: self._dirInfo - }); - }); + }) + }) this._shareTab.on('sharesChanged', function(shareModel) { - var shareTypes = []; - var shares = shareModel.getSharesWithCurrentItem(); + var shareTypes = [] + var shares = shareModel.getSharesWithCurrentItem() - for(var i = 0; i < shares.length; i++) { + for (var i = 0; i < shares.length; i++) { if (shareTypes.indexOf(shares[i].share_type) === -1) { - shareTypes.push(shares[i].share_type); + shareTypes.push(shares[i].share_type) } } if (shareModel.hasLinkShares()) { - shareTypes.push(OC.Share.SHARE_TYPE_LINK); + shareTypes.push(OC.Share.SHARE_TYPE_LINK) } // Since the dirInfo isn't updated we need to do this dark hackery - self._dirInfo.shareTypes = shareTypes; + self._dirInfo.shareTypes = shareTypes self.render({ dirInfo: self._dirInfo - }); - }); - OCA.Files.App.fileList.showDetailsView(fileInfoModel, 'shareTabView'); + }) + }) + OCA.Files.App.fileList.showDetailsView(fileInfoModel, 'shareTabView') } - }); + }) - OCA.Sharing.ShareBreadCrumbView = BreadCrumbView; -})(); + OCA.Sharing.ShareBreadCrumbView = BreadCrumbView +})() diff --git a/apps/files_sharing/src/sharetabview.js b/apps/files_sharing/src/sharetabview.js index 44c863fbe9efff9e5754a9c3e2c3064a1ac9aeca..da0708b85a681ebb1516b89b20d61f6bb4e8c082 100644 --- a/apps/files_sharing/src/sharetabview.js +++ b/apps/files_sharing/src/sharetabview.js @@ -11,77 +11,77 @@ /* @global Handlebars */ (function() { - var TEMPLATE = - '<div>' + - '<div class="dialogContainer"></div>' + - '<div id="collaborationResources"></div>' + - '</div>'; + var TEMPLATE + = '<div>' + + '<div class="dialogContainer"></div>' + + '<div id="collaborationResources"></div>' + + '</div>' /** * @memberof OCA.Sharing */ var ShareTabView = OCA.Files.DetailTabView.extend( /** @lends OCA.Sharing.ShareTabView.prototype */ { - id: 'shareTabView', - className: 'tab shareTabView', + id: 'shareTabView', + className: 'tab shareTabView', - initialize: function(name, options) { - OCA.Files.DetailTabView.prototype.initialize.call(this, name, options); - OC.Plugins.attach('OCA.Sharing.ShareTabView', this); - }, + initialize: function(name, options) { + OCA.Files.DetailTabView.prototype.initialize.call(this, name, options) + OC.Plugins.attach('OCA.Sharing.ShareTabView', this) + }, - template: function(params) { - return TEMPLATE; - }, + template: function(params) { + return TEMPLATE + }, - getLabel: function() { - return t('files_sharing', 'Sharing'); - }, + getLabel: function() { + return t('files_sharing', 'Sharing') + }, - getIcon: function() { - return 'icon-shared'; - }, + getIcon: function() { + return 'icon-shared' + }, - /** + /** * Renders this details view */ - render: function() { - var self = this; - if (this._dialog) { + render: function() { + var self = this + if (this._dialog) { // remove/destroy older instance - this._dialog.model.off(); - this._dialog.remove(); - this._dialog = null; - } - - if (this.model) { - this.$el.html(this.template()); - - if (_.isUndefined(this.model.get('sharePermissions'))) { - this.model.set('sharePermissions', OCA.Sharing.Util.getSharePermissions(this.model.attributes)); + this._dialog.model.off() + this._dialog.remove() + this._dialog = null } - // TODO: the model should read these directly off the passed fileInfoModel - var attributes = { - itemType: this.model.isDirectory() ? 'folder' : 'file', - itemSource: this.model.get('id'), - possiblePermissions: this.model.get('sharePermissions') - }; - var configModel = new OC.Share.ShareConfigModel(); - var shareModel = new OC.Share.ShareItemModel(attributes, { - configModel: configModel, - fileInfoModel: this.model - }); - this._dialog = new OC.Share.ShareDialogView({ - configModel: configModel, - model: shareModel - }); - this.$el.find('.dialogContainer').append(this._dialog.$el); - this._dialog.render(); - this._dialog.model.fetch(); - this._dialog.model.on('change', function() { - self.trigger('sharesChanged', shareModel); - }); + if (this.model) { + this.$el.html(this.template()) + + if (_.isUndefined(this.model.get('sharePermissions'))) { + this.model.set('sharePermissions', OCA.Sharing.Util.getSharePermissions(this.model.attributes)) + } + + // TODO: the model should read these directly off the passed fileInfoModel + var attributes = { + itemType: this.model.isDirectory() ? 'folder' : 'file', + itemSource: this.model.get('id'), + possiblePermissions: this.model.get('sharePermissions') + } + var configModel = new OC.Share.ShareConfigModel() + var shareModel = new OC.Share.ShareItemModel(attributes, { + configModel: configModel, + fileInfoModel: this.model + }) + this._dialog = new OC.Share.ShareDialogView({ + configModel: configModel, + model: shareModel + }) + this.$el.find('.dialogContainer').append(this._dialog.$el) + this._dialog.render() + this._dialog.model.fetch() + this._dialog.model.on('change', function() { + self.trigger('sharesChanged', shareModel) + }) import('./collaborationresources').then((Resources) => { var vm = new Resources.Vue({ @@ -89,20 +89,19 @@ render: h => h(Resources.View), data: { model: this.model.toJSON() - }, - }); + } + }) this.model.on('change', () => { vm.data = this.model.toJSON() }) }) - } else { - this.$el.empty(); + } else { + this.$el.empty() // TODO: render placeholder text? + } + this.trigger('rendered') } - this.trigger('rendered'); - } - }); - - OCA.Sharing.ShareTabView = ShareTabView; -})(); + }) + OCA.Sharing.ShareTabView = ShareTabView +})() diff --git a/apps/files_sharing/src/views/CollaborationView.vue b/apps/files_sharing/src/views/CollaborationView.vue index 0307488b5a9e1346c5c33a68c59ce3f1ed7f2a27..5cfcd299cce23b716e849df1ca14af7a69c854fc 100644 --- a/apps/files_sharing/src/views/CollaborationView.vue +++ b/apps/files_sharing/src/views/CollaborationView.vue @@ -21,7 +21,10 @@ --> <template> - <collection-list v-if="fileId" type="file" :id="fileId" :name="filename"></collection-list> + <CollectionList v-if="fileId" + :id="fileId" + type="file" + :name="filename" /> </template> <script> @@ -29,22 +32,22 @@ import { CollectionList } from 'nextcloud-vue-collections' export default { name: 'CollaborationView', + components: { + CollectionList + }, computed: { fileId() { if (this.$root.model && this.$root.model.id) { - return '' + this.$root.model.id; + return '' + this.$root.model.id } - return null; + return null }, filename() { if (this.$root.model && this.$root.model.name) { - return '' + this.$root.model.name; + return '' + this.$root.model.name } - return ''; + return '' } - }, - components: { - CollectionList } } </script> diff --git a/apps/files_trashbin/js/files_trashbin.js b/apps/files_trashbin/js/files_trashbin.js index ceab18813b2a6ef367213ae5b20cddf548932b95..d381919b0aa071fe032be05991bccb775a3405c5 100644 Binary files a/apps/files_trashbin/js/files_trashbin.js and b/apps/files_trashbin/js/files_trashbin.js differ diff --git a/apps/files_trashbin/js/files_trashbin.js.map b/apps/files_trashbin/js/files_trashbin.js.map index 8ab7cb7680bb088088f340f677a5139d0741b6fd..1def7411fb04853da3c5a41ce9fb83bd54addc67 100644 Binary files a/apps/files_trashbin/js/files_trashbin.js.map and b/apps/files_trashbin/js/files_trashbin.js.map differ diff --git a/apps/files_trashbin/src/app.js b/apps/files_trashbin/src/app.js index 2bc8e08b81218535ab5dd01da1a657c2e2a49676..3405f79b15f92d18d6b5e230d275152397cdb5a7 100644 --- a/apps/files_trashbin/src/app.js +++ b/apps/files_trashbin/src/app.js @@ -1,4 +1,4 @@ -/* +/** * Copyright (c) 2014 * * This file is licensed under the Affero General Public License version 3 @@ -11,7 +11,7 @@ /** * @namespace OCA.Trashbin */ -OCA.Trashbin = {}; +OCA.Trashbin = {} /** * @namespace OCA.Trashbin.App */ @@ -20,19 +20,19 @@ OCA.Trashbin.App = { /** @type {OC.Files.Client} */ client: null, - initialize: function ($el) { + initialize: function($el) { if (this._initialized) { - return; + return } - this._initialized = true; + this._initialized = true this.client = new OC.Files.Client({ host: OC.getHost(), port: OC.getPort(), root: OC.linkToRemoteBase('dav') + '/trashbin/' + OC.getCurrentUser().uid, useHTTPS: OC.getProtocol() === 'https' - }); - var urlParams = OC.Util.History.parseUrlQuery(); + }) + var urlParams = OC.Util.History.parseUrlQuery() this.fileList = new OCA.Trashbin.FileList( $('#app-content-trashbin'), { fileActions: this._createFileActions(), @@ -43,12 +43,12 @@ OCA.Trashbin.App = { { name: 'restore', displayName: t('files_trashbin', 'Restore'), - iconClass: 'icon-history', + iconClass: 'icon-history' }, { name: 'delete', displayName: t('files_trashbin', 'Delete permanently'), - iconClass: 'icon-delete', + iconClass: 'icon-delete' } ], client: this.client, @@ -57,18 +57,18 @@ OCA.Trashbin.App = { // if handling the event with the file list already created. shown: true } - ); + ) }, - _createFileActions: function () { - var client = this.client; - var fileActions = new OCA.Files.FileActions(); - fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { - var dir = context.fileList.getCurrentDirectory(); - context.fileList.changeDirectory(OC.joinPaths(dir, filename)); - }); + _createFileActions: function() { + var client = this.client + var fileActions = new OCA.Files.FileActions() + fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function(filename, context) { + var dir = context.fileList.getCurrentDirectory() + context.fileList.changeDirectory(OC.joinPaths(dir, filename)) + }) - fileActions.setDefault('dir', 'Open'); + fileActions.setDefault('dir', 'Open') fileActions.registerAction({ name: 'Restore', @@ -77,21 +77,21 @@ OCA.Trashbin.App = { mime: 'all', permissions: OC.PERMISSION_READ, iconClass: 'icon-history', - actionHandler: function (filename, context) { - var fileList = context.fileList; - var tr = fileList.findFileEl(filename); - fileList.showFileBusyState(tr, true); - var dir = context.fileList.getCurrentDirectory(); + actionHandler: function(filename, context) { + var fileList = context.fileList + var tr = fileList.findFileEl(filename) + fileList.showFileBusyState(tr, true) + var dir = context.fileList.getCurrentDirectory() client.move(OC.joinPaths('trash', dir, filename), OC.joinPaths('restore', filename), true) .then( fileList._removeCallback.bind(fileList, [filename]), - function () { - fileList.showFileBusyState(tr, false); - OC.Notification.show(t('files_trashbin', 'Error while restoring file from trashbin')); + function() { + fileList.showFileBusyState(tr, false) + OC.Notification.show(t('files_trashbin', 'Error while restoring file from trashbin')) } - ); + ) } - }); + }) fileActions.registerAction({ name: 'Delete', @@ -99,39 +99,38 @@ OCA.Trashbin.App = { mime: 'all', permissions: OC.PERMISSION_READ, iconClass: 'icon-delete', - render: function (actionSpec, isDefault, context) { - var $actionLink = fileActions._makeActionLink(actionSpec, context); - $actionLink.attr('original-title', t('files_trashbin', 'Delete permanently')); - $actionLink.children('img').attr('alt', t('files_trashbin', 'Delete permanently')); - context.$file.find('td:last').append($actionLink); - return $actionLink; + render: function(actionSpec, isDefault, context) { + var $actionLink = fileActions._makeActionLink(actionSpec, context) + $actionLink.attr('original-title', t('files_trashbin', 'Delete permanently')) + $actionLink.children('img').attr('alt', t('files_trashbin', 'Delete permanently')) + context.$file.find('td:last').append($actionLink) + return $actionLink }, - actionHandler: function (filename, context) { - var fileList = context.fileList; - $('.tipsy').remove(); - var tr = fileList.findFileEl(filename); - fileList.showFileBusyState(tr, true); - var dir = context.fileList.getCurrentDirectory(); + actionHandler: function(filename, context) { + var fileList = context.fileList + $('.tipsy').remove() + var tr = fileList.findFileEl(filename) + fileList.showFileBusyState(tr, true) + var dir = context.fileList.getCurrentDirectory() client.remove(OC.joinPaths('trash', dir, filename)) .then( fileList._removeCallback.bind(fileList, [filename]), - function () { - fileList.showFileBusyState(tr, false); - OC.Notification.show(t('files_trashbin', 'Error while removing file from trashbin')); + function() { + fileList.showFileBusyState(tr, false) + OC.Notification.show(t('files_trashbin', 'Error while removing file from trashbin')) } - ); + ) } - }); - return fileActions; + }) + return fileActions } -}; +} -$(document).ready(function () { - $('#app-content-trashbin').one('show', function () { - var App = OCA.Trashbin.App; - App.initialize($('#app-content-trashbin')); +$(document).ready(function() { + $('#app-content-trashbin').one('show', function() { + var App = OCA.Trashbin.App + App.initialize($('#app-content-trashbin')) // force breadcrumb init // App.fileList.changeDirectory(App.fileList.getCurrentDirectory(), false, true); - }); -}); - + }) +}) diff --git a/apps/files_trashbin/src/filelist.js b/apps/files_trashbin/src/filelist.js index 07e041faefd05c4f7140c9cda78c640ee6be34fa..661b2f012f4d7a88fd62bb02f4ae19cf172306a3 100644 --- a/apps/files_trashbin/src/filelist.js +++ b/apps/files_trashbin/src/filelist.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2014 * @@ -8,25 +9,25 @@ * */ (function() { - var DELETED_REGEXP = new RegExp(/^(.+)\.d[0-9]+$/); - var FILENAME_PROP = '{http://nextcloud.org/ns}trashbin-filename'; - var DELETION_TIME_PROP = '{http://nextcloud.org/ns}trashbin-deletion-time'; - var TRASHBIN_ORIGINAL_LOCATION = '{http://nextcloud.org/ns}trashbin-original-location'; + var DELETED_REGEXP = new RegExp(/^(.+)\.d[0-9]+$/) + var FILENAME_PROP = '{http://nextcloud.org/ns}trashbin-filename' + var DELETION_TIME_PROP = '{http://nextcloud.org/ns}trashbin-deletion-time' + var TRASHBIN_ORIGINAL_LOCATION = '{http://nextcloud.org/ns}trashbin-original-location' /** * Convert a file name in the format filename.d12345 to the real file name. * This will use basename. * The name will not be changed if it has no ".d12345" suffix. * @param {String} name file name - * @return {String} converted file name + * @returns {String} converted file name */ function getDeletedFileName(name) { - name = OC.basename(name); - var match = DELETED_REGEXP.exec(name); + name = OC.basename(name) + var match = DELETED_REGEXP.exec(name) if (match && match.length > 1) { - name = match[1]; + name = match[1] } - return name; + return name } /** @@ -39,283 +40,282 @@ * @param [options] map of options */ var FileList = function($el, options) { - this.client = options.client; - this.initialize($el, options); - }; + this.client = options.client + this.initialize($el, options) + } FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, /** @lends OCA.Trashbin.FileList.prototype */ { - id: 'trashbin', - appName: t('files_trashbin', 'Deleted files'), - /** @type {OC.Files.Client} */ - client: null, + id: 'trashbin', + appName: t('files_trashbin', 'Deleted files'), + /** @type {OC.Files.Client} */ + client: null, - /** + /** * @private */ - initialize: function() { - this.client.addFileInfoParser(function(response, data) { - var props = response.propStat[0].properties; - var path = props[TRASHBIN_ORIGINAL_LOCATION]; - return { - displayName: props[FILENAME_PROP], - mtime: parseInt(props[DELETION_TIME_PROP], 10) * 1000, - hasPreview: true, - path: path, - extraData: path - } - }); + initialize: function() { + this.client.addFileInfoParser(function(response, data) { + var props = response.propStat[0].properties + var path = props[TRASHBIN_ORIGINAL_LOCATION] + return { + displayName: props[FILENAME_PROP], + mtime: parseInt(props[DELETION_TIME_PROP], 10) * 1000, + hasPreview: true, + path: path, + extraData: path + } + }) - var result = OCA.Files.FileList.prototype.initialize.apply(this, arguments); - this.$el.find('.undelete').click('click', _.bind(this._onClickRestoreSelected, this)); + var result = OCA.Files.FileList.prototype.initialize.apply(this, arguments) + this.$el.find('.undelete').click('click', _.bind(this._onClickRestoreSelected, this)) - this.setSort('mtime', 'desc'); - /** + this.setSort('mtime', 'desc') + /** * Override crumb making to add "Deleted Files" entry * and convert files with ".d" extensions to a more * user friendly name. */ - this.breadcrumb._makeCrumbs = function() { - var parts = OCA.Files.BreadCrumb.prototype._makeCrumbs.apply(this, arguments); - for (var i = 1; i < parts.length; i++) { - parts[i].name = getDeletedFileName(parts[i].name); + this.breadcrumb._makeCrumbs = function() { + var parts = OCA.Files.BreadCrumb.prototype._makeCrumbs.apply(this, arguments) + for (var i = 1; i < parts.length; i++) { + parts[i].name = getDeletedFileName(parts[i].name) + } + return parts } - return parts; - }; - OC.Plugins.attach('OCA.Trashbin.FileList', this); - return result; - }, + OC.Plugins.attach('OCA.Trashbin.FileList', this) + return result + }, - /** + /** * Override to only return read permissions */ - getDirectoryPermissions: function() { - return OC.PERMISSION_READ | OC.PERMISSION_DELETE; - }, + getDirectoryPermissions: function() { + return OC.PERMISSION_READ | OC.PERMISSION_DELETE + }, - _setCurrentDir: function(targetDir) { - OCA.Files.FileList.prototype._setCurrentDir.apply(this, arguments); + _setCurrentDir: function(targetDir) { + OCA.Files.FileList.prototype._setCurrentDir.apply(this, arguments) - var baseDir = OC.basename(targetDir); - if (baseDir !== '') { - this.setPageTitle(getDeletedFileName(baseDir)); - } - }, + var baseDir = OC.basename(targetDir) + if (baseDir !== '') { + this.setPageTitle(getDeletedFileName(baseDir)) + } + }, - _createRow: function() { + _createRow: function() { // FIXME: MEGAHACK until we find a better solution - var tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments); - tr.find('td.filesize').remove(); - return tr; - }, - - getAjaxUrl: function(action, params) { - var q = ''; - if (params) { - q = '?' + OC.buildQueryString(params); - } - return OC.filePath('files_trashbin', 'ajax', action + '.php') + q; - }, + var tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments) + tr.find('td.filesize').remove() + return tr + }, + + getAjaxUrl: function(action, params) { + var q = '' + if (params) { + q = '?' + OC.buildQueryString(params) + } + return OC.filePath('files_trashbin', 'ajax', action + '.php') + q + }, - setupUploadEvents: function() { + setupUploadEvents: function() { // override and do nothing - }, + }, - linkTo: function(dir){ - return OC.linkTo('files', 'index.php')+"?view=trashbin&dir="+ encodeURIComponent(dir).replace(/%2F/g, '/'); - }, + linkTo: function(dir) { + return OC.linkTo('files', 'index.php') + '?view=trashbin&dir=' + encodeURIComponent(dir).replace(/%2F/g, '/') + }, - elementToFile: function($el) { - var fileInfo = OCA.Files.FileList.prototype.elementToFile($el); - if (this.getCurrentDirectory() === '/') { - fileInfo.displayName = getDeletedFileName(fileInfo.name); - } - // no size available - delete fileInfo.size; - return fileInfo; - }, - - updateEmptyContent: function(){ - var exists = this.$fileList.find('tr:first').exists(); - this.$el.find('#emptycontent').toggleClass('hidden', exists); - this.$el.find('#filestable th').toggleClass('hidden', !exists); - }, - - _removeCallback: function(files) { - var $el; - for (var i = 0; i < files.length; i++) { - $el = this.remove(OC.basename(files[i]), {updateSummary: false}); - this.fileSummary.remove({type: $el.attr('data-type'), size: $el.attr('data-size')}); - } - this.fileSummary.update(); - this.updateEmptyContent(); - }, - - _onClickRestoreSelected: function(event) { - event.preventDefault(); - var self = this; - var files = _.pluck(this.getSelectedFiles(), 'name'); - for (var i = 0; i < files.length; i++) { - var tr = this.findFileEl(files[i]); - this.showFileBusyState(tr, true); - } - - this.fileMultiSelectMenu.toggleLoading('restore', true); - var restorePromises = files.map(function(file) { - return self.client.move(OC.joinPaths('trash', self.getCurrentDirectory(), file), OC.joinPaths('restore', file), true) - .then( - function() { - self._removeCallback([file]); - } - ); - }); - return Promise.all(restorePromises).then( - function() { - self.fileMultiSelectMenu.toggleLoading('restore', false); - }, - function() { - OC.Notification.show(t('files_trashbin', 'Error while restoring files from trashbin')); + elementToFile: function($el) { + var fileInfo = OCA.Files.FileList.prototype.elementToFile($el) + if (this.getCurrentDirectory() === '/') { + fileInfo.displayName = getDeletedFileName(fileInfo.name) + } + // no size available + delete fileInfo.size + return fileInfo + }, + + updateEmptyContent: function() { + var exists = this.$fileList.find('tr:first').exists() + this.$el.find('#emptycontent').toggleClass('hidden', exists) + this.$el.find('#filestable th').toggleClass('hidden', !exists) + }, + + _removeCallback: function(files) { + var $el + for (var i = 0; i < files.length; i++) { + $el = this.remove(OC.basename(files[i]), { updateSummary: false }) + this.fileSummary.remove({ type: $el.attr('data-type'), size: $el.attr('data-size') }) + } + this.fileSummary.update() + this.updateEmptyContent() + }, + + _onClickRestoreSelected: function(event) { + event.preventDefault() + var self = this + var files = _.pluck(this.getSelectedFiles(), 'name') + for (var i = 0; i < files.length; i++) { + var tr = this.findFileEl(files[i]) + this.showFileBusyState(tr, true) } - ); - }, - - _onClickDeleteSelected: function(event) { - event.preventDefault(); - var self = this; - var allFiles = this.$el.find('.select-all').is(':checked'); - var files = _.pluck(this.getSelectedFiles(), 'name'); - for (var i = 0; i < files.length; i++) { - var tr = this.findFileEl(files[i]); - this.showFileBusyState(tr, true); - } - if (allFiles) { - return this.client.remove(OC.joinPaths('trash', this.getCurrentDirectory())) - .then( - function() { - self.hideMask(); - self.setFiles([]); - }, - function() { - OC.Notification.show(t('files_trashbin', 'Error while emptying trashbin')); - } - ); - } else { - this.fileMultiSelectMenu.toggleLoading('delete', true); - var deletePromises = files.map(function(file) { - return self.client.remove(OC.joinPaths('trash', self.getCurrentDirectory(), file)) + this.fileMultiSelectMenu.toggleLoading('restore', true) + var restorePromises = files.map(function(file) { + return self.client.move(OC.joinPaths('trash', self.getCurrentDirectory(), file), OC.joinPaths('restore', file), true) .then( function() { - self._removeCallback([file]); + self._removeCallback([file]) } - ); - }); - return Promise.all(deletePromises).then( + ) + }) + return Promise.all(restorePromises).then( function() { - self.fileMultiSelectMenu.toggleLoading('delete', false); + self.fileMultiSelectMenu.toggleLoading('restore', false) }, function() { - OC.Notification.show(t('files_trashbin', 'Error while removing files from trashbin')); + OC.Notification.show(t('files_trashbin', 'Error while restoring files from trashbin')) } - ); - } - }, + ) + }, + + _onClickDeleteSelected: function(event) { + event.preventDefault() + var self = this + var allFiles = this.$el.find('.select-all').is(':checked') + var files = _.pluck(this.getSelectedFiles(), 'name') + for (var i = 0; i < files.length; i++) { + var tr = this.findFileEl(files[i]) + this.showFileBusyState(tr, true) + } - _onClickFile: function(event) { - var mime = $(this).parent().parent().data('mime'); - if (mime !== 'httpd/unix-directory') { - event.preventDefault(); - } - return OCA.Files.FileList.prototype._onClickFile.apply(this, arguments); - }, + if (allFiles) { + return this.client.remove(OC.joinPaths('trash', this.getCurrentDirectory())) + .then( + function() { + self.hideMask() + self.setFiles([]) + }, + function() { + OC.Notification.show(t('files_trashbin', 'Error while emptying trashbin')) + } + ) + } else { + this.fileMultiSelectMenu.toggleLoading('delete', true) + var deletePromises = files.map(function(file) { + return self.client.remove(OC.joinPaths('trash', self.getCurrentDirectory(), file)) + .then( + function() { + self._removeCallback([file]) + } + ) + }) + return Promise.all(deletePromises).then( + function() { + self.fileMultiSelectMenu.toggleLoading('delete', false) + }, + function() { + OC.Notification.show(t('files_trashbin', 'Error while removing files from trashbin')) + } + ) + } + }, + + _onClickFile: function(event) { + var mime = $(this).parent().parent().data('mime') + if (mime !== 'httpd/unix-directory') { + event.preventDefault() + } + return OCA.Files.FileList.prototype._onClickFile.apply(this, arguments) + }, - generatePreviewUrl: function(urlSpec) { - return OC.generateUrl('/apps/files_trashbin/preview?') + $.param(urlSpec); - }, + generatePreviewUrl: function(urlSpec) { + return OC.generateUrl('/apps/files_trashbin/preview?') + $.param(urlSpec) + }, - getDownloadUrl: function() { + getDownloadUrl: function() { // no downloads - return '#'; - }, + return '#' + }, - updateStorageStatistics: function() { + updateStorageStatistics: function() { // no op because the trashbin doesn't have // storage info like free space / used space - }, + }, - isSelectedDeletable: function() { - return true; - }, + isSelectedDeletable: function() { + return true + }, - /** + /** * Returns list of webdav properties to request */ - _getWebdavProperties: function() { - return [FILENAME_PROP, DELETION_TIME_PROP, TRASHBIN_ORIGINAL_LOCATION].concat(this.filesClient.getPropfindProperties()); - }, + _getWebdavProperties: function() { + return [FILENAME_PROP, DELETION_TIME_PROP, TRASHBIN_ORIGINAL_LOCATION].concat(this.filesClient.getPropfindProperties()) + }, - /** + /** * Reloads the file list using ajax call * - * @return ajax call object + * @returns ajax call object */ - reload: function() { - this._selectedFiles = {}; - this._selectionSummary.clear(); - this.$el.find('.select-all').prop('checked', false); - this.showMask(); - if (this._reloadCall) { - this._reloadCall.abort(); - } - this._reloadCall = this.client.getFolderContents( - 'trash/' + this.getCurrentDirectory(), { - includeParent: false, - properties: this._getWebdavProperties() + reload: function() { + this._selectedFiles = {} + this._selectionSummary.clear() + this.$el.find('.select-all').prop('checked', false) + this.showMask() + if (this._reloadCall) { + this._reloadCall.abort() + } + this._reloadCall = this.client.getFolderContents( + 'trash/' + this.getCurrentDirectory(), { + includeParent: false, + properties: this._getWebdavProperties() + } + ) + var callBack = this.reloadCallback.bind(this) + return this._reloadCall.then(callBack, callBack) + }, + reloadCallback: function(status, result) { + delete this._reloadCall + this.hideMask() + + if (status === 401) { + return false } - ); - var callBack = this.reloadCallback.bind(this); - return this._reloadCall.then(callBack, callBack); - }, - reloadCallback: function(status, result) { - delete this._reloadCall; - this.hideMask(); - - if (status === 401) { - return false; - } - // Firewall Blocked request? - if (status === 403) { + // Firewall Blocked request? + if (status === 403) { // Go home - this.changeDirectory('/'); - OC.Notification.show(t('files', 'This operation is forbidden')); - return false; - } + this.changeDirectory('/') + OC.Notification.show(t('files', 'This operation is forbidden')) + return false + } - // Did share service die or something else fail? - if (status === 500) { + // Did share service die or something else fail? + if (status === 500) { // Go home - this.changeDirectory('/'); - OC.Notification.show(t('files', 'This directory is unavailable, please check the logs or contact the administrator')); - return false; - } + this.changeDirectory('/') + OC.Notification.show(t('files', 'This directory is unavailable, please check the logs or contact the administrator')) + return false + } - if (status === 404) { + if (status === 404) { // go back home - this.changeDirectory('/'); - return false; - } - // aborted ? - if (status === 0){ - return true; - } - - this.setFiles(result); - return true; - }, + this.changeDirectory('/') + return false + } + // aborted ? + if (status === 0) { + return true + } - }); + this.setFiles(result) + return true + } - OCA.Trashbin.FileList = FileList; -})(); + }) + OCA.Trashbin.FileList = FileList +})() diff --git a/apps/files_versions/js/files_versions.js b/apps/files_versions/js/files_versions.js index 0da35fb8d315eadcc53ebb2a2ff1fd518996218c..b75c059e89090599b2a1199c9ae27ef794099096 100644 Binary files a/apps/files_versions/js/files_versions.js and b/apps/files_versions/js/files_versions.js differ diff --git a/apps/files_versions/js/files_versions.js.map b/apps/files_versions/js/files_versions.js.map index bd8cd955c2ce3d4fdcec9daeacc69e1fbedb12ab..eeee8054e370ea1802b9610b69801d6e56ecf111 100644 Binary files a/apps/files_versions/js/files_versions.js.map and b/apps/files_versions/js/files_versions.js.map differ diff --git a/apps/files_versions/src/filesplugin.js b/apps/files_versions/src/filesplugin.js index a9457c522d6c50187efe49d364594007b87f1d7d..e47003d661102df1a64632659af9479200e49a47 100644 --- a/apps/files_versions/src/filesplugin.js +++ b/apps/files_versions/src/filesplugin.js @@ -9,7 +9,7 @@ */ (function() { - OCA.Versions = OCA.Versions || {}; + OCA.Versions = OCA.Versions || {} /** * @namespace @@ -22,13 +22,12 @@ */ attach: function(fileList) { if (fileList.id === 'trashbin' || fileList.id === 'files.public') { - return; + return } - fileList.registerTabView(new OCA.Versions.VersionsTabView('versionsTabView', {order: -10})); + fileList.registerTabView(new OCA.Versions.VersionsTabView('versionsTabView', { order: -10 })) } - }; -})(); - -OC.Plugins.register('OCA.Files.FileList', OCA.Versions.Util); + } +})() +OC.Plugins.register('OCA.Files.FileList', OCA.Versions.Util) diff --git a/apps/files_versions/src/versioncollection.js b/apps/files_versions/src/versioncollection.js index 2ee683b38ef6272ad76d9df90efbd4e76a8b2847..1f3356e43d3a40c5dac6ac168753ae00b11cc23f 100644 --- a/apps/files_versions/src/versioncollection.js +++ b/apps/files_versions/src/versioncollection.js @@ -8,7 +8,7 @@ * */ -(function () { +(function() { /** * @memberof OCA.Versions */ @@ -25,24 +25,24 @@ _client: null, - setFileInfo: function (fileInfo) { - this._fileInfo = fileInfo; + setFileInfo: function(fileInfo) { + this._fileInfo = fileInfo }, - getFileInfo: function () { - return this._fileInfo; + getFileInfo: function() { + return this._fileInfo }, setCurrentUser: function(user) { - this._currentUser = user; + this._currentUser = user }, getCurrentUser: function() { - return this._currentUser || OC.getCurrentUser().uid; + return this._currentUser || OC.getCurrentUser().uid }, setClient: function(client) { - this._client = client; + this._client = client }, getClient: function() { @@ -50,35 +50,34 @@ host: OC.getHost(), root: OC.linkToRemoteBase('dav') + '/versions/' + this.getCurrentUser(), useHTTPS: OC.getProtocol() === 'https' - }); + }) }, - url: function () { - return OC.linkToRemoteBase('dav') + '/versions/' + this.getCurrentUser() + '/versions/' + this._fileInfo.get('id'); + url: function() { + return OC.linkToRemoteBase('dav') + '/versions/' + this.getCurrentUser() + '/versions/' + this._fileInfo.get('id') }, parse: function(result) { - var fullPath = this._fileInfo.getFullPath(); - var fileId = this._fileInfo.get('id'); - var name = this._fileInfo.get('name'); - var user = this.getCurrentUser(); - var client = this.getClient(); + var fullPath = this._fileInfo.getFullPath() + var fileId = this._fileInfo.get('id') + var name = this._fileInfo.get('name') + var user = this.getCurrentUser() + var client = this.getClient() return _.map(result, function(version) { - version.fullPath = fullPath; - version.fileId = fileId; - version.name = name; - version.timestamp = parseInt(moment(new Date(version.timestamp)).format('X'), 10); - version.id = OC.basename(version.href); - version.size = parseInt(version.size, 10); - version.user = user; - version.client = client; - return version; - }); + version.fullPath = fullPath + version.fileId = fileId + version.name = name + version.timestamp = parseInt(moment(new Date(version.timestamp)).format('X'), 10) + version.id = OC.basename(version.href) + version.size = parseInt(version.size, 10) + version.user = user + version.client = client + return version + }) } - }); + }) - OCA.Versions = OCA.Versions || {}; - - OCA.Versions.VersionCollection = VersionCollection; -})(); + OCA.Versions = OCA.Versions || {} + OCA.Versions.VersionCollection = VersionCollection +})() diff --git a/apps/files_versions/src/versionmodel.js b/apps/files_versions/src/versionmodel.js index 9857bf950b4d96bb9b253c81b6bac6f5c1b0d0d5..e36e59ac046e90301c2be92305d849e49246994b 100644 --- a/apps/files_versions/src/versionmodel.js +++ b/apps/files_versions/src/versionmodel.js @@ -8,9 +8,7 @@ * */ -/* global moment */ - -(function () { +(function() { /** * @memberof OCA.Versions */ @@ -20,53 +18,55 @@ davProperties: { 'size': '{DAV:}getcontentlength', 'mimetype': '{DAV:}getcontenttype', - 'timestamp': '{DAV:}getlastmodified', + 'timestamp': '{DAV:}getlastmodified' }, /** * Restores the original file to this revision + * + * @param {Object} [options] options + * @returns {Promise} */ - revert: function (options) { - options = options ? _.clone(options) : {}; - var model = this; + revert: function(options) { + options = options ? _.clone(options) : {} + var model = this - var client = this.get('client'); + var client = this.get('client') return client.move('/versions/' + this.get('fileId') + '/' + this.get('id'), '/restore/target', true) - .done(function () { + .done(function() { if (options.success) { - options.success.call(options.context, model, {}, options); + options.success.call(options.context, model, {}, options) } - model.trigger('revert', model, options); + model.trigger('revert', model, options) }) - .fail(function () { + .fail(function() { if (options.error) { - options.error.call(options.context, model, {}, options); + options.error.call(options.context, model, {}, options) } - model.trigger('error', model, {}, options); - }); + model.trigger('error', model, {}, options) + }) }, - getFullPath: function () { - return this.get('fullPath'); + getFullPath: function() { + return this.get('fullPath') }, - getPreviewUrl: function () { - var url = OC.generateUrl('/apps/files_versions/preview'); + getPreviewUrl: function() { + var url = OC.generateUrl('/apps/files_versions/preview') var params = { file: this.get('fullPath'), version: this.get('id') - }; - return url + '?' + OC.buildQueryString(params); + } + return url + '?' + OC.buildQueryString(params) }, - getDownloadUrl: function () { - return OC.linkToRemoteBase('dav') + '/versions/' + this.get('user') + '/versions/' + this.get('fileId') + '/' + this.get('id'); + getDownloadUrl: function() { + return OC.linkToRemoteBase('dav') + '/versions/' + this.get('user') + '/versions/' + this.get('fileId') + '/' + this.get('id') } - }); - - OCA.Versions = OCA.Versions || {}; + }) - OCA.Versions.VersionModel = VersionModel; -})(); + OCA.Versions = OCA.Versions || {} + OCA.Versions.VersionModel = VersionModel +})() diff --git a/apps/files_versions/src/versionstabview.js b/apps/files_versions/src/versionstabview.js index 8adfe549aa98ce8cd99c80ea629795f9d4499cf1..b85cb6b08f58c48c1ca8346e54c07e7ce1aad308 100644 --- a/apps/files_versions/src/versionstabview.js +++ b/apps/files_versions/src/versionstabview.js @@ -8,7 +8,7 @@ * */ -import ItemTemplate from './templates/item.handlebars'; +import ItemTemplate from './templates/item.handlebars' import Template from './templates/template.handlebars'; (function() { @@ -28,137 +28,137 @@ import Template from './templates/template.handlebars'; }, initialize: function() { - OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments); - this.collection = new OCA.Versions.VersionCollection(); - this.collection.on('request', this._onRequest, this); - this.collection.on('sync', this._onEndRequest, this); - this.collection.on('update', this._onUpdate, this); - this.collection.on('error', this._onError, this); - this.collection.on('add', this._onAddModel, this); + OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments) + this.collection = new OCA.Versions.VersionCollection() + this.collection.on('request', this._onRequest, this) + this.collection.on('sync', this._onEndRequest, this) + this.collection.on('update', this._onUpdate, this) + this.collection.on('error', this._onError, this) + this.collection.on('add', this._onAddModel, this) }, getLabel: function() { - return t('files_versions', 'Versions'); + return t('files_versions', 'Versions') }, getIcon: function() { - return 'icon-history'; + return 'icon-history' }, nextPage: function() { if (this._loading) { - return; + return } if (this.collection.getFileInfo() && this.collection.getFileInfo().isDirectory()) { - return; + return } - this.collection.fetch(); + this.collection.fetch() }, _onClickRevertVersion: function(ev) { - var self = this; - var $target = $(ev.target); - var fileInfoModel = this.collection.getFileInfo(); - var revision; + var self = this + var $target = $(ev.target) + var fileInfoModel = this.collection.getFileInfo() + var revision if (!$target.is('li')) { - $target = $target.closest('li'); + $target = $target.closest('li') } - ev.preventDefault(); - revision = $target.attr('data-revision'); + ev.preventDefault() + revision = $target.attr('data-revision') - var versionModel = this.collection.get(revision); + var versionModel = this.collection.get(revision) versionModel.revert({ success: function() { // reset and re-fetch the updated collection - self.$versionsContainer.empty(); - self.collection.setFileInfo(fileInfoModel); - self.collection.reset([], {silent: true}); - self.collection.fetch(); + self.$versionsContainer.empty() + self.collection.setFileInfo(fileInfoModel) + self.collection.reset([], { silent: true }) + self.collection.fetch() - self.$el.find('.versions').removeClass('hidden'); + self.$el.find('.versions').removeClass('hidden') // update original model - fileInfoModel.trigger('busy', fileInfoModel, false); + fileInfoModel.trigger('busy', fileInfoModel, false) fileInfoModel.set({ size: versionModel.get('size'), mtime: versionModel.get('timestamp') * 1000, // temp dummy, until we can do a PROPFIND etag: versionModel.get('id') + versionModel.get('timestamp') - }); + }) }, error: function() { - fileInfoModel.trigger('busy', fileInfoModel, false); - self.$el.find('.versions').removeClass('hidden'); - self._toggleLoading(false); + fileInfoModel.trigger('busy', fileInfoModel, false) + self.$el.find('.versions').removeClass('hidden') + self._toggleLoading(false) OC.Notification.show(t('files_version', 'Failed to revert {file} to revision {timestamp}.', { file: versionModel.getFullPath(), timestamp: OC.Util.formatDate(versionModel.get('timestamp') * 1000) }), - { - type: 'error' - } - ); + { + type: 'error' + } + ) } - }); + }) // spinner - this._toggleLoading(true); - fileInfoModel.trigger('busy', fileInfoModel, true); + this._toggleLoading(true) + fileInfoModel.trigger('busy', fileInfoModel, true) }, _toggleLoading: function(state) { - this._loading = state; - this.$el.find('.loading').toggleClass('hidden', !state); + this._loading = state + this.$el.find('.loading').toggleClass('hidden', !state) }, _onRequest: function() { - this._toggleLoading(true); + this._toggleLoading(true) }, _onEndRequest: function() { - this._toggleLoading(false); - this.$el.find('.empty').toggleClass('hidden', !!this.collection.length); + this._toggleLoading(false) + this.$el.find('.empty').toggleClass('hidden', !!this.collection.length) }, _onAddModel: function(model) { - var $el = $(this.itemTemplate(this._formatItem(model))); - this.$versionsContainer.append($el); - $el.find('.has-tooltip').tooltip(); + var $el = $(this.itemTemplate(this._formatItem(model))) + this.$versionsContainer.append($el) + $el.find('.has-tooltip').tooltip() }, template: function(data) { - return Template(data); + return Template(data) }, itemTemplate: function(data) { - return ItemTemplate(data); + return ItemTemplate(data) }, setFileInfo: function(fileInfo) { if (fileInfo) { - this.render(); - this.collection.setFileInfo(fileInfo); - this.collection.reset([], {silent: true}); - this.nextPage(); + this.render() + this.collection.setFileInfo(fileInfo) + this.collection.reset([], { silent: true }) + this.nextPage() } else { - this.render(); - this.collection.reset(); + this.render() + this.collection.reset() } }, _formatItem: function(version) { - var timestamp = version.get('timestamp') * 1000; - var size = version.has('size') ? version.get('size') : 0; - var preview = OC.MimeType.getIconUrl(version.get('mimetype')); - var img = new Image(); - img.onload = function () { - $('li[data-revision=' + version.get('id') + '] .preview').attr('src', version.getPreviewUrl()); - }; - img.src = version.getPreviewUrl(); + var timestamp = version.get('timestamp') * 1000 + var size = version.has('size') ? version.get('size') : 0 + var preview = OC.MimeType.getIconUrl(version.get('mimetype')) + var img = new Image() + img.onload = function() { + $('li[data-revision=' + version.get('id') + '] .preview').attr('src', version.getPreviewUrl()) + } + img.src = version.getPreviewUrl() return _.extend({ versionId: version.get('id'), @@ -175,7 +175,7 @@ import Template from './templates/template.handlebars'; previewUrl: preview, revertLabel: t('files_versions', 'Restore'), canRevert: (this.collection.getFileInfo().get('permissions') & OC.PERMISSION_UPDATE) !== 0 - }, version.attributes); + }, version.attributes) }, /** @@ -183,27 +183,27 @@ import Template from './templates/template.handlebars'; */ render: function() { this.$el.html(this.template({ - emptyResultLabel: t('files_versions', 'No other versions available'), - })); - this.$el.find('.has-tooltip').tooltip(); - this.$versionsContainer = this.$el.find('ul.versions'); - this.delegateEvents(); + emptyResultLabel: t('files_versions', 'No other versions available') + })) + this.$el.find('.has-tooltip').tooltip() + this.$versionsContainer = this.$el.find('ul.versions') + this.delegateEvents() }, /** * Returns true for files, false for folders. - * - * @return {bool} true for files, false for folders + * @param {FileInfo} fileInfo fileInfo + * @returns {bool} true for files, false for folders */ canDisplay: function(fileInfo) { if (!fileInfo) { - return false; + return false } - return !fileInfo.isDirectory(); + return !fileInfo.isDirectory() } - }); + }) - OCA.Versions = OCA.Versions || {}; + OCA.Versions = OCA.Versions || {} - OCA.Versions.VersionsTabView = VersionsTabView; -})(); + OCA.Versions.VersionsTabView = VersionsTabView +})() diff --git a/apps/oauth2/js/oauth2.js b/apps/oauth2/js/oauth2.js index 84a45ba90971ba5cf062468cfdf6306b08bcfcc7..7b232a21a3964e625b010bc77b5f037835fc170c 100644 Binary files a/apps/oauth2/js/oauth2.js and b/apps/oauth2/js/oauth2.js differ diff --git a/apps/oauth2/js/oauth2.js.map b/apps/oauth2/js/oauth2.js.map index 45190261b9f9395d838f76ee342b648220b587a5..cf404d1ed3943611a5a7dff058ef7b4015285457 100644 Binary files a/apps/oauth2/js/oauth2.js.map and b/apps/oauth2/js/oauth2.js.map differ diff --git a/apps/oauth2/src/App.vue b/apps/oauth2/src/App.vue index 1d6998ce3054b248aa8f1754f4b14dfc1d6ea8d9..9098401be10967d219612c4d640e9351a360de0f 100644 --- a/apps/oauth2/src/App.vue +++ b/apps/oauth2/src/App.vue @@ -22,52 +22,71 @@ <template> <div id="oauth2" class="section"> <h2>{{ t('oauth2', 'OAuth 2.0 clients') }}</h2> - <p class="settings-hint">{{ t('oauth2', 'OAuth 2.0 allows external services to request access to {instanceName}.', { instanceName: OC.theme.name}) }}</p> - <table class="grid" v-if="clients.length > 0"> + <p class="settings-hint"> + {{ t('oauth2', 'OAuth 2.0 allows external services to request access to {instanceName}.', { instanceName: OC.theme.name}) }} + </p> + <table v-if="clients.length > 0" class="grid"> <thead> <tr> - <th id="headerName" scope="col">{{ t('oauth2', 'Name') }}</th> - <th id="headerRedirectUri" scope="col">{{ t('oauth2', 'Redirection URI') }}</th> - <th id="headerClientIdentifier" scope="col">{{ t('oauth2', 'Client Identifier') }}</th> - <th id="headerSecret" scope="col">{{ t('oauth2', 'Secret') }}</th> - <th id="headerRemove"> </th> + <th id="headerName" scope="col"> + {{ t('oauth2', 'Name') }} + </th> + <th id="headerRedirectUri" scope="col"> + {{ t('oauth2', 'Redirection URI') }} + </th> + <th id="headerClientIdentifier" scope="col"> + {{ t('oauth2', 'Client Identifier') }} + </th> + <th id="headerSecret" scope="col"> + {{ t('oauth2', 'Secret') }} + </th> + <th id="headerRemove"> + + </th> </tr> </thead> <tbody> <OAuthItem v-for="client in clients" :key="client.id" :client="client" - @delete="deleteClient" - /> + @delete="deleteClient" /> </tbody> </table> - <br/> + <br> <h3>{{ t('oauth2', 'Add client') }}</h3> - <span v-if="newClient.error" class="msg error">{{newClient.errorMsg}}</span> + <span v-if="newClient.error" class="msg error">{{ newClient.errorMsg }}</span> <form @submit.prevent="addClient"> - <input type="text" id="name" name="name" :placeholder="t('oauth2', 'Name')" v-model="newClient.name"> - <input type="url" id="redirectUri" name="redirectUri" :placeholder="t('oauth2', 'Redirection URI')" v-model="newClient.redirectUri"> + <input id="name" + v-model="newClient.name" + type="text" + name="name" + :placeholder="t('oauth2', 'Name')"> + <input id="redirectUri" + v-model="newClient.redirectUri" + type="url" + name="redirectUri" + :placeholder="t('oauth2', 'Redirection URI')"> <input type="submit" class="button" :value="t('oauth2', 'Add')"> </form> </div> </template> <script> -import Axios from 'nextcloud-axios' -import OAuthItem from './components/OAuthItem'; +import axios from 'nextcloud-axios' +import OAuthItem from './components/OAuthItem' export default { name: 'App', + components: { + OAuthItem + }, props: { clients: { type: Array, required: true } }, - components: { - OAuthItem - }, data: function() { return { newClient: { @@ -76,34 +95,34 @@ export default { errorMsg: '', error: false } - }; + } }, methods: { deleteClient(id) { - Axios.delete(OC.generateUrl('apps/oauth2/clients/{id}', {id: id})) + axios.delete(OC.generateUrl('apps/oauth2/clients/{id}', { id: id })) .then((response) => { - this.clients = this.clients.filter(client => client.id !== id); - }); + this.clients = this.clients.filter(client => client.id !== id) + }) }, addClient() { - this.newClient.error = false; + this.newClient.error = false - Axios.post( + axios.post( OC.generateUrl('apps/oauth2/clients'), { name: this.newClient.name, redirectUri: this.newClient.redirectUri } ).then(response => { - this.clients.push(response.data); + this.clients.push(response.data) - this.newClient.name = ''; - this.newClient.redirectUri = ''; + this.newClient.name = '' + this.newClient.redirectUri = '' }).catch(reason => { - this.newClient.error = true; - this.newClient.errorMsg = reason.response.data.message; - }); + this.newClient.error = true + this.newClient.errorMsg = reason.response.data.message + }) } - }, + } } </script> diff --git a/apps/oauth2/src/components/OAuthItem.vue b/apps/oauth2/src/components/OAuthItem.vue index 361c5ed5032ddef2bec87c318cc6d0ae3a56c252..2d670447d72b66b2403f8b352ac30717a9fa5519 100644 --- a/apps/oauth2/src/components/OAuthItem.vue +++ b/apps/oauth2/src/components/OAuthItem.vue @@ -21,11 +21,13 @@ --> <template> <tr> - <td>{{name}}</td> - <td>{{redirectUri}}</td> - <td><code>{{clientId}}</code></td> - <td><code>{{renderedSecret}}</code><a class='icon-toggle has-tooltip' :title="t('oauth2', 'Show client secret')" @click="toggleSecret"></a></td> - <td class="action-column"><span><a class="icon-delete has-tooltip" :title="t('oauth2', 'Delete')" @click="$emit('delete', id)"></a></span></td> + <td>{{ name }}</td> + <td>{{ redirectUri }}</td> + <td><code>{{ clientId }}</code></td> + <td><code>{{ renderedSecret }}</code><a class="icon-toggle has-tooltip" :title="t('oauth2', 'Show client secret')" @click="toggleSecret" /></td> + <td class="action-column"> + <span><a class="icon-delete has-tooltip" :title="t('oauth2', 'Delete')" @click="$emit('delete', id)" /></span> + </td> </tr> </template> @@ -39,27 +41,27 @@ export default { } }, data: function() { - return { - id: this.client.id, - name: this.client.name, - redirectUri: this.client.redirectUri, - clientId: this.client.clientId, - clientSecret: this.client.clientSecret, - renderSecret: false, - }; + return { + id: this.client.id, + name: this.client.name, + redirectUri: this.client.redirectUri, + clientId: this.client.clientId, + clientSecret: this.client.clientSecret, + renderSecret: false + } }, computed: { renderedSecret: function() { if (this.renderSecret) { - return this.clientSecret; + return this.clientSecret } else { - return '****'; + return '****' } } }, methods: { toggleSecret() { - this.renderSecret = !this.renderSecret; + this.renderSecret = !this.renderSecret } } } diff --git a/apps/oauth2/src/main.js b/apps/oauth2/src/main.js index c5714497f3d86395952d2fefbb5d43918e9992e5..45b9d6427672f2af95711ea455d656c30bdb3dcd 100644 --- a/apps/oauth2/src/main.js +++ b/apps/oauth2/src/main.js @@ -20,18 +20,19 @@ * */ -import Vue from 'vue'; -import App from './App.vue'; +import Vue from 'vue' +import App from './App.vue' import { loadState } from 'nextcloud-initial-state' -Vue.prototype.t = t; -Vue.prototype.OC = OC; +Vue.prototype.t = t +Vue.prototype.OC = OC -const clients = loadState('oauth2', 'clients'); +const clients = loadState('oauth2', 'clients') const View = Vue.extend(App) -new View({ +const oauth = new View({ propsData: { clients } -}).$mount('#oauth2'); +}) +oauth.$mount('#oauth2') diff --git a/apps/settings/js/vue-0.js b/apps/settings/js/vue-0.js index 2ac92f73796c505b854dc1c061f597d46b43d6fb..2660dcb63b485446aab09830ff20db14dd3af897 100644 Binary files a/apps/settings/js/vue-0.js and b/apps/settings/js/vue-0.js differ diff --git a/apps/settings/js/vue-0.js.map b/apps/settings/js/vue-0.js.map index 97a887845c7dbbfcf13e2024890d712354bc1070..6ea22b145b821bd0e15391c80bdf55eab8e042e2 100644 Binary files a/apps/settings/js/vue-0.js.map and b/apps/settings/js/vue-0.js.map differ diff --git a/apps/settings/js/vue-4.js b/apps/settings/js/vue-4.js index 692203a204216d53b9d3749312c9224f34cd78b5..af46105bc8e270a570814ca492f3b5e624f463f6 100644 Binary files a/apps/settings/js/vue-4.js and b/apps/settings/js/vue-4.js differ diff --git a/apps/settings/js/vue-4.js.map b/apps/settings/js/vue-4.js.map index 8bb62c0c9384d3e7c5793ebac3e194a20d893943..8b5c23f468c7ee23cc9f333ec42ff8d4a9f75892 100644 Binary files a/apps/settings/js/vue-4.js.map and b/apps/settings/js/vue-4.js.map differ diff --git a/apps/settings/js/vue-5.js b/apps/settings/js/vue-5.js index d347444cee682a8a352059b4f25b7670909462fc..3d8fb7e11d6385db27f73694b40ba1cf5038f6b5 100644 Binary files a/apps/settings/js/vue-5.js and b/apps/settings/js/vue-5.js differ diff --git a/apps/settings/js/vue-5.js.map b/apps/settings/js/vue-5.js.map index d987053312afca0c59a35a13cd3e07fcd1718837..319fa9a4691036a02d9f8d3e4f3bb4c4454ffba6 100644 Binary files a/apps/settings/js/vue-5.js.map and b/apps/settings/js/vue-5.js.map differ diff --git a/apps/settings/js/vue-6.js b/apps/settings/js/vue-6.js index beefac77128f7988847bc467f0edd4af8ef2fa4c..f0517624d3ae9f039b0eb2ab4dc9d13b21e3bc4e 100644 Binary files a/apps/settings/js/vue-6.js and b/apps/settings/js/vue-6.js differ diff --git a/apps/settings/js/vue-6.js.map b/apps/settings/js/vue-6.js.map index 4f73dab4223054e31de47e709b4fbf622290f700..68e0f13690698f72d156d62b082e8edd5be34a9a 100644 Binary files a/apps/settings/js/vue-6.js.map and b/apps/settings/js/vue-6.js.map differ diff --git a/apps/settings/js/vue-settings-admin-security.js b/apps/settings/js/vue-settings-admin-security.js index a6ab1fbba547823d32c53afa5b411df4fa6be28d..6d6b54ffdb18a43defa27b0588edc0e30cc62799 100644 Binary files a/apps/settings/js/vue-settings-admin-security.js and b/apps/settings/js/vue-settings-admin-security.js differ diff --git a/apps/settings/js/vue-settings-admin-security.js.map b/apps/settings/js/vue-settings-admin-security.js.map index 07d4eee5d2b32eccbb29afa6a381bc9225c2635c..ec376c7d73ffe011ae52f273b3e11441fd06665d 100644 Binary files a/apps/settings/js/vue-settings-admin-security.js.map and b/apps/settings/js/vue-settings-admin-security.js.map differ diff --git a/apps/settings/js/vue-settings-apps-users-management.js b/apps/settings/js/vue-settings-apps-users-management.js index 6b7d2764888b03bfd3a44ef84e187e6b717761dc..bd58aba3031b780f32a1f125ae1589dbf1a0c7e7 100644 Binary files a/apps/settings/js/vue-settings-apps-users-management.js and b/apps/settings/js/vue-settings-apps-users-management.js differ diff --git a/apps/settings/js/vue-settings-apps-users-management.js.map b/apps/settings/js/vue-settings-apps-users-management.js.map index 885b23ad4c0688e64d2781c605eae1c07eb360b0..9b0484171dc9d41fcb9019df22a5fd81ee9bc5b6 100644 Binary files a/apps/settings/js/vue-settings-apps-users-management.js.map and b/apps/settings/js/vue-settings-apps-users-management.js.map differ diff --git a/apps/settings/js/vue-settings-personal-security.js b/apps/settings/js/vue-settings-personal-security.js index 5714277dfd73b059986fd0329a1a93ce0e1039f0..92f8d0bb74db9a409c77f2691d691d333f19cdb2 100644 Binary files a/apps/settings/js/vue-settings-personal-security.js and b/apps/settings/js/vue-settings-personal-security.js differ diff --git a/apps/settings/js/vue-settings-personal-security.js.map b/apps/settings/js/vue-settings-personal-security.js.map index 1154e3a6fe0876c977f0801ce62522cb967fb352..704243590f943e518b517cebd5156371425fdd42 100644 Binary files a/apps/settings/js/vue-settings-personal-security.js.map and b/apps/settings/js/vue-settings-personal-security.js.map differ diff --git a/apps/settings/src/App.vue b/apps/settings/src/App.vue index c7c32ba9d63fc5e6ddb852b36f07f68e6620ff9a..591a784c02a34a9248670e06fc5e45294079f2f7 100644 --- a/apps/settings/src/App.vue +++ b/apps/settings/src/App.vue @@ -21,7 +21,7 @@ --> <template> - <router-view></router-view> + <router-view /> </template> <script> @@ -29,9 +29,9 @@ export default { name: 'App', beforeMount: function() { // importing server data into the store - const serverDataElmt = document.getElementById('serverData'); + const serverDataElmt = document.getElementById('serverData') if (serverDataElmt !== null) { - this.$store.commit('setServerData', JSON.parse(document.getElementById('serverData').dataset.server)); + this.$store.commit('setServerData', JSON.parse(document.getElementById('serverData').dataset.server)) } } } diff --git a/apps/settings/src/components/AdminTwoFactor.vue b/apps/settings/src/components/AdminTwoFactor.vue index a1f28f5cfdd15211ce2e3f6178d60c014aeac642..934b77928dc936511726bb6455b96f8532c4382c 100644 --- a/apps/settings/src/components/AdminTwoFactor.vue +++ b/apps/settings/src/components/AdminTwoFactor.vue @@ -4,14 +4,14 @@ {{ t('settings', 'Two-factor authentication can be enforced for all users and specific groups. If they do not have a two-factor provider configured, they will be unable to log into the system.') }} </p> <p v-if="loading"> - <span class="icon-loading-small two-factor-loading"></span> + <span class="icon-loading-small two-factor-loading" /> <span>{{ t('settings', 'Enforce two-factor authentication') }}</span> </p> <p v-else> - <input type="checkbox" - id="two-factor-enforced" - class="checkbox" - v-model="enforced"> + <input id="two-factor-enforced" + v-model="enforced" + type="checkbox" + class="checkbox"> <label for="two-factor-enforced">{{ t('settings', 'Enforce two-factor authentication') }}</label> </p> <template v-if="enforced"> @@ -22,32 +22,30 @@ </p> <p> <Multiselect v-model="enforcedGroups" - :options="groups" - :placeholder="t('settings', 'Enforced groups')" - :disabled="loading" - :multiple="true" - :searchable="true" - @search-change="searchGroup" - :loading="loadingGroups" - :show-no-options="false" - :close-on-select="false"> - </Multiselect> + :options="groups" + :placeholder="t('settings', 'Enforced groups')" + :disabled="loading" + :multiple="true" + :searchable="true" + :loading="loadingGroups" + :show-no-options="false" + :close-on-select="false" + @search-change="searchGroup" /> </p> <p> {{ t('settings', 'Two-factor authentication is not enforced for members of the following groups.') }} </p> <p> <Multiselect v-model="excludedGroups" - :options="groups" - :placeholder="t('settings', 'Excluded groups')" - :disabled="loading" - :multiple="true" - :searchable="true" - @search-change="searchGroup" - :loading="loadingGroups" - :show-no-options="false" - :close-on-select="false"> - </Multiselect> + :options="groups" + :placeholder="t('settings', 'Excluded groups')" + :disabled="loading" + :multiple="true" + :searchable="true" + :loading="loadingGroups" + :show-no-options="false" + :close-on-select="false" + @search-change="searchGroup" /> </p> <p> <em> @@ -57,10 +55,10 @@ </p> </template> <p> - <button class="button primary" - v-if="dirty" - v-on:click="saveChanges" - :disabled="loading"> + <button v-if="dirty" + class="button primary" + :disabled="loading" + @click="saveChanges"> {{ t('settings', 'Save changes') }} </button> </p> @@ -68,94 +66,93 @@ </template> <script> - import Axios from 'nextcloud-axios' - import { mapState } from 'vuex' - import {Multiselect} from 'nextcloud-vue' - import _ from 'lodash' +import axios from 'nextcloud-axios' +import { Multiselect } from 'nextcloud-vue' +import _ from 'lodash' - export default { - name: "AdminTwoFactor", - components: { - Multiselect - }, - data () { - return { - loading: false, - dirty: false, - groups: [], - loadingGroups: false, +export default { + name: 'AdminTwoFactor', + components: { + Multiselect + }, + data() { + return { + loading: false, + dirty: false, + groups: [], + loadingGroups: false + } + }, + computed: { + enforced: { + get: function() { + return this.$store.state.enforced + }, + set: function(val) { + this.dirty = true + this.$store.commit('setEnforced', val) } }, - computed: { - enforced: { - get: function () { - return this.$store.state.enforced - }, - set: function (val) { - this.dirty = true - this.$store.commit('setEnforced', val) - } - }, - enforcedGroups: { - get: function () { - return this.$store.state.enforcedGroups - }, - set: function (val) { - this.dirty = true - this.$store.commit('setEnforcedGroups', val) - } - }, - excludedGroups: { - get: function () { - return this.$store.state.excludedGroups - }, - set: function (val) { - this.dirty = true - this.$store.commit('setExcludedGroups', val) - } + enforcedGroups: { + get: function() { + return this.$store.state.enforcedGroups }, + set: function(val) { + this.dirty = true + this.$store.commit('setEnforcedGroups', val) + } }, - mounted () { - // Groups are loaded dynamically, but the assigned ones *should* - // be valid groups, so let's add them as initial state - this.groups = _.sortedUniq(_.uniq(this.enforcedGroups.concat(this.excludedGroups))) + excludedGroups: { + get: function() { + return this.$store.state.excludedGroups + }, + set: function(val) { + this.dirty = true + this.$store.commit('setExcludedGroups', val) + } + } + }, + mounted() { + // Groups are loaded dynamically, but the assigned ones *should* + // be valid groups, so let's add them as initial state + this.groups = _.sortedUniq(_.uniq(this.enforcedGroups.concat(this.excludedGroups))) - // Populate the groups with a first set so the dropdown is not empty - // when opening the page the first time - this.searchGroup('') - }, - methods: { - searchGroup: _.debounce(function (query) { - this.loadingGroups = true - Axios.get(OC.linkToOCS(`cloud/groups?offset=0&search=${encodeURIComponent(query)}&limit=20`, 2)) - .then(res => res.data.ocs) - .then(ocs => ocs.data.groups) - .then(groups => this.groups = _.sortedUniq(_.uniq(this.groups.concat(groups)))) - .catch(err => console.error('could not search groups', err)) - .then(() => this.loadingGroups = false) - }, 500), + // Populate the groups with a first set so the dropdown is not empty + // when opening the page the first time + this.searchGroup('') + }, + methods: { + searchGroup: _.debounce(function(query) { + this.loadingGroups = true + axios.get(OC.linkToOCS(`cloud/groups?offset=0&search=${encodeURIComponent(query)}&limit=20`, 2)) + .then(res => res.data.ocs) + .then(ocs => ocs.data.groups) + .then(groups => { this.groups = _.sortedUniq(_.uniq(this.groups.concat(groups))) }) + .catch(err => console.error('could not search groups', err)) + .then(() => { this.loadingGroups = false }) + }, 500), - saveChanges () { - this.loading = true + saveChanges() { + this.loading = true - const data = { - enforced: this.enforced, - enforcedGroups: this.enforcedGroups, - excludedGroups: this.excludedGroups, - } - Axios.put(OC.generateUrl('/settings/api/admin/twofactorauth'), data) - .then(resp => resp.data) - .then(state => { - this.state = state - this.dirty = false - }) - .catch(err => { - console.error('could not save changes', err) - }) - .then(() => this.loading = false) + const data = { + enforced: this.enforced, + enforcedGroups: this.enforcedGroups, + excludedGroups: this.excludedGroups } + axios.put(OC.generateUrl('/settings/api/admin/twofactorauth'), data) + .then(resp => resp.data) + .then(state => { + this.state = state + this.dirty = false + }) + .catch(err => { + console.error('could not save changes', err) + }) + .then(() => { this.loading = false }) } } +} </script> <style> diff --git a/apps/settings/src/components/AppDetails.vue b/apps/settings/src/components/AppDetails.vue new file mode 100644 index 0000000000000000000000000000000000000000..b4af8f51e4d357dfcba727c1c973fdb6ec28dd35 --- /dev/null +++ b/apps/settings/src/components/AppDetails.vue @@ -0,0 +1,326 @@ +<!-- + - @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<template> + <div id="app-details-view" style="padding: 20px;"> + <h2> + <div v-if="!app.preview" class="icon-settings-dark" /> + <svg v-if="app.previewAsIcon && app.preview" + width="32" + height="32" + viewBox="0 0 32 32"> + <defs><filter :id="filterId"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /></filter></defs> + <image x="0" + y="0" + width="32" + height="32" + preserveAspectRatio="xMinYMin meet" + :filter="filterUrl" + :xlink:href="app.preview" + class="app-icon" /> + </svg> + {{ app.name }} + </h2> + <img v-if="app.screenshot" :src="app.screenshot" width="100%"> + <div v-if="app.level === 300 || app.level === 200 || hasRating" class="app-level"> + <span v-if="app.level === 300" + v-tooltip.auto="t('settings', 'This app is supported via your current Nextcloud subscription.')" + class="supported icon-checkmark-color"> + {{ t('settings', 'Supported') }}</span> + <span v-if="app.level === 200" + v-tooltip.auto="t('settings', 'Official apps are developed by and within the community. They offer central functionality and are ready for production use.')" + class="official icon-checkmark"> + {{ t('settings', 'Official') }}</span> + <AppScore v-if="hasRating" :score="app.appstoreData.ratingOverall" /> + </div> + + <div v-if="author" class="app-author"> + {{ t('settings', 'by') }} + <span v-for="(a, index) in author" :key="index"> + <a v-if="a['@attributes'] && a['@attributes']['homepage']" :href="a['@attributes']['homepage']">{{ a['@value'] }}</a><span v-else-if="a['@value']">{{ a['@value'] }}</span><span v-else>{{ a }}</span><span v-if="index+1 < author.length">, </span> + </span> + </div> + <div v-if="licence" class="app-licence"> + {{ licence }} + </div> + <div class="actions"> + <div class="actions-buttons"> + <input v-if="app.update" + class="update primary" + type="button" + :value="t('settings', 'Update to {version}', {version: app.update})" + :disabled="installing || loading(app.id)" + @click="update(app.id)"> + <input v-if="app.canUnInstall" + class="uninstall" + type="button" + :value="t('settings', 'Remove')" + :disabled="installing || loading(app.id)" + @click="remove(app.id)"> + <input v-if="app.active" + class="enable" + type="button" + :value="t('settings','Disable')" + :disabled="installing || loading(app.id)" + @click="disable(app.id)"> + <input v-if="!app.active && (app.canInstall || app.isCompatible)" + v-tooltip.auto="enableButtonTooltip" + class="enable primary" + type="button" + :value="enableButtonText" + :disabled="!app.canInstall || installing || loading(app.id)" + @click="enable(app.id)"> + <input v-else-if="!app.active" + v-tooltip.auto="forceEnableButtonTooltip" + class="enable force" + type="button" + :value="forceEnableButtonText" + :disabled="installing || loading(app.id)" + @click="forceEnable(app.id)"> + </div> + <div class="app-groups"> + <div v-if="app.active && canLimitToGroups(app)" class="groups-enable"> + <input :id="prefix('groups_enable', app.id)" + v-model="groupCheckedAppsData" + type="checkbox" + :value="app.id" + class="groups-enable__checkbox checkbox" + @change="setGroupLimit"> + <label :for="prefix('groups_enable', app.id)">{{ t('settings', 'Limit to groups') }}</label> + <input type="hidden" + class="group_select" + :title="t('settings', 'All')" + value=""> + <Multiselect v-if="isLimitedToGroups(app)" + :options="groups" + :value="appGroups" + :options-limit="5" + :placeholder="t('settings', 'Limit app usage to groups')" + label="name" + track-by="id" + class="multiselect-vue" + :multiple="true" + :close-on-select="false" + :tag-width="60" + @select="addGroupLimitation" + @remove="removeGroupLimitation" + @search-change="asyncFindGroup"> + <span slot="noResult">{{ t('settings', 'No results') }}</span> + </Multiselect> + </div> + </div> + </div> + + <ul class="app-dependencies"> + <li v-if="app.missingMinOwnCloudVersion"> + {{ t('settings', 'This app has no minimum Nextcloud version assigned. This will be an error in the future.') }} + </li> + <li v-if="app.missingMaxOwnCloudVersion"> + {{ t('settings', 'This app has no maximum Nextcloud version assigned. This will be an error in the future.') }} + </li> + <li v-if="!app.canInstall"> + {{ t('settings', 'This app cannot be installed because the following dependencies are not fulfilled:') }} + <ul class="missing-dependencies"> + <li v-for="(dep, index) in app.missingDependencies" :key="index"> + {{ dep }} + </li> + </ul> + </li> + </ul> + + <p class="documentation"> + <a v-if="!app.internal" + class="appslink" + :href="appstoreUrl" + target="_blank" + rel="noreferrer noopener">{{ t('settings', 'View in store') }} ↗</a> + + <a v-if="app.website" + class="appslink" + :href="app.website" + target="_blank" + rel="noreferrer noopener">{{ t('settings', 'Visit website') }} ↗</a> + <a v-if="app.bugs" + class="appslink" + :href="app.bugs" + target="_blank" + rel="noreferrer noopener">{{ t('settings', 'Report a bug') }} ↗</a> + + <a v-if="app.documentation && app.documentation.user" + class="appslink" + :href="app.documentation.user" + target="_blank" + rel="noreferrer noopener">{{ t('settings', 'User documentation') }} ↗</a> + <a v-if="app.documentation && app.documentation.admin" + class="appslink" + :href="app.documentation.admin" + target="_blank" + rel="noreferrer noopener">{{ t('settings', 'Admin documentation') }} ↗</a> + <a v-if="app.documentation && app.documentation.developer" + class="appslink" + :href="app.documentation.developer" + target="_blank" + rel="noreferrer noopener">{{ t('settings', 'Developer documentation') }} ↗</a> + </p> + + <div class="app-description" v-html="renderMarkdown" /> + </div> +</template> + +<script> +import { Multiselect } from 'nextcloud-vue' +import marked from 'marked' +import dompurify from 'dompurify' + +import AppScore from './AppList/AppScore' +import AppManagement from './AppManagement' +import PrefixMixin from './PrefixMixin' +import SvgFilterMixin from './SvgFilterMixin' + +export default { + name: 'AppDetails', + components: { + Multiselect, + AppScore + }, + mixins: [AppManagement, PrefixMixin, SvgFilterMixin], + props: ['category', 'app'], + data() { + return { + groupCheckedAppsData: false + } + }, + computed: { + appstoreUrl() { + return `https://apps.nextcloud.com/apps/${this.app.id}` + }, + licence() { + if (this.app.licence) { + return t('settings', '{license}-licensed', { license: ('' + this.app.licence).toUpperCase() }) + } + return null + }, + hasRating() { + return this.app.appstoreData && this.app.appstoreData.ratingNumOverall > 5 + }, + author() { + if (typeof this.app.author === 'string') { + return [ + { + '@value': this.app.author + } + ] + } + if (this.app.author['@value']) { + return [this.app.author] + } + return this.app.author + }, + appGroups() { + return this.app.groups.map(group => { return { id: group, name: group } }) + }, + groups() { + return this.$store.getters.getGroups + .filter(group => group.id !== 'disabled') + .sort((a, b) => a.name.localeCompare(b.name)) + }, + renderMarkdown() { + var renderer = new marked.Renderer() + renderer.link = function(href, title, text) { + try { + var prot = decodeURIComponent(unescape(href)) + .replace(/[^\w:]/g, '') + .toLowerCase() + } catch (e) { + return '' + } + + if (prot.indexOf('http:') !== 0 && prot.indexOf('https:') !== 0) { + return '' + } + + var out = '<a href="' + href + '" rel="noreferrer noopener"' + if (title) { + out += ' title="' + title + '"' + } + out += '>' + text + '</a>' + return out + } + renderer.image = function(href, title, text) { + if (text) { + return text + } + return title + } + renderer.blockquote = function(quote) { + return quote + } + return dompurify.sanitize( + marked(this.app.description.trim(), { + renderer: renderer, + gfm: false, + highlight: false, + tables: false, + breaks: false, + pedantic: false, + sanitize: true, + smartLists: true, + smartypants: false + }), + { + SAFE_FOR_JQUERY: true, + ALLOWED_TAGS: [ + 'strong', + 'p', + 'a', + 'ul', + 'ol', + 'li', + 'em', + 'del', + 'blockquote' + ] + } + ) + } + }, + mounted() { + if (this.app.groups.length > 0) { + this.groupCheckedAppsData = true + } + } +} +</script> + +<style scoped> + .force { + background: var(--color-main-background); + border-color: var(--color-error); + color: var(--color-error); + } + .force:hover, + .force:active { + background: var(--color-error); + border-color: var(--color-error) !important; + color: var(--color-main-background); + } +</style> diff --git a/apps/settings/src/components/AppList.vue b/apps/settings/src/components/AppList.vue new file mode 100644 index 0000000000000000000000000000000000000000..3e40eeb5fbbe0650ab0e7b4e80038d8305341f08 --- /dev/null +++ b/apps/settings/src/components/AppList.vue @@ -0,0 +1,201 @@ +<!-- + - @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<template> + <div id="app-content-inner"> + <div id="apps-list" class="apps-list" :class="{installed: (useBundleView || useListView), store: useAppStoreView}"> + <template v-if="useListView"> + <transition-group name="app-list" tag="div" class="apps-list-container"> + <AppItem v-for="app in apps" + :key="app.id" + :app="app" + :category="category" /> + </transition-group> + </template> + <transition-group v-if="useBundleView" + name="app-list" + tag="div" + class="apps-list-container"> + <template v-for="bundle in bundles"> + <div :key="bundle.id" class="apps-header"> + <div class="app-image" /> + <h2>{{ bundle.name }} <input type="button" :value="bundleToggleText(bundle.id)" @click="toggleBundle(bundle.id)"></h2> + <div class="app-version" /> + <div class="app-level" /> + <div class="app-groups" /> + <div class="actions"> + + </div> + </div> + <AppItem v-for="app in bundleApps(bundle.id)" + :key="bundle.id + app.id" + :app="app" + :category="category" /> + </template> + </transition-group> + <template v-if="useAppStoreView"> + <AppItem v-for="app in apps" + :key="app.id" + :app="app" + :category="category" + :list-view="false" /> + </template> + </div> + + <div id="apps-list-search" class="apps-list installed"> + <div class="apps-list-container"> + <template v-if="search !== '' && searchApps.length > 0"> + <div class="section"> + <div /> + <td colspan="5"> + <h2>{{ t('settings', 'Results from other categories') }}</h2> + </td> + </div> + <AppItem v-for="app in searchApps" + :key="app.id" + :app="app" + :category="category" + :list-view="true" /> + </template> + </div> + </div> + + <div v-if="search !== '' && !loading && searchApps.length === 0 && apps.length === 0" id="apps-list-empty" class="emptycontent emptycontent-search"> + <div id="app-list-empty-icon" class="icon-settings-dark" /> + <h2>{{ t('settings', 'No apps found for your version') }}</h2> + </div> + + <div id="searchresults" /> + </div> +</template> + +<script> +import AppItem from './AppList/AppItem' +import PrefixMixin from './PrefixMixin' + +export default { + name: 'AppList', + components: { + AppItem + }, + mixins: [PrefixMixin], + props: ['category', 'app', 'search'], + computed: { + loading() { + return this.$store.getters.loading('list') + }, + apps() { + let apps = this.$store.getters.getAllApps + .filter(app => app.name.toLowerCase().search(this.search.toLowerCase()) !== -1) + .sort(function(a, b) { + const sortStringA = '' + (a.active ? 0 : 1) + (a.update ? 0 : 1) + a.name + const sortStringB = '' + (b.active ? 0 : 1) + (b.update ? 0 : 1) + b.name + return OC.Util.naturalSortCompare(sortStringA, sortStringB) + }) + + if (this.category === 'installed') { + return apps.filter(app => app.installed) + } + if (this.category === 'enabled') { + return apps.filter(app => app.active && app.installed) + } + if (this.category === 'disabled') { + return apps.filter(app => !app.active && app.installed) + } + if (this.category === 'app-bundles') { + return apps.filter(app => app.bundles) + } + if (this.category === 'updates') { + return apps.filter(app => app.update) + } + // filter app store categories + return apps.filter(app => { + return app.appstore && app.category !== undefined + && (app.category === this.category || app.category.indexOf(this.category) > -1) + }) + }, + bundles() { + return this.$store.getters.getServerData.bundles.filter(bundle => this.bundleApps(bundle.id).length > 0) + }, + bundleApps() { + return function(bundle) { + return this.$store.getters.getAllApps + .filter(app => app.bundleId === bundle) + } + }, + searchApps() { + if (this.search === '') { + return [] + } + return this.$store.getters.getAllApps + .filter(app => { + if (app.name.toLowerCase().search(this.search.toLowerCase()) !== -1) { + return (!this.apps.find(_app => _app.id === app.id)) + } + return false + }) + }, + useAppStoreView() { + return !this.useListView && !this.useBundleView + }, + useListView() { + return (this.category === 'installed' || this.category === 'enabled' || this.category === 'disabled' || this.category === 'updates') + }, + useBundleView() { + return (this.category === 'app-bundles') + }, + allBundlesEnabled() { + let self = this + return function(id) { + return self.bundleApps(id).filter(app => !app.active).length === 0 + } + }, + bundleToggleText() { + let self = this + return function(id) { + if (self.allBundlesEnabled(id)) { + return t('settings', 'Disable all') + } + return t('settings', 'Enable all') + } + } + }, + methods: { + toggleBundle(id) { + if (this.allBundlesEnabled(id)) { + return this.disableBundle(id) + } + return this.enableBundle(id) + }, + enableBundle(id) { + let apps = this.bundleApps(id).map(app => app.id) + this.$store.dispatch('enableApp', { appId: apps, groups: [] }) + .catch((error) => { console.error(error); OC.Notification.show(error) }) + }, + disableBundle(id) { + let apps = this.bundleApps(id).map(app => app.id) + this.$store.dispatch('disableApp', { appId: apps, groups: [] }) + .catch((error) => { OC.Notification.show(error) }) + } + } +} +</script> diff --git a/apps/settings/src/components/AppList/AppItem.vue b/apps/settings/src/components/AppList/AppItem.vue new file mode 100644 index 0000000000000000000000000000000000000000..6b66163cfed5245c7cfe25fabeeba56c0c0a5db0 --- /dev/null +++ b/apps/settings/src/components/AppList/AppItem.vue @@ -0,0 +1,179 @@ +<!-- + - @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<template> + <div class="section" :class="{ selected: isSelected }" @click="showAppDetails"> + <div class="app-image app-image-icon" @click="showAppDetails"> + <div v-if="(listView && !app.preview) || (!listView && !app.screenshot)" class="icon-settings-dark" /> + + <svg v-if="listView && app.preview" + width="32" + height="32" + viewBox="0 0 32 32"> + <defs><filter :id="filterId"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /></filter></defs> + <image x="0" + y="0" + width="32" + height="32" + preserveAspectRatio="xMinYMin meet" + :filter="filterUrl" + :xlink:href="app.preview" + class="app-icon" /> + </svg> + + <img v-if="!listView && app.screenshot" :src="app.screenshot" width="100%"> + </div> + <div class="app-name" @click="showAppDetails"> + {{ app.name }} + </div> + <div v-if="!listView" class="app-summary"> + {{ app.summary }} + </div> + <div v-if="listView" class="app-version"> + <span v-if="app.version">{{ app.version }}</span> + <span v-else-if="app.appstoreData.releases[0].version">{{ app.appstoreData.releases[0].version }}</span> + </div> + + <div class="app-level"> + <span v-if="app.level === 300" + v-tooltip.auto="t('settings', 'This app is supported via your current Nextcloud subscription.')" + class="supported icon-checkmark-color"> + {{ t('settings', 'Supported') }}</span> + <span v-if="app.level === 200" + v-tooltip.auto="t('settings', 'Official apps are developed by and within the community. They offer central functionality and are ready for production use.')" + class="official icon-checkmark"> + {{ t('settings', 'Official') }}</span> + <AppScore v-if="hasRating && !listView" :score="app.score" /> + </div> + + <div class="actions"> + <div v-if="app.error" class="warning"> + {{ app.error }} + </div> + <div v-if="loading(app.id)" class="icon icon-loading-small" /> + <input v-if="app.update" + class="update primary" + type="button" + :value="t('settings', 'Update to {update}', {update:app.update})" + :disabled="installing || loading(app.id)" + @click.stop="update(app.id)"> + <input v-if="app.canUnInstall" + class="uninstall" + type="button" + :value="t('settings', 'Remove')" + :disabled="installing || loading(app.id)" + @click.stop="remove(app.id)"> + <input v-if="app.active" + class="enable" + type="button" + :value="t('settings','Disable')" + :disabled="installing || loading(app.id)" + @click.stop="disable(app.id)"> + <input v-if="!app.active && (app.canInstall || app.isCompatible)" + v-tooltip.auto="enableButtonTooltip" + class="enable" + type="button" + :value="enableButtonText" + :disabled="!app.canInstall || installing || loading(app.id)" + @click.stop="enable(app.id)"> + <input v-else-if="!app.active" + v-tooltip.auto="forceEnableButtonTooltip" + class="enable force" + type="button" + :value="forceEnableButtonText" + :disabled="installing || loading(app.id)" + @click.stop="forceEnable(app.id)"> + </div> + </div> +</template> + +<script> +import AppScore from './AppScore' +import AppManagement from '../AppManagement' +import SvgFilterMixin from '../SvgFilterMixin' + +export default { + name: 'AppItem', + components: { + AppScore + }, + mixins: [AppManagement, SvgFilterMixin], + props: { + app: {}, + category: {}, + listView: { + type: Boolean, + default: true + } + }, + data() { + return { + isSelected: false, + scrolled: false + } + }, + computed: { + hasRating() { + return this.app.appstoreData && this.app.appstoreData.ratingNumOverall > 5 + } + }, + watch: { + '$route.params.id': function(id) { + this.isSelected = (this.app.id === id) + } + }, + mounted() { + this.isSelected = (this.app.id === this.$route.params.id) + }, + watchers: { + + }, + methods: { + showAppDetails(event) { + if (event.currentTarget.tagName === 'INPUT' || event.currentTarget.tagName === 'A') { + return + } + this.$router.push({ + name: 'apps-details', + params: { category: this.category, id: this.app.id } + }) + }, + prefix(prefix, content) { + return prefix + '_' + content + } + } +} +</script> + +<style scoped> + .force { + background: var(--color-main-background); + border-color: var(--color-error); + color: var(--color-error); + } + .force:hover, + .force:active { + background: var(--color-error); + border-color: var(--color-error) !important; + color: var(--color-main-background); + } +</style> diff --git a/apps/settings/src/components/AppList/AppScore.vue b/apps/settings/src/components/AppList/AppScore.vue new file mode 100644 index 0000000000000000000000000000000000000000..810a8b54e8f25f162495155174eb8f4bae0a9438 --- /dev/null +++ b/apps/settings/src/components/AppList/AppScore.vue @@ -0,0 +1,38 @@ +<!-- + - @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<template> + <img :src="scoreImage" class="app-score-image"> +</template> +<script> +export default { + name: 'AppScore', + props: ['score'], + computed: { + scoreImage() { + let score = Math.round(this.score * 10) + let imageName = 'rating/s' + score + '.svg' + return OC.imagePath('core', imageName) + } + } +} +</script> diff --git a/apps/settings/src/components/AppManagement.vue b/apps/settings/src/components/AppManagement.vue new file mode 100644 index 0000000000000000000000000000000000000000..4de40728c5d4b8d04b2a5dbf2e6269d360823a22 --- /dev/null +++ b/apps/settings/src/components/AppManagement.vue @@ -0,0 +1,138 @@ +<!-- + - @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<script> +export default { + computed: { + appGroups() { + return this.app.groups.map(group => { return { id: group, name: group } }) + }, + loading() { + let self = this + return function(id) { + return self.$store.getters.loading(id) + } + }, + installing() { + return this.$store.getters.loading('install') + }, + enableButtonText() { + if (this.app.needsDownload) { + return t('settings', 'Download and enable') + } + return t('settings', 'Enable') + }, + forceEnableButtonText() { + if (this.app.needsDownload) { + return t('settings', 'Enable untested app') + } + return t('settings', 'Enable untested app') + }, + enableButtonTooltip() { + if (this.app.needsDownload) { + return t('settings', 'The app will be downloaded from the app store') + } + return false + }, + forceEnableButtonTooltip() { + const base = t('settings', 'This app is not marked as compatible with your Nextcloud version. If you continue you will still be able to install the app. Note that the app might not work as expected.') + if (this.app.needsDownload) { + return base + ' ' + t('settings', 'The app will be downloaded from the app store') + } + return base + } + }, + mounted() { + if (this.app.groups.length > 0) { + this.groupCheckedAppsData = true + } + }, + methods: { + asyncFindGroup(query) { + return this.$store.dispatch('getGroups', { search: query, limit: 5, offset: 0 }) + }, + isLimitedToGroups(app) { + if (this.app.groups.length || this.groupCheckedAppsData) { + return true + } + return false + }, + setGroupLimit: function() { + if (!this.groupCheckedAppsData) { + this.$store.dispatch('enableApp', { appId: this.app.id, groups: [] }) + } + }, + canLimitToGroups(app) { + if ((app.types && app.types.includes('filesystem')) + || app.types.includes('prelogin') + || app.types.includes('authentication') + || app.types.includes('logging') + || app.types.includes('prevent_group_restriction')) { + return false + } + return true + }, + addGroupLimitation(group) { + let groups = this.app.groups.concat([]).concat([group.id]) + this.$store.dispatch('enableApp', { appId: this.app.id, groups: groups }) + }, + removeGroupLimitation(group) { + let currentGroups = this.app.groups.concat([]) + let index = currentGroups.indexOf(group.id) + if (index > -1) { + currentGroups.splice(index, 1) + } + this.$store.dispatch('enableApp', { appId: this.app.id, groups: currentGroups }) + }, + forceEnable(appId) { + this.$store.dispatch('forceEnableApp', { appId: appId, groups: [] }) + .then((response) => { OC.Settings.Apps.rebuildNavigation() }) + .catch((error) => { OC.Notification.show(error) }) + }, + enable(appId) { + this.$store.dispatch('enableApp', { appId: appId, groups: [] }) + .then((response) => { OC.Settings.Apps.rebuildNavigation() }) + .catch((error) => { OC.Notification.show(error) }) + }, + disable(appId) { + this.$store.dispatch('disableApp', { appId: appId }) + .then((response) => { OC.Settings.Apps.rebuildNavigation() }) + .catch((error) => { OC.Notification.show(error) }) + }, + remove(appId) { + this.$store.dispatch('uninstallApp', { appId: appId }) + .then((response) => { OC.Settings.Apps.rebuildNavigation() }) + .catch((error) => { OC.Notification.show(error) }) + }, + install(appId) { + this.$store.dispatch('enableApp', { appId: appId }) + .then((response) => { OC.Settings.Apps.rebuildNavigation() }) + .catch((error) => { OC.Notification.show(error) }) + }, + update(appId) { + this.$store.dispatch('updateApp', { appId: appId }) + .then((response) => { OC.Settings.Apps.rebuildNavigation() }) + .catch((error) => { OC.Notification.show(error) }) + } + } +} +</script> diff --git a/apps/settings/src/components/AuthToken.vue b/apps/settings/src/components/AuthToken.vue index fb5a331b72ed74740d76d4c71a641d20d5d518b5..034efc4b6a878ec2641d6506549b52aaf6528219 100644 --- a/apps/settings/src/components/AuthToken.vue +++ b/apps/settings/src/components/AuthToken.vue @@ -23,31 +23,29 @@ <tr :data-id="token.id" :class="wiping"> <td class="client"> - <div :class="iconName.icon"></div> + <div :class="iconName.icon" /> </td> <td class="token-name"> <input v-if="token.canRename && renaming" - type="text" - ref="input" - v-model="newName" - @keyup.enter="rename" - @blur="cancelRename" - @keyup.esc="cancelRename"> - <span v-else>{{iconName.name}}</span> - <span v-if="wiping" - class="wiping-warning">({{ t('settings', 'Marked for remote wipe') }})</span> + ref="input" + v-model="newName" + type="text" + @keyup.enter="rename" + @blur="cancelRename" + @keyup.esc="cancelRename"> + <span v-else>{{ iconName.name }}</span> + <span v-if="wiping" class="wiping-warning">({{ t('settings', 'Marked for remote wipe') }})</span> </td> <td> - <span class="last-activity" v-tooltip="lastActivity">{{lastActivityRelative}}</span> + <span v-tooltip="lastActivity" class="last-activity">{{ lastActivityRelative }}</span> </td> <td class="more"> <Actions v-if="!token.current" - :actions="actions" - :open.sync="actionOpen" v-tooltip.auto="{ content: t('settings', 'Device settings'), container: 'body' - }"> + }" + :open.sync="actionOpen"> <ActionCheckbox v-if="token.type === 1" :checked="token.scope.filesystem" @change.stop.prevent="$emit('toggleScope', token, 'filesystem', !token.scope.filesystem)"> @@ -91,7 +89,7 @@ import { Actions, ActionButton, ActionCheckbox -} from 'nextcloud-vue'; +} from 'nextcloud-vue' const userAgentMap = { ie: /(?:MSIE|Trident|Trident\/7.0; rv)[ :](\d+)/, @@ -106,18 +104,18 @@ const userAgentMap = { // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent androidChrome: /Android.*(?:; (.*) Build\/).*Chrome\/(\d+)[0-9.]+/, iphone: / *CPU +iPhone +OS +([0-9]+)_(?:[0-9_])+ +like +Mac +OS +X */, - ipad: /\(iPad\; *CPU +OS +([0-9]+)_(?:[0-9_])+ +like +Mac +OS +X */, - iosClient: /^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/, - androidClient: /^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/, - iosTalkClient: /^Mozilla\/5\.0 \(iOS\) Nextcloud\-Talk.*$/, - androidTalkClient: /^Mozilla\/5\.0 \(Android\) Nextcloud\-Talk.*$/, + ipad: /\(iPad; *CPU +OS +([0-9]+)_(?:[0-9_])+ +like +Mac +OS +X */, + iosClient: /^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)-iOS.*$/, + androidClient: /^Mozilla\/5\.0 \(Android\) ownCloud-android.*$/, + iosTalkClient: /^Mozilla\/5\.0 \(iOS\) Nextcloud-Talk.*$/, + androidTalkClient: /^Mozilla\/5\.0 \(Android\) Nextcloud-Talk.*$/, // DAVdroid/1.2 (2016/07/03; dav4android; okhttp3) Android/6.0.1 davDroid: /DAV(droid|x5)\/([0-9.]+)/, // Mozilla/5.0 (U; Linux; Maemo; Jolla; Sailfish; like Android 4.3) AppleWebKit/538.1 (KHTML, like Gecko) WebPirate/2.0 like Mobile Safari/538.1 (compatible) webPirate: /(Sailfish).*WebPirate\/(\d+)/, // Mozilla/5.0 (Maemo; Linux; U; Jolla; Sailfish; Mobile; rv:31.0) Gecko/31.0 Firefox/31.0 SailfishBrowser/1.0 sailfishBrowser: /(Sailfish).*SailfishBrowser\/(\d+)/ -}; +} const nameMap = { ie: t('setting', 'Internet Explorer'), edge: t('setting', 'Edge'), @@ -134,7 +132,7 @@ const nameMap = { davDroid: 'DAVdroid', webPirate: 'WebPirate', sailfishBrowser: 'SailfishBrowser' -}; +} const iconMap = { ie: 'icon-desktop', edge: 'icon-desktop', @@ -151,10 +149,10 @@ const iconMap = { davDroid: 'icon-phone', webPirate: 'icon-link', sailfishBrowser: 'icon-link' -}; +} export default { - name: "AuthToken", + name: 'AuthToken', components: { Actions, ActionButton, @@ -163,91 +161,93 @@ export default { props: { token: { type: Object, - required: true, + required: true + } + }, + data() { + return { + showMore: this.token.canScope || this.token.canDelete, + renaming: false, + newName: '', + actionOpen: false } }, computed: { - lastActivityRelative () { - return OC.Util.relativeModifiedDate(this.token.lastActivity * 1000); + lastActivityRelative() { + return OC.Util.relativeModifiedDate(this.token.lastActivity * 1000) }, - lastActivity () { - return OC.Util.formatDate(this.token.lastActivity * 1000, 'LLL'); + lastActivity() { + return OC.Util.formatDate(this.token.lastActivity * 1000, 'LLL') }, - iconName () { + iconName() { // pretty format sync client user agent - let matches = this.token.name.match(/Mozilla\/5\.0 \((\w+)\) (?:mirall|csyncoC)\/(\d+\.\d+\.\d+)/); + let matches = this.token.name.match(/Mozilla\/5\.0 \((\w+)\) (?:mirall|csyncoC)\/(\d+\.\d+\.\d+)/) - let icon = ''; + let icon = '' if (matches) { + /* eslint-disable-next-line */ this.token.name = t('settings', 'Sync client - {os}', { os: matches[1], version: matches[2] - }); - icon = 'icon-desktop'; + }) + icon = 'icon-desktop' } // preserve title for cases where we format it further - const title = this.token.name; - let name = this.token.name; + const title = this.token.name + let name = this.token.name for (let client in userAgentMap) { - if (matches = title.match(userAgentMap[client])) { + const matches = title.match(userAgentMap[client]) + if (matches) { if (matches[2] && matches[1]) { // version number and os - name = nameMap[client] + ' ' + matches[2] + ' - ' + matches[1]; + name = nameMap[client] + ' ' + matches[2] + ' - ' + matches[1] } else if (matches[1]) { // only version number - name = nameMap[client] + ' ' + matches[1]; + name = nameMap[client] + ' ' + matches[1] } else { - name = nameMap[client]; + name = nameMap[client] } - icon = iconMap[client]; + icon = iconMap[client] } } if (this.token.current) { - name = t('settings', 'This session'); + name = t('settings', 'This session') } return { icon, - name, - }; + name + } }, wiping() { - return this.token.type === 2; + return this.token.type === 2 } }, - data () { - return { - showMore: this.token.canScope || this.token.canDelete, - renaming: false, - newName: '', - actionOpen: false, - }; - }, methods: { startRename() { // Close action (popover menu) - this.actionOpen = false; + this.actionOpen = false - this.newName = this.token.name; - this.renaming = true; + this.newName = this.token.name + this.renaming = true this.$nextTick(() => { - this.$refs.input.select(); - }); + this.$refs.input.select() + }) }, cancelRename() { - this.renaming = false; + this.renaming = false }, revoke() { - this.actionOpen = false; + this.actionOpen = false this.$emit('delete', this.token) }, rename() { - this.renaming = false; - this.$emit('rename', this.token, this.newName); + this.renaming = false + this.$emit('rename', this.token, this.newName) }, wipe() { - this.actionOpen = false; - this.$emit('wipe', this.token); + this.actionOpen = false + this.$emit('wipe', this.token) } } } diff --git a/apps/settings/src/components/AuthTokenList.vue b/apps/settings/src/components/AuthTokenList.vue index a7c0faf1b7b785dd697308e31e66085abc2eef4a..65f42197eda000592bdda562c88b2660a65a3e16 100644 --- a/apps/settings/src/components/AuthTokenList.vue +++ b/apps/settings/src/components/AuthTokenList.vue @@ -22,67 +22,67 @@ <template> <table id="app-tokens-table"> <thead v-if="tokens.length"> - <tr> - <th></th> - <th>{{ t('settings', 'Device') }}</th> - <th>{{ t('settings', 'Last activity') }}</th> - <th></th> - </tr> + <tr> + <th /> + <th>{{ t('settings', 'Device') }}</th> + <th>{{ t('settings', 'Last activity') }}</th> + <th /> + </tr> </thead> <tbody class="token-list"> - <AuthToken v-for="token in sortedTokens" - :key="token.id" - :token="token" - @toggleScope="toggleScope" - @rename="rename" - @delete="onDelete" - @wipe="onWipe" /> + <AuthToken v-for="token in sortedTokens" + :key="token.id" + :token="token" + @toggleScope="toggleScope" + @rename="rename" + @delete="onDelete" + @wipe="onWipe" /> </tbody> </table> </template> <script> - import AuthToken from './AuthToken'; +import AuthToken from './AuthToken' - export default { - name: 'AuthTokenList', - components: { - AuthToken +export default { + name: 'AuthTokenList', + components: { + AuthToken + }, + props: { + tokens: { + type: Array, + required: true + } + }, + computed: { + sortedTokens() { + return this.tokens.slice().sort((t1, t2) => { + var ts1 = parseInt(t1.lastActivity, 10) + var ts2 = parseInt(t2.lastActivity, 10) + return ts2 - ts1 + }) + } + }, + methods: { + toggleScope(token, scope, value) { + // Just pass it on + this.$emit('toggleScope', token, scope, value) }, - props: { - tokens: { - type: Array, - required: true, - } + rename(token, newName) { + // Just pass it on + this.$emit('rename', token, newName) }, - computed: { - sortedTokens () { - return this.tokens.sort((t1, t2) => { - var ts1 = parseInt(t1.lastActivity, 10); - var ts2 = parseInt(t2.lastActivity, 10); - return ts2 - ts1; - }) - } + onDelete(token) { + // Just pass it on + this.$emit('delete', token) }, - methods: { - toggleScope (token, scope, value) { - // Just pass it on - this.$emit('toggleScope', token, scope, value); - }, - rename (token, newName) { - // Just pass it on - this.$emit('rename', token, newName); - }, - onDelete (token) { - // Just pass it on - this.$emit('delete', token); - }, - onWipe(token) { - // Just pass it on - this.$emit('wipe', token); - } + onWipe(token) { + // Just pass it on + this.$emit('wipe', token) } } +} </script> <style lang="scss" scoped> diff --git a/apps/settings/src/components/AuthTokenSection.vue b/apps/settings/src/components/AuthTokenSection.vue index c53256102d3c86d6d77e4ec565ce833c785cb7fb..fe7ec640afb3a6c91a9faaf2c9e2a0ed5b3a3300 100644 --- a/apps/settings/src/components/AuthTokenSection.vue +++ b/apps/settings/src/components/AuthTokenSection.vue @@ -22,155 +22,159 @@ <template> <div id="security" class="section"> <h2>{{ t('settings', 'Devices & sessions') }}</h2> - <p class="settings-hint hidden-when-empty">{{ t('settings', 'Web, desktop and mobile clients currently logged in to your account.') }}</p> + <p class="settings-hint hidden-when-empty"> + {{ t('settings', 'Web, desktop and mobile clients currently logged in to your account.') }} + </p> <AuthTokenList :tokens="tokens" - @toggleScope="toggleTokenScope" - @rename="rename" - @delete="deleteToken" - @wipe="wipeToken" /> + @toggleScope="toggleTokenScope" + @rename="rename" + @delete="deleteToken" + @wipe="wipeToken" /> <AuthTokenSetupDialogue v-if="canCreateToken" :add="addNewToken" /> </div> </template> <script> - import Axios from 'nextcloud-axios'; - import confirmPassword from 'nextcloud-password-confirmation'; - - import AuthTokenList from './AuthTokenList'; - import AuthTokenSetupDialogue from './AuthTokenSetupDialogue'; - - const confirm = () => { - return new Promise(res => { - OC.dialogs.confirm( - t('core', 'Do you really want to wipe your data from this device?'), - t('core', 'Confirm wipe'), - res, - true - ) - }) - } +import axios from 'nextcloud-axios' +import confirmPassword from 'nextcloud-password-confirmation' + +import AuthTokenList from './AuthTokenList' +import AuthTokenSetupDialogue from './AuthTokenSetupDialogue' + +const confirm = () => { + return new Promise(resolve => { + OC.dialogs.confirm( + t('core', 'Do you really want to wipe your data from this device?'), + t('core', 'Confirm wipe'), + resolve, + true + ) + }) +} + +/** + * Tap into a promise without losing the value + * @param {Function} cb the callback + * @returns {any} val the value + */ +const tap = cb => val => { + cb(val) + return val +} + +export default { + name: 'AuthTokenSection', + components: { + AuthTokenSetupDialogue, + AuthTokenList + }, + props: { + tokens: { + type: Array, + required: true + }, + canCreateToken: { + type: Boolean, + required: true + } + }, + data() { + return { + baseUrl: OC.generateUrl('/settings/personal/authtokens') + } + }, + methods: { + addNewToken(name) { + console.debug('creating a new app token', name) - /** - * Tap into a promise without losing the value - */ - const tap = cb => val => { - cb(val); - return val; - }; - - export default { - name: "AuthTokenSection", - props: { - tokens: { - type: Array, - required: true, - }, - canCreateToken: { - type: Boolean, - required: true + const data = { + name } + return axios.post(this.baseUrl, data) + .then(resp => resp.data) + .then(tap(() => console.debug('app token created'))) + .then(tap(data => this.tokens.push(data.deviceToken))) + .catch(err => { + console.error.bind('could not create app password', err) + OC.Notification.showTemporary(t('core', 'Error while creating device token')) + throw err + }) }, - components: { - AuthTokenSetupDialogue, - AuthTokenList + toggleTokenScope(token, scope, value) { + console.debug('updating app token scope', token.id, scope, value) + + const oldVal = token.scope[scope] + token.scope[scope] = value + + return this.updateToken(token) + .then(tap(() => console.debug('app token scope updated'))) + .catch(err => { + console.error.bind('could not update app token scope', err) + OC.Notification.showTemporary(t('core', 'Error while updating device token scope')) + + // Restore + token.scope[scope] = oldVal + + throw err + }) }, - data() { - return { - baseUrl: OC.generateUrl('/settings/personal/authtokens'), - } + rename(token, newName) { + console.debug('renaming app token', token.id, token.name, newName) + + const oldName = token.name + token.name = newName + + return this.updateToken(token) + .then(tap(() => console.debug('app token name updated'))) + .catch(err => { + console.error.bind('could not update app token name', err) + OC.Notification.showTemporary(t('core', 'Error while updating device token name')) + + // Restore + token.name = oldName + }) + }, + updateToken(token) { + return axios.put(this.baseUrl + '/' + token.id, token) + .then(resp => resp.data) + }, + deleteToken(token) { + console.debug('deleting app token', token) + + this.tokens = this.tokens.filter(t => t !== token) + + return axios.delete(this.baseUrl + '/' + token.id) + .then(resp => resp.data) + .then(tap(() => console.debug('app token deleted'))) + .catch(err => { + console.error.bind('could not delete app token', err) + OC.Notification.showTemporary(t('core', 'Error while deleting the token')) + + // Restore + this.tokens.push(token) + }) }, - methods: { - addNewToken (name) { - console.debug('creating a new app token', name); - - const data = { - name, - }; - return Axios.post(this.baseUrl, data) - .then(resp => resp.data) - .then(tap(() => console.debug('app token created'))) - .then(tap(data => this.tokens.push(data.deviceToken))) - .catch(err => { - console.error.bind('could not create app password', err); - OC.Notification.showTemporary(t('core', 'Error while creating device token')); - throw err; - }); - }, - toggleTokenScope (token, scope, value) { - console.debug('updating app token scope', token.id, scope, value); - - const oldVal = token.scope[scope]; - token.scope[scope] = value; - - return this.updateToken(token) - .then(tap(() => console.debug('app token scope updated'))) - .catch(err => { - console.error.bind('could not update app token scope', err); - OC.Notification.showTemporary(t('core', 'Error while updating device token scope')); - - // Restore - token.scope[scope] = oldVal; - - throw err; - }) - }, - rename (token, newName) { - console.debug('renaming app token', token.id, token.name, newName); - - const oldName = token.name; - token.name = newName; - - return this.updateToken(token) - .then(tap(() => console.debug('app token name updated'))) - .catch(err => { - console.error.bind('could not update app token name', err); - OC.Notification.showTemporary(t('core', 'Error while updating device token name')); - - // Restore - token.name = oldName; - }) - }, - updateToken (token) { - return Axios.put(this.baseUrl + '/' + token.id, token) - .then(resp => resp.data) - }, - deleteToken (token) { - console.debug('deleting app token', token); - - this.tokens = this.tokens.filter(t => t !== token); - - return Axios.delete(this.baseUrl + '/' + token.id) - .then(resp => resp.data) - .then(tap(() => console.debug('app token deleted'))) - .catch(err => { - console.error.bind('could not delete app token', err); - OC.Notification.showTemporary(t('core', 'Error while deleting the token')); - - // Restore - this.tokens.push(token); - }) - }, - async wipeToken(token) { - console.debug('wiping app token', token); - - try { - await confirmPassword() - - if (!(await confirm())) { - console.debug('wipe aborted by user') - return; - } - await Axios.post(this.baseUrl + '/wipe/' + token.id) - console.debug('app token marked for wipe') - - token.type = 2; - } catch (err) { - console.error('could not wipe app token', err); - OC.Notification.showTemporary(t('core', 'Error while wiping the device with the token')); + async wipeToken(token) { + console.debug('wiping app token', token) + + try { + await confirmPassword() + + if (!(await confirm())) { + console.debug('wipe aborted by user') + return } + await axios.post(this.baseUrl + '/wipe/' + token.id) + console.debug('app token marked for wipe') + + token.type = 2 + } catch (err) { + console.error('could not wipe app token', err) + OC.Notification.showTemporary(t('core', 'Error while wiping the device with the token')) } } } +} </script> <style scoped> diff --git a/apps/settings/src/components/AuthTokenSetupDialogue.vue b/apps/settings/src/components/AuthTokenSetupDialogue.vue index 000e873e659d7de81d4b528850aa4f062bdf1f3c..22c867b3c96e3963d426b87b3faf4880eb18909a 100644 --- a/apps/settings/src/components/AuthTokenSetupDialogue.vue +++ b/apps/settings/src/components/AuthTokenSetupDialogue.vue @@ -22,13 +22,14 @@ <template> <div v-if="!adding"> <input v-model="deviceName" - type="text" - @keydown.enter="submit" - :disabled="loading" - :placeholder="t('settings', 'App name')"> + type="text" + :disabled="loading" + :placeholder="t('settings', 'App name')" + @keydown.enter="submit"> <button class="button" - :disabled="loading" - @click="submit">{{ t('settings', 'Create new app password') }} + :disabled="loading" + @click="submit"> + {{ t('settings', 'Create new app password') }} </button> </div> <div v-else> @@ -37,142 +38,142 @@ <div class="app-password-row"> <span class="app-password-label">{{ t('settings', 'Username') }}</span> <input :value="loginName" - type="text" - class="monospaced" - readonly="readonly" - @focus="selectInput"/> + type="text" + class="monospaced" + readonly="readonly" + @focus="selectInput"> </div> <div class="app-password-row"> <span class="app-password-label">{{ t('settings', 'Password') }}</span> - <input :value="appPassword" - type="text" - class="monospaced" - ref="appPassword" - readonly="readonly" - @focus="selectInput"/> - <a class="icon icon-clippy" - ref="clipboardButton" - v-tooltip="copyTooltipOptions" - @mouseover="hoveringCopyButton = true" - @mouseleave="hoveringCopyButton = false" - v-clipboard:copy="appPassword" - v-clipboard:success="onCopyPassword" - v-clipboard:error="onCopyPasswordFailed"></a> + <input ref="appPassword" + :value="appPassword" + type="text" + class="monospaced" + readonly="readonly" + @focus="selectInput"> + <a ref="clipboardButton" + v-tooltip="copyTooltipOptions" + v-clipboard:copy="appPassword" + v-clipboard:success="onCopyPassword" + v-clipboard:error="onCopyPasswordFailed" + class="icon icon-clippy" + @mouseover="hoveringCopyButton = true" + @mouseleave="hoveringCopyButton = false" /> <button class="button" - @click="reset"> + @click="reset"> {{ t('settings', 'Done') }} </button> </div> <div class="app-password-row"> - <span class="app-password-label"></span> + <span class="app-password-label" /> <a v-if="!showQR" - @click="showQR = true"> + @click="showQR = true"> {{ t('settings', 'Show QR code for mobile apps') }} </a> <QR v-else - :value="qrUrl"></QR> + :value="qrUrl" /> </div> </div> </template> <script> - import QR from '@chenfengyuan/vue-qrcode'; - import confirmPassword from 'nextcloud-password-confirmation'; +import QR from '@chenfengyuan/vue-qrcode' +import confirmPassword from 'nextcloud-password-confirmation' - export default { - name: 'AuthTokenSetupDialogue', - components: { - QR, - }, - props: { - add: { - type: Function, - required: true, - } - }, - data () { - return { - adding: false, - loading: false, - deviceName: '', - appPassword: '', - loginName: '', - passwordCopied: false, - showQR: false, - qrUrl: '', - hoveringCopyButton: false, +export default { + name: 'AuthTokenSetupDialogue', + components: { + QR + }, + props: { + add: { + type: Function, + required: true + } + }, + data() { + return { + adding: false, + loading: false, + deviceName: '', + appPassword: '', + loginName: '', + passwordCopied: false, + showQR: false, + qrUrl: '', + hoveringCopyButton: false + } + }, + computed: { + copyTooltipOptions() { + const base = { + hideOnTargetClick: false, + trigger: 'manual' } - }, - computed: { - copyTooltipOptions() { - const base = { - hideOnTargetClick: false, - trigger: 'manual', - }; - if (this.passwordCopied) { - return { - ...base, - content:t('core', 'Copied!'), - show: true, - } - } else { - return { - ...base, - content: t('core', 'Copy'), - show: this.hoveringCopyButton, - } + if (this.passwordCopied) { + return { + ...base, + content: t('core', 'Copied!'), + show: true + } + } else { + return { + ...base, + content: t('core', 'Copy'), + show: this.hoveringCopyButton } } + } + }, + methods: { + selectInput(e) { + e.currentTarget.select() }, - methods: { - selectInput (e) { - e.currentTarget.select(); - }, - submit: function () { - confirmPassword() - .then(() => { - this.loading = true; - return this.add(this.deviceName) - }) - .then(token => { - this.adding = true; - this.loginName = token.loginName; - this.appPassword = token.token; + submit: function() { + confirmPassword() + .then(() => { + this.loading = true + return this.add(this.deviceName) + }) + .then(token => { + this.adding = true + this.loginName = token.loginName + this.appPassword = token.token - const server = window.location.protocol + '//' + window.location.host + OC.getRootPath(); - this.qrUrl = `nc://login/user:${token.loginName}&password:${token.token}&server:${server}`; + const server = window.location.protocol + '//' + window.location.host + OC.getRootPath() + this.qrUrl = `nc://login/user:${token.loginName}&password:${token.token}&server:${server}` - this.$nextTick(() => { - this.$refs.appPassword.select(); - }); + this.$nextTick(() => { + this.$refs.appPassword.select() }) - .catch(err => { - console.error('could not create a new app password', err); - OC.Notification.showTemporary(t('core', 'Error while creating device token')); + }) + .catch(err => { + console.error('could not create a new app password', err) + OC.Notification.showTemporary(t('core', 'Error while creating device token')) - this.reset(); - }); - }, - onCopyPassword() { - this.passwordCopied = true; - this.$refs.clipboardButton.blur(); - setTimeout(() => this.passwordCopied = false, 3000); - }, - onCopyPasswordFailed() { - OC.Notification.showTemporary(t('core', 'Could not copy app password. Please copy it manually.')); - }, - reset () { - this.adding = false; - this.loading = false; - this.showQR = false; - this.qrUrl = ''; - this.deviceName = ''; - this.appPassword = ''; - this.loginName = ''; - } + this.reset() + }) + }, + onCopyPassword() { + this.passwordCopied = true + this.$refs.clipboardButton.blur() + setTimeout(() => { this.passwordCopied = false }, 3000) + }, + onCopyPasswordFailed() { + OC.Notification.showTemporary(t('core', 'Could not copy app password. Please copy it manually.')) + }, + reset() { + this.adding = false + this.loading = false + this.showQR = false + this.qrUrl = '' + this.deviceName = '' + this.appPassword = '' + this.loginName = '' } } +} </script> <style lang="scss" scoped> diff --git a/apps/settings/src/components/PrefixMixin.vue b/apps/settings/src/components/PrefixMixin.vue new file mode 100644 index 0000000000000000000000000000000000000000..e43063d25b038f0d1a5d8821071a5b84be75435f --- /dev/null +++ b/apps/settings/src/components/PrefixMixin.vue @@ -0,0 +1,32 @@ +<!-- + - @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<script> +export default { + name: 'PrefixMixin', + methods: { + prefix(prefix, content) { + return prefix + '_' + content + } + } +} +</script> diff --git a/apps/settings/src/components/SvgFilterMixin.vue b/apps/settings/src/components/SvgFilterMixin.vue new file mode 100644 index 0000000000000000000000000000000000000000..eab5bee973b15132f39286d75a4a92d00280778a --- /dev/null +++ b/apps/settings/src/components/SvgFilterMixin.vue @@ -0,0 +1,40 @@ +<!-- + - @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<script> +export default { + name: 'SvgFilterMixin', + data() { + return { + filterId: '' + } + }, + computed: { + filterUrl() { + return `url(#${this.filterId})` + } + }, + mounted() { + this.filterId = 'invertIconApps' + Math.floor((Math.random() * 100)) + new Date().getSeconds() + new Date().getMilliseconds() + } +} +</script> diff --git a/apps/settings/src/components/UserList.vue b/apps/settings/src/components/UserList.vue new file mode 100644 index 0000000000000000000000000000000000000000..f832837f910da6eff07a2822e454cd341ba0451f --- /dev/null +++ b/apps/settings/src/components/UserList.vue @@ -0,0 +1,553 @@ +<!-- + - @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com> + - + - @author John Molakvoæ <skjnldsv@protonmail.com> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<template> + <div id="app-content" class="user-list-grid" @scroll.passive="onScroll"> + <div id="grid-header" class="row" :class="{'sticky': scrolled && !showConfig.showNewUserForm}"> + <div id="headerAvatar" class="avatar" /> + <div id="headerName" class="name"> + {{ t('settings', 'Username') }} + </div> + <div id="headerDisplayName" class="displayName"> + {{ t('settings', 'Display name') }} + </div> + <div id="headerPassword" class="password"> + {{ t('settings', 'Password') }} + </div> + <div id="headerAddress" class="mailAddress"> + {{ t('settings', 'Email') }} + </div> + <div id="headerGroups" class="groups"> + {{ t('settings', 'Groups') }} + </div> + <div v-if="subAdminsGroups.length>0 && settings.isAdmin" + id="headerSubAdmins" + class="subadmins"> + {{ t('settings', 'Group admin for') }} + </div> + <div id="headerQuota" class="quota"> + {{ t('settings', 'Quota') }} + </div> + <div v-if="showConfig.showLanguages" + id="headerLanguages" + class="languages"> + {{ t('settings', 'Language') }} + </div> + <div v-if="showConfig.showStoragePath" + class="headerStorageLocation storageLocation"> + {{ t('settings', 'Storage location') }} + </div> + <div v-if="showConfig.showUserBackend" + class="headerUserBackend userBackend"> + {{ t('settings', 'User backend') }} + </div> + <div v-if="showConfig.showLastLogin" + class="headerLastLogin lastLogin"> + {{ t('settings', 'Last login') }} + </div> + <div class="userActions" /> + </div> + + <form v-show="showConfig.showNewUserForm" + id="new-user" + class="row" + :disabled="loading.all" + :class="{'sticky': scrolled && showConfig.showNewUserForm}" + @submit.prevent="createUser"> + <div :class="loading.all?'icon-loading-small':'icon-add'" /> + <div class="name"> + <input id="newusername" + ref="newusername" + v-model="newUser.id" + type="text" + required + :placeholder="settings.newUserGenerateUserID + ? t('settings', 'Will be autogenerated') + : t('settings', 'Username')" + name="username" + autocomplete="off" + autocapitalize="none" + autocorrect="off" + pattern="[a-zA-Z0-9 _\.@\-']+" + :disabled="settings.newUserGenerateUserID"> + </div> + <div class="displayName"> + <input id="newdisplayname" + v-model="newUser.displayName" + type="text" + :placeholder="t('settings', 'Display name')" + name="displayname" + autocomplete="off" + autocapitalize="none" + autocorrect="off"> + </div> + <div class="password"> + <input id="newuserpassword" + ref="newuserpassword" + v-model="newUser.password" + type="password" + :required="newUser.mailAddress===''" + :placeholder="t('settings', 'Password')" + name="password" + autocomplete="new-password" + autocapitalize="none" + autocorrect="off" + :minlength="minPasswordLength"> + </div> + <div class="mailAddress"> + <input id="newemail" + v-model="newUser.mailAddress" + type="email" + :required="newUser.password==='' || settings.newUserRequireEmail" + :placeholder="t('settings', 'Email')" + name="email" + autocomplete="off" + autocapitalize="none" + autocorrect="off"> + </div> + <div class="groups"> + <!-- hidden input trick for vanilla html5 form validation --> + <input v-if="!settings.isAdmin" + id="newgroups" + type="text" + :value="newUser.groups" + tabindex="-1" + :required="!settings.isAdmin" + :class="{'icon-loading-small': loading.groups}"> + <Multiselect v-model="newUser.groups" + :options="canAddGroups" + :disabled="loading.groups||loading.all" + tag-placeholder="create" + :placeholder="t('settings', 'Add user in group')" + label="name" + track-by="id" + class="multiselect-vue" + :multiple="true" + :taggable="true" + :close-on-select="false" + :tag-width="60" + @tag="createGroup"> + <!-- If user is not admin, he is a subadmin. + Subadmins can't create users outside their groups + Therefore, empty select is forbidden --> + <span slot="noResult">{{ t('settings', 'No results') }}</span> + </Multiselect> + </div> + <div v-if="subAdminsGroups.length>0 && settings.isAdmin" class="subadmins"> + <Multiselect v-model="newUser.subAdminsGroups" + :options="subAdminsGroups" + :placeholder="t('settings', 'Set user as admin for')" + label="name" + track-by="id" + class="multiselect-vue" + :multiple="true" + :close-on-select="false" + :tag-width="60"> + <span slot="noResult">{{ t('settings', 'No results') }}</span> + </Multiselect> + </div> + <div class="quota"> + <Multiselect v-model="newUser.quota" + :options="quotaOptions" + :placeholder="t('settings', 'Select user quota')" + label="label" + track-by="id" + class="multiselect-vue" + :allow-empty="false" + :taggable="true" + @tag="validateQuota" /> + </div> + <div v-if="showConfig.showLanguages" class="languages"> + <Multiselect v-model="newUser.language" + :options="languages" + :placeholder="t('settings', 'Default language')" + label="name" + track-by="code" + class="multiselect-vue" + :allow-empty="false" + group-values="languages" + group-label="label" /> + </div> + <div v-if="showConfig.showStoragePath" class="storageLocation" /> + <div v-if="showConfig.showUserBackend" class="userBackend" /> + <div v-if="showConfig.showLastLogin" class="lastLogin" /> + <div class="userActions"> + <input id="newsubmit" + type="submit" + class="button primary icon-checkmark-white has-tooltip" + value="" + :title="t('settings', 'Add a new user')"> + </div> + </form> + + <user-row v-for="(user, key) in filteredUsers" + :key="key" + :user="user" + :settings="settings" + :show-config="showConfig" + :groups="groups" + :sub-admins-groups="subAdminsGroups" + :quota-options="quotaOptions" + :languages="languages" + :external-actions="externalActions" /> + <InfiniteLoading ref="infiniteLoading" @infinite="infiniteHandler"> + <div slot="spinner"> + <div class="users-icon-loading icon-loading" /> + </div> + <div slot="no-more"> + <div class="users-list-end" /> + </div> + <div slot="no-results"> + <div id="emptycontent"> + <div class="icon-contacts-dark" /> + <h2>{{ t('settings', 'No users in here') }}</h2> + </div> + </div> + </InfiniteLoading> + </div> +</template> + +<script> +import userRow from './userList/UserRow' +import { Multiselect } from 'nextcloud-vue' +import InfiniteLoading from 'vue-infinite-loading' +import Vue from 'vue' + +const unlimitedQuota = { + id: 'none', + label: t('settings', 'Unlimited') +} +const defaultQuota = { + id: 'default', + label: t('settings', 'Default quota') +} +const newUser = { + id: '', + displayName: '', + password: '', + mailAddress: '', + groups: [], + subAdminsGroups: [], + quota: defaultQuota, + language: { + code: 'en', + name: t('settings', 'Default language') + } +} + +export default { + name: 'UserList', + components: { + userRow, + Multiselect, + InfiniteLoading + }, + props: { + users: { + type: Array, + default: () => [] + }, + showConfig: { + type: Object, + required: true + }, + selectedGroup: { + type: String, + default: null + }, + externalActions: { + type: Array, + default: () => [] + } + }, + data() { + return { + unlimitedQuota, + defaultQuota, + loading: { + all: false, + groups: false + }, + scrolled: false, + searchQuery: '', + newUser: Object.assign({}, newUser) + } + }, + computed: { + settings() { + return this.$store.getters.getServerData + }, + filteredUsers() { + if (this.selectedGroup === 'disabled') { + return this.users.filter(user => user.enabled === false) + } + if (!this.settings.isAdmin) { + // we don't want subadmins to edit themselves + return this.users.filter(user => user.enabled !== false && user.id !== OC.getCurrentUser().uid) + } + return this.users.filter(user => user.enabled !== false) + }, + groups() { + // data provided php side + remove the disabled group + return this.$store.getters.getGroups + .filter(group => group.id !== 'disabled') + .sort((a, b) => a.name.localeCompare(b.name)) + }, + canAddGroups() { + // disabled if no permission to add new users to group + return this.groups.map(group => { + // clone object because we don't want + // to edit the original groups + group = Object.assign({}, group) + group.$isDisabled = group.canAdd === false + return group + }) + }, + subAdminsGroups() { + // data provided php side + return this.$store.getters.getSubadminGroups + }, + quotaOptions() { + // convert the preset array into objects + let quotaPreset = this.settings.quotaPreset.reduce((acc, cur) => acc.concat({ id: cur, label: cur }), []) + // add default presets + quotaPreset.unshift(this.unlimitedQuota) + quotaPreset.unshift(this.defaultQuota) + return quotaPreset + }, + minPasswordLength() { + return this.$store.getters.getPasswordPolicyMinLength + }, + usersOffset() { + return this.$store.getters.getUsersOffset + }, + usersLimit() { + return this.$store.getters.getUsersLimit + }, + usersCount() { + return this.users.length + }, + + /* LANGUAGES */ + languages() { + return [ + { + label: t('settings', 'Common languages'), + languages: this.settings.languages.commonlanguages + }, + { + label: t('settings', 'All languages'), + languages: this.settings.languages.languages + } + ] + } + }, + watch: { + // watch url change and group select + selectedGroup: function(val, old) { + // if selected is the disabled group but it's empty + this.redirectIfDisabled() + this.$store.commit('resetUsers') + this.$refs.infiniteLoading.stateChanger.reset() + this.setNewUserDefaultGroup(val) + }, + + // make sure the infiniteLoading state is changed if we manually + // add/remove data from the store + usersCount: function(val, old) { + // deleting the last user, reset the list + if (val === 0 && old === 1) { + this.$refs.infiniteLoading.stateChanger.reset() + // adding the first user, warn the infiniteLoader that + // the list is not empty anymore (we don't fetch the newly + // added user as we already have all the info we need) + } else if (val === 1 && old === 0) { + this.$refs.infiniteLoading.stateChanger.loaded() + } + } + }, + mounted() { + if (!this.settings.canChangePassword) { + OC.Notification.showTemporary(t('settings', 'Password change is disabled because the master key is disabled')) + } + + /** + * Reset and init new user form + */ + this.resetForm() + + /** + * Register search + */ + this.userSearch = new OCA.Search(this.search, this.resetSearch) + + /** + * If disabled group but empty, redirect + */ + this.redirectIfDisabled() + }, + methods: { + onScroll(event) { + this.scrolled = event.target.scrollTo > 0 + }, + + /** + * Validate quota string to make sure it's a valid human file size + * + * @param {string} quota Quota in readable format '5 GB' + * @returns {Object} + */ + validateQuota(quota) { + // only used for new presets sent through @Tag + let validQuota = OC.Util.computerFileSize(quota) + if (validQuota !== null && validQuota >= 0) { + // unify format output + quota = OC.Util.humanFileSize(OC.Util.computerFileSize(quota)) + this.newUser.quota = { id: quota, label: quota } + return this.newUser.quota + } + // Default is unlimited + this.newUser.quota = this.quotaOptions[0] + return this.quotaOptions[0] + }, + + infiniteHandler($state) { + this.$store.dispatch('getUsers', { + offset: this.usersOffset, + limit: this.usersLimit, + group: this.selectedGroup !== 'disabled' ? this.selectedGroup : '', + search: this.searchQuery + }) + .then((response) => { response ? $state.loaded() : $state.complete() }) + }, + + /* SEARCH */ + search(query) { + this.searchQuery = query + this.$store.commit('resetUsers') + this.$refs.infiniteLoading.stateChanger.reset() + }, + resetSearch() { + this.search('') + }, + + resetForm() { + // revert form to original state + this.newUser = Object.assign({}, newUser) + + /** + * Init default language from server data. The use of this.settings + * requires a computed variable, which break the v-model binding of the form, + * this is a much easier solution than getter and setter on a computed var + */ + if (this.settings.defaultLanguage) { + Vue.set(this.newUser.language, 'code', this.settings.defaultLanguage) + } + + /** + * In case the user directly loaded the user list within a group + * the watch won't be triggered. We need to initialize it. + */ + this.setNewUserDefaultGroup(this.selectedGroup) + + this.loading.all = false + }, + createUser() { + this.loading.all = true + this.$store.dispatch('addUser', { + userid: this.newUser.id, + password: this.newUser.password, + displayName: this.newUser.displayName, + email: this.newUser.mailAddress, + groups: this.newUser.groups.map(group => group.id), + subadmin: this.newUser.subAdminsGroups.map(group => group.id), + quota: this.newUser.quota.id, + language: this.newUser.language.code + }) + .then(() => { + this.resetForm() + this.$refs.newusername.focus() + }) + .catch((error) => { + this.loading.all = false + if (error.response && error.response.data && error.response.data.ocs && error.response.data.ocs.meta) { + const statuscode = error.response.data.ocs.meta.statuscode + if (statuscode === 102) { + // wrong username + this.$refs.newusername.focus() + } else if (statuscode === 107) { + // wrong password + this.$refs.newuserpassword.focus() + } + } + }) + }, + setNewUserDefaultGroup(value) { + if (value && value.length > 0) { + // setting new user default group to the current selected one + let currentGroup = this.groups.find(group => group.id === value) + if (currentGroup) { + this.newUser.groups = [currentGroup] + return + } + } + // fallback, empty selected group + this.newUser.groups = [] + }, + + /** + * Create a new group + * + * @param {string} gid Group id + * @returns {Promise} + */ + createGroup(gid) { + this.loading.groups = true + this.$store.dispatch('addGroup', gid) + .then((group) => { + this.newUser.groups.push(this.groups.find(group => group.id === gid)) + this.loading.groups = false + }) + .catch(() => { + this.loading.groups = false + }) + return this.$store.getters.getGroups[this.groups.length] + }, + + /** + * If the selected group is the disabled group but the count is 0 + * redirect to the all users page. + * * we only check for 0 because we don't have the count on ldap + * * and we therefore set the usercount to -1 in this specific case + */ + redirectIfDisabled() { + const allGroups = this.$store.getters.getGroups + if (this.selectedGroup === 'disabled' + && allGroups.findIndex(group => group.id === 'disabled' && group.usercount === 0) > -1) { + // disabled group is empty, redirection to all users + this.$router.push({ name: 'users' }) + this.$refs.infiniteLoading.stateChanger.reset() + } + } + } +} +</script> diff --git a/apps/settings/src/components/appList.vue b/apps/settings/src/components/appList.vue index dd693b2fdce1621f2585451130f0b85b3b1bc28c..3e40eeb5fbbe0650ab0e7b4e80038d8305341f08 100644 --- a/apps/settings/src/components/appList.vue +++ b/apps/settings/src/components/appList.vue @@ -25,160 +25,177 @@ <div id="apps-list" class="apps-list" :class="{installed: (useBundleView || useListView), store: useAppStoreView}"> <template v-if="useListView"> <transition-group name="app-list" tag="div" class="apps-list-container"> - <app-item v-for="app in apps" :key="app.id" :app="app" :category="category" /> + <AppItem v-for="app in apps" + :key="app.id" + :app="app" + :category="category" /> </transition-group> </template> - <template v-for="bundle in bundles" v-if="useBundleView && bundleApps(bundle.id).length > 0"> - <transition-group name="app-list" tag="div" class="apps-list-container"> - - <div class="apps-header" :key="bundle.id"> - <div class="app-image"></div> - <h2>{{ bundle.name }} <input type="button" :value="bundleToggleText(bundle.id)" v-on:click="toggleBundle(bundle.id)"></h2> - <div class="app-version"></div> - <div class="app-level"></div> - <div class="app-groups"></div> - <div class="actions"> </div> + <transition-group v-if="useBundleView" + name="app-list" + tag="div" + class="apps-list-container"> + <template v-for="bundle in bundles"> + <div :key="bundle.id" class="apps-header"> + <div class="app-image" /> + <h2>{{ bundle.name }} <input type="button" :value="bundleToggleText(bundle.id)" @click="toggleBundle(bundle.id)"></h2> + <div class="app-version" /> + <div class="app-level" /> + <div class="app-groups" /> + <div class="actions"> + + </div> </div> - <app-item v-for="app in bundleApps(bundle.id)" :key="bundle.id + app.id" :app="app" :category="category"/> - </transition-group> - </template> + <AppItem v-for="app in bundleApps(bundle.id)" + :key="bundle.id + app.id" + :app="app" + :category="category" /> + </template> + </transition-group> <template v-if="useAppStoreView"> - <app-item v-for="app in apps" :key="app.id" :app="app" :category="category" :list-view="false" /> + <AppItem v-for="app in apps" + :key="app.id" + :app="app" + :category="category" + :list-view="false" /> </template> - </div> <div id="apps-list-search" class="apps-list installed"> <div class="apps-list-container"> <template v-if="search !== '' && searchApps.length > 0"> <div class="section"> - <div></div> + <div /> <td colspan="5"> <h2>{{ t('settings', 'Results from other categories') }}</h2> </td> </div> - <app-item v-for="app in searchApps" :key="app.id" :app="app" :category="category" :list-view="true" /> + <AppItem v-for="app in searchApps" + :key="app.id" + :app="app" + :category="category" + :list-view="true" /> </template> </div> </div> - <div id="apps-list-empty" class="emptycontent emptycontent-search" v-if="search !== '' && !loading && searchApps.length === 0 && apps.length === 0"> - <div id="app-list-empty-icon" class="icon-settings-dark"></div> - <h2>{{ t('settings', 'No apps found for your version')}}</h2> + <div v-if="search !== '' && !loading && searchApps.length === 0 && apps.length === 0" id="apps-list-empty" class="emptycontent emptycontent-search"> + <div id="app-list-empty-icon" class="icon-settings-dark" /> + <h2>{{ t('settings', 'No apps found for your version') }}</h2> </div> - <div id="searchresults"></div> + <div id="searchresults" /> </div> </template> <script> -import appItem from './appList/appItem'; -import prefix from './prefixMixin'; +import AppItem from './AppList/AppItem' +import PrefixMixin from './PrefixMixin' export default { - name: 'appList', - mixins: [prefix], - props: ['category', 'app', 'search'], + name: 'AppList', components: { - appItem + AppItem }, + mixins: [PrefixMixin], + props: ['category', 'app', 'search'], computed: { loading() { - return this.$store.getters.loading('list'); + return this.$store.getters.loading('list') }, apps() { let apps = this.$store.getters.getAllApps .filter(app => app.name.toLowerCase().search(this.search.toLowerCase()) !== -1) - .sort(function (a, b) { - const sortStringA = '' + (a.active ? 0 : 1) + (a.update ? 0 : 1) + a.name; - const sortStringB = '' + (b.active ? 0 : 1) + (b.update ? 0 : 1) + b.name; - return OC.Util.naturalSortCompare(sortStringA, sortStringB); - }); + .sort(function(a, b) { + const sortStringA = '' + (a.active ? 0 : 1) + (a.update ? 0 : 1) + a.name + const sortStringB = '' + (b.active ? 0 : 1) + (b.update ? 0 : 1) + b.name + return OC.Util.naturalSortCompare(sortStringA, sortStringB) + }) if (this.category === 'installed') { - return apps.filter(app => app.installed); + return apps.filter(app => app.installed) } if (this.category === 'enabled') { - return apps.filter(app => app.active && app.installed); + return apps.filter(app => app.active && app.installed) } if (this.category === 'disabled') { - return apps.filter(app => !app.active && app.installed); + return apps.filter(app => !app.active && app.installed) } if (this.category === 'app-bundles') { - return apps.filter(app => app.bundles); + return apps.filter(app => app.bundles) } if (this.category === 'updates') { - return apps.filter(app => app.update); + return apps.filter(app => app.update) } // filter app store categories return apps.filter(app => { - return app.appstore && app.category !== undefined && - (app.category === this.category || app.category.indexOf(this.category) > -1); - }); + return app.appstore && app.category !== undefined + && (app.category === this.category || app.category.indexOf(this.category) > -1) + }) }, bundles() { - return this.$store.getters.getServerData.bundles; + return this.$store.getters.getServerData.bundles.filter(bundle => this.bundleApps(bundle.id).length > 0) }, bundleApps() { return function(bundle) { return this.$store.getters.getAllApps - .filter(app => app.bundleId === bundle); + .filter(app => app.bundleId === bundle) } }, searchApps() { if (this.search === '') { - return []; + return [] } return this.$store.getters.getAllApps .filter(app => { if (app.name.toLowerCase().search(this.search.toLowerCase()) !== -1) { - return (!this.apps.find(_app => _app.id === app.id)); + return (!this.apps.find(_app => _app.id === app.id)) } - return false; - }); + return false + }) }, useAppStoreView() { - return !this.useListView && !this.useBundleView; + return !this.useListView && !this.useBundleView }, useListView() { - return (this.category === 'installed' || this.category === 'enabled' || this.category === 'disabled' || this.category === 'updates'); + return (this.category === 'installed' || this.category === 'enabled' || this.category === 'disabled' || this.category === 'updates') }, useBundleView() { - return (this.category === 'app-bundles'); + return (this.category === 'app-bundles') }, allBundlesEnabled() { - let self = this; + let self = this return function(id) { - return self.bundleApps(id).filter(app => !app.active).length === 0; + return self.bundleApps(id).filter(app => !app.active).length === 0 } }, bundleToggleText() { - let self = this; + let self = this return function(id) { if (self.allBundlesEnabled(id)) { - return t('settings', 'Disable all'); + return t('settings', 'Disable all') } - return t('settings', 'Enable all'); + return t('settings', 'Enable all') } } }, methods: { toggleBundle(id) { if (this.allBundlesEnabled(id)) { - return this.disableBundle(id); + return this.disableBundle(id) } - return this.enableBundle(id); + return this.enableBundle(id) }, enableBundle(id) { - let apps = this.bundleApps(id).map(app => app.id); + let apps = this.bundleApps(id).map(app => app.id) this.$store.dispatch('enableApp', { appId: apps, groups: [] }) - .catch((error) => { console.log(error); OC.Notification.show(error)}); + .catch((error) => { console.error(error); OC.Notification.show(error) }) }, disableBundle(id) { - let apps = this.bundleApps(id).map(app => app.id); + let apps = this.bundleApps(id).map(app => app.id) this.$store.dispatch('disableApp', { appId: apps, groups: [] }) - .catch((error) => { OC.Notification.show(error)}); + .catch((error) => { OC.Notification.show(error) }) } - }, + } } </script> diff --git a/apps/settings/src/components/appList/appScore.vue b/apps/settings/src/components/appList/appScore.vue index bf04c68818680a73a00db83f357cc371f5959f91..810a8b54e8f25f162495155174eb8f4bae0a9438 100644 --- a/apps/settings/src/components/appList/appScore.vue +++ b/apps/settings/src/components/appList/appScore.vue @@ -21,18 +21,18 @@ --> <template> - <img :src="scoreImage" class="app-score-image" /> + <img :src="scoreImage" class="app-score-image"> </template> <script> - export default { - name: 'appScore', - props: ['score'], - computed: { - scoreImage() { - let score = Math.round( this.score * 10 ); - let imageName = 'rating/s' + score + '.svg'; - return OC.imagePath('core', imageName); - } +export default { + name: 'AppScore', + props: ['score'], + computed: { + scoreImage() { + let score = Math.round(this.score * 10) + let imageName = 'rating/s' + score + '.svg' + return OC.imagePath('core', imageName) } - }; -</script> \ No newline at end of file + } +} +</script> diff --git a/apps/settings/src/components/prefixMixin.vue b/apps/settings/src/components/prefixMixin.vue index e2feb63276d237785b789c70753827993912f4b3..e43063d25b038f0d1a5d8821071a5b84be75435f 100644 --- a/apps/settings/src/components/prefixMixin.vue +++ b/apps/settings/src/components/prefixMixin.vue @@ -21,12 +21,12 @@ --> <script> - export default { - name: 'prefixMixin', - methods: { - prefix (prefix, content) { - return prefix + '_' + content; - }, +export default { + name: 'PrefixMixin', + methods: { + prefix(prefix, content) { + return prefix + '_' + content } } -</script> \ No newline at end of file +} +</script> diff --git a/apps/settings/src/components/svgFilterMixin.vue b/apps/settings/src/components/svgFilterMixin.vue index 1d6e83d482953b979cccd708874c813ed3fc701e..eab5bee973b15132f39286d75a4a92d00280778a 100644 --- a/apps/settings/src/components/svgFilterMixin.vue +++ b/apps/settings/src/components/svgFilterMixin.vue @@ -21,20 +21,20 @@ --> <script> - export default { - name: 'svgFilterMixin', - mounted() { - this.filterId = 'invertIconApps' + Math.floor((Math.random() * 100 )) + new Date().getSeconds() + new Date().getMilliseconds(); - }, - computed: { - filterUrl () { - return `url(#${this.filterId})`; - }, - }, - data() { - return { - filterId: '', - }; - }, +export default { + name: 'SvgFilterMixin', + data() { + return { + filterId: '' + } + }, + computed: { + filterUrl() { + return `url(#${this.filterId})` + } + }, + mounted() { + this.filterId = 'invertIconApps' + Math.floor((Math.random() * 100)) + new Date().getSeconds() + new Date().getMilliseconds() } -</script> \ No newline at end of file +} +</script> diff --git a/apps/settings/src/components/userList/UserRow.vue b/apps/settings/src/components/userList/UserRow.vue new file mode 100644 index 0000000000000000000000000000000000000000..c27523dfadf70f68a9ce16aa5f70eb6afb04b161 --- /dev/null +++ b/apps/settings/src/components/userList/UserRow.vue @@ -0,0 +1,706 @@ +<!-- + - @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com> + - + - @author John Molakvoæ <skjnldsv@protonmail.com> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<template> + <!-- Obfuscated user: Logged in user does not have permissions to see all of the data --> + <div v-if="Object.keys(user).length ===1" class="row" :data-id="user.id"> + <div class="avatar" :class="{'icon-loading-small': loading.delete || loading.disable || loading.wipe}"> + <img v-if="!loading.delete && !loading.disable && !loading.wipe" + alt="" + width="32" + height="32" + :src="generateAvatar(user.id, 32)" + :srcset="generateAvatar(user.id, 64)+' 2x, '+generateAvatar(user.id, 128)+' 4x'"> + </div> + <div class="name"> + {{ user.id }} + </div> + <div class="obfuscated"> + {{ t('settings','You do not have permissions to see the details of this user') }} + </div> + </div> + + <!-- User full data --> + <div v-else + class="row" + :class="{'disabled': loading.delete || loading.disable}" + :data-id="user.id"> + <div class="avatar" :class="{'icon-loading-small': loading.delete || loading.disable || loading.wipe}"> + <img v-if="!loading.delete && !loading.disable && !loading.wipe" + alt="" + width="32" + height="32" + :src="generateAvatar(user.id, 32)" + :srcset="generateAvatar(user.id, 64)+' 2x, '+generateAvatar(user.id, 128)+' 4x'"> + </div> + <!-- dirty hack to ellipsis on two lines --> + <div class="name"> + {{ user.id }} + </div> + <form class="displayName" :class="{'icon-loading-small': loading.displayName}" @submit.prevent="updateDisplayName"> + <template v-if="user.backendCapabilities.setDisplayName"> + <input v-if="user.backendCapabilities.setDisplayName" + :id="'displayName'+user.id+rand" + ref="displayName" + type="text" + :disabled="loading.displayName||loading.all" + :value="user.displayname" + autocomplete="new-password" + autocorrect="off" + autocapitalize="off" + spellcheck="false"> + <input v-if="user.backendCapabilities.setDisplayName" + type="submit" + class="icon-confirm" + value=""> + </template> + <div v-else v-tooltip.auto="t('settings', 'The backend does not support changing the display name')" class="name"> + {{ user.displayname }} + </div> + </form> + <form v-if="settings.canChangePassword && user.backendCapabilities.setPassword" + class="password" + :class="{'icon-loading-small': loading.password}" + @submit.prevent="updatePassword"> + <input :id="'password'+user.id+rand" + ref="password" + type="password" + required + :disabled="loading.password||loading.all" + :minlength="minPasswordLength" + value="" + :placeholder="t('settings', 'New password')" + autocomplete="new-password" + autocorrect="off" + autocapitalize="off" + spellcheck="false"> + <input type="submit" class="icon-confirm" value=""> + </form> + <div v-else /> + <form class="mailAddress" :class="{'icon-loading-small': loading.mailAddress}" @submit.prevent="updateEmail"> + <input :id="'mailAddress'+user.id+rand" + ref="mailAddress" + type="email" + :disabled="loading.mailAddress||loading.all" + :value="user.email" + autocomplete="new-password" + autocorrect="off" + autocapitalize="off" + spellcheck="false"> + <input type="submit" class="icon-confirm" value=""> + </form> + <div class="groups" :class="{'icon-loading-small': loading.groups}"> + <Multiselect :value="userGroups" + :options="availableGroups" + :disabled="loading.groups||loading.all" + tag-placeholder="create" + :placeholder="t('settings', 'Add user in group')" + label="name" + track-by="id" + class="multiselect-vue" + :limit="2" + :multiple="true" + :taggable="settings.isAdmin" + :close-on-select="false" + :tag-width="60" + @tag="createGroup" + @select="addUserGroup" + @remove="removeUserGroup"> + <span slot="limit" v-tooltip.auto="formatGroupsTitle(userGroups)" class="multiselect__limit">+{{ userGroups.length-2 }}</span> + <span slot="noResult">{{ t('settings', 'No results') }}</span> + </Multiselect> + </div> + <div v-if="subAdminsGroups.length>0 && settings.isAdmin" class="subadmins" :class="{'icon-loading-small': loading.subadmins}"> + <Multiselect :value="userSubAdminsGroups" + :options="subAdminsGroups" + :disabled="loading.subadmins||loading.all" + :placeholder="t('settings', 'Set user as admin for')" + label="name" + track-by="id" + class="multiselect-vue" + :limit="2" + :multiple="true" + :close-on-select="false" + :tag-width="60" + @select="addUserSubAdmin" + @remove="removeUserSubAdmin"> + <span slot="limit" v-tooltip.auto="formatGroupsTitle(userSubAdminsGroups)" class="multiselect__limit">+{{ userSubAdminsGroups.length-2 }}</span> + <span slot="noResult">{{ t('settings', 'No results') }}</span> + </Multiselect> + </div> + <div v-tooltip.auto="usedSpace" class="quota" :class="{'icon-loading-small': loading.quota}"> + <Multiselect :value="userQuota" + :options="quotaOptions" + :disabled="loading.quota||loading.all" + tag-placeholder="create" + :placeholder="t('settings', 'Select user quota')" + label="label" + track-by="id" + class="multiselect-vue" + :allow-empty="false" + :taggable="true" + @tag="validateQuota" + @input="setUserQuota" /> + <progress class="quota-user-progress" + :class="{'warn':usedQuota>80}" + :value="usedQuota" + max="100" /> + </div> + <div v-if="showConfig.showLanguages" + class="languages" + :class="{'icon-loading-small': loading.languages}"> + <Multiselect :value="userLanguage" + :options="languages" + :disabled="loading.languages||loading.all" + :placeholder="t('settings', 'No language set')" + label="name" + track-by="code" + class="multiselect-vue" + :allow-empty="false" + group-values="languages" + group-label="label" + @input="setUserLanguage" /> + </div> + <div v-if="showConfig.showStoragePath" class="storageLocation"> + {{ user.storageLocation }} + </div> + <div v-if="showConfig.showUserBackend" class="userBackend"> + {{ user.backend }} + </div> + <div v-if="showConfig.showLastLogin" v-tooltip.auto="user.lastLogin>0 ? OC.Util.formatDate(user.lastLogin) : ''" class="lastLogin"> + {{ user.lastLogin>0 ? OC.Util.relativeModifiedDate(user.lastLogin) : t('settings','Never') }} + </div> + <div class="userActions"> + <div v-if="OC.currentUser !== user.id && user.id !== 'admin' && !loading.all" class="toggleUserActions"> + <div v-click-outside="hideMenu" class="icon-more" @click="toggleMenu" /> + <div class="popovermenu" :class="{ 'open': openedMenu }"> + <PopoverMenu :menu="userActions" /> + </div> + </div> + <div class="feedback" :style="{opacity: feedbackMessage !== '' ? 1 : 0}"> + <div class="icon-checkmark" /> + {{ feedbackMessage }} + </div> + </div> + </div> +</template> + +<script> +import ClickOutside from 'vue-click-outside' +import Vue from 'vue' +import VTooltip from 'v-tooltip' +import { PopoverMenu, Multiselect } from 'nextcloud-vue' + +Vue.use(VTooltip) + +export default { + name: 'UserRow', + components: { + PopoverMenu, + Multiselect + }, + directives: { + ClickOutside + }, + props: { + user: { + type: Object, + required: true + }, + settings: { + type: Object, + default: () => ({}) + }, + groups: { + type: Array, + default: () => [] + }, + subAdminsGroups: { + type: Array, + default: () => [] + }, + quotaOptions: { + type: Array, + default: () => [] + }, + showConfig: { + type: Object, + default: () => ({}) + }, + languages: { + type: Array, + required: true + }, + externalActions: { + type: Array, + default: () => [] + } + }, + data() { + return { + rand: parseInt(Math.random() * 1000), + openedMenu: false, + feedbackMessage: '', + loading: { + all: false, + displayName: false, + password: false, + mailAddress: false, + groups: false, + subadmins: false, + quota: false, + delete: false, + disable: false, + languages: false, + wipe: false + } + } + }, + computed: { + /* USER POPOVERMENU ACTIONS */ + userActions() { + let actions = [ + { + icon: 'icon-delete', + text: t('settings', 'Delete user'), + action: this.deleteUser + }, + { + icon: 'icon-delete', + text: t('settings', 'Wipe all devices'), + action: this.wipeUserDevices + }, + { + icon: this.user.enabled ? 'icon-close' : 'icon-add', + text: this.user.enabled ? t('settings', 'Disable user') : t('settings', 'Enable user'), + action: this.enableDisableUser + } + ] + if (this.user.email !== null && this.user.email !== '') { + actions.push({ + icon: 'icon-mail', + text: t('settings', 'Resend welcome email'), + action: this.sendWelcomeMail + }) + } + return actions.concat(this.externalActions) + }, + + /* GROUPS MANAGEMENT */ + userGroups() { + let userGroups = this.groups.filter(group => this.user.groups.includes(group.id)) + return userGroups + }, + userSubAdminsGroups() { + let userSubAdminsGroups = this.subAdminsGroups.filter(group => this.user.subadmin.includes(group.id)) + return userSubAdminsGroups + }, + availableGroups() { + return this.groups.map((group) => { + // clone object because we don't want + // to edit the original groups + let groupClone = Object.assign({}, group) + + // two settings here: + // 1. user NOT in group but no permission to add + // 2. user is in group but no permission to remove + groupClone.$isDisabled + = (group.canAdd === false + && !this.user.groups.includes(group.id)) + || (group.canRemove === false + && this.user.groups.includes(group.id)) + return groupClone + }) + }, + + /* QUOTA MANAGEMENT */ + usedSpace() { + if (this.user.quota.used) { + return t('settings', '{size} used', { size: OC.Util.humanFileSize(this.user.quota.used) }) + } + return t('settings', '{size} used', { size: OC.Util.humanFileSize(0) }) + }, + usedQuota() { + let quota = this.user.quota.quota + if (quota > 0) { + quota = Math.min(100, Math.round(this.user.quota.used / quota * 100)) + } else { + var usedInGB = this.user.quota.used / (10 * Math.pow(2, 30)) + // asymptotic curve approaching 50% at 10GB to visualize used stace with infinite quota + quota = 95 * (1 - (1 / (usedInGB + 1))) + } + return isNaN(quota) ? 0 : quota + }, + // Mapping saved values to objects + userQuota() { + if (this.user.quota.quota >= 0) { + // if value is valid, let's map the quotaOptions or return custom quota + let humanQuota = OC.Util.humanFileSize(this.user.quota.quota) + let userQuota = this.quotaOptions.find(quota => quota.id === humanQuota) + return userQuota || { id: humanQuota, label: humanQuota } + } else if (this.user.quota.quota === 'default') { + // default quota is replaced by the proper value on load + return this.quotaOptions[0] + } + return this.quotaOptions[1] // unlimited + }, + + /* PASSWORD POLICY? */ + minPasswordLength() { + return this.$store.getters.getPasswordPolicyMinLength + }, + + /* LANGUAGE */ + userLanguage() { + let availableLanguages = this.languages[0].languages.concat(this.languages[1].languages) + let userLang = availableLanguages.find(lang => lang.code === this.user.language) + if (typeof userLang !== 'object' && this.user.language !== '') { + return { + code: this.user.language, + name: this.user.language + } + } else if (this.user.language === '') { + return false + } + return userLang + } + }, + mounted() { + // required if popup needs to stay opened after menu click + // since we only have disable/delete actions, let's close it directly + // this.popupItem = this.$el; + }, + methods: { + /* MENU HANDLING */ + toggleMenu() { + this.openedMenu = !this.openedMenu + }, + hideMenu() { + this.openedMenu = false + }, + + /** + * Generate avatar url + * + * @param {string} user The user name + * @param {int} size Size integer, default 32 + * @returns {string} + */ + generateAvatar(user, size = 32) { + return OC.generateUrl( + '/avatar/{user}/{size}?v={version}', + { + user: user, + size: size, + version: oc_userconfig.avatar.version + } + ) + }, + + /** + * Format array of groups objects to a string for the popup + * + * @param {array} groups The groups + * @returns {string} + */ + formatGroupsTitle(groups) { + let names = groups.map(group => group.name) + return names.slice(2).join(', ') + }, + + wipeUserDevices() { + this.loading.wipe = true + this.loading.all = true + let userid = this.user.id + return this.$store.dispatch('wipeUserDevices', userid) + .then(() => { + this.loading.wipe = false + this.loading.all = false + }) + }, + + deleteUser() { + this.loading.delete = true + this.loading.all = true + let userid = this.user.id + return this.$store.dispatch('deleteUser', userid) + .then(() => { + this.loading.delete = false + this.loading.all = false + }) + }, + + enableDisableUser() { + this.loading.delete = true + this.loading.all = true + let userid = this.user.id + let enabled = !this.user.enabled + return this.$store.dispatch('enableDisableUser', { userid, enabled }) + .then(() => { + this.loading.delete = false + this.loading.all = false + }) + }, + + /** + * Set user displayName + * + * @param {string} displayName The display name + */ + updateDisplayName() { + let displayName = this.$refs.displayName.value + this.loading.displayName = true + this.$store.dispatch('setUserData', { + userid: this.user.id, + key: 'displayname', + value: displayName + }).then(() => { + this.loading.displayName = false + this.$refs.displayName.value = displayName + }) + }, + + /** + * Set user password + * + * @param {string} password The email adress + */ + updatePassword() { + let password = this.$refs.password.value + this.loading.password = true + this.$store.dispatch('setUserData', { + userid: this.user.id, + key: 'password', + value: password + }).then(() => { + this.loading.password = false + this.$refs.password.value = '' // empty & show placeholder + }) + }, + + /** + * Set user mailAddress + * + * @param {string} mailAddress The email adress + */ + updateEmail() { + let mailAddress = this.$refs.mailAddress.value + this.loading.mailAddress = true + this.$store.dispatch('setUserData', { + userid: this.user.id, + key: 'email', + value: mailAddress + }).then(() => { + this.loading.mailAddress = false + this.$refs.mailAddress.value = mailAddress + }) + }, + + /** + * Create a new group and add user to it + * + * @param {string} gid Group id + */ + async createGroup(gid) { + this.loading = { groups: true, subadmins: true } + try { + await this.$store.dispatch('addGroup', gid) + let userid = this.user.id + await this.$store.dispatch('addUserGroup', { userid, gid }) + } catch (error) { + console.error(error) + } finally { + this.loading = { groups: false, subadmins: false } + } + return this.$store.getters.getGroups[this.groups.length] + }, + + /** + * Add user to group + * + * @param {object} group Group object + */ + async addUserGroup(group) { + if (group.canAdd === false) { + return false + } + this.loading.groups = true + let userid = this.user.id + let gid = group.id + try { + await this.$store.dispatch('addUserGroup', { userid, gid }) + } catch (error) { + console.error(error) + } finally { + this.loading.groups = false + } + }, + + /** + * Remove user from group + * + * @param {object} group Group object + */ + async removeUserGroup(group) { + if (group.canRemove === false) { + return false + } + + this.loading.groups = true + let userid = this.user.id + let gid = group.id + + try { + await this.$store.dispatch('removeUserGroup', { userid, gid }) + this.loading.groups = false + // remove user from current list if current list is the removed group + if (this.$route.params.selectedGroup === gid) { + this.$store.commit('deleteUser', userid) + } + } catch { + this.loading.groups = false + } + }, + + /** + * Add user to group + * + * @param {object} group Group object + */ + async addUserSubAdmin(group) { + this.loading.subadmins = true + let userid = this.user.id + let gid = group.id + + try { + await this.$store.dispatch('addUserSubAdmin', { userid, gid }) + this.loading.subadmins = false + } catch (error) { + console.error(error) + } + }, + + /** + * Remove user from group + * + * @param {object} group Group object + */ + async removeUserSubAdmin(group) { + this.loading.subadmins = true + let userid = this.user.id + let gid = group.id + + try { + await this.$store.dispatch('removeUserSubAdmin', { userid, gid }) + } catch (error) { + console.error(error) + } finally { + this.loading.subadmins = false + } + }, + + /** + * Dispatch quota set request + * + * @param {string|Object} quota Quota in readable format '5 GB' or Object {id: '5 GB', label: '5GB'} + * @returns {string} + */ + async setUserQuota(quota = 'none') { + this.loading.quota = true + // ensure we only send the preset id + quota = quota.id ? quota.id : quota + + try { + await this.$store.dispatch('setUserData', { + userid: this.user.id, + key: 'quota', + value: quota + }) + } catch (error) { + console.error(error) + } finally { + this.loading.quota = false + } + return quota + }, + + /** + * Validate quota string to make sure it's a valid human file size + * + * @param {string} quota Quota in readable format '5 GB' + * @returns {Promise|boolean} + */ + validateQuota(quota) { + // only used for new presets sent through @Tag + let validQuota = OC.Util.computerFileSize(quota) + if (validQuota !== null && validQuota >= 0) { + // unify format output + return this.setUserQuota(OC.Util.humanFileSize(OC.Util.computerFileSize(quota))) + } + // if no valid do not change + return false + }, + + /** + * Dispatch language set request + * + * @param {Object} lang language object {code:'en', name:'English'} + * @returns {Object} + */ + async setUserLanguage(lang) { + this.loading.languages = true + // ensure we only send the preset id + try { + await this.$store.dispatch('setUserData', { + userid: this.user.id, + key: 'language', + value: lang.code + }) + } catch (error) { + console.error(error) + } finally { + this.loading.languages = false + } + return lang + }, + + /** + * Dispatch new welcome mail request + */ + sendWelcomeMail() { + this.loading.all = true + this.$store.dispatch('sendWelcomeMail', this.user.id) + .then(success => { + if (success) { + // Show feedback to indicate the success + this.feedbackMessage = t('setting', 'Welcome mail sent!') + setTimeout(() => { + this.feedbackMessage = '' + }, 2000) + } + this.loading.all = false + }) + } + + } +} +</script> diff --git a/apps/settings/src/components/userList/userRow.vue b/apps/settings/src/components/userList/userRow.vue deleted file mode 100644 index 4bcc40965b01a56f0d555d06f36e58215ed5480c..0000000000000000000000000000000000000000 --- a/apps/settings/src/components/userList/userRow.vue +++ /dev/null @@ -1,574 +0,0 @@ -<!-- - - @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com> - - - - @author John Molakvoæ <skjnldsv@protonmail.com> - - - - @license GNU AGPL version 3 or any later version - - - - This program is free software: you can redistribute it and/or modify - - it under the terms of the GNU Affero General Public License as - - published by the Free Software Foundation, either version 3 of the - - License, or (at your option) any later version. - - - - This program is distributed in the hope that it will be useful, - - but WITHOUT ANY WARRANTY; without even the implied warranty of - - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - - GNU Affero General Public License for more details. - - - - You should have received a copy of the GNU Affero General Public License - - along with this program. If not, see <http://www.gnu.org/licenses/>. - - - --> - -<template> - <!-- Obfuscated user: Logged in user does not have permissions to see all of the data --> - <div class="row" v-if="Object.keys(user).length ===1" :data-id="user.id"> - <div class="avatar" :class="{'icon-loading-small': loading.delete || loading.disable || loading.wipe}"> - <img alt="" width="32" height="32" :src="generateAvatar(user.id, 32)" - :srcset="generateAvatar(user.id, 64)+' 2x, '+generateAvatar(user.id, 128)+' 4x'" - v-if="!loading.delete && !loading.disable && !loading.wipe"> - </div> - <div class="name">{{user.id}}</div> - <div class="obfuscated">{{t('settings','You do not have permissions to see the details of this user')}}</div> - </div> - - <!-- User full data --> - <div class="row" v-else :class="{'disabled': loading.delete || loading.disable}" :data-id="user.id"> - <div class="avatar" :class="{'icon-loading-small': loading.delete || loading.disable || loading.wipe}"> - <img alt="" width="32" height="32" :src="generateAvatar(user.id, 32)" - :srcset="generateAvatar(user.id, 64)+' 2x, '+generateAvatar(user.id, 128)+' 4x'" - v-if="!loading.delete && !loading.disable && !loading.wipe"> - </div> - <!-- dirty hack to ellipsis on two lines --> - <div class="name">{{user.id}}</div> - <form class="displayName" :class="{'icon-loading-small': loading.displayName}" v-on:submit.prevent="updateDisplayName"> - <template v-if="user.backendCapabilities.setDisplayName"> - <input v-if="user.backendCapabilities.setDisplayName" - :id="'displayName'+user.id+rand" type="text" - :disabled="loading.displayName||loading.all" - :value="user.displayname" ref="displayName" - autocomplete="new-password" autocorrect="off" autocapitalize="off" spellcheck="false" /> - <input v-if="user.backendCapabilities.setDisplayName" type="submit" class="icon-confirm" value="" /> - </template> - <div v-else class="name" v-tooltip.auto="t('settings', 'The backend does not support changing the display name')">{{user.displayname}}</div> - </form> - <form class="password" v-if="settings.canChangePassword && user.backendCapabilities.setPassword" :class="{'icon-loading-small': loading.password}" - v-on:submit.prevent="updatePassword"> - <input :id="'password'+user.id+rand" type="password" required - :disabled="loading.password||loading.all" :minlength="minPasswordLength" - value="" :placeholder="t('settings', 'New password')" ref="password" - autocomplete="new-password" autocorrect="off" autocapitalize="off" spellcheck="false" /> - <input type="submit" class="icon-confirm" value="" /> - </form> - <div v-else></div> - <form class="mailAddress" :class="{'icon-loading-small': loading.mailAddress}" v-on:submit.prevent="updateEmail"> - <input :id="'mailAddress'+user.id+rand" type="email" - :disabled="loading.mailAddress||loading.all" - :value="user.email" ref="mailAddress" - autocomplete="new-password" autocorrect="off" autocapitalize="off" spellcheck="false" /> - <input type="submit" class="icon-confirm" value="" /> - </form> - <div class="groups" :class="{'icon-loading-small': loading.groups}"> - <multiselect :value="userGroups" :options="availableGroups" :disabled="loading.groups||loading.all" - tag-placeholder="create" :placeholder="t('settings', 'Add user in group')" - label="name" track-by="id" class="multiselect-vue" :limit="2" - :multiple="true" :taggable="settings.isAdmin" :closeOnSelect="false" - :tag-width="60" - @tag="createGroup" @select="addUserGroup" @remove="removeUserGroup"> - <span slot="limit" class="multiselect__limit" v-tooltip.auto="formatGroupsTitle(userGroups)">+{{userGroups.length-2}}</span> - <span slot="noResult">{{t('settings', 'No results')}}</span> - </multiselect> - </div> - <div class="subadmins" v-if="subAdminsGroups.length>0 && settings.isAdmin" :class="{'icon-loading-small': loading.subadmins}"> - <multiselect :value="userSubAdminsGroups" :options="subAdminsGroups" :disabled="loading.subadmins||loading.all" - :placeholder="t('settings', 'Set user as admin for')" - label="name" track-by="id" class="multiselect-vue" :limit="2" - :multiple="true" :closeOnSelect="false" :tag-width="60" - @select="addUserSubAdmin" @remove="removeUserSubAdmin"> - <span slot="limit" class="multiselect__limit" v-tooltip.auto="formatGroupsTitle(userSubAdminsGroups)">+{{userSubAdminsGroups.length-2}}</span> - <span slot="noResult">{{t('settings', 'No results')}}</span> - </multiselect> - </div> - <div class="quota" :class="{'icon-loading-small': loading.quota}" v-tooltip.auto="usedSpace"> - <multiselect :value="userQuota" :options="quotaOptions" :disabled="loading.quota||loading.all" - tag-placeholder="create" :placeholder="t('settings', 'Select user quota')" - label="label" track-by="id" class="multiselect-vue" - :allowEmpty="false" :taggable="true" - @tag="validateQuota" @input="setUserQuota"> - </multiselect> - <progress class="quota-user-progress" :class="{'warn':usedQuota>80}" :value="usedQuota" max="100"></progress> - </div> - <div class="languages" :class="{'icon-loading-small': loading.languages}" - v-if="showConfig.showLanguages"> - <multiselect :value="userLanguage" :options="languages" :disabled="loading.languages||loading.all" - :placeholder="t('settings', 'No language set')" - label="name" track-by="code" class="multiselect-vue" - :allowEmpty="false" group-values="languages" group-label="label" - @input="setUserLanguage"> - </multiselect> - </div> - <div class="storageLocation" v-if="showConfig.showStoragePath">{{user.storageLocation}}</div> - <div class="userBackend" v-if="showConfig.showUserBackend">{{user.backend}}</div> - <div class="lastLogin" v-if="showConfig.showLastLogin" v-tooltip.auto="user.lastLogin>0 ? OC.Util.formatDate(user.lastLogin) : ''"> - {{user.lastLogin>0 ? OC.Util.relativeModifiedDate(user.lastLogin) : t('settings','Never')}} - </div> - <div class="userActions"> - <div class="toggleUserActions" v-if="OC.currentUser !== user.id && user.id !== 'admin' && !loading.all"> - <div class="icon-more" v-click-outside="hideMenu" @click="toggleMenu"></div> - <div class="popovermenu" :class="{ 'open': openedMenu }"> - <popover-menu :menu="userActions" /> - </div> - </div> - <div class="feedback" :style="{opacity: feedbackMessage !== '' ? 1 : 0}"> - <div class="icon-checkmark"></div> - {{feedbackMessage}} - </div> - </div> - </div> -</template> - -<script> -import ClickOutside from 'vue-click-outside'; -import Vue from 'vue' -import VTooltip from 'v-tooltip' -import { PopoverMenu, Multiselect } from 'nextcloud-vue' - -Vue.use(VTooltip) - -export default { - name: 'userRow', - props: ['user', 'settings', 'groups', 'subAdminsGroups', 'quotaOptions', 'showConfig', 'languages', 'externalActions'], - components: { - PopoverMenu, - Multiselect - }, - directives: { - ClickOutside - }, - mounted() { - // required if popup needs to stay opened after menu click - // since we only have disable/delete actions, let's close it directly - // this.popupItem = this.$el; - }, - data() { - return { - rand: parseInt(Math.random() * 1000), - openedMenu: false, - feedbackMessage: '', - loading: { - all: false, - displayName: false, - password: false, - mailAddress: false, - groups: false, - subadmins: false, - quota: false, - delete: false, - disable: false, - languages: false, - wipe: false, - } - } - }, - computed: { - /* USER POPOVERMENU ACTIONS */ - userActions() { - let actions = [ - { - icon: 'icon-delete', - text: t('settings', 'Delete user'), - action: this.deleteUser, - }, - { - icon: 'icon-delete', - text: t('settings', 'Wipe all devices'), - action: this.wipeUserDevices, - }, - { - icon: this.user.enabled ? 'icon-close' : 'icon-add', - text: this.user.enabled ? t('settings', 'Disable user') : t('settings', 'Enable user'), - action: this.enableDisableUser, - }, - ]; - if (this.user.email !== null && this.user.email !== '') { - actions.push({ - icon: 'icon-mail', - text: t('settings','Resend welcome email'), - action: this.sendWelcomeMail - }) - } - return actions.concat(this.externalActions); - }, - - /* GROUPS MANAGEMENT */ - userGroups() { - let userGroups = this.groups.filter(group => this.user.groups.includes(group.id)); - return userGroups; - }, - userSubAdminsGroups() { - let userSubAdminsGroups = this.subAdminsGroups.filter(group => this.user.subadmin.includes(group.id)); - return userSubAdminsGroups; - }, - availableGroups() { - return this.groups.map((group) => { - // clone object because we don't want - // to edit the original groups - let groupClone = Object.assign({}, group); - - // two settings here: - // 1. user NOT in group but no permission to add - // 2. user is in group but no permission to remove - groupClone.$isDisabled = - (group.canAdd === false && - !this.user.groups.includes(group.id)) || - (group.canRemove === false && - this.user.groups.includes(group.id)); - return groupClone; - }); - }, - - /* QUOTA MANAGEMENT */ - usedSpace() { - if (this.user.quota.used) { - return t('settings', '{size} used', {size: OC.Util.humanFileSize(this.user.quota.used)}); - } - return t('settings', '{size} used', {size: OC.Util.humanFileSize(0)}); - }, - usedQuota() { - let quota = this.user.quota.quota; - if (quota > 0) { - quota = Math.min(100, Math.round(this.user.quota.used / quota * 100)); - } else { - var usedInGB = this.user.quota.used / (10 * Math.pow(2, 30)); - //asymptotic curve approaching 50% at 10GB to visualize used stace with infinite quota - quota = 95 * (1 - (1 / (usedInGB + 1))); - } - return isNaN(quota) ? 0 : quota; - }, - // Mapping saved values to objects - userQuota() { - if (this.user.quota.quota >= 0) { - // if value is valid, let's map the quotaOptions or return custom quota - let humanQuota = OC.Util.humanFileSize(this.user.quota.quota); - let userQuota = this.quotaOptions.find(quota => quota.id === humanQuota); - return userQuota ? userQuota : {id:humanQuota, label:humanQuota}; - } else if (this.user.quota.quota === 'default') { - // default quota is replaced by the proper value on load - return this.quotaOptions[0]; - } - return this.quotaOptions[1]; // unlimited - }, - - /* PASSWORD POLICY? */ - minPasswordLength() { - return this.$store.getters.getPasswordPolicyMinLength; - }, - - /* LANGUAGE */ - userLanguage() { - let availableLanguages = this.languages[0].languages.concat(this.languages[1].languages); - let userLang = availableLanguages.find(lang => lang.code === this.user.language); - if (typeof userLang !== 'object' && this.user.language !== '') { - return { - code: this.user.language, - name: this.user.language - } - } else if(this.user.language === '') { - return false; - } - return userLang; - } - }, - methods: { - /* MENU HANDLING */ - toggleMenu() { - this.openedMenu = !this.openedMenu; - }, - hideMenu() { - this.openedMenu = false; - }, - - /** - * Generate avatar url - * - * @param {string} user The user name - * @param {int} size Size integer, default 32 - * @returns {string} - */ - generateAvatar(user, size=32) { - return OC.generateUrl( - '/avatar/{user}/{size}?v={version}', - { - user: user, - size: size, - version: oc_userconfig.avatar.version - } - ); - }, - - /** - * Format array of groups objects to a string for the popup - * - * @param {array} groups The groups - * @returns {string} - */ - formatGroupsTitle(groups) { - let names = groups.map(group => group.name); - return names.slice(2,).join(', '); - }, - - wipeUserDevices() { - this.loading.wipe = true; - this.loading.all = true; - let userid = this.user.id; - return this.$store.dispatch('wipeUserDevices', userid) - .then(() => { - this.loading.wipe = false - this.loading.all = false - }); - }, - - deleteUser() { - this.loading.delete = true; - this.loading.all = true; - let userid = this.user.id; - return this.$store.dispatch('deleteUser', userid) - .then(() => { - this.loading.delete = false - this.loading.all = false - }); - }, - - enableDisableUser() { - this.loading.delete = true; - this.loading.all = true; - let userid = this.user.id; - let enabled = !this.user.enabled; - return this.$store.dispatch('enableDisableUser', {userid, enabled}) - .then(() => { - this.loading.delete = false - this.loading.all = false - }); - }, - - /** - * Set user displayName - * - * @param {string} displayName The display name - * @returns {Promise} - */ - updateDisplayName() { - let displayName = this.$refs.displayName.value; - this.loading.displayName = true; - this.$store.dispatch('setUserData', { - userid: this.user.id, - key: 'displayname', - value: displayName - }).then(() => { - this.loading.displayName = false; - this.$refs.displayName.value = displayName; - }); - }, - - /** - * Set user password - * - * @param {string} password The email adress - * @returns {Promise} - */ - updatePassword() { - let password = this.$refs.password.value; - this.loading.password = true; - this.$store.dispatch('setUserData', { - userid: this.user.id, - key: 'password', - value: password - }).then(() => { - this.loading.password = false; - this.$refs.password.value = ''; // empty & show placeholder - }); - }, - - /** - * Set user mailAddress - * - * @param {string} mailAddress The email adress - * @returns {Promise} - */ - updateEmail() { - let mailAddress = this.$refs.mailAddress.value; - this.loading.mailAddress = true; - this.$store.dispatch('setUserData', { - userid: this.user.id, - key: 'email', - value: mailAddress - }).then(() => { - this.loading.mailAddress = false; - this.$refs.mailAddress.value = mailAddress; - }); - }, - - /** - * Create a new group and add user to it - * - * @param {string} groups Group id - * @returns {Promise} - */ - createGroup(gid) { - this.loading = {groups:true, subadmins:true} - this.$store.dispatch('addGroup', gid) - .then(() => { - this.loading = {groups:false, subadmins:false}; - let userid = this.user.id; - this.$store.dispatch('addUserGroup', {userid, gid}); - }) - .catch(() => { - this.loading = {groups:false, subadmins:false}; - }); - return this.$store.getters.getGroups[this.groups.length]; - }, - - /** - * Add user to group - * - * @param {object} group Group object - * @returns {Promise} - */ - addUserGroup(group) { - if (group.canAdd === false) { - return false; - } - this.loading.groups = true; - let userid = this.user.id; - let gid = group.id; - return this.$store.dispatch('addUserGroup', {userid, gid}) - .then(() => this.loading.groups = false); - }, - - /** - * Remove user from group - * - * @param {object} group Group object - * @returns {Promise} - */ - removeUserGroup(group) { - if (group.canRemove === false) { - return false; - } - this.loading.groups = true; - let userid = this.user.id; - let gid = group.id; - return this.$store.dispatch('removeUserGroup', {userid, gid}) - .then(() => { - this.loading.groups = false - // remove user from current list if current list is the removed group - if (this.$route.params.selectedGroup === gid) { - this.$store.commit('deleteUser', userid); - } - }) - .catch(() => { - this.loading.groups = false - }); - }, - - /** - * Add user to group - * - * @param {object} group Group object - * @returns {Promise} - */ - addUserSubAdmin(group) { - this.loading.subadmins = true; - let userid = this.user.id; - let gid = group.id; - return this.$store.dispatch('addUserSubAdmin', {userid, gid}) - .then(() => this.loading.subadmins = false); - }, - - /** - * Remove user from group - * - * @param {object} group Group object - * @returns {Promise} - */ - removeUserSubAdmin(group) { - this.loading.subadmins = true; - let userid = this.user.id; - let gid = group.id; - return this.$store.dispatch('removeUserSubAdmin', {userid, gid}) - .then(() => this.loading.subadmins = false); - }, - - /** - * Dispatch quota set request - * - * @param {string|Object} quota Quota in readable format '5 GB' or Object {id: '5 GB', label: '5GB'} - * @returns {string} - */ - setUserQuota(quota = 'none') { - this.loading.quota = true; - // ensure we only send the preset id - quota = quota.id ? quota.id : quota; - this.$store.dispatch('setUserData', { - userid: this.user.id, - key: 'quota', - value: quota - }).then(() => this.loading.quota = false); - return quota; - }, - - /** - * Validate quota string to make sure it's a valid human file size - * - * @param {string} quota Quota in readable format '5 GB' - * @returns {Promise|boolean} - */ - validateQuota(quota) { - // only used for new presets sent through @Tag - let validQuota = OC.Util.computerFileSize(quota); - if (validQuota !== null && validQuota >= 0) { - // unify format output - return this.setUserQuota(OC.Util.humanFileSize(OC.Util.computerFileSize(quota))); - } - // if no valid do not change - return false; - }, - - /** - * Dispatch language set request - * - * @param {Object} lang language object {code:'en', name:'English'} - * @returns {Object} - */ - setUserLanguage(lang) { - this.loading.languages = true; - // ensure we only send the preset id - this.$store.dispatch('setUserData', { - userid: this.user.id, - key: 'language', - value: lang.code - }).then(() => this.loading.languages = false); - return lang; - }, - - /** - * Dispatch new welcome mail request - */ - sendWelcomeMail() { - this.loading.all = true; - this.$store.dispatch('sendWelcomeMail', this.user.id) - .then(success => { - if (success) { - // Show feedback to indicate the success - this.feedbackMessage = t('setting', 'Welcome mail sent!'); - setTimeout(() => { - this.feedbackMessage = ''; - }, 2000); - } - this.loading.all = false; - }); - } - - } -} -</script> diff --git a/apps/settings/src/main-admin-security.js b/apps/settings/src/main-admin-security.js index a728c085b4381fba8fca4c9db3c4379635e891f5..d775f6ac1333834cab7be481a7a447286b1303da 100644 --- a/apps/settings/src/main-admin-security.js +++ b/apps/settings/src/main-admin-security.js @@ -3,13 +3,14 @@ import Vue from 'vue' import AdminTwoFactor from './components/AdminTwoFactor.vue' import store from './store/admin-security' +// eslint-disable-next-line camelcase __webpack_nonce__ = btoa(OC.requestToken) -Vue.prototype.t = t; +Vue.prototype.t = t // Not used here but required for legacy templates -window.OC = window.OC || {}; -window.OC.Settings = window.OC.Settings || {}; +window.OC = window.OC || {} +window.OC.Settings = window.OC.Settings || {} store.replaceState( OCP.InitialState.loadState('settings', 'mandatory2FAState') diff --git a/apps/settings/src/main-apps-users-management.js b/apps/settings/src/main-apps-users-management.js index a8627c2277d3f242bc702bb761414eace8a85bc3..e7d5b4e91c3fb82744e26c06028476d800ad6160 100644 --- a/apps/settings/src/main-apps-users-management.js +++ b/apps/settings/src/main-apps-users-management.js @@ -20,17 +20,17 @@ * */ -import Vue from 'vue'; -import VTooltip from 'v-tooltip'; -import { sync } from 'vuex-router-sync'; +import Vue from 'vue' +import VTooltip from 'v-tooltip' +import { sync } from 'vuex-router-sync' -import App from './App.vue'; -import router from './router'; -import store from './store'; +import App from './App.vue' +import router from './router' +import store from './store' -Vue.use(VTooltip, { defaultHtml: false }); +Vue.use(VTooltip, { defaultHtml: false }) -sync(store, router); +sync(store, router) // CSP config for webpack dynamic chunk loading // eslint-disable-next-line @@ -43,15 +43,16 @@ __webpack_nonce__ = btoa(OC.requestToken) __webpack_public_path__ = OC.linkTo('settings', 'js/') // bind to window -Vue.prototype.t = t; -Vue.prototype.OC = OC; -Vue.prototype.OCA = OCA; -Vue.prototype.oc_userconfig = oc_userconfig; +Vue.prototype.t = t +Vue.prototype.OC = OC +Vue.prototype.OCA = OCA +// eslint-disable-next-line camelcase +Vue.prototype.oc_userconfig = oc_userconfig const app = new Vue({ router, store, render: h => h(App) -}).$mount('#content'); +}).$mount('#content') -export { app, router, store }; +export { app, router, store } diff --git a/apps/settings/src/main-personal-security.js b/apps/settings/src/main-personal-security.js index 9f020efd5f4da4c88d2493e7859aadac9e7696c2..dd20d5d1031bf9c1e6bb5efae58559bc1597ea5d 100644 --- a/apps/settings/src/main-personal-security.js +++ b/apps/settings/src/main-personal-security.js @@ -19,22 +19,23 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import Vue from 'vue'; -import VueClipboard from 'vue-clipboard2'; -import VTooltip from 'v-tooltip'; +import Vue from 'vue' +import VueClipboard from 'vue-clipboard2' +import VTooltip from 'v-tooltip' -import AuthTokenSection from './components/AuthTokenSection'; +import AuthTokenSection from './components/AuthTokenSection' -__webpack_nonce__ = btoa(OC.requestToken); +// eslint-disable-next-line camelcase +__webpack_nonce__ = btoa(OC.requestToken) -Vue.use(VueClipboard); -Vue.use(VTooltip, { defaultHtml: false }); -Vue.prototype.t = t; +Vue.use(VueClipboard) +Vue.use(VTooltip, { defaultHtml: false }) +Vue.prototype.t = t -const View = Vue.extend(AuthTokenSection); +const View = Vue.extend(AuthTokenSection) new View({ propsData: { tokens: OCP.InitialState.loadState('settings', 'app_tokens'), - canCreateToken: OCP.InitialState.loadState('settings', 'can_create_app_token'), + canCreateToken: OCP.InitialState.loadState('settings', 'can_create_app_token') } -}).$mount('#security-authtokens'); +}).$mount('#security-authtokens') diff --git a/apps/settings/src/router.js b/apps/settings/src/router.js index 512ab813840d7a3e9b5ad16adae9e5b48eaaadba..7612fdea8953ec0b745bcf397d19c72c7e59afaf 100644 --- a/apps/settings/src/router.js +++ b/apps/settings/src/router.js @@ -21,14 +21,14 @@ * */ -import Vue from 'vue'; -import Router from 'vue-router'; +import Vue from 'vue' +import Router from 'vue-router' // Dynamic loading -const Users = () => import('./views/Users'); -const Apps = () => import('./views/Apps'); +const Users = () => import('./views/Users') +const Apps = () => import('./views/Apps') -Vue.use(Router); +Vue.use(Router) /* * This is the list of routes where the vuejs app will @@ -80,4 +80,4 @@ export default new Router({ ] } ] -}); +}) diff --git a/apps/settings/src/store/admin-security.js b/apps/settings/src/store/admin-security.js index 997aab2af58b641fbc7ef6967ca57c08351bcbad..e86783557f232b128cbdef95664fa77f55bfea14 100644 --- a/apps/settings/src/store/admin-security.js +++ b/apps/settings/src/store/admin-security.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Roeland Jago Douma <roeland@famdouma.nl> * * @author 2019 Roeland Jago Douma <roeland@famdouma.nl> @@ -24,7 +24,13 @@ import Vuex from 'vuex' Vue.use(Vuex) -export const mutations = { +const state = { + enforced: false, + enforcedGroups: [], + excludedGroups: [] +} + +const mutations = { setEnforced(state, enabled) { Vue.set(state, 'enforced', enabled) }, @@ -36,28 +42,8 @@ export const mutations = { } } -export const actions = { - save ({commit}, ) { - commit('setEnabled', false); - - return generateCodes() - .then(({codes, state}) => { - commit('setEnabled', state.enabled); - commit('setTotal', state.total); - commit('setUsed', state.used); - commit('setCodes', codes); - return true; - }); - } -} - export default new Vuex.Store({ strict: process.env.NODE_ENV !== 'production', - state: { - enforced: false, - enforcedGroups: [], - excludedGroups: [], - }, - mutations, - actions + state, + mutations }) diff --git a/apps/settings/src/store/api.js b/apps/settings/src/store/api.js index 185e80253a357a1d2a375c2c0d2a033958fcf917..98959de7bf5ff1b70d9728719c459f63803acce7 100644 --- a/apps/settings/src/store/api.js +++ b/apps/settings/src/store/api.js @@ -21,11 +21,11 @@ */ import axios from 'nextcloud-axios' -import confirmPassword from 'nextcloud-password-confirmation' +import confirmPassword from 'nextcloud-password-confirmation' const sanitize = function(url) { - return url.replace(/\/$/, ''); // Remove last url slash -}; + return url.replace(/\/$/, '') // Remove last url slash +} export default { @@ -47,35 +47,35 @@ export default { * * Since Promise.then().catch().then() will always execute the last then * this.$store.dispatch('action').then will always be executed - * + * * If you want requireAdmin failure to also catch the API request failure * you will need to throw a new error in the api.get.catch() - * + * * e.g * api.requireAdmin().then((response) => { * api.get('url') * .then((response) => {API success}) * .catch((error) => {throw error;}); * }).catch((error) => {requireAdmin OR API failure}); - * + * * @returns {Promise} */ requireAdmin() { - return confirmPassword(); + return confirmPassword() }, get(url) { - return axios.get(sanitize(url)); + return axios.get(sanitize(url)) }, post(url, data) { - return axios.post(sanitize(url), data); + return axios.post(sanitize(url), data) }, patch(url, data) { - return axios.patch(sanitize(url), data); + return axios.patch(sanitize(url), data) }, put(url, data) { - return axios.put(sanitize(url), data); + return axios.put(sanitize(url), data) }, delete(url, data) { - return axios.delete(sanitize(url), { data: data }); + return axios.delete(sanitize(url), { data: data }) } -}; \ No newline at end of file +} diff --git a/apps/settings/src/store/apps.js b/apps/settings/src/store/apps.js index 8074eac2e0047f3e1d7a624100a763e079b4fb5c..47698382904abd38776118612efbde218a7fc997 100644 --- a/apps/settings/src/store/apps.js +++ b/apps/settings/src/store/apps.js @@ -20,158 +20,158 @@ * */ -import api from './api'; -import Vue from 'vue'; +import api from './api' +import Vue from 'vue' const state = { apps: [], categories: [], updateCount: 0, loading: {}, - loadingList: false, -}; + loadingList: false +} const mutations = { APPS_API_FAILURE(state, error) { - OC.Notification.showHtml(t('settings','An error occured during the request. Unable to proceed.')+'<br>'+error.error.response.data.data.message, {timeout: 7}); - console.log(state, error); + OC.Notification.showHtml(t('settings', 'An error occured during the request. Unable to proceed.') + '<br>' + error.error.response.data.data.message, { timeout: 7 }) + console.error(state, error) }, - initCategories(state, {categories, updateCount}) { - state.categories = categories; - state.updateCount = updateCount; + initCategories(state, { categories, updateCount }) { + state.categories = categories + state.updateCount = updateCount }, setUpdateCount(state, updateCount) { - state.updateCount = updateCount; + state.updateCount = updateCount }, addCategory(state, category) { - state.categories.push(category); + state.categories.push(category) }, appendCategories(state, categoriesArray) { // convert obj to array - state.categories = categoriesArray; + state.categories = categoriesArray }, setAllApps(state, apps) { - state.apps = apps; + state.apps = apps }, - setError(state, {appId, error}) { + setError(state, { appId, error }) { if (!Array.isArray(appId)) { - appId = [appId]; + appId = [appId] } appId.forEach((_id) => { - let app = state.apps.find(app => app.id === _id); - app.error = error; - }); + let app = state.apps.find(app => app.id === _id) + app.error = error + }) }, - clearError(state, {appId, error}) { - let app = state.apps.find(app => app.id === appId); - app.error = null; + clearError(state, { appId, error }) { + let app = state.apps.find(app => app.id === appId) + app.error = null }, - enableApp(state, {appId, groups}) { - let app = state.apps.find(app => app.id === appId); - app.active = true; - app.groups = groups; + enableApp(state, { appId, groups }) { + let app = state.apps.find(app => app.id === appId) + app.active = true + app.groups = groups }, disableApp(state, appId) { - let app = state.apps.find(app => app.id === appId); - app.active = false; - app.groups = []; + let app = state.apps.find(app => app.id === appId) + app.active = false + app.groups = [] if (app.removable) { - app.canUnInstall = true; + app.canUnInstall = true } }, uninstallApp(state, appId) { - state.apps.find(app => app.id === appId).active = false; - state.apps.find(app => app.id === appId).groups = []; - state.apps.find(app => app.id === appId).needsDownload = true; - state.apps.find(app => app.id === appId).installed = false; - state.apps.find(app => app.id === appId).canUnInstall = false; - state.apps.find(app => app.id === appId).canInstall = true; + state.apps.find(app => app.id === appId).active = false + state.apps.find(app => app.id === appId).groups = [] + state.apps.find(app => app.id === appId).needsDownload = true + state.apps.find(app => app.id === appId).installed = false + state.apps.find(app => app.id === appId).canUnInstall = false + state.apps.find(app => app.id === appId).canInstall = true }, updateApp(state, appId) { - let app = state.apps.find(app => app.id === appId); - let version = app.update; - app.update = null; - app.version = version; - state.updateCount--; + let app = state.apps.find(app => app.id === appId) + let version = app.update + app.update = null + app.version = version + state.updateCount-- }, resetApps(state) { - state.apps = []; + state.apps = [] }, reset(state) { - state.apps = []; - state.categories = []; - state.updateCount = 0; + state.apps = [] + state.categories = [] + state.updateCount = 0 }, startLoading(state, id) { if (Array.isArray(id)) { id.forEach((_id) => { - Vue.set(state.loading, _id, true); + Vue.set(state.loading, _id, true) }) } else { - Vue.set(state.loading, id, true); + Vue.set(state.loading, id, true) } }, stopLoading(state, id) { if (Array.isArray(id)) { id.forEach((_id) => { - Vue.set(state.loading, _id, false); + Vue.set(state.loading, _id, false) }) } else { - Vue.set(state.loading, id, false); + Vue.set(state.loading, id, false) } - }, -}; + } +} const getters = { loading(state) { return function(id) { - return state.loading[id]; + return state.loading[id] } }, getCategories(state) { - return state.categories; + return state.categories }, getAllApps(state) { - return state.apps; + return state.apps }, getUpdateCount(state) { - return state.updateCount; + return state.updateCount } -}; +} const actions = { enableApp(context, { appId, groups }) { - let apps; + let apps if (Array.isArray(appId)) { - apps = appId; + apps = appId } else { - apps = [appId]; + apps = [appId] } return api.requireAdmin().then((response) => { - context.commit('startLoading', apps); - context.commit('startLoading', 'install'); - return api.post(OC.generateUrl(`settings/apps/enable`), {appIds: apps, groups: groups}) + context.commit('startLoading', apps) + context.commit('startLoading', 'install') + return api.post(OC.generateUrl(`settings/apps/enable`), { appIds: apps, groups: groups }) .then((response) => { - context.commit('stopLoading', apps); - context.commit('stopLoading', 'install'); + context.commit('stopLoading', apps) + context.commit('stopLoading', 'install') apps.forEach(_appId => { - context.commit('enableApp', {appId: _appId, groups: groups}); - }); + context.commit('enableApp', { appId: _appId, groups: groups }) + }) // check for server health return api.get(OC.generateUrl('apps/files')) @@ -182,146 +182,146 @@ const actions = { 'settings', 'The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds.' ), - t('settings','App update'), - function () { - window.location.reload(); + t('settings', 'App update'), + function() { + window.location.reload() }, true - ); + ) setTimeout(function() { - location.reload(); - }, 5000); + location.reload() + }, 5000) } }) - .catch((error) => { + .catch(() => { if (!Array.isArray(appId)) { context.commit('setError', { appId: apps, error: t('settings', 'Error: This app can not be enabled because it makes the server unstable') - }); + }) } - }); + }) }) .catch((error) => { - context.commit('stopLoading', apps); - context.commit('stopLoading', 'install'); + context.commit('stopLoading', apps) + context.commit('stopLoading', 'install') context.commit('setError', { appId: apps, error: error.response.data.data.message - }); - context.commit('APPS_API_FAILURE', { appId, error}); + }) + context.commit('APPS_API_FAILURE', { appId, error }) }) - }).catch((error) => context.commit('API_FAILURE', { appId, error })); + }).catch((error) => context.commit('API_FAILURE', { appId, error })) }, forceEnableApp(context, { appId, groups }) { - let apps; + let apps if (Array.isArray(appId)) { - apps = appId; + apps = appId } else { - apps = [appId]; + apps = [appId] } return api.requireAdmin().then(() => { - context.commit('startLoading', apps); - context.commit('startLoading', 'install'); - return api.post(OC.generateUrl(`settings/apps/force`), {appId}) + context.commit('startLoading', apps) + context.commit('startLoading', 'install') + return api.post(OC.generateUrl(`settings/apps/force`), { appId }) .then((response) => { // TODO: find a cleaner solution - location.reload(); + location.reload() }) .catch((error) => { - context.commit('stopLoading', apps); - context.commit('stopLoading', 'install'); + context.commit('stopLoading', apps) + context.commit('stopLoading', 'install') context.commit('setError', { appId: apps, error: error.response.data.data.message - }); - context.commit('APPS_API_FAILURE', { appId, error}); + }) + context.commit('APPS_API_FAILURE', { appId, error }) }) - }).catch((error) => context.commit('API_FAILURE', { appId, error })); + }).catch((error) => context.commit('API_FAILURE', { appId, error })) }, disableApp(context, { appId }) { - let apps; + let apps if (Array.isArray(appId)) { - apps = appId; + apps = appId } else { - apps = [appId]; + apps = [appId] } return api.requireAdmin().then((response) => { - context.commit('startLoading', apps); - return api.post(OC.generateUrl(`settings/apps/disable`), {appIds: apps}) + context.commit('startLoading', apps) + return api.post(OC.generateUrl(`settings/apps/disable`), { appIds: apps }) .then((response) => { - context.commit('stopLoading', apps); + context.commit('stopLoading', apps) apps.forEach(_appId => { - context.commit('disableApp', _appId); - }); - return true; + context.commit('disableApp', _appId) + }) + return true }) .catch((error) => { - context.commit('stopLoading', apps); + context.commit('stopLoading', apps) context.commit('APPS_API_FAILURE', { appId, error }) }) - }).catch((error) => context.commit('API_FAILURE', { appId, error })); + }).catch((error) => context.commit('API_FAILURE', { appId, error })) }, uninstallApp(context, { appId }) { return api.requireAdmin().then((response) => { - context.commit('startLoading', appId); + context.commit('startLoading', appId) return api.get(OC.generateUrl(`settings/apps/uninstall/${appId}`)) .then((response) => { - context.commit('stopLoading', appId); - context.commit('uninstallApp', appId); - return true; + context.commit('stopLoading', appId) + context.commit('uninstallApp', appId) + return true }) .catch((error) => { - context.commit('stopLoading', appId); + context.commit('stopLoading', appId) context.commit('APPS_API_FAILURE', { appId, error }) }) - }).catch((error) => context.commit('API_FAILURE', { appId, error })); + }).catch((error) => context.commit('API_FAILURE', { appId, error })) }, updateApp(context, { appId }) { return api.requireAdmin().then((response) => { - context.commit('startLoading', appId); - context.commit('startLoading', 'install'); + context.commit('startLoading', appId) + context.commit('startLoading', 'install') return api.get(OC.generateUrl(`settings/apps/update/${appId}`)) .then((response) => { - context.commit('stopLoading', 'install'); - context.commit('stopLoading', appId); - context.commit('updateApp', appId); - return true; + context.commit('stopLoading', 'install') + context.commit('stopLoading', appId) + context.commit('updateApp', appId) + return true }) .catch((error) => { - context.commit('stopLoading', appId); - context.commit('stopLoading', 'install'); + context.commit('stopLoading', appId) + context.commit('stopLoading', 'install') context.commit('APPS_API_FAILURE', { appId, error }) }) - }).catch((error) => context.commit('API_FAILURE', { appId, error })); + }).catch((error) => context.commit('API_FAILURE', { appId, error })) }, getAllApps(context) { - context.commit('startLoading', 'list'); + context.commit('startLoading', 'list') return api.get(OC.generateUrl(`settings/apps/list`)) .then((response) => { - context.commit('setAllApps', response.data.apps); - context.commit('stopLoading', 'list'); - return true; + context.commit('setAllApps', response.data.apps) + context.commit('stopLoading', 'list') + return true }) .catch((error) => context.commit('API_FAILURE', error)) }, getCategories(context) { - context.commit('startLoading', 'categories'); + context.commit('startLoading', 'categories') return api.get(OC.generateUrl('settings/apps/categories')) .then((response) => { if (response.data.length > 0) { - context.commit('appendCategories', response.data); - context.commit('stopLoading', 'categories'); - return true; + context.commit('appendCategories', response.data) + context.commit('stopLoading', 'categories') + return true } - return false; + return false }) - .catch((error) => context.commit('API_FAILURE', error)); - }, + .catch((error) => context.commit('API_FAILURE', error)) + } -}; +} -export default { state, mutations, getters, actions }; \ No newline at end of file +export default { state, mutations, getters, actions } diff --git a/apps/settings/src/store/index.js b/apps/settings/src/store/index.js index 00bcd67db395f896ef5fdf9985b1984b524b4227..c640d32cd6accd678e026e1bc3051f4972386131 100644 --- a/apps/settings/src/store/index.js +++ b/apps/settings/src/store/index.js @@ -1,4 +1,4 @@ -/* +/** * @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com> * * @author John Molakvoæ <skjnldsv@protonmail.com> @@ -21,28 +21,28 @@ * */ -import Vue from 'vue'; -import Vuex from 'vuex'; -import users from './users'; -import apps from './apps'; -import settings from './settings'; -import oc from './oc'; +import Vue from 'vue' +import Vuex from 'vuex' +import users from './users' +import apps from './apps' +import settings from './settings' +import oc from './oc' Vue.use(Vuex) -const debug = process.env.NODE_ENV !== 'production'; +const debug = process.env.NODE_ENV !== 'production' const mutations = { API_FAILURE(state, error) { try { - let message = error.error.response.data.ocs.meta.message; - OC.Notification.showHtml(t('settings','An error occured during the request. Unable to proceed.')+'<br>'+message, {timeout: 7}); - } catch(e) { - OC.Notification.showTemporary(t('settings','An error occured during the request. Unable to proceed.')); + let message = error.error.response.data.ocs.meta.message + OC.Notification.showHtml(t('settings', 'An error occured during the request. Unable to proceed.') + '<br>' + message, { timeout: 7 }) + } catch (e) { + OC.Notification.showTemporary(t('settings', 'An error occured during the request. Unable to proceed.')) } - console.log(state, error); + console.error(state, error) } -}; +} export default new Vuex.Store({ modules: { @@ -54,4 +54,4 @@ export default new Vuex.Store({ strict: debug, mutations -}); +}) diff --git a/apps/settings/src/store/oc.js b/apps/settings/src/store/oc.js index afa13fe6b18e5494089f49576d626c286b1b20b5..ff14f8ae7e3914a536ad33436ae8abb1df54b116 100644 --- a/apps/settings/src/store/oc.js +++ b/apps/settings/src/store/oc.js @@ -1,4 +1,4 @@ -/* +/** * @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com> * * @author John Molakvoæ <skjnldsv@protonmail.com> @@ -20,28 +20,28 @@ * */ -import api from './api'; +import api from './api' -const state = {}; -const mutations = {}; -const getters = {}; +const state = {} +const mutations = {} +const getters = {} const actions = { /** * Set application config in database - * - * @param {Object} context - * @param {Object} options + * + * @param {Object} context store context + * @param {Object} options destructuring object * @param {string} options.app Application name * @param {boolean} options.key Config key * @param {boolean} options.value Value to set * @returns{Promise} */ - setAppConfig(context, {app, key, value}) { + setAppConfig(context, { app, key, value }) { return api.requireAdmin().then((response) => { - return api.post(OC.linkToOCS(`apps/provisioning_api/api/v1/config/apps/${app}/${key}`, 2), {value: value}) - .catch((error) => {throw error;}); - }).catch((error) => context.commit('API_FAILURE', { app, key, value, error }));; - } -}; + return api.post(OC.linkToOCS(`apps/provisioning_api/api/v1/config/apps/${app}/${key}`, 2), { value: value }) + .catch((error) => { throw error }) + }).catch((error) => context.commit('API_FAILURE', { app, key, value, error })) + } +} -export default {state, mutations, getters, actions}; +export default { state, mutations, getters, actions } diff --git a/apps/settings/src/store/settings.js b/apps/settings/src/store/settings.js index 5f0bcfa60aa8e5e8dcd47560f0a5cd5e57a366a8..88086910c39fad0c0ff5fb5c0740fb424619fe14 100644 --- a/apps/settings/src/store/settings.js +++ b/apps/settings/src/store/settings.js @@ -1,4 +1,4 @@ -/* +/** * @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com> * * @author John Molakvoæ <skjnldsv@protonmail.com> @@ -20,21 +20,19 @@ * */ -import api from './api'; - const state = { serverData: {} -}; +} const mutations = { setServerData(state, data) { - state.serverData = data; + state.serverData = data } -}; +} const getters = { getServerData(state) { - return state.serverData; + return state.serverData } -}; -const actions = {}; +} +const actions = {} -export default {state, mutations, getters, actions}; +export default { state, mutations, getters, actions } diff --git a/apps/settings/src/store/users.js b/apps/settings/src/store/users.js index 1b174b21bf468c61c4fe97238158beeab19b5161..e2eb94610af5b163e020d0d728b7eb3c9274665f 100644 --- a/apps/settings/src/store/users.js +++ b/apps/settings/src/store/users.js @@ -1,4 +1,4 @@ -/* +/** * @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com> * * @author John Molakvoæ <skjnldsv@protonmail.com> @@ -20,7 +20,7 @@ * */ -import api from './api'; +import api from './api' const orderGroups = function(groups, orderBy) { /* const SORT_USERCOUNT = 1; @@ -28,11 +28,11 @@ const orderGroups = function(groups, orderBy) { * https://github.com/nextcloud/server/blob/208e38e84e1a07a49699aa90dc5b7272d24489f0/lib/private/Group/MetaData.php#L34 */ if (orderBy === 1) { - return groups.sort((a, b) => a.usercount-a.disabled < b.usercount - b.disabled); + return groups.sort((a, b) => a.usercount - a.disabled < b.usercount - b.disabled) } else { - return groups.sort((a, b) => a.name.localeCompare(b.name)); + return groups.sort((a, b) => a.name.localeCompare(b.name)) } -}; +} const defaults = { group: { @@ -43,7 +43,7 @@ const defaults = { canAdd: true, canRemove: true } -}; +} const state = { users: [], @@ -53,145 +53,146 @@ const state = { usersOffset: 0, usersLimit: 25, userCount: 0 -}; +} const mutations = { appendUsers(state, usersObj) { // convert obj to array - let users = state.users.concat(Object.keys(usersObj).map(userid => usersObj[userid])); - state.usersOffset += state.usersLimit; - state.users = users; + let users = state.users.concat(Object.keys(usersObj).map(userid => usersObj[userid])) + state.usersOffset += state.usersLimit + state.users = users }, setPasswordPolicyMinLength(state, length) { - state.minPasswordLength = length!=='' ? length : 0; + state.minPasswordLength = length !== '' ? length : 0 }, - initGroups(state, {groups, orderBy, userCount}) { - state.groups = groups.map(group => Object.assign({}, defaults.group, group)); - state.orderBy = orderBy; - state.userCount = userCount; - state.groups = orderGroups(state.groups, state.orderBy); - + initGroups(state, { groups, orderBy, userCount }) { + state.groups = groups.map(group => Object.assign({}, defaults.group, group)) + state.orderBy = orderBy + state.userCount = userCount + state.groups = orderGroups(state.groups, state.orderBy) + }, - addGroup(state, {gid, displayName}) { + addGroup(state, { gid, displayName }) { try { if (typeof state.groups.find((group) => group.id === gid) !== 'undefined') { - return; + return } // extend group to default values let group = Object.assign({}, defaults.group, { id: gid, - name: displayName, - }); - state.groups.push(group); - state.groups = orderGroups(state.groups, state.orderBy); + name: displayName + }) + state.groups.push(group) + state.groups = orderGroups(state.groups, state.orderBy) } catch (e) { - console.log('Can\'t create group', e); + console.error('Can\'t create group', e) } }, removeGroup(state, gid) { - let groupIndex = state.groups.findIndex(groupSearch => groupSearch.id == gid); + let groupIndex = state.groups.findIndex(groupSearch => groupSearch.id === gid) if (groupIndex >= 0) { - state.groups.splice(groupIndex, 1); + state.groups.splice(groupIndex, 1) } }, addUserGroup(state, { userid, gid }) { - let group = state.groups.find(groupSearch => groupSearch.id == gid); - let user = state.users.find(user => user.id == userid); + let group = state.groups.find(groupSearch => groupSearch.id === gid) + let user = state.users.find(user => user.id === userid) // increase count if user is enabled if (group && user.enabled) { - group.usercount++; + group.usercount++ } - let groups = user.groups; - groups.push(gid); - state.groups = orderGroups(state.groups, state.orderBy); + let groups = user.groups + groups.push(gid) + state.groups = orderGroups(state.groups, state.orderBy) }, removeUserGroup(state, { userid, gid }) { - let group = state.groups.find(groupSearch => groupSearch.id == gid); - let user = state.users.find(user => user.id == userid); + let group = state.groups.find(groupSearch => groupSearch.id === gid) + let user = state.users.find(user => user.id === userid) // lower count if user is enabled if (group && user.enabled) { - group.usercount--; + group.usercount-- } - let groups = user.groups; - groups.splice(groups.indexOf(gid),1); - state.groups = orderGroups(state.groups, state.orderBy); + let groups = user.groups + groups.splice(groups.indexOf(gid), 1) + state.groups = orderGroups(state.groups, state.orderBy) }, addUserSubAdmin(state, { userid, gid }) { - let groups = state.users.find(user => user.id == userid).subadmin; - groups.push(gid); + let groups = state.users.find(user => user.id === userid).subadmin + groups.push(gid) }, removeUserSubAdmin(state, { userid, gid }) { - let groups = state.users.find(user => user.id == userid).subadmin; - groups.splice(groups.indexOf(gid),1); + let groups = state.users.find(user => user.id === userid).subadmin + groups.splice(groups.indexOf(gid), 1) }, deleteUser(state, userid) { - let userIndex = state.users.findIndex(user => user.id == userid); - state.users.splice(userIndex, 1); + let userIndex = state.users.findIndex(user => user.id === userid) + state.users.splice(userIndex, 1) }, addUserData(state, response) { - state.users.push(response.data.ocs.data); + state.users.push(response.data.ocs.data) }, enableDisableUser(state, { userid, enabled }) { - let user = state.users.find(user => user.id == userid); - user.enabled = enabled; + let user = state.users.find(user => user.id === userid) + user.enabled = enabled // increment or not - state.groups.find(group => group.id == 'disabled').usercount += enabled ? -1 : 1; - state.userCount += enabled ? 1 : -1; + state.groups.find(group => group.id === 'disabled').usercount += enabled ? -1 : 1 + state.userCount += enabled ? 1 : -1 user.groups.forEach(group => { // Increment disabled count - state.groups.find(groupSearch => groupSearch.id == group).disabled += enabled ? -1 : 1; - }); + state.groups.find(groupSearch => groupSearch.id === group).disabled += enabled ? -1 : 1 + }) }, setUserData(state, { userid, key, value }) { if (key === 'quota') { - let humanValue = OC.Util.computerFileSize(value); - state.users.find(user => user.id == userid)[key][key] = humanValue!==null ? humanValue : value; + let humanValue = OC.Util.computerFileSize(value) + state.users.find(user => user.id === userid)[key][key] = humanValue !== null ? humanValue : value } else { - state.users.find(user => user.id == userid)[key] = value; + state.users.find(user => user.id === userid)[key] = value } }, /** * Reset users list + * @param {Object} state the store state */ resetUsers(state) { - state.users = []; - state.usersOffset = 0; + state.users = [] + state.usersOffset = 0 } -}; +} const getters = { getUsers(state) { - return state.users; + return state.users }, getGroups(state) { - return state.groups; + return state.groups }, getSubadminGroups(state) { // Can't be subadmin of admin or disabled - return state.groups.filter(group => group.id !== 'admin' && group.id !== 'disabled'); + return state.groups.filter(group => group.id !== 'admin' && group.id !== 'disabled') }, getPasswordPolicyMinLength(state) { - return state.minPasswordLength; + return state.minPasswordLength }, getUsersOffset(state) { - return state.usersOffset; + return state.usersOffset }, getUsersLimit(state) { - return state.usersLimit; + return state.usersLimit }, getUserCount(state) { - return state.userCount; + return state.userCount } -}; +} const actions = { /** * Get all users with full details - * - * @param {Object} context - * @param {Object} options + * + * @param {Object} context store context + * @param {Object} options destructuring object * @param {int} options.offset List offset to request * @param {int} options.limit List number to return from offset * @param {string} options.search Search amongst users @@ -199,74 +200,74 @@ const actions = { * @returns {Promise} */ getUsers(context, { offset, limit, search, group }) { - search = typeof search === 'string' ? search : ''; - group = typeof group === 'string' ? group : ''; + search = typeof search === 'string' ? search : '' + group = typeof group === 'string' ? group : '' if (group !== '') { return api.get(OC.linkToOCS(`cloud/groups/${group}/users/details?offset=${offset}&limit=${limit}&search=${search}`, 2)) - .then((response) => { - if (Object.keys(response.data.ocs.data.users).length > 0) { - context.commit('appendUsers', response.data.ocs.data.users); - return true; - } - return false; - }) - .catch((error) => context.commit('API_FAILURE', error)); + .then((response) => { + if (Object.keys(response.data.ocs.data.users).length > 0) { + context.commit('appendUsers', response.data.ocs.data.users) + return true + } + return false + }) + .catch((error) => context.commit('API_FAILURE', error)) } return api.get(OC.linkToOCS(`cloud/users/details?offset=${offset}&limit=${limit}&search=${search}`, 2)) .then((response) => { if (Object.keys(response.data.ocs.data.users).length > 0) { - context.commit('appendUsers', response.data.ocs.data.users); - return true; + context.commit('appendUsers', response.data.ocs.data.users) + return true } - return false; + return false }) - .catch((error) => context.commit('API_FAILURE', error)); + .catch((error) => context.commit('API_FAILURE', error)) }, getGroups(context, { offset, limit, search }) { - search = typeof search === 'string' ? search : ''; - let limitParam = limit === -1 ? '' : `&limit=${limit}`; + search = typeof search === 'string' ? search : '' + let limitParam = limit === -1 ? '' : `&limit=${limit}` return api.get(OC.linkToOCS(`cloud/groups?offset=${offset}&search=${search}${limitParam}`, 2)) .then((response) => { if (Object.keys(response.data.ocs.data.groups).length > 0) { response.data.ocs.data.groups.forEach(function(group) { - context.commit('addGroup', {gid: group, displayName: group}); - }); - return true; + context.commit('addGroup', { gid: group, displayName: group }) + }) + return true } - return false; + return false }) - .catch((error) => context.commit('API_FAILURE', error)); + .catch((error) => context.commit('API_FAILURE', error)) }, /** * Get all users with full details - * - * @param {Object} context - * @param {Object} options + * + * @param {Object} context store context + * @param {Object} options destructuring object * @param {int} options.offset List offset to request * @param {int} options.limit List number to return from offset * @returns {Promise} */ getUsersFromList(context, { offset, limit, search }) { - search = typeof search === 'string' ? search : ''; + search = typeof search === 'string' ? search : '' return api.get(OC.linkToOCS(`cloud/users/details?offset=${offset}&limit=${limit}&search=${search}`, 2)) .then((response) => { if (Object.keys(response.data.ocs.data.users).length > 0) { - context.commit('appendUsers', response.data.ocs.data.users); - return true; + context.commit('appendUsers', response.data.ocs.data.users) + return true } - return false; + return false }) - .catch((error) => context.commit('API_FAILURE', error)); + .catch((error) => context.commit('API_FAILURE', error)) }, /** * Get all users with full details from a groupid - * - * @param {Object} context - * @param {Object} options + * + * @param {Object} context store context + * @param {Object} options destructuring object * @param {int} options.offset List offset to request * @param {int} options.limit List number to return from offset * @returns {Promise} @@ -274,45 +275,44 @@ const actions = { getUsersFromGroup(context, { groupid, offset, limit }) { return api.get(OC.linkToOCS(`cloud/users/${groupid}/details?offset=${offset}&limit=${limit}`, 2)) .then((response) => context.commit('getUsersFromList', response.data.ocs.data.users)) - .catch((error) => context.commit('API_FAILURE', error)); + .catch((error) => context.commit('API_FAILURE', error)) }, - getPasswordPolicyMinLength(context) { - if(OC.getCapabilities().password_policy && OC.getCapabilities().password_policy.minLength) { - context.commit('setPasswordPolicyMinLength', OC.getCapabilities().password_policy.minLength); - return OC.getCapabilities().password_policy.minLength; + if (OC.getCapabilities().password_policy && OC.getCapabilities().password_policy.minLength) { + context.commit('setPasswordPolicyMinLength', OC.getCapabilities().password_policy.minLength) + return OC.getCapabilities().password_policy.minLength } - return false; + return false }, /** * Add group - * - * @param {Object} context + * + * @param {Object} context store context * @param {string} gid Group id * @returns {Promise} */ addGroup(context, gid) { return api.requireAdmin().then((response) => { - return api.post(OC.linkToOCS(`cloud/groups`, 2), {groupid: gid}) + return api.post(OC.linkToOCS(`cloud/groups`, 2), { groupid: gid }) .then((response) => { - context.commit('addGroup', {gid: gid, displayName: gid}) - return {gid: gid, displayName: gid} + context.commit('addGroup', { gid: gid, displayName: gid }) + return { gid: gid, displayName: gid } }) - .catch((error) => {throw error;}); + .catch((error) => { throw error }) }).catch((error) => { - context.commit('API_FAILURE', { gid, error }); + context.commit('API_FAILURE', { gid, error }) // let's throw one more time to prevent the view // from adding the user to a group that doesn't exists - throw error; - }); + throw error + }) }, /** * Remove group - * - * @param {Object} context + * + * @param {Object} context store context * @param {string} gid Group id * @returns {Promise} */ @@ -320,15 +320,15 @@ const actions = { return api.requireAdmin().then((response) => { return api.delete(OC.linkToOCS(`cloud/groups/${gid}`, 2)) .then((response) => context.commit('removeGroup', gid)) - .catch((error) => {throw error;}); - }).catch((error) => context.commit('API_FAILURE', { gid, error })); + .catch((error) => { throw error }) + }).catch((error) => context.commit('API_FAILURE', { gid, error })) }, /** * Add user to group - * - * @param {Object} context - * @param {Object} options + * + * @param {Object} context store context + * @param {Object} options destructuring object * @param {string} options.userid User id * @param {string} options.gid Group id * @returns {Promise} @@ -337,15 +337,15 @@ const actions = { return api.requireAdmin().then((response) => { return api.post(OC.linkToOCS(`cloud/users/${userid}/groups`, 2), { groupid: gid }) .then((response) => context.commit('addUserGroup', { userid, gid })) - .catch((error) => {throw error;}); - }).catch((error) => context.commit('API_FAILURE', { userid, error })); + .catch((error) => { throw error }) + }).catch((error) => context.commit('API_FAILURE', { userid, error })) }, /** * Remove user from group - * - * @param {Object} context - * @param {Object} options + * + * @param {Object} context store context + * @param {Object} options destructuring object * @param {string} options.userid User id * @param {string} options.gid Group id * @returns {Promise} @@ -354,37 +354,37 @@ const actions = { return api.requireAdmin().then((response) => { return api.delete(OC.linkToOCS(`cloud/users/${userid}/groups`, 2), { groupid: gid }) .then((response) => context.commit('removeUserGroup', { userid, gid })) - .catch((error) => {throw error;}); + .catch((error) => { throw error }) }).catch((error) => { - context.commit('API_FAILURE', { userid, error }); + context.commit('API_FAILURE', { userid, error }) // let's throw one more time to prevent // the view from removing the user row on failure - throw error; - }); + throw error + }) }, /** * Add user to group admin - * - * @param {Object} context - * @param {Object} options + * + * @param {Object} context store context + * @param {Object} options destructuring object * @param {string} options.userid User id * @param {string} options.gid Group id * @returns {Promise} */ addUserSubAdmin(context, { userid, gid }) { return api.requireAdmin().then((response) => { - return api.post(OC.linkToOCS(`cloud/users/${userid}/subadmins`, 2), { groupid: gid }) + return api.post(OC.linkToOCS(`cloud/users/${userid}/subadmins`, 2), { groupid: gid }) .then((response) => context.commit('addUserSubAdmin', { userid, gid })) - .catch((error) => {throw error;}); - }).catch((error) => context.commit('API_FAILURE', { userid, error })); + .catch((error) => { throw error }) + }).catch((error) => context.commit('API_FAILURE', { userid, error })) }, /** * Remove user from group admin - * - * @param {Object} context - * @param {Object} options + * + * @param {Object} context store context + * @param {Object} options destructuring object * @param {string} options.userid User id * @param {string} options.gid Group id * @returns {Promise} @@ -393,44 +393,44 @@ const actions = { return api.requireAdmin().then((response) => { return api.delete(OC.linkToOCS(`cloud/users/${userid}/subadmins`, 2), { groupid: gid }) .then((response) => context.commit('removeUserSubAdmin', { userid, gid })) - .catch((error) => {throw error;}); - }).catch((error) => context.commit('API_FAILURE', { userid, error })); + .catch((error) => { throw error }) + }).catch((error) => context.commit('API_FAILURE', { userid, error })) }, /** * Mark all user devices for remote wipe * - * @param {Object} context + * @param {Object} context store context * @param {string} userid User id * @returns {Promise} */ wipeUserDevices(context, userid) { return api.requireAdmin().then((response) => { return api.post(OC.linkToOCS(`cloud/users/${userid}/wipe`, 2)) - .catch((error) => {throw error;}); - }).catch((error) => context.commit('API_FAILURE', { userid, error })); + .catch((error) => { throw error }) + }).catch((error) => context.commit('API_FAILURE', { userid, error })) }, /** * Delete a user - * - * @param {Object} context - * @param {string} userid User id + * + * @param {Object} context store context + * @param {string} userid User id * @returns {Promise} */ deleteUser(context, userid) { return api.requireAdmin().then((response) => { return api.delete(OC.linkToOCS(`cloud/users/${userid}`, 2)) .then((response) => context.commit('deleteUser', userid)) - .catch((error) => {throw error;}); - }).catch((error) => context.commit('API_FAILURE', { userid, error })); + .catch((error) => { throw error }) + }).catch((error) => context.commit('API_FAILURE', { userid, error })) }, /** * Add a user - * - * @param {Object} context - * @param {Object} options + * + * @param {Object} context store context + * @param {Object} options destructuring object * @param {string} options.userid User id * @param {string} options.password User password * @param {string} options.displayName User display name @@ -440,93 +440,93 @@ const actions = { * @param {string} options.quota User email * @returns {Promise} */ - addUser({commit, dispatch}, { userid, password, displayName, email, groups, subadmin, quota, language }) { + addUser({ commit, dispatch }, { userid, password, displayName, email, groups, subadmin, quota, language }) { return api.requireAdmin().then((response) => { return api.post(OC.linkToOCS(`cloud/users`, 2), { userid, password, displayName, email, groups, subadmin, quota, language }) .then((response) => dispatch('addUserData', userid || response.data.ocs.data.id)) - .catch((error) => {throw error;}); + .catch((error) => { throw error }) }).catch((error) => { - commit('API_FAILURE', { userid, error }); - throw error; - }); + commit('API_FAILURE', { userid, error }) + throw error + }) }, /** * Get user data and commit addition - * - * @param {Object} context - * @param {string} userid User id + * + * @param {Object} context store context + * @param {string} userid User id * @returns {Promise} */ addUserData(context, userid) { return api.requireAdmin().then((response) => { return api.get(OC.linkToOCS(`cloud/users/${userid}`, 2)) .then((response) => context.commit('addUserData', response)) - .catch((error) => {throw error;}); - }).catch((error) => context.commit('API_FAILURE', { userid, error })); + .catch((error) => { throw error }) + }).catch((error) => context.commit('API_FAILURE', { userid, error })) }, - /** Enable or disable user - * - * @param {Object} context - * @param {Object} options + /** Enable or disable user + * + * @param {Object} context store context + * @param {Object} options destructuring object * @param {string} options.userid User id * @param {boolean} options.enabled User enablement status * @returns {Promise} */ enableDisableUser(context, { userid, enabled = true }) { - let userStatus = enabled ? 'enable' : 'disable'; + let userStatus = enabled ? 'enable' : 'disable' return api.requireAdmin().then((response) => { return api.put(OC.linkToOCS(`cloud/users/${userid}/${userStatus}`, 2)) .then((response) => context.commit('enableDisableUser', { userid, enabled })) - .catch((error) => {throw error;}); - }).catch((error) => context.commit('API_FAILURE', { userid, error })); + .catch((error) => { throw error }) + }).catch((error) => context.commit('API_FAILURE', { userid, error })) }, /** * Edit user data - * - * @param {Object} context - * @param {Object} options + * + * @param {Object} context store context + * @param {Object} options destructuring object * @param {string} options.userid User id * @param {string} options.key User field to edit * @param {string} options.value Value of the change * @returns {Promise} */ setUserData(context, { userid, key, value }) { - let allowedEmpty = ['email', 'displayname']; + let allowedEmpty = ['email', 'displayname'] if (['email', 'language', 'quota', 'displayname', 'password'].indexOf(key) !== -1) { // We allow empty email or displayname - if (typeof value === 'string' && - ( - (allowedEmpty.indexOf(key) === -1 && value.length > 0) || - allowedEmpty.indexOf(key) !== -1 + if (typeof value === 'string' + && ( + (allowedEmpty.indexOf(key) === -1 && value.length > 0) + || allowedEmpty.indexOf(key) !== -1 ) ) { return api.requireAdmin().then((response) => { return api.put(OC.linkToOCS(`cloud/users/${userid}`, 2), { key: key, value: value }) .then((response) => context.commit('setUserData', { userid, key, value })) - .catch((error) => {throw error;}); - }).catch((error) => context.commit('API_FAILURE', { userid, error })); + .catch((error) => { throw error }) + }).catch((error) => context.commit('API_FAILURE', { userid, error })) } } - return Promise.reject(new Error('Invalid request data')); + return Promise.reject(new Error('Invalid request data')) }, /** * Send welcome mail - * - * @param {Object} context - * @param {string} userid User id + * + * @param {Object} context store context + * @param {string} userid User id * @returns {Promise} */ sendWelcomeMail(context, userid) { return api.requireAdmin().then((response) => { return api.post(OC.linkToOCS(`cloud/users/${userid}/welcome`, 2)) .then(response => true) - .catch((error) => {throw error;}); - }).catch((error) => context.commit('API_FAILURE', { userid, error })); + .catch((error) => { throw error }) + }).catch((error) => context.commit('API_FAILURE', { userid, error })) } -}; +} -export default { state, mutations, getters, actions }; +export default { state, mutations, getters, actions } diff --git a/apps/settings/src/views/Apps.vue b/apps/settings/src/views/Apps.vue index 35caf7cea5936de545dbe7fa74b0dc01b0f619f3..2e0649f7f56555c5f350990ee3890377796301f6 100644 --- a/apps/settings/src/views/Apps.vue +++ b/apps/settings/src/views/Apps.vue @@ -21,8 +21,10 @@ --> <template> - <Content app-name="settings" :class="{ 'with-app-sidebar': currentApp}" - :content-class="{ 'icon-loading': loadingList }" :navigation-class="{ 'icon-loading': loading }"> + <Content app-name="settings" + :class="{ 'with-app-sidebar': currentApp}" + :content-class="{ 'icon-loading': loadingList }" + :navigation-class="{ 'icon-loading': loading }"> <AppNavigation> <ul id="appscategories"> <AppNavigationItem v-for="item in menu" :key="item.key" :item="item" /> @@ -38,32 +40,22 @@ </template> <script> -import { +import { AppContent, AppNavigation, AppNavigationItem, AppSidebar, Content -} from 'nextcloud-vue'; -import AppList from '../components/appList'; -import Vue from 'vue'; +} from 'nextcloud-vue' +import AppList from '../components/AppList' +import Vue from 'vue' import VueLocalStorage from 'vue-localstorage' -import AppDetails from '../components/appDetails'; +import AppDetails from '../components/AppDetails' Vue.use(VueLocalStorage) export default { name: 'Apps', - props: { - category: { - type: String, - default: 'installed', - }, - id: { - type: String, - default: '', - } - }, components: { AppContent, AppNavigation, @@ -73,110 +65,88 @@ export default { AppDetails, AppList }, - methods: { - setSearch(query) { - this.searchQuery = query; - }, - resetSearch() { - this.setSearch(''); + props: { + category: { + type: String, + default: 'installed' }, - hideAppDetails() { - this.$router.push({ - name: 'apps-category', - params: {category: this.category} - }) + id: { + type: String, + default: '' } }, - beforeMount() { - this.$store.dispatch('getCategories'); - this.$store.dispatch('getAllApps'); - this.$store.dispatch('getGroups', {offset: 0, limit: 5}); - this.$store.commit('setUpdateCount', this.$store.getters.getServerData.updateCount) - }, - mounted() { - /** - * Register search - */ - this.appSearch = new OCA.Search(this.setSearch, this.resetSearch); - }, data() { return { searchQuery: '' } }, - watch: { - category: function (val, old) { - this.setSearch(''); - } - }, computed: { loading() { - return this.$store.getters.loading('categories'); + return this.$store.getters.loading('categories') }, loadingList() { - return this.$store.getters.loading('list'); + return this.$store.getters.loading('list') }, currentApp() { - return this.apps.find(app => app.id === this.id ); + return this.apps.find(app => app.id === this.id) }, categories() { - return this.$store.getters.getCategories; + return this.$store.getters.getCategories }, apps() { - return this.$store.getters.getAllApps; + return this.$store.getters.getAllApps }, updateCount() { - return this.$store.getters.getUpdateCount; + return this.$store.getters.getUpdateCount }, settings() { - return this.$store.getters.getServerData; + return this.$store.getters.getServerData }, // BUILD APP NAVIGATION MENU OBJECT menu() { // Data provided php side - let categories = this.$store.getters.getCategories; - categories = Array.isArray(categories) ? categories : []; + let categories = this.$store.getters.getCategories + categories = Array.isArray(categories) ? categories : [] // Map groups categories = categories.map(category => { - let item = {}; - item.id = 'app-category-' + category.ident; - item.icon = 'icon-category-' + category.ident; - item.classes = []; // empty classes, active will be set later + let item = {} + item.id = 'app-category-' + category.ident + item.icon = 'icon-category-' + category.ident + item.classes = [] // empty classes, active will be set later item.router = { // router link to name: 'apps-category', - params: {category: category.ident} - }; - item.text = category.displayName; - - return item; - }); + params: { category: category.ident } + } + item.text = category.displayName + return item + }) // Add everyone group let defaultCategories = [ { id: 'app-category-your-apps', classes: [], - router: {name: 'apps'}, + router: { name: 'apps' }, icon: 'icon-category-installed', - text: t('settings', 'Your apps'), + text: t('settings', 'Your apps') }, { id: 'app-category-enabled', classes: [], icon: 'icon-category-enabled', - router: {name: 'apps-category', params: {category: 'enabled'}}, - text: t('settings', 'Active apps'), + router: { name: 'apps-category', params: { category: 'enabled' } }, + text: t('settings', 'Active apps') }, { id: 'app-category-disabled', classes: [], icon: 'icon-category-disabled', - router: {name: 'apps-category', params: {category: 'disabled'}}, - text: t('settings', 'Disabled apps'), + router: { name: 'apps-category', params: { category: 'disabled' } }, + text: t('settings', 'Disabled apps') } - ]; + ] if (!this.settings.appstoreEnabled) { return defaultCategories @@ -187,40 +157,71 @@ export default { id: 'app-category-updates', classes: [], icon: 'icon-download', - router: {name: 'apps-category', params: {category: 'updates'}}, + router: { name: 'apps-category', params: { category: 'updates' } }, text: t('settings', 'Updates'), - utils: {counter: this.$store.getters.getUpdateCount} - }); + utils: { counter: this.$store.getters.getUpdateCount } + }) } defaultCategories.push({ id: 'app-category-app-bundles', classes: [], icon: 'icon-category-app-bundles', - router: {name: 'apps-category', params: {category: 'app-bundles'}}, - text: t('settings', 'App bundles'), - }); + router: { name: 'apps-category', params: { category: 'app-bundles' } }, + text: t('settings', 'App bundles') + }) - categories = defaultCategories.concat(categories); + categories = defaultCategories.concat(categories) // Set current group as active - let activeGroup = categories.findIndex(group => group.id === 'app-category-' + this.category); + let activeGroup = categories.findIndex(group => group.id === 'app-category-' + this.category) if (activeGroup >= 0) { - categories[activeGroup].classes.push('active'); + categories[activeGroup].classes.push('active') } else { - categories[0].classes.push('active'); + categories[0].classes.push('active') } categories.push({ id: 'app-developer-docs', classes: [], href: this.settings.developerDocumentation, - text: t('settings', 'Developer documentation') + ' ↗', - }); + text: t('settings', 'Developer documentation') + ' ↗' + }) // Return return categories + } + }, + watch: { + category: function(val, old) { + this.setSearch('') + } + }, + beforeMount() { + this.$store.dispatch('getCategories') + this.$store.dispatch('getAllApps') + this.$store.dispatch('getGroups', { offset: 0, limit: 5 }) + this.$store.commit('setUpdateCount', this.$store.getters.getServerData.updateCount) + }, + mounted() { + /** + * Register search + */ + this.appSearch = new OCA.Search(this.setSearch, this.resetSearch) + }, + methods: { + setSearch(query) { + this.searchQuery = query }, + resetSearch() { + this.setSearch('') + }, + hideAppDetails() { + this.$router.push({ + name: 'apps-category', + params: { category: this.category } + }) + } } } </script> diff --git a/apps/settings/src/views/Users.vue b/apps/settings/src/views/Users.vue index dcbef1cd7995c18da61cde97d3afe73e0ea48edb..3c3082fc0778db0f20c48eddf9afe1d9d6734643 100644 --- a/apps/settings/src/views/Users.vue +++ b/apps/settings/src/views/Users.vue @@ -23,47 +23,69 @@ <template> <Content app-name="settings" :navigation-class="{ 'icon-loading': loadingAddGroup }"> <AppNavigation> - <AppNavigationNew button-id="new-user-button" :text="t('settings','New user')" button-class="icon-add" @click="toggleNewUserMenu" /> + <AppNavigationNew button-id="new-user-button" + :text="t('settings','New user')" + button-class="icon-add" + @click="toggleNewUserMenu" /> <ul id="usergrouplist"> <AppNavigationItem v-for="item in menu" :key="item.key" :item="item" /> </ul> <AppNavigationSettings> <div> - <p>{{t('settings', 'Default quota:')}}</p> - <Multiselect :value="defaultQuota" :options="quotaOptions" - tag-placeholder="create" :placeholder="t('settings', 'Select default quota')" - label="label" track-by="id" - :allowEmpty="false" :taggable="true" - @tag="validateQuota" @input="setDefaultQuota"> - </Multiselect> - + <p>{{ t('settings', 'Default quota:') }}</p> + <Multiselect :value="defaultQuota" + :options="quotaOptions" + tag-placeholder="create" + :placeholder="t('settings', 'Select default quota')" + label="label" + track-by="id" + :allow-empty="false" + :taggable="true" + @tag="validateQuota" + @input="setDefaultQuota" /> </div> <div> - <input type="checkbox" id="showLanguages" class="checkbox" v-model="showLanguages"> - <label for="showLanguages">{{t('settings', 'Show Languages')}}</label> + <input id="showLanguages" + v-model="showLanguages" + type="checkbox" + class="checkbox"> + <label for="showLanguages">{{ t('settings', 'Show Languages') }}</label> </div> <div> - <input type="checkbox" id="showLastLogin" class="checkbox" v-model="showLastLogin"> - <label for="showLastLogin">{{t('settings', 'Show last login')}}</label> + <input id="showLastLogin" + v-model="showLastLogin" + type="checkbox" + class="checkbox"> + <label for="showLastLogin">{{ t('settings', 'Show last login') }}</label> </div> <div> - <input type="checkbox" id="showUserBackend" class="checkbox" v-model="showUserBackend"> - <label for="showUserBackend">{{t('settings', 'Show user backend')}}</label> + <input id="showUserBackend" + v-model="showUserBackend" + type="checkbox" + class="checkbox"> + <label for="showUserBackend">{{ t('settings', 'Show user backend') }}</label> </div> <div> - <input type="checkbox" id="showStoragePath" class="checkbox" v-model="showStoragePath"> - <label for="showStoragePath">{{t('settings', 'Show storage path')}}</label> + <input id="showStoragePath" + v-model="showStoragePath" + type="checkbox" + class="checkbox"> + <label for="showStoragePath">{{ t('settings', 'Show storage path') }}</label> </div> </AppNavigationSettings> </AppNavigation> <AppContent> - <UserList #content :users="users" :showConfig="showConfig" :selectedGroup="selectedGroup" :externalActions="externalActions" /> + <UserList #content + :users="users" + :show-config="showConfig" + :selected-group="selectedGroup" + :external-actions="externalActions" /> </AppContent> </Content> </template> <script> -import Vue from 'vue'; +import Vue from 'vue' import VueLocalStorage from 'vue-localstorage' import { AppContent, @@ -71,52 +93,35 @@ import { AppNavigationItem, AppNavigationNew, AppNavigationSettings, - AppSidebar, Content, Multiselect -} from 'nextcloud-vue'; -import UserList from '../components/userList'; -import api from '../store/api'; +} from 'nextcloud-vue' +import UserList from '../components/UserList' Vue.use(VueLocalStorage) export default { name: 'Users', - props: ['selectedGroup'], components: { AppContent, AppNavigation, AppNavigationItem, AppNavigationNew, AppNavigationSettings, - AppSidebar, Content, UserList, - Multiselect, + Multiselect }, - beforeMount() { - this.$store.commit('initGroups', { - groups: this.$store.getters.getServerData.groups, - orderBy: this.$store.getters.getServerData.sortGroups, - userCount: this.$store.getters.getServerData.userCount - }); - this.$store.dispatch('getPasswordPolicyMinLength'); - }, - created() { - // init the OCA.Settings.UserList object - // and add the registerAction method - Object.assign(OCA, { - Settings: { - UserList: { - registerAction: this.registerAction - } - } - }); + props: { + selectedGroup: { + type: String, + default: null + } }, data() { return { // default quota is set to unlimited - unlimitedQuota: {id: 'none', label: t('settings', 'Unlimited')}, + unlimitedQuota: { id: 'none', label: t('settings', 'Unlimited') }, // temporary value used for multiselect change selectedQuota: false, externalActions: [], @@ -131,217 +136,103 @@ export default { } } }, - methods: { - toggleNewUserMenu() { - this.showConfig.showNewUserForm = !this.showConfig.showNewUserForm; - if (this.showConfig.showNewUserForm) { - Vue.nextTick(() => { - window.newusername.focus(); - }); - } - }, - getLocalstorage(key) { - // force initialization - let localConfig = this.$localStorage.get(key); - // if localstorage is null, fallback to original values - this.showConfig[key] = localConfig !== null ? localConfig === 'true' : this.showConfig[key]; - return this.showConfig[key]; - }, - setLocalStorage(key, status) { - this.showConfig[key] = status; - this.$localStorage.set(key, status); - return status; - }, - removeGroup(groupid) { - let self = this; - // TODO migrate to a vue js confirm dialog component - OC.dialogs.confirm( - t('settings', 'You are about to remove the group {group}. The users will NOT be deleted.', {group: groupid}), - t('settings','Please confirm the group removal '), - function (success) { - if (success) { - self.$store.dispatch('removeGroup', groupid); - } - } - ); - }, - - /** - * Dispatch default quota set request - * - * @param {string|Object} quota Quota in readable format '5 GB' or Object {id: '5 GB', label: '5GB'} - * @returns {string} - */ - setDefaultQuota(quota = 'none') { - this.$store.dispatch('setAppConfig', { - app: 'files', - key: 'default_quota', - // ensure we only send the preset id - value: quota.id ? quota.id : quota - }).then(() => { - if (typeof quota !== 'object') { - quota = {id: quota, label: quota}; - } - this.defaultQuota = quota; - }); - }, - - /** - * Validate quota string to make sure it's a valid human file size - * - * @param {string} quota Quota in readable format '5 GB' - * @returns {Promise|boolean} - */ - validateQuota(quota) { - // only used for new presets sent through @Tag - let validQuota = OC.Util.computerFileSize(quota); - if (validQuota === null) { - return this.setDefaultQuota('none'); - } else { - // unify format output - return this.setDefaultQuota(OC.Util.humanFileSize(OC.Util.computerFileSize(quota))); - } - // if no valid do not change - return false; - }, - - /** - * Register a new action for the user menu - * - * @param {string} icon the icon class - * @param {string} text the text to display - * @param {function} action the function to run - */ - registerAction(icon, text, action) { - this.externalActions.push({ - icon: icon, - text: text, - action: action - }); - return this.externalActions; - }, - - /** - * Create a new group - * - * @param {Object} event The form submit event - */ - createGroup(event) { - let gid = event.target[0].value; - this.loadingAddGroup = true; - this.$store.dispatch('addGroup', gid) - .then(() => { - this.showAddGroupEntry = false; - this.loadingAddGroup = false; - this.$router.push({ - name: 'group', - params: { - selectedGroup: gid - } - }) - }) - .catch(() => { - this.loadingAddGroup = false; - }); - } - }, computed: { users() { - return this.$store.getters.getUsers; + return this.$store.getters.getUsers }, usersOffset() { - return this.$store.getters.getUsersOffset; + return this.$store.getters.getUsersOffset }, usersLimit() { - return this.$store.getters.getUsersLimit; + return this.$store.getters.getUsersLimit }, // Local settings showLanguages: { - get: function() {return this.getLocalstorage('showLanguages')}, + get: function() { return this.getLocalstorage('showLanguages') }, set: function(status) { - this.setLocalStorage('showLanguages', status); + this.setLocalStorage('showLanguages', status) } }, showLastLogin: { - get: function() {return this.getLocalstorage('showLastLogin')}, + get: function() { return this.getLocalstorage('showLastLogin') }, set: function(status) { - this.setLocalStorage('showLastLogin', status); + this.setLocalStorage('showLastLogin', status) } }, showUserBackend: { - get: function() {return this.getLocalstorage('showUserBackend')}, + get: function() { return this.getLocalstorage('showUserBackend') }, set: function(status) { - this.setLocalStorage('showUserBackend', status); + this.setLocalStorage('showUserBackend', status) } }, showStoragePath: { - get: function() {return this.getLocalstorage('showStoragePath')}, + get: function() { return this.getLocalstorage('showStoragePath') }, set: function(status) { - this.setLocalStorage('showStoragePath', status); + this.setLocalStorage('showStoragePath', status) } }, userCount() { - return this.$store.getters.getUserCount; + return this.$store.getters.getUserCount }, settings() { - return this.$store.getters.getServerData; + return this.$store.getters.getServerData }, // default quota quotaOptions() { // convert the preset array into objects - let quotaPreset = this.settings.quotaPreset.reduce((acc, cur) => acc.concat({id:cur, label:cur}), []); + let quotaPreset = this.settings.quotaPreset.reduce((acc, cur) => acc.concat({ id: cur, label: cur }), []) // add default presets - quotaPreset.unshift(this.unlimitedQuota); - return quotaPreset; + quotaPreset.unshift(this.unlimitedQuota) + return quotaPreset }, // mapping saved values to objects defaultQuota: { get: function() { if (this.selectedQuota !== false) { - return this.selectedQuota; + return this.selectedQuota } if (this.settings.defaultQuota !== this.unlimitedQuota.id && OC.Util.computerFileSize(this.settings.defaultQuota) >= 0) { // if value is valid, let's map the quotaOptions or return custom quota - return {id:this.settings.defaultQuota, label:this.settings.defaultQuota}; + return { id: this.settings.defaultQuota, label: this.settings.defaultQuota } } - return this.unlimitedQuota; // unlimited + return this.unlimitedQuota // unlimited }, set: function(quota) { - this.selectedQuota = quota; + this.selectedQuota = quota } - + }, // BUILD APP NAVIGATION MENU OBJECT menu() { // Data provided php side - let self = this; - let groups = this.$store.getters.getGroups; - groups = Array.isArray(groups) ? groups : []; + let self = this + let groups = this.$store.getters.getGroups + groups = Array.isArray(groups) ? groups : [] // Map groups groups = groups.map(group => { - let item = {}; - item.id = group.id.replace(' ', '_'); - item.key = item.id; + let item = {} + item.id = group.id.replace(' ', '_') + item.key = item.id item.utils = {} // router link to item.router = { name: 'group', - params: {selectedGroup: group.id} - }; + params: { selectedGroup: group.id } + } // group name - item.text = group.name; - item.title = group.name; + item.text = group.name + item.title = group.name // users count for all groups if (group.usercount - group.disabled > 0 || group.usercount === -1) { - item.utils.counter = group.usercount - group.disabled; + item.utils.counter = group.usercount - group.disabled } if (item.id !== 'admin' && item.id !== 'disabled' && this.settings.isAdmin) { @@ -352,65 +243,64 @@ export default { action: function() { self.removeGroup(group.id) } - }]; - }; - return item; - }); + }] + } + return item + }) // Every item is added on top of the array, so we're going backward // Groups, separator, disabled, admin, everyone // Add separator - let realGroups = groups.find((group) => {return group.id !== 'disabled' && group.id !== 'admin'}); - realGroups = typeof realGroups === 'undefined' ? [] : realGroups; - realGroups = Array.isArray(realGroups) ? realGroups : [realGroups]; + let realGroups = groups.find((group) => { return group.id !== 'disabled' && group.id !== 'admin' }) + realGroups = typeof realGroups === 'undefined' ? [] : realGroups + realGroups = Array.isArray(realGroups) ? realGroups : [realGroups] if (realGroups.length > 0) { let separator = { caption: true, text: t('settings', 'Groups') - }; - groups.unshift(separator); + } + groups.unshift(separator) } // Adjust admin and disabled groups - let adminGroup = groups.find(group => group.id == 'admin'); - let disabledGroup = groups.find(group => group.id == 'disabled'); + let adminGroup = groups.find(group => group.id === 'admin') + let disabledGroup = groups.find(group => group.id === 'disabled') // filter out admin and disabled - groups = groups.filter(group => ['admin', 'disabled'].indexOf(group.id) === -1); + groups = groups.filter(group => ['admin', 'disabled'].indexOf(group.id) === -1) if (adminGroup && adminGroup.text) { - adminGroup.text = t('settings', 'Admins'); // rename admin group - adminGroup.icon = 'icon-user-admin'; // set icon - groups.unshift(adminGroup); // add admin group if present + adminGroup.text = t('settings', 'Admins') // rename admin group + adminGroup.icon = 'icon-user-admin' // set icon + groups.unshift(adminGroup) // add admin group if present } if (disabledGroup && disabledGroup.text) { - disabledGroup.text = t('settings', 'Disabled users'); // rename disabled group - disabledGroup.icon = 'icon-disabled-users'; // set icon + disabledGroup.text = t('settings', 'Disabled users') // rename disabled group + disabledGroup.icon = 'icon-disabled-users' // set icon if (disabledGroup.utils && ( - disabledGroup.utils.counter > 0 // add disabled if not empty - || disabledGroup.utils.counter === -1) // add disabled if ldap enabled + disabledGroup.utils.counter > 0 // add disabled if not empty + || disabledGroup.utils.counter === -1) // add disabled if ldap enabled ) { - groups.unshift(disabledGroup); + groups.unshift(disabledGroup) } } - // Add everyone group let everyoneGroup = { id: 'everyone', key: 'everyone', icon: 'icon-contacts-dark', - router: {name:'users'}, - text: t('settings', 'Everyone'), - }; + router: { name: 'users' }, + text: t('settings', 'Everyone') + } // users count if (this.userCount > 0) { Vue.set(everyoneGroup, 'utils', { counter: this.userCount - }); + }) } - groups.unshift(everyoneGroup); + groups.unshift(everyoneGroup) let addGroup = { id: 'addgroup', @@ -418,7 +308,7 @@ export default { icon: 'icon-add', text: t('settings', 'Add group'), classes: this.loadingAddGroup ? 'icon-loading-small' : '' - }; + } if (this.showAddGroupEntry) { Vue.set(addGroup, 'edit', { text: t('settings', 'Add group'), @@ -426,8 +316,8 @@ export default { reset: function() { self.showAddGroupEntry = false } - }); - addGroup.classes = 'editing'; + }) + addGroup.classes = 'editing' } else { Vue.set(addGroup, 'action', function() { self.showAddGroupEntry = true @@ -437,10 +327,141 @@ export default { }) }) } - groups.unshift(addGroup); + groups.unshift(addGroup) - return groups; + return groups + } + }, + beforeMount() { + this.$store.commit('initGroups', { + groups: this.$store.getters.getServerData.groups, + orderBy: this.$store.getters.getServerData.sortGroups, + userCount: this.$store.getters.getServerData.userCount + }) + this.$store.dispatch('getPasswordPolicyMinLength') + }, + created() { + // init the OCA.Settings.UserList object + // and add the registerAction method + Object.assign(OCA, { + Settings: { + UserList: { + registerAction: this.registerAction + } + } + }) + }, + methods: { + toggleNewUserMenu() { + this.showConfig.showNewUserForm = !this.showConfig.showNewUserForm + if (this.showConfig.showNewUserForm) { + Vue.nextTick(() => { + window.newusername.focus() + }) + } }, + getLocalstorage(key) { + // force initialization + let localConfig = this.$localStorage.get(key) + // if localstorage is null, fallback to original values + this.showConfig[key] = localConfig !== null ? localConfig === 'true' : this.showConfig[key] + return this.showConfig[key] + }, + setLocalStorage(key, status) { + this.showConfig[key] = status + this.$localStorage.set(key, status) + return status + }, + removeGroup(groupid) { + let self = this + // TODO migrate to a vue js confirm dialog component + OC.dialogs.confirm( + t('settings', 'You are about to remove the group {group}. The users will NOT be deleted.', { group: groupid }), + t('settings', 'Please confirm the group removal '), + function(success) { + if (success) { + self.$store.dispatch('removeGroup', groupid) + } + } + ) + }, + + /** + * Dispatch default quota set request + * + * @param {string|Object} quota Quota in readable format '5 GB' or Object {id: '5 GB', label: '5GB'} + */ + setDefaultQuota(quota = 'none') { + this.$store.dispatch('setAppConfig', { + app: 'files', + key: 'default_quota', + // ensure we only send the preset id + value: quota.id ? quota.id : quota + }).then(() => { + if (typeof quota !== 'object') { + quota = { id: quota, label: quota } + } + this.defaultQuota = quota + }) + }, + + /** + * Validate quota string to make sure it's a valid human file size + * + * @param {string} quota Quota in readable format '5 GB' + * @returns {Promise|boolean} + */ + validateQuota(quota) { + // only used for new presets sent through @Tag + let validQuota = OC.Util.computerFileSize(quota) + if (validQuota === null) { + return this.setDefaultQuota('none') + } else { + // unify format output + return this.setDefaultQuota(OC.Util.humanFileSize(OC.Util.computerFileSize(quota))) + } + }, + + /** + * Register a new action for the user menu + * + * @param {string} icon the icon class + * @param {string} text the text to display + * @param {Function} action the function to run + * @returns {Array} + */ + registerAction(icon, text, action) { + this.externalActions.push({ + icon: icon, + text: text, + action: action + }) + return this.externalActions + }, + + /** + * Create a new group + * + * @param {Object} event The form submit event + */ + createGroup(event) { + let gid = event.target[0].value + this.loadingAddGroup = true + this.$store.dispatch('addGroup', gid) + .then(() => { + this.showAddGroupEntry = false + this.loadingAddGroup = false + this.$router.push({ + name: 'group', + params: { + selectedGroup: gid + } + }) + }) + .catch(() => { + this.loadingAddGroup = false + }) + } } } </script> diff --git a/apps/systemtags/js/systemtags.js b/apps/systemtags/js/systemtags.js index 607119f4acafe844e41e2537b2377863d36a9eb0..ad03d5517d45c563820e39a5ff28dd97798f9edc 100644 Binary files a/apps/systemtags/js/systemtags.js and b/apps/systemtags/js/systemtags.js differ diff --git a/apps/systemtags/js/systemtags.js.map b/apps/systemtags/js/systemtags.js.map index 59c3f5c2b54f537a3f288eba0e8467fa7b40ca6c..3d95b07e93557172331cc53e3357387709845932 100644 Binary files a/apps/systemtags/js/systemtags.js.map and b/apps/systemtags/js/systemtags.js.map differ diff --git a/apps/systemtags/src/app.js b/apps/systemtags/src/app.js index 2ef88564528318774b897d469728b25ecca5d0e5..ee836907b0ddbc7f1ca2d1dd4bdb303e2dae127d 100644 --- a/apps/systemtags/src/app.js +++ b/apps/systemtags/src/app.js @@ -13,14 +13,14 @@ /** * @namespace */ - OCA.SystemTags = {}; + OCA.SystemTags = {} } OCA.SystemTags.App = { initFileList: function($el) { if (this._fileList) { - return this._fileList; + return this._fileList } this._fileList = new OCA.SystemTags.FileList( @@ -35,56 +35,56 @@ // created. shown: true } - ); + ) - this._fileList.appName = t('systemtags', 'Tags'); - return this._fileList; + this._fileList.appName = t('systemtags', 'Tags') + return this._fileList }, removeFileList: function() { if (this._fileList) { - this._fileList.$fileList.empty(); + this._fileList.$fileList.empty() } }, _createFileActions: function() { // inherit file actions from the files app - var fileActions = new OCA.Files.FileActions(); + var fileActions = new OCA.Files.FileActions() // note: not merging the legacy actions because legacy apps are not // compatible with the sharing overview and need to be adapted first - fileActions.registerDefaultActions(); - fileActions.merge(OCA.Files.fileActions); + fileActions.registerDefaultActions() + fileActions.merge(OCA.Files.fileActions) if (!this._globalActionsInitialized) { // in case actions are registered later - this._onActionsUpdated = _.bind(this._onActionsUpdated, this); - OCA.Files.fileActions.on('setDefault.app-systemtags', this._onActionsUpdated); - OCA.Files.fileActions.on('registerAction.app-systemtags', this._onActionsUpdated); - this._globalActionsInitialized = true; + this._onActionsUpdated = _.bind(this._onActionsUpdated, this) + OCA.Files.fileActions.on('setDefault.app-systemtags', this._onActionsUpdated) + OCA.Files.fileActions.on('registerAction.app-systemtags', this._onActionsUpdated) + this._globalActionsInitialized = true } // when the user clicks on a folder, redirect to the corresponding // folder in the files app instead of opening it directly - fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { - OCA.Files.App.setActiveView('files', {silent: true}); - OCA.Files.App.fileList.changeDirectory(OC.joinPaths(context.$file.attr('data-path'), filename), true, true); - }); - fileActions.setDefault('dir', 'Open'); - return fileActions; + fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function(filename, context) { + OCA.Files.App.setActiveView('files', { silent: true }) + OCA.Files.App.fileList.changeDirectory(OC.joinPaths(context.$file.attr('data-path'), filename), true, true) + }) + fileActions.setDefault('dir', 'Open') + return fileActions }, _onActionsUpdated: function(ev) { if (!this._fileList) { - return; + return } if (ev.action) { - this._fileList.fileActions.registerAction(ev.action); + this._fileList.fileActions.registerAction(ev.action) } else if (ev.defaultAction) { this._fileList.fileActions.setDefault( ev.defaultAction.mime, ev.defaultAction.name - ); + ) } }, @@ -92,21 +92,21 @@ * Destroy the app */ destroy: function() { - OCA.Files.fileActions.off('setDefault.app-systemtags', this._onActionsUpdated); - OCA.Files.fileActions.off('registerAction.app-systemtags', this._onActionsUpdated); - this.removeFileList(); - this._fileList = null; - delete this._globalActionsInitialized; + OCA.Files.fileActions.off('setDefault.app-systemtags', this._onActionsUpdated) + OCA.Files.fileActions.off('registerAction.app-systemtags', this._onActionsUpdated) + this.removeFileList() + this._fileList = null + delete this._globalActionsInitialized } - }; + } -})(); +})() $(document).ready(function() { $('#app-content-systemtagsfilter').on('show', function(e) { - OCA.SystemTags.App.initFileList($(e.target)); - }); + OCA.SystemTags.App.initFileList($(e.target)) + }) $('#app-content-systemtagsfilter').on('hide', function() { - OCA.SystemTags.App.removeFileList(); - }); -}); + OCA.SystemTags.App.removeFileList() + }) +}) diff --git a/apps/systemtags/src/filesplugin.js b/apps/systemtags/src/filesplugin.js index 7cf39e9dfd8ab8dbb43c0f27b0910da54e2b369f..ec2734fa9725fafeb6db8e0bf77f3bba798afe85 100644 --- a/apps/systemtags/src/filesplugin.js +++ b/apps/systemtags/src/filesplugin.js @@ -9,12 +9,12 @@ */ (function() { - OCA.SystemTags = _.extend({}, OCA.SystemTags); + OCA.SystemTags = _.extend({}, OCA.SystemTags) if (!OCA.SystemTags) { /** * @namespace */ - OCA.SystemTags = {}; + OCA.SystemTags = {} } /** @@ -28,37 +28,35 @@ attach: function(fileList) { if (this.ignoreLists.indexOf(fileList.id) >= 0) { - return; + return } - var systemTagsInfoView = new OCA.SystemTags.SystemTagsInfoView(); - fileList.registerDetailView(systemTagsInfoView); + var systemTagsInfoView = new OCA.SystemTags.SystemTagsInfoView() + fileList.registerDetailView(systemTagsInfoView) _.each(fileList.getRegisteredDetailViews(), function(detailView) { if (detailView instanceof OCA.Files.MainFileInfoDetailView) { - var systemTagsInfoViewToggleView = - new OCA.SystemTags.SystemTagsInfoViewToggleView({ + var systemTagsInfoViewToggleView + = new OCA.SystemTags.SystemTagsInfoViewToggleView({ systemTagsInfoView: systemTagsInfoView - }); - systemTagsInfoViewToggleView.render(); + }) + systemTagsInfoViewToggleView.render() // The toggle view element is detached before the // MainFileInfoDetailView is rendered to prevent its event // handlers from being removed. systemTagsInfoViewToggleView.listenTo(detailView, 'pre-render', function() { - systemTagsInfoViewToggleView.$el.detach(); - }); + systemTagsInfoViewToggleView.$el.detach() + }) systemTagsInfoViewToggleView.listenTo(detailView, 'post-render', function() { - detailView.$el.find('.file-details').append(systemTagsInfoViewToggleView.$el); - }); + detailView.$el.find('.file-details').append(systemTagsInfoViewToggleView.$el) + }) - return; } - }); + }) } - }; - -})(); + } -OC.Plugins.register('OCA.Files.FileList', OCA.SystemTags.FilesPlugin); +})() +OC.Plugins.register('OCA.Files.FileList', OCA.SystemTags.FilesPlugin) diff --git a/apps/systemtags/src/systemtags.js b/apps/systemtags/src/systemtags.js index 265e41a21db8fc9a74720122c1296f872520867d..80033742a8b256a4fe256a655627a2e5e3ff488a 100644 --- a/apps/systemtags/src/systemtags.js +++ b/apps/systemtags/src/systemtags.js @@ -5,4 +5,4 @@ import './systemtagsinfoview' import './systemtagsinfoviewtoggleview' import './css/systemtagsfilelist.scss' -window.OCA.SystemTags = OCA.SystemTags; +window.OCA.SystemTags = OCA.SystemTags diff --git a/apps/systemtags/src/systemtagsfilelist.js b/apps/systemtags/src/systemtagsfilelist.js index ad6bf7dd78d19447cc5b4bdb89392d024b62a44d..373ab650b9ebfd52a4fed190fc9928983b13e362 100644 --- a/apps/systemtags/src/systemtagsfilelist.js +++ b/apps/systemtags/src/systemtagsfilelist.js @@ -15,268 +15,319 @@ * @classdesc SystemTags file list. * Contains a list of files filtered by system tags. * - * @param $el container element with existing markup for the #controls - * and a table - * @param [options] map of options, see other parameters + * @param {Object} $el container element with existing markup for the #controls and a table + * @param {Array} [options] map of options, see other parameters * @param {Array.<string>} [options.systemTagIds] array of system tag ids to * filter by */ var FileList = function($el, options) { - this.initialize($el, options); - }; - FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, + this.initialize($el, options) + } + FileList.prototype = _.extend( + {}, + OCA.Files.FileList.prototype, /** @lends OCA.SystemTags.FileList.prototype */ { - id: 'systemtagsfilter', - appName: t('systemtags', 'Tagged files'), - - /** - * Array of system tag ids to filter by - * - * @type Array.<string> - */ - _systemTagIds: [], - _lastUsedTags: [], - - _clientSideSort: true, - _allowSelection: false, - - _filterField: null, - - /** - * @private - */ - initialize: function($el, options) { - OCA.Files.FileList.prototype.initialize.apply(this, arguments); - if (this.initialized) { - return; - } + id: 'systemtagsfilter', + appName: t('systemtags', 'Tagged files'), + + /** + * Array of system tag ids to filter by + * + * @type Array.<string> + */ + _systemTagIds: [], + _lastUsedTags: [], + + _clientSideSort: true, + _allowSelection: false, + + _filterField: null, + + /** + * @private + * @param {Object} $el container element + * @param {Object} [options] map of options, see other parameters + */ + initialize: function($el, options) { + OCA.Files.FileList.prototype.initialize.apply(this, arguments) + if (this.initialized) { + return + } - if (options && options.systemTagIds) { - this._systemTagIds = options.systemTagIds; - } + if (options && options.systemTagIds) { + this._systemTagIds = options.systemTagIds + } - OC.Plugins.attach('OCA.SystemTags.FileList', this); + OC.Plugins.attach('OCA.SystemTags.FileList', this) - var $controls = this.$el.find('#controls').empty(); + var $controls = this.$el.find('#controls').empty() - _.defer(_.bind(this._getLastUsedTags, this)); - this._initFilterField($controls); - }, - - destroy: function() { - this.$filterField.remove(); + _.defer(_.bind(this._getLastUsedTags, this)) + this._initFilterField($controls) + }, - OCA.Files.FileList.prototype.destroy.apply(this, arguments); - }, + destroy: function() { + this.$filterField.remove() - _getLastUsedTags: function() { - var self = this; - $.ajax({ - type: 'GET', - url: OC.generateUrl('/apps/systemtags/lastused'), - success: function (response) { - self._lastUsedTags = response; - } - }); - }, - - _initFilterField: function($container) { - var self = this; - this.$filterField = $('<input type="hidden" name="tags"/>'); - $container.append(this.$filterField); - this.$filterField.select2({ - placeholder: t('systemtags', 'Select tags to filter by'), - allowClear: false, - multiple: true, - toggleSelect: true, - separator: ',', - query: _.bind(this._queryTagsAutocomplete, this), - - id: function(tag) { - return tag.id; - }, - - initSelection: function(element, callback) { - var val = $(element).val().trim(); - if (val) { - var tagIds = val.split(','), - tags = []; - - OC.SystemTags.collection.fetch({ - success: function() { - _.each(tagIds, function(tagId) { - var tag = OC.SystemTags.collection.get(tagId); - if (!_.isUndefined(tag)) { - tags.push(tag.toJSON()); - } - }); - - callback(tags); + OCA.Files.FileList.prototype.destroy.apply(this, arguments) + }, + + _getLastUsedTags: function() { + var self = this + $.ajax({ + type: 'GET', + url: OC.generateUrl('/apps/systemtags/lastused'), + success: function(response) { + self._lastUsedTags = response + } + }) + }, + + _initFilterField: function($container) { + var self = this + this.$filterField = $('<input type="hidden" name="tags"/>') + $container.append(this.$filterField) + this.$filterField.select2({ + placeholder: t('systemtags', 'Select tags to filter by'), + allowClear: false, + multiple: true, + toggleSelect: true, + separator: ',', + query: _.bind(this._queryTagsAutocomplete, this), + + id: function(tag) { + return tag.id + }, + + initSelection: function(element, callback) { + var val = $(element) + .val() + .trim() + if (val) { + var tagIds = val.split(',') + var tags = [] + + OC.SystemTags.collection.fetch({ + success: function() { + _.each(tagIds, function(tagId) { + var tag = OC.SystemTags.collection.get( + tagId + ) + if (!_.isUndefined(tag)) { + tags.push(tag.toJSON()) + } + }) + + callback(tags) + } + }) + } else { + // eslint-disable-next-line standard/no-callback-literal + callback([]) + } + }, + + formatResult: function(tag) { + return OC.SystemTags.getDescriptiveTag(tag) + }, + + formatSelection: function(tag) { + return OC.SystemTags.getDescriptiveTag(tag)[0] + .outerHTML + }, + + sortResults: function(results) { + results.sort(function(a, b) { + var aLastUsed = self._lastUsedTags.indexOf(a.id) + var bLastUsed = self._lastUsedTags.indexOf(b.id) + + if (aLastUsed !== bLastUsed) { + if (bLastUsed === -1) { + return -1 + } + if (aLastUsed === -1) { + return 1 + } + return aLastUsed < bLastUsed ? -1 : 1 } - }); + + // Both not found + return OC.Util.naturalSortCompare(a.name, b.name) + }) + return results + }, + + escapeMarkup: function(m) { + // prevent double markup escape + return m + }, + formatNoMatches: function() { + return t('systemtags', 'No tags found') + } + }) + this.$filterField.on( + 'change', + _.bind(this._onTagsChanged, this) + ) + return this.$filterField + }, + + /** + * Autocomplete function for dropdown results + * + * @param {Object} query select2 query object + */ + _queryTagsAutocomplete: function(query) { + OC.SystemTags.collection.fetch({ + success: function() { + var results = OC.SystemTags.collection.filterByName( + query.term + ) + + query.callback({ + results: _.invoke(results, 'toJSON') + }) + } + }) + }, + + /** + * Event handler for when the URL changed + * + * @param {Event} e the urlchanged event + */ + _onUrlChanged: function(e) { + if (e.dir) { + var tags = _.filter(e.dir.split('/'), function(val) { + return val.trim() !== '' + }) + this.$filterField.select2('val', tags || []) + this._systemTagIds = tags + this.reload() + } + }, + + _onTagsChanged: function(ev) { + var val = $(ev.target) + .val() + .trim() + if (val !== '') { + this._systemTagIds = val.split(',') + } else { + this._systemTagIds = [] + } + + this.$el.trigger( + $.Event('changeDirectory', { + dir: this._systemTagIds.join('/') + }) + ) + this.reload() + }, + + updateEmptyContent: function() { + var dir = this.getCurrentDirectory() + if (dir === '/') { + // root has special permissions + if (!this._systemTagIds.length) { + // no tags selected + this.$el + .find('#emptycontent') + .html( + '<div class="icon-systemtags"></div>' + + '<h2>' + + t( + 'systemtags', + 'Please select tags to filter by' + ) + + '</h2>' + ) } else { - callback([]); + // tags selected but no results + this.$el + .find('#emptycontent') + .html( + '<div class="icon-systemtags"></div>' + + '<h2>' + + t( + 'systemtags', + 'No files found for the selected tags' + ) + + '</h2>' + ) } - }, + this.$el + .find('#emptycontent') + .toggleClass('hidden', !this.isEmpty) + this.$el + .find('#filestable thead th') + .toggleClass('hidden', this.isEmpty) + } else { + OCA.Files.FileList.prototype.updateEmptyContent.apply( + this, + arguments + ) + } + }, - formatResult: function (tag) { - return OC.SystemTags.getDescriptiveTag(tag); - }, + getDirectoryPermissions: function() { + return OC.PERMISSION_READ | OC.PERMISSION_DELETE + }, - formatSelection: function (tag) { - return OC.SystemTags.getDescriptiveTag(tag)[0].outerHTML; - }, + updateStorageStatistics: function() { + // no op because it doesn't have + // storage info like free space / used space + }, - sortResults: function(results) { - results.sort(function(a, b) { - var aLastUsed = self._lastUsedTags.indexOf(a.id); - var bLastUsed = self._lastUsedTags.indexOf(b.id); + reload: function() { + // there is only root + this._setCurrentDir('/', false) - if (aLastUsed !== bLastUsed) { - if (bLastUsed === -1) { - return -1; - } - if (aLastUsed === -1) { - return 1; - } - return aLastUsed < bLastUsed ? -1 : 1; - } + if (!this._systemTagIds.length) { + // don't reload + this.updateEmptyContent() + this.setFiles([]) + return $.Deferred().resolve() + } - // Both not found - return OC.Util.naturalSortCompare(a.name, b.name); - }); - return results; - }, - - escapeMarkup: function(m) { - // prevent double markup escape - return m; - }, - formatNoMatches: function() { - return t('systemtags', 'No tags found'); + this._selectedFiles = {} + this._selectionSummary.clear() + if (this._currentFileModel) { + this._currentFileModel.off() } - }); - this.$filterField.on('change', _.bind(this._onTagsChanged, this)); - return this.$filterField; - }, - - /** - * Autocomplete function for dropdown results - * - * @param {Object} query select2 query object - */ - _queryTagsAutocomplete: function(query) { - OC.SystemTags.collection.fetch({ - success: function() { - var results = OC.SystemTags.collection.filterByName(query.term); - - query.callback({ - results: _.invoke(results, 'toJSON') - }); + this._currentFileModel = null + this.$el.find('.select-all').prop('checked', false) + this.showMask() + this._reloadCall = this.filesClient.getFilteredFiles( + { + systemTagIds: this._systemTagIds + }, + { + properties: this._getWebdavProperties() + } + ) + if (this._detailsView) { + // close sidebar + this._updateDetailsView(null) } - }); - }, - - /** - * Event handler for when the URL changed - */ - _onUrlChanged: function(e) { - if (e.dir) { - var tags = _.filter(e.dir.split('/'), function(val) { return val.trim() !== ''; }); - this.$filterField.select2('val', tags || []); - this._systemTagIds = tags; - this.reload(); - } - }, - - _onTagsChanged: function(ev) { - var val = $(ev.target).val().trim(); - if (val !== '') { - this._systemTagIds = val.split(','); - } else { - this._systemTagIds = []; - } - - this.$el.trigger(jQuery.Event('changeDirectory', { - dir: this._systemTagIds.join('/') - })); - this.reload(); - }, - - updateEmptyContent: function() { - var dir = this.getCurrentDirectory(); - if (dir === '/') { - // root has special permissions - if (!this._systemTagIds.length) { - // no tags selected - this.$el.find('#emptycontent').html('<div class="icon-systemtags"></div>' + - '<h2>' + t('systemtags', 'Please select tags to filter by') + '</h2>'); - } else { - // tags selected but no results - this.$el.find('#emptycontent').html('<div class="icon-systemtags"></div>' + - '<h2>' + t('systemtags', 'No files found for the selected tags') + '</h2>'); + var callBack = this.reloadCallback.bind(this) + return this._reloadCall.then(callBack, callBack) + }, + + reloadCallback: function(status, result) { + if (result) { + // prepend empty dir info because original handler + result.unshift({}) } - this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty); - this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty); - } - else { - OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments); - } - }, - - getDirectoryPermissions: function() { - return OC.PERMISSION_READ | OC.PERMISSION_DELETE; - }, - - updateStorageStatistics: function() { - // no op because it doesn't have - // storage info like free space / used space - }, - - reload: function() { - // there is only root - this._setCurrentDir('/', false); - - if (!this._systemTagIds.length) { - // don't reload - this.updateEmptyContent(); - this.setFiles([]); - return $.Deferred().resolve(); - } - this._selectedFiles = {}; - this._selectionSummary.clear(); - if (this._currentFileModel) { - this._currentFileModel.off(); + return OCA.Files.FileList.prototype.reloadCallback.call( + this, + status, + result + ) } - this._currentFileModel = null; - this.$el.find('.select-all').prop('checked', false); - this.showMask(); - this._reloadCall = this.filesClient.getFilteredFiles( - { - systemTagIds: this._systemTagIds - }, - { - properties: this._getWebdavProperties() - } - ); - if (this._detailsView) { - // close sidebar - this._updateDetailsView(null); - } - var callBack = this.reloadCallback.bind(this); - return this._reloadCall.then(callBack, callBack); - }, - - reloadCallback: function(status, result) { - if (result) { - // prepend empty dir info because original handler - result.unshift({}); - } - - return OCA.Files.FileList.prototype.reloadCallback.call(this, status, result); } - }); + ) - OCA.SystemTags.FileList = FileList; -})(); + OCA.SystemTags.FileList = FileList +})() diff --git a/apps/systemtags/src/systemtagsinfoview.js b/apps/systemtags/src/systemtagsinfoview.js index 1bf7287342fd2af1f25dd1af2d04b5a9294fb9f5..548a147591cbe0245295b80f057483dc4325c1b6 100644 --- a/apps/systemtags/src/systemtagsinfoview.js +++ b/apps/systemtags/src/systemtagsinfoview.js @@ -11,11 +11,11 @@ (function(OCA) { function modelToSelection(model) { - var data = model.toJSON(); + var data = model.toJSON() if (!OC.isUserAdmin() && !data.canAssign) { - data.locked = true; + data.locked = true } - return data; + return data } /** @@ -28,144 +28,142 @@ var SystemTagsInfoView = OCA.Files.DetailFileInfoView.extend( /** @lends OCA.SystemTags.SystemTagsInfoView.prototype */ { - _rendered: false, + _rendered: false, - className: 'systemTagsInfoView hidden', + className: 'systemTagsInfoView hidden', - /** - * @type OC.SystemTags.SystemTagsInputField - */ - _inputView: null, + /** + * @type OC.SystemTags.SystemTagsInputField + */ + _inputView: null, - initialize: function(options) { - var self = this; - options = options || {}; + initialize: function(options) { + var self = this + options = options || {} - this._inputView = new OC.SystemTags.SystemTagsInputField({ - multiple: true, - allowActions: true, - allowCreate: true, - isAdmin: OC.isUserAdmin(), - initSelection: function(element, callback) { - callback(self.selectedTagsCollection.map(modelToSelection)); - } - }); + this._inputView = new OC.SystemTags.SystemTagsInputField({ + multiple: true, + allowActions: true, + allowCreate: true, + isAdmin: OC.isUserAdmin(), + initSelection: function(element, callback) { + callback(self.selectedTagsCollection.map(modelToSelection)) + } + }) - this.selectedTagsCollection = new OC.SystemTags.SystemTagsMappingCollection([], {objectType: 'files'}); + this.selectedTagsCollection = new OC.SystemTags.SystemTagsMappingCollection([], { objectType: 'files' }) - this._inputView.collection.on('change:name', this._onTagRenamedGlobally, this); - this._inputView.collection.on('remove', this._onTagDeletedGlobally, this); + this._inputView.collection.on('change:name', this._onTagRenamedGlobally, this) + this._inputView.collection.on('remove', this._onTagDeletedGlobally, this) - this._inputView.on('select', this._onSelectTag, this); - this._inputView.on('deselect', this._onDeselectTag, this); - }, + this._inputView.on('select', this._onSelectTag, this) + this._inputView.on('deselect', this._onDeselectTag, this) + }, - /** - * Event handler whenever a tag was selected - */ - _onSelectTag: function(tag) { + /** + * Event handler whenever a tag was selected + * @param {Object} tag the tag to create + */ + _onSelectTag: function(tag) { // create a mapping entry for this tag - this.selectedTagsCollection.create(tag.toJSON()); - }, - - /** - * Event handler whenever a tag gets deselected. - * Removes the selected tag from the mapping collection. - * - * @param {string} tagId tag id - */ - _onDeselectTag: function(tagId) { - this.selectedTagsCollection.get(tagId).destroy(); - }, - - /** - * Event handler whenever a tag was renamed globally. - * - * This will automatically adjust the tag mapping collection to - * container the new name. - * - * @param {OC.Backbone.Model} changedTag tag model that has changed - */ - _onTagRenamedGlobally: function(changedTag) { + this.selectedTagsCollection.create(tag.toJSON()) + }, + + /** + * Event handler whenever a tag gets deselected. + * Removes the selected tag from the mapping collection. + * + * @param {string} tagId tag id + */ + _onDeselectTag: function(tagId) { + this.selectedTagsCollection.get(tagId).destroy() + }, + + /** + * Event handler whenever a tag was renamed globally. + * + * This will automatically adjust the tag mapping collection to + * container the new name. + * + * @param {OC.Backbone.Model} changedTag tag model that has changed + */ + _onTagRenamedGlobally: function(changedTag) { // also rename it in the selection, if applicable - var selectedTagMapping = this.selectedTagsCollection.get(changedTag.id); - if (selectedTagMapping) { - selectedTagMapping.set(changedTag.toJSON()); - } - }, - - /** - * Event handler whenever a tag was deleted globally. - * - * This will automatically adjust the tag mapping collection to - * container the new name. - * - * @param {OC.Backbone.Model} tagId tag model that has changed - */ - _onTagDeletedGlobally: function(tagId) { + var selectedTagMapping = this.selectedTagsCollection.get(changedTag.id) + if (selectedTagMapping) { + selectedTagMapping.set(changedTag.toJSON()) + } + }, + + /** + * Event handler whenever a tag was deleted globally. + * + * This will automatically adjust the tag mapping collection to + * container the new name. + * + * @param {OC.Backbone.Model} tagId tag model that has changed + */ + _onTagDeletedGlobally: function(tagId) { // also rename it in the selection, if applicable - this.selectedTagsCollection.remove(tagId); - }, + this.selectedTagsCollection.remove(tagId) + }, - setFileInfo: function(fileInfo) { - var self = this; - if (!this._rendered) { - this.render(); - } + setFileInfo: function(fileInfo) { + var self = this + if (!this._rendered) { + this.render() + } - if (fileInfo) { - this.selectedTagsCollection.setObjectId(fileInfo.id); - this.selectedTagsCollection.fetch({ - success: function(collection) { - collection.fetched = true; + if (fileInfo) { + this.selectedTagsCollection.setObjectId(fileInfo.id) + this.selectedTagsCollection.fetch({ + success: function(collection) { + collection.fetched = true - var appliedTags = collection.map(modelToSelection); - self._inputView.setData(appliedTags); + var appliedTags = collection.map(modelToSelection) + self._inputView.setData(appliedTags) - if (appliedTags.length !== 0) { - self.show(); - } else { - self.hide(); + if (appliedTags.length !== 0) { + self.show() + } else { + self.hide() + } } - } - }); - } - - this.hide(); - }, - - /** - * Renders this details view - */ - render: function() { - var self = this; + }) + } - this.$el.append(this._inputView.$el); - this._inputView.render(); - }, + this.hide() + }, - isVisible: function() { - return !this.$el.hasClass('hidden'); - }, + /** + * Renders this details view + */ + render: function() { + this.$el.append(this._inputView.$el) + this._inputView.render() + }, - show: function() { - this.$el.removeClass('hidden'); - }, + isVisible: function() { + return !this.$el.hasClass('hidden') + }, - hide: function() { - this.$el.addClass('hidden'); - }, + show: function() { + this.$el.removeClass('hidden') + }, - openDropdown: function() { - this.$el.find('.systemTagsInputField').select2('open'); - }, + hide: function() { + this.$el.addClass('hidden') + }, - remove: function() { - this._inputView.remove(); - } - }); + openDropdown: function() { + this.$el.find('.systemTagsInputField').select2('open') + }, - OCA.SystemTags.SystemTagsInfoView = SystemTagsInfoView; + remove: function() { + this._inputView.remove() + } + }) -})(OCA); + OCA.SystemTags.SystemTagsInfoView = SystemTagsInfoView +})(OCA) diff --git a/apps/systemtags/src/systemtagsinfoviewtoggleview.js b/apps/systemtags/src/systemtagsinfoviewtoggleview.js index a3261e7994d22d5a9a3d5d5c8ce8fe517558afc4..1d10ebf67da5b07ab4975c7293155013fe9d67f4 100644 --- a/apps/systemtags/src/systemtagsinfoviewtoggleview.js +++ b/apps/systemtags/src/systemtagsinfoviewtoggleview.js @@ -32,66 +32,66 @@ var SystemTagsInfoViewToggleView = OC.Backbone.View.extend( /** @lends OC.Backbone.View.prototype */ { - tagName: 'span', + tagName: 'span', - className: 'tag-label', + className: 'tag-label', - events: { - 'click': 'click' - }, + events: { + 'click': 'click' + }, - /** - * @type OCA.SystemTags.SystemTagsInfoView - */ - _systemTagsInfoView: null, + /** + * @type OCA.SystemTags.SystemTagsInfoView + */ + _systemTagsInfoView: null, - template: function(data) { - return '<span class="icon icon-tag"/>' + t('systemtags', 'Tags'); - }, + template: function(data) { + return '<span class="icon icon-tag"/>' + t('systemtags', 'Tags') + }, - /** - * Initialize this toggle view. - * - * The options must provide a systemTagsInfoView parameter that - * references the SystemTagsInfoView to associate to this toggle view. - */ - initialize: function(options) { - var self = this; - options = options || {}; + /** + * Initialize this toggle view. + * + * The options must provide a systemTagsInfoView parameter that + * references the SystemTagsInfoView to associate to this toggle view. + * @param {Object} options options + */ + initialize: function(options) { + options = options || {} - this._systemTagsInfoView = options.systemTagsInfoView; - if (!this._systemTagsInfoView) { - throw 'Missing required parameter "systemTagsInfoView"'; - } - }, + this._systemTagsInfoView = options.systemTagsInfoView + if (!this._systemTagsInfoView) { + throw new Error('Missing required parameter "systemTagsInfoView"') + } + }, - /** + /** * Toggles the visibility of the associated SystemTagsInfoView. * * When the systemTagsInfoView is shown its dropdown is also opened. */ - click: function() { - if (this._systemTagsInfoView.isVisible()) { - this._systemTagsInfoView.hide(); - } else { - this._systemTagsInfoView.show(); - this._systemTagsInfoView.openDropdown(); - } - }, + click: function() { + if (this._systemTagsInfoView.isVisible()) { + this._systemTagsInfoView.hide() + } else { + this._systemTagsInfoView.show() + this._systemTagsInfoView.openDropdown() + } + }, - /** - * Renders this toggle view. - * - * @return OCA.SystemTags.SystemTagsInfoViewToggleView this object. - */ - render: function() { - this.$el.html(this.template()); + /** + * Renders this toggle view. + * + * @returns {OCA.SystemTags.SystemTagsInfoViewToggleView} this object. + */ + render: function() { + this.$el.html(this.template()) - return this; - }, + return this + } - }); + }) - OCA.SystemTags.SystemTagsInfoViewToggleView = SystemTagsInfoViewToggleView; + OCA.SystemTags.SystemTagsInfoViewToggleView = SystemTagsInfoViewToggleView -})(OCA); +})(OCA) diff --git a/apps/twofactor_backupcodes/js/settings.js b/apps/twofactor_backupcodes/js/settings.js index f0f996abefbe8461168c1c9021e4bb31dfa507ec..f0911d4bf360e2c0a9dedd8e6669757a18d495d1 100644 Binary files a/apps/twofactor_backupcodes/js/settings.js and b/apps/twofactor_backupcodes/js/settings.js differ diff --git a/apps/twofactor_backupcodes/js/settings.js.map b/apps/twofactor_backupcodes/js/settings.js.map index fe043a3714e05dca25dea0d417a5bef083205a9a..a7c128ed49be8cd703b82952cdf61bdb12473988 100644 Binary files a/apps/twofactor_backupcodes/js/settings.js.map and b/apps/twofactor_backupcodes/js/settings.js.map differ diff --git a/apps/twofactor_backupcodes/src/service/BackupCodesService.js b/apps/twofactor_backupcodes/src/service/BackupCodesService.js index a42b7b8b5ad08d6fef401653f66ad0fccccdbe40..0657fd8ca1ab7320668ca051b8ab30cfbaa6231c 100644 --- a/apps/twofactor_backupcodes/src/service/BackupCodesService.js +++ b/apps/twofactor_backupcodes/src/service/BackupCodesService.js @@ -1,7 +1,7 @@ import Axios from 'nextcloud-axios' -export function generateCodes () { - const url = OC.generateUrl('/apps/twofactor_backupcodes/settings/create'); +export function generateCodes() { + const url = OC.generateUrl('/apps/twofactor_backupcodes/settings/create') return Axios.post(url, {}).then(resp => resp.data) } diff --git a/apps/twofactor_backupcodes/src/service/PrintService.js b/apps/twofactor_backupcodes/src/service/PrintService.js index 7f4a0f46c59cb3d172187bdd25cb96c545102cbf..7383edfdf46b007e04983ad9312f5d7d1891b307 100644 --- a/apps/twofactor_backupcodes/src/service/PrintService.js +++ b/apps/twofactor_backupcodes/src/service/PrintService.js @@ -1,8 +1,8 @@ -export function print (data) { - const name = OC.theme.name || 'Nextcloud'; - const newTab = window.open('', t('twofactor_backupcodes', '{name} backup codes', {name: name})); - newTab.document.write('<h1>' + t('twofactor_backupcodes', '{name} backup codes', {name: name}) + '</h1>'); - newTab.document.write('<pre>' + data + '</pre>'); - newTab.print(); - newTab.close(); +export function print(data) { + const name = OC.theme.name || 'Nextcloud' + const newTab = window.open('', t('twofactor_backupcodes', '{name} backup codes', { name: name })) + newTab.document.write('<h1>' + t('twofactor_backupcodes', '{name} backup codes', { name: name }) + '</h1>') + newTab.document.write('<pre>' + data + '</pre>') + newTab.print() + newTab.close() } diff --git a/apps/twofactor_backupcodes/src/settings.js b/apps/twofactor_backupcodes/src/settings.js index 5a8251121da5e8c576e73729ac724f6ecee83e47..89a56ee2e5a810508e5737aacda45be68486c85c 100644 --- a/apps/twofactor_backupcodes/src/settings.js +++ b/apps/twofactor_backupcodes/src/settings.js @@ -1,10 +1,10 @@ -import Vue from 'vue'; -import PersonalSettings from './views/PersonalSettings'; -import store from './store'; +import Vue from 'vue' +import PersonalSettings from './views/PersonalSettings' +import store from './store' -Vue.prototype.t = t; +Vue.prototype.t = t -const initialState = OCP.InitialState.loadState('twofactor_backupcodes', 'state'); +const initialState = OCP.InitialState.loadState('twofactor_backupcodes', 'state') store.replaceState( initialState ) diff --git a/apps/twofactor_backupcodes/src/store.js b/apps/twofactor_backupcodes/src/store.js index 3af299ca619d4edf41b7db0eb70004ce97c80420..18f2d26bc18c69dd14d4da79259200a0fb9f9f23 100644 --- a/apps/twofactor_backupcodes/src/store.js +++ b/apps/twofactor_backupcodes/src/store.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Roeland Jago Douma <roeland@famdouma.nl> * * @author 2019 Roeland Jago Douma <roeland@famdouma.nl> @@ -11,7 +11,7 @@ * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of + * but WITHOUT ANY WARRANTY without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * @@ -21,12 +21,18 @@ import Vue from 'vue' import Vuex from 'vuex' - -import {generateCodes} from './service/BackupCodesService' +import { generateCodes } from './service/BackupCodesService' Vue.use(Vuex) -export const mutations = { +const state = { + enabled: false, + total: 0, + used: 0, + codes: [] +} + +const mutations = { setEnabled(state, enabled) { Vue.set(state, 'enabled', enabled) }, @@ -41,29 +47,23 @@ export const mutations = { } } -export const actions = { - generate ({commit}) { - commit('setEnabled', false); +const actions = { + generate({ commit }) { + commit('setEnabled', false) - return generateCodes() - .then(({codes, state}) => { - commit('setEnabled', state.enabled); - commit('setTotal', state.total); - commit('setUsed', state.used); - commit('setCodes', codes); - return true; - }); + return generateCodes().then(({ codes, state }) => { + commit('setEnabled', state.enabled) + commit('setTotal', state.total) + commit('setUsed', state.used) + commit('setCodes', codes) + return true + }) } } export default new Vuex.Store({ strict: process.env.NODE_ENV !== 'production', - state: { - enabled: false, - total: 0, - used: 0, - codes: undefined - }, + state, mutations, actions }) diff --git a/apps/twofactor_backupcodes/src/views/PersonalSettings.vue b/apps/twofactor_backupcodes/src/views/PersonalSettings.vue index 15e20395e1bc006919220f694efcfbd7f15c4fd9..d05e8b360afb377e67fa793014b00178d08d4db4 100644 --- a/apps/twofactor_backupcodes/src/views/PersonalSettings.vue +++ b/apps/twofactor_backupcodes/src/views/PersonalSettings.vue @@ -1,107 +1,120 @@ <template> <div> <button v-if="!enabled" - id="generate-backup-codes" - v-on:click="generateBackupCodes">{{ t('twofactor_backupcodes', 'Generate backup codes') }}</button> + id="generate-backup-codes" + @click="generateBackupCodes"> + {{ t('twofactor_backupcodes', 'Generate backup codes') }} + </button> <template v-else> <p> - <template v-if="!codes"> + <template v-if="!haveCodes"> {{ t('twofactor_backupcodes', 'Backup codes have been generated. {used} of {total} codes have been used.', {used, total}) }} </template> <template v-else> {{ t('twofactor_backupcodes', 'These are your backup codes. Please save and/or print them as you will not be able to read the codes again later') }} <ul> - <li v-for="code in codes" class="backup-code">{{code}}</li> + <li v-for="code in codes" :key="code" class="backup-code"> + {{ code }} + </li> </ul> <a :href="downloadUrl" - class="button primary" - :download="downloadFilename">{{ t('twofactor_backupcodes', 'Save backup codes') }}</a> + class="button primary" + :download="downloadFilename">{{ t('twofactor_backupcodes', 'Save backup codes') }}</a> <button class="button" - v-on:click="printCodes">{{ t('twofactor_backupcodes', 'Print backup codes') }}</button> + @click="printCodes"> + {{ t('twofactor_backupcodes', 'Print backup codes') }} + </button> </template> </p> <p> <button id="generate-backup-codes" - :class="{'icon-loading-small': generatingCodes}" - v-on:click="generateBackupCodes">{{ t('twofactor_backupcodes', 'Regenerate backup codes') }}</button> + :class="{'icon-loading-small': generatingCodes}" + @click="generateBackupCodes"> + {{ t('twofactor_backupcodes', 'Regenerate backup codes') }} + </button> + </p> + <p> + <em> + {{ t('twofactor_backupcodes', 'If you regenerate backup codes, you automatically invalidate old codes.') }} + </em> </p> - <p><em> - {{ t('twofactor_backupcodes', 'If you regenerate backup codes, you automatically invalidate old codes.') }} - </em></p> </template> </div> </template> <script> - import confirmPassword from 'nextcloud-password-confirmation'; - import {print} from '../service/PrintService'; +import confirmPassword from 'nextcloud-password-confirmation' +import { print } from '../service/PrintService' - export default { - name: "PersonalSettings", - data() { - return { - generatingCodes: false, - }; - }, - computed: { - downloadUrl: function() { - if (!this.codes) { - return ''; - } - return 'data:text/plain,' + encodeURIComponent(this.codes.reduce((prev, code) => { - return prev + code + '\r\n'; - }, '')); - }, - downloadFilename: function() { - const name = OC.theme.name || 'Nextcloud'; - return name + '-backup-codes.txt'; - }, - enabled: function() { - return this.$store.state.enabled - }, - total: function() { - return this.$store.state.total - }, - used: function() { - return this.$store.state.used - }, - codes: function() { - return this.$store.state.codes - }, - name: function() { - return OC.theme.name || 'Nextcloud' +export default { + name: 'PersonalSettings', + data() { + return { + generatingCodes: false + } + }, + computed: { + downloadUrl: function() { + if (!this.codes) { + return '' } + return 'data:text/plain,' + encodeURIComponent(this.codes.reduce((prev, code) => { + return prev + code + '\r\n' + }, '')) }, - methods: { - generateBackupCodes: function() { - confirmPassword().then(() => { - // Hide old codes - this.generatingCodes = true; - - this.$store.dispatch('generate').then(data => { - this.generatingCodes = false; - }).catch(err => { - OC.Notification.showTemporary(t('twofactor_backupcodes', 'An error occurred while generating your backup codes')); - this.generatingCodes = false; - throw err; - }); - }).catch(console.error.bind(this)); - }, + downloadFilename: function() { + const name = OC.theme.name || 'Nextcloud' + return name + '-backup-codes.txt' + }, + enabled: function() { + return this.$store.state.enabled + }, + total: function() { + return this.$store.state.total + }, + used: function() { + return this.$store.state.used + }, + codes: function() { + return this.$store.state.codes + }, + name: function() { + return OC.theme.name || 'Nextcloud' + }, + haveCodes() { + return this.codes && this.codes.length > 0 + } + }, + methods: { + generateBackupCodes: function() { + confirmPassword().then(() => { + // Hide old codes + this.generatingCodes = true - getPrintData: function(codes) { - if (!codes) { - return ''; - } - return codes.reduce((prev, code) => { - return prev + code + "<br>"; - }, ''); - }, + this.$store.dispatch('generate').then(data => { + this.generatingCodes = false + }).catch(err => { + OC.Notification.showTemporary(t('twofactor_backupcodes', 'An error occurred while generating your backup codes')) + this.generatingCodes = false + throw err + }) + }).catch(console.error.bind(this)) + }, - printCodes: function() { - print(this.getPrintData(this.codes)); + getPrintData: function(codes) { + if (!codes) { + return '' } + return codes.reduce((prev, code) => { + return prev + code + '<br>' + }, '') + }, + + printCodes: function() { + print(this.getPrintData(this.codes)) } } +} </script> <style scoped> diff --git a/apps/updatenotification/js/updatenotification.js b/apps/updatenotification/js/updatenotification.js index cf198d8bc993ef6b87b53de427dd37b7631e322d..f2ea74f6836ca1cb2c2c5233698750f12693be8b 100644 Binary files a/apps/updatenotification/js/updatenotification.js and b/apps/updatenotification/js/updatenotification.js differ diff --git a/apps/updatenotification/js/updatenotification.js.map b/apps/updatenotification/js/updatenotification.js.map index 24def4b193a776d8855fbe63b9d9502a8383fa36..053fc098a1d01c3fb917afb6366fbdc067f0ff32 100644 Binary files a/apps/updatenotification/js/updatenotification.js.map and b/apps/updatenotification/js/updatenotification.js.map differ diff --git a/apps/updatenotification/src/components/UpdateNotification.vue b/apps/updatenotification/src/components/UpdateNotification.vue new file mode 100644 index 0000000000000000000000000000000000000000..6d726b46d491e87a2fb8411a39d0c59d86459709 --- /dev/null +++ b/apps/updatenotification/src/components/UpdateNotification.vue @@ -0,0 +1,520 @@ +<template> + <div id="updatenotification" class="followupsection"> + <div class="update"> + <template v-if="isNewVersionAvailable"> + <p v-if="versionIsEol"> + <span class="warning"> + <span class="icon icon-error-white" /> + {{ t('updatenotification', 'The version you are running is not maintained anymore. Please make sure to update to a supported version as soon as possible.') }} + </span> + </p> + + <p> + <span v-html="newVersionAvailableString" /><br> + <span v-if="!isListFetched" class="icon icon-loading-small" /> + <span v-html="statusText" /> + </p> + + <template v-if="missingAppUpdates.length"> + <h3 @click="toggleHideMissingUpdates"> + {{ t('updatenotification', 'Apps missing updates') }} + <span v-if="!hideMissingUpdates" class="icon icon-triangle-n" /> + <span v-if="hideMissingUpdates" class="icon icon-triangle-s" /> + </h3> + <ul v-if="!hideMissingUpdates" class="applist"> + <li v-for="(app, index) in missingAppUpdates" :key="index"> + <a :href="'https://apps.nextcloud.com/apps/' + app.appId" :title="t('settings', 'View in store')">{{ app.appName }} ↗</a> + </li> + </ul> + </template> + + <template v-if="availableAppUpdates.length"> + <h3 @click="toggleHideAvailableUpdates"> + {{ t('updatenotification', 'Apps with available updates') }} + <span v-if="!hideAvailableUpdates" class="icon icon-triangle-n" /> + <span v-if="hideAvailableUpdates" class="icon icon-triangle-s" /> + </h3> + <ul v-if="!hideAvailableUpdates" class="applist"> + <li v-for="(app, index) in availableAppUpdates" :key="index"> + <a :href="'https://apps.nextcloud.com/apps/' + app.appId" :title="t('settings', 'View in store')">{{ app.appName }} ↗</a> + </li> + </ul> + </template> + + <div> + <a v-if="updaterEnabled" + href="#" + class="button primary" + @click="clickUpdaterButton">{{ t('updatenotification', 'Open updater') }}</a> + <a v-if="downloadLink" + :href="downloadLink" + class="button" + :class="{ hidden: !updaterEnabled }">{{ t('updatenotification', 'Download now') }}</a> + <div v-if="whatsNew" class="whatsNew"> + <div class="toggleWhatsNew"> + <a v-click-outside="hideMenu" class="button" @click="toggleMenu">{{ t('updatenotification', 'What\'s new?') }}</a> + <div class="popovermenu" :class="{ 'menu-center': true, open: openedWhatsNew }"> + <PopoverMenu :menu="whatsNew" /> + </div> + </div> + </div> + </div> + </template> + <template v-else-if="!isUpdateChecked"> + {{ t('updatenotification', 'The update check is not yet finished. Please refresh the page.') }} + </template> + <template v-else> + {{ t('updatenotification', 'Your version is up to date.') }} + <span v-tooltip.auto="lastCheckedOnString" class="icon-info svg" /> + </template> + + <template v-if="!isDefaultUpdateServerURL"> + <p class="topMargin"> + <em>{{ t('updatenotification', 'A non-default update server is in use to be checked for updates:') }} <code>{{ updateServerURL }}</code></em> + </p> + </template> + </div> + + <h3 class="update-channel-selector"> + {{ t('updatenotification', 'Update channel:') }} + <div class="update-menu"> + <span class="icon-update-menu" @click="toggleUpdateChannelMenu"> + {{ localizedChannelName }} + <span class="icon-triangle-s" /> + </span> + <div class="popovermenu menu menu-center" :class="{ 'show-menu': openedUpdateChannelMenu}"> + <PopoverMenu :menu="channelList" /> + </div> + </div> + </h3> + <span id="channel_save_msg" class="msg" /><br> + <p> + <em>{{ t('updatenotification', 'You can always update to a newer version. But you can never downgrade to a more stable version.') }}</em><br> + <em>{{ t('updatenotification', 'Note that after a new release it can take some time before it shows up here. We roll out new versions spread out over time to our users and sometimes skip a version when issues are found.') }}</em> + </p> + + <p id="oca_updatenotification_groups"> + {{ t('updatenotification', 'Notify members of the following groups about available updates:') }} + <Multiselect v-model="notifyGroups" + :options="availableGroups" + :multiple="true" + label="label" + track-by="value" + :tag-width="75" /><br> + <em v-if="currentChannel === 'daily' || currentChannel === 'git'">{{ t('updatenotification', 'Only notification for app updates are available.') }}</em> + <em v-if="currentChannel === 'daily'">{{ t('updatenotification', 'The selected update channel makes dedicated notifications for the server obsolete.') }}</em> + <em v-if="currentChannel === 'git'">{{ t('updatenotification', 'The selected update channel does not support updates of the server.') }}</em> + </p> + </div> +</template> + +<script> +import { PopoverMenu, Multiselect } from 'nextcloud-vue' +import { VTooltip } from 'v-tooltip' +import ClickOutside from 'vue-click-outside' + +VTooltip.options.defaultHtml = false + +export default { + name: 'UpdateNotification', + components: { + Multiselect, + PopoverMenu + }, + directives: { + ClickOutside, + tooltip: VTooltip + }, + data: function() { + return { + newVersionString: '', + lastCheckedDate: '', + isUpdateChecked: false, + updaterEnabled: true, + versionIsEol: false, + downloadLink: '', + isNewVersionAvailable: false, + hasValidSubscription: false, + updateServerURL: '', + changelogURL: '', + whatsNewData: [], + currentChannel: '', + channels: [], + notifyGroups: '', + availableGroups: [], + isDefaultUpdateServerURL: true, + enableChangeWatcher: false, + + availableAppUpdates: [], + missingAppUpdates: [], + appStoreFailed: false, + appStoreDisabled: false, + isListFetched: false, + hideMissingUpdates: false, + hideAvailableUpdates: true, + openedWhatsNew: false, + openedUpdateChannelMenu: false + } + }, + + _$el: null, + _$notifyGroups: null, + + computed: { + newVersionAvailableString: function() { + return t('updatenotification', 'A new version is available: <strong>{newVersionString}</strong>', { + newVersionString: this.newVersionString + }) + }, + + lastCheckedOnString: function() { + return t('updatenotification', 'Checked on {lastCheckedDate}', { + lastCheckedDate: this.lastCheckedDate + }) + }, + + statusText: function() { + if (!this.isListFetched) { + return t('updatenotification', 'Checking apps for compatible updates') + } + + if (this.appStoreDisabled) { + return t('updatenotification', 'Please make sure your config.php does not set <samp>appstoreenabled</samp> to false.') + } + + if (this.appStoreFailed) { + return t('updatenotification', 'Could not connect to the appstore or the appstore returned no updates at all. Search manually for updates or make sure your server has access to the internet and can connect to the appstore.') + } + + return this.missingAppUpdates.length === 0 + ? t('updatenotification', '<strong>All</strong> apps have an update for this version available', this) + : n('updatenotification', '<strong>%n</strong> app has no update for this version available', '<strong>%n</strong> apps have no update for this version available', this.missingAppUpdates.length) + }, + + whatsNew: function() { + if (this.whatsNewData.length === 0) { + return null + } + var whatsNew = [] + for (var i in this.whatsNewData) { + whatsNew[i] = { icon: 'icon-checkmark', longtext: this.whatsNewData[i] } + } + if (this.changelogURL) { + whatsNew.push({ + href: this.changelogURL, + text: t('updatenotification', 'View changelog'), + icon: 'icon-link', + target: '_blank', + action: '' + }) + } + return whatsNew + }, + + channelList: function() { + let channelList = [] + + channelList.push({ + text: t('updatenotification', 'Enterprise'), + longtext: t('updatenotification', 'For enterprise use. Provides always the latest patch level, but will not update to the next major release immediately. That update happens once Nextcloud GmbH has done additional hardening and testing for large-scale and mission-critical deployments. This channel is only available to customers and provides the Nextcloud Enterprise package.'), + icon: 'icon-star', + active: this.currentChannel === 'enterprise', + disabled: !this.hasValidSubscription, + action: this.changeReleaseChannelToEnterprise + }) + + channelList.push({ + text: t('updatenotification', 'Stable'), + longtext: t('updatenotification', 'The most recent stable version. It is suited for regular use and will always update to the latest major version.'), + icon: 'icon-checkmark', + active: this.currentChannel === 'stable', + action: this.changeReleaseChannelToStable + }) + + channelList.push({ + text: t('updatenotification', 'Beta'), + longtext: t('updatenotification', 'A pre-release version only for testing new features, not for production environments.'), + icon: 'icon-category-customization', + active: this.currentChannel === 'beta', + action: this.changeReleaseChannelToBeta + }) + + if (this.isNonDefaultChannel) { + channelList.push({ + text: this.currentChannel, + icon: 'icon-rename', + active: true + }) + } + + return channelList + }, + + isNonDefaultChannel: function() { + return this.currentChannel !== 'enterprise' && this.currentChannel !== 'stable' && this.currentChannel !== 'beta' + }, + + localizedChannelName: function() { + switch (this.currentChannel) { + case 'enterprise': + return t('updatenotification', 'Enterprise') + case 'stable': + return t('updatenotification', 'Stable') + case 'beta': + return t('updatenotification', 'Beta') + default: + return this.currentChannel + } + } + }, + + watch: { + notifyGroups: function(selectedOptions) { + if (!this.enableChangeWatcher) { + return + } + + var selectedGroups = [] + _.each(selectedOptions, function(group) { + selectedGroups.push(group.value) + }) + + OCP.AppConfig.setValue('updatenotification', 'notify_groups', JSON.stringify(selectedGroups)) + }, + isNewVersionAvailable: function() { + if (!this.isNewVersionAvailable) { + return + } + + $.ajax({ + url: OC.linkToOCS('apps/updatenotification/api/v1/applist', 2) + this.newVersion, + type: 'GET', + beforeSend: function(request) { + request.setRequestHeader('Accept', 'application/json') + }, + success: function(response) { + this.availableAppUpdates = response.ocs.data.available + this.missingAppUpdates = response.ocs.data.missing + this.isListFetched = true + this.appStoreFailed = false + }.bind(this), + error: function(xhr) { + this.availableAppUpdates = [] + this.missingAppUpdates = [] + this.appStoreDisabled = xhr.responseJSON.ocs.data.appstore_disabled + this.isListFetched = true + this.appStoreFailed = true + }.bind(this) + }) + } + }, + beforeMount: function() { + // Parse server data + var data = JSON.parse($('#updatenotification').attr('data-json')) + + this.newVersion = data.newVersion + this.newVersionString = data.newVersionString + this.lastCheckedDate = data.lastChecked + this.isUpdateChecked = data.isUpdateChecked + this.updaterEnabled = data.updaterEnabled + this.downloadLink = data.downloadLink + this.isNewVersionAvailable = data.isNewVersionAvailable + this.updateServerURL = data.updateServerURL + this.currentChannel = data.currentChannel + this.channels = data.channels + this.notifyGroups = data.notifyGroups + this.isDefaultUpdateServerURL = data.isDefaultUpdateServerURL + this.versionIsEol = data.versionIsEol + this.hasValidSubscription = data.hasValidSubscription + if (data.changes && data.changes.changelogURL) { + this.changelogURL = data.changes.changelogURL + } + if (data.changes && data.changes.whatsNew) { + if (data.changes.whatsNew.admin) { + this.whatsNewData = this.whatsNewData.concat(data.changes.whatsNew.admin) + } + this.whatsNewData = this.whatsNewData.concat(data.changes.whatsNew.regular) + } + }, + mounted: function() { + this._$el = $(this.$el) + this._$notifyGroups = this._$el.find('#oca_updatenotification_groups_list') + this._$notifyGroups.on('change', function() { + this.$emit('input') + }.bind(this)) + + $.ajax({ + url: OC.linkToOCS('cloud', 2) + '/groups', + dataType: 'json', + success: function(data) { + var results = [] + $.each(data.ocs.data.groups, function(i, group) { + results.push({ value: group, label: group }) + }) + + this.availableGroups = results + this.enableChangeWatcher = true + }.bind(this) + }) + }, + + methods: { + /** + * Creates a new authentication token and loads the updater URL + */ + clickUpdaterButton: function() { + $.ajax({ + url: OC.generateUrl('/apps/updatenotification/credentials') + }).success(function(token) { + // create a form to send a proper post request to the updater + var form = document.createElement('form') + form.setAttribute('method', 'post') + form.setAttribute('action', OC.getRootPath() + '/updater/') + + var hiddenField = document.createElement('input') + hiddenField.setAttribute('type', 'hidden') + hiddenField.setAttribute('name', 'updater-secret-input') + hiddenField.setAttribute('value', token) + + form.appendChild(hiddenField) + + document.body.appendChild(form) + form.submit() + }) + }, + changeReleaseChannelToEnterprise: function() { + this.changeReleaseChannel('enterprise') + }, + changeReleaseChannelToStable: function() { + this.changeReleaseChannel('stable') + }, + changeReleaseChannelToBeta: function() { + this.changeReleaseChannel('beta') + }, + changeReleaseChannel: function(channel) { + this.currentChannel = channel + + $.ajax({ + url: OC.generateUrl('/apps/updatenotification/channel'), + type: 'POST', + data: { + 'channel': this.currentChannel + }, + success: function(data) { + OC.msg.finishedAction('#channel_save_msg', data) + } + }) + + this.openedUpdateChannelMenu = false + }, + toggleUpdateChannelMenu: function() { + this.openedUpdateChannelMenu = !this.openedUpdateChannelMenu + }, + toggleHideMissingUpdates: function() { + this.hideMissingUpdates = !this.hideMissingUpdates + }, + toggleHideAvailableUpdates: function() { + this.hideAvailableUpdates = !this.hideAvailableUpdates + }, + toggleMenu: function() { + this.openedWhatsNew = !this.openedWhatsNew + }, + hideMenu: function() { + this.openedWhatsNew = false + } + } +} +</script> + +<style lang="scss" scoped> + #updatenotification { + margin-top: -25px; + margin-bottom: 200px; + div.update, + p:not(.inlineblock) { + margin-bottom: 25px; + } + h2.inlineblock { + margin-top: 25px; + } + h3 { + cursor: pointer; + .icon { + cursor: pointer; + } + &:first-of-type { + margin-top: 0; + } + &.update-channel-selector { + display: inline-block; + cursor: inherit; + } + } + .icon { + display: inline-block; + margin-bottom: -3px; + } + .icon-triangle-s, .icon-triangle-n { + opacity: 0.5; + } + .whatsNew { + display: inline-block; + } + .toggleWhatsNew { + position: relative; + } + .popovermenu { + p { + margin-bottom: 0; + width: 100%; + } + margin-top: 5px; + width: 300px; + } + .applist { + margin-bottom: 25px; + } + + .update-menu { + position: relative; + cursor: pointer; + margin-left: 3px; + display: inline-block; + .icon-update-menu { + cursor: inherit; + .icon-triangle-s { + display: inline-block; + vertical-align: middle; + cursor: inherit; + opacity: 1; + } + } + .popovermenu { + display: none; + top: 28px; + &.show-menu { + display: block; + } + } + } + } +</style> +<style lang="scss"> + /* override needed to make menu wider */ + #updatenotification .popovermenu { + p { + margin-top: 5px; + width: 100%; + } + margin-top: 5px; + width: 300px; + } + /* override needed to replace yellow hover state with a dark one */ + #updatenotification .update-menu .icon-star:hover, + #updatenotification .update-menu .icon-star:focus { + background-image: var(--icon-star-000); + } + #updatenotification .topMargin { + margin-top: 15px; + } +</style> diff --git a/apps/updatenotification/src/components/root.vue b/apps/updatenotification/src/components/root.vue deleted file mode 100644 index 0f6a427bb3393934e948f9beb229c7300d89dc92..0000000000000000000000000000000000000000 --- a/apps/updatenotification/src/components/root.vue +++ /dev/null @@ -1,508 +0,0 @@ -<template> - <div id="updatenotification" class="followupsection"> - <div class="update"> - <template v-if="isNewVersionAvailable"> - <p v-if="versionIsEol"> - <span class="warning"> - <span class="icon icon-error-white"></span> - {{ t('updatenotification', 'The version you are running is not maintained anymore. Please make sure to update to a supported version as soon as possible.') }} - </span> - </p> - - <p> - <span v-html="newVersionAvailableString"></span><br> - <span v-if="!isListFetched" class="icon icon-loading-small"></span> - <span v-html="statusText"></span> - </p> - - <template v-if="missingAppUpdates.length"> - <h3 @click="toggleHideMissingUpdates"> - {{ t('updatenotification', 'Apps missing updates') }} - <span v-if="!hideMissingUpdates" class="icon icon-triangle-n"></span> - <span v-if="hideMissingUpdates" class="icon icon-triangle-s"></span> - </h3> - <ul class="applist" v-if="!hideMissingUpdates"> - <li v-for="app in missingAppUpdates"><a :href="'https://apps.nextcloud.com/apps/' + app.appId" :title="t('settings', 'View in store')">{{app.appName}} ↗</a></li> - </ul> - </template> - - <template v-if="availableAppUpdates.length"> - <h3 @click="toggleHideAvailableUpdates"> - {{ t('updatenotification', 'Apps with available updates') }} - <span v-if="!hideAvailableUpdates" class="icon icon-triangle-n"></span> - <span v-if="hideAvailableUpdates" class="icon icon-triangle-s"></span> - </h3> - <ul class="applist"> - <li v-for="app in availableAppUpdates" v-if="!hideAvailableUpdates"><a :href="'https://apps.nextcloud.com/apps/' + app.appId" :title="t('settings', 'View in store')">{{app.appName}} ↗</a></li> - </ul> - </template> - - <div> - <a v-if="updaterEnabled" href="#" class="button primary" @click="clickUpdaterButton">{{ t('updatenotification', 'Open updater') }}</a> - <a v-if="downloadLink" :href="downloadLink" class="button" :class="{ hidden: !updaterEnabled }">{{ t('updatenotification', 'Download now') }}</a> - <div class="whatsNew" v-if="whatsNew"> - <div class="toggleWhatsNew"> - <a class="button" v-click-outside="hideMenu" @click="toggleMenu">{{ t('updatenotification', 'What\'s new?') }}</a> - <div class="popovermenu" :class="{ 'menu-center': true, open: openedWhatsNew }"> - <popover-menu :menu="whatsNew" /> - </div> - </div> - </div> - </div> - </template> - <template v-else-if="!isUpdateChecked">{{ t('updatenotification', 'The update check is not yet finished. Please refresh the page.') }}</template> - <template v-else> - {{ t('updatenotification', 'Your version is up to date.') }} - <span class="icon-info svg" v-tooltip.auto="lastCheckedOnString"></span> - </template> - - <template v-if="!isDefaultUpdateServerURL"> - <p class="topMargin"> - <em>{{ t('updatenotification', 'A non-default update server is in use to be checked for updates:') }} <code>{{updateServerURL}}</code></em> - </p> - </template> - </div> - - <h3 class="update-channel-selector"> - {{ t('updatenotification', 'Update channel:') }} - <div class="update-menu"> - <span class="icon-update-menu" @click="toggleUpdateChannelMenu"> - {{ localizedChannelName }} - <span class="icon-triangle-s"></span> - </span> - <div class="popovermenu menu menu-center" v-bind:class="{ 'show-menu': openedUpdateChannelMenu}"> - <popover-menu :menu="channelList" /> - </div> - </div> - </h3> - <span id="channel_save_msg" class="msg"></span><br /> - <p> - <em>{{ t('updatenotification', 'You can always update to a newer version. But you can never downgrade to a more stable version.') }}</em><br /> - <em>{{ t('updatenotification', 'Note that after a new release it can take some time before it shows up here. We roll out new versions spread out over time to our users and sometimes skip a version when issues are found.') }}</em> - </p> - - <p id="oca_updatenotification_groups"> - {{ t('updatenotification', 'Notify members of the following groups about available updates:') }} - <multiselect v-model="notifyGroups" :options="availableGroups" :multiple="true" label="label" track-by="value" :tag-width="75" /><br /> - <em v-if="currentChannel === 'daily' || currentChannel === 'git'">{{ t('updatenotification', 'Only notification for app updates are available.') }}</em> - <em v-if="currentChannel === 'daily'">{{ t('updatenotification', 'The selected update channel makes dedicated notifications for the server obsolete.') }}</em> - <em v-if="currentChannel === 'git'">{{ t('updatenotification', 'The selected update channel does not support updates of the server.') }}</em> - </p> - </div> -</template> - -<script> - import { PopoverMenu, Multiselect } from 'nextcloud-vue'; - import { VTooltip } from 'v-tooltip'; - import ClickOutside from 'vue-click-outside'; - - VTooltip.options.defaultHtml = false - - export default { - name: 'root', - components: { - Multiselect, - PopoverMenu, - }, - directives: { - ClickOutside, - tooltip: VTooltip - }, - data: function () { - return { - newVersionString: '', - lastCheckedDate: '', - isUpdateChecked: false, - updaterEnabled: true, - versionIsEol: false, - downloadLink: '', - isNewVersionAvailable: false, - hasValidSubscription: false, - updateServerURL: '', - changelogURL: '', - whatsNewData: [], - currentChannel: '', - channels: [], - notifyGroups: '', - availableGroups: [], - isDefaultUpdateServerURL: true, - enableChangeWatcher: false, - - availableAppUpdates: [], - missingAppUpdates: [], - appStoreFailed: false, - appStoreDisabled: false, - isListFetched: false, - hideMissingUpdates: false, - hideAvailableUpdates: true, - openedWhatsNew: false, - openedUpdateChannelMenu: false, - }; - }, - - _$el: null, - _$notifyGroups: null, - - watch: { - notifyGroups: function(selectedOptions) { - if (!this.enableChangeWatcher) { - return; - } - - var selectedGroups = []; - _.each(selectedOptions, function(group) { - selectedGroups.push(group.value); - }); - - OCP.AppConfig.setValue('updatenotification', 'notify_groups', JSON.stringify(selectedGroups)); - }, - isNewVersionAvailable: function() { - if (!this.isNewVersionAvailable) { - return; - } - - $.ajax({ - url: OC.linkToOCS('apps/updatenotification/api/v1/applist', 2) + this.newVersion, - type: 'GET', - beforeSend: function (request) { - request.setRequestHeader('Accept', 'application/json'); - }, - success: function(response) { - this.availableAppUpdates = response.ocs.data.available; - this.missingAppUpdates = response.ocs.data.missing; - this.isListFetched = true; - this.appStoreFailed = false; - }.bind(this), - error: function(xhr) { - this.availableAppUpdates = []; - this.missingAppUpdates = []; - this.appStoreDisabled = xhr.responseJSON.ocs.data.appstore_disabled; - this.isListFetched = true; - this.appStoreFailed = true; - }.bind(this) - }); - } - }, - - computed: { - newVersionAvailableString: function() { - return t('updatenotification', 'A new version is available: <strong>{newVersionString}</strong>', { - newVersionString: this.newVersionString - }); - }, - - lastCheckedOnString: function() { - return t('updatenotification', 'Checked on {lastCheckedDate}', { - lastCheckedDate: this.lastCheckedDate - }); - }, - - statusText: function() { - if (!this.isListFetched) { - return t('updatenotification', 'Checking apps for compatible updates'); - } - - if (this.appStoreDisabled) { - return t('updatenotification', 'Please make sure your config.php does not set <samp>appstoreenabled</samp> to false.'); - } - - if (this.appStoreFailed) { - return t('updatenotification', 'Could not connect to the appstore or the appstore returned no updates at all. Search manually for updates or make sure your server has access to the internet and can connect to the appstore.'); - } - - return this.missingAppUpdates.length === 0 ? t('updatenotification', '<strong>All</strong> apps have an update for this version available', this) : n('updatenotification', - '<strong>%n</strong> app has no update for this version available', - '<strong>%n</strong> apps have no update for this version available', - this.missingAppUpdates.length); - }, - - whatsNew: function() { - if(this.whatsNewData.length === 0) { - return null; - } - var whatsNew = []; - for (var i in this.whatsNewData) { - whatsNew[i] = { icon: 'icon-checkmark', longtext: this.whatsNewData[i] }; - } - if(this.changelogURL) { - whatsNew.push({ - href: this.changelogURL, - text: t('updatenotification', 'View changelog'), - icon: 'icon-link', - target: '_blank', - action: '' - }); - } - return whatsNew; - }, - - channelList: function() { - let channelList = []; - - channelList.push({ - text: t('updatenotification', 'Enterprise'), - longtext: t('updatenotification', 'For enterprise use. Provides always the latest patch level, but will not update to the next major release immediately. That update happens once Nextcloud GmbH has done additional hardening and testing for large-scale and mission-critical deployments. This channel is only available to customers and provides the Nextcloud Enterprise package.'), - icon: 'icon-star', - active: this.currentChannel === 'enterprise', - disabled: !this.hasValidSubscription, - action: this.changeReleaseChannelToEnterprise - }); - - channelList.push({ - text: t('updatenotification', 'Stable'), - longtext: t('updatenotification', 'The most recent stable version. It is suited for regular use and will always update to the latest major version.'), - icon: 'icon-checkmark', - active: this.currentChannel === 'stable', - action: this.changeReleaseChannelToStable - }); - - channelList.push({ - text: t('updatenotification', 'Beta'), - longtext: t('updatenotification', 'A pre-release version only for testing new features, not for production environments.'), - icon: 'icon-category-customization', - active: this.currentChannel === 'beta', - action: this.changeReleaseChannelToBeta - }); - - if (this.isNonDefaultChannel) { - channelList.push({ - text: this.currentChannel, - icon: 'icon-rename', - active: true - }); - } - - return channelList; - }, - - isNonDefaultChannel: function() { - return this.currentChannel !== 'enterprise' && this.currentChannel !== 'stable' && this.currentChannel !== 'beta'; - }, - - localizedChannelName: function() { - switch (this.currentChannel) { - case 'enterprise': - return t('updatenotification', 'Enterprise'); - break; - case 'stable': - return t('updatenotification', 'Stable'); - break; - case 'beta': - return t('updatenotification', 'Beta'); - break; - default: - return this.currentChannel; - break; - } - } - }, - - methods: { - /** - * Creates a new authentication token and loads the updater URL - */ - clickUpdaterButton: function() { - $.ajax({ - url: OC.generateUrl('/apps/updatenotification/credentials') - }).success(function(token) { - // create a form to send a proper post request to the updater - var form = document.createElement('form'); - form.setAttribute('method', 'post'); - form.setAttribute('action', OC.getRootPath() + '/updater/'); - - var hiddenField = document.createElement('input'); - hiddenField.setAttribute('type', 'hidden'); - hiddenField.setAttribute('name', 'updater-secret-input'); - hiddenField.setAttribute('value', token); - - form.appendChild(hiddenField); - - document.body.appendChild(form); - form.submit(); - }.bind(this)); - }, - changeReleaseChannelToEnterprise: function() { - this.changeReleaseChannel('enterprise') - }, - changeReleaseChannelToStable: function() { - this.changeReleaseChannel('stable') - }, - changeReleaseChannelToBeta: function() { - this.changeReleaseChannel('beta') - }, - changeReleaseChannel: function(channel) { - this.currentChannel = channel; - - $.ajax({ - url: OC.generateUrl('/apps/updatenotification/channel'), - type: 'POST', - data: { - 'channel': this.currentChannel - }, - success: function (data) { - OC.msg.finishedAction('#channel_save_msg', data); - } - }); - - this.openedUpdateChannelMenu = false; - }, - toggleUpdateChannelMenu: function() { - this.openedUpdateChannelMenu = !this.openedUpdateChannelMenu; - }, - toggleHideMissingUpdates: function() { - this.hideMissingUpdates = !this.hideMissingUpdates; - }, - toggleHideAvailableUpdates: function() { - this.hideAvailableUpdates = !this.hideAvailableUpdates; - }, - toggleMenu: function() { - this.openedWhatsNew = !this.openedWhatsNew; - }, - hideMenu: function() { - this.openedWhatsNew = false; - }, - }, - beforeMount: function() { - // Parse server data - var data = JSON.parse($('#updatenotification').attr('data-json')); - - this.newVersion = data.newVersion; - this.newVersionString = data.newVersionString; - this.lastCheckedDate = data.lastChecked; - this.isUpdateChecked = data.isUpdateChecked; - this.updaterEnabled = data.updaterEnabled; - this.downloadLink = data.downloadLink; - this.isNewVersionAvailable = data.isNewVersionAvailable; - this.updateServerURL = data.updateServerURL; - this.currentChannel = data.currentChannel; - this.channels = data.channels; - this.notifyGroups = data.notifyGroups; - this.isDefaultUpdateServerURL = data.isDefaultUpdateServerURL; - this.versionIsEol = data.versionIsEol; - this.hasValidSubscription = data.hasValidSubscription; - if(data.changes && data.changes.changelogURL) { - this.changelogURL = data.changes.changelogURL; - } - if(data.changes && data.changes.whatsNew) { - if(data.changes.whatsNew.admin) { - this.whatsNewData = this.whatsNewData.concat(data.changes.whatsNew.admin); - } - this.whatsNewData = this.whatsNewData.concat(data.changes.whatsNew.regular); - } - }, - mounted: function () { - this._$el = $(this.$el); - this._$notifyGroups = this._$el.find('#oca_updatenotification_groups_list'); - this._$notifyGroups.on('change', function () { - this.$emit('input'); - }.bind(this)); - - $.ajax({ - url: OC.linkToOCS('cloud', 2)+ '/groups', - dataType: 'json', - success: function(data) { - var results = []; - $.each(data.ocs.data.groups, function(i, group) { - results.push({value: group, label: group}); - }); - - this.availableGroups = results; - this.enableChangeWatcher = true; - }.bind(this) - }); - } - } -</script> - -<style lang="scss" scoped> - #updatenotification { - margin-top: -25px; - margin-bottom: 200px; - div.update, - p:not(.inlineblock) { - margin-bottom: 25px; - } - h2.inlineblock { - margin-top: 25px; - } - h3 { - cursor: pointer; - .icon { - cursor: pointer; - } - &:first-of-type { - margin-top: 0; - } - &.update-channel-selector { - display: inline-block; - cursor: inherit; - } - } - .icon { - display: inline-block; - margin-bottom: -3px; - } - .icon-triangle-s, .icon-triangle-n { - opacity: 0.5; - } - .whatsNew { - display: inline-block; - } - .toggleWhatsNew { - position: relative; - } - .popovermenu { - p { - margin-bottom: 0; - width: 100%; - } - margin-top: 5px; - width: 300px; - } - .applist { - margin-bottom: 25px; - } - - .update-menu { - position: relative; - cursor: pointer; - margin-left: 3px; - display: inline-block; - .icon-update-menu { - cursor: inherit; - .icon-triangle-s { - display: inline-block; - vertical-align: middle; - cursor: inherit; - opacity: 1; - } - } - .popovermenu { - display: none; - top: 28px; - &.show-menu { - display: block; - } - } - } - } -</style> -<style lang="scss"> - /* override needed to make menu wider */ - #updatenotification .popovermenu { - p { - margin-top: 5px; - width: 100%; - } - margin-top: 5px; - width: 300px; - } - /* override needed to replace yellow hover state with a dark one */ - #updatenotification .update-menu .icon-star:hover, - #updatenotification .update-menu .icon-star:focus { - background-image: var(--icon-star-000); - } - #updatenotification .topMargin { - margin-top: 15px; - } -</style> diff --git a/apps/updatenotification/src/init.js b/apps/updatenotification/src/init.js index d874cb67f33ecaadb58821d9813fac045b917965..19782799db2d291f3d2a676ab0dbcbfe131e6b6b 100644 --- a/apps/updatenotification/src/init.js +++ b/apps/updatenotification/src/init.js @@ -18,23 +18,22 @@ * */ -/* global define, $ */ -import Vue from 'vue'; -import Root from './components/root' +import Vue from 'vue' +import Root from './components/UpdateNotification' Vue.mixin({ methods: { t: function(app, text, vars, count, options) { - return OC.L10N.translate(app, text, vars, count, options); + return OC.L10N.translate(app, text, vars, count, options) }, n: function(app, textSingular, textPlural, count, vars, options) { - return OC.L10N.translatePlural(app, textSingular, textPlural, count, vars, options); + return OC.L10N.translatePlural(app, textSingular, textPlural, count, vars, options) } } -}); +}) -const vm = new Vue({ +// eslint-disable-next-line no-new +new Vue({ + el: '#updatenotification', render: h => h(Root) -}).$mount('#updatenotification'); - - +}) diff --git a/apps/user_ldap/js/wizard/wizardTabGeneric.js b/apps/user_ldap/js/wizard/wizardTabGeneric.js index 7b0df4992d63d700a8768b3e0c83b7552ac6ee1a..f24c520c1f9ed925231cc225eabf99ce29e3d0eb 100644 --- a/apps/user_ldap/js/wizard/wizardTabGeneric.js +++ b/apps/user_ldap/js/wizard/wizardTabGeneric.js @@ -392,9 +392,9 @@ OCA = OCA || {}; /** * @typedef {object} viewSaveInfo - * @property {function} val - * @property {function} attr - * @property {function} is + * @property {Function} val + * @property {Function} attr + * @property {Function} is */ /** diff --git a/apps/workflowengine/js/workflowengine.js b/apps/workflowengine/js/workflowengine.js index 2143a2afe61fbe2d3b63c9f2301c00eed9826773..ff40c801185c30194b116d2cb74c172ffbd55a8c 100644 Binary files a/apps/workflowengine/js/workflowengine.js and b/apps/workflowengine/js/workflowengine.js differ diff --git a/apps/workflowengine/js/workflowengine.js.map b/apps/workflowengine/js/workflowengine.js.map index 16c62805db25082ee686ec1933ee176f7ab02d3b..db7d112b8db7d8158f2e150d7f42c0ac54e29a3a 100644 Binary files a/apps/workflowengine/js/workflowengine.js.map and b/apps/workflowengine/js/workflowengine.js.map differ diff --git a/apps/workflowengine/src/components/Check.vue b/apps/workflowengine/src/components/Check.vue index 06667b1a7ee8fa37beec833f20186e1e46d0f466..4f68e3944956dc76b52ef7f468fffeee00622b3e 100644 --- a/apps/workflowengine/src/components/Check.vue +++ b/apps/workflowengine/src/components/Check.vue @@ -1,19 +1,36 @@ <template> <div v-click-outside="hideDelete" class="check" @click="showDelete"> - <Multiselect ref="checkSelector" v-model="currentOption" :options="options" - label="name" track-by="class" :allow-empty="false" - :placeholder="t('workflowengine', 'Select a filter')" @input="updateCheck" /> - <Multiselect v-model="currentOperator" :disabled="!currentOption" :options="operators" - label="name" track-by="operator" :allow-empty="false" - :placeholder="t('workflowengine', 'Select a comparator')" @input="updateCheck" /> - <component :is="currentOption.component" v-if="currentOperator && currentComponent" v-model="check.value" - :disabled="!currentOption" :check="check" + <Multiselect ref="checkSelector" + v-model="currentOption" + :options="options" + label="name" + track-by="class" + :allow-empty="false" + :placeholder="t('workflowengine', 'Select a filter')" + @input="updateCheck" /> + <Multiselect v-model="currentOperator" + :disabled="!currentOption" + :options="operators" + label="name" + track-by="operator" + :allow-empty="false" + :placeholder="t('workflowengine', 'Select a comparator')" + @input="updateCheck" /> + <component :is="currentOption.component" + v-if="currentOperator && currentComponent" + v-model="check.value" + :disabled="!currentOption" + :check="check" @input="updateCheck" @valid="(valid=true) && validate()" @invalid="(valid=false) && validate()" /> - <input v-else v-model="check.value" type="text" + <input v-else + v-model="check.value" + type="text" :class="{ invalid: !valid }" - :disabled="!currentOption" :placeholder="valuePlaceholder" @input="updateCheck"> + :disabled="!currentOption" + :placeholder="valuePlaceholder" + @input="updateCheck"> <Actions v-if="deleteVisible || !currentOption"> <ActionButton icon="icon-delete" @click="$emit('remove')" /> </Actions> diff --git a/apps/workflowengine/src/components/Checks/FileMimeType.vue b/apps/workflowengine/src/components/Checks/FileMimeType.vue index e99bf679f00d2ab29754ed439e6e96a628f485eb..7e07d89aea2b996e5c0510af896f198def885e19 100644 --- a/apps/workflowengine/src/components/Checks/FileMimeType.vue +++ b/apps/workflowengine/src/components/Checks/FileMimeType.vue @@ -5,7 +5,9 @@ :placeholder="t('workflowengine', 'Select a file type')" label="label" track-by="pattern" - :options="options" :multiple="false" :tagging="false" + :options="options" + :multiple="false" + :tagging="false" @input="setValue"> <template slot="singleLabel" slot-scope="props"> <span class="option__icon" :class="props.option.icon" /> @@ -16,7 +18,9 @@ <span class="option__title">{{ props.option.label }}</span> </template> </Multiselect> - <input v-if="!isPredefined" type="text" :value="currentValue.pattern" + <input v-if="!isPredefined" + type="text" + :value="currentValue.pattern" @input="updateCustom"> </div> </template> diff --git a/apps/workflowengine/src/components/Checks/FileSystemTag.vue b/apps/workflowengine/src/components/Checks/FileSystemTag.vue index e2f66b30a4bb8415c8c1ec54a2c5bc130d988eb3..d3fd440f1b19e6613912f11eb1df8924f2f49d85 100644 --- a/apps/workflowengine/src/components/Checks/FileSystemTag.vue +++ b/apps/workflowengine/src/components/Checks/FileSystemTag.vue @@ -21,7 +21,8 @@ --> <template> - <MultiselectTag v-model="newValue" :multiple="false" + <MultiselectTag v-model="newValue" + :multiple="false" label="Select a tag" @input="update" /> </template> diff --git a/apps/workflowengine/src/components/Checks/MultiselectTag/MultiselectTag.vue b/apps/workflowengine/src/components/Checks/MultiselectTag/MultiselectTag.vue index 88b56a1d4e9de72cf1724abbea35ac39fea5aa28..020ed958733cef53c86c61914e26a47d3b985bc1 100644 --- a/apps/workflowengine/src/components/Checks/MultiselectTag/MultiselectTag.vue +++ b/apps/workflowengine/src/components/Checks/MultiselectTag/MultiselectTag.vue @@ -22,13 +22,17 @@ <template> <Multiselect v-model="inputValObjects" - :options="tags" :options-limit="5" + :options="tags" + :options-limit="5" :placeholder="label" track-by="id" :custom-label="tagLabel" - class="multiselect-vue" :multiple="multiple" - :close-on-select="false" :tag-width="60" - :disabled="disabled" @input="update"> + class="multiselect-vue" + :multiple="multiple" + :close-on-select="false" + :tag-width="60" + :disabled="disabled" + @input="update"> <span slot="noResult">{{ t('core', 'No results') }}</span> <template #option="scope"> {{ tagLabel(scope.option) }} diff --git a/apps/workflowengine/src/components/Checks/RequestTime.vue b/apps/workflowengine/src/components/Checks/RequestTime.vue index ce306c0541e694123ebecbdc0056bbe0574bb44f..1d7950f64f8ff769978d2bba0206428260cdc4ba 100644 --- a/apps/workflowengine/src/components/Checks/RequestTime.vue +++ b/apps/workflowengine/src/components/Checks/RequestTime.vue @@ -1,9 +1,14 @@ <template> <div class="timeslot"> <Multiselect v-model="newValue.timezone" :options="timezones" @input="update" /> - <input v-model="newValue.startTime" type="text" class="timeslot--start" - placeholder="08:00" @input="update"> - <input v-model="newValue.endTime" type="text" placeholder="18:00" + <input v-model="newValue.startTime" + type="text" + class="timeslot--start" + placeholder="08:00" + @input="update"> + <input v-model="newValue.endTime" + type="text" + placeholder="18:00" @input="update"> </div> </template> diff --git a/apps/workflowengine/src/components/Checks/RequestURL.vue b/apps/workflowengine/src/components/Checks/RequestURL.vue index 2ddba526d1e1ad99caa19f813634302fb5d30f3f..593d6234998e673d16bcd97f99432d1d157824b4 100644 --- a/apps/workflowengine/src/components/Checks/RequestURL.vue +++ b/apps/workflowengine/src/components/Checks/RequestURL.vue @@ -29,7 +29,9 @@ track-by="pattern" group-values="children" group-label="label" - :options="options" :multiple="false" :tagging="false" + :options="options" + :multiple="false" + :tagging="false" @input="setValue"> <template slot="singleLabel" slot-scope="props"> <span class="option__icon" :class="props.option.icon" /> @@ -40,9 +42,11 @@ <span class="option__title">{{ props.option.label }} {{ props.option.$groupLabel }}</span> </template> </Multiselect> - <input v-if="!isPredefined" type="text" + <input v-if="!isPredefined" + type="text" :value="currentValue.pattern" - :placeholder="placeholder" @input="updateCustom"> + :placeholder="placeholder" + @input="updateCustom"> </div> </template> diff --git a/apps/workflowengine/src/components/Checks/RequestUserAgent.vue b/apps/workflowengine/src/components/Checks/RequestUserAgent.vue index e80071ab9fa5ed7ca1269c65c9c99bdebffdaff9..9c43ac86326f6f9ac7464fa63b015d997d03d58a 100644 --- a/apps/workflowengine/src/components/Checks/RequestUserAgent.vue +++ b/apps/workflowengine/src/components/Checks/RequestUserAgent.vue @@ -29,7 +29,9 @@ track-by="pattern" group-values="children" group-label="label" - :options="options" :multiple="false" :tagging="false" + :options="options" + :multiple="false" + :tagging="false" @input="setValue"> <template slot="singleLabel" slot-scope="props"> <span class="option__icon" :class="props.option.icon" /> @@ -40,7 +42,9 @@ <span class="option__title">{{ props.option.label }} {{ props.option.$groupLabel }}</span> </template> </Multiselect> - <input v-if="!isPredefined" type="text" :value="currentValue.pattern" + <input v-if="!isPredefined" + type="text" + :value="currentValue.pattern" @input="updateCustom"> </div> </template> diff --git a/apps/workflowengine/src/components/Checks/RequestUserGroup.vue b/apps/workflowengine/src/components/Checks/RequestUserGroup.vue index 843bbf127e0124659bb0419f0fa2bcdb5151b4ed..ccd556c99c77621c0f5f16c4f0a16543237aa55d 100644 --- a/apps/workflowengine/src/components/Checks/RequestUserGroup.vue +++ b/apps/workflowengine/src/components/Checks/RequestUserGroup.vue @@ -23,9 +23,11 @@ <template> <div> <Multiselect v-model="newValue" - :class="{'icon-loading-small': groups.length === 0}" :options="groups" + :class="{'icon-loading-small': groups.length === 0}" + :options="groups" :multiple="false" - label="displayname" track-by="id" + label="displayname" + track-by="id" @input="setValue" /> </div> </template> diff --git a/apps/workflowengine/src/components/Event.vue b/apps/workflowengine/src/components/Event.vue index ea153758c741af0e1217ce6ee6f091b021ea8f33..a06835f5f8230fcdcf0c732859071bbf036308aa 100644 --- a/apps/workflowengine/src/components/Event.vue +++ b/apps/workflowengine/src/components/Event.vue @@ -4,9 +4,14 @@ <img class="option__icon" :src="entity.icon"> <span class="option__title option__title_single">{{ operation.triggerHint }}</span> </div> - <Multiselect v-else :value="currentEvent" :options="allEvents" - label="eventName" track-by="id" :allow-empty="false" - :disabled="allEvents.length <= 1" @input="updateEvent"> + <Multiselect v-else + :value="currentEvent" + :options="allEvents" + label="eventName" + track-by="id" + :allow-empty="false" + :disabled="allEvents.length <= 1" + @input="updateEvent"> <template slot="singleLabel" slot-scope="props"> <img class="option__icon" :src="props.option.entity.icon"> <span class="option__title option__title_single">{{ props.option.displayName }}</span> diff --git a/apps/workflowengine/src/components/Rule.vue b/apps/workflowengine/src/components/Rule.vue index 76d332ac41494311c97f3802809cbe0b03c65c28..2be9b0fc5e5279104670781fee3978db56d3b512 100644 --- a/apps/workflowengine/src/components/Rule.vue +++ b/apps/workflowengine/src/components/Rule.vue @@ -7,13 +7,18 @@ </p> <p v-for="(check, index) in rule.checks" :key="index"> <span>{{ t('workflowengine', 'and') }}</span> - <Check :check="check" :rule="rule" @update="updateRule" + <Check :check="check" + :rule="rule" + @update="updateRule" @remove="removeCheck(check)" /> </p> <p> <span /> - <input v-if="lastCheckComplete" type="button" class="check--add" - value="Add a new filter" @click="rule.checks.push({class: null, operator: null, value: null})"> + <input v-if="lastCheckComplete" + type="button" + class="check--add" + value="Add a new filter" + @click="rule.checks.push({class: null, operator: null, value: null})"> </p> </div> <div class="flow-icon icon-confirm" /> @@ -29,10 +34,14 @@ </Actions> </div> <Operation :operation="operation" :colored="false"> - <component :is="operation.options" v-if="operation.options" v-model="rule.operation" + <component :is="operation.options" + v-if="operation.options" + v-model="rule.operation" @input="updateOperation" /> </Operation> - <button v-tooltip="ruleStatus.tooltip" class="status-button icon" :class="ruleStatus.class" + <button v-tooltip="ruleStatus.tooltip" + class="status-button icon" + :class="ruleStatus.class" @click="saveRule"> {{ ruleStatus.title }} </button> diff --git a/apps/workflowengine/src/components/Workflow.vue b/apps/workflowengine/src/components/Workflow.vue index b4fab5a058caf41f9357144790899f0327e534f9..452dbacc5f6355d3e284cf3b1a13c5df5e1ff965 100644 --- a/apps/workflowengine/src/components/Workflow.vue +++ b/apps/workflowengine/src/components/Workflow.vue @@ -4,12 +4,15 @@ <h2>{{ t('workflowengine', 'Workflows') }}</h2> <transition-group name="slide" tag="div" class="actions"> - <Operation v-for="operation in getMainOperations" :key="operation.id" :operation="operation" + <Operation v-for="operation in getMainOperations" + :key="operation.id" + :operation="operation" @click.native="createNewRule(operation)" /> </transition-group> <div v-if="hasMoreOperations" class="actions__more"> - <button class="icon" :class="showMoreOperations ? 'icon-triangle-n' : 'icon-triangle-s'" + <button class="icon" + :class="showMoreOperations ? 'icon-triangle-n' : 'icon-triangle-s'" @click="showMoreOperations=!showMoreOperations"> {{ showMoreOperations ? t('workflowengine', 'Show less') : t('workflowengine', 'Show more') }} </button> diff --git a/apps/workflowengine/src/store.js b/apps/workflowengine/src/store.js index ad1a4d3f1bfc2e6cbd1171b11eee70156a493300..8c55be8126cdefd84bb66c33745897eec53858d4 100644 --- a/apps/workflowengine/src/store.js +++ b/apps/workflowengine/src/store.js @@ -1,4 +1,4 @@ -/* +/** * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> * * @author Julius Härtl <jus@bitgrid.net> @@ -144,8 +144,12 @@ const store = new Vuex.Store({ getEventsForOperation(state) { return (operation) => state.events }, + /** * Return all available checker plugins for a given entity class + * @param {Object} state the store state + * @param {Object} entity the entity class + * @returns {Array} the available plugins */ getChecksForEntity(state) { return (entity) => { diff --git a/apps/workflowengine/src/workflowengine.js b/apps/workflowengine/src/workflowengine.js index 2eb8161fc87b96d14229e847dafdad297d9aaaa5..e479bbfa4e81fb8ceb0ab69c5f9360411a59c259 100644 --- a/apps/workflowengine/src/workflowengine.js +++ b/apps/workflowengine/src/workflowengine.js @@ -44,16 +44,16 @@ window.OCA.WorkflowEngine = Object.assign({}, OCA.WorkflowEngine, { /** * - * @param {CheckPlugin} Plugin + * @param {CheckPlugin} Plugin the plugin to register */ - registerCheck: function (Plugin) { + registerCheck: function(Plugin) { store.commit('addPluginCheck', Plugin) }, /** * - * @param {OperatorPlugin} Plugin + * @param {OperatorPlugin} Plugin the plugin to register */ - registerOperator: function (Plugin) { + registerOperator: function(Plugin) { store.commit('addPluginOperator', Plugin) } }) diff --git a/core/js/dist/login.js b/core/js/dist/login.js index 2db891d3b03ab438972362bbf7a9133bda8a8917..70bb6017ea68e7fd2597c50717721cd3fa8e3e4b 100644 Binary files a/core/js/dist/login.js and b/core/js/dist/login.js differ diff --git a/core/js/dist/login.js.map b/core/js/dist/login.js.map index 6f0a80c162495c958077b3ec0c8c1f52386bec14..9eb112cbe1a8d4e2208904bf536b2c90be769735 100644 Binary files a/core/js/dist/login.js.map and b/core/js/dist/login.js.map differ diff --git a/core/js/dist/main.js b/core/js/dist/main.js index c2a86c24444cdd2366170084e34fd1e059a72b28..99633f9e716dd14802f0fbcd93fe2830c16c02ab 100644 Binary files a/core/js/dist/main.js and b/core/js/dist/main.js differ diff --git a/core/js/dist/main.js.map b/core/js/dist/main.js.map index d209ba8cc0675c019a532924b6976cab65047053..0971391d4a1ea0e0c92d0466b74fec5cbc8e0775 100644 Binary files a/core/js/dist/main.js.map and b/core/js/dist/main.js.map differ diff --git a/core/js/dist/maintenance.js b/core/js/dist/maintenance.js index ea41042e2e005d89efe99aa0075ad6659f63f35c..aa873b696a692acd328eb821c5a7e93ad9626899 100644 Binary files a/core/js/dist/maintenance.js and b/core/js/dist/maintenance.js differ diff --git a/core/js/dist/maintenance.js.map b/core/js/dist/maintenance.js.map index 7035aa1d4392d2c223ae1f14e92d7da644f91063..9c418602ee465824aab564ed372772f5f092d72a 100644 Binary files a/core/js/dist/maintenance.js.map and b/core/js/dist/maintenance.js.map differ diff --git a/core/js/dist/share_backend.js b/core/js/dist/share_backend.js index 539ad99643ac819cf6d24478e6b4e395e6e15295..c72516337e2f7ad7b4a9e899d6d19166d4c9fc5a 100644 Binary files a/core/js/dist/share_backend.js and b/core/js/dist/share_backend.js differ diff --git a/core/js/dist/share_backend.js.map b/core/js/dist/share_backend.js.map index e23968b7315164efc861cccf1a7ca94bb177c18f..f348e16de9b506eb86f2fc80c417bfaa38e02c1e 100644 Binary files a/core/js/dist/share_backend.js.map and b/core/js/dist/share_backend.js.map differ diff --git a/core/js/dist/systemtags.js b/core/js/dist/systemtags.js index f01991d2ab8f30b0ce8be35cce0b870be3f23c02..e4eafeb84843273bde3d5f2c7119428123298f58 100644 Binary files a/core/js/dist/systemtags.js and b/core/js/dist/systemtags.js differ diff --git a/core/js/dist/systemtags.js.map b/core/js/dist/systemtags.js.map index dae918b35065d1247b73444dc5d49e501b0065c8..e388aba428f653e513e8ce96f011ca1afd3793b7 100644 Binary files a/core/js/dist/systemtags.js.map and b/core/js/dist/systemtags.js.map differ diff --git a/core/js/merged-share-backend.js b/core/js/merged-share-backend.js index a27bae9aa2d4211feb2bc153196d23fa378a1eed..b96783603a474e353046e3b7429635181c081771 100644 --- a/core/js/merged-share-backend.js +++ b/core/js/merged-share-backend.js @@ -1,9 +1,9 @@ -import './shareconfigmodel.js'; -import './sharetemplates.js'; -import './shareitemmodel.js'; -import './sharesocialmanager.js'; -import './sharedialogresharerinfoview.js'; -import './sharedialoglinkshareview.js'; -import './sharedialogshareelistview.js'; -import './sharedialogview.js'; -import './share.js'; +import './shareconfigmodel.js' +import './sharetemplates.js' +import './shareitemmodel.js' +import './sharesocialmanager.js' +import './sharedialogresharerinfoview.js' +import './sharedialoglinkshareview.js' +import './sharedialogshareelistview.js' +import './sharedialogview.js' +import './share.js' diff --git a/core/js/share.js b/core/js/share.js index 26b420ab477991e0aae25140ee1d988a569e5857..8dc5e4906475b62f817404869fb435c9b5ca54d7 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -1,34 +1,33 @@ -/* global escapeHTML */ - +/* eslint-disable */ /** * @namespace */ OC.Share = _.extend(OC.Share || {}, { - SHARE_TYPE_USER:0, - SHARE_TYPE_GROUP:1, - SHARE_TYPE_LINK:3, - SHARE_TYPE_EMAIL:4, - SHARE_TYPE_REMOTE:6, - SHARE_TYPE_CIRCLE:7, - SHARE_TYPE_GUEST:8, - SHARE_TYPE_REMOTE_GROUP:9, - SHARE_TYPE_ROOM:10, + SHARE_TYPE_USER: 0, + SHARE_TYPE_GROUP: 1, + SHARE_TYPE_LINK: 3, + SHARE_TYPE_EMAIL: 4, + SHARE_TYPE_REMOTE: 6, + SHARE_TYPE_CIRCLE: 7, + SHARE_TYPE_GUEST: 8, + SHARE_TYPE_REMOTE_GROUP: 9, + SHARE_TYPE_ROOM: 10, /** * Regular expression for splitting parts of remote share owners: * "user@example.com/path/to/owncloud" * "user@anotherexample.com@example.com/path/to/owncloud */ - _REMOTE_OWNER_REGEXP: new RegExp("^([^@]*)@(([^@]*)@)?([^/]*)([/](.*)?)?$"), + _REMOTE_OWNER_REGEXP: new RegExp('^([^@]*)@(([^@]*)@)?([^/]*)([/](.*)?)?$'), /** * @deprecated use OC.Share.currentShares instead */ - itemShares:[], + itemShares: [], /** * Full list of all share statuses */ - statuses:{}, + statuses: {}, /** * Shares for the currently selected file. * (for which the dropdown is open) @@ -40,7 +39,7 @@ OC.Share = _.extend(OC.Share || {}, { /** * Whether the share dropdown is opened. */ - droppedDown:false, + droppedDown: false, /** * Loads ALL share statuses from server, stores them in * OC.Share.statuses then calls OC.Share.updateIcons() to update the @@ -53,12 +52,12 @@ OC.Share = _.extend(OC.Share || {}, { * @param fileList file list instance, defaults to OCA.Files.App.fileList * @param callback function to call after the shares were loaded */ - loadIcons:function(itemType, fileList, callback) { - var path = fileList.dirInfo.path; + loadIcons: function(itemType, fileList, callback) { + var path = fileList.dirInfo.path if (path === '/') { - path = ''; + path = '' } - path += '/' + fileList.dirInfo.name; + path += '/' + fileList.dirInfo.name // Load all share icons $.get( @@ -69,23 +68,23 @@ OC.Share = _.extend(OC.Share || {}, { format: 'json' }, function(result) { if (result && result.ocs.meta.statuscode === 200) { - OC.Share.statuses = {}; + OC.Share.statuses = {} $.each(result.ocs.data, function(it, share) { if (!(share.item_source in OC.Share.statuses)) { - OC.Share.statuses[share.item_source] = {link: false}; + OC.Share.statuses[share.item_source] = { link: false } } if (share.share_type === OC.Share.SHARE_TYPE_LINK) { - OC.Share.statuses[share.item_source] = {link: true}; + OC.Share.statuses[share.item_source] = { link: true } } - }); + }) if (_.isFunction(callback)) { - callback(OC.Share.statuses); + callback(OC.Share.statuses) } else { - OC.Share.updateIcons(itemType, fileList); + OC.Share.updateIcons(itemType, fileList) } } } - ); + ) }, /** * Updates the files' "Share" icons according to the known @@ -96,106 +95,106 @@ OC.Share = _.extend(OC.Share || {}, { * @param fileList file list instance * defaults to OCA.Files.App.fileList */ - updateIcons:function(itemType, fileList){ - var item; - var $fileList; - var currentDir; + updateIcons: function(itemType, fileList) { + var item + var $fileList + var currentDir if (!fileList && OCA.Files) { - fileList = OCA.Files.App.fileList; + fileList = OCA.Files.App.fileList } // fileList is usually only defined in the files app if (fileList) { - $fileList = fileList.$fileList; - currentDir = fileList.getCurrentDirectory(); + $fileList = fileList.$fileList + currentDir = fileList.getCurrentDirectory() } // TODO: iterating over the files might be more efficient - for (item in OC.Share.statuses){ - var iconClass = 'icon-shared'; - var data = OC.Share.statuses[item]; - var hasLink = data.link; + for (item in OC.Share.statuses) { + var iconClass = 'icon-shared' + var data = OC.Share.statuses[item] + var hasLink = data.link // Links override shared in terms of icon display if (hasLink) { - iconClass = 'icon-public'; + iconClass = 'icon-public' } if (itemType !== 'file' && itemType !== 'folder') { - $('a.share[data-item="'+item+'"] .icon').removeClass('icon-shared icon-public').addClass(iconClass); + $('a.share[data-item="' + item + '"] .icon').removeClass('icon-shared icon-public').addClass(iconClass) } else { // TODO: ultimately this part should be moved to files_sharing app - var file = $fileList.find('tr[data-id="'+item+'"]'); - var shareFolder = OC.imagePath('core', 'filetypes/folder-shared'); - var img; + var file = $fileList.find('tr[data-id="' + item + '"]') + var shareFolder = OC.imagePath('core', 'filetypes/folder-shared') + var img if (file.length > 0) { - this.markFileAsShared(file, true, hasLink); + this.markFileAsShared(file, true, hasLink) } else { - var dir = currentDir; + var dir = currentDir if (dir.length > 1) { - var last = ''; - var path = dir; + var last = '' + var path = dir // Search for possible parent folders that are shared while (path != last) { if (path === data.path && !data.link) { - var actions = $fileList.find('.fileactions .action[data-action="Share"]'); - var files = $fileList.find('.filename'); - var i; + var actions = $fileList.find('.fileactions .action[data-action="Share"]') + var files = $fileList.find('.filename') + var i for (i = 0; i < actions.length; i++) { // TODO: use this.markFileAsShared() - img = $(actions[i]).find('img'); + img = $(actions[i]).find('img') if (img.attr('src') !== OC.imagePath('core', 'actions/public')) { - img.attr('src', image); - $(actions[i]).addClass('permanent'); - $(actions[i]).html('<span> '+t('core', 'Shared')+'</span>').prepend(img); + img.attr('src', image) + $(actions[i]).addClass('permanent') + $(actions[i]).html('<span> ' + t('core', 'Shared') + '</span>').prepend(img) } } - for(i = 0; i < files.length; i++) { + for (i = 0; i < files.length; i++) { if ($(files[i]).closest('tr').data('type') === 'dir') { - $(files[i]).find('.thumbnail').css('background-image', 'url('+shareFolder+')'); + $(files[i]).find('.thumbnail').css('background-image', 'url(' + shareFolder + ')') } } } - last = path; - path = OC.Share.dirname(path); + last = path + path = OC.Share.dirname(path) } } } } } }, - updateIcon:function(itemType, itemSource) { - var shares = false; - var link = false; - var iconClass = ''; + updateIcon: function(itemType, itemSource) { + var shares = false + var link = false + var iconClass = '' $.each(OC.Share.itemShares, function(index) { if (OC.Share.itemShares[index]) { if (index == OC.Share.SHARE_TYPE_LINK) { if (OC.Share.itemShares[index] == true) { - shares = true; - iconClass = 'icon-public'; - link = true; - return; + shares = true + iconClass = 'icon-public' + link = true + } } else if (OC.Share.itemShares[index].length > 0) { - shares = true; - iconClass = 'icon-shared'; + shares = true + iconClass = 'icon-shared' } } - }); + }) if (itemType != 'file' && itemType != 'folder') { - $('a.share[data-item="'+itemSource+'"] .icon').removeClass('icon-shared icon-public').addClass(iconClass); + $('a.share[data-item="' + itemSource + '"] .icon').removeClass('icon-shared icon-public').addClass(iconClass) } else { - var $tr = $('tr').filterAttr('data-id', String(itemSource)); + var $tr = $('tr').filterAttr('data-id', String(itemSource)) if ($tr.length > 0) { // it might happen that multiple lists exist in the DOM // with the same id $tr.each(function() { - OC.Share.markFileAsShared($(this), shares, link); - }); + OC.Share.markFileAsShared($(this), shares, link) + }) } } if (shares) { - OC.Share.statuses[itemSource] = OC.Share.statuses[itemSource] || {}; - OC.Share.statuses[itemSource].link = link; + OC.Share.statuses[itemSource] = OC.Share.statuses[itemSource] || {} + OC.Share.statuses[itemSource].link = link } else { - delete OC.Share.statuses[itemSource]; + delete OC.Share.statuses[itemSource] } }, /** @@ -204,55 +203,55 @@ OC.Share = _.extend(OC.Share || {}, { * @param {String} shareWith userid, full remote share, or whatever * @param {String} shareWithDisplayName * @param {String} message - * @return {String} HTML code to display + * @returns {String} HTML code to display */ _formatRemoteShare: function(shareWith, shareWithDisplayName, message) { - var parts = this._REMOTE_OWNER_REGEXP.exec(shareWith); + var parts = this._REMOTE_OWNER_REGEXP.exec(shareWith) if (!parts) { // display avatar of the user - var avatar = '<span class="avatar" data-username="' + escapeHTML(shareWith) + '" title="' + message + " " + escapeHTML(shareWithDisplayName) + '"></span>'; - var hidden = '<span class="hidden-visually">' + message + ' ' + escapeHTML(shareWithDisplayName) + '</span> '; - return avatar + hidden; + var avatar = '<span class="avatar" data-username="' + escapeHTML(shareWith) + '" title="' + message + ' ' + escapeHTML(shareWithDisplayName) + '"></span>' + var hidden = '<span class="hidden-visually">' + message + ' ' + escapeHTML(shareWithDisplayName) + '</span> ' + return avatar + hidden } - var userName = parts[1]; - var userDomain = parts[3]; - var server = parts[4]; - var tooltip = message + ' ' + userName; + var userName = parts[1] + var userDomain = parts[3] + var server = parts[4] + var tooltip = message + ' ' + userName if (userDomain) { - tooltip += '@' + userDomain; + tooltip += '@' + userDomain } if (server) { if (!userDomain) { - userDomain = '…'; + userDomain = '…' } - tooltip += '@' + server; + tooltip += '@' + server } - var html = '<span class="remoteAddress" title="' + escapeHTML(tooltip) + '">'; - html += '<span class="username">' + escapeHTML(userName) + '</span>'; + var html = '<span class="remoteAddress" title="' + escapeHTML(tooltip) + '">' + html += '<span class="username">' + escapeHTML(userName) + '</span>' if (userDomain) { - html += '<span class="userDomain">@' + escapeHTML(userDomain) + '</span>'; + html += '<span class="userDomain">@' + escapeHTML(userDomain) + '</span>' } - html += '</span> '; - return html; + html += '</span> ' + return html }, /** * Loop over all recipients in the list and format them using * all kind of fancy magic. * * @param {Object} recipients array of all the recipients - * @return {String[]} modified list of recipients + * @returns {String[]} modified list of recipients */ _formatShareList: function(recipients) { - var _parent = this; - recipients = _.toArray(recipients); + var _parent = this + recipients = _.toArray(recipients) recipients.sort(function(a, b) { - return a.shareWithDisplayName.localeCompare(b.shareWithDisplayName); - }); + return a.shareWithDisplayName.localeCompare(b.shareWithDisplayName) + }) return $.map(recipients, function(recipient) { - return _parent._formatRemoteShare(recipient.shareWith, recipient.shareWithDisplayName, t('core', 'Shared with')); - }); + return _parent._formatRemoteShare(recipient.shareWith, recipient.shareWithDisplayName, t('core', 'Shared with')) + }) }, /** * Marks/unmarks a given file as shared by changing its action icon @@ -263,78 +262,78 @@ OC.Share = _.extend(OC.Share || {}, { * @param hasLink whether link share is available */ markFileAsShared: function($tr, hasShares, hasLink) { - var action = $tr.find('.fileactions .action[data-action="Share"]'); - var type = $tr.data('type'); - var icon = action.find('.icon'); - var message, recipients, avatars; - var ownerId = $tr.attr('data-share-owner-id'); - var owner = $tr.attr('data-share-owner'); - var mountType = $tr.attr('data-mounttype'); - var shareFolderIcon; - var iconClass = 'icon-shared'; - action.removeClass('shared-style'); + var action = $tr.find('.fileactions .action[data-action="Share"]') + var type = $tr.data('type') + var icon = action.find('.icon') + var message, recipients, avatars + var ownerId = $tr.attr('data-share-owner-id') + var owner = $tr.attr('data-share-owner') + var mountType = $tr.attr('data-mounttype') + var shareFolderIcon + var iconClass = 'icon-shared' + action.removeClass('shared-style') // update folder icon if (type === 'dir' && (hasShares || hasLink || ownerId)) { if (typeof mountType !== 'undefined' && mountType !== 'shared-root' && mountType !== 'shared') { - shareFolderIcon = OC.MimeType.getIconUrl('dir-' + mountType); + shareFolderIcon = OC.MimeType.getIconUrl('dir-' + mountType) } else if (hasLink) { - shareFolderIcon = OC.MimeType.getIconUrl('dir-public'); + shareFolderIcon = OC.MimeType.getIconUrl('dir-public') } else { - shareFolderIcon = OC.MimeType.getIconUrl('dir-shared'); + shareFolderIcon = OC.MimeType.getIconUrl('dir-shared') } - $tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')'); - $tr.attr('data-icon', shareFolderIcon); + $tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')') + $tr.attr('data-icon', shareFolderIcon) } else if (type === 'dir') { - var isEncrypted = $tr.attr('data-e2eencrypted'); + var isEncrypted = $tr.attr('data-e2eencrypted') // FIXME: duplicate of FileList._createRow logic for external folder, // need to refactor the icon logic into a single code path eventually if (isEncrypted === 'true') { - shareFolderIcon = OC.MimeType.getIconUrl('dir-encrypted'); - $tr.attr('data-icon', shareFolderIcon); + shareFolderIcon = OC.MimeType.getIconUrl('dir-encrypted') + $tr.attr('data-icon', shareFolderIcon) } else if (mountType && mountType.indexOf('external') === 0) { - shareFolderIcon = OC.MimeType.getIconUrl('dir-external'); - $tr.attr('data-icon', shareFolderIcon); + shareFolderIcon = OC.MimeType.getIconUrl('dir-external') + $tr.attr('data-icon', shareFolderIcon) } else { - shareFolderIcon = OC.MimeType.getIconUrl('dir'); + shareFolderIcon = OC.MimeType.getIconUrl('dir') // back to default - $tr.removeAttr('data-icon'); + $tr.removeAttr('data-icon') } - $tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')'); + $tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')') } // update share action text / icon if (hasShares || ownerId) { - recipients = $tr.data('share-recipient-data'); - action.addClass('shared-style'); + recipients = $tr.data('share-recipient-data') + action.addClass('shared-style') - avatars = '<span>' + t('core', 'Shared') + '</span>'; + avatars = '<span>' + t('core', 'Shared') + '</span>' // even if reshared, only show "Shared by" if (ownerId) { - message = t('core', 'Shared by'); - avatars = this._formatRemoteShare(ownerId, owner, message); + message = t('core', 'Shared by') + avatars = this._formatRemoteShare(ownerId, owner, message) } else if (recipients) { - avatars = this._formatShareList(recipients); + avatars = this._formatShareList(recipients) } - action.html(avatars).prepend(icon); + action.html(avatars).prepend(icon) if (ownerId || recipients) { - var avatarElement = action.find('.avatar'); - avatarElement.each(function () { - $(this).avatar($(this).data('username'), 32); - }); - action.find('span[title]').tooltip({placement: 'top'}); + var avatarElement = action.find('.avatar') + avatarElement.each(function() { + $(this).avatar($(this).data('username'), 32) + }) + action.find('span[title]').tooltip({ placement: 'top' }) } } else { - action.html('<span class="hidden-visually">' + t('core', 'Shared') + '</span>').prepend(icon); + action.html('<span class="hidden-visually">' + t('core', 'Shared') + '</span>').prepend(icon) } if (hasLink) { - iconClass = 'icon-public'; + iconClass = 'icon-public' } - icon.removeClass('icon-shared icon-public').addClass(iconClass); + icon.removeClass('icon-shared icon-public').addClass(iconClass) }, - showDropDown:function(itemType, itemSource, appendTo, link, possiblePermissions, filename) { - var configModel = new OC.Share.ShareConfigModel(); - var attributes = {itemType: itemType, itemSource: itemSource, possiblePermissions: possiblePermissions}; - var itemModel = new OC.Share.ShareItemModel(attributes, {configModel: configModel}); + showDropDown: function(itemType, itemSource, appendTo, link, possiblePermissions, filename) { + var configModel = new OC.Share.ShareConfigModel() + var attributes = { itemType: itemType, itemSource: itemSource, possiblePermissions: possiblePermissions } + var itemModel = new OC.Share.ShareItemModel(attributes, { configModel: configModel }) var dialogView = new OC.Share.ShareDialogView({ id: 'dropdown', model: itemModel, @@ -345,38 +344,38 @@ OC.Share = _.extend(OC.Share || {}, { 'data-item-type': itemType, 'data-item-source': itemSource } - }); - dialogView.setShowLink(link); - var $dialog = dialogView.render().$el; - $dialog.appendTo(appendTo); + }) + dialogView.setShowLink(link) + var $dialog = dialogView.render().$el + $dialog.appendTo(appendTo) $dialog.slideDown(OC.menuSpeed, function() { - OC.Share.droppedDown = true; - }); - itemModel.fetch(); + OC.Share.droppedDown = true + }) + itemModel.fetch() }, - hideDropDown:function(callback) { - OC.Share.currentShares = null; + hideDropDown: function(callback) { + OC.Share.currentShares = null $('#dropdown').slideUp(OC.menuSpeed, function() { - OC.Share.droppedDown = false; - $('#dropdown').remove(); + OC.Share.droppedDown = false + $('#dropdown').remove() if (typeof FileActions !== 'undefined') { - $('tr').removeClass('mouseOver'); + $('tr').removeClass('mouseOver') } if (callback) { - callback.call(); + callback.call() } - }); + }) }, - dirname:function(path) { - return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, ''); + dirname: function(path) { + return path.replace(/\\/g, '/').replace(/\/[^\/]*$/, '') } -}); +}) $(document).ready(function() { - if(typeof monthNames != 'undefined'){ + if (typeof monthNames !== 'undefined') { // min date should always be the next day - var minDate = new Date(); - minDate.setDate(minDate.getDate()+1); + var minDate = new Date() + minDate.setDate(minDate.getDate() + 1) $.datepicker.setDefaults({ monthNames: monthNames, monthNamesShort: monthNamesShort, @@ -384,19 +383,17 @@ $(document).ready(function() { dayNamesMin: dayNamesMin, dayNamesShort: dayNamesShort, firstDay: firstDay, - minDate : minDate - }); + minDate: minDate + }) } $(this).click(function(event) { - var target = $(event.target); + var target = $(event.target) var isMatched = !target.is('.drop, .ui-datepicker-next, .ui-datepicker-prev, .ui-icon') - && !target.closest('#ui-datepicker-div').length && !target.closest('.ui-autocomplete').length; + && !target.closest('#ui-datepicker-div').length && !target.closest('.ui-autocomplete').length if (OC.Share && OC.Share.droppedDown && isMatched && $('#dropdown').has(event.target).length === 0) { - OC.Share.hideDropDown(); + OC.Share.hideDropDown() } - }); - - + }) -}); +}) diff --git a/core/js/shareconfigmodel.js b/core/js/shareconfigmodel.js index 184a121075598a8330c9893b360d1b06bd102d6d..0d066116b53596cdd7949be4f1cfb3b282315de3 100644 --- a/core/js/shareconfigmodel.js +++ b/core/js/shareconfigmodel.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2015 * @@ -12,8 +13,8 @@ (function() { if (!OC.Share) { - OC.Share = {}; - OC.Share.Types = {}; + OC.Share = {} + OC.Share.Types = {} } // FIXME: the config model should populate its own model attributes based on @@ -37,36 +38,35 @@ * @returns {boolean} */ isPublicUploadEnabled: function() { - var publicUploadEnabled = $('#filestable').data('allow-public-upload'); - return publicUploadEnabled === 'yes'; + var publicUploadEnabled = $('#filestable').data('allow-public-upload') + return publicUploadEnabled === 'yes' }, /** * @returns {boolean} */ isShareWithLinkAllowed: function() { - return $('#allowShareWithLink').val() === 'yes'; + return $('#allowShareWithLink').val() === 'yes' }, /** * @returns {string} */ getFederatedShareDocLink: function() { - return OC.appConfig.core.federatedCloudShareDoc; + return OC.appConfig.core.federatedCloudShareDoc }, - getDefaultExpirationDateString: function () { - var expireDateString = ''; + getDefaultExpirationDateString: function() { + var expireDateString = '' if (this.get('isDefaultExpireDateEnabled')) { - var date = moment.utc(); - var expireAfterDays = this.get('defaultExpireDate'); - date.add(expireAfterDays, 'days'); - expireDateString = date.format('YYYY-MM-DD 00:00:00'); + var date = moment.utc() + var expireAfterDays = this.get('defaultExpireDate') + date.add(expireAfterDays, 'days') + expireDateString = date.format('YYYY-MM-DD 00:00:00') } - return expireDateString; + return expireDateString } - }); + }) - - OC.Share.ShareConfigModel = ShareConfigModel; -})(); + OC.Share.ShareConfigModel = ShareConfigModel +})() diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js index b9bf88886750e7bb5598922be873b525cec2a05c..96ebd8ebbac35e6023a8450bc039f815b20fb0f3 100644 --- a/core/js/sharedialoglinkshareview.js +++ b/core/js/sharedialoglinkshareview.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2015 * @@ -12,12 +13,12 @@ (function() { if (!OC.Share) { - OC.Share = {}; + OC.Share = {} } - var PASSWORD_PLACEHOLDER = '**********'; - var PASSWORD_PLACEHOLDER_MESSAGE = t('core', 'Choose a password for the public link'); - var PASSWORD_PLACEHOLDER_MESSAGE_OPTIONAL = t('core', 'Choose a password for the public link or press the "Enter" key'); + var PASSWORD_PLACEHOLDER = '**********' + var PASSWORD_PLACEHOLDER_MESSAGE = t('core', 'Choose a password for the public link') + var PASSWORD_PLACEHOLDER_MESSAGE_OPTIONAL = t('core', 'Choose a password for the public link or press the "Enter" key') /** * @class OCA.Share.ShareDialogLinkShareView @@ -54,7 +55,7 @@ // hide download 'change .hideDownloadCheckbox': 'onHideDownloadChange', // password - 'click input.share-pass-submit': 'onPasswordEntered', + 'click input.share-pass-submit': 'onPasswordEntered', 'keyup input.linkPassText': 'onPasswordKeyUp', // check for the enter key 'change .showPasswordCheckbox': 'onShowPasswordClick', 'change .passwordByTalkCheckbox': 'onPasswordByTalkChange', @@ -66,9 +67,9 @@ // permission change 'change .publicUploadRadio': 'onPublicUploadChange', // expire date - 'click .expireDate' : 'onExpireDateChange', + 'click .expireDate': 'onExpireDateChange', 'change .datepicker': 'onChangeExpirationDate', - 'click .datepicker' : 'showDatePicker', + 'click .datepicker': 'showDatePicker', // note 'click .share-add': 'showNoteForm', 'click .share-note-delete': 'deleteNote', @@ -78,27 +79,27 @@ // new share 'click .new-share': 'newShare', // enforced pass set - 'submit .enforcedPassForm': 'enforcedPasswordSet', + 'submit .enforcedPassForm': 'enforcedPasswordSet' }, initialize: function(options) { - var view = this; + var view = this this.model.on('change:permissions', function() { - view.render(); - }); + view.render() + }) this.model.on('change:itemType', function() { - view.render(); - }); + view.render() + }) this.model.on('change:allowPublicUploadStatus', function() { - view.render(); - }); + view.render() + }) this.model.on('change:hideFileListStatus', function() { - view.render(); - }); + view.render() + }) this.model.on('change:linkShares', function(model, linkShares) { // The "Password protect by Talk" item is shown only when there @@ -110,113 +111,113 @@ // changes; other changes in the link shares does not trigger // a rendering, so the view must be rendered again as needed in // those cases (for example, when a link share is removed). - - var previousLinkShares = model.previous('linkShares'); + + var previousLinkShares = model.previous('linkShares') if (previousLinkShares.length !== linkShares.length) { - return; + return } - var i; + var i for (i = 0; i < linkShares.length; i++) { if (linkShares[i].id !== previousLinkShares[i].id) { // A resorting should never happen, but just in case. - return; + return } if (linkShares[i].password !== previousLinkShares[i].password) { - view.render(); + view.render() - return; + return } } - }); + }) - if(!_.isUndefined(options.configModel)) { - this.configModel = options.configModel; + if (!_.isUndefined(options.configModel)) { + this.configModel = options.configModel } else { - throw 'missing OC.Share.ShareConfigModel'; + throw 'missing OC.Share.ShareConfigModel' } - var clipboard = new Clipboard('.clipboard-button'); + var clipboard = new Clipboard('.clipboard-button') clipboard.on('success', function(e) { - var $trigger = $(e.trigger); + var $trigger = $(e.trigger) $trigger.tooltip('hide') .attr('data-original-title', t('core', 'Copied!')) .tooltip('fixTitle') - .tooltip({placement: 'bottom', trigger: 'manual'}) - .tooltip('show'); + .tooltip({ placement: 'bottom', trigger: 'manual' }) + .tooltip('show') _.delay(function() { $trigger.tooltip('hide') .attr('data-original-title', t('core', 'Copy link')) .tooltip('fixTitle') - }, 3000); - }); - clipboard.on('error', function (e) { - var $trigger = $(e.trigger); - var $menu = $trigger.next('.share-menu').find('.popovermenu'); - var $linkTextMenu = $menu.find('li.linkTextMenu'); - var $input = $linkTextMenu.find('.linkText'); + }, 3000) + }) + clipboard.on('error', function(e) { + var $trigger = $(e.trigger) + var $menu = $trigger.next('.share-menu').find('.popovermenu') + var $linkTextMenu = $menu.find('li.linkTextMenu') + var $input = $linkTextMenu.find('.linkText') - var $li = $trigger.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); + var $li = $trigger.closest('li[data-share-id]') + var shareId = $li.data('share-id') // show menu - OC.showMenu(null, $menu); + OC.showMenu(null, $menu) - var actionMsg = ''; + var actionMsg = '' if (/iPhone|iPad/i.test(navigator.userAgent)) { - actionMsg = t('core', 'Not supported!'); + actionMsg = t('core', 'Not supported!') } else if (/Mac/i.test(navigator.userAgent)) { - actionMsg = t('core', 'Press ⌘-C to copy.'); + actionMsg = t('core', 'Press ⌘-C to copy.') } else { - actionMsg = t('core', 'Press Ctrl-C to copy.'); + actionMsg = t('core', 'Press Ctrl-C to copy.') } - $linkTextMenu.removeClass('hidden'); - $input.select(); + $linkTextMenu.removeClass('hidden') + $input.select() $input.tooltip('hide') .attr('data-original-title', actionMsg) .tooltip('fixTitle') - .tooltip({placement: 'bottom', trigger: 'manual'}) - .tooltip('show'); - _.delay(function () { - $input.tooltip('hide'); + .tooltip({ placement: 'bottom', trigger: 'manual' }) + .tooltip('show') + _.delay(function() { + $input.tooltip('hide') $input.attr('data-original-title', t('core', 'Copy')) - .tooltip('fixTitle'); - }, 3000); - }); + .tooltip('fixTitle') + }, 3000) + }) }, newShare: function(event) { - var self = this; - var $target = $(event.target); - var $li = $target.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); - var $loading = $li.find('.share-menu > .icon-loading-small'); + var self = this + var $target = $(event.target) + var $li = $target.closest('li[data-share-id]') + var shareId = $li.data('share-id') + var $loading = $li.find('.share-menu > .icon-loading-small') - if(!$loading.hasClass('hidden') && this.password === '') { + if (!$loading.hasClass('hidden') && this.password === '') { // in process - return false; + return false } // hide all icons and show loading - $li.find('.icon').addClass('hidden'); - $loading.removeClass('hidden'); + $li.find('.icon').addClass('hidden') + $loading.removeClass('hidden') // hide menu - OC.hideMenus(); + OC.hideMenus() var shareData = {} - var isPasswordEnforced = this.configModel.get('enforcePasswordForPublicLink'); - var isExpirationEnforced = this.configModel.get('isDefaultExpireDateEnforced'); + var isPasswordEnforced = this.configModel.get('enforcePasswordForPublicLink') + var isExpirationEnforced = this.configModel.get('isDefaultExpireDateEnforced') // set default expire date if (isExpirationEnforced) { - var defaultExpireDays = this.configModel.get('defaultExpireDate'); + var defaultExpireDays = this.configModel.get('defaultExpireDate') var expireDate = moment().add(defaultExpireDays, 'day').format('DD-MM-YYYY') - shareData.expireDate = expireDate; + shareData.expireDate = expireDate } // if password is set, add to data @@ -224,25 +225,25 @@ shareData.password = this.password } - var newShareId = false; + var newShareId = false // We need a password before the share creation if (isPasswordEnforced && !this.showPending && this.password === '') { - this.showPending = shareId; - var self = this.render(); - self.$el.find('.pending #enforcedPassText').focus(); + this.showPending = shareId + var self = this.render() + self.$el.find('.pending #enforcedPassText').focus() } else { // else, we have a password or it is not enforced $.when(this.model.saveLinkShare(shareData, { success: function() { - $loading.addClass('hidden'); - $li.find('.icon').removeClass('hidden'); - self.render(); + $loading.addClass('hidden') + $li.find('.icon').removeClass('hidden') + self.render() // open the menu by default // we can only do that after the render if (newShareId) { - var shares = self.$el.find('li[data-share-id]'); - var $newShare = self.$el.find('li[data-share-id="'+newShareId+'"]'); + var shares = self.$el.find('li[data-share-id]') + var $newShare = self.$el.find('li[data-share-id="' + newShareId + '"]') } }, error: function() { @@ -253,49 +254,49 @@ self.password = '' if (isPasswordEnforced && response && response.responseJSON && response.responseJSON.ocs.meta && response.responseJSON.ocs.meta.message) { var $input = self.$el.find('.pending #enforcedPassText') - $input.tooltip('destroy'); - $input.attr('title', response.responseJSON.ocs.meta.message); - $input.tooltip({placement: 'bottom', trigger: 'manual'}); - $input.tooltip('show'); + $input.tooltip('destroy') + $input.attr('title', response.responseJSON.ocs.meta.message) + $input.tooltip({ placement: 'bottom', trigger: 'manual' }) + $input.tooltip('show') } else { - OC.Notification.showTemporary(t('core', 'Unable to create a link share')); - $loading.addClass('hidden'); - $li.find('.icon').removeClass('hidden'); + OC.Notification.showTemporary(t('core', 'Unable to create a link share')) + $loading.addClass('hidden') + $li.find('.icon').removeClass('hidden') } }).then(function(response) { // resolve before success newShareId = response.ocs.data.id - }); + }) } }, enforcedPasswordSet: function(event) { - event.preventDefault(); - var $form = $(event.target); - var $input = $form.find('input.enforcedPassText'); - this.password = $input.val(); - this.showPending = false; - this.newShare(event); + event.preventDefault() + var $form = $(event.target) + var $input = $form.find('input.enforcedPassText') + this.password = $input.val() + this.showPending = false + this.newShare(event) }, onLinkTextClick: function(event) { - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var $el = $li.find('.linkText'); - $el.focus(); - $el.select(); + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var $el = $li.find('.linkText') + $el.focus() + $el.select() }, onHideDownloadChange: function(event) { - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); - var $checkbox = $li.find('.hideDownloadCheckbox'); - $checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock'); - - var hideDownload = false; - if($checkbox.is(':checked')) { - hideDownload = true; + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') + var $checkbox = $li.find('.hideDownloadCheckbox') + $checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock') + + var hideDownload = false + if ($checkbox.is(':checked')) { + hideDownload = true } this.model.saveLinkShare({ @@ -303,103 +304,103 @@ cid: shareId }, { success: function() { - $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock'); + $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock') }, error: function(obj, msg) { - OC.Notification.showTemporary(t('core', 'Unable to toggle this option')); - $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock'); + OC.Notification.showTemporary(t('core', 'Unable to toggle this option')) + $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock') } - }); + }) }, onShowPasswordClick: function(event) { - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); - $li.find('.linkPass').slideToggle(OC.menuSpeed); - $li.find('.linkPassMenu').toggleClass('hidden'); - if(!$li.find('.showPasswordCheckbox').is(':checked')) { + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') + $li.find('.linkPass').slideToggle(OC.menuSpeed) + $li.find('.linkPassMenu').toggleClass('hidden') + if (!$li.find('.showPasswordCheckbox').is(':checked')) { this.model.saveLinkShare({ password: '', cid: shareId - }); + }) } else { if (!OC.Util.isIE()) { - $li.find('.linkPassText').focus(); + $li.find('.linkPassText').focus() } } }, onPasswordKeyUp: function(event) { - if(event.keyCode === 13) { - this.onPasswordEntered(event); + if (event.keyCode === 13) { + this.onPasswordEntered(event) } }, onPasswordEntered: function(event) { - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); - var $loading = $li.find('.linkPassMenu .icon-loading-small'); + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') + var $loading = $li.find('.linkPassMenu .icon-loading-small') if (!$loading.hasClass('hidden')) { // still in process - return; + return } - var $input = $li.find('.linkPassText'); - $input.removeClass('error'); - $input.parent().find('input').removeClass('error'); - var password = $input.val(); + var $input = $li.find('.linkPassText') + $input.removeClass('error') + $input.parent().find('input').removeClass('error') + var password = $input.val() if ($li.find('.linkPassText').attr('placeholder') === PASSWORD_PLACEHOLDER_MESSAGE_OPTIONAL) { // in IE9 the password might be the placeholder due to bugs in the placeholders polyfill - if(password === PASSWORD_PLACEHOLDER_MESSAGE_OPTIONAL) { - password = ''; + if (password === PASSWORD_PLACEHOLDER_MESSAGE_OPTIONAL) { + password = '' } } else { // in IE9 the password might be the placeholder due to bugs in the placeholders polyfill - if(password === '' || password === PASSWORD_PLACEHOLDER || password === PASSWORD_PLACEHOLDER_MESSAGE) { - return; + if (password === '' || password === PASSWORD_PLACEHOLDER || password === PASSWORD_PLACEHOLDER_MESSAGE) { + return } } $loading .removeClass('hidden') - .addClass('inlineblock'); + .addClass('inlineblock') this.model.saveLinkShare({ password: password, cid: shareId }, { complete: function(model) { - $loading.removeClass('inlineblock').addClass('hidden'); + $loading.removeClass('inlineblock').addClass('hidden') }, error: function(model, msg) { // Add visual feedback to both the input and the submit button - $input.parent().find('input').addClass('error'); + $input.parent().find('input').addClass('error') // destroy old tooltips - var $container = $input.parent(); - $container.tooltip('destroy'); - $input.addClass('error'); - $container.attr('title', msg); - $container.tooltip({placement: 'bottom', trigger: 'manual'}); - $container.tooltip('show'); + var $container = $input.parent() + $container.tooltip('destroy') + $input.addClass('error') + $container.attr('title', msg) + $container.tooltip({ placement: 'bottom', trigger: 'manual' }) + $container.tooltip('show') } - }); + }) }, onPasswordByTalkChange: function(event) { - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); - var $checkbox = $li.find('.passwordByTalkCheckbox'); - $checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock'); - - var sendPasswordByTalk = false; - if($checkbox.is(':checked')) { - sendPasswordByTalk = true; + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') + var $checkbox = $li.find('.passwordByTalkCheckbox') + $checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock') + + var sendPasswordByTalk = false + if ($checkbox.is(':checked')) { + sendPasswordByTalk = true } this.model.saveLinkShare({ @@ -407,25 +408,25 @@ cid: shareId }, { success: function() { - $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock'); + $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock') }, error: function(obj, msg) { - OC.Notification.showTemporary(t('core', 'Unable to toggle this option')); - $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock'); + OC.Notification.showTemporary(t('core', 'Unable to toggle this option')) + $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock') } - }); + }) }, onAllowPublicEditingChange: function(event) { - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); - var $checkbox = $li.find('.publicEditingCheckbox'); - $checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock'); - - var permissions = OC.PERMISSION_READ; - if($checkbox.is(':checked')) { - permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_READ; + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') + var $checkbox = $li.find('.publicEditingCheckbox') + $checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock') + + var permissions = OC.PERMISSION_READ + if ($checkbox.is(':checked')) { + permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_READ } this.model.saveLinkShare({ @@ -433,168 +434,165 @@ cid: shareId }, { success: function() { - $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock'); + $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock') }, error: function(obj, msg) { - OC.Notification.showTemporary(t('core', 'Unable to toggle this option')); - $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock'); + OC.Notification.showTemporary(t('core', 'Unable to toggle this option')) + $checkbox.siblings('.icon-loading-small').addClass('hidden').removeClass('inlineblock') } - }); + }) }, - onPublicUploadChange: function(event) { - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); - var permissions = event.currentTarget.value; + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') + var permissions = event.currentTarget.value this.model.saveLinkShare({ permissions: permissions, cid: shareId - }); + }) }, showNoteForm: function(event) { - event.preventDefault(); - event.stopPropagation(); - var self = this; - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var $menu = $element.closest('li'); - var $form = $menu.next('li.share-note-form'); + event.preventDefault() + event.stopPropagation() + var self = this + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var $menu = $element.closest('li') + var $form = $menu.next('li.share-note-form') // show elements - $menu.find('.share-note-delete').toggleClass('hidden'); - $form.toggleClass('hidden'); - $form.find('textarea').focus(); + $menu.find('.share-note-delete').toggleClass('hidden') + $form.toggleClass('hidden') + $form.find('textarea').focus() }, deleteNote: function(event) { - event.preventDefault(); - event.stopPropagation(); - var self = this; - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); - var $menu = $element.closest('li'); - var $form = $menu.next('li.share-note-form'); + event.preventDefault() + event.stopPropagation() + var self = this + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') + var $menu = $element.closest('li') + var $form = $menu.next('li.share-note-form') - $form.find('.share-note').val(''); + $form.find('.share-note').val('') - $form.addClass('hidden'); - $menu.find('.share-note-delete').addClass('hidden'); + $form.addClass('hidden') + $menu.find('.share-note-delete').addClass('hidden') - self.sendNote('', shareId, $menu); + self.sendNote('', shareId, $menu) }, updateNote: function(event) { - event.preventDefault(); - event.stopPropagation(); - var self = this; - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); - var $form = $element.closest('li.share-note-form'); - var $menu = $form.prev('li'); - var message = $form.find('.share-note').val().trim(); + event.preventDefault() + event.stopPropagation() + var self = this + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') + var $form = $element.closest('li.share-note-form') + var $menu = $form.prev('li') + var message = $form.find('.share-note').val().trim() if (message.length < 1) { - return; + return } - self.sendNote(message, shareId, $menu); + self.sendNote(message, shareId, $menu) }, sendNote: function(note, shareId, $menu) { - var $form = $menu.next('li.share-note-form'); - var $submit = $form.find('input.share-note-submit'); - var $error = $form.find('input.share-note-error'); + var $form = $menu.next('li.share-note-form') + var $submit = $form.find('input.share-note-submit') + var $error = $form.find('input.share-note-error') - $submit.prop('disabled', true); - $menu.find('.icon-loading-small').removeClass('hidden'); - $menu.find('.icon-edit').hide(); + $submit.prop('disabled', true) + $menu.find('.icon-loading-small').removeClass('hidden') + $menu.find('.icon-edit').hide() var complete = function() { - $submit.prop('disabled', false); - $menu.find('.icon-loading-small').addClass('hidden'); - $menu.find('.icon-edit').show(); - }; + $submit.prop('disabled', false) + $menu.find('.icon-loading-small').addClass('hidden') + $menu.find('.icon-edit').show() + } var error = function() { - $error.show(); + $error.show() setTimeout(function() { - $error.hide(); - }, 3000); - }; + $error.hide() + }, 3000) + } // send data $.ajax({ method: 'PUT', - url: OC.linkToOCS('apps/files_sharing/api/v1/shares',2) + shareId + '?' + OC.buildQueryString({format: 'json'}), + url: OC.linkToOCS('apps/files_sharing/api/v1/shares', 2) + shareId + '?' + OC.buildQueryString({ format: 'json' }), data: { note: note }, - complete : complete, + complete: complete, error: error - }); + }) }, render: function() { - this.$el.find('.has-tooltip').tooltip(); + this.$el.find('.has-tooltip').tooltip() // reset previously set passwords - this.password = ''; + this.password = '' - var linkShareTemplate = this.template(); - var resharingAllowed = this.model.sharePermissionPossible(); + var linkShareTemplate = this.template() + var resharingAllowed = this.model.sharePermissionPossible() - if(!resharingAllowed + if (!resharingAllowed || !this.showLink - || !this.configModel.isShareWithLinkAllowed()) - { - var templateData = {shareAllowed: false}; + || !this.configModel.isShareWithLinkAllowed()) { + var templateData = { shareAllowed: false } if (!resharingAllowed) { // add message - templateData.noSharingPlaceholder = t('core', 'Resharing is not allowed'); + templateData.noSharingPlaceholder = t('core', 'Resharing is not allowed') } - this.$el.html(linkShareTemplate(templateData)); - return this; + this.$el.html(linkShareTemplate(templateData)) + return this } - var publicUpload = - this.model.isFolder() + var publicUpload + = this.model.isFolder() && this.model.createPermissionPossible() - && this.configModel.isPublicUploadEnabled(); + && this.configModel.isPublicUploadEnabled() - - var publicEditingChecked = ''; - if(this.model.isPublicEditingAllowed()) { - publicEditingChecked = 'checked="checked"'; + var publicEditingChecked = '' + if (this.model.isPublicEditingAllowed()) { + publicEditingChecked = 'checked="checked"' } - var isPasswordEnforced = this.configModel.get('enforcePasswordForPublicLink'); - var isPasswordEnabledByDefault = this.configModel.get('enableLinkPasswordByDefault') === true; + var isPasswordEnforced = this.configModel.get('enforcePasswordForPublicLink') + var isPasswordEnabledByDefault = this.configModel.get('enableLinkPasswordByDefault') === true var passwordPlaceholderInitial = this.configModel.get('enforcePasswordForPublicLink') - ? PASSWORD_PLACEHOLDER_MESSAGE : PASSWORD_PLACEHOLDER_MESSAGE_OPTIONAL; + ? PASSWORD_PLACEHOLDER_MESSAGE : PASSWORD_PLACEHOLDER_MESSAGE_OPTIONAL - var publicEditable = - !this.model.isFolder() - && this.model.updatePermissionPossible(); + var publicEditable + = !this.model.isFolder() + && this.model.updatePermissionPossible() - var isExpirationEnforced = this.configModel.get('isDefaultExpireDateEnforced'); + var isExpirationEnforced = this.configModel.get('isDefaultExpireDateEnforced') // what if there is another date picker on that page? - var minDate = new Date(); + var minDate = new Date() // min date should always be the next day - minDate.setDate(minDate.getDate()+1); + minDate.setDate(minDate.getDate() + 1) $.datepicker.setDefaults({ minDate: minDate - }); + }) - this.$el.find('.datepicker').datepicker({dateFormat : 'dd-mm-yy'}); + this.$el.find('.datepicker').datepicker({ dateFormat: 'dd-mm-yy' }) var minPasswordLength = 4 // password policy? - if(OC.getCapabilities().password_policy && OC.getCapabilities().password_policy.minLength) { - minPasswordLength = OC.getCapabilities().password_policy.minLength; + if (OC.getCapabilities().password_policy && OC.getCapabilities().password_policy.minLength) { + minPasswordLength = OC.getCapabilities().password_policy.minLength } var popoverBase = { @@ -624,34 +622,34 @@ addNoteLabel: t('core', 'Note to recipient'), unshareLabel: t('core', 'Unshare'), unshareLinkLabel: t('core', 'Delete share link'), - newShareLabel: t('core', 'Add another link'), - }; + newShareLabel: t('core', 'Add another link') + } var pendingPopover = { isPasswordEnforced: isPasswordEnforced, enforcedPasswordLabel: t('core', 'Password protection for links is mandatory'), passwordPlaceholder: passwordPlaceholderInitial, - minPasswordLength: minPasswordLength, - }; + minPasswordLength: minPasswordLength + } var pendingPopoverMenu = this.pendingPopoverMenuTemplate(_.extend({}, pendingPopover)) - var linkShares = this.getShareeList(); - if(_.isArray(linkShares)) { + var linkShares = this.getShareeList() + if (_.isArray(linkShares)) { for (var i = 0; i < linkShares.length; i++) { - var social = []; - OC.Share.Social.Collection.each(function (model) { - var url = model.get('url'); - url = url.replace('{{reference}}', linkShares[i].shareLinkURL); + var social = [] + OC.Share.Social.Collection.each(function(model) { + var url = model.get('url') + url = url.replace('{{reference}}', linkShares[i].shareLinkURL) social.push({ url: url, - label: t('core', 'Share to {name}', {name: model.get('name')}), + label: t('core', 'Share to {name}', { name: model.get('name') }), name: model.get('name'), iconClass: model.get('iconClass'), newWindow: model.get('newWindow') - }); - }); + }) + }) var popover = this.getPopoverObject(linkShares[i]) - linkShares[i].popoverMenu = this.popoverMenuTemplate(_.extend({}, popoverBase, popover, {social: social})); + linkShares[i].popoverMenu = this.popoverMenuTemplate(_.extend({}, popoverBase, popover, { social: social })) linkShares[i].pendingPopoverMenu = pendingPopoverMenu } } @@ -664,33 +662,33 @@ newShareTitle: t('core', 'New share link'), pendingPopoverMenu: pendingPopoverMenu, showPending: this.showPending === this.newShareId, - newShareId: this.newShareId, - })); + newShareId: this.newShareId + })) - this.delegateEvents(); + this.delegateEvents() // new note autosize - autosize(this.$el.find('.share-note-form .share-note')); + autosize(this.$el.find('.share-note-form .share-note')) - return this; + return this }, onToggleMenu: function(event) { - event.preventDefault(); - event.stopPropagation(); - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var $menu = $li.find('.sharingOptionsGroup .popovermenu'); - var shareId = $li.data('share-id'); + event.preventDefault() + event.stopPropagation() + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var $menu = $li.find('.sharingOptionsGroup .popovermenu') + var shareId = $li.data('share-id') - OC.showMenu(null, $menu); + OC.showMenu(null, $menu) // focus the password if not set and enforced - var isPasswordEnabledByDefault = this.configModel.get('enableLinkPasswordByDefault') === true; - var haspassword = $menu.find('.linkPassText').val() !== ''; + var isPasswordEnabledByDefault = this.configModel.get('enableLinkPasswordByDefault') === true + var haspassword = $menu.find('.linkPassText').val() !== '' if (!haspassword && isPasswordEnabledByDefault) { - $menu.find('.linkPassText').focus(); + $menu.find('.linkPassText').focus() } }, @@ -698,8 +696,8 @@ * @returns {Function} from Handlebars * @private */ - template: function () { - return OC.Share.Templates['sharedialoglinkshareview']; + template: function() { + return OC.Share.Templates['sharedialoglinkshareview'] }, /** @@ -709,7 +707,7 @@ * @returns {string} */ popoverMenuTemplate: function(data) { - return OC.Share.Templates['sharedialoglinkshareview_popover_menu'](data); + return OC.Share.Templates['sharedialoglinkshareview_popover_menu'](data) }, /** @@ -719,85 +717,85 @@ * @returns {string} */ pendingPopoverMenuTemplate: function(data) { - return OC.Share.Templates['sharedialoglinkshareview_popover_menu_pending'](data); + return OC.Share.Templates['sharedialoglinkshareview_popover_menu_pending'](data) }, onPopUpClick: function(event) { - event.preventDefault(); - event.stopPropagation(); + event.preventDefault() + event.stopPropagation() - var url = $(event.currentTarget).data('url'); - var newWindow = $(event.currentTarget).data('window'); - $(event.currentTarget).tooltip('hide'); + var url = $(event.currentTarget).data('url') + var newWindow = $(event.currentTarget).data('window') + $(event.currentTarget).tooltip('hide') if (url) { if (newWindow === true) { - var width = 600; - var height = 400; - var left = (screen.width / 2) - (width / 2); - var top = (screen.height / 2) - (height / 2); + var width = 600 + var height = 400 + var left = (screen.width / 2) - (width / 2) + var top = (screen.height / 2) - (height / 2) - window.open(url, 'name', 'width=' + width + ', height=' + height + ', top=' + top + ', left=' + left); + window.open(url, 'name', 'width=' + width + ', height=' + height + ', top=' + top + ', left=' + left) } else { - window.location.href = url; + window.location.href = url } } }, onExpireDateChange: function(event) { - var $element = $(event.target); - var li = $element.closest('li[data-share-id]'); - var shareId = li.data('share-id'); - var expirationDatePicker = '#expirationDateContainer-' + shareId; - var datePicker = $(expirationDatePicker); - var state = $element.prop('checked'); - datePicker.toggleClass('hidden', !state); + var $element = $(event.target) + var li = $element.closest('li[data-share-id]') + var shareId = li.data('share-id') + var expirationDatePicker = '#expirationDateContainer-' + shareId + var datePicker = $(expirationDatePicker) + var state = $element.prop('checked') + datePicker.toggleClass('hidden', !state) if (!state) { // disabled, let's hide the input and // set the expireDate to nothing - $element.closest('li').next('li').addClass('hidden'); - this.setExpirationDate('', shareId); + $element.closest('li').next('li').addClass('hidden') + this.setExpirationDate('', shareId) } else { // enabled, show the input and the datepicker - $element.closest('li').next('li').removeClass('hidden'); - this.showDatePicker(event); + $element.closest('li').next('li').removeClass('hidden') + this.showDatePicker(event) } }, showDatePicker: function(event) { - var $element = $(event.target); - var li = $element.closest('li[data-share-id]'); - var shareId = li.data('share-id'); - var maxDate = $element.data('max-date'); - var expirationDatePicker = '#expirationDatePicker-' + shareId; - var self = this; + var $element = $(event.target) + var li = $element.closest('li[data-share-id]') + var shareId = li.data('share-id') + var maxDate = $element.data('max-date') + var expirationDatePicker = '#expirationDatePicker-' + shareId + var self = this $(expirationDatePicker).datepicker({ - dateFormat : 'dd-mm-yy', - onSelect: function (expireDate) { - self.setExpirationDate(expireDate, shareId); + dateFormat: 'dd-mm-yy', + onSelect: function(expireDate) { + self.setExpirationDate(expireDate, shareId) }, maxDate: maxDate - }); - $(expirationDatePicker).datepicker('show'); - $(expirationDatePicker).focus(); + }) + $(expirationDatePicker).datepicker('show') + $(expirationDatePicker).focus() }, setExpirationDate: function(expireDate, shareId) { - this.model.saveLinkShare({expireDate: expireDate, cid: shareId}); + this.model.saveLinkShare({ expireDate: expireDate, cid: shareId }) }, onChangeExpirationDate: function(event) { - var $element = $(event.target); - var expireDate = $element.val(); - var li = $element.closest('li[data-share-id]'); - var shareId = li.data('share-id'); - var expirationDatePicker = '#expirationDatePicker-' + shareId; - - this.setExpirationDate(expireDate, shareId); - $(expirationDatePicker).datepicker('hide'); + var $element = $(event.target) + var expireDate = $element.val() + var li = $element.closest('li[data-share-id]') + var shareId = li.data('share-id') + var expirationDatePicker = '#expirationDatePicker-' + shareId + + this.setExpirationDate(expireDate, shareId) + $(expirationDatePicker).datepicker('hide') }, /** @@ -806,21 +804,21 @@ * @returns {Array} */ getShareeList: function() { - var shares = this.model.get('linkShares'); + var shares = this.model.get('linkShares') - if(!this.model.hasLinkShares()) { - return []; + if (!this.model.hasLinkShares()) { + return [] } - var list = []; - for(var index = 0; index < shares.length; index++) { - var share = this.getShareeObject(index); + var list = [] + for (var index = 0; index < shares.length; index++) { + var share = this.getShareeObject(index) // first empty {} is necessary, otherwise we get in trouble // with references - list.push(_.extend({}, share)); + list.push(_.extend({}, share)) } - return list; + return list }, /** @@ -829,7 +827,7 @@ * @returns {object} */ getShareeObject: function(shareIndex) { - var share = this.model.get('linkShares')[shareIndex]; + var share = this.model.get('linkShares')[shareIndex] return _.extend({}, share, { cid: share.id, @@ -845,53 +843,53 @@ }, getPopoverObject: function(share) { - var publicUploadRWChecked = ''; - var publicUploadRChecked = ''; - var publicUploadWChecked = ''; + var publicUploadRWChecked = '' + var publicUploadRChecked = '' + var publicUploadWChecked = '' switch (this.model.linkSharePermissions(share.id)) { - case OC.PERMISSION_READ: - publicUploadRChecked = 'checked'; - break; - case OC.PERMISSION_CREATE: - publicUploadWChecked = 'checked'; - break; - case OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE | OC.PERMISSION_READ | OC.PERMISSION_DELETE: - publicUploadRWChecked = 'checked'; - break; - } - - var isPasswordSet = !!share.password; - var isPasswordEnabledByDefault = this.configModel.get('enableLinkPasswordByDefault') === true; - var isPasswordEnforced = this.configModel.get('enforcePasswordForPublicLink'); - var isExpirationEnforced = this.configModel.get('isDefaultExpireDateEnforced'); - var defaultExpireDays = this.configModel.get('defaultExpireDate'); - var hasExpireDate = !!share.expiration || isExpirationEnforced; - - var expireDate; + case OC.PERMISSION_READ: + publicUploadRChecked = 'checked' + break + case OC.PERMISSION_CREATE: + publicUploadWChecked = 'checked' + break + case OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE | OC.PERMISSION_READ | OC.PERMISSION_DELETE: + publicUploadRWChecked = 'checked' + break + } + + var isPasswordSet = !!share.password + var isPasswordEnabledByDefault = this.configModel.get('enableLinkPasswordByDefault') === true + var isPasswordEnforced = this.configModel.get('enforcePasswordForPublicLink') + var isExpirationEnforced = this.configModel.get('isDefaultExpireDateEnforced') + var defaultExpireDays = this.configModel.get('defaultExpireDate') + var hasExpireDate = !!share.expiration || isExpirationEnforced + + var expireDate if (hasExpireDate) { - expireDate = moment(share.expiration, 'YYYY-MM-DD').format('DD-MM-YYYY'); + expireDate = moment(share.expiration, 'YYYY-MM-DD').format('DD-MM-YYYY') } - var isTalkEnabled = OC.appswebroots['spreed'] !== undefined; - var sendPasswordByTalk = share.sendPasswordByTalk; + var isTalkEnabled = OC.appswebroots['spreed'] !== undefined + var sendPasswordByTalk = share.sendPasswordByTalk - var hideDownload = share.hideDownload; + var hideDownload = share.hideDownload - var maxDate = null; + var maxDate = null - if(hasExpireDate) { - if(isExpirationEnforced) { + if (hasExpireDate) { + if (isExpirationEnforced) { // TODO: hack: backend returns string instead of integer - var shareTime = share.stime; + var shareTime = share.stime if (_.isNumber(shareTime)) { - shareTime = new Date(shareTime * 1000); + shareTime = new Date(shareTime * 1000) } if (!shareTime) { - shareTime = new Date(); // now + shareTime = new Date() // now } - shareTime = OC.Util.stripTime(shareTime).getTime(); - maxDate = new Date(shareTime + defaultExpireDays * 24 * 3600 * 1000); + shareTime = OC.Util.stripTime(shareTime).getTime() + maxDate = new Date(shareTime + defaultExpireDays * 24 * 3600 * 1000) } } @@ -912,46 +910,45 @@ hasNote: share.note !== '', maxDate: maxDate, hideDownload: hideDownload, - isExpirationEnforced: isExpirationEnforced, + isExpirationEnforced: isExpirationEnforced } }, onUnshare: function(event) { - event.preventDefault(); - event.stopPropagation(); - var self = this; - var $element = $(event.target); + event.preventDefault() + event.stopPropagation() + var self = this + var $element = $(event.target) if (!$element.is('a')) { - $element = $element.closest('a'); + $element = $element.closest('a') } - var $loading = $element.find('.icon-loading-small').eq(0); - if(!$loading.hasClass('hidden')) { + var $loading = $element.find('.icon-loading-small').eq(0) + if (!$loading.hasClass('hidden')) { // in process - return false; + return false } - $loading.removeClass('hidden'); + $loading.removeClass('hidden') - var $li = $element.closest('li[data-share-id]'); + var $li = $element.closest('li[data-share-id]') - var shareId = $li.data('share-id'); + var shareId = $li.data('share-id') self.model.removeShare(shareId, { success: function() { - $li.remove(); + $li.remove() self.render() }, error: function() { - $loading.addClass('hidden'); - OC.Notification.showTemporary(t('core', 'Could not unshare')); + $loading.addClass('hidden') + OC.Notification.showTemporary(t('core', 'Could not unshare')) } - }); - return false; - }, - + }) + return false + } - }); + }) - OC.Share.ShareDialogLinkShareView = ShareDialogLinkShareView; + OC.Share.ShareDialogLinkShareView = ShareDialogLinkShareView -})(); +})() diff --git a/core/js/sharedialogresharerinfoview.js b/core/js/sharedialogresharerinfoview.js index f55f2bcc5277094fd7406db3e014418a5c352cc3..b235adf435253571ddd132fa7895bc7084ce3a78 100644 --- a/core/js/sharedialogresharerinfoview.js +++ b/core/js/sharedialogresharerinfoview.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2015 * @@ -12,7 +13,7 @@ (function() { if (!OC.Share) { - OC.Share = {}; + OC.Share = {} } /** @@ -42,32 +43,31 @@ _template: undefined, initialize: function(options) { - var view = this; + var view = this this.model.on('change:reshare', function() { - view.render(); - }); + view.render() + }) - if(!_.isUndefined(options.configModel)) { - this.configModel = options.configModel; + if (!_.isUndefined(options.configModel)) { + this.configModel = options.configModel } else { - throw 'missing OC.Share.ShareConfigModel'; + throw 'missing OC.Share.ShareConfigModel' } }, render: function() { if (!this.model.hasReshare() - || this.model.getReshareOwner() === OC.currentUser) - { - this.$el.empty(); - return this; + || this.model.getReshareOwner() === OC.currentUser) { + this.$el.empty() + return this } - var reshareTemplate = this.template(); - var ownerDisplayName = this.model.getReshareOwnerDisplayname(); - var shareNote = this.model.getReshareNote(); - - var sharedByText = ''; + var reshareTemplate = this.template() + var ownerDisplayName = this.model.getReshareOwnerDisplayname() + var shareNote = this.model.getReshareNote() + + var sharedByText = '' if (this.model.getReshareType() === OC.Share.SHARE_TYPE_GROUP) { sharedByText = t( @@ -78,8 +78,8 @@ owner: ownerDisplayName }, undefined, - {escape: false} - ); + { escape: false } + ) } else if (this.model.getReshareType() === OC.Share.SHARE_TYPE_CIRCLE) { sharedByText = t( 'core', @@ -89,8 +89,8 @@ owner: ownerDisplayName }, undefined, - {escape: false} - ); + { escape: false } + ) } else if (this.model.getReshareType() === OC.Share.SHARE_TYPE_ROOM) { if (this.model.get('reshare').share_with_displayname) { sharedByText = t( @@ -101,8 +101,8 @@ owner: ownerDisplayName }, undefined, - {escape: false} - ); + { escape: false } + ) } else { sharedByText = t( 'core', @@ -111,8 +111,8 @@ owner: ownerDisplayName }, undefined, - {escape: false} - ); + { escape: false } + ) } } else { sharedByText = t( @@ -120,42 +120,40 @@ 'Shared with you by {owner}', { owner: ownerDisplayName }, undefined, - {escape: false} - ); + { escape: false } + ) } - - this.$el.html(reshareTemplate({ reshareOwner: this.model.getReshareOwner(), sharedByText: sharedByText, shareNote: shareNote, hasShareNote: shareNote !== '' - })); + })) this.$el.find('.avatar').each(function() { - var $this = $(this); - $this.avatar($this.data('username'), 32); - }); + var $this = $(this) + $this.avatar($this.data('username'), 32) + }) this.$el.find('.reshare').contactsMenu( this.model.getReshareOwner(), OC.Share.SHARE_TYPE_USER, - this.$el); + this.$el) - return this; + return this }, /** * @returns {Function} from Handlebars * @private */ - template: function () { - return OC.Share.Templates['sharedialogresharerinfoview']; + template: function() { + return OC.Share.Templates['sharedialogresharerinfoview'] } - }); + }) - OC.Share.ShareDialogResharerInfoView = ShareDialogResharerInfoView; + OC.Share.ShareDialogResharerInfoView = ShareDialogResharerInfoView -})(); +})() diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index 088adaff318a9c0b193ce5c41506b01e1b44fcb4..5fdb352fd50a4cb53afb0c166df03867c6872ca3 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* global OC, Handlebars */ /* @@ -14,11 +15,11 @@ (function() { - var PASSWORD_PLACEHOLDER = '**********'; - var PASSWORD_PLACEHOLDER_MESSAGE = t('core', 'Choose a password for the mail share'); + var PASSWORD_PLACEHOLDER = '**********' + var PASSWORD_PLACEHOLDER_MESSAGE = t('core', 'Choose a password for the mail share') if (!OC.Share) { - OC.Share = {}; + OC.Share = {} } /** @@ -50,27 +51,27 @@ 'click .share-note-submit': 'updateNote', 'click .share-menu .icon-more': 'onToggleMenu', 'click .permissions': 'onPermissionChange', - 'click .expireDate' : 'onExpireDateChange', - 'click .password' : 'onMailSharePasswordProtectChange', - 'click .passwordByTalk' : 'onMailSharePasswordProtectByTalkChange', - 'click .secureDrop' : 'onSecureDropChange', + 'click .expireDate': 'onExpireDateChange', + 'click .password': 'onMailSharePasswordProtectChange', + 'click .passwordByTalk': 'onMailSharePasswordProtectByTalkChange', + 'click .secureDrop': 'onSecureDropChange', 'keyup input.passwordField': 'onMailSharePasswordKeyUp', 'focusout input.passwordField': 'onMailSharePasswordEntered', 'change .datepicker': 'onChangeExpirationDate', - 'click .datepicker' : 'showDatePicker' + 'click .datepicker': 'showDatePicker' }, initialize: function(options) { - if(!_.isUndefined(options.configModel)) { - this.configModel = options.configModel; + if (!_.isUndefined(options.configModel)) { + this.configModel = options.configModel } else { - throw 'missing OC.Share.ShareConfigModel'; + throw 'missing OC.Share.ShareConfigModel' } - var view = this; + var view = this this.model.on('change:shares', function() { - view.render(); - }); + view.render() + }) }, /** @@ -79,63 +80,62 @@ * @returns {object} */ getShareeObject: function(shareIndex) { - var shareWith = this.model.getShareWith(shareIndex); - var shareWithDisplayName = this.model.getShareWithDisplayName(shareIndex); - var shareWithAvatar = this.model.getShareWithAvatar(shareIndex); - var shareWithTitle = ''; - var shareType = this.model.getShareType(shareIndex); - var sharedBy = this.model.getSharedBy(shareIndex); - var sharedByDisplayName = this.model.getSharedByDisplayName(shareIndex); - var fileOwnerUid = this.model.getFileOwnerUid(shareIndex); - - var hasPermissionOverride = {}; + var shareWith = this.model.getShareWith(shareIndex) + var shareWithDisplayName = this.model.getShareWithDisplayName(shareIndex) + var shareWithAvatar = this.model.getShareWithAvatar(shareIndex) + var shareWithTitle = '' + var shareType = this.model.getShareType(shareIndex) + var sharedBy = this.model.getSharedBy(shareIndex) + var sharedByDisplayName = this.model.getSharedByDisplayName(shareIndex) + var fileOwnerUid = this.model.getFileOwnerUid(shareIndex) + + var hasPermissionOverride = {} if (shareType === OC.Share.SHARE_TYPE_GROUP) { - shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'group') + ')'; + shareWithDisplayName = shareWithDisplayName + ' (' + t('core', 'group') + ')' } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) { - shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'remote') + ')'; + shareWithDisplayName = shareWithDisplayName + ' (' + t('core', 'remote') + ')' } else if (shareType === OC.Share.SHARE_TYPE_REMOTE_GROUP) { - shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'remote group') + ')'; + shareWithDisplayName = shareWithDisplayName + ' (' + t('core', 'remote group') + ')' } else if (shareType === OC.Share.SHARE_TYPE_EMAIL) { - shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'email') + ')'; + shareWithDisplayName = shareWithDisplayName + ' (' + t('core', 'email') + ')' } else if (shareType === OC.Share.SHARE_TYPE_CIRCLE) { } else if (shareType === OC.Share.SHARE_TYPE_ROOM) { - shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'conversation') + ')'; + shareWithDisplayName = shareWithDisplayName + ' (' + t('core', 'conversation') + ')' } if (shareType === OC.Share.SHARE_TYPE_GROUP) { - shareWithTitle = shareWith + " (" + t('core', 'group') + ')'; + shareWithTitle = shareWith + ' (' + t('core', 'group') + ')' } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) { - shareWithTitle = shareWith + " (" + t('core', 'remote') + ')'; + shareWithTitle = shareWith + ' (' + t('core', 'remote') + ')' } else if (shareType === OC.Share.SHARE_TYPE_REMOTE_GROUP) { - shareWithTitle = shareWith + " (" + t('core', 'remote group') + ')'; - } - else if (shareType === OC.Share.SHARE_TYPE_EMAIL) { - shareWithTitle = shareWith + " (" + t('core', 'email') + ')'; + shareWithTitle = shareWith + ' (' + t('core', 'remote group') + ')' + } else if (shareType === OC.Share.SHARE_TYPE_EMAIL) { + shareWithTitle = shareWith + ' (' + t('core', 'email') + ')' } else if (shareType === OC.Share.SHARE_TYPE_CIRCLE) { - shareWithTitle = shareWith; + shareWithTitle = shareWith // Force "shareWith" in the template to a safe value, as the // original "shareWith" returned by the model may contain // problematic characters like "'". - shareWith = 'circle-' + shareIndex; + shareWith = 'circle-' + shareIndex } if (sharedBy !== OC.getCurrentUser().uid) { - var empty = shareWithTitle === ''; + var empty = shareWithTitle === '' if (!empty) { - shareWithTitle += ' ('; + shareWithTitle += ' (' } - shareWithTitle += t('core', 'shared by {sharer}', {sharer: sharedByDisplayName}); + shareWithTitle += t('core', 'shared by {sharer}', { sharer: sharedByDisplayName }) if (!empty) { - shareWithTitle += ')'; + shareWithTitle += ')' } } - var share = this.model.get('shares')[shareIndex]; - var password = share.password; - var hasPassword = password !== null && password !== ''; - var sendPasswordByTalk = share.send_password_by_talk; + var share = this.model.get('shares')[shareIndex] + var password = share.password + var hasPassword = password !== null && password !== '' + var sendPasswordByTalk = share.send_password_by_talk - var shareNote = this.model.getNote(shareIndex); + var shareNote = this.model.getNote(shareIndex) return _.extend(hasPermissionOverride, { cid: this.cid, @@ -178,8 +178,8 @@ // brief time between disabling sending the password by email // and receiving the updated share. passwordPlaceholder: hasPassword ? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE, - passwordByTalkPlaceholder: (hasPassword && sendPasswordByTalk)? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE, - }); + passwordByTalkPlaceholder: (hasPassword && sendPasswordByTalk) ? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE + }) }, getShareProperties: function() { @@ -212,7 +212,7 @@ deletePermission: OC.PERMISSION_DELETE, readPermission: OC.PERMISSION_READ, isFolder: this.model.isFolder() - }; + } }, /** @@ -221,160 +221,160 @@ * @returns {Array} */ getShareeList: function() { - var universal = this.getShareProperties(); + var universal = this.getShareProperties() - if(!this.model.hasUserShares()) { - return []; + if (!this.model.hasUserShares()) { + return [] } - var shares = this.model.get('shares'); - var list = []; - for(var index = 0; index < shares.length; index++) { - var share = this.getShareeObject(index); + var shares = this.model.get('shares') + var list = [] + for (var index = 0; index < shares.length; index++) { + var share = this.getShareeObject(index) if (share.shareType === OC.Share.SHARE_TYPE_LINK) { - continue; + continue } // first empty {} is necessary, otherwise we get in trouble // with references - list.push(_.extend({}, universal, share)); + list.push(_.extend({}, universal, share)) } - return list; + return list }, getLinkReshares: function() { var universal = { - unshareLabel: t('core', 'Unshare'), - }; + unshareLabel: t('core', 'Unshare') + } - if(!this.model.hasUserShares()) { - return []; + if (!this.model.hasUserShares()) { + return [] } - var shares = this.model.get('shares'); - var list = []; - for(var index = 0; index < shares.length; index++) { - var share = this.getShareeObject(index); + var shares = this.model.get('shares') + var list = [] + for (var index = 0; index < shares.length; index++) { + var share = this.getShareeObject(index) if (share.shareType !== OC.Share.SHARE_TYPE_LINK) { - continue; + continue } // first empty {} is necessary, otherwise we get in trouble // with references list.push(_.extend({}, universal, share, { shareInitiator: shares[index].uid_owner, - shareInitiatorText: t('core', '{shareInitiatorDisplayName} shared via link', {shareInitiatorDisplayName: shares[index].displayname_owner}) - })); + shareInitiatorText: t('core', '{shareInitiatorDisplayName} shared via link', { shareInitiatorDisplayName: shares[index].displayname_owner }) + })) } - return list; + return list }, render: function() { - if(!this._renderPermissionChange) { + if (!this._renderPermissionChange) { this.$el.html(this.template({ cid: this.cid, sharees: this.getShareeList(), linkReshares: this.getLinkReshares() - })); + })) - this.$('.avatar').each(function () { - var $this = $(this); + this.$('.avatar').each(function() { + var $this = $(this) if ($this.hasClass('imageplaceholderseed')) { - $this.css({width: 32, height: 32}); + $this.css({ width: 32, height: 32 }) if ($this.data('avatar')) { - $this.css('border-radius', '0%'); - $this.css('background', 'url(' + $this.data('avatar') + ') no-repeat'); - $this.css('background-size', '31px'); + $this.css('border-radius', '0%') + $this.css('background', 'url(' + $this.data('avatar') + ') no-repeat') + $this.css('background-size', '31px') } else { - $this.imageplaceholder($this.data('seed')); + $this.imageplaceholder($this.data('seed')) } } else { // user, size, ie8fix, hidedefault, callback, displayname - $this.avatar($this.data('username'), 32, undefined, undefined, undefined, $this.data('displayname')); + $this.avatar($this.data('username'), 32, undefined, undefined, undefined, $this.data('displayname')) } - }); + }) this.$('.has-tooltip').tooltip({ placement: 'bottom' - }); + }) this.$('ul.shareWithList > li').each(function() { - var $this = $(this); + var $this = $(this) - var shareWith = $this.data('share-with'); - var shareType = $this.data('share-type'); + var shareWith = $this.data('share-with') + var shareType = $this.data('share-type') - $this.find('div.avatar, span.username').contactsMenu(shareWith, shareType, $this); - }); + $this.find('div.avatar, span.username').contactsMenu(shareWith, shareType, $this) + }) } else { - var permissionChangeShareId = parseInt(this._renderPermissionChange, 10); - var shareWithIndex = this.model.findShareWithIndex(permissionChangeShareId); - var sharee = this.getShareeObject(shareWithIndex); - $.extend(sharee, this.getShareProperties()); - var $li = this.$('li[data-share-id=' + permissionChangeShareId + ']'); - $li.find('.sharingOptionsGroup .popovermenu').replaceWith(this.popoverMenuTemplate(sharee)); + var permissionChangeShareId = parseInt(this._renderPermissionChange, 10) + var shareWithIndex = this.model.findShareWithIndex(permissionChangeShareId) + var sharee = this.getShareeObject(shareWithIndex) + $.extend(sharee, this.getShareProperties()) + var $li = this.$('li[data-share-id=' + permissionChangeShareId + ']') + $li.find('.sharingOptionsGroup .popovermenu').replaceWith(this.popoverMenuTemplate(sharee)) } - var _this = this; + var _this = this this.getShareeList().forEach(function(sharee) { - var $edit = _this.$('#canEdit-' + _this.cid + '-' + sharee.shareId); - if($edit.length === 1) { - $edit.prop('checked', sharee.editPermissionState === 'checked'); + var $edit = _this.$('#canEdit-' + _this.cid + '-' + sharee.shareId) + if ($edit.length === 1) { + $edit.prop('checked', sharee.editPermissionState === 'checked') if (sharee.isFolder) { - $edit.prop('indeterminate', sharee.editPermissionState === 'indeterminate'); + $edit.prop('indeterminate', sharee.editPermissionState === 'indeterminate') } } - }); + }) this.$('.popovermenu').on('afterHide', function() { - _this._menuOpen = false; - }); + _this._menuOpen = false + }) this.$('.popovermenu').on('beforeHide', function() { - var shareId = parseInt(_this._menuOpen, 10); - if(!_.isNaN(shareId)) { - var datePickerClass = '.expirationDateContainer-' + _this.cid + '-' + shareId; - var datePickerInput = '#expirationDatePicker-' + _this.cid + '-' + shareId; - var expireDateCheckbox = '#expireDate-' + _this.cid + '-' + shareId; + var shareId = parseInt(_this._menuOpen, 10) + if (!_.isNaN(shareId)) { + var datePickerClass = '.expirationDateContainer-' + _this.cid + '-' + shareId + var datePickerInput = '#expirationDatePicker-' + _this.cid + '-' + shareId + var expireDateCheckbox = '#expireDate-' + _this.cid + '-' + shareId if ($(expireDateCheckbox).prop('checked')) { - $(datePickerInput).removeClass('hidden-visually'); - $(datePickerClass).removeClass('hasDatepicker'); - $(datePickerClass + ' .ui-datepicker').hide(); + $(datePickerInput).removeClass('hidden-visually') + $(datePickerClass).removeClass('hasDatepicker') + $(datePickerClass + ' .ui-datepicker').hide() } } - }); + }) if (this._menuOpen !== false) { // Open menu again if it was opened before - var shareId = parseInt(this._menuOpen, 10); - if(!_.isNaN(shareId)) { - var liSelector = 'li[data-share-id=' + shareId + ']'; - OC.showMenu(null, this.$(liSelector + ' .sharingOptionsGroup .popovermenu')); + var shareId = parseInt(this._menuOpen, 10) + if (!_.isNaN(shareId)) { + var liSelector = 'li[data-share-id=' + shareId + ']' + OC.showMenu(null, this.$(liSelector + ' .sharingOptionsGroup .popovermenu')) } } - this._renderPermissionChange = false; + this._renderPermissionChange = false // new note autosize - autosize(this.$el.find('.share-note-form .share-note')); + autosize(this.$el.find('.share-note-form .share-note')) - this.delegateEvents(); + this.delegateEvents() - return this; + return this }, /** * @returns {Function} from Handlebars * @private */ - template: function (data) { - var sharees = data.sharees; - if(_.isArray(sharees)) { + template: function(data) { + var sharees = data.sharees + if (_.isArray(sharees)) { for (var i = 0; i < sharees.length; i++) { - data.sharees[i].popoverMenu = this.popoverMenuTemplate(sharees[i]); + data.sharees[i].popoverMenu = this.popoverMenuTemplate(sharees[i]) } } - return OC.Share.Templates['sharedialogshareelistview'](data); + return OC.Share.Templates['sharedialogshareelistview'](data) }, /** @@ -384,235 +384,235 @@ * @returns {string} */ popoverMenuTemplate: function(data) { - return OC.Share.Templates['sharedialogshareelistview_popover_menu'](data); + return OC.Share.Templates['sharedialogshareelistview_popover_menu'](data) }, showNoteForm: function(event) { - event.preventDefault(); - event.stopPropagation(); - var $element = $(event.target); - var $menu = $element.closest('li'); - var $form = $menu.next('li.share-note-form'); + event.preventDefault() + event.stopPropagation() + var $element = $(event.target) + var $menu = $element.closest('li') + var $form = $menu.next('li.share-note-form') // show elements - $menu.find('.share-note-delete').toggleClass('hidden'); - $form.toggleClass('hidden'); - $form.find('textarea').focus(); + $menu.find('.share-note-delete').toggleClass('hidden') + $form.toggleClass('hidden') + $form.find('textarea').focus() }, deleteNote: function(event) { - event.preventDefault(); - event.stopPropagation(); - var self = this; - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); - var $menu = $element.closest('li'); - var $form = $menu.next('li.share-note-form'); - - console.log($form.find('.share-note')); - $form.find('.share-note').val(''); - - $form.addClass('hidden'); - $menu.find('.share-note-delete').addClass('hidden'); - - self.sendNote('', shareId, $menu); + event.preventDefault() + event.stopPropagation() + var self = this + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') + var $menu = $element.closest('li') + var $form = $menu.next('li.share-note-form') + + console.log($form.find('.share-note')) + $form.find('.share-note').val('') + + $form.addClass('hidden') + $menu.find('.share-note-delete').addClass('hidden') + + self.sendNote('', shareId, $menu) }, updateNote: function(event) { - event.preventDefault(); - event.stopPropagation(); - var self = this; - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); - var $form = $element.closest('li.share-note-form'); - var $menu = $form.prev('li'); - var message = $form.find('.share-note').val().trim(); + event.preventDefault() + event.stopPropagation() + var self = this + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') + var $form = $element.closest('li.share-note-form') + var $menu = $form.prev('li') + var message = $form.find('.share-note').val().trim() if (message.length < 1) { - return; + return } - self.sendNote(message, shareId, $menu); + self.sendNote(message, shareId, $menu) }, sendNote: function(note, shareId, $menu) { - var $form = $menu.next('li.share-note-form'); - var $submit = $form.find('input.share-note-submit'); - var $error = $form.find('input.share-note-error'); + var $form = $menu.next('li.share-note-form') + var $submit = $form.find('input.share-note-submit') + var $error = $form.find('input.share-note-error') - $submit.prop('disabled', true); - $menu.find('.icon-loading-small').removeClass('hidden'); - $menu.find('.icon-edit').hide(); + $submit.prop('disabled', true) + $menu.find('.icon-loading-small').removeClass('hidden') + $menu.find('.icon-edit').hide() var complete = function() { - $submit.prop('disabled', false); - $menu.find('.icon-loading-small').addClass('hidden'); - $menu.find('.icon-edit').show(); - }; + $submit.prop('disabled', false) + $menu.find('.icon-loading-small').addClass('hidden') + $menu.find('.icon-edit').show() + } var error = function() { - $error.show(); + $error.show() setTimeout(function() { - $error.hide(); - }, 3000); - }; + $error.hide() + }, 3000) + } // send data $.ajax({ method: 'PUT', - url: OC.linkToOCS('apps/files_sharing/api/v1/shares',2) + shareId + '?' + OC.buildQueryString({format: 'json'}), + url: OC.linkToOCS('apps/files_sharing/api/v1/shares', 2) + shareId + '?' + OC.buildQueryString({ format: 'json' }), data: { note: note }, - complete : complete, + complete: complete, error: error - }); + }) }, onUnshare: function(event) { - event.preventDefault(); - event.stopPropagation(); - var self = this; - var $element = $(event.target); + event.preventDefault() + event.stopPropagation() + var self = this + var $element = $(event.target) if (!$element.is('a')) { - $element = $element.closest('a'); + $element = $element.closest('a') } - var $loading = $element.find('.icon-loading-small').eq(0); - if(!$loading.hasClass('hidden')) { + var $loading = $element.find('.icon-loading-small').eq(0) + if (!$loading.hasClass('hidden')) { // in process - return false; + return false } - $loading.removeClass('hidden'); + $loading.removeClass('hidden') - var $li = $element.closest('li[data-share-id]'); + var $li = $element.closest('li[data-share-id]') - var shareId = $li.data('share-id'); + var shareId = $li.data('share-id') self.model.removeShare(shareId) .done(function() { - $li.remove(); + $li.remove() }) .fail(function() { - $loading.addClass('hidden'); - OC.Notification.showTemporary(t('core', 'Could not unshare')); - }); - return false; + $loading.addClass('hidden') + OC.Notification.showTemporary(t('core', 'Could not unshare')) + }) + return false }, onToggleMenu: function(event) { - event.preventDefault(); - event.stopPropagation(); - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var $menu = $li.find('.sharingOptionsGroup .popovermenu'); - - OC.showMenu(null, $menu); - this._menuOpen = $li.data('share-id'); + event.preventDefault() + event.stopPropagation() + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var $menu = $li.find('.sharingOptionsGroup .popovermenu') + + OC.showMenu(null, $menu) + this._menuOpen = $li.data('share-id') }, onExpireDateChange: function(event) { - var $element = $(event.target); - var li = $element.closest('li[data-share-id]'); - var shareId = li.data('share-id'); - var datePickerClass = '.expirationDateContainer-' + this.cid + '-' + shareId; - var datePicker = $(datePickerClass); - var state = $element.prop('checked'); - datePicker.toggleClass('hidden', !state); + var $element = $(event.target) + var li = $element.closest('li[data-share-id]') + var shareId = li.data('share-id') + var datePickerClass = '.expirationDateContainer-' + this.cid + '-' + shareId + var datePicker = $(datePickerClass) + var state = $element.prop('checked') + datePicker.toggleClass('hidden', !state) if (!state) { // disabled, let's hide the input and // set the expireDate to nothing - $element.closest('li').next('li').addClass('hidden'); - this.setExpirationDate(shareId, ''); + $element.closest('li').next('li').addClass('hidden') + this.setExpirationDate(shareId, '') } else { // enabled, show the input and the datepicker - $element.closest('li').next('li').removeClass('hidden'); - this.showDatePicker(event); + $element.closest('li').next('li').removeClass('hidden') + this.showDatePicker(event) } }, showDatePicker: function(event) { - var element = $(event.target); - var li = element.closest('li[data-share-id]'); - var shareId = li.data('share-id'); - var expirationDatePicker = '#expirationDatePicker-' + this.cid + '-' + shareId; - var view = this; + var element = $(event.target) + var li = element.closest('li[data-share-id]') + var shareId = li.data('share-id') + var expirationDatePicker = '#expirationDatePicker-' + this.cid + '-' + shareId + var view = this $(expirationDatePicker).datepicker({ - dateFormat : 'dd-mm-yy', - onSelect: function (expireDate) { - view.setExpirationDate(shareId, expireDate); + dateFormat: 'dd-mm-yy', + onSelect: function(expireDate) { + view.setExpirationDate(shareId, expireDate) } - }); - $(expirationDatePicker).focus(); + }) + $(expirationDatePicker).focus() }, setExpirationDate: function(shareId, expireDate) { - this.model.updateShare(shareId, {expireDate: expireDate}, {}); + this.model.updateShare(shareId, { expireDate: expireDate }, {}) }, onMailSharePasswordProtectChange: function(event) { - var element = $(event.target); - var li = element.closest('li[data-share-id]'); - var shareId = li.data('share-id'); - var passwordContainerClass = '.passwordMenu-' + this.cid + '-' + shareId; - var passwordContainer = $(passwordContainerClass); - var loading = this.$el.find(passwordContainerClass + ' .icon-loading-small'); - var inputClass = '#passwordField-' + this.cid + '-' + shareId; - var passwordField = $(inputClass); - var state = element.prop('checked'); - var passwordByTalkElement = $('#passwordByTalk-' + this.cid + '-' + shareId); - var passwordByTalkState = passwordByTalkElement.prop('checked'); + var element = $(event.target) + var li = element.closest('li[data-share-id]') + var shareId = li.data('share-id') + var passwordContainerClass = '.passwordMenu-' + this.cid + '-' + shareId + var passwordContainer = $(passwordContainerClass) + var loading = this.$el.find(passwordContainerClass + ' .icon-loading-small') + var inputClass = '#passwordField-' + this.cid + '-' + shareId + var passwordField = $(inputClass) + var state = element.prop('checked') + var passwordByTalkElement = $('#passwordByTalk-' + this.cid + '-' + shareId) + var passwordByTalkState = passwordByTalkElement.prop('checked') if (!state && !passwordByTalkState) { - this.model.updateShare(shareId, {password: '', sendPasswordByTalk: false}); - passwordField.attr('value', ''); - passwordField.removeClass('error'); - passwordField.tooltip('hide'); - loading.addClass('hidden'); - passwordField.attr('placeholder', PASSWORD_PLACEHOLDER_MESSAGE); + this.model.updateShare(shareId, { password: '', sendPasswordByTalk: false }) + passwordField.attr('value', '') + passwordField.removeClass('error') + passwordField.tooltip('hide') + loading.addClass('hidden') + passwordField.attr('placeholder', PASSWORD_PLACEHOLDER_MESSAGE) // We first need to reset the password field before we hide it - passwordContainer.toggleClass('hidden', !state); + passwordContainer.toggleClass('hidden', !state) } else if (state) { if (passwordByTalkState) { // Switching from sending the password by Talk to sending // the password by mail can be done keeping the previous // password sent by Talk. - this.model.updateShare(shareId, {sendPasswordByTalk: false}); + this.model.updateShare(shareId, { sendPasswordByTalk: false }) - var passwordByTalkContainerClass = '.passwordByTalkMenu-' + this.cid + '-' + shareId; - var passwordByTalkContainer = $(passwordByTalkContainerClass); - passwordByTalkContainer.addClass('hidden'); - passwordByTalkElement.prop('checked', false); + var passwordByTalkContainerClass = '.passwordByTalkMenu-' + this.cid + '-' + shareId + var passwordByTalkContainer = $(passwordByTalkContainerClass) + passwordByTalkContainer.addClass('hidden') + passwordByTalkElement.prop('checked', false) } - passwordContainer.toggleClass('hidden', !state); - passwordField = '#passwordField-' + this.cid + '-' + shareId; - this.$(passwordField).focus(); + passwordContainer.toggleClass('hidden', !state) + passwordField = '#passwordField-' + this.cid + '-' + shareId + this.$(passwordField).focus() } }, onMailSharePasswordProtectByTalkChange: function(event) { - var element = $(event.target); - var li = element.closest('li[data-share-id]'); - var shareId = li.data('share-id'); - var passwordByTalkContainerClass = '.passwordByTalkMenu-' + this.cid + '-' + shareId; - var passwordByTalkContainer = $(passwordByTalkContainerClass); - var loading = this.$el.find(passwordByTalkContainerClass + ' .icon-loading-small'); - var inputClass = '#passwordByTalkField-' + this.cid + '-' + shareId; - var passwordByTalkField = $(inputClass); - var state = element.prop('checked'); - var passwordElement = $('#password-' + this.cid + '-' + shareId); - var passwordState = passwordElement.prop('checked'); + var element = $(event.target) + var li = element.closest('li[data-share-id]') + var shareId = li.data('share-id') + var passwordByTalkContainerClass = '.passwordByTalkMenu-' + this.cid + '-' + shareId + var passwordByTalkContainer = $(passwordByTalkContainerClass) + var loading = this.$el.find(passwordByTalkContainerClass + ' .icon-loading-small') + var inputClass = '#passwordByTalkField-' + this.cid + '-' + shareId + var passwordByTalkField = $(inputClass) + var state = element.prop('checked') + var passwordElement = $('#password-' + this.cid + '-' + shareId) + var passwordState = passwordElement.prop('checked') if (!state) { - this.model.updateShare(shareId, {password: '', sendPasswordByTalk: false}); - passwordByTalkField.attr('value', ''); - passwordByTalkField.removeClass('error'); - passwordByTalkField.tooltip('hide'); - loading.addClass('hidden'); - passwordByTalkField.attr('placeholder', PASSWORD_PLACEHOLDER_MESSAGE); + this.model.updateShare(shareId, { password: '', sendPasswordByTalk: false }) + passwordByTalkField.attr('value', '') + passwordByTalkField.removeClass('error') + passwordByTalkField.tooltip('hide') + loading.addClass('hidden') + passwordByTalkField.attr('placeholder', PASSWORD_PLACEHOLDER_MESSAGE) // We first need to reset the password field before we hide it - passwordByTalkContainer.toggleClass('hidden', !state); + passwordByTalkContainer.toggleClass('hidden', !state) } else if (state) { if (passwordState) { // Enabling sending the password by Talk requires a new @@ -622,53 +622,52 @@ // the share is not updated until the user explicitly gives // the new password. - var passwordContainerClass = '.passwordMenu-' + this.cid + '-' + shareId; - var passwordContainer = $(passwordContainerClass); - passwordContainer.addClass('hidden'); - passwordElement.prop('checked', false); + var passwordContainerClass = '.passwordMenu-' + this.cid + '-' + shareId + var passwordContainer = $(passwordContainerClass) + passwordContainer.addClass('hidden') + passwordElement.prop('checked', false) } - passwordByTalkContainer.toggleClass('hidden', !state); - passwordByTalkField = '#passwordByTalkField-' + this.cid + '-' + shareId; - this.$(passwordByTalkField).focus(); + passwordByTalkContainer.toggleClass('hidden', !state) + passwordByTalkField = '#passwordByTalkField-' + this.cid + '-' + shareId + this.$(passwordByTalkField).focus() } }, onMailSharePasswordKeyUp: function(event) { - if(event.keyCode === 13) { - this.onMailSharePasswordEntered(event); + if (event.keyCode === 13) { + this.onMailSharePasswordEntered(event) } }, onMailSharePasswordEntered: function(event) { - var passwordField = $(event.target); - var li = passwordField.closest('li[data-share-id]'); - var shareId = li.data('share-id'); - var passwordContainerClass = '.passwordMenu-' + this.cid + '-' + shareId; - var passwordByTalkContainerClass = '.passwordByTalkMenu-' + this.cid + '-' + shareId; - var sendPasswordByTalk = passwordField.attr('id').startsWith('passwordByTalk'); - var loading; + var passwordField = $(event.target) + var li = passwordField.closest('li[data-share-id]') + var shareId = li.data('share-id') + var passwordContainerClass = '.passwordMenu-' + this.cid + '-' + shareId + var passwordByTalkContainerClass = '.passwordByTalkMenu-' + this.cid + '-' + shareId + var sendPasswordByTalk = passwordField.attr('id').startsWith('passwordByTalk') + var loading if (sendPasswordByTalk) { - loading = this.$el.find(passwordByTalkContainerClass + ' .icon-loading-small'); + loading = this.$el.find(passwordByTalkContainerClass + ' .icon-loading-small') } else { - loading = this.$el.find(passwordContainerClass + ' .icon-loading-small'); + loading = this.$el.find(passwordContainerClass + ' .icon-loading-small') } if (!loading.hasClass('hidden')) { // still in process - return; + return } - passwordField.removeClass('error'); - var password = passwordField.val(); + passwordField.removeClass('error') + var password = passwordField.val() // in IE9 the password might be the placeholder due to bugs in the placeholders polyfill - if(password === '' || password === PASSWORD_PLACEHOLDER || password === PASSWORD_PLACEHOLDER_MESSAGE) { - return; + if (password === '' || password === PASSWORD_PLACEHOLDER || password === PASSWORD_PLACEHOLDER_MESSAGE) { + return } loading .removeClass('hidden') - .addClass('inlineblock'); - + .addClass('inlineblock') this.model.updateShare(shareId, { password: password, @@ -676,104 +675,103 @@ }, { error: function(model, msg) { // destroy old tooltips - passwordField.tooltip('destroy'); - loading.removeClass('inlineblock').addClass('hidden'); - passwordField.addClass('error'); - passwordField.attr('title', msg); - passwordField.tooltip({placement: 'bottom', trigger: 'manual'}); - passwordField.tooltip('show'); + passwordField.tooltip('destroy') + loading.removeClass('inlineblock').addClass('hidden') + passwordField.addClass('error') + passwordField.attr('title', msg) + passwordField.tooltip({ placement: 'bottom', trigger: 'manual' }) + passwordField.tooltip('show') }, success: function(model, msg) { - passwordField.blur(); - passwordField.attr('value', ''); - passwordField.attr('placeholder', PASSWORD_PLACEHOLDER); - loading.removeClass('inlineblock').addClass('hidden'); + passwordField.blur() + passwordField.attr('value', '') + passwordField.attr('placeholder', PASSWORD_PLACEHOLDER) + loading.removeClass('inlineblock').addClass('hidden') } - }); + }) }, onPermissionChange: function(event) { - event.preventDefault(); - event.stopPropagation(); - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); + event.preventDefault() + event.stopPropagation() + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') - var permissions = OC.PERMISSION_READ; + var permissions = OC.PERMISSION_READ if (this.model.isFolder()) { // adjust checkbox states - var $checkboxes = $('.permissions', $li).not('input[name="edit"]').not('input[name="share"]'); - var checked; + var $checkboxes = $('.permissions', $li).not('input[name="edit"]').not('input[name="share"]') + var checked if ($element.attr('name') === 'edit') { - checked = $element.is(':checked'); + checked = $element.is(':checked') // Check/uncheck Create, Update, and Delete checkboxes if Edit is checked/unck - $($checkboxes).prop('checked', checked); + $($checkboxes).prop('checked', checked) if (checked) { - permissions |= OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE; + permissions |= OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE } } else { - var numberChecked = $checkboxes.filter(':checked').length; - checked = numberChecked === $checkboxes.length; - var $editCb = $('input[name="edit"]', $li); - $editCb.prop('checked', checked); - $editCb.prop('indeterminate', !checked && numberChecked > 0); + var numberChecked = $checkboxes.filter(':checked').length + checked = numberChecked === $checkboxes.length + var $editCb = $('input[name="edit"]', $li) + $editCb.prop('checked', checked) + $editCb.prop('indeterminate', !checked && numberChecked > 0) } } else { if ($element.attr('name') === 'edit' && $element.is(':checked')) { - permissions |= OC.PERMISSION_UPDATE; + permissions |= OC.PERMISSION_UPDATE } } $('.permissions', $li).not('input[name="edit"]').filter(':checked').each(function(index, checkbox) { - permissions |= $(checkbox).data('permissions'); - }); - + permissions |= $(checkbox).data('permissions') + }) /** disable checkboxes during save operation to avoid race conditions **/ - $li.find('input[type=checkbox]').prop('disabled', true); + $li.find('input[type=checkbox]').prop('disabled', true) var enableCb = function() { - $li.find('input[type=checkbox]').prop('disabled', false); - }; + $li.find('input[type=checkbox]').prop('disabled', false) + } var errorCb = function(elem, msg) { - OC.dialogs.alert(msg, t('core', 'Error while sharing')); - enableCb(); - }; + OC.dialogs.alert(msg, t('core', 'Error while sharing')) + enableCb() + } - this.model.updateShare(shareId, {permissions: permissions}, {error: errorCb, success: enableCb}); + this.model.updateShare(shareId, { permissions: permissions }, { error: errorCb, success: enableCb }) - this._renderPermissionChange = shareId; + this._renderPermissionChange = shareId }, onSecureDropChange: function(event) { - event.preventDefault(); - event.stopPropagation(); - var $element = $(event.target); - var $li = $element.closest('li[data-share-id]'); - var shareId = $li.data('share-id'); + event.preventDefault() + event.stopPropagation() + var $element = $(event.target) + var $li = $element.closest('li[data-share-id]') + var shareId = $li.data('share-id') - var permissions = OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE | OC.PERMISSION_READ; + var permissions = OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE | OC.PERMISSION_READ if ($element.is(':checked')) { - permissions = OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE; + permissions = OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE } /** disable checkboxes during save operation to avoid race conditions **/ - $li.find('input[type=checkbox]').prop('disabled', true); + $li.find('input[type=checkbox]').prop('disabled', true) var enableCb = function() { - $li.find('input[type=checkbox]').prop('disabled', false); - }; + $li.find('input[type=checkbox]').prop('disabled', false) + } var errorCb = function(elem, msg) { - OC.dialogs.alert(msg, t('core', 'Error while sharing')); - enableCb(); - }; + OC.dialogs.alert(msg, t('core', 'Error while sharing')) + enableCb() + } - this.model.updateShare(shareId, {permissions: permissions}, {error: errorCb, success: enableCb}); + this.model.updateShare(shareId, { permissions: permissions }, { error: errorCb, success: enableCb }) - this._renderPermissionChange = shareId; + this._renderPermissionChange = shareId } - }); + }) - OC.Share.ShareDialogShareeListView = ShareDialogShareeListView; + OC.Share.ShareDialogShareeListView = ShareDialogShareeListView -})(); +})() diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 2765cf43ad712150f6044535fd6b1685cb55fb34..9bc87a69463869f2eb1bc58181805f72cc85cbd1 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2015 * @@ -11,8 +12,8 @@ /* globals Handlebars */ (function() { - if(!OC.Share) { - OC.Share = {}; + if (!OC.Share) { + OC.Share = {} } /** @@ -67,47 +68,47 @@ }, initialize: function(options) { - var view = this; + var view = this this.model.on('fetchError', function() { - OC.Notification.showTemporary(t('core', 'Share details could not be loaded for this item.')); - }); + OC.Notification.showTemporary(t('core', 'Share details could not be loaded for this item.')) + }) - if(!_.isUndefined(options.configModel)) { - this.configModel = options.configModel; + if (!_.isUndefined(options.configModel)) { + this.configModel = options.configModel } else { - throw 'missing OC.Share.ShareConfigModel'; + throw 'missing OC.Share.ShareConfigModel' } this.configModel.on('change:isRemoteShareAllowed', function() { - view.render(); - }); + view.render() + }) this.configModel.on('change:isRemoteGroupShareAllowed', function() { - view.render(); - }); + view.render() + }) this.model.on('change:permissions', function() { - view.render(); - }); + view.render() + }) - this.model.on('request', this._onRequest, this); - this.model.on('sync', this._onEndRequest, this); + this.model.on('request', this._onRequest, this) + this.model.on('sync', this._onEndRequest, this) var subViewOptions = { model: this.model, configModel: this.configModel - }; + } var subViews = { resharerInfoView: 'ShareDialogResharerInfoView', linkShareView: 'ShareDialogLinkShareView', shareeListView: 'ShareDialogShareeListView' - }; + } - for(var name in subViews) { - var className = subViews[name]; + for (var name in subViews) { + var className = subViews[name] this[name] = _.isUndefined(options[name]) ? new OC.Share[className](subViewOptions) - : options[name]; + : options[name] } _.bindAll(this, @@ -115,35 +116,35 @@ '_onSelectRecipient', 'onShareWithFieldChanged', 'onShareWithFieldFocus' - ); + ) - OC.Plugins.attach('OC.Share.ShareDialogView', this); + OC.Plugins.attach('OC.Share.ShareDialogView', this) }, onShareWithFieldChanged: function() { - var $el = this.$el.find('.shareWithField'); + var $el = this.$el.find('.shareWithField') if ($el.val().length < 2) { - $el.removeClass('error').tooltip('hide'); + $el.removeClass('error').tooltip('hide') } }, /* trigger search after the field was re-selected */ onShareWithFieldFocus: function() { - var $shareWithField = this.$el.find('.shareWithField'); - $shareWithField.autocomplete("search", $shareWithField.val()); + var $shareWithField = this.$el.find('.shareWithField') + $shareWithField.autocomplete('search', $shareWithField.val()) }, _getSuggestions: function(searchTerm, perPage, model, lookup) { - if (this._lastSuggestions && - this._lastSuggestions.searchTerm === searchTerm && - this._lastSuggestions.lookup === lookup && - this._lastSuggestions.perPage === perPage && - this._lastSuggestions.model === model) { - return this._lastSuggestions.promise; + if (this._lastSuggestions + && this._lastSuggestions.searchTerm === searchTerm + && this._lastSuggestions.lookup === lookup + && this._lastSuggestions.perPage === perPage + && this._lastSuggestions.model === model) { + return this._lastSuggestions.promise } - var deferred = $.Deferred(); - var view = this; + var deferred = $.Deferred() + var view = this $.get( OC.linkToOCS('apps/files_sharing/api/v1') + 'sharees', @@ -154,115 +155,115 @@ perPage: perPage, itemType: model.get('itemType') }, - function (result) { + function(result) { if (result.ocs.meta.statuscode === 100) { var filter = function(users, groups, remotes, remote_groups, emails, circles, rooms) { - if (typeof(emails) === 'undefined') { - emails = []; + if (typeof (emails) === 'undefined') { + emails = [] } - if (typeof(circles) === 'undefined') { - circles = []; + if (typeof (circles) === 'undefined') { + circles = [] } - if (typeof(rooms) === 'undefined') { - rooms = []; + if (typeof (rooms) === 'undefined') { + rooms = [] } - var usersLength; - var groupsLength; - var remotesLength; - var remoteGroupsLength; - var emailsLength; - var circlesLength; - var roomsLength; + var usersLength + var groupsLength + var remotesLength + var remoteGroupsLength + var emailsLength + var circlesLength + var roomsLength - var i, j; + var i, j - //Filter out the current user - usersLength = users.length; + // Filter out the current user + usersLength = users.length for (i = 0; i < usersLength; i++) { if (users[i].value.shareWith === OC.currentUser) { - users.splice(i, 1); - break; + users.splice(i, 1) + break } } // Filter out the owner of the share if (model.hasReshare()) { - usersLength = users.length; - for (i = 0 ; i < usersLength; i++) { + usersLength = users.length + for (i = 0; i < usersLength; i++) { if (users[i].value.shareWith === model.getReshareOwner()) { - users.splice(i, 1); - break; + users.splice(i, 1) + break } } } - var shares = model.get('shares'); - var sharesLength = shares.length; + var shares = model.get('shares') + var sharesLength = shares.length // Now filter out all sharees that are already shared with for (i = 0; i < sharesLength; i++) { - var share = shares[i]; + var share = shares[i] if (share.share_type === OC.Share.SHARE_TYPE_USER) { - usersLength = users.length; + usersLength = users.length for (j = 0; j < usersLength; j++) { if (users[j].value.shareWith === share.share_with) { - users.splice(j, 1); - break; + users.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_GROUP) { - groupsLength = groups.length; + groupsLength = groups.length for (j = 0; j < groupsLength; j++) { if (groups[j].value.shareWith === share.share_with) { - groups.splice(j, 1); - break; + groups.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_REMOTE) { - remotesLength = remotes.length; + remotesLength = remotes.length for (j = 0; j < remotesLength; j++) { if (remotes[j].value.shareWith === share.share_with) { - remotes.splice(j, 1); - break; + remotes.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_REMOTE_GROUP) { - remoteGroupsLength = remote_groups.length; + remoteGroupsLength = remote_groups.length for (j = 0; j < remoteGroupsLength; j++) { if (remote_groups[j].value.shareWith === share.share_with) { - remote_groups.splice(j, 1); - break; + remote_groups.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_EMAIL) { - emailsLength = emails.length; + emailsLength = emails.length for (j = 0; j < emailsLength; j++) { if (emails[j].value.shareWith === share.share_with) { - emails.splice(j, 1); - break; + emails.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_CIRCLE) { - circlesLength = circles.length; + circlesLength = circles.length for (j = 0; j < circlesLength; j++) { if (circles[j].value.shareWith === share.share_with) { - circles.splice(j, 1); - break; + circles.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_ROOM) { - roomsLength = rooms.length; + roomsLength = rooms.length for (j = 0; j < roomsLength; j++) { if (rooms[j].value.shareWith === share.share_with) { - rooms.splice(j, 1); - break; + rooms.splice(j, 1) + break } } } } - }; + } filter( result.ocs.data.exact.users, @@ -272,26 +273,26 @@ result.ocs.data.exact.emails, result.ocs.data.exact.circles, result.ocs.data.exact.rooms - ); - - var exactUsers = result.ocs.data.exact.users; - var exactGroups = result.ocs.data.exact.groups; - var exactRemotes = result.ocs.data.exact.remotes; - var exactRemoteGroups = result.ocs.data.exact.remote_groups; - var exactEmails = []; - if (typeof(result.ocs.data.emails) !== 'undefined') { - exactEmails = result.ocs.data.exact.emails; + ) + + var exactUsers = result.ocs.data.exact.users + var exactGroups = result.ocs.data.exact.groups + var exactRemotes = result.ocs.data.exact.remotes + var exactRemoteGroups = result.ocs.data.exact.remote_groups + var exactEmails = [] + if (typeof (result.ocs.data.emails) !== 'undefined') { + exactEmails = result.ocs.data.exact.emails } - var exactCircles = []; - if (typeof(result.ocs.data.circles) !== 'undefined') { - exactCircles = result.ocs.data.exact.circles; + var exactCircles = [] + if (typeof (result.ocs.data.circles) !== 'undefined') { + exactCircles = result.ocs.data.exact.circles } - var exactRooms = []; - if (typeof(result.ocs.data.rooms) !== 'undefined') { - exactRooms = result.ocs.data.exact.rooms; + var exactRooms = [] + if (typeof (result.ocs.data.rooms) !== 'undefined') { + exactRooms = result.ocs.data.exact.rooms } - var exactMatches = exactUsers.concat(exactGroups).concat(exactRemotes).concat(exactRemoteGroups).concat(exactEmails).concat(exactCircles).concat(exactRooms); + var exactMatches = exactUsers.concat(exactGroups).concat(exactRemotes).concat(exactRemoteGroups).concat(exactEmails).concat(exactCircles).concat(exactRooms) filter( result.ocs.data.users, @@ -301,66 +302,66 @@ result.ocs.data.emails, result.ocs.data.circles, result.ocs.data.rooms - ); - - var users = result.ocs.data.users; - var groups = result.ocs.data.groups; - var remotes = result.ocs.data.remotes; - var remoteGroups = result.ocs.data.remote_groups; - var lookup = result.ocs.data.lookup; - var lookupEnabled = result.ocs.data.lookupEnabled; - var emails = []; - if (typeof(result.ocs.data.emails) !== 'undefined') { - emails = result.ocs.data.emails; + ) + + var users = result.ocs.data.users + var groups = result.ocs.data.groups + var remotes = result.ocs.data.remotes + var remoteGroups = result.ocs.data.remote_groups + var lookup = result.ocs.data.lookup + var lookupEnabled = result.ocs.data.lookupEnabled + var emails = [] + if (typeof (result.ocs.data.emails) !== 'undefined') { + emails = result.ocs.data.emails } - var circles = []; - if (typeof(result.ocs.data.circles) !== 'undefined') { - circles = result.ocs.data.circles; + var circles = [] + if (typeof (result.ocs.data.circles) !== 'undefined') { + circles = result.ocs.data.circles } - var rooms = []; - if (typeof(result.ocs.data.rooms) !== 'undefined') { - rooms = result.ocs.data.rooms; + var rooms = [] + if (typeof (result.ocs.data.rooms) !== 'undefined') { + rooms = result.ocs.data.rooms } - var suggestions = exactMatches.concat(users).concat(groups).concat(remotes).concat(remoteGroups).concat(emails).concat(circles).concat(rooms).concat(lookup); + var suggestions = exactMatches.concat(users).concat(groups).concat(remotes).concat(remoteGroups).concat(emails).concat(circles).concat(rooms).concat(lookup) function dynamicSort(property) { - return function (a,b) { - var aProperty = ''; - var bProperty = ''; + return function(a, b) { + var aProperty = '' + var bProperty = '' if (typeof a[property] !== 'undefined') { - aProperty = a[property]; + aProperty = a[property] } if (typeof b[property] !== 'undefined') { - bProperty = b[property]; + bProperty = b[property] } - return (aProperty < bProperty) ? -1 : (aProperty > bProperty) ? 1 : 0; + return (aProperty < bProperty) ? -1 : (aProperty > bProperty) ? 1 : 0 } } /** * Sort share entries by uuid to properly group them */ - var grouped = suggestions.sort(dynamicSort('uuid')); + var grouped = suggestions.sort(dynamicSort('uuid')) - var previousUuid = null; - var groupedLength = grouped.length; - var result = []; + var previousUuid = null + var groupedLength = grouped.length + var result = [] /** * build the result array that only contains all contact entries from * merged contacts, if the search term matches its contact name */ for (var i = 0; i < groupedLength; i++) { if (typeof grouped[i].uuid !== 'undefined' && grouped[i].uuid === previousUuid) { - grouped[i].merged = true; + grouped[i].merged = true } if (searchTerm === grouped[i].name || typeof grouped[i].merged === 'undefined') { - result.push(grouped[i]); + result.push(grouped[i]) } - previousUuid = grouped[i].uuid; + previousUuid = grouped[i].uuid } - var moreResultsAvailable = - ( + var moreResultsAvailable + = ( OC.config['sharing.maxAutocompleteResults'] > 0 && Math.min(perPage, OC.config['sharing.maxAutocompleteResults']) <= Math.max( @@ -373,7 +374,7 @@ rooms.length + exactRooms.length, lookup.length ) - ); + ) if (!view._lookup && lookupEnabled) { result.push( { @@ -384,14 +385,14 @@ ) } - deferred.resolve(result, exactMatches, moreResultsAvailable, lookupEnabled); + deferred.resolve(result, exactMatches, moreResultsAvailable, lookupEnabled) } else { - deferred.reject(result.ocs.meta.message); + deferred.reject(result.ocs.meta.message) } } ).fail(function() { - deferred.reject(); - }); + deferred.reject() + }) this._lastSuggestions = { searchTerm: searchTerm, @@ -399,18 +400,18 @@ perPage: perPage, model: model, promise: deferred.promise() - }; + } - return this._lastSuggestions.promise; + return this._lastSuggestions.promise }, _getRecommendations: function(model) { - if (this._lastRecommendations && - this._lastRecommendations.model === model) { - return this._lastRecommendations.promise; + if (this._lastRecommendations + && this._lastRecommendations.model === model) { + return this._lastRecommendations.promise } - var deferred = $.Deferred(); + var deferred = $.Deferred() $.get( OC.linkToOCS('apps/files_sharing/api/v1') + 'sharees_recommended', @@ -418,115 +419,115 @@ format: 'json', itemType: model.get('itemType') }, - function (result) { + function(result) { if (result.ocs.meta.statuscode === 100) { var filter = function(users, groups, remotes, remote_groups, emails, circles, rooms) { - if (typeof(emails) === 'undefined') { - emails = []; + if (typeof (emails) === 'undefined') { + emails = [] } - if (typeof(circles) === 'undefined') { - circles = []; + if (typeof (circles) === 'undefined') { + circles = [] } - if (typeof(rooms) === 'undefined') { - rooms = []; + if (typeof (rooms) === 'undefined') { + rooms = [] } - var usersLength; - var groupsLength; - var remotesLength; - var remoteGroupsLength; - var emailsLength; - var circlesLength; - var roomsLength; + var usersLength + var groupsLength + var remotesLength + var remoteGroupsLength + var emailsLength + var circlesLength + var roomsLength - var i, j; + var i, j - //Filter out the current user - usersLength = users.length; + // Filter out the current user + usersLength = users.length for (i = 0; i < usersLength; i++) { if (users[i].value.shareWith === OC.currentUser) { - users.splice(i, 1); - break; + users.splice(i, 1) + break } } // Filter out the owner of the share if (model.hasReshare()) { - usersLength = users.length; - for (i = 0 ; i < usersLength; i++) { + usersLength = users.length + for (i = 0; i < usersLength; i++) { if (users[i].value.shareWith === model.getReshareOwner()) { - users.splice(i, 1); - break; + users.splice(i, 1) + break } } } - var shares = model.get('shares'); - var sharesLength = shares.length; + var shares = model.get('shares') + var sharesLength = shares.length // Now filter out all sharees that are already shared with for (i = 0; i < sharesLength; i++) { - var share = shares[i]; + var share = shares[i] if (share.share_type === OC.Share.SHARE_TYPE_USER) { - usersLength = users.length; + usersLength = users.length for (j = 0; j < usersLength; j++) { if (users[j].value.shareWith === share.share_with) { - users.splice(j, 1); - break; + users.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_GROUP) { - groupsLength = groups.length; + groupsLength = groups.length for (j = 0; j < groupsLength; j++) { if (groups[j].value.shareWith === share.share_with) { - groups.splice(j, 1); - break; + groups.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_REMOTE) { - remotesLength = remotes.length; + remotesLength = remotes.length for (j = 0; j < remotesLength; j++) { if (remotes[j].value.shareWith === share.share_with) { - remotes.splice(j, 1); - break; + remotes.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_REMOTE_GROUP) { - remoteGroupsLength = remote_groups.length; + remoteGroupsLength = remote_groups.length for (j = 0; j < remoteGroupsLength; j++) { if (remote_groups[j].value.shareWith === share.share_with) { - remote_groups.splice(j, 1); - break; + remote_groups.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_EMAIL) { - emailsLength = emails.length; + emailsLength = emails.length for (j = 0; j < emailsLength; j++) { if (emails[j].value.shareWith === share.share_with) { - emails.splice(j, 1); - break; + emails.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_CIRCLE) { - circlesLength = circles.length; + circlesLength = circles.length for (j = 0; j < circlesLength; j++) { if (circles[j].value.shareWith === share.share_with) { - circles.splice(j, 1); - break; + circles.splice(j, 1) + break } } } else if (share.share_type === OC.Share.SHARE_TYPE_ROOM) { - roomsLength = rooms.length; + roomsLength = rooms.length for (j = 0; j < roomsLength; j++) { if (rooms[j].value.shareWith === share.share_with) { - rooms.splice(j, 1); - break; + rooms.splice(j, 1) + break } } } } - }; + } filter( result.ocs.data.exact.users, @@ -536,26 +537,26 @@ result.ocs.data.exact.emails, result.ocs.data.exact.circles, result.ocs.data.exact.rooms - ); - - var exactUsers = result.ocs.data.exact.users; - var exactGroups = result.ocs.data.exact.groups; - var exactRemotes = result.ocs.data.exact.remotes || []; - var exactRemoteGroups = result.ocs.data.exact.remote_groups || []; - var exactEmails = []; - if (typeof(result.ocs.data.emails) !== 'undefined') { - exactEmails = result.ocs.data.exact.emails; + ) + + var exactUsers = result.ocs.data.exact.users + var exactGroups = result.ocs.data.exact.groups + var exactRemotes = result.ocs.data.exact.remotes || [] + var exactRemoteGroups = result.ocs.data.exact.remote_groups || [] + var exactEmails = [] + if (typeof (result.ocs.data.emails) !== 'undefined') { + exactEmails = result.ocs.data.exact.emails } - var exactCircles = []; - if (typeof(result.ocs.data.circles) !== 'undefined') { - exactCircles = result.ocs.data.exact.circles; + var exactCircles = [] + if (typeof (result.ocs.data.circles) !== 'undefined') { + exactCircles = result.ocs.data.exact.circles } - var exactRooms = []; - if (typeof(result.ocs.data.rooms) !== 'undefined') { - exactRooms = result.ocs.data.exact.rooms; + var exactRooms = [] + if (typeof (result.ocs.data.rooms) !== 'undefined') { + exactRooms = result.ocs.data.exact.rooms } - var exactMatches = exactUsers.concat(exactGroups).concat(exactRemotes).concat(exactRemoteGroups).concat(exactEmails).concat(exactCircles).concat(exactRooms); + var exactMatches = exactUsers.concat(exactGroups).concat(exactRemotes).concat(exactRemoteGroups).concat(exactEmails).concat(exactCircles).concat(exactRooms) filter( result.ocs.data.users, @@ -565,124 +566,124 @@ result.ocs.data.emails, result.ocs.data.circles, result.ocs.data.rooms - ); - - var users = result.ocs.data.users; - var groups = result.ocs.data.groups; - var remotes = result.ocs.data.remotes || []; - var remoteGroups = result.ocs.data.remote_groups || []; - var lookup = result.ocs.data.lookup || []; - var emails = []; - if (typeof(result.ocs.data.emails) !== 'undefined') { - emails = result.ocs.data.emails; + ) + + var users = result.ocs.data.users + var groups = result.ocs.data.groups + var remotes = result.ocs.data.remotes || [] + var remoteGroups = result.ocs.data.remote_groups || [] + var lookup = result.ocs.data.lookup || [] + var emails = [] + if (typeof (result.ocs.data.emails) !== 'undefined') { + emails = result.ocs.data.emails } - var circles = []; - if (typeof(result.ocs.data.circles) !== 'undefined') { - circles = result.ocs.data.circles; + var circles = [] + if (typeof (result.ocs.data.circles) !== 'undefined') { + circles = result.ocs.data.circles } - var rooms = []; - if (typeof(result.ocs.data.rooms) !== 'undefined') { - rooms = result.ocs.data.rooms; + var rooms = [] + if (typeof (result.ocs.data.rooms) !== 'undefined') { + rooms = result.ocs.data.rooms } - var suggestions = exactMatches.concat(users).concat(groups).concat(remotes).concat(remoteGroups).concat(emails).concat(circles).concat(rooms).concat(lookup); + var suggestions = exactMatches.concat(users).concat(groups).concat(remotes).concat(remoteGroups).concat(emails).concat(circles).concat(rooms).concat(lookup) function dynamicSort(property) { - return function (a,b) { - var aProperty = ''; - var bProperty = ''; + return function(a, b) { + var aProperty = '' + var bProperty = '' if (typeof a[property] !== 'undefined') { - aProperty = a[property]; + aProperty = a[property] } if (typeof b[property] !== 'undefined') { - bProperty = b[property]; + bProperty = b[property] } - return (aProperty < bProperty) ? -1 : (aProperty > bProperty) ? 1 : 0; + return (aProperty < bProperty) ? -1 : (aProperty > bProperty) ? 1 : 0 } } /** * Sort share entries by uuid to properly group them */ - var grouped = suggestions.sort(dynamicSort('uuid')); + var grouped = suggestions.sort(dynamicSort('uuid')) - var previousUuid = null; - var groupedLength = grouped.length; - var result = []; + var previousUuid = null + var groupedLength = grouped.length + var result = [] /** * build the result array that only contains all contact entries from * merged contacts, if the search term matches its contact name */ for (var i = 0; i < groupedLength; i++) { if (typeof grouped[i].uuid !== 'undefined' && grouped[i].uuid === previousUuid) { - grouped[i].merged = true; + grouped[i].merged = true } if (typeof grouped[i].merged === 'undefined') { - result.push(grouped[i]); + result.push(grouped[i]) } - previousUuid = grouped[i].uuid; + previousUuid = grouped[i].uuid } - deferred.resolve(result, exactMatches, false); + deferred.resolve(result, exactMatches, false) } else { - deferred.reject(result.ocs.meta.message); + deferred.reject(result.ocs.meta.message) } } ).fail(function() { - deferred.reject(); - }); + deferred.reject() + }) this._lastRecommendations = { model: model, promise: deferred.promise() - }; + } - return this._lastRecommendations.promise; + return this._lastRecommendations.promise }, - recommendationHandler: function (response) { - var view = this; - var $shareWithField = $('.shareWithField'); + recommendationHandler: function(response) { + var view = this + var $shareWithField = $('.shareWithField') this._getRecommendations( view.model ).done(function(suggestions) { - console.info('recommendations', suggestions); + console.info('recommendations', suggestions) if (suggestions.length > 0) { $shareWithField - .autocomplete("option", "autoFocus", true); + .autocomplete('option', 'autoFocus', true) - response(suggestions); + response(suggestions) } else { - console.info('no sharing recommendations found'); - response(); + console.info('no sharing recommendations found') + response() } }).fail(function(message) { console.error('could not load recommendations', message) - }); + }) }, - autocompleteHandler: function (search, response) { + autocompleteHandler: function(search, response) { // If nothing is entered we show recommendations instead of search // results if (search.term.length === 0) { - console.info(search.term, 'empty search term -> using recommendations'); - this.recommendationHandler(response); - return; + console.info(search.term, 'empty search term -> using recommendations') + this.recommendationHandler(response) + return } - var $shareWithField = $('.shareWithField'), - view = this, - $loading = this.$el.find('.shareWithLoading'), - $confirm = this.$el.find('.shareWithConfirm'); + var $shareWithField = $('.shareWithField') + var view = this + var $loading = this.$el.find('.shareWithLoading') + var $confirm = this.$el.find('.shareWithConfirm') - var count = OC.config['sharing.minSearchStringLength']; + var count = OC.config['sharing.minSearchStringLength'] if (search.term.trim().length < count) { var title = n('core', 'At least {count} character is needed for autocompletion', 'At least {count} characters are needed for autocompletion', count, { count: count } - ); + ) $shareWithField.addClass('error') .attr('data-original-title', title) .tooltip('hide') @@ -691,50 +692,50 @@ trigger: 'manual' }) .tooltip('fixTitle') - .tooltip('show'); - response(); - return; + .tooltip('show') + response() + return } - $loading.removeClass('hidden'); - $loading.addClass('inlineblock'); - $confirm.addClass('hidden'); - this._pendingOperationsCount++; + $loading.removeClass('hidden') + $loading.addClass('inlineblock') + $confirm.addClass('hidden') + this._pendingOperationsCount++ $shareWithField.removeClass('error') - .tooltip('hide'); + .tooltip('hide') - var perPage = parseInt(OC.config['sharing.maxAutocompleteResults'], 10) || 200; + var perPage = parseInt(OC.config['sharing.maxAutocompleteResults'], 10) || 200 this._getSuggestions( search.term.trim(), perPage, view.model, view._lookup ).done(function(suggestions, exactMatches, moreResultsAvailable) { - view._pendingOperationsCount--; + view._pendingOperationsCount-- if (view._pendingOperationsCount === 0) { - $loading.addClass('hidden'); - $loading.removeClass('inlineblock'); - $confirm.removeClass('hidden'); + $loading.addClass('hidden') + $loading.removeClass('inlineblock') + $confirm.removeClass('hidden') } if (suggestions.length > 0) { $shareWithField - .autocomplete("option", "autoFocus", true); + .autocomplete('option', 'autoFocus', true) - response(suggestions); + response(suggestions) // show a notice that the list is truncated // this is the case if one of the search results is at least as long as the max result config option - if(moreResultsAvailable) { - var message = t('core', 'This list is maybe truncated - please refine your search term to see more results.'); - $('.ui-autocomplete').append('<li class="autocomplete-note">' + message + '</li>'); + if (moreResultsAvailable) { + var message = t('core', 'This list is maybe truncated - please refine your search term to see more results.') + $('.ui-autocomplete').append('<li class="autocomplete-note">' + message + '</li>') } } else { - var title = t('core', 'No users or groups found for {search}', {search: $shareWithField.val()}); + var title = t('core', 'No users or groups found for {search}', { search: $shareWithField.val() }) if (!view.configModel.get('allowGroupSharing')) { - title = t('core', 'No users found for {search}', {search: $('.shareWithField').val()}); + title = t('core', 'No users found for {search}', { search: $('.shareWithField').val() }) } $shareWithField.addClass('error') .attr('data-original-title', title) @@ -744,197 +745,198 @@ trigger: 'manual' }) .tooltip('fixTitle') - .tooltip('show'); - response(); + .tooltip('show') + response() } }).fail(function(message) { - view._pendingOperationsCount--; + view._pendingOperationsCount-- if (view._pendingOperationsCount === 0) { - $loading.addClass('hidden'); - $loading.removeClass('inlineblock'); - $confirm.removeClass('hidden'); + $loading.addClass('hidden') + $loading.removeClass('inlineblock') + $confirm.removeClass('hidden') } if (message) { - OC.Notification.showTemporary(t('core', 'An error occurred ("{message}"). Please try again', { message: message })); + OC.Notification.showTemporary(t('core', 'An error occurred ("{message}"). Please try again', { message: message })) } else { - OC.Notification.showTemporary(t('core', 'An error occurred. Please try again')); + OC.Notification.showTemporary(t('core', 'An error occurred. Please try again')) } - }); + }) }, autocompleteRenderItem: function(ul, item) { - var icon = 'icon-user'; - var text = escapeHTML(item.label); - var description = ''; - var type = ''; + var icon = 'icon-user' + var text = escapeHTML(item.label) + var description = '' + var type = '' var getTranslatedType = function(type) { switch (type) { - case 'HOME': - return t('core', 'Home'); - case 'WORK': - return t('core', 'Work'); - case 'OTHER': - return t('core', 'Other'); - default: - return '' + type; + case 'HOME': + return t('core', 'Home') + case 'WORK': + return t('core', 'Work') + case 'OTHER': + return t('core', 'Other') + default: + return '' + type } - }; + } if (typeof item.type !== 'undefined' && item.type !== null) { - type = getTranslatedType(item.type) + ' '; + type = getTranslatedType(item.type) + ' ' } if (typeof item.name !== 'undefined') { - text = escapeHTML(item.name); + text = escapeHTML(item.name) } if (item.value.shareType === OC.Share.SHARE_TYPE_GROUP) { - icon = 'icon-contacts-dark'; + icon = 'icon-contacts-dark' } else if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE) { - icon = 'icon-shared'; - description += item.value.shareWith; + icon = 'icon-shared' + description += item.value.shareWith } else if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE_GROUP) { - text = t('core', '{sharee} (remote group)', { sharee: text }, undefined, { escape: false }); - icon = 'icon-shared'; - description += item.value.shareWith; + text = t('core', '{sharee} (remote group)', { sharee: text }, undefined, { escape: false }) + icon = 'icon-shared' + description += item.value.shareWith } else if (item.value.shareType === OC.Share.SHARE_TYPE_EMAIL) { - icon = 'icon-mail'; - description += item.value.shareWith; + icon = 'icon-mail' + description += item.value.shareWith } else if (item.value.shareType === OC.Share.SHARE_TYPE_CIRCLE) { - text = t('core', '{sharee} ({type}, {owner})', {sharee: text, type: item.value.circleInfo, owner: item.value.circleOwner}, undefined, {escape: false}); - icon = 'icon-circle'; + text = t('core', '{sharee} ({type}, {owner})', { sharee: text, type: item.value.circleInfo, owner: item.value.circleOwner }, undefined, { escape: false }) + icon = 'icon-circle' } else if (item.value.shareType === OC.Share.SHARE_TYPE_ROOM) { - icon = 'icon-talk'; + icon = 'icon-talk' } - var insert = $("<div class='share-autocomplete-item'/>"); + var insert = $("<div class='share-autocomplete-item'/>") if (item.merged) { - insert.addClass('merged'); - text = item.value.shareWith; - description = type; + insert.addClass('merged') + text = item.value.shareWith + description = type } else if (item.lookup) { - text = item.label; - icon = false; - insert.append('<span class="icon icon-search search-globally"></span>'); + text = item.label + icon = false + insert.append('<span class="icon icon-search search-globally"></span>') } else { - var avatar = $("<div class='avatardiv'></div>").appendTo(insert); + var avatar = $("<div class='avatardiv'></div>").appendTo(insert) if (item.value.shareType === OC.Share.SHARE_TYPE_USER || item.value.shareType === OC.Share.SHARE_TYPE_CIRCLE) { - avatar.avatar(item.value.shareWith, 32, undefined, undefined, undefined, item.label); + avatar.avatar(item.value.shareWith, 32, undefined, undefined, undefined, item.label) } else { if (typeof item.uuid === 'undefined') { - item.uuid = text; + item.uuid = text } - avatar.imageplaceholder(item.uuid, text, 32); + avatar.imageplaceholder(item.uuid, text, 32) } - description = type + description; + description = type + description } if (description !== '') { - insert.addClass('with-description'); + insert.addClass('with-description') } $("<div class='autocomplete-item-text'></div>") .html( text.replace( - new RegExp(this.term, "gi"), - "<span class='ui-state-highlight'>$&</span>") + new RegExp(this.term, 'gi'), + "<span class='ui-state-highlight'>$&</span>") + '<span class="autocomplete-item-details">' + description + '</span>' ) - .appendTo(insert); - insert.attr('title', item.value.shareWith); + .appendTo(insert) + insert.attr('title', item.value.shareWith) if (icon) { - insert.append('<span class="icon ' + icon + '" title="' + text + '"></span>'); + insert.append('<span class="icon ' + icon + '" title="' + text + '"></span>') } - insert = $("<a>") - .append(insert); - return $("<li>") + insert = $('<a>') + .append(insert) + return $('<li>') .addClass((item.value.shareType === OC.Share.SHARE_TYPE_GROUP) ? 'group' : 'user') .append(insert) - .appendTo(ul); + .appendTo(ul) }, _onSelectRecipient: function(e, s) { - var self = this; + var self = this if (e.keyCode == 9) { - e.preventDefault(); + e.preventDefault() if (typeof s.item.name !== 'undefined') { - e.target.value = s.item.name; + e.target.value = s.item.name } else { - e.target.value = s.item.label; + e.target.value = s.item.label } setTimeout(function() { $(e.target).attr('disabled', false) - .autocomplete('search', $(e.target).val()); - }, 0); - return false; + .autocomplete('search', $(e.target).val()) + }, 0) + return false } if (s.item.lookup) { // Retrigger search but with global lookup this time - this._lookup = true; - var $shareWithField = this.$el.find('.shareWithField'); - var val = $shareWithField.val(); + this._lookup = true + var $shareWithField = this.$el.find('.shareWithField') + var val = $shareWithField.val() setTimeout(function() { - console.debug('searching again, but globally. search term: ' + val); - $shareWithField.autocomplete("search", val); - }, 0); - return false; + console.debug('searching again, but globally. search term: ' + val) + $shareWithField.autocomplete('search', val) + }, 0) + return false } - e.preventDefault(); + e.preventDefault() // Ensure that the keydown handler for the input field is not // called; otherwise it would try to add the recipient again, which // would fail. - e.stopImmediatePropagation(); + e.stopImmediatePropagation() $(e.target).attr('disabled', true) - .val(s.item.label); + .val(s.item.label) - var $loading = this.$el.find('.shareWithLoading'); - var $confirm = this.$el.find('.shareWithConfirm'); + var $loading = this.$el.find('.shareWithLoading') + var $confirm = this.$el.find('.shareWithConfirm') - $loading.removeClass('hidden'); - $loading.addClass('inlineblock'); - $confirm.addClass('hidden'); - this._pendingOperationsCount++; + $loading.removeClass('hidden') + $loading.addClass('inlineblock') + $confirm.addClass('hidden') + this._pendingOperationsCount++ - this.model.addShare(s.item.value, {success: function() { + this.model.addShare(s.item.value, { success: function() { // Adding a share changes the suggestions. - self._lastSuggestions = undefined; + self._lastSuggestions = undefined $(e.target).val('') - .attr('disabled', false); + .attr('disabled', false) - self._pendingOperationsCount--; + self._pendingOperationsCount-- if (self._pendingOperationsCount === 0) { - $loading.addClass('hidden'); - $loading.removeClass('inlineblock'); - $confirm.removeClass('hidden'); + $loading.addClass('hidden') + $loading.removeClass('inlineblock') + $confirm.removeClass('hidden') } - }, error: function(obj, msg) { - OC.Notification.showTemporary(msg); + }, + error: function(obj, msg) { + OC.Notification.showTemporary(msg) $(e.target).attr('disabled', false) - .autocomplete('search', $(e.target).val()); + .autocomplete('search', $(e.target).val()) - self._pendingOperationsCount--; + self._pendingOperationsCount-- if (self._pendingOperationsCount === 0) { - $loading.addClass('hidden'); - $loading.removeClass('inlineblock'); - $confirm.removeClass('hidden'); + $loading.addClass('hidden') + $loading.removeClass('inlineblock') + $confirm.removeClass('hidden') } - }}); + } }) }, _confirmShare: function() { - var self = this; - var $shareWithField = $('.shareWithField'); - var $loading = this.$el.find('.shareWithLoading'); - var $confirm = this.$el.find('.shareWithConfirm'); + var self = this + var $shareWithField = $('.shareWithField') + var $loading = this.$el.find('.shareWithLoading') + var $confirm = this.$el.find('.shareWithConfirm') - $loading.removeClass('hidden'); - $loading.addClass('inlineblock'); - $confirm.addClass('hidden'); - this._pendingOperationsCount++; + $loading.removeClass('hidden') + $loading.addClass('inlineblock') + $confirm.addClass('hidden') + this._pendingOperationsCount++ - $shareWithField.prop('disabled', true); + $shareWithField.prop('disabled', true) // Disabling the autocompletion does not clear its search timeout; // removing the focus from the input field does, but only if the @@ -942,22 +944,22 @@ // Thus, the field has to be disabled before disabling the // autocompletion to prevent an old pending search result from // appearing once the field is enabled again. - $shareWithField.autocomplete('close'); - $shareWithField.autocomplete('disable'); + $shareWithField.autocomplete('close') + $shareWithField.autocomplete('disable') var restoreUI = function() { - self._pendingOperationsCount--; + self._pendingOperationsCount-- if (self._pendingOperationsCount === 0) { - $loading.addClass('hidden'); - $loading.removeClass('inlineblock'); - $confirm.removeClass('hidden'); + $loading.addClass('hidden') + $loading.removeClass('inlineblock') + $confirm.removeClass('hidden') } - $shareWithField.prop('disabled', false); - $shareWithField.focus(); - }; + $shareWithField.prop('disabled', false) + $shareWithField.focus() + } - var perPage = parseInt(OC.config['sharing.maxAutocompleteResults'], 10) || 200; + var perPage = parseInt(OC.config['sharing.maxAutocompleteResults'], 10) || 200 this._getSuggestions( $shareWithField.val(), perPage, @@ -965,139 +967,139 @@ this._lookup ).done(function(suggestions, exactMatches) { if (suggestions.length === 0) { - restoreUI(); + restoreUI() - $shareWithField.autocomplete('enable'); + $shareWithField.autocomplete('enable') // There is no need to show an error message here; it will // be automatically shown when the autocomplete is activated // again (due to the focus on the field) and it finds no // matches. - return; + return } if (exactMatches.length !== 1) { - restoreUI(); + restoreUI() - $shareWithField.autocomplete('enable'); + $shareWithField.autocomplete('enable') - return; + return } var actionSuccess = function() { // Adding a share changes the suggestions. - self._lastSuggestions = undefined; + self._lastSuggestions = undefined - $shareWithField.val(''); + $shareWithField.val('') - restoreUI(); + restoreUI() - $shareWithField.autocomplete('enable'); - }; + $shareWithField.autocomplete('enable') + } var actionError = function(obj, msg) { - restoreUI(); + restoreUI() - $shareWithField.autocomplete('enable'); + $shareWithField.autocomplete('enable') - OC.Notification.showTemporary(msg); - }; + OC.Notification.showTemporary(msg) + } self.model.addShare(exactMatches[0].value, { success: actionSuccess, error: actionError - }); + }) }).fail(function(message) { - restoreUI(); + restoreUI() - $shareWithField.autocomplete('enable'); + $shareWithField.autocomplete('enable') // There is no need to show an error message here; it will be // automatically shown when the autocomplete is activated again // (due to the focus on the field) and getting the suggestions // fail. - }); + }) }, _toggleLoading: function(state) { - this._loading = state; - this.$el.find('.subView').toggleClass('hidden', state); - this.$el.find('.loading').toggleClass('hidden', !state); + this._loading = state + this.$el.find('.subView').toggleClass('hidden', state) + this.$el.find('.loading').toggleClass('hidden', !state) }, _onRequest: function() { // only show the loading spinner for the first request (for now) if (!this._loadingOnce) { - this._toggleLoading(true); + this._toggleLoading(true) } }, _onEndRequest: function() { - var self = this; - this._toggleLoading(false); + var self = this + this._toggleLoading(false) if (!this._loadingOnce) { - this._loadingOnce = true; + this._loadingOnce = true } }, render: function() { - var self = this; - var baseTemplate = OC.Share.Templates['sharedialogview']; + var self = this + var baseTemplate = OC.Share.Templates['sharedialogview'] this.$el.html(baseTemplate({ cid: this.cid, shareLabel: t('core', 'Share'), sharePlaceholder: this._renderSharePlaceholderPart(), isSharingAllowed: this.model.sharePermissionPossible() - })); + })) - var $shareField = this.$el.find('.shareWithField'); + var $shareField = this.$el.find('.shareWithField') if ($shareField.length) { var shareFieldKeydownHandler = function(event) { if (event.keyCode !== 13) { - return true; + return true } - self._confirmShare(); + self._confirmShare() - return false; - }; + return false + } $shareField.autocomplete({ minLength: 0, delay: 750, focus: function(event) { - event.preventDefault(); + event.preventDefault() }, source: this.autocompleteHandler, select: this._onSelectRecipient, open: function() { - var autocomplete = $(this).autocomplete('widget'); - var numberOfItems = autocomplete.find('li').size(); - autocomplete.removeClass('item-count-1'); - autocomplete.removeClass('item-count-2'); + var autocomplete = $(this).autocomplete('widget') + var numberOfItems = autocomplete.find('li').size() + autocomplete.removeClass('item-count-1') + autocomplete.removeClass('item-count-2') if (numberOfItems <= 2) { - autocomplete.addClass('item-count-' + numberOfItems); + autocomplete.addClass('item-count-' + numberOfItems) } } - }).data('ui-autocomplete')._renderItem = this.autocompleteRenderItem; + }).data('ui-autocomplete')._renderItem = this.autocompleteRenderItem - $shareField.on('keydown', null, shareFieldKeydownHandler); + $shareField.on('keydown', null, shareFieldKeydownHandler) } - this.resharerInfoView.$el = this.$el.find('.resharerInfoView'); - this.resharerInfoView.render(); + this.resharerInfoView.$el = this.$el.find('.resharerInfoView') + this.resharerInfoView.render() - this.linkShareView.$el = this.$el.find('.linkShareView'); - this.linkShareView.render(); + this.linkShareView.$el = this.$el.find('.linkShareView') + this.linkShareView.render() - this.shareeListView.$el = this.$el.find('.shareeListView'); - this.shareeListView.render(); + this.shareeListView.$el = this.$el.find('.shareeListView') + this.shareeListView.render() - this.$el.find('.hasTooltip').tooltip(); + this.$el.find('.hasTooltip').tooltip() - return this; + return this }, /** @@ -1107,29 +1109,29 @@ * @param {bool} showLink */ setShowLink: function(showLink) { - this._showLink = (typeof showLink === 'boolean') ? showLink : true; - this.linkShareView.showLink = this._showLink; + this._showLink = (typeof showLink === 'boolean') ? showLink : true + this.linkShareView.showLink = this._showLink }, - _renderSharePlaceholderPart: function () { - var allowRemoteSharing = this.configModel.get('isRemoteShareAllowed'); - var allowMailSharing = this.configModel.get('isMailShareAllowed'); + _renderSharePlaceholderPart: function() { + var allowRemoteSharing = this.configModel.get('isRemoteShareAllowed') + var allowMailSharing = this.configModel.get('isMailShareAllowed') if (!allowRemoteSharing && allowMailSharing) { - return t('core', 'Name or email address...'); + return t('core', 'Name or email address...') } if (allowRemoteSharing && !allowMailSharing) { - return t('core', 'Name or federated cloud ID...'); + return t('core', 'Name or federated cloud ID...') } if (allowRemoteSharing && allowMailSharing) { - return t('core', 'Name, federated cloud ID or email address...'); + return t('core', 'Name, federated cloud ID or email address...') } - return t('core', 'Name...'); - }, + return t('core', 'Name...') + } - }); + }) - OC.Share.ShareDialogView = ShareDialogView; + OC.Share.ShareDialogView = ShareDialogView -})(); +})() diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index 2349f19092ff194446c666a9c5415bd4f3a29f62..e86731ab1b1514012f36b9c285ee69d79cf273cd 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2015 * @@ -9,9 +10,9 @@ */ (function() { - if(!OC.Share) { - OC.Share = {}; - OC.Share.Types = {}; + if (!OC.Share) { + OC.Share = {} + OC.Share.Types = {} } /** @@ -65,7 +66,7 @@ var SHARE_RESPONSE_INT_PROPS = [ 'id', 'file_parent', 'mail_send', 'file_source', 'item_source', 'permissions', 'storage', 'share_type', 'parent', 'stime' - ]; + ] /** * @class OCA.Share.ShareItemModel @@ -85,15 +86,15 @@ _linkShareId: null, initialize: function(attributes, options) { - if(!_.isUndefined(options.configModel)) { - this.configModel = options.configModel; + if (!_.isUndefined(options.configModel)) { + this.configModel = options.configModel } - if(!_.isUndefined(options.fileInfoModel)) { + if (!_.isUndefined(options.fileInfoModel)) { /** @type {OC.Files.FileInfo} **/ - this.fileInfoModel = options.fileInfoModel; + this.fileInfoModel = options.fileInfoModel } - _.bindAll(this, 'addShare'); + _.bindAll(this, 'addShare') }, defaults: { @@ -117,26 +118,26 @@ * TODO: this should be a separate model */ saveLinkShare: function(attributes, options) { - options = options || {}; - attributes = _.extend({}, attributes); + options = options || {} + attributes = _.extend({}, attributes) - var shareId = null; - var call; + var shareId = null + var call // oh yeah... if (attributes.expiration) { - attributes.expireDate = attributes.expiration; - delete attributes.expiration; + attributes.expireDate = attributes.expiration + delete attributes.expiration } - var linkShares = this.get('linkShares'); - var shareIndex = _.findIndex(linkShares, function(share) {return share.id === attributes.cid}) + var linkShares = this.get('linkShares') + var shareIndex = _.findIndex(linkShares, function(share) { return share.id === attributes.cid }) if (linkShares.length > 0 && shareIndex !== -1) { - shareId = linkShares[shareIndex].id; + shareId = linkShares[shareIndex].id // note: update can only update a single value at a time - call = this.updateShare(shareId, attributes, options); + call = this.updateShare(shareId, attributes, options) } else { attributes = _.defaults(attributes, { hideDownload: false, @@ -146,38 +147,38 @@ permissions: OC.PERMISSION_READ, expireDate: this.configModel.getDefaultExpirationDateString(), shareType: OC.Share.SHARE_TYPE_LINK - }); + }) - call = this.addShare(attributes, options); + call = this.addShare(attributes, options) } - return call; + return call }, addShare: function(attributes, options) { - var shareType = attributes.shareType; - attributes = _.extend({}, attributes); + var shareType = attributes.shareType + attributes = _.extend({}, attributes) // get default permissions - var defaultPermissions = OC.getCapabilities()['files_sharing']['default_permissions'] || OC.PERMISSION_ALL; - var possiblePermissions = OC.PERMISSION_READ; + var defaultPermissions = OC.getCapabilities()['files_sharing']['default_permissions'] || OC.PERMISSION_ALL + var possiblePermissions = OC.PERMISSION_READ if (this.updatePermissionPossible()) { - possiblePermissions = possiblePermissions | OC.PERMISSION_UPDATE; + possiblePermissions = possiblePermissions | OC.PERMISSION_UPDATE } if (this.createPermissionPossible()) { - possiblePermissions = possiblePermissions | OC.PERMISSION_CREATE; + possiblePermissions = possiblePermissions | OC.PERMISSION_CREATE } if (this.deletePermissionPossible()) { - possiblePermissions = possiblePermissions | OC.PERMISSION_DELETE; + possiblePermissions = possiblePermissions | OC.PERMISSION_DELETE } if (this.configModel.get('isResharingAllowed') && (this.sharePermissionPossible())) { - possiblePermissions = possiblePermissions | OC.PERMISSION_SHARE; + possiblePermissions = possiblePermissions | OC.PERMISSION_SHARE } - attributes.permissions = defaultPermissions & possiblePermissions; + attributes.permissions = defaultPermissions & possiblePermissions if (_.isUndefined(attributes.path)) { - attributes.path = this.fileInfoModel.getFullPath(); + attributes.path = this.fileInfoModel.getFullPath() } return this._addOrUpdateShare({ @@ -185,7 +186,7 @@ url: this._getUrl('shares'), data: attributes, dataType: 'json' - }, options); + }, options) }, updateShare: function(shareId, attrs, options) { @@ -194,105 +195,105 @@ url: this._getUrl('shares/' + encodeURIComponent(shareId)), data: attrs, dataType: 'json' - }, options); + }, options) }, _addOrUpdateShare: function(ajaxSettings, options) { - var self = this; - options = options || {}; + var self = this + options = options || {} return $.ajax( ajaxSettings ).always(function() { if (_.isFunction(options.complete)) { - options.complete(self); + options.complete(self) } }).done(function() { self.fetch().done(function() { if (_.isFunction(options.success)) { - options.success(self); + options.success(self) } - }); + }) }).fail(function(xhr) { - var msg = t('core', 'Error'); - var result = xhr.responseJSON; + var msg = t('core', 'Error') + var result = xhr.responseJSON if (result && result.ocs && result.ocs.meta) { - msg = result.ocs.meta.message; + msg = result.ocs.meta.message } if (_.isFunction(options.error)) { - options.error(self, msg); + options.error(self, msg) } else { - OC.dialogs.alert(msg, t('core', 'Error while sharing')); + OC.dialogs.alert(msg, t('core', 'Error while sharing')) } - }); + }) }, /** * Deletes the share with the given id * * @param {int} shareId share id - * @return {jQuery} + * @returns {jQuery} */ removeShare: function(shareId, options) { - var self = this; - options = options || {}; + var self = this + options = options || {} return $.ajax({ type: 'DELETE', - url: this._getUrl('shares/' + encodeURIComponent(shareId)), + url: this._getUrl('shares/' + encodeURIComponent(shareId)) }).done(function() { self.fetch({ success: function() { if (_.isFunction(options.success)) { - options.success(self); + options.success(self) } } - }); + }) }).fail(function(xhr) { - var msg = t('core', 'Error'); - var result = xhr.responseJSON; + var msg = t('core', 'Error') + var result = xhr.responseJSON if (result.ocs && result.ocs.meta) { - msg = result.ocs.meta.message; + msg = result.ocs.meta.message } if (_.isFunction(options.error)) { - options.error(self, msg); + options.error(self, msg) } else { - OC.dialogs.alert(msg, t('core', 'Error removing share')); + OC.dialogs.alert(msg, t('core', 'Error removing share')) } - }); + }) }, /** * @returns {boolean} */ isPublicUploadAllowed: function() { - return this.get('allowPublicUploadStatus'); + return this.get('allowPublicUploadStatus') }, isPublicEditingAllowed: function() { - return this.get('allowPublicEditingStatus'); + return this.get('allowPublicEditingStatus') }, /** * @returns {boolean} */ isHideFileListSet: function() { - return this.get('hideFileListStatus'); + return this.get('hideFileListStatus') }, /** * @returns {boolean} */ isFolder: function() { - return this.get('itemType') === 'folder'; + return this.get('itemType') === 'folder' }, /** * @returns {boolean} */ isFile: function() { - return this.get('itemType') === 'file'; + return this.get('itemType') === 'file' }, /** @@ -300,8 +301,8 @@ * @returns {boolean} */ hasReshare: function() { - var reshare = this.get('reshare'); - return _.isObject(reshare) && !_.isUndefined(reshare.uid_owner); + var reshare = this.get('reshare') + return _.isObject(reshare) && !_.isUndefined(reshare.uid_owner) }, /** @@ -309,85 +310,85 @@ * @returns {boolean} */ hasUserShares: function() { - return this.getSharesWithCurrentItem().length > 0; + return this.getSharesWithCurrentItem().length > 0 }, /** * Returns whether this item has link shares * - * @return {bool} true if a link share exists, false otherwise + * @returns {bool} true if a link share exists, false otherwise */ hasLinkShares: function() { - var linkShares = this.get('linkShares'); + var linkShares = this.get('linkShares') if (linkShares && linkShares.length > 0) { - return true; + return true } - return false; + return false }, /** * @returns {string} */ getReshareOwner: function() { - return this.get('reshare').uid_owner; + return this.get('reshare').uid_owner }, /** * @returns {string} */ getReshareOwnerDisplayname: function() { - return this.get('reshare').displayname_owner; + return this.get('reshare').displayname_owner }, /** * @returns {string} */ getReshareNote: function() { - return this.get('reshare').note; + return this.get('reshare').note }, /** * @returns {string} */ getReshareWith: function() { - return this.get('reshare').share_with; + return this.get('reshare').share_with }, /** * @returns {string} */ getReshareWithDisplayName: function() { - var reshare = this.get('reshare'); - return reshare.share_with_displayname || reshare.share_with; + var reshare = this.get('reshare') + return reshare.share_with_displayname || reshare.share_with }, /** * @returns {number} */ getReshareType: function() { - return this.get('reshare').share_type; + return this.get('reshare').share_type }, getExpireDate: function(shareIndex) { - return this._shareExpireDate(shareIndex); + return this._shareExpireDate(shareIndex) }, getNote: function(shareIndex) { - return this._shareNote(shareIndex); + return this._shareNote(shareIndex) }, /** * Returns all share entries that only apply to the current item * (file/folder) * - * @return {Array.<OC.Share.Types.ShareInfo>} + * @returns {Array.<OC.Share.Types.ShareInfo>} */ getSharesWithCurrentItem: function() { - var shares = this.get('shares') || []; - var fileId = this.fileInfoModel.get('id'); + var shares = this.get('shares') || [] + var fileId = this.fileInfoModel.get('id') return _.filter(shares, function(share) { - return share.item_source === fileId; - }); + return share.item_source === fileId + }) }, /** @@ -396,11 +397,11 @@ */ getShareWith: function(shareIndex) { /** @type OC.Share.Types.ShareInfo **/ - var share = this.get('shares')[shareIndex]; - if(!_.isObject(share)) { - throw "Unknown Share"; + var share = this.get('shares')[shareIndex] + if (!_.isObject(share)) { + throw 'Unknown Share' } - return share.share_with; + return share.share_with }, /** @@ -409,25 +410,24 @@ */ getShareWithDisplayName: function(shareIndex) { /** @type OC.Share.Types.ShareInfo **/ - var share = this.get('shares')[shareIndex]; - if(!_.isObject(share)) { - throw "Unknown Share"; + var share = this.get('shares')[shareIndex] + if (!_.isObject(share)) { + throw 'Unknown Share' } - return share.share_with_displayname; + return share.share_with_displayname }, - /** * @param shareIndex * @returns {string} */ getShareWithAvatar: function(shareIndex) { /** @type OC.Share.Types.ShareInfo **/ - var share = this.get('shares')[shareIndex]; - if(!_.isObject(share)) { - throw "Unknown Share"; + var share = this.get('shares')[shareIndex] + if (!_.isObject(share)) { + throw 'Unknown Share' } - return share.share_with_avatar; + return share.share_with_avatar }, /** @@ -436,11 +436,11 @@ */ getSharedBy: function(shareIndex) { /** @type OC.Share.Types.ShareInfo **/ - var share = this.get('shares')[shareIndex]; - if(!_.isObject(share)) { - throw "Unknown Share"; + var share = this.get('shares')[shareIndex] + if (!_.isObject(share)) { + throw 'Unknown Share' } - return share.uid_owner; + return share.uid_owner }, /** @@ -449,11 +449,11 @@ */ getSharedByDisplayName: function(shareIndex) { /** @type OC.Share.Types.ShareInfo **/ - var share = this.get('shares')[shareIndex]; - if(!_.isObject(share)) { - throw "Unknown Share"; + var share = this.get('shares')[shareIndex] + if (!_.isObject(share)) { + throw 'Unknown Share' } - return share.displayname_owner; + return share.displayname_owner }, /** @@ -462,11 +462,11 @@ */ getFileOwnerUid: function(shareIndex) { /** @type OC.Share.Types.ShareInfo **/ - var share = this.get('shares')[shareIndex]; - if(!_.isObject(share)) { - throw "Unknown Share"; + var share = this.get('shares')[shareIndex] + if (!_.isObject(share)) { + throw 'Unknown Share' } - return share.uid_file_owner; + return share.uid_file_owner }, /** @@ -476,26 +476,26 @@ * @returns {number} */ findShareWithIndex: function(shareId) { - var shares = this.get('shares'); - if(!_.isArray(shares)) { - throw "Unknown Share"; - } - for(var i = 0; i < shares.length; i++) { - var shareWith = shares[i]; - if(shareWith.id === shareId) { - return i; + var shares = this.get('shares') + if (!_.isArray(shares)) { + throw 'Unknown Share' + } + for (var i = 0; i < shares.length; i++) { + var shareWith = shares[i] + if (shareWith.id === shareId) { + return i } } - throw "Unknown Sharee"; + throw 'Unknown Sharee' }, getShareType: function(shareIndex) { /** @type OC.Share.Types.ShareInfo **/ - var share = this.get('shares')[shareIndex]; - if(!_.isObject(share)) { - throw "Unknown Share"; + var share = this.get('shares')[shareIndex] + if (!_.isObject(share)) { + throw 'Unknown Share' } - return share.share_type; + return share.share_type }, /** @@ -508,44 +508,42 @@ */ _shareHasPermission: function(shareIndex, permission) { /** @type OC.Share.Types.ShareInfo **/ - var share = this.get('shares')[shareIndex]; - if(!_.isObject(share)) { - throw "Unknown Share"; + var share = this.get('shares')[shareIndex] + if (!_.isObject(share)) { + throw 'Unknown Share' } - return (share.permissions & permission) === permission; + return (share.permissions & permission) === permission }, - _shareExpireDate: function(shareIndex) { - var share = this.get('shares')[shareIndex]; - if(!_.isObject(share)) { - throw "Unknown Share"; + var share = this.get('shares')[shareIndex] + if (!_.isObject(share)) { + throw 'Unknown Share' } - var date2 = share.expiration; - return date2; + var date2 = share.expiration + return date2 }, - _shareNote: function(shareIndex) { - var share = this.get('shares')[shareIndex]; - if(!_.isObject(share)) { - throw "Unknown Share"; + var share = this.get('shares')[shareIndex] + if (!_.isObject(share)) { + throw 'Unknown Share' } - return share.note; + return share.note }, /** - * @return {int} + * @returns {int} */ getPermissions: function() { - return this.get('permissions'); + return this.get('permissions') }, /** * @returns {boolean} */ sharePermissionPossible: function() { - return (this.get('permissions') & OC.PERMISSION_SHARE) === OC.PERMISSION_SHARE; + return (this.get('permissions') & OC.PERMISSION_SHARE) === OC.PERMISSION_SHARE }, /** @@ -553,14 +551,14 @@ * @returns {boolean} */ hasSharePermission: function(shareIndex) { - return this._shareHasPermission(shareIndex, OC.PERMISSION_SHARE); + return this._shareHasPermission(shareIndex, OC.PERMISSION_SHARE) }, /** * @returns {boolean} */ createPermissionPossible: function() { - return (this.get('permissions') & OC.PERMISSION_CREATE) === OC.PERMISSION_CREATE; + return (this.get('permissions') & OC.PERMISSION_CREATE) === OC.PERMISSION_CREATE }, /** @@ -568,14 +566,14 @@ * @returns {boolean} */ hasCreatePermission: function(shareIndex) { - return this._shareHasPermission(shareIndex, OC.PERMISSION_CREATE); + return this._shareHasPermission(shareIndex, OC.PERMISSION_CREATE) }, /** * @returns {boolean} */ updatePermissionPossible: function() { - return (this.get('permissions') & OC.PERMISSION_UPDATE) === OC.PERMISSION_UPDATE; + return (this.get('permissions') & OC.PERMISSION_UPDATE) === OC.PERMISSION_UPDATE }, /** @@ -583,14 +581,14 @@ * @returns {boolean} */ hasUpdatePermission: function(shareIndex) { - return this._shareHasPermission(shareIndex, OC.PERMISSION_UPDATE); + return this._shareHasPermission(shareIndex, OC.PERMISSION_UPDATE) }, /** * @returns {boolean} */ deletePermissionPossible: function() { - return (this.get('permissions') & OC.PERMISSION_DELETE) === OC.PERMISSION_DELETE; + return (this.get('permissions') & OC.PERMISSION_DELETE) === OC.PERMISSION_DELETE }, /** @@ -598,20 +596,20 @@ * @returns {boolean} */ hasDeletePermission: function(shareIndex) { - return this._shareHasPermission(shareIndex, OC.PERMISSION_DELETE); + return this._shareHasPermission(shareIndex, OC.PERMISSION_DELETE) }, hasReadPermission: function(shareIndex) { - return this._shareHasPermission(shareIndex, OC.PERMISSION_READ); + return this._shareHasPermission(shareIndex, OC.PERMISSION_READ) }, /** * @returns {boolean} */ editPermissionPossible: function() { - return this.createPermissionPossible() + return this.createPermissionPossible() || this.updatePermissionPossible() - || this.deletePermissionPossible(); + || this.deletePermissionPossible() }, /** @@ -623,69 +621,69 @@ * - 'indeterminate': some but not all permissions */ editPermissionState: function(shareIndex) { - var hcp = this.hasCreatePermission(shareIndex); - var hup = this.hasUpdatePermission(shareIndex); - var hdp = this.hasDeletePermission(shareIndex); + var hcp = this.hasCreatePermission(shareIndex) + var hup = this.hasUpdatePermission(shareIndex) + var hdp = this.hasDeletePermission(shareIndex) if (this.isFile()) { if (hcp || hup || hdp) { - return 'checked'; + return 'checked' } - return ''; + return '' } if (!hcp && !hup && !hdp) { - return ''; + return '' } - if ( (this.createPermissionPossible() && !hcp) + if ((this.createPermissionPossible() && !hcp) || (this.updatePermissionPossible() && !hup) - || (this.deletePermissionPossible() && !hdp) ) { - return 'indeterminate'; + || (this.deletePermissionPossible() && !hdp)) { + return 'indeterminate' } - return 'checked'; + return 'checked' }, /** * @returns {int} */ linkSharePermissions: function(shareId) { - var linkShares = this.get('linkShares'); - var shareIndex = _.findIndex(linkShares, function(share) {return share.id === shareId}) + var linkShares = this.get('linkShares') + var shareIndex = _.findIndex(linkShares, function(share) { return share.id === shareId }) if (!this.hasLinkShares()) { - return -1; + return -1 } else if (linkShares.length > 0 && shareIndex !== -1) { - return linkShares[shareIndex].permissions; + return linkShares[shareIndex].permissions } - return -1; + return -1 }, _getUrl: function(base, params) { - params = _.extend({format: 'json'}, params || {}); - return OC.linkToOCS('apps/files_sharing/api/v1', 2) + base + '?' + OC.buildQueryString(params); + params = _.extend({ format: 'json' }, params || {}) + return OC.linkToOCS('apps/files_sharing/api/v1', 2) + base + '?' + OC.buildQueryString(params) }, _fetchShares: function() { - var path = this.fileInfoModel.getFullPath(); + var path = this.fileInfoModel.getFullPath() return $.ajax({ type: 'GET', - url: this._getUrl('shares', {path: path, reshares: true}) - }); + url: this._getUrl('shares', { path: path, reshares: true }) + }) }, _fetchReshare: function() { // only fetch original share once if (!this._reshareFetched) { - var path = this.fileInfoModel.getFullPath(); - this._reshareFetched = true; + var path = this.fileInfoModel.getFullPath() + this._reshareFetched = true return $.ajax({ type: 'GET', - url: this._getUrl('shares', {path: path, shared_with_me: true}) - }); + url: this._getUrl('shares', { path: path, shared_with_me: true }) + }) } else { return $.Deferred().resolve([{ ocs: { data: [this.get('reshare')] } - }]); + }]) } }, @@ -695,58 +693,58 @@ * combines the permissions to be the most permissive. * * @param {Array} reshares - * @return {Object} reshare + * @returns {Object} reshare */ _groupReshares: function(reshares) { if (!reshares || !reshares.length) { - return false; + return false } - var superShare = reshares.shift(); - var combinedPermissions = superShare.permissions; + var superShare = reshares.shift() + var combinedPermissions = superShare.permissions _.each(reshares, function(reshare) { // use share have higher priority than group share if (reshare.share_type === OC.Share.SHARE_TYPE_USER && superShare.share_type === OC.Share.SHARE_TYPE_GROUP) { - superShare = reshare; + superShare = reshare } - combinedPermissions |= reshare.permissions; - }); + combinedPermissions |= reshare.permissions + }) - superShare.permissions = combinedPermissions; - return superShare; + superShare.permissions = combinedPermissions + return superShare }, fetch: function(options) { - var model = this; - this.trigger('request', this); + var model = this + this.trigger('request', this) var deferred = $.when( this._fetchShares(), this._fetchReshare() - ); + ) deferred.done(function(data1, data2) { - model.trigger('sync', 'GET', this); - var sharesMap = {}; + model.trigger('sync', 'GET', this) + var sharesMap = {} _.each(data1[0].ocs.data, function(shareItem) { - sharesMap[shareItem.id] = shareItem; - }); + sharesMap[shareItem.id] = shareItem + }) - var reshare = false; + var reshare = false if (data2[0].ocs.data.length) { - reshare = model._groupReshares(data2[0].ocs.data); + reshare = model._groupReshares(data2[0].ocs.data) } model.set(model.parse({ shares: sharesMap, reshare: reshare - })); + })) - if(!_.isUndefined(options) && _.isFunction(options.success)) { - options.success(); + if (!_.isUndefined(options) && _.isFunction(options.success)) { + options.success() } - }); + }) - return deferred; + return deferred }, /** @@ -757,111 +755,110 @@ * in the file list. */ _legacyFillCurrentShares: function(shares) { - var fileId = this.fileInfoModel.get('id'); + var fileId = this.fileInfoModel.get('id') if (!shares || !shares.length) { - delete OC.Share.statuses[fileId]; - OC.Share.currentShares = {}; - OC.Share.itemShares = []; - return; + delete OC.Share.statuses[fileId] + OC.Share.currentShares = {} + OC.Share.itemShares = [] + return } - var currentShareStatus = OC.Share.statuses[fileId]; + var currentShareStatus = OC.Share.statuses[fileId] if (!currentShareStatus) { - currentShareStatus = {link: false}; - OC.Share.statuses[fileId] = currentShareStatus; + currentShareStatus = { link: false } + OC.Share.statuses[fileId] = currentShareStatus } - currentShareStatus.link = false; + currentShareStatus.link = false - OC.Share.currentShares = {}; - OC.Share.itemShares = []; + OC.Share.currentShares = {} + OC.Share.itemShares = [] _.each(shares, /** * @param {OC.Share.Types.ShareInfo} share */ function(share) { if (share.share_type === OC.Share.SHARE_TYPE_LINK) { - OC.Share.itemShares[share.share_type] = true; - currentShareStatus.link = true; + OC.Share.itemShares[share.share_type] = true + currentShareStatus.link = true } else { if (!OC.Share.itemShares[share.share_type]) { - OC.Share.itemShares[share.share_type] = []; + OC.Share.itemShares[share.share_type] = [] } - OC.Share.itemShares[share.share_type].push(share.share_with); + OC.Share.itemShares[share.share_type].push(share.share_with) } } - ); + ) }, parse: function(data) { - if(data === false) { - console.warn('no data was returned'); - this.trigger('fetchError'); - return {}; + if (data === false) { + console.warn('no data was returned') + this.trigger('fetchError') + return {} } - var permissions = this.fileInfoModel.get('permissions'); - if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions) && data.reshare.uid_owner !== OC.currentUser) { - permissions = permissions & data.reshare.permissions; + var permissions = this.fileInfoModel.get('permissions') + if (!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions) && data.reshare.uid_owner !== OC.currentUser) { + permissions = permissions & data.reshare.permissions } - var allowPublicUploadStatus = false; - if(!_.isUndefined(data.shares)) { - $.each(data.shares, function (key, value) { + var allowPublicUploadStatus = false + if (!_.isUndefined(data.shares)) { + $.each(data.shares, function(key, value) { if (value.share_type === OC.Share.SHARE_TYPE_LINK) { - allowPublicUploadStatus = (value.permissions & OC.PERMISSION_CREATE) ? true : false; - return true; + allowPublicUploadStatus = !!((value.permissions & OC.PERMISSION_CREATE)) + return true } - }); + }) } - var allowPublicEditingStatus = true; - if(!_.isUndefined(data.shares)) { - $.each(data.shares, function (key, value) { + var allowPublicEditingStatus = true + if (!_.isUndefined(data.shares)) { + $.each(data.shares, function(key, value) { if (value.share_type === OC.Share.SHARE_TYPE_LINK) { - allowPublicEditingStatus = (value.permissions & OC.PERMISSION_UPDATE) ? true : false; - return true; + allowPublicEditingStatus = !!((value.permissions & OC.PERMISSION_UPDATE)) + return true } - }); + }) } - - var hideFileListStatus = false; - if(!_.isUndefined(data.shares)) { - $.each(data.shares, function (key, value) { + var hideFileListStatus = false + if (!_.isUndefined(data.shares)) { + $.each(data.shares, function(key, value) { if (value.share_type === OC.Share.SHARE_TYPE_LINK) { - hideFileListStatus = (value.permissions & OC.PERMISSION_READ) ? false : true; - return true; + hideFileListStatus = !((value.permissions & OC.PERMISSION_READ)) + return true } - }); + }) } /** @type {OC.Share.Types.ShareInfo[]} **/ var shares = _.map(data.shares, function(share) { // properly parse some values because sometimes the server // returns integers as string... - var i; + var i for (i = 0; i < SHARE_RESPONSE_INT_PROPS.length; i++) { - var prop = SHARE_RESPONSE_INT_PROPS[i]; + var prop = SHARE_RESPONSE_INT_PROPS[i] if (!_.isUndefined(share[prop])) { - share[prop] = parseInt(share[prop], 10); + share[prop] = parseInt(share[prop], 10) } } - return share; - }); + return share + }) - this._legacyFillCurrentShares(shares); + this._legacyFillCurrentShares(shares) - var linkShares = []; + var linkShares = [] // filter out the share by link shares = _.reject(shares, /** * @param {OC.Share.Types.ShareInfo} share */ function(share) { - var isShareLink = - share.share_type === OC.Share.SHARE_TYPE_LINK - && ( share.file_source === this.get('itemSource') - || share.item_source === this.get('itemSource')); + var isShareLink + = share.share_type === OC.Share.SHARE_TYPE_LINK + && (share.file_source === this.get('itemSource') + || share.item_source === this.get('itemSource')) if (isShareLink) { /** @@ -869,20 +866,20 @@ * FIXME: Find a way to display properly */ if (share.uid_owner !== OC.currentUser) { - return; + return } - var link = window.location.protocol + '//' + window.location.host; + var link = window.location.protocol + '//' + window.location.host if (!share.token) { // pre-token link - var fullPath = this.fileInfoModel.get('path') + '/' + - this.fileInfoModel.get('name'); - var location = '/' + OC.currentUser + '/files' + fullPath; - var type = this.fileInfoModel.isDirectory() ? 'folder' : 'file'; - link += OC.linkTo('', 'public.php') + '?service=files&' + - type + '=' + encodeURIComponent(location); + var fullPath = this.fileInfoModel.get('path') + '/' + + this.fileInfoModel.get('name') + var location = '/' + OC.currentUser + '/files' + fullPath + var type = this.fileInfoModel.isDirectory() ? 'folder' : 'file' + link += OC.linkTo('', 'public.php') + '?service=files&' + + type + '=' + encodeURIComponent(location) } else { - link += OC.generateUrl('/s/') + share.token; + link += OC.generateUrl('/s/') + share.token } linkShares.push(_.extend({}, share, { // hide_download is returned as an int, so force it @@ -890,13 +887,13 @@ hideDownload: !!share.hide_download, password: share.share_with, sendPasswordByTalk: share.send_password_by_talk - })); + })) - return share; + return share } }, this - ); + ) return { reshare: data.reshare, @@ -906,7 +903,7 @@ allowPublicUploadStatus: allowPublicUploadStatus, allowPublicEditingStatus: allowPublicEditingStatus, hideFileListStatus: hideFileListStatus - }; + } }, /** @@ -919,30 +916,30 @@ if (_.isString(time)) { // skip empty strings and hex values if (time === '' || (time.length > 1 && time[0] === '0' && time[1] === 'x')) { - return null; + return null } - time = parseInt(time, 10); - if(isNaN(time)) { - time = null; + time = parseInt(time, 10) + if (isNaN(time)) { + time = null } } - return time; + return time }, /** * Returns a list of share types from the existing shares. * - * @return {Array.<int>} array of share types + * @returns {Array.<int>} array of share types */ getShareTypes: function() { - var result; - result = _.pluck(this.getSharesWithCurrentItem(), 'share_type'); + var result + result = _.pluck(this.getSharesWithCurrentItem(), 'share_type') if (this.hasLinkShares()) { - result.push(OC.Share.SHARE_TYPE_LINK); + result.push(OC.Share.SHARE_TYPE_LINK) } - return _.uniq(result); + return _.uniq(result) } - }); + }) - OC.Share.ShareItemModel = ShareItemModel; -})(); + OC.Share.ShareItemModel = ShareItemModel +})() diff --git a/core/js/sharesocialmanager.js b/core/js/sharesocialmanager.js index c0e10a47481ab8eb46878c3c0b07bffaf525202b..65fc4cd0f65054bc2132e1b21b1e4032aa756a32 100644 --- a/core/js/sharesocialmanager.js +++ b/core/js/sharesocialmanager.js @@ -22,10 +22,10 @@ (function() { if (!OC.Share) { - OC.Share = {}; + OC.Share = {} } - OC.Share.Social = {}; + OC.Share.Social = {} var SocialModel = OC.Backbone.Model.extend({ defaults: { @@ -40,16 +40,15 @@ /** Open in new windows */ newWindow: true } - }); + }) - OC.Share.Social.Model = SocialModel; + OC.Share.Social.Model = SocialModel var SocialCollection = OC.Backbone.Collection.extend({ model: OC.Share.Social.Model, comparator: 'key' - }); + }) - - OC.Share.Social.Collection = new SocialCollection; -})(); + OC.Share.Social.Collection = new SocialCollection() +})() diff --git a/core/js/systemtags/systemtagmodel.js b/core/js/systemtags/systemtagmodel.js index 72450a2abd0477a6633d237c5b233d15fe01e52e..c51706944a41d05f27cd45754fd5ebccaca6dbcf 100644 --- a/core/js/systemtags/systemtagmodel.js +++ b/core/js/systemtags/systemtagmodel.js @@ -12,11 +12,11 @@ _.extend(OC.Files.Client, { PROPERTY_FILEID: '{' + OC.Files.Client.NS_OWNCLOUD + '}id', - PROPERTY_CAN_ASSIGN:'{' + OC.Files.Client.NS_OWNCLOUD + '}can-assign', + PROPERTY_CAN_ASSIGN: '{' + OC.Files.Client.NS_OWNCLOUD + '}can-assign', PROPERTY_DISPLAYNAME: '{' + OC.Files.Client.NS_OWNCLOUD + '}display-name', PROPERTY_USERVISIBLE: '{' + OC.Files.Client.NS_OWNCLOUD + '}user-visible', - PROPERTY_USERASSIGNABLE:'{' + OC.Files.Client.NS_OWNCLOUD + '}user-assignable', - }); + PROPERTY_USERASSIGNABLE: '{' + OC.Files.Client.NS_OWNCLOUD + '}user-assignable' + }) /** * @class OCA.SystemTags.SystemTagsCollection @@ -27,34 +27,34 @@ */ var SystemTagModel = OC.Backbone.Model.extend( /** @lends OCA.SystemTags.SystemTagModel.prototype */ { - sync: OC.Backbone.davSync, + sync: OC.Backbone.davSync, - defaults: { - userVisible: true, - userAssignable: true, - canAssign: true - }, + defaults: { + userVisible: true, + userAssignable: true, + canAssign: true + }, - davProperties: { - 'id': OC.Files.Client.PROPERTY_FILEID, - 'name': OC.Files.Client.PROPERTY_DISPLAYNAME, - 'userVisible': OC.Files.Client.PROPERTY_USERVISIBLE, - 'userAssignable': OC.Files.Client.PROPERTY_USERASSIGNABLE, - // read-only, effective permissions computed by the server, - 'canAssign': OC.Files.Client.PROPERTY_CAN_ASSIGN - }, + davProperties: { + 'id': OC.Files.Client.PROPERTY_FILEID, + 'name': OC.Files.Client.PROPERTY_DISPLAYNAME, + 'userVisible': OC.Files.Client.PROPERTY_USERVISIBLE, + 'userAssignable': OC.Files.Client.PROPERTY_USERASSIGNABLE, + // read-only, effective permissions computed by the server, + 'canAssign': OC.Files.Client.PROPERTY_CAN_ASSIGN + }, - parse: function(data) { - return { - id: data.id, - name: data.name, - userVisible: data.userVisible === true || data.userVisible === 'true', - userAssignable: data.userAssignable === true || data.userAssignable === 'true', - canAssign: data.canAssign === true || data.canAssign === 'true' - }; - } - }); + parse: function(data) { + return { + id: data.id, + name: data.name, + userVisible: data.userVisible === true || data.userVisible === 'true', + userAssignable: data.userAssignable === true || data.userAssignable === 'true', + canAssign: data.canAssign === true || data.canAssign === 'true' + } + } + }) - OC.SystemTags = OC.SystemTags || {}; - OC.SystemTags.SystemTagModel = SystemTagModel; -})(OC); + OC.SystemTags = OC.SystemTags || {} + OC.SystemTags.SystemTagModel = SystemTagModel +})(OC) diff --git a/core/js/systemtags/systemtags.js b/core/js/systemtags/systemtags.js index 05ead6f3dcd4acfbaba82b50b9c38de5358f4259..3a39b55f73f7731bc654754e67cfca2f0ebe330d 100644 --- a/core/js/systemtags/systemtags.js +++ b/core/js/systemtags/systemtags.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2016 * @@ -16,11 +17,11 @@ /** * * @param {OC.SystemTags.SystemTagModel|Object|String} tag - * @return {jQuery} + * @returns {jQuery} */ getDescriptiveTag: function(tag) { if (_.isUndefined(tag.name) && !_.isUndefined(tag.toJSON)) { - tag = tag.toJSON(); + tag = tag.toJSON() } if (_.isUndefined(tag.name)) { @@ -28,30 +29,29 @@ t('core', 'Non-existing tag #{tag}', { tag: tag }) - ); + ) } - var $span = $('<span>'); - $span.append(escapeHTML(tag.name)); + var $span = $('<span>') + $span.append(escapeHTML(tag.name)) - var scope; + var scope if (!tag.userAssignable) { - scope = t('core', 'restricted'); + scope = t('core', 'restricted') } if (!tag.userVisible) { // invisible also implicitly means not assignable - scope = t('core', 'invisible'); + scope = t('core', 'invisible') } if (scope) { - var $tag = $('<em>').text(' ' + - t('core', '({scope})', { + var $tag = $('<em>').text(' ' + + t('core', '({scope})', { scope: scope }) - ); - $span.append($tag); + ) + $span.append($tag) } - return $span; + return $span } - }; -})(OC); - + } +})(OC) diff --git a/core/js/systemtags/systemtagscollection.js b/core/js/systemtags/systemtagscollection.js index 8a5d309bacb20d90fe7d96d39b49fb930b4c6523..fe3f286855824599af32e0d37019573d0b657c5e 100644 --- a/core/js/systemtags/systemtagscollection.js +++ b/core/js/systemtags/systemtagscollection.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2015 * @@ -11,7 +12,7 @@ (function(OC) { function filterFunction(model, term) { - return model.get('name').substr(0, term.length).toLowerCase() === term.toLowerCase(); + return model.get('name').substr(0, term.length).toLowerCase() === term.toLowerCase() } /** @@ -24,26 +25,26 @@ var SystemTagsCollection = OC.Backbone.Collection.extend( /** @lends OC.SystemTags.SystemTagsCollection.prototype */ { - sync: OC.Backbone.davSync, + sync: OC.Backbone.davSync, - model: OC.SystemTags.SystemTagModel, + model: OC.SystemTags.SystemTagModel, - url: function() { - return OC.linkToRemote('dav') + '/systemtags/'; - }, + url: function() { + return OC.linkToRemote('dav') + '/systemtags/' + }, - filterByName: function(name) { - return this.filter(function(model) { - return filterFunction(model, name); - }); - }, + filterByName: function(name) { + return this.filter(function(model) { + return filterFunction(model, name) + }) + }, - reset: function() { - this.fetched = false; - return OC.Backbone.Collection.prototype.reset.apply(this, arguments); - }, + reset: function() { + this.fetched = false + return OC.Backbone.Collection.prototype.reset.apply(this, arguments) + }, - /** + /** * Lazy fetch. * Only fetches once, subsequent calls will directly call the success handler. * @@ -52,38 +53,37 @@ * * @see Backbone.Collection#fetch */ - fetch: function(options) { - var self = this; - options = options || {}; - if (this.fetched || options.force) { + fetch: function(options) { + var self = this + options = options || {} + if (this.fetched || options.force) { // directly call handler - if (options.success) { - options.success(this, null, options); + if (options.success) { + options.success(this, null, options) + } + // trigger sync event + this.trigger('sync', this, null, options) + return Promise.resolve() } - // trigger sync event - this.trigger('sync', this, null, options); - return Promise.resolve(); - } - var success = options.success; - options = _.extend({}, options); - options.success = function() { - self.fetched = true; - if (success) { - return success.apply(this, arguments); + var success = options.success + options = _.extend({}, options) + options.success = function() { + self.fetched = true + if (success) { + return success.apply(this, arguments) + } } - }; - return OC.Backbone.Collection.prototype.fetch.call(this, options); - } - }); + return OC.Backbone.Collection.prototype.fetch.call(this, options) + } + }) - OC.SystemTags = OC.SystemTags || {}; - OC.SystemTags.SystemTagsCollection = SystemTagsCollection; + OC.SystemTags = OC.SystemTags || {} + OC.SystemTags.SystemTagsCollection = SystemTagsCollection /** * @type OC.SystemTags.SystemTagsCollection */ - OC.SystemTags.collection = new OC.SystemTags.SystemTagsCollection(); -})(OC); - + OC.SystemTags.collection = new OC.SystemTags.SystemTagsCollection() +})(OC) diff --git a/core/js/systemtags/systemtagsinputfield.js b/core/js/systemtags/systemtagsinputfield.js index 82fd659c72e3d95d57b151377e6c64b774f752b2..6065b0e28d64ce84119f84b3e93d91d7c53a4568 100644 --- a/core/js/systemtags/systemtagsinputfield.js +++ b/core/js/systemtags/systemtagsinputfield.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2015 * @@ -22,19 +23,19 @@ var SystemTagsInputField = OC.Backbone.View.extend( /** @lends OC.SystemTags.SystemTagsInputField.prototype */ { - _rendered: false, + _rendered: false, - _newTag: null, + _newTag: null, - _lastUsedTags: [], + _lastUsedTags: [], - className: 'systemTagsInputFieldContainer', + className: 'systemTagsInputFieldContainer', - template: function(data) { - return '<input class="systemTagsInputField" type="hidden" name="tags" value=""/>'; - }, + template: function(data) { + return '<input class="systemTagsInputField" type="hidden" name="tags" value=""/>' + }, - /** + /** * Creates a new SystemTagsInputField * * @param {Object} [options] @@ -45,402 +46,401 @@ * @param {bool} [options.isAdmin=true] whether the user is an administrator * @param {Function} options.initSelection function to convert selection to data */ - initialize: function(options) { - options = options || {}; + initialize: function(options) { + options = options || {} - this._multiple = !!options.multiple; - this._allowActions = _.isUndefined(options.allowActions) || !!options.allowActions; - this._allowCreate = _.isUndefined(options.allowCreate) || !!options.allowCreate; - this._isAdmin = !!options.isAdmin; + this._multiple = !!options.multiple + this._allowActions = _.isUndefined(options.allowActions) || !!options.allowActions + this._allowCreate = _.isUndefined(options.allowCreate) || !!options.allowCreate + this._isAdmin = !!options.isAdmin - if (_.isFunction(options.initSelection)) { - this._initSelection = options.initSelection; - } + if (_.isFunction(options.initSelection)) { + this._initSelection = options.initSelection + } - this.collection = options.collection || OC.SystemTags.collection; + this.collection = options.collection || OC.SystemTags.collection - var self = this; - this.collection.on('change:name remove', function() { + var self = this + this.collection.on('change:name remove', function() { // refresh selection - _.defer(self._refreshSelection); - }); - - _.defer(_.bind(this._getLastUsedTags, this)); - - _.bindAll( - this, - '_refreshSelection', - '_onClickRenameTag', - '_onClickDeleteTag', - '_onSelectTag', - '_onDeselectTag', - '_onSubmitRenameTag' - ); - }, - - _getLastUsedTags: function() { - var self = this; - $.ajax({ - type: 'GET', - url: OC.generateUrl('/apps/systemtags/lastused'), - success: function (response) { - self._lastUsedTags = response; - } - }); - }, + _.defer(self._refreshSelection) + }) + + _.defer(_.bind(this._getLastUsedTags, this)) + + _.bindAll( + this, + '_refreshSelection', + '_onClickRenameTag', + '_onClickDeleteTag', + '_onSelectTag', + '_onDeselectTag', + '_onSubmitRenameTag' + ) + }, + + _getLastUsedTags: function() { + var self = this + $.ajax({ + type: 'GET', + url: OC.generateUrl('/apps/systemtags/lastused'), + success: function(response) { + self._lastUsedTags = response + } + }) + }, - /** + /** * Refreshes the selection, triggering a call to * select2's initSelection */ - _refreshSelection: function() { - this.$tagsField.select2('val', this.$tagsField.val()); - }, + _refreshSelection: function() { + this.$tagsField.select2('val', this.$tagsField.val()) + }, - /** + /** * Event handler whenever the user clicked the "rename" action. * This will display the rename field. */ - _onClickRenameTag: function(ev) { - var $item = $(ev.target).closest('.systemtags-item'); - var tagId = $item.attr('data-id'); - var tagModel = this.collection.get(tagId); - - var oldName = tagModel.get('name'); - var $renameForm = $(OC.SystemTags.Templates['result_form']({ - cid: this.cid, - name: oldName, - deleteTooltip: t('core', 'Delete'), - renameLabel: t('core', 'Rename'), - isAdmin: this._isAdmin - })); - $item.find('.label').after($renameForm); - $item.find('.label, .systemtags-actions').addClass('hidden'); - $item.closest('.select2-result').addClass('has-form'); - - $renameForm.find('[title]').tooltip({ - placement: 'bottom', - container: 'body' - }); - $renameForm.find('input').focus().selectRange(0, oldName.length); - return false; - }, - - /** + _onClickRenameTag: function(ev) { + var $item = $(ev.target).closest('.systemtags-item') + var tagId = $item.attr('data-id') + var tagModel = this.collection.get(tagId) + + var oldName = tagModel.get('name') + var $renameForm = $(OC.SystemTags.Templates['result_form']({ + cid: this.cid, + name: oldName, + deleteTooltip: t('core', 'Delete'), + renameLabel: t('core', 'Rename'), + isAdmin: this._isAdmin + })) + $item.find('.label').after($renameForm) + $item.find('.label, .systemtags-actions').addClass('hidden') + $item.closest('.select2-result').addClass('has-form') + + $renameForm.find('[title]').tooltip({ + placement: 'bottom', + container: 'body' + }) + $renameForm.find('input').focus().selectRange(0, oldName.length) + return false + }, + + /** * Event handler whenever the rename form has been submitted after * the user entered a new tag name. - * This will submit the change to the server. + * This will submit the change to the server. * * @param {Object} ev event */ - _onSubmitRenameTag: function(ev) { - ev.preventDefault(); - var $form = $(ev.target); - var $item = $form.closest('.systemtags-item'); - var tagId = $item.attr('data-id'); - var tagModel = this.collection.get(tagId); - var newName = $(ev.target).find('input').val().trim(); - if (newName && newName !== tagModel.get('name')) { - tagModel.save({'name': newName}); - // TODO: spinner, and only change text after finished saving - $item.find('.label').text(newName); - } - $item.find('.label, .systemtags-actions').removeClass('hidden'); - $form.remove(); - $item.closest('.select2-result').removeClass('has-form'); - }, + _onSubmitRenameTag: function(ev) { + ev.preventDefault() + var $form = $(ev.target) + var $item = $form.closest('.systemtags-item') + var tagId = $item.attr('data-id') + var tagModel = this.collection.get(tagId) + var newName = $(ev.target).find('input').val().trim() + if (newName && newName !== tagModel.get('name')) { + tagModel.save({ 'name': newName }) + // TODO: spinner, and only change text after finished saving + $item.find('.label').text(newName) + } + $item.find('.label, .systemtags-actions').removeClass('hidden') + $form.remove() + $item.closest('.select2-result').removeClass('has-form') + }, - /** + /** * Event handler whenever a tag must be deleted * * @param {Object} ev event */ - _onClickDeleteTag: function(ev) { - var $item = $(ev.target).closest('.systemtags-item'); - var tagId = $item.attr('data-id'); - this.collection.get(tagId).destroy(); - $(ev.target).tooltip('hide'); - $item.closest('.select2-result').remove(); - // TODO: spinner - return false; - }, - - _addToSelect2Selection: function(selection) { - var data = this.$tagsField.select2('data'); - data.push(selection); - this.$tagsField.select2('data', data); - }, - - /** + _onClickDeleteTag: function(ev) { + var $item = $(ev.target).closest('.systemtags-item') + var tagId = $item.attr('data-id') + this.collection.get(tagId).destroy() + $(ev.target).tooltip('hide') + $item.closest('.select2-result').remove() + // TODO: spinner + return false + }, + + _addToSelect2Selection: function(selection) { + var data = this.$tagsField.select2('data') + data.push(selection) + this.$tagsField.select2('data', data) + }, + + /** * Event handler whenever a tag is selected. * Also called whenever tag creation is requested through the dummy tag object. * * @param {Object} e event */ - _onSelectTag: function(e) { - var self = this; - var tag; - if (e.object && e.object.isNew) { + _onSelectTag: function(e) { + var self = this + var tag + if (e.object && e.object.isNew) { // newly created tag, check if existing // create a new tag - tag = this.collection.create({ - name: e.object.name.trim(), - userVisible: true, - userAssignable: true, - canAssign: true - }, { - success: function(model) { - self._addToSelect2Selection(model.toJSON()); - self._lastUsedTags.unshift(model.id); - self.trigger('select', model); - }, - error: function(model, xhr) { - if (xhr.status === 409) { + tag = this.collection.create({ + name: e.object.name.trim(), + userVisible: true, + userAssignable: true, + canAssign: true + }, { + success: function(model) { + self._addToSelect2Selection(model.toJSON()) + self._lastUsedTags.unshift(model.id) + self.trigger('select', model) + }, + error: function(model, xhr) { + if (xhr.status === 409) { // re-fetch collection to get the missing tag - self.collection.reset(); - self.collection.fetch({ - success: function(collection) { + self.collection.reset() + self.collection.fetch({ + success: function(collection) { // find the tag in the collection - var model = collection.where({ - name: e.object.name.trim(), - userVisible: true, - userAssignable: true - }); - if (model.length) { - model = model[0]; - // the tag already exists or was already assigned, - // add it to the list anyway - self._addToSelect2Selection(model.toJSON()); - self.trigger('select', model); + var model = collection.where({ + name: e.object.name.trim(), + userVisible: true, + userAssignable: true + }) + if (model.length) { + model = model[0] + // the tag already exists or was already assigned, + // add it to the list anyway + self._addToSelect2Selection(model.toJSON()) + self.trigger('select', model) + } } - } - }); + }) + } } - } - }); - this.$tagsField.select2('close'); - e.preventDefault(); - return false; - } else { - tag = this.collection.get(e.object.id); - this._lastUsedTags.unshift(tag.id); - } - this._newTag = null; - this.trigger('select', tag); - }, + }) + this.$tagsField.select2('close') + e.preventDefault() + return false + } else { + tag = this.collection.get(e.object.id) + this._lastUsedTags.unshift(tag.id) + } + this._newTag = null + this.trigger('select', tag) + }, - /** + /** * Event handler whenever a tag gets deselected. * * @param {Object} e event */ - _onDeselectTag: function(e) { - this.trigger('deselect', e.choice.id); - }, + _onDeselectTag: function(e) { + this.trigger('deselect', e.choice.id) + }, - /** + /** * Autocomplete function for dropdown results * * @param {Object} query select2 query object */ - _queryTagsAutocomplete: function(query) { - var self = this; - this.collection.fetch({ - success: function(collection) { - var tagModels = collection.filterByName(query.term.trim()); - if (!self._isAdmin) { - tagModels = _.filter(tagModels, function(tagModel) { - return tagModel.get('canAssign'); - }); + _queryTagsAutocomplete: function(query) { + var self = this + this.collection.fetch({ + success: function(collection) { + var tagModels = collection.filterByName(query.term.trim()) + if (!self._isAdmin) { + tagModels = _.filter(tagModels, function(tagModel) { + return tagModel.get('canAssign') + }) + } + query.callback({ + results: _.invoke(tagModels, 'toJSON') + }) } - query.callback({ - results: _.invoke(tagModels, 'toJSON') - }); - } - }); - }, + }) + }, - _preventDefault: function(e) { - e.stopPropagation(); - }, + _preventDefault: function(e) { + e.stopPropagation() + }, - /** + /** * Formats a single dropdown result * * @param {Object} data data to format - * @return {string} HTML markup + * @returns {string} HTML markup */ - _formatDropDownResult: function(data) { - return OC.SystemTags.Templates['result'](_.extend({ - renameTooltip: t('core', 'Rename'), - allowActions: this._allowActions, - tagMarkup: this._isAdmin ? OC.SystemTags.getDescriptiveTag(data)[0].innerHTML : null, - isAdmin: this._isAdmin - }, data)); - }, - - /** + _formatDropDownResult: function(data) { + return OC.SystemTags.Templates['result'](_.extend({ + renameTooltip: t('core', 'Rename'), + allowActions: this._allowActions, + tagMarkup: this._isAdmin ? OC.SystemTags.getDescriptiveTag(data)[0].innerHTML : null, + isAdmin: this._isAdmin + }, data)) + }, + + /** * Formats a single selection item * * @param {Object} data data to format - * @return {string} HTML markup + * @returns {string} HTML markup */ - _formatSelection: function(data) { - return OC.SystemTags.Templates['selection'](_.extend({ - tagMarkup: this._isAdmin ? OC.SystemTags.getDescriptiveTag(data)[0].innerHTML : null, - isAdmin: this._isAdmin - }, data)); - }, - - /** + _formatSelection: function(data) { + return OC.SystemTags.Templates['selection'](_.extend({ + tagMarkup: this._isAdmin ? OC.SystemTags.getDescriptiveTag(data)[0].innerHTML : null, + isAdmin: this._isAdmin + }, data)) + }, + + /** * Create new dummy choice for select2 when the user * types an arbitrary string * * @param {string} term entered term - * @return {Object} dummy tag + * @returns {Object} dummy tag */ - _createSearchChoice: function(term) { - term = term.trim(); - if (this.collection.filter(function(entry) { - return entry.get('name') === term; + _createSearchChoice: function(term) { + term = term.trim() + if (this.collection.filter(function(entry) { + return entry.get('name') === term }).length) { - return; - } - if (!this._newTag) { - this._newTag = { - id: -1, - name: term, - userAssignable: true, - userVisible: true, - canAssign: true, - isNew: true - }; - } else { - this._newTag.name = term; - } + return + } + if (!this._newTag) { + this._newTag = { + id: -1, + name: term, + userAssignable: true, + userVisible: true, + canAssign: true, + isNew: true + } + } else { + this._newTag.name = term + } - return this._newTag; - }, + return this._newTag + }, - _initSelection: function(element, callback) { - var self = this; - var ids = $(element).val().split(','); + _initSelection: function(element, callback) { + var self = this + var ids = $(element).val().split(',') - function modelToSelection(model) { - var data = model.toJSON(); - if (!self._isAdmin && !data.canAssign) { + function modelToSelection(model) { + var data = model.toJSON() + if (!self._isAdmin && !data.canAssign) { // lock static tags for non-admins - data.locked = true; + data.locked = true + } + return data } - return data; - } - - function findSelectedObjects(ids) { - var selectedModels = self.collection.filter(function(model) { - return ids.indexOf(model.id) >= 0 && (self._isAdmin || model.get('userVisible')); - }); - return _.map(selectedModels, modelToSelection); - } - this.collection.fetch({ - success: function() { - callback(findSelectedObjects(ids)); + function findSelectedObjects(ids) { + var selectedModels = self.collection.filter(function(model) { + return ids.indexOf(model.id) >= 0 && (self._isAdmin || model.get('userVisible')) + }) + return _.map(selectedModels, modelToSelection) } - }); - }, - /** + this.collection.fetch({ + success: function() { + callback(findSelectedObjects(ids)) + } + }) + }, + + /** * Renders this details view */ - render: function() { - var self = this; - this.$el.html(this.template()); - - this.$el.find('[title]').tooltip({placement: 'bottom'}); - this.$tagsField = this.$el.find('[name=tags]'); - this.$tagsField.select2({ - placeholder: t('core', 'Collaborative tags'), - containerCssClass: 'systemtags-select2-container', - dropdownCssClass: 'systemtags-select2-dropdown', - closeOnSelect: false, - allowClear: false, - multiple: this._multiple, - toggleSelect: this._multiple, - query: _.bind(this._queryTagsAutocomplete, this), - id: function(tag) { - return tag.id; - }, - initSelection: _.bind(this._initSelection, this), - formatResult: _.bind(this._formatDropDownResult, this), - formatSelection: _.bind(this._formatSelection, this), - createSearchChoice: this._allowCreate ? _.bind(this._createSearchChoice, this) : undefined, - sortResults: function(results) { - var selectedItems = _.pluck(self.$tagsField.select2('data'), 'id'); - results.sort(function(a, b) { - var aSelected = selectedItems.indexOf(a.id) >= 0; - var bSelected = selectedItems.indexOf(b.id) >= 0; - if (aSelected === bSelected) { - var aLastUsed = self._lastUsedTags.indexOf(a.id); - var bLastUsed = self._lastUsedTags.indexOf(b.id); - - if (aLastUsed !== bLastUsed) { - if (bLastUsed === -1) { - return -1; - } - if (aLastUsed === -1) { - return 1; + render: function() { + var self = this + this.$el.html(this.template()) + + this.$el.find('[title]').tooltip({ placement: 'bottom' }) + this.$tagsField = this.$el.find('[name=tags]') + this.$tagsField.select2({ + placeholder: t('core', 'Collaborative tags'), + containerCssClass: 'systemtags-select2-container', + dropdownCssClass: 'systemtags-select2-dropdown', + closeOnSelect: false, + allowClear: false, + multiple: this._multiple, + toggleSelect: this._multiple, + query: _.bind(this._queryTagsAutocomplete, this), + id: function(tag) { + return tag.id + }, + initSelection: _.bind(this._initSelection, this), + formatResult: _.bind(this._formatDropDownResult, this), + formatSelection: _.bind(this._formatSelection, this), + createSearchChoice: this._allowCreate ? _.bind(this._createSearchChoice, this) : undefined, + sortResults: function(results) { + var selectedItems = _.pluck(self.$tagsField.select2('data'), 'id') + results.sort(function(a, b) { + var aSelected = selectedItems.indexOf(a.id) >= 0 + var bSelected = selectedItems.indexOf(b.id) >= 0 + if (aSelected === bSelected) { + var aLastUsed = self._lastUsedTags.indexOf(a.id) + var bLastUsed = self._lastUsedTags.indexOf(b.id) + + if (aLastUsed !== bLastUsed) { + if (bLastUsed === -1) { + return -1 + } + if (aLastUsed === -1) { + return 1 + } + return aLastUsed < bLastUsed ? -1 : 1 } - return aLastUsed < bLastUsed ? -1 : 1; - } - // Both not found - return OC.Util.naturalSortCompare(a.name, b.name); - } - if (aSelected && !bSelected) { - return -1; - } - return 1; - }); - return results; - }, - formatNoMatches: function() { - return t('core', 'No tags found'); + // Both not found + return OC.Util.naturalSortCompare(a.name, b.name) + } + if (aSelected && !bSelected) { + return -1 + } + return 1 + }) + return results + }, + formatNoMatches: function() { + return t('core', 'No tags found') + } + }) + .on('select2-selecting', this._onSelectTag) + .on('select2-removing', this._onDeselectTag) + + var $dropDown = this.$tagsField.select2('dropdown') + // register events for inside the dropdown + $dropDown.on('mouseup', '.rename', this._onClickRenameTag) + $dropDown.on('mouseup', '.delete', this._onClickDeleteTag) + $dropDown.on('mouseup', '.select2-result-selectable.has-form', this._preventDefault) + $dropDown.on('submit', '.systemtags-rename-form', this._onSubmitRenameTag) + + this.delegateEvents() + }, + + remove: function() { + if (this.$tagsField) { + this.$tagsField.select2('destroy') } - }) - .on('select2-selecting', this._onSelectTag) - .on('select2-removing', this._onDeselectTag); - - var $dropDown = this.$tagsField.select2('dropdown'); - // register events for inside the dropdown - $dropDown.on('mouseup', '.rename', this._onClickRenameTag); - $dropDown.on('mouseup', '.delete', this._onClickDeleteTag); - $dropDown.on('mouseup', '.select2-result-selectable.has-form', this._preventDefault); - $dropDown.on('submit', '.systemtags-rename-form', this._onSubmitRenameTag); - - this.delegateEvents(); - }, - - remove: function() { - if (this.$tagsField) { - this.$tagsField.select2('destroy'); - } - }, + }, - getValues: function() { - this.$tagsField.select2('val'); - }, + getValues: function() { + this.$tagsField.select2('val') + }, - setValues: function(values) { - this.$tagsField.select2('val', values); - }, + setValues: function(values) { + this.$tagsField.select2('val', values) + }, - setData: function(data) { - this.$tagsField.select2('data', data); - } - }); - - OC.SystemTags = OC.SystemTags || {}; - OC.SystemTags.SystemTagsInputField = SystemTagsInputField; + setData: function(data) { + this.$tagsField.select2('data', data) + } + }) -})(OC); + OC.SystemTags = OC.SystemTags || {} + OC.SystemTags.SystemTagsInputField = SystemTagsInputField +})(OC) diff --git a/core/js/systemtags/systemtagsmappingcollection.js b/core/js/systemtags/systemtagsmappingcollection.js index f30a9dbd98e9c1c0f80ed1b4c1f2220926895683..d3f1d0b873610485ceed78db423f6a35a25752d3 100644 --- a/core/js/systemtags/systemtagsmappingcollection.js +++ b/core/js/systemtags/systemtagsmappingcollection.js @@ -19,69 +19,68 @@ var SystemTagsMappingCollection = OC.Backbone.Collection.extend( /** @lends OC.SystemTags.SystemTagsMappingCollection.prototype */ { - sync: OC.Backbone.davSync, + sync: OC.Backbone.davSync, - /** + /** * Use PUT instead of PROPPATCH */ - usePUT: true, + usePUT: true, - /** + /** * Id of the file for which to filter activities by * * @var int */ - _objectId: null, + _objectId: null, - /** + /** * Type of the object to filter by * * @var string */ - _objectType: 'files', + _objectType: 'files', - model: OC.SystemTags.SystemTagModel, + model: OC.SystemTags.SystemTagModel, - url: function() { - return OC.linkToRemote('dav') + '/systemtags-relations/' + this._objectType + '/' + this._objectId; - }, + url: function() { + return OC.linkToRemote('dav') + '/systemtags-relations/' + this._objectType + '/' + this._objectId + }, - /** + /** * Sets the object id to filter by or null for all. * * @param {int} objectId file id or null */ - setObjectId: function(objectId) { - this._objectId = objectId; - }, + setObjectId: function(objectId) { + this._objectId = objectId + }, - /** + /** * Sets the object type to filter by or null for all. * * @param {int} objectType file id or null */ - setObjectType: function(objectType) { - this._objectType = objectType; - }, + setObjectType: function(objectType) { + this._objectType = objectType + }, - initialize: function(models, options) { - options = options || {}; - if (!_.isUndefined(options.objectId)) { - this._objectId = options.objectId; - } - if (!_.isUndefined(options.objectType)) { - this._objectType = options.objectType; - } - }, + initialize: function(models, options) { + options = options || {} + if (!_.isUndefined(options.objectId)) { + this._objectId = options.objectId + } + if (!_.isUndefined(options.objectType)) { + this._objectType = options.objectType + } + }, - getTagIds: function() { - return this.map(function(model) { - return model.id; - }); - } - }); - - OC.SystemTags = OC.SystemTags || {}; - OC.SystemTags.SystemTagsMappingCollection = SystemTagsMappingCollection; -})(OC); + getTagIds: function() { + return this.map(function(model) { + return model.id + }) + } + }) + OC.SystemTags = OC.SystemTags || {} + OC.SystemTags.SystemTagsMappingCollection = SystemTagsMappingCollection +})(OC) diff --git a/core/search/js/search.js b/core/search/js/search.js index 0822eee03640957f0338c873f74f5af7cf169624..4f5499d03c8a407ffca1d4a85ac6dd9fb89c5056 100644 --- a/core/search/js/search.js +++ b/core/search/js/search.js @@ -31,8 +31,8 @@ * This is a simple method. Register a new search with your function as references. * The events will forward the search or reset directly * - * @param {function} searchCallback the function to run on a query search - * @param {function} resetCallback the function to run when the user reset the form + * @param {Function} searchCallback the function to run on a query search + * @param {Function} resetCallback the function to run when the user reset the form */ var Search = function(searchCallback, resetCallback) { this.initialize(searchCallback, resetCallback); @@ -45,8 +45,8 @@ /** * Initialize the search box * - * @param {function} searchCallback the function to run on a query search - * @param {function} resetCallback the function to run when the user reset the form + * @param {Function} searchCallback the function to run on a query search + * @param {Function} resetCallback the function to run when the user reset the form */ initialize: function(searchCallback, resetCallback) { diff --git a/core/src/OC/admin.js b/core/src/OC/admin.js index 565af9946f7629e17252bd97215a63b7631f7c0c..008016645d9c6a1bd7a0e6a09e63570f86b9c385 100644 --- a/core/src/OC/admin.js +++ b/core/src/OC/admin.js @@ -24,7 +24,7 @@ const isAdmin = !!window._oc_isadmin /** * Returns whether the current user is an administrator * - * @return {bool} true if the user is an admin, false otherwise + * @returns {bool} true if the user is an admin, false otherwise * @since 9.0.0 */ export const isUserAdmin = () => isAdmin diff --git a/core/src/OC/appconfig.js b/core/src/OC/appconfig.js index 1248475ce77f8b88c871a83e0f515d442f5791c5..37fc3ca420dfb52aad7188d4f89effb13c84ba3d 100644 --- a/core/src/OC/appconfig.js +++ b/core/src/OC/appconfig.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * @@ -18,7 +19,7 @@ * */ -import {getValue, setValue, getApps, getKeys, deleteKey} from '../OCP/appconfig' + import { getValue, setValue, getApps, getKeys, deleteKey } from '../OCP/appconfig' export const appConfig = window.oc_appconfig || {} @@ -30,42 +31,42 @@ export const AppConfig = { /** * @deprecated Use OCP.AppConfig.getValue() instead */ - getValue: function (app, key, defaultValue, callback) { + getValue: function(app, key, defaultValue, callback) { getValue(app, key, defaultValue, { success: callback - }); + }) }, /** * @deprecated Use OCP.AppConfig.setValue() instead */ - setValue: function (app, key, value) { - setValue(app, key, value); + setValue: function(app, key, value) { + setValue(app, key, value) }, /** * @deprecated Use OCP.AppConfig.getApps() instead */ - getApps: function (callback) { + getApps: function(callback) { getApps({ success: callback - }); + }) }, /** * @deprecated Use OCP.AppConfig.getKeys() instead */ - getKeys: function (app, callback) { + getKeys: function(app, callback) { getKeys(app, { success: callback - }); + }) }, /** * @deprecated Use OCP.AppConfig.deleteKey() instead */ - deleteKey: function (app, key) { - deleteKey(app, key); + deleteKey: function(app, key) { + deleteKey(app, key) } -}; +} diff --git a/core/src/OC/apps.js b/core/src/OC/apps.js index d476e74c811c962e6d6f4720446f66d4af7544f3..f94059a42127834293504527156d23cd100ba470 100644 --- a/core/src/OC/apps.js +++ b/core/src/OC/apps.js @@ -8,26 +8,26 @@ * @copyright Bernhard Posselt 2014 */ -import $ from 'jquery'; +import $ from 'jquery' -var dynamicSlideToggleEnabled = false; +var dynamicSlideToggleEnabled = false const Apps = { - enableDynamicSlideToggle: function () { - dynamicSlideToggleEnabled = true; + enableDynamicSlideToggle: function() { + dynamicSlideToggleEnabled = true } -}; +} /** * Shows the #app-sidebar and add .with-app-sidebar to subsequent siblings * * @param {Object} [$el] sidebar element to show, defaults to $('#app-sidebar') */ -Apps.showAppSidebar = function ($el) { - var $appSidebar = $el || $('#app-sidebar'); - $appSidebar.removeClass('disappear').show(); - $('#app-content').trigger(new $.Event('appresized')); -}; +Apps.showAppSidebar = function($el) { + var $appSidebar = $el || $('#app-sidebar') + $appSidebar.removeClass('disappear').show() + $('#app-content').trigger(new $.Event('appresized')) +} /** * Shows the #app-sidebar and removes .with-app-sidebar from subsequent @@ -35,11 +35,11 @@ Apps.showAppSidebar = function ($el) { * * @param {Object} [$el] sidebar element to hide, defaults to $('#app-sidebar') */ -Apps.hideAppSidebar = function ($el) { - var $appSidebar = $el || $('#app-sidebar'); - $appSidebar.hide().addClass('disappear'); - $('#app-content').trigger(new $.Event('appresized')); -}; +Apps.hideAppSidebar = function($el) { + var $appSidebar = $el || $('#app-sidebar') + $appSidebar.hide().addClass('disappear') + $('#app-content').trigger(new $.Event('appresized')) +} /** * Provides a way to slide down a target area through a button and slide it @@ -51,40 +51,40 @@ Apps.hideAppSidebar = function ($el) { * <div class=".slide-area" class="hidden">I'm sliding up</div> */ export const registerAppsSlideToggle = () => { - var buttons = $('[data-apps-slide-toggle]'); + var buttons = $('[data-apps-slide-toggle]') if (buttons.length === 0) { - $('#app-navigation').addClass('without-app-settings'); + $('#app-navigation').addClass('without-app-settings') } - $(document).click(function (event) { + $(document).click(function(event) { if (dynamicSlideToggleEnabled) { - buttons = $('[data-apps-slide-toggle]'); + buttons = $('[data-apps-slide-toggle]') } - buttons.each(function (index, button) { + buttons.each(function(index, button) { - var areaSelector = $(button).data('apps-slide-toggle'); - var area = $(areaSelector); + var areaSelector = $(button).data('apps-slide-toggle') + var area = $(areaSelector) - function hideArea () { - area.slideUp(OC.menuSpeed * 4, function () { - area.trigger(new $.Event('hide')); - }); - area.removeClass('opened'); - $(button).removeClass('opened'); + function hideArea() { + area.slideUp(OC.menuSpeed * 4, function() { + area.trigger(new $.Event('hide')) + }) + area.removeClass('opened') + $(button).removeClass('opened') } - function showArea () { - area.slideDown(OC.menuSpeed * 4, function () { - area.trigger(new $.Event('show')); - }); - area.addClass('opened'); - $(button).addClass('opened'); - var input = $(areaSelector + ' [autofocus]'); + function showArea() { + area.slideDown(OC.menuSpeed * 4, function() { + area.trigger(new $.Event('show')) + }) + area.addClass('opened') + $(button).addClass('opened') + var input = $(areaSelector + ' [autofocus]') if (input.length === 1) { - input.focus(); + input.focus() } } @@ -94,23 +94,23 @@ export const registerAppsSlideToggle = () => { // button toggles the area if ($(button).is($(event.target).closest('[data-apps-slide-toggle]'))) { if (area.is(':visible')) { - hideArea(); + hideArea() } else { - showArea(); + showArea() } // all other areas that have not been clicked but are open // should be slid up } else { - var closest = $(event.target).closest(areaSelector); + var closest = $(event.target).closest(areaSelector) if (area.is(':visible') && closest[0] !== area[0]) { - hideArea(); + hideArea() } } } - }); + }) - }); -}; + }) +} -export default Apps; +export default Apps diff --git a/core/src/OC/appsettings.js b/core/src/OC/appsettings.js index b62af5c592a1221a6fd95e5f699bdb8aa5fc1397..7665d93fb7ceff06a0077fa6f685d4edea3de7df 100644 --- a/core/src/OC/appsettings.js +++ b/core/src/OC/appsettings.js @@ -1,4 +1,5 @@ -/* +/* eslint-disable */ +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -18,9 +19,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - import $ from 'jquery' -import {filePath} from './routing' +import { filePath } from './routing' /** * Opens a popup with the setting for an app. @@ -42,7 +42,7 @@ export const appSettings = args => { message: 'The parameter appid is missing' } } - var props = {scriptName: 'settings.php', cache: true} + var props = { scriptName: 'settings.php', cache: true } $.extend(props, args) var settings = $('#appsettings') if (settings.length === 0) { @@ -61,10 +61,10 @@ export const appSettings = args => { popup.hide().remove() } else { const arrowclass = settings.hasClass('topright') ? 'up' : 'left' - $.get(filePath(props.appid, '', props.scriptName), function (data) { - popup.html(data).ready(function () { + $.get(filePath(props.appid, '', props.scriptName), function(data) { + popup.html(data).ready(function() { popup.prepend('<span class="arrow ' + arrowclass + '"></span><h2>' + t('core', 'Settings') + '</h2><a class="close"></a>').show() - popup.find('.close').bind('click', function () { + popup.find('.close').bind('click', function() { popup.remove() }) if (typeof props.loadJS !== 'undefined') { @@ -80,10 +80,10 @@ export const appSettings = args => { } } if (props.cache) { - $.ajaxSetup({cache: true}) + $.ajaxSetup({ cache: true }) } $.getScript(filePath(props.appid, 'js', scriptname)) - .fail(function (jqxhr, settings, e) { + .fail(function(jqxhr, settings, e) { throw e }) } diff --git a/core/src/OC/backbone-webdav.js b/core/src/OC/backbone-webdav.js index d007054630f6f20275967063f61dbcebe00a799b..b27d117e5689cb5dbff89109d30ba8a9818f10d6 100644 --- a/core/src/OC/backbone-webdav.js +++ b/core/src/OC/backbone-webdav.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* * Copyright (c) 2015 * @@ -53,8 +54,8 @@ * }); */ -import _ from 'underscore'; -import dav from 'davclient.js'; +import _ from 'underscore' +import dav from 'davclient.js' const methodMap = { create: 'POST', @@ -62,11 +63,11 @@ const methodMap = { patch: 'PROPPATCH', delete: 'DELETE', read: 'PROPFIND' -}; +} // Throw an error when a URL is needed, and none is supplied. -function urlError () { - throw new Error('A "url" property or function must be specified'); +function urlError() { + throw new Error('A "url" property or function must be specified') } /** @@ -75,184 +76,184 @@ function urlError () { * @param {Object} result * @param {Object} davProperties properties mapping */ -function parsePropFindResult (result, davProperties) { +function parsePropFindResult(result, davProperties) { if (_.isArray(result)) { - return _.map(result, function (subResult) { - return parsePropFindResult(subResult, davProperties); - }); + return _.map(result, function(subResult) { + return parsePropFindResult(subResult, davProperties) + }) } var props = { href: result.href - }; + } - _.each(result.propStat, function (propStat) { + _.each(result.propStat, function(propStat) { if (propStat.status !== 'HTTP/1.1 200 OK') { - return; + return } for (var key in propStat.properties) { - var propKey = key; + var propKey = key if (key in davProperties) { - propKey = davProperties[key]; + propKey = davProperties[key] } - props[propKey] = propStat.properties[key]; + props[propKey] = propStat.properties[key] } - }); + }) if (!props.id) { // parse id from href - props.id = parseIdFromLocation(props.href); + props.id = parseIdFromLocation(props.href) } - return props; + return props } /** * Parse ID from location * * @param {string} url url - * @return {string} id + * @returns {string} id */ -function parseIdFromLocation (url) { - var queryPos = url.indexOf('?'); +function parseIdFromLocation(url) { + var queryPos = url.indexOf('?') if (queryPos > 0) { - url = url.substr(0, queryPos); + url = url.substr(0, queryPos) } - var parts = url.split('/'); - var result; + var parts = url.split('/') + var result do { - result = parts[parts.length - 1]; - parts.pop(); + result = parts[parts.length - 1] + parts.pop() // note: first result can be empty when there is a trailing slash, // so we take the part before that - } while (!result && parts.length > 0); + } while (!result && parts.length > 0) - return result; + return result } -function isSuccessStatus (status) { - return status >= 200 && status <= 299; +function isSuccessStatus(status) { + return status >= 200 && status <= 299 } -function convertModelAttributesToDavProperties (attrs, davProperties) { - var props = {}; - var key; +function convertModelAttributesToDavProperties(attrs, davProperties) { + var props = {} + var key for (key in attrs) { - var changedProp = davProperties[key]; - var value = attrs[key]; + var changedProp = davProperties[key] + var value = attrs[key] if (!changedProp) { - console.warn('No matching DAV property for property "' + key); - changedProp = key; + console.warn('No matching DAV property for property "' + key) + changedProp = key } if (_.isBoolean(value) || _.isNumber(value)) { // convert to string - value = '' + value; + value = '' + value } - props[changedProp] = value; + props[changedProp] = value } - return props; + return props } -function callPropFind (client, options, model, headers) { +function callPropFind(client, options, model, headers) { return client.propFind( options.url, _.values(options.davProperties) || [], options.depth, headers - ).then(function (response) { + ).then(function(response) { if (isSuccessStatus(response.status)) { if (_.isFunction(options.success)) { - var propsMapping = _.invert(options.davProperties); - var results = parsePropFindResult(response.body, propsMapping); + var propsMapping = _.invert(options.davProperties) + var results = parsePropFindResult(response.body, propsMapping) if (options.depth > 0) { // discard root entry - results.shift(); + results.shift() } - options.success(results); - return; + options.success(results) + } } else if (_.isFunction(options.error)) { - options.error(response); + options.error(response) } - }); + }) } -function callPropPatch (client, options, model, headers) { +function callPropPatch(client, options, model, headers) { return client.propPatch( options.url, convertModelAttributesToDavProperties(model.changed, options.davProperties), headers - ).then(function (result) { + ).then(function(result) { if (isSuccessStatus(result.status)) { if (_.isFunction(options.success)) { // pass the object's own values because the server // does not return the updated model - options.success(model.toJSON()); + options.success(model.toJSON()) } } else if (_.isFunction(options.error)) { - options.error(result); + options.error(result) } - }); + }) } -function callMkCol (client, options, model, headers) { +function callMkCol(client, options, model, headers) { // call MKCOL without data, followed by PROPPATCH return client.request( options.type, options.url, headers, null - ).then(function (result) { + ).then(function(result) { if (!isSuccessStatus(result.status)) { if (_.isFunction(options.error)) { - options.error(result); + options.error(result) } - return; + return } - callPropPatch(client, options, model, headers); - }); + callPropPatch(client, options, model, headers) + }) } -function callMethod (client, options, model, headers) { - headers['Content-Type'] = 'application/json'; +function callMethod(client, options, model, headers) { + headers['Content-Type'] = 'application/json' return client.request( options.type, options.url, headers, options.data - ).then(function (result) { + ).then(function(result) { if (!isSuccessStatus(result.status)) { if (_.isFunction(options.error)) { - options.error(result); + options.error(result) } - return; + return } if (_.isFunction(options.success)) { if (options.type === 'PUT' || options.type === 'POST' || options.type === 'MKCOL') { // pass the object's own values because the server // does not return anything - var responseJson = result.body || model.toJSON(); - var locationHeader = result.xhr.getResponseHeader('Content-Location'); + var responseJson = result.body || model.toJSON() + var locationHeader = result.xhr.getResponseHeader('Content-Location') if (options.type === 'POST' && locationHeader) { - responseJson.id = parseIdFromLocation(locationHeader); + responseJson.id = parseIdFromLocation(locationHeader) } - options.success(responseJson); - return; + options.success(responseJson) + return } // if multi-status, parse if (result.status === 207) { - var propsMapping = _.invert(options.davProperties); - options.success(parsePropFindResult(result.body, propsMapping)); + var propsMapping = _.invert(options.davProperties) + options.success(parsePropFindResult(result.body, propsMapping)) } else { - options.success(result.body); + options.success(result.body) } } - }); + }) } export const davCall = (options, model) => { @@ -262,22 +263,22 @@ export const davCall = (options, model) => { 'DAV:': 'd', 'http://owncloud.org/ns': 'oc' }, options.xmlNamespaces || {}) - }); - client.resolveUrl = function () { - return options.url; - }; + }) + client.resolveUrl = function() { + return options.url + } var headers = _.extend({ 'X-Requested-With': 'XMLHttpRequest', 'requesttoken': OC.requestToken - }, options.headers); + }, options.headers) if (options.type === 'PROPFIND') { - return callPropFind(client, options, model, headers); + return callPropFind(client, options, model, headers) } else if (options.type === 'PROPPATCH') { - return callPropPatch(client, options, model, headers); + return callPropPatch(client, options, model, headers) } else if (options.type === 'MKCOL') { - return callMkCol(client, options, model, headers); + return callMkCol(client, options, model, headers) } else { - return callMethod(client, options, model, headers); + return callMethod(client, options, model, headers) } } @@ -285,73 +286,73 @@ export const davCall = (options, model) => { * DAV transport */ export const davSync = Backbone => (method, model, options) => { - var params = {type: methodMap[method] || method}; - var isCollection = (model instanceof Backbone.Collection); + var params = { type: methodMap[method] || method } + var isCollection = (model instanceof Backbone.Collection) if (method === 'update') { // if a model has an inner collection, it must define an // attribute "hasInnerCollection" that evaluates to true if (model.hasInnerCollection) { // if the model itself is a Webdav collection, use MKCOL - params.type = 'MKCOL'; + params.type = 'MKCOL' } else if (model.usePUT || (model.collection && model.collection.usePUT)) { // use PUT instead of PROPPATCH - params.type = 'PUT'; + params.type = 'PUT' } } // Ensure that we have a URL. if (!options.url) { - params.url = _.result(model, 'url') || urlError(); + params.url = _.result(model, 'url') || urlError() } // Ensure that we have the appropriate request data. if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { - params.data = JSON.stringify(options.attrs || model.toJSON(options)); + params.data = JSON.stringify(options.attrs || model.toJSON(options)) } // Don't process data on a non-GET request. if (params.type !== 'PROPFIND') { - params.processData = false; + params.processData = false } if (params.type === 'PROPFIND' || params.type === 'PROPPATCH') { - var davProperties = model.davProperties; + var davProperties = model.davProperties if (!davProperties && model.model) { // use dav properties from model in case of collection - davProperties = model.model.prototype.davProperties; + davProperties = model.model.prototype.davProperties } if (davProperties) { if (_.isFunction(davProperties)) { - params.davProperties = davProperties.call(model); + params.davProperties = davProperties.call(model) } else { - params.davProperties = davProperties; + params.davProperties = davProperties } } - params.davProperties = _.extend(params.davProperties || {}, options.davProperties); + params.davProperties = _.extend(params.davProperties || {}, options.davProperties) if (_.isUndefined(options.depth)) { if (isCollection) { - options.depth = 1; + options.depth = 1 } else { - options.depth = 0; + options.depth = 0 } } } // Pass along `textStatus` and `errorThrown` from jQuery. - var error = options.error; - options.error = function (xhr, textStatus, errorThrown) { - options.textStatus = textStatus; - options.errorThrown = errorThrown; + var error = options.error + options.error = function(xhr, textStatus, errorThrown) { + options.textStatus = textStatus + options.errorThrown = errorThrown if (error) { - error.call(options.context, xhr, textStatus, errorThrown); + error.call(options.context, xhr, textStatus, errorThrown) } - }; + } // Make the request, allowing the user to override any Ajax options. - var xhr = options.xhr = Backbone.davCall(_.extend(params, options), model); - model.trigger('request', model, xhr, options); - return xhr; + var xhr = options.xhr = Backbone.davCall(_.extend(params, options), model) + model.trigger('request', model, xhr, options) + return xhr } diff --git a/core/src/OC/backbone.js b/core/src/OC/backbone.js index 07b5593eff92f2f97ee9139961635d99078201cf..38bfc0465015a449ce2f0d39084ca29d95ffd3c4 100644 --- a/core/src/OC/backbone.js +++ b/core/src/OC/backbone.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -19,15 +19,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import VendorBackbone from 'backbone'; -import {davCall, davSync} from './backbone-webdav'; +import VendorBackbone from 'backbone' +import { davCall, davSync } from './backbone-webdav' -const Backbone = VendorBackbone.noConflict(); +const Backbone = VendorBackbone.noConflict() // Patch Backbone for DAV Object.assign(Backbone, { davCall, - davSync: davSync(Backbone), -}); + davSync: davSync(Backbone) +}) -export default Backbone; +export default Backbone diff --git a/core/src/OC/capabilities.js b/core/src/OC/capabilities.js index 1adfe80d288f0659a606daf7cb3aef7177be22c1..2e0a8da39951e1eb434b6239e7316152d5420cf8 100644 --- a/core/src/OC/capabilities.js +++ b/core/src/OC/capabilities.js @@ -24,7 +24,7 @@ const capabilities = window._oc_capabilities || {} /** * Returns the capabilities * - * @return {Array} capabilities + * @returns {Array} capabilities * * @since 14.0 */ diff --git a/core/src/OC/constants.js b/core/src/OC/constants.js index d77a2d28c70d5df2f439f8b29758a2d8ef96ddcd..972848997ae47eb2b94c94db9df3280dfc2f4620 100644 --- a/core/src/OC/constants.js +++ b/core/src/OC/constants.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -19,7 +19,7 @@ * along with this program. If not see <http://www.gnu.org/licenses/>. */ -export const coreApps = ['', 'admin','log','core/search','core','3rdparty'] +export const coreApps = ['', 'admin', 'log', 'core/search', 'core', '3rdparty'] export const menuSpeed = 50 export const PERMISSION_NONE = 0 export const PERMISSION_CREATE = 4 diff --git a/core/src/OC/contactsmenu.js b/core/src/OC/contactsmenu.js index 0af4bad412d0f257ed34b98e7f549f9262251417..e986c46ec209d7a1d7dc2f4a982bf9f903f6f6d9 100644 --- a/core/src/OC/contactsmenu.js +++ b/core/src/OC/contactsmenu.js @@ -1,4 +1,4 @@ -/* global Backbone, Handlebars, Promise, _ */ +/* eslint-disable */ /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -22,10 +22,10 @@ * */ -import $ from 'jquery'; -import {Collection, Model, View} from 'backbone'; +import $ from 'jquery' +import { Collection, Model, View } from 'backbone' -import OC from './index'; +import OC from './index' /** * @class Contact @@ -43,18 +43,18 @@ const Contact = Model.extend({ /** * @returns {undefined} */ - initialize: function () { + initialize: function() { // Add needed property for easier template rendering if (this.get('actions').length === 0) { - this.set('hasOneAction', true); + this.set('hasOneAction', true) } else if (this.get('actions').length === 1) { - this.set('hasTwoActions', true); - this.set('secondAction', this.get('actions')[0]); + this.set('hasTwoActions', true) + this.set('secondAction', this.get('actions')[0]) } else { - this.set('hasManyActions', true); + this.set('hasManyActions', true) } } -}); +}) /** * @class ContactCollection @@ -62,7 +62,7 @@ const Contact = Model.extend({ */ const ContactCollection = Collection.extend({ model: Contact -}); +}) /** * @class ContactsListView @@ -80,29 +80,29 @@ const ContactsListView = View.extend({ * @param {object} options * @returns {undefined} */ - initialize: function (options) { - this._collection = options.collection; + initialize: function(options) { + this._collection = options.collection }, /** * @returns {self} */ - render: function () { - var self = this; - self.$el.html(''); - self._subViews = []; + render: function() { + var self = this + self.$el.html('') + self._subViews = [] - self._collection.forEach(function (contact) { + self._collection.forEach(function(contact) { var item = new ContactsListItemView({ model: contact - }); - item.render(); - self.$el.append(item.$el); - item.on('toggle:actionmenu', self._onChildActionMenuToggle, self); - self._subViews.push(item); - }); - - return self; + }) + item.render() + self.$el.append(item.$el) + item.on('toggle:actionmenu', self._onChildActionMenuToggle, self) + self._subViews.push(item) + }) + + return self }, /** @@ -111,12 +111,12 @@ const ContactsListView = View.extend({ * @param {type} $src * @returns {undefined} */ - _onChildActionMenuToggle: function ($src) { - this._subViews.forEach(function (view) { - view.trigger('parent:toggle:actionmenu', $src); - }); + _onChildActionMenuToggle: function($src) { + this._subViews.forEach(function(view) { + view.trigger('parent:toggle:actionmenu', $src) + }) } -}); +}) /** * @class ContactsListItemView @@ -146,37 +146,37 @@ const ContactsListItemView = View.extend({ * @param {object} data * @returns {undefined} */ - template: function (data) { - return this.contactTemplate(data); + template: function(data) { + return this.contactTemplate(data) }, /** * @param {object} options * @returns {undefined} */ - initialize: function (options) { - this._model = options.model; - this.on('parent:toggle:actionmenu', this._onOtherActionMenuOpened, this); + initialize: function(options) { + this._model = options.model + this.on('parent:toggle:actionmenu', this._onOtherActionMenuOpened, this) }, /** * @returns {self} */ - render: function () { + render: function() { this.$el.html(this.template({ contact: this._model.toJSON() - })); - this.delegateEvents(); + })) + this.delegateEvents() // Show placeholder if no avatar is available (avatar is rendered as img, not div) - this.$('div.avatar').imageplaceholder(this._model.get('fullName')); + this.$('div.avatar').imageplaceholder(this._model.get('fullName')) // Show tooltip for top action - this.$('.top-action').tooltip({placement: 'left'}); + this.$('.top-action').tooltip({ placement: 'left' }) // Show tooltip for second action - this.$('.second-action').tooltip({placement: 'left'}); + this.$('.second-action').tooltip({ placement: 'left' }) - return this; + return this }, /** @@ -185,14 +185,14 @@ const ContactsListItemView = View.extend({ * @private * @returns {undefined} */ - _onToggleActionsMenu: function () { - this._actionMenuShown = !this._actionMenuShown; + _onToggleActionsMenu: function() { + this._actionMenuShown = !this._actionMenuShown if (this._actionMenuShown) { - this.$('.menu').show(); + this.$('.menu').show() } else { - this.$('.menu').hide(); + this.$('.menu').hide() } - this.trigger('toggle:actionmenu', this.$el); + this.trigger('toggle:actionmenu', this.$el) }, /** @@ -200,15 +200,15 @@ const ContactsListItemView = View.extend({ * @argument {jQuery} $src * @returns {undefined} */ - _onOtherActionMenuOpened: function ($src) { + _onOtherActionMenuOpened: function($src) { if (this.$el.is($src)) { // Ignore - return; + return } - this._actionMenuShown = false; - this.$('.menu').hide(); + this._actionMenuShown = false + this.$('.menu').hide() } -}); +}) /** * @class ContactsMenuView @@ -248,15 +248,15 @@ const ContactsMenuView = View.extend({ /** * @returns {undefined} */ - _onSearch: _.debounce(function (e) { - var searchTerm = this.$('#contactsmenu-search').val(); + _onSearch: _.debounce(function(e) { + var searchTerm = this.$('#contactsmenu-search').val() // IE11 triggers an 'input' event after the view has been rendered // resulting in an endless loading loop. To prevent this, we remember // the last search term to savely ignore some events // See https://github.com/nextcloud/server/issues/5281 if (searchTerm !== this._searchTerm) { - this.trigger('search', this.$('#contactsmenu-search').val()); - this._searchTerm = searchTerm; + this.trigger('search', this.$('#contactsmenu-search').val()) + this._searchTerm = searchTerm } }, 700), @@ -264,75 +264,75 @@ const ContactsMenuView = View.extend({ * @param {object} data * @returns {string} */ - loadingTemplate: function (data) { - return this.templates.loading(data); + loadingTemplate: function(data) { + return this.templates.loading(data) }, /** * @param {object} data * @returns {string} */ - errorTemplate: function (data) { + errorTemplate: function(data) { return this.templates.error( _.extend({ couldNotLoadText: t('core', 'Could not load your contacts') }, data) - ); + ) }, /** * @param {object} data * @returns {string} */ - contentTemplate: function (data) { + contentTemplate: function(data) { return this.templates.menu( _.extend({ searchContactsText: t('core', 'Search contacts …') }, data) - ); + ) }, /** * @param {object} data * @returns {string} */ - contactsTemplate: function (data) { + contactsTemplate: function(data) { return this.templates.list( _.extend({ noContactsFoundText: t('core', 'No contacts found'), showAllContactsText: t('core', 'Show all contacts …'), contactsAppMgmtText: t('core', 'Install the Contacts app') }, data) - ); + ) }, /** * @param {object} options * @returns {undefined} */ - initialize: function (options) { - this.options = options; + initialize: function(options) { + this.options = options }, /** * @param {string} text * @returns {undefined} */ - showLoading: function (text) { - this.render(); - this._contacts = undefined; + showLoading: function(text) { + this.render() + this._contacts = undefined this.$('.content').html(this.loadingTemplate({ loadingText: text - })); + })) }, /** * @returns {undefined} */ - showError: function () { - this.render(); - this._contacts = undefined; - this.$('.content').html(this.errorTemplate()); + showError: function() { + this.render() + this._contacts = undefined + this.$('.content').html(this.errorTemplate()) }, /** @@ -340,16 +340,16 @@ const ContactsMenuView = View.extend({ * @param {string} searchTerm * @returns {undefined} */ - showContacts: function (viewData, searchTerm) { - this._contacts = viewData.contacts; + showContacts: function(viewData, searchTerm) { + this._contacts = viewData.contacts this.render({ contacts: viewData.contacts - }); + }) var list = new ContactsListView({ collection: viewData.contacts - }); - list.render(); + }) + list.render() this.$('.content').html(this.contactsTemplate({ contacts: viewData.contacts, searchTerm: searchTerm, @@ -357,25 +357,25 @@ const ContactsMenuView = View.extend({ contactsAppURL: OC.generateUrl('/apps/contacts'), canInstallApp: OC.isUserAdmin(), contactsAppMgmtURL: OC.generateUrl('/settings/apps/social/contacts') - })); - this.$('#contactsmenu-contacts').html(list.$el); + })) + this.$('#contactsmenu-contacts').html(list.$el) }, /** * @param {object} data * @returns {self} */ - render: function (data) { - var searchVal = this.$('#contactsmenu-search').val(); - this.$el.html(this.contentTemplate(data)); + render: function(data) { + var searchVal = this.$('#contactsmenu-search').val() + this.$el.html(this.contentTemplate(data)) // Focus search - this.$('#contactsmenu-search').val(searchVal); - this.$('#contactsmenu-search').focus(); - return this; + this.$('#contactsmenu-search').val(searchVal) + this.$('#contactsmenu-search').focus() + return this } -}); +}) /** * @param {Object} options @@ -384,9 +384,9 @@ const ContactsMenuView = View.extend({ * @class ContactsMenu * @memberOf OC */ -const ContactsMenu = function (options) { - this.initialize(options); -}; +const ContactsMenu = function(options) { + this.initialize(options) +} ContactsMenu.prototype = { /** @type {jQuery} */ @@ -407,23 +407,23 @@ ContactsMenu.prototype = { * @param {jQuery} options.trigger - the element to click on to open the menu * @returns {undefined} */ - initialize: function (options) { - this.$el = options.el; - this._$trigger = options.trigger; + initialize: function(options) { + this.$el = options.el + this._$trigger = options.trigger this._view = new ContactsMenuView({ el: this.$el - }); - this._view.on('search', function (searchTerm) { - this._loadContacts(searchTerm); - }, this); - - OC.registerMenu(this._$trigger, this.$el, function () { - this._toggleVisibility(true); - }.bind(this), true); - this.$el.on('beforeHide', function () { - this._toggleVisibility(false); - }.bind(this)); + }) + this._view.on('search', function(searchTerm) { + this._loadContacts(searchTerm) + }, this) + + OC.registerMenu(this._$trigger, this.$el, function() { + this._toggleVisibility(true) + }.bind(this), true) + this.$el.on('beforeHide', function() { + this._toggleVisibility(false) + }.bind(this)) }, /** @@ -431,12 +431,12 @@ ContactsMenu.prototype = { * @param {boolean} show * @returns {Promise} */ - _toggleVisibility: function (show) { + _toggleVisibility: function(show) { if (show) { - return this._loadContacts(); + return this._loadContacts() } else { - this.$el.html(''); - return Promise.resolve(); + this.$el.html('') + return Promise.resolve() } }, @@ -445,48 +445,48 @@ ContactsMenu.prototype = { * @param {string|undefined} searchTerm * @returns {Promise} */ - _getContacts: function (searchTerm) { - var url = OC.generateUrl('/contactsmenu/contacts'); + _getContacts: function(searchTerm) { + var url = OC.generateUrl('/contactsmenu/contacts') return Promise.resolve($.ajax(url, { method: 'POST', data: { filter: searchTerm } - })); + })) }, /** * @param {string|undefined} searchTerm * @returns {undefined} */ - _loadContacts: function (searchTerm) { - var self = this; + _loadContacts: function(searchTerm) { + var self = this if (!self._contactsPromise) { - self._contactsPromise = self._getContacts(searchTerm); + self._contactsPromise = self._getContacts(searchTerm) } if (_.isUndefined(searchTerm) || searchTerm === '') { - self._view.showLoading(t('core', 'Loading your contacts …')); + self._view.showLoading(t('core', 'Loading your contacts …')) } else { self._view.showLoading(t('core', 'Looking for {term} …', { term: searchTerm - })); + })) } - return self._contactsPromise.then(function (data) { + return self._contactsPromise.then(function(data) { // Convert contact entries to Backbone collection - data.contacts = new ContactCollection(data.contacts); + data.contacts = new ContactCollection(data.contacts) - self._view.showContacts(data, searchTerm); - }, function (e) { - self._view.showError(); - console.error('There was an error loading your contacts', e); - }).then(function () { + self._view.showContacts(data, searchTerm) + }, function(e) { + self._view.showError() + console.error('There was an error loading your contacts', e) + }).then(function() { // Delete promise, so that contacts are fetched again when the // menu is opened the next time. - delete self._contactsPromise; - }).catch(console.error.bind(this)); + delete self._contactsPromise + }).catch(console.error.bind(this)) } -}; +} -export default ContactsMenu; +export default ContactsMenu diff --git a/core/src/OC/currentuser.js b/core/src/OC/currentuser.js index 061abba89d669982b288e0a96f7bfa27dc84c534..c8f9d59c1ebc249bf2ee372ac586d16aa98c14da 100644 --- a/core/src/OC/currentuser.js +++ b/core/src/OC/currentuser.js @@ -31,6 +31,6 @@ export const currentUser = rawUid !== undefined ? rawUid : false export const getCurrentUser = () => { return { uid: currentUser, - displayName, + displayName } } diff --git a/core/src/OC/dialogs.js b/core/src/OC/dialogs.js index b5a2c2c01b33dbb8d3fe800f599e7fae81a10bf4..abc5b3e1c3a4b237fbd97410f345c38f4bceceeb 100644 --- a/core/src/OC/dialogs.js +++ b/core/src/OC/dialogs.js @@ -1,5 +1,5 @@ /* global alert */ - +/* eslint-disable */ /* * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * @@ -42,14 +42,15 @@ const Dialogs = { // used to name each dialog dialogsCounter: 0, + /** * displays alert dialog - * @param text content of dialog - * @param title dialog title - * @param callback which will be triggered when user presses OK - * @param modal make the dialog modal + * @param {string} text content of dialog + * @param {string} title dialog title + * @param {function} callback which will be triggered when user presses OK + * @param {boolean} [modal] make the dialog modal */ - alert: function (text, title, callback, modal) { + alert: function(text, title, callback, modal) { this.message( text, title, @@ -57,27 +58,28 @@ const Dialogs = { Dialogs.OK_BUTTON, callback, modal - ); + ) }, /** * displays info dialog - * @param text content of dialog - * @param title dialog title - * @param callback which will be triggered when user presses OK - * @param modal make the dialog modal + * @param {string} text content of dialog + * @param {string} title dialog title + * @param {function} callback which will be triggered when user presses OK + * @param {boolean} [modal] make the dialog modal */ - info: function (text, title, callback, modal) { - this.message(text, title, 'info', Dialogs.OK_BUTTON, callback, modal); + info: function(text, title, callback, modal) { + this.message(text, title, 'info', Dialogs.OK_BUTTON, callback, modal) }, + /** * displays confirmation dialog - * @param text content of dialog - * @param title dialog title - * @param callback which will be triggered when user presses YES or NO - * (true or false would be passed to callback respectively) - * @param modal make the dialog modal + * @param {string} text content of dialog + * @param {string} title dialog title + * @param {function} callback which will be triggered when user presses OK (true or false would be passed to callback respectively) + * @param {boolean} [modal] make the dialog modal + * @returns {Promise} */ - confirm: function (text, title, callback, modal) { + confirm: function(text, title, callback, modal) { return this.message( text, title, @@ -85,17 +87,17 @@ const Dialogs = { Dialogs.YES_NO_BUTTONS, callback, modal - ); + ) }, /** * displays confirmation dialog - * @param text content of dialog - * @param title dialog title - * @param callback which will be triggered when user presses YES or NO - * (true or false would be passed to callback respectively) - * @param modal make the dialog modal + * @param {string} text content of dialog + * @param {string} title dialog title + * @param {function} callback which will be triggered when user presses OK (true or false would be passed to callback respectively) + * @param {boolean} [modal] make the dialog modal + * @returns {Promise} */ - confirmHtml: function (text, title, callback, modal) { + confirmHtml: function(text, title, callback, modal) { return this.message( text, title, @@ -104,79 +106,81 @@ const Dialogs = { callback, modal, true - ); + ) }, /** * displays prompt dialog - * @param text content of dialog - * @param title dialog title - * @param callback which will be triggered when user presses YES or NO - * (true or false would be passed to callback respectively) - * @param modal make the dialog modal - * @param name name of the input field - * @param password whether the input should be a password input + * @param {string} text content of dialog + * @param {string} title dialog title + * @param {function} callback which will be triggered when user presses OK (true or false would be passed to callback respectively) + * @param {boolean} [modal] make the dialog modal + * @param {string} name name of the input field + * @param {boolean} password whether the input should be a password input + * @returns {Promise} */ - prompt: function (text, title, callback, modal, name, password) { - return $.when(this._getMessageTemplate()).then(function ($tmpl) { - var dialogName = 'oc-dialog-' + Dialogs.dialogsCounter + '-content'; - var dialogId = '#' + dialogName; + prompt: function(text, title, callback, modal, name, password) { + return $.when(this._getMessageTemplate()).then(function($tmpl) { + var dialogName = 'oc-dialog-' + Dialogs.dialogsCounter + '-content' + var dialogId = '#' + dialogName var $dlg = $tmpl.octemplate({ dialog_name: dialogName, title: title, message: text, type: 'notice' - }); - var input = $('<input/>'); - input.attr('type', password ? 'password' : 'text').attr('id', dialogName + '-input').attr('placeholder', name); - var label = $('<label/>').attr('for', dialogName + '-input').text(name + ': '); - $dlg.append(label); - $dlg.append(input); + }) + var input = $('<input/>') + input.attr('type', password ? 'password' : 'text').attr('id', dialogName + '-input').attr('placeholder', name) + var label = $('<label/>').attr('for', dialogName + '-input').text(name + ': ') + $dlg.append(label) + $dlg.append(input) if (modal === undefined) { - modal = false; + modal = false } - $('body').append($dlg); + $('body').append($dlg) // wrap callback in _.once(): // only call callback once and not twice (button handler and close // event) but call it for the close event, if ESC or the x is hit if (callback !== undefined) { - callback = _.once(callback); + callback = _.once(callback) } var buttonlist = [{ text: t('core', 'No'), - click: function () { + click: function() { if (callback !== undefined) { - callback(false, input.val()); + // eslint-disable-next-line standard/no-callback-literal + callback(false, input.val()) } - $(dialogId).ocdialog('close'); + $(dialogId).ocdialog('close') } }, { text: t('core', 'Yes'), - click: function () { + click: function() { if (callback !== undefined) { - callback(true, input.val()); + // eslint-disable-next-line standard/no-callback-literal + callback(true, input.val()) } - $(dialogId).ocdialog('close'); + $(dialogId).ocdialog('close') }, defaultButton: true - } - ]; + }] $(dialogId).ocdialog({ closeOnEscape: true, modal: modal, buttons: buttonlist, - close: function () { + close: function() { // callback is already fired if Yes/No is clicked directly if (callback !== undefined) { - callback(false, input.val()); + // eslint-disable-next-line standard/no-callback-literal + callback(false, input.val()) } } - }); - input.focus(); - Dialogs.dialogsCounter++; - }); + }) + input.focus() + Dialogs.dialogsCounter++ + }) }, /** * show a file picker to pick a file from @@ -188,56 +192,56 @@ const Dialogs = { * be able to select both files and folders "['*', 'httpd/unix-directory']" * should be used instead. * - * @param title dialog title - * @param callback which will be triggered when user presses Choose - * @param multiselect whether it should be possible to select multiple files - * @param mimetypeFilter mimetype to filter by - directories will always be included - * @param modal make the dialog modal - * @param type Type of file picker : Choose, copy, move, copy and move - * @param path path to the folder that the the file can be picket from - * @param options additonal options that need to be set + * @param {string} title dialog title + * @param {function} callback which will be triggered when user presses Choose + * @param {boolean} [multiselect] whether it should be possible to select multiple files + * @param {string[]} [mimetypeFilter] mimetype to filter by - directories will always be included + * @param {boolean} [modal] make the dialog modal + * @param {string} [type] Type of file picker : Choose, copy, move, copy and move + * @param {string} [path] path to the folder that the the file can be picket from + * @param {Object} [options] additonal options that need to be set */ - filepicker: function (title, callback, multiselect, mimetypeFilter, modal, type, path, options) { - var self = this; + filepicker: function(title, callback, multiselect, mimetypeFilter, modal, type, path, options) { + var self = this - this.filepicker.sortField = 'name'; - this.filepicker.sortOrder = 'asc'; + this.filepicker.sortField = 'name' + this.filepicker.sortOrder = 'asc' // avoid opening the picker twice if (this.filepicker.loading) { - return; + return } if (type === undefined) { - type = this.FILEPICKER_TYPE_CHOOSE; + type = this.FILEPICKER_TYPE_CHOOSE } - var emptyText = t('core', 'No files in here'); - var newText = t('files', 'New folder'); + var emptyText = t('core', 'No files in here') + var newText = t('files', 'New folder') if (type === this.FILEPICKER_TYPE_COPY || type === this.FILEPICKER_TYPE_MOVE || type === this.FILEPICKER_TYPE_COPY_MOVE) { - emptyText = t('core', 'No more subfolders in here'); + emptyText = t('core', 'No more subfolders in here') } - this.filepicker.loading = true; - this.filepicker.filesClient = (OCA.Sharing && OCA.Sharing.PublicApp && OCA.Sharing.PublicApp.fileList) ? OCA.Sharing.PublicApp.fileList.filesClient : OC.Files.getClient(); + this.filepicker.loading = true + this.filepicker.filesClient = (OCA.Sharing && OCA.Sharing.PublicApp && OCA.Sharing.PublicApp.fileList) ? OCA.Sharing.PublicApp.fileList.filesClient : OC.Files.getClient() - this.filelist = null; - path = path || ''; + this.filelist = null + path = path || '' options = Object.assign({ allowDirectoryChooser: false }, options) - $.when(this._getFilePickerTemplate()).then(function ($tmpl) { - self.filepicker.loading = false; - var dialogName = 'oc-dialog-filepicker-content'; + $.when(this._getFilePickerTemplate()).then(function($tmpl) { + self.filepicker.loading = false + var dialogName = 'oc-dialog-filepicker-content' if (self.$filePicker) { - self.$filePicker.ocdialog('close'); + self.$filePicker.ocdialog('close') } if (mimetypeFilter === undefined || mimetypeFilter === null) { - mimetypeFilter = []; + mimetypeFilter = [] } - if (typeof (mimetypeFilter) === "string") { - mimetypeFilter = [mimetypeFilter]; + if (typeof (mimetypeFilter) === 'string') { + mimetypeFilter = [mimetypeFilter] } self.$filePicker = $tmpl.octemplate({ @@ -251,190 +255,190 @@ const Dialogs = { }).data('path', path).data('multiselect', multiselect).data('mimetype', mimetypeFilter).data('allowDirectoryChooser', options.allowDirectoryChooser) if (modal === undefined) { - modal = false; + modal = false } if (multiselect === undefined) { - multiselect = false; + multiselect = false } // No grid for IE! if (OC.Util.isIE()) { - self.$filePicker.find('#picker-view-toggle').remove(); - self.$filePicker.find('#picker-filestable').removeClass('view-grid'); + self.$filePicker.find('#picker-view-toggle').remove() + self.$filePicker.find('#picker-filestable').removeClass('view-grid') } - $('body').append(self.$filePicker); + $('body').append(self.$filePicker) - self.$showGridView = $('input#picker-showgridview'); - self.$showGridView.on('change', _.bind(self._onGridviewChange, self)); + self.$showGridView = $('input#picker-showgridview') + self.$showGridView.on('change', _.bind(self._onGridviewChange, self)) if (!OC.Util.isIE()) { - self._getGridSettings(); + self._getGridSettings() } - var newButton = self.$filePicker.find('.actions.creatable .button-add'); + var newButton = self.$filePicker.find('.actions.creatable .button-add') if (type === self.FILEPICKER_TYPE_CHOOSE) { - newButton.hide(); + newButton.hide() } - newButton.on('focus', function () { - self.$filePicker.ocdialog('setEnterCallback', function () { - event.stopImmediatePropagation(); - event.preventDefault(); - newButton.click(); - }); - }); - newButton.on('blur', function () { - self.$filePicker.ocdialog('unsetEnterCallback'); - }); - - OC.registerMenu(newButton, self.$filePicker.find('.menu'), function () { - $input.focus(); - self.$filePicker.ocdialog('setEnterCallback', function () { - event.stopImmediatePropagation(); - event.preventDefault(); - self.$form.submit(); - }); - var newName = $input.val(); - var lastPos = newName.lastIndexOf('.'); + newButton.on('focus', function() { + self.$filePicker.ocdialog('setEnterCallback', function() { + event.stopImmediatePropagation() + event.preventDefault() + newButton.click() + }) + }) + newButton.on('blur', function() { + self.$filePicker.ocdialog('unsetEnterCallback') + }) + + OC.registerMenu(newButton, self.$filePicker.find('.menu'), function() { + $input.focus() + self.$filePicker.ocdialog('setEnterCallback', function() { + event.stopImmediatePropagation() + event.preventDefault() + self.$form.submit() + }) + var newName = $input.val() + var lastPos = newName.lastIndexOf('.') if (lastPos === -1) { - lastPos = newName.length; + lastPos = newName.length } - $input.selectRange(0, lastPos); - }); - var $form = self.$filePicker.find('.filenameform'); - var $input = $form.find('input[type=\'text\']'); - var $submit = $form.find('input[type=\'submit\']'); - $submit.on('click', function (event) { - event.stopImmediatePropagation(); - event.preventDefault(); - $form.submit(); - }); - - var checkInput = function () { - var filename = $input.val(); + $input.selectRange(0, lastPos) + }) + var $form = self.$filePicker.find('.filenameform') + var $input = $form.find('input[type=\'text\']') + var $submit = $form.find('input[type=\'submit\']') + $submit.on('click', function(event) { + event.stopImmediatePropagation() + event.preventDefault() + $form.submit() + }) + + var checkInput = function() { + var filename = $input.val() try { if (!Files.isFileNameValid(filename)) { // Files.isFileNameValid(filename) throws an exception itself - } else if (self.filelist.find(function (file) { - return file.name === this; + } else if (self.filelist.find(function(file) { + return file.name === this }, filename)) { - throw t('files', '{newName} already exists', {newName: filename}, undefined, { + throw t('files', '{newName} already exists', { newName: filename }, undefined, { escape: false - }); + }) } else { - return true; + return true } } catch (error) { - $input.attr('title', error); + $input.attr('title', error) $input.tooltip({ placement: 'right', trigger: 'manual', 'container': '.newFolderMenu' - }); - $input.tooltip('fixTitle'); - $input.tooltip('show'); - $input.addClass('error'); + }) + $input.tooltip('fixTitle') + $input.tooltip('show') + $input.addClass('error') } - return false; - }; + return false + } - $form.on('submit', function (event) { - event.stopPropagation(); - event.preventDefault(); + $form.on('submit', function(event) { + event.stopPropagation() + event.preventDefault() if (checkInput()) { - var newname = $input.val(); + var newname = $input.val() self.filepicker.filesClient.createDirectory(self.$filePicker.data('path') + "/" + newname).always(function (status) { - self._fillFilePicker(self.$filePicker.data('path') + "/" + newname); - }); - OC.hideMenus(); - self.$filePicker.ocdialog('unsetEnterCallback'); - self.$filePicker.click(); - $input.val(newText); + self._fillFilePicker(self.$filePicker.data('path') + "/" + newname) + }) + OC.hideMenus() + self.$filePicker.ocdialog('unsetEnterCallback') + self.$filePicker.click() + $input.val(newText) } - }); - $input.keypress(function (event) { + }) + $input.keypress(function(event) { if (event.keyCode === 13 || event.which === 13) { - event.stopImmediatePropagation(); - event.preventDefault(); - $form.submit(); + event.stopImmediatePropagation() + event.preventDefault() + $form.submit() } - }); - - self.$filePicker.ready(function () { - self.$fileListHeader = self.$filePicker.find('.filelist thead tr'); - self.$filelist = self.$filePicker.find('.filelist tbody'); - self.$filelistContainer = self.$filePicker.find('.filelist-container'); - self.$dirTree = self.$filePicker.find('.dirtree'); - self.$dirTree.on('click', 'div:not(:last-child)', self, function (event) { - self._handleTreeListSelect(event, type); - }); - self.$filelist.on('click', 'tr', function (event) { - self._handlePickerClick(event, $(this), type); - }); - self.$fileListHeader.on('click', 'a', function (event) { - var dir = self.$filePicker.data('path'); - self.filepicker.sortField = $(event.currentTarget).data('sort'); - self.filepicker.sortOrder = self.filepicker.sortOrder === 'asc' ? 'desc' : 'asc'; - self._fillFilePicker(dir); - }); - self._fillFilePicker(path); - }); + }) + + self.$filePicker.ready(function() { + self.$fileListHeader = self.$filePicker.find('.filelist thead tr') + self.$filelist = self.$filePicker.find('.filelist tbody') + self.$filelistContainer = self.$filePicker.find('.filelist-container') + self.$dirTree = self.$filePicker.find('.dirtree') + self.$dirTree.on('click', 'div:not(:last-child)', self, function(event) { + self._handleTreeListSelect(event, type) + }) + self.$filelist.on('click', 'tr', function(event) { + self._handlePickerClick(event, $(this), type) + }) + self.$fileListHeader.on('click', 'a', function(event) { + var dir = self.$filePicker.data('path') + self.filepicker.sortField = $(event.currentTarget).data('sort') + self.filepicker.sortOrder = self.filepicker.sortOrder === 'asc' ? 'desc' : 'asc' + self._fillFilePicker(dir) + }) + self._fillFilePicker(path) + }) // build buttons - var functionToCall = function (returnType) { + var functionToCall = function(returnType) { if (callback !== undefined) { - var datapath; + var datapath if (multiselect === true) { - datapath = []; - self.$filelist.find('tr.filepicker_element_selected').each(function (index, element) { - datapath.push(self.$filePicker.data('path') + '/' + $(element).data('entryname')); - }); + datapath = [] + self.$filelist.find('tr.filepicker_element_selected').each(function(index, element) { + datapath.push(self.$filePicker.data('path') + '/' + $(element).data('entryname')) + }) } else { - datapath = self.$filePicker.data('path'); - var selectedName = self.$filelist.find('tr.filepicker_element_selected').data('entryname'); + datapath = self.$filePicker.data('path') + var selectedName = self.$filelist.find('tr.filepicker_element_selected').data('entryname') if (selectedName) { - datapath += '/' + selectedName; + datapath += '/' + selectedName } } - callback(datapath, returnType); - self.$filePicker.ocdialog('close'); + callback(datapath, returnType) + self.$filePicker.ocdialog('close') } - }; + } - var chooseCallback = function () { - functionToCall(Dialogs.FILEPICKER_TYPE_CHOOSE); - }; + var chooseCallback = function() { + functionToCall(Dialogs.FILEPICKER_TYPE_CHOOSE) + } - var copyCallback = function () { - functionToCall(Dialogs.FILEPICKER_TYPE_COPY); - }; + var copyCallback = function() { + functionToCall(Dialogs.FILEPICKER_TYPE_COPY) + } - var moveCallback = function () { - functionToCall(Dialogs.FILEPICKER_TYPE_MOVE); - }; + var moveCallback = function() { + functionToCall(Dialogs.FILEPICKER_TYPE_MOVE) + } - var buttonlist = []; + var buttonlist = [] if (type === Dialogs.FILEPICKER_TYPE_CHOOSE) { buttonlist.push({ text: t('core', 'Choose'), click: chooseCallback, defaultButton: true - }); + }) } else { if (type === Dialogs.FILEPICKER_TYPE_COPY || type === Dialogs.FILEPICKER_TYPE_COPY_MOVE) { buttonlist.push({ text: t('core', 'Copy'), click: copyCallback, defaultButton: false - }); + }) } if (type === Dialogs.FILEPICKER_TYPE_MOVE || type === Dialogs.FILEPICKER_TYPE_COPY_MOVE) { buttonlist.push({ text: t('core', 'Move'), click: moveCallback, defaultButton: true - }); + }) } } @@ -446,108 +450,108 @@ const Dialogs = { modal: modal, buttons: buttonlist, style: { - buttons: 'aside', + buttons: 'aside' }, - close: function () { + close: function() { try { - $(this).ocdialog('destroy').remove(); + $(this).ocdialog('destroy').remove() } catch (e) { } - self.$filePicker = null; + self.$filePicker = null } - }); + }) // We can access primary class only from oc-dialog. // Hence this is one of the approach to get the choose button. - var getOcDialog = self.$filePicker.closest('.oc-dialog'); - var buttonEnableDisable = getOcDialog.find('.primary'); - if (self.$filePicker.data('mimetype').indexOf("httpd/unix-directory") !== -1 && !self.$filePicker.data('.allowDirectoryChooser')) { - buttonEnableDisable.prop("disabled", false); + var getOcDialog = self.$filePicker.closest('.oc-dialog') + var buttonEnableDisable = getOcDialog.find('.primary') + if (self.$filePicker.data('mimetype').indexOf('httpd/unix-directory') !== -1 && !self.$filePicker.data('.allowDirectoryChooser')) { + buttonEnableDisable.prop('disabled', false) } else { - buttonEnableDisable.prop("disabled", true); + buttonEnableDisable.prop('disabled', true) } }) - .fail(function (status, error) { + .fail(function(status, error) { // If the method is called while navigating away // from the page, it is probably not needed ;) - self.filepicker.loading = false; + self.filepicker.loading = false if (status !== 0) { - alert(t('core', 'Error loading file picker template: {error}', {error: error})); + alert(t('core', 'Error loading file picker template: {error}', { error: error })) } - }); + }) }, /** * Displays raw dialog * You better use a wrapper instead ... */ - message: function (content, title, dialogType, buttons, callback, modal, allowHtml) { - return $.when(this._getMessageTemplate()).then(function ($tmpl) { - var dialogName = 'oc-dialog-' + Dialogs.dialogsCounter + '-content'; - var dialogId = '#' + dialogName; + message: function(content, title, dialogType, buttons, callback, modal, allowHtml) { + return $.when(this._getMessageTemplate()).then(function($tmpl) { + var dialogName = 'oc-dialog-' + Dialogs.dialogsCounter + '-content' + var dialogId = '#' + dialogName var $dlg = $tmpl.octemplate({ dialog_name: dialogName, title: title, message: content, type: dialogType - }, allowHtml ? {escapeFunction: ''} : {}); + }, allowHtml ? { escapeFunction: '' } : {}) if (modal === undefined) { - modal = false; + modal = false } - $('body').append($dlg); - var buttonlist = []; + $('body').append($dlg) + var buttonlist = [] switch (buttons) { - case Dialogs.YES_NO_BUTTONS: - buttonlist = [{ - text: t('core', 'No'), - click: function () { - if (callback !== undefined) { - callback(false); - } - $(dialogId).ocdialog('close'); + case Dialogs.YES_NO_BUTTONS: + buttonlist = [{ + text: t('core', 'No'), + click: function() { + if (callback !== undefined) { + callback(false) } - }, - { - text: t('core', 'Yes'), - click: function () { - if (callback !== undefined) { - callback(true); - } - $(dialogId).ocdialog('close'); - }, - defaultButton: true - }]; - break; - case Dialogs.OK_BUTTON: - var functionToCall = function () { - $(dialogId).ocdialog('close'); + $(dialogId).ocdialog('close') + } + }, + { + text: t('core', 'Yes'), + click: function() { if (callback !== undefined) { - callback(); + callback(true) } - }; - buttonlist[0] = { - text: t('core', 'OK'), - click: functionToCall, - defaultButton: true - }; - break; + $(dialogId).ocdialog('close') + }, + defaultButton: true + }] + break + case Dialogs.OK_BUTTON: + var functionToCall = function() { + $(dialogId).ocdialog('close') + if (callback !== undefined) { + callback() + } + } + buttonlist[0] = { + text: t('core', 'OK'), + click: functionToCall, + defaultButton: true + } + break } $(dialogId).ocdialog({ closeOnEscape: true, modal: modal, buttons: buttonlist - }); - Dialogs.dialogsCounter++; + }) + Dialogs.dialogsCounter++ }) - .fail(function (status, error) { + .fail(function(status, error) { // If the method is called while navigating away from // the page, we still want to deliver the message. if (status === 0) { - alert(title + ': ' + content); + alert(title + ': ' + content) } else { - alert(t('core', 'Error loading message template: {error}', {error: error})); + alert(t('core', 'Error loading message template: {error}', { error: error })) } - }); + }) }, _fileexistsshown: false, /** @@ -556,64 +560,64 @@ const Dialogs = { * @param {object} original file with name, size and mtime * @param {object} replacement file with name, size and mtime * @param {object} controller with onCancel, onSkip, onReplace and onRename methods - * @return {Promise} jquery promise that resolves after the dialog template was loaded + * @returns {Promise} jquery promise that resolves after the dialog template was loaded */ - fileexists: function (data, original, replacement, controller) { - var self = this; - var dialogDeferred = new $.Deferred(); + fileexists: function(data, original, replacement, controller) { + var self = this + var dialogDeferred = new $.Deferred() - var getCroppedPreview = function (file) { - var deferred = new $.Deferred(); + var getCroppedPreview = function(file) { + var deferred = new $.Deferred() // Only process image files. - var type = file.type && file.type.split('/').shift(); + var type = file.type && file.type.split('/').shift() if (window.FileReader && type === 'image') { - var reader = new FileReader(); - reader.onload = function (e) { - var blob = new Blob([e.target.result]); - window.URL = window.URL || window.webkitURL; - var originalUrl = window.URL.createObjectURL(blob); - var image = new Image(); - image.src = originalUrl; - image.onload = function () { - var url = crop(image); - deferred.resolve(url); - }; - }; - reader.readAsArrayBuffer(file); + var reader = new FileReader() + reader.onload = function(e) { + var blob = new Blob([e.target.result]) + window.URL = window.URL || window.webkitURL + var originalUrl = window.URL.createObjectURL(blob) + var image = new Image() + image.src = originalUrl + image.onload = function() { + var url = crop(image) + deferred.resolve(url) + } + } + reader.readAsArrayBuffer(file) } else { - deferred.reject(); + deferred.reject() } - return deferred; - }; + return deferred + } - var crop = function (img) { - var canvas = document.createElement('canvas'), - targetSize = 96, - width = img.width, - height = img.height, - x, y, size; + var crop = function(img) { + var canvas = document.createElement('canvas') + var targetSize = 96 + var width = img.width + var height = img.height + var x; var y; var size // Calculate the width and height, constraining the proportions if (width > height) { - y = 0; - x = (width - height) / 2; + y = 0 + x = (width - height) / 2 } else { - y = (height - width) / 2; - x = 0; + y = (height - width) / 2 + x = 0 } - size = Math.min(width, height); + size = Math.min(width, height) // Set canvas size to the cropped area - canvas.width = size; - canvas.height = size; - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, x, y, size, size, 0, 0, size, size); + canvas.width = size + canvas.height = size + var ctx = canvas.getContext('2d') + ctx.drawImage(img, x, y, size, size, 0, 0, size, size) // Resize the canvas to match the destination (right size uses 96px) - resampleHermite(canvas, size, size, targetSize, targetSize); + resampleHermite(canvas, size, size, targetSize, targetSize) - return canvas.toDataURL("image/png", 0.7); - }; + return canvas.toDataURL('image/png', 0.7) + } /** * Fast image resize/resample using Hermite filter with JavaScript. @@ -626,130 +630,129 @@ const Dialogs = { * @param {number} W2 * @param {number} H2 */ - var resampleHermite = function (canvas, W, H, W2, H2) { - W2 = Math.round(W2); - H2 = Math.round(H2); - var img = canvas.getContext("2d").getImageData(0, 0, W, H); - var img2 = canvas.getContext("2d").getImageData(0, 0, W2, H2); - var data = img.data; - var data2 = img2.data; - var ratio_w = W / W2; - var ratio_h = H / H2; - var ratio_w_half = Math.ceil(ratio_w / 2); - var ratio_h_half = Math.ceil(ratio_h / 2); + var resampleHermite = function(canvas, W, H, W2, H2) { + W2 = Math.round(W2) + H2 = Math.round(H2) + var img = canvas.getContext('2d').getImageData(0, 0, W, H) + var img2 = canvas.getContext('2d').getImageData(0, 0, W2, H2) + var data = img.data + var data2 = img2.data + var ratio_w = W / W2 + var ratio_h = H / H2 + var ratio_w_half = Math.ceil(ratio_w / 2) + var ratio_h_half = Math.ceil(ratio_h / 2) for (var j = 0; j < H2; j++) { for (var i = 0; i < W2; i++) { - var x2 = (i + j * W2) * 4; - var weight = 0; - var weights = 0; - var weights_alpha = 0; - var gx_r = 0; - var gx_g = 0; - var gx_b = 0; - var gx_a = 0; - var center_y = (j + 0.5) * ratio_h; + var x2 = (i + j * W2) * 4 + var weight = 0 + var weights = 0 + var weights_alpha = 0 + var gx_r = 0 + var gx_g = 0 + var gx_b = 0 + var gx_a = 0 + var center_y = (j + 0.5) * ratio_h for (var yy = Math.floor(j * ratio_h); yy < (j + 1) * ratio_h; yy++) { - var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half; - var center_x = (i + 0.5) * ratio_w; - var w0 = dy * dy; //pre-calc part of w + var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half + var center_x = (i + 0.5) * ratio_w + var w0 = dy * dy // pre-calc part of w for (var xx = Math.floor(i * ratio_w); xx < (i + 1) * ratio_w; xx++) { - var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half; - var w = Math.sqrt(w0 + dx * dx); + var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half + var w = Math.sqrt(w0 + dx * dx) if (w >= -1 && w <= 1) { - //hermite filter - weight = 2 * w * w * w - 3 * w * w + 1; + // hermite filter + weight = 2 * w * w * w - 3 * w * w + 1 if (weight > 0) { - dx = 4 * (xx + yy * W); - //alpha - gx_a += weight * data[dx + 3]; - weights_alpha += weight; - //colors - if (data[dx + 3] < 255) - weight = weight * data[dx + 3] / 250; - gx_r += weight * data[dx]; - gx_g += weight * data[dx + 1]; - gx_b += weight * data[dx + 2]; - weights += weight; + dx = 4 * (xx + yy * W) + // alpha + gx_a += weight * data[dx + 3] + weights_alpha += weight + // colors + if (data[dx + 3] < 255) { weight = weight * data[dx + 3] / 250 } + gx_r += weight * data[dx] + gx_g += weight * data[dx + 1] + gx_b += weight * data[dx + 2] + weights += weight } } } } - data2[x2] = gx_r / weights; - data2[x2 + 1] = gx_g / weights; - data2[x2 + 2] = gx_b / weights; - data2[x2 + 3] = gx_a / weights_alpha; + data2[x2] = gx_r / weights + data2[x2 + 1] = gx_g / weights + data2[x2 + 2] = gx_b / weights + data2[x2 + 3] = gx_a / weights_alpha } } - canvas.getContext("2d").clearRect(0, 0, Math.max(W, W2), Math.max(H, H2)); - canvas.width = W2; - canvas.height = H2; - canvas.getContext("2d").putImageData(img2, 0, 0); - }; + canvas.getContext('2d').clearRect(0, 0, Math.max(W, W2), Math.max(H, H2)) + canvas.width = W2 + canvas.height = H2 + canvas.getContext('2d').putImageData(img2, 0, 0) + } - var addConflict = function ($conflicts, original, replacement) { + var addConflict = function($conflicts, original, replacement) { - var $conflict = $conflicts.find('.template').clone().removeClass('template').addClass('conflict'); - var $originalDiv = $conflict.find('.original'); - var $replacementDiv = $conflict.find('.replacement'); + var $conflict = $conflicts.find('.template').clone().removeClass('template').addClass('conflict') + var $originalDiv = $conflict.find('.original') + var $replacementDiv = $conflict.find('.replacement') - $conflict.data('data', data); + $conflict.data('data', data) - $conflict.find('.filename').text(original.name); - $originalDiv.find('.size').text(humanFileSize(original.size)); - $originalDiv.find('.mtime').text(formatDate(original.mtime)); + $conflict.find('.filename').text(original.name) + $originalDiv.find('.size').text(humanFileSize(original.size)) + $originalDiv.find('.mtime').text(formatDate(original.mtime)) // ie sucks if (replacement.size && replacement.lastModifiedDate) { - $replacementDiv.find('.size').text(humanFileSize(replacement.size)); - $replacementDiv.find('.mtime').text(formatDate(replacement.lastModifiedDate)); + $replacementDiv.find('.size').text(humanFileSize(replacement.size)) + $replacementDiv.find('.mtime').text(formatDate(replacement.lastModifiedDate)) } - var path = original.directory + '/' + original.name; + var path = original.directory + '/' + original.name var urlSpec = { file: path, x: 96, y: 96, c: original.etag, forceIcon: 0 - }; - var previewpath = Files.generatePreviewUrl(urlSpec); + } + var previewpath = Files.generatePreviewUrl(urlSpec) // Escaping single quotes - previewpath = previewpath.replace(/'/g, "%27"); - $originalDiv.find('.icon').css({"background-image": "url('" + previewpath + "')"}); + previewpath = previewpath.replace(/'/g, '%27') + $originalDiv.find('.icon').css({ 'background-image': "url('" + previewpath + "')" }) getCroppedPreview(replacement).then( - function (path) { - $replacementDiv.find('.icon').css('background-image', 'url(' + path + ')'); - }, function () { - path = OC.MimeType.getIconUrl(replacement.type); - $replacementDiv.find('.icon').css('background-image', 'url(' + path + ')'); + function(path) { + $replacementDiv.find('.icon').css('background-image', 'url(' + path + ')') + }, function() { + path = OC.MimeType.getIconUrl(replacement.type) + $replacementDiv.find('.icon').css('background-image', 'url(' + path + ')') } - ); + ) // connect checkboxes with labels - var checkboxId = $conflicts.find('.conflict').length; - $originalDiv.find('input:checkbox').attr('id', 'checkbox_original_' + checkboxId); - $replacementDiv.find('input:checkbox').attr('id', 'checkbox_replacement_' + checkboxId); + var checkboxId = $conflicts.find('.conflict').length + $originalDiv.find('input:checkbox').attr('id', 'checkbox_original_' + checkboxId) + $replacementDiv.find('input:checkbox').attr('id', 'checkbox_replacement_' + checkboxId) - $conflicts.append($conflict); + $conflicts.append($conflict) - //set more recent mtime bold + // set more recent mtime bold // ie sucks if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() > original.mtime) { - $replacementDiv.find('.mtime').css('font-weight', 'bold'); + $replacementDiv.find('.mtime').css('font-weight', 'bold') } else if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() < original.mtime) { - $originalDiv.find('.mtime').css('font-weight', 'bold'); + $originalDiv.find('.mtime').css('font-weight', 'bold') } else { - //TODO add to same mtime collection? + // TODO add to same mtime collection? } // set bigger size bold if (replacement.size && replacement.size > original.size) { - $replacementDiv.find('.size').css('font-weight', 'bold'); + $replacementDiv.find('.size').css('font-weight', 'bold') } else if (replacement.size && replacement.size < original.size) { - $originalDiv.find('.size').css('font-weight', 'bold'); + $originalDiv.find('.size').css('font-weight', 'bold') } else { - //TODO add to same size collection? + // TODO add to same size collection? } - //TODO show skip action for files with same size and mtime in bottom row + // TODO show skip action for files with same size and mtime in bottom row // always keep readonly files @@ -758,40 +761,40 @@ const Dialogs = { .addClass('readonly') .find('input[type="checkbox"]') .prop('checked', true) - .prop('disabled', true); + .prop('disabled', true) $originalDiv.find('.message') - .text(t('core', 'read-only')); + .text(t('core', 'read-only')) } - }; - //var selection = controller.getSelection(data.originalFiles); - //if (selection.defaultAction) { + } + // var selection = controller.getSelection(data.originalFiles); + // if (selection.defaultAction) { // controller[selection.defaultAction](data); - //} else { - var dialogName = 'oc-dialog-fileexists-content'; - var dialogId = '#' + dialogName; + // } else { + var dialogName = 'oc-dialog-fileexists-content' + var dialogId = '#' + dialogName if (this._fileexistsshown) { // add conflict - var $conflicts = $(dialogId + ' .conflicts'); - addConflict($conflicts, original, replacement); + var $conflicts = $(dialogId + ' .conflicts') + addConflict($conflicts, original, replacement) - var count = $(dialogId + ' .conflict').length; + var count = $(dialogId + ' .conflict').length var title = n('core', '{count} file conflict', '{count} file conflicts', count, - {count: count} - ); - $(dialogId).parent().children('.oc-dialog-title').text(title); + { count: count } + ) + $(dialogId).parent().children('.oc-dialog-title').text(title) - //recalculate dimensions - $(window).trigger('resize'); - dialogDeferred.resolve(); + // recalculate dimensions + $(window).trigger('resize') + dialogDeferred.resolve() } else { - //create dialog - this._fileexistsshown = true; - $.when(this._getFileExistsTemplate()).then(function ($tmpl) { - var title = t('core', 'One file conflict'); + // create dialog + this._fileexistsshown = true + $.when(this._getFileExistsTemplate()).then(function($tmpl) { + var title = t('core', 'One file conflict') var $dlg = $tmpl.octemplate({ dialog_name: dialogName, title: title, @@ -802,34 +805,34 @@ const Dialogs = { why: t('core', 'Which files do you want to keep?'), what: t('core', 'If you select both versions, the copied file will have a number added to its name.') - }); - $('body').append($dlg); + }) + $('body').append($dlg) if (original && replacement) { - var $conflicts = $dlg.find('.conflicts'); - addConflict($conflicts, original, replacement); + var $conflicts = $dlg.find('.conflicts') + addConflict($conflicts, original, replacement) } var buttonlist = [{ text: t('core', 'Cancel'), classes: 'cancel', - click: function () { + click: function() { if (typeof controller.onCancel !== 'undefined') { - controller.onCancel(data); + controller.onCancel(data) } - $(dialogId).ocdialog('close'); + $(dialogId).ocdialog('close') } }, - { - text: t('core', 'Continue'), - classes: 'continue', - click: function () { - if (typeof controller.onContinue !== 'undefined') { - controller.onContinue($(dialogId + ' .conflict')); - } - $(dialogId).ocdialog('close'); + { + text: t('core', 'Continue'), + classes: 'continue', + click: function() { + if (typeof controller.onContinue !== 'undefined') { + controller.onContinue($(dialogId + ' .conflict')) } - }]; + $(dialogId).ocdialog('close') + } + }] $(dialogId).ocdialog({ width: 500, @@ -837,157 +840,157 @@ const Dialogs = { modal: true, buttons: buttonlist, closeButton: null, - close: function () { - self._fileexistsshown = false; - $(this).ocdialog('destroy').remove(); + close: function() { + self._fileexistsshown = false + $(this).ocdialog('destroy').remove() } - }); + }) - $(dialogId).css('height', 'auto'); + $(dialogId).css('height', 'auto') - var $primaryButton = $dlg.closest('.oc-dialog').find('button.continue'); - $primaryButton.prop('disabled', true); + var $primaryButton = $dlg.closest('.oc-dialog').find('button.continue') + $primaryButton.prop('disabled', true) - function updatePrimaryButton () { - var checkedCount = $dlg.find('.conflicts .checkbox:checked').length; - $primaryButton.prop('disabled', checkedCount === 0); + function updatePrimaryButton() { + var checkedCount = $dlg.find('.conflicts .checkbox:checked').length + $primaryButton.prop('disabled', checkedCount === 0) } - //add checkbox toggling actions - $(dialogId).find('.allnewfiles').on('click', function () { - var $checkboxes = $(dialogId).find('.conflict .replacement input[type="checkbox"]'); - $checkboxes.prop('checked', $(this).prop('checked')); - }); - $(dialogId).find('.allexistingfiles').on('click', function () { - var $checkboxes = $(dialogId).find('.conflict .original:not(.readonly) input[type="checkbox"]'); - $checkboxes.prop('checked', $(this).prop('checked')); - }); - $(dialogId).find('.conflicts').on('click', '.replacement,.original:not(.readonly)', function () { - var $checkbox = $(this).find('input[type="checkbox"]'); - $checkbox.prop('checked', !$checkbox.prop('checked')); - }); - $(dialogId).find('.conflicts').on('click', '.replacement input[type="checkbox"],.original:not(.readonly) input[type="checkbox"]', function () { - var $checkbox = $(this); - $checkbox.prop('checked', !$checkbox.prop('checked')); - }); - - //update counters - $(dialogId).on('click', '.replacement,.allnewfiles', function () { - var count = $(dialogId).find('.conflict .replacement input[type="checkbox"]:checked').length; + // add checkbox toggling actions + $(dialogId).find('.allnewfiles').on('click', function() { + var $checkboxes = $(dialogId).find('.conflict .replacement input[type="checkbox"]') + $checkboxes.prop('checked', $(this).prop('checked')) + }) + $(dialogId).find('.allexistingfiles').on('click', function() { + var $checkboxes = $(dialogId).find('.conflict .original:not(.readonly) input[type="checkbox"]') + $checkboxes.prop('checked', $(this).prop('checked')) + }) + $(dialogId).find('.conflicts').on('click', '.replacement,.original:not(.readonly)', function() { + var $checkbox = $(this).find('input[type="checkbox"]') + $checkbox.prop('checked', !$checkbox.prop('checked')) + }) + $(dialogId).find('.conflicts').on('click', '.replacement input[type="checkbox"],.original:not(.readonly) input[type="checkbox"]', function() { + var $checkbox = $(this) + $checkbox.prop('checked', !$checkbox.prop('checked')) + }) + + // update counters + $(dialogId).on('click', '.replacement,.allnewfiles', function() { + var count = $(dialogId).find('.conflict .replacement input[type="checkbox"]:checked').length if (count === $(dialogId + ' .conflict').length) { - $(dialogId).find('.allnewfiles').prop('checked', true); - $(dialogId).find('.allnewfiles + .count').text(t('core', '(all selected)')); + $(dialogId).find('.allnewfiles').prop('checked', true) + $(dialogId).find('.allnewfiles + .count').text(t('core', '(all selected)')) } else if (count > 0) { - $(dialogId).find('.allnewfiles').prop('checked', false); - $(dialogId).find('.allnewfiles + .count').text(t('core', '({count} selected)', {count: count})); + $(dialogId).find('.allnewfiles').prop('checked', false) + $(dialogId).find('.allnewfiles + .count').text(t('core', '({count} selected)', { count: count })) } else { - $(dialogId).find('.allnewfiles').prop('checked', false); - $(dialogId).find('.allnewfiles + .count').text(''); + $(dialogId).find('.allnewfiles').prop('checked', false) + $(dialogId).find('.allnewfiles + .count').text('') } - updatePrimaryButton(); - }); - $(dialogId).on('click', '.original,.allexistingfiles', function () { - var count = $(dialogId).find('.conflict .original input[type="checkbox"]:checked').length; + updatePrimaryButton() + }) + $(dialogId).on('click', '.original,.allexistingfiles', function() { + var count = $(dialogId).find('.conflict .original input[type="checkbox"]:checked').length if (count === $(dialogId + ' .conflict').length) { - $(dialogId).find('.allexistingfiles').prop('checked', true); - $(dialogId).find('.allexistingfiles + .count').text(t('core', '(all selected)')); + $(dialogId).find('.allexistingfiles').prop('checked', true) + $(dialogId).find('.allexistingfiles + .count').text(t('core', '(all selected)')) } else if (count > 0) { - $(dialogId).find('.allexistingfiles').prop('checked', false); + $(dialogId).find('.allexistingfiles').prop('checked', false) $(dialogId).find('.allexistingfiles + .count') - .text(t('core', '({count} selected)', {count: count})); + .text(t('core', '({count} selected)', { count: count })) } else { - $(dialogId).find('.allexistingfiles').prop('checked', false); - $(dialogId).find('.allexistingfiles + .count').text(''); + $(dialogId).find('.allexistingfiles').prop('checked', false) + $(dialogId).find('.allexistingfiles + .count').text('') } - updatePrimaryButton(); - }); + updatePrimaryButton() + }) - dialogDeferred.resolve(); + dialogDeferred.resolve() }) - .fail(function () { - dialogDeferred.reject(); - alert(t('core', 'Error loading file exists template')); - }); + .fail(function() { + dialogDeferred.reject() + alert(t('core', 'Error loading file exists template')) + }) } - //} - return dialogDeferred.promise(); + // } + return dialogDeferred.promise() }, // get the gridview setting and set the input accordingly - _getGridSettings: function () { - var self = this; - $.get(OC.generateUrl('/apps/files/api/v1/showgridview'), function (response) { - self.$showGridView.get(0).checked = response.gridview; + _getGridSettings: function() { + var self = this + $.get(OC.generateUrl('/apps/files/api/v1/showgridview'), function(response) { + self.$showGridView.get(0).checked = response.gridview self.$showGridView.next('#picker-view-toggle') .removeClass('icon-toggle-filelist icon-toggle-pictures') .addClass(response.gridview ? 'icon-toggle-filelist' : 'icon-toggle-pictures') - $('.list-container').toggleClass('view-grid', response.gridview); - }); + $('.list-container').toggleClass('view-grid', response.gridview) + }) }, - _onGridviewChange: function () { - var show = this.$showGridView.is(':checked'); + _onGridviewChange: function() { + var show = this.$showGridView.is(':checked') // only save state if user is logged in if (OC.currentUser) { $.post(OC.generateUrl('/apps/files/api/v1/showgridview'), { show: show - }); + }) } this.$showGridView.next('#picker-view-toggle') .removeClass('icon-toggle-filelist icon-toggle-pictures') .addClass(show ? 'icon-toggle-filelist' : 'icon-toggle-pictures') - $('.list-container').toggleClass('view-grid', show); + $('.list-container').toggleClass('view-grid', show) }, - _getFilePickerTemplate: function () { - var defer = $.Deferred(); + _getFilePickerTemplate: function() { + var defer = $.Deferred() if (!this.$filePickerTemplate) { - var self = this; - $.get(OC.filePath('core', 'templates', 'filepicker.html'), function (tmpl) { - self.$filePickerTemplate = $(tmpl); - self.$listTmpl = self.$filePickerTemplate.find('.filelist tbody tr:first-child').detach(); - defer.resolve(self.$filePickerTemplate); + var self = this + $.get(OC.filePath('core', 'templates', 'filepicker.html'), function(tmpl) { + self.$filePickerTemplate = $(tmpl) + self.$listTmpl = self.$filePickerTemplate.find('.filelist tbody tr:first-child').detach() + defer.resolve(self.$filePickerTemplate) }) - .fail(function (jqXHR, textStatus, errorThrown) { - defer.reject(jqXHR.status, errorThrown); - }); + .fail(function(jqXHR, textStatus, errorThrown) { + defer.reject(jqXHR.status, errorThrown) + }) } else { - defer.resolve(this.$filePickerTemplate); + defer.resolve(this.$filePickerTemplate) } - return defer.promise(); + return defer.promise() }, - _getMessageTemplate: function () { - var defer = $.Deferred(); + _getMessageTemplate: function() { + var defer = $.Deferred() if (!this.$messageTemplate) { - var self = this; - $.get(OC.filePath('core', 'templates', 'message.html'), function (tmpl) { - self.$messageTemplate = $(tmpl); - defer.resolve(self.$messageTemplate); + var self = this + $.get(OC.filePath('core', 'templates', 'message.html'), function(tmpl) { + self.$messageTemplate = $(tmpl) + defer.resolve(self.$messageTemplate) }) - .fail(function (jqXHR, textStatus, errorThrown) { - defer.reject(jqXHR.status, errorThrown); - }); + .fail(function(jqXHR, textStatus, errorThrown) { + defer.reject(jqXHR.status, errorThrown) + }) } else { - defer.resolve(this.$messageTemplate); + defer.resolve(this.$messageTemplate) } - return defer.promise(); + return defer.promise() }, - _getFileExistsTemplate: function () { - var defer = $.Deferred(); + _getFileExistsTemplate: function() { + var defer = $.Deferred() if (!this.$fileexistsTemplate) { - var self = this; - $.get(OC.filePath('files', 'templates', 'fileexists.html'), function (tmpl) { - self.$fileexistsTemplate = $(tmpl); - defer.resolve(self.$fileexistsTemplate); + var self = this + $.get(OC.filePath('files', 'templates', 'fileexists.html'), function(tmpl) { + self.$fileexistsTemplate = $(tmpl) + defer.resolve(self.$fileexistsTemplate) }) - .fail(function () { - defer.reject(); - }); + .fail(function() { + defer.reject() + }) } else { - defer.resolve(this.$fileexistsTemplate); + defer.resolve(this.$fileexistsTemplate) } - return defer.promise(); + return defer.promise() }, - _getFileList: function (dir, mimeType) { //this is only used by the spreedme app atm - if (typeof (mimeType) === "string") { - mimeType = [mimeType]; + _getFileList: function(dir, mimeType) { // this is only used by the spreedme app atm + if (typeof (mimeType) === 'string') { + mimeType = [mimeType] } return $.getJSON( @@ -996,88 +999,88 @@ const Dialogs = { dir: dir, mimetypes: JSON.stringify(mimeType) } - ); + ) }, /** * fills the filepicker with files */ - _fillFilePicker: function (dir) { - var self = this; - this.$filelist.empty(); - this.$filePicker.find('.emptycontent').hide(); - this.$filelistContainer.addClass('icon-loading'); - this.$filePicker.data('path', dir); - var filter = this.$filePicker.data('mimetype'); - if (typeof (filter) === "string") { - filter = [filter]; + _fillFilePicker: function(dir) { + var self = this + this.$filelist.empty() + this.$filePicker.find('.emptycontent').hide() + this.$filelistContainer.addClass('icon-loading') + this.$filePicker.data('path', dir) + var filter = this.$filePicker.data('mimetype') + if (typeof (filter) === 'string') { + filter = [filter] } - self.$fileListHeader.find('.sort-indicator').addClass('hidden').removeClass('icon-triangle-n').removeClass('icon-triangle-s'); - self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').removeClass('hidden'); + self.$fileListHeader.find('.sort-indicator').addClass('hidden').removeClass('icon-triangle-n').removeClass('icon-triangle-s') + self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').removeClass('hidden') if (self.filepicker.sortOrder === 'asc') { - self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-n'); + self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-n') } else { - self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-s'); + self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-s') } - self.filepicker.filesClient.getFolderContents(dir).then(function (status, files) { - self.filelist = files; + self.filepicker.filesClient.getFolderContents(dir).then(function(status, files) { + self.filelist = files if (filter && filter.length > 0 && filter.indexOf('*') === -1) { - files = files.filter(function (file) { - return file.type === 'dir' || filter.indexOf(file.mimetype) !== -1; - }); + files = files.filter(function(file) { + return file.type === 'dir' || filter.indexOf(file.mimetype) !== -1 + }) } var Comparators = { - name: function (fileInfo1, fileInfo2) { + name: function(fileInfo1, fileInfo2) { if (fileInfo1.type === 'dir' && fileInfo2.type !== 'dir') { - return -1; + return -1 } if (fileInfo1.type !== 'dir' && fileInfo2.type === 'dir') { - return 1; + return 1 } - return OC.Util.naturalSortCompare(fileInfo1.name, fileInfo2.name); + return OC.Util.naturalSortCompare(fileInfo1.name, fileInfo2.name) }, - size: function (fileInfo1, fileInfo2) { - return fileInfo1.size - fileInfo2.size; + size: function(fileInfo1, fileInfo2) { + return fileInfo1.size - fileInfo2.size }, - mtime: function (fileInfo1, fileInfo2) { - return fileInfo1.mtime - fileInfo2.mtime; + mtime: function(fileInfo1, fileInfo2) { + return fileInfo1.mtime - fileInfo2.mtime + } + } + var comparator = Comparators[self.filepicker.sortField] || Comparators.name + files = files.sort(function(file1, file2) { + var isFavorite = function(fileInfo) { + return fileInfo.tags && fileInfo.tags.indexOf(OC.TAG_FAVORITE) >= 0 } - }; - var comparator = Comparators[self.filepicker.sortField] || Comparators.name; - files = files.sort(function (file1, file2) { - var isFavorite = function (fileInfo) { - return fileInfo.tags && fileInfo.tags.indexOf(OC.TAG_FAVORITE) >= 0; - }; if (isFavorite(file1) && !isFavorite(file2)) { - return -1; + return -1 } else if (!isFavorite(file1) && isFavorite(file2)) { - return 1; + return 1 } - return self.filepicker.sortOrder === 'asc' ? comparator(file1, file2) : -comparator(file1, file2); - }); + return self.filepicker.sortOrder === 'asc' ? comparator(file1, file2) : -comparator(file1, file2) + }) - self._fillSlug(); + self._fillSlug() if (files.length === 0) { - self.$filePicker.find('.emptycontent').show(); - self.$fileListHeader.hide(); + self.$filePicker.find('.emptycontent').show() + self.$fileListHeader.hide() } else { - self.$filePicker.find('.emptycontent').hide(); - self.$fileListHeader.show(); + self.$filePicker.find('.emptycontent').hide() + self.$fileListHeader.show() } - $.each(files, function (idx, entry) { - entry.icon = OC.MimeType.getIconUrl(entry.mimetype); - var simpleSize, sizeColor; + $.each(files, function(idx, entry) { + entry.icon = OC.MimeType.getIconUrl(entry.mimetype) + var simpleSize, sizeColor if (typeof (entry.size) !== 'undefined' && entry.size >= 0) { - simpleSize = humanFileSize(parseInt(entry.size, 10), true); - sizeColor = Math.round(160 - Math.pow((entry.size / (1024 * 1024)), 2)); + simpleSize = humanFileSize(parseInt(entry.size, 10), true) + sizeColor = Math.round(160 - Math.pow((entry.size / (1024 * 1024)), 2)) } else { - simpleSize = t('files', 'Pending'); - sizeColor = 80; + simpleSize = t('files', 'Pending') + sizeColor = 80 } // split the filename in half if the size is bigger than 20 char @@ -1102,90 +1105,90 @@ const Dialogs = { size: simpleSize, sizeColor: sizeColor, icon: entry.icon - }); + }) if (entry.type === 'file') { var urlSpec = { file: dir + '/' + entry.name, x: 100, y: 100 - }; - var img = new Image(); - var previewUrl = OC.generateUrl('/core/preview.png?') + $.param(urlSpec); - img.onload = function () { + } + var img = new Image() + var previewUrl = OC.generateUrl('/core/preview.png?') + $.param(urlSpec) + img.onload = function() { if (img.width > 5) { - $row.find('td.filename').attr('style', 'background-image:url(' + previewUrl + ')'); + $row.find('td.filename').attr('style', 'background-image:url(' + previewUrl + ')') } - }; - img.src = previewUrl; + } + img.src = previewUrl } - self.$filelist.append($row); - }); + self.$filelist.append($row) + }) - self.$filelistContainer.removeClass('icon-loading'); - }); + self.$filelistContainer.removeClass('icon-loading') + }) }, /** * fills the tree list with directories */ - _fillSlug: function () { - this.$dirTree.empty(); - var self = this; - var dir; - var path = this.$filePicker.data('path'); - var $template = $('<div data-dir="{dir}"><a>{name}</a></div>').addClass('crumb'); + _fillSlug: function() { + this.$dirTree.empty() + var self = this + var dir + var path = this.$filePicker.data('path') + var $template = $('<div data-dir="{dir}"><a>{name}</a></div>').addClass('crumb') if (path) { - var paths = path.split('/'); - $.each(paths, function (index, dir) { - dir = paths.pop(); + var paths = path.split('/') + $.each(paths, function(index, dir) { + dir = paths.pop() if (dir === '') { - return false; + return false } self.$dirTree.prepend($template.octemplate({ dir: paths.join('/') + '/' + dir, name: dir - })); - }); + })) + }) } $template.octemplate({ dir: '', name: '' // Ugly but works ;) - }, {escapeFunction: null}).prependTo(this.$dirTree); + }, { escapeFunction: null }).prependTo(this.$dirTree) }, /** * handle selection made in the tree list */ - _handleTreeListSelect: function (event, type) { - var self = event.data; - var dir = $(event.target).closest('.crumb').data('dir'); - self._fillFilePicker(dir); - var getOcDialog = (event.target).closest('.oc-dialog'); - var buttonEnableDisable = $('.primary', getOcDialog); - this._changeButtonsText(type, dir.split(/[/]+/).pop()); - if (this.$filePicker.data('mimetype').indexOf("httpd/unix-directory") !== -1) { - buttonEnableDisable.prop("disabled", false); + _handleTreeListSelect: function(event, type) { + var self = event.data + var dir = $(event.target).closest('.crumb').data('dir') + self._fillFilePicker(dir) + var getOcDialog = (event.target).closest('.oc-dialog') + var buttonEnableDisable = $('.primary', getOcDialog) + this._changeButtonsText(type, dir.split(/[/]+/).pop()) + if (this.$filePicker.data('mimetype').indexOf('httpd/unix-directory') !== -1) { + buttonEnableDisable.prop('disabled', false) } else { - buttonEnableDisable.prop("disabled", true); + buttonEnableDisable.prop('disabled', true) } }, /** * handle clicks made in the filepicker */ - _handlePickerClick: function (event, $element, type) { - var getOcDialog = this.$filePicker.closest('.oc-dialog'); - var buttonEnableDisable = getOcDialog.find('.primary'); + _handlePickerClick: function(event, $element, type) { + var getOcDialog = this.$filePicker.closest('.oc-dialog') + var buttonEnableDisable = getOcDialog.find('.primary') if ($element.data('type') === 'file') { if (this.$filePicker.data('multiselect') !== true || !event.ctrlKey) { - this.$filelist.find('.filepicker_element_selected').removeClass('filepicker_element_selected'); + this.$filelist.find('.filepicker_element_selected').removeClass('filepicker_element_selected') } - $element.toggleClass('filepicker_element_selected'); - buttonEnableDisable.prop("disabled", false); + $element.toggleClass('filepicker_element_selected') + buttonEnableDisable.prop('disabled', false) } else if ($element.data('type') === 'dir') { - this._fillFilePicker(this.$filePicker.data('path') + '/' + $element.data('entryname')); - this._changeButtonsText(type, $element.data('entryname')); - if (this.$filePicker.data('mimetype').indexOf("httpd/unix-directory") !== -1 || this.$filePicker.data('allowDirectoryChooser')) { - buttonEnableDisable.prop("disabled", false); + this._fillFilePicker(this.$filePicker.data('path') + '/' + $element.data('entryname')) + this._changeButtonsText(type, $element.data('entryname')) + if (this.$filePicker.data('mimetype').indexOf('httpd/unix-directory') !== -1 || this.$filePicker.data('allowDirectoryChooser')) { + buttonEnableDisable.prop('disabled', false) } else { - buttonEnableDisable.prop("disabled", true); + buttonEnableDisable.prop('disabled', true) } } }, @@ -1196,23 +1199,23 @@ const Dialogs = { * @param dir on which to change buttons text * @private */ - _changeButtonsText: function (type, dir) { - var copyText = dir === '' ? t('core', 'Copy') : t('core', 'Copy to {folder}', {folder: dir}); - var moveText = dir === '' ? t('core', 'Move') : t('core', 'Move to {folder}', {folder: dir}); - var buttons = $('.oc-dialog-buttonrow button'); + _changeButtonsText: function(type, dir) { + var copyText = dir === '' ? t('core', 'Copy') : t('core', 'Copy to {folder}', { folder: dir }) + var moveText = dir === '' ? t('core', 'Move') : t('core', 'Move to {folder}', { folder: dir }) + var buttons = $('.oc-dialog-buttonrow button') switch (type) { - case this.FILEPICKER_TYPE_CHOOSE: - break; - case this.FILEPICKER_TYPE_COPY: - buttons.text(copyText); - break; - case this.FILEPICKER_TYPE_MOVE: - buttons.text(moveText); - break; - case this.FILEPICKER_TYPE_COPY_MOVE: - buttons.eq(0).text(copyText); - buttons.eq(1).text(moveText); - break; + case this.FILEPICKER_TYPE_CHOOSE: + break + case this.FILEPICKER_TYPE_COPY: + buttons.text(copyText) + break + case this.FILEPICKER_TYPE_MOVE: + buttons.text(moveText) + break + case this.FILEPICKER_TYPE_COPY_MOVE: + buttons.eq(0).text(copyText) + buttons.eq(1).text(moveText) + break } } } diff --git a/core/src/OC/eventsource.js b/core/src/OC/eventsource.js index ec70e6650148cbdb42dd33f76512ec161c9d3c37..cc576d8655aef1285abc98f178eedf33a00e5771 100644 --- a/core/src/OC/eventsource.js +++ b/core/src/OC/eventsource.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /** * ownCloud * @@ -32,7 +33,7 @@ import $ from 'jquery' -import {getToken} from './requesttoken' +import { getToken } from './requesttoken' /** * Create a new event source @@ -41,62 +42,62 @@ import {getToken} from './requesttoken' * * @constructs OCEventSource */ -const OCEventSource = function (src, data) { - var dataStr = ''; - var name; - var joinChar; - this.typelessListeners = []; - this.closed = false; - this.listeners = {}; +const OCEventSource = function(src, data) { + var dataStr = '' + var name + var joinChar + this.typelessListeners = [] + this.closed = false + this.listeners = {} if (data) { for (name in data) { - dataStr += name + '=' + encodeURIComponent(data[name]) + '&'; + dataStr += name + '=' + encodeURIComponent(data[name]) + '&' } } - dataStr += 'requesttoken=' + encodeURIComponent(getToken()); + dataStr += 'requesttoken=' + encodeURIComponent(getToken()) if (!this.useFallBack && typeof EventSource !== 'undefined') { - joinChar = '&'; + joinChar = '&' if (src.indexOf('?') === -1) { - joinChar = '?'; + joinChar = '?' } - this.source = new EventSource(src + joinChar + dataStr); - this.source.onmessage = function (e) { + this.source = new EventSource(src + joinChar + dataStr) + this.source.onmessage = function(e) { for (var i = 0; i < this.typelessListeners.length; i++) { - this.typelessListeners[i](JSON.parse(e.data)); + this.typelessListeners[i](JSON.parse(e.data)) } - }.bind(this); + }.bind(this) } else { - var iframeId = 'oc_eventsource_iframe_' + OCEventSource.iframeCount; - OCEventSource.fallBackSources[OCEventSource.iframeCount] = this; - this.iframe = $('<iframe/>'); - this.iframe.attr('id', iframeId); - this.iframe.hide(); + var iframeId = 'oc_eventsource_iframe_' + OCEventSource.iframeCount + OCEventSource.fallBackSources[OCEventSource.iframeCount] = this + this.iframe = $('<iframe/>') + this.iframe.attr('id', iframeId) + this.iframe.hide() - joinChar = '&'; + joinChar = '&' if (src.indexOf('?') === -1) { - joinChar = '?'; + joinChar = '?' } - this.iframe.attr('src', src + joinChar + 'fallback=true&fallback_id=' + OCEventSource.iframeCount + '&' + dataStr); - $('body').append(this.iframe); - this.useFallBack = true; - OCEventSource.iframeCount++; + this.iframe.attr('src', src + joinChar + 'fallback=true&fallback_id=' + OCEventSource.iframeCount + '&' + dataStr) + $('body').append(this.iframe) + this.useFallBack = true + OCEventSource.iframeCount++ } - //add close listener - this.listen('__internal__', function (data) { + // add close listener + this.listen('__internal__', function(data) { if (data === 'close') { - this.close(); + this.close() } - }.bind(this)); -}; -OCEventSource.fallBackSources = []; -OCEventSource.iframeCount = 0;//number of fallback iframes -OCEventSource.fallBackCallBack = function (id, type, data) { - OCEventSource.fallBackSources[id].fallBackCallBack(type, data); -}; + }.bind(this)) +} +OCEventSource.fallBackSources = [] +OCEventSource.iframeCount = 0// number of fallback iframes +OCEventSource.fallBackCallBack = function(id, type, data) { + OCEventSource.fallBackSources[id].fallBackCallBack(type, data) +} OCEventSource.prototype = { typelessListeners: [], iframe: null, - listeners: {},//only for fallback + listeners: {}, // only for fallback useFallBack: false, /** * Fallback callback for browsers that don't have the @@ -108,63 +109,63 @@ OCEventSource.prototype = { * @param {String} type event type * @param {Object} data received data */ - fallBackCallBack: function (type, data) { - var i; + fallBackCallBack: function(type, data) { + var i // ignore messages that might appear after closing if (this.closed) { - return; + return } if (type) { if (typeof this.listeners.done !== 'undefined') { for (i = 0; i < this.listeners[type].length; i++) { - this.listeners[type][i](data); + this.listeners[type][i](data) } } } else { for (i = 0; i < this.typelessListeners.length; i++) { - this.typelessListeners[i](data); + this.typelessListeners[i](data) } } }, - lastLength: 0,//for fallback + lastLength: 0, // for fallback /** * Listen to a given type of events. * * @param {String} type event type * @param {Function} callback event callback */ - listen: function (type, callback) { + listen: function(type, callback) { if (callback && callback.call) { if (type) { if (this.useFallBack) { if (!this.listeners[type]) { - this.listeners[type] = []; + this.listeners[type] = [] } - this.listeners[type].push(callback); + this.listeners[type].push(callback) } else { - this.source.addEventListener(type, function (e) { + this.source.addEventListener(type, function(e) { if (typeof e.data !== 'undefined') { - callback(JSON.parse(e.data)); + callback(JSON.parse(e.data)) } else { - callback(''); + callback('') } - }, false); + }, false) } } else { - this.typelessListeners.push(callback); + this.typelessListeners.push(callback) } } }, /** * Closes this event source. */ - close: function () { - this.closed = true; + close: function() { + this.closed = true if (typeof this.source !== 'undefined') { - this.source.close(); + this.source.close() } } -}; +} -export default OCEventSource; +export default OCEventSource diff --git a/core/src/OC/get_set.js b/core/src/OC/get_set.js index 9fafe12138719c21097f381f3cdc754dd75d4fe3..bf45ce85fc7b8f2cb678836e02ec9b70ce7816ed 100644 --- a/core/src/OC/get_set.js +++ b/core/src/OC/get_set.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -21,8 +21,8 @@ /** * Get a variable by name - * @param {string} name - * @return {*} + * @param {string} context context + * @returns {Function} getter */ export const get = context => name => { const namespaces = name.split('.') @@ -39,11 +39,11 @@ export const get = context => name => { /** * Set a variable by name - * @param {string} name - * @param {*} value + * @param {string} context context + * @returns {Function} setter */ export const set = context => (name, value) => { - const namespaces = name.split(".") + const namespaces = name.split('.') const tail = namespaces.pop() for (let i = 0; i < namespaces.length; i++) { @@ -53,4 +53,5 @@ export const set = context => (name, value) => { context = context[namespaces[i]] } context[tail] = value + return value } diff --git a/core/src/OC/host.js b/core/src/OC/host.js index ac02c63a72adb19bc7f2f737e931e99fe874bf4d..f90ca65b4da70b6a08bd2979679eda4df175ef9e 100644 --- a/core/src/OC/host.js +++ b/core/src/OC/host.js @@ -21,7 +21,7 @@ /** * Protocol that is used to access this Nextcloud instance - * @return {string} Used protocol + * @returns {string} Used protocol * @deprecated 17.0.0 use window.location.protocol directly */ export const getProtocol = () => window.location.protocol.split(':')[0] @@ -35,7 +35,7 @@ export const getProtocol = () => window.location.protocol.split(':')[0] * https://example.com => example.com * http://example.com:8080 => example.com:8080 * - * @return {string} host + * @returns {string} host * * @since 8.2 * @deprecated 17.0.0 use window.location.host directly @@ -46,7 +46,7 @@ export const getHost = () => window.location.host * Returns the hostname used to access this Nextcloud instance * The hostname is always stripped of the port * - * @return {string} hostname + * @returns {string} hostname * @since 9.0 * @deprecated 17.0.0 use window.location.hostname directly */ @@ -55,7 +55,7 @@ export const getHostName = () => window.location.hostname /** * Returns the port number used to access this Nextcloud instance * - * @return {int} port number + * @returns {int} port number * * @since 8.2 * @deprecated 17.0.0 use window.location.port directly diff --git a/core/src/OC/index.js b/core/src/OC/index.js index 461e0160526752f3a6fac63a6c570c4c457cd363..0c95fe3e95fc1273047c6f3b49c2c93335ccf31f 100644 --- a/core/src/OC/index.js +++ b/core/src/OC/index.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -19,15 +19,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import {addScript, addStyle} from './legacy-loader' +import { addScript, addStyle } from './legacy-loader' import { ajaxConnectionLostHandler, processAjaxError, - registerXHRForErrorProcessing, + registerXHRForErrorProcessing } from './xhr-error' import Apps from './apps' -import {AppConfig, appConfig} from './appconfig' -import {appSettings} from './appsettings' +import { AppConfig, appConfig } from './appconfig' +import { appSettings } from './appsettings' import appswebroots from './appswebroots' import Backbone from './backbone' import { @@ -35,11 +35,11 @@ import { dirname, encodePath, isSamePath, - joinPaths, + joinPaths } from './path' import { build as buildQueryString, - parse as parseQueryString, + parse as parseQueryString } from './query-string' import Config from './config' import { @@ -52,37 +52,37 @@ import { PERMISSION_READ, PERMISSION_SHARE, PERMISSION_UPDATE, - TAG_FAVORITE, + TAG_FAVORITE } from './constants' import ContactsMenu from './contactsmenu' -import {currentUser, getCurrentUser} from './currentuser' +import { currentUser, getCurrentUser } from './currentuser' import Dialogs from './dialogs' import EventSource from './eventsource' -import {get, set} from './get_set' -import {getCapabilities} from './capabilities' +import { get, set } from './get_set' +import { getCapabilities } from './capabilities' import { getHost, getHostName, getPort, - getProtocol, + getProtocol } from './host' import { getToken as getRequestToken, - subscribe as subscribeToRequestTokenChange, + subscribe as subscribeToRequestTokenChange } from './requesttoken' import { hideMenus, registerMenu, showMenu, - unregisterMenu, + unregisterMenu } from './menu' -import {isUserAdmin} from './admin' -import L10N from './l10n' -import { +import { isUserAdmin } from './admin' +import L10N, { getCanonicalLocale, getLanguage, - getLocale, + getLocale } from './l10n' + import { filePath, generateUrl, @@ -91,17 +91,17 @@ import { linkTo, linkToOCS, linkToRemote, - linkToRemoteBase, + linkToRemoteBase } from './routing' import msg from './msg' import Notification from './notification' import PasswordConfirmation from './password-confirmation' import Plugins from './plugins' import search from './search' -import {theme} from './theme' +import { theme } from './theme' import Util from './util' -import {debug} from './debug' -import {redirect, reload} from './navigation' +import { debug } from './debug' +import { redirect, reload } from './navigation' import webroot from './webroot' /** @namespace OC */ @@ -126,6 +126,7 @@ export default { /** * Check if a user file is allowed to be handled. * @param {string} file to check + * @returns {Boolean} * @deprecated 17.0.0 */ fileIsBlacklisted: file => !!(file.match(Config.blacklist_files_regex)), @@ -253,8 +254,8 @@ export default { * @deprecated since 8.2, use OC.getRootPath() instead * @see OC#getRootPath */ - webroot, + webroot } // Keep the request token prop in sync -subscribeToRequestTokenChange(token => OC.requestToken = token) +subscribeToRequestTokenChange(token => { OC.requestToken = token }) diff --git a/core/src/OC/l10n-registry.js b/core/src/OC/l10n-registry.js index 6f537c86c0069531c4c85321f5fa4f3049d8a178..44dc6f91be5321c48f6402db25a5be3305b5551e 100644 --- a/core/src/OC/l10n-registry.js +++ b/core/src/OC/l10n-registry.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -24,9 +24,9 @@ window._oc_l10n_registry_translations = window._oc_l10n_registry_translations || window._oc_l10n_registry_plural_functions = window._oc_l10n_registry_plural_functions || {} /** - * @param {String} appId - * @param {Object} translations - * @param {Function} pluralFunction + * @param {String} appId the app id + * @param {Object} translations the translations list + * @param {Function} pluralFunction the translations list */ const register = (appId, translations, pluralFunction) => { window._oc_l10n_registry_translations[appId] = translations @@ -34,9 +34,9 @@ const register = (appId, translations, pluralFunction) => { } /** - * @param {String} appId - * @param {Object} translations - * @param {Function} pluralFunction + * @param {String} appId the app id + * @param {Object} translations the translations list + * @param {Function} pluralFunction the translations list */ const extend = (appId, translations, pluralFunction) => { window._oc_l10n_registry_translations[appId] = Object.assign( @@ -47,9 +47,9 @@ const extend = (appId, translations, pluralFunction) => { } /** - * @param {String} appId - * @param {Object} translations - * @param {Function} pluralFunction + * @param {String} appId the app id + * @param {Object} translations the translations list + * @param {Function} pluralFunction the translations list */ export const registerAppTranslations = (appId, translations, pluralFunction) => { if (!hasAppTranslations(appId)) { @@ -60,7 +60,7 @@ export const registerAppTranslations = (appId, translations, pluralFunction) => } /** - * @param {String} appId + * @param {String} appId the app id */ export const unregisterAppTranslations = appId => { delete window._oc_l10n_registry_translations[appId] @@ -68,8 +68,8 @@ export const unregisterAppTranslations = appId => { } /** - * @param {String} appId - * @return {Boolean} + * @param {String} appId the app id + * @returns {Boolean} */ export const hasAppTranslations = appId => { return window._oc_l10n_registry_translations[appId] !== undefined @@ -77,12 +77,12 @@ export const hasAppTranslations = appId => { } /** - * @param {String} appId - * @return {Object} + * @param {String} appId the app id + * @returns {Object} */ export const getAppTranslations = appId => { return { translations: window._oc_l10n_registry_translations[appId] || {}, - pluralFunction: window._oc_l10n_registry_plural_functions[appId], + pluralFunction: window._oc_l10n_registry_plural_functions[appId] } } diff --git a/core/src/OC/l10n.js b/core/src/OC/l10n.js index 769d55326baf43c90ff91a8a6526e9f0c1a13a64..fd81d3282d790deba6b0747abe3b66dd0c6ec8a0 100644 --- a/core/src/OC/l10n.js +++ b/core/src/OC/l10n.js @@ -34,39 +34,39 @@ const L10n = { * @param {String} appName name of the app * @param {Function} callback callback to be called when * the translations are loaded - * @return {Promise} promise + * @returns {Promise} promise */ load: function(appName, callback) { // already available ? if (hasAppTranslations(appName) || OC.getLocale() === 'en') { - var deferred = $.Deferred(); - var promise = deferred.promise(); - promise.then(callback); - deferred.resolve(); - return promise; + var deferred = $.Deferred() + var promise = deferred.promise() + promise.then(callback) + deferred.resolve() + return promise } - var self = this; - var url = OC.filePath(appName, 'l10n', OC.getLocale() + '.json'); + var self = this + var url = OC.filePath(appName, 'l10n', OC.getLocale() + '.json') // load JSON translation bundle per AJAX return $.get(url) .then( function(result) { if (result.translations) { - self.register(appName, result.translations, result.pluralForm); + self.register(appName, result.translations, result.pluralForm) } }) - .then(callback); + .then(callback) }, /** * Register an app's translation bundle. * * @param {String} appName name of the app - * @param {Object<String,String>} bundle + * @param {Object<String,String>} bundle bundle */ - register: function(appName, bundle, pluralForm) { + register: function(appName, bundle) { registerAppTranslations(appName, bundle, this._getPlural) }, @@ -79,49 +79,49 @@ const L10n = { * Translate a string * @param {string} app the id of the app for which to translate the string * @param {string} text the string to translate - * @param [vars] map of placeholder key to value + * @param {Object} [vars] map of placeholder key to value * @param {number} [count] number to replace %n with * @param {array} [options] options array * @param {bool} [options.escape=true] enable/disable auto escape of placeholders (by default enabled) - * @return {string} + * @returns {string} */ translate: function(app, text, vars, count, options) { var defaultOptions = { - escape: true - }, - allOptions = options || {}; - _.defaults(allOptions, defaultOptions); + escape: true + } + var allOptions = options || {} + _.defaults(allOptions, defaultOptions) // TODO: cache this function to avoid inline recreation // of the same function over and over again in case // translate() is used in a loop - var _build = function (text, vars, count) { + var _build = function(text, vars, count) { return text.replace(/%n/g, count).replace(/{([^{}]*)}/g, - function (a, b) { - var r = vars[b]; - if(typeof r === 'string' || typeof r === 'number') { - if(allOptions.escape) { - return DOMPurify.sanitize(escapeHTML(r)); + function(a, b) { + var r = vars[b] + if (typeof r === 'string' || typeof r === 'number') { + if (allOptions.escape) { + return DOMPurify.sanitize(escapeHTML(r)) } else { - return DOMPurify.sanitize(r); + return DOMPurify.sanitize(r) } } else { - return DOMPurify.sanitize(a); + return DOMPurify.sanitize(a) } } - ); - }; - var translation = text; - var bundle = getAppTranslations(app); - var value = bundle.translations[text]; - if( typeof(value) !== 'undefined' ){ - translation = value; + ) + } + var translation = text + var bundle = getAppTranslations(app) + var value = bundle.translations[text] + if (typeof (value) !== 'undefined') { + translation = value } - if(typeof vars === 'object' || count !== undefined ) { - return DOMPurify.sanitize(_build(translation, vars, count)); + if (typeof vars === 'object' || count !== undefined) { + return DOMPurify.sanitize(_build(translation, vars, count)) } else { - return DOMPurify.sanitize(translation); + return DOMPurify.sanitize(translation) } }, @@ -131,50 +131,50 @@ const L10n = { * @param {string} textSingular the string to translate for exactly one object * @param {string} textPlural the string to translate for n objects * @param {number} count number to determine whether to use singular or plural - * @param [vars] map of placeholder key to value + * @param {Object} [vars] map of placeholder key to value * @param {array} [options] options array * @param {bool} [options.escape=true] enable/disable auto escape of placeholders (by default enabled) - * @return {string} Translated string + * @returns {string} Translated string */ translatePlural: function(app, textSingular, textPlural, count, vars, options) { - const identifier = '_' + textSingular + '_::_' + textPlural + '_'; - const bundle = getAppTranslations(app); - const value = bundle.translations[identifier]; - if( typeof(value) !== 'undefined' ){ - var translation = value; + const identifier = '_' + textSingular + '_::_' + textPlural + '_' + const bundle = getAppTranslations(app) + const value = bundle.translations[identifier] + if (typeof (value) !== 'undefined') { + var translation = value if ($.isArray(translation)) { - var plural = bundle.pluralFunction(count); - return this.translate(app, translation[plural], vars, count, options); + var plural = bundle.pluralFunction(count) + return this.translate(app, translation[plural], vars, count, options) } } if (count === 1) { - return this.translate(app, textSingular, vars, count, options); + return this.translate(app, textSingular, vars, count, options) } else { - return this.translate(app, textPlural, vars, count, options); + return this.translate(app, textPlural, vars, count, options) } }, /** * The plural function taken from symfony * - * @param {number} number + * @param {number} number the number of elements * @returns {number} * @private */ _getPlural: function(number) { - var language = OC.getLanguage(); - if ('pt_BR' === language) { + var language = OC.getLanguage() + if (language === 'pt_BR') { // temporary set a locale for brazilian - language = 'xbr'; + language = 'xbr' } if (typeof language === 'undefined' || language === '') { - return (1 == number) ? 0 : 1; + return (number === 1) ? 0 : 1 } if (language.length > 3) { - language = language.substring(0, language.lastIndexOf('_')); + language = language.substring(0, language.lastIndexOf('_')) } /* @@ -183,146 +183,146 @@ const L10n = { * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) */ switch (language) { - case 'az': - case 'bo': - case 'dz': - case 'id': - case 'ja': - case 'jv': - case 'ka': - case 'km': - case 'kn': - case 'ko': - case 'ms': - case 'th': - case 'tr': - case 'vi': - case 'zh': - return 0; - - case 'af': - case 'bn': - case 'bg': - case 'ca': - case 'da': - case 'de': - case 'el': - case 'en': - case 'eo': - case 'es': - case 'et': - case 'eu': - case 'fa': - case 'fi': - case 'fo': - case 'fur': - case 'fy': - case 'gl': - case 'gu': - case 'ha': - case 'he': - case 'hu': - case 'is': - case 'it': - case 'ku': - case 'lb': - case 'ml': - case 'mn': - case 'mr': - case 'nah': - case 'nb': - case 'ne': - case 'nl': - case 'nn': - case 'no': - case 'oc': - case 'om': - case 'or': - case 'pa': - case 'pap': - case 'ps': - case 'pt': - case 'so': - case 'sq': - case 'sv': - case 'sw': - case 'ta': - case 'te': - case 'tk': - case 'ur': - case 'zu': - return (1 == number) ? 0 : 1; - - case 'am': - case 'bh': - case 'fil': - case 'fr': - case 'gun': - case 'hi': - case 'hy': - case 'ln': - case 'mg': - case 'nso': - case 'xbr': - case 'ti': - case 'wa': - return ((0 == number) || (1 == number)) ? 0 : 1; - - case 'be': - case 'bs': - case 'hr': - case 'ru': - case 'sh': - case 'sr': - case 'uk': - return ((1 == number % 10) && (11 != number % 100)) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2); - - case 'cs': - case 'sk': - return (1 == number) ? 0 : (((number >= 2) && (number <= 4)) ? 1 : 2); - - case 'ga': - return (1 == number) ? 0 : ((2 == number) ? 1 : 2); - - case 'lt': - return ((1 == number % 10) && (11 != number % 100)) ? 0 : (((number % 10 >= 2) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2); - - case 'sl': - return (1 == number % 100) ? 0 : ((2 == number % 100) ? 1 : (((3 == number % 100) || (4 == number % 100)) ? 2 : 3)); - - case 'mk': - return (1 == number % 10) ? 0 : 1; - - case 'mt': - return (1 == number) ? 0 : (((0 == number) || ((number % 100 > 1) && (number % 100 < 11))) ? 1 : (((number % 100 > 10) && (number % 100 < 20)) ? 2 : 3)); - - case 'lv': - return (0 == number) ? 0 : (((1 == number % 10) && (11 != number % 100)) ? 1 : 2); - - case 'pl': - return (1 == number) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 12) || (number % 100 > 14))) ? 1 : 2); - - case 'cy': - return (1 == number) ? 0 : ((2 == number) ? 1 : (((8 == number) || (11 == number)) ? 2 : 3)); - - case 'ro': - return (1 == number) ? 0 : (((0 == number) || ((number % 100 > 0) && (number % 100 < 20))) ? 1 : 2); - - case 'ar': - return (0 == number) ? 0 : ((1 == number) ? 1 : ((2 == number) ? 2 : (((number % 100 >= 3) && (number % 100 <= 10)) ? 3 : (((number % 100 >= 11) && (number % 100 <= 99)) ? 4 : 5)))); - - default: - return 0; + case 'az': + case 'bo': + case 'dz': + case 'id': + case 'ja': + case 'jv': + case 'ka': + case 'km': + case 'kn': + case 'ko': + case 'ms': + case 'th': + case 'tr': + case 'vi': + case 'zh': + return 0 + + case 'af': + case 'bn': + case 'bg': + case 'ca': + case 'da': + case 'de': + case 'el': + case 'en': + case 'eo': + case 'es': + case 'et': + case 'eu': + case 'fa': + case 'fi': + case 'fo': + case 'fur': + case 'fy': + case 'gl': + case 'gu': + case 'ha': + case 'he': + case 'hu': + case 'is': + case 'it': + case 'ku': + case 'lb': + case 'ml': + case 'mn': + case 'mr': + case 'nah': + case 'nb': + case 'ne': + case 'nl': + case 'nn': + case 'no': + case 'oc': + case 'om': + case 'or': + case 'pa': + case 'pap': + case 'ps': + case 'pt': + case 'so': + case 'sq': + case 'sv': + case 'sw': + case 'ta': + case 'te': + case 'tk': + case 'ur': + case 'zu': + return (number === 1) ? 0 : 1 + + case 'am': + case 'bh': + case 'fil': + case 'fr': + case 'gun': + case 'hi': + case 'hy': + case 'ln': + case 'mg': + case 'nso': + case 'xbr': + case 'ti': + case 'wa': + return ((number === 0) || (number === 1)) ? 0 : 1 + + case 'be': + case 'bs': + case 'hr': + case 'ru': + case 'sh': + case 'sr': + case 'uk': + return ((number % 10 === 1) && (number % 100 !== 11)) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2) + + case 'cs': + case 'sk': + return (number === 1) ? 0 : (((number >= 2) && (number <= 4)) ? 1 : 2) + + case 'ga': + return (number === 1) ? 0 : ((number === 2) ? 1 : 2) + + case 'lt': + return ((number % 10 === 1) && (number % 100 !== 11)) ? 0 : (((number % 10 >= 2) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2) + + case 'sl': + return (number % 100 === 1) ? 0 : ((number % 100 === 2) ? 1 : (((number % 100 === 3) || (number % 100 === 4)) ? 2 : 3)) + + case 'mk': + return (number % 10 === 1) ? 0 : 1 + + case 'mt': + return (number === 1) ? 0 : (((number === 0) || ((number % 100 > 1) && (number % 100 < 11))) ? 1 : (((number % 100 > 10) && (number % 100 < 20)) ? 2 : 3)) + + case 'lv': + return (number === 0) ? 0 : (((number % 10 === 1) && (number % 100 !== 11)) ? 1 : 2) + + case 'pl': + return (number === 1) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 12) || (number % 100 > 14))) ? 1 : 2) + + case 'cy': + return (number === 1) ? 0 : ((number === 2) ? 1 : (((number === 8) || (number === 11)) ? 2 : 3)) + + case 'ro': + return (number === 1) ? 0 : (((number === 0) || ((number % 100 > 0) && (number % 100 < 20))) ? 1 : 2) + + case 'ar': + return (number === 0) ? 0 : ((number === 1) ? 1 : ((number === 2) ? 2 : (((number % 100 >= 3) && (number % 100 <= 10)) ? 3 : (((number % 100 >= 11) && (number % 100 <= 99)) ? 4 : 5)))) + + default: + return 0 } } -}; +} -export default L10n; +export default L10n /** * Returns the user's locale as a BCP 47 compliant language tag * - * @return {String} locale string + * @returns {String} locale string */ export const getCanonicalLocale = () => { const locale = getLocale() @@ -332,7 +332,7 @@ export const getCanonicalLocale = () => { /** * Returns the user's locale * - * @return {String} locale string + * @returns {String} locale string */ export const getLocale = () => $('html').data('locale') @@ -344,6 +344,5 @@ export const getLocale = () => $('html').data('locale') export const getLanguage = () => $('html').prop('lang') Handlebars.registerHelper('t', function(app, text) { - return L10n.translate(app, text); -}); - + return L10n.translate(app, text) +}) diff --git a/core/src/OC/legacy-loader.js b/core/src/OC/legacy-loader.js index a2c76c2e3ab9f66071817a8027f1721903f47309..ece9f3c3fc13b9f565f1c29c7cc295a52a5863b1 100644 --- a/core/src/OC/legacy-loader.js +++ b/core/src/OC/legacy-loader.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -29,7 +29,8 @@ const loadedStyles = [] * the event handler will be called directly * @param {string} app the app id to which the script belongs * @param {string} script the filename of the script - * @param ready event handler to be called when the script is loaded + * @param {Function} ready event handler to be called when the script is loaded + * @returns {jQuery.Deferred} * @deprecated 16.0.0 Use OCP.Loader.loadScript */ export const addScript = (app, script, ready) => { diff --git a/core/src/OC/menu.js b/core/src/OC/menu.js index 04d0e078b9a9aba84a39fd1fa4d9c381a26e76e5..82cde9e862d619fd8fec4ec184caf844d24e65c5 100644 --- a/core/src/OC/menu.js +++ b/core/src/OC/menu.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -22,33 +22,32 @@ import _ from 'underscore' import $ from 'jquery' -import {menuSpeed} from './constants' +import { menuSpeed } from './constants' -let currentMenu = null -let currentMenuToggle = null +export let currentMenu = null +export let currentMenuToggle = null /** * For menu toggling * - * @param {jQuery} $toggle - * @param {jQuery} $menuEl + * @param {jQuery} $toggle the toggle element + * @param {jQuery} $menuEl the menu container element * @param {function|undefined} toggle callback invoked everytime the menu is opened * @param {boolean} headerMenu is this a top right header menu? * @returns {undefined} */ -export const registerMenu = ($toggle, $menuEl, toggle, headerMenu) => { +export const registerMenu = function($toggle, $menuEl, toggle, headerMenu) { $menuEl.addClass('menu') + const isClickableElement = $toggle.prop('tagName') === 'A' || $toggle.prop('tagName') === 'BUTTON' // On link and button, the enter key trigger a click event // Only use the click to avoid two fired events - $toggle.on(($toggle.prop('tagName') === 'A' || $toggle.prop('tagName') === 'BUTTON') - ? 'click.menu' - : 'click.menu keyup.menu', function (event) { + $toggle.on(isClickableElement ? 'click.menu' : 'click.menu keyup.menu', function(event) { // prevent the link event (append anchor to URL) event.preventDefault() // allow enter key as a trigger - if (event.key && event.key !== "Enter") { + if (event.key && event.key !== 'Enter') { return } @@ -75,7 +74,10 @@ export const registerMenu = ($toggle, $menuEl, toggle, headerMenu) => { } /** - * @todo Write documentation + * Unregister a previously registered menu + * + * @param {jQuery} $toggle the toggle element + * @param {jQuery} $menuEl the menu container element */ export const unregisterMenu = ($toggle, $menuEl) => { // close menu if opened @@ -95,7 +97,7 @@ export const hideMenus = function(complete) { if (currentMenu) { const lastMenu = currentMenu currentMenu.trigger(new $.Event('beforeHide')) - currentMenu.slideUp(menuSpeed, function () { + currentMenu.slideUp(menuSpeed, function() { lastMenu.trigger(new $.Event('afterHide')) if (complete) { complete.apply(this, arguments) diff --git a/core/src/OC/msg.js b/core/src/OC/msg.js index 1d43b4375e70e50fdf9bf5d2aab3987f333805e4..e210f32bb14b6993eb13398e2c5b633375442177 100644 --- a/core/src/OC/msg.js +++ b/core/src/OC/msg.js @@ -21,7 +21,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import $ from 'jquery'; +import $ from 'jquery' /** * A little class to manage a status field for a "saving" process. @@ -36,8 +36,8 @@ export default { * * @param {Object} selector Placeholder to display the message in */ - startSaving: function (selector) { - this.startAction(selector, t('core', 'Saving...')); + startSaving: function(selector) { + this.startAction(selector, t('core', 'Saving...')) }, /** @@ -46,12 +46,12 @@ export default { * @param {Object} selector Placeholder to display the message in * @param {string} message Plain text message to display (no HTML allowed) */ - startAction: function (selector, message) { + startAction: function(selector, message) { $(selector).text(message) .removeClass('success') .removeClass('error') .stop(true, true) - .show(); + .show() }, /** @@ -64,8 +64,8 @@ export default { * @param {string} response.status is being used to decide whether the message * is displayed as an error/success */ - finishedSaving: function (selector, response) { - this.finishedAction(selector, response); + finishedSaving: function(selector, response) { + this.finishedAction(selector, response) }, /** @@ -78,11 +78,11 @@ export default { * @param {string} response.status is being used to decide whether the message * is displayed as an error/success */ - finishedAction: function (selector, response) { - if (response.status === "success") { - this.finishedSuccess(selector, response.data.message); + finishedAction: function(selector, response) { + if (response.status === 'success') { + this.finishedSuccess(selector, response.data.message) } else { - this.finishedError(selector, response.data.message); + this.finishedError(selector, response.data.message) } }, @@ -92,14 +92,14 @@ export default { * @param {Object} selector Placeholder to display the message in * @param {string} message Plain text success message to display (no HTML allowed) */ - finishedSuccess: function (selector, message) { + finishedSuccess: function(selector, message) { $(selector).text(message) .addClass('success') .removeClass('error') .stop(true, true) .delay(3000) .fadeOut(900) - .show(); + .show() }, /** @@ -108,10 +108,10 @@ export default { * @param {Object} selector Placeholder to display the message in * @param {string} message Plain text error message to display (no HTML allowed) */ - finishedError: function (selector, message) { + finishedError: function(selector, message) { $(selector).text(message) .addClass('error') .removeClass('success') - .show(); + .show() } } diff --git a/core/src/OC/navigation.js b/core/src/OC/navigation.js index b37d339b41b768fa11c4f00d029470a211e88e8a..f9e6789950a27c2f8ca2aa134b456d01e92b0ce5 100644 --- a/core/src/OC/navigation.js +++ b/core/src/OC/navigation.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -24,10 +24,10 @@ * @param {string} targetURL URL to redirect to * @deprecated 17.0.0 use window.location directly */ -export const redirect = targetURL => window.location = targetURL +export const redirect = targetURL => { window.location = targetURL } /** * Reloads the current page * @deprecated 17.0.0 use window.location.reload directly */ -export const reload = () => window.location.reload() +export const reload = () => { window.location.reload() } diff --git a/core/src/OC/notification.js b/core/src/OC/notification.js index f31b37e4ba4dc0ff9483db694b6556b68debab32..b56fb6b14def04b1c5cd4a0cc635730481eefd91 100644 --- a/core/src/OC/notification.js +++ b/core/src/OC/notification.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -21,7 +21,6 @@ import _ from 'underscore' import $ from 'jquery' -import Toastify from 'toastify-js' /** * @todo Write documentation @@ -35,11 +34,11 @@ export default { getDefaultNotificationFunction: null, /** - * @param callback + * @param {Function} callback callback function * @deprecated 17.0.0 use OCP.Toast */ - setDefault: function (callback) { - this.getDefaultNotificationFunction = callback; + setDefault: function(callback) { + this.getDefaultNotificationFunction = callback }, /** @@ -52,23 +51,20 @@ export default { * @param {Function} [callback] callback * @deprecated 17.0.0 use OCP.Toast */ - hide: function ($row, callback) { - var self = this; - var $notification = $('#content'); - + hide: function($row, callback) { if (_.isFunction($row)) { // first arg is the callback - callback = $row; - $row = undefined; + callback = $row + $row = undefined } if (!$row) { - console.error('Missing argument $row in OC.Notification.hide() call, caller needs to be adjusted to only dismiss its own notification'); - return; + console.error('Missing argument $row in OC.Notification.hide() call, caller needs to be adjusted to only dismiss its own notification') + return } // remove the row directly - $row.each(function () { + $row.each(function() { $(this)[0].toastify.hideToast() if (this === this.updatableNotification) { this.updatableNotification = null @@ -91,10 +87,10 @@ export default { * @param {Object} [options] options * @param {string} [options.type] notification type * @param {int} [options.timeout=0] timeout value, defaults to 0 (permanent) - * @return {jQuery} jQuery element for notification row + * @returns {jQuery} jQuery element for notification row * @deprecated 17.0.0 use OCP.Toast */ - showHtml: function (html, options) { + showHtml: function(html, options) { options = options || {} options.isHTML = true options.timeout = (!options.timeout) ? -1 : options.timeout @@ -109,29 +105,29 @@ export default { * @param {Object} [options] options * @param {string} [options.type] notification type * @param {int} [options.timeout=0] timeout value, defaults to 0 (permanent) - * @return {jQuery} jQuery element for notification row + * @returns {jQuery} jQuery element for notification row * @deprecated 17.0.0 use OCP.Toast */ - show: function (text, options) { - options = options || {}; - options.timeout = (!options.timeout) ? -1 : options.timeout; - const toast = window.OCP.Toast.message(text, options); - return $(toast.toastElement); + show: function(text, options) { + options = options || {} + options.timeout = (!options.timeout) ? -1 : options.timeout + const toast = window.OCP.Toast.message(text, options) + return $(toast.toastElement) }, /** * Updates (replaces) a sanitized notification. * * @param {string} text Message to display - * @return {jQuery} JQuery element for notificaiton row + * @returns {jQuery} JQuery element for notificaiton row * @deprecated 17.0.0 use OCP.Toast */ - showUpdate: function (text) { + showUpdate: function(text) { if (this.updatableNotification) { - this.updatableNotification.hideToast(); + this.updatableNotification.hideToast() } - this.updatableNotification = OCP.Toast.message(text, {timeout: -1}) - return $(this.updatableNotification.toastElement); + this.updatableNotification = OCP.Toast.message(text, { timeout: -1 }) + return $(this.updatableNotification.toastElement) }, /** @@ -143,21 +139,22 @@ export default { * @param {int} [options.timeout=7] timeout in seconds, if this is 0 it will show the message permanently * @param {boolean} [options.isHTML=false] an indicator for HTML notifications (true) or text (false) * @param {string} [options.type] notification type + * @returns {JQuery<any>} the toast element * @deprecated 17.0.0 use OCP.Toast */ - showTemporary: function (text, options) { + showTemporary: function(text, options) { options = options || {} - options.timeout = options.timeout || 7; - const toast = window.OCP.Toast.message(text, options); - return $(toast.toastElement); + options.timeout = options.timeout || 7 + const toast = window.OCP.Toast.message(text, options) + return $(toast.toastElement) }, /** * Returns whether a notification is hidden. - * @return {boolean} + * @returns {boolean} * @deprecated 17.0.0 use OCP.Toast */ - isHidden: function () { - return !$('#content').find('.toastify').length; + isHidden: function() { + return !$('#content').find('.toastify').length } } diff --git a/core/src/OC/password-confirmation.js b/core/src/OC/password-confirmation.js index a38f32b4b5ec2d5d4c685ca720a96d9482d8c4be..e303d70bf5de5a4ed42a41d695493f889471d59e 100644 --- a/core/src/OC/password-confirmation.js +++ b/core/src/OC/password-confirmation.js @@ -1,6 +1,4 @@ -/* global nc_pageLoad */ - -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -35,24 +33,26 @@ export default { pageLoadTime: null, - init: function () { - $('.password-confirm-required').on('click', _.bind(this.requirePasswordConfirmation, this)); - this.pageLoadTime = moment.now(); + init: function() { + $('.password-confirm-required').on('click', _.bind(this.requirePasswordConfirmation, this)) + this.pageLoadTime = moment.now() }, - requiresPasswordConfirmation: function () { - var serverTimeDiff = this.pageLoadTime - (nc_pageLoad * 1000); - var timeSinceLogin = moment.now() - (serverTimeDiff + (nc_lastLogin * 1000)); + requiresPasswordConfirmation: function() { + var serverTimeDiff = this.pageLoadTime - (window.nc_pageLoad * 1000) + var timeSinceLogin = moment.now() - (serverTimeDiff + (window.nc_lastLogin * 1000)) // if timeSinceLogin > 30 minutes and user backend allows password confirmation - return (backendAllowsPasswordConfirmation && timeSinceLogin > 30 * 60 * 1000); + return (window.backendAllowsPasswordConfirmation && timeSinceLogin > 30 * 60 * 1000) }, /** - * @param {function} callback + * @param {Function} callback success callback function + * @param {Object} options options + * @param {Function} rejectCallback error callback function */ - requirePasswordConfirmation: function (callback, options, rejectCallback) { - options = typeof options !== 'undefined' ? options : {}; + requirePasswordConfirmation: function(callback, options, rejectCallback) { + options = typeof options !== 'undefined' ? options : {} var defaults = { title: t('core', 'Authentication required'), text: t( @@ -61,20 +61,20 @@ export default { ), confirm: t('core', 'Confirm'), label: t('core', 'Password'), - error: '', - }; + error: '' + } - var config = _.extend(defaults, options); + var config = _.extend(defaults, options) - var self = this; + var self = this if (this.requiresPasswordConfirmation()) { OC.dialogs.prompt( config.text, config.title, - function (result, password) { + function(result, password) { if (result && password !== '') { - self._confirmPassword(password, config); + self._confirmPassword(password, config) } else if (_.isFunction(rejectCallback)) { rejectCallback() } @@ -82,27 +82,27 @@ export default { true, config.label, true - ).then(function () { - var $dialog = $('.oc-dialog:visible'); - $dialog.find('.ui-icon').remove(); - $dialog.addClass('password-confirmation'); + ).then(function() { + var $dialog = $('.oc-dialog:visible') + $dialog.find('.ui-icon').remove() + $dialog.addClass('password-confirmation') if (config.error !== '') { - var $error = $('<p></p>').addClass('msg warning').text(config.error); + var $error = $('<p></p>').addClass('msg warning').text(config.error) } - $dialog.find('.oc-dialog-content').append($error); - $dialog.find('.oc-dialog-buttonrow').addClass('aside'); + $dialog.find('.oc-dialog-content').append($error) + $dialog.find('.oc-dialog-buttonrow').addClass('aside') - var $buttons = $dialog.find('button'); - $buttons.eq(0).hide(); - $buttons.eq(1).text(config.confirm); - }); + var $buttons = $dialog.find('button') + $buttons.eq(0).hide() + $buttons.eq(1).text(config.confirm) + }) } - this.callback = callback; + this.callback = callback }, - _confirmPassword: function (password, config) { - var self = this; + _confirmPassword: function(password, config) { + var self = this $.ajax({ url: OC.generateUrl('/login/confirm'), @@ -110,17 +110,17 @@ export default { password: password }, type: 'POST', - success: function (response) { - nc_lastLogin = response.lastLogin; + success: function(response) { + window.nc_lastLogin = response.lastLogin if (_.isFunction(self.callback)) { - self.callback(); + self.callback() } }, - error: function () { - config.error = t('core', 'Failed to authenticate, try again'); - OC.PasswordConfirmation.requirePasswordConfirmation(self.callback, config); + error: function() { + config.error = t('core', 'Failed to authenticate, try again') + OC.PasswordConfirmation.requirePasswordConfirmation(self.callback, config) } - }); + }) } -}; +} diff --git a/core/src/OC/path.js b/core/src/OC/path.js index ef58caf4850dd4f284347ad8fdd5306587c9e835..145c90d1e5e9ddbba31546dd699994d2387ffc80 100644 --- a/core/src/OC/path.js +++ b/core/src/OC/path.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -22,8 +22,8 @@ /** * URI-Encodes a file path but keep the path slashes. * - * @param {String} path - * @return {String} encoded path + * @param {String} path path + * @returns {String} encoded path */ export const encodePath = path => { if (!path) { @@ -41,8 +41,8 @@ export const encodePath = path => { * Returns the base name of the given path. * For example for "/abc/somefile.txt" it will return "somefile.txt" * - * @param {String} path - * @return {String} base name + * @param {String} path path + * @returns {String} base name */ export const basename = path => path.replace(/\\/g, '/').replace(/.*\//, '') @@ -50,10 +50,10 @@ export const basename = path => path.replace(/\\/g, '/').replace(/.*\//, '') * Returns the dir name of the given path. * For example for "/abc/somefile.txt" it will return "/abc" * - * @param {String} path - * @return {String} dir name + * @param {String} path path + * @returns {String} dir name */ -export const dirname = path => path.replace(/\\/g, '/').replace(/\/[^\/]*$/, '') +export const dirname = path => path.replace(/\\/g, '/').replace(/\/[^/]*$/, '') /** * Returns whether the given paths are the same, without @@ -62,7 +62,7 @@ export const dirname = path => path.replace(/\\/g, '/').replace(/\/[^\/]*$/, '') * * @param {String} path1 first path * @param {String} path2 second path - * @return {bool} true if the paths are the same + * @returns {bool} true if the paths are the same * * @since 9.0 */ @@ -80,13 +80,13 @@ export const isSamePath = (path1, path2) => { * * @param {...String} path sections * - * @return {String} joined path, any leading or trailing slash + * @returns {String} joined path, any leading or trailing slash * will be kept * * @since 8.2 */ export const joinPaths = (...args) => { - if (arguments.length < 1) { + if (args.length < 1) { return '' } @@ -98,7 +98,7 @@ export const joinPaths = (...args) => { const lastArg = nonEmptyArgs[nonEmptyArgs.length - 1] const leadingSlash = nonEmptyArgs[0].charAt(0) === '/' - const trailingSlash = lastArg.charAt(lastArg.length - 1) === '/'; + const trailingSlash = lastArg.charAt(lastArg.length - 1) === '/' const sections = nonEmptyArgs.reduce((acc, section) => acc.concat(section.split('/')), []) let first = !leadingSlash diff --git a/core/src/OC/plugins.js b/core/src/OC/plugins.js index dd25610c2dbd150a0a4727317499a3c0df954255..124116b9a59b4bd160b22876cb328e394ae2c280 100644 --- a/core/src/OC/plugins.js +++ b/core/src/OC/plugins.js @@ -33,14 +33,14 @@ export default { * Register plugin * * @param {String} targetName app name / class name to hook into - * @param {OC.Plugin} plugin + * @param {OC.Plugin} plugin plugin */ - register: function (targetName, plugin) { - var plugins = this._plugins[targetName]; + register: function(targetName, plugin) { + var plugins = this._plugins[targetName] if (!plugins) { - plugins = this._plugins[targetName] = []; + plugins = this._plugins[targetName] = [] } - plugins.push(plugin); + plugins.push(plugin) }, /** @@ -48,24 +48,24 @@ export default { * name / app name / class name. * * @param {String} targetName app name / class name to hook into - * @return {Array.<OC.Plugin>} array of plugins + * @returns {Array.<OC.Plugin>} array of plugins */ - getPlugins: function (targetName) { - return this._plugins[targetName] || []; + getPlugins: function(targetName) { + return this._plugins[targetName] || [] }, /** * Call attach() on all plugins registered to the given target name. * * @param {String} targetName app name / class name - * @param {Object} object to be extended + * @param {Object} targetObject to be extended * @param {Object} [options] options */ - attach: function (targetName, targetObject, options) { - var plugins = this.getPlugins(targetName); + attach: function(targetName, targetObject, options) { + var plugins = this.getPlugins(targetName) for (var i = 0; i < plugins.length; i++) { if (plugins[i].attach) { - plugins[i].attach(targetObject, options); + plugins[i].attach(targetObject, options) } } }, @@ -74,16 +74,16 @@ export default { * Call detach() on all plugins registered to the given target name. * * @param {String} targetName app name / class name - * @param {Object} object to be extended + * @param {Object} targetObject to be extended * @param {Object} [options] options */ - detach: function (targetName, targetObject, options) { - var plugins = this.getPlugins(targetName); + detach: function(targetName, targetObject, options) { + var plugins = this.getPlugins(targetName) for (var i = 0; i < plugins.length; i++) { if (plugins[i].detach) { - plugins[i].detach(targetObject, options); + plugins[i].detach(targetObject, options) } } } -} \ No newline at end of file +} diff --git a/core/src/OC/query-string.js b/core/src/OC/query-string.js index 973412de5793e614092357180dd54f308963c3c4..a814177790575c5f0ce2a39a71641799b832408a 100644 --- a/core/src/OC/query-string.js +++ b/core/src/OC/query-string.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -22,15 +22,14 @@ /** * Parses a URL query string into a JS map * @param {string} queryString query string in the format param1=1234¶m2=abcde¶m3=xyz - * @return {Object.<string, string>} map containing key/values matching the URL parameters + * @returns {Object.<string, string>} map containing key/values matching the URL parameters */ export const parse = queryString => { - let parts, - pos, - components, - result = {}, - key, - value + let parts + let pos + let components + let result = {} + let key if (!queryString) { return null } @@ -73,13 +72,13 @@ export const parse = queryString => { /** * Builds a URL query from a JS map. * @param {Object.<string, string>} params map containing key/values matching the URL parameters - * @return {string} String containing a URL query (without question) mark + * @returns {string} String containing a URL query (without question) mark */ export const build = params => { if (!params) { return '' } - return $.map(params, function (value, key) { + return $.map(params, function(value, key) { var s = encodeURIComponent(key) if (value !== null && typeof (value) !== 'undefined') { s += '=' + encodeURIComponent(value) diff --git a/core/src/OC/requesttoken.js b/core/src/OC/requesttoken.js index 3c4a185f1c0c777358072993197d0739be4c60a7..a92eb1695770facaeef16cfca7c2fbc9b60dca37 100644 --- a/core/src/OC/requesttoken.js +++ b/core/src/OC/requesttoken.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -19,25 +19,24 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -let token = document.getElementsByTagName('head')[0].getAttribute('data-requesttoken'); +let token = document.getElementsByTagName('head')[0].getAttribute('data-requesttoken') const observers = [] /** - * @return {string} + * @returns {string} */ export const getToken = () => token /** - * @param {Function} observer - * @return {number} + * @param {Function} observer observer + * @returns {number} */ export const subscribe = observer => observers.push(observer) /** - * @param {String} newToken + * @param {String} newToken new token */ export const setToken = newToken => { token = newToken - observers.forEach(o => o(token)) } diff --git a/core/src/OC/routing.js b/core/src/OC/routing.js index 0223a6cfd1e816d59d5847d85201cf8ef830b546..36fd00090323439b90df7e354f1866ec6387f2b7 100644 --- a/core/src/OC/routing.js +++ b/core/src/OC/routing.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -22,27 +22,27 @@ import _ from 'underscore' import OC from './index' -import {coreApps} from './constants' +import { coreApps } from './constants' /** * Get an absolute url to a file in an app * @param {string} app the id of the app the file belongs to * @param {string} file the file path relative to the app folder - * @return {string} Absolute URL to a file + * @returns {string} Absolute URL to a file */ export const linkTo = (app, file) => filePath(app, '', file) /** * Creates a relative url for remote use * @param {string} service id - * @return {string} the url + * @returns {string} the url */ export const linkToRemoteBase = service => getRootPath() + '/remote.php/' + service /** * @brief Creates an absolute url for remote use * @param {string} service id - * @return {string} the url + * @returns {string} the url */ export const linkToRemote = service => window.location.protocol + '//' + window.location.host + linkToRemoteBase(service) @@ -50,7 +50,7 @@ export const linkToRemote = service => window.location.protocol + '//' + window. * Gets the base path for the given OCS API service. * @param {string} service name * @param {int} version OCS API version - * @return {string} OCS API base path + * @returns {string} OCS API base path */ export const linkToOCS = (service, version) => { version = (version !== 2) ? 1 : 2 @@ -60,42 +60,42 @@ export const linkToOCS = (service, version) => { /** * Generates the absolute url for the given relative url, which can contain parameters. * Parameters will be URL encoded automatically. - * @param {string} url - * @param [params] params - * @param [options] options + * @param {string} url the url + * @param {Object} [params] params + * @param {Object} [options] destructuring object * @param {bool} [options.escape=true] enable/disable auto escape of placeholders (by default enabled) - * @return {string} Absolute URL for the given relative URL + * @returns {string} Absolute URL for the given relative URL */ export const generateUrl = (url, params, options) => { const defaultOptions = { - escape: true - }, - allOptions = options || {}; - _.defaults(allOptions, defaultOptions); + escape: true + } + const allOptions = options || {} + _.defaults(allOptions, defaultOptions) - const _build = function (text, vars) { - vars = vars || []; + const _build = function(text, vars) { + vars = vars || [] return text.replace(/{([^{}]*)}/g, - function (a, b) { - var r = (vars[b]); + function(a, b) { + var r = (vars[b]) if (allOptions.escape) { - return (typeof r === 'string' || typeof r === 'number') ? encodeURIComponent(r) : encodeURIComponent(a); + return (typeof r === 'string' || typeof r === 'number') ? encodeURIComponent(r) : encodeURIComponent(a) } else { - return (typeof r === 'string' || typeof r === 'number') ? r : a; + return (typeof r === 'string' || typeof r === 'number') ? r : a } } - ); - }; + ) + } if (url.charAt(0) !== '/') { - url = '/' + url; + url = '/' + url } if (OC.config.modRewriteWorking === true) { - return getRootPath() + _build(url, params); + return getRootPath() + _build(url, params) } - return getRootPath() + '/index.php' + _build(url, params); + return getRootPath() + '/index.php' + _build(url, params) } /** @@ -105,11 +105,11 @@ export const generateUrl = (url, params, options) => { * * @param {string} app the app id to which the image belongs * @param {string} file the name of the image file - * @return {string} + * @returns {string} */ export const imagePath = (app, file) => { if (file.indexOf('.') === -1) { - //if no extension is given, use svg + // if no extension is given, use svg return filePath(app, 'img', file + '.svg') } @@ -121,13 +121,13 @@ export const imagePath = (app, file) => { * @param {string} app the id of the app * @param {string} type the type of the file to link to (e.g. css,img,ajax.template) * @param {string} file the filename - * @return {string} Absolute URL for a file in an app + * @returns {string} Absolute URL for a file in an app */ export const filePath = (app, type, file) => { const isCore = coreApps.indexOf(app) !== -1 let link = getRootPath() if (file.substring(file.length - 3) === 'php' && !isCore) { - link += '/index.php/apps/' + app; + link += '/index.php/apps/' + app if (file !== 'index.php') { link += '/' if (type) { @@ -136,7 +136,7 @@ export const filePath = (app, type, file) => { link += file } } else if (file.substring(file.length - 3) !== 'php' && !isCore) { - link = OC.appswebroots[app]; + link = OC.appswebroots[app] if (type) { link += '/' + type + '/' } @@ -170,7 +170,7 @@ export const filePath = (app, type, file) => { * is accessible, with a leading slash. * For example "/nextcloud". * - * @return {string} web root path + * @returns {string} web root path * * @since 8.2 */ diff --git a/core/src/OC/search.js b/core/src/OC/search.js index 31f8df7ecf193315a918290569e6312564da9f0a..281907a06a76acaced39824cff32a291c5bfe6d3 100644 --- a/core/src/OC/search.js +++ b/core/src/OC/search.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -25,17 +25,17 @@ import OC from './index' * Do a search query and display the results * @param {string} query the search query */ -const search = function (query) { - OC.Search.search(query, null, 0, 30); -}; +const search = function(query) { + OC.Search.search(query, null, 0, 30) +} /** * @namespace OC.search */ -search.customResults = {}; +search.customResults = {} /** * @deprecated use get/setFormatter() instead */ -search.resultTypes = {}; +search.resultTypes = {} -export default search; +export default search diff --git a/core/src/OC/util-history.js b/core/src/OC/util-history.js index 3dd1a104ef873eb42fdb25cd7bab13080a69fff7..77607b64022cd967cf376e98296f88b34239654a 100644 --- a/core/src/OC/util-history.js +++ b/core/src/OC/util-history.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -20,7 +20,6 @@ */ import _ from 'underscore' - import OC from './index' /** @@ -46,40 +45,41 @@ export default { * using the params as query string * @param {boolean} [replace=false] whether to replace instead of pushing */ - _pushState: function (params, url, replace) { - var strParams; + _pushState: function(params, url, replace) { + var strParams if (typeof (params) === 'string') { - strParams = params; + strParams = params } else { - strParams = OC.buildQueryString(params); + strParams = OC.buildQueryString(params) } + if (window.history.pushState) { - url = url || location.pathname + '?' + strParams; + url = url || location.pathname + '?' + strParams // Workaround for bug with SVG and window.history.pushState on Firefox < 51 // https://bugzilla.mozilla.org/show_bug.cgi?id=652991 - var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1 if (isFirefox && parseInt(navigator.userAgent.split('/').pop()) < 51) { - var patterns = document.querySelectorAll('[fill^="url(#"], [stroke^="url(#"], [filter^="url(#invert"]'); + var patterns = document.querySelectorAll('[fill^="url(#"], [stroke^="url(#"], [filter^="url(#invert"]') for (var i = 0, ii = patterns.length, pattern; i < ii; i++) { - pattern = patterns[i]; - pattern.style.fill = pattern.style.fill; - pattern.style.stroke = pattern.style.stroke; - pattern.removeAttribute("filter"); - pattern.setAttribute("filter", "url(#invert)"); + const { fill, stroke } = pattern.style + pattern = patterns[i] + pattern.style.fill = fill + pattern.style.stroke = stroke + pattern.removeAttribute('filter') + pattern.setAttribute('filter', 'url(#invert)') } } if (replace) { - window.history.replaceState(params, '', url); + window.history.replaceState(params, '', url) } else { - window.history.pushState(params, '', url); + window.history.pushState(params, '', url) } - } - // use URL hash for IE8 - else { - window.location.hash = '?' + strParams; + } else { + // use URL hash for IE8 + window.location.hash = '?' + strParams // inhibit next onhashchange that just added itself // to the event queue - this._cancelPop = true; + this._cancelPop = true } }, @@ -89,13 +89,11 @@ export default { * Note: this includes a workaround for IE8/IE9 that uses * the hash part instead of the search part. * - * @param {Object|string} params to append to the URL, can be either a string - * or a map - * @param {string} [url] URL to be used, otherwise the current URL will be used, - * using the params as query string + * @param {Object|string} params to append to the URL, can be either a string or a map + * @param {string} [url] URL to be used, otherwise the current URL will be used, using the params as query string */ - pushState: function (params, url) { - return this._pushState(params, url, false); + pushState: function(params, url) { + this._pushState(params, url, false) }, /** @@ -109,75 +107,76 @@ export default { * @param {string} [url] URL to be used, otherwise the current URL will be used, * using the params as query string */ - replaceState: function (params, url) { - return this._pushState(params, url, true); + replaceState: function(params, url) { + this._pushState(params, url, true) }, /** * Add a popstate handler * - * @param handler function + * @param {Function} handler handler */ - addOnPopStateHandler: function (handler) { - this._handlers.push(handler); + addOnPopStateHandler: function(handler) { + this._handlers.push(handler) }, /** * Parse a query string from the hash part of the URL. * (workaround for IE8 / IE9) + * @returns {string} */ - _parseHashQuery: function () { - var hash = window.location.hash, - pos = hash.indexOf('?'); + _parseHashQuery: function() { + var hash = window.location.hash + var pos = hash.indexOf('?') if (pos >= 0) { - return hash.substr(pos + 1); + return hash.substr(pos + 1) } if (hash.length) { // remove hash sign - return hash.substr(1); + return hash.substr(1) } - return ''; + return '' }, - _decodeQuery: function (query) { - return query.replace(/\+/g, ' '); + _decodeQuery: function(query) { + return query.replace(/\+/g, ' ') }, /** * Parse the query/search part of the URL. * Also try and parse it from the URL hash (for IE8) * - * @return map of parameters + * @returns {Object} map of parameters */ - parseUrlQuery: function () { - var query = this._parseHashQuery(), - params; + parseUrlQuery: function() { + var query = this._parseHashQuery() + var params // try and parse from URL hash first if (query) { - params = OC.parseQueryString(this._decodeQuery(query)); + params = OC.parseQueryString(this._decodeQuery(query)) } // else read from query attributes - params = _.extend(params || {}, OC.parseQueryString(this._decodeQuery(location.search))); - return params || {}; + params = _.extend(params || {}, OC.parseQueryString(this._decodeQuery(location.search))) + return params || {} }, - _onPopState: function (e) { + _onPopState: function(e) { if (this._cancelPop) { - this._cancelPop = false; - return; + this._cancelPop = false + return } - var params; + var params if (!this._handlers.length) { - return; + return } - params = (e && e.state); + params = (e && e.state) if (_.isString(params)) { - params = OC.parseQueryString(params); + params = OC.parseQueryString(params) } else if (!params) { - params = this.parseUrlQuery() || {}; + params = this.parseUrlQuery() || {} } for (var i = 0; i < this._handlers.length; i++) { - this._handlers[i](params); + this._handlers[i](params) } } } diff --git a/core/src/OC/util.js b/core/src/OC/util.js index dd66f4b29541e5f1bd2159c4dc0107d540115e63..7c4a44b77d9de8b0fb21d3af1d369cd52111b57f 100644 --- a/core/src/OC/util.js +++ b/core/src/OC/util.js @@ -1,6 +1,4 @@ -/* global t */ - -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -30,22 +28,26 @@ import humanFileSize from '../Util/human-file-size' function chunkify(t) { // Adapted from http://my.opera.com/GreyWyvern/blog/show.dml/1671288 - let tz = [], x = 0, y = -1, n = 0, code, c; + let tz = [] + let x = 0 + let y = -1 + let n = 0 + let c while (x < t.length) { - c = t.charAt(x); + c = t.charAt(x) // only include the dot in strings - var m = ((!n && c === '.') || (c >= '0' && c <= '9')); + var m = ((!n && c === '.') || (c >= '0' && c <= '9')) if (m !== n) { // next chunk - y++; - tz[y] = ''; - n = m; + y++ + tz[y] = '' + n = m } - tz[y] += c; - x++; + tz[y] += c + x++ } - return tz; + return tz } /** @@ -64,17 +66,17 @@ export default { * Makes 2kB to 2048. * Inspired by computerFileSize in helper.php * @param {string} string file size in human readable format - * @return {number} or null if string could not be parsed + * @returns {number} or null if string could not be parsed * * */ - computerFileSize: function (string) { + computerFileSize: function(string) { if (typeof string !== 'string') { - return null; + return null } - var s = string.toLowerCase().trim(); - var bytes = null; + var s = string.toLowerCase().trim() + var bytes = null var bytesArray = { 'b': 1, @@ -88,164 +90,165 @@ export default { 't': 1024 * 1024 * 1024 * 1024, 'pb': 1024 * 1024 * 1024 * 1024 * 1024, 'p': 1024 * 1024 * 1024 * 1024 * 1024 - }; + } - var matches = s.match(/^[\s+]?([0-9]*)(\.([0-9]+))?( +)?([kmgtp]?b?)$/i); + var matches = s.match(/^[\s+]?([0-9]*)(\.([0-9]+))?( +)?([kmgtp]?b?)$/i) if (matches !== null) { - bytes = parseFloat(s); + bytes = parseFloat(s) if (!isFinite(bytes)) { - return null; + return null } } else { - return null; + return null } if (matches[5]) { - bytes = bytes * bytesArray[matches[5]]; + bytes = bytes * bytesArray[matches[5]] } - bytes = Math.round(bytes); - return bytes; + bytes = Math.round(bytes) + return bytes }, /** - * @param timestamp - * @param format + * @param {string|number} timestamp timestamp + * @param {string} format date format, see momentjs docs * @returns {string} timestamp formatted as requested */ - formatDate: function (timestamp, format) { - format = format || "LLL"; - return moment(timestamp).format(format); + formatDate: function(timestamp, format) { + format = format || 'LLL' + return moment(timestamp).format(format) }, /** - * @param timestamp + * @param {string|number} timestamp timestamp * @returns {string} human readable difference from now */ - relativeModifiedDate: function (timestamp) { - var diff = moment().diff(moment(timestamp)); + relativeModifiedDate: function(timestamp) { + var diff = moment().diff(moment(timestamp)) if (diff >= 0 && diff < 45000) { - return t('core', 'seconds ago'); + return t('core', 'seconds ago') } - return moment(timestamp).fromNow(); + return moment(timestamp).fromNow() }, /** * Returns whether this is IE * - * @return {bool} true if this is IE, false otherwise + * @returns {bool} true if this is IE, false otherwise */ - isIE: function () { - return $('html').hasClass('ie'); + isIE: function() { + return $('html').hasClass('ie') }, /** * Returns the width of a generic browser scrollbar * - * @return {int} width of scrollbar + * @returns {int} width of scrollbar */ - getScrollBarWidth: function () { + getScrollBarWidth: function() { if (this._scrollBarWidth) { - return this._scrollBarWidth; + return this._scrollBarWidth } - var inner = document.createElement('p'); - inner.style.width = "100%"; - inner.style.height = "200px"; - - var outer = document.createElement('div'); - outer.style.position = "absolute"; - outer.style.top = "0px"; - outer.style.left = "0px"; - outer.style.visibility = "hidden"; - outer.style.width = "200px"; - outer.style.height = "150px"; - outer.style.overflow = "hidden"; - outer.appendChild(inner); - - document.body.appendChild(outer); - var w1 = inner.offsetWidth; - outer.style.overflow = 'scroll'; - var w2 = inner.offsetWidth; + var inner = document.createElement('p') + inner.style.width = '100%' + inner.style.height = '200px' + + var outer = document.createElement('div') + outer.style.position = 'absolute' + outer.style.top = '0px' + outer.style.left = '0px' + outer.style.visibility = 'hidden' + outer.style.width = '200px' + outer.style.height = '150px' + outer.style.overflow = 'hidden' + outer.appendChild(inner) + + document.body.appendChild(outer) + var w1 = inner.offsetWidth + outer.style.overflow = 'scroll' + var w2 = inner.offsetWidth if (w1 === w2) { - w2 = outer.clientWidth; + w2 = outer.clientWidth } - document.body.removeChild(outer); + document.body.removeChild(outer) - this._scrollBarWidth = (w1 - w2); + this._scrollBarWidth = (w1 - w2) - return this._scrollBarWidth; + return this._scrollBarWidth }, /** * Remove the time component from a given date * * @param {Date} date date - * @return {Date} date with stripped time + * @returns {Date} date with stripped time */ - stripTime: function (date) { + stripTime: function(date) { // FIXME: likely to break when crossing DST // would be better to use a library like momentJS - return new Date(date.getFullYear(), date.getMonth(), date.getDate()); + return new Date(date.getFullYear(), date.getMonth(), date.getDate()) }, /** * Compare two strings to provide a natural sort - * @param a first string to compare - * @param b second string to compare - * @return -1 if b comes before a, 1 if a comes before b + * @param {string} a first string to compare + * @param {string} b second string to compare + * @returns {number} -1 if b comes before a, 1 if a comes before b * or 0 if the strings are identical */ - naturalSortCompare: function (a, b) { - var x; - var aa = chunkify(a); - var bb = chunkify(b); + naturalSortCompare: function(a, b) { + var x + var aa = chunkify(a) + var bb = chunkify(b) for (x = 0; aa[x] && bb[x]; x++) { if (aa[x] !== bb[x]) { - var aNum = Number(aa[x]), bNum = Number(bb[x]); + var aNum = Number(aa[x]); var bNum = Number(bb[x]) // note: == is correct here + /* eslint-disable-next-line */ if (aNum == aa[x] && bNum == bb[x]) { - return aNum - bNum; + return aNum - bNum } else { // Note: This locale setting isn't supported by all browsers but for the ones // that do there will be more consistency between client-server sorting - return aa[x].localeCompare(bb[x], OC.getLanguage()); + return aa[x].localeCompare(bb[x], OC.getLanguage()) } } } - return aa.length - bb.length; + return aa.length - bb.length }, /** * Calls the callback in a given interval until it returns true - * @param {function} callback + * @param {function} callback function to call on success * @param {integer} interval in milliseconds */ - waitFor: function (callback, interval) { - var internalCallback = function () { + waitFor: function(callback, interval) { + var internalCallback = function() { if (callback() !== true) { - setTimeout(internalCallback, interval); + setTimeout(internalCallback, interval) } - }; + } - internalCallback(); + internalCallback() }, /** * Checks if a cookie with the given name is present and is set to the provided value. * @param {string} name name of the cookie * @param {string} value value of the cookie - * @return {boolean} true if the cookie with the given name has the given value + * @returns {boolean} true if the cookie with the given name has the given value */ - isCookieSetToValue: function (name, value) { - var cookies = document.cookie.split(';'); + isCookieSetToValue: function(name, value) { + var cookies = document.cookie.split(';') for (var i = 0; i < cookies.length; i++) { - var cookie = cookies[i].split('='); + var cookie = cookies[i].split('=') if (cookie[0].trim() === name && cookie[1].trim() === value) { - return true; + return true } } - return false; + return false } } diff --git a/core/src/OC/xhr-error.js b/core/src/OC/xhr-error.js index 43b2eba7229e8c9d48c208cde84cb661c5e890be..8d2ad11533678b2e283c8270d77857b2a562b03d 100644 --- a/core/src/OC/xhr-error.js +++ b/core/src/OC/xhr-error.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -34,11 +34,12 @@ import Notification from './notification' */ export const ajaxConnectionLostHandler = _.throttle(() => { Notification.showTemporary(t('core', 'Connection to server lost')) -}, 7 * 1000, {trailing: false}) +}, 7 * 1000, { trailing: false }) /** * Process ajax error, redirects to main page * if an error/auth error status was returned. + * @param {XMLHttpRequest} xhr xhr request */ export const processAjaxError = xhr => { // purposefully aborted request ? @@ -50,18 +51,18 @@ export const processAjaxError = xhr => { if (_.contains([302, 303, 307, 401], xhr.status) && OC.currentUser) { // sometimes "beforeunload" happens later, so need to defer the reload a bit - setTimeout(function () { + setTimeout(function() { if (!OC._userIsNavigatingAway && !OC._reloadCalled) { let timer = 0 const seconds = 5 - const interval = setInterval(function () { - Notification.showUpdate(n('core', 'Problem loading page, reloading in %n second', 'Problem loading page, reloading in %n seconds', seconds - timer)) - if (timer >= seconds) { - clearInterval(interval) - OC.reload() - } - timer++ - }, 1000 // 1 second interval + const interval = setInterval(function() { + Notification.showUpdate(n('core', 'Problem loading page, reloading in %n second', 'Problem loading page, reloading in %n seconds', seconds - timer)) + if (timer >= seconds) { + clearInterval(interval) + OC.reload() + } + timer++ + }, 1000 // 1 second interval ) // only call reload once @@ -70,7 +71,7 @@ export const processAjaxError = xhr => { }, 100) } else if (xhr.status === 0) { // Connection lost (e.g. WiFi disconnected or server is down) - setTimeout(function () { + setTimeout(function() { if (!OC._userIsNavigatingAway && !OC._reloadCalled) { // TODO: call method above directly OC._ajaxConnectionLostHandler() @@ -85,7 +86,7 @@ export const processAjaxError = xhr => { * This means that if this XHR object returns 401 or session timeout errors, * the current page will automatically be reloaded. * - * @param {XMLHttpRequest} xhr + * @param {XMLHttpRequest} xhr xhr request */ export const registerXHRForErrorProcessing = xhr => { const loadCallback = () => { @@ -93,7 +94,7 @@ export const registerXHRForErrorProcessing = xhr => { return } - if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) { + if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { return } diff --git a/core/src/OCA/index.js b/core/src/OCA/index.js index fa354456c9186ed499774bd269158e89080b0ede..4b87d325a55ca45a6d4a9501432411ffd591ea26 100644 --- a/core/src/OCA/index.js +++ b/core/src/OCA/index.js @@ -23,4 +23,4 @@ * Namespace for apps * @namespace OCA */ -export default {}; +export default {} diff --git a/core/src/OCP/appconfig.js b/core/src/OCP/appconfig.js index 9c5b063566fabe7f32060ece4e885ab9b876304e..f4213419aab52e04de955d69d1fde7de9930adf0 100644 --- a/core/src/OCP/appconfig.js +++ b/core/src/OCP/appconfig.js @@ -23,94 +23,94 @@ import $ from 'jquery' import OC from '../OC/index' /** - * @param {string} method - * @param {string} endpoint - * @param {Object} [options] - * @param {Object} [options.data] - * @param {function} [options.success] - * @param {function} [options.error] + * @param {string} method 'post' or 'delete' + * @param {string} endpoint endpoint + * @param {Object} [options] destructuring object + * @param {Object} [options.data] option data + * @param {function} [options.success] success callback + * @param {function} [options.error] error callback * @internal */ -function call (method, endpoint, options) { +function call(method, endpoint, options) { if ((method === 'post' || method === 'delete') && OC.PasswordConfirmation.requiresPasswordConfirmation()) { - OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(call, this, method, endpoint, options)); - return; + OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(call, this, method, endpoint, options)) + return } - options = options || {}; + options = options || {} $.ajax({ type: method.toUpperCase(), url: OC.linkToOCS('apps/provisioning_api/api/v1', 2) + 'config/apps' + endpoint, data: options.data || {}, success: options.success, error: options.error - }); + }) } /** - * @param {Object} [options] - * @param {function} [options.success] + * @param {Object} [options] destructuring object + * @param {function} [options.success] success callback * @since 11.0.0 */ -export function getApps (options) { - call('get', '', options); +export function getApps(options) { + call('get', '', options) } /** - * @param {string} app - * @param {Object} [options] - * @param {function} [options.success] - * @param {function} [options.error] + * @param {string} app app id + * @param {Object} [options] destructuring object + * @param {function} [options.success] success callback + * @param {function} [options.error] error callback * @since 11.0.0 */ -export function getKeys (app, options) { - call('get', '/' + app, options); +export function getKeys(app, options) { + call('get', '/' + app, options) } /** - * @param {string} app - * @param {string} key - * @param {string|function} defaultValue - * @param {Object} [options] - * @param {function} [options.success] - * @param {function} [options.error] + * @param {string} app app id + * @param {string} key key + * @param {string|function} defaultValue default value + * @param {Object} [options] destructuring object + * @param {function} [options.success] success callback + * @param {function} [options.error] error callback * @since 11.0.0 */ -export function getValue (app, key, defaultValue, options) { - options = options || {}; +export function getValue(app, key, defaultValue, options) { + options = options || {} options.data = { defaultValue: defaultValue - }; + } - call('get', '/' + app + '/' + key, options); + call('get', '/' + app + '/' + key, options) } /** - * @param {string} app - * @param {string} key - * @param {string} value - * @param {Object} [options] - * @param {function} [options.success] - * @param {function} [options.error] + * @param {string} app app id + * @param {string} key key + * @param {string} value value + * @param {Object} [options] destructuring object + * @param {function} [options.success] success callback + * @param {function} [options.error] error callback * @since 11.0.0 */ -export function setValue (app, key, value, options) { - options = options || {}; +export function setValue(app, key, value, options) { + options = options || {} options.data = { value: value - }; + } - call('post', '/' + app + '/' + key, options); + call('post', '/' + app + '/' + key, options) } /** - * @param {string} app - * @param {string} key - * @param {Object} [options] - * @param {function} [options.success] - * @param {function} [options.error] + * @param {string} app app id + * @param {string} key key + * @param {Object} [options] destructuring object + * @param {function} [options.success] success callback + * @param {function} [options.error] error callback * @since 11.0.0 */ -export function deleteKey (app, key, options) { - call('delete', '/' + app + '/' + key, options); +export function deleteKey(app, key, options) { + call('delete', '/' + app + '/' + key, options) } diff --git a/core/src/OCP/collaboration.js b/core/src/OCP/collaboration.js index 162fe8e2a7561c3dc7a3ffebf7aba1af7828feae..4eea517f3aa12f59ee8b952990e60c86700ee826 100644 --- a/core/src/OCP/collaboration.js +++ b/core/src/OCP/collaboration.js @@ -1,4 +1,4 @@ -/* +/** * @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> * * @author Julius Härtl <jus@bitgrid.net> @@ -30,13 +30,14 @@ /** * @type {TypeDefinition[]} **/ -let types = {}; +let types = {} /** * Those translations will be used by the vue component but they should be shipped with the server * FIXME: Those translations should be added to the library + * @returns {Array} */ -const l10nProjects = () => { +export const l10nProjects = () => { return [ t('core', 'Add to a project'), t('core', 'Show details'), @@ -47,32 +48,32 @@ const l10nProjects = () => { t('core', 'Failed to add the item to the project'), t('core', 'Connect items to a project to make them easier to find'), t('core', 'Type to search for existing projects') - ]; + ] } export default { /** * - * @param type - * @param {TypeDefinition} typeDefinition + * @param {string} type type + * @param {TypeDefinition} typeDefinition typeDefinition */ registerType(type, typeDefinition) { - types[type] = typeDefinition; + types[type] = typeDefinition }, trigger(type) { return types[type].action() }, getTypes() { - return Object.keys(types); + return Object.keys(types) }, getIcon(type) { - return types[type].typeIconClass || ''; + return types[type].typeIconClass || '' }, getLabel(type) { return escapeHTML(types[type].typeString || type) }, getLink(type, id) { /* TODO: Allow action to be executed instead of href as well */ - return typeof types[type] !== 'undefined' ? types[type].link(id) : ''; + return typeof types[type] !== 'undefined' ? types[type].link(id) : '' } -}; +} diff --git a/core/src/OCP/comments.js b/core/src/OCP/comments.js index 1500e1937c3e58a51fedf5be6132f0f952cb620c..2e12accddceadd48daa6ef5fe59c45ed5f704cb8 100644 --- a/core/src/OCP/comments.js +++ b/core/src/OCP/comments.js @@ -18,34 +18,34 @@ import $ from 'jquery' * The downside: anything not ascii is excluded. Not sure how common it is in areas using different * alphabets… the upside: fake domains with similar looking characters won't be formatted as links */ -const urlRegex = /(\s|^)(https?:\/\/)?((?:[-A-Z0-9+_]+\.)+[-A-Z]+(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\s|$)/ig; +const urlRegex = /(\s|^)(https?:\/\/)?((?:[-A-Z0-9+_]+\.)+[-A-Z]+(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\s|$)/ig -export function plainToRich (content) { - return this.formatLinksRich(content); +export function plainToRich(content) { + return this.formatLinksRich(content) } -export function richToPlain (content) { - return this.formatLinksPlain(content); +export function richToPlain(content) { + return this.formatLinksPlain(content) } -export function formatLinksRich (content) { - return content.replace(urlRegex, function (_, leadingSpace, protocol, url, trailingSpace) { - let linkText = url; +export function formatLinksRich(content) { + return content.replace(urlRegex, function(_, leadingSpace, protocol, url, trailingSpace) { + let linkText = url if (!protocol) { - protocol = 'https://'; + protocol = 'https://' } else if (protocol === 'http://') { - linkText = protocol + url; + linkText = protocol + url } - return leadingSpace + '<a class="external" target="_blank" rel="noopener noreferrer" href="' + protocol + url + '">' + linkText + '</a>' + trailingSpace; - }); + return leadingSpace + '<a class="external" target="_blank" rel="noopener noreferrer" href="' + protocol + url + '">' + linkText + '</a>' + trailingSpace + }) } -export function formatLinksPlain (content) { - const $content = $('<div></div>').html(content); - $content.find('a').each(function () { - const $this = $(this); - $this.html($this.attr('href')); - }); - return $content.html(); +export function formatLinksPlain(content) { + const $content = $('<div></div>').html(content) + $content.find('a').each(function() { + const $this = $(this) + $this.html($this.attr('href')) + }) + return $content.html() } diff --git a/core/src/OCP/index.js b/core/src/OCP/index.js index a4d8f46b88d10230b7f12c5cedafb2dd7863db7a..7d5edd5a9d00ebc2fef04adc462f8a21fef188b8 100644 --- a/core/src/OCP/index.js +++ b/core/src/OCP/index.js @@ -18,4 +18,4 @@ export default { Loader, Toast, WhatsNew -}; +} diff --git a/core/src/OCP/initialstate.js b/core/src/OCP/initialstate.js index af5dd91e3f994788ace2454d3b44a4b200d56dd8..0a6f364ed000e0f8ed44fa64199ef039989cd3df 100644 --- a/core/src/OCP/initialstate.js +++ b/core/src/OCP/initialstate.js @@ -24,19 +24,19 @@ * @namespace OCP.InitialState */ -export function loadState (app, key) { - const elem = document.querySelector(`#initial-state-${app}-${key}`); +export function loadState(app, key) { + const elem = document.querySelector(`#initial-state-${app}-${key}`) if (elem === null) { const msg = `Could not find initial state ${key} of ${app}` - console.debug(msg); - throw new Error(msg); + console.debug(msg) + throw new Error(msg) } try { - return JSON.parse(atob(elem.value)); + return JSON.parse(atob(elem.value)) } catch (e) { const msg = `Could not parse initial state ${key} of ${app}` - console.debug(msg); - throw new Error(msg); + console.debug(msg) + throw new Error(msg) } } diff --git a/core/src/OCP/loader.js b/core/src/OCP/loader.js index 9f551b90a1f1feb592f9e68f1eb6b13d7b606b88..7f72c47ca64597a46383af8f62eb96d2efa7fbf2 100644 --- a/core/src/OCP/loader.js +++ b/core/src/OCP/loader.js @@ -1,4 +1,4 @@ -/* +/** * @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> * * @author Julius Härtl <jus@bitgrid.net> @@ -20,61 +20,60 @@ * */ -let loadedScripts = {}; -let loadedStylesheets = {}; +let loadedScripts = {} +let loadedStylesheets = {} /** * @namespace OCP * @class Loader */ export default { - /** * Load a script asynchronously * - * @param {string} app - * @param {string} file + * @param {string} app the app name + * @param {string} file the script file name * @returns {Promise} */ loadScript: function(app, file) { - const key = app + file; + const key = app + file if (loadedScripts.hasOwnProperty(key)) { - return Promise.resolve(); + return Promise.resolve() } - loadedScripts[key] = true; - return new Promise(function (resolve, reject) { - var scriptPath = OC.filePath(app, 'js', file); - var script = document.createElement('script'); - script.src = scriptPath; - script.setAttribute('nonce', btoa(OC.requestToken)); - script.onload = () => resolve(); - script.onerror = () => reject(`Failed to load script from ${scriptPath}`); - document.head.appendChild(script); - }); + loadedScripts[key] = true + return new Promise(function(resolve, reject) { + var scriptPath = OC.filePath(app, 'js', file) + var script = document.createElement('script') + script.src = scriptPath + script.setAttribute('nonce', btoa(OC.requestToken)) + script.onload = () => resolve() + script.onerror = () => reject(new Error(`Failed to load script from ${scriptPath}`)) + document.head.appendChild(script) + }) }, /** * Load a stylesheet file asynchronously * - * @param {string} app - * @param {string} file + * @param {string} app the app name + * @param {string} file the script file name * @returns {Promise} */ loadStylesheet: function(app, file) { - const key = app + file; + const key = app + file if (loadedStylesheets.hasOwnProperty(key)) { - return Promise.resolve(); + return Promise.resolve() } - loadedStylesheets[key] = true; - return new Promise(function (resolve, reject) { - var stylePath = OC.filePath(app, 'css', file); - var link = document.createElement('link'); - link.href = stylePath; - link.type = 'text/css'; - link.rel = 'stylesheet'; - link.onload = () => resolve(); - link.onerror = () => reject(`Failed to load stylesheet from ${stylePath}`); - document.head.appendChild(link); - }); - }, + loadedStylesheets[key] = true + return new Promise(function(resolve, reject) { + var stylePath = OC.filePath(app, 'css', file) + var link = document.createElement('link') + link.href = stylePath + link.type = 'text/css' + link.rel = 'stylesheet' + link.onload = () => resolve() + link.onerror = () => reject(new Error(`Failed to load stylesheet from ${stylePath}`)) + document.head.appendChild(link) + }) + } } diff --git a/core/src/OCP/toast.js b/core/src/OCP/toast.js index 113d37ab412c3b34fc6b85badc88e1ee5660b8b4..58f98d93480cdc14414bb3e5be67713a8823a867 100644 --- a/core/src/OCP/toast.js +++ b/core/src/OCP/toast.js @@ -33,34 +33,34 @@ const TOAST_TYPE_CLASES = { const Toast = { success(text, options = {}) { - options.type = 'success'; + options.type = 'success' return this.message(text, options) }, warning(text, options = {}) { - options.type = 'warning'; + options.type = 'warning' return this.message(text, options) }, error(text, options = {}) { - options.type = 'error'; + options.type = 'error' return this.message(text, options) }, info(text, options = {}) { - options.type = 'info'; + options.type = 'info' return this.message(text, options) }, message(text, options) { - options = options || {}; + options = options || {} _.defaults(options, { timeout: 7, isHTML: false, type: undefined, close: true, callback: () => {} - }); + }) if (!options.isHTML) { text = $('<div/>').text(text).html() } @@ -71,7 +71,7 @@ const Toast = { const toast = Toastify({ text: text, - duration: options.timeout ? options.timeout*1000 : null, + duration: options.timeout ? options.timeout * 1000 : null, callback: options.callback, close: options.close, gravity: 'top', @@ -82,7 +82,7 @@ const Toast = { }) toast.showToast() // add toastify object to the element for reference in legacy OC.Notification - toast.toastElement.toastify = toast; + toast.toastElement.toastify = toast return toast } } diff --git a/core/src/OCP/whatsnew.js b/core/src/OCP/whatsnew.js index 837cb23217c0e82b5ca6596dabb142dd0c9afebb..c2935c5e78cc275c264a40930457b98639b4cae4 100644 --- a/core/src/OCP/whatsnew.js +++ b/core/src/OCP/whatsnew.js @@ -12,121 +12,121 @@ import $ from 'jquery' import OC from '../OC/index' -export function query (options) { - options = options || {}; - var dismissOptions = options.dismiss || {}; +export function query(options) { + options = options || {} + var dismissOptions = options.dismiss || {} $.ajax({ type: 'GET', url: options.url || OC.linkToOCS('core', 2) + 'whatsnew?format=json', - success: options.success || function (data, statusText, xhr) { - onQuerySuccess(data, statusText, xhr, dismissOptions); + success: options.success || function(data, statusText, xhr) { + onQuerySuccess(data, statusText, xhr, dismissOptions) }, error: options.error || onQueryError - }); + }) } -export function dismiss (version, options) { - options = options || {}; +export function dismiss(version, options) { + options = options || {} $.ajax({ type: 'POST', url: options.url || OC.linkToOCS('core', 2) + 'whatsnew', - data: {version: encodeURIComponent(version)}, + data: { version: encodeURIComponent(version) }, success: options.success || onDismissSuccess, error: options.error || onDismissError - }); + }) // remove element immediately - $('.whatsNewPopover').remove(); + $('.whatsNewPopover').remove() } -function onQuerySuccess (data, statusText, xhr, dismissOptions) { - console.debug('querying Whats New data was successful: ' + statusText); - console.debug(data); +function onQuerySuccess(data, statusText, xhr, dismissOptions) { + console.debug('querying Whats New data was successful: ' + statusText) + console.debug(data) if (xhr.status !== 200) { - return; + return } - var item, menuItem, text, icon; + var item, menuItem, text, icon - var div = document.createElement('div'); - div.classList.add('popovermenu', 'open', 'whatsNewPopover', 'menu-left'); + var div = document.createElement('div') + div.classList.add('popovermenu', 'open', 'whatsNewPopover', 'menu-left') - var list = document.createElement('ul'); + var list = document.createElement('ul') // header - item = document.createElement('li'); - menuItem = document.createElement('span'); - menuItem.className = "menuitem"; - - text = document.createElement('span'); - text.innerText = t('core', 'New in') + ' ' + data['ocs']['data']['product']; - text.className = 'caption'; - menuItem.appendChild(text); - - icon = document.createElement('span'); - icon.className = 'icon-close'; - icon.onclick = function () { - dismiss(data['ocs']['data']['version'], dismissOptions); - }; - menuItem.appendChild(icon); + item = document.createElement('li') + menuItem = document.createElement('span') + menuItem.className = 'menuitem' + + text = document.createElement('span') + text.innerText = t('core', 'New in') + ' ' + data['ocs']['data']['product'] + text.className = 'caption' + menuItem.appendChild(text) + + icon = document.createElement('span') + icon.className = 'icon-close' + icon.onclick = function() { + dismiss(data['ocs']['data']['version'], dismissOptions) + } + menuItem.appendChild(icon) - item.appendChild(menuItem); - list.appendChild(item); + item.appendChild(menuItem) + list.appendChild(item) // Highlights for (var i in data['ocs']['data']['whatsNew']['regular']) { - var whatsNewTextItem = data['ocs']['data']['whatsNew']['regular'][i]; - item = document.createElement('li'); + var whatsNewTextItem = data['ocs']['data']['whatsNew']['regular'][i] + item = document.createElement('li') - menuItem = document.createElement('span'); - menuItem.className = "menuitem"; + menuItem = document.createElement('span') + menuItem.className = 'menuitem' - icon = document.createElement('span'); - icon.className = 'icon-checkmark'; - menuItem.appendChild(icon); + icon = document.createElement('span') + icon.className = 'icon-checkmark' + menuItem.appendChild(icon) - text = document.createElement('p'); - text.innerHTML = _.escape(whatsNewTextItem); - menuItem.appendChild(text); + text = document.createElement('p') + text.innerHTML = _.escape(whatsNewTextItem) + menuItem.appendChild(text) - item.appendChild(menuItem); - list.appendChild(item); + item.appendChild(menuItem) + list.appendChild(item) } // Changelog URL if (!_.isUndefined(data['ocs']['data']['changelogURL'])) { - item = document.createElement('li'); + item = document.createElement('li') - menuItem = document.createElement('a'); - menuItem.href = data['ocs']['data']['changelogURL']; - menuItem.rel = 'noreferrer noopener'; - menuItem.target = '_blank'; + menuItem = document.createElement('a') + menuItem.href = data['ocs']['data']['changelogURL'] + menuItem.rel = 'noreferrer noopener' + menuItem.target = '_blank' - icon = document.createElement('span'); - icon.className = 'icon-link'; - menuItem.appendChild(icon); + icon = document.createElement('span') + icon.className = 'icon-link' + menuItem.appendChild(icon) - text = document.createElement('span'); - text.innerText = t('core', 'View changelog'); - menuItem.appendChild(text); + text = document.createElement('span') + text.innerText = t('core', 'View changelog') + menuItem.appendChild(text) - item.appendChild(menuItem); - list.appendChild(item); + item.appendChild(menuItem) + list.appendChild(item) } - div.appendChild(list); - document.body.appendChild(div); + div.appendChild(list) + document.body.appendChild(div) } -function onQueryError (x, t, e) { - console.debug('querying Whats New Data resulted in an error: ' + t + e); - console.debug(x); +function onQueryError(x, t, e) { + console.debug('querying Whats New Data resulted in an error: ' + t + e) + console.debug(x) } -function onDismissSuccess (data) { - //noop +function onDismissSuccess(data) { + // noop } -function onDismissError (data) { - console.debug('dismissing Whats New data resulted in an error: ' + data); -} \ No newline at end of file +function onDismissError(data) { + console.debug('dismissing Whats New data resulted in an error: ' + data) +} diff --git a/core/src/Polyfill/console.js b/core/src/Polyfill/console.js index 72a1165e43777eaa3090d428823a489a15cdf76d..faa32c457c6c5a79c6c093a411930b7db9b90c11 100644 --- a/core/src/Polyfill/console.js +++ b/core/src/Polyfill/console.js @@ -1,4 +1,5 @@ -/* +/* eslint-disable no-console */ +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -19,13 +20,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -if (typeof console === "undefined" || typeof console.log === "undefined") { +if (typeof console === 'undefined' || typeof console.log === 'undefined') { if (!window.console) { - window.console = {}; + window.console = {} } - const noOp = () => {}; - const methods = ['log', 'debug', 'warn', 'info', 'error', 'assert', 'time', 'timeEnd']; + const noOp = () => {} + const methods = ['log', 'debug', 'warn', 'info', 'error', 'assert', 'time', 'timeEnd'] for (var i = 0; i < methods.length; i++) { - console[methods[i]] = noOp; + console[methods[i]] = noOp } } diff --git a/core/src/Polyfill/tooltip.js b/core/src/Polyfill/tooltip.js index 0ad3686d016742b0970cbcd3a711efaad2e6380c..08cc58c338c672bbb977cdd3ad95a14304d759e4 100644 --- a/core/src/Polyfill/tooltip.js +++ b/core/src/Polyfill/tooltip.js @@ -22,16 +22,16 @@ $.prototype.tooltip = (function(tooltip) { return function(config) { try { - return tooltip.call(this, config); + return tooltip.call(this, config) } catch (ex) { if (ex instanceof TypeError && config === 'destroy') { - console.error('Deprecated call $.tooltip(\'destroy\') has been deprecated and should be removed'); - return tooltip.call(this, 'dispose'); + console.error('Deprecated call $.tooltip(\'destroy\') has been deprecated and should be removed') + return tooltip.call(this, 'dispose') } if (ex instanceof TypeError && config === 'fixTitle') { - console.error('Deprecated call $.tooltip(\'fixTitle\') has been deprecated and should be removed'); - return tooltip.call(this, '_fixTitle'); + console.error('Deprecated call $.tooltip(\'fixTitle\') has been deprecated and should be removed') + return tooltip.call(this, '_fixTitle') } } - }; -})($.prototype.tooltip); + } +})($.prototype.tooltip) diff --git a/core/src/Polyfill/windows-phone.js b/core/src/Polyfill/windows-phone.js index 3d59074856bb58febacd93e95a9667f3f6816e83..983e412e45369e50661fbbb1b4b414c3a3f930c5 100644 --- a/core/src/Polyfill/windows-phone.js +++ b/core/src/Polyfill/windows-phone.js @@ -20,10 +20,10 @@ */ // fix device width on windows phone -if ("-ms-user-select" in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/)) { - const msViewportStyle = document.createElement("style"); +if ('-ms-user-select' in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/)) { + const msViewportStyle = document.createElement('style') msViewportStyle.appendChild( - document.createTextNode("@-ms-viewport{width:auto!important}") - ); - document.getElementsByTagName("head")[0].appendChild(msViewportStyle); + document.createTextNode('@-ms-viewport{width:auto!important}') + ) + document.getElementsByTagName('head')[0].appendChild(msViewportStyle) } diff --git a/core/src/Util/escapeHTML.js b/core/src/Util/escapeHTML.js index f6cf868a6d07f59b78b632b6b88ac993dc1765cf..b6596d44d215df7da2fa8a4872cf878140c62605 100644 --- a/core/src/Util/escapeHTML.js +++ b/core/src/Util/escapeHTML.js @@ -22,9 +22,9 @@ /** * Sanitizes a HTML string by replacing all potential dangerous characters with HTML entities * @param {string} s String to sanitize - * @return {string} Sanitized string + * @returns {string} Sanitized string */ -export default function escapeHTML (s) { +export default function escapeHTML(s) { return s.toString() .split('&') .join('&') @@ -32,5 +32,5 @@ export default function escapeHTML (s) { .join('<').split('>') .join('>').split('"') .join('"').split('\'') - .join('''); + .join(''') } diff --git a/core/src/Util/format-date.js b/core/src/Util/format-date.js index 04a2d274de50241d62486d2cac86b4054a7bd30d..9551804d4abfe5df5702d6917229ee04465d3a2b 100644 --- a/core/src/Util/format-date.js +++ b/core/src/Util/format-date.js @@ -25,10 +25,10 @@ import OC from '../OC/index' /** * Format an UNIX timestamp to a human understandable format * @param {number} timestamp UNIX timestamp - * @return {string} Human readable format + * @returns {string} Human readable format * @deprecated 16.0.0 use OC.Util.formatDate instead */ -export default function formatDate (timestamp) { +export default function formatDate(timestamp) { console.warn('formatDate is deprecated, use OC.Util.formatDate instead') - return OC.Util.formatDate(timestamp); + return OC.Util.formatDate(timestamp) } diff --git a/core/src/Util/get-url-parameter.js b/core/src/Util/get-url-parameter.js index 6051e923e9de8391c5f28369dfe4c618faf33430..6f809994f101c131bf27f3689bc16e7870a987c9 100644 --- a/core/src/Util/get-url-parameter.js +++ b/core/src/Util/get-url-parameter.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -23,10 +23,11 @@ * Get the value of a URL parameter * @link http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery * @param {string} name URL parameter - * @return {string} + * @returns {string} */ -export default function getURLParameter (name) { +export default function getURLParameter(name) { return decodeURIComponent( + // eslint-disable-next-line no-sparse-arrays (new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [, ''])[1].replace(/\+/g, '%20') - ) || ''; + ) || '' } diff --git a/core/src/Util/human-file-size.js b/core/src/Util/human-file-size.js index e671a86d053a51b149e3da8a2e20177c1ba7b669..586ef0e92675001e1798315623dd431b9228cf71 100644 --- a/core/src/Util/human-file-size.js +++ b/core/src/Util/human-file-size.js @@ -23,29 +23,29 @@ * Returns a human readable file size * @param {number} size Size in bytes * @param {boolean} skipSmallSizes return '< 1 kB' for small files - * @return {string} + * @returns {string} */ -export default function humanFileSize (size, skipSmallSizes) { - var humanList = ['B', 'KB', 'MB', 'GB', 'TB']; +export default function humanFileSize(size, skipSmallSizes) { + var humanList = ['B', 'KB', 'MB', 'GB', 'TB'] // Calculate Log with base 1024: size = 1024 ** order - var order = size > 0 ? Math.floor(Math.log(size) / Math.log(1024)) : 0; + var order = size > 0 ? Math.floor(Math.log(size) / Math.log(1024)) : 0 // Stay in range of the byte sizes that are defined - order = Math.min(humanList.length - 1, order); - var readableFormat = humanList[order]; - var relativeSize = (size / Math.pow(1024, order)).toFixed(1); + order = Math.min(humanList.length - 1, order) + var readableFormat = humanList[order] + var relativeSize = (size / Math.pow(1024, order)).toFixed(1) if (skipSmallSizes === true && order === 0) { - if (relativeSize !== "0.0") { - return '< 1 KB'; + if (relativeSize !== '0.0') { + return '< 1 KB' } else { - return '0 KB'; + return '0 KB' } } if (order < 2) { - relativeSize = parseFloat(relativeSize).toFixed(0); + relativeSize = parseFloat(relativeSize).toFixed(0) } else if (relativeSize.substr(relativeSize.length - 2, 2) === '.0') { - relativeSize = relativeSize.substr(0, relativeSize.length - 2); + relativeSize = relativeSize.substr(0, relativeSize.length - 2) } else { - relativeSize = parseFloat(relativeSize).toLocaleString(OC.getCanonicalLocale()); + relativeSize = parseFloat(relativeSize).toLocaleString(OC.getCanonicalLocale()) } - return relativeSize + ' ' + readableFormat; + return relativeSize + ' ' + readableFormat } diff --git a/core/src/Util/relative-modified-date.js b/core/src/Util/relative-modified-date.js index 3837d2c372e9a05cfb735132305d2211fe62eb69..4e41f939250776af74a4ddfb26778a05a8c9adb5 100644 --- a/core/src/Util/relative-modified-date.js +++ b/core/src/Util/relative-modified-date.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -24,14 +24,16 @@ import OC from '../OC/index' /** * Takes an absolute timestamp and return a string with a human-friendly relative date + * * @param {number} timestamp A Unix timestamp * @deprecated use OC.Util.relativeModifiedDate instead but beware the argument value + * @returns {string} */ -export default function relative_modified_date (timestamp) { - console.warn('relative_modified_date is deprecated, use OC.Util.relativeModifiedDate instead') +export default function relativeModifiedDate(timestamp) { + console.warn('relativeModifiedDate is deprecated, use OC.Util.relativeModifiedDate instead') /* Were multiplying by 1000 to bring the timestamp back to its original value per https://github.com/owncloud/core/pull/10647#discussion_r16790315 */ - return OC.Util.relativeModifiedDate(timestamp * 1000); + return OC.Util.relativeModifiedDate(timestamp * 1000) } diff --git a/core/src/components/ContactsMenu.js b/core/src/components/ContactsMenu.js index 8243b9ca1f26a3b9cdb087d6f4d23ba7d9cc78fb..661b6d8e7c6b4e5c8bc4a33ecd97335ad91a985b 100644 --- a/core/src/components/ContactsMenu.js +++ b/core/src/components/ContactsMenu.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -20,13 +20,13 @@ */ import $ from 'jquery' - import OC from '../OC' /** * @todo move to contacts menu code https://github.com/orgs/nextcloud/projects/31#card-21213129 */ export const setUp = () => { + // eslint-disable-next-line no-new new OC.ContactsMenu({ el: $('#contactsmenu .menu'), trigger: $('#contactsmenu .menutoggle') diff --git a/core/src/components/login/LoginForm.vue b/core/src/components/login/LoginForm.vue index 3ba13b4f02588b8ff71824830474c2cfe36b7866..e9f44cccb8f58fef06613a34a8c4f66dcbba750e 100644 --- a/core/src/components/login/LoginForm.vue +++ b/core/src/components/login/LoginForm.vue @@ -21,195 +21,195 @@ <template> <form method="post" - name="login" - :action="OC.generateUrl('login')" - @submit="submit"> + name="login" + :action="OC.generateUrl('login')" + @submit="submit"> <fieldset> <div v-if="apacheAuthFailed" - class="warning"> + class="warning"> {{ t('core', 'Server side authentication failed!') }}<br> <small>{{ t('core', 'Please contact your administrator.') }} </small> </div> - <div v-for="message in messages" - class="warning"> + <div v-for="(message, index) in messages" + :key="index" + class="warning"> {{ message }}<br> </div> <div v-if="internalException" - class="warning"> + class="warning"> {{ t('core', 'An internal error occurred.') }}<br> <small>{{ t('core', 'Please try again or contact your administrator.') }} </small> </div> <div id="message" - class="hidden"> - <img class="float-spinner" alt="" - :src="OC.imagePath('core', 'loading-dark.gif')"> - <span id="messageText"></span> + class="hidden"> + <img class="float-spinner" + alt="" + :src="OC.imagePath('core', 'loading-dark.gif')"> + <span id="messageText" /> <!-- the following div ensures that the spinner is always inside the #message div --> - <div style="clear: both;"></div> + <div style="clear: both;" /> </div> <p class="grouptop" - :class="{shake: invalidPassword}"> - <input type="text" - name="user" - id="user" - ref="user" - :autocomplete="autoCompleteAllowed ? 'on' : 'off'" - :placeholder="t('core', 'Username or email')" - :aria-label="t('core', 'Username or email')" - v-model="user" - @change="updateUsername" - required> + :class="{shake: invalidPassword}"> + <input id="user" + ref="user" + v-model="user" + type="text" + name="user" + :autocomplete="autoCompleteAllowed ? 'on' : 'off'" + :placeholder="t('core', 'Username or email')" + :aria-label="t('core', 'Username or email')" + required + @change="updateUsername"> <label for="user" class="infield">{{ t('core', 'Username or email') }}</label> </p> <p class="groupbottom" - :class="{shake: invalidPassword}"> - <input :type="passwordInputType" - class="password-with-toggle" - name="password" - id="password" - ref="password" - :autocomplete="autoCompleteAllowed ? 'on' : 'off'" - :placeholder="t('core', 'Password')" - :aria-label="t('core', 'Password')" - required> + :class="{shake: invalidPassword}"> + <input id="password" + ref="password" + :type="passwordInputType" + class="password-with-toggle" + name="password" + :autocomplete="autoCompleteAllowed ? 'on' : 'off'" + :placeholder="t('core', 'Password')" + :aria-label="t('core', 'Password')" + required> <label for="password" - class="infield">{{ t('Password') }}</label> - <a href="#" @click.stop.prevent="togglePassword" class="toggle-password"> - <img :src="OC.imagePath('core', 'actions/toggle.svg')"/> + class="infield">{{ t('Password') }}</label> + <a href="#" class="toggle-password" @click.stop.prevent="togglePassword"> + <img :src="OC.imagePath('core', 'actions/toggle.svg')"> </a> </p> <div id="submit-wrapper"> - <input type="submit" - class="login primary" - name="submit" - id="submit-form" - title="" - :value="!loading ? t('core', 'Log in') : t('core', 'Logging in …')" /> + <input id="submit-form" + type="submit" + class="login primary" + title="" + :value="!loading ? t('core', 'Log in') : t('core', 'Logging in …')"> <div class="submit-icon" - :class="{ - 'icon-confirm-white': !loading, - 'icon-loading-small': loading && invertedColors, - 'icon-loading-small-dark': loading && !invertedColors, - }"></div> + :class="{ + 'icon-confirm-white': !loading, + 'icon-loading-small': loading && invertedColors, + 'icon-loading-small-dark': loading && !invertedColors, + }" /> </div> <p v-if="invalidPassword" - class="warning wrongPasswordMsg"> + class="warning wrongPasswordMsg"> {{ t('core', 'Wrong username or password.') }} </p> <p v-else-if="userDisabled" - class="warning userDisabledMsg"> + class="warning userDisabledMsg"> {{ t('lib', 'User disabled') }} </p> <p v-if="throttleDelay && throttleDelay > 5000" - class="warning throttledMsg"> + class="warning throttledMsg"> {{ t('core', 'We have detected multiple invalid login attempts from your IP. Therefore your next login is throttled up to 30 seconds.') }} </p> <input v-if="redirectUrl" - type="hidden" - name="redirect_url" - :value="redirectUrl"> + type="hidden" + name="redirect_url" + :value="redirectUrl"> <input type="hidden" - name="timezone" - :value="timezone" /> + name="timezone" + :value="timezone"> <input type="hidden" - name="timezone_offset" - :value="timezoneOffset"/> + name="timezone_offset" + :value="timezoneOffset"> <input type="hidden" - name="requesttoken" - :value="OC.requestToken"> + name="requesttoken" + :value="OC.requestToken"> </fieldset> </form> </template> <script> - import jstz from 'jstimezonedetect' +import jstz from 'jstimezonedetect' - export default { - name: 'LoginForm', - props: { - username: { - type: String, - default: '', - }, - redirectUrl: { - type: String, - }, - errors: { - type: Array, - default: () => [], - }, - messages: { - type: Array, - default: () => [], - }, - throttleDelay: { - type: Number, - }, - invertedColors: { - type: Boolean, - default: false, - }, - autoCompleteAllowed: { - type: Boolean, - default: true, - }, +export default { + name: 'LoginForm', + props: { + username: { + type: String, + default: '' }, - data () { - return { - loading: false, - timezone: jstz.determine().name(), - timezoneOffset: (-new Date().getTimezoneOffset() / 60), - user: this.username, - password: '', - passwordInputType: 'password', - } + redirectUrl: { + type: String + }, + errors: { + type: Array, + default: () => [] + }, + messages: { + type: Array, + default: () => [] + }, + throttleDelay: { + type: Number }, - computed: { - apacheAuthFailed () { - return this.errors.indexOf('apacheAuthFailed') !== -1 - }, - internalException () { - return this.errors.indexOf('internalexception') !== -1 - }, - invalidPassword () { - return this.errors.indexOf('invalidpassword') !== -1 - }, - userDisabled () { - return this.errors.indexOf('userdisabled') !== -1 - }, + invertedColors: { + type: Boolean, + default: false + }, + autoCompleteAllowed: { + type: Boolean, + default: true + } + }, + data() { + return { + loading: false, + timezone: jstz.determine().name(), + timezoneOffset: (-new Date().getTimezoneOffset() / 60), + user: this.username, + password: '', + passwordInputType: 'password' + } + }, + computed: { + apacheAuthFailed() { + return this.errors.indexOf('apacheAuthFailed') !== -1 }, - mounted () { - if (this.username === '') { - this.$refs.user.focus() + internalException() { + return this.errors.indexOf('internalexception') !== -1 + }, + invalidPassword() { + return this.errors.indexOf('invalidpassword') !== -1 + }, + userDisabled() { + return this.errors.indexOf('userdisabled') !== -1 + } + }, + mounted() { + if (this.username === '') { + this.$refs.user.focus() + } else { + this.$refs.password.focus() + } + }, + methods: { + togglePassword() { + if (this.passwordInputType === 'password') { + this.passwordInputType = 'text' } else { - this.$refs.password.focus() + this.passwordInputType = 'password' } }, - methods: { - togglePassword () { - if(this.passwordInputType === 'password'){ - this.passwordInputType = 'text' - } - else{ - this.passwordInputType = 'password' - } - }, - updateUsername () { - this.$emit('update:username', this.user) - }, - submit () { - this.loading = true - this.$emit('submit') - } + updateUsername() { + this.$emit('update:username', this.user) + }, + submit() { + this.loading = true + this.$emit('submit') } } +} </script> <style scoped> diff --git a/core/src/components/login/ResetPassword.vue b/core/src/components/login/ResetPassword.vue index ac152342d4f6dafd1d4427b8a3bf5672bdb6fa49..0fd50e43ea917435ead2f0094b3c8e3713444880 100644 --- a/core/src/components/login/ResetPassword.vue +++ b/core/src/components/login/ResetPassword.vue @@ -22,125 +22,123 @@ <template> <form @submit.prevent="submit"> <p> - <input type="text" - name="user" - id="user" - :placeholder="t('core', 'Username or email')" - :aria-label="t('core', 'Username or email')" - v-model="user" - @change="updateUsername" - required> + <input id="user" + v-model="user" + type="text" + name="user" + :placeholder="t('core', 'Username or email')" + :aria-label="t('core', 'Username or email')" + required + @change="updateUsername"> <!--<?php p($_['user_autofocus'] ? 'autofocus' : ''); ?> autocomplete="<?php p($_['login_form_autocomplete']); ?>" autocapitalize="none" autocorrect="off"--> <label for="user" class="infield">{{ t('core', 'Username or email') }}</label> </p> <div id="reset-password-wrapper"> - <input type="submit" - id="reset-password-submit" - class="login primary" - title="" - :value="t('core', 'Reset password')"/> + <input id="reset-password-submit" + type="submit" + class="login primary" + title="" + :value="t('core', 'Reset password')"> <div class="submit-icon" - :class="{ - 'icon-confirm-white': !loading, - 'icon-loading-small': loading && invertedColors, - 'icon-loading-small-dark': loading && !invertedColors, - }"></div> + :class="{ + 'icon-confirm-white': !loading, + 'icon-loading-small': loading && invertedColors, + 'icon-loading-small-dark': loading && !invertedColors, + }" /> </div> <p v-if="message === 'send-success'" - class="update"> + class="update"> {{ t('core', 'A password reset message has been sent to the e-mail address of this account. If you do not receive it, check your spam/junk folders or ask your local administrator for help.') }} <br> {{ t('core', 'If it is not there ask your local administrator.') }} </p> <p v-else-if="message === 'send-error'" - class="update warning"> + class="update warning"> {{ t('core', 'Couldn\'t send reset email. Please contact your administrator.') }} </p> <p v-else-if="message === 'reset-error'" - class="update warning"> + class="update warning"> {{ t('core', 'Password can not be changed. Please contact your administrator.') }} </p> <p v-else-if="message" - class="update" - :class="{warning: error}"> - - </p> + class="update" + :class="{warning: error}" /> <a href="#" - @click.prevent="$emit('abort')"> + @click.prevent="$emit('abort')"> {{ t('core', 'Back to login') }} </a> </form> </template> <script> - import Axios from 'nextcloud-axios' +import axios from 'nextcloud-axios' - import {generateUrl} from '../../OC/routing' +import { generateUrl } from '../../OC/routing' - export default { - name: 'ResetPassword', - props: { - username: { - type: String, - required: true, - }, - resetPasswordLink: { - type: String, - required: true, - }, - invertedColors: { - type: Boolean, - default: false, - } +export default { + name: 'ResetPassword', + props: { + username: { + type: String, + required: true }, - data() { - return { - error: false, - loading: false, - message: undefined, - user: this.username, - } + resetPasswordLink: { + type: String, + required: true }, - watch: { - username (value) { - this.user = value - } + invertedColors: { + type: Boolean, + default: false + } + }, + data() { + return { + error: false, + loading: false, + message: undefined, + user: this.username + } + }, + watch: { + username(value) { + this.user = value + } + }, + methods: { + updateUsername() { + this.$emit('update:username', this.user) }, - methods: { - updateUsername () { - this.$emit('update:username', this.user) - }, - submit () { - this.loading = true - this.error = false - this.message = '' - const url = generateUrl('/lostpassword/email'); + submit() { + this.loading = true + this.error = false + this.message = '' + const url = generateUrl('/lostpassword/email') - const data = { - user: this.user, - } + const data = { + user: this.user + } - return Axios.post(url, data) - .then(resp => resp.data) - .then(data => { - if (data.status !== 'success') { - throw new Error(`got status ${data.status}`) - } + return axios.post(url, data) + .then(resp => resp.data) + .then(data => { + if (data.status !== 'success') { + throw new Error(`got status ${data.status}`) + } - this.message = 'send-success' - }) - .catch(e => { - console.error('could not send reset e-mail request', e) + this.message = 'send-success' + }) + .catch(e => { + console.error('could not send reset e-mail request', e) - this.error = true - this.message = 'send-error' - }) - .then(() => this.loading = false) - } - }, + this.error = true + this.message = 'send-error' + }) + .then(() => { this.loading = false }) + } } +} </script> <style scoped> diff --git a/core/src/components/login/UpdatePassword.vue b/core/src/components/login/UpdatePassword.vue index 3b5ac32133db5fdd9341cb952a436105c09ef107..c0976bf0d6fffc13f450382a4be46c2b63716e53 100644 --- a/core/src/components/login/UpdatePassword.vue +++ b/core/src/components/login/UpdatePassword.vue @@ -25,32 +25,38 @@ <fieldset> <p> <label for="password" class="infield">{{ t('core', 'New password') }}</label> - <input type="password" name="password" - id="password" v-model="password" required - :placeholder="t('core', 'New password')" /> + <input id="password" + v-model="password" + type="password" + name="password" + required + :placeholder="t('core', 'New password')"> </p> <div v-if="encrypted" class="update"> <p> {{ t('core', 'Your files are encrypted. There will be no way to get your data back after your password is reset. If you are not sure what to do, please contact your administrator before you continue. Do you really want to continue?') }} </p> - <input type="checkbox" class="checkbox" - id="encrypted-continue" v-model="proceed" /> + <input id="encrypted-continue" + v-model="proceed" + type="checkbox" + class="checkbox"> <label for="encrypted-continue"> {{ t('core', 'I know what I\'m doing') }} </label> </div> <div id="submit-wrapper"> - <input type="submit" - id="submit" - class="login primary" - title="" - :value="!loading ? t('core', 'Reset password') : t('core', 'Resetting password')" /> - <div class="submit-icon" :class="{ - 'icon-loading-small': loading && invertedColors, - 'icon-loading-small-dark': loading && !invertedColors - }"></div> + <input id="submit" + type="submit" + class="login primary" + title="" + :value="!loading ? t('core', 'Reset password') : t('core', 'Resetting password')"> + <div class="submit-icon" + :class="{ + 'icon-loading-small': loading && invertedColors, + 'icon-loading-small-dark': loading && !invertedColors + }" /> </div> <p v-if="error && message" :class="{warning: error}"> @@ -61,71 +67,71 @@ </template> <script> - import Axios from 'nextcloud-axios' +import Axios from 'nextcloud-axios' - export default { - name: 'UpdatePassword', - props: { - username: { - type: String, - required: true, - }, - resetPasswordTarget: { - type: String, - required: true, - }, - invertedColors: { - type: Boolean, - default: false, - } +export default { + name: 'UpdatePassword', + props: { + username: { + type: String, + required: true }, - data() { - return { - error: false, - loading: false, - message: undefined, - user: this.username, - password: '', - encrypted: false, - proceed: false - } + resetPasswordTarget: { + type: String, + required: true }, - watch: { - username (value) { - this.user = value - } - }, - methods: { - async submit () { - this.loading = true - this.error = false - this.message = '' + invertedColors: { + type: Boolean, + default: false + } + }, + data() { + return { + error: false, + loading: false, + message: undefined, + user: this.username, + password: '', + encrypted: false, + proceed: false + } + }, + watch: { + username(value) { + this.user = value + } + }, + methods: { + async submit() { + this.loading = true + this.error = false + this.message = '' - try { - const { data } = await Axios.post(this.resetPasswordTarget, { - password: this.password, - proceed: this.proceed - }) - if (data && data.status === 'success') { - this.message = 'send-success' - this.$emit('update:username', this.user) - this.$emit('done') - } else if (data && data.encryption) { - this.encrypted = true - } else if (data && data.msg) { - throw new Error(data.msg) - } else { - throw new Error() - } - } catch (e) { - this.error = true - this.message = e.message ? e.message : t('core', 'Password can not be changed. Please contact your administrator.') - } finally { - this.loading = false + try { + const { data } = await Axios.post(this.resetPasswordTarget, { + password: this.password, + proceed: this.proceed + }) + if (data && data.status === 'success') { + this.message = 'send-success' + this.$emit('update:username', this.user) + this.$emit('done') + } else if (data && data.encryption) { + this.encrypted = true + } else if (data && data.msg) { + throw new Error(data.msg) + } else { + throw new Error() } + } catch (e) { + this.error = true + this.message = e.message ? e.message : t('core', 'Password can not be changed. Please contact your administrator.') + } finally { + this.loading = false } - }, + } } +} </script> <style scoped> diff --git a/core/src/globals.js b/core/src/globals.js index 8f0fe6dbf868857e5d47b9d98114f46ccd8d2526..eb72a9fef30d22d9f01ce07e58d14dfbba25aa1f 100644 --- a/core/src/globals.js +++ b/core/src/globals.js @@ -1,3 +1,4 @@ +/* eslint-disable nextcloud/no-deprecations */ /** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * @@ -19,7 +20,46 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import {initCore} from './init' +import { initCore } from './init' + +import _ from 'underscore' +import $ from 'jquery' +import 'jquery-migrate/dist/jquery-migrate.min' +// TODO: switch to `jquery-ui` package and import widgets and effects individually +// `jquery-ui-dist` is used as a workaround for the issue of missing effects +import 'jquery-ui-dist/jquery-ui' +import 'jquery-ui-dist/jquery-ui.css' +import 'jquery-ui-dist/jquery-ui.theme.css' +// END TODO +import autosize from 'autosize' +import Backbone from 'backbone' +import 'bootstrap/js/dist/tooltip' +import './Polyfill/tooltip' +import ClipboardJS from 'clipboard' +import dav from 'davclient.js' +import DOMPurify from 'dompurify' +import Handlebars from 'handlebars' +import 'jcrop/js/jquery.Jcrop' +import 'jcrop/css/jquery.Jcrop.css' +import jstimezonedetect from 'jstimezonedetect' +import marked from 'marked' +import md5 from 'blueimp-md5' +import moment from 'moment' +import 'select2' +import 'select2/select2.css' +import 'snap.js/dist/snap' +import 'strengthify' +import 'strengthify/strengthify.css' + +import OC from './OC/index' +import OCP from './OCP/index' +import OCA from './OCA/index' +import escapeHTML from './Util/escapeHTML' +import formatDate from './Util/format-date' +import { getToken as getRequestToken } from './OC/requesttoken' +import getURLParameter from './Util/get-url-parameter' +import humanFileSize from './Util/human-file-size' +import relativeModifiedDate from './Util/relative-modified-date' const warnIfNotTesting = function() { if (window.TESTING === undefined) { @@ -28,9 +68,12 @@ const warnIfNotTesting = function() { } /** + * Mark a function as deprecated and automatically + * warn if used! * * @param {Function} func the library to deprecate * @param {String} funcName the name of the library + * @returns {function} */ const deprecate = (func, funcName) => { const oldFunc = func @@ -59,53 +102,12 @@ const setDeprecatedProp = (global, cb, msg) => { }) } -import _ from 'underscore' -import $ from 'jquery' -import 'jquery-migrate/dist/jquery-migrate.min' -// TODO: switch to `jquery-ui` package and import widgets and effects individually -// `jquery-ui-dist` is used as a workaround for the issue of missing effects -import 'jquery-ui-dist/jquery-ui' -import 'jquery-ui-dist/jquery-ui.css' -import 'jquery-ui-dist/jquery-ui.theme.css' -// END TODO -import autosize from 'autosize' -import Backbone from 'backbone' -import 'bootstrap/js/dist/tooltip' -import './Polyfill/tooltip' -import ClipboardJS from 'clipboard' -import cssVars from 'css-vars-ponyfill' -import dav from 'davclient.js' -import DOMPurify from 'dompurify' -import Handlebars from 'handlebars' -import 'jcrop/js/jquery.Jcrop' -import 'jcrop/css/jquery.Jcrop.css' -import jstimezonedetect from 'jstimezonedetect' -import marked from 'marked' -import md5 from 'blueimp-md5' -import moment from 'moment' -import 'select2' -import 'select2/select2.css' -import 'snap.js/dist/snap' -import 'strengthify' -import 'strengthify/strengthify.css' - -import OC from './OC/index' -import OCP from './OCP/index' -import OCA from './OCA/index' -import escapeHTML from './Util/escapeHTML' -import formatDate from './Util/format-date' -import {getToken as getRequestToken} from './OC/requesttoken' -import getURLParameter from './Util/get-url-parameter' -import humanFileSize from './Util/human-file-size' -import relative_modified_date from './Util/relative-modified-date' - window['_'] = _ window['$'] = $ window['autosize'] = autosize window['Backbone'] = Backbone window['Clipboard'] = ClipboardJS window['ClipboardJS'] = ClipboardJS -window['cssVars'] = cssVars window['dav'] = dav setDeprecatedProp('DOMPurify', () => DOMPurify, 'The global DOMPurify is deprecated, ship your own') window['Handlebars'] = Handlebars @@ -118,7 +120,7 @@ window['moment'] = moment window['OC'] = OC setDeprecatedProp('initCore', () => initCore, 'this is an internal function') -setDeprecatedProp('oc_appswebroots', () => OC.appswebroots, 'use OC.appswebroots instead') +setDeprecatedProp('oc_appswebroots', () => OC.appswebroots, 'use OC.appswebroots instead') setDeprecatedProp('oc_capabilities', OC.getCapabilities, 'use OC.getCapabilities instead') setDeprecatedProp('oc_config', () => OC.config, 'use OC.config instead') setDeprecatedProp('oc_current_user', () => OC.getCurrentUser().uid, 'use OC.getCurrentUser().uid instead') @@ -134,7 +136,7 @@ window['escapeHTML'] = deprecate(escapeHTML, 'escapeHTML') window['formatDate'] = deprecate(formatDate, 'formatDate') window['getURLParameter'] = deprecate(getURLParameter, 'getURLParameter') window['humanFileSize'] = deprecate(humanFileSize, 'humanFileSize') -window['relative_modified_date'] = deprecate(relative_modified_date, 'relative_modified_date') +window['relative_modified_date'] = deprecate(relativeModifiedDate, 'relative_modified_date') $.fn.select2 = deprecate($.fn.select2, 'select2') /** @@ -145,7 +147,7 @@ $.fn.select2 = deprecate($.fn.select2, 'select2') * @param {number} [count] number to replace %n with * @return {string} */ -window.t = _.bind(OC.L10N.translate, OC.L10N); +window.t = _.bind(OC.L10N.translate, OC.L10N) /** * translate a string @@ -156,4 +158,4 @@ window.t = _.bind(OC.L10N.translate, OC.L10N); * @param [vars] map of placeholder key to value * @return {string} Translated string */ -window.n = _.bind(OC.L10N.translatePlural, OC.L10N); +window.n = _.bind(OC.L10N.translatePlural, OC.L10N) diff --git a/core/src/init.js b/core/src/init.js index b6cdf319739725cf9c60bd6539255b9701b62e12..ee232e8fdbf4fec59e5cb32116a8aa776095d50e 100644 --- a/core/src/init.js +++ b/core/src/init.js @@ -1,4 +1,5 @@ -/* +/* globals Snap */ +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -22,16 +23,17 @@ import _ from 'underscore' import $ from 'jquery' import moment from 'moment' +import cssVars from 'css-vars-ponyfill' -import {initSessionHeartBeat} from './session-heartbeat' +import { initSessionHeartBeat } from './session-heartbeat' import OC from './OC/index' -import {setUp as setUpContactsMenu} from './components/ContactsMenu' -import {setUp as setUpMainMenu} from './components/MainMenu' -import {setUp as setUpUserMenu} from './components/UserMenu' +import { setUp as setUpContactsMenu } from './components/ContactsMenu' +import { setUp as setUpMainMenu } from './components/MainMenu' +import { setUp as setUpUserMenu } from './components/UserMenu' import PasswordConfirmation from './OC/password-confirmation' // keep in sync with core/css/variables.scss -const breakpoint_mobile_width = 1024; +const breakpointMobileWidth = 1024 const resizeMenu = () => { const appList = $('#appmenu li') @@ -40,7 +42,7 @@ const resizeMenu = () => { const usePercentualAppMenuLimit = 0.33 const minAppsDesktop = 8 let availableWidth = headerWidth - $('#nextcloud').outerWidth() - (rightHeaderWidth > 210 ? rightHeaderWidth : 210) - const isMobile = $(window).width() < breakpoint_mobile_width + const isMobile = $(window).width() < breakpointMobileWidth if (!isMobile) { availableWidth = availableWidth * usePercentualAppMenuLimit } @@ -90,7 +92,7 @@ const resizeMenu = () => { const initLiveTimestamps = () => { // Update live timestamps every 30 seconds setInterval(() => { - $('.live-relative-timestamp').each(function () { + $('.live-relative-timestamp').each(function() { $(this).text(OC.Util.relativeModifiedDate(parseInt($(this).attr('data-timestamp'), 10))) }) }, 30 * 1000) @@ -129,7 +131,7 @@ export const initCore = () => { }) } - $(window).on('unload.main', () => OC._unloadCalled = true) + $(window).on('unload.main', () => { OC._unloadCalled = true }) $(window).on('beforeunload.main', () => { // super-trick thanks to http://stackoverflow.com/a/4651049 // in case another handler displays a confirmation dialog (ex: navigating away @@ -150,14 +152,14 @@ export const initCore = () => { }, 10000) }, 1) }) - $(document).on('ajaxError.main', function (event, request, settings) { + $(document).on('ajaxError.main', function(event, request, settings) { if (settings && settings.allowAuthErrors) { return } OC._processAjaxError(request) }) - initSessionHeartBeat(); + initSessionHeartBeat() OC.registerMenu($('#expand'), $('#expanddiv'), false, true) @@ -184,7 +186,7 @@ export const initCore = () => { const caretPosition = $('.header-appname + .icon-caret').offset().left - 2 if (caretPosition > 255) { // if the app name is longer than the menu, just put the triangle in the middle - return + } else { $('head').append('<style id="menu-css-helper">#navigation:after { left: ' + caretPosition + 'px }</style>') } @@ -235,20 +237,20 @@ export const initCore = () => { $appNavigation.delegate('a, :button', 'click', event => { const $target = $(event.target) // don't hide navigation when changing settings or adding things - if ($target.is('.app-navigation-noclose') || - $target.closest('.app-navigation-noclose').length) { + if ($target.is('.app-navigation-noclose') + || $target.closest('.app-navigation-noclose').length) { return } - if ($target.is('.app-navigation-entry-utils-menu-button') || - $target.closest('.app-navigation-entry-utils-menu-button').length) { + if ($target.is('.app-navigation-entry-utils-menu-button') + || $target.closest('.app-navigation-entry-utils-menu-button').length) { return } - if ($target.is('.add-new') || - $target.closest('.add-new').length) { + if ($target.is('.add-new') + || $target.closest('.add-new').length) { return } - if ($target.is('#app-settings') || - $target.closest('#app-settings').length) { + if ($target.is('#app-settings') + || $target.closest('#app-settings').length) { return } snapper.close() @@ -282,7 +284,7 @@ export const initCore = () => { } const toggleSnapperOnSize = () => { - if ($(window).width() > breakpoint_mobile_width) { + if ($(window).width() > breakpointMobileWidth) { snapper.close() snapper.disable() diff --git a/core/src/jquery/avatar.js b/core/src/jquery/avatar.js index 75aef6d958f8160dbf7ba1c7a3780e87161a07ae..eaedce1461b22d4b101aaa9973278dcdfc0bac09 100644 --- a/core/src/jquery/avatar.js +++ b/core/src/jquery/avatar.js @@ -64,46 +64,46 @@ import OC from '../OC' * */ -$.fn.avatar = function (user, size, ie8fix, hidedefault, callback, displayname) { - var setAvatarForUnknownUser = function (target) { - target.imageplaceholder('?'); - target.css('background-color', '#b9b9b9'); - }; +$.fn.avatar = function(user, size, ie8fix, hidedefault, callback, displayname) { + var setAvatarForUnknownUser = function(target) { + target.imageplaceholder('?') + target.css('background-color', '#b9b9b9') + } if (typeof (user) !== 'undefined') { - user = String(user); + user = String(user) } if (typeof (displayname) !== 'undefined') { - displayname = String(displayname); + displayname = String(displayname) } if (typeof (size) === 'undefined') { if (this.height() > 0) { - size = this.height(); + size = this.height() } else if (this.data('size') > 0) { - size = this.data('size'); + size = this.data('size') } else { - size = 64; + size = 64 } } - this.height(size); - this.width(size); + this.height(size) + this.width(size) if (typeof (user) === 'undefined') { if (typeof (this.data('user')) !== 'undefined') { - user = this.data('user'); + user = this.data('user') } else { - setAvatarForUnknownUser(this); - return; + setAvatarForUnknownUser(this) + return } } // sanitize - user = String(user).replace(/\//g, ''); + user = String(user).replace(/\//g, '') - var $div = this; - var url; + var $div = this + var url // If this is our own avatar we have to use the version attribute if (user === OC.getCurrentUser().uid) { @@ -113,50 +113,50 @@ $.fn.avatar = function (user, size, ie8fix, hidedefault, callback, displayname) user: user, size: Math.ceil(size * window.devicePixelRatio), version: oc_userconfig.avatar.version - }); + }) } else { url = OC.generateUrl( '/avatar/{user}/{size}', { user: user, size: Math.ceil(size * window.devicePixelRatio) - }); + }) } - var img = new Image(); + var img = new Image() // If the new image loads successfully set it. - img.onload = function () { - $div.clearimageplaceholder(); - $div.append(img); + img.onload = function() { + $div.clearimageplaceholder() + $div.append(img) if (typeof callback === 'function') { - callback(); + callback() } - }; + } // Fallback when avatar loading fails: // Use old placeholder when a displayname attribute is defined, // otherwise show the unknown user placeholder. - img.onerror = function () { - $div.clearimageplaceholder(); + img.onerror = function() { + $div.clearimageplaceholder() if (typeof (displayname) !== 'undefined') { - $div.imageplaceholder(user, displayname); + $div.imageplaceholder(user, displayname) } else { - setAvatarForUnknownUser($div); + setAvatarForUnknownUser($div) } if (typeof callback === 'function') { - callback(); + callback() } - }; + } if (size < 32) { - $div.addClass('icon-loading-small'); + $div.addClass('icon-loading-small') } else { - $div.addClass('icon-loading'); + $div.addClass('icon-loading') } - img.width = size; - img.height = size; - img.src = url; - img.alt = ''; -}; + img.width = size + img.height = size + img.src = url + img.alt = '' +} diff --git a/core/src/jquery/contactsmenu.js b/core/src/jquery/contactsmenu.js index 4f90640eda07e59442eb8e0d0db9505f2340849f..389c8809be0189fc403ac1d486d2d61b9e50f78f 100644 --- a/core/src/jquery/contactsmenu.js +++ b/core/src/jquery/contactsmenu.js @@ -32,96 +32,92 @@ const LIST = '' + ' </a>' + ' </li>' + ' </ul>' - + '</div>'; + + '</div>' -const entryTemplate = require('./contactsmenu/jquery_entry.handlebars'); +const entryTemplate = require('./contactsmenu/jquery_entry.handlebars') -$.fn.contactsMenu = function (shareWith, shareType, appendTo) { +$.fn.contactsMenu = function(shareWith, shareType, appendTo) { // 0 - user, 4 - email, 6 - remote - var allowedTypes = [0, 4, 6]; + var allowedTypes = [0, 4, 6] if (allowedTypes.indexOf(shareType) === -1) { - return; + return } - var $div = this; - appendTo.append(LIST); - var $list = appendTo.find('div.contactsmenu-popover'); + var $div = this + appendTo.append(LIST) + var $list = appendTo.find('div.contactsmenu-popover') - $div.click(function () { + $div.click(function() { if (!$list.hasClass('hidden')) { - $list.addClass('hidden'); - $list.hide(); - return; + $list.addClass('hidden') + $list.hide() + return } - $list.removeClass('hidden'); - $list.show(); + $list.removeClass('hidden') + $list.show() if ($list.hasClass('loaded')) { - return; + return } - $list.addClass('loaded'); + $list.addClass('loaded') $.ajax(OC.generateUrl('/contactsmenu/findOne'), { method: 'POST', data: { shareType: shareType, shareWith: shareWith } - }).then(function (data) { - $list.find('ul').find('li').addClass('hidden'); + }).then(function(data) { + $list.find('ul').find('li').addClass('hidden') - var actions; + var actions if (!data.topAction) { actions = [{ hyperlink: '#', title: t('core', 'No action available') - }]; + }] } else { - actions = [data.topAction].concat(data.actions); + actions = [data.topAction].concat(data.actions) } - actions.forEach(function (action) { - var template = entryTemplate; - $list.find('ul').append(template(action)); - }); + actions.forEach(function(action) { + var template = entryTemplate + $list.find('ul').append(template(action)) + }) + }, function(jqXHR) { + $list.find('ul').find('li').addClass('hidden') - if (actions.length === 0) { - - } - }, function (jqXHR) { - $list.find('ul').find('li').addClass('hidden'); - - var title; + var title if (jqXHR.status === 404) { - title = t('core', 'No action available'); + title = t('core', 'No action available') } else { - title = t('core', 'Error fetching contact actions'); + title = t('core', 'Error fetching contact actions') } - var template = entryTemplate; + var template = entryTemplate $list.find('ul').append(template({ hyperlink: '#', title: title - })); - }); - }); + })) + }) + }) - $(document).click(function (event) { - var clickedList = ($list.has(event.target).length > 0); - var clickedTarget = ($div.has(event.target).length > 0); + $(document).click(function(event) { + var clickedList = ($list.has(event.target).length > 0) + var clickedTarget = ($div.has(event.target).length > 0) - $div.each(function () { + $div.each(function() { if ($(this).is(event.target)) { - clickedTarget = true; + clickedTarget = true } - }); + }) if (clickedList || clickedTarget) { - return; + return } - $list.addClass('hidden'); - $list.hide(); - }); -}; + $list.addClass('hidden') + $list.hide() + }) +} diff --git a/core/src/jquery/exists.js b/core/src/jquery/exists.js index bdf62ab7d4b038ed430eb4c07aa1e8f4672e6f62..3481faeb6cd0ffa2e67a977221900e155e937786 100644 --- a/core/src/jquery/exists.js +++ b/core/src/jquery/exists.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -25,7 +25,8 @@ import $ from 'jquery' * check if an element exists. * allows you to write if ($('#myid').exists()) to increase readability * @link http://stackoverflow.com/questions/31044/is-there-an-exists-function-for-jquery + * @returns {boolean} */ -$.fn.exists = function () { - return this.length > 0; -}; +$.fn.exists = function() { + return this.length > 0 +} diff --git a/core/src/jquery/filterattr.js b/core/src/jquery/filterattr.js index 7675eaa1e2d1d4215d3e4d743d0d6d238f731653..204d04b46a0fcaab7513cf02978db3ba9fa66ed3 100644 --- a/core/src/jquery/filterattr.js +++ b/core/src/jquery/filterattr.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -23,9 +23,13 @@ import $ from 'jquery' /** * Filter jQuery selector by attribute value + * + * @param {string} attrName attribute name + * @param {string} attrValue attribute value + * @returns {Void} */ -$.fn.filterAttr = function (attrName, attrValue) { - return this.filter(function () { - return $(this).attr(attrName) === attrValue; - }); -}; +$.fn.filterAttr = function(attrName, attrValue) { + return this.filter(function() { + return $(this).attr(attrName) === attrValue + }) +} diff --git a/core/src/jquery/index.js b/core/src/jquery/index.js index e2efc76bdf10cdf5e3e33b405e419617df2c8a71..d131ae5245a80de9f58e80a15b9ce3d9ab0b239b 100644 --- a/core/src/jquery/index.js +++ b/core/src/jquery/index.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -59,5 +59,5 @@ $.ajaxSetup({ * * This thus mitigates some unexpected XSS vectors. */ -$.globalEval = function () { +$.globalEval = function() { } diff --git a/core/src/jquery/ocdialog.js b/core/src/jquery/ocdialog.js index 8899d9433d1c3db17503aeac0c5a22bfa708225a..68384311531ab0f1dc2208b6968d805ba22e70df 100644 --- a/core/src/jquery/ocdialog.js +++ b/core/src/jquery/ocdialog.js @@ -29,17 +29,17 @@ $.widget('oc.ocdialog', { closeOnEscape: true, modal: false }, - _create: function () { - var self = this; + _create: function() { + var self = this this.originalCss = { display: this.element[0].style.display, width: this.element[0].style.width, height: this.element[0].style.height - }; + } - this.originalTitle = this.element.attr('title'); - this.options.title = this.options.title || this.originalTitle; + this.originalTitle = this.element.attr('title') + this.options.title = this.options.title || this.originalTitle this.$dialog = $('<div class="oc-dialog" />') .attr({ @@ -47,224 +47,224 @@ $.widget('oc.ocdialog', { tabIndex: -1, role: 'dialog' }) - .insertBefore(this.element); - this.$dialog.append(this.element.detach()); - this.element.removeAttr('title').addClass('oc-dialog-content').appendTo(this.$dialog); + .insertBefore(this.element) + this.$dialog.append(this.element.detach()) + this.element.removeAttr('title').addClass('oc-dialog-content').appendTo(this.$dialog) this.$dialog.css({ display: 'inline-block', position: 'fixed' - }); + }) - this.enterCallback = null; + this.enterCallback = null - $(document).on('keydown keyup', function (event) { + $(document).on('keydown keyup', function(event) { if ( - event.target !== self.$dialog.get(0) && - self.$dialog.find($(event.target)).length === 0 + event.target !== self.$dialog.get(0) + && self.$dialog.find($(event.target)).length === 0 ) { - return; + return } // Escape if ( - event.keyCode === 27 && - event.type === 'keydown' && - self.options.closeOnEscape + event.keyCode === 27 + && event.type === 'keydown' + && self.options.closeOnEscape ) { - event.stopImmediatePropagation(); - self.close(); - return false; + event.stopImmediatePropagation() + self.close() + return false } // Enter if (event.keyCode === 13) { - event.stopImmediatePropagation(); + event.stopImmediatePropagation() if (self.enterCallback !== null) { - self.enterCallback(); - event.preventDefault(); - return false; + self.enterCallback() + event.preventDefault() + return false } if (event.type === 'keyup') { - event.preventDefault(); - return false; + event.preventDefault() + return false } // If no button is selected we trigger the primary if ( - self.$buttonrow && - self.$buttonrow.find($(event.target)).length === 0 + self.$buttonrow + && self.$buttonrow.find($(event.target)).length === 0 ) { - var $button = self.$buttonrow.find('button.primary'); + var $button = self.$buttonrow.find('button.primary') if ($button && !$button.prop('disabled')) { - $button.trigger('click'); + $button.trigger('click') } } else if (self.$buttonrow) { - $(event.target).trigger('click'); + $(event.target).trigger('click') } - return false; + return false } - }); + }) - this._setOptions(this.options); - this._createOverlay(); + this._setOptions(this.options) + this._createOverlay() }, - _init: function () { - this.$dialog.focus(); - this._trigger('open'); + _init: function() { + this.$dialog.focus() + this._trigger('open') }, - _setOption: function (key, value) { - var self = this; + _setOption: function(key, value) { + var self = this switch (key) { - case 'title': - if (this.$title) { - this.$title.text(value); - } else { - var $title = $('<h2 class="oc-dialog-title">' + case 'title': + if (this.$title) { + this.$title.text(value) + } else { + var $title = $('<h2 class="oc-dialog-title">' + value - + '</h2>'); - this.$title = $title.prependTo(this.$dialog); - } - this._setSizes(); - break; - case 'buttons': - if (this.$buttonrow) { - this.$buttonrow.empty(); - } else { - var $buttonrow = $('<div class="oc-dialog-buttonrow" />'); - this.$buttonrow = $buttonrow.appendTo(this.$dialog); - } - if (value.length === 1) { - this.$buttonrow.addClass('onebutton'); - } else if (value.length === 2) { - this.$buttonrow.addClass('twobuttons'); - } else if (value.length === 3) { - this.$buttonrow.addClass('threebuttons'); - } - $.each(value, function (idx, val) { - var $button = $('<button>').text(val.text); - if (val.classes) { - $button.addClass(val.classes); - } - if (val.defaultButton) { - $button.addClass('primary'); - self.$defaultButton = $button; - } - self.$buttonrow.append($button); - $button.click(function () { - val.click.apply(self.element[0], arguments); - }); - }); - this.$buttonrow.find('button') - .on('focus', function (event) { - self.$buttonrow.find('button').removeClass('primary'); - $(this).addClass('primary'); - }); - this._setSizes(); - break; - case 'style': - if (value.buttons !== undefined) { - this.$buttonrow.addClass(value.buttons); + + '</h2>') + this.$title = $title.prependTo(this.$dialog) + } + this._setSizes() + break + case 'buttons': + if (this.$buttonrow) { + this.$buttonrow.empty() + } else { + var $buttonrow = $('<div class="oc-dialog-buttonrow" />') + this.$buttonrow = $buttonrow.appendTo(this.$dialog) + } + if (value.length === 1) { + this.$buttonrow.addClass('onebutton') + } else if (value.length === 2) { + this.$buttonrow.addClass('twobuttons') + } else if (value.length === 3) { + this.$buttonrow.addClass('threebuttons') + } + $.each(value, function(idx, val) { + var $button = $('<button>').text(val.text) + if (val.classes) { + $button.addClass(val.classes) } - break; - case 'closeButton': - if (value) { - var $closeButton = $('<a class="oc-dialog-close"></a>'); - this.$dialog.prepend($closeButton); - $closeButton.on('click', function () { - self.close(); - }); - } else { - this.$dialog.find('.oc-dialog-close').remove(); + if (val.defaultButton) { + $button.addClass('primary') + self.$defaultButton = $button } - break; - case 'width': - this.$dialog.css('width', value); - break; - case 'height': - this.$dialog.css('height', value); - break; - case 'close': - this.closeCB = value; - break; + self.$buttonrow.append($button) + $button.click(function() { + val.click.apply(self.element[0], arguments) + }) + }) + this.$buttonrow.find('button') + .on('focus', function(event) { + self.$buttonrow.find('button').removeClass('primary') + $(this).addClass('primary') + }) + this._setSizes() + break + case 'style': + if (value.buttons !== undefined) { + this.$buttonrow.addClass(value.buttons) + } + break + case 'closeButton': + if (value) { + var $closeButton = $('<a class="oc-dialog-close"></a>') + this.$dialog.prepend($closeButton) + $closeButton.on('click', function() { + self.close() + }) + } else { + this.$dialog.find('.oc-dialog-close').remove() + } + break + case 'width': + this.$dialog.css('width', value) + break + case 'height': + this.$dialog.css('height', value) + break + case 'close': + this.closeCB = value + break } - //this._super(key, value); - $.Widget.prototype._setOption.apply(this, arguments); + // this._super(key, value); + $.Widget.prototype._setOption.apply(this, arguments) }, - _setOptions: function (options) { - //this._super(options); - $.Widget.prototype._setOptions.apply(this, arguments); + _setOptions: function(options) { + // this._super(options); + $.Widget.prototype._setOptions.apply(this, arguments) }, - _setSizes: function () { - var lessHeight = 0; + _setSizes: function() { + var lessHeight = 0 if (this.$title) { - lessHeight += this.$title.outerHeight(true); + lessHeight += this.$title.outerHeight(true) } if (this.$buttonrow) { - lessHeight += this.$buttonrow.outerHeight(true); + lessHeight += this.$buttonrow.outerHeight(true) } this.element.css({ 'height': 'calc(100% - ' + lessHeight + 'px)' - }); + }) }, - _createOverlay: function () { + _createOverlay: function() { if (!this.options.modal) { - return; + return } - var self = this; + var self = this this.overlay = $('<div>') .addClass('oc-dialog-dim') - .appendTo($('#content')); - this.overlay.on('click keydown keyup', function (event) { + .appendTo($('#content')) + this.overlay.on('click keydown keyup', function(event) { if (event.target !== self.$dialog.get(0) && self.$dialog.find($(event.target)).length === 0) { - event.preventDefault(); - event.stopPropagation(); - return; + event.preventDefault() + event.stopPropagation() + } - }); + }) }, - _destroyOverlay: function () { + _destroyOverlay: function() { if (!this.options.modal) { - return; + return } if (this.overlay) { - this.overlay.off('click keydown keyup'); - this.overlay.remove(); - this.overlay = null; + this.overlay.off('click keydown keyup') + this.overlay.remove() + this.overlay = null } }, - widget: function () { - return this.$dialog; + widget: function() { + return this.$dialog }, - setEnterCallback: function (callback) { - this.enterCallback = callback; + setEnterCallback: function(callback) { + this.enterCallback = callback }, - unsetEnterCallback: function () { - this.enterCallback = null; + unsetEnterCallback: function() { + this.enterCallback = null }, - close: function () { - this._destroyOverlay(); - var self = this; + close: function() { + this._destroyOverlay() + var self = this // Ugly hack to catch remaining keyup events. - setTimeout(function () { - self._trigger('close', self); - }, 200); + setTimeout(function() { + self._trigger('close', self) + }, 200) - self.$dialog.remove(); - this.destroy(); + self.$dialog.remove() + this.destroy() }, - destroy: function () { + destroy: function() { if (this.$title) { - this.$title.remove(); + this.$title.remove() } if (this.$buttonrow) { - this.$buttonrow.remove(); + this.$buttonrow.remove() } if (this.originalTitle) { - this.element.attr('title', this.originalTitle); + this.element.attr('title', this.originalTitle) } this.element.removeClass('oc-dialog-content') - .css(this.originalCss).detach().insertBefore(this.$dialog); - this.$dialog.remove(); + .css(this.originalCss).detach().insertBefore(this.$dialog) + this.$dialog.remove() } -}); +}) diff --git a/core/src/jquery/octemplate.js b/core/src/jquery/octemplate.js index 61fbea2af5f22fdb84d2368181d812ef7a0ebf9e..24df673922589e8494fd3d5414729e5fbbdc9ad3 100644 --- a/core/src/jquery/octemplate.js +++ b/core/src/jquery/octemplate.js @@ -56,49 +56,49 @@ import escapeHTML from '../Util/escapeHTML' * Inspired by micro templating done by e.g. underscore.js */ const Template = { - init: function (vars, options, elem) { + init: function(vars, options, elem) { // Mix in the passed in options with the default options - this.vars = vars; - this.options = $.extend({}, this.options, options); + this.vars = vars + this.options = $.extend({}, this.options, options) - this.elem = elem; - var self = this; + this.elem = elem + var self = this if (typeof this.options.escapeFunction === 'function') { - var keys = Object.keys(this.vars); + var keys = Object.keys(this.vars) for (var key = 0; key < keys.length; key++) { if (typeof this.vars[keys[key]] === 'string') { - this.vars[keys[key]] = self.options.escapeFunction(this.vars[keys[key]]); + this.vars[keys[key]] = self.options.escapeFunction(this.vars[keys[key]]) } } } - var _html = this._build(this.vars); - return $(_html); + var _html = this._build(this.vars) + return $(_html) }, // From stackoverflow.com/questions/1408289/best-way-to-do-variable-interpolation-in-javascript - _build: function (o) { - var data = this.elem.attr('type') === 'text/template' ? this.elem.html() : this.elem.get(0).outerHTML; + _build: function(o) { + var data = this.elem.attr('type') === 'text/template' ? this.elem.html() : this.elem.get(0).outerHTML try { return data.replace(/{([^{}]*)}/g, - function (a, b) { - var r = o[b]; - return typeof r === 'string' || typeof r === 'number' ? r : a; + function(a, b) { + var r = o[b] + return typeof r === 'string' || typeof r === 'number' ? r : a } - ); + ) } catch (e) { - console.error(e, 'data:', data); + console.error(e, 'data:', data) } }, options: { escapeFunction: escapeHTML } -}; +} -$.fn.octemplate = function (vars, options) { - vars = vars || {}; +$.fn.octemplate = function(vars, options) { + vars = vars || {} if (this.length) { - var _template = Object.create(Template); - return _template.init(vars, options, this); + var _template = Object.create(Template) + return _template.init(vars, options, this) } -}; +} diff --git a/core/src/jquery/placeholder.js b/core/src/jquery/placeholder.js index 074ad5029b58a8ab9ce4d1959754411cded68295..029071c4d998dde5f1bfc2420348fecc515ab04b 100644 --- a/core/src/jquery/placeholder.js +++ b/core/src/jquery/placeholder.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /** * ownCloud * @@ -61,114 +62,114 @@ import $ from 'jquery' * */ -String.prototype.toRgb = function () { +String.prototype.toRgb = function() { // Normalize hash - var hash = this.toLowerCase(); + var hash = this.toLowerCase() // Already a md5 hash? if (hash.match(/^([0-9a-f]{4}-?){8}$/) === null) { - hash = md5(hash); + hash = md5(hash) } - hash = hash.replace(/[^0-9a-f]/g, ''); + hash = hash.replace(/[^0-9a-f]/g, '') - function Color (r, g, b) { - this.r = r; - this.g = g; - this.b = b; + function Color(r, g, b) { + this.r = r + this.g = g + this.b = b } - function stepCalc (steps, ends) { - var step = new Array(3); - step[0] = (ends[1].r - ends[0].r) / steps; - step[1] = (ends[1].g - ends[0].g) / steps; - step[2] = (ends[1].b - ends[0].b) / steps; - return step; + function stepCalc(steps, ends) { + var step = new Array(3) + step[0] = (ends[1].r - ends[0].r) / steps + step[1] = (ends[1].g - ends[0].g) / steps + step[2] = (ends[1].b - ends[0].b) / steps + return step } - function mixPalette (steps, color1, color2) { - var count = steps + 1; - var palette = new Array(); - palette.push(color1); + function mixPalette(steps, color1, color2) { + var count = steps + 1 + var palette = new Array() + palette.push(color1) var step = stepCalc(steps, [color1, color2]) for (var i = 1; i < steps; i++) { - var r = parseInt(color1.r + (step[0] * i)); - var g = parseInt(color1.g + (step[1] * i)); - var b = parseInt(color1.b + (step[2] * i)); - palette.push(new Color(r, g, b)); + var r = parseInt(color1.r + (step[0] * i)) + var g = parseInt(color1.g + (step[1] * i)) + var b = parseInt(color1.b + (step[2] * i)) + palette.push(new Color(r, g, b)) } - return palette; + return palette } - var red = new Color(182, 70, 157); - var yellow = new Color(221, 203, 85); - var blue = new Color(0, 130, 201); // Nextcloud blue + var red = new Color(182, 70, 157) + var yellow = new Color(221, 203, 85) + var blue = new Color(0, 130, 201) // Nextcloud blue // Number of steps to go from a color to another // 3 colors * 6 will result in 18 generated colors - var steps = 6; + var steps = 6 - var palette1 = mixPalette(steps, red, yellow); - var palette2 = mixPalette(steps, yellow, blue); - var palette3 = mixPalette(steps, blue, red); + var palette1 = mixPalette(steps, red, yellow) + var palette2 = mixPalette(steps, yellow, blue) + var palette3 = mixPalette(steps, blue, red) - var finalPalette = palette1.concat(palette2).concat(palette3); + var finalPalette = palette1.concat(palette2).concat(palette3) // Convert a string to an integer evenly - function hashToInt (hash, maximum) { - var finalInt = 0; - var result = Array(); + function hashToInt(hash, maximum) { + var finalInt = 0 + var result = Array() // Splitting evenly the string for (var i = 0; i < hash.length; i++) { // chars in md5 goes up to f, hex:16 - result.push(parseInt(hash.charAt(i), 16) % 16); + result.push(parseInt(hash.charAt(i), 16) % 16) } // Adds up all results for (var j in result) { - finalInt += result[j]; + finalInt += result[j] } // chars in md5 goes up to f, hex:16 // make sure we're always using int in our operation - return parseInt(parseInt(finalInt) % maximum); + return parseInt(parseInt(finalInt) % maximum) } - return finalPalette[hashToInt(hash, steps * 3)]; -}; + return finalPalette[hashToInt(hash, steps * 3)] +} -$.fn.imageplaceholder = function (seed, text, size) { - text = text || seed; +$.fn.imageplaceholder = function(seed, text, size) { + text = text || seed // Compute the hash - var rgb = seed.toRgb(); - this.css('background-color', 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')'); + var rgb = seed.toRgb() + this.css('background-color', 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')') // Placeholders are square - var height = this.height() || size || 32; - this.height(height); - this.width(height); + var height = this.height() || size || 32 + this.height(height) + this.width(height) // CSS rules - this.css('color', '#fff'); - this.css('font-weight', 'normal'); - this.css('text-align', 'center'); + this.css('color', '#fff') + this.css('font-weight', 'normal') + this.css('text-align', 'center') // calculate the height - this.css('line-height', height + 'px'); - this.css('font-size', (height * 0.55) + 'px'); + this.css('line-height', height + 'px') + this.css('font-size', (height * 0.55) + 'px') if (seed !== null && seed.length) { - this.html(text[0].toUpperCase()); + this.html(text[0].toUpperCase()) } -}; - -$.fn.clearimageplaceholder = function () { - this.css('background-color', ''); - this.css('color', ''); - this.css('font-weight', ''); - this.css('text-align', ''); - this.css('line-height', ''); - this.css('font-size', ''); - this.html(''); - this.removeClass('icon-loading'); - this.removeClass('icon-loading-small'); -}; +} + +$.fn.clearimageplaceholder = function() { + this.css('background-color', '') + this.css('color', '') + this.css('font-weight', '') + this.css('text-align', '') + this.css('line-height', '') + this.css('font-size', '') + this.html('') + this.removeClass('icon-loading') + this.removeClass('icon-loading-small') +} diff --git a/core/src/jquery/requesttoken.js b/core/src/jquery/requesttoken.js index a8fb024d04a174c291baa0a13ffbd4f9182ef634..5f15d43aa178ab7da05d9cd6b58672141f44313f 100644 --- a/core/src/jquery/requesttoken.js +++ b/core/src/jquery/requesttoken.js @@ -21,11 +21,11 @@ import $ from 'jquery' -import {getToken} from '../OC/requesttoken' +import { getToken } from '../OC/requesttoken' -$(document).on('ajaxSend',function(elm, xhr, settings) { - if(settings.crossDomain === false) { - xhr.setRequestHeader('requesttoken', getToken()); - xhr.setRequestHeader('OCS-APIREQUEST', 'true'); +$(document).on('ajaxSend', function(elm, xhr, settings) { + if (settings.crossDomain === false) { + xhr.setRequestHeader('requesttoken', getToken()) + xhr.setRequestHeader('OCS-APIREQUEST', 'true') } -}); +}) diff --git a/core/src/jquery/selectrange.js b/core/src/jquery/selectrange.js index b7e20361805879359148d68940f4c9cd3f1f8128..b53bac4fc02cbca9621a2945ae62b370d6ea0016 100644 --- a/core/src/jquery/selectrange.js +++ b/core/src/jquery/selectrange.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -24,20 +24,21 @@ import $ from 'jquery' /** * select a range in an input field * @link http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area - * @param {type} start - * @param {type} end + * @param {int} start start selection from + * @param {int} end number of char from start + * @returns {Void} */ -$.fn.selectRange = function (start, end) { - return this.each(function () { +$.fn.selectRange = function(start, end) { + return this.each(function() { if (this.setSelectionRange) { - this.focus(); - this.setSelectionRange(start, end); + this.focus() + this.setSelectionRange(start, end) } else if (this.createTextRange) { - var range = this.createTextRange(); - range.collapse(true); - range.moveEnd('character', end); - range.moveStart('character', start); - range.select(); + var range = this.createTextRange() + range.collapse(true) + range.moveEnd('character', end) + range.moveStart('character', start) + range.select() } - }); -}; + }) +} diff --git a/core/src/jquery/showpassword.js b/core/src/jquery/showpassword.js index 4dc39340e76d7273e70dfab71be1e54a29243a05..a9857f4d6d4860d17a7e42ff013c456de3f57711 100644 --- a/core/src/jquery/showpassword.js +++ b/core/src/jquery/showpassword.js @@ -36,18 +36,18 @@ import $ from 'jquery' * @licens MIT License - http://www.opensource.org/licenses/mit-license.php */ $.fn.extend({ - showPassword: function (c) { + showPassword: function(c) { // Setup callback object - var callback = {'fn': null, 'args': {}}; - callback.fn = c; + var callback = { 'fn': null, 'args': {} } + callback.fn = c // Clones passwords and turn the clones into text inputs - var cloneElement = function (element) { + var cloneElement = function(element) { - var $element = $(element); + var $element = $(element) - var $clone = $("<input />"); + var $clone = $('<input />') // Name added for JQuery Validation compatibility // Element name is required to avoid script warning. @@ -59,91 +59,90 @@ $.fn.extend({ 'name': $element.attr('name') + '-clone', 'tabindex': $element.attr('tabindex'), 'autocomplete': 'off' - }); + }) if ($element.attr('placeholder') !== undefined) { - $clone.attr('placeholder', $element.attr('placeholder')); + $clone.attr('placeholder', $element.attr('placeholder')) } - return $clone; + return $clone - }; + } // Transfers values between two elements - var update = function (a, b) { - b.val(a.val()); - }; + var update = function(a, b) { + b.val(a.val()) + } // Shows a or b depending on checkbox - var setState = function (checkbox, a, b) { + var setState = function(checkbox, a, b) { if (checkbox.is(':checked')) { - update(a, b); - b.show(); - a.hide(); + update(a, b) + b.show() + a.hide() } else { - update(b, a); - b.hide(); - a.show(); + update(b, a) + b.hide() + a.show() } - }; + } - return this.each(function () { + return this.each(function() { - var $input = $(this), - $checkbox = $($input.data('typetoggle')); + var $input = $(this) + var $checkbox = $($input.data('typetoggle')) // Create clone - var $clone = cloneElement($input); - $clone.insertAfter($input); + var $clone = cloneElement($input) + $clone.insertAfter($input) // Set callback arguments if (callback.fn) { - callback.args.input = $input; - callback.args.checkbox = $checkbox; - callback.args.clone = $clone; + callback.args.input = $input + callback.args.checkbox = $checkbox + callback.args.clone = $clone } + $checkbox.bind('click', function() { + setState($checkbox, $input, $clone) + }) - $checkbox.bind('click', function () { - setState($checkbox, $input, $clone); - }); + $input.bind('keyup', function() { + update($input, $clone) + }) - $input.bind('keyup', function () { - update($input, $clone); - }); - - $clone.bind('keyup', function () { - update($clone, $input); + $clone.bind('keyup', function() { + update($clone, $input) // Added for JQuery Validation compatibility // This will trigger validation if it's ON for keyup event - $input.trigger('keyup'); + $input.trigger('keyup') - }); + }) // Added for JQuery Validation compatibility // This will trigger validation if it's ON for blur event - $clone.bind('blur', function () { - $input.trigger('focusout'); - }); + $clone.bind('blur', function() { + $input.trigger('focusout') + }) - setState($checkbox, $input, $clone); + setState($checkbox, $input, $clone) // set type of password field clone (type=text) to password right on submit // to prevent browser save the value of this field - $clone.closest('form').submit(function (e) { + $clone.closest('form').submit(function(e) { // .prop has to be used, because .attr throws // an error while changing a type of an input // element - $clone.prop('type', 'password'); - }); + $clone.prop('type', 'password') + }) if (callback.fn) { - callback.fn(callback.args); + callback.fn(callback.args) } - }); + }) } -}); +}) diff --git a/core/src/jquery/tipsy.js b/core/src/jquery/tipsy.js index 816a93fb9a932c409a32bec20777b9b3c5cebba2..75e90b1c060ae51a2a3c94cb2c8f837078d22eb3 100644 --- a/core/src/jquery/tipsy.js +++ b/core/src/jquery/tipsy.js @@ -1,4 +1,4 @@ -/* +/** * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> @@ -23,61 +23,63 @@ import $ from 'jquery' /** * $ tipsy shim for the bootstrap tooltip + * @param {Object} argument options + * @returns {Object} this * @deprecated */ -$.fn.tipsy = function (argument) { - console.warn('Deprecation warning: tipsy is deprecated. Use tooltip instead.'); +$.fn.tipsy = function(argument) { + console.warn('Deprecation warning: tipsy is deprecated. Use tooltip instead.') if (typeof argument === 'object' && argument !== null) { // tipsy defaults var options = { placement: 'bottom', - delay: {'show': 0, 'hide': 0}, + delay: { 'show': 0, 'hide': 0 }, trigger: 'hover', html: false, container: 'body' - }; + } if (argument.gravity) { switch (argument.gravity) { - case 'n': - case 'nw': - case 'ne': - options.placement = 'bottom'; - break; - case 's': - case 'sw': - case 'se': - options.placement = 'top'; - break; - case 'w': - options.placement = 'right'; - break; - case 'e': - options.placement = 'left'; - break; + case 'n': + case 'nw': + case 'ne': + options.placement = 'bottom' + break + case 's': + case 'sw': + case 'se': + options.placement = 'top' + break + case 'w': + options.placement = 'right' + break + case 'e': + options.placement = 'left' + break } } if (argument.trigger) { - options.trigger = argument.trigger; + options.trigger = argument.trigger } if (argument.delayIn) { - options.delay.show = argument.delayIn; + options.delay.show = argument.delayIn } if (argument.delayOut) { - options.delay.hide = argument.delayOut; + options.delay.hide = argument.delayOut } if (argument.html) { - options.html = true; + options.html = true } if (argument.fallback) { - options.title = argument.fallback; + options.title = argument.fallback } // destroy old tooltip in case the title has changed - $.fn.tooltip.call(this, 'destroy'); - $.fn.tooltip.call(this, options); + $.fn.tooltip.call(this, 'destroy') + $.fn.tooltip.call(this, options) } else { - this.tooltip(argument); - $.fn.tooltip.call(this, argument); + this.tooltip(argument) + $.fn.tooltip.call(this, argument) } - return this; -}; \ No newline at end of file + return this +} diff --git a/core/src/jquery/ui-fixes.js b/core/src/jquery/ui-fixes.js index d3cebfc346e5d3cf9022445b0a0a617858953241..fb5ce4d7ba8a80874fa6c69e93ca1964c6fb7b1f 100644 --- a/core/src/jquery/ui-fixes.js +++ b/core/src/jquery/ui-fixes.js @@ -2,7 +2,7 @@ import $ from 'jquery' // Set autocomplete width the same as the related input // See http://stackoverflow.com/a/11845718 -$.ui.autocomplete.prototype._resizeMenu = function () { - var ul = this.menu.element; - ul.outerWidth(this.element.outerWidth()); -}; +$.ui.autocomplete.prototype._resizeMenu = function() { + var ul = this.menu.element + ul.outerWidth(this.element.outerWidth()) +} diff --git a/core/src/login.js b/core/src/login.js index 12f3a8963081fb040c4ab4afdc50d48fc2b62972..4314da7ab07000f00a3387d8c2e2e46a5337725e 100644 --- a/core/src/login.js +++ b/core/src/login.js @@ -19,25 +19,26 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import Vue from 'vue'; -import queryString from 'query-string'; +import Vue from 'vue' +import queryString from 'query-string' -import OC from './OC/index'; // TODO: Not needed but L10n breaks if removed -import LoginView from './views/Login.vue'; -import Nextcloud from './mixins/Nextcloud'; +// eslint-disable-next-line no-unused-vars +import OC from './OC/index' // TODO: Not needed but L10n breaks if removed +import LoginView from './views/Login.vue' +import Nextcloud from './mixins/Nextcloud' -const query = queryString.parse(location.search); +const query = queryString.parse(location.search) if (query.clear === '1') { try { - window.localStorage.clear(); - window.sessionStorage.clear(); - console.debug('Browser storage cleared'); + window.localStorage.clear() + window.sessionStorage.clear() + console.debug('Browser storage cleared') } catch (e) { - console.error('Could not clear browser storage', e); + console.error('Could not clear browser storage', e) } } -Vue.mixin(Nextcloud); +Vue.mixin(Nextcloud) const fromStateOr = (key, orValue) => { try { @@ -47,7 +48,7 @@ const fromStateOr = (key, orValue) => { } } -const View = Vue.extend(LoginView); +const View = Vue.extend(LoginView) new View({ propsData: { errors: fromStateOr('loginErrors', []), @@ -60,6 +61,6 @@ new View({ resetPasswordLink: fromStateOr('loginResetPasswordLink', ''), autoCompleteAllowed: fromStateOr('loginAutocomplete', true), resetPasswordTarget: fromStateOr('resetPasswordTarget', ''), - resetPasswordUser: fromStateOr('resetPasswordUser', ''), + resetPasswordUser: fromStateOr('resetPasswordUser', '') } -}).$mount('#login'); +}).$mount('#login') diff --git a/core/src/main.js b/core/src/main.js index 1f8f12c1a0fabd64bcddece58b371ae50e0ec566..b8c1c23628f0f43663f3ef924df167036119c711 100644 --- a/core/src/main.js +++ b/core/src/main.js @@ -24,15 +24,15 @@ import '@babel/polyfill' import './Polyfill/index' // If you remove the line below, tests won't pass +// eslint-disable-next-line no-unused-vars import OC from './OC/index' import './globals' import './jquery/index' -import {initCore} from './init' -import {registerAppsSlideToggle} from './OC/apps' +import { initCore } from './init' +import { registerAppsSlideToggle } from './OC/apps' -$(document).ready(function () { - initCore(); - - registerAppsSlideToggle(); -}); +$(document).ready(function() { + initCore() + registerAppsSlideToggle() +}) diff --git a/core/src/maintenance.js b/core/src/maintenance.js index 4715b65c0219006738005568de0e0e3843d8e02d..14451b59fd0617ba5c44b7da0905275aac953f3a 100644 --- a/core/src/maintenance.js +++ b/core/src/maintenance.js @@ -40,10 +40,10 @@ const check = () => { console.info('Nextcloud is still in maintenance mode') // Wait 20sec before the next request - setTimeout(check, 20 * 1000); + setTimeout(check, 20 * 1000) }) .catch(console.error.bind(this)) -}; +} // Off we go! -check(); +check() diff --git a/core/src/mixins/Nextcloud.js b/core/src/mixins/Nextcloud.js index 3ca755b3052a2effcaa1f8df74858f32682da836..53c30b297cec307c9f117ded9e00ffdf69e7c84e 100644 --- a/core/src/mixins/Nextcloud.js +++ b/core/src/mixins/Nextcloud.js @@ -25,11 +25,11 @@ import OC from '../OC/index' export default { data() { return { - OC, + OC } }, methods: { t: L10n.translate.bind(L10n), - n: L10n.translatePlural.bind(L10n), - }, + n: L10n.translatePlural.bind(L10n) + } } diff --git a/core/src/session-heartbeat.js b/core/src/session-heartbeat.js index 9a4981e4bb7063c79c917f6da356b7033000be53..49cf547aa362d15add66cbfbc6b070b20d11652f 100644 --- a/core/src/session-heartbeat.js +++ b/core/src/session-heartbeat.js @@ -21,13 +21,13 @@ import $ from 'jquery' -import {generateUrl} from './OC/routing' +import { generateUrl } from './OC/routing' import OC from './OC' -import {setToken as setRequestToken} from './OC/requesttoken' +import { setToken as setRequestToken } from './OC/requesttoken' /** * session heartbeat (defaults to enabled) - * @return {boolean} + * @returns {boolean} */ const keepSessionAlive = () => { return OC.config.session_keepalive === undefined @@ -36,7 +36,7 @@ const keepSessionAlive = () => { /** * get interval in seconds - * @return {Number} + * @returns {Number} */ const getInterval = () => { let interval = NaN @@ -61,7 +61,7 @@ const getInterval = () => { export const initSessionHeartBeat = () => { if (!keepSessionAlive()) { console.info('session heartbeat disabled') - return; + return } setInterval(() => { diff --git a/core/src/views/Login.vue b/core/src/views/Login.vue index d1127e05f953d123c933cbfecf08087138489873..dd65261ffc63bf07105ea9762fec5f5872e9bc3b 100644 --- a/core/src/views/Login.vue +++ b/core/src/views/Login.vue @@ -23,7 +23,7 @@ <div> <transition name="fade" mode="out-in"> <div v-if="!resetPassword && resetPasswordTarget === ''" - key="login"> + key="login"> <LoginForm :username.sync="user" :redirect-url="redirectUrl" @@ -32,103 +32,103 @@ :throttle-delay="throttleDelay" :inverted-colors="invertedColors" :auto-complete-allowed="autoCompleteAllowed" - @submit="loading = true"/> + @submit="loading = true" /> <a v-if="canResetPassword && resetPasswordLink !== ''" - id="lost-password" - :href="resetPasswordLink"> + id="lost-password" + :href="resetPasswordLink"> {{ t('core', 'Forgot password?') }} </a> <a v-else-if="canResetPassword && !resetPassword" - id="lost-password" - :href="resetPasswordLink" - @click.prevent="resetPassword = true"> + id="lost-password" + :href="resetPasswordLink" + @click.prevent="resetPassword = true"> {{ t('core', 'Forgot password?') }} </a> </div> <div v-else-if="!loading && canResetPassword" - key="reset" - class="login-additional"> + key="reset" + class="login-additional"> <div class="lost-password-container"> <ResetPassword v-if="resetPassword" - :username.sync="user" - :reset-password-link="resetPasswordLink" - :inverted-colors="invertedColors" - @abort="resetPassword = false"/> + :username.sync="user" + :reset-password-link="resetPasswordLink" + :inverted-colors="invertedColors" + @abort="resetPassword = false" /> </div> </div> <div v-else-if="resetPasswordTarget !== ''"> <UpdatePassword :username.sync="user" - :reset-password-target="resetPasswordTarget" - :inverted-colors="invertedColors" - @done="passwordResetFinished"/> + :reset-password-target="resetPasswordTarget" + :inverted-colors="invertedColors" + @done="passwordResetFinished" /> </div> </transition> </div> </template> <script> - import LoginForm from '../components/login/LoginForm.vue' - import ResetPassword from '../components/login/ResetPassword.vue' - import UpdatePassword from '../components/login/UpdatePassword.vue' +import LoginForm from '../components/login/LoginForm.vue' +import ResetPassword from '../components/login/ResetPassword.vue' +import UpdatePassword from '../components/login/UpdatePassword.vue' - export default { - name: 'Login', - props: { - username: { - type: String, - default: '', - }, - redirectUrl: { - type: String, - }, - errors: { - type: Array, - default: () => [], - }, - messages: { - type: Array, - default: () => [], - }, - throttleDelay: { - type: Number, - }, - canResetPassword: { - type: Boolean, - default: false, - }, - resetPasswordLink: { - type: String, - }, - resetPasswordTarget: { - type: String, - }, - invertedColors: { - type: Boolean, - default: false, - }, - autoCompleteAllowed: { - type: Boolean, - default: true, - }, +export default { + name: 'Login', + components: { + LoginForm, + ResetPassword, + UpdatePassword + }, + props: { + username: { + type: String, + default: '' + }, + redirectUrl: { + type: String + }, + errors: { + type: Array, + default: () => [] + }, + messages: { + type: Array, + default: () => [] + }, + throttleDelay: { + type: Number }, - components: { - LoginForm, - ResetPassword, - UpdatePassword, + canResetPassword: { + type: Boolean, + default: false }, - data () { - return { - loading: false, - user: this.username, - resetPassword: false, - } + resetPasswordLink: { + type: String }, - methods: { - passwordResetFinished() { - this.resetPasswordTarget = '' - } + resetPasswordTarget: { + type: String + }, + invertedColors: { + type: Boolean, + default: false + }, + autoCompleteAllowed: { + type: Boolean, + default: true + } + }, + data() { + return { + loading: false, + user: this.username, + resetPassword: false + } + }, + methods: { + passwordResetFinished() { + this.resetPasswordTarget = '' } } +} </script> <style> diff --git a/package-lock.json b/package-lock.json index 7f78b74d2dbae45aa15c57c16d07919136604402..e0ba5b4f9cd0ea953627f99b1ce4011110ae95b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,41 +35,12 @@ "source-map": "^0.5.0" }, "dependencies": { - "@babel/generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", - "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", - "dev": true, - "requires": { - "@babel/types": "^7.6.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, "@babel/parser": { "version": "7.6.2", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", "dev": true }, - "@babel/traverse": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", - "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.2", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.2", - "@babel/types": "^7.6.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -88,16 +59,15 @@ } }, "@babel/generator": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.0.tgz", - "integrity": "sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", + "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", "dev": true, "requires": { "@babel/types": "^7.6.0", "jsesc": "^2.5.1", "lodash": "^4.17.13", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "source-map": "^0.5.0" }, "dependencies": { "source-map": { @@ -309,58 +279,6 @@ "@babel/template": "^7.6.0", "@babel/traverse": "^7.6.2", "@babel/types": "^7.6.0" - }, - "dependencies": { - "@babel/generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", - "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", - "dev": true, - "requires": { - "@babel/types": "^7.6.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/parser": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", - "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", - "dev": true - }, - "@babel/traverse": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", - "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.2", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.2", - "@babel/types": "^7.6.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } } }, "@babel/highlight": { @@ -873,24 +791,24 @@ }, "dependencies": { "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", + "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", "dev": true } } }, "@babel/traverse": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.0.tgz", - "integrity": "sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", + "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", + "@babel/generator": "^7.6.2", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.0", + "@babel/parser": "^7.6.2", "@babel/types": "^7.6.0", "debug": "^4.1.0", "globals": "^11.1.0", @@ -898,9 +816,9 @@ }, "dependencies": { "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", + "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", "dev": true }, "debug": { @@ -1256,6 +1174,12 @@ "array-back": "^3.0.1" } }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -1344,6 +1268,16 @@ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -1427,6 +1361,12 @@ "resolved": "https://registry.npmjs.org/ast-to-markdown/-/ast-to-markdown-0.2.2.tgz", "integrity": "sha512-JT8FYlvBKqpgZcI5YCL2azdkovjAZVviOH1ylTiCYlhgSKoFIAKI2W7hHJl9SX9xLXw0/gIVOHNFw9N9U29uXw==" }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -1752,7 +1692,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -1789,7 +1729,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -1840,7 +1780,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -1920,9 +1860,9 @@ "dev": true }, "yallist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.0.tgz", - "integrity": "sha512-6gpP93MR+VOOehKbCPchro3wFZNSNmek8A2kbkOAZLIZAYx1KP/zAqwO0sOHi3xJEb+UBz8NaYt/17UNit1Q9w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } @@ -1964,6 +1904,12 @@ } } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "camelcase": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", @@ -2035,6 +1981,12 @@ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.3.tgz", "integrity": "sha512-VOq6PRzQBam/8Jm6XBGk2fNEnHXAdGd6go0rtd4weAGECBamHDwwCQSOT12TACIYUZegUXnV6xBXqUssijtxIg==" }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, "charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", @@ -2121,6 +2073,21 @@ } } }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, "clipboard": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", @@ -2355,6 +2322,12 @@ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "dev": true }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "convert-source-map": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", @@ -2430,7 +2403,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -2443,7 +2416,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -2660,6 +2633,12 @@ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, "deepmerge": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", @@ -2771,7 +2750,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -2811,6 +2790,15 @@ } } }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dom-serializer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", @@ -2894,9 +2882,9 @@ } }, "electron-to-chromium": { - "version": "1.3.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.267.tgz", - "integrity": "sha512-9Q2ixAJC+oHjWNtJV0MQ4vJMCWSowIrC6V6vcr+bwPddTDHj2ddv9xxXCzf4jT/fy6HP7maPoW0gifXkRxCttQ==", + "version": "1.3.270", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.270.tgz", + "integrity": "sha512-426qbfgLn0hVE4pDxok2dcAhA3u5lwXlBg2+i6VWQJvnMZNgevkC6s/qr91YH/avVMKXKwxnR5iBznpivg210A==", "dev": true }, "elliptic": { @@ -2967,107 +2955,106 @@ "is-arrayish": "^0.2.1" } }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "es-abstract": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", + "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", - "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" - }, - "dependencies": { - "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" - } + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.0.0", + "string.prototype.trimright": "^2.0.0" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "dev": true, "requires": { - "estraverse": "^4.1.0" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" } }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, - "events": { - "version": "3.0.0", - "resolved": "http://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", - "dev": true + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", "dev": true, "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" }, "dependencies": { + "acorn-jsx": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", + "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "requires": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -3075,64 +3062,592 @@ "shebang-command": "^1.2.0", "which": "^1.2.9" } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { + }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" } }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "ansi-regex": "^3.0.0" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true } } }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, + "eslint-config-nextcloud": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/eslint-config-nextcloud/-/eslint-config-nextcloud-0.0.5.tgz", + "integrity": "sha512-innKCAJBpTTdEGriCQduV6XwDySdZ2uHD7PduaKbbSfL12a/eXU6/jcIDfxM3cFAgCN7sJYOSE4/TM9nygO8kQ==", + "dev": true + }, + "eslint-config-standard": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz", + "integrity": "sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-3.0.2.tgz", + "integrity": "sha512-S5VnD+UpVY1PyYRqeBd/4pgsmkvSokbHqTXAQMpvCyRr3XN2tvSLo9spm2nEpqQqh9dezw3os/0zWihLeOg2Rw==", + "dev": true, + "requires": { + "fs-extra": "^8.1.0", + "loader-fs-cache": "^1.0.2", + "loader-utils": "^1.2.3", + "object-hash": "^1.3.1", + "schema-utils": "^2.2.0" + } + }, + "eslint-module-utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz", + "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==", + "dev": true, + "requires": { + "debug": "^2.6.8", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-es": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-2.0.0.tgz", + "integrity": "sha512-f6fceVtg27BR02EYnBhgWLFQfK6bN4Ll0nQFrBHOlCsAyxeZkn0NHns5O0YZOPrV1B3ramd6cgFwaoFLcSkwEQ==", + "dev": true, + "requires": { + "eslint-utils": "^1.4.2", + "regexpp": "^3.0.0" + }, + "dependencies": { + "regexpp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.18.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", + "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.11.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "eslint-plugin-nextcloud": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-nextcloud/-/eslint-plugin-nextcloud-0.3.0.tgz", + "integrity": "sha512-LUD2qdirGL0BRt4uaMDGxen17mWVq9JwuGDt7P7Celz7bzdu0X48RrS8mhXn9e0w78+nYN5kPoULG2Bw04r4HA==", + "dev": true, + "requires": { + "requireindex": "~1.2.0" + } + }, + "eslint-plugin-node": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz", + "integrity": "sha512-1CSyM/QCjs6PXaT18+zuAXsjXGIGo5Rw630rSKwokSs2jrYURQc4R5JZpoanNCqwNmepg+0eZ9L7YiRUJb8jiQ==", + "dev": true, + "requires": { + "eslint-plugin-es": "^2.0.0", + "eslint-utils": "^1.4.2", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", + "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "dev": true + }, + "eslint-plugin-standard": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", + "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "dev": true + }, + "eslint-plugin-vue": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-5.2.3.tgz", + "integrity": "sha512-mGwMqbbJf0+VvpGR5Lllq0PMxvTdrZ/ZPjmhkacrCHbubJeJOt+T6E3HUzAifa2Mxi7RSdJfC9HFpOeSYVMMIw==", + "dev": true, + "requires": { + "vue-eslint-parser": "^5.0.0" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", + "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.0.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "requires": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, "requires": { "homedir-polyfill": "^1.0.1" } @@ -3181,6 +3696,17 @@ } } }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -3264,6 +3790,12 @@ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "dev": true }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fastparse": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", @@ -3281,6 +3813,24 @@ "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", "dev": true }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, "file-loader": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.2.0.tgz", @@ -3384,6 +3934,23 @@ "resolve-dir": "^1.0.1" } }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "dev": true + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -3452,6 +4019,25 @@ "js-yaml": "^3.13.1" } }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + } + } + }, "fs-then-native": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", @@ -4044,6 +4630,12 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -4487,6 +5079,15 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "icss-utils": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", @@ -4508,6 +5109,30 @@ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -4571,6 +5196,79 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + } + } + }, "interpret": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", @@ -4651,6 +5349,12 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -4671,6 +5375,12 @@ } } }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, "is-decimal": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.3.tgz", @@ -4794,11 +5504,35 @@ "isobject": "^3.0.1" } }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -5024,6 +5758,12 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -5047,6 +5787,15 @@ } } }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -5087,6 +5836,16 @@ "invert-kv": "^1.0.0" } }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "linkify-it": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", @@ -5116,6 +5875,57 @@ } } }, + "loader-fs-cache": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz", + "integrity": "sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw==", + "dev": true, + "requires": { + "find-cache-dir": "^0.1.1", + "mkdirp": "0.5.1" + }, + "dependencies": { + "find-cache-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "mkdirp": "^0.5.1", + "pkg-dir": "^1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "^1.0.0" + } + } + } + }, "loader-runner": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", @@ -5592,6 +6402,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, "nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", @@ -5618,6 +6434,12 @@ "to-regex": "^3.0.1" } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "neo-async": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", @@ -5812,9 +6634,9 @@ } }, "node-releases": { - "version": "1.1.32", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.32.tgz", - "integrity": "sha512-VhVknkitq8dqtWoluagsGPn3dxTvN9fwgR59fV3D7sLBHe0JfDramsMI8n8mY//ccq/Kkrf8ZRHRpsyVZ3qw1A==", + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.33.tgz", + "integrity": "sha512-I0V30bWQEoHb+10W8oedVoUrdjW5wIkYm0w7vvcrPO95pZY738m1k77GF5sO0vKg5eXYg9oGtrMAETbgZGm11A==", "dev": true, "requires": { "semver": "^5.3.0" @@ -5986,6 +6808,18 @@ "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.0.tgz", "integrity": "sha1-ciu9tgA576R8rTxtws5RqFwCxa4=" }, + "object-hash": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", + "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", + "dev": true + }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "dev": true + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -6027,6 +6861,18 @@ "isobject": "^3.0.1" } }, + "object.values": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", + "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -6035,6 +6881,15 @@ "wrappy": "1" } }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, "opener": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz", @@ -6049,6 +6904,28 @@ "wordwrap": "~0.0.2" } }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + }, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + } + } + }, "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -6144,9 +7021,18 @@ "readable-stream": "^2.1.5" } }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse-asn1": { "version": "5.1.5", - "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", "dev": true, "requires": { @@ -6202,7 +7088,7 @@ }, "path-browserify": { "version": "0.0.1", - "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", "dev": true }, @@ -6484,6 +7370,12 @@ "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==", "dev": true }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, "prettier": { "version": "1.16.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.3.tgz", @@ -6508,6 +7400,12 @@ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -6952,6 +7850,12 @@ "safe-regex": "^1.1.0" } }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, "regexpu-core": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", @@ -7121,6 +8025,12 @@ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, + "requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true + }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -7192,6 +8102,16 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -7216,6 +8136,15 @@ "inherits": "^2.0.1" } }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -7225,6 +8154,15 @@ "aproba": "^1.1.1" } }, + "rxjs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", + "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -7285,14 +8223,33 @@ } }, "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.4.1.tgz", + "integrity": "sha512-RqYLpkPZX5Oc3fw/kHHHyP56fg5Y+XBpIpV8nCg0znIALfq3OH+Ea9Hfeac9BAMwG5IICltiZ0vxFvJQONfA5w==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + } } }, "scss-tokenizer": { @@ -7373,7 +8330,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -7408,6 +8365,25 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, "snap.js": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/snap.js/-/snap.js-2.0.9.tgz", @@ -7710,7 +8686,7 @@ }, "stream-browserify": { "version": "2.0.2", - "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", "dev": true, "requires": { @@ -7790,6 +8766,26 @@ "strip-ansi": "^3.0.0" } }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -7884,6 +8880,64 @@ "has-flag": "^3.0.0" } }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "table-layout": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", @@ -8001,6 +9055,17 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } } } }, @@ -8023,6 +9088,18 @@ } } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -8047,6 +9124,15 @@ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -8140,12 +9226,6 @@ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", "dev": true }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, "trim-trailing-lines": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.2.tgz", @@ -8173,7 +9253,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -8192,6 +9272,15 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -8351,6 +9440,12 @@ "unist-util-is": "^3.0.0" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -8553,7 +9648,7 @@ }, "vm-browserify": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==", "dev": true }, @@ -8575,6 +9670,48 @@ "clipboard": "^2.0.0" } }, + "vue-eslint-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz", + "integrity": "sha512-JlHVZwBBTNVvzmifwjpZYn0oPWH2SgWv5dojlZBsrhablDu95VFD+hriB1rQGwbD+bms6g+rAFhQHk6+NyiS6g==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0", + "espree": "^4.1.0", + "esquery": "^1.0.1", + "lodash": "^4.17.11" + }, + "dependencies": { + "acorn-jsx": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", + "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "espree": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", + "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", + "dev": true, + "requires": { + "acorn": "^6.0.2", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + } + } + }, "vue-hot-reload-api": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz", @@ -8897,6 +10034,17 @@ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, "tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -9204,6 +10352,15 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, "x-is-string": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", diff --git a/package.json b/package.json index 3b3dfd5e4e82773ecb012615f1fc0ad54f949a67..829139712153750201c22921b4efe3a55640a6eb 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,10 @@ }, "scripts": { "build": "NODE_ENV=production webpack --progress --hide-modules --config webpack.prod.js", - "dev": "NODE_ENV=development webpack --progress --watch --config webpack.dev.js", - "watch": "NODE_ENV=development webpack --progress --watch --config webpack.dev.js" + "dev": "NODE_ENV=development webpack --progress --config webpack.dev.js", + "watch": "NODE_ENV=development webpack --progress --watch --config webpack.dev.js", + "lint:fix": "ESLINT_FIX=true webpack --progress --config webpack.dev.js", + "lint:fix-watch": "ESLINT_FIX=true webpack --progress --watch --config webpack.dev.js" }, "repository": { "type": "git", @@ -72,6 +74,16 @@ "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", "browserslist-config-nextcloud": "0.0.1", "css-loader": "^3.2.0", + "eslint": "^5.16.0", + "eslint-config-nextcloud": "0.0.5", + "eslint-config-standard": "^12.0.0", + "eslint-loader": "^3.0.0", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-nextcloud": "^0.3.0", + "eslint-plugin-node": "^10.0.0", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-standard": "^4.0.1", + "eslint-plugin-vue": "^5.2.3", "exports-loader": "^0.7.0", "file-loader": "^4.2.0", "handlebars-loader": "^1.7.1", diff --git a/webpack.common.js b/webpack.common.js index 695dcc9c2261b187a4772885d8820f2b1b4cd7ad..2dc69b201a4c989fce6017034abb3f316bf21d9e 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -1,20 +1,21 @@ -const path = require('path'); -const merge = require('webpack-merge'); -const { VueLoaderPlugin } = require('vue-loader'); +/* eslint-disable camelcase */ +const path = require('path') +const merge = require('webpack-merge') +const { VueLoaderPlugin } = require('vue-loader') -const core = require('./core/webpack'); -const accessibility = require('./apps/accessibility/webpack'); +const core = require('./core/webpack') -const comments = require('./apps/comments/webpack'); -const files_sharing = require('./apps/files_sharing/webpack'); -const files_trashbin = require('./apps/files_trashbin/webpack'); -const files_versions = require('./apps/files_versions/webpack'); -const oauth2 = require('./apps/oauth2/webpack'); -const systemtags = require('./apps/systemtags/webpack'); -const settings = require('./apps/settings/webpack'); -const twofactor_backupscodes = require('./apps/twofactor_backupcodes/webpack'); -const updatenotifications = require('./apps/updatenotification/webpack'); -const workflowengine = require('./apps/workflowengine/webpack'); +const accessibility = require('./apps/accessibility/webpack') +const comments = require('./apps/comments/webpack') +const files_sharing = require('./apps/files_sharing/webpack') +const files_trashbin = require('./apps/files_trashbin/webpack') +const files_versions = require('./apps/files_versions/webpack') +const oauth2 = require('./apps/oauth2/webpack') +const settings = require('./apps/settings/webpack') +const systemtags = require('./apps/systemtags/webpack') +const twofactor_backupscodes = require('./apps/twofactor_backupcodes/webpack') +const updatenotifications = require('./apps/updatenotification/webpack') +const workflowengine = require('./apps/workflowengine/webpack') module.exports = [] .concat( @@ -31,55 +32,67 @@ module.exports = [] updatenotifications, workflowengine ) - .map(config => - merge.smart({ - module: { - rules: [ - { - test: /\.css$/, - use: ['style-loader', 'css-loader'] - }, - { - test: /\.scss$/, - use: ['style-loader', 'css-loader', 'sass-loader'] - }, - { - test: /\.js$/, - loader: 'babel-loader', - exclude: /node_modules/ - }, - { - test: /\.vue$/, - loader: 'vue-loader' - }, - { - test: /\.(png|jpg|gif)$/, - loader: 'url-loader', - options: { - name: '[name].[ext]?[hash]', - limit: 8192 - } - }, - { - test: /\.handlebars/, - loader: "handlebars-loader", - query: { - extensions: '.handlebars' - } + .map(config => merge.smart({ + module: { + rules: [ + { + test: /\.css$/, + use: ['vue-style-loader', 'css-loader'] + }, + { + test: /\.scss$/, + use: ['vue-style-loader', 'css-loader', 'sass-loader'] + }, + { + test: /\.(js|vue)$/, + loader: 'eslint-loader', + // no checks against vendors, modules or handlebar compiled files + exclude: /node_modules|vendor|templates\.js/, + enforce: 'pre', + options: { + // we cannot simply use the eslint binary as we + // don't want to parse all the js files so let's + // use it from within webpack and only check + // against our compiled files + fix: process.env.ESLINT_FIX === 'true' } - ] - }, - plugins: [ - new VueLoaderPlugin() - ], - resolve: { - alias: { - OC: path.resolve(__dirname, './core/src/OC'), - OCA: path.resolve(__dirname, './core/src/OCA'), - // make sure to use the handlebar runtime when importing - handlebars: 'handlebars/runtime' }, - extensions: ['*', '.js', '.vue', '.json'] - } - }, config) - ); + { + test: /\.vue$/, + loader: 'vue-loader', + exclude: /node_modules/ + }, + { + test: /\.js$/, + loader: 'babel-loader', + exclude: /node_modules/ + }, + { + test: /\.(png|jpg|gif)$/, + loader: 'url-loader', + options: { + name: '[name].[ext]?[hash]', + limit: 8192 + } + }, + { + test: /\.handlebars/, + loader: 'handlebars-loader', + query: { + extensions: '.handlebars' + } + } + + ] + }, + plugins: [new VueLoaderPlugin()], + resolve: { + alias: { + OC: path.resolve(__dirname, './core/src/OC'), + OCA: path.resolve(__dirname, './core/src/OCA'), + // make sure to use the handlebar runtime when importing + handlebars: 'handlebars/runtime' + }, + extensions: ['*', '.js', '.vue'] + } + }, config))