Skip to content

Commit

Permalink
Fix: Create view repair, sharing shared elements possible now
Browse files Browse the repository at this point in the history
Signed-off-by: Philipp Hempel <Philipp.Hempel1@web.de>
  • Loading branch information
Hephi2 committed Aug 3, 2023
1 parent a01cbd2 commit f753c5b
Show file tree
Hide file tree
Showing 11 changed files with 72 additions and 103 deletions.
1 change: 0 additions & 1 deletion lib/Db/ShareMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ public function findAllSharesForNode(string $nodeType, int $nodeId, string $send
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->table)
->where($qb->expr()->eq('sender', $qb->createNamedParameter($sender, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq('node_type', $qb->createNamedParameter($nodeType, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq('node_id', $qb->createNamedParameter($nodeId, IQueryBuilder::PARAM_INT)));
return $this->findEntities($qb);
Expand Down
68 changes: 38 additions & 30 deletions lib/Service/PermissionsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,19 @@ public function canAccessView($view, ?string $userId = null): bool {
return false;
}

/**
* @param int $elementId
* @param string $nodeType
* @param string|null $userId
* @return bool
* @throws InternalError
*/
public function canManageElementById(int $elementId, string $nodeType = 'table', ?string $userId = null): bool {
if ($nodeType === 'table') return $this->canManageTableById($elementId, $userId);
else if ($nodeType === 'view') return $this->canManageViewById($elementId, $userId);
else throw new InternalError('Cannot read permission for node type '.$nodeType);
}

/**
* @param View $view
* @param string|null $userId
Expand Down Expand Up @@ -113,6 +126,22 @@ public function canManageTableById(int $tableId, ?string $userId = null): bool {
return $this->canManageTable($table, $userId);
}

public function canManageViewById(int $viewId, ?string $userId = null): bool {
try {
$view = $this->viewMapper->find($viewId);
} catch (MultipleObjectsReturnedException $e) {
$this->logger->warning('Multiple tables were found for this id');
return false;
} catch (DoesNotExistException $e) {
$this->logger->warning('No table was found for this id');
return false;
} catch (InternalError | Exception $e) {
$this->logger->warning('Error occurred: '.$e->getMessage());
return false;
}
return $this->canManageView($view, $userId);
}


// ***** COLUMNS permissions *****

Expand Down Expand Up @@ -237,6 +266,15 @@ public function canReadShare(Share $share, ?string $userId = null): bool {
if ($userId === '') {
return true;
}
try {
if ($this->canManageElementById($share->getNodeId(), $share->getNodeType())){
return true;
}
} catch (InternalError $e) {
$this->logger->warning('Cannot check manage permissions, permission denied');
return false;
}


if ($share->getSender() === $userId) {
return true;
Expand All @@ -263,36 +301,6 @@ public function canReadShare(Share $share, ?string $userId = null): bool {
return false;
}

public function canUpdateShare(Share $item, ?string $userId = null): bool {
try {
$userId = $this->preCheckUserId($userId);
} catch (InternalError $e) {
$this->logger->warning('Cannot pre check the user id, permission denied');
return false;
}

if ($userId === '') {
return true;
}

return $item->getSender() === $userId;
}

public function canDeleteShare(Share $item, ?string $userId = null): bool {
try {
$userId = $this->preCheckUserId($userId);
} catch (InternalError $e) {
$this->logger->warning('Cannot pre check the user id, permission denied');
return false;
}

if ($userId === '') {
return true;
}

return $item->getSender() === $userId;
}

/**
* @param int $elementId
* @param string|null $elementType
Expand Down
4 changes: 2 additions & 2 deletions lib/Service/ShareService.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public function updatePermission(int $id, string $permission, bool $value): Shar
$item = $this->mapper->find($id);

// security
if (!$this->permissionsService->canUpdateShare($item)) {
if (!$this->permissionsService->canManageElementById($item->getNodeId(), $item->getNodeType())) {
throw new PermissionError('PermissionError: can not update share with id '.$id);
}

Expand Down Expand Up @@ -272,7 +272,7 @@ public function delete(int $id): Share {
$item = $this->mapper->find($id);

// security
if (!$this->permissionsService->canDeleteShare($item)) {
if (!$this->permissionsService->canManageElementById($item->getNodeId(), $item->getNodeType())) {
throw new PermissionError('PermissionError: can not delete share with id '.$id);
}

Expand Down
81 changes: 21 additions & 60 deletions src/modules/main/modals/ViewSettings.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<NcAppSettingsDialog :open.sync="open" :show-navigation="true" :title="getModalTitle">
<NcAppSettingsDialog :open.sync="open" :show-navigation="true" :title="createView ? t('tables', 'Create view') : t('tables', 'Edit view')">
<NcAppSettingsSection v-if="columns === null" id="loading" :title="t('tables', 'Loading')">
<div class="icon-loading" />
</NcAppSettingsSection>
Expand All @@ -8,7 +8,7 @@
<div class="col-4" style="display: inline-flex;">
<NcEmojiPicker :close-on-select="true" @select="setIcon">
<NcButton type="tertiary"
:aria-label="emojiPlaceholder"
:aria-label="t('tables', 'Select emoji for view')"
:title="t('tables', 'Select emoji')"
@click.prevent>
{{ icon }}
Expand All @@ -17,38 +17,38 @@
<input v-model="title"
:class="{missing: errorTitle}"
type="text"
:placeholder="titlePlaceholder">
:placeholder="createView ? t('tables', 'Title of the new view') : t('tables', 'New title of the view')">
</div>
</NcAppSettingsSection>
<!--columns & order-->
<NcAppSettingsSection v-if="columns != null" id="columns-and-order" :title="t('tables', 'Columns')">
<SelectedViewColumns
:columns="canManageTable(view) ? allColumns : columns.map(id => allColumns.find(col => col.id === id))"
:selected-columns="selectedColumns"
:view-column-ids="view.columns"
:generated-column-ids="generatedView.columns"
:view-column-ids="viewSetting ? view.columns : null"
:generated-column-ids="viewSetting ? generatedView.columns : null"
:disable-hide="!canManageTable(view)" />
</NcAppSettingsSection>
<!--filtering-->
<NcAppSettingsSection v-if="columns != null && canManageTable(view)" id="filter" :title="t('tables', 'Filter')">
<FilterForm
:filters="mutableView.filter"
:view-filters="view.filter"
:generated-filters="generatedView.filter"
:view-filters="viewSetting ? view.filter : null"
:generated-filters="viewSetting ? generatedView.filter : null"
:columns="allColumns" />
</NcAppSettingsSection>
<!--sorting-->
<NcAppSettingsSection v-if="columns != null" id="sort" :title="t('tables', 'Sort')">
<SortForm
:sort="mutableView.sort"
:view-sort="view.sort"
:generated-sort="generatedView.sort"
:view-sort="viewSetting ? view.sort : null"
:generated-sort="viewSetting ? generatedView.sort : null"
:columns="allColumns" />
</NcAppSettingsSection>

<div class="row sticky">
<div class="fix-col-4 space-T end">
<button v-if="!localLoading && type === 'edit-view'" class="primary" :aria-label="createNewViewText" @click="createNewView()">
<button v-if="!localLoading && !createView" class="primary" :aria-label="createNewViewText" @click="createNewView()">
{{ createNewViewText }}
</button>
<button v-if="!localLoading" class="primary" :aria-label="saveText" @click="saveView()">
Expand Down Expand Up @@ -90,7 +90,7 @@ export default {
type: Object,
default: null,
},
// Possible types: If a new view is created or a existing view or table is edit
// If a new view is created or an existing view is edit
createView: {
type: Boolean,
default: false,
Expand Down Expand Up @@ -119,51 +119,18 @@ export default {
}
},
computed: {
getMetaColumns() {
return MetaColumns
},
getModalTitle() {
switch (this.type) {
case 'edit-view': return t('tables', 'Edit view')
case 'edit-table': return t('tables', 'Edit table')
case 'create-view': return t('tables', 'Create view')
default: throw Error('The type ' + this.type + ' is not valid for this modal')
}
},
emojiPlaceholder() {
switch (this.type) {
case 'edit-table': return t('tables', 'Select emoji for table')
case 'edit-view':
case 'create-view': return t('tables', 'Select emoji for view')
default: throw Error('The type ' + this.type + ' is not valid for this modal')
}
},
titlePlaceholder() {
switch (this.type) {
case 'edit-view': return t('tables', 'New title of the view')
case 'edit-table': return t('tables', 'New title of the table')
case 'create-view': return t('tables', 'Title of the new view')
default: throw Error('The type ' + this.type + ' is not valid for this modal')
}
},
saveText() {
switch (this.type) {
case 'edit-table': return t('tables', 'Save Table')
case 'edit-view':
if (this.viewSettings) return t('tables', 'Save modified View')
else return t('tables', 'Save View')
case 'create-view': return t('tables', 'Create View')
default: throw Error('The type ' + this.type + ' is not valid for this modal')
if (this.creatView) {
return t('tables', 'Create View')
} else if (this.viewSettings) {
return t('tables', 'Save modified View')
} else {
return t('tables', 'Save View')
}
},
createNewViewText() {
return t('tables', 'Create new view')
},
type() {
if (!this.showModal) return 'create-view'
if (this.createView) return 'create-view'
else return 'edit-view'
},
generateViewConfigData() {
if (!this.viewSetting) return this.view
const mergedViewSettings = JSON.parse(JSON.stringify(this.view))
Expand Down Expand Up @@ -231,24 +198,18 @@ export default {
this.columns = await this.$store.dispatch('getColumnsFromBE', { tableId: this.mutableView.tableId, viewId: this.mutableView.id })
if (this.selectedColumns === null) this.selectedColumns = this.columns.map(col => col.id)
// Show columns of view first
this.allColumns = this.columns.concat(this.getMetaColumns)
this.allColumns = this.columns.concat(MetaColumns)
this.allColumns = (this.view.columns ?? this.selectedColumns).map(id => this.allColumns.find(col => col.id === id)).concat(this.allColumns.filter(col => !(this.view.columns ?? this.selectedColumns).includes(col.id)))
},
async saveView() {
if (this.title === '') {
let titleErrorText
switch (this.type) {
case 'edit-view': titleErrorText = t('tables', 'Cannot update view.'); break
case 'edit-table': titleErrorText = t('tables', 'Cannot update table.'); break
case 'create-view': titleErrorText = t('tables', 'Cannot create view.'); break
default: throw Error('The type ' + this.type + ' is not valid for this modal')
}
let titleErrorText = this.createView ? t('tables', 'Cannot create view.') : t('tables', 'Cannot update view.')
titleErrorText += ' ' + t('tables', 'Title is missing.')
showError(titleErrorText)
this.errorTitle = true
} else {
this.localLoading = true
if (this.type === 'create-view') {
if (this.createView) {
this.mutableView.id = await this.sendNewViewToBE()
}
const success = await this.updateViewToBE(this.mutableView.id)
Expand Down Expand Up @@ -306,7 +267,7 @@ export default {
this.$emit('reload-view')
return res
} else {
showError(this.type === 'edit-table' ? t('tables', 'Could not update table') : t('tables', 'Could not update view'))
showError(t('tables', 'Could not update view'))
}
},
reset() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export default {
},
methods: {
isLocallyRemoved(columnId) {
if (!this.viewColumnIds || !this.generatedColumnIds) return false
return !this.selectedColumns.includes(columnId) && !this.generatedColumnIds.includes(columnId) && this.viewColumnIds.includes(columnId)
},
onToggle(columnId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<div v-for="(filterGroup, i) in mutableFilters" :key="i">
<FilterGroup
:filter-group="filterGroup"
:view-filter-group="viewFilters[i] ?? []"
:generated-filter-group="generatedFilters[i] ?? []"
:view-filter-group="viewFilters ? viewFilters[i] ?? [] : null"
:generated-filter-group="generatedFilters ? generatedFilters[i] ?? [] : null"
:columns="columns"
@delete-filter-group="deleteFilterGroup(i)" />
<div v-if="i < filters.length - 1" class="filter-text">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export default {
return Object.keys(searchObject).every((key) => object[key] === searchObject[key])
},
isLocallyAdded(filter) {
if (!this.viewFilterGroup || !this.generatedFilterGroup) return false
return this.generatedFilterGroup.some(e => this.isSameEntry(e, filter)) && !this.viewFilterGroup.some(e => this.isSameEntry(e, filter))
},
addFilter() {
Expand Down
2 changes: 2 additions & 0 deletions src/modules/main/partials/editViewPartials/sort/SortForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export default {
},
computed: {
removedSortingRules() {
if (!this.viewSort || !this.generatedSort) return []
return this.viewSort.filter(entry => !this.generatedSort.some(e => this.isSameEntry(e, entry)) && !this.sort.some(e => this.isSameEntry(e, entry)))
},
},
Expand All @@ -77,6 +78,7 @@ export default {
this.mutableSort.unshift(entry)
},
isLocallyAdded(entry) {
if (!this.viewSort || !this.generatedSort) return false
return this.generatedSort.some(e => this.isSameEntry(e, entry)) && !this.viewSort.some(e => this.isSameEntry(e, entry))
},
isSameEntry(object, searchObject) {
Expand Down
2 changes: 1 addition & 1 deletion src/modules/sidebar/mixins/shareAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default {
return false
}
if (this.isView) await this.$store.dispatch('setViewHasShares', { viewId: this.activeElement.id, hasShares: true })
await this.$store.dispatch('setTableHasShares', { tableId: this.isView ? this.activeElement.tableId : this.activeElement.id, hasShares: true })
else await this.$store.dispatch('setTableHasShares', { tableId: this.isView ? this.activeElement.tableId : this.activeElement.id, hasShares: true })
return true
},
async removeShareFromBE(shareId) {
Expand Down
8 changes: 3 additions & 5 deletions src/modules/sidebar/sections/SidebarSharing.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
<template>
<div v-if="activeElement" class="sharing">
<div v-if="!activeElement.isShared || activeElement.ownership === getCurrentUser().uid">
<div v-if="canShareElement(activeElement)">
<ShareForm :shares="shares" @add="addShare" @update="updateShare" />
<ShareList :shares="shares" @remove="removeShare" @update="updateShare" />
</div>
<!-- <div v-else style="margin-top: 12px;">
{{ activeView ? t('tables', 'This table is shared with you. Resharing is not possible.') : t('tables', 'This view is shared with you. Resharing is not possible.') }}
</div> -->
</div>
</template>

Expand All @@ -16,14 +13,15 @@ import shareAPI from '../mixins/shareAPI.js'
import ShareForm from '../partials/ShareForm.vue'
import ShareList from '../partials/ShareList.vue'
import { getCurrentUser } from '@nextcloud/auth'
import permissionsMixin from '../../../shared/components/ncTable/mixins/permissionsMixin.js'
export default {
components: {
ShareForm,
ShareList,
},
mixins: [shareAPI],
mixins: [shareAPI, permissionsMixin],
data() {
return {
Expand Down
3 changes: 1 addition & 2 deletions src/shared/components/ncTable/mixins/permissionsMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,10 @@ export default {
},

canShareElement(element) {
if (!element.isShared || element.ownership === getCurrentUser().uid) {
if (!element.isShared || element.ownership === getCurrentUser().uid || this.canManageElement(element)) {
return true
}

// resharing is not allowed
return false
},

Expand Down

0 comments on commit f753c5b

Please sign in to comment.