Sotaro KARASAWA 9 лет назад
Родитель
Сommit
5ce336b347

+ 1 - 0
lib/routes/index.js

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

+ 14 - 1
lib/routes/page.js

@@ -323,7 +323,8 @@ module.exports = function(crowi, app) {
 
 
           var fixed = Page.fixToCreatableName(path)
           var fixed = Page.fixToCreatableName(path)
           if (fixed !== path) {
           if (fixed !== path) {
-            res.redirect(fixed);
+            debug('fixed page name', fixed)
+            res.redirect(encodeURI(fixed));
             return ;
             return ;
           }
           }
 
 
@@ -518,6 +519,18 @@ module.exports = function(crowi, app) {
     });
     });
   };
   };
 
 
+  /**
+   * @api {post} /pages.create Create new page
+   * @apiName CreatePage
+   * @apiGroup Page
+   *
+   * @apiParam {String} body
+   * @apiParam {String} path
+   * @apiParam {String} revision_id
+   */
+  api.create = function(req, res){
+  };
+
   /**
   /**
    * @api {get} /pages.get Get page data
    * @api {get} /pages.get Get page data
    * @apiName GetPage
    * @apiName GetPage

+ 17 - 12
lib/util/middlewares.js

@@ -71,18 +71,23 @@ exports.swigFilters = function(app, swig) {
     });
     });
 
 
     swig.setFilter('normalizeDateInPath', function(path) {
     swig.setFilter('normalizeDateInPath', function(path) {
-      // warning for /(20\d{4}|20\d{6}|20\d{2}_\d{1,2}|20\d{2}_\d{1,2}_\d{1,2})/
-      if (path.match(/20(\d{2})(\d{2})(\d{2})/g)) {
-        return path.replace(/20(\d{2})(\d{2})(\d{2})/g, '20$1/$2/$3');
-      }
-      if (path.match(/20(\d{2})(\d{2})/g)) {
-        return path.replace(/20(\d{2})(\d{2})/g, '20$1/$2');
-      }
-      if (path.match(/20(\d{2})_(\d{1,2})_(\d{1,2})/g)) {
-        return path.replace(/20(\d{2})_(\d{1,2})_(\d{1,2})/g, '20$1/$2/$3');
-      }
-      if (path.match(/20(\d{2})_(\d{1,2})/g)) {
-        return path.replace(/20(\d{2})_(\d{1,2})/g, '20$1/$2');
+      var patterns = [
+        [/20(\d{2})(\d{2})(\d{2})(.+)/g, '20$1/$2/$3/$4'],
+        [/20(\d{2})(\d{2})(\d{2})/g, '20$1/$2/$3'],
+        [/20(\d{2})(\d{2})(.+)/g, '20$1/$2/$3'],
+        [/20(\d{2})(\d{2})/g, '20$1/$2'],
+        [/20(\d{2})_(\d{1,2})_(\d{1,2})_?(.+)/g, '20$1/$2/$3/$4'],
+        [/20(\d{2})_(\d{1,2})_(\d{1,2})/g, '20$1/$2/$3'],
+        [/20(\d{2})_(\d{1,2})_?(.+)/g, '20$1/$2/$3'],
+        [/20(\d{2})_(\d{1,2})/g, '20$1/$2'],
+      ];
+
+      for (var i = 0; i < patterns.length ; i++) {
+        var mat = patterns[i][0];
+        var rep = patterns[i][1];
+        if (path.match(mat)) {
+          return path.replace(mat, rep);
+        }
       }
       }
 
 
       return path;
       return path;

+ 1 - 0
lib/views/page.html

@@ -42,6 +42,7 @@
   data-path-shortname="{{ path|path2name }}"
   data-path-shortname="{{ path|path2name }}"
   data-page-id="{% if page %}{{ page._id.toString() }}{% endif %}"
   data-page-id="{% if page %}{{ page._id.toString() }}{% endif %}"
   data-current-user="{% if user %}{{ user._id.toString() }}{% endif %}"
   data-current-user="{% if user %}{{ user._id.toString() }}{% endif %}"
+  data-current-username="{% if user %}{{ user.username }}{% endif %}"
   data-page-revision-id="{% if revision %}{{ revision._id.toString() }}{% endif %}"
   data-page-revision-id="{% if revision %}{{ revision._id.toString() }}{% endif %}"
   data-page-revision-created="{% if revision %}{{ revision.createdAt|datetz('U') }}{% endif %}"
   data-page-revision-created="{% if revision %}{{ revision.createdAt|datetz('U') }}{% endif %}"
   data-page-is-seen="{% if page and page.isSeenUser(user) %}1{% else %}0{% endif %}"
   data-page-is-seen="{% if page and page.isSeenUser(user) %}1{% else %}0{% endif %}"

+ 1 - 1
resource/js/app.js

@@ -13,7 +13,7 @@ if (!window) {
   window = {};
   window = {};
 }
 }
 // FIXME
 // FIXME
-const crowi = new Crowi({/* context */}, window);
+const crowi = new Crowi({me: $('#content-main').data('current-username')}, window);
 window.crowi = crowi;
 window.crowi = crowi;
 crowi.fetchUsers();
 crowi.fetchUsers();
 
 

+ 10 - 3
resource/js/crowi-form.js

@@ -16,15 +16,20 @@ $(function() {
     });
     });
   }
   }
 
 
-  //
-  if (pagePath.match(/(20\d{4}|20\d{6}|20\d{2}_\d{1,2}|20\d{2}_\d{1,2}_\d{1,2})/)) {
-    $('#page-warning-modal').modal('show');
+  // restore draft
+  var draft = crowi.findDraft(pagePath);
+  if (draft) {
+    $('#form-body').val(draft);
   }
   }
 
 
   var slackConfigured = $('#page-form-setting').data('slack-configured');
   var slackConfigured = $('#page-form-setting').data('slack-configured');
 
 
   // for new page
   // for new page
   if (!pageId) {
   if (!pageId) {
+    if (!pageId && pagePath.match(/(20\d{4}|20\d{6}|20\d{2}_\d{1,2}|20\d{2}_\d{1,2}_\d{1,2})/)) {
+      $('#page-warning-modal').modal('show');
+    }
+
     if (slackConfigured) {
     if (slackConfigured) {
       FetchPagesUpdatePostAndInsert(pagePath);
       FetchPagesUpdatePostAndInsert(pagePath);
     }
     }
@@ -57,6 +62,8 @@ $(function() {
       var parsedHTML = crowiRenderer.render(content);
       var parsedHTML = crowiRenderer.render(content);
       $('#preview-body').html(parsedHTML);
       $('#preview-body').html(parsedHTML);
 
 
+      crowi.saveDraft(pagePath, content);
+
       prevContent = content;
       prevContent = content;
     }
     }
   }, 500);
   }, 500);

+ 10 - 0
resource/js/crowi.js

@@ -456,6 +456,16 @@ $(function() {
       var parsedHTML = crowiRenderer.render(markdown);
       var parsedHTML = crowiRenderer.render(markdown);
       $('#revision-body-content').html(parsedHTML);
       $('#revision-body-content').html(parsedHTML);
 
 
+
+      $('.template-create-button').on('click', function() {
+        var path = $(this).data('path');
+        var templateId = $(this).data('template');
+        var template = $('#' + templateId).html();
+
+        crowi.saveDraft(path, template);
+        top.location.href = path;
+      });
+
       Crowi.correctHeaders('#revision-body-content');
       Crowi.correctHeaders('#revision-body-content');
       Crowi.revisionToc('#revision-body-content', '#revision-toc');
       Crowi.revisionToc('#revision-body-content', '#revision-toc');
     }
     }

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

@@ -19,14 +19,20 @@ export default class Crowi {
 
 
     this.users = [];
     this.users = [];
     this.userByName = {};
     this.userByName = {};
+    this.draft = {};
 
 
     this.recoverData();
     this.recoverData();
   }
   }
 
 
+  getContext() {
+    return context;
+  }
+
   recoverData() {
   recoverData() {
     const keys = [
     const keys = [
       'userByName',
       'userByName',
       'users',
       'users',
+      'draft',
     ];
     ];
 
 
     keys.forEach(key => {
     keys.forEach(key => {
@@ -70,6 +76,24 @@ export default class Crowi {
     });
     });
   }
   }
 
 
+  clearDraft(path) {
+    this.draft[path] = null;
+    this.localStorage.draft = JSON.stringify(this.draft);
+  }
+
+  saveDraft(path, body) {
+    this.draft[path] = body;
+    this.localStorage.draft = JSON.stringify(this.draft);
+  }
+
+  findDraft(path) {
+    if (this.draft && this.draft[path]) {
+      return this.draft[path];
+    }
+
+    return null;
+  }
+
   findUser(username) {
   findUser(username) {
     if (this.userByName && this.userByName[username]) {
     if (this.userByName && this.userByName[username]) {
       return this.userByName[username];
       return this.userByName[username];

+ 7 - 3
resource/js/util/CrowiRenderer.js

@@ -6,6 +6,7 @@ import Linker        from './PreProcessor/Linker';
 import ImageExpander from './PreProcessor/ImageExpander';
 import ImageExpander from './PreProcessor/ImageExpander';
 
 
 import Tsv2Table from './LangProcessor/Tsv2Table';
 import Tsv2Table from './LangProcessor/Tsv2Table';
+import Template from './LangProcessor/Template';
 
 
 export default class CrowiRenderer {
 export default class CrowiRenderer {
 
 
@@ -20,6 +21,7 @@ export default class CrowiRenderer {
     this.langProcessors = {
     this.langProcessors = {
       'tsv2table': new Tsv2Table(),
       'tsv2table': new Tsv2Table(),
       'tsv2table-h': new Tsv2Table({header: true}),
       'tsv2table-h': new Tsv2Table({header: true}),
+      'template': new Template(),
     };
     };
 
 
     this.parseMarkdown = this.parseMarkdown.bind(this);
     this.parseMarkdown = this.parseMarkdown.bind(this);
@@ -39,11 +41,13 @@ export default class CrowiRenderer {
   codeRenderer(code, lang, escaped) {
   codeRenderer(code, lang, escaped) {
     let result = '', hl;
     let result = '', hl;
 
 
-    if (lang && this.langProcessors[lang]) {
-      return this.langProcessors[lang].process(code);
-    }
 
 
     if (lang) {
     if (lang) {
+      const langPattern = lang.split(':')[0];
+      if (this.langProcessors[langPattern]) {
+        return this.langProcessors[langPattern].process(code, lang);
+      }
+
       try {
       try {
         hl = hljs.highlight(lang, code);
         hl = hljs.highlight(lang, code);
         result = hl.value;
         result = hl.value;

+ 53 - 0
resource/js/util/LangProcessor/Template.js

@@ -0,0 +1,53 @@
+import moment from 'moment';
+
+export default class Template {
+
+  constructor() {
+    this.templatePattern = {
+      'year': this.getYear,
+      'month': this.getMonth,
+      'date': this.getDate,
+      'user': this.getUser,
+    };
+  }
+
+  getYear() {
+    return moment().format('YYYY');
+  }
+
+  getMonth() {
+    return moment().format('YYYY/MM');
+  }
+
+  getDate() {
+    return moment().format('YYYY/MM/DD');
+  }
+
+  getUser() {
+    return '/user/sotarok';
+  }
+
+  getPageName(pageNameTemplate) {
+    let pageName = pageNameTemplate;
+
+    Object.keys(this.templatePattern).forEach(key => {
+      const matcher = new RegExp(`{${key}}`, 'g');
+      if (pageName.match(matcher)) {
+        const replacer = this.templatePattern[key]();
+        pageName = pageName.replace(matcher, replacer);
+      }
+    });
+
+    return pageName;
+  }
+
+  process(code, lang) {
+    const templateId = new Date().getTime().toString(16) + Math.floor(1000 * Math.random()).toString(16);
+    let pageName = lang;
+    if (lang.match(':')) {
+      pageName = this.getPageName(lang.split(':')[1]);
+    }
+    return `<button class="template-create-button btn btn-primary" data-template="${templateId}" data-path="${pageName}">${pageName} のページを作成する</button>
+      <pre><code id="${templateId}" class="lang-${lang}">${code}\n</code></pre>\n`;
+  }
+}

+ 0 - 1
resource/js/util/LangProcessor/Tsv2Table.js

@@ -1,4 +1,3 @@
-import React from 'react';
 
 
 export default class Tsv2Table {
 export default class Tsv2Table {