#!/usr/bin/ruby # # This file is part of centurio.work/ing/commands. # # centurio.work/ing/commands is free software: you can redistribute it and/or # modify it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # centurio.work/ing/commands is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along with # centurio.work/ing/commands (file COPYING in the main directory). If not, see # . require 'rubygems' require 'json' require 'xml/smart' require 'riddl/server' require 'fileutils' require 'typhoeus' # process: # https://centurio.work/customers/evva/flow/?monitor=https://centurio.work/flow-test/engine/729/ class NewInstance < Riddl::Implementation def response entries = Dir.entries(File.join('data')).size Dir.mkdir(File.join('data',entries.to_s)) rescue nil Riddl::Parameter::Complex.new('text','text/plain',entries.to_s) end end class Index < Riddl::Implementation def response Riddl::Parameter::Complex.new('url','text/html',File.open(File.join(__dir__,'template','index.html'))) end end class Builder < Riddl::Implementation def response Riddl::Parameter::Complex.new('url','text/html',File.open(File.join(__dir__,'template','builder.html'))) end end class DisplayForm < Riddl::Implementation def response #Riddl::Parameter::Complex.new('url','text/html',File.open(File.join('data',@r[0],'form_min.html'))) Riddl::Parameter::Complex.new('url','text/html',File.open(File.join(__dir__,'template','form.html'))) end end class SaveForm < Riddl::Implementation def response File.write(File.join('data',@r[0],'form.json'),@p[0].value.read) end end class SaveHtmlForm < Riddl::Implementation def response file = File.read(File.join(__dir__,'template','form_min.html')) File.write(File.join('data',@r[0],'form_min.html'),(file.sub! '', @p[0].value.read)) end end class GetJson < Riddl::Implementation #{{{ def response fname = File.join('data',@r[-2],'form.json') if File.exists? fname Riddl::Parameter::Complex.new('value','application/json',File.read(fname)) else @status = 404 end end end #}}} class Get < Riddl::Implementation def response if @r[0] == 'test' Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','test.html'))) elsif @r[0] == 'menu' Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','menu.html'))) else Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','template.html'))) end end end class Put < Riddl::Implementation def response Dir.mkdir(File.join('data',@r.last)) rescue nil File.write(File.join('data',@r.last,'style.url'),@p[0].value) File.write(File.join('data',@r.last,'document.xml'),@p[1].value) File.write(File.join('data',@r.last,'frames.json'),JSON.dump(JSON.parse('{"data":[]}'))) 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 + '"]}'))) File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK']) @a[0].send('new') nil end #def headers # Riddl::Header.new('CPEE-CALLBACK', 'true') #end end class NewFrame < Riddl::Implementation def response path = File.join('data',@r.last,'frames.json') file = File.read(path) data_hash = JSON::parse(file) data_hash["data"].each do | c | 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)) data_hash["data"].delete(c) end end # example # myObj = { # "lx":3, # "ly":3, # "rx":3, # "ry":3, # "url": { # "de-at":"xyz.at", # "en-us":"xyz.com" # } # } urls = JSON::parse(@p[4].value); 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}; data_hash["data"].push(hash) File.write(path, JSON.dump(data_hash)) #only send active url to client infofile = File.join('data',@r.last,'info.json') infojson = JSON::parse(File.read(infofile)) hash["url"] = urls[infojson["lang"]] @a[0].send(JSON.dump(hash)) nil end end class NewFramePut < Riddl::Implementation def response path = File.join('data',@r.last,'frames.json') file = File.read(path) data_hash = JSON::parse(file) data_hash["data"].each do | c | 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)) data_hash["data"].delete(c) end end urls = JSON::parse(@p[4].value); 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, callback: @h['CPEE_CALLBACK']}; data_hash["data"].push(hash) File.write(path, JSON.dump(data_hash)) #only send active url to client infofile = File.join('data',@r.last,'info.json') infojson = JSON::parse(File.read(infofile)) hash["url"] = urls[infojson["lang"]] @a[0].send(JSON.dump(hash)) nil end def headers Riddl::Header.new('CPEE-CALLBACK', 'true') end end def doOverlap(l1x, l1y, r1x, r1y, l2x, l2y, r2x, r2y) if l1x > r2x || l2x > r1x return false; end if l1y > r2y || l2y > r1y return false; end return true; end class Delete < Riddl::Implementation def response if cbu = File.read(File.join('data',@r.last,'callback')) send = { 'operation' => @p[0].value } case send['operation'] when 'result' send['target'] = JSON::parse(@p[1].value.read) end cbu += '/' unless cbu[-1] == '/' Typhoeus.put(cbu, body: JSON::generate(send), headers: { 'content-type' => 'application/json'}) end File.unlink(File.join('data',@r.last,'callback')) rescue nil File.unlink(File.join('data',@r.last,'style.url')) rescue nil File.unlink(File.join('data',@r.last,'document.xml')) rescue nil File.unlink(File.join('data',@r.last,'info.json')) rescue nil @a[0].send('reset') nil end end class GetFrames < Riddl::Implementation #{{{ def response fname = File.join('data',@r[-2],'frames.json') if File.exists? fname infofile = File.join('data',@r[-2],'info.json') infojson = JSON::parse(File.read(infofile)) #remove not used languages file = JSON::parse(File.read(fname)) file["data"].each do |child| child["url"] = child["url"][infojson["lang"]] end Riddl::Parameter::Complex.new('value','application/json',JSON.dump(file)) else @status = 404 end end end #}}} class GetLangs < Riddl::Implementation #{{{ def response fname = File.join('data',@r[-2],'document.xml') if File.exists? fname doc = XML::Smart.open_unprotected(fname) ndoc = XML::Smart.string('') doc.find('//@lang').each do |e| ndoc.root.add('language',e.value) end Riddl::Parameter::Complex.new('value','text/xml',ndoc.to_s) else @status = 404 end end end #}}} class SetLang < Riddl::Implementation #{{{ def response fname = File.join('data',@r[-2],'info.json') if File.exists? fname infojson = JSON::parse(File.read(fname)) infojson["lang"] = @p[0].value #add to langs if !infojson["langs"].include?(@p[0].value) infojson["langs"].push(@p[0].value) end File.write(fname, JSON.dump(infojson)) @a[0].send('reset') nil else @status = 404 end end end #}}} class GetStyle < Riddl::Implementation #{{{ def response fname = File.join('data',@r[-2],'style.url') if File.exists? fname Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip) else @status = 404 end end end #}}} class GetDocument < Riddl::Implementation #{{{ def response fname = File.join('data',@r[-3],'document.xml') if File.exists? fname doc = XML::Smart.open_unprotected(fname) val = nil doc.find("//variant[@lang='#{@r[-1]}']").each do |e| val = e.text end if val Riddl::Parameter::Complex.new('url','text/plain',val.strip) else @status = 404 end else @status = 404 end end end #}}} class GetButton < Riddl::Implementation #{{{ def response fname = File.join('data',@r[-3],'document.xml') if File.exists? fname doc = XML::Smart.open_unprotected(fname) val = nil doc.find("//variant[@lang='#{@r[-1]}']").each do |e| val = e.attributes['button'] end if val Riddl::Parameter::Complex.new('url','text/plain',val.strip) else @status = 404 end else @status = 404 end end end #}}} class SSE < Riddl::SSEImplementation #{{{ def onopen signals = @a[0] signals.add self send 'started' end def onclose signals = @a[0] signals.remove self nil end end #}}} class Signaling # {{{ def initialize @binding = [] end def add(binding) @binding << binding end def remove(binding) @binding.delete(binding) end def length @binding.length end def send(value) @binding.each do |b| b.send(value) end end end #}}} server = Riddl::Server.new(File.join(__dir__,'/forms.xml'), :host => 'localhost') do |opts| accessible_description true cross_site_xhr true opts[:signals] = {} parallel do loop do opts[:signals].each do |k,v| v.send('keepalive') end sleep 5 end end on resource do run Index if get run NewInstance if post on resource do |r| run DisplayForm if get on resource 'builder' do run Builder if get run SaveForm if post 'form' run SaveHtmlForm if post 'htmlform' end on resource 'json' do run GetJson if get end idx = r[:r][0] opts[:signals][idx] ||= Signaling.new run Put, opts[:signals][idx] if put 'input' run NewFrame, opts[:signals][idx] if post 'frame' run NewFramePut, opts[:signals][idx] if put 'frame' run Delete, opts[:signals][idx] if delete 'opa' run Delete, opts[:signals][idx] if delete 'opb' on resource 'sse' do run SSE, opts[:signals][idx] if sse end on resource 'languages' do run GetLangs if get run SetLang, opts[:signals][idx] if post 'lang' end on resource 'style.url' do run GetStyle if get end on resource 'frames.json' do run GetFrames if get end on resource 'buttons' do on resource '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*' do run GetButton if get end end on resource 'documents' do on resource '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*' do run GetDocument if get end end end end end.loop!