Răsfoiți Sursa

WIP: GC-524: organize starting process

* modify fields of Page model
* impl hackmd route
Yuki Takei 7 ani în urmă
părinte
comite
b33e9e6580

+ 15 - 1
lib/models/page.js

@@ -70,7 +70,8 @@ module.exports = function(crowi) {
       }
     },
     pageIdOnHackmd: String,
-    revisionHackmdSynced: { type: ObjectId, ref: 'Revision' },
+    revisionHackmdSynced: { type: ObjectId, ref: 'Revision' },  // the revision that is synced to HackMD
+    isHackmdBodyHasDraft: { type: Boolean },                    // set true if revision and revisionHackmdSynced are same but HackMD document has modified
     createdAt: { type: Date, default: Date.now },
     updatedAt: Date
   }, {
@@ -1314,7 +1315,20 @@ module.exports = function(crowi) {
     }
 
     pageData.pageIdOnHackmd = pageIdOnHackmd;
+
+    return this.syncRevisionToHackmd(pageData);
+  };
+
+  /**
+   * update revisionHackmdSynced
+   * @param {Page} pageData
+   */
+  pageSchema.statics.syncRevisionToHackmd = function(pageData) {
     pageData.revisionHackmdSynced = pageData.revision;
+    pageData.isHackmdBodyHasDraft = false;
+    return pageData.save();
+  };
+
 
     // update page
     return pageData.save();

+ 45 - 12
lib/routes/hackmd.js

