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
150
RadicalxChange @ TEDxWeekend Taipei
audreyt
0
87
RadicalxChange @ Devcon 5
audreyt
1
140
11/07 工研院 IEK 「眺望2017產業發展趨勢研討會」演講
audreyt
2
250
2016.10.18 社會企業行動方案報告
audreyt
0
1.3k
2016.10.18 推動社會企業執行報告
audreyt
0
320
vTaiwan meeting 20161008
audreyt
0
480
運用網路平台協助公民參與
audreyt
0
200
Other Decks in Programming
See All in Programming
飲食業界向けマルチプロダクトを実現させる開発体制とリアルな現状
hiroya0601
1
400
Universal Linksの実装方法と陥りがちな罠
kaitokudou
1
220
gopls を改造したら開発生産性が高まった
satorunooshie
8
240
Piniaの現状と今後
waka292
5
1.5k
LLM生成文章の精度評価自動化とプロンプトチューニングの効率化について
layerx
PRO
2
140
RailsのPull requestsのレビューの時に私が考えていること
yahonda
5
1.7k
Amazon Neptuneで始めてみるグラフDB-OpenSearchによるグラフの全文検索-
satoshi256kbyte
4
330
offers_20241022_imakiire.pdf
imakurusu
2
360
Realtime API 入門
riofujimon
0
110
VR HMDとしてのVision Pro+ゲーム開発について
yasei_no_otoko
0
110
Dev ContainersとGitHub Codespacesの素敵な関係
ymd65536
1
130
What’s New in Compose Multiplatform - A Live Tour (droidcon London 2024)
zsmb
1
350
Featured
See All Featured
Scaling GitHub
holman
458
140k
Embracing the Ebb and Flow
colly
84
4.4k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.2k
4 Signs Your Business is Dying
shpigford
180
21k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
26
2.1k
Imperfection Machines: The Place of Print at Facebook
scottboms
264
13k
Stop Working from a Prison Cell
hatefulcrawdad
267
20k
BBQ
matthewcrist
85
9.3k
GitHub's CSS Performance
jonrohan
1030
460k
The Language of Interfaces
destraynor
154
24k
Building Applications with DynamoDB
mza
90
6.1k
Art, The Web, and Tiny UX
lynnandtonic
296
20k
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