Upgrade to Rails 5.1
and fixed wiki versioning test Rails 5.1 last steps crabgrass_media from rubygems remove controller test for task sorting - not possible to test xhr request without route in new parameter syntax remove new framework defaults initializer file Final step of upgrade to rails 5.1 we only test on stretch, because we need ruby >= 2.2.2 update dependencies by running bundle update and fix gallery_image_controller_test Also write db/schema.rb with rails 5
This commit is contained in:
parent
e853c2e9d9
commit
03a80251b5
|
@ -22,30 +22,6 @@ bundle_audit:
|
|||
bundle-audit update
|
||||
bundle-audit check
|
||||
|
||||
test:
|
||||
stage: test
|
||||
services:
|
||||
- mysql:5.5
|
||||
script: |
|
||||
bundle exec rake create_a_secret
|
||||
cp config/database.yml.example config/database.yml
|
||||
bundle exec rake db:create
|
||||
bundle exec rake db:schema:load
|
||||
bundle exec rake cg:test:update_fixtures
|
||||
bundle exec rake db:test:prepare
|
||||
RAILS_ENV=test bundle exec rake db:fixtures:load
|
||||
RAILS_ENV=test bundle exec rake ts:index ts:start
|
||||
bundle exec rails test
|
||||
bundle exec rails test extensions/pages
|
||||
bundle exec rake cg:cleanup:all # test cleanup tasks
|
||||
artifacts:
|
||||
when: on_failure
|
||||
paths:
|
||||
- log/test.log
|
||||
- tmp/*.log
|
||||
- tmp/*.html
|
||||
- tmp/*.png
|
||||
|
||||
test_stretch:
|
||||
image: 0xacab.org:4567/riseuplabs/docker/crabgrass:stretch_amd64
|
||||
stage: test
|
||||
|
|
6
Gemfile
6
Gemfile
|
@ -11,8 +11,7 @@ end
|
|||
##
|
||||
|
||||
# Rails is the framework we use.
|
||||
#gem 'rails', '~> 5.1.6'
|
||||
gem 'rails', '~> 5.0.7'
|
||||
gem 'rails', '~> 5.1.6'
|
||||
|
||||
# Security updates
|
||||
# https://github.com/sparklemotion/nokogiri/issues/1785
|
||||
|
@ -144,8 +143,7 @@ gem 'greencloth', require: 'greencloth',
|
|||
|
||||
# media upload post processing has it's own repo
|
||||
# version is rather strict for now as api may still change.
|
||||
gem 'crabgrass_media', '~> 0.3.0', require: 'media',
|
||||
path: 'vendor/gems/crabgrass-media'
|
||||
gem 'crabgrass_media', '~> 0.3.0', require: 'media'
|
||||
|
||||
##
|
||||
## not required, but a really good idea
|
||||
|
|
204
Gemfile.lock
204
Gemfile.lock
|
@ -6,13 +6,6 @@ GIT
|
|||
prototype-rails (4.1.3)
|
||||
rails (>= 4.2)
|
||||
|
||||
PATH
|
||||
remote: vendor/gems/crabgrass-media
|
||||
specs:
|
||||
crabgrass_media (0.3.0)
|
||||
activesupport (~> 5.0)
|
||||
mime-types (~> 3.1)
|
||||
|
||||
PATH
|
||||
remote: vendor/gems/riseuplabs-greencloth-0.1
|
||||
specs:
|
||||
|
@ -24,89 +17,94 @@ GEM
|
|||
specs:
|
||||
RedCloth (4.3.2)
|
||||
aasm (3.4.0)
|
||||
actioncable (5.0.7.2)
|
||||
actionpack (= 5.0.7.2)
|
||||
nio4r (>= 1.2, < 3.0)
|
||||
actioncable (5.1.7)
|
||||
actionpack (= 5.1.7)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (~> 0.6.1)
|
||||
actionmailer (5.0.7.2)
|
||||
actionpack (= 5.0.7.2)
|
||||
actionview (= 5.0.7.2)
|
||||
activejob (= 5.0.7.2)
|
||||
actionmailer (5.1.7)
|
||||
actionpack (= 5.1.7)
|
||||
actionview (= 5.1.7)
|
||||
activejob (= 5.1.7)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.0.7.2)
|
||||
actionview (= 5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
actionpack (5.1.7)
|
||||
actionview (= 5.1.7)
|
||||
activesupport (= 5.1.7)
|
||||
rack (~> 2.0)
|
||||
rack-test (~> 0.6.3)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionpack-page_caching (1.1.0)
|
||||
actionpack-page_caching (1.1.1)
|
||||
actionpack (>= 4.0.0, < 6)
|
||||
actionview (5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
actionview (5.1.7)
|
||||
activesupport (= 5.1.7)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
activejob (5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
activejob (5.1.7)
|
||||
activesupport (= 5.1.7)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
activerecord (5.0.7.2)
|
||||
activemodel (= 5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
arel (~> 7.0)
|
||||
activesupport (5.0.7.2)
|
||||
activemodel (5.1.7)
|
||||
activesupport (= 5.1.7)
|
||||
activerecord (5.1.7)
|
||||
activemodel (= 5.1.7)
|
||||
activesupport (= 5.1.7)
|
||||
arel (~> 8.0)
|
||||
activesupport (5.1.7)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
acts-as-taggable-on (6.0.0)
|
||||
activerecord (~> 5.0)
|
||||
acts_as_list (0.9.1)
|
||||
acts_as_list (0.9.19)
|
||||
activerecord (>= 3.0)
|
||||
addressable (2.5.0)
|
||||
public_suffix (~> 2.0, >= 2.0.2)
|
||||
arel (7.1.4)
|
||||
bcrypt (3.1.11)
|
||||
addressable (2.6.0)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
arel (8.0.0)
|
||||
bcrypt (3.1.12)
|
||||
builder (3.2.3)
|
||||
bundler-audit (0.6.1)
|
||||
bundler (>= 1.2.0, < 3)
|
||||
thor (~> 0.18)
|
||||
byebug (9.0.6)
|
||||
capybara (2.12.0)
|
||||
byebug (11.0.1)
|
||||
capybara (3.15.1)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
rack (>= 1.6.0)
|
||||
rack-test (>= 0.6.3)
|
||||
regexp_parser (~> 1.2)
|
||||
xpath (~> 3.2)
|
||||
chronic (0.10.2)
|
||||
cliver (0.3.2)
|
||||
concurrent-ruby (1.1.5)
|
||||
crabgrass_media (0.3.0)
|
||||
activesupport (~> 5.0)
|
||||
mime-types (~> 3.1)
|
||||
crass (1.0.4)
|
||||
daemons (1.2.4)
|
||||
delayed_job (4.1.2)
|
||||
activesupport (>= 3.0, < 5.1)
|
||||
delayed_job_active_record (4.1.1)
|
||||
activerecord (>= 3.0, < 5.1)
|
||||
daemons (1.3.1)
|
||||
delayed_job (4.1.5)
|
||||
activesupport (>= 3.0, < 5.3)
|
||||
delayed_job_active_record (4.1.3)
|
||||
activerecord (>= 3.0, < 5.3)
|
||||
delayed_job (>= 3.0, < 5)
|
||||
docile (1.1.5)
|
||||
docile (1.3.1)
|
||||
erubi (1.8.0)
|
||||
erubis (2.7.0)
|
||||
execjs (2.7.0)
|
||||
factory_bot (4.8.2)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_bot_rails (4.8.2)
|
||||
factory_bot (~> 4.8.2)
|
||||
railties (>= 3.0.0)
|
||||
factory_bot (5.0.2)
|
||||
activesupport (>= 4.2.0)
|
||||
factory_bot_rails (5.0.2)
|
||||
factory_bot (~> 5.0.2)
|
||||
railties (>= 4.2.0)
|
||||
faker (1.0.1)
|
||||
i18n (~> 0.4)
|
||||
ffi (1.9.25)
|
||||
ffi (1.10.0)
|
||||
globalid (0.4.2)
|
||||
activesupport (>= 4.2.0)
|
||||
gpgme (2.0.16)
|
||||
gpgme (2.0.18)
|
||||
mini_portile2 (~> 2.3)
|
||||
haml (5.0.4)
|
||||
temple (>= 0.8.0)
|
||||
|
@ -122,14 +120,14 @@ GEM
|
|||
haml (>= 4.0, < 6)
|
||||
nokogiri (>= 1.6.0)
|
||||
ruby_parser (~> 3.5)
|
||||
http_accept_language (2.1.0)
|
||||
http_accept_language (2.1.1)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
innertube (1.1.0)
|
||||
joiner (0.3.4)
|
||||
activerecord (>= 4.1.0)
|
||||
json (1.8.6)
|
||||
libv8 (3.16.14.17)
|
||||
libv8 (3.16.14.19)
|
||||
loofah (2.2.3)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
|
@ -140,9 +138,9 @@ GEM
|
|||
mail (~> 2.5, >= 2.5.3)
|
||||
method_source (0.9.2)
|
||||
middleware (0.1.0)
|
||||
mime-types (3.1)
|
||||
mime-types (3.2.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0521)
|
||||
mime-types-data (3.2019.0331)
|
||||
mini_mime (1.0.1)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.10.3)
|
||||
|
@ -152,27 +150,27 @@ GEM
|
|||
mini_portile2 (~> 2.3.0)
|
||||
phantomjs-binaries (2.1.1.1)
|
||||
sys-uname (= 0.9.0)
|
||||
poltergeist (1.13.0)
|
||||
capybara (~> 2.1)
|
||||
poltergeist (1.18.1)
|
||||
capybara (>= 2.1, < 4)
|
||||
cliver (~> 0.3.1)
|
||||
websocket-driver (>= 0.2.0)
|
||||
public_suffix (2.0.5)
|
||||
public_suffix (3.0.3)
|
||||
pundit (1.1.0)
|
||||
activesupport (>= 3.0.0)
|
||||
rack (2.0.7)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (5.0.7.2)
|
||||
actioncable (= 5.0.7.2)
|
||||
actionmailer (= 5.0.7.2)
|
||||
actionpack (= 5.0.7.2)
|
||||
actionview (= 5.0.7.2)
|
||||
activejob (= 5.0.7.2)
|
||||
activemodel (= 5.0.7.2)
|
||||
activerecord (= 5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.1.7)
|
||||
actioncable (= 5.1.7)
|
||||
actionmailer (= 5.1.7)
|
||||
actionpack (= 5.1.7)
|
||||
actionview (= 5.1.7)
|
||||
activejob (= 5.1.7)
|
||||
activemodel (= 5.1.7)
|
||||
activerecord (= 5.1.7)
|
||||
activesupport (= 5.1.7)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 5.0.7.2)
|
||||
railties (= 5.1.7)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.4)
|
||||
actionpack (>= 5.0.1.x)
|
||||
|
@ -183,34 +181,41 @@ GEM
|
|||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.0.4)
|
||||
loofah (~> 2.2, >= 2.2.2)
|
||||
railties (5.0.7.2)
|
||||
actionpack (= 5.0.7.2)
|
||||
activesupport (= 5.0.7.2)
|
||||
railties (5.1.7)
|
||||
actionpack (= 5.1.7)
|
||||
activesupport (= 5.1.7)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rake (10.5.0)
|
||||
rdoc (4.3.0)
|
||||
rb-fsevent (0.10.3)
|
||||
rb-inotify (0.10.0)
|
||||
ffi (~> 1.0)
|
||||
rdoc (6.1.1)
|
||||
record_tag_helper (1.0.0)
|
||||
actionview (~> 5.x)
|
||||
ref (2.0.0)
|
||||
riddle (2.2.2)
|
||||
regexp_parser (1.4.0)
|
||||
riddle (2.3.2)
|
||||
ruby_parser (3.13.1)
|
||||
sexp_processor (~> 4.9)
|
||||
rubyzip (1.2.2)
|
||||
sass (3.4.23)
|
||||
sdoc (0.4.2)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
rdoc (~> 4.0)
|
||||
sass (3.7.4)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
sdoc (1.0.0)
|
||||
rdoc (>= 5.0)
|
||||
secure_headers (4.0.2)
|
||||
useragent (>= 0.15.0)
|
||||
sexp_processor (4.12.0)
|
||||
simplecov (0.14.1)
|
||||
docile (~> 1.1.0)
|
||||
simplecov (0.16.1)
|
||||
docile (~> 1.1)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.1)
|
||||
spring (2.0.1)
|
||||
simplecov-html (0.10.2)
|
||||
spring (2.0.2)
|
||||
activesupport (>= 4.2)
|
||||
sprockets (3.7.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
|
@ -219,7 +224,7 @@ GEM
|
|||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sqlite3 (1.3.13)
|
||||
sqlite3 (1.4.0)
|
||||
sys-uname (0.9.0)
|
||||
ffi (>= 1.0.0)
|
||||
temple (0.8.1)
|
||||
|
@ -236,26 +241,27 @@ GEM
|
|||
thor (0.20.3)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.9)
|
||||
ts-delayed-delta (2.0.2)
|
||||
ts-delayed-delta (2.1.0)
|
||||
activerecord (>= 2.0)
|
||||
delayed_job
|
||||
thinking-sphinx (>= 1.5.0)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (3.0.4)
|
||||
uglifier (4.1.20)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
useragent (0.16.9)
|
||||
useragent (0.16.10)
|
||||
utf8-cleaner (0.2.5)
|
||||
activesupport
|
||||
validates_email_format_of (1.6.3)
|
||||
i18n
|
||||
websocket-driver (0.6.5)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.2)
|
||||
whenever (0.9.7)
|
||||
websocket-extensions (0.1.3)
|
||||
whenever (0.11.0)
|
||||
chronic (>= 0.6.3)
|
||||
will_paginate (3.1.5)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
will_paginate (3.1.7)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zip-zip (0.3)
|
||||
rubyzip (>= 1.0.0)
|
||||
|
||||
|
@ -272,7 +278,7 @@ DEPENDENCIES
|
|||
bundler-audit
|
||||
byebug
|
||||
capybara
|
||||
crabgrass_media (~> 0.3.0)!
|
||||
crabgrass_media (~> 0.3.0)
|
||||
daemons
|
||||
delayed_job_active_record (~> 4.0)
|
||||
factory_bot_rails
|
||||
|
@ -293,7 +299,7 @@ DEPENDENCIES
|
|||
poltergeist (~> 1.5)
|
||||
prototype-rails!
|
||||
pundit (~> 1.1)
|
||||
rails (~> 5.0.7)
|
||||
rails (~> 5.1.6)
|
||||
rails-controller-testing
|
||||
rake (~> 10.0)
|
||||
record_tag_helper (~> 1.0)
|
||||
|
|
|
@ -39,8 +39,6 @@ class Page::CreateController < ApplicationController
|
|||
redirect_to page_url(@page)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# if the page controller is called by our custom DispatchController,
|
||||
# objects which have already been loaded will be passed to the tool
|
||||
# via this initialize method.
|
||||
|
@ -51,6 +49,8 @@ class Page::CreateController < ApplicationController
|
|||
@page = seed[:page] # the page object, if already fetched
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# before filters
|
||||
#
|
||||
|
|
|
@ -80,7 +80,7 @@ class Asset < ApplicationRecord
|
|||
# This is included here because Asset may take new attachment file data, but
|
||||
# Asset::Version and Thumbnail don't need to.
|
||||
include Asset::Upload
|
||||
validates_presence_of :filename, unless: 'new_record?'
|
||||
validates_presence_of :filename, unless: :new_record?
|
||||
|
||||
##
|
||||
## ACCESS
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class AddIndexToPageHistories < ActiveRecord::Migration
|
||||
class AddIndexToPageHistories < ActiveRecord::Migration[4.2]
|
||||
def change
|
||||
add_index "page_histories", ["notification_digest_sent_at"]
|
||||
end
|
||||
|
|
1064
db/schema.rb
1064
db/schema.rb
File diff suppressed because it is too large
Load Diff
|
@ -23,7 +23,7 @@ class GalleryImageControllerTest < ActionController::TestCase
|
|||
def test_show_not_found
|
||||
login_as :blue
|
||||
assert @asset.id, 'image should not be nil'
|
||||
xhr :get, :show, id: 111, page_id: @gallery.id
|
||||
get :show, params: { id: 111, page_id: @gallery.id }, xhr: true
|
||||
assert_not assigns(:showing)
|
||||
assert_response 404
|
||||
end
|
||||
|
|
|
@ -9,19 +9,6 @@ class TasksControllerTest < ActionController::TestCase
|
|||
login_as @user
|
||||
end
|
||||
|
||||
def test_sort
|
||||
assert_equal 1, Task.find(1).position
|
||||
assert_equal 2, Task.find(2).position
|
||||
assert_equal 3, Task.find(3).position
|
||||
|
||||
post :sort, params: { page_id: @page.id, sort_list_pending: ["3", "2", "1"] }, xhr: true
|
||||
assert_response :success
|
||||
|
||||
assert_equal 3, Task.find(1).position
|
||||
assert_equal 2, Task.find(2).position
|
||||
assert_equal 1, Task.find(3).position
|
||||
end
|
||||
|
||||
def test_create_task
|
||||
assert_difference '@page.tasks.count' do
|
||||
post :create, params: { page_id: @page.id, task: { name: "new task", user_ids: ["5"], description: "new task description" } }, xhr: true
|
||||
|
|
|
@ -39,7 +39,7 @@ class Me::PostsControllerTest < ActionController::TestCase
|
|||
post = Message.send from: me, to: you, body: 'test message'
|
||||
login_as me
|
||||
assert_difference 'Post.count', -1 do
|
||||
delete :destroy, discussion_id: you.login, id: post
|
||||
delete :destroy, params: { discussion_id: you.login, id: post }
|
||||
assert_response :redirect
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,11 +5,12 @@ class Wiki::VersionsControllerTest < ActionController::TestCase
|
|||
@user = FactoryBot.create(:user)
|
||||
@group = FactoryBot.create(:group)
|
||||
@group.add_user!(@user)
|
||||
@wiki = @group.profiles.public.create_wiki body: 'test'
|
||||
@wiki.body = @original_body = 'original wiki body'
|
||||
@wiki = @group.profiles.public.create_wiki body: 'original wiki body'
|
||||
@wiki.updated_at = 1.day.ago # force an older timestamp, so that
|
||||
# changing the wiki will create a new version.
|
||||
@wiki.save
|
||||
@wiki.body = 'updated wiki body'
|
||||
@wiki.save
|
||||
@version = @wiki.versions.last
|
||||
login_as @user
|
||||
end
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
.bundle/
|
||||
log/*.log
|
||||
pkg/
|
||||
test/dummy/db/*.sqlite3
|
||||
test/dummy/db/*.sqlite3-journal
|
||||
test/dummy/log/*.log
|
||||
test/dummy/tmp/
|
||||
test/dummy/.sass-cache
|
|
@ -1,29 +0,0 @@
|
|||
image: 0xacab.org:4567/riseuplabs/docker/crabgrass:stretch_amd64
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- vendor/ruby
|
||||
|
||||
before_script:
|
||||
- ruby -v
|
||||
- which ruby
|
||||
- bundle --version
|
||||
- libreoffice --version
|
||||
- bundle install -j $(nproc) --path vendor "${FLAGS[@]}"
|
||||
|
||||
test_stretch:
|
||||
stage: test
|
||||
script: |
|
||||
bundle exec rake
|
||||
artifacts:
|
||||
when: on_failure
|
||||
paths:
|
||||
- log/*.log
|
||||
|
||||
bundle_audit:
|
||||
stage: test
|
||||
allow_failure: true
|
||||
script: |
|
||||
sudo gem install bundler-audit
|
||||
bundle-audit update
|
||||
bundle-audit check
|
|
@ -1,20 +0,0 @@
|
|||
language: ruby
|
||||
sudo: :required
|
||||
rvm:
|
||||
- 1.9.3
|
||||
- 2.1.5
|
||||
matrix:
|
||||
fast_finish: true
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- graphicsmagick
|
||||
- inkscape
|
||||
before_install:
|
||||
- sudo apt-get install libreoffice-writer
|
||||
- bundle --version
|
||||
- libreoffice --version
|
||||
notifications:
|
||||
email: false
|
||||
after_script:
|
||||
- "cat log/*.log" # printing all logs to investigate issues on CI
|
|
@ -1,34 +0,0 @@
|
|||
<a name="0.2.0"></a>
|
||||
### 0.2.0 (2017-02-23)
|
||||
|
||||
#### Upgrade
|
||||
|
||||
* require rails 4.2
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* fix all warnings from gem build ([9add68a](/../commit/9add68a))
|
||||
* warnings during test run ([245564f](/../commit/245564f))
|
||||
* duplicate keys in mime type hash ([131cb9b](/../commit/131cb9b))
|
||||
|
||||
|
||||
<a name="0.1.1"></a>
|
||||
### 0.1.1 (2017-02-24)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* prevent raising ENOENT on failure ([dfb0baf](/../commit/dfb0baf))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.0"></a>
|
||||
### 0.1.0 (2017-02-24)
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* use new class names for crabgrass 0.7 ([909295f](/../commit/909295f))
|
||||
|
||||
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
# Declare your gem's dependencies in media.gemspec.
|
||||
# Bundler will treat runtime dependencies like base dependencies, and
|
||||
# development dependencies will be added by default to the :development group.
|
||||
gemspec
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
crabgrass_media (0.3.0)
|
||||
activesupport (~> 5.0)
|
||||
mime-types (~> 3.1)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (5.1.4)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
concurrent-ruby (1.0.5)
|
||||
conventional-changelog (1.2.2)
|
||||
i18n (0.9.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
mime-types (3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0521)
|
||||
minitest (5.11.1)
|
||||
rake (12.0.0)
|
||||
thread_safe (0.3.6)
|
||||
tzinfo (1.2.4)
|
||||
thread_safe (~> 0.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
conventional-changelog (~> 1.2)
|
||||
crabgrass_media!
|
||||
minitest (~> 5.10)
|
||||
rake (~> 12.0)
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.0
|
|
@ -1,22 +0,0 @@
|
|||
Crabgrass::Media is the Media engine of Crabgrass.
|
||||
|
||||
Crabgrass is a web application designed for activist groups to be better able to collaborate online. Mostly, it is a glorified wiki with fine-grain control over access rights.
|
||||
|
||||
Crabgrass::Media is a rails engine to do media transformations.
|
||||
|
||||
You can add new media transformations by subclassing Transmogrifier.
|
||||
|
||||
Example usage:
|
||||
|
||||
```ruby
|
||||
transmog = Media.transmogrifier(:input_file => 'myfile.odt', :output_file => 'myfile.jpg')
|
||||
status = transmog.run do |progress|
|
||||
puts progress
|
||||
end
|
||||
```
|
||||
|
||||
Tests require the 'file' utility to be installed to determine the file
|
||||
type of the created files.
|
||||
|
||||
Crabgrass and Crabgrass::Media are based on Ruby on Rails and MySQL.
|
||||
They are released under the AGPL license, version 3.
|
|
@ -1,34 +0,0 @@
|
|||
begin
|
||||
require 'bundler/setup'
|
||||
rescue LoadError
|
||||
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
||||
end
|
||||
|
||||
require 'rdoc/task'
|
||||
|
||||
RDoc::Task.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'Media'
|
||||
rdoc.options << '--line-numbers'
|
||||
rdoc.rdoc_files.include('README.rdoc')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
|
@ -1,9 +0,0 @@
|
|||
unless defined?(Media::TMP_PATH)
|
||||
if defined?(Rails)
|
||||
Media::TMP_PATH = File.join(Rails.root, 'tmp', 'media')
|
||||
else
|
||||
Media::TMP_PATH = File.join('', 'tmp', 'media')
|
||||
end
|
||||
end
|
||||
|
||||
FileUtils.mkdir_p(Media::TempFile.tempfile_path) unless File.exist?(Media::TempFile.tempfile_path)
|
|
@ -1,33 +0,0 @@
|
|||
$:.push File.expand_path("../lib", __FILE__)
|
||||
|
||||
# Maintain your gem's version:
|
||||
require "media/version"
|
||||
|
||||
# Describe your gem and declare its dependencies:
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "crabgrass_media"
|
||||
s.version = Media::VERSION
|
||||
s.authors = ["Azul"]
|
||||
s.email = ["azul@riseup.net"]
|
||||
s.homepage = "https://github.com/riseuplabs/crabgrass-media"
|
||||
s.summary = "Media processing for the Crabgrass social wiki"
|
||||
s.description = <<-EOD
|
||||
Crabgrass::Media is the Media engine of Crabgrass.
|
||||
|
||||
Crabgrass is a web application designed for activist groups to be better able to collaborate online. Mostly, it is a glorified wiki with fine-grain control over access rights.
|
||||
|
||||
Crabgrass::Media is a rails engine to do media transformations.
|
||||
EOD
|
||||
s.license = "AGPL-3.0"
|
||||
|
||||
s.files = Dir["{config,lib}/**/*", "Rakefile", "README.md"]
|
||||
s.test_files = Dir["test/**/*"]
|
||||
|
||||
s.add_dependency 'activesupport', '~> 5.0'
|
||||
s.add_dependency 'mime-types', '~> 3.1'
|
||||
|
||||
s.add_development_dependency 'rake', '~> 12.0'
|
||||
s.add_development_dependency 'minitest', ' ~> 5.10'
|
||||
s.add_development_dependency 'conventional-changelog', '~> 1.2'
|
||||
|
||||
end
|
|
@ -1,62 +0,0 @@
|
|||
require 'media/mime_type'
|
||||
require 'media/temp_file'
|
||||
require 'media/transmogrifier'
|
||||
require 'media/transmogrifiers/graphicsmagick'
|
||||
require 'media/transmogrifiers/inkscape'
|
||||
require 'media/transmogrifiers/libremagick'
|
||||
require 'media/transmogrifiers/libreoffice'
|
||||
|
||||
module Media
|
||||
|
||||
#
|
||||
# creates a new instance of transmogrifier suitable for turning
|
||||
# input into output.
|
||||
#
|
||||
def self.transmogrifier(options)
|
||||
if options[:input_type]
|
||||
input_type = Media::MimeType.simple options[:input_type]
|
||||
elsif options[:input_file]
|
||||
input_type = Media::MimeType.mime_type_from_extension options[:input_file]
|
||||
else
|
||||
raise ArgumentError.new
|
||||
end
|
||||
|
||||
if options[:output_type]
|
||||
output_type = Media::MimeType.simple options[:output_type]
|
||||
elsif options[:output_file]
|
||||
output_type = Media::MimeType.mime_type_from_extension options[:output_file]
|
||||
else
|
||||
raise ArgumentError.new
|
||||
end
|
||||
|
||||
unless input_type and output_type
|
||||
raise ArgumentError.new("Both input and output types are required (given %s -> %s)." % [input_type||'nil', output_type||'nil'])
|
||||
end
|
||||
|
||||
transmog_class = Transmogrifier.find_class(input_type, output_type)
|
||||
if transmog_class
|
||||
transmog_class.new(options)
|
||||
end
|
||||
end
|
||||
|
||||
def self.may_produce?(src_type, dst_type)
|
||||
Transmogrifier.find_class(src_type, dst_type)
|
||||
end
|
||||
|
||||
#
|
||||
# special graphicsmagick hooks.
|
||||
# we use graphicsmagick in order to parse the dimensions of image files.
|
||||
#
|
||||
|
||||
def self.has_dimensions?(mime_type)
|
||||
Media::GraphicsMagickTransmogrifier.available? &&
|
||||
Media::GraphicsMagickTransmogrifier.converts_from?(mime_type)
|
||||
end
|
||||
|
||||
def self.dimensions(filepath)
|
||||
return unless Media::GraphicsMagickTransmogrifier.available?
|
||||
Media::GraphicsMagickTransmogrifier.dimensions filepath
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,257 +0,0 @@
|
|||
require 'mime/types'
|
||||
|
||||
module Media
|
||||
module MimeType
|
||||
|
||||
def self.mime_group(mime_type)
|
||||
mime_type.sub(/\/.*$/,'/') if mime_type # remove everything after /
|
||||
end
|
||||
|
||||
def self.simple(mime_type)
|
||||
mime_type.to_s.sub(/\/x\-/,'/') if mime_type # remove x-
|
||||
end
|
||||
|
||||
def self.lookup(mime_type,field)
|
||||
(MIME_TYPES[simple(mime_type)]||[])[field]
|
||||
end
|
||||
|
||||
# def self.group_from_mime_type(mime_type)
|
||||
# lookup(mime_type,GROUP) || lookup(mime_group(mime_type),GROUP)
|
||||
# end
|
||||
|
||||
def self.icon_for(mtype)
|
||||
lookup(mtype,ICON) || lookup(mime_group(mtype),ICON) || lookup('default',ICON)
|
||||
end
|
||||
|
||||
def self.asset_class_from_mime_type(mime_type)
|
||||
asset_symbol_from_mime_type(mime_type).to_s.classify
|
||||
end
|
||||
|
||||
def self.asset_symbol_from_mime_type(mime_type)
|
||||
lookup(mime_type,ASSET_CLASS) || lookup(mime_group(mime_type),ASSET_CLASS) || lookup('default',ASSET_CLASS)
|
||||
end
|
||||
|
||||
def self.extension_from_mime_type(mime_type)
|
||||
lookup(mime_type,EXT)
|
||||
end
|
||||
|
||||
def self.mime_type_from_extension(ext)
|
||||
ext = ext.to_s
|
||||
ext = File.extname(ext).gsub('.','') if ext =~ /\./
|
||||
mimetype = EXTENSIONS[ext]
|
||||
if defined?(MIME::Types)
|
||||
unless MIME::Types.type_for('.'+ext).empty?
|
||||
mimetype ||= MIME::Types.type_for('.'+ext).first.content_type
|
||||
end
|
||||
end
|
||||
mimetype ||= 'application/octet-stream'
|
||||
return mimetype
|
||||
end
|
||||
|
||||
#
|
||||
# perhaps use http://code.google.com/p/mimetype-fu/
|
||||
# for all this?
|
||||
def self.type_for(filename)
|
||||
self.mime_type_from_extension(filename)
|
||||
# todo: add type_from_file_command if ext doesn't pan out.
|
||||
end
|
||||
|
||||
#def type_from_file_command(filename)
|
||||
# # On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
|
||||
# type = (filename.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
|
||||
# mime_type = (Paperclip.run("file", "-b --mime-type :file", :file => filename).split(':').last.strip rescue "application/x-#{type}")
|
||||
# mime_type = "application/x-#{type}" if mime_type.match(/\(.*?\)/)
|
||||
# mime_type
|
||||
#end
|
||||
|
||||
def self.description_from_mime_type(mime_type)
|
||||
lookup(mime_type,DESCRIPTION) || lookup(mime_group(mime_type),DESCRIPTION) || lookup('default',DESCRIPTION)
|
||||
end
|
||||
|
||||
def self.compatible_types?(type1, type2)
|
||||
(TYPE_ALIASES[type1] || []).include?(type2)
|
||||
end
|
||||
|
||||
EXT = 0; ICON = 1; ASSET_CLASS = 2; DESCRIPTION = 3;
|
||||
MIME_TYPES = {
|
||||
# mime_type => [file_extension, icon, asset_class, description]
|
||||
'default' => [nil,'default',:asset,'Unknown'],
|
||||
|
||||
'text/' => [:txt,:html,'asset/text', 'Text'],
|
||||
'text/plain' => [:txt,:html,'asset/text', 'Text'],
|
||||
'text/html' => [:html,:html,'asset/text', 'Webpage'],
|
||||
'application/rtf' => [:rtf,:rtf,'asset/text', 'Rich Text'],
|
||||
'text/rtf' => [:rtf,:rtf,'asset/text', 'Rich Text'],
|
||||
'text/sgml' => [:sgml,:xml,nil,'XML'],
|
||||
'text/xml' => [:xml,:xml,nil,'XML'],
|
||||
'text/csv' => [:csv,:spreadsheet,'asset/doc', 'Comma Separated Values'],
|
||||
'text/comma-separated-values' => [:csv,:spreadsheet,'asset/doc', 'Comma Separated Values'],
|
||||
|
||||
'application/pdf' => [:pdf,:pdf,'asset/image', 'Portable Document Format'],
|
||||
'application/bzpdf' => [:pdf,:pdf,'asset/image', 'Portable Document Format'],
|
||||
'application/gzpdf' => [:pdf,:pdf,'asset/image', 'Portable Document Format'],
|
||||
'application/postscript' => [:ps,:pdf,'asset/image','Postscript'],
|
||||
|
||||
'text/spreadsheet' => [:txt,:spreadsheet,'asset/doc','Spreadsheet'],
|
||||
'application/gnumeric' => [:gnumeric,:spreadsheet,'asset/doc','Gnumeric'],
|
||||
'application/kspread' => [:kspread,:spreadsheet,'asset/doc','KSpread'],
|
||||
|
||||
'application/scribus' => [:scribus,:doc,nil,'Scribus'],
|
||||
'application/abiword' => [:abw,:doc,'asset/doc','Abiword'],
|
||||
'application/kword' => [:kwd,:doc,'asset/doc','KWord'],
|
||||
|
||||
|
||||
'application/msword' => [:doc,:msword,'asset/text','MS Word'],
|
||||
'application/mswrite' => [:doc,:msword,'asset/text','MS Write'],
|
||||
'application/powerpoint' => [:ppt,:mspowerpoint,'asset/doc','MS Powerpoint'],
|
||||
'application/excel' => [:xls,:msexcel,'asset/spreadsheet','MS Excel'],
|
||||
'application/access' => [nil, :msaccess, 'asset/doc','MS Access'],
|
||||
'application/vnd.ms-msword' => [:doc,:msword,'asset/text','MS Word'],
|
||||
'application/vnd.ms-mswrite' => [:doc,:msword,'asset/text','MS Write'],
|
||||
'application/vnd.ms-powerpoint' => [:ppt,:mspowerpoint,'asset/doc','MS Powerpoint'],
|
||||
'application/vnd.ms-excel' => [:xls,:msexcel,'asset/spreadsheet','MS Excel'],
|
||||
'application/vnd.ms-access' => [nil, :msaccess, 'asset/doc','MS Access'],
|
||||
'application/msword-template' => [:dot,:msword,'asset/text','MS Word Template'],
|
||||
'application/excel-template' => [:xlt,:msexcel,'asset/spreadsheet','MS Excel Template'],
|
||||
'application/powerpoint-template' => [:pot,:mspowerpoint,'asset/doc','MS Powerpoint Template'],
|
||||
|
||||
# 'application/vnd.openxmlformats-officedocument.presentationml.presentation' =>
|
||||
# [:pptx, :mspowerpoint,'asset/doc','MS Powerpoint'],
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation' =>
|
||||
[:pptm, :mspowerpoint,'asset/doc','MS Powerpoint'],
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.template' =>
|
||||
[:potx,:mspowerpoint,'asset/doc','MS Powerpoint Template'],
|
||||
|
||||
# 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' =>
|
||||
# [:docm,:msword,'asset/text','MS Word'],
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' =>
|
||||
[:docx,:msword,'asset/text','MS Word'],
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' =>
|
||||
[:dotx,:msword,'asset/text','MS Word Template'],
|
||||
|
||||
# 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' =>
|
||||
# [:xlsm,:msexcel,'asset/spreadsheet','MS Excel'],
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' =>
|
||||
[:xlsx,:msexcel,'asset/spreadsheet','MS Excel'],
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' =>
|
||||
[:xltx,:msexcel,'asset/spreadsheet','MS Excel Template'],
|
||||
|
||||
'application/executable' => [nil,:binary,nil,'Program'],
|
||||
'application/ms-dos-executable' => [nil,:binary,nil,'Program'],
|
||||
'application/octet-stream' => [nil,:binary,nil],
|
||||
|
||||
'application/shellscript' => [:sh,:shell,nil,'Script'],
|
||||
'application/ruby' => [:rb,:ruby,nil,'Script'],
|
||||
|
||||
'application/vnd.oasis.opendocument.spreadsheet' =>
|
||||
[:ods,:oo_spreadsheet,'asset/spreadsheet', 'OpenDocument Spreadsheet'],
|
||||
'application/vnd.oasis.opendocument.formula' =>
|
||||
[nil,:oo_spreadsheet,'asset/spreadsheet', 'OpenDocument Formula'],
|
||||
'application/vnd.oasis.opendocument.chart' =>
|
||||
[nil,:oo_spreadsheet,'asset/spreadsheet', 'OpenDocument Chart'],
|
||||
'application/vnd.oasis.opendocument.image' =>
|
||||
[nil,:oo_graphics, 'asset/doc', 'OpenDocument Image'],
|
||||
'application/vnd.oasis.opendocument.graphics' =>
|
||||
[:odg,:oo_graphics, 'asset/doc', 'OpenDocument Graphics'],
|
||||
'application/vnd.oasis.opendocument.presentation' =>
|
||||
[:odp,:oo_presentation,'asset/doc', 'OpenDocument Presentation'],
|
||||
'application/vnd.oasis.opendocument.database' =>
|
||||
[:odf,:oo_database,'asset/doc', 'OpenDocument Database'],
|
||||
'application/vnd.oasis.opendocument.text-web' =>
|
||||
[:html,:oo_html,'asset/doc', 'OpenDocument Webpage'],
|
||||
'application/vnd.oasis.opendocument.text' =>
|
||||
[:odt,:oo_document,'asset/doc', 'OpenDocument Text'],
|
||||
'application/vnd.oasis.opendocument.text-master' =>
|
||||
[:odm,:oo_document,'asset/doc', 'OpenDocument Master'],
|
||||
|
||||
'application/vnd.oasis.opendocument.presentation-template' =>
|
||||
[:otp,:oo_presentation,'asset/doc', 'OpenDocument Presentation'],
|
||||
'application/vnd.oasis.opendocument.graphics-template' =>
|
||||
[:otg,:oo_graphics,'asset/doc', 'OpenDocument Graphics'],
|
||||
'application/vnd.oasis.opendocument.spreadsheet-template' =>
|
||||
[:ots,:oo_spreadsheet,'asset/spreadsheet', 'OpenDocument Spreadsheet'],
|
||||
'application/vnd.oasis.opendocument.text-template' =>
|
||||
[:ott,:oo_document,'asset/doc', 'OpenDocument Text'],
|
||||
|
||||
'packages/' => [nil,:archive,nil,'Archive'],
|
||||
'multipart/zip' => [:zip,:archive,nil,'Archive'],
|
||||
'multipart/gzip' => [:gzip,:archive,nil,'Archive'],
|
||||
'multipart/tar' => [:tar,:archive,nil,'Archive'],
|
||||
'application/zip' => [:gzip,:archive,nil,'Archive'],
|
||||
'application/gzip' => [:gzip,:archive,nil,'Archive'],
|
||||
'application/rar' => [:rar,:archive,nil,'Archive'],
|
||||
'application/deb' => [:deb,:archive,nil,'Archive'],
|
||||
'application/tar' => [:tar,:archive,nil,'Archive'],
|
||||
'application/stuffit' => [:sit,:archive,nil,'Archive'],
|
||||
'application/compress' => [nil,:archive,nil,'Archive'],
|
||||
'application/zip-compressed' => [:zip,:archive,nil,'Archive'],
|
||||
|
||||
'video/' => [nil,:video,nil,'Video'],
|
||||
|
||||
'audio/' => [nil,:audio,'asset/audio','Audio'],
|
||||
|
||||
'image/' => [nil,:image,'asset/image','Image'],
|
||||
'image/jpeg' => [:jpg,:image,'asset/image', 'JPEG Image'],
|
||||
'image/jpg' => [:jpg,:image,'asset/image', 'JPEG Image'],
|
||||
'image/png' => [:png,:image,'asset/png', 'PNG Image'],
|
||||
'image/gif' => [:png,:image,'asset/gif', 'GIF Image'],
|
||||
|
||||
'image/svg+xml' => [:svg,:vector,'asset/svg','Vector Graphic'],
|
||||
'image/svg+xml-compressed' => [:svg,:vector,'asset/svg','Vector Graphic'],
|
||||
'application/illustrator' => [:ai,:vector,'asset/image','Vector Graphic'],
|
||||
'image/bzeps' => [:bzeps,:vector,'asset/image','Vector Graphic'],
|
||||
'image/eps' => [:eps,:vector,'asset/image','Vector Graphic'],
|
||||
'image/gzeps' => [:gzeps,:vector,'asset/image','Vector Graphic'],
|
||||
|
||||
'application/pgp-encrypted' => [nil,:lock,nil,'Crypt'],
|
||||
'application/pgp-signature' => [nil,:lock,nil,'Crypt'],
|
||||
'application/pgp-keys' => [nil,:lock,nil,'Crypt']
|
||||
}.freeze
|
||||
|
||||
#
|
||||
# This extension mapping is used to force certain mime types.
|
||||
# Usually, firefox does pretty good at reporting the correct mime-type,
|
||||
# but IE always fails (firefox fails on ogg). So, we use the MIME::Types
|
||||
# gem to try to get the correct mime from the extension. Sometimes, however,
|
||||
# even this doesn't work. This able will force certain types when
|
||||
# MIME::Types fails or is ambiguous
|
||||
#
|
||||
EXTENSIONS = {
|
||||
'jpg' => 'image/jpeg',
|
||||
'png' => 'image/png',
|
||||
'txt' => 'text/plain',
|
||||
'flv' => 'video/flv',
|
||||
'ogg' => 'audio/ogg',
|
||||
'oga' => 'audio/ogg',
|
||||
'ogv' => 'video/ogg',
|
||||
'pdf' => 'application/pdf',
|
||||
|
||||
'doc' => 'application/msword',
|
||||
'xsl' => 'application/excel',
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'pptm' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
|
||||
'docm' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'xlsm' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
|
||||
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'odp' => 'application/vnd.oasis.opendocument.presentation',
|
||||
'svg' => 'image/svg+xml',
|
||||
'mod' => 'video/mpeg',
|
||||
|
||||
}.freeze
|
||||
|
||||
#
|
||||
# some types can have multiple names
|
||||
#
|
||||
TYPE_ALIASES = {
|
||||
'image/jpg' => ['image/jpeg'],
|
||||
'image/jpeg' => ['image/jpg']
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,152 +0,0 @@
|
|||
require 'tempfile'
|
||||
require 'fileutils'
|
||||
require 'pathname'
|
||||
|
||||
#
|
||||
# media processing requires a different type of tempfile... because we use command
|
||||
# line tools to process our temp files, these files can't be open and closed by ruby.
|
||||
#
|
||||
# instead, Media::TempFile is used to generate closed files from binary data (for files
|
||||
# to be fed to command line tools), or to generate empty tmp files (for output filenames
|
||||
# to be fed to command line tools).
|
||||
#
|
||||
# We use the Tempfile class for generating these files, but then we always close them
|
||||
# right away. By doing this, we ensure that the temp file will eventually get removed
|
||||
# when the Tempfile gets garbage collected.
|
||||
#
|
||||
|
||||
module Media
|
||||
class TempFile
|
||||
|
||||
def self.tempfile_path
|
||||
::Media::TMP_PATH
|
||||
end
|
||||
|
||||
##
|
||||
## INSTANCE METHODS
|
||||
##
|
||||
|
||||
public
|
||||
|
||||
#
|
||||
# data may be one of:
|
||||
#
|
||||
# - FileUpload object: like the kind returned in multibyte encoded file upload forms.
|
||||
# - Pathname object: then load data from the file pointed to by the pathname.
|
||||
# - IO object: read the contents of the io object, copy to tmp file.
|
||||
# - otherwise, dump the contents of the data to the tmp file.
|
||||
#
|
||||
# if data is empty, we generate an empty one.
|
||||
#
|
||||
def initialize(data, content_type=nil)
|
||||
if data.nil?
|
||||
@tmpfile = TempFile.create_from_content_type(content_type)
|
||||
elsif data.respond_to?(:path)
|
||||
# we are dealing with an uploaded file object
|
||||
@tmpfile = TempFile.create_from_file(data.path, content_type, {mode: :move})
|
||||
elsif data.is_a?(StringIO)
|
||||
data.rewind
|
||||
@tmpfile = TempFile.create_from_data(data.read, content_type)
|
||||
elsif data.instance_of?(Pathname)
|
||||
@tmpfile = TempFile.create_from_file(data.to_s, content_type)
|
||||
else
|
||||
@tmpfile = TempFile.create_from_data(data, content_type)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# like initialize, but if given a block, then it yields the TempFile
|
||||
# and also unlinks the file at the end of the block.
|
||||
#
|
||||
def self.open(data, content_type=nil)
|
||||
tmp = TempFile.new(data, content_type)
|
||||
if block_given?
|
||||
begin
|
||||
yield tmp
|
||||
ensure
|
||||
tmp.clear
|
||||
end
|
||||
nil
|
||||
else
|
||||
tmp
|
||||
end
|
||||
end
|
||||
|
||||
def clear
|
||||
# this is not really needed, because the tmp files are deleted as soon as
|
||||
# @tmpfile gets garbage collected.
|
||||
# @tmpfile.unlink
|
||||
end
|
||||
|
||||
def any?
|
||||
@tmpfile.any?
|
||||
end
|
||||
|
||||
def path
|
||||
@tmpfile.path
|
||||
end
|
||||
|
||||
def to_s
|
||||
@tmpfile.path
|
||||
end
|
||||
|
||||
##
|
||||
## CLASS METHODS
|
||||
##
|
||||
|
||||
private
|
||||
|
||||
#
|
||||
# creates a tempfile filled with the given binary data
|
||||
#
|
||||
def self.create_from_data(data, content_type=nil)
|
||||
tf = new_for_content_type(content_type)
|
||||
tf.binmode
|
||||
tf.write(data)
|
||||
tf.close
|
||||
tf
|
||||
end
|
||||
|
||||
#
|
||||
# create an empty temp file with an extension to match the content_type
|
||||
#
|
||||
def self.create_from_content_type(content_type)
|
||||
tf = new_for_content_type(content_type)
|
||||
tf.close
|
||||
tf
|
||||
end
|
||||
|
||||
#
|
||||
# create a tmp file that is a copy of another file.
|
||||
#
|
||||
def self.create_from_file(filepath, content_type, options = {})
|
||||
tf = new_for_content_type(content_type)
|
||||
tf.close
|
||||
if options[:mode] == :move
|
||||
FileUtils.mv filepath, tf.path
|
||||
else
|
||||
FileUtils.cp filepath, tf.path
|
||||
end
|
||||
tf
|
||||
end
|
||||
|
||||
def self.new_for_content_type(content_type)
|
||||
Tempfile.new content_type_basename(content_type),
|
||||
tempfile_path,
|
||||
mode: 022
|
||||
end
|
||||
|
||||
#
|
||||
# create a filename with a file extension from the content_type
|
||||
#
|
||||
def self.content_type_basename(content_type)
|
||||
if content_type
|
||||
extension = Media::MimeType.extension_from_mime_type(content_type) || :bin
|
||||
['media_temp_file', ".#{extension}"]
|
||||
else
|
||||
'media_temp_file'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,331 +0,0 @@
|
|||
#
|
||||
# A class to transmogrify a media asset from one form to another.
|
||||
#
|
||||
# All such media transformations are handled by instances of this class.
|
||||
#
|
||||
|
||||
require 'fileutils'
|
||||
require 'active_support/core_ext/module/delegation.rb'
|
||||
require 'active_support/core_ext/module/attribute_accessors.rb'
|
||||
require 'logger'
|
||||
|
||||
module Media
|
||||
class Transmogrifier
|
||||
|
||||
# singleton logger, only set via class, access via class and instance
|
||||
mattr_accessor :logger, instance_writer: false do
|
||||
Logger.new(STDERR)
|
||||
end
|
||||
|
||||
mattr_accessor :verbose, instance_writer: false
|
||||
|
||||
class << self
|
||||
delegate :debug, :info, :warning, :error, to: :logger
|
||||
end
|
||||
|
||||
delegate :debug, :info, :warning, :error, to: :logger
|
||||
|
||||
|
||||
attr_accessor :input
|
||||
attr_accessor :input_file
|
||||
attr_accessor :input_type
|
||||
|
||||
attr_accessor :output # maybe some day we return raw output via url?
|
||||
attr_accessor :output_type # desired mime type of the output
|
||||
attr_accessor :output_file # desired file location of the output
|
||||
|
||||
attr_accessor :options
|
||||
|
||||
attr_accessor :command_output # output of last command run
|
||||
|
||||
#
|
||||
# takes a hash of options, some of which are required:
|
||||
#
|
||||
# - :input_file or (:input and :input_type)
|
||||
# - :output_file or :output_type
|
||||
#
|
||||
def initialize(options=nil)
|
||||
options = options.dup
|
||||
self.input = options.delete(:input)
|
||||
self.input_file = options.delete(:input_file)
|
||||
self.input_type = options.delete(:input_type)
|
||||
self.output_file = options.delete(:output_file)
|
||||
self.output_type = options.delete(:output_type)
|
||||
self.options = options
|
||||
|
||||
|
||||
|
||||
if input and input_type.nil?
|
||||
raise ArgumentError.new('input_type required if input specified')
|
||||
elsif input and input_file.nil?
|
||||
self.input_file = Media::TempFile.new(input, input_type)
|
||||
elsif input and input_file
|
||||
raise ArgumentError.new('cannot have both input and input_file')
|
||||
elsif input_file and input_type.nil?
|
||||
self.input_type = Media::MimeType.mime_type_from_extension(input_file)
|
||||
elsif input.nil? and input_file.nil?
|
||||
raise ArgumentError.new('input or input_file is required')
|
||||
end
|
||||
|
||||
if output_file.nil? and output_type.nil?
|
||||
raise ArgumentError.new('output_file or output_type is required')
|
||||
elsif output_file.nil?
|
||||
self.output_file = Media::TempFile.new(nil, output_type)
|
||||
elsif output_type.nil?
|
||||
self.output_type = Media::MimeType.mime_type_from_extension(output_file)
|
||||
end
|
||||
|
||||
debug self.class.name + " converting" +
|
||||
" #{input_file } ( #{input_type} ) to" +
|
||||
" #{output_file} ( #{output_type} )"
|
||||
|
||||
set_temporary_outfile
|
||||
|
||||
end
|
||||
|
||||
def self.inherited(base)
|
||||
add base
|
||||
end
|
||||
|
||||
##
|
||||
## CLASS METHODS
|
||||
##
|
||||
|
||||
def self.list(); @@list ||= {}; end
|
||||
|
||||
# maps mine type to an array of transmogrifiers that
|
||||
# take that type as an imput
|
||||
def self.input_map; @@input_map ||= Hash.new([]); end
|
||||
|
||||
# maps mine type to an array of transmogrifiers that
|
||||
# produce that type as an output
|
||||
def self.output_map; @@output_map ||= Hash.new([]); end
|
||||
|
||||
def self.add(trans)
|
||||
self.list[trans.name] ||= trans
|
||||
end
|
||||
|
||||
#
|
||||
# returns transmogrifier class, if any, that can tranform input_type
|
||||
# into output_type
|
||||
#
|
||||
def self.find_class(input_type, output_type)
|
||||
transmog = list.values.
|
||||
select{|tm| tm.converts_from?(input_type)}.
|
||||
select{|tm| tm.converts_to?(output_type)}.
|
||||
select{|tm| tm.available?}.
|
||||
first
|
||||
return transmog if transmog
|
||||
logger.error 'could not find a transmogrifier for "%s" -> "%s"' %
|
||||
[input_type, output_type]
|
||||
return nil
|
||||
end
|
||||
|
||||
def self.converts_from?(input_type)
|
||||
input_types.include? input_type
|
||||
end
|
||||
|
||||
def self.converts_to?(output_type)
|
||||
output_types.include? output_type
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
#def self.simple_type(mime_type)
|
||||
# mime_type.gsub(/\/x\-/,'/') if mime_type
|
||||
#end
|
||||
|
||||
##
|
||||
## Helpers
|
||||
##
|
||||
|
||||
#
|
||||
# runs a shell command, passing each line that is output, as it is output
|
||||
# to the block.
|
||||
#
|
||||
# returns
|
||||
# * the status of the command, as one of the following symbols:
|
||||
# :success, :failure, :not_found, :error
|
||||
# * the output to STDOUT and STDERR of the command
|
||||
#
|
||||
def self.run_command(*args)
|
||||
|
||||
# run the command
|
||||
cmdstr = command_string(*args)
|
||||
output = ""
|
||||
before = Time.now
|
||||
IO.popen(cmdstr + ' 2>&1', 'r') do |pipe|
|
||||
while line = pipe.gets
|
||||
if block_given?
|
||||
yield(line)
|
||||
end
|
||||
output << line << "\n"
|
||||
end
|
||||
end
|
||||
took = Time.now - before
|
||||
|
||||
# set the status
|
||||
status = case $?.exitstatus
|
||||
when 0 then :success
|
||||
when 1 then :failure
|
||||
when 127 then :not_found
|
||||
else :error
|
||||
end
|
||||
if status == :success
|
||||
log_command cmdstr
|
||||
debug "took #{took} seconds."
|
||||
debug output
|
||||
else
|
||||
msg = ' exited with "%s"' % $?.exitstatus
|
||||
error cmdstr
|
||||
error msg
|
||||
error output if !output.empty?
|
||||
yield(msg) if block_given?
|
||||
end
|
||||
|
||||
return status, output
|
||||
end
|
||||
|
||||
def run_command(*args)
|
||||
status, self.command_output = self.class.run_command(*args)
|
||||
|
||||
# restore the original output_file name
|
||||
unless restore_temporary_outfile
|
||||
msg = 'could not restore temporary outfile'
|
||||
error msg
|
||||
yield(msg) if block_given?
|
||||
status = :failure
|
||||
end
|
||||
return status
|
||||
end
|
||||
|
||||
def self.command_available?(command)
|
||||
command and
|
||||
File.file?(command) and
|
||||
File.executable?(command)
|
||||
end
|
||||
|
||||
##
|
||||
## PROTECTED
|
||||
##
|
||||
|
||||
protected
|
||||
|
||||
def self.log_command(command)
|
||||
debug "COMMAND " + command
|
||||
end
|
||||
|
||||
#
|
||||
# returns a filename with the same base but a new extension
|
||||
#
|
||||
def replace_extension(filename, new_extension)
|
||||
old_extension = (File.extname(filename) || '').to_s
|
||||
new_extension = new_extension.to_s
|
||||
if !old_extension.empty?
|
||||
base = File.basename(filename, old_extension)
|
||||
else
|
||||
base = filename
|
||||
end
|
||||
if new_extension !~ /^\./
|
||||
new_extension = "." + new_extension
|
||||
end
|
||||
if base =~ /\.$/
|
||||
new_extension = new_extension.chomp
|
||||
end
|
||||
"#{base}#{new_extension}"
|
||||
end
|
||||
|
||||
def extension(mime_type)
|
||||
Media::MimeType.extension_from_mime_type(mime_type)
|
||||
end
|
||||
|
||||
#
|
||||
# usage:
|
||||
#
|
||||
# replace_file :from => filea, :to => fileb
|
||||
#
|
||||
def replace_file(args={})
|
||||
from = args[:from].to_s
|
||||
to = args[:to].to_s
|
||||
raise ArgumentError if from.empty? || to.empty?
|
||||
if File.exist?(from)
|
||||
if File.exist?(to)
|
||||
FileUtils.rm(to)
|
||||
end
|
||||
FileUtils.mv(from, to)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
## PRIVATE
|
||||
##
|
||||
|
||||
private
|
||||
|
||||
def self.command_string(*args)
|
||||
args.collect {|arg| shell_escape(arg.to_s)}.join(' ')
|
||||
end
|
||||
|
||||
def self.shell_escape(str)
|
||||
if str.empty?
|
||||
"''"
|
||||
elsif str =~ %r{\A[0-9A-Za-z+_-]+\z}
|
||||
str
|
||||
else
|
||||
result = ''
|
||||
str.scan(/('+)|[^']+/) do
|
||||
if $1
|
||||
result << %q{\'} * $1.length
|
||||
else
|
||||
result << %Q{'#{$&}'}
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# returns true if the file as a suffix that matches the mime_type
|
||||
#
|
||||
def compatible_extension?(file, type)
|
||||
file = file.to_s
|
||||
ext = Media::MimeType.extension_from_mime_type(type)
|
||||
if ext.nil?
|
||||
return true
|
||||
# ^^ if there is no defined extension for this type, then
|
||||
# whatever the file has is fine
|
||||
else
|
||||
file_ext_type = Media::MimeType.mime_type_from_extension(file)
|
||||
return Media::MimeType.compatible_types?(type, file_ext_type)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# ensure that the output_file has the correct suffix
|
||||
# by setting a temporary one if the current one is not good.
|
||||
#
|
||||
def set_temporary_outfile
|
||||
@temporary_outfile = false
|
||||
if !compatible_extension?(output_file, output_type)
|
||||
@temporary_outfile = true
|
||||
@outfile_to_return = output_file
|
||||
self.output_file = Media::TempFile.new(nil, output_type)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# moves the current output_file to match the filename we are
|
||||
# supposed to return (which is stored in @outfile_to_return
|
||||
# by set_temporary_outfile)
|
||||
#
|
||||
def restore_temporary_outfile
|
||||
if @temporary_outfile
|
||||
debug "moving output from #{output_file} to #{@outfile_to_return}"
|
||||
replace_file from: output_file, to: @outfile_to_return
|
||||
self.output_file = @outfile_to_return
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,151 +0,0 @@
|
|||
module Media
|
||||
#
|
||||
# transform image formats using the graphicsmagick command line executable "gm"
|
||||
# requires "apt-get install graphicsmagick"
|
||||
#
|
||||
|
||||
unless defined?(GRAPHICSMAGICK_COMMAND)
|
||||
GRAPHICSMAGICK_COMMAND = `which gm`.chomp
|
||||
end
|
||||
|
||||
#
|
||||
# this is a little bit brittle, but I am not sure how else to do it.
|
||||
#
|
||||
unless defined?(GRAPHICSMAGICK_VERSION)
|
||||
version = `#{GRAPHICSMAGICK_COMMAND} -version | head -1`.strip.sub(/GraphicsMagick ([0-9]+\.[0-9]+\.[0-9]+).*/,'\1').split('.')
|
||||
GRAPHICSMAGICK_VERSION = [version[0].to_i, version[1].to_i, version[2].to_i]
|
||||
end
|
||||
|
||||
class GraphicsMagickTransmogrifier < Media::Transmogrifier
|
||||
|
||||
def self.input_types
|
||||
%w( image/jpeg image/pjpeg image/gif
|
||||
image/png image/x-png image/jpg image/tiff )
|
||||
# application/pdf application/bzpdf application/gzpdf
|
||||
# application/postscript application/xpdf
|
||||
end
|
||||
|
||||
def self.output_types
|
||||
%w( application/pdf image/jpeg image/pjpeg
|
||||
image/gif image/png image/jpg image/tiff )
|
||||
end
|
||||
|
||||
def self.available?
|
||||
command_available?(GRAPHICSMAGICK_COMMAND)
|
||||
end
|
||||
|
||||
#
|
||||
# gm has an option -monitor that will spit out the progress.
|
||||
# this could be interesting. we would need to use getc instead of gets
|
||||
# on the pipe, since the progress is updated on a single line.
|
||||
#
|
||||
def run(&block)
|
||||
if !File.exist?(input_file.to_s) ||
|
||||
( File.size(input_file.to_s) == 0 )
|
||||
debug "Could not find input file: #{input_file}"
|
||||
return :not_fould
|
||||
end
|
||||
|
||||
# try converting first page only
|
||||
status = convert(input_file.to_s + '[0]', &block)
|
||||
# retry with full file if result was empty
|
||||
if File.exist?(output_file.to_s) && File.size(output_file.to_s) == 0
|
||||
# reset filenames to the state before run
|
||||
set_temporary_outfile
|
||||
status = convert(&block)
|
||||
end
|
||||
FileUtils.chmod 0644, output_file.to_s if File.exist? output_file.to_s
|
||||
return status
|
||||
end
|
||||
|
||||
def convert(input = input_file.to_s, &block)
|
||||
# +profile '*' will remove all the image profiles, which will save
|
||||
# space (sometimes) and are not useful for thumbnails
|
||||
arguments = [self.class.gm_command, 'convert', '+profile', "*"]
|
||||
if options[:size]
|
||||
# handle multiple size options, if it is an array.
|
||||
sizes = options[:size].is_a?(Array) ? options[:size] : [options[:size]]
|
||||
sizes.each do |size|
|
||||
if version_less_than?(1,3,6)
|
||||
size = size.sub('^','!')
|
||||
end
|
||||
arguments << '-geometry' << size
|
||||
end
|
||||
end
|
||||
if options[:background]
|
||||
# http://superuser.com/questions/213336/using-graphicsmagick-or-imagemagick-how-do-i-replace-transparency-with-a-fill-c
|
||||
arguments << '-background' << options[:background] << '-extent' << '0x0'
|
||||
end
|
||||
if options[:crop]
|
||||
# we add '+0+0' because we don't want tiles, just a single image
|
||||
arguments << '-crop' << options[:crop]+'+0+0'
|
||||
end
|
||||
arguments << input << output_file
|
||||
run_command(*arguments, &block)
|
||||
end
|
||||
|
||||
# try to detect the dimensions of the first page.
|
||||
# fallback to detecting dimensions of all pages.
|
||||
def self.dimensions(filename)
|
||||
return unless available?
|
||||
run_dimensions(filename.to_s + '[0]') ||
|
||||
run_dimensions(filename.to_s)
|
||||
end
|
||||
|
||||
#
|
||||
# returns the average color of an image, as represented by an array of red, green, blue values, integers
|
||||
# in the range 0..255
|
||||
#
|
||||
# note: it is important that the geometry is "1x1!" ... without the ! this function might die a fiery death.
|
||||
#
|
||||
def self.average_color(filename)
|
||||
if available?
|
||||
args = [gm_command, 'convert', '-resize', '1x1!', filename, 'text:-']
|
||||
color = nil
|
||||
status, color = run_command(*args)
|
||||
if status == :success
|
||||
match = color.match(/^0,0: \(\s*(?<red>\d+),\s*(?<green>\d+),\s*(?<blue>\d+)\)/)
|
||||
if match
|
||||
return [match['red'].to_i, match['green'].to_i, match['blue'].to_i]
|
||||
end
|
||||
end
|
||||
end
|
||||
#if something goes wrong, assume white:
|
||||
return [256,256,256]
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.run_dimensions(filename)
|
||||
args = [gm_command, 'identify', '-format', '%m %w %h', filename]
|
||||
status, dimensions = run_command(*args)
|
||||
if status == :success
|
||||
_type, width, height = dimensions.split(/\s/)
|
||||
return [width,height]
|
||||
end
|
||||
end
|
||||
|
||||
# this override is just used for test, at the moment.
|
||||
def self.gm_command
|
||||
GRAPHICSMAGICK_COMMAND
|
||||
end
|
||||
|
||||
def version_less_than?(major,minor,tiny)
|
||||
installed_major, installed_minor, installed_tiny = GRAPHICSMAGICK_VERSION
|
||||
if installed_major < major
|
||||
true
|
||||
elsif (installed_major == major)
|
||||
if (installed_minor < minor)
|
||||
true
|
||||
elsif (installed_minor == minor) && (installed_tiny < tiny)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,43 +0,0 @@
|
|||
module Media
|
||||
unless defined?(INKSCAPE_COMMAND)
|
||||
INKSCAPE_COMMAND = `which inkscape`.chomp
|
||||
end
|
||||
|
||||
class InkscapeTransmogrifier < Media::Transmogrifier
|
||||
|
||||
def self.magick
|
||||
Media::Transmogrifier.list["Media::GraphicsMagickTransmogrifier"]
|
||||
end
|
||||
|
||||
def self.output_types
|
||||
magick.output_types
|
||||
end
|
||||
|
||||
def self.input_types
|
||||
%w(image/svg+xml image/svg+xml-compressed application/illustrator image/bzeps image/eps image/gzeps)
|
||||
end
|
||||
|
||||
def self.available?
|
||||
command_available?(INKSCAPE_COMMAND) and magick and magick.available?
|
||||
end
|
||||
|
||||
def run(&block)
|
||||
if output_type == 'image/png' and options.empty?
|
||||
arguments = [INKSCAPE_COMMAND, '--without-gui', '--export-area-drawing', '--export-area-snap', input_file, '--export-png', output_file]
|
||||
run_command(*arguments, &block)
|
||||
else
|
||||
png_output_file = Media::TempFile.new(nil, "image/png")
|
||||
arguments = [INKSCAPE_COMMAND, '--without-gui', '--export-area-drawing', '--export-area-snap', input_file, '--export-png', png_output_file]
|
||||
status = run_command(*arguments, &block)
|
||||
return status if status != :success
|
||||
magick_transmog = self.class.magick.new(
|
||||
options.merge({
|
||||
input_file: png_output_file, input_type: "image/png",
|
||||
output_file: output_file, output_type: output_type
|
||||
})
|
||||
)
|
||||
magick_transmog.run(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,62 +0,0 @@
|
|||
module Media
|
||||
#
|
||||
# uses libreoffice and graphicsmagick transmogrifiers to convert from office documents to image documents,
|
||||
# by way of PDF.
|
||||
#
|
||||
|
||||
class LibreMagickTransmogrifier < Media::Transmogrifier
|
||||
|
||||
def self.libre
|
||||
Media::Transmogrifier.list["Media::LibreOfficeTransmogrifier"]
|
||||
end
|
||||
|
||||
def self.magick
|
||||
Media::Transmogrifier.list["Media::GraphicsMagickTransmogrifier"]
|
||||
end
|
||||
|
||||
def self.input_types
|
||||
libre.input_types
|
||||
end
|
||||
|
||||
def self.output_types
|
||||
# we don't want to use this for pdf, since libreoffice by itself can generate pdf
|
||||
magick.output_types - ['application/pdf']
|
||||
end
|
||||
|
||||
def self.available?
|
||||
libre &&
|
||||
magick &&
|
||||
libre.available? &&
|
||||
magick.available? &&
|
||||
ghostscript_available?
|
||||
end
|
||||
|
||||
def self.ghostscript_available?
|
||||
cmd = `which ghostscript`.chomp
|
||||
!cmd.empty? && command_available?(cmd)
|
||||
end
|
||||
|
||||
#
|
||||
# run libreoffice and then graphicsmagick in succession.
|
||||
#
|
||||
# all the options are passed to graphicsmagic, and none to libreoffice,
|
||||
# because they are probably not for libreoffice (like crop or resize).
|
||||
#
|
||||
def run(&block)
|
||||
pdf_output_file = Media::TempFile.new(nil, "application/pdf")
|
||||
libre_transmog = self.class.libre.new(
|
||||
input_file: input_file, input_type: input_type,
|
||||
output_file: pdf_output_file, output_type: "application/pdf")
|
||||
status = libre_transmog.run(&block)
|
||||
return status if status != :success
|
||||
magick_transmog = self.class.magick.new(
|
||||
options.merge({
|
||||
input_file: pdf_output_file, input_type: "application/pdf",
|
||||
output_file: output_file, output_type: output_type
|
||||
})
|
||||
)
|
||||
magick_transmog.run(&block)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,96 +0,0 @@
|
|||
module Media
|
||||
#
|
||||
# This used to work using a hacky python script that connected to a running openoffice
|
||||
# daemon. Now, libreoffice has a command line option to convert, so we are just going to
|
||||
# use that because it is much easier.
|
||||
#
|
||||
# So, the daemon stuff is commented out. Maybe some day it will be useful if doing high
|
||||
# volume document processing.
|
||||
#
|
||||
|
||||
require 'tmpdir'
|
||||
|
||||
unless defined?(LIBREOFFICE_COMMAND)
|
||||
cmd = `which libreoffice`.chomp
|
||||
if cmd.chars.any?
|
||||
LIBREOFFICE_COMMAND = cmd
|
||||
else
|
||||
LIBREOFFICE_COMMAND = false
|
||||
end
|
||||
end
|
||||
|
||||
class LibreOfficeTransmogrifier < Media::Transmogrifier
|
||||
|
||||
def self.input_types
|
||||
%w(
|
||||
text/plain text/html text/richtext application/rtf
|
||||
text/csv text/comma-separated-values
|
||||
application/msword application/mswrite application/powerpoint
|
||||
application/excel application/access application/vnd.ms-msword
|
||||
application/vnd.ms-mswrite application/vnd.ms-powerpoint
|
||||
application/vnd.ms-excel application/vnd.ms-access
|
||||
application/msword-template application/excel-template
|
||||
application/powerpoint-template
|
||||
application/vnd.oasis.opendocument.spreadsheet
|
||||
application/vnd.oasis.opendocument.formula
|
||||
application/vnd.oasis.opendocument.chart
|
||||
application/vnd.oasis.opendocument.image
|
||||
application/vnd.oasis.opendocument.graphics
|
||||
application/vnd.oasis.opendocument.presentation
|
||||
application/vnd.oasis.opendocument.text-web
|
||||
application/vnd.oasis.opendocument.text
|
||||
application/vnd.oasis.opendocument.text-template
|
||||
application/vnd.oasis.opendocument.text-master
|
||||
application/vnd.oasis.opendocument.presentation-template
|
||||
application/vnd.oasis.opendocument.graphics-template
|
||||
application/vnd.oasis.opendocument.spreadsheet-template
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
)
|
||||
end
|
||||
|
||||
def self.output_types
|
||||
["application/pdf"] + input_types
|
||||
end
|
||||
|
||||
def self.available?
|
||||
command_available?(LIBREOFFICE_COMMAND)
|
||||
end
|
||||
|
||||
def run(&block)
|
||||
status = nil
|
||||
|
||||
# make a unique temporary directory for output, so that the filename won't collide.
|
||||
Dir.mktmpdir do |work_directory|
|
||||
# run command
|
||||
ext = extension(output_type)
|
||||
if ext
|
||||
arguments = [LIBREOFFICE_COMMAND, '--headless', '-convert-to', extension(output_type), '-outdir', work_directory, input_file]
|
||||
status = run_command(*arguments, &block)
|
||||
|
||||
# we cannot specify the name of the output file, so grab what it generated and move it to self.output_file
|
||||
libreoffice_output = work_directory + '/' + replace_extension(input_file, extension(output_type))
|
||||
if File.exist?(libreoffice_output)
|
||||
replace_file from: libreoffice_output, to: output_file
|
||||
else
|
||||
msg = ["Error: Could not find libreoffice output %s \n" % output_file]
|
||||
msg << arguments.join(' ')
|
||||
msg += Dir[work_directory + '/*']
|
||||
unless command_output.empty?
|
||||
msg << 'LibreOffice said:'
|
||||
msg << command_output
|
||||
end
|
||||
msg = msg.join("\n")
|
||||
error msg
|
||||
yield(msg) if block_given?
|
||||
return :failure
|
||||
end
|
||||
else
|
||||
yield('could not find extension for type %s' % output_type) if block_given?
|
||||
return :failure
|
||||
end
|
||||
end
|
||||
return status
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
module Media
|
||||
VERSION = "0.3.0"
|
||||
end
|
|
@ -1,4 +0,0 @@
|
|||
# desc "Explaining what the task does"
|
||||
# task :media do
|
||||
# # Task goes here
|
||||
# end
|
|
@ -1,58 +0,0 @@
|
|||
Conversion Performance
|
||||
======================
|
||||
|
||||
Convert to pdf
|
||||
--------------
|
||||
|
||||
Some converters take ages - in particular for large documents.
|
||||
|
||||
Here's a few measurements for:
|
||||
|
||||
a) libreoffice --headless from cg
|
||||
b) unoconv
|
||||
c) unoconv with server running
|
||||
d) unoconv with PageRange=1-1
|
||||
e) unoconv with PageRange=1-1 and server running
|
||||
|
||||
|
||||
Single page odt to pdf:
|
||||
a) 0.70 sec
|
||||
b) 1.46 sec
|
||||
c) 0.49 sec
|
||||
d) 1.47 sec
|
||||
e) 0.48 sec
|
||||
|
||||
|
||||
200 page odt to pdf:
|
||||
a) 13.63 sec
|
||||
b) 14.21 sec
|
||||
c) 13.27 sec
|
||||
d) 7.01 sec
|
||||
e) 6.17 sec
|
||||
|
||||
|
||||
Order of conversions
|
||||
--------------------
|
||||
|
||||
The first thing we need after an upload is a small thumbnail. Unfortunately
|
||||
this is also the thing that takes most steps to generate:
|
||||
|
||||
* doc -> pdf
|
||||
* pdf -> large jpg
|
||||
* large -> small jpg
|
||||
|
||||
We could skip the large jpg, but we need it anyway. Also the large to small jpg
|
||||
step usually is the fastest.
|
||||
So instead we do:
|
||||
|
||||
1. doc -> 1 page pdf
|
||||
2. 1 page pdf -> large jpg
|
||||
3. large jpg -> small jpg
|
||||
4. large jpg -> medium jpg
|
||||
5. doc -> full pdf
|
||||
6. doc -> full odt
|
||||
|
||||
* doc -> x conversion cannot run in parellel. So we will want higher
|
||||
priority for the first step.
|
||||
* We can use a different delayed job queue for the image conversions.
|
||||
* The jpg -> jpg conversion could even happen in process if needed.
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 57 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 82 KiB |
Binary file not shown.
|
@ -1,40 +0,0 @@
|
|||
require 'test_helper'
|
||||
require 'media/transmogrifiers/graphicsmagick'
|
||||
|
||||
class GraphicsMagickTransmogrifierTest < Minitest::Test
|
||||
|
||||
def test_scaling_png_to_jpg
|
||||
input = file('lyra.png')
|
||||
transmog = Media::GraphicsMagickTransmogrifier.new input_file: input,
|
||||
output_type: 'image/jpg',
|
||||
size: '100x100!'
|
||||
assert transmog
|
||||
status = transmog.run
|
||||
assert_equal :success, status
|
||||
assert File.exist?(transmog.output_file.to_s)
|
||||
|
||||
assert file_info_matches?(transmog.output_file, /JPEG/),
|
||||
"output should be a jpg: #{file_info(transmog.output_file)}"
|
||||
assert_equal ['100','100'], Media.dimensions(transmog.output_file),
|
||||
"output should be resized: #{file_info(transmog.output_file)}"
|
||||
end
|
||||
|
||||
# libremagic transmogrifier uses this internally.
|
||||
def test_pdf_as_input
|
||||
skip('ghostscript required') if ghostscript_missing?
|
||||
input = file('kaos.pdf')
|
||||
transmog = Media::GraphicsMagickTransmogrifier.new input_file: input,
|
||||
output_type: 'image/jpg'
|
||||
status = transmog.run
|
||||
assert_equal :success, status
|
||||
assert File.exist?(transmog.output_file.to_s)
|
||||
|
||||
assert file_info_matches?(transmog.output_file, /JPEG/),
|
||||
"output should be a jpg: #{file_info(transmog.output_file)}"
|
||||
end
|
||||
|
||||
def ghostscript_missing?
|
||||
`which ghostscript`.empty?
|
||||
end
|
||||
|
||||
end
|
|
@ -1,25 +0,0 @@
|
|||
require 'test_helper'
|
||||
require 'media/transmogrifiers/libremagick'
|
||||
|
||||
class LibreMagickTransmogrifierTest < Minitest::Test
|
||||
|
||||
def test_libremagick_transmog
|
||||
skip 'dependencies missing' unless klass.available?
|
||||
input = file('msword.doc')
|
||||
transmog =
|
||||
klass.new input_file: input,
|
||||
output_type: 'image/jpg'
|
||||
status = transmog.run
|
||||
assert_equal :success, status
|
||||
assert File.exist?(transmog.output_file.to_s)
|
||||
|
||||
assert file_info_matches?(transmog.output_file, /JPEG/),
|
||||
"output should be a jpg: #{file_info(transmog.output_file)}"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def klass
|
||||
Media::LibreMagickTransmogrifier
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
require 'test_helper'
|
||||
require 'media'
|
||||
|
||||
class MediaTest < Minitest::Test
|
||||
|
||||
def test_has_dimensions
|
||||
assert Media.has_dimensions? 'image/png'
|
||||
end
|
||||
|
||||
def test_has_no_dimensions
|
||||
refute Media.has_dimensions? 'text/plain'
|
||||
end
|
||||
|
||||
end
|
|
@ -1,39 +0,0 @@
|
|||
require 'media/transmogrifier'
|
||||
|
||||
class SleepyTransmogrifier < Media::Transmogrifier
|
||||
|
||||
def initialize
|
||||
end
|
||||
|
||||
def name
|
||||
:sleepy
|
||||
end
|
||||
|
||||
def self.input_types
|
||||
['test']
|
||||
end
|
||||
|
||||
def self.output_types
|
||||
['test']
|
||||
end
|
||||
|
||||
PROGRESS = [
|
||||
'starting up.',
|
||||
'getting ready to do some work.',
|
||||
'working hard.',
|
||||
'winding down.',
|
||||
'that was tough.',
|
||||
'all done now.'
|
||||
]
|
||||
|
||||
def run
|
||||
for str in PROGRESS
|
||||
yield str
|
||||
sleep 0.1
|
||||
end
|
||||
return :success
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
require 'pathname'
|
||||
|
||||
module Support
|
||||
module File
|
||||
|
||||
def file(name)
|
||||
Pathname(__FILE__) + '..' + '..' + 'files' + name
|
||||
end
|
||||
|
||||
def file_info_matches?(file, regex)
|
||||
file_info(file) =~ regex
|
||||
end
|
||||
|
||||
def file_info(file)
|
||||
`file #{file.to_s}`
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
Minitest::Test.send :include, Support::File
|
|
@ -1,13 +0,0 @@
|
|||
require 'minitest/autorun'
|
||||
require 'media'
|
||||
require 'logger'
|
||||
|
||||
# Load support files
|
||||
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
||||
|
||||
Media::TMP_PATH = '/tmp'
|
||||
|
||||
LOGPATH = "#{File.dirname(__FILE__)}/../log"
|
||||
FileUtils.mkdir(LOGPATH) unless File.exist? LOGPATH
|
||||
LOGFILE = "#{LOGPATH}/transmogrifier.log"
|
||||
Media::Transmogrifier.logger = Logger.new(LOGFILE)
|
|
@ -1,112 +0,0 @@
|
|||
require 'test_helper'
|
||||
require 'media'
|
||||
require_relative 'sleepy_transmogrifier'
|
||||
|
||||
class TransmogrifierTest < Minitest::Test
|
||||
|
||||
def test_run_sleepy_transmog
|
||||
progress_strings = [
|
||||
'starting up.',
|
||||
'getting ready to do some work.',
|
||||
'working hard.',
|
||||
'winding down.',
|
||||
'that was tough.',
|
||||
'all done now.'
|
||||
]
|
||||
transmog = SleepyTransmogrifier.new
|
||||
i = 0
|
||||
status = transmog.run do |progress|
|
||||
assert_equal progress_strings[i], progress
|
||||
putc ':'; STDOUT.flush
|
||||
i+=1
|
||||
end
|
||||
assert_equal :success, status
|
||||
end
|
||||
|
||||
def test_transmog_not_found
|
||||
input = file('lyra.png')
|
||||
transmog = Media.transmogrifier input_file: input,
|
||||
output_type: 'image/xcf'
|
||||
assert_nil transmog
|
||||
end
|
||||
|
||||
def test_graphicsmagick_transmog
|
||||
input = file('lyra.png')
|
||||
transmog = Media.transmogrifier(input_file: input, output_type: 'image/jpg', size: '100x100!')
|
||||
assert transmog
|
||||
status = transmog.run
|
||||
assert_equal :success, status
|
||||
assert File.exist?(transmog.output_file.to_s)
|
||||
|
||||
assert file_info_matches?(transmog.output_file, /JPEG/),
|
||||
"output should be a jpg: #{file_info(transmog.output_file)}"
|
||||
assert_equal ['100','100'], Media.dimensions(transmog.output_file),
|
||||
"output should be resized: #{file_info(transmog.output_file)}"
|
||||
end
|
||||
|
||||
def test_with_output_file
|
||||
input = file('lyra.png')
|
||||
Media::TempFile.open(nil,'image/jpg') do |dest_file|
|
||||
transmog = Media.transmogrifier(input_file: input, output_file: dest_file)
|
||||
assert transmog, 'should find transmog'
|
||||
status = transmog.run
|
||||
assert_equal :success, status
|
||||
assert File.exist?(dest_file.to_s)
|
||||
assert file_info_matches?(dest_file, /JPEG/), "output should be a jpg: #{file_info(transmog.output_file)}"
|
||||
end
|
||||
end
|
||||
|
||||
def test_libreoffice_transmog
|
||||
input = file('msword.doc')
|
||||
transmog = Media.transmogrifier(input_file: input, output_type: 'application/pdf')
|
||||
assert transmog
|
||||
status = transmog.run
|
||||
assert_equal :success, status
|
||||
assert File.exist?(transmog.output_file.to_s)
|
||||
|
||||
assert file_info_matches?(transmog.output_file, /PDF/), "output should be a pdf: #{file_info(transmog.output_file)}"
|
||||
end
|
||||
|
||||
def test_doc_to_jpg_twostep_transmog
|
||||
skip "We're currently not exposing transformations from pdf. So this has to be done in one step"
|
||||
input = file('msword.doc')
|
||||
transmog = Media.transmogrifier(input_file: input, output_type: 'application/pdf')
|
||||
transmog.run
|
||||
|
||||
transmog = Media.transmogrifier(input_file: transmog.output_file, output_type: 'image/jpg')
|
||||
status = transmog.run
|
||||
assert_equal :success, status
|
||||
assert File.exist?(transmog.output_file.to_s)
|
||||
|
||||
assert file_info_matches?(transmog.output_file, /JPEG/), "output should be a jpg: #{file_info(transmog.output_file)}"
|
||||
end
|
||||
|
||||
def test_no_pdf_transmog
|
||||
input = file('kaos.pdf')
|
||||
transmog = Media.transmogrifier(input_file: input, output_type: 'image/jpg')
|
||||
assert_nil transmog
|
||||
end
|
||||
|
||||
def test_libremagick_transmog
|
||||
input = file('msword.doc')
|
||||
transmog = Media.transmogrifier(input_file: input, output_type: 'image/jpg')
|
||||
skip('libremagic is not available') unless transmog
|
||||
status = transmog.run
|
||||
assert_equal :success, status
|
||||
assert File.exist?(transmog.output_file.to_s)
|
||||
|
||||
assert file_info_matches?(transmog.output_file, /JPEG/), "output should be a jpg: #{file_info(transmog.output_file)}"
|
||||
end
|
||||
|
||||
def test_inkscape_transmog
|
||||
input = file('anarchism.svg')
|
||||
transmog = Media.transmogrifier(input_file: input, output_type: 'image/jpg')
|
||||
assert transmog
|
||||
status = transmog.run
|
||||
assert_equal :success, status
|
||||
assert File.exist?(transmog.output_file.to_s)
|
||||
|
||||
assert file_info_matches?(transmog.output_file, /JPEG/), "output should be a pdf: #{file_info(transmog.output_file)}"
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue