Преглед изворни кода

Merge pull request #389 from weseek/imprv/brushup-group-select-ui

Imprv/brushup group select ui
Yuki Takei пре 8 година
родитељ
комит
a5386f46c2

+ 3 - 0
lib/models/page-group-relation.js

@@ -143,6 +143,9 @@ class PageGroupRelation {
    */
   static findByPage(page) {
 
+    if (page == null) {
+      return null;
+    }
     return this
       .find({ targetPage: page.id })
       .populate('relatedGroup')

+ 1 - 1
lib/models/page.js

@@ -344,7 +344,7 @@ module.exports = function(crowi) {
     grantLabels[GRANT_PUBLIC]     = 'Public'; // 公開
     grantLabels[GRANT_RESTRICTED] = 'Anyone with the link'; // リンクを知っている人のみ
     //grantLabels[GRANT_SPECIFIED]  = 'Specified users only'; // 特定ユーザーのみ
-    grantLabels[GRANT_USER_GROUP] = 'Only inside the group'; // 特定グループのみ
+    // grantLabels[GRANT_USER_GROUP] = 'Only inside the group'; // 特定グループのみ
     grantLabels[GRANT_OWNER]      = 'Just me'; // 自分のみ
 
     return grantLabels;

+ 24 - 7
lib/routes/page.js

@@ -9,6 +9,7 @@ module.exports = function(crowi, app) {
     , Revision = crowi.model('Revision')
     , Bookmark = crowi.model('Bookmark')
     , UserGroupRelation = crowi.model('UserGroupRelation')
+    , PageGroupRelation = crowi.model('PageGroupRelation')
     , ApiResponse = require('../util/apiResponse')
     , interceptorManager = crowi.getInterceptorManager()
     , pagePathUtil = require('../util/pagePathUtil')
@@ -238,6 +239,7 @@ module.exports = function(crowi, app) {
       pages: [],
       tree: [],
       userRelatedGroups: [],
+      pageRelatedGroup: null,
     };
 
     var pageTeamplate = 'customlayout-selector/page';
@@ -343,9 +345,15 @@ module.exports = function(crowi, app) {
     .then(function() {
       return UserGroupRelation.findAllRelationForUser(req.user);
     }).then(function(groupRelations) {
-      debug('findPage : relatedGroups ', groupRelations);
-      renderVars.userRelatedGroups = groupRelations.map(relation => relation.relatedGroup);
-      debug('findPage : groups ', renderVars.userRelatedGroups);
+      if (groupRelations != null) {
+        renderVars.userRelatedGroups = groupRelations.map(relation => relation.relatedGroup);
+      }
+
+      return PageGroupRelation.findByPage(renderVars.page);
+    }).then((pageGroupRelation) => {
+      if (pageGroupRelation != null) {
+        renderVars.pageRelatedGroup = pageGroupRelation.relatedGroup;
+      }
 
       return Promise.resolve();
     });
@@ -423,10 +431,19 @@ module.exports = function(crowi, app) {
   function renderPage(pageData, req, res) {
     // create page
     if (!pageData) {
-      return res.render('customlayout-selector/not_found', {
-        author: {},
-        page: false,
-      });
+      var userRelatedGroups
+      UserGroupRelation.findAllRelationForUser(req.user)
+        .then((groupRelations) => {
+          userRelatedGroups = groupRelations.map(relation => relation.relatedGroup);
+          return Promise.resolve();
+        }).then(() => {
+          debug('not found page user group resolver : ', userRelatedGroups);
+          return res.render('customlayout-selector/not_found', {
+            author: {},
+            page: false,
+            userRelatedGroups: userRelatedGroups,
+          });
+        });
     }
 
     if (pageData.redirectTo) {

+ 27 - 8
lib/views/_form.html

@@ -49,19 +49,38 @@
       {% if forceGrant %}
       <input type="hidden" name="pageForm[grant]" value="{{ forceGrant }}">
       {% else %}
-      <select name="pageForm[grant]" class="m-r-5 selectpicker btn-group-sm">
+      <div>
+        <div id="page-grant-selector"></div>
+      </div>
+      <input type="hidden" id="page-grant" name="pageForm[grant]" value="{{ pageForm.grant|default(page.grant) }}">
+      <input id="grant-group" type="hidden" name="pageForm[grantUserGroupId]" value="{% if pageForm.grant %}{{ pageForm.grant }}{% endif %}">
+<!--
+      <select id="select-grant" name="pageForm[grant]" class="m-r-5 selectpicker btn-group-sm">
         {% for grantId, grantLabel in consts.pageGrants %}
-        <option value="{{ grantId }}" {% if pageForm.grant|default(page.grant) == grantId %}selected{% endif %} {% if grantId == 5 && userRelatedGroups.length == 0 %}disabled{% endif %}>{{ t(grantLabel) }}</option>
+        <option value="{{ grantId }}" {% if pageForm.grant|default(page.grant) == grantId %}selected{% endif %}>{{ t(grantLabel) }}</option>
         {% endfor %}
+        {% if user and user.admin && userRelatedGroups %}
+        <option id="no-group" value="/admin/user-groups">{{ t('Only inside the group') }} you have no groups.</option>
+        {% endif %}
+        <option id="group-grant" value="5">{{ t('Only inside the group') }}</option>
+        {% if pageForm.grant|default(page.grant) == "5" && pageRelatedGroup != null %}
+        <option id="group-grant" value="5" selected>{{pageRelatedGroup}}</option>
+        {% endif %}
       </select>
+      <input id="select-grant-pre" type="hidden" value="{{ page.grant }}">
       {% endif %}
+      <input id="grant-group" type="hidden" name="pageForm[grantUserGroupId]" value="">
       {% if userRelatedGroups.length != 0 %}
-      <select name="pageForm[grantUserGroupId]" class="selectpicker btn-group-sm">
-        {% for userGroup in userRelatedGroups %}
-        <option value="{{ userGroup.id }}">{{ userGroup.name }}</option>
-        {% endfor %}
-      </select>
-      {% endif %}
+      <div class="collapse width">
+        <select name="pageForm[grantUserGroupId]" class="selectpicker btn-group-sm">
+          {% for userGroup in userRelatedGroups %}
+          <option value="{{ userGroup.id }}">{{ userGroup.name }}</option>
+          {% endfor %}
+        </select>
+      </div>
+      {% endif %} -->
+      <!-- <input type="hidden" id="page-grant" value="{{ page.grant }}"> -->
+      <input type="hidden" id="user-related-group-data" value="{{userRelatedGroups}}">
       <input type="hidden" id="edit-form-csrf" name="_csrf" value="{{ csrf() }}">
       <button type="submit" class="btn btn-primary btn-submit" id="edit-form-submit">{{ t('Update') }}</button>
     </div>

+ 10 - 0
lib/views/layout-growi/not_found.html

@@ -18,3 +18,13 @@
     </div> {# /.col- #}
   </div>
 {% endblock %}
+
+{% block body_end %}
+  <div id="presentation-layer" class="fullscreen-layer">
+    <div id="presentation-container"></div>
+  </div>
+
+  <div id="crowi-modals">
+    {% include '../modal/select_grant_group.html' %}
+  </div>
+{% endblock %}

+ 23 - 0
lib/views/modal/select_grant_group.html

@@ -0,0 +1,23 @@
+<div class="modal select-grant-group" id="select-grant-group">
+  <div class="modal-dialog">
+    <div class="modal-content">
+
+      <div class="modal-header bg-primary">
+        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+        <div class="modal-title">{{ t('SelectGrantGroup') }}</div>
+      </div>
+
+      <div class="modal-body">
+        <p>グループを下のリストから選択</p>
+
+        <ul class="list-inline">
+          {% for sGroup in userRelatedGroups %}
+          <li><button class="btn btn-xs btn-primary" onclick="$('#grant-group').val('{{sGroup.id}}')" data-dismiss="modal">{{sGroup.name}}</button></li>
+          {% endfor %}
+        </ul>
+
+      </div><!-- /.modal-body -->
+
+    </div><!-- /.modal-content -->
+  </div><!-- /.modal-dialog -->
+</div><!-- /.modal -->

+ 1 - 0
lib/views/widget/page_modals.html

@@ -3,3 +3,4 @@
 {% include '../modal/duplicate.html' %}
 {% include '../modal/put_back.html' %}
 {% include '../modal/page_name_warning.html' %}
+{% include '../modal/select_grant_group.html' %}

+ 30 - 0
resource/js/app.js

@@ -10,6 +10,7 @@ import SearchPage       from './components/SearchPage';
 import PageEditor       from './components/PageEditor';
 import OptionsSelector  from './components/PageEditor/OptionsSelector';
 import { EditorOptions, PreviewOptions } from './components/PageEditor/OptionsSelector';
+import GrantSelector, { UserGroup, PageGrant } from './components/PageEditor/GrantSelector';
 import Page             from './components/Page';
 import PageListSearch   from './components/PageListSearch';
 import PageHistory      from './components/PageHistory';
@@ -39,6 +40,7 @@ let pageRevisionCreatedAt = null;
 let pagePath;
 let pageContent = '';
 let markdown = '';
+let pageGrant = null;
 if (mainContent !== null) {
   pageId = mainContent.getAttribute('data-page-id');
   pageRevisionId = mainContent.getAttribute('data-page-revision-id');
@@ -178,6 +180,34 @@ if (pageEditorOptionsSelectorElem) {
     pageEditorOptionsSelectorElem
   );
 }
+// render GrantSelector
+const userRelatedGroups = JSON.parse(document.getElementById('user-related-group-data').textContent || '{}', (value) => {
+  return new UserGroup(value);
+});
+const pageEditorGrantSelectorElem = document.getElementById('page-grant-selector');
+const pageGrantElem = document.getElementById('page-grant');
+const pageGrantGroupElem = document.getElementById('grant-group');
+function updatePageGrantElems(newPageGrant) {
+  pageGrantElem.value = newPageGrant.grant;
+  pageGrantGroupElem.value = newPageGrant.grantGroup.userGroupId || '';
+}
+if (pageEditorGrantSelectorElem) {
+  pageGrant = new PageGrant();
+  pageGrant.grant = document.getElementById('page-grant').value;
+  const grantGroupData = JSON.parse(document.getElementById('grant-group').textContent || '{}');
+  if (grantGroupData != null) {
+    const grantGroup = new UserGroup();
+    grantGroup.userGroupId = grantGroupData.id;
+    grantGroup.userGroup = grantGroupData;
+    pageGrant.grantGroup = grantGroup;
+  }
+  ReactDOM.render(
+    <GrantSelector crowi={crowi}
+      userRelatedGroups={userRelatedGroups} pageGrant={pageGrant}
+      onChange={updatePageGrantElems} />,
+    pageEditorGrantSelectorElem
+  );
+}
 
 // render for admin
 const customCssEditorElem = document.getElementById('custom-css-editor');

+ 182 - 0
resource/js/components/PageEditor/GrantSelector.js

@@ -0,0 +1,182 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import FormGroup from 'react-bootstrap/es/FormGroup';
+import FormControl from 'react-bootstrap/es/FormControl';
+import ControlLabel from 'react-bootstrap/es/ControlLabel';
+// import Button from 'react-bootstrap/es/Button';
+
+// import Modal from 'react-bootstrap/es/Modal';
+
+/**
+ * Page grant select component
+ *
+ * @export
+ * @class GrantSelector
+ * @extends {React.Component}
+ */
+export default class GrantSelector extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      pageGrant: this.props.pageGrant,
+      isGroupModalShown: false,
+    };
+
+    this.availableGrants = [1, 2, /*3, */4, 5];
+
+    this.availableGrantLabels = {
+      1: 'Public',
+      2: 'Anyone with the linc',
+      // 3:'Specified users only',
+      4: 'Just me',
+      5: 'Only inside the group',
+    };
+
+    this.onChangeGrant = this.onChangeGrant.bind(this);
+  }
+
+  // Init component when the component did mount.
+  componentDidMount() {
+    this.init();
+  }
+
+  // Initialize the component.
+  init() {
+    this.grantSelectorInputEl.value = this.state.pageGrant.grant;
+  }
+
+  /**
+   * On change event handler for pagegrant.
+   * @param {any} grant page grant
+   * @memberof GrantSelector
+   */
+  onChangeGrant(grant) {
+    const newValue = this.grantSelectorInputEl.value;
+    const newGrant = Object.assign(this.state.pageGrant, {grant: newValue});
+    this.setState({ pageGrant: newGrant });
+
+    // dispatch event
+    this.dispatchOnChange();
+  }
+
+  // (TBD)
+  // /**
+  //  * On click event handler for grant usergroup.
+  //  *
+  //  * @memberof GrantSelector
+  //  */
+  // onClickGrantGroup() {
+  //   const newValue = this.groupSelectorInputEl.value;
+  //   const newGrant = Object.assign(this.state.pageGrant, { grantGroup: newValue });
+  //   this.setState({ pageGrant: newGrant });
+
+  //   // dispatch event
+  //   this.dispatchOnChange();
+  //   // close group select modal
+  //   if (this.state.isModalShown) {
+  //     this.setState({ isGroupModalShown: false });
+  //   }
+  // }
+
+  /**
+   * dispatch onChange event
+   * @memberof GrantSelector
+   */
+  dispatchOnChange() {
+    if (this.props.onChange != null) {
+      this.props.onChange(this.state.pageGrant);
+    }
+  }
+
+  /**
+   * Render grant selector DOM.
+   * @returns
+   * @memberof GrantSelector
+   */
+  renderGrantSelector() {
+    const grantElems = this.availableGrants.map((grant) => {
+      return <option key={grant} value={grant}>{this.availableGrantLabels[grant]}</option>;
+    });
+
+    const bsClassName = 'form-control-dummy'; // set form-control* to shrink width
+
+    return (
+      <FormGroup controlId="formControlsSelect">
+        <ControlLabel>Grant:</ControlLabel>
+        <FormControl componentClass="select" placeholder="select" defaultValue={this.state.pageGrant.grant} bsClass={bsClassName} className="btn-group-sm selectpicker"
+          onChange={this.onChangeGrant}
+          inputRef={ el => this.grantSelectorInputEl=el }>
+
+          {grantElems}
+
+        </FormControl>
+      </FormGroup>
+    );
+  }
+
+  // (TBD)
+  // /**
+  //  * Render select grantgroup modal.
+  //  *
+  //  * @returns
+  //  * @memberof GrantSelector
+  //  */
+  // renderSelectGroupModal() {
+  //   // const userRelatedGroups = this.props.userRelatedGroups;
+  //   const groupList = this.userRelatedGroups.map((group) => {
+  //     return <li>
+  //         <Button onClick={this.onClickGrantGroup(group)} bsClass="btn btn-sm btn-primary">{group.name}</Button>
+  //       </li>;
+  //   });
+  //   return (
+  //     <Modal show={this.props.isGroupModalShown} className="select-grant-group">
+  //       <Modal.Header closeButton>
+  //         <Modal.Title>
+  //           Select a Group
+  //         </Modal.Title>
+  //       </Modal.Header>
+  //       <Modal.Body>
+
+  //         <ul className="list-inline">
+  //           {groupList}
+  //         </ul>
+  //       </Modal.Body>
+  //     </Modal>
+  //   );
+  // }
+
+  render() {
+    return <span>
+      <span className="m-l-5">{this.renderGrantSelector()}</span>
+    </span>;
+  }
+}
+
+export class PageGrant {
+  constructor(props) {
+    this.grant = '';
+    this.grantGroup = null;
+
+    Object.assign(this, props);
+  }
+}
+
+export class UserGroup {
+  constructor(props) {
+    this.userGroupId = '';
+    this.userGroup;
+
+    Object.assign(this, props);
+  }
+}
+
+GrantSelector.propTypes = {
+  crowi: PropTypes.object.isRequired,
+  isGroupModalShown: PropTypes.bool,
+  userRelatedGroups: PropTypes.object,
+  pageGrant: PropTypes.instanceOf(PageGrant),
+  onChange: PropTypes.func,
+};

+ 1 - 0
resource/js/util/Crowi.js

@@ -43,6 +43,7 @@ export default class Crowi {
     this.userById   = {};
     this.draft = {};
     this.editorOptions = {};
+    this.userRelatedGroups = {};
 
     this.recoverData();
   }