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

How to walk in the Crowi

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Norio Suzuki 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/

Avatar for Norio Suzuki

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