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

Merge branch 'master' into support/share-link-for-outside-for-merge

itizawa 5 лет назад
Родитель
Сommit
7792d0b871

+ 2 - 2
src/client/js/components/Navbar/PersonalDropdown.jsx

@@ -118,10 +118,10 @@ const PersonalDropdown = (props) => {
           </div>
 
           <div className="btn-group btn-block mt-2" role="group">
-            <a className="btn btn-sm btn-outline-secondary" href={`/user/${user.username}`}>
+            <a className="btn btn-sm btn-outline-secondary col" href={`/user/${user.username}`}>
               <i className="icon-fw icon-home"></i>{ t('personal_dropdown.home') }
             </a>
-            <a className="btn btn-sm btn-outline-secondary" href="/me">
+            <a className="btn btn-sm btn-outline-secondary col" href="/me">
               <i className="icon-fw icon-wrench"></i>{ t('personal_dropdown.settings') }
             </a>
           </div>

+ 0 - 16
src/client/js/util/interceptor/drawio-interceptor.js

@@ -18,22 +18,6 @@ export class DrawioInterceptor extends BasicInterceptor {
 
     this.previousPreviewContext = null;
     this.appContainer = appContainer;
-
-    // define callback function invoked by viewer.min.js of draw.io
-    // refs: https://github.com/jgraph/drawio/blob/v12.9.1/etc/build/build.xml#L219-L232
-    window.onDrawioViewerLoad = function() {
-      const DrawioViewer = window.GraphViewer;
-
-      if (DrawioViewer != null) {
-        // disable useResizeSensor and checkVisibleState
-        //   for preventing resize event by viewer.min.js
-        DrawioViewer.useResizeSensor = false;
-        DrawioViewer.prototype.checkVisibleState = false;
-
-        // initialize
-        DrawioViewer.processElements();
-      }
-    };
   }
 
   /**

+ 8 - 0
src/client/styles/scss/_me.scss

@@ -1,2 +1,10 @@
 .user-settings-page {
+  .title {
+    padding: 0.5rem 15px;
+
+    line-height: 1em;
+
+    @include variable-font-size(28px);
+    line-height: 1.1em;
+  }
 }

+ 33 - 28
src/server/models/page.js

@@ -702,6 +702,32 @@ module.exports = function(crowi) {
     return await findListFromBuilderAndViewer(builder, user, false, option);
   };
 
+  /**
+   * find pages that is match with `path` and its descendants whitch user is able to manage
+   */
+  pageSchema.statics.findManageableListWithDescendants = async function(page, user, option = {}) {
+    if (user == null) {
+      return null;
+    }
+
+    const builder = new PageQueryBuilder(this.find());
+    builder.addConditionToListWithDescendants(page.path, option);
+    builder.addConditionToExcludeRedirect();
+
+    // add grant conditions
+    await addConditionToFilteringByViewerToEdit(builder, user);
+
+    const { pages } = await findListFromBuilderAndViewer(builder, user, false, option);
+
+    // add page if 'grant' is GRANT_RESTRICTED
+    // because addConditionToListWithDescendants excludes GRANT_RESTRICTED pages
+    if (page.grant === GRANT_RESTRICTED) {
+      pages.push(page);
+    }
+
+    return pages;
+  };
+
   /**
    * find pages that start with `path`
    */
@@ -1096,14 +1122,8 @@ module.exports = function(crowi) {
       throw new Error('This method does NOT supports deleting trashed pages.');
     }
 
-    // find descendants (this array does not include GRANT_RESTRICTED)
-    const result = await this.findListWithDescendants(targetPage.path, user);
-    const pages = result.pages;
-    // add targetPage if 'grant' is GRANT_RESTRICTED
-    //  because findListWithDescendants excludes GRANT_RESTRICTED pages
-    if (targetPage.grant === GRANT_RESTRICTED) {
-      pages.push(targetPage);
-    }
+    // find manageable descendants (this array does not include GRANT_RESTRICTED)
+    const pages = await this.findManageableListWithDescendants(targetPage, user, options);
 
     await Promise.all(pages.map((page) => {
       return this.deletePage(page, user, options);
@@ -1135,8 +1155,7 @@ module.exports = function(crowi) {
 
   pageSchema.statics.revertDeletedPageRecursively = async function(targetPage, user, options = {}) {
     const findOpts = { includeTrashed: true };
-    const result = await this.findListWithDescendants(targetPage.path, user, findOpts);
-    const pages = result.pages;
+    const pages = await this.findManageableListWithDescendants(targetPage, user, findOpts);
 
     let updatedPage = null;
     await Promise.all(pages.map((page) => {
@@ -1190,18 +1209,10 @@ module.exports = function(crowi) {
    * Delete Bookmarks, Attachments, Revisions, Pages and emit delete
    */
   pageSchema.statics.completelyDeletePageRecursively = async function(targetPage, user, options = {}) {
-    const pagePath = targetPage.path;
-
     const findOpts = { includeTrashed: true };
 
-    // find descendants (this array does not include GRANT_RESTRICTED)
-    const result = await this.findListWithDescendants(pagePath, user, findOpts);
-    const pages = result.pages;
-    // add targetPage if 'grant' is GRANT_RESTRICTED
-    //  because findListWithDescendants excludes GRANT_RESTRICTED pages
-    if (targetPage.grant === GRANT_RESTRICTED) {
-      pages.push(targetPage);
-    }
+    // find manageable descendants (this array does not include GRANT_RESTRICTED)
+    const pages = await this.findManageableListWithDescendants(targetPage, user, findOpts);
 
     await Promise.all(pages.map((page) => {
       return this.completelyDeletePage(page, user, options);
@@ -1282,14 +1293,8 @@ module.exports = function(crowi) {
     // sanitize path
     newPagePathPrefix = crowi.xss.process(newPagePathPrefix); // eslint-disable-line no-param-reassign
 
-    // find descendants (this array does not include GRANT_RESTRICTED)
-    const result = await this.findListWithDescendants(path, user, options);
-    const pages = result.pages;
-    // add targetPage if 'grant' is GRANT_RESTRICTED
-    //  because findListWithDescendants excludes GRANT_RESTRICTED pages
-    if (targetPage.grant === GRANT_RESTRICTED) {
-      pages.push(targetPage);
-    }
+    // find manageable descendants
+    const pages = await this.findManageableListWithDescendants(targetPage, user, options);
 
     await Promise.all(pages.map((page) => {
       const newPagePath = page.path.replace(pathRegExp, newPagePathPrefix);

+ 6 - 0
src/server/service/config-loader.js

@@ -107,6 +107,12 @@ const ENV_VAR_NAME_TO_CONFIG_INFO = {
   //   type:    ,
   //   default:
   // },
+  DRAWIO_URI: {
+    ns:      'crowi',
+    key:     'app:drawioUri',
+    type:    TYPES.STRING,
+    default: null,
+  },
   NCHAN_URI: {
     ns:      'crowi',
     key:     'app:nchanUri',

+ 2 - 0
src/server/views/layout/layout.html

@@ -23,6 +23,8 @@
     {% include '../widget/headers/mathjax.html' %}
   {% endif %}
 
+  {% include '../widget/headers/drawio.html' %}
+
   {% include '../widget/headers/scripts-for-dev.html' %}
 
   <script src="{{ webpack_asset('js/boot.js') }}"></script>

+ 31 - 0
src/server/views/widget/headers/drawio.html

@@ -0,0 +1,31 @@
+<!-- draw.io -->
+{% if getConfig('crowi', 'app:drawioUri') %}
+<script type="text/javascript">
+  // refs: https://github.com/jgraph/drawio/blob/v13.4.3/etc/build/build.xml#L35-L38
+  let url = new URL("{{ getConfig('crowi', 'app:drawioUri') }}");
+  let origin = url.origin;
+  window.DRAWIO_BASE_URL = origin;
+  window.DRAWIO_LIGHTBOX_URL = origin;
+  window.STENCIL_PATH = [origin, 'stencils'].join('/');
+  window.SHAPES_PATH = [origin, 'shapes'].join('/');
+  window.mxBasePath = [origin, 'mxgraph'].join('/');
+</script>
+{% endif %}
+
+<script type="text/javascript">
+  // define callback function invoked by viewer.min.js of draw.io
+  // refs: https://github.com/jgraph/drawio/blob/v12.9.1/etc/build/build.xml#L219-L232
+  window.onDrawioViewerLoad = function() {
+    const DrawioViewer = window.GraphViewer;
+
+    if (DrawioViewer != null) {
+      // disable useResizeSensor and checkVisibleState
+      //   for preventing resize event by viewer.min.js
+      DrawioViewer.useResizeSensor = false;
+      DrawioViewer.prototype.checkVisibleState = false;
+
+      // initialize
+      DrawioViewer.processElements();
+    }
+  };
+</script>

+ 1 - 1
src/server/views/widget/page_list.html

@@ -8,7 +8,7 @@
 {% endif %}
 
 <li>
-  <img src="{{ listPage.lastUpdateUser.imageUrlCached }}" class="picture rounded-circle">
+  <img src="{{ listPage.lastUpdateUser.imageUrlCached|default('/images/icons/user.svg') }}" class="picture rounded-circle">
   <a href="{{ encodeURI(listPage.path) }}" class="text-break ml-1">
     {{ listPage.path | preventXss }}
   </a>

+ 125 - 0
src/test/models/page.test.js

@@ -6,6 +6,7 @@ let testUser0;
 let testUser1;
 let testUser2;
 let testGroup0;
+let parentPage;
 
 describe('Page', () => {
   // eslint-disable-next-line no-unused-vars
@@ -61,6 +62,12 @@ describe('Page', () => {
         grantedUsers: [testUser0],
         creator: testUser0,
       },
+      {
+        path: '/grant',
+        grant: Page.GRANT_PUBLIC,
+        grantedUsers: [testUser0],
+        creator: testUser0,
+      },
       {
         path: '/grant/public',
         grant: Page.GRANT_PUBLIC,
@@ -115,6 +122,8 @@ describe('Page', () => {
       },
     ]);
 
+    parentPage = await Page.findOne({ path: '/grant' });
+
     done();
   });
 
@@ -395,4 +404,120 @@ describe('Page', () => {
       expect(pagePaths).toContainEqual('/page2');
     });
   });
+
+  describe('.findListWithDescendants', () => {
+    test('can retrieve all pages with testUser0', async() => {
+      const result = await Page.findListWithDescendants('/grant', testUser0);
+      const { pages } = result;
+
+      // assert totalCount
+      expect(pages.length).toEqual(5);
+
+      // assert paths
+      const pagePaths = await pages.map((page) => { return page.path });
+      expect(pagePaths).toContainEqual('/grant/groupacl');
+      expect(pagePaths).toContainEqual('/grant/specified');
+      expect(pagePaths).toContainEqual('/grant/owner');
+      expect(pagePaths).toContainEqual('/grant/public');
+      expect(pagePaths).toContainEqual('/grant');
+    });
+
+    test('can retrieve all pages with testUser1', async() => {
+      const result = await Page.findListWithDescendants('/grant', testUser1);
+      const { pages } = result;
+
+      // assert totalCount
+      expect(pages.length).toEqual(5);
+
+      // assert paths
+      const pagePaths = await pages.map((page) => { return page.path });
+      expect(pagePaths).toContainEqual('/grant/groupacl');
+      expect(pagePaths).toContainEqual('/grant/specified');
+      expect(pagePaths).toContainEqual('/grant/owner');
+      expect(pagePaths).toContainEqual('/grant/public');
+      expect(pagePaths).toContainEqual('/grant');
+    });
+
+    test('can retrieve all pages with testUser2', async() => {
+      const result = await Page.findListWithDescendants('/grant', testUser2);
+      const { pages } = result;
+
+      // assert totalCount
+      expect(pages.length).toEqual(5);
+
+      // assert paths
+      const pagePaths = await pages.map((page) => { return page.path });
+      expect(pagePaths).toContainEqual('/grant/groupacl');
+      expect(pagePaths).toContainEqual('/grant/specified');
+      expect(pagePaths).toContainEqual('/grant/owner');
+      expect(pagePaths).toContainEqual('/grant/public');
+      expect(pagePaths).toContainEqual('/grant');
+    });
+
+    test('can retrieve all pages without user', async() => {
+      const result = await Page.findListWithDescendants('/grant', null);
+      const { pages } = result;
+
+      // assert totalCount
+      expect(pages.length).toEqual(5);
+
+      // assert paths
+      const pagePaths = await pages.map((page) => { return page.path });
+      expect(pagePaths).toContainEqual('/grant/groupacl');
+      expect(pagePaths).toContainEqual('/grant/specified');
+      expect(pagePaths).toContainEqual('/grant/owner');
+      expect(pagePaths).toContainEqual('/grant/public');
+      expect(pagePaths).toContainEqual('/grant');
+    });
+  });
+
+  describe('.findManageableListWithDescendants', () => {
+    test('can retrieve all pages with testUser0', async() => {
+      const pages = await Page.findManageableListWithDescendants(parentPage, testUser0);
+
+      // assert totalCount
+      expect(pages.length).toEqual(5);
+
+      // assert paths
+      const pagePaths = await pages.map((page) => { return page.path });
+      expect(pagePaths).toContainEqual('/grant/groupacl');
+      expect(pagePaths).toContainEqual('/grant/specified');
+      expect(pagePaths).toContainEqual('/grant/owner');
+      expect(pagePaths).toContainEqual('/grant/public');
+      expect(pagePaths).toContainEqual('/grant');
+    });
+
+    test('can retrieve group page and public page which starts with testUser1', async() => {
+      const pages = await Page.findManageableListWithDescendants(parentPage, testUser1);
+
+      // assert totalCount
+      expect(pages.length).toEqual(3);
+
+      // assert paths
+      const pagePaths = await pages.map((page) => { return page.path });
+      expect(pagePaths).toContainEqual('/grant/groupacl');
+      expect(pagePaths).toContainEqual('/grant/public');
+      expect(pagePaths).toContainEqual('/grant');
+    });
+
+    test('can retrieve only public page which starts with testUser2', async() => {
+      const pages = await Page.findManageableListWithDescendants(parentPage, testUser2);
+
+      // assert totalCount
+      expect(pages.length).toEqual(2);
+
+      // assert paths
+      const pagePaths = await pages.map((page) => { return page.path });
+      expect(pagePaths).toContainEqual('/grant/public');
+      expect(pagePaths).toContainEqual('/grant');
+    });
+
+    test('can retrieve only public page which starts without user', async() => {
+      const pages = await Page.findManageableListWithDescendants(parentPage, null);
+
+      // assert totalCount
+      expect(pages).toBeNull();
+    });
+  });
+
 });