Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a printer job queue via the printer plugin
- Loading branch information
Showing
24 changed files
with
561 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
db/migrate/20181201000000_create_printer_jobs.foodsoft_printer_engine.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
class CreatePrinterJobs < ActiveRecord::Migration | ||
def change | ||
create_table :printer_jobs do |t| | ||
t.references :order | ||
t.string :document, null: false | ||
t.integer :created_by_user_id, null: false | ||
t.integer :finished_by_user_id | ||
t.datetime :finished_at, index: true | ||
end | ||
|
||
create_table :printer_job_updates do |t| | ||
t.references :printer_job, null: false | ||
t.datetime :created_at, null: false | ||
t.string :state, null: false | ||
t.text :message | ||
end | ||
|
||
add_index :printer_job_updates, [:printer_job_id, :created_at] | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
FoodsoftPrinter | ||
================= | ||
|
||
This plugin adds a printer queue to allow mebers to print PDF with one click. | ||
Usually a mini computer with a printer at a room in the foodcoop will wait for | ||
new printer jobs and prints them. | ||
|
||
This plugin is not enabled by default. To install it, add uncomment the | ||
corresponding line in the `Gemfile`, or add: | ||
|
||
```Gemfile | ||
gem 'foodsoft_printer', path: 'plugins/foodsoft_printer' | ||
``` | ||
|
||
This plugin introduces the foodcoop config option `printer_token`, which takes | ||
a random string for authentication at the endpoint. Additionally a set of | ||
PDF files can be selected, which will be generated when a print is triggered. | ||
|
||
The communication with the printer client happens via two endpoints, which both | ||
require the `printer_token` as `Bearer` token in the `Authorization` header. | ||
* `/:foodcoop/printer/socket`: main WebSocket communication | ||
* `/:foodcoop/printer/:id`: HTTP GET for documents | ||
|
||
The main communication happens via JSON messages via an WebSocket connection, | ||
which sends an array of docuement ids to the client, which need to be printed. | ||
Addionally the docuemnt can be downloaded as PDF via a separate endpoint. | ||
The client can updated the status of the documents by sending an object with | ||
the following keys to the server: | ||
* `id` (NBR, REQUIRED): id of the document, which should be updated | ||
* `state` (ENUM): the current sate of the printing progress. | ||
* `message` (STR, REQUIRES `state`): detailed description of the current state | ||
(e.g. download progress) | ||
* `finish` (BOOL): when set to `true` the job will be marked as done | ||
|
||
The following values are valid for the `state` property: | ||
* `queued`: the document is not yet ready for printing | ||
* `ready`: the document is ready to be downloaded | ||
* `downloading`: transfer of the document is in progress | ||
* `pending`: download completed, waiting for the printer | ||
* `held`: e.g., for "PIN printing" | ||
* `processing`: printing is in progress | ||
* `stopped`: out of paper, etc.) | ||
* `cancelled`: the user stopped the action | ||
* `aborted`: the printer stopped the action | ||
* `completed`: print was successful | ||
|
||
**Example**: | ||
A server sending `{"unfinished_jobs":[12,16]}` via WebSocket indicates that the | ||
two documents `12` and `16` are ready for printing. The client will request the | ||
first document from `/foodcoop/printer/12` and send it to the printer. The | ||
status can be updated by sending `{"id":12,"state":"pending"}` via WebSocket to | ||
the server. Sending `{"id":12,"state":"completed","finish":true}` as soon as | ||
when the printing succeded will mark the job done. | ||
|
||
To use this plugin the webserver must support WebSockets. The current | ||
implementation uses socket hijack, which is not supported by `thin`. `puma` | ||
supports that, but might lead to other problems, since it's not well tested | ||
in combination with foodsoft. Please be careful when switching the webserver! | ||
|
||
This plugin is part of the foodsoft package and uses the AGPL-3 license (see | ||
foodsoft's LICENSE for the full license text). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#!/usr/bin/env rake | ||
begin | ||
require 'bundler/setup' | ||
rescue LoadError | ||
puts 'You must `gem install bundler` and `bundle install` to run rake tasks' | ||
end | ||
begin | ||
require 'rdoc/task' | ||
rescue LoadError | ||
require 'rdoc/rdoc' | ||
require 'rake/rdoctask' | ||
RDoc::Task = Rake::RDocTask | ||
end | ||
|
||
RDoc::Task.new(:rdoc) do |rdoc| | ||
rdoc.rdoc_dir = 'rdoc' | ||
rdoc.title = 'FoodsoftPrinter' | ||
rdoc.options << '--line-numbers' | ||
rdoc.rdoc_files.include('README.rdoc') | ||
rdoc.rdoc_files.include('lib/**/*.rb') | ||
end | ||
|
||
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) | ||
load 'rails/tasks/engine.rake' | ||
|
||
|
||
|
||
Bundler::GemHelper.install_tasks | ||
|
||
require 'rake/testtask' | ||
|
||
Rake::TestTask.new(:test) do |t| | ||
t.libs << 'lib' | ||
t.libs << 'test' | ||
t.pattern = 'test/**/*_test.rb' | ||
t.verbose = false | ||
end | ||
|
||
|
||
task :default => :test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
class PrinterController < ApplicationController | ||
include Concerns::SendOrderPdf | ||
include Tubesock::Hijack | ||
|
||
skip_before_filter :authenticate | ||
before_filter :authenticate_printer | ||
before_filter -> { require_plugin_enabled FoodsoftPrinter } | ||
|
||
def socket | ||
hijack do |tubesock| | ||
tubesock.onopen do | ||
tubesock.send_data unfinished_jobs | ||
end | ||
|
||
tubesock.onmessage do |data| | ||
update_job data | ||
tubesock.send_data unfinished_jobs | ||
end | ||
end | ||
end | ||
|
||
def show | ||
job = PrinterJob.find(params[:id]) | ||
send_order_pdf job.order, job.document | ||
end | ||
|
||
private | ||
|
||
def unfinished_jobs | ||
{ | ||
unfinished_jobs: PrinterJob.pending.map(&:id) | ||
}.to_json | ||
end | ||
|
||
def update_job(data) | ||
json = JSON.parse data, symbolize_names: true | ||
job = PrinterJob.unfinished.find_by_id(json[:id]) | ||
return unless job | ||
if json[:state] | ||
job.add_update! json[:state], json[:message] | ||
end | ||
job.finish! if json[:finish] | ||
end | ||
|
||
protected | ||
|
||
def bearer_token | ||
pattern = /^Bearer / | ||
header = request.headers['Authorization'] | ||
header.gsub(pattern, '') if header && header.match(pattern) | ||
end | ||
|
||
def authenticate_printer | ||
return head(:unauthorized) unless bearer_token | ||
return head(:forbidden) if bearer_token != FoodsoftConfig[:printer_token] | ||
end | ||
|
||
end |
44 changes: 44 additions & 0 deletions
44
plugins/printer/app/controllers/printer_jobs_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
class PrinterJobsController < ApplicationController | ||
include Concerns::SendOrderPdf | ||
|
||
before_filter -> { require_plugin_enabled FoodsoftPrinter } | ||
|
||
def index | ||
jobs = PrinterJob.includes(:printer_job_updates) | ||
@pending_jobs = jobs.pending | ||
@queued_jobs = jobs.queued | ||
@finished_jobs = jobs.finished.order(finished_at: :desc).page(params[:page]).per(@per_page) | ||
end | ||
|
||
def create | ||
order = Order.find(params[:order]) | ||
state = order.open? ? 'queued' : 'ready' | ||
count = 0 | ||
PrinterJob.transaction do | ||
%w(articles fax groups matrix).each do |document| | ||
next unless FoodsoftConfig["printer_print_order_#{document}"] | ||
job = PrinterJob.create! order: order, document: document, created_by: current_user | ||
job.add_update! state | ||
count += 1 | ||
end | ||
end | ||
redirect_to order, notice: t('.notice', count: count) | ||
end | ||
|
||
def show | ||
@job = PrinterJob.find(params[:id]) | ||
end | ||
|
||
def document | ||
job = PrinterJob.find(params[:id]) | ||
send_order_pdf job.order, job.document | ||
end | ||
|
||
def destroy | ||
job = PrinterJob.find(params[:id]) | ||
job.finish! current_user | ||
redirect_to printer_jobs_path, notice: t('.notice') | ||
rescue => error | ||
redirect_to printer_jobs_path, t('errors.general_msg', msg: error.message) | ||
end | ||
end |
Oops, something went wrong.