frames 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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. # process:
  25. # https://centurio.work/customers/evva/flow/?monitor=https://centurio.work/flow-test/engine/729/
  26. class Get < Riddl::Implementation
  27. def response
  28. if @r[0] == 'test'
  29. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','test.html')))
  30. elsif @r[0] == 'menu'
  31. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','menu.html')))
  32. else
  33. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','template.html')))
  34. end
  35. end
  36. end
  37. class Put < Riddl::Implementation
  38. def response
  39. Dir.mkdir(File.join('data',@r.last)) rescue nil
  40. File.write(File.join('data',@r.last,'style.url'),@p[0].value)
  41. File.write(File.join('data',@r.last,'document.xml'),@p[1].value)
  42. File.write(File.join('data',@r.last,'frames.json'),JSON.dump(JSON.parse('{"data":[]}')))
  43. File.write(File.join('data',@r.last,'info.json'),JSON.dump(JSON.parse('{"x_amount":' + @p[2].value + ', "y_amount":' + @p[3].value + ', "lang":"' + @p[4].value + '", "langs":["' + @p[4].value + '"]}')))
  44. File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
  45. @a[0].send('new')
  46. nil
  47. end
  48. #def headers
  49. # Riddl::Header.new('CPEE-CALLBACK', 'true')
  50. #end
  51. end
  52. #https://coderwall.com/p/atyfyq/ruby-string-to-boolean
  53. class String
  54. def to_bool
  55. return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
  56. return false if self == false || self.empty? || self =~ (/(false|f|no|n|0)$/i)
  57. raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
  58. end
  59. end
  60. class NewFrame < Riddl::Implementation
  61. def response
  62. path = File.join('data',@r.last,'frames.json')
  63. file = File.read(path)
  64. data_hash = JSON::parse(file)
  65. data_hash["data"].each do | c |
  66. if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[0].value.to_i, @p[1].value.to_i, (@p[0].value.to_i + @p[2].value.to_i - 1), (@p[1].value.to_i + @p[3].value.to_i - 1))
  67. data_hash["data"].delete(c)
  68. end
  69. end
  70. # example
  71. # myObj = {
  72. # "lx":3,
  73. # "ly":3,
  74. # "rx":3,
  75. # "ry":3,
  76. # "url": {
  77. # "de-at":"xyz.at",
  78. # "en-us":"xyz.com"
  79. # }
  80. # }
  81. urls = JSON::parse(@p[4].value);
  82. hash = {lx: @p[0].value.to_i, ly: @p[1].value.to_i, rx: (@p[0].value.to_i + @p[2].value.to_i - 1), ry: (@p[1].value.to_i + @p[3].value.to_i - 1), url: urls, showbutton: @p[5].value.to_bool};
  83. data_hash["data"].push(hash)
  84. File.write(path, JSON.dump(data_hash))
  85. #only send active url to client
  86. infofile = File.join('data',@r.last,'info.json')
  87. infojson = JSON::parse(File.read(infofile))
  88. hash["url"] = urls[infojson["lang"]]
  89. @a[0].send(JSON.dump(hash))
  90. nil
  91. end
  92. end
  93. class NewFramePut < Riddl::Implementation
  94. def response
  95. path = File.join('data',@r.last,'frames.json')
  96. file = File.read(path)
  97. data_hash = JSON::parse(file)
  98. data_hash["data"].each do | c |
  99. if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[0].value.to_i, @p[1].value.to_i, (@p[0].value.to_i + @p[2].value.to_i - 1), (@p[1].value.to_i + @p[3].value.to_i - 1))
  100. data_hash["data"].delete(c)
  101. end
  102. end
  103. urls = JSON::parse(@p[4].value);
  104. hash = {lx: @p[0].value.to_i, ly: @p[1].value.to_i, rx: (@p[0].value.to_i + @p[2].value.to_i - 1), ry: (@p[1].value.to_i + @p[3].value.to_i - 1), url: urls, showbutton: @p[5].value.to_bool, callback: @h['CPEE_CALLBACK']};
  105. data_hash["data"].push(hash)
  106. File.write(path, JSON.dump(data_hash))
  107. #only send active url to client
  108. infofile = File.join('data',@r.last,'info.json')
  109. infojson = JSON::parse(File.read(infofile))
  110. hash["url"] = urls[infojson["lang"]]
  111. @a[0].send(JSON.dump(hash))
  112. nil
  113. end
  114. def headers
  115. Riddl::Header.new('CPEE-CALLBACK', 'true')
  116. end
  117. end
  118. class DeleteFrame < Riddl::Implementation
  119. def response
  120. puts "deleting"
  121. path = File.join('data',@r.last,'frames.json')
  122. file = File.read(path)
  123. data_hash = JSON::parse(file)
  124. data_hash["data"].each do | c |
  125. if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[0].value.to_i, @p[1].value.to_i, (@p[0].value.to_i + 1), (@p[1].value.to_i + 1))
  126. data_hash["data"].delete(c)
  127. end
  128. end
  129. File.write(path, JSON.dump(data_hash))
  130. end
  131. end
  132. def doOverlap(l1x, l1y, r1x, r1y, l2x, l2y, r2x, r2y)
  133. if l1x > r2x || l2x > r1x
  134. return false;
  135. end
  136. if l1y > r2y || l2y > r1y
  137. return false;
  138. end
  139. return true;
  140. end
  141. class Delete < Riddl::Implementation
  142. def response
  143. if cbu = File.read(File.join('data',@r.last,'callback'))
  144. send = { 'operation' => @p[0].value }
  145. case send['operation']
  146. when 'result'
  147. send['target'] = JSON::parse(@p[1].value.read)
  148. end
  149. cbu += '/' unless cbu[-1] == '/'
  150. Typhoeus.put(cbu, body: JSON::generate(send), headers: { 'content-type' => 'application/json'})
  151. end
  152. File.unlink(File.join('data',@r.last,'callback')) rescue nil
  153. File.unlink(File.join('data',@r.last,'style.url')) rescue nil
  154. File.unlink(File.join('data',@r.last,'document.xml')) rescue nil
  155. File.unlink(File.join('data',@r.last,'info.json')) rescue nil
  156. @a[0].send('reset')
  157. nil
  158. end
  159. end
  160. class GetFrames < Riddl::Implementation #{{{
  161. def response
  162. fname = File.join('data',@r[-2],'frames.json')
  163. if File.exists? fname
  164. infofile = File.join('data',@r[-2],'info.json')
  165. infojson = JSON::parse(File.read(infofile))
  166. #remove not used languages
  167. file = JSON::parse(File.read(fname))
  168. file["data"].each do |child|
  169. child["url"] = child["url"][infojson["lang"]]
  170. end
  171. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(file))
  172. else
  173. @status = 404
  174. end
  175. end
  176. end #}}}
  177. class GetInfo < Riddl::Implementation #{{{
  178. def response
  179. fname = File.join('data',@r[-2],'info.json')
  180. if File.exists? fname
  181. Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  182. else
  183. @status = 404
  184. end
  185. end
  186. end #}}}
  187. class GetLangs < Riddl::Implementation #{{{
  188. def response
  189. fname = File.join('data',@r[-2],'document.xml')
  190. if File.exists? fname
  191. doc = XML::Smart.open_unprotected(fname)
  192. ndoc = XML::Smart.string('<languages/>')
  193. doc.find('//@lang').each do |e|
  194. ndoc.root.add('language',e.value)
  195. end
  196. Riddl::Parameter::Complex.new('value','text/xml',ndoc.to_s)
  197. else
  198. @status = 404
  199. end
  200. end
  201. end #}}}
  202. class SetLang < Riddl::Implementation #{{{
  203. def response
  204. fname = File.join('data',@r[-2],'info.json')
  205. if File.exists? fname
  206. infojson = JSON::parse(File.read(fname))
  207. infojson["lang"] = @p[0].value
  208. #add to langs
  209. if !infojson["langs"].include?(@p[0].value)
  210. infojson["langs"].push(@p[0].value)
  211. end
  212. File.write(fname, JSON.dump(infojson))
  213. @a[0].send('reset')
  214. nil
  215. else
  216. @status = 404
  217. end
  218. end
  219. end #}}}
  220. class GetStyle < Riddl::Implementation #{{{
  221. def response
  222. fname = File.join('data',@r[-2],'style.url')
  223. if File.exists? fname
  224. Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
  225. else
  226. @status = 404
  227. end
  228. end
  229. end #}}}
  230. class GetDocument < Riddl::Implementation #{{{
  231. def response
  232. fname = File.join('data',@r[-3],'document.xml')
  233. if File.exists? fname
  234. doc = XML::Smart.open_unprotected(fname)
  235. val = nil
  236. doc.find("//variant[@lang='#{@r[-1]}']").each do |e|
  237. val = e.text
  238. end
  239. if val
  240. Riddl::Parameter::Complex.new('url','text/plain',val.strip)
  241. else
  242. @status = 404
  243. end
  244. else
  245. @status = 404
  246. end
  247. end
  248. end #}}}
  249. class GetButton < Riddl::Implementation #{{{
  250. def response
  251. fname = File.join('data',@r[-3],'document.xml')
  252. if File.exists? fname
  253. doc = XML::Smart.open_unprotected(fname)
  254. val = nil
  255. doc.find("//variant[@lang='#{@r[-1]}']").each do |e|
  256. val = e.attributes['button']
  257. end
  258. if val
  259. Riddl::Parameter::Complex.new('url','text/plain',val.strip)
  260. else
  261. @status = 404
  262. end
  263. else
  264. @status = 404
  265. end
  266. end
  267. end #}}}
  268. class Handler < Riddl::Implementation
  269. def response
  270. topic = @p[1].value
  271. event_name = @p[2].value
  272. log_dir = @a[0]
  273. template = @a[1]
  274. notification = JSON.parse(@p[3].value.read)
  275. instancenr = notification['instance']
  276. content = notification['content']
  277. activity = content['activity']
  278. parameters = content['parameters']
  279. receiving = content['received']
  280. #puts instancenr
  281. #puts activity
  282. if content['values']&.any?
  283. puts alldata['ausfuehrungen']
  284. end
  285. nil
  286. end
  287. end
  288. class SSE < Riddl::SSEImplementation #{{{
  289. def onopen
  290. signals = @a[0]
  291. signals.add self
  292. send 'started'
  293. true
  294. end
  295. def onclose
  296. signals = @a[0]
  297. signals.remove self
  298. nil
  299. end
  300. end #}}}
  301. class Signaling # {{{
  302. def initialize
  303. @binding = []
  304. end
  305. def add(binding)
  306. @binding << binding
  307. end
  308. def remove(binding)
  309. @binding.delete(binding)
  310. end
  311. def length
  312. @binding.length
  313. end
  314. def send(value)
  315. @binding.each do |b|
  316. b.send(value)
  317. end
  318. end
  319. end #}}}
  320. server = Riddl::Server.new(File.join(__dir__,'/frames.xml'), :host => 'localhost') do |opts|
  321. accessible_description true
  322. cross_site_xhr true
  323. opts[:signals] = {}
  324. parallel do
  325. loop do
  326. opts[:signals].each do |k,v|
  327. v.send('keepalive')
  328. end
  329. sleep 5
  330. end
  331. end
  332. on resource do
  333. on resource do |r|
  334. idx = r[:r][0]
  335. opts[:signals][idx] ||= Signaling.new
  336. run Get if get
  337. run Put, opts[:signals][idx] if put 'input'
  338. run NewFrame, opts[:signals][idx] if post 'frame'
  339. run NewFramePut, opts[:signals][idx] if put 'frame'
  340. run DeleteFrame, opts[:signals][idx] if post 'deleteframe'
  341. on resource 'handler' do
  342. run Handler if post
  343. end
  344. run Delete, opts[:signals][idx] if delete 'opa'
  345. run Delete, opts[:signals][idx] if delete 'opb'
  346. on resource 'sse' do
  347. run SSE, opts[:signals][idx] if sse
  348. end
  349. on resource 'languages' do
  350. run GetLangs if get
  351. run SetLang, opts[:signals][idx] if post 'lang'
  352. end
  353. on resource 'style.url' do
  354. run GetStyle if get
  355. end
  356. on resource 'info.json' do
  357. run GetInfo if get
  358. end
  359. on resource 'frames.json' do
  360. run GetFrames if get
  361. end
  362. on resource 'buttons' do
  363. on resource '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*' do
  364. run GetButton if get
  365. end
  366. end
  367. on resource 'documents' do
  368. on resource '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*' do
  369. run GetDocument if get
  370. end
  371. end
  372. end
  373. end
  374. end.loop!