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

Merge pull request #269 from weseek/feat/custom-header-html

Feat/custom header html
Yuki Takei 8 лет назад
Родитель
Сommit
0071f47a41

+ 9 - 0
lib/form/admin/customheader.js

@@ -0,0 +1,9 @@
+'use strict';
+
+var form = require('express-form')
+  , field = form.field
+  ;
+
+module.exports = form(
+  field('settingForm[customize:header]')
+);

+ 1 - 0
lib/form/index.js

@@ -22,6 +22,7 @@ module.exports = {
     markdown: require('./admin/markdown'),
     customcss: require('./admin/customcss'),
     customscript: require('./admin/customscript'),
+    customheader: require('./admin/customheader'),
     custombehavior: require('./admin/custombehavior'),
     customlayout: require('./admin/customlayout'),
     customfeatures: require('./admin/customfeatures'),

+ 7 - 0
lib/models/config.js

@@ -84,6 +84,7 @@ module.exports = function(crowi) {
 
       'customize:css' : '',
       'customize:script' : '',
+      'customize:header' : '',
       'customize:behavior' : 'crowi',
       'customize:layout' : 'crowi',
       'customize:isEnabledTimeline' : true,
@@ -351,6 +352,12 @@ module.exports = function(crowi) {
     return this._customScript;
   }
 
+  configSchema.statics.customHeader = function(config)
+  {
+    const key = 'customize:header';
+    return getValueForCrowiNS(config, key);
+  }
+
   configSchema.statics.behaviorType = function(config)
   {
     const key = 'customize:behavior';

+ 1 - 0
lib/routes/index.js

@@ -73,6 +73,7 @@ module.exports = function(crowi, app) {
   app.get('/admin/customize'                , loginRequired(crowi, app) , middleware.adminRequired() , admin.customize.index);
   app.post('/_api/admin/customize/css'      , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customcss, admin.api.customizeSetting);
   app.post('/_api/admin/customize/script'   , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customscript, admin.api.customizeSetting);
+  app.post('/_api/admin/customize/header'   , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customheader, admin.api.customizeSetting);
   app.post('/_api/admin/customize/behavior' , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.custombehavior, admin.api.customizeSetting);
   app.post('/_api/admin/customize/layout'   , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customlayout, admin.api.customizeSetting);
   app.post('/_api/admin/customize/features' , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customfeatures, admin.api.customizeSetting);

+ 5 - 0
lib/util/swigFunctions.js

@@ -92,6 +92,11 @@ module.exports = function(crowi, app, req, locals) {
     return Config.customScript();
   }
 
+  locals.customHeader = function() {
+    var config = crowi.getConfig()
+    return Config.customHeader(config);
+  }
+
   locals.behaviorType = function() {
     var config = crowi.getConfig()
     return Config.behaviorType(config);

+ 32 - 1
lib/views/admin/customize.html

@@ -295,11 +295,42 @@ window.addEventListener('load', (event) => {
       </fieldset>
       </form>
 
+      <form action="/_api/admin/customize/header" method="post" class="form-horizontal" id="cutomheaderSettingForm" role="form">
+      <fieldset>
+        <legend>カスタムヘッダー</legend>
+
+        <p class="well">
+          システム全体に適用されるHTMLを記述できます。<br>
+          変更の反映はページの更新が必要です。
+        </p>
+
+        <div class="form-group">
+          <div class="col-xs-12">
+            <div id="custom-header-editor"></div>
+            <input type="hidden" id="inputCustomHeader" name="settingForm[customize:header]" value="{{ settingForm['customize:header'] }}">
+          </div>
+          <div class="col-xs-12">
+            <p class="help-block text-right">
+              <i class="fa fa-fw fa-keyboard-o" aria-hidden="true"></i>
+              Ctrl+Space でコード補完
+            </p>
+          </div>
+        </div>
+
+        <div class="form-group">
+          <div class="col-xs-offset-5 col-xs-6">
+            <input type="hidden" name="_csrf" value="{{ csrf() }}">
+            <button type="submit" class="btn btn-primary">更新</button>
+          </div>
+        </div>
+
+      </fieldset>
+      </form>
     </div>
   </div>
 
   <script>
-    $('#cutomcssSettingForm, #cutomscriptSettingForm, #cutomlayoutSettingForm, #cutombehaviorSettingForm, #customfeaturesSettingForm').each(function() {
+    $('#cutomcssSettingForm, #cutomscriptSettingForm, #cutomlayoutSettingForm, #cutombehaviorSettingForm, #customfeaturesSettingForm, #cutomheaderSettingForm').each(function() {
       $(this).submit(function()
       {
         function showMessage(formId, msg, status) {

+ 2 - 0
lib/views/layout/layout.html

@@ -22,6 +22,8 @@
   <link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
   <link rel="icon" type="image/png" href="/android-chrome-192x192.png" sizes="192x192">
 
+  {{ customHeader() }}
+
   <!-- polyfills for IE11 -->
   <script>
     var userAgent = window.navigator.userAgent.toLowerCase();

+ 11 - 0
resource/js/app.js

@@ -25,6 +25,7 @@ import SearchTypeahead  from './components/SearchTypeahead';
 
 import CustomCssEditor  from './components/Admin/CustomCssEditor';
 import CustomScriptEditor from './components/Admin/CustomScriptEditor';
+import CustomHeaderEditor from './components/Admin/CustomHeaderEditor';
 
 import * as entities from 'entities';
 
@@ -193,6 +194,16 @@ if (customScriptEditorElem != null) {
     customScriptEditorElem
   )
 }
+const customHeaderEditorElem = document.getElementById('custom-header-editor');
+if (customHeaderEditorElem != null) {
+  // get input[type=hidden] element
+  const customHeaderInputElem = document.getElementById('inputCustomHeader');
+
+  ReactDOM.render(
+    <CustomHeaderEditor inputElem={customHeaderInputElem} />,
+    customHeaderEditorElem
+  )
+}
 
 // うわーもうー
 $('a[data-toggle="tab"][href="#revision-history"]').on('show.bs.tab', function() {

+ 59 - 0
resource/js/components/Admin/CustomHeaderEditor.js

@@ -0,0 +1,59 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { UnControlled as CodeMirror } from 'react-codemirror2';
+require('codemirror/lib/codemirror.css');
+require('codemirror/addon/display/autorefresh');
+require('codemirror/addon/hint/show-hint');
+require('codemirror/addon/edit/matchbrackets');
+require('codemirror/addon/edit/closebrackets');
+require('codemirror/mode/htmlmixed/htmlmixed');
+require('codemirror/theme/eclipse.css');
+
+require('jquery-ui/ui/widgets/resizable');
+
+export default class CustomHeaderEditor extends React.Component {
+
+  constructor(props) {
+    super(props);
+  }
+
+  render() {
+    // get initial value from inputElem
+    const value = this.props.inputElem.value;
+
+    return (
+      <CodeMirror
+        value={value}
+        autoFocus={true}
+        options={{
+          mode: 'htmlmixed',
+          lineNumbers: true,
+          tabSize: 2,
+          indentUnit: 2,
+          theme: 'eclipse',
+          autoRefresh: true,
+          matchBrackets: true,
+          autoCloseBrackets: true,
+          extraKeys: {"Ctrl-Space": "autocomplete"},
+        }}
+        editorDidMount={(editor, next) => {
+          // resizable with jquery.ui
+          $(editor.getWrapperElement()).resizable({
+            resize: function() {
+              editor.setSize($(this).width(), $(this).height());
+            }
+          });
+        }}
+        onChange={(editor, data, value) => {
+          this.props.inputElem.value = value;
+        }}
+      />
+    )
+  }
+
+}
+
+CustomHeaderEditor.propTypes = {
+  inputElem: PropTypes.object.isRequired,
+};