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

EtherCalc for Drupal

F63068d724b6084f4807a86426b3b9eb?s=47 唐鳳
July 07, 2012

EtherCalc for Drupal

First presented at DrupalCamp Taipei as a keynote talk.

F63068d724b6084f4807a86426b3b9eb?s=128

唐鳳

July 07, 2012
Tweet

More Decks by 唐鳳

Other Decks in Programming

Transcript

  1. EtherCalc 多人即時 協作試算表

  2. EtherCalc 多人即時 協作試算表 for Drupal

  3. 僅代表個人立場

  4. 只講故事 不講程式

  5. 只講故事 不講程式 概念

  6. SheetNode.org

  7. SheetNode.org

  8. SheetNode.org ‣ npm install -g ethercalc ‣ ethercalc Please connect

    to: http://0:8000/
  9. aosabook.org ⟪開源應用架構⟫ EtherCalc.tw

  10. 緣起

  11. VisiCalc, 1979 Dan Bricklin

  12. 哈佛商學院, 1977

  13. 哈佛商學院, 1977

  14. 哈佛商學院, 1977

  15. 哈佛商學院, 1977

  16. 哈佛商學院, 1977

  17. 最初的願景

  18. 最初的願景 Alto 工作站

  19. 最初的願景 滑鼠計算機 Alto 工作站

  20. 最初的願景 頭戴顯示器 滑鼠計算機 Alto 工作站

  21. 最初的願景 頭戴顯示器 滑鼠計算機 Alto 工作站

  22. None
  23. None
  24. =SUM( ) 0

  25. 10 20 30 =SUM( ) 0 10 30 60

  26. 10 20 30 =SUM( ) 0 10 30 60

  27. 1977 → 1978

  28. 1977 → 1978

  29. 1977 → 1978 Integer BASIC +

  30. 1978 → 1979

  31. 10 20 30 =SUM( ) 60 1978 → 1979

  32. 10 20 30 =SUM( ) 60 A B C D

    1 2 1978 → 1979
  33. 10 20 30 =SUM( ) 60 A B C D

    1 2 A1,B1,C1 1978 → 1979
  34. Bob & Dan 10 20 30 =SUM( ) 60 A

    B C D 1 2 A1,B1,C1 1978 → 1979
  35. Bob & Dan ‣6 年售出 700,000 套 10 20 30

    =SUM( ) 60 A B C D 1 2 A1,B1,C1 1978 → 1979
  36. Bob & Dan ‣6 年售出 700,000 套 ‣「殺手級應用」的始祖 10 20

    30 =SUM( ) 60 A B C D 1 2 A1,B1,C1 1978 → 1979
  37. 1981

  38. None
  39. None
  40. None
  41. None
  42. None
  43. 二十年來

  44. 二十年來

  45. 二十年來

  46. 二十年來

  47. 二十年來 始終如一

  48. None
  49. “打不開”

  50. “打不開” “變亂碼”

  51. “有病毒!” “打不開” “變亂碼”

  52. None
  53. 維基百科, 2001

  54. 維基百科, 2001

  55. 維基百科, 2001

  56. wikiCalc, 2005

  57. ✓ 跨伺服器引用數值。 wikiCalc, 2005

  58. ✓ 跨伺服器引用數值。 ✓ 保留每個版本,可隨時回復 。 wikiCalc, 2005

  59. ✓ 跨伺服器引用數值。 ✓ 保留每個版本,可隨時回復 。 ✓ 支援純文字、HTML、Wiki 語法。 wikiCalc, 2005

  60. ✓ 跨伺服器引用數值。 ✓ 保留每個版本,可隨時回復 。 ✓ 支援純文字、HTML、Wiki 語法。 ✓ 開放源碼!

    wikiCalc, 2005
  61. wikiCalc.pl

  62. 網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl

  63. 網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ

  64. 網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格

  65. 網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格

    A1: 100
  66. 網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格

    A1: 100 A2: =A1*2
  67. 網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格

    A1: 100 A2: =A1*2 B1: =XXX!C1
  68. 網站 ./wkcdata/sites/Foo ./wkcdata/sites/Bar ./wkcdata/sites/Baz wikiCalc.pl 頁面 XXX YYY ZZZ 儲存格

    A1: 100 A2: =A1*2 B1: =XXX!C1
  69. 網站 ./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
  70. 網站 ./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
  71. 網站 ./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 跨頁引用
  72. wikiCalc 編輯流程

  73. wikiCalc 編輯流程 A1: 100 A2: =A1*2

  74. wikiCalc 編輯流程 A1: 100 A2: =A1*2

  75. wikicalc.pl wikiCalc 編輯流程 A1: 100 A2: =A1*2 POST / ajaxsetcell=host:page:A1:300

  76. 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>
  77. “載入中…”

  78. “載入中…”

  79. “載入中…” “C100k” 問題

  80. “載入中…” “C100k” 問題

  81. None
  82. 打掉重練

  83. 打掉重練 ؄ڱ቎ࡣ

  84. SocialCalc, 2006 Dan Bricklin Ross Mayfield

  85. 設計目標

  86. 設計目標 ‣引擎用 JavaScript 重寫。

  87. 設計目標 ‣引擎用 JavaScript 重寫。 ‣即時編輯及還原/重作。

  88. 設計目標 ‣引擎用 JavaScript 重寫。 ‣即時編輯及還原/重作。 ‣能處理十萬個儲存格。

  89. 系統架構

  90. 系統架構 SocialCalc.js HTTP Server

  91. 系統架構 SocialCalc.js HTTP Server GET

  92. 系統架構 SocialCalc.js HTTP Server GET

  93. 系統架構 SocialCalc.js HTTP Server GET GET

  94. 系統架構 SocialCalc.js HTTP Server GET GET ($)

  95. 系統架構 SocialCalc.js HTTP Server GET PUT GET ($)

  96. 指令設計模式

  97. 指令設計模式 set A1 value n 42

  98. 指令設計模式 set A1 value n 42 set A2 formula A1*2

  99. 指令設計模式 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 ...
  100. 指令設計模式 ‣ 背景處理計算。 set A1 value n 42 set A2

    formula A1*2
  101. 指令設計模式 ‣ 背景處理計算。 ‣ 無限次還原重做。 set A1 value n 42

    set A2 formula A1*2
  102. 指令設計模式 ‣ 背景處理計算。 ‣ 無限次還原重做。 ‣ 鍵盤滑鼠隨時可用! set A1 value

    n 42 set A2 formula A1*2
  103. “社會化” 試算表

  104. “社會化” 試算表

  105. “社會化” 試算表 評論、按讚、推薦、 標記、分享、嵌入...

  106. 社會物件 㱻 人際連結

  107. 社會物件 㱻 人際連結

  108. 社會物件 㱻 人際連結

  109. None
  110. 工ӉϺЛх ࢰЗ્ЛЦ

  111. CPAL 通用公共授權

  112. CPAL 通用公共授權 ῜ BSD, MIT

  113. CPAL 通用公共授權 ῜ BSD, MIT © LGPL, MPL

  114. CPAL 通用公共授權 ῜ BSD, MIT © LGPL, MPL ++© GPL

  115. CPAL 通用公共授權 ῜ BSD, MIT © LGPL, MPL ++© GPL

    “ASP
  116. CPAL 通用公共授權 ῜ BSD, MIT © LGPL, MPL ++© GPL

    Affero GPL “ASP
  117. CPAL 通用公共授權 ῜ BSD, MIT © LGPL, MPL ++© GPL

    Affero GPL CPAL “ASP
  118. CPAL 通用公共授權 ῜ BSD, MIT © LGPL, MPL ++© GPL

    Affero GPL CPAL “ASP
  119. CPAL 通用公共授權 ῜ BSD, MIT © LGPL, MPL ++© GPL

    Affero GPL CPAL “ASP
  120. CPAL 通用公共授權 ῜ BSD, MIT © LGPL, MPL ++© GPL

    Affero GPL CPAL “ASP
  121. Sheetnode, 2008 Karim Ratib

  122. Sheetnode, 2008 Karim Ratib Views + Fields + CCK

  123. Sheetnode, 2008 Karim Ratib Views + Fields + CCK SocialCalc.js

  124. Sheetnode, 2008 Karim Ratib Views + Fields + CCK SocialCalc.js

  125. Sheetnode, 2008 Karim Ratib Views + Fields + CCK SocialCalc.js

  126. Sheetnode, 2008 Karim Ratib Views + Fields + CCK SocialCalc.js

  127. Sheetnode, 2008

  128. Sheetnode, 2008 I was looking for an open source equivalent

    to Google Docs that would allow tighter integration with a company's data:
  129. 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:
  130. SheetNode.org

  131. SheetNode.org

  132. SheetNode.org Views

  133. OLPC, 2008

  134. Luke Closs & Dan OLPC, 2008

  135. None
  136. None
  137. Mesh 網絡

  138. None
  139. Manusheel Gupta Vijit Singh

  140. Manusheel Gupta Vijit Singh SocialCalcActivity.py XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js

  141. Manusheel Gupta Vijit Singh SocialCalcActivity.py XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js set

    A1 value n 42
  142. Manusheel Gupta Vijit Singh SocialCalcActivity.py XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js D-Bus

    + Telepathy set A1 value n 42
  143. Manusheel Gupta Vijit Singh SocialCalcActivity.py XoCom.py Gecko/XPCOM SocialCalc.js XoCom.js D-Bus

    + Telepathy set A1 value n 42 OLPC Mesh 網絡廣播
  144. 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 網絡廣播
  145. 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 網絡廣播
  146. 很讚,但是...

  147. ‣漏接訊息無法復原。 很讚,但是...

  148. ‣漏接訊息無法復原。 ‣編輯同一格時會衝突。 很讚,但是...

  149. ‣漏接訊息無法復原。 ‣編輯同一格時會衝突。 ‣只能在 OLPC 上使用! 很讚,但是...

  150. YAPC::Tiny, 2009

  151. EV: 事件驅動

  152. Tatsumaki EV: 事件驅動 @miyagawa

  153. Tatsumaki Web::Hippie @clkao EV: 事件驅動 @miyagawa

  154. Tatsumaki Web::Hippie @clkao Feersum @stash EV: 事件驅動 @miyagawa

  155. WebSocket 同步編輯 multiserver.pl Web::Hippie Plack Feersum EV/libev

  156. WebSocket 同步編輯 multiserver.pl Web::Hippie Plack Feersum EV/libev ScheduleScheetCommand set A1

    value n 2046 SpreadsheetControl RenderSheet
  157. WebSocket 同步編輯 multiserver.pl Web::Hippie Plack Feersum EV/libev ScheduleScheetCommand set A1

    value n 2046 SpreadsheetControl RenderSheet 傳送
  158. WebSocket 同步編輯 multiserver.pl Web::Hippie Plack Feersum EV/libev ScheduleScheetCommand set A1

    value n 2046 SpreadsheetControl RenderSheet 傳送 群播
  159. 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 群播
  160. 新增功能

  161. ✓斷線重連可以復原。 新增功能

  162. ✓斷線重連可以復原。 ✓顯示別人的游標位置。 新增功能

  163. ✓斷線重連可以復原。 ✓顯示別人的游標位置。 ✓可以在各平台上運行! 新增功能

  164. ✓斷線重連可以復原。 ✓顯示別人的游標位置。 ✓可以在各平台上運行! 新增功能

  165. 更讚了,但是...

  166. ‣要相信誰的目前狀態? 更讚了,但是...

  167. ‣要相信誰的目前狀態? ‣所有人離線:資料消失? 更讚了,但是...

  168. ‣要相信誰的目前狀態? ‣所有人離線:資料消失? ‣重新連接:回播所有指令? 更讚了,但是...

  169. ‣要相信誰的目前狀態? ‣所有人離線:資料消失? ‣重新連接:回播所有指令? 更讚了,但是...

  170. None
  171. 打掉重練

  172. 打掉重練 ؄ڱ቎ࡣ

  173. YAPC::NA, 2006

  174. “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
  175. YAPC::NA, 2006

  176. 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. ”
  177. YAPC::NA, 2006

  178. YAPC::NA, 2006 “Because, as we all know, worse is better,

    so the worst scripting language is doomed to become the best.”
  179. YAPC::NA, 2006 “Because, as we all know, worse is better,

    so the worst scripting language is doomed to become the best.” 劣即是夯
  180. None
  181. None
  182. None
  183. JavaScript: 缺點減少

  184. JavaScript: 缺點減少 CoffeeScript: 標點減半 Jeremy Ashkenas cs = (js) ->

    js/2
  185. JavaScript: 缺點減少 CoffeeScript: 標點減半 Jeremy Ashkenas cs = (js) ->

    js/2
  186. JavaScript: 缺點減少 CoffeeScript: 標點減半 Jeremy Ashkenas cs = (js) ->

    js/2 “原 JavaScript 行數: 22k。  重寫過的 CoffeeScript 行數: 5k。  {async, jsdom, zappa, optimist etc}++”
  187. None
  188. {x,y} = @offset

  189. {x,y} = @offset var offset = this.offset;

  190. {x,y} = @offset var offset = this.offset; var x =

    offset.x;
  191. {x,y} = @offset var offset = this.offset; var x =

    offset.x; var y = offset.y;
  192. {x,y} = @offset js2coffee.org var offset = this.offset; var x

    = offset.x; var y = offset.y;
  193. COSCUP, 2011

  194. COSCUP, 2011

  195. COSCUP, 2011 hack

  196. COSCUP, 2011 hack

  197. EtherCalc 系統架構

  198. EtherCalc 系統架構 main.coffee Zappa Socket.io Express Node.js EV/libuv sc.coffee SocialCalc.js

    db.coffee redis.js SocialCalc.js
  199. EtherCalc 系統架構 main.coffee Zappa Socket.io Express Node.js EV/libuv sc.coffee SocialCalc.js

    db.coffee redis.js Redis (optional) SocialCalc.js
  200. 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
  201. 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
  202. 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
  203. 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
  204. 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
  205. 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
  206. 跨頁即時更新

  207. 跨頁即時更新 伺 服 端

  208. 跨頁即時更新 伺 服 端 客 戶 端

  209. 跨頁即時更新 ask.log: XXX 伺 服 端 客 戶 端

  210. 跨頁即時更新 ask.log: XXX log: XXX,snapshot,log 伺 服 端 客 戶

  211. 跨頁即時更新 ask.log: XXX log: XXX,snapshot,log execute: set A1 formula YYY!B2

    伺 服 端 客 戶 端
  212. 跨頁即時更新 ask.log: XXX log: XXX,snapshot,log execute: set A1 formula YYY!B2

    recalc: YYY,snapshot 伺 服 端 客 戶 端
  213. 跨頁即時更新 ask.log: XXX log: XXX,snapshot,log execute: set A1 formula YYY!B2

    recalc: YYY,snapshot 伺 服 端 客 戶 端 recalc: YYY,snapshot
  214. 跨頁即時更新 ask.log: XXX log: XXX,snapshot,log execute: set A1 formula YYY!B2

    recalc: YYY,snapshot 伺 服 端 客 戶 端 recalc: YYY,snapshot recalc: YYY,snapshot
  215. REST 資源界面

  216. REST 資源界面 GET /_/page PUT /_/page

  217. REST 資源界面 GET /_/page PUT /_/page POST /_/page {commands:[…]}

  218. REST 資源界面 GET /_/page PUT /_/page POST /_/page {commands:[…]} GET

    /_/page/cells/A1 PUT /_/page/cells/B2
  219. None
  220. + =

  221. + = Coco + =

  222. + = Coco + = + = Coco

  223. None
  224. stove.on("heat", function() {

  225. stove.on("heat", function() { pot.on("boil", function() {

  226. stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) {

  227. stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) { setTimeout(function()

    {
  228. stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) { setTimeout(function()

    { dish.serve(); }, 60000);
  229. stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) { setTimeout(function()

    { dish.serve(); }, 60000); }); }); });
  230. stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) { setTimeout(function()

    { dish.serve(); }, 60000); }); }); });
  231. stove.on("heat", function() { pot.on("boil", function() { rice.on("ready", function(dish) { setTimeout(function()

    { dish.serve(); }, 60000); }); }); });
  232. None
  233. stove.on "heat", ->

  234. stove.on "heat", -> pot.on "boil", ->

  235. stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->

  236. stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->

    setTimeout(
  237. stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->

    setTimeout( -> dish.serve()
  238. stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->

    setTimeout( -> dish.serve() 60000
  239. stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->

    setTimeout( -> dish.serve() 60000 )
  240. stove.on "heat", -> pot.on "boil", -> rice.on "ready", (dish) ->

    setTimeout( -> dish.serve() 60000 )
  241. None
  242. <- stove.on \heat

  243. <- stove.on \heat <- pot.on \boil

  244. <- stove.on \heat <- pot.on \boil dish <- rice.on \ready

  245. <- stove.on \heat <- pot.on \boil dish <- rice.on \ready

    <- (`setTimeout` 60000)
  246. <- stove.on \heat <- pot.on \boil dish <- rice.on \ready

    <- (`setTimeout` 60000) dish.serve!
  247. <- stove.on \heat <- pot.on \boil dish <- rice.on \ready

    <- (`setTimeout` 60000) dish.serve!
  248. OSDC.tw, 2012

  249. OSDC.tw, 2012

  250. OSDC.tw, 2012

  251. OSDC.tw, 2012

  252. 哪來的「高風亮節」…

  253. 只是沒寫過 Drupal 模組。 哪來的「高風亮節」…

  254. None
  255. 雖然 Isis 架過 許多 Drupal 網站 我也幫忙改了一些…

  256. 雖然 Isis 架過 許多 Drupal 網站 我也幫忙改了一些…

  257. 雖然 Isis 架過 許多 Drupal 網站 我也幫忙改了一些…

  258. 雖然 Isis 架過 許多 Drupal 網站 我也幫忙改了一些…

  259. 可是我對架構 完全沒有概念。

  260. 可是我對架構 完全沒有概念。

  261. None
  262. ⟪開源之樂⟫, 2012. 7. 1.

  263. ⟪開源之樂⟫, 2012. 7. 1.

  264. ⟪開源之樂⟫, 2012. 7. 1.

  265. ⟪開源之樂⟫, 2012. 7. 1. “內容過於抽象。”

  266. ⟪開源之樂⟫, 2012. 7. 1. “內容過於抽象。” “這跟 Drupal 到底有何關係?”

  267. None
  268. 2012. 7. 2.

  269. “你還是把 EtherCalc for Drupal 寫出來, 比較有意義。” 2012. 7. 2.

  270. “你還是把 EtherCalc for Drupal 寫出來, 比較有意義。” 2012. 7. 2.

  271. “你還是把 EtherCalc for Drupal 寫出來, 比較有意義。” 2012. 7. 2.

  272. None
  273. 2012. 7. 3.

  274. 2012. 7. 3. 感謝 Karim 幫忙

  275. 2012. 7. 3. 一個早上  就寫完了。 感謝 Karim 幫忙

  276. /** * * 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, )); }
  277. /** * * 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, )); }
  278. /** * 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#'); } }
  279. /** * 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#'); } }
  280. None
  281. 2012. 7. 4.

  282. 2012. 7. 4.

  283. 2012. 7. 4. Lith.tw

  284. 2012. 7. 4. Lith.tw

  285. 2012. 7. 4. Lith.tw

  286. 2012. 7. 4. Lith.tw

  287. 2012. 7. 4. Lith.tw

  288. 2012. 7. 4. Lith.tw

  289. None
  290. 結論是:

  291. 結論是:

  292. 寫 Drupal 模組 真的很簡單! 結論是:

  293. 感謝收看! EtherCalc for Drupal

  294. 以著作結合本文件之人,在法律許可 之範圍內,拋棄該著作依著作權法所 享有之權利,及其相關或鄰接的法律 權利,宣告該著作貢獻至公共領域。 採用 CC0 之著作,不要求姓名表彰。 EtherCalc SheetNode.org