Просмотр исходного кода

Merge pull request #629 from weseek/feat/gc-939-941-recentcreated

Feat/gc 939 941 recentcreated
Yuki Takei 7 лет назад
Родитель
Сommit
90652c7d1e

+ 3 - 1
resource/locales/en-US/translation.json

@@ -497,7 +497,9 @@
     "Custom script": "Custom script",
     "write_java": "You can write Javascript that is applied to whole system.",
     "attach_title_header": "Add h1 section when create new page automatically",
-    "attach_title_header_desc": "Add page path to the first line as h1 section when create new page"
+    "attach_title_header_desc": "Add page path to the first line as h1 section when create new page",
+    "show_document_number": "Manage the number of documents to be displayed",
+    "show_document_number_desc": "Set the number of items to display on one page in Recent Created on the home screen"
   },
 
   "user_management": {

+ 3 - 1
resource/locales/ja/translation.json

@@ -514,7 +514,9 @@
     "Custom script": "カスタムスクリプト",
     "write_java": "システム全体に適用されるJavaScriptを記述できます。",
     "attach_title_header": "新規ページ作成時の h1 セクション自動挿入",
-    "attach_title_header_desc": "新規作成したページの1行目に、ページのパスを h1 セクションとして挿入します。"
+    "attach_title_header_desc": "新規作成したページの1行目に、ページのパスを h1 セクションとして挿入します。",
+    "show_document_number": "表示ドキュメント数管理",
+    "show_document_number_desc": "ホーム画面の Recent Created で、1ページに表示する件数を設定します。"
   },
 
   "user_management": {

+ 16 - 0
src/client/js/app.js

@@ -33,6 +33,7 @@ import RevisionPath     from './components/Page/RevisionPath';
 import RevisionUrl      from './components/Page/RevisionUrl';
 import BookmarkButton   from './components/BookmarkButton';
 import NewPageNameInput from './components/NewPageNameInput';
+import RecentCreated from './components/RecentCreated/RecentCreated';
 
 import CustomCssEditor  from './components/Admin/CustomCssEditor';
 import CustomScriptEditor from './components/Admin/CustomScriptEditor';
@@ -330,6 +331,21 @@ if (savePageControlsElem) {
   componentInstances.savePageControls = savePageControls;
 }
 
+// RecentCreated dev GC-939 start
+const recentCreatedControlsElem = document.getElementById('user-created-list');
+if (recentCreatedControlsElem) {
+  let limit = crowi.getConfig().recentCreatedLimit;
+  if (null == limit) {
+    limit = 10;
+  }
+  ReactDOM.render(
+    <RecentCreated  crowi={crowi} pageId={pageId} limit={limit} >
+
+    </RecentCreated>, document.getElementById('user-created-list')
+  );
+}
+// RecentCreated dev GC-939 end
+
 /*
  * HackMD Editor
  */

+ 197 - 0
src/client/js/components/RecentCreated/RecentCreated.js

@@ -0,0 +1,197 @@
+import React from 'react';
+import UserPicture from '../User/UserPicture';
+
+import PropTypes from 'prop-types';
+import Pagination from 'react-bootstrap/lib/Pagination';
+export default class RecentCreated extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      pages: [],
+      activePage: 1,
+      PaginationNumbers: {},
+
+    };
+    this.calculatePagination = this.calculatePagination.bind(this);
+  }
+
+
+  componentWillMount() {
+    this.getRecentCreatedList(1);
+  }
+  getRecentCreatedList(selectPageNumber) {
+
+    const pageId = this.props.pageId;
+    const userId = this.props.crowi.me;
+    const limit = this.props.limit;
+    const offset = (selectPageNumber - 1) * limit;
+
+    // pagesList get and pagination calculate
+    this.props.crowi.apiGet('/pages.recentCreated', {page_id: pageId, user: userId, limit: limit, offset: offset, })
+      .then(res => {
+        const totalCount = res.pages[0].totalCount;
+        const activePage = selectPageNumber;
+        const pages = res.pages[1];
+        // pagiNation calculate function call
+        const PaginationNumbers = this.calculatePagination(limit, totalCount, activePage);
+        this.setState({
+          pages,
+          activePage,
+          PaginationNumbers,
+        });
+      });
+  }
+  calculatePagination(limit, totalCount, activePage) {
+    let PaginationNumbers = {};
+    // pagiNation totalPageNumber calculate
+    let totalPage = Math.floor(totalCount / limit) + (totalCount % limit === 0 ? 0  : 1);
+    let paginationStart = activePage - 2;
+    let maxViewPageNum =  activePage + 2;
+    // pagiNation Number area size = 5 , pageNuber calculate in here
+    // activePage Position calculate ex. 4 5 [6] 7 8 (Page8 over is Max), 3 4 5 [6] 7 (Page7 is Max)
+    if ( paginationStart < 1 ) {
+      const diff = 1 - paginationStart;
+      paginationStart += diff;
+      maxViewPageNum = Math.min(totalPage, maxViewPageNum + diff);
+    }
+    if ( maxViewPageNum > totalPage ) {
+      const diff = maxViewPageNum - totalPage;
+      maxViewPageNum -= diff;
+      paginationStart = Math.max(1, paginationStart - diff);
+    }
+    PaginationNumbers.totalPage = totalPage;
+    PaginationNumbers.paginationStart = paginationStart;
+    PaginationNumbers.maxViewPageNum = maxViewPageNum;
+
+    return PaginationNumbers;
+  }
+  /**
+   * generate Elements of Page
+   *
+   * @param {any} pages Array of pages Model Obj
+   *
+   */
+  generatePageList(pages) {
+    return pages.map((page, i) => {
+      const pageuser = page.lastUpdateUser;
+      return (
+        <li key={i}>
+          <UserPicture user={pageuser} />
+          <a href={page.path} className="page-list-link" data-path={page.path} data-short-path={page.revision.body}>{decodeURI(page.path)}</a>
+        </li>
+      );
+    });
+
+  }
+
+  /**
+   * generate Elements of Pagination First Prev
+   * ex.  <<   <   1  2  3  >  >>
+   * this function set << & <
+   */
+  generateFirstPrev(activePage) {
+    let paginationItems = [];
+    if (1 != activePage) {
+      paginationItems.push(
+        <Pagination.First key="first" onClick={() => this.getRecentCreatedList(1)} />
+      );
+      paginationItems.push(
+        <Pagination.Prev key="prev" onClick={() => this.getRecentCreatedList(this.state.activePage - 1)} />
+      );
+    }
+    else {
+      paginationItems.push(
+        <Pagination.First key="first" disabled />
+      );
+      paginationItems.push(
+        <Pagination.Prev key="prev" disabled />
+      );
+
+    }
+    return paginationItems;
+  }
+
+  /**
+   * generate Elements of Pagination First Prev
+   *  ex. << < 4 5 6 7 8 > >>, << < 1 2 3 4 > >>
+   * this function set  numbers
+   */
+  generatePaginations(activePage, paginationStart, maxViewPageNum) {
+    let paginationItems = [];
+    for (let number = paginationStart; number <= maxViewPageNum; number++) {
+      paginationItems.push(
+        <Pagination.Item key={number} active={number === activePage} onClick={ () => this.getRecentCreatedList(number)}>{number}</Pagination.Item>
+      );
+    }
+    return paginationItems;
+  }
+
+  /**
+   * generate Elements of Pagination First Prev
+   * ex.  <<   <   1  2  3  >  >>
+   * this function set > & >>
+   */
+  generateNextLast(activePage, totalPage) {
+    let paginationItems = [];
+    if (totalPage != activePage) {
+      paginationItems.push(
+        <Pagination.Next key="next" onClick={() => this.getRecentCreatedList(this.state.activePage + 1)} />
+      );
+      paginationItems.push(
+        <Pagination.Last key="last" onClick={() => this.getRecentCreatedList(totalPage)} />
+      );
+    }
+    else {
+      paginationItems.push(
+        <Pagination.Next key="next" disabled />
+      );
+      paginationItems.push(
+        <Pagination.Last key="last" disabled />
+      );
+
+    }
+    return paginationItems;
+
+  }
+
+  render() {
+    const pageList = this.generatePageList(this.state.pages);
+
+    let paginationItems = [];
+    let activePage = this.state.activePage;
+    let totalPage = this.state.PaginationNumbers.totalPage;
+    let paginationStart = this.state.PaginationNumbers.paginationStart;
+    let maxViewPageNum =  this.state.PaginationNumbers.maxViewPageNum;
+    let firstPrevItems = this.generateFirstPrev(activePage);
+    paginationItems.push(firstPrevItems);
+    let paginations = this.generatePaginations(activePage, paginationStart, maxViewPageNum);
+    paginationItems.push(paginations);
+    let nextLastItems = this.generateNextLast(activePage, totalPage);
+    paginationItems.push(nextLastItems);
+
+    return (
+      <div className="page-list-container-create">
+        <ul className="page-list-ul page-list-ul-flat">
+            {pageList}
+        </ul>
+        {
+        <Pagination bsSize="small">{paginationItems}</Pagination>
+        }
+      </div>
+    );
+  }
+}
+
+
+
+RecentCreated.propTypes = {
+  pageId: PropTypes.string.isRequired,
+  crowi: PropTypes.object.isRequired,
+  limit: PropTypes.number,
+};
+
+RecentCreated.defaultProps = {
+};
+

