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
EtherCalc for Drupal
Search
唐鳳
July 07, 2012
Programming
3
320
EtherCalc for Drupal
First presented at DrupalCamp Taipei as a keynote talk.
唐鳳
July 07, 2012
Tweet
Share
More Decks by 唐鳳
See All by 唐鳳
20210518簡訊實聯制簡報(純圖卡)
audreyt
0
460k
Global Youth Trends Forum
audreyt
0
160
RadicalxChange @ TEDxWeekend Taipei
audreyt
0
93
RadicalxChange @ Devcon 5
audreyt
1
150
11/07 工研院 IEK 「眺望2017產業發展趨勢研討會」演講
audreyt
2
260
2016.10.18 社會企業行動方案報告
audreyt
0
1.3k
2016.10.18 推動社會企業執行報告
audreyt
0
350
vTaiwan meeting 20161008
audreyt
0
480
運用網路平台協助公民參與
audreyt
0
210
Other Decks in Programming
See All in Programming
KubeCon + CloudNativeCon NA 2024 Overviewat Kubernetes Meetup Tokyo #68 / amsy810_k8sjp68
masayaaoyama
0
250
PHPUnitしか使ってこなかった 一般PHPerがPestに乗り換えた実録
mashirou1234
0
210
ゆるやかにgolangci-lintのルールを強くする / Kyoto.go #56
utgwkk
2
390
わたしの星のままで一番星になる ~ 出産を機にSIerからEC事業会社に転職した話 ~
kimura_m_29
0
180
Semantic Kernelのネイティブプラグインで知識拡張をしてみる
tomokusaba
0
180
Cloudflare MCP ServerでClaude Desktop からWeb APIを構築
kutakutat
1
550
Symfony Mapper Component
soyuka
2
740
testcontainers のススメ
sgash708
1
120
各クラウドサービスにおける.NETの対応と見解
ymd65536
0
110
暇に任せてProxmoxコンソール 作ってみました
karugamo
2
720
From Translations to Multi Dimension Entities
alexanderschranz
2
130
テストコード文化を0から作り、変化し続けた組織
kazatohiei
2
1.5k
Featured
See All Featured
BBQ
matthewcrist
85
9.4k
Music & Morning Musume
bryan
46
6.2k
YesSQL, Process and Tooling at Scale
rocio
169
14k
Optimising Largest Contentful Paint
csswizardry
33
3k
It's Worth the Effort
3n
183
28k
Typedesign – Prime Four
hannesfritz
40
2.4k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
229
52k
Fontdeck: Realign not Redesign
paulrobertlloyd
82
5.3k
Making the Leap to Tech Lead
cromwellryan
133
9k
Six Lessons from altMBA
skipperchong
27
3.5k
Side Projects
sachag
452
42k
Agile that works and the tools we love
rasmusluckow
328
21k
Transcript
EtherCalc 多人即時 協作試算表
EtherCalc 多人即時 協作試算表 for Drupal
僅代表個人立場
只講故事 不講程式
只講故事 不講程式 概念
SheetNode.org
SheetNode.org
SheetNode.org ‣ npm install -g ethercalc ‣ ethercalc Please connect
to: http://0:8000/
aosabook.org ⟪開源應用架構⟫ EtherCalc.tw
緣起
VisiCalc, 1979 Dan Bricklin
哈佛商學院, 1977
哈佛商學院, 1977
哈佛商學院, 1977
哈佛商學院, 1977
哈佛商學院, 1977
最初的願景
最初的願景 Alto 工作站
最初的願景 滑鼠計算機 Alto 工作站
最初的願景 頭戴顯示器 滑鼠計算機 Alto 工作站
最初的願景 頭戴顯示器 滑鼠計算機 Alto 工作站
None
None
=SUM( ) 0
10 20 30 =SUM( ) 0 10 30 60
10 20 30 =SUM( ) 0 10 30 60
1977 → 1978
1977 → 1978
1977 → 1978 Integer BASIC +
1978 → 1979
10 20 30 =SUM( ) 60 1978 → 1979
10 20 30 =SUM( ) 60 A B C D
1 2 1978 → 1979
10 20 30 =SUM( ) 60 A B C D
1 2 A1,B1,C1 1978 → 1979
Bob & Dan 10 20 30 =SUM( ) 60 A
B C D 1 2 A1,B1,C1 1978 → 1979
Bob & Dan ‣6 年售出 700,000 套 10 20 30
=SUM( ) 60 A B C D 1 2 A1,B1,C1 1978 → 1979
Bob & Dan ‣6 年售出 700,000 套 ‣「殺手級應用」的始祖 10 20
30 =SUM( ) 60 A B C D 1 2 A1,B1,C1 1978 → 1979
1981
None
None
None
None
None
二十年來
二十年來
二十年來
二十年來
二十年來 始終如一
None
“打不開”
“打不開” “變亂碼”
“有病毒!” “打不開” “變亂碼”
None
維基百科, 2001
維基百科, 2001
維基百科, 2001
wikiCalc, 2005
✓ 跨伺服器引用數值。 wikiCalc, 2005
✓ 跨伺服器引用數值。 ✓ 保留每個版本,可隨時回復 。 wikiCalc, 2005
✓ 跨伺服器引用數值。 ✓ 保留每個版本,可隨時回復 。 ✓ 支援純文字、HTML、Wiki 語法。 wikiCalc, 2005
✓ 跨伺服器引用數值。 ✓ 保留每個版本,可隨時回復 。 ✓ 支援純文字、HTML、Wiki 語法。 ✓ 開放源碼!
wikiCalc, 2005
wikiCalc.pl
網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl
網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ
網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格
網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格
A1: 100
網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格
A1: 100 A2: =A1*2
網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格
A1: 100 A2: =A1*2 B1: =XXX!C1
網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格
A1: 100 A2: =A1*2 B1: =XXX!C1
網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格
A1: 100 A2: =A1*2 B1: =XXX!C1 B2: =YYY!D2
網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格
A1: 100 A2: =A1*2 B1: =XXX!C1 B2: =YYY!D2
網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格
A1: 100 A2: =A1*2 B1: =XXX!C1 B2: =YYY!D2 跨頁引用
wikiCalc 編輯流程
wikiCalc 編輯流程 A1: 100 A2: =A1*2
wikiCalc 編輯流程 A1: 100 A2: =A1*2
wikicalc.pl wikiCalc 編輯流程 A1: 100 A2: =A1*2 POST / ajaxsetcell=host:page:A1:300
wikicalc.pl wikiCalc 編輯流程 A1: 100 A2: =A1*2 POST / ajaxsetcell=host:page:A1:300
200 OK <?xml version="1.0"?> <root><![CDATA[ A1:v:300:300:right:1:1:: A2:f:600:A1*2:right:1:1:: ]]></root>
“載入中…”
“載入中…”
“載入中…” “C100k” 問題
“載入中…” “C100k” 問題
None
打掉重練
打掉重練 ڱࡣ
SocialCalc, 2006 Dan Bricklin Ross Mayfield
設計目標
設計目標 ‣引擎用 JavaScript 重寫。
設計目標 ‣引擎用 JavaScript 重寫。 ‣即時編輯及還原/重作。
設計目標 ‣引擎用 JavaScript 重寫。 ‣即時編輯及還原/重作。 ‣能處理十萬個儲存格。
系統架構
系統架構 SocialCalc.js HTTP Server
系統架構 SocialCalc.js HTTP Server GET
系統架構 SocialCalc.js HTTP Server GET
系統架構 SocialCalc.js HTTP Server GET GET
系統架構 SocialCalc.js HTTP Server GET GET ($)
系統架構 SocialCalc.js HTTP Server GET PUT GET ($)
指令設計模式
指令設計模式 set A1 value n 42
指令設計模式 set A1 value n 42 set A2 formula A1*2
指令設計模式 set A1 value n 42 set A2 formula A1*2
merge A1:B2 cut A3 paste A4 sort A1:B9 A up B down set sheet defaultcolor blue ...
指令設計模式 ‣ 背景處理計算。 set A1 value n 42 set A2
formula A1*2
指令設計模式 ‣ 背景處理計算。 ‣ 無限次還原重做。 set A1 value n 42
set A2 formula A1*2
指令設計模式 ‣ 背景處理計算。 ‣ 無限次還原重做。 ‣ 鍵盤滑鼠隨時可用! set A1 value
n 42 set A2 formula A1*2
“社會化” 試算表
“社會化” 試算表
“社會化” 試算表 評論、按讚、推薦、 標記、分享、嵌入...
社會物件 㱻 人際連結
社會物件 㱻 人際連結
社會物件 㱻 人際連結
None
工ӉϺЛх ࢰЗ્ЛЦ
CPAL 通用公共授權
CPAL 通用公共授權 BSD, MIT
CPAL 通用公共授權 BSD, MIT © LGPL, MPL
CPAL 通用公共授權 BSD, MIT © LGPL, MPL ++© GPL
CPAL 通用公共授權 BSD, MIT © LGPL, MPL ++© GPL
“ASP
CPAL 通用公共授權 BSD, MIT © LGPL, MPL ++© GPL
Affero GPL “ASP
CPAL 通用公共授權 BSD, MIT © LGPL, MPL ++© GPL
Affero GPL CPAL “ASP
CPAL 通用公共授權 BSD, MIT © LGPL, MPL ++© GPL
Affero GPL CPAL “ASP
CPAL 通用公共授權 BSD, MIT © LGPL, MPL ++© GPL
Affero GPL CPAL “ASP
CPAL 通用公共授權 BSD, MIT © LGPL, MPL ++© GPL
Affero GPL CPAL “ASP
Sheetnode, 2008 Karim Ratib
Sheetnode, 2008 Karim Ratib Views + Fields + CCK
Sheetnode, 2008 Karim Ratib Views + Fields + CCK SocialCalc.js
Sheetnode, 2008 Karim Ratib Views + Fields + CCK SocialCalc.js
Sheetnode, 2008 Karim Ratib Views + Fields + CCK SocialCalc.js
Sheetnode, 2008 Karim Ratib Views + Fields + CCK SocialCalc.js
Sheetnode, 2008
Sheetnode, 2008 I was looking for an open source equivalent
to Google Docs that would allow tighter integration with a company's data:
Sheetnode, 2008 “Real-time reports, created out of Drupal data.” I
was looking for an open source equivalent to Google Docs that would allow tighter integration with a company's data:
SheetNode.org
SheetNode.org
SheetNode.org Views
OLPC, 2008
Luke Closs & Dan OLPC, 2008
None
None
Mesh 網絡
None
Manusheel Gupta Vijit Singh
Manusheel Gupta Vijit Singh SocialCalcActivity.py XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js
Manusheel Gupta Vijit Singh SocialCalcActivity.py XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js set
A1 value n 42
Manusheel Gupta Vijit Singh SocialCalcActivity.py XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js D-Bus
+ Telepathy set A1 value n 42
Manusheel Gupta Vijit Singh SocialCalcActivity.py XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js D-Bus
+ Telepathy set A1 value n 42 OLPC Mesh 網絡廣播
Manusheel Gupta Vijit Singh SocialCalcActivity.py XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js SocialCalcActivity.py
XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js D-Bus + Telepathy D-Bus + Telepathy set A1 value n 42 OLPC Mesh 網絡廣播
Manusheel Gupta Vijit Singh SocialCalcActivity.py XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js SocialCalcActivity.py
XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js D-Bus + Telepathy D-Bus + Telepathy set A1 value n 42 set A1 value n 42 OLPC Mesh 網絡廣播
很讚,但是...
‣漏接訊息無法復原。 很讚,但是...
‣漏接訊息無法復原。 ‣編輯同一格時會衝突。 很讚,但是...
‣漏接訊息無法復原。 ‣編輯同一格時會衝突。 ‣只能在 OLPC 上使用! 很讚,但是...
YAPC::Tiny, 2009
EV: 事件驅動
Tatsumaki EV: 事件驅動 @miyagawa
Tatsumaki Web::Hippie @clkao EV: 事件驅動 @miyagawa
Tatsumaki Web::Hippie @clkao Feersum @stash EV: 事件驅動 @miyagawa
WebSocket 同步編輯 multiserver.pl Web::Hippie Plack Feersum EV/libev
WebSocket 同步編輯 multiserver.pl Web::Hippie Plack Feersum EV/libev ScheduleScheetCommand set A1
value n 2046 SpreadsheetControl RenderSheet
WebSocket 同步編輯 multiserver.pl Web::Hippie Plack Feersum EV/libev ScheduleScheetCommand set A1
value n 2046 SpreadsheetControl RenderSheet 傳送
WebSocket 同步編輯 multiserver.pl Web::Hippie Plack Feersum EV/libev ScheduleScheetCommand set A1
value n 2046 SpreadsheetControl RenderSheet 傳送 群播
WebSocket 同步編輯 multiserver.pl Web::Hippie Plack Feersum EV/libev ScheduleScheetCommand set A1
value n 2046 SpreadsheetControl RenderSheet 傳送 RenderSheet ScheduleScheetCommand (isRemote = true) set A1 value n 2046 群播
新增功能
✓斷線重連可以復原。 新增功能
✓斷線重連可以復原。 ✓顯示別人的游標位置。 新增功能
✓斷線重連可以復原。 ✓顯示別人的游標位置。 ✓可以在各平台上運行! 新增功能
✓斷線重連可以復原。 ✓顯示別人的游標位置。 ✓可以在各平台上運行! 新增功能
更讚了,但是...
‣要相信誰的目前狀態? 更讚了,但是...
‣要相信誰的目前狀態? ‣所有人離線:資料消失? 更讚了,但是...
‣要相信誰的目前狀態? ‣所有人離線:資料消失? ‣重新連接:回播所有指令? 更讚了,但是...
‣要相信誰的目前狀態? ‣所有人離線:資料消失? ‣重新連接:回播所有指令? 更讚了,但是...
None
打掉重練
打掉重練 ڱࡣ
YAPC::NA, 2006
“I think, but I cannot prove, that by the next
year JavaScript 2.0 will bootstrap itself, complete self hosting, compile back to JavaScript, and replace Ruby as the Next Big Thing in all environments. ” YAPC::NA, 2006
YAPC::NA, 2006
YAPC::NA, 2006 “JavaScript will become the common backend for all
dynamic languages, and so you can write Perl to run in the browser, on the server, and inside databases, all with the same set of development tools. ”
YAPC::NA, 2006
YAPC::NA, 2006 “Because, as we all know, worse is better,
so the worst scripting language is doomed to become the best.”
YAPC::NA, 2006 “Because, as we all know, worse is better,
so the worst scripting language is doomed to become the best.” 劣即是夯
None
None
None
JavaScript: 缺點減少
JavaScript: 缺點減少 CoffeeScript: 標點減半 Jeremy Ashkenas cs = (js) ->
js/2
JavaScript: 缺點減少 CoffeeScript: 標點減半 Jeremy Ashkenas cs = (js) ->
js/2
JavaScript: 缺點減少 CoffeeScript: 標點減半 Jeremy Ashkenas cs = (js) ->
js/2 “原 JavaScript 行數: 22k。 重寫過的 CoffeeScript 行數: 5k。 {async, jsdom, zappa, optimist etc}++”
None
{x,y} = @offset
{x,y} = @offset var offset = this.offset;
{x,y} = @offset var offset = this.offset; var x =
offset.x;
{x,y} = @offset var offset = this.offset; var x =
offset.x; var y = offset.y;
{x,y} = @offset js2coffee.org var offset = this.offset; var x
= offset.x; var y = offset.y;
COSCUP, 2011
COSCUP, 2011
COSCUP, 2011 hack
COSCUP, 2011 hack
EtherCalc 系統架構
EtherCalc 系統架構 main.coffee Zappa Socket.io Express Node.js EV/libuv sc.coffee SocialCalc.js
db.coffee redis.js SocialCalc.js
EtherCalc 系統架構 main.coffee Zappa Socket.io Express Node.js EV/libuv sc.coffee SocialCalc.js
db.coffee redis.js Redis (optional) SocialCalc.js
EtherCalc 系統架構 player.coffee SocialCalc.js main.coffee Zappa Socket.io Express Node.js EV/libuv
sc.coffee SocialCalc.js db.coffee redis.js Redis (optional) SocialCalc.js SocialCalc.js
EtherCalc 系統架構 player.coffee SocialCalc.js main.coffee Zappa Socket.io Express Node.js EV/libuv
sc.coffee SocialCalc.js db.coffee redis.js Redis (optional) GET snapshot LRANGE log SocialCalc.js SocialCalc.js
EtherCalc 系統架構 player.coffee SocialCalc.js main.coffee Zappa Socket.io Express Node.js EV/libuv
sc.coffee SocialCalc.js db.coffee redis.js Redis (optional) RPUSH log cmd GET snapshot LRANGE log SocialCalc.js SocialCalc.js
EtherCalc 系統架構 player.coffee SocialCalc.js main.coffee Zappa Socket.io Express Node.js EV/libuv
sc.coffee SocialCalc.js db.coffee redis.js Redis (optional) RPUSH log cmd GET snapshot LRANGE log SocialCalc.js SocialCalc.js
EtherCalc 系統架構 player.coffee SocialCalc.js main.coffee Zappa Socket.io Express Node.js EV/libuv
sc.coffee SocialCalc.js db.coffee redis.js Redis (optional) RPUSH log cmd GET snapshot LRANGE log SocialCalc.js SocialCalc.js
EtherCalc 系統架構 player.coffee SocialCalc.js main.coffee Zappa Socket.io Express Node.js EV/libuv
sc.coffee SocialCalc.js db.coffee redis.js Redis (optional) RPUSH log cmd GET snapshot LRANGE log DEL log SET snapshot snapshot SocialCalc.js SocialCalc.js
跨頁即時更新
跨頁即時更新 伺 服 端
跨頁即時更新 伺 服 端 客 戶 端
跨頁即時更新 ask.log: XXX 伺 服 端 客 戶 端
跨頁即時更新 ask.log: XXX log: XXX,snapshot,log 伺 服 端 客 戶
端
跨頁即時更新 ask.log: XXX log: XXX,snapshot,log execute: set A1 formula YYY!B2
伺 服 端 客 戶 端
跨頁即時更新 ask.log: XXX log: XXX,snapshot,log execute: set A1 formula YYY!B2
recalc: YYY,snapshot 伺 服 端 客 戶 端
跨頁即時更新 ask.log: XXX log: XXX,snapshot,log execute: set A1 formula YYY!B2
recalc: YYY,snapshot 伺 服 端 客 戶 端 recalc: YYY,snapshot
跨頁即時更新 ask.log: XXX log: XXX,snapshot,log execute: set A1 formula YYY!B2
recalc: YYY,snapshot 伺 服 端 客 戶 端 recalc: YYY,snapshot recalc: YYY,snapshot
REST 資源界面
REST 資源界面 GET /_/page PUT /_/page
REST 資源界面 GET /_/page PUT /_/page POST /_/page {commands:[…]}
REST 資源界面 GET /_/page PUT /_/page POST /_/page {commands:[…]} GET
/_/page/cells/A1 PUT /_/page/cells/B2
None
+ =
+ = Coco + =
+ = Coco + = + = Coco
None
stove.on("heat", function() {
stove.on("heat", function() { pot.on("boil", function() {
stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) {
stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) { setTimeout(function()
{
stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) { setTimeout(function()
{ dish.serve(); }, 60000);
stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) { setTimeout(function()
{ dish.serve(); }, 60000); }); }); });
stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) { setTimeout(function()
{ dish.serve(); }, 60000); }); }); });
stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) { setTimeout(function()
{ dish.serve(); }, 60000); }); }); });
None
stove.on "heat", ->
stove.on "heat", -> pot.on "boil", ->
stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->
stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->
setTimeout(
stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->
setTimeout( -> dish.serve()
stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->
setTimeout( -> dish.serve() 60000
stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->
setTimeout( -> dish.serve() 60000 )
stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->
setTimeout( -> dish.serve() 60000 )
None
<- stove.on \heat
<- stove.on \heat <- pot.on \boil
<- stove.on \heat <- pot.on \boil dish <- rice.on \ready
<- stove.on \heat <- pot.on \boil dish <- rice.on \ready
<- (`setTimeout` 60000)
<- stove.on \heat <- pot.on \boil dish <- rice.on \ready
<- (`setTimeout` 60000) dish.serve!
<- stove.on \heat <- pot.on \boil dish <- rice.on \ready
<- (`setTimeout` 60000) dish.serve!
OSDC.tw, 2012
OSDC.tw, 2012
OSDC.tw, 2012
OSDC.tw, 2012
哪來的「高風亮節」…
只是沒寫過 Drupal 模組。 哪來的「高風亮節」…
None
雖然 Isis 架過 許多 Drupal 網站 我也幫忙改了一些…
雖然 Isis 架過 許多 Drupal 網站 我也幫忙改了一些…
雖然 Isis 架過 許多 Drupal 網站 我也幫忙改了一些…
雖然 Isis 架過 許多 Drupal 網站 我也幫忙改了一些…
可是我對架構 完全沒有概念。
可是我對架構 完全沒有概念。
None
⟪開源之樂⟫, 2012. 7. 1.
⟪開源之樂⟫, 2012. 7. 1.
⟪開源之樂⟫, 2012. 7. 1.
⟪開源之樂⟫, 2012. 7. 1. “內容過於抽象。”
⟪開源之樂⟫, 2012. 7. 1. “內容過於抽象。” “這跟 Drupal 到底有何關係?”
None
2012. 7. 2.
“你還是把 EtherCalc for Drupal 寫出來, 比較有意義。” 2012. 7. 2.
“你還是把 EtherCalc for Drupal 寫出來, 比較有意義。” 2012. 7. 2.
“你還是把 EtherCalc for Drupal 寫出來, 比較有意義。” 2012. 7. 2.
None
2012. 7. 3.
2012. 7. 3. 感謝 Karim 幫忙
2012. 7. 3. 一個早上 就寫完了。 感謝 Karim 幫忙
/** * * Implements hook_menu(). * * In sheetnode_ethercalc_menu.info: *
configure = admin/config/content/sheetnode/ethercalc * */ function sheetnode_ethercalc_menu() { array('admin/config/content/sheetnode/ethercalc' => array( 'title' => 'EtherCalc', 'access arguments' => array('administer site configuration'), 'page callback' => 'drupal_get_form', 'page arguments' => array('_sheetnode_ethercalc_settings'), 'description' => 'Administer settings for EtherCalc.', 'type' => MENU_LOCAL_TASK, )); }
/** * * Implements hook_menu(). * * In sheetnode_ethercalc_menu.info: *
configure = admin/config/content/sheetnode/ethercalc * */ function sheetnode_ethercalc_menu() { array('admin/config/content/sheetnode/ethercalc' => array( 'title' => 'EtherCalc', 'access arguments' => array('administer site configuration'), 'page callback' => 'drupal_get_form', 'page arguments' => array('_sheetnode_ethercalc_settings'), 'description' => 'Administer settings for EtherCalc.', 'type' => MENU_LOCAL_TASK, )); }
/** * Implements hook_sheetnode_plugins(). */ function sheetnode_ethercalc_sheetnode_plugins( $value, $save_element, $context
) { // Only turn on Ethercalc if we're editing the node. if (!empty($save_element)) { $ethercalc_host = variable_get('sheetnode_ethercalc_host', ''); $ethercalc_port = variable_get('sheetnode_ethercalc_port', '8000'); $ethercalc_path = …; drupal_add_js($ethercalc_path . '/socket.io/socket.io.js#'); drupal_add_js($ethercalc_path . '/zappa/zappa.js#'); drupal_add_js($ethercalc_path . '/static/md5.js#'); drupal_add_js($ethercalc_path . '/player/broadcast.js#'); drupal_add_js($ethercalc_path . '/player/main.js#'); } }
/** * Implements hook_sheetnode_plugins(). */ function sheetnode_ethercalc_sheetnode_plugins( $value, $save_element, $context
) { // Only turn on Ethercalc if we're editing the node. if (!empty($save_element)) { $ethercalc_host = variable_get('sheetnode_ethercalc_host', ''); $ethercalc_port = variable_get('sheetnode_ethercalc_port', '8000'); $ethercalc_path = …; drupal_add_js($ethercalc_path . '/socket.io/socket.io.js#'); drupal_add_js($ethercalc_path . '/zappa/zappa.js#'); drupal_add_js($ethercalc_path . '/static/md5.js#'); drupal_add_js($ethercalc_path . '/player/broadcast.js#'); drupal_add_js($ethercalc_path . '/player/main.js#'); } }
None
2012. 7. 4.
2012. 7. 4.
2012. 7. 4. Lith.tw
2012. 7. 4. Lith.tw
2012. 7. 4. Lith.tw
2012. 7. 4. Lith.tw
2012. 7. 4. Lith.tw
2012. 7. 4. Lith.tw
None
結論是:
結論是:
寫 Drupal 模組 真的很簡單! 結論是:
感謝收看! EtherCalc for Drupal
以著作結合本文件之人,在法律許可 之範圍內,拋棄該著作依著作權法所 享有之權利,及其相關或鄰接的法律 權利,宣告該著作貢獻至公共領域。 採用 CC0 之著作,不要求姓名表彰。 EtherCalc SheetNode.org