Browse Source

Merge pull request #11 from weseek/imprv/markdown-optional-linebreaks

Imprv/markdown optional linebreaks
Yuki Takei 9 years ago
parent
commit
efd9730eec

+ 10 - 0
lib/form/admin/markdown.js

@@ -0,0 +1,10 @@
+'use strict';
+
+var form = require('express-form')
+  , field = form.field;
+
+module.exports = form(
+  field('markdownSetting[markdown:isEnabledLinebreaks]').trim().toBooleanStrict(),
+  field('markdownSetting[markdown:isEnabledLinebreaksInComments]').trim().toBooleanStrict()
+);
+

+ 1 - 0
lib/form/index.js

@@ -16,6 +16,7 @@ module.exports = {
     aws: require('./admin/aws'),
     google: require('./admin/google'),
     plugin: require('./admin/plugin'),
+    markdown: require('./admin/markdown'),
     userInvite: require('./admin/userInvite'),
     slackSetting: require('./admin/slackSetting'),
   },

+ 37 - 3
lib/models/config.js

@@ -47,6 +47,13 @@ module.exports = function(crowi) {
     };
   }
 
+  function getDefaultMarkdownConfigs() {
+    return {
+      'markdown:isEnabledLinebreaks': true,
+      'markdown:isEnabledLinebreaksInComments': true,
+    }
+  }
+
   configSchema.statics.getRegistrationModeLabels = function()
   {
     var labels = {};
@@ -90,17 +97,22 @@ module.exports = function(crowi) {
   configSchema.statics.setupCofigFormData = function(ns, config)
   {
     var defaultConfig = {};
+
+    // set Default Settings
     if (ns === 'crowi') {
-      defaultConfig  = getArrayForInstalling();
+      defaultConfig = getArrayForInstalling();
     }
+    else if (ns === 'markdown') {
+      defaultConfig = getDefaultMarkdownConfigs();
+    }
+
     if (!defaultConfig[ns]) {
       defaultConfig[ns] = {};
     }
     Object.keys(config[ns] || {}).forEach(function (key) {
-      if (config[ns][key]) {
+      if (config[ns][key] !== undefined) {
         defaultConfig[key] = config[ns][key];
       }
-
     });
     return defaultConfig;
   };
@@ -182,6 +194,28 @@ module.exports = function(crowi) {
     return method != 'none';
   };
 
+  configSchema.statics.isEnabledLinebreaks = function(config)
+  {
+    var defaultValue = getDefaultMarkdownConfigs()['markdown:isEnabledLinebreaks'];
+
+    if (undefined === config.markdown || undefined === config.markdown['markdown:isEnabledLinebreaks']) {
+      return defaultValue;
+    }
+
+    return config.markdown['markdown:isEnabledLinebreaks'];
+  };
+
+  configSchema.statics.isEnabledLinebreaksInComments = function(config)
+  {
+    var defaultValue = getDefaultMarkdownConfigs()['markdown:isEnabledLinebreaksInComments'];
+
+    if (undefined === config.markdown || undefined === config.markdown['markdown:isEnabledLinebreaksInComments']) {
+      return defaultValue;
+    }
+
+    return config.markdown['markdown:isEnabledLinebreaksInComments'];
+  };
+
   configSchema.statics.hasSlackConfig = function(config)
   {
     if (!config.notification) {

+ 30 - 1
lib/routes/admin.js

@@ -69,6 +69,7 @@ module.exports = function(crowi, app) {
     return res.render('admin/index');
   };
 
+  // app.get('/admin/app'                  , admin.app.index);
   actions.app = {};
   actions.app.index = function(req, res) {
     var settingForm;
@@ -82,6 +83,34 @@ module.exports = function(crowi, app) {
   actions.app.settingUpdate = function(req, res) {
   };
 
+  // app.get('/admin/markdonw'                  , admin.markdonw.index);
+  actions.markdown = {};
+  actions.markdown.index = function(req, res) {
+    var config = crowi.getConfig();
+    var markdownSetting = Config.setupCofigFormData('markdown', config);
+    return res.render('admin/markdown', {
+      markdownSetting: markdownSetting,
+    });
+  };
+
+  // app.post('/admin/markdown/lineBreaksSetting' , admin.markdown.lineBreaksSetting);
+  actions.markdown.lineBreaksSetting = function(req, res) {
+    var markdownSetting = req.form.markdownSetting;
+
+    req.session.markdownSetting = markdownSetting;
+    if (req.form.isValid) {
+      Config.updateNamespaceByArray('markdown', markdownSetting, function(err, config) {
+        Config.updateConfigCache('markdown', config);
+        req.session.markdownSetting = null;
+        req.flash('successMessage', ['Successfully updated!']);
+        return res.redirect('/admin/markdown');
+      });
+    } else {
+      req.flash('errorMessage', req.form.errors);
+      return res.redirect('/admin/markdown');
+    }
+  };
+
   // app.get('/admin/notification'               , admin.notification.index);
   actions.notification = {};
   actions.notification.index = function(req, res) {
@@ -117,7 +146,7 @@ module.exports = function(crowi, app) {
     });
   };
 
-  // app.post('/admin/notification/slackSetting' , admin.notification.slackauth);
+  // app.post('/admin/notification/slackSetting' , admin.notification.slackSetting);
   actions.notification.slackSetting = function(req, res) {
     var slackSetting = req.form.slackSetting;
 

+ 4 - 0
lib/routes/index.js

@@ -47,6 +47,10 @@ module.exports = function(crowi, app) {
   app.post('/_api/admin/settings/google', loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.google, admin.api.appSetting);
   app.post('/_api/admin/settings/plugin', loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.plugin, admin.api.appSetting);
 
+  // markdown admin
+  app.get('/admin/markdown'                   , loginRequired(crowi, app) , middleware.adminRequired() , admin.markdown.index);
+  app.post('/admin/markdown/lineBreaksSetting', loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.markdown, admin.markdown.lineBreaksSetting);
+
   // search admin
   app.get('/admin/search'              , loginRequired(crowi, app) , middleware.adminRequired() , admin.search.index);
   app.post('/admin/search/build'       , loginRequired(crowi, app) , middleware.adminRequired() , csrf, admin.search.buildIndex);

+ 10 - 0
lib/util/swigFunctions.js

@@ -22,6 +22,16 @@ module.exports = function(crowi, app, req, locals) {
     return false;
   };
 
+  locals.isEnabledLinebreaks = function() {
+    var config = crowi.getConfig()
+    return Config.isEnabledLinebreaks(config);
+  }
+
+  locals.isEnabledLinebreaksInComments = function() {
+    var config = crowi.getConfig()
+    return Config.isEnabledLinebreaksInComments(config);
+  }
+
   locals.slackConfigured = function() {
     var config = crowi.getConfig()
     if (Config.hasSlackToken(config)) {

+ 101 - 0
lib/views/admin/markdown.html

@@ -0,0 +1,101 @@
+{% extends '../layout/admin.html' %}
+
+{% block html_title %}Markdown設定 · {{ path }}{% endblock %}
+
+{% block content_head %}
+<div class="header-wrap">
+  <header id="page-header">
+    <h1 class="title" id="">Markdown設定</h1>
+  </header>
+</div>
+{% endblock %}
+
+{% block content_main %}
+<div class="content-main">
+  <div class="row">
+    <div class="col-md-3">
+      {% include './widget/menu.html' with {current: 'markdown'} %}
+    </div>
+    <div class="col-md-9">
+
+      {% set smessage = req.flash('successMessage') %}
+      {% if smessage.length %}
+      <div class="alert alert-success">
+        {% for e in smessage %}
+          {{ e }}<br>
+        {% endfor %}
+      </div>
+      {% endif %}
+
+      {% set emessage = req.flash('errorMessage') %}
+      {% if emessage.length %}
+      <div class="alert alert-danger">
+        {% for e in emessage %}
+        {{ e }}<br>
+        {% endfor %}
+      </div>
+      {% endif %}
+
+      <form action="/admin/markdown/lineBreaksSetting" method="post" class="form-horizontal" id="markdownSettingForm" role="form">
+      <fieldset>
+        <legend>Markdown設定</legend>
+        <p class="well">Markdownレンダリングの設定を変更できます。</p>
+
+        <div class="form-group">
+          <label for="markdownSetting[markdown:isEnabledLinebreaks]" class="col-xs-4 control-label">
+            Line Break を有効にする
+          </label>
+          <div class="col-xs-5">
+            <div class="btn-group btn-toggle" data-toggle="buttons">
+              <label class="btn {% if markdownSetting['markdown:isEnabledLinebreaks'] %}btn-primary active{% else %}btn-default{% endif %}">
+                <input name="markdownSetting[markdown:isEnabledLinebreaks]" value="true" type="radio"
+                    {% if true === markdownSetting['markdown:isEnabledLinebreaks'] %}checked{% endif %}> 有効
+              </label>
+              <label class="btn {% if !markdownSetting['markdown:isEnabledLinebreaks'] %}btn-primary active{% else %}btn-default{% endif %}">
+                <input name="markdownSetting[markdown:isEnabledLinebreaks]" value="false" type="radio"
+                    {% if !markdownSetting['markdown:isEnabledLinebreaks'] %}checked{% endif %}> 無効
+              </label>
+            </div>
+            <p class="help-block">ページテキスト中の改行を、HTML内で&lt;br&gt;として扱います</p>
+          </div>
+        </div>
+
+        <div class="form-group">
+          <label for="markdownSetting[markdown:isEnabledLinebreaksInComments]" class="col-xs-4 control-label">
+            (TBD)<br>コメント欄で Line Break を有効にする
+          </label>
+          <div class="col-xs-5">
+            <div class="btn-group btn-toggle" data-toggle="buttons">
+              <label class="btn {% if markdownSetting['markdown:isEnabledLinebreaksInComments'] %}btn-primary active{% else %}btn-default{% endif %}">
+                <input name="markdownSetting[markdown:isEnabledLinebreaksInComments]" value="true" type="radio"
+                    {% if true === markdownSetting['markdown:isEnabledLinebreaksInComments'] %}checked{% endif %}> 有効
+              </label>
+              <label class="btn {% if !markdownSetting['markdown:isEnabledLinebreaksInComments'] %}btn-primary active{% else %}btn-default{% endif %}">
+                <input name="markdownSetting[markdown:isEnabledLinebreaksInComments]" value="false" type="radio"
+                    {% if !markdownSetting['markdown:isEnabledLinebreaksInComments'] %}checked{% endif %}> 無効
+              </label>
+            </div>
+            <p class="help-block">コメント中の改行を、HTML内で&lt;br&gt;として扱います<br>(TBD: コメント欄の Markdown 化は未だ実装されていません)</p>
+          </div>
+        </div>
+
+        <div class="form-group">
+          <div class="col-xs-offset-4 col-xs-5">
+            <input type="hidden" name="_csrf" value="{{ csrf() }}">
+            <button type="submit" class="btn btn-primary">更新</button>
+          </div>
+        </div>
+      </fieldset>
+      </form>
+
+    </div>
+  </div>
+
+</div>
+{% endblock content_main %}
+
+{% block content_footer %}
+{% endblock content_footer %}
+
+
+

+ 1 - 0
lib/views/admin/widget/menu.html

@@ -4,6 +4,7 @@
 <ul class="nav nav-pills nav-stacked">
   <li class="{% if current == 'index'%}active{% endif %}"><a href="/admin"><i class="fa fa-cube"></i> Wiki管理トップ</a></li>
   <li class="{% if current == 'app'%}active{% endif %}"><a href="/admin/app"><i class="fa fa-gears"></i> アプリ設定</a></li>
+  <li class="{% if current == 'markdown'%}active{% endif %}"><a href="/admin/markdown"><i class="fa fa-gears"></i> Markdown設定</a></li>
   <li class="{% if current == 'notification'%}active{% endif %}"><a href="/admin/notification"><i class="fa fa-bell"></i> 通知設定</a></li>
   <li class="{% if current == 'user'%}active{% endif %}"><a href="/admin/users"><i class="fa fa-users"></i> ユーザー管理</a></li>
   {% if searchConfigured() %}

+ 1 - 0
lib/views/page.html

@@ -61,6 +61,7 @@
   data-page-revision-id="{% if revision %}{{ revision._id.toString() }}{% 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-linebreaks-enabled="{{ isEnabledLinebreaks() }}"
   >
 
   {% if not page %}

+ 1 - 0
lib/views/page_list.html

@@ -72,6 +72,7 @@
   data-page-revision-id="{% if revision %}{{ revision._id.toString() }}{% 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-linebreaks-enabled="{{ isEnabledLinebreaks() }}"
   >
 
 <div class="portal {% if not page or req.query.offset > 0 %}hide{% endif %}">

+ 10 - 1
resource/js/legacy/crowi-form.js

@@ -1,6 +1,15 @@
 $(function() {
   var pageId = $('#content-main').data('page-id');
   var pagePath= $('#content-main').data('path');
+  var isEnabledLineBreaks = $('#content-main').data('linebreaks-enabled');
+
+  // generate options obj
+  var rendererOptions = {
+    // see: https://www.npmjs.com/package/marked
+    marked: {
+      breaks: isEnabledLineBreaks
+    }
+  };
 
   require('bootstrap-sass');
   require('inline-attachment/src/inline-attachment');
@@ -63,7 +72,7 @@ $(function() {
 
   function renderPreview() {
     var markdown = $('#form-body').val();
-    var parsedHTML = crowiRenderer.render(markdown);
+    var parsedHTML = crowiRenderer.render(markdown, rendererOptions);
 
     // create context object
     var context = {markdown, parsedHTML, currentPagePath: location.pathname};

+ 11 - 2
resource/js/legacy/crowi.js

@@ -184,6 +184,15 @@ $(function() {
   var currentUser = $('#content-main').data('current-user');
   var isSeen = $('#content-main').data('page-is-seen');
   var pagePath= $('#content-main').data('path');
+  var isEnabledLineBreaks = $('#content-main').data('linebreaks-enabled');
+
+  // generate options obj
+  var rendererOptions = {
+    // see: https://www.npmjs.com/package/marked
+    marked: {
+      breaks: isEnabledLineBreaks
+    }
+  };
 
   Crowi.linkPath();
 
@@ -371,7 +380,7 @@ $(function() {
         var revisionPath = '#' + id + ' .revision-path';
 
         var markdown = Crowi.unescape($(contentId).html());
-        var parsedHTML = crowiRenderer.render(markdown);
+        var parsedHTML = crowiRenderer.render(markdown, rendererOptions);
         $(revisionBody).html(parsedHTML);
 
         $('.template-create-button', revisionBody).on('click', function() {
@@ -417,7 +426,7 @@ $(function() {
     var $rawTextOriginal = $('#raw-text-original');
     if ($rawTextOriginal.length > 0) {
       var markdown = Crowi.unescape($('#raw-text-original').html());
-      var parsedHTML = crowiRenderer.render(markdown);
+      var parsedHTML = crowiRenderer.render(markdown, rendererOptions);
 
       // create context object
       var context = {markdown, parsedHTML, currentPagePath: location.pathname};

+ 23 - 6
resource/js/util/CrowiRenderer.js

@@ -74,15 +74,15 @@ export default class CrowiRenderer {
 
   }
 
-  parseMarkdown(markdown) {
+  parseMarkdown(markdown, markedOpts) {
     let parsed = '';
 
     const markedRenderer = new marked.Renderer();
     markedRenderer.code = this.codeRenderer;
 
     try {
-      // TODO
-      marked.setOptions({
+      // concat
+      let concatMarkedOpts = Object.assign({
         gfm: true,
         tables: true,
         breaks: true,
@@ -91,7 +91,9 @@ export default class CrowiRenderer {
         smartLists: true,
         smartypants: false,
         renderer: markedRenderer,
-      });
+      }, markedOpts);
+
+      marked.setOptions(concatMarkedOpts);
 
       // override
       marked.Lexer.lex = function(src, options) {
@@ -111,11 +113,26 @@ export default class CrowiRenderer {
     return parsed;
   }
 
-  render(markdown) {
+  /**
+   * render
+   *
+   * @param {string} markdown
+   * @param {object} options
+   *  ex:
+   *  ```
+   *    {
+   *      marked: {...} // marked options
+   *    }
+   *  ```
+   * @returns
+   *
+   * @memberOf CrowiRenderer
+   */
+  render(markdown, rendererOptions) {
     let html = '';
 
     markdown = this.preProcess(markdown);
-    html = this.parseMarkdown(markdown);
+    html = this.parseMarkdown(markdown, rendererOptions.marked || {});
 
     return html;
   }