frames 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  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. else
  31. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','template.html')))
  32. end
  33. end
  34. end
  35. class Put < Riddl::Implementation
  36. def response
  37. Dir.mkdir(File.join('data',@r.last)) rescue nil
  38. File.write(File.join('data',@r.last,'style.url'),@p[0].value)
  39. File.write(File.join('data',@r.last,'document.xml'),@p[1].value)
  40. File.write(File.join('data',@r.last,'frames.json'),JSON.dump(JSON.parse('{"data":[]}')))
  41. 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 + '"]}')))
  42. File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
  43. @a[0].send('new')
  44. nil
  45. end
  46. def headers
  47. Riddl::Header.new('CPEE-CALLBACK', 'true')
  48. end
  49. end
  50. class NewFrame < Riddl::Implementation
  51. def response
  52. path = File.join('data',@r.last,'frames.json')
  53. file = File.read(path)
  54. data_hash = JSON::parse(file)
  55. data_hash["data"].each do | c |
  56. 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))
  57. data_hash["data"].delete(c)
  58. end
  59. end
  60. # example
  61. # myObj = {
  62. # "lx":3,
  63. # "ly":3,
  64. # "rx":3,
  65. # "ry":3,
  66. # "url": {
  67. # "de-at":"xyz.at",
  68. # "en-us":"xyz.com"
  69. # }
  70. # }
  71. urls = JSON::parse(@p[4].value);
  72. 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};
  73. data_hash["data"].push(hash)
  74. File.write(path, JSON.dump(data_hash))
  75. #only send active url to client
  76. infofile = File.join('data',@r.last,'info.json')
  77. infojson = JSON::parse(File.read(infofile))
  78. hash["url"] = urls[infojson["lang"]]
  79. @a[0].send(JSON.dump(hash))
  80. nil
  81. end
  82. end
  83. def doOverlap(l1x, l1y, r1x, r1y, l2x, l2y, r2x, r2y)
  84. if l1x > r2x || l2x > r1x
  85. return false;
  86. end
  87. if l1y > r2y || l2y > r1y
  88. return false;
  89. end
  90. return true;
  91. end
  92. class Delete < Riddl::Implementation
  93. def response
  94. if cbu = File.read(File.join('data',@r.last,'callback'))
  95. send = { 'operation' => @p[0].value }
  96. case send['operation']
  97. when 'result'
  98. send['target'] = JSON::parse(@p[1].value.read)
  99. end
  100. cbu += '/' unless cbu[-1] == '/'
  101. Typhoeus.put(cbu, body: JSON::generate(send), headers: { 'content-type' => 'application/json'})
  102. end
  103. File.unlink(File.join('data',@r.last,'callback')) rescue nil
  104. File.unlink(File.join('data',@r.last,'style.url')) rescue nil
  105. File.unlink(File.join('data',@r.last,'document.xml')) rescue nil
  106. File.unlink(File.join('data',@r.last,'info.json')) rescue nil
  107. @a[0].send('reset')
  108. nil
  109. end
  110. end
  111. class GetFrames < Riddl::Implementation #{{{
  112. def response
  113. fname = File.join('data',@r[-2],'frames.json')
  114. if File.exists? fname
  115. infofile = File.join('data',@r[-2],'info.json')
  116. infojson = JSON::parse(File.read(infofile))
  117. #remove not used languages
  118. file = JSON::parse(File.read(fname))
  119. file["data"].each do |child|
  120. child["url"] = child["url"][infojson["lang"]]
  121. end
  122. Riddl::Parameter::Complex.new('value','application/json',JSON.dump(file))
  123. else
  124. @status = 404
  125. end
  126. end
  127. end #}}}
  128. class GetInfo < Riddl::Implementation #{{{
  129. def response
  130. fname = File.join('data',@r[-2],'info.json')
  131. if File.exists? fname
  132. Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  133. else
  134. @status = 404
  135. end
  136. end
  137. end #}}}
  138. class GetLangs < Riddl::Implementation #{{{
  139. def response
  140. fname = File.join('data',@r[-2],'document.xml')
  141. if File.exists? fname
  142. doc = XML::Smart.open_unprotected(fname)
  143. ndoc = XML::Smart.string('<languages/>')
  144. doc.find('//@lang').each do |e|
  145. ndoc.root.add('language',e.value)
  146. end
  147. Riddl::Parameter::Complex.new('value','text/xml',ndoc.to_s)
  148. else
  149. @status = 404
  150. end
  151. end
  152. end #}}}
  153. class SetLang < Riddl::Implementation #{{{
  154. def response
  155. fname = File.join('data',@r[-2],'info.json')
  156. if File.exists? fname
  157. infojson = JSON::parse(File.read(fname))
  158. infojson["lang"] = @p[0].value
  159. #add to langs
  160. if !infojson["langs"].include?(@p[0].value)
  161. infojson["langs"].push(@p[0].value)
  162. end
  163. File.write(fname, JSON.dump(infojson))
  164. @a[0].send('reset')
  165. nil
  166. else
  167. @status = 404
  168. end
  169. end
  170. end #}}}
  171. class GetStyle < Riddl::Implementation #{{{
  172. def response
  173. fname = File.join('data',@r[-2],'style.url')
  174. if File.exists? fname
  175. Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
  176. else
  177. @status = 404
  178. end
  179. end
  180. end #}}}
  181. class GetDocument < Riddl::Implementation #{{{
  182. def response
  183. fname = File.join('data',@r[-3],'document.xml')
  184. if File.exists? fname
  185. doc = XML::Smart.open_unprotected(fname)
  186. val = nil
  187. doc.find("//variant[@lang='#{@r[-1]}']").each do |e|
  188. val = e.text
  189. end
  190. if val
  191. Riddl::Parameter::Complex.new('url','text/plain',val.strip)
  192. else
  193. @status = 404
  194. end
  195. else
  196. @status = 404
  197. end
  198. end
  199. end #}}}
  200. class GetButton < Riddl::Implementation #{{{
  201. def response
  202. fname = File.join('data',@r[-3],'document.xml')
  203. if File.exists? fname
  204. doc = XML::Smart.open_unprotected(fname)
  205. val = nil
  206. doc.find("//variant[@lang='#{@r[-1]}']").each do |e|
  207. val = e.attributes['button']
  208. end
  209. if val
  210. Riddl::Parameter::Complex.new('url','text/plain',val.strip)
  211. else
  212. @status = 404
  213. end
  214. else
  215. @status = 404
  216. end
  217. end
  218. end #}}}
  219. class SSE < Riddl::SSEImplementation #{{{
  220. def onopen
  221. signals = @a[0]
  222. signals.add self
  223. send 'started'
  224. end
  225. def onclose
  226. signals = @a[0]
  227. signals.remove self
  228. nil
  229. end
  230. end #}}}
  231. class Signaling # {{{
  232. def initialize
  233. @binding = []
  234. end
  235. def add(binding)
  236. @binding << binding
  237. end
  238. def remove(binding)
  239. @binding.delete(binding)
  240. end
  241. def length
  242. @binding.length
  243. end
  244. def send(value)
  245. @binding.each do |b|
  246. b.send(value)
  247. end
  248. end
  249. end #}}}
  250. server = Riddl::Server.new(File.join(__dir__,'/frames.xml'), :host => 'localhost') do |opts|
  251. accessible_description true
  252. cross_site_xhr true
  253. opts[:signals] = {}
  254. parallel do
  255. loop do
  256. opts[:signals].each do |k,v|
  257. v.send('keepalive')
  258. end
  259. sleep 5
  260. end
  261. end
  262. on resource do
  263. on resource do |r|
  264. idx = r[:r][0]
  265. opts[:signals][idx] ||= Signaling.new
  266. run Get if get
  267. run NewFrame, opts[:signals][idx] if post 'frame'
  268. run Put, opts[:signals][idx] if put 'input'
  269. run Delete, opts[:signals][idx] if delete 'opa'
  270. run Delete, opts[:signals][idx] if delete 'opb'
  271. on resource 'sse' do
  272. run SSE, opts[:signals][idx] if sse
  273. end
  274. on resource 'languages' do
  275. run GetLangs if get
  276. run SetLang, opts[:signals][idx] if post 'lang'
  277. end
  278. on resource 'style.url' do
  279. run GetStyle if get
  280. end
  281. on resource 'info.json' do
  282. run GetInfo if get
  283. end
  284. on resource 'frames.json' do
  285. run GetFrames if get
  286. end
  287. on resource 'buttons' do
  288. on resource '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*' do
  289. run GetButton if get
  290. end
  291. end
  292. on resource 'documents' do
  293. on resource '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*' do
  294. run GetDocument if get
  295. end
  296. end
  297. end
  298. end
  299. end.loop!