Allow to select users and groups for acl management

Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Julius Härtl 2019-07-18 14:31:01 +02:00
parent e4a5747cda
commit 97bb847d75
No known key found for this signature in database
GPG Key ID: 4C614C6ED2CDE6DF
13 changed files with 335 additions and 109 deletions

View File

@ -43,7 +43,7 @@ return ['routes' => [
],
[
'name' => 'Folder#setManageACL',
'url' => '/folders/{id}/groups/{group}/manageACL',
'url' => '/folders/{id}/manageACL',
'verb' => 'POST'
],
[

View File

@ -7,18 +7,31 @@ export interface Group {
displayname: string;
}
export interface GroupProps {
permissions: number;
manage_acl: boolean;
export interface OCSUser {
uid: string;
displayname: string;
}
export interface OCSGroup {
gid: string;
displayname: string;
}
export interface ManageRuleProps {
type: string;
id: string;
displayname: string;
}
export interface Folder {
id: number;
mount_point: string;
quota: number;
size: number;
groups: { [group: string]: GroupProps };
groups: { [group: string]: number };
acl: boolean;
manage: ManageRuleProps[];
}
export class Api {
@ -79,8 +92,10 @@ export class Api {
});
}
setManageACL(folderId: number, group: string, manageACL: boolean): Thenable<void> {
return $.post(this.getUrl(`folders/${folderId}/groups/${group}/manageACL`), {
setManageACL(folderId: number, type: string, id: string, manageACL: boolean): Thenable<void> {
return $.post(this.getUrl(`folders/${folderId}/manageACL`), {
mappingType: type,
mappingId: id,
manageAcl: manageACL ? 1 : 0
});
}
@ -102,4 +117,26 @@ export class Api {
mountpoint
});
}
aclMappingSearch(folderId: number, search: string): Thenable<{groups: OCSGroup[], users: OCSUser[]}> {
return $.getJSON(this.getUrl(`folders/${folderId}/search?format=json&search=${search}`))
.then((data: OCSResult<{ groups: OCSGroup[]; users: OCSUser[]; }>) => {
return {
groups: data.ocs.data.groups.map((item) => {
return {
type: 'group',
id: item.gid,
displayname: item.displayname
}
}),
users: Object.values(data.ocs.data.users).map((item) => {
return {
type: 'user',
id: item.uid,
displayname: item.displayname
}
})
}
});
}
}

View File

@ -14,7 +14,7 @@
}
tr {
height: 32px;
min-height: 62px;
}
th {
@ -58,8 +58,24 @@
}
}
&.mountpoint {
width: 15%;
}
&.groups {
width: 400px;
min-width: 300px;
}
&.quota {
width: 180px;
}
&.acl {
display: flex;
& > div {
min-width: 250px;
flex-grow: 1;
}
& > label {
padding-top: 10px;
}
}
&.remove {
@ -94,7 +110,6 @@
.icon {
display: inline-block;
opacity: 0;
//transition: opacity 0.5s;
&.icon-visible {
opacity: 0.5;

View File

@ -1,14 +1,15 @@
import * as React from 'react';
import {ChangeEvent, Component} from 'react';
import {Api, Folder, Group} from './Api';
import {Api, Folder, Group, ManageRuleProps, OCSGroup, OCSUser} from './Api';
import {FolderGroups} from './FolderGroups';
import {QuotaSelect} from './QuotaSelect';
import './App.css';
import {SubmitInput} from "./SubmitInput";
import {SortArrow} from "./SortArrow";
import FlipMove from "react-flip-move";
import AsyncSelect from 'react-select/async'
import Thenable = JQuery.Thenable;
const defaultQuotaOptions = {
'1 GB': 1073741274,
@ -70,7 +71,8 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
quota: -3,
size: 0,
id,
acl: false
acl: false,
manage: []
});
this.setState({folders});
});
@ -98,7 +100,7 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
addGroup(folder: Folder, group: string) {
const folders = this.state.folders;
folder.groups[group] = {permissions: OC.PERMISSION_ALL, manage_acl: false};
folder.groups[group] = OC.PERMISSION_ALL;
this.setState({folders});
this.api.addGroup(folder.id, group);
}
@ -112,16 +114,17 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
setPermissions(folder: Folder, group: string, newPermissions: number) {
const folders = this.state.folders;
folder.groups[group].permissions = newPermissions;
folder.groups[group] = newPermissions;
this.setState({folders});
this.api.setPermissions(folder.id, group, newPermissions);
}
setManageACL(folder: Folder, group: string, manageACL: boolean) {
const folders = this.state.folders;
folder.groups[group].manage_acl = manageACL;
this.setState({folders});
this.api.setManageACL(folder.id, group, manageACL);
setManageACL(folder: Folder, type: string, id: string, manageACL: boolean) {
this.api.setManageACL(folder.id, type, id, manageACL);
}
searchMappings(folder: Folder, search: string) {
return this.api.aclMappingSearch(folder.id, search)
}
setQuota(folder: Folder, quota: number) {
@ -225,7 +228,6 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
onAddGroup={this.addGroup.bind(this, folder)}
removeGroup={this.removeGroup.bind(this, folder)}
onSetPermissions={this.setPermissions.bind(this, folder)}
onSetManageACL={this.setManageACL.bind(this, folder)}
/>
</td>
<td className="quota">
@ -235,13 +237,21 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
onChange={this.setQuota.bind(this, folder)}/>
</td>
<td className="acl">
<input type="checkbox" checked={folder.acl} disabled={!App.supportACL()}
<input id={`acl-${folder.id}`} type="checkbox" className="checkbox" checked={folder.acl} disabled={!App.supportACL()}
title={
App.supportACL()?
t('groupfolders', 'Advanced permissions allows setting permissions on a per-file basis but comes with a performance overhead'):
t('groupfolders', 'Advanced permissions are only supported with Nextcloud 16 and up')}
onChange={(event) => this.setAcl(folder, event.target.checked)}
/>
<label htmlFor={`acl-${folder.id}`}></label>
{folder.acl &&
<ManageAclSelect
folder={folder}
onChange={this.setManageACL.bind(this, folder)}
onSearch={this.searchMappings.bind(this, folder)}
/>
}
</td>
<td className="remove">
<a className="icon icon-delete icon-visible"
@ -281,7 +291,7 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
<th/>
</tr>
</thead>
<FlipMove typeName='tbody'>
<FlipMove typeName='tbody' enterAnimation="accordionVertical" leaveAnimation="accordionVertical">
{rows}
<tr>
<td>
@ -304,3 +314,81 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
</div>;
}
}
interface ManageAclSelectProps {
folder: Folder;
onChange: (type: string, id: string, manageAcl: boolean) => void;
onSearch: (name: string) => Thenable<{ groups: OCSGroup[]; users: OCSUser[]; }>;
};
function ManageAclSelect({onChange, onSearch, folder}: ManageAclSelectProps) {
const handleSearch = (inputValue: string) => {
return new Promise(resolve => {
onSearch(inputValue).then((result) => {
resolve([...result.groups, ...result.users])
})
})
}
const typeLabel = (item) => {
return item.type === 'user' ? 'User' : 'Group'
}
return <AsyncSelect
loadOptions={handleSearch}
isMulti
cacheOptions
defaultOptions
defaultValue={folder.manage}
isClearable={false}
onChange={(option, details) => {
if (details.action === 'select-option') {
const addedOption = details.option
onChange && onChange(addedOption.type, addedOption.id, true)
}
if (details.action === 'remove-value') {
const removedValue = details.removedValue
onChange && onChange(removedValue.type, removedValue.id, false)
}
}}
placeholder={t('groupfolders', 'Users/groups that can manage')}
getOptionLabel={(option) => `${option.displayname} (${typeLabel(option)})`}
getOptionValue={(option) => option.type + '/' + option.id }
styles={{
control: base => ({
...base,
minHeight: 25,
borderWidth: 1
}),
dropdownIndicator: base => ({
...base,
padding: 4
}),
clearIndicator: base => ({
...base,
padding: 4
}),
multiValue: base => ({
...base,
backgroundColor: 'var(--color-background-dark)',
color: 'var(--color-text)'
}),
valueContainer: base => ({
...base,
padding: '0px 6px'
}),
input: base => ({
...base,
margin: 0,
padding: 0
}),
menu: (provided) => ({
...provided,
backgroundColor: 'var(--color-main-background)',
borderColor: 'var(--color-border)',
})
}}
/>
}