+ 2 - 1
src/server/form/admin/customfeatures.js

@@ -6,6 +6,7 @@ var form = require('express-form')
 module.exports = form(
   field('settingForm[customize:isEnabledTimeline]').trim().toBooleanStrict(),
   field('settingForm[customize:isSavedStatesOfTabChanges]').trim().toBooleanStrict(),
-  field('settingForm[customize:isEnabledAttachTitleHeader]').trim().toBooleanStrict()
+  field('settingForm[customize:isEnabledAttachTitleHeader]').trim().toBooleanStrict(),
+  field('settingForm[customize:showRecentCreatedNumber]').trim().toInt()
 );
 

+ 7 - 0
src/server/models/config.js

@@ -106,6 +106,7 @@ module.exports = function(crowi) {
       'customize:isEnabledTimeline' : true,
       'customize:isSavedStatesOfTabChanges' : true,
       'customize:isEnabledAttachTitleHeader' : false,
+      'customize:showRecentCreatedNumber' : 10,
 
       'importer:esa:team_name': '',
       'importer:esa:access_token': '',
@@ -552,6 +553,11 @@ module.exports = function(crowi) {
     return getValueForCrowiNS(config, key);
   };
 
+  configSchema.statics.showRecentCreatedNumber = function(config) {
+    const key = 'customize:showRecentCreatedNumber';
+    return getValueForCrowiNS(config, key);
+  };
+
   configSchema.statics.fileUploadEnabled = function(config) {
     const Config = this;
 
@@ -622,6 +628,7 @@ module.exports = function(crowi) {
         HACKMD_URI: env.HACKMD_URI || null,
         MATHJAX: env.MATHJAX || null,
       },
+      recentCreatedLimit: Config.showRecentCreatedNumber(config),
     };
 
     return local_config;

+ 35 - 25
src/server/models/page.js

@@ -669,38 +669,48 @@ module.exports = function(crowi) {
     });
   };
 
