Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
How to walk in the Crowi
Search
Norio Suzuki
November 03, 2016
Programming
6
16k
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
Share
More Decks by Norio Suzuki
See All by Norio Suzuki
リーダブル プルリクエスト 分割プルリクエスト編 / Readable Pull Request
suzuki
3
1.6k
ふんわり使うPlantUML
suzuki
0
1.1k
Symfony Serializer Deep Dive
suzuki
0
2.1k
Swift Mailer Update
suzuki
2
2.4k
Guzzle Promiseを使った 非同期処理によるAPIコールの高速化
suzuki
15
16k
PHP BLT #4
suzuki
1
4.8k
Learning Swift2 with PHP7
suzuki
2
4.2k
ポモドーロ・テクニック入門の入門
suzuki
0
680
Automatically run the JavaScript test in multiple browsers
suzuki
5
6.2k
Other Decks in Programming
See All in Programming
カオスに立ち向かう小規模チームの装備の選択〜フルスタックTSという装備の強み _ 弱み〜/Choosing equipment for a small team facing chaos ~ Strengths and weaknesses of full-stack TS~
bitkey
1
110
趣味全開のAITuber開発
kokushin
0
200
SwiftUI API Design Lessons
niw
1
300
Exit 8 for SwiftUI
ojun9
0
140
fieldalignmentから見るGoの構造体
kuro_kurorrr
0
110
Compose Hot Reload is here, stop re-launching your apps! (Android Makers 2025)
zsmb
1
550
2ヶ月で生産性2倍、お買い物アプリ「カウシェ」4チーム同時改善の取り組み
ike002jp
1
100
小田原でみんなで一句詠みたいな #phpcon_odawara
stefafafan
0
350
Ruby on Railroad: The Power of Visualizing CFG
ydah
0
200
複雑なフォームの jotai 設計 / Designing jotai(state) for Complex Forms #layerx_frontend
izumin5210
4
1.2k
[NG India] Event-Based State Management with NgRx SignalStore
markostanimirovic
1
170
Deoptimization: How YJIT Speeds Up Ruby by Slowing Down / RubyKaigi 2025
k0kubun
0
1.3k
Featured
See All Featured
Why You Should Never Use an ORM
jnunemaker
PRO
56
9.3k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
12k
Building Adaptive Systems
keathley
41
2.5k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
13
1.4k
Building Applications with DynamoDB
mza
94
6.3k
Reflections from 52 weeks, 52 projects
jeffersonlam
349
20k
Optimising Largest Contentful Paint
csswizardry
37
3.2k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
13
790
The Language of Interfaces
destraynor
157
25k
It's Worth the Effort
3n
184
28k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
5
530
Transcript
How to walk in the Crowi ʙ How PHPer developed
the product of Node.js ʙ PHP Conference 2016 (Tokyo) 2016-11-03 @suzuki
Crowi ͷา͖ํ ʙPHPer ͕ Node.js ͷϓϩμΫτΛ৮ΔΑ͏ʹͳΔ·Ͱʙ PHP Conference 2016 (Tokyo)
2016-11-03 @suzuki
About me
Twitter: @suzuki GitHub: @suzuki
PHP Conference Fukuoka 2016 https://speakerdeck.com/suzuki/guzzle-promisewoshi-tuta-fei-tong-qi-chu-li-niyoruapikorufalsegao-su-hua
PHP Conference Kansai 2016 https://speakerdeck.com/suzuki/swift-mailer-update
Contributor of Crowi https://github.com/crowi/crowi/graphs/contributors
What is Crowi?
Crowi http://site.crowi.wiki/
Features ɾ Wiki ɾ Markdown ɾ Realtime preview ɾ Login
by Google Account ɾ Notification to Slack
GitHub: crowi/crowi https://github.com/crowi/crowi
More information https://medium.com/crowi-book
More information https://speakerdeck.com/sotarok/crowi
Why am I started to develop the Crowi?
When the year-end party in 2015
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
He is product founder of Crowi https://github.com/crowi/crowi/graphs/contributors
I tried to implement
Pull request https://github.com/crowi/crowi/pull/38
Pull request https://github.com/crowi/crowi/pull/38
This is my opportunity to develop the Crowi
Crowi overview
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
<$SPXJ> $MJFOU4JEF 4FSWFS4JEF Inside Crowi /PEFKT &YQSFTT .POHPPTF 4XJH #SPXTFS
K2VFSZ 3FBDU #PPUTUSBQ
How to develop?
Install and Configuration https://github.com/crowi/crowi/wiki/Install-and-Configuration
or
GitHub: suzuki/crowi-develop https://github.com/suzuki/crowi-develop
What is "crowi-develop" ɾ Setup Docker containers ɾMongoDB ɾRedis ɾElasticsearch
ɾ Require ɾNode.js ɾDocker for Mac
What is "suzuki/crowi-develop" .POHP%# &MBTUJDTFBSDI 3FEJT "NB[PO4 4MBDL 3FRVJSF 0QUJPOBM
<$SPXJ> /PEFKT (PPHMF Prepare these
How to initialize "crowi-develop"
Clone "crowi-develop" repository $ git clone https://github.com/suzuki/crowi-develop
Clone "crowi" repository $ cd crowi-develop $ git clone
[email protected]
:crowi/crowi.git
Files in "crowi-develop" LICENSE README.md crowi/ data/ docker-compose.yml Crowi directory
Data directory for Docker containers
Run Docker containers $ cd crowi-develop $ docker-compose up -d
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
Open localhost:3000 http://localhost:3000/
Create administrator http://localhost:3000/installer
Open administration menu http://localhost:3000/ Administration menu
Open search menu http://localhost:3000/admin Search menu
Build search index http://localhost:3000/admin/search Build index
Daily routine: start up "crowi-develop"
Run Docker container $ cd crowi-develop $ docker-compose up -d
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
Daily routine: shutdown "crowi-develop"
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
Shutdown Docker container $ docker-compose stop
Case study: I want to modify…
I want to modify CSS
<$SPXJ> $MJFOU4JEF 4FSWFS4JEF CSS /PEFKT &YQSFTT .POHPPTF 4XJH #SPXTFS K2VFSZ
3FBDU #PPUTUSBQ
Keywords of using tools ɾ Bootstrap ɾ Sass ɾ Gulp
Go to directory $ cd crowi/resource/css/
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
Build $ ./node_modules/.bin/gulp css
Check result ɾ Reload your browser
Watch files $ ./node_modules/.bin/gulp watch
I want to modify HTML
<$SPXJ> $MJFOU4JEF 4FSWFS4JEF HTML (Template) /PEFKT &YQSFTT .POHPPTF 4XJH #SPXTFS
K2VFSZ 3FBDU #PPUTUSBQ
Keywords of using tools ɾ Swig
Swig: template engine https://www.npmjs.com/package/swig
Format sample <h1>{{ pagename }}</h1> <ul> {% for author in
authors %} <li>{{ author }}</li> {% endfor %} </ul> variable name for loop
Swig is similar to the Twig http://twig.sensiolabs.org/
Go to directory $ cd crowi/lib/views/
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
Build Nothing to do
Check result ɾ Restart Crowi ɾ Reload your browser
I want to modify Front-end JavaScript
<$SPXJ> $MJFOU4JEF 4FSWFS4JEF Front-end JavaScript /PEFKT &YQSFTT .POHPPTF 4XJH #SPXTFS
K2VFSZ 3FBDU #PPUTUSBQ
Keywords of using tools ɾ jQuery
Go to directory $ cd crowi/resource/js/
Modify files $ ls -1F app.js components/ crowi-admin.js crowi-form.js crowi-presentation.js
crowi.js util/
Build $ ./node_modules/.bin/gulp [dev]
I want to modify Front-end JavaScript (React)
<$SPXJ> $MJFOU4JEF 4FSWFS4JEF Front-end JavaScript (React) /PEFKT &YQSFTT .POHPPTF 4XJH
#SPXTFS K2VFSZ 3FBDU #PPUTUSBQ
Keywords of using tools ɾ Swig ɾ React
Our future plan: Use React more
Go to directory $ cd crowi/resource/js/
Modify files $ ls -1F app.js components/ crowi-admin.js crowi-form.js crowi-presentation.js
crowi.js util/ React components ReactDOM.render()
Modify files (components) $ ls -1F components/ HeaderSearchBox/ HeaderSearchBox.js Page/
PageList/ PageListSearch.js SearchPage/ SearchPage.js User/
Modify Template 4XJH5FNQMBUF EJWJE40.&@*%@'03@3&/%&3EJW React component will render here
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)
Similar case http://techlife.cookpad.com/entry/2016/10/26/135818
Build $ ./node_modules/.bin/gulp [dev]
Check result ɾ Restart Crowi ɾif you modified template ɾ
Reload your browser
I want to add Some New feature
<$SPXJ> $MJFOU4JEF 4FSWFS4JEF Some new feature (server side) /PEFKT &YQSFTT
.POHPPTF 4XJH #SPXTFS K2VFSZ 3FBDU #PPUTUSBQ
Keywords of using tools ɾ Express ɾ Mongoose ɾ Swig
MVC of Crowi .PEFM .POHPPTF 7JFX 4XJH 3FBDU $POUSPMMFS &YQSFTT
$POUSPMMFS .PEFM Request flow SPVUFTJOEFYKT SPVUFT3065&@KT NPEFMT.0%&-@KT Request NPEFMT.0%&-@KT NPEFMT.0%&-@OKT
SPVUFT3065&@KT SPVUFT3065&@OKT
Controller: Express
Express http://expressjs.com/
Go to directory $ cd crowi/lib/routes/
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
Router: index.js
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
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
Model: Mongoose
Mongoose http://mongoosejs.com/
Go to directory $ cd crowi/lib/models/
Modify files $ ls -1F attachment.js bookmark.js comment.js config.js index.js
page.js revision.js updatePost.js user.js
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}, }
Sample Data
Sample page Page + Revision data
"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
"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
"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'
"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'
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", }
Schema types of Mongoose ɾ String ɾ Number ɾ Date
ɾ Buffer ɾ Boolean ɾ Mixed ɾ ObjectId ɾ Array MongoDB is schema-less, But, Mongoose has schema types
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
View: Swig + React
See CSS, Front-end JS page
How to debug
Debug log
var debug = require('debug')('crowi:routes:page'); Define debug (routes/page.js) Key name
actions.pageListShow = function(req, res) { var path = getPathFromRequest(req); debug('Page
list show', path); Call debug (routes/page.js) No output by default
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.
Run Crowi with DEBUG environment [development] Express server listening on
port 3000 crowi:routes:page Page list show +0ms / Debug log
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:*
More information about DEBUG ɾ Debugging Express ɾ http://expressjs.com/en/guide/debugging.html (English)
ɾ http://expressjs.com/ja/guide/debugging.html (Japanese)
View data
Get container name MondoDB container name is here. For this
example, "crowidevelop_mongodb_1"
Login container $ docker exec -it crowidevelop_mongodb_1 /bin/bash Execute "/bin/bash"
in container
Run mongo CLI root@f7e7c8334c37:/# mongo MongoDB shell version: 3.2.9 connecting
to: local >
Select database > use crowi switched to db crowi
Show collections (SHOW TABLES) > show collections attachments bookmarks comments
configs pages revisions updateposts users Collections are mapped to Models
Find data (SELECT data) > db.pages.find(); SELECT * FROM pages;
Find data: WHERE Clause > db.pages.find({"_id": ObjectId("580fe42645419f6dcf5f4c49")}); SELECT * FROM
pages WHERE _id = ObjectId("580fe42645419f6dcf5f4c49")
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") }
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") }
More information about MongoDB ɾ The mongo Shell ɾ https://docs.mongodb.com/v3.2/mongo/
ɾ GUI ɾ https://docs.mongodb.com/ecosystem/tools/administration-interfaces/
How to Test?
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"
Files $ cd crowi/tests/ $ ls -1F bootstrap.js crowi/ models/
util/ utils.js
Problems ɾ Coverage ɾ We have little tests... ɾ No
View test ɾ React component tests? ɾ E2E tests?
Conclusion
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
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
Today's theme http://phpcon.php.gr.jp/2016/
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
And, we are hiring! https://www.mercari.com/jp/jobs/ But, a full-time Crowi job
is not exist
Thank you
Appendix
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