View File

@ -2,9 +2,9 @@
min-width: 300px;
top: 0;
position: absolute;
background-color: var(--color-main-background);
background-color: var(--color-main-background, #fff);
z-index: 10;
border: 1px solid #ddd;
border: 1px solid var(--color-border, #ddd);
td {
padding: 2px 10px;

View File

@ -1,7 +1,7 @@
import * as React from 'react';
import './FolderGroups.css';
import {SyntheticEvent} from "react";
import {Folder, Group, GroupProps} from "./Api";
import {Group} from "./Api";
import Select from 'react-select'
function hasPermissions(value: number, check: number): boolean {
@ -9,29 +9,24 @@ function hasPermissions(value: number, check: number): boolean {
}
export interface FolderGroupsProps {
groups: { [group: string]: GroupProps },
groups: { [group: string]: number },
allGroups?: Group[],
onAddGroup: (name: string) => void;
removeGroup: (name: string) => void;
edit: boolean;
showEdit: (event: SyntheticEvent<any>) => void;
onSetPermissions: (name: string, permissions: number) => void;
onSetManageACL: (name: string, manageACL: boolean) => void;
}
export function FolderGroups({groups, allGroups = [], onAddGroup, removeGroup, edit, showEdit, onSetPermissions, onSetManageACL}: FolderGroupsProps) {
export function FolderGroups({groups, allGroups = [], onAddGroup, removeGroup, edit, showEdit, onSetPermissions}: FolderGroupsProps) {
if (edit) {
const setPermissions = (change: number, groupId: string): void => {
const newPermissions = groups[groupId].permissions ^ change;
const newPermissions = groups[groupId] ^ change;
onSetPermissions(groupId, newPermissions);
};
const setManageACL = (change: boolean, groupId: string): void => {
onSetManageACL(groupId, change);
};
const rows = Object.keys(groups).map(groupId => {
const group = groups[groupId];
const permissions = groups[groupId];
return <tr key={groupId}>
<td>
{(
@ -46,22 +41,17 @@ export function FolderGroups({groups, allGroups = [], onAddGroup, removeGroup, e
<td className="permissions">
<input type="checkbox"
onChange={setPermissions.bind(null, OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE, groupId)}
checked={hasPermissions(group.permissions, (OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE))}/>
checked={hasPermissions(permissions, (OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE))}/>
</td>
<td className="permissions">
<input type="checkbox"
onChange={setPermissions.bind(null, OC.PERMISSION_SHARE, groupId)}
checked={hasPermissions(group.permissions, OC.PERMISSION_SHARE)}/>
checked={hasPermissions(permissions, OC.PERMISSION_SHARE)}/>
</td>
<td className="permissions">
<input type="checkbox"
onChange={setPermissions.bind(null, OC.PERMISSION_DELETE, groupId)}
checked={hasPermissions(group.permissions, (OC.PERMISSION_DELETE))}/>
</td>
<td className="permissions">
<input type="checkbox"
onChange={setManageACL.bind(null, !group.manage_acl, groupId)}
checked={group.manage_acl}/>
checked={hasPermissions(permissions, (OC.PERMISSION_DELETE))}/>
</td>
<td>
<a onClick={removeGroup.bind(this, groupId)}>
@ -79,7 +69,6 @@ export function FolderGroups({groups, allGroups = [], onAddGroup, removeGroup, e
<th>Write</th>
<th>Share</th>
<th>Delete</th>
<th>Manage ACL</th>
<th/>
</tr>
</thead>

View File

@ -77,6 +77,8 @@ class ACL extends Base {
->addArgument('folder_id', InputArgument::REQUIRED, 'Id of the folder to configure')
->addOption('enable', 'e', InputOption::VALUE_NONE, 'Enable advanced permissions for the folder')
->addOption('disable', 'd', InputOption::VALUE_NONE, 'Disable advanced permissions for the folder')
->addOption('manage-add', 'm', InputOption::VALUE_NONE, 'Add manage permission for user or group')
->addOption('manage-remove', 'r', InputOption::VALUE_NONE, 'Remove manage permission for user or group')
->addArgument('path', InputArgument::OPTIONAL, 'The path within the folder to set permissions for')
->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'The user to configure the permissions for')
->addOption('group', 'g', InputOption::VALUE_REQUIRED, 'The group to configure the permissions for')
@ -122,6 +124,14 @@ class ACL extends Base {
!$input->getOption('group')
) {
$this->printPermissions($input, $output, $folder);
} else if ($input->getOption('manage-add') && ($input->getOption('user') || $input->getOption('group'))) {
$mappingType = $input->getOption('user') ? 'user' : 'group';
$mappingId = $input->getOption('user') ? $input->getOption('user') : $input->getOption('group');
$this->folderManager->setManageACL($folderId, $mappingType, $mappingId, true);
} else if ($input->getOption('manage-remove') && ($input->getOption('user') || $input->getOption('group'))) {
$mappingType = $input->getOption('user') ? 'user' : 'group';
$mappingId = $input->getOption('user') ? $input->getOption('user') : $input->getOption('group');
$this->folderManager->setManageACL($folderId, $mappingType, $mappingId, false);
} else if (!$input->getArgument('path')) {
$output->writeln('<error><path> argument has to be set when not using --enable or --disable</error>');
return -3;

View File

@ -59,7 +59,6 @@ class Group extends Base {
->addArgument('folder_id', InputArgument::REQUIRED, 'Id of the folder to configure')
->addArgument('group', InputArgument::REQUIRED, 'The group to configure')
->addArgument('permissions', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The permissions to set for the group, leave empty for read only')
->addOption('manage-acl', 'a', InputOption::VALUE_REQUIRED, 'The group has permission to manage advanced permissions')
->addOption('delete', 'd', InputOption::VALUE_NONE, 'Remove access for the group');
parent::configure();
@ -82,9 +81,6 @@ class Group extends Base {
$this->folderManager->addApplicableGroup($folderId, $groupString);
}
$this->folderManager->setGroupPermissions($folderId, $groupString, $permissions);
if ($input->hasOption('manage-acl')) {
$this->folderManager->setManageACL($folderId, $groupString, (bool)$input->getOption('manage-acl'));
}
return 0;
} else {
$output->writeln('<error>Unable to parse permissions input: ' . implode(' ', $permissionsString) . '</error>');

View File

@ -74,16 +74,19 @@ class ListCommand extends Base {
$this->writeArrayInOutputFormat($input, $output, $folders);
} else {
$table = new Table($output);
$table->setHeaders(['Folder Id', 'Name', 'Groups', 'Quota', 'Size', 'Advanced Permissions']);
$table->setHeaders(['Folder Id', 'Name', 'Groups', 'Quota', 'Size', 'Advanced Permissions', 'Manage advanced permissions']);
$table->setRows(array_map(function ($folder) {
$folder['size'] = \OCP\Util::humanFileSize($folder['size']);
$folder['quota'] = ($folder['quota'] > 0) ? \OCP\Util::humanFileSize($folder['quota']) : 'Unlimited';
$groupStrings = array_map(function (string $groupId, array $applicable) {
$manageAclString = $applicable['manage_acl'] ? ', acl': '';
return $groupId . ': ' . $this->permissionsToString((int)$applicable['permissions']) . $manageAclString;
$groupStrings = array_map(function (string $groupId, int $permissions) {
return $groupId . ': ' . $this->permissionsToString($permissions);
}, array_keys($folder['groups']), array_values($folder['groups']));
$folder['groups'] = implode("\n", $groupStrings);
$folder['acl'] = $folder['acl'] ? 'Enabled' : 'Disabled';
$manageStrings = array_map(function ($manage) {
return $manage['id'] . ' (' . $manage['type'] . ')';
}, $folder['manage']);
$folder['manage'] = implode("\n", $manageStrings);
return $folder;
}, $folders));
$table->render();

View File

@ -143,8 +143,8 @@ class FolderController extends OCSController {
* @param bool $manageAcl
* @return DataResponse
*/
public function setManageACL($id, $group, $manageAcl) {
$this->manager->setManageACL($id, $group, $manageAcl);
public function setManageACL($id, $mappingType, $mappingId, $manageAcl) {
$this->manager->setManageACL($id, $mappingType, $mappingId, $manageAcl);
return new DataResponse(true);
}

View File

@ -101,7 +101,7 @@ class FolderManager {
}
public function getAllFoldersWithSize($rootStorageId) {
$applicableMap = $this->getAllApplicable(false);
$applicableMap = $this->getAllApplicable();
$query = $this->connection->getQueryBuilder();
@ -120,15 +120,46 @@ class FolderManager {
'groups' => isset($applicableMap[$id]) ? $applicableMap[$id] : [],
'quota' => $row['quota'],
'size' => $row['size'] ? $row['size'] : 0,
'acl' => (bool)$row['acl']
'acl' => (bool)$row['acl'],
'manage' => $this->getManageAcl($id)
];
}
return $folderMap;
}
public function getFolder($id, $rootStorageId, $onlyPermissions = true) {
$applicableMap = $this->getAllApplicable($onlyPermissions);
private function getManageAcl($folderId) {
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('group_folders_manage')
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)));
$result = $query->execute()->fetchAll();
return array_filter(array_map(function ($entry) {
if ($entry['mapping_type'] === 'user') {
$user = \OC::$server->getUserManager()->get($entry['mapping_id']);
if ($user === null) {
return null;
}
return [
'type' => 'user',
'id' => $user->getUID(),
'displayname' => $user->getDisplayName()
];
}
$group = \OC::$server->getGroupManager()->get($entry['mapping_id']);
if ($group === null) {
return [];
}
return [
'type' => 'group',
'id' => $group->getGID(),
'displayname' => $group->getDisplayName()
];
}, $result), function($element) { return $element !== null; });
}
public function getFolder($id, $rootStorageId) {
$applicableMap = $this->getAllApplicable();
$query = $this->connection->getQueryBuilder();
@ -159,7 +190,7 @@ class FolderManager {
private function getAllApplicable(bool $permissionOnly = true) {
$query = $this->connection->getQueryBuilder();
$query->select('folder_id', 'group_id', 'permissions', 'manage_acl')
$query->select('folder_id', 'group_id', 'permissions')
->from('group_folders_groups');
$rows = $query->execute()->fetchAll();
@ -170,12 +201,7 @@ class FolderManager {
if (!isset($applicableMap[$id])) {
$applicableMap[$id] = [];
}
if ($permissionOnly) {
$applicableMap[$id][$row['group_id']] = (int)$row['permissions'];
} else {
$applicableMap[$id][$row['group_id']]['permissions'] = (int)$row['permissions'];
$applicableMap[$id][$row['group_id']]['manage_acl'] = (bool)$row['manage_acl'];
}
$applicableMap[$id][$row['group_id']] = (int)$row['permissions'];
}
return $applicableMap;
@ -193,11 +219,28 @@ class FolderManager {
}
public function canManageACL($folderId, $userId): bool {
$folder = $this->getFolder($folderId, -1, false);
$groups = $folder['groups'];
foreach ($groups as $group => $groupDetails) {
$canManageACL = $groupDetails['manage_acl'] ?? false;
if ((bool)$canManageACL === true && $this->groupManager->isInGroup($userId, $group)) {
if ($this->groupManager->isAdmin($userId)) {
return true;
}
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('group_folders_manage')
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)))
->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter('user')))
->andWhere($query->expr()->eq('mapping_id', $query->createNamedParameter($userId)));
if ($query->execute()->rowCount() === 1) {
return true;
}
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('group_folders_manage')
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)))
->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter('group')));
$groups = $query->execute()->fetchAll();
foreach ($groups as $manageRule) {
if ($this->groupManager->isInGroup($userId, $manageRule['mapping_id'])) {
return true;
}
}
@ -321,14 +364,21 @@ class FolderManager {
$query->execute();
}
public function setManageACL($folderId, $groupId, $manageAcl) {
public function setManageACL($folderId, $type, $id, $manageAcl) {
$query = $this->connection->getQueryBuilder();
$query->update('group_folders_groups')
->set('manage_acl', $query->createNamedParameter($manageAcl, IQueryBuilder::PARAM_BOOL))
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->eq('group_id', $query->createNamedParameter($groupId)));
if ($manageAcl === true) {
$query->insert('group_folders_manage')
->values([
'folder_id' => $query->createNamedParameter($folderId),
'mapping_type' => $query->createNamedParameter($type),
'mapping_id' => $query->createNamedParameter($id)
]);
} else {
$query->delete('group_folders_manage')
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)))
->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter($type)))
->andWhere($query->expr()->eq('mapping_id', $query->createNamedParameter($id)));
}
$query->execute();
}
@ -373,6 +423,13 @@ class FolderManager {
->set('acl', $query->createNamedParameter((int)$acl, IQueryBuilder::PARAM_INT))
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)));
$query->execute();
if ($acl === false) {
$query = $this->connection->getQueryBuilder();
$query->delete('group_folders_manage')
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)));
$query->execute();
}
}
/**

View File

@ -1,32 +0,0 @@
<?php
declare(strict_types=1);
namespace OCA\GroupFolders\Migration;
use Closure;
use Doctrine\DBAL\Types\Type;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\SimpleMigrationStep;
use OCP\Migration\IOutput;
/**
* Auto-generated migration step: Please modify to your needs!
*/
class Version401000Date20190715092137 extends SimpleMigrationStep {
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('group_folders_groups');
if (!$table->hasColumn('manage_acl')) {
$table->addColumn('manage_acl', Type::BOOLEAN, [
'notnull' => true,
'default' => false
]);
}
return $schema;
}
}

View File

@ -0,0 +1,63 @@
<?php
/**
* @copyright Copyright (c) 2019 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/>.
*
*/
declare(strict_types=1);
namespace OCA\GroupFolders\Migration;
use Closure;
use Doctrine\DBAL\Types\Type;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\SimpleMigrationStep;
use OCP\Migration\IOutput;
/**
* Auto-generated migration step: Please modify to your needs!
*/
class Version401001Date20190715092137 extends SimpleMigrationStep {
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if (!$schema->hasTable('group_folders_manage')) {
$table = $schema->createTable('group_folders_manage');
$table->addColumn('folder_id', 'bigint', [
'notnull' => true,
'length' => 6,
]);
$table->addColumn('mapping_type', 'string', [
'notnull' => true,
'length' => 16,
]);
$table->addColumn('mapping_id', 'string', [
'notnull' => true,
'length' => 64,
]);
$table->setPrimaryKey(['folder_id', 'mapping_type', 'mapping_id']);
$table->addUniqueIndex(['folder_id', 'mapping_type', 'mapping_id'], 'groups_folder_manage_unique');
}
return $schema;
}
}