-  pageSchema.statics.findListByCreator = function(user, option, currentUser) {
-    var Page = this;
-    var User = crowi.model('User');
-    var limit = option.limit || 50;
-    var offset = option.offset || 0;
-    var conditions = {
+  pageSchema.statics.findListByCreator = async function(user, option, currentUser) {
+    let Page = this;
+    let User = crowi.model('User');
+    let limit = option.limit || 50;
+    let offset = option.offset || 0;
+    let conditions = setPageListConditions(user);
+
+    let pages =  await Page.find(conditions).sort({createdAt: -1}).skip(offset).limit(limit).populate('revision').exec();
+    let PagesList = await Page.populate(pages, {path: 'lastUpdateUser', model: 'User', select: User.USER_PUBLIC_FIELDS});
+    let totalCount = await Page.countListByCreator(user);
+    let PagesArray = [
+      {totalCount: totalCount}
+    ];
+    PagesArray.push(PagesList);
+    return PagesArray;
+  };
+  function setPageListConditions(user) {
+    const conditions = {
       creator: user._id,
       redirectTo: null,
-      $or: [
-        {status: null},
-        {status: STATUS_PUBLISHED},
-      ],
+      $and: [
+        {$or: [
+          {status: null},
+          {status: STATUS_PUBLISHED},
+        ]},
+        {$or: [
+          {grant: GRANT_PUBLIC},
+          {grant: GRANT_USER_GROUP},
+        ]}],
     };
 
-    if (!user.equals(currentUser._id)) {
-      conditions.grant = GRANT_PUBLIC;
-    }
+    return conditions;
+  }
 
-    return new Promise(function(resolve, reject) {
-      Page
-      .find(conditions)
-      .sort({createdAt: -1})
-      .skip(offset)
-      .limit(limit)
-      .populate('revision')
-      .exec()
-      .then(function(pages) {
-        return Page.populate(pages, {path: 'lastUpdateUser', model: 'User', select: User.USER_PUBLIC_FIELDS}).then(resolve);
-      });
-    });
+  pageSchema.statics.countListByCreator = function(user) {
+    let Page = this;
+    let conditions = setPageListConditions(user);
+
+    return Page.find(conditions).count();
   };
 
+
   /**
    * Bulk get (for internal only)
    */

+ 1 - 0
src/server/routes/index.js

@@ -188,6 +188,7 @@ module.exports = function(crowi, app) {
   // HTTP RPC Styled API (に徐々に移行していいこうと思う)
   app.get('/_api/users.list'          , accessTokenParser , loginRequired(crowi, app, false) , user.api.list);
   app.get('/_api/pages.list'          , accessTokenParser , loginRequired(crowi, app, false) , page.api.list);
+  app.get('/_api/pages.recentCreated' , accessTokenParser , loginRequired(crowi, app, false) , page.api.recentCreated);
   app.post('/_api/pages.create'       , accessTokenParser , loginRequired(crowi, app) , csrf, page.api.create);
   app.post('/_api/pages.update'       , accessTokenParser , loginRequired(crowi, app) , csrf, page.api.update);
   app.get('/_api/pages.get'           , accessTokenParser , loginRequired(crowi, app, false) , page.api.get);

+ 29 - 1
src/server/routes/page.js

@@ -734,7 +734,7 @@ module.exports = function(crowi, app) {
   api.list = function(req, res) {
     const username = req.query.user || null;
     const path = req.query.path || null;
-    const limit = 50;
+    const limit = + req.query.limit || 50;
     const offset = parseInt(req.query.offset) || 0;
 
     const pagerOptions = { offset: offset, limit: limit };
@@ -1262,5 +1262,33 @@ module.exports = function(crowi, app) {
     });
   };
 
+  api.recentCreated = async function(req, res) {
+    const username = req.query.user || null;
+    const limit = + req.query.limit || 50;
+    const offset = + req.query.offset || 0;
+
+    const queryOptions = { offset: offset, limit: limit };
+
+    if (username == null ) {
+      return res.json(ApiResponse.error('Parameter user is required.'));
+    }
+
+    try {
+      let user = await User.findUserByUsername(username);
+      if (user == null) {
+        throw new Error('The user not found.');
+      }
+      let pages = await Page.findListByCreator(user, queryOptions, req.user);
+
+      const result = {};
+      result.pages = pagePathUtils.encodePagesPath(pages);
+
+      return res.json(ApiResponse.success(result));
+    }
+    catch (err) {
+      return res.json(ApiResponse.error(err));
+    }
+  };
+
   return actions;
 };

+ 15 - 0
src/server/views/admin/customize.html

@@ -284,6 +284,21 @@
           </div>
         </div>
 
+        <div class="form-group">
+          <label for="settingForm[customize:showRecentCreatedNumber]" class="col-xs-3 control-label">{{ t("customize_page.show_document_number") }}</label>
+          <div class="col-xs-5">
+            <select class="form-control selectpicker" name="settingForm[customize:showRecentCreatedNumber]" value="{{ settingForm['customize:showRecentCreatedNumber'] }}">
+              <option value="10" {% if 10 == settingForm['customize:showRecentCreatedNumber'] %}selected{% endif %}>10</option>
+              <option value="30" {% if 30 == settingForm['customize:showRecentCreatedNumber'] %}selected{% endif %}>30</option>
+              <option value="50" {% if 50 == settingForm['customize:showRecentCreatedNumber'] %}selected{% endif %}>50</option>
+            </select>
+
+            <p class="help-block">
+              {{ t("customize_page.show_document_number_desc") }}
+            </p>
+          </div>
+        </div>
+
         <div class="form-group">
           <div class="col-xs-offset-3 col-xs-6">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">

+ 0 - 5
src/server/views/widget/user_page_content.html

@@ -27,11 +27,6 @@
 
     <div class="tab-pane user-created-list page-list" id="user-created-list">
       <div class="page-list-container">
-        {% if createdList.length == 0 %}
-        No created pages yet.
-        {% else %}
-          {% include 'page_list.html' with { pages: createdList } %}
-        {% endif %}
       </div>
     </div>
   </div>