How to walk in the Crowi

8e528456ff66ec543952daa815353a01?s=47 Norio Suzuki
November 03, 2016

How to walk in the Crowi

How to walk in the Crowi / Crowi の歩き方

at PHP Conference 2016
http://phpcon.php.gr.jp/2016/

8e528456ff66ec543952daa815353a01?s=128

Norio Suzuki

November 03, 2016
Tweet

Transcript

  1. How to walk in the Crowi ʙ How PHPer developed

    the product of Node.js ʙ PHP Conference 2016 (Tokyo) 2016-11-03 @suzuki
  2. Crowi ͷา͖ํ ʙPHPer ͕ Node.js ͷϓϩμΫτΛ৮ΔΑ͏ʹͳΔ·Ͱʙ PHP Conference 2016 (Tokyo)

    2016-11-03 @suzuki
  3. About me

  4. Twitter: @suzuki GitHub: @suzuki

  5. PHP Conference Fukuoka 2016 https://speakerdeck.com/suzuki/guzzle-promisewoshi-tuta-fei-tong-qi-chu-li-niyoruapikorufalsegao-su-hua

  6. PHP Conference Kansai 2016 https://speakerdeck.com/suzuki/swift-mailer-update

  7. Contributor of Crowi https://github.com/crowi/crowi/graphs/contributors

  8. What is Crowi?

  9. Crowi http://site.crowi.wiki/

  10. Features ɾ Wiki ɾ Markdown ɾ Realtime preview ɾ Login

    by Google Account ɾ Notification to Slack
  11. GitHub: crowi/crowi https://github.com/crowi/crowi

  12. More information https://medium.com/crowi-book

  13. More information https://speakerdeck.com/sotarok/crowi

  14. Why am I started to develop the Crowi?

  15. When the year-end party in 2015

  16. Talk about "esa.io" I like "esa.io". I love their Markdown

    helper. OK. You create it. I want to use that helper on Crowi. esa logo : https://docs.esa.io/posts/125
  17. He is product founder of Crowi https://github.com/crowi/crowi/graphs/contributors

  18. I tried to implement

  19. Pull request https://github.com/crowi/crowi/pull/38

  20. Pull request https://github.com/crowi/crowi/pull/38

  21. This is my opportunity
 to develop the Crowi

  22. Crowi overview

  23. System Diagram .POHP%# &MBTUJDTFBSDI 3FEJT "NB[PO4 4MBDL 3FRVJSF 0QUJPOBM Remote

    Notification Full text search Data Store Session Data File upload <$SPXJ> /PEFKT (PPHMF Social Login
  24. <$SPXJ> $MJFOU4JEF 4FSWFS4JEF Inside Crowi /PEFKT &YQSFTT .POHPPTF 4XJH #SPXTFS

    K2VFSZ 3FBDU #PPUTUSBQ
  25. How to develop?

  26. Install and Configuration https://github.com/crowi/crowi/wiki/Install-and-Configuration

  27. or

  28. GitHub: suzuki/crowi-develop https://github.com/suzuki/crowi-develop

  29. What is "crowi-develop" ɾ Setup Docker containers ɾMongoDB ɾRedis ɾElasticsearch

    ɾ Require ɾNode.js ɾDocker for Mac
  30. What is "suzuki/crowi-develop" .POHP%# &MBTUJDTFBSDI 3FEJT "NB[PO4 4MBDL 3FRVJSF 0QUJPOBM

    <$SPXJ> /PEFKT (PPHMF Prepare these
  31. How to initialize
 "crowi-develop"

  32. Clone "crowi-develop" repository $ git clone https://github.com/suzuki/crowi-develop

  33. Clone "crowi" repository $ cd crowi-develop $ git clone git@github.com:crowi/crowi.git

  34. Files in "crowi-develop" LICENSE
 README.md
 crowi/
 data/
 docker-compose.yml Crowi directory

    Data directory
 for Docker containers
  35. Run Docker containers $ cd crowi-develop
 $ docker-compose up -d

  36. Run Crowi $ cd crowi-develop/crowi 
 $ PASSWORD_SEED=somesecretstring \
 MONGO_URI=mongodb://localhost/crowi

    \
 ELASTICSEARCH_URI=localhost:9200 \
 REDIS_URL=localhost \
 FILE_UPLOAD=local \
 node app.js Change your own word
  37. Open localhost:3000 http://localhost:3000/

  38. Create administrator http://localhost:3000/installer

  39. Open administration menu http://localhost:3000/ Administration menu

  40. Open search menu http://localhost:3000/admin Search menu

  41. Build search index http://localhost:3000/admin/search Build index

  42. Daily routine: start up "crowi-develop"

  43. Run Docker container $ cd crowi-develop
 $ docker-compose up -d

  44. Run Crowi $ cd crowi-develop/crowi 
 $ PASSWORD_SEED=somesecretstring \
 MONGO_URI=mongodb://localhost/crowi

    \
 ELASTICSEARCH_URI=localhost:9200 \
 REDIS_URL=localhost \
 FILE_UPLOAD=local \
 node app.js Change your own word
  45. Daily routine: shutdown "crowi-develop"

  46. Shutdown Crowi 
 $ PASSWORD_SEED=somesecretstring \
 MONGO_URI=mongodb://localhost/crowi \
 ELASTICSEARCH_URI=localhost:9200 \


    REDIS_URL=localhost \
 FILE_UPLOAD=local \
 node app.js
 Press Ctrl + C
  47. Shutdown Docker container $ docker-compose stop

  48. Case study: I want to modify…

  49. I want to modify CSS

  50. <$SPXJ> $MJFOU4JEF 4FSWFS4JEF CSS /PEFKT &YQSFTT .POHPPTF 4XJH #SPXTFS K2VFSZ

    3FBDU #PPUTUSBQ
  51. Keywords of using tools ɾ Bootstrap ɾ Sass ɾ Gulp

  52. Go to directory $ cd crowi/resource/css/

  53. Modify files $ ls -1F
 _admin.scss
 _comment.scss
 _form.scss
 _layout.scss
 _mixins.scss


    _page.scss
 _page_list.scss
 _portal.scss
 _search.scss
 _user.scss
 _utilities.scss
 _variables.scss
 _wiki.scss
 crowi-reveal.scss
 crowi.scss "_NAME.scss" are component "crowi.scss" is main file
  54. Build $ ./node_modules/.bin/gulp css

  55. Check result ɾ Reload your browser

  56. Watch files $ ./node_modules/.bin/gulp watch

  57. I want to modify HTML

  58. <$SPXJ> $MJFOU4JEF 4FSWFS4JEF HTML (Template) /PEFKT &YQSFTT .POHPPTF 4XJH #SPXTFS

    K2VFSZ 3FBDU #PPUTUSBQ
  59. Keywords of using tools ɾ Swig

  60. Swig: template engine https://www.npmjs.com/package/swig

  61. Format sample <h1>{{ pagename }}</h1> <ul> {% for author in

    authors %} <li>{{ author }}</li> {% endfor %} </ul> variable name for loop
  62. Swig is similar to the Twig http://twig.sensiolabs.org/

  63. Go to directory $ cd crowi/lib/views/

  64. Modify files $ ls -1F
 500.html
 _form.html
 admin/
 index.html
 installer.html


    invited.html
 layout/
 login/
 login.html
 mail/
 me/
 modal/
 page.html
 page_list.html
 page_presentation.html
 search.html
 user/
 user_page.html
 widget/ "*.html" are template of Swig
  65. Build Nothing to do

  66. Check result ɾ Restart Crowi ɾ Reload your browser

  67. I want to modify Front-end JavaScript

  68. <$SPXJ> $MJFOU4JEF 4FSWFS4JEF Front-end JavaScript /PEFKT &YQSFTT .POHPPTF 4XJH #SPXTFS

    K2VFSZ 3FBDU #PPUTUSBQ
  69. Keywords of using tools ɾ jQuery

  70. Go to directory $ cd crowi/resource/js/

  71. Modify files $ ls -1F
 app.js
 components/
 crowi-admin.js
 crowi-form.js
 crowi-presentation.js


    crowi.js
 util/
  72. Build $ ./node_modules/.bin/gulp [dev]

  73. I want to modify Front-end JavaScript (React)

  74. <$SPXJ> $MJFOU4JEF 4FSWFS4JEF Front-end JavaScript (React) /PEFKT &YQSFTT .POHPPTF 4XJH

    #SPXTFS K2VFSZ 3FBDU #PPUTUSBQ
  75. Keywords of using tools ɾ Swig ɾ React

  76. Our future plan: Use React more

  77. Go to directory $ cd crowi/resource/js/

  78. Modify files $ ls -1F
 app.js
 components/
 crowi-admin.js
 crowi-form.js
 crowi-presentation.js


    crowi.js
 util/ React components ReactDOM.render()
  79. Modify files (components) $ ls -1F components/
 HeaderSearchBox/
 HeaderSearchBox.js
 Page/


    PageList/
 PageListSearch.js
 SearchPage/
 SearchPage.js
 User/
  80. Modify Template 4XJH5FNQMBUF EJWJE40.&@*%@'03@3&/%&3EJW React component will render here

  81. Modify React renderer (in app.js) const componentMappings = { 'search-top':

    <HeaderSearchBox />, 'search-page': <SearchPage />, 'page-list-search': <PageListSearch />, }; Object.keys(componentMappings).forEach((key) => { const elem = document.getElementById(key); if (elem) { ReactDOM.render(componentMappings[key], elem); } }); DOM id (in Swig template)
  82. Similar case http://techlife.cookpad.com/entry/2016/10/26/135818

  83. Build $ ./node_modules/.bin/gulp [dev]

  84. Check result ɾ Restart Crowi ɾif you modified template ɾ

    Reload your browser
  85. I want to add Some New feature

  86. <$SPXJ> $MJFOU4JEF 4FSWFS4JEF Some new feature (server side) /PEFKT &YQSFTT

    .POHPPTF 4XJH #SPXTFS K2VFSZ 3FBDU #PPUTUSBQ
  87. Keywords of using tools ɾ Express ɾ Mongoose ɾ Swig

  88. MVC of Crowi .PEFM .POHPPTF 7JFX 4XJH 3FBDU $POUSPMMFS &YQSFTT

  89. $POUSPMMFS .PEFM Request flow SPVUFTJOEFYKT SPVUFT3065&@KT NPEFMT.0%&-@KT Request NPEFMT.0%&-@KT NPEFMT.0%&-@OKT

    SPVUFT3065&@KT SPVUFT3065&@OKT
  90. Controller: Express

  91. Express http://expressjs.com/

  92. Go to directory $ cd crowi/lib/routes/

  93. Modify files $ ls -1F
 admin.js
 attachment.js
 bookmark.js
 comment.js
 index.js


    installer.js
 login.js
 logout.js
 me.js
 page.js
 revision.js
 search.js
 user.js
  94. Router: index.js

  95. Router (index.js) var page = require('./page')(crowi, app); var middleware =

    require('../util/middlewares'); var loginRequired = middleware.loginRequired; app.get('/*/$', loginRequired(crowi, app), page.pageListShow); app.get('/*', loginRequired(crowi, app), page.pageShow); HTTP method Request path Router (Controller) method
  96. page.pageShow (page.js) actions.pageShow = function(req, res) { var path =

    path || getPathFromRequest(req); Page.findPage(path, req.user, req.query.revision) .then(function(page) { // some routines return renderPage(page, req, res); }).catch(function(err) { // not found }); }; Page model method
  97. Model: Mongoose

  98. Mongoose http://mongoosejs.com/

  99. Go to directory $ cd crowi/lib/models/

  100. Modify files $ ls -1F
 attachment.js
 bookmark.js
 comment.js
 config.js
 index.js


    page.js
 revision.js
 updatePost.js
 user.js
  101. DB Schema (page.js) pageSchema = new mongoose.Schema({ path: {type: String,

    required: true, index: true, unique: true}, revision: {type: ObjectId, ref: 'Revision'}, status: {type: String, default: STATUS_PUBLISHED, index: true}, creator: {type: ObjectId, ref: 'User', index: true}, createdAt: {type: Date, default: Date.now}, }
  102. Sample Data

  103. Sample page Page + Revision data

  104. "path" field: String pageSchema = new mongoose.Schema({ path: {type: String,

    required: true, index: true, unique: true}, revision: {type: ObjectId, ref: 'Revision'}, status: {type: String, default: STATUS_PUBLISHED, index: true}, creator: {type: ObjectId, ref: 'User', index: true}, createdAt: {type: Date, default: Date.now}, } type: String
  105. "path" field data { "_id": ObjectId("580fe42645419f6dcf5f4c49"), "path": "/user/suzuki/memo/2016/10/26/test_page", "revision": ObjectId("580fe42645419f6dcf5f4c4a")

    "status": "published", "creator": ObjectId("580401458462d38221a37094"), "createdAt": ISODate("2016-10-25T23:00:54.020Z"), } String data
  106. "revision" field: ObjectId pageSchema = new mongoose.Schema({ path: {type: String,

    required: true, index: true, unique: true}, revision: {type: ObjectId, ref: 'Revision'}, status: {type: String, default: STATUS_PUBLISHED, index: true}, creator: {type: ObjectId, ref: 'User', index: true}, createdAt: {type: Date, default: Date.now}, } type: ObjectId ref: 'Revision'
  107. "revision" field data { "_id": ObjectId("580fe42645419f6dcf5f4c49"), "path": "/user/suzuki/memo/2016/10/26/test_page", "revision": ObjectId("580fe42645419f6dcf5f4c4a")

    "status": "published", "creator": ObjectId("580401458462d38221a37094"), "createdAt": ISODate("2016-10-25T23:00:54.020Z"), } This means reference 'Revision'
  108. Revision data { "_id": ObjectId("580fe42645419f6dcf5f4c4a"), "author": ObjectId("580401458462d38221a37094"), "body": "# test_page\r\n\r\nThis

    is a test page.\r\n", "path": "/user/suzuki/memo/2016/10/26/test_page", }
  109. Schema types of Mongoose ɾ String ɾ Number ɾ Date

    ɾ Buffer ɾ Boolean ɾ Mixed ɾ ObjectId ɾ Array MongoDB is schema-less, But, Mongoose has schema types
  110. Method (page.js) // find page and check if granted user

    pageSchema.statics.findPage = function(path, userData, revisionId, ignoreNotFound) { var self = this; return new Promise(function(resolve, reject) { self.findOne({path: path}, function(err, pageData) { if (err) { return reject(err); } if (pageData === null) { if (ignoreNotFound) { return resolve(null); } var pageNotFoundError = new Error('Page Not Found') pageNotFoundError.name = 'Crowi:Page:NotFound'; return reject(pageNotFoundError); } // snip
  111. View: Swig + React

  112. See CSS, Front-end JS page

  113. How to debug

  114. Debug log

  115. var debug = require('debug')('crowi:routes:page'); Define debug (routes/page.js) Key name

  116. actions.pageListShow = function(req, res) { var path = getPathFromRequest(req); debug('Page

    list show', path); Call debug (routes/page.js) No output by default
  117. Run Crowi with DEBUG environment 
 $ PASSWORD_SEED=somesecretstring \
 MONGO_URI=mongodb://localhost/crowi

    \
 ELASTICSEARCH_URI=localhost:9200 \
 REDIS_URL=localhost \
 FILE_UPLOAD=local \
 DEBUG=crowi:routes:page \
 node app.js Set DEBUG env.
  118. Run Crowi with DEBUG environment [development] Express server listening on

    port 3000
 crowi:routes:page Page list show +0ms / Debug log
  119. lib/routes/page.js: var debug = require('debug')('crowi:routes:page'); lib/routes/revision.js: var debug = require('debug')('crowi:routes:revision');

    lib/models/page.js: var debug = require('debug')('crowi:models:page'); lib/models/comment.js: var debug = require('debug')('crowi:models:comment'); Wildcard DEBUG env. DEBUG=crowi:models:*
  120. More information about DEBUG ɾ Debugging Express ɾ http://expressjs.com/en/guide/debugging.html (English)

    ɾ http://expressjs.com/ja/guide/debugging.html (Japanese)
  121. View data

  122. Get container name MondoDB container name is here. For this

    example, "crowidevelop_mongodb_1"
  123. Login container $ docker exec -it crowidevelop_mongodb_1 /bin/bash Execute "/bin/bash"

    in container
  124. Run mongo CLI root@f7e7c8334c37:/# mongo
 MongoDB shell version: 3.2.9
 connecting

    to: local
 >
  125. Select database > use crowi
 switched to db crowi

  126. Show collections (SHOW TABLES) > show collections
 attachments
 bookmarks
 comments


    configs
 pages
 revisions
 updateposts
 users Collections are mapped to Models
  127. Find data (SELECT data) > db.pages.find(); SELECT * FROM pages;

  128. Find data: WHERE Clause > db.pages.find({"_id": ObjectId("580fe42645419f6dcf5f4c49")}); SELECT * FROM

    pages WHERE _id = ObjectId("580fe42645419f6dcf5f4c49")
  129. find() > db.pages.find();
 { "_id" : ObjectId("580fe42645419f6dcf5f4c49"), "redirectTo" : null,

    "updatedAt" : ISODate("2016-10-25T23:00:54.091Z"), "lastUpdateUser" : ObjectId("580401458462d38221a37094"), "creator" : ObjectId("580401458462d38221a37094"), "path" : "/ user/suzuki/memo/2016/10/26/test_page", "createdAt" : ISODate("2016-10-25T23:00:54.020Z"), "extended" : "{}", "commentCount" : 1, "seenUsers" : [ ObjectId("580401458462d38221a37094") ], "liker" : [ ], "grantedUsers" : [ ObjectId("580401458462d38221a37094") ], "grant" : 1, "status" : "published", "__v" : 1, "revision" : ObjectId("580fe42645419f6dcf5f4c4a") }
  130. find().pretty() > db.pages.find().pretty();
 {
 "_id" : ObjectId("580fe42645419f6dcf5f4c49"),
 "redirectTo" : null,


    "updatedAt" : ISODate("2016-10-25T23:00:54.091Z"),
 "lastUpdateUser" : ObjectId("580401458462d38221a37094"),
 "creator" : ObjectId("580401458462d38221a37094"),
 "path" : "/user/suzuki/memo/2016/10/26/test_page",
 "createdAt" : ISODate("2016-10-25T23:00:54.020Z"),
 "extended" : "{}",
 "commentCount" : 1,
 // snip
 "status" : "published",
 "revision" : ObjectId("580fe42645419f6dcf5f4c4a")
 }
  131. More information about MongoDB ɾ The mongo Shell ɾ https://docs.mongodb.com/v3.2/mongo/

    ɾ GUI ɾ https://docs.mongodb.com/ecosystem/tools/administration-interfaces/
  132. How to Test?

  133. Run tests $ ./node_modules/.bin/gulp test OR $ MONGO_URI=localhost:crowi_test \
 ./node_modules/.bin/gulp

    test "crowi_test" DB is used "crowi" (default) DB is used I recommend to use different name from "crowi"
  134. Files $ cd crowi/tests/ $ ls -1F
 bootstrap.js
 crowi/
 models/


    util/
 utils.js
  135. Problems ɾ Coverage ɾ We have little tests... ɾ No

    View test ɾ React component tests? ɾ E2E tests?
  136. Conclusion

  137. Crowi is a growing software ɾ Crowi is very simple

    and useful ɾ Growing software ɾ We are using the Crowi in our company ɾ When received feedback, 
 we will fixed that problem or create new features ɾ Try to use the Crowi in your organization
  138. We learned first-step to develop Crowi ɾ Today, we have

    learned how to develop the Crowi ɾ You can use your experience in PHP ɾ It is not difficult to develop
  139. Today's theme http://phpcon.php.gr.jp/2016/

  140. Next Action: Contribute! ɾ Code for Crowi ɾ Pull Request,

    Issue, Document, etc. ɾ Talk about Crowi ɾ Gitter, Twitter, Facebook, Blog, etc. ɾ #crowi in Twitter ɾ Gitter: https://gitter.im/crowi/general
  141. And, we are hiring! https://www.mercari.com/jp/jobs/ But, a full-time Crowi job

    is not exist
  142. Thank you

  143. Appendix

  144. Appendix ɾ Crowi ɾ https://github.com/crowi/crowi ɾ Crowi develop ɾ https://github.com/suzuki/crowi-develop

    ɾ Crowi book (blog) ɾ https://medium.com/crowi-book ɾ Crowi gitter ɾ https://gitter.im/crowi/general