Simplify code and make it use our standard components Signed-off-by: Carl Schwan <carl@carlschwan.eu>tags/v26.0.0beta1
@@ -1,74 +0,0 @@ | |||
.versionsTabView .clear-float { | |||
clear: both; | |||
} | |||
.versionsTabView li { | |||
width: 100%; | |||
cursor: default; | |||
height: 56px; | |||
float: left; | |||
border-bottom: 1px solid rgba(100,100,100,.1); | |||
} | |||
.versionsTabView li:last-child { | |||
border-bottom: none; | |||
} | |||
.versionsTabView a, | |||
.versionsTabView div > span { | |||
vertical-align: middle; | |||
opacity: .5; | |||
} | |||
.versionsTabView li a{ | |||
padding: 15px 10px 11px; | |||
} | |||
.versionsTabView a:hover, | |||
.versionsTabView a:focus { | |||
opacity: 1; | |||
} | |||
.versionsTabView .preview-container { | |||
display: inline-block; | |||
vertical-align: top; | |||
} | |||
.versionsTabView .version-container img, .revertVersion img { | |||
filter: var(--background-invert-if-dark); | |||
} | |||
.versionsTabView img { | |||
cursor: pointer; | |||
padding-right: 4px; | |||
} | |||
.versionsTabView img.preview { | |||
cursor: default; | |||
} | |||
.versionsTabView .version-container { | |||
display: inline-block; | |||
} | |||
.versionsTabView .versiondate { | |||
min-width: 100px; | |||
vertical-align: super; | |||
} | |||
.versionsTabView .version-details { | |||
text-align: left; | |||
} | |||
.versionsTabView .version-details > span { | |||
padding: 0 10px; | |||
} | |||
.versionsTabView .revertVersion { | |||
cursor: pointer; | |||
float: right; | |||
margin-right: -10px; | |||
} | |||
.versionsTabView .emptycontent { | |||
margin-top: 50px !important; | |||
} |
@@ -1,29 +0,0 @@ | |||
/** | |||
* @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> | |||
* | |||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||
* | |||
* @license AGPL-3.0-or-later | |||
* | |||
* 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/>. | |||
* | |||
*/ | |||
import './versionmodel' | |||
import './versioncollection' | |||
import './versionstabview' | |||
import './filesplugin' | |||
import './css/versions.css' | |||
window.OCA.Versions = OCA.Versions |
@@ -0,0 +1,63 @@ | |||
/** | |||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> | |||
* @license AGPL-3.0-or-later | |||
* | |||
* 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/>. | |||
* | |||
*/ | |||
import Vue from 'vue' | |||
import { translate as t, translatePlural as n } from '@nextcloud/l10n' | |||
import VersionTab from './views/VersionTab.vue' | |||
import VTooltip from 'v-tooltip' | |||
Vue.prototype.t = t | |||
Vue.prototype.n = n | |||
Vue.use(VTooltip) | |||
// Init Sharing tab component | |||
const View = Vue.extend(VersionTab) | |||
let TabInstance = null | |||
window.addEventListener('DOMContentLoaded', function() { | |||
if (OCA.Files && OCA.Files.Sidebar) { | |||
OCA.Files.Sidebar.registerTab(new OCA.Files.Sidebar.Tab({ | |||
id: 'version_vue', | |||
name: t('files_versions', 'Version'), | |||
icon: 'icon-history', | |||
async mount(el, fileInfo, context) { | |||
if (TabInstance) { | |||
TabInstance.$destroy() | |||
} | |||
TabInstance = new View({ | |||
// Better integration with vue parent component | |||
parent: context, | |||
}) | |||
// Only mount after we have all the info we need | |||
await TabInstance.update(fileInfo) | |||
TabInstance.$mount(el) | |||
}, | |||
update(fileInfo) { | |||
TabInstance.update(fileInfo) | |||
}, | |||
destroy() { | |||
TabInstance.$destroy() | |||
TabInstance = null | |||
}, | |||
})) | |||
} | |||
}) |
@@ -1,46 +0,0 @@ | |||
/** | |||
* Copyright (c) 2015 | |||
* | |||
* @author John Molakvoæ <skjnldsv@protonmail.com> | |||
* @author Vincent Petry <vincent@nextcloud.com> | |||
* | |||
* @license AGPL-3.0-or-later | |||
* | |||
* 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/>. | |||
* | |||
*/ | |||
(function() { | |||
OCA.Versions = OCA.Versions || {} | |||
/** | |||
* @namespace | |||
*/ | |||
OCA.Versions.Util = { | |||
/** | |||
* Initialize the versions plugin. | |||
* | |||
* @param {OCA.Files.FileList} fileList file list to be extended | |||
*/ | |||
attach(fileList) { | |||
if (fileList.id === 'trashbin' || fileList.id === 'files.public') { | |||
return | |||
} | |||
fileList.registerTabView(new OCA.Versions.VersionsTabView('versionsTabView', { order: -10 })) | |||
}, | |||
} | |||
})() | |||
OC.Plugins.register('OCA.Files.FileList', OCA.Versions.Util) |
@@ -1,22 +0,0 @@ | |||
<li data-revision="{{id}}"> | |||
<div> | |||
<div class="preview-container"> | |||
<img class="preview" src="{{previewUrl}}" width="44" height="44"/> | |||
</div> | |||
<div class="version-container"> | |||
<div> | |||
<a href="{{downloadUrl}}" class="downloadVersion" download="{{downloadName}}"><img src="{{downloadIconUrl}}" /> | |||
<span class="versiondate has-tooltip live-relative-timestamp" data-timestamp="{{millisecondsTimestamp}}" title="{{formattedTimestamp}}">{{relativeTimestamp}}</span> | |||
</a> | |||
</div> | |||
{{#hasDetails}} | |||
<div class="version-details"> | |||
<span class="size has-tooltip" title="{{altSize}}">{{humanReadableSize}}</span> | |||
</div> | |||
{{/hasDetails}} | |||
</div> | |||
{{#canRevert}} | |||
<a href="#" class="revertVersion" title="{{revertLabel}}"><img src="{{revertIconUrl}}" /></a> | |||
{{/canRevert}} | |||
</div> | |||
</li> |
@@ -1,10 +0,0 @@ | |||
<ul class="versions"></ul> | |||
<div class="clear-float"></div> | |||
<div class="empty hidden"> | |||
<div class="emptycontent"> | |||
<div class="icon-history"></div> | |||
<p>{{emptyResultLabel}}</p> | |||
</div> | |||
</div> | |||
<input type="button" class="showMoreVersions hidden" value="{{moreVersionsLabel}}" name="show-more-versions" id="show-more-versions" /> | |||
<div class="loading hidden" style="height: 50px"></div> |
@@ -1,97 +0,0 @@ | |||
/** | |||
* Copyright (c) 2015 | |||
* | |||
* @author John Molakvoæ <skjnldsv@protonmail.com> | |||
* @author Robin Appelman <robin@icewind.nl> | |||
* @author Vincent Petry <vincent@nextcloud.com> | |||
* | |||
* @license AGPL-3.0-or-later | |||
* | |||
* 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/>. | |||
* | |||
*/ | |||
(function() { | |||
/** | |||
* @memberof OCA.Versions | |||
*/ | |||
const VersionCollection = OC.Backbone.Collection.extend({ | |||
model: OCA.Versions.VersionModel, | |||
sync: OC.Backbone.davSync, | |||
/** | |||
* @member OCA.Files.FileInfoModel | |||
*/ | |||
_fileInfo: null, | |||
_currentUser: null, | |||
_client: null, | |||
setFileInfo(fileInfo) { | |||
this._fileInfo = fileInfo | |||
}, | |||
getFileInfo() { | |||
return this._fileInfo | |||
}, | |||
setCurrentUser(user) { | |||
this._currentUser = user | |||
}, | |||
getCurrentUser() { | |||
return this._currentUser || OC.getCurrentUser().uid | |||
}, | |||
setClient(client) { | |||
this._client = client | |||
}, | |||
getClient() { | |||
return this._client || new OC.Files.Client({ | |||
host: OC.getHost(), | |||
root: OC.linkToRemoteBase('dav') + '/versions/' + this.getCurrentUser(), | |||
useHTTPS: OC.getProtocol() === 'https', | |||
}) | |||
}, | |||
url() { | |||
return OC.linkToRemoteBase('dav') + '/versions/' + this.getCurrentUser() + '/versions/' + this._fileInfo.get('id') | |||
}, | |||
parse(result) { | |||
const fullPath = this._fileInfo.getFullPath() | |||
const fileId = this._fileInfo.get('id') | |||
const name = this._fileInfo.get('name') | |||
const user = this.getCurrentUser() | |||
const 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 | |||
}) | |||
}, | |||
}) | |||
OCA.Versions = OCA.Versions || {} | |||
OCA.Versions.VersionCollection = VersionCollection | |||
})() |
@@ -1,86 +0,0 @@ | |||
/** | |||
* Copyright (c) 2015 | |||
* | |||
* @author John Molakvoæ <skjnldsv@protonmail.com> | |||
* @author Robin Appelman <robin@icewind.nl> | |||
* @author Vincent Petry <vincent@nextcloud.com> | |||
* | |||
* @license AGPL-3.0-or-later | |||
* | |||
* 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/>. | |||
* | |||
*/ | |||
(function() { | |||
/** | |||
* @memberof OCA.Versions | |||
*/ | |||
const VersionModel = OC.Backbone.Model.extend({ | |||
sync: OC.Backbone.davSync, | |||
davProperties: { | |||
size: '{DAV:}getcontentlength', | |||
mimetype: '{DAV:}getcontenttype', | |||
timestamp: '{DAV:}getlastmodified', | |||
}, | |||
/** | |||
* Restores the original file to this revision | |||
* | |||
* @param {object} [options] options | |||
* @return {Promise} | |||
*/ | |||
revert(options) { | |||
options = options ? _.clone(options) : {} | |||
const model = this | |||
const client = this.get('client') | |||
return client.move('/versions/' + this.get('fileId') + '/' + this.get('id'), '/restore/target', true) | |||
.done(function() { | |||
if (options.success) { | |||
options.success.call(options.context, model, {}, options) | |||
} | |||
model.trigger('revert', model, options) | |||
}) | |||
.fail(function() { | |||
if (options.error) { | |||
options.error.call(options.context, model, {}, options) | |||
} | |||
model.trigger('error', model, {}, options) | |||
}) | |||
}, | |||
getFullPath() { | |||
return this.get('fullPath') | |||
}, | |||
getPreviewUrl() { | |||
const url = OC.generateUrl('/apps/files_versions/preview') | |||
const params = { | |||
file: this.get('fullPath'), | |||
version: this.get('id'), | |||
} | |||
return url + '?' + OC.buildQueryString(params) | |||
}, | |||
getDownloadUrl() { | |||
return OC.linkToRemoteBase('dav') + '/versions/' + this.get('user') + '/versions/' + this.get('fileId') + '/' + this.get('id') | |||
}, | |||
}) | |||
OCA.Versions = OCA.Versions || {} | |||
OCA.Versions.VersionModel = VersionModel | |||
})() |
@@ -1,231 +0,0 @@ | |||
/** | |||
* Copyright (c) 2015 | |||
* | |||
* @author Jan-Christoph Borchardt <hey@jancborchardt.net> | |||
* @author John Molakvoæ <skjnldsv@protonmail.com> | |||
* @author Julius Härtl <jus@bitgrid.net> | |||
* @author Michael Jobst <mjobst+github@tecratech.de> | |||
* @author noveens <noveen.sachdeva@research.iiit.ac.in> | |||
* @author Robin Appelman <robin@icewind.nl> | |||
* @author Vincent Petry <vincent@nextcloud.com> | |||
* | |||
* @license AGPL-3.0-or-later | |||
* | |||
* 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/>. | |||
* | |||
*/ | |||
import ItemTemplate from './templates/item.handlebars' | |||
import Template from './templates/template.handlebars'; | |||
(function() { | |||
if (!OCA.Files.DetailTabView) { | |||
// Only register the versions tab within the files app | |||
return | |||
} | |||
/** | |||
* @memberof OCA.Versions | |||
*/ | |||
const VersionsTabView = OCA.Files.DetailTabView.extend(/** @lends OCA.Versions.VersionsTabView.prototype */{ | |||
id: 'versionsTabView', | |||
className: 'tab versionsTabView', | |||
_template: null, | |||
$versionsContainer: null, | |||
events: { | |||
'click .revertVersion': '_onClickRevertVersion', | |||
}, | |||
initialize() { | |||
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() { | |||
return t('files_versions', 'Versions') | |||
}, | |||
getIcon() { | |||
return 'icon-history' | |||
}, | |||
nextPage() { | |||
if (this._loading) { | |||
return | |||
} | |||
if (this.collection.getFileInfo() && this.collection.getFileInfo().isDirectory()) { | |||
return | |||
} | |||
this.collection.fetch() | |||
}, | |||
_onClickRevertVersion(ev) { | |||
const self = this | |||
let $target = $(ev.target) | |||
const fileInfoModel = this.collection.getFileInfo() | |||
if (!$target.is('li')) { | |||
$target = $target.closest('li') | |||
} | |||
ev.preventDefault() | |||
const revision = $target.attr('data-revision') | |||
const versionModel = this.collection.get(revision) | |||
versionModel.revert({ | |||
success() { | |||
// reset and re-fetch the updated collection | |||
self.$versionsContainer.empty() | |||
self.collection.setFileInfo(fileInfoModel) | |||
self.collection.reset([], { silent: true }) | |||
self.collection.fetch() | |||
self.$el.find('.versions').removeClass('hidden') | |||
// update original model | |||
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() { | |||
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', | |||
} | |||
) | |||
}, | |||
}) | |||
// spinner | |||
this._toggleLoading(true) | |||
fileInfoModel.trigger('busy', fileInfoModel, true) | |||
}, | |||
_toggleLoading(state) { | |||
this._loading = state | |||
this.$el.find('.loading').toggleClass('hidden', !state) | |||
}, | |||
_onRequest() { | |||
this._toggleLoading(true) | |||
}, | |||
_onEndRequest() { | |||
this._toggleLoading(false) | |||
this.$el.find('.empty').toggleClass('hidden', !!this.collection.length) | |||
}, | |||
_onAddModel(model) { | |||
const $el = $(this.itemTemplate(this._formatItem(model))) | |||
this.$versionsContainer.append($el) | |||
$el.find('.has-tooltip').tooltip() | |||
}, | |||
template(data) { | |||
return Template(data) | |||
}, | |||
itemTemplate(data) { | |||
return ItemTemplate(data) | |||
}, | |||
setFileInfo(fileInfo) { | |||
if (fileInfo) { | |||
this.render() | |||
this.collection.setFileInfo(fileInfo) | |||
this.collection.reset([], { silent: true }) | |||
this.nextPage() | |||
} else { | |||
this.render() | |||
this.collection.reset() | |||
} | |||
}, | |||
_formatItem(version) { | |||
const timestamp = version.get('timestamp') * 1000 | |||
const size = version.has('size') ? version.get('size') : 0 | |||
const preview = OC.MimeType.getIconUrl(version.get('mimetype')) | |||
const 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'), | |||
formattedTimestamp: OC.Util.formatDate(timestamp), | |||
relativeTimestamp: OC.Util.relativeModifiedDate(timestamp), | |||
millisecondsTimestamp: timestamp, | |||
humanReadableSize: OC.Util.humanFileSize(size, true), | |||
altSize: n('files', '%n byte', '%n bytes', size), | |||
hasDetails: version.has('size'), | |||
downloadUrl: version.getDownloadUrl(), | |||
downloadIconUrl: OC.imagePath('core', 'actions/download'), | |||
downloadName: version.get('name'), | |||
revertIconUrl: OC.imagePath('core', 'actions/history'), | |||
previewUrl: preview, | |||
revertLabel: t('files_versions', 'Restore'), | |||
canRevert: (this.collection.getFileInfo().get('permissions') & OC.PERMISSION_UPDATE) !== 0, | |||
}, version.attributes) | |||
}, | |||
/** | |||
* Renders this details view | |||
*/ | |||
render() { | |||
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() | |||
}, | |||
/** | |||
* Returns true for files, false for folders. | |||
* | |||
* @param {FileInfo} fileInfo fileInfo | |||
* @return {boolean} true for files, false for folders | |||
*/ | |||
canDisplay(fileInfo) { | |||
if (!fileInfo) { | |||
return false | |||
} | |||
return !fileInfo.isDirectory() | |||
}, | |||
}) | |||
OCA.Versions = OCA.Versions || {} | |||
OCA.Versions.VersionsTabView = VersionsTabView | |||
})() |
@@ -0,0 +1,222 @@ | |||
<!-- | |||
- @copyright 2022 Carl Schwan <carl@carlschwan.eu> | |||
- @license AGPL-3.0-or-later | |||
- | |||
- 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> | |||
<ul> | |||
<li v-for="version in versions" class="version"> | |||
<img lazy="true" | |||
:src="version.preview" | |||
height="256" | |||
width="256" | |||
class="version-image"> | |||
<div class="version-info"> | |||
<a v-tooltip="version.dateTime" :href="version.url">{{ version.relativeTime }}</a> | |||
<div class="version-info-size"> | |||
{{ version.size }} | |||
</div> | |||
</div> | |||
<NcButton v-tooltip="t('files_versions', `Download file ${fileInfo.name} with version ${version.displayVersionName}`)" | |||
type="secondary" | |||
class="download-button" | |||
:href="version.url" | |||
:aria-label="t('files_versions', `Download file ${fileInfo.name} with version ${version.displayVersionName}`)"> | |||
<template #icon> | |||
<Download :size="22" /> | |||
</template> | |||
</NcButton> | |||
<NcButton v-tooltip="t('files_versions', `Restore file ${fileInfo.name} with version ${version.displayVersionName}`)" | |||
type="secondary" | |||
class="restore-button" | |||
:aria-label="t('files_versions', `Restore file ${fileInfo.name} with version ${version.displayVersionName}`)" | |||
@click="restoreVersion(version)"> | |||
<template #icon> | |||
<BackupRestore :size="22" /> | |||
</template> | |||
</NcButton> | |||
</li> | |||
</ul> | |||
</div> | |||
</template> | |||
<script> | |||
import { createClient, getPatcher } from 'webdav' | |||
import axios from '@nextcloud/axios' | |||
import parseUrl from 'url-parse' | |||
import { generateRemoteUrl, generateUrl } from '@nextcloud/router' | |||
import { getCurrentUser } from '@nextcloud/auth' | |||
import { translate } from '@nextcloud/l10n' | |||
import BackupRestore from 'vue-material-design-icons/BackupRestore.vue' | |||
import Download from 'vue-material-design-icons/Download.vue' | |||
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' | |||
import { showError, showSuccess } from '@nextcloud/dialogs' | |||
import moment from '@nextcloud/moment' | |||
import { basename, joinPaths } from '@nextcloud/paths' | |||
/** | |||
* | |||
*/ | |||
function getDavRequest() { | |||
return `<?xml version="1.0"?> | |||
<d:propfind xmlns:d="DAV:" | |||
xmlns:oc="http://owncloud.org/ns" | |||
xmlns:nc="http://nextcloud.org/ns" | |||
xmlns:ocs="http://open-collaboration-services.org/ns"> | |||
<d:prop> | |||
<d:getcontentlength /> | |||
<d:getcontenttype /> | |||
<d:getlastmodified /> | |||
</d:prop> | |||
</d:propfind>` | |||
} | |||
/** | |||
* | |||
* @param version | |||
* @param fileInfo | |||
*/ | |||
function formatVersion(version, fileInfo) { | |||
const fileVersion = basename(version.filename) | |||
const preview = generateUrl('/apps/files_versions/preview?file={file}&version={fileVersion}', { | |||
file: joinPaths(fileInfo.path, fileInfo.name), | |||
fileVersion, | |||
}) | |||
return { | |||
displayVersionName: fileVersion, | |||
fileName: version.filename, | |||
mimeType: version.mime, | |||
size: OC.Util.humanFileSize(version.size), | |||
type: version.type, | |||
dateTime: moment(version.lastmod), | |||
relativeTime: moment(version.lastmod).fromNow(), | |||
preview, | |||
url: joinPaths('/remote.php/dav', version.filename), | |||
fileVersion, | |||
} | |||
} | |||
export default { | |||
name: 'VersionTab', | |||
components: { | |||
NcButton, | |||
BackupRestore, | |||
Download, | |||
}, | |||
data() { | |||
const rootPath = 'dav' | |||
// force our axios | |||
const patcher = getPatcher() | |||
patcher.patch('request', axios) | |||
// init webdav client on default dav endpoint | |||
const remote = generateRemoteUrl(rootPath) | |||
const client = createClient(remote) | |||
return { | |||
fileInfo: null, | |||
versions: [], | |||
client, | |||
remote, | |||
} | |||
}, | |||
methods: { | |||
/** | |||
* Update current fileInfo and fetch new data | |||
* | |||
* @param {object} fileInfo the current file FileInfo | |||
*/ | |||
async update(fileInfo) { | |||
this.fileInfo = fileInfo | |||
this.resetState() | |||
this.fetchVersions() | |||
}, | |||
/** | |||
* Get the existing versions infos | |||
*/ | |||
async fetchVersions() { | |||
const path = `/versions/${getCurrentUser().uid}/versions/${this.fileInfo.id}` | |||
const remotePath = parseUrl(this.remote).pathname | |||
const response = await this.client.getDirectoryContents(path, { | |||
data: getDavRequest(), | |||
}) | |||
this.versions = response.filter(version => version.mime !== '') | |||
.map(version => formatVersion(version, this.fileInfo)) | |||
}, | |||
/** | |||
* Restore the given version | |||
* | |||
* @param version | |||
*/ | |||
async restoreVersion(version) { | |||
try { | |||
console.debug('restore version', version.url) | |||
const response = await this.client.moveFile( | |||
`/versions/${getCurrentUser().uid}/versions/${this.fileInfo.id}/${version.fileVersion}`, | |||
`/versions/${getCurrentUser().uid}/restore/target` | |||
) | |||
showSuccess(t('files_versions', 'Version restored')) | |||
await this.fetchVersions() | |||
} catch (exception) { | |||
console.error('Could not restore version', exception) | |||
showError(t('files_versions', 'Could not restore version')) | |||
} | |||
}, | |||
/** | |||
* Reset the current view to its default state | |||
*/ | |||
resetState() { | |||
this.versions = [] | |||
}, | |||
}, | |||
} | |||
</script> | |||
<style scopped lang="scss"> | |||
.version { | |||
display: flex; | |||
flex-direction: row; | |||
&-info { | |||
display: flex; | |||
flex-direction: column; | |||
&-size { | |||
color: var(--color-text-lighter); | |||
} | |||
} | |||
&-image { | |||
width: 3rem; | |||
height: 3rem; | |||
filter: drop-shadow(0 1px 2px var(--color-box-shadow)); | |||
margin-right: 1rem; | |||
border-radius: var(--border-radius); | |||
} | |||
.restore-button { | |||
margin-left: 1rem; | |||
align-self: center; | |||
} | |||
.download-button { | |||
margin-left: auto; | |||
align-self: center; | |||
} | |||
} | |||
</style> |
@@ -65,7 +65,7 @@ module.exports = { | |||
files_trashbin: path.join(__dirname, 'apps/files_trashbin/src', 'files_trashbin.js'), | |||
}, | |||
files_versions: { | |||
files_versions: path.join(__dirname, 'apps/files_versions/src', 'files_versions.js'), | |||
files_versions: path.join(__dirname, 'apps/files_versions/src', 'files_versions_tab.js'), | |||
}, | |||
oauth2: { | |||
oauth2: path.join(__dirname, 'apps/oauth2/src', 'main.js'), |