#!/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 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
#https://coderwall.com/p/atyfyq/ruby-string-to-boolean
class String
def to_bool
return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
return false if self == false || self.empty? || self =~ (/(false|f|no|n|0)$/i)
raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
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, showbutton: @p[5].value.to_bool};
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, showbutton: @p[5].value.to_bool, 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
class DeleteFrame < Riddl::Implementation
def response
puts "deleting"
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 + 1), (@p[1].value.to_i + 1))
data_hash["data"].delete(c)
end
end
File.write(path, JSON.dump(data_hash))
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 GetInfo < Riddl::Implementation #{{{
def response
fname = File.join('data',@r[-2],'info.json')
if File.exists? fname
Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
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 Handler < Riddl::Implementation
def response
topic = @p[1].value
event_name = @p[2].value
log_dir = @a[0]
template = @a[1]
notification = JSON.parse(@p[3].value.read)
instancenr = notification['instance']
content = notification['content']
activity = content['activity']
parameters = content['parameters']
receiving = content['received']
#puts instancenr
#puts activity
if content['values']&.any?
puts alldata['ausfuehrungen']
end
nil
end
end
class SSE < Riddl::SSEImplementation #{{{
def onopen
signals = @a[0]
signals.add self
send 'started'
true
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__,'/frames.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
on resource do |r|
idx = r[:r][0]
opts[:signals][idx] ||= Signaling.new
run Get if get
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 DeleteFrame, opts[:signals][idx] if post 'deleteframe'
on resource 'handler' do
run Handler if post
end
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 'info.json' do
run GetInfo 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!