bs.util.registerNamespace( 'bs.permissionManager.panel' ); bs.permissionManager.panel.PermissionManager = function ( cfg ) { cfg = cfg || {}; cfg = Object.assign( { expanded: false, padded: false, classes: [ 'bs-permission-manager-inner' ] }, cfg ); bs.permissionManager.panel.PermissionManager.parent.call( this, cfg ); this.activeGroup = null; this.dirtyState = {}; this.matrixMode = 'simple'; this.matrixInstances = {}; this.disableModeToggle = false; }; OO.inheritClass( bs.permissionManager.panel.PermissionManager, OO.ui.PanelLayout ); bs.permissionManager.panel.PermissionManager.prototype.init = async function () { this.groupSelector = new bs.permissionManager.panel.GroupManager( { editable: true, classes: [ 'permission-manager-group-selector' ], types: [ 'core-minimal', 'implicit', 'custom', 'extension-minimal' ], blacklist: [ 'autoconfirmed' ] } ); this.$matrixCnt = $( '
' ).addClass( 'permission-manager-matrix' ); const [ groupResult, permissionResult ] = await Promise.all( [ // eslint-disable-line no-unused-vars this.groupSelector.init(), this.loadData() ] ); this.permissionOriginalData = permissionResult; this.permissionRawData = $.extend( true, {}, this.permissionOriginalData ); this.advancedSwitch = new OO.ui.ToggleSwitchWidget( { value: false, classes: [ 'permission-manager-advanced-switch' ] } ); this.advancedSwitch.connect( this, { change: 'toggleAdvanced' } ); this.$matrixHeader = $( '
' ).addClass( 'permission-manager-matrix-header' ); this.$matrixCnt.append( this.$matrixHeader ); this.$matrixHeader.append( new OO.ui.FieldLayout( this.advancedSwitch, { align: 'right', label: mw.message( 'bs-permissionmanager-advanced-switch' ).text() } ).$element ); this.matrixInstances.simple = new bs.permissionManager.panel.SimpleMatrix( { manager: this } ); this.$matrixCnt.append( this.matrixInstances.simple.$element ); this.$element.append( this.groupSelector.$element, this.$matrixCnt ); this.groupSelector.connect( this, { groupSelected: 'groupSelected' } ); this.groupSelector.selectFirst(); }; bs.permissionManager.panel.PermissionManager.prototype.toggleAdvanced = function ( value ) { if ( this.disableModeToggle ) { return; } if ( this.isDirty() ) { OO.ui.confirm( mw.message( 'bs-permissionmanager-advanced-switch-confirm' ).text() ) .done( ( confirmed ) => { if ( confirmed ) { this.doToggleAdvanced( value ); } else { this.disableModeToggle = true; this.advancedSwitch.setValue( !value ); this.disableModeToggle = false; } } ); } else { this.doToggleAdvanced( value ); } }; bs.permissionManager.panel.PermissionManager.prototype.doToggleAdvanced = function ( value ) { this.reset(); this.matrixMode = value ? 'advanced' : 'simple'; if ( this.matrixMode === 'advanced' && !this.matrixInstances.hasOwnProperty( 'advanced' ) ) { this.matrixInstances.advanced = new bs.permissionManager.panel.GridMatrix( { manager: this } ); this.$matrixCnt.append( this.matrixInstances.advanced.$element ); } this.matrixInstances.simple.toggle( this.matrixMode === 'simple' ); this.matrixInstances.advanced.toggle( this.matrixMode === 'advanced' ); if ( this.activeGroup ) { this.groupSelected( this.activeGroup ); } }; bs.permissionManager.panel.PermissionManager.prototype.groupSelected = function ( group ) { const matrixData = this.makeMatrixData( group ); this.activeGroup = group; this.matrixInstances[ this.matrixMode ].setActiveGroup( group ); this.matrixInstances[ this.matrixMode ].render( matrixData ); }; bs.permissionManager.panel.PermissionManager.prototype.reset = function () { this.permissionRawData = $.extend( true, {}, this.permissionOriginalData ); this.dirtyState = {}; this.groupSelector.resetDirty(); this.matrixInstances[ this.matrixMode ].reset( this.makeMatrixData( this.activeGroup ) ); }; bs.permissionManager.panel.PermissionManager.prototype.isDirty = function () { return !$.isEmptyObject( this.dirtyState ); }; bs.permissionManager.panel.PermissionManager.prototype.loadData = async function () { return new Promise( ( resolve, reject ) => { $.ajax( { url: mw.util.wikiScript( 'rest' ) + '/bs-permission-manager/v1/permissions', success: function ( data ) { resolve( data ); }, error: function ( jqXHR, textStatus, errorThrown ) { reject( errorThrown ); } } ); } ); }; bs.permissionManager.panel.PermissionManager.prototype.makeMatrixData = function ( forGroup ) { const data = []; for ( let i = 0; i < this.permissionRawData.roles.length; i++ ) { data.push( this.getMatrixDataForRoleAndGroup( this.permissionRawData.roles[ i ].role, forGroup ) ); } return data; }; bs.permissionManager.panel.PermissionManager.prototype.getMatrixDataForRoleAndGroup = function ( role, forGroup ) { const row = {}, roleData = this.permissionRawData.roles.find( ( r ) => r.role === role ); row.role = role; row.roleHint = roleData.hint; row.roleHintHtml = roleData.hintHtml; row.global = this.isGlobalExplicitlyAssigned( forGroup, row.role ); row.global_meta = this.getGlobalMeta( forGroup, row.role ); // eslint-disable-line camelcase for ( let j = 0; j < this.permissionRawData.namespaces.length; j++ ) { const namespaceInfo = this.permissionRawData.namespaces[ j ]; row[ 'ns_' + namespaceInfo.id ] = this.isNamespaceAssigned( forGroup, row.role, namespaceInfo.id ); row[ 'ns_' + namespaceInfo.id + '_meta' ] = this.getNamespaceMeta( forGroup, row.role, namespaceInfo.id, namespaceInfo.name ); } return row; }; bs.permissionManager.panel.PermissionManager.prototype.isGlobalExplicitlyAssigned = function ( group, role ) { return this.permissionRawData.groupRoles.hasOwnProperty( group ) && this.permissionRawData.groupRoles[ group ].hasOwnProperty( role ) && this.permissionRawData.groupRoles[ group ][ role ] === true; }; bs.permissionManager.panel.PermissionManager.prototype.getGlobalMeta = function ( group, role ) { const explicit = this.isGlobalExplicitlyAssigned( group, role ); const upper = []; if ( explicit ) { return { assignment: 'explicit' }; } if ( group === 'user' ) { upper.push( '*' ); } else if ( group !== '*' ) { upper.push( '*' ); upper.push( 'user' ); } for ( let i = 0; i < upper.length; i++ ) { if ( this.isGlobalExplicitlyAssigned( upper[ i ], role ) ) { return { assignment: 'inherit', inheritFrom: upper[ i ] }; } } return { assignment: false }; }; bs.permissionManager.panel.PermissionManager.prototype.isNamespaceAssigned = function ( group, role, namespace ) { return this.permissionRawData.nsLockdown.hasOwnProperty( namespace ) && this.permissionRawData.nsLockdown[ namespace ].hasOwnProperty( role ) && this.permissionRawData.nsLockdown[ namespace ][ role ].indexOf( group ) !== -1; }; bs.permissionManager.panel.PermissionManager.prototype.getNamespaceMeta = function ( group, role, namespace, namespaceName ) { const meta = { nsId: namespace, nsName: namespaceName }; const explicit = this.permissionRawData.nsLockdown.hasOwnProperty( namespace ) && this.permissionRawData.nsLockdown[ namespace ].hasOwnProperty( role ) && this.permissionRawData.nsLockdown[ namespace ][ role ].indexOf( group ) !== -1; if ( explicit ) { meta.assignment = 'explicit'; } else if ( this.getGlobalMeta( group, role ).assignment !== false ) { meta.assignment = 'global'; } else { meta.assignment = false; return meta; } if ( this.permissionRawData.roleDependencyTree.hasOwnProperty( role ) && Object.keys( this.permissionRawData.roleDependencyTree[ role ] ).length > 0 ) { const blockingDependencies = this.getBlockingDependencies( role, this.permissionRawData.roleDependencyTree[ role ], namespace, group ); if ( !$.isEmptyObject( blockingDependencies ) ) { meta.dependencies = blockingDependencies; meta.isBlocked = true; } } meta.blocking = this.getAssignedGroupsForNamespaceAndRole( namespace, role ).filter( ( g ) => g !== group ); meta.blocking = this.removeImplicitGroupsFromBlock( group, meta.blocking ); meta.blocking = meta.blocking.map( ( g ) => this.groupSelector.getGroupLabel( g ) || g ); if ( meta.blocking.length > 0 && meta.assignment !== 'explicit' ) { meta.isBlocked = true; } return meta; }; bs.permissionManager.panel.PermissionManager.prototype.getBlockingDependencies = function ( role, dependencies, namespace, group ) { const res = {}; for ( const permission in dependencies ) { if ( !dependencies.hasOwnProperty( permission ) ) { continue; } const roles = dependencies[ permission ]; for ( let i = 0; i < roles.length; i++ ) { let blocking = this.getAssignedGroupsForNamespaceAndRole( namespace, roles[ i ] ); blocking = this.removeImplicitGroupsFromBlock( group, blocking ); if ( blocking.length > 0 && blocking.indexOf( group ) === -1 ) { if ( !res.hasOwnProperty( permission ) ) { res[ permission ] = []; } res[ permission ].push( roles[ i ] ); } } } return res; }; bs.permissionManager.panel.PermissionManager.prototype.removeImplicitGroupsFromBlock = function ( forGroup, blocking ) { blocking = $.extend( [], blocking ); if ( forGroup !== '*' && blocking.indexOf( '*' ) !== -1 ) { // Remove any roles blocked by `*` if the group is not `*` blocking.splice( blocking.indexOf( '*' ), 1 ); } if ( forGroup !== 'user' && forGroup !== '*' ) { // Remove any roles blocked by `user` and `*` if the group is not `user` or `*` if ( blocking.indexOf( 'user' ) !== -1 ) { blocking.splice( blocking.indexOf( 'user' ), 1 ); } if ( blocking.indexOf( '*' ) !== -1 ) { // Remove `*` if it is present, as it is not relevant for non-user groups blocking.splice( blocking.indexOf( '*' ), 1 ); } } return blocking; }; bs.permissionManager.panel.PermissionManager.prototype.getAssignedGroupsForNamespaceAndRole = function ( namespace, role ) { const groups = []; if ( !this.permissionRawData.nsLockdown.hasOwnProperty( namespace ) ) { return groups; } if ( !this.permissionRawData.nsLockdown[ namespace ].hasOwnProperty( role ) ) { return groups; } return this.permissionRawData.nsLockdown[ namespace ][ role ]; }; bs.permissionManager.panel.PermissionManager.prototype.setGlobal = function ( role, value ) { if ( !this.permissionRawData.groupRoles.hasOwnProperty( this.activeGroup ) ) { this.permissionRawData.groupRoles[ this.activeGroup ] = {}; } this.permissionRawData.groupRoles[ this.activeGroup ][ role ] = value; if ( !value ) { // If global permission is removed, remove all NS assignments for ( const namespace in this.permissionRawData.nsLockdown ) { if ( this.permissionRawData.nsLockdown[ namespace ].hasOwnProperty( role ) ) { const index = this.permissionRawData.nsLockdown[ namespace ][ role ].indexOf( this.activeGroup ); if ( index !== -1 ) { this.permissionRawData.nsLockdown[ namespace ][ role ].splice( index, 1 ); } } } } }; bs.permissionManager.panel.PermissionManager.prototype.setNamespace = function ( namespace, role, value ) { if ( !this.permissionRawData.nsLockdown.hasOwnProperty( namespace ) ) { this.permissionRawData.nsLockdown[ namespace ] = {}; } if ( !this.permissionRawData.nsLockdown[ namespace ].hasOwnProperty( role ) ) { this.permissionRawData.nsLockdown[ namespace ][ role ] = []; } const index = this.permissionRawData.nsLockdown[ namespace ][ role ].indexOf( this.activeGroup ); if ( value && index === -1 ) { this.permissionRawData.nsLockdown[ namespace ][ role ].push( this.activeGroup ); } else if ( !value && index !== -1 ) { this.permissionRawData.nsLockdown[ namespace ][ role ].splice( index, 1 ); } if ( value ) { // Assign global permission if namespace permission is set if ( !this.permissionRawData.groupRoles.hasOwnProperty( this.activeGroup ) ) { this.permissionRawData.groupRoles[ this.activeGroup ] = {}; } this.permissionRawData.groupRoles[ this.activeGroup ][ role ] = true; } }; bs.permissionManager.panel.PermissionManager.prototype.setDirty = function ( column, role, dirty ) { if ( !this.dirtyState.hasOwnProperty( this.activeGroup ) ) { this.dirtyState[ this.activeGroup ] = {}; } if ( !this.dirtyState[ this.activeGroup ].hasOwnProperty( role ) ) { this.dirtyState[ this.activeGroup ][ role ] = []; } if ( dirty ) { if ( this.dirtyState[ this.activeGroup ][ role ].indexOf( column ) === -1 ) { this.dirtyState[ this.activeGroup ][ role ].push( column ); } } else { const index = this.dirtyState[ this.activeGroup ][ role ].indexOf( column ); if ( index !== -1 ) { this.dirtyState[ this.activeGroup ][ role ].splice( index, 1 ); } if ( this.dirtyState[ this.activeGroup ][ role ].length === 0 ) { delete this.dirtyState[ this.activeGroup ][ role ]; } if ( Object.keys( this.dirtyState[ this.activeGroup ] ).length === 0 ) { delete this.dirtyState[ this.activeGroup ]; } } this.groupSelector.setDirty( this.activeGroup, this.dirtyState.hasOwnProperty( this.activeGroup ) ); }; bs.permissionManager.panel.PermissionManager.prototype.save = async function () { if ( !this.isDirty() ) { return $.Deferred().resolve().promise(); } return this.doSave( { groupRoles: this.permissionRawData.groupRoles, roleLockdown: this.permissionRawData.nsLockdown } ); }; bs.permissionManager.panel.PermissionManager.prototype.doSave = function ( data ) { const manager = this; return new Promise( ( resolve, reject ) => { $.ajax( { url: mw.util.wikiScript( 'rest' ) + '/bs-permission-manager/v1/permissions', type: 'POST', data: JSON.stringify( data ), contentType: 'application/json', success: function () { manager.permissionOriginalData = $.extend( true, {}, manager.permissionRawData ); manager.reset(); resolve(); }, error: function ( jqXHR, textStatus, errorThrown ) { reject( jqXHR.hasOwnProperty( 'responseJSON' ) ? jqXHR.responseJSON : errorThrown ); } } ); } ); };