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
15k
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.3k
ふんわり使うPlantUML
suzuki
0
1k
Symfony Serializer Deep Dive
suzuki
0
2k
Swift Mailer Update
suzuki
2
2.3k
Guzzle Promiseを使った 非同期処理によるAPIコールの高速化
suzuki
15
16k
PHP BLT #4
suzuki
1
4.6k
Learning Swift2 with PHP7
suzuki
2
4.1k
ポモドーロ・テクニック入門の入門
suzuki
0
660
Automatically run the JavaScript test in multiple browsers
suzuki
5
6.1k
Other Decks in Programming
See All in Programming
役立つログに取り組もう
irof
28
9.6k
EventSourcingの理想と現実
wenas
6
2.3k
as(型アサーション)を書く前にできること
marokanatani
10
2.6k
A Journey of Contribution and Collaboration in Open Source
ivargrimstad
0
890
TypeScriptでライブラリとの依存を限定的にする方法
tutinoko
2
660
CSC509 Lecture 09
javiergs
PRO
0
140
3 Effective Rules for Using Signals in Angular
manfredsteyer
PRO
0
100
광고 소재 심사 과정에 AI를 도입하여 광고 서비스 생산성 향상시키기
kakao
PRO
0
170
ローコードSaaSのUXを向上させるためのTypeScript
taro28
1
610
距離関数を極める! / SESSIONS 2024
gam0022
0
280
watsonx.ai Dojo #4 生成AIを使ったアプリ開発、応用編
oniak3ibm
PRO
1
100
3rd party scriptでもReactを使いたい! Preact + Reactのハイブリッド開発
righttouch
PRO
1
600
Featured
See All Featured
Writing Fast Ruby
sferik
627
61k
A designer walks into a library…
pauljervisheath
203
24k
Put a Button on it: Removing Barriers to Going Fast.
kastner
59
3.5k
For a Future-Friendly Web
brad_frost
175
9.4k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
4
370
Building Adaptive Systems
keathley
38
2.3k
GitHub's CSS Performance
jonrohan
1030
460k
GraphQLの誤解/rethinking-graphql
sonatard
67
10k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
126
18k
Building Flexible Design Systems
yeseniaperezcruz
327
38k
How STYLIGHT went responsive
nonsquared
95
5.2k
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