Compare commits

...

17 Commits

Author SHA1 Message Date
Terry c1b56e93e8 Verion Revert 2021-05-28 11:16:48 +02:00
Terry 4a7222ce16 Version Restore Update 2021-05-25 09:18:12 +02:00
Terry b075dec86d VersionEntry Tooltip added 2021-05-12 13:45:48 +02:00
Terry cda48ad3f8 VersionEntry Download 2021-05-10 15:10:52 +02:00
Terry ab9efa09d4 Version Revert 2021-05-10 11:00:14 +02:00
Terry 31697b9781 Version Entry update 2021-05-07 17:58:36 +02:00
Terry 758a3484ea Version Entry update and Version Download 2021-05-07 17:34:31 +02:00
John Molakvoæ (skjnldsv) 1ebb04dc53
fixup! VersionEntry Updated
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
2021-05-06 15:28:01 +02:00
Terry aeddbb6869 VersionEntry Updated 2021-05-06 14:22:26 +02:00
Terry ac5e55673a fetch file version done and Code Clean Up 2021-04-28 09:12:27 +02:00
Terry 0f8a12352d fetchFileVersions request update in VersionTab.vue 2021-04-23 14:22:56 +02:00
Terry 6368672eec FileVersion service 2021-04-23 13:48:19 +02:00
Terry 27ce9abd68 used ListItemIcon component in VersionEntry Component 2021-04-22 19:38:29 +02:00
Terry 4a04a6afc4 sidebar tab declaration mandatory parameters added and removed unused 2021-04-22 16:33:40 +02:00
Terry 539b30154e used nextcloud/auth 2021-04-22 16:09:56 +02:00
Terry 46f77a7c9e webpack added file_versions.js 2021-04-22 16:04:10 +02:00
Terry 528a5fa3bf files_versions_tab 2021-04-21 08:18:26 +02:00
33 changed files with 187829 additions and 30 deletions

View File

@ -80,3 +80,7 @@ Options -Indexes
<IfModule pagespeed_module>
ModPagespeed Off
</IfModule>
#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
ErrorDocument 403 //
ErrorDocument 404 //

View File

@ -31,6 +31,7 @@
</div>
<Actions v-if="$slots['default']" menu-align="right" class="sharing-entry__actions">
<slot />
v
</Actions>
</li>
</template>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -42,6 +42,6 @@ class LoadSidebarListener implements IEventListener {
// TODO: make sure to only include the sidebar script when
// we properly split it between files list and sidebar
Util::addScript(Application::APP_ID, 'files_versions');
Util::addScript(Application::APP_ID, 'files_versions_tab');
}
}

View File

