Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to walk in the Crowi

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/

Norio Suzuki

November 03, 2016
Tweet

More Decks by Norio Suzuki

Other Decks in Programming

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. Features ɾ Wiki ɾ Markdown ɾ Realtime preview ɾ Login

    by Google Account ɾ Notification to Slack
  3. 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
  4. 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
  5. or

  6. 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
  7. 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
  8. 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
  9. Format sample <h1>{{ pagename }}</h1> <ul> {% for author in

    authors %} <li>{{ author }}</li> {% endfor %} </ul> variable name for loop
  10. 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
  11. Modify files (components) $ ls -1F components/
 HeaderSearchBox/
 HeaderSearchBox.js
 Page/


    PageList/
 PageListSearch.js
 SearchPage/
 SearchPage.js
 User/
  12. 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)
  13. 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
  14. 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
  15. 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
  16. 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}, }
  17. "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
  18. "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
  19. "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'
  20. "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'
  21. Schema types of Mongoose ɾ String ɾ Number ɾ Date

    ɾ Buffer ɾ Boolean ɾ Mixed ɾ ObjectId ɾ Array MongoDB is schema-less, But, Mongoose has schema types
  22. 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
  23. actions.pageListShow = function(req, res) { var path = getPathFromRequest(req); debug('Page

    list show', path); Call debug (routes/page.js) No output by default
  24. 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.
  25. Run Crowi with DEBUG environment [development] Express server listening on

    port 3000
 crowi:routes:page Page list show +0ms / Debug log
  26. 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:*
  27. Show collections (SHOW TABLES) > show collections
 attachments
 bookmarks
 comments


    configs
 pages
 revisions
 updateposts
 users Collections are mapped to Models
  28. 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") }
  29. 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")
 }
  30. More information about MongoDB ɾ The mongo Shell ɾ https://docs.mongodb.com/v3.2/mongo/

    ɾ GUI ɾ https://docs.mongodb.com/ecosystem/tools/administration-interfaces/
  31. 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"
  32. Problems ɾ Coverage ɾ We have little tests... ɾ No

    View test ɾ React component tests? ɾ E2E tests?
  33. 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
  34. 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
  35. 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
  36. 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