@@ -16,6 +16,8 @@ module.exports = function(crowi, app) {
 
 
   /**
+   * GET /_hackmd/load-agent
+   *
    * loadAgent action
    * This should be access from HackMD and send agent script
    *
@@ -38,15 +40,10 @@ module.exports = function(crowi, app) {
     res.send(script);
   };
 
-  /**
-   * Create page on HackMD and start to integrate
-   * @param {object} req
-   * @param {object} res
-   */
-  const integrate = async function(req, res) {
+  const validateForApi = async function(req, res, next) {
     // validate process.env.HACKMD_URI
-    const hackMdUri = process.env.HACKMD_URI;
-    if (hackMdUri == null) {
+    const hackmdUri = process.env.HACKMD_URI;
+    if (hackmdUri == null) {
       return res.json(ApiResponse.error('HackMD for GROWI has not been setup'));
     }
     // validate pageId
@@ -59,19 +56,54 @@ module.exports = function(crowi, app) {
     if (page == null) {
       return res.json(ApiResponse.error(`Page('${pageId}') does not exist`));
     }
-    if (page.pageIdOnHackmd != null) {
-      return res.json(ApiResponse.error(`'pageIdOnHackmd' of the page '${page.path}' is not empty`));
+
+    req.page = page;
+    next();
+  };
+
+  /**
+   * POST /_api/hackmd.integrate
+   *
+   * Create page on HackMD and start to integrate
+   * @param {object} req
+   * @param {object} res
+   */
+  const integrate = async function(req, res) {
+    const hackmdUri = process.env.HACKMD_URI;
+    let page = req.page;
+
+    try {
+      if (page.pageIdOnHackmd == null) {
+        page = await createNewPageOnHackmdAndRegister(hackmdUri, page);
+      }
+      else {
+        page = await Page.syncRevisionToHackmd(page);
+      }
+
+      const data = {
+        pageIdOnHackmd: page.pageIdOnHackmd,
+        revisionIdHackmdSynced: page.revisionHackmdSynced,
+        isHackmdBodyHasDraft: page.isHackmdBodyHasDraft,
+      };
+      return res.json(ApiResponse.success(data));
+    }
+    catch (err) {
+      return res.json(ApiResponse.error(err));
     }
+  };
 
+  async function createNewPageOnHackmdAndRegister(hackmdUri, page) {
     // access to HackMD and create page
-    const response = await axios.get(`${hackMdUri}/new`);
+    const response = await axios.get(`${hackmdUri}/new`);
     logger.debug('HackMD responds', response);
 
     // extract page id on HackMD
     const pagePathOnHackmd = response.request.path;     // e.g. '/NC7bSRraT1CQO1TO7wjCPw'
     const pageIdOnHackmd = pagePathOnHackmd.substr(1);  // strip the head '/'
 
-    // persist
+    return Page.registerHackmdPage(page, pageIdOnHackmd);
+  }
+
     try {
       await Page.registerHackmdPage(page, pageIdOnHackmd);
 
@@ -87,6 +119,7 @@ module.exports = function(crowi, app) {
 
   return {
     loadAgent,
+    validateForApi,
     integrate,
   };
 };

+ 2 - 2
lib/routes/index.js

@@ -201,8 +201,8 @@ module.exports = function(crowi, app) {
   app.get('/trash/$'                 , loginRequired(crowi, app, false) , page.trashPageListShowWrapper);
   app.get('/trash/*/$'               , loginRequired(crowi, app, false) , page.deletedPageListShowWrapper);
 
-  app.get('/_hackmd/load-agent'      , hackmd.loadAgent);
-  app.post('/_api/hackmd/integrate'  , accessTokenParser , loginRequired(crowi, app) , csrf, hackmd.integrate);
+  app.get('/_hackmd/load-agent'        , hackmd.loadAgent);
+  app.post('/_api/hackmd.integrate'    , accessTokenParser , loginRequired(crowi, app) , csrf, hackmd.validateForApi, hackmd.integrate);
 
   app.get('/*/$'                   , loginRequired(crowi, app, false) , page.pageListShowWrapper);
   app.get('/*'                     , loginRequired(crowi, app, false) , page.pageShowWrapper);

+ 5 - 2
lib/routes/page.js

@@ -179,8 +179,9 @@ module.exports = function(crowi, app) {
 
       if (portalPage) {
         renderVars.revision = portalPage.revision;
-        renderVars.revisionHackmdSynced = portalPage.revisionHackmdSynced;
         renderVars.pageIdOnHackmd = portalPage.pageIdOnHackmd;
+        renderVars.revisionHackmdSynced = portalPage.revisionHackmdSynced;
+        renderVars.isHackmdBodyHasDraft = portalPage.isHackmdBodyHasDraft;
         return Revision.findRevisionList(portalPage.path, {});
       }
       else {
@@ -258,6 +259,7 @@ module.exports = function(crowi, app) {
       pageRelatedGroup: null,
       template: null,
       revisionHackmdSynced: null,
+      isHackmdBodyHasDraft: false,
       slack: '',
     };
 
@@ -281,8 +283,9 @@ module.exports = function(crowi, app) {
         renderVars.path = page.path;
         renderVars.revision = page.revision;
         renderVars.author = page.revision.author;
-        renderVars.revisionHackmdSynced = page.revisionHackmdSynced;
         renderVars.pageIdOnHackmd = page.pageIdOnHackmd;
+        renderVars.revisionHackmdSynced = page.revisionHackmdSynced;
+        renderVars.isHackmdBodyHasDraft = page.isHackmdBodyHasDraft;
 
         return Revision.findRevisionList(page.path, {})
         .then(function(tree) {

+ 1 - 0
lib/views/widget/page_content.html

@@ -7,6 +7,7 @@
   data-page-revision-created="{% if revision %}{{ revision.createdAt|datetz('U') }}{% endif %}"
   data-page-revision-id-hackmd-synced="{% if revisionHackmdSynced %}{{ revisionHackmdSynced.toString() }}{% endif %}"
   data-page-id-on-hackmd="{% if pageIdOnHackmd %}{{ pageIdOnHackmd.toString() }}{% endif %}"
+  data-page-hackmd-body-has-draft="{% if isHackmdBodyHasDraft %}{{ isHackmdBodyHasDraft.toString() }}{% endif %}"
   data-page-is-seen="{% if page and page.isSeenUser(user) %}1{% else %}0{% endif %}"
   data-slack-channels="{{ slack|default('') }}"
   >

+ 3 - 2
resource/js/app.js

@@ -52,11 +52,11 @@ let pageId = null;
 let pageRevisionId = null;
 let pageRevisionCreatedAt = null;
 let pageRevisionIdHackmdSynced = null;
+let isHackmdBodyHasDraft = false;
 let pageIdOnHackmd = null;
 let pagePath;
 let pageContent = '';
 let markdown = '';
-let pageGrant = null;
 let slackChannels = '';
 if (mainContent !== null) {
   pageId = mainContent.getAttribute('data-page-id');
@@ -64,6 +64,7 @@ if (mainContent !== null) {
   pageRevisionCreatedAt = +mainContent.getAttribute('data-page-revision-created');
   pageRevisionIdHackmdSynced = mainContent.getAttribute('data-page-revision-id-hackmd-synced') || null;
   pageIdOnHackmd = mainContent.getAttribute('data-page-id-on-hackmd') || null;
+  isHackmdBodyHasDraft = !!mainContent.getAttribute('data-page-hackmd-body-has-draft');
   pagePath = mainContent.attributes['data-path'].value;
   slackChannels = mainContent.getAttribute('data-slack-channels');
   const rawText = document.getElementById('raw-text-original');
@@ -282,7 +283,7 @@ if (pageEditorWithHackmdElem) {
   pageEditor = ReactDOM.render(
     <PageEditorByHackmd crowi={crowi}
         pageId={pageId} revisionId={pageRevisionId}
-        revisionIdHackmdSynced={pageRevisionIdHackmdSynced} pageIdOnHackmd={pageIdOnHackmd}
+        pageIdOnHackmd={pageIdOnHackmd} revisionIdHackmdSynced={pageRevisionIdHackmdSynced} isHackmdBodyHasDraft={isHackmdBodyHasDraft}
         markdown={markdown}
         onSaveSuccess={onSaveSuccess} />,
     pageEditorWithHackmdElem

+ 22 - 18
resource/js/components/PageEditorByHackmd.jsx

@@ -54,7 +54,7 @@ export default class PageEditorByHackmd extends React.PureComponent {
     const params = {
       pageId: this.props.pageId,
     };
-    this.props.crowi.apiPost('/hackmd/integrate', params)
+    this.props.crowi.apiPost('/hackmd.integrate', params)
       .then(res => {
         if (!res.ok) {
           throw new Error(res.error);
@@ -63,6 +63,7 @@ export default class PageEditorByHackmd extends React.PureComponent {
         this.setState({
           isInitialized: true,
           pageIdOnHackmd: res.pageIdOnHackmd,
+          revisionIdHackmdSynced: res.revisionIdHackmdSynced,
         });
       })
       .catch(this.apiErrorHandler)
@@ -71,6 +72,9 @@ export default class PageEditorByHackmd extends React.PureComponent {
       });
   }
 
+  /**
+   * Start to edit w/o any api request
+   */
   resumeToEdit() {
     this.setState({isInitialized: true});
   }
@@ -92,9 +96,9 @@ export default class PageEditorByHackmd extends React.PureComponent {
     if (this.state.isInitialized) {
       return (
         <HackmdEditor
-        markdown={this.props.markdown}
-        hackmdUri={hackmdUri}
-        pageIdOnHackmd={this.state.pageIdOnHackmd}
+          markdown={this.props.markdown}
+          hackmdUri={hackmdUri}
+          pageIdOnHackmd={this.state.pageIdOnHackmd}
         >
         </HackmdEditor>
       );
@@ -112,35 +116,34 @@ export default class PageEditorByHackmd extends React.PureComponent {
         </div>
       );
     }
-    // Page does not have 'pageIdOnHackmd' or revisions are mismatch
-    else if (!isPageExistsOnHackmd || !isRevisionMatch) {
+    // page is exists, revisions are match, isHackmdBodyHasDraft is true
+    else if (isPageExistsOnHackmd && isRevisionMatch && this.props.isHackmdBodyHasDraft) {
       content = (
         <div>
           <p className="text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is READY!</p>
           <p className="text-center">
-            <button className="btn btn-info btn-lg waves-effect waves-light" type="button"
-                onClick={() => this.syncAndStartToEdit()} disabled={this.state.isInitializing}>
-              <span className="btn-label"><i className="icon-paper-plane"></i></span>
-              Start to edit with HackMD
+            <button className="btn btn-success btn-lg waves-effect waves-light" type="button"
+                onClick={() => this.resumeToEdit()}>
+              <span className="btn-label"><i className="icon-control-end"></i></span>
+              Resume to edit with HackMD
             </button>
           </p>
-          <p className="text-center">Click to clone page content and start to edit.</p>
+          <p className="text-center">Click to edit from the previous continuation.</p>
         </div>
       );
     }
-    // revision match -> continue
     else {
       content = (
         <div>
           <p className="text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is READY!</p>
           <p className="text-center">
-            <button className="btn btn-success btn-lg waves-effect waves-light" type="button"
-                onClick={() => this.resumeToEdit()}>
-              <span className="btn-label"><i className="icon-control-end"></i></span>
-              Resume to edit with HackMD
+            <button className="btn btn-info btn-lg waves-effect waves-light" type="button"
+                onClick={() => this.syncAndStartToEdit()} disabled={this.state.isInitializing}>
+              <span className="btn-label"><i className="icon-paper-plane"></i></span>
+              Start to edit with HackMD
             </button>
           </p>
-          <p className="text-center">Click to edit from the previous continuation.</p>
+          <p className="text-center">Click to clone page content and start to edit.</p>
         </div>
       );
     }
@@ -158,6 +161,7 @@ PageEditorByHackmd.propTypes = {
   markdown: PropTypes.string.isRequired,
   pageId: PropTypes.string,
   revisionId: PropTypes.string,
-  revisionIdHackmdSynced: PropTypes.string,
   pageIdOnHackmd: PropTypes.string,
+  revisionIdHackmdSynced: PropTypes.string,
+  isHackmdBodyHasDraft: PropTypes.bool,
 };