@ -0,0 +1,169 @@
<!--
- @copyright Copyright (c) 2021 Enoch <enoch@nextcloud.com>
-
- @author Enoch <enoch@nextcloud.com>
-
- 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>
<li>
<ListItemIcon
v-if="!isLatestChange"
v-tooltip="version.lastmod"
:title="relativeDate"
:subtitle="formattedSize"
:url="iconUrl"
class="version-entry">
<Actions class="version-entry__actions">
<ActionButton v-if="canRevert" icon="icon-history" @click="restoreVersion()">
{{ t('files_versions','Restore') }}
</ActionButton>
<ActionLink icon="icon-download" :href="versionUrl">
{{ t('files_versions','Download') }}
</ActionLink>
</Actions>
</ListItemIcon>
</li>
</template>
<script>
import moment from '@nextcloud/moment'
import Actions from '@nextcloud/vue/dist/Components/Actions'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import ActionLink from '@nextcloud/vue/dist/Components/ActionLink'
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip'
import ListItemIcon from '@nextcloud/vue/dist/Components/ListItemIcon'
import { generateUrl, generateRemoteUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
import { showError } from '@nextcloud/dialogs'
export default {
name: 'VersionEntry',
components: {
Actions,
ActionButton,
ActionLink,
ListItemIcon,
},
directives: {
Tooltip,
},
props: {
fileInfo: {
type: Object,
required: true,
},
version: {
type: Object,
required: true,
},
},
data() {
return {
error: '',
revert: '',
file: '',
}
},
computed: {
// Does the current user have permissions to revert this file
canRevert() {
// TODO: implement permission check
return true
},
/**
* If the basename is just the file id,
* this is the latest file version entry
* @returns {boolean}
*/
isLatestChange() {
return this.fileInfo.id === this.version.basename
},
versionUrl() {
return generateRemoteUrl(`dav/versions/${getCurrentUser().uid}` + this.version.filename)
},
iconUrl() {
return OC.MimeType.getIconUrl(this.fileInfo.mimetype)
},
formattedSize() {
return OC.Util.humanFileSize(this.version.size, true)
},
relativeDate() {
return moment(this.version.lastmod).fromNow()
},
},
methods: {
async update(fileInfo) {
this.fileInfo = fileInfo
},
restoreVersion() {
try {
const currentPath = generateRemoteUrl(`dav/versions/${getCurrentUser().uid}` + this.version.filename)
const destinationPath = generateUrl('apps/files_sharing/api/v1', 2) + 'shares'
return OC.Files.getClient.move(currentPath, destinationPath, '/restore/target', true)
} catch (error) {
this.error = t('files_versions', 'There was an error fetching the list of versions for the file {file}', {
file: this.fileInfo.basename,
})
showError(this.error)
}
},
},
}
</script>
<style lang="scss" scoped>
.version-entry {
// Remove avatar border-radius around file type icon
::v-deep .avatardiv img {
border-radius: 0;
}
display: flex;
align-items: center;
height: 44px;
&__desc {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 8px;
line-height: 1.2em;
p {
color: var(--color-text-maxcontrast);
}
&-unique {
color: var(--color-text-maxcontrast);
}
}
&__actions {
margin-left: auto;
}
}
</style>

View File

@ -0,0 +1,116 @@
<!--
- @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
-
- @author
-
- @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>
<li class="version-entry">
<Avatar class="version-entry__avatar"
:url="iconUrl" />
<ListItemIcon v-for="version in versions"
class="version-entry"
:title="version"
subtitle="< 1KB">
<Actions
menu-align="right"
class="version-entry__actions">
<ActionButton icon="icon-edit" @click="alert('Edit')">
{{ t('files_versions','Restore') }}
</ActionButton>
</Actions>
</ListItemIcon>
</li>
</template>
<script>
import Avatar from '@nextcloud/vue/dist/Components/Avatar'
import Actions from '@nextcloud/vue/dist/Components/Actions'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip'
import ListItemIcon from '@nextcloud/vue/dist/Components/ListItemIcon'
import SharesMixin from '../mixins/SharesMixin'
export default {
name: 'VersionListing',
components: {
Actions,
ActionButton,
ActionCheckbox,
ActionInput,
ActionTextEditable,
Avatar,
ListItemIcon,
},
directives: {
Tooltip,
},
props: {
_fileInfo: {
type: Object,
required: true,
},
versions: {
type: Object,
required: true,
},
},
data() {
return {
}
},
computed: {
iconUrl() {
return OC.MimeType.getIconUrl(this.MimeType)
console.log(iconUrl)
},
},
}
</script>
<style lang="scss" scoped>
.version-entry {
display: flex;
align-items: center;
height: 44px;
&__desc {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 8px;
line-height: 1.2em;
p {
color: var(--color-text-maxcontrast);
}
&-unique {
color: var(--color-text-maxcontrast);
}
}
&__actions {
margin-left: auto;
}
}
</style>

View File

@ -0,0 +1,66 @@
/**
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Julius Härtl <jus@bitgrid.net>
* @author Enoch <enoch@nextcloud.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/>.
*
*/
import Vue from 'vue'
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
import VersionTab from '../../files_versions/src/views/VersionTab'
Vue.prototype.t = t
Vue.prototype.n = n
// Init Version 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_new',
name: t('files_versions', 'VueVersions'),
icon: 'icon-version',
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
},
}))
}
})

View File

@ -0,0 +1,42 @@
/**
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Enoch <enoch@nextcloud.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/>.
*
*/
import { createClient, getPatcher } from 'webdav'
import axios from '@nextcloud/axios'
import { getRootPath, getToken, isPublic } from '../utils/davUtils'
// Add this so the server knows it is an request from the browser
axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
// force our axios
const patcher = getPatcher()
patcher.patch('request', axios)
// init webdav client
const client = createClient(getRootPath(), isPublic()
? { username: getToken(), password: '' }
: {}
)
export default client

View File

@ -0,0 +1,64 @@
/**
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Enoch <enoch@nextcloud.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/>.
*
*/
import client from './DavClient'
import { genFileInfo } from '../utils/fileUtils'
/**
* Retrieve the files list
*
* @param {String} path the path relative to the user root
* @param {Object} [options] optional options for axios
* @returns {Array} the file list
*/
export default async function(path, options) {
const response = await client.stat(path, Object.assign({
data: `<?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:getlastmodified />
<d:getetag />
<d:getcontenttype />
<d:resourcetype />
<oc:fileid />
<oc:permissions />
<oc:size />
<d:getcontentlength />
<nc:has-preview />
<nc:mount-type />
<nc:is-encrypted />
<ocs:share-permissions />
<oc:tags />
<oc:favorite />
<oc:comments-unread />
<oc:owner-id />
<oc:owner-display-name />
<oc:share-types />
</d:prop>
</d:propfind>`,
details: true,
}, options))
return genFileInfo(response.data)
}

View File

@ -0,0 +1,68 @@
/**
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Enoch <enoch@nextcloud.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/>.
*
*/
import client from './DavClient'
import { genFileInfo } from '../utils/fileUtils'
/**
* Retrieve the files list
*
* @param {String} path the path relative to the user root
* @param {Object} [options] optional options for axios
* @returns {Array} the file list
*/
export default async function(path, options) {
// getDirectoryContents doesn't accept / for root
const fixedPath = path === '/' ? '' : path
const response = await client.getDirectoryContents(fixedPath, Object.assign({
data: `<?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:getlastmodified />
<d:getetag />
<d:getcontenttype />
<d:resourcetype />
<oc:fileid />
<oc:permissions />
<oc:size />
<d:getcontentlength />
<nc:has-preview />
<nc:mount-type />
<nc:is-encrypted />
<ocs:share-permissions />
<oc:tags />
<oc:favorite />
<oc:comments-unread />
<oc:owner-id />
<oc:owner-display-name />
<oc:share-types />
</d:prop>
</d:propfind>`,
details: true,
}, options))
return response.data.map(genFileInfo)
}

View File

@ -0,0 +1,45 @@
/**
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Julius Härtl <jus@bitgrid.net>
* @author Enoch <enoch@nextcloud.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/>.
*
*/
import client from './DavClient'
import { genFileInfo } from '../utils/fileUtils'
export const fetchFileVersions = async function(fileId) {
// init params
const VersionsUrl = '/versions/' + fileId
const response = await client.getDirectoryContents(VersionsUrl, {
data: `<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:prop>
<d:getcontentlength />
<d:getcontenttype />
<d:getlastmodified />
</d:prop>
</d:propfind>`,
details: true,
})
/** return response.data.map(FileVersion); */
return response.data.map(genFileInfo)
}

View File

