dashboard 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. #!/usr/bin/ruby
  2. #
  3. # This file is part of centurio.work/ing/commands.
  4. #
  5. # centurio.work/ing/commands is free software: you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or (at your
  8. # option) any later version.
  9. #
  10. # centurio.work/ing/commands is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
  13. # Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along with
  16. # centurio.work/ing/commands (file COPYING in the main directory). If not, see
  17. # <http://www.gnu.org/licenses/>.
  18. require 'rubygems'
  19. require 'json'
  20. require 'xml/smart'
  21. require 'riddl/server'
  22. require 'fileutils'
  23. require 'typhoeus'
  24. require 'sqlite3'
  25. class ConfigSites < Riddl::Implementation
  26. def response
  27. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','index.html')))
  28. end
  29. end
  30. class GetAllConfigs < Riddl::Implementation
  31. def response
  32. Dir.chdir( __dir__ + '/data')
  33. databack = JSON::pretty_generate(Dir.glob('*/').sort_by{|x| x.downcase});
  34. Dir.chdir( __dir__)
  35. Riddl::Parameter::Complex.new('list','application/json',databack)
  36. end
  37. end
  38. class GetAllVisus < Riddl::Implementation
  39. def response
  40. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'data', @r[-2],'visus.html')))
  41. end
  42. end
  43. class Get < Riddl::Implementation
  44. def response
  45. #Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','template.html')))
  46. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'data', @r.last,'index.html')))
  47. end
  48. end
  49. class GetAllData < Riddl::Implementation
  50. def response
  51. fname = File.join('data',@r[-2],'data.db')
  52. db = SQLite3::Database.open fname
  53. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  54. #db.execute "CREATE TABLE IF NOT EXISTS Entries(seriennummer TEXT PRIMARY KEY, produktcode TEXT, activity TEXT, cpeeInstance INTEGER)"
  55. db.results_as_hash = true
  56. alldata = {};
  57. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC")
  58. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  59. else
  60. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
  61. end
  62. end
  63. end
  64. class AddColumn < Riddl::Implementation
  65. def response
  66. colname = @p[0].value
  67. colvalue = @p[1].value
  68. fname = File.join('data',@r[-2],'data.db')
  69. db = SQLite3::Database.open fname
  70. searchstring = "SELECT DISTINCT __orderID__, alldata FROM Entries"
  71. result = db.execute searchstring;
  72. result.each do |item|
  73. key = item[0]
  74. newalldata = item[1]
  75. thaData = JSON.parse(newalldata)
  76. thaData[colname] = colvalue;
  77. db.execute("UPDATE Entries SET alldata = ? WHERE __orderID__ = ?", thaData.to_json, key )
  78. end
  79. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({status: "looks good?"}))
  80. end
  81. end
  82. class DeleteColumn < Riddl::Implementation
  83. def response
  84. colname = @p[0].value
  85. fname = File.join('data',@r[-2],'data.db')
  86. db = SQLite3::Database.open fname
  87. searchstring = "SELECT DISTINCT __orderID__, alldata FROM Entries"
  88. result = db.execute searchstring;
  89. result.each do |item|
  90. key = item[0]
  91. newalldata = item[1]
  92. thaData = JSON.parse(newalldata)
  93. thaData.delete(colname)
  94. db.execute("UPDATE Entries SET alldata = ? WHERE __orderID__ = ?", thaData.to_json, key )
  95. end
  96. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({status: "looks good?"}))
  97. end
  98. end
  99. class GetNData < Riddl::Implementation
  100. def response
  101. fname = File.join('data',@r[-2],'data.db')
  102. db = SQLite3::Database.open fname
  103. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  104. db.results_as_hash = true
  105. alldata = {};
  106. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1 OFFSET " + @r[-1])
  107. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  108. else
  109. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
  110. end
  111. end
  112. end
  113. class DeleteNData < Riddl::Implementation
  114. def response
  115. fname = File.join('data',@r[-2],'data.db')
  116. db = SQLite3::Database.open fname
  117. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  118. db.results_as_hash = true
  119. alldata = {};
  120. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1 OFFSET " + @r[-1])
  121. if alldata["data"] != []
  122. id = alldata["data"][0]["__orderID__"].to_s
  123. db.execute("DELETE FROM Entries WHERE __orderID__ = '" + id + "'")
  124. end
  125. else
  126. nil
  127. end
  128. @a[0].send("reset")
  129. end
  130. end
  131. # used when executed from e.g. website, in such a case no cpee instance is given
  132. class AddEntry < Riddl::Implementation
  133. def response
  134. newEntry = JSON.parse(@p[0].value.read)
  135. fname = File.join('data',@r.last,'data.db')
  136. db = SQLite3::Database.open fname
  137. # in case primary key is given
  138. # PRIMARY KEY Momentan noch nicht implementiert
  139. if newEntry.has_key?("pkvalue")
  140. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, pkvalue TEXT, cpeeInstance INTEGER)"
  141. db.execute("INSERT OR REPLACE INTO Entries (alldata, pkvalue, cpeeInstance) VALUES (?,?,?)",JSON.dump(newEntry["alldata"]), newEntry["pkvalue"], "Direkt")
  142. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: JSON.dump(newEntry["alldata"]), pkvalue: newEntry["pkvalue"], cpeeInstance: "Direkt"};
  143. else
  144. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, cpeeInstance INTEGER)"
  145. db.execute("INSERT OR REPLACE INTO Entries (alldata, cpeeInstance) VALUES (?,?)",JSON.dump(newEntry["alldata"]), "Direkt")
  146. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: JSON.dump(newEntry["alldata"]), cpeeInstance: "Direkt"};
  147. end
  148. @a[0].send("reset")
  149. nil
  150. end
  151. end
  152. class Put < Riddl::Implementation
  153. def self::putentry(r, p, h)
  154. Dir.mkdir(File.join('data',r.last)) rescue nil
  155. Dir.mkdir(File.join('data',r.last, "js")) rescue nil
  156. Dir.mkdir(File.join('data',r.last, "visus")) rescue nil
  157. #prevent overriding ui and index in case user changed something there
  158. if !File.file?(File.join('data',r.last,'js/ui.js'))
  159. File.write(File.join('data',r.last,'js/ui.js'),File.open("defaultContent/js/ui.js").read)
  160. end
  161. if !File.file?(File.join('data',r.last,'index.html'))
  162. file = File.open("defaultContent/index.html").read
  163. newfile = file.gsub("!replaceThisString!", r.last)
  164. File.write(File.join('data',r.last,'index.html'), newfile)
  165. end
  166. #prevent overriding visualization
  167. if !File.file?(File.join('data',r.last,'visus.html'))
  168. File.write(File.join('data',r.last,'visus.html'),File.open("defaultContent/visus.html").read)
  169. end
  170. fname = File.join('data',r.last,'data.db')
  171. db = SQLite3::Database.open fname
  172. #in case primary key is given
  173. if p[1] != nil
  174. keyfunction = ""
  175. if p[2] != nil
  176. keyfunction = p[2].value
  177. end
  178. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, pkvalue TEXT " + keyfunction + ", cpeeInstance INTEGER)"
  179. db.execute("INSERT OR REPLACE INTO Entries (alldata, pkvalue, cpeeInstance) VALUES (?,?,?)",p[0].value, p[1].value, h['CPEE_INSTANCE'])
  180. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: p[0].value, pkvalue: p[1].value, cpeeInstance: h['CPEE_INSTANCE']};
  181. else
  182. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, cpeeInstance INTEGER)"
  183. db.execute("INSERT OR REPLACE INTO Entries (alldata, cpeeInstance) VALUES (?,?)",p[0].value, h['CPEE_INSTANCE'])
  184. hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: p[0].value, cpeeInstance: h['CPEE_INSTANCE']};
  185. end
  186. hash;
  187. end
  188. def response
  189. hash = Put::putentry(@r, @p, @h)
  190. @a[0].send("reset")
  191. nil
  192. end
  193. end
  194. class PostHtmlVals < Riddl::Implementation
  195. def response
  196. Dir.mkdir(File.join('data',@r[-2])) rescue nil
  197. Dir.mkdir(File.join('data',@r[-2], "js")) rescue nil
  198. Dir.mkdir(File.join('data',@r[-2], "visus")) rescue nil
  199. #prevent overriding ui and index in case user changed something there
  200. if !File.file?(File.join('data',@r[-2],'js/ui.js'))
  201. File.write(File.join('data',@r[-2],'js/ui.js'),File.open("defaultContent/js/ui.js").read)
  202. end
  203. if !File.file?(File.join('data',@r[-2],'index.html'))
  204. file = File.open("defaultContent/index.html").read
  205. newfile = file.gsub("!replaceThisString!", @r[-2])
  206. File.write(File.join('data',@r[-2],'index.html'), newfile)
  207. end
  208. #prevent overriding visualization
  209. if !File.file?(File.join('data',@r[-2],'visus.html'))
  210. File.write(File.join('data',@r[-2],'visus.html'),File.open("defaultContent/visus.html").read)
  211. end
  212. file = JSON['{}']
  213. @p.each_with_index do |child, idx|
  214. file[child.name] = child.value
  215. end
  216. fname = File.join('data',@r[-2],'data.db')
  217. db = SQLite3::Database.open fname
  218. db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, cpeeInstance INTEGER)"
  219. db.execute("INSERT OR REPLACE INTO Entries (alldata, cpeeInstance) VALUES (?,?)", JSON[file], "none")
  220. @a[0].send("reset")
  221. Riddl::Parameter::Complex.new('ui','text/html', '<html><head><meta http-equiv="refresh" content="0; url =' +@h["REFERER"]+'"/></head>')
  222. end
  223. end
  224. class PutPrio < Riddl::Implementation
  225. def response
  226. hash = Put::putentry(@r, @p, @h) #add entry
  227. hash = Move::moveentry(hash[:__orderID__][0], 0,@r) #move to front
  228. @a[0].send("reset")
  229. nil
  230. end
  231. end
  232. class PutPrioN < Riddl::Implementation
  233. def response
  234. #move all back
  235. fname = File.join('data',@r.last,'data.db')
  236. db = SQLite3::Database.open fname
  237. #Updating entries directly + X would result in unique constraint failed
  238. #Therefore updates first to negative values then *-1
  239. db.execute "UPDATE Entries SET __orderID__= 0 - (__orderID__ + " + @p[1].value + ")"
  240. db.execute "UPDATE Entries SET __orderID__= __orderID__ *-1"
  241. #Add entries
  242. i = 0
  243. while i < @p[1].value.to_i do
  244. #db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, cpeeInstance) VALUES (?,?,?)",i, @p[0].value, @h['CPEE_INSTANCE'])
  245. #in case primary key is given
  246. if @p[2] != nil
  247. db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, pkvalue, cpeeInstance) VALUES (?,?,?,?)",i, @p[0].value, @p[2].value, @h['CPEE_INSTANCE'])
  248. else
  249. db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, cpeeInstance) VALUES (?,?,?)",i, @p[0].value, @h['CPEE_INSTANCE'])
  250. end
  251. i +=1
  252. end
  253. @a[0].send("reset")
  254. nil
  255. end
  256. end
  257. class Move < Riddl::Implementation
  258. def self::moveentry(from, to,r)
  259. fname = File.join('data',r.last,'data.db')
  260. db = SQLite3::Database.open fname
  261. #getMaxID
  262. result = db.execute "SELECT MAX(__orderID__) FROM Entries"
  263. if(result[0][0] == nil)
  264. maxImgId = 0
  265. else
  266. maxImgId = result[0][0] +1
  267. end
  268. i = 0
  269. if to < from
  270. while to < from do
  271. #nutze max ID zum tauschen
  272. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxImgId, from - 1])
  273. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from - 1, from])
  274. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from, maxImgId])
  275. from -=1
  276. end
  277. elsif from < to
  278. while from < to do
  279. #nutze max ID zum tauschen
  280. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxImgId, from + 1])
  281. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from + 1, from])
  282. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from, maxImgId])
  283. from +=1
  284. end
  285. end
  286. nil
  287. end
  288. def response
  289. movement = JSON.parse(@p[0].value.read)
  290. from = movement["From"]
  291. to = movement["To"]
  292. hash = Move::moveentry(from, to,@r)
  293. nil
  294. end
  295. end
  296. class Update < Riddl::Implementation
  297. def response
  298. fname = File.join('data',@r.last,'data.db')
  299. db = SQLite3::Database.open fname
  300. updateData = JSON.parse(@p[0].value.read)
  301. orderID = updateData["ID"]
  302. alldata = JSON.generate(updateData["alldata"])
  303. db.execute("UPDATE Entries SET alldata = ? WHERE __orderID__ = ?", [alldata, orderID])
  304. nil
  305. end
  306. end
  307. class Storesortedtable < Riddl::Implementation
  308. def response
  309. fname = File.join('data',@r.last,'data.db')
  310. db = SQLite3::Database.open fname
  311. #invert keys from + to - to prevent duplicate primary keys when reordering
  312. db.execute("UPDATE Entries SET __orderID__ = __orderID__ *-1 -1")
  313. updateData = JSON.parse(@p[0].value.read)
  314. updateData.each_with_index do |child, idx|
  315. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [idx, child.to_i*-1 -1])
  316. end
  317. #security
  318. #in case there are negative entries remaining add them with positive value to the end
  319. result = db.execute "SELECT MAX(__orderID__) FROM Entries"
  320. if(result[0][0] == nil)
  321. maxorderID = 0
  322. else
  323. maxorderID = result[0][0] +1
  324. end
  325. resultpattern = db.execute "SELECT * FROM Entries WHERE __orderID__ < 0"
  326. resultpattern.each do |row2|
  327. db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxorderID, row2[0]]);
  328. maxorderID = maxorderID +1;
  329. end
  330. @a[0].send("reset")
  331. nil
  332. end
  333. end
  334. class Delete < Riddl::Implementation
  335. def response
  336. fname = File.join('data',@r.last,'data.db')
  337. db = SQLite3::Database.open fname
  338. alldata = @p[0].value.read
  339. db.execute("DELETE FROM Entries WHERE alldata = '" + alldata + "'")
  340. @a[0].send("reset")
  341. nil
  342. end
  343. end
  344. class DeleteID < Riddl::Implementation
  345. def self::remove(dbfolder, id)
  346. fname = File.join('data',dbfolder,'data.db')
  347. db = SQLite3::Database.open fname
  348. db.execute("DELETE FROM Entries WHERE __orderID__ = '" + id.to_s + "'")
  349. end
  350. def response
  351. DeleteID::remove(@r.last, @p[0].value.read)
  352. @a[0].send("reset")
  353. nil
  354. end
  355. end
  356. class DeleteIDDirect < Riddl::Implementation
  357. def response
  358. DeleteID::remove(@r.last, @p[0].value)
  359. @a[0].send("reset")
  360. nil
  361. end
  362. end
  363. class DeletePK < Riddl::Implementation
  364. def response
  365. fname = File.join('data',@r.last,'data.db')
  366. db = SQLite3::Database.open fname
  367. pkvalue = @p[0].value
  368. db.execute("DELETE FROM Entries WHERE pkvalue = '" + pkvalue + "'")
  369. @a[0].send("reset")
  370. nil
  371. end
  372. end
  373. class SearchPK < Riddl::Implementation
  374. def response
  375. fname = File.join('data',@r[-4],'data.db')
  376. db = SQLite3::Database.open fname
  377. pkvalue = @r.last
  378. if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
  379. db.results_as_hash = true
  380. alldata = {};
  381. alldata["data"] = (db.execute "Select * from Entries WHERE pkvalue = '" + pkvalue + "'")
  382. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  383. else
  384. Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
  385. end
  386. end
  387. end
  388. class GetNext < Riddl::Implementation
  389. def response
  390. fname = File.join('data',@r[-2],'data.db')
  391. db = SQLite3::Database.open fname
  392. #get value
  393. db.results_as_hash = true
  394. alldata = {};
  395. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1")[0]
  396. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  397. end
  398. end
  399. class DeleteNext < Riddl::Implementation
  400. def response
  401. fname = File.join('data',@r[-2],'data.db')
  402. db = SQLite3::Database.open fname
  403. #get value
  404. db.results_as_hash = true
  405. alldata = {};
  406. alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1")[0]
  407. #remove from DB
  408. DeleteID::remove(@r[-2], alldata["data"]["__orderID__"])
  409. @a[0].send("reset")
  410. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
  411. end
  412. end
  413. class DeleteAll < Riddl::Implementation
  414. def response
  415. File.unlink(File.join('data',@r.last,'data.db')) rescue nil
  416. nil
  417. end
  418. end
  419. class SSE < Riddl::SSEImplementation #{{{
  420. def onopen
  421. signals = @a[0]
  422. signals.add self
  423. send 'started'
  424. true
  425. end
  426. def onclose
  427. signals = @a[0]
  428. signals.remove self
  429. nil
  430. end
  431. end #}}}
  432. class Signaling # {{{
  433. def initialize
  434. @binding = []
  435. end
  436. def add(binding)
  437. @binding << binding
  438. end
  439. def remove(binding)
  440. @binding.delete(binding)
  441. end
  442. def length
  443. @binding.length
  444. end
  445. def send(value)
  446. @binding.each do |b|
  447. b.send(value)
  448. end
  449. end
  450. end #}}}
  451. server = Riddl::Server.new(File.join(__dir__,'/dashboard.xml'), :host => 'localhost') do |opts|
  452. accessible_description true
  453. cross_site_xhr true
  454. opts[:signals] = {}
  455. parallel do
  456. loop do
  457. opts[:signals].each do |k,v|
  458. v.send('keepalive')
  459. end
  460. sleep 5
  461. end
  462. end
  463. on resource do
  464. run ConfigSites if get
  465. on resource 'getconfigs' do
  466. run GetAllConfigs if get
  467. end
  468. on resource do |r|
  469. idx = r[:r][0]
  470. opts[:signals][idx] ||= Signaling.new
  471. run Get if get
  472. run Put, opts[:signals][idx] if put 'input'
  473. run PutPrio, opts[:signals][idx] if put 'inputprio'
  474. run PutPrioN, opts[:signals][idx] if put 'inputNprio'
  475. run Move if put 'move'
  476. run Update if put 'update'
  477. run Storesortedtable, opts[:signals][idx] if put 'storesortedtable'
  478. run AddEntry, opts[:signals][idx] if post 'directADD'
  479. run Delete, opts[:signals][idx] if delete 'deleteMsg'
  480. run DeleteID, opts[:signals][idx] if delete 'deleteByID'
  481. run DeletePK, opts[:signals][idx] if delete 'deletePK'
  482. run DeleteAll, opts[:signals][idx] if delete 'deleteAll'
  483. run DeleteIDDirect, opts[:signals][idx] if delete
  484. on resource '\d+' do
  485. run GetNData, opts if get
  486. run DeleteNData, opts[:signals][idx] if delete
  487. end
  488. on resource 'sse' do
  489. run SSE, opts[:signals][idx] if sse
  490. end
  491. on resource 'data.db' do
  492. run GetAllData if get
  493. end
  494. on resource 'Column' do
  495. run AddColumn if post 'addColumn'
  496. run DeleteColumn if post 'removeColumn'
  497. end
  498. on resource 'htmlform' do
  499. run PostHtmlVals, opts[:signals][idx] if post
  500. end
  501. on resource 'search' do
  502. on resource 'PK' do
  503. on resource '.*' do
  504. run SearchPK if get
  505. end
  506. end
  507. end
  508. on resource 'visus' do
  509. run GetAllVisus if get
  510. end
  511. on resource 'next' do
  512. run GetNext, opts[:signals][idx] if get
  513. run DeleteNext, opts[:signals][idx] if delete
  514. end
  515. end
  516. end
  517. end.loop!