123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- <!--
- - @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/>.
- -
- -->
- <template>
- <Tab :icon="icon" :name="name" :class="{ 'icon-loading': loading }">
- <!-- error message -->
- <div v-if="error" class="emptycontent">
- <div class="icon icon-error" />
- <h2>{{ error }}</h2>
- </div>
- <!-- shares content -->
- <template v-else>
- <!-- shared with me information -->
- <SharingEntrySimple v-if="isSharedWithMe" v-bind="sharedWithMe" class="sharing-entry__reshare">
- <template #avatar>
- <Avatar #avatar
- :user="sharedWithMe.user"
- :display-name="sharedWithMe.displayName"
- class="sharing-entry__avatar"
- tooltip-message="" />
- </template>
- </SharingEntrySimple>
- <!-- add new share input -->
- <SharingInput v-if="!loading"
- :can-reshare="canReshare"
- :file-info="fileInfo"
- :link-shares="linkShares"
- :reshare="reshare"
- :shares="shares"
- @add:share="addShare" />
- <!-- link shares list -->
- <SharingLinkList v-if="!loading"
- :can-reshare="canReshare"
- :file-info="fileInfo"
- :shares="linkShares" />
- <!-- other shares list -->
- <SharingList v-if="!loading"
- :shares="shares"
- :file-info="fileInfo" />
- <!-- internal link copy -->
- <SharingEntryInternal :file-info="fileInfo" />
- </template>
- </Tab>
- </template>
- <script>
- import { generateOcsUrl } from '@nextcloud/router'
- import Tab from 'nextcloud-vue/dist/Components/AppSidebarTab'
- import Avatar from 'nextcloud-vue/dist/Components/Avatar'
- import axios from '@nextcloud/axios'
- import { shareWithTitle } from '../utils/SharedWithMe'
- import Share from '../models/Share'
- import ShareTypes from '../mixins/ShareTypes'
- import SharingEntryInternal from '../components/SharingEntryInternal'
- import SharingEntrySimple from '../components/SharingEntrySimple'
- import SharingInput from '../components/SharingInput'
- import SharingLinkList from './SharingLinkList'
- import SharingList from './SharingList'
- export default {
- name: 'SharingTab',
- components: {
- Avatar,
- SharingEntryInternal,
- SharingEntrySimple,
- SharingInput,
- SharingLinkList,
- SharingList,
- Tab
- },
- mixins: [ShareTypes],
- props: {
- fileInfo: {
- type: Object,
- default: () => {},
- required: true
- }
- },
- data() {
- return {
- error: '',
- expirationInterval: null,
- icon: 'icon-share',
- loading: true,
- name: t('files_sharing', 'Sharing'),
- // reshare Share object
- reshare: null,
- sharedWithMe: {},
- shares: [],
- linkShares: [],
- sections: OCA.Sharing.ShareTabSections.getSections()
- }
- },
- computed: {
- /**
- * Needed to differenciate the tabs
- * pulled from the AppSidebarTab component
- *
- * @returns {string}
- */
- id() {
- return this.name.toLowerCase().replace(/ /g, '-')
- },
- /**
- * Returns the current active tab
- * needed because AppSidebarTab also uses $parent.activeTab
- *
- * @returns {string}
- */
- activeTab() {
- return this.$parent.activeTab
- },
- /**
- * Is this share shared with me?
- *
- * @returns {boolean}
- */
- isSharedWithMe() {
- return Object.keys(this.sharedWithMe).length > 0
- },
- canReshare() {
- return !!(this.fileInfo.permissions & OC.PERMISSION_SHARE)
- || !!(this.reshare && this.reshare.hasSharePermission)
- }
- },
- watch: {
- fileInfo() {
- this.resetState()
- this.getShares()
- }
- },
- beforeMount() {
- this.getShares()
- },
- methods: {
- /**
- * Get the existing shares infos
- */
- async getShares() {
- try {
- this.loading = true
- // init params
- const shareUrl = generateOcsUrl('apps/files_sharing/api/v1', 2) + 'shares'
- const format = 'json'
- // TODO: replace with proper getFUllpath implementation of our own FileInfo model
- const path = (this.fileInfo.path + '/' + this.fileInfo.name).replace('//', '/')
- // fetch shares
- const fetchShares = axios.get(shareUrl, {
- params: {
- format,
- path,
- reshares: true
- }
- })
- const fetchSharedWithMe = axios.get(shareUrl, {
- params: {
- format,
- path,
- shared_with_me: true
- }
- })
- // wait for data
- const [shares, sharedWithMe] = await Promise.all([fetchShares, fetchSharedWithMe])
- this.loading = false
- // process results
- this.processSharedWithMe(sharedWithMe)
- this.processShares(shares)
- } catch (error) {
- this.error = t('files_sharing', 'Unable to load the shares list')
- this.loading = false
- console.error('Error loading the shares list', error)
- }
- },
- /**
- * Reset the current view to its default state
- */
- resetState() {
- clearInterval(this.expirationInterval)
- this.loading = true
- this.error = ''
- this.sharedWithMe = {}
- this.shares = []
- },
- /**
- * Update sharedWithMe.subtitle with the appropriate
- * expiration time left
- *
- * @param {Share} share the sharedWith Share object
- */
- updateExpirationSubtitle(share) {
- const expiration = moment(share.expireDate).unix()
- this.$set(this.sharedWithMe, 'subtitle', t('files_sharing', 'Expires {relativetime}', {
- relativetime: OC.Util.relativeModifiedDate(expiration * 1000)
- }))
- // share have expired
- if (moment().unix() > expiration) {
- clearInterval(this.expirationInterval)
- // TODO: clear ui if share is expired
- this.$set(this.sharedWithMe, 'subtitle', t('files_sharing', 'this share just expired.'))
- }
- },
- /**
- * Process the current shares data
- * and init shares[]
- *
- * @param {Object} share the share ocs api request data
- * @param {Object} share.data the request data
- */
- processShares({ data }) {
- if (data.ocs && data.ocs.data && data.ocs.data.length > 0) {
- // create Share objects and sort by newest
- const shares = data.ocs.data
- .map(share => new Share(share))
- .sort((a, b) => b.createdTime - a.createdTime)
- this.linkShares = shares.filter(share => share.type === this.SHARE_TYPES.SHARE_TYPE_LINK || share.type === this.SHARE_TYPES.SHARE_TYPE_EMAIL)
- this.shares = shares.filter(share => share.type !== this.SHARE_TYPES.SHARE_TYPE_LINK && share.type !== this.SHARE_TYPES.SHARE_TYPE_EMAIL)
- }
- },
- /**
- * Process the sharedWithMe share data
- * and init sharedWithMe
- *
- * @param {Object} share the share ocs api request data
- * @param {Object} share.data the request data
- */
- processSharedWithMe({ data }) {
- if (data.ocs && data.ocs.data && data.ocs.data[0]) {
- const share = new Share(data)
- const title = shareWithTitle(share)
- const displayName = share.ownerDisplayName
- const user = share.owner
- this.sharedWithMe = {
- displayName,
- title,
- user
- }
- this.reshare = share
- // If we have an expiration date, use it as subtitle
- // Refresh the status every 10s and clear if expired
- if (share.expireDate && moment(share.expireDate).unix() > moment().unix()) {
- // first update
- this.updateExpirationSubtitle(share)
- // interval update
- this.expirationInterval = setInterval(this.updateExpirationSubtitle, 10000, share)
- }
- }
- },
- /**
- * Insert share at top of arrays
- *
- * @param {Share} share the share to insert
- */
- addShare(share) {
- // only catching share type MAIL as link shares are added differently
- // meaning: not from the ShareInput
- if (share.type === this.SHARE_TYPES.SHARE_TYPE_EMAIL) {
- this.linkShares.unshift(share)
- } else {
- this.shares.unshift(share)
- }
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- </style>
|