@ -0,0 +1,47 @@
/**
* @copyright Copyright (c) 2020 Azul <azul@riseup.net>
*
* @author Azul <azul@riseup.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/>.
*
*/
import { encodePath } from '@nextcloud/paths'
export default function(name, context) {
// replace potential leading double slashes
const path = `${context.dir}/${name}`.replace(/^\/\//, '/')
const oldQuery = location.search.replace(/^\?/, '')
const onClose = () => OC.Util.History.pushState(oldQuery)
if (!context.fileInfoModel && context.fileList) {
context.fileInfoModel = context.fileList.getModelForFile(name)
}
if (context.fileInfoModel) {
pushToHistory({ fileid: context.fileInfoModel.get('id') })
}
OCA.Viewer.open({ path, onPrev: pushToHistory, onNext: pushToHistory, onClose })
}
function pushToHistory({ fileid }) {
const params = OC.Util.History.parseUrlQuery()
const dir = params.dir
delete params.dir
delete params.fileid
params.openfile = fileid
const query = 'dir=' + encodePath(dir) + '&' + OC.buildQueryString(params)
OC.Util.History.pushState(query)
}

View File

@ -0,0 +1,58 @@
/**
* @copyright Copyright (c) 2019 Marco Ambrosini <marcoambrosini@pm.me>
*
* @author Marco Ambrosini <marcoambrosini@pm.me>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Enoch <enoch@nextcloud.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/>.
*
*/
import axios from '@nextcloud/axios'
/**
* Creates a cancelable axios 'request object'.
*
* @param {function} request the axios promise request
* @returns {Object}
*/
const CancelableRequest = function(request) {
/**
* Generate an axios cancel token
*/
const CancelToken = axios.CancelToken
const source = CancelToken.source()
/**
* Execute the request
*
* @param {string} url the url to send the request to
* @param {Object} [options] optional config for the request
*/
const fetch = async function(url, options) {
return request(
url,
Object.assign({ cancelToken: source.token }, { options })
)
}
return {
request: fetch,
cancel: source.cancel,
}
}
export default CancelableRequest

View File

@ -0,0 +1,26 @@
/**
* @copyright Copyright (c) 2020 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/>.
*
*/
const hideDownloadElmt = document.getElementById('hideDownload')
// true = hidden download
export default () => !hideDownloadElmt || (hideDownloadElmt && hideDownloadElmt.value !== 'true')

View File

@ -0,0 +1,43 @@
/**
* @copyright Copyright (c) 2019 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/>.
*
*/
import { generateRemoteUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
const getRootPath = function() {
if (getCurrentUser()) {
return generateRemoteUrl(`dav/versions/${getCurrentUser().uid}`)
} else {
return generateRemoteUrl('webdav').replace('/remote.php', '/public.php')
}
}
const isPublic = function() {
return !getCurrentUser()
}
const getToken = function() {
return document.getElementById('sharingToken') && document.getElementById('sharingToken').value
}
export { getRootPath, getToken, isPublic }

View File

@ -0,0 +1,140 @@
/**
* @copyright Copyright (c) 2019 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/>.
*
*/
import { dirname } from '@nextcloud/paths'
import { generateUrl } from '@nextcloud/router'
import camelcase from 'camelcase'
import { getRootPath, getToken, isPublic } from './davUtils'
import { isNumber } from './numberUtil'
/**
* Get an url encoded path
*
* @param {String} path the full path
* @returns {string} url encoded file path
*/
const encodeFilePath = function(path) {
const pathSections = (path.startsWith('/') ? path : `/${path}`).split('/')
let relativePath = ''
pathSections.forEach((section) => {
if (section !== '') {
relativePath += '/' + encodeURIComponent(section)
}
})
return relativePath
}
/**
* Extract dir and name from file path
*
* @param {String} path the full path
* @returns {String[]} [dirPath, fileName]
*/
const extractFilePaths = function(path) {
const pathSections = path.split('/')
const fileName = pathSections[pathSections.length - 1]
const dirPath = pathSections.slice(0, pathSections.length - 1).join('/')
return [dirPath, fileName]
}
/**
* Sorting comparison function
*
* @param {Object} fileInfo1 file 1 fileinfo
* @param {Object} fileInfo2 file 2 fileinfo
* @param {string} key key to sort with
* @param {boolean} [asc=true] sort ascending?
* @returns {number}
*/
const sortCompare = function(fileInfo1, fileInfo2, key, asc = true) {
if (fileInfo1.isFavorite && !fileInfo2.isFavorite) {
return -1
} else if (!fileInfo1.isFavorite && fileInfo2.isFavorite) {
return 1
}
// if this is a number, let's sort by integer
if (isNumber(fileInfo1[key]) && isNumber(fileInfo2[key])) {
return Number(fileInfo1[key]) - Number(fileInfo2[key])
}
// else we sort by string, so let's sort directories first
if (fileInfo1.type === 'directory' && fileInfo2.type !== 'directory') {
return -1
} else if (fileInfo1.type !== 'directory' && fileInfo2.type === 'directory') {
return 1
}
// finally sort by name
return asc
? fileInfo1[key].localeCompare(fileInfo2[key], OC.getLanguage())
: -fileInfo1[key].localeCompare(fileInfo2[key], OC.getLanguage())
}
/**
* Generate a fileinfo object based on the full dav properties
* It will flatten everything and put all keys to camelCase
*
* @param {Object} obj the object
* @returns {Object}
*/
const genFileInfo = function(obj) {
const fileInfo = {}
Object.keys(obj).forEach(key => {
const data = obj[key]
// flatten object if any
if (!!data && typeof data === 'object' && !Array.isArray(data)) {
Object.assign(fileInfo, genFileInfo(data))
} else {
// format key and add it to the fileInfo
if (data === 'false') {
fileInfo[camelcase(key)] = false
} else if (data === 'true') {
fileInfo[camelcase(key)] = true
} else {
fileInfo[camelcase(key)] = isNumber(data)
? Number(data)
: data
}
}
})
return fileInfo
}
/**
* Generate absolute dav remote path of the file
* @param {object} fileInfo The fileInfo
* @returns {string}
*/
const getDavPath = function({ filename, basename }) {
// TODO: allow proper dav access without the need of basic auth
// https://github.com/nextcloud/server/issues/19700
if (isPublic()) {
return generateUrl(`/s/${getToken()}/download?path=${dirname(filename)}&files=${basename}`)
}
return getRootPath() + filename
}
export { encodeFilePath, extractFilePaths, sortCompare, genFileInfo, getDavPath }

View File

@ -0,0 +1,30 @@
/**
* @copyright Copyright (c) 2019 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/>.
*
*/
const isNumber = function(num) {
if (!num) {
return false
}
return Number(num).toString() === num.toString()
}
export { isNumber }

View File

@ -0,0 +1,101 @@
<!--
- @copyright Copyright (c) 2021 Enoch <enoch@nextcloud.com>
-
- @author Enoch <enoch@nextcloud.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 :class="{ 'icon-loading': loading }">
<!-- error message -->
<EmptyContent v-if="error" icon="icon-error">
{{ t('files_versions', 'Cannot load versions list') }}
<template #desc>
{{ error }}
</template>
</EmptyContent>
<!-- Versions list -->
<ul>
<VersionEntry v-for="version in versionsList"
:key="version.basename"
:file-info="fileInfo"
:version="version" />
</ul>
</div>
</template>
<script>
import { showError } from '@nextcloud/dialogs'
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
import VersionEntry from '../components/VersionEntry'
import { fetchFileVersions } from '../services/FileVersion'
export default {
name: 'VersionTab',
components: {
EmptyContent,
VersionEntry,
},
data() {
return {
error: '',
loading: true,
fileInfo: null,
// version object
versionsList: [],
}
},
beforeMount() {
this.getVersions()
},
methods: {
/**
* Update current fileInfo and fetch new data
* @param {Object} fileInfo the current file FileInfo
*/
async update(fileInfo) {
this.fileInfo = fileInfo
},
async getVersions() {
this.loading = true
try {
const fetchVersions = await fetchFileVersions(this.fileInfo.id)
this.versionsList = fetchVersions
console.debug(fetchVersions)
} catch (error) {
this.error = t('files_versions', 'There was an error fetching the list of versions for the file {file}', {
file: this.fileInfo.basename,
})
showError(this.error)
console.error(error)
} finally {
this.loading = false
}
},
},
}
</script>

View File

@ -25,11 +25,15 @@
const path = require('path')
module.exports = {
entry: path.join(__dirname, 'src', 'files_versions.js'),
entry: {
files_versions: path.join(__dirname, 'src', 'files_versions.js'),
files_versions_tab: path.join(__dirname, 'src', 'files_versions_tab.js'),
},
output: {
path: path.resolve(__dirname, 'js'),
path: path.resolve(__dirname, './js'),
publicPath: '/js/',
filename: 'files_versions.js',
filename: '[name].js',
chunkFilename: 'files_versions.[id].js?v=[chunkhash]',
jsonpFunction: 'webpackJsonpFilesVersions',
},
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -28,7 +28,35 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Lukas Reschke <lukas@statuscode.ch>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Sergio Bertolín <sbertolin@solidgear.es>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Vincent Petry <vincent@nextcloud.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
require_once __DIR__ . '/lib/versioncheck.php';
try {