rails_admin_jcrop 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,82 @@
1
+ # RailsAdmin Jcrop Plugin
2
+
3
+ ## Image cropping made easy! ##
4
+
5
+ 1. Add it to your Gemfile and run `bundle install`:
6
+
7
+ gem 'rails_admin'
8
+ gem 'rails_admin_jcrop' #, git: 'git://github.com/janx/rails_admin_jcrop.git'
9
+
10
+ 2. Configure your model field to use Jcrop:
11
+
12
+ # RAILS_ROOT/config/initializers/rails_admin.rb
13
+ config.model User do
14
+ configure :avatar, :jcrop
15
+
16
+ # Below is optional
17
+ edit do
18
+ field :avatar do
19
+ # use jcrop_options to pass any options you like to .Jcrop call in javascript
20
+ jcrop_options aspectRatio: 500.0/320.0
21
+ end
22
+ end
23
+ end
24
+
25
+ 3. Call :rails_admin_crop in your uploader:
26
+
27
+ class AvatarUploader < CarrierWave::Uploader::Base
28
+
29
+ version :thumb do
30
+ process :rails_admin_crop
31
+ process :resize_to_fill: [500,320]
32
+ end
33
+
34
+ end
35
+
36
+ 4. Done! Click the image on your RailsAdmin model edit page and enjoy cropping!
37
+ ![Cropping Screenshot](https://github.com/janx/rails_admin_jcrop/raw/master/screenshot.png)
38
+
39
+ ## Localization ##
40
+
41
+ Localize the crop form by adding these entries:
42
+
43
+ zh:
44
+ admin:
45
+ actions:
46
+ crop:
47
+ title: '剪裁'
48
+ menu: '剪裁'
49
+
50
+ ## Dependencies ##
51
+
52
+ * MRI 1.9.3 (All above 1.8.6 should work, I only tested on 1.9.3)
53
+ * Rails 3.x
54
+ * ORM
55
+ - ActiveRecord
56
+ * Upload plugin
57
+ - CarrierWave
58
+ * Image processor
59
+ - MiniMagick
60
+
61
+ ## TODO ##
62
+
63
+ * MongoDB support
64
+ * Paperclip support
65
+ * RMagick support
66
+
67
+ ## Contributing ##
68
+
69
+ Any help is encouraged. Here are some ways you can contribute:
70
+
71
+ * by using it
72
+ * by telling people this plugin
73
+ * by reporting bugs or suggesting new features on github issue tracker
74
+ * by fixing bugs or implementing features
75
+ * by giving author a hug (especially if you're girl)
76
+
77
+ ## Thanks ##
78
+
79
+ Life is easier with you.
80
+
81
+ * [RailsAdmin](https://github.com/sferik/rails_admin/)
82
+ * [Jcrop](http://deepliquid.com/content/Jcrop.html)
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'RailsAdminJcrop'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
@@ -0,0 +1,141 @@
1
+ (function($) {
2
+ $.widget("ra.jcropForm", {
3
+
4
+ _create: function() {
5
+ var widget = this;
6
+ var dom_widget = widget.element;
7
+
8
+ dom_widget.find('a.thumbnail').unbind().bind("click", function(e){
9
+ widget._bindModalOpening(e, dom_widget.find('a.jcrop_handle').data('link'));
10
+ return false;
11
+ });
12
+ },
13
+
14
+ _bindModalOpening: function(e, url) {
15
+ e.preventDefault();
16
+ widget = this;
17
+ if($("#modal").length)
18
+ return false;
19
+
20
+ var dialog = this._getModal();
21
+
22
+ setTimeout(function(){ // fix race condition with modal insertion in the dom (Chrome => Team/add a new fan => #modal not found when it should have). Somehow .on('show') is too early, tried it too.
23
+ $.ajax({
24
+ url: url,
25
+ beforeSend: function(xhr) {
26
+ xhr.setRequestHeader("Accept", "text/javascript");
27
+ },
28
+ success: function(data, status, xhr) {
29
+ dialog.find('.modal-body').html(data);
30
+ widget._bindFormEvents();
31
+ },
32
+ error: function(xhr, status, error) {
33
+ dialog.find('.modal-body').html(xhr.responseText);
34
+ },
35
+ dataType: 'text'
36
+ });
37
+ },100);
38
+
39
+ },
40
+
41
+ _bindFormEvents: function() {
42
+ var widget = this,
43
+ dialog = this._getModal(),
44
+ form = dialog.find("form"),
45
+ saveButtonText = dialog.find(":submit[name=_save]").html(),
46
+ cancelButtonText = dialog.find(":submit[name=_continue]").html();
47
+ dialog.find('.form-actions').remove();
48
+
49
+ var jcrop_options = $.extend({
50
+ bgColor: 'white',
51
+ keySupport: false,
52
+ onSelect: widget.updateCoordinates
53
+ }, rails_admin_jcrop_options);
54
+ dialog.find('img.jcrop-subject').Jcrop(jcrop_options)
55
+
56
+ form.attr("data-remote", true);
57
+ dialog.find('.modal-header-title').text(form.data('title'));
58
+ dialog.find('.cancel-action').unbind().click(function(){
59
+ dialog.modal('hide');
60
+ return false;
61
+ }).html(cancelButtonText);
62
+
63
+ dialog.find('.save-action').unbind().click(function(){
64
+ form.submit();
65
+ return false;
66
+ }).html(saveButtonText);
67
+
68
+ $(document).trigger('rails_admin.dom_ready');
69
+
70
+ form.bind("ajax:complete", function(xhr, data, status) {
71
+ if (status == 'error') {
72
+ dialog.find('.modal-body').html(data.responseText);
73
+ widget._bindFormEvents();
74
+ } else {
75
+ var json = $.parseJSON(data.responseText);
76
+ var select = widget.element.find('select').filter(":hidden");
77
+
78
+ thumb = widget.element.find('a.jcrop_handle').data('thumb');
79
+ widget.element.find('a.thumbnail > img').removeAttr('src').attr('src', json.urls[thumb] + '?' + new Date().valueOf());
80
+
81
+ widget._trigger("success");
82
+ dialog.modal("hide");
83
+ }
84
+ });
85
+ },
86
+
87
+ updateCoordinates: function(c) {
88
+ var rx = 100/c.w;
89
+ var ry = 100/c.h;
90
+ var lw = $('img.jcrop-subject').width();
91
+ var lh = $('img.jcrop-subject').height();
92
+ var ratio = $('img.jcrop-subject').data('geometry').split(',')[0] / lw ;
93
+
94
+ $('#preview').css({
95
+ width: Math.round(rx * lw) + 'px',
96
+ height: Math.round(ry * lh) + 'px',
97
+ marginLeft: '-' + Math.round(rx * c.x) + 'px',
98
+ marginTop: '-' + Math.round(ry * c.y) + 'px'
99
+ });
100
+
101
+ $("#crop_x").val(Math.round(c.x * ratio));
102
+ $("#crop_y").val(Math.round(c.y * ratio));
103
+ $("#crop_w").val(Math.round(c.w * ratio));
104
+ $("#crop_h").val(Math.round(c.h * ratio));
105
+ },
106
+
107
+ _getModal: function() {
108
+ var widget = this;
109
+ if (!widget.dialog) {
110
+ widget.dialog = $('\
111
+ <div id="modal" class="modal fade">\
112
+ <div class="modal-header">\
113
+ <a href="#" class="close" data-dismiss="modal">&times;</a>\
114
+ <h3 class="modal-header-title">...</h3>\
115
+ </div>\
116
+ <div class="modal-body">\
117
+ ...\
118
+ </div>\
119
+ <div class="modal-footer">\
120
+ <a href="#" class="btn cancel-action">...</a>\
121
+ <a href="#" class="btn btn-primary save-action">...</a>\
122
+ </div>\
123
+ </div>')
124
+ .modal({
125
+ keyboard: true,
126
+ backdrop: true,
127
+ show: true
128
+ })
129
+ .on('hidden', function(){
130
+ widget.dialog.remove(); // We don't want to reuse closed modals
131
+ widget.dialog = null;
132
+ });
133
+ }
134
+ return this.dialog;
135
+ }
136
+ });
137
+ })(jQuery);
138
+
139
+ $(function() {
140
+ $('div.jcrop_type').jcropForm();
141
+ });
@@ -0,0 +1,52 @@
1
+ module RailsAdmin
2
+
3
+ class JcropController < RailsAdmin::ApplicationController
4
+ skip_before_filter :get_model
5
+ before_filter :get_model, :get_object, :get_field
6
+
7
+ helper_method :abstract_model, :geometry
8
+
9
+ def edit
10
+ respond_to do |format|
11
+ format.html
12
+ format.js { render :edit, :layout => false }
13
+ end
14
+ end
15
+
16
+ def update
17
+ @object.rails_admin_crop! params
18
+
19
+ respond_to do |format|
20
+ format.html { redirect_to_on_success }
21
+ format.js do
22
+ asset = @object.send @field
23
+ urls = {:original => asset.url}
24
+ @object.class.uploaders[@field.to_sym].versions.each {|name, _| urls[name] = asset.url(name)}
25
+
26
+ render :json => {
27
+ :id => @object.id,
28
+ :label => @model_config.with(:object => @object).object_label,
29
+ :field => @field,
30
+ :urls => urls
31
+ }
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def get_field
39
+ @field = params[:field]
40
+ end
41
+
42
+ def geometry(image_path)
43
+ image = MiniMagick::Image.open(image_path)
44
+ [image[:width], image[:height]]
45
+ end
46
+
47
+ def cropping?
48
+ [:crop_x, :crop_y, :crop_w, :crop_h].all? {|c| params[c].present?}
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,8 @@
1
+ = form_tag nil, :method => :put, 'data-title' => "#{I18n.t('admin.actions.crop.menu').capitalize} #{abstract_model.model.human_attribute_name @field}" do
2
+ = image_tag @object.send(@field).url, :class => 'jcrop-subject', 'data-geometry' => geometry(@object.send(@field).path).join(',')
3
+ = hidden_field_tag :crop_x
4
+ = hidden_field_tag :crop_y
5
+ = hidden_field_tag :crop_w
6
+ = hidden_field_tag :crop_h
7
+ = hidden_field_tag :crop_field, @field
8
+ = render :partial => 'rails_admin/main/submit_buttons'
@@ -0,0 +1,21 @@
1
+ :javascript
2
+ var rails_admin_jcrop_options = #{field.jcrop_options.to_json};
3
+
4
+ = stylesheet_link_tag 'rails_admin/jquery.Jcrop'
5
+ = javascript_include_tag 'rails_admin/jquery.Jcrop'
6
+ = javascript_include_tag 'rails_admin/ra.jcrop'
7
+
8
+ - file = form.object.send(field.method_name).presence
9
+
10
+ .toggle{:style => ('display:none;' if file && field.delete_method && form.object.send(field.delete_method) == '1')}
11
+ - if value = field.pretty_value
12
+ = value
13
+ = form.file_field(field.name, field.html_attributes.reverse_merge({ :data => { :fileupload => true } }))
14
+ = link_to 'Edit Image', '#', :data => {:link => jcrop_path(:model_name => form.object.class.to_param, :modal => true, :id => form.object.id, :field => field.name), :thumb => field.thumb_method}, :style => 'display:none', :class => "jcrop_handle"
15
+ - if field.optional? && field.errors.blank? && file && field.delete_method
16
+ %a.btn.btn-info{:href => '#', :'data-toggle' => 'button', :onclick => "$(this).siblings('[type=checkbox]').click(); $(this).siblings('.toggle').toggle('slow'); jQuery(this).toggleClass('btn-danger btn-info'); return false"}
17
+ %i.icon-white.icon-trash
18
+ = I18n.t('admin.actions.delete.menu').capitalize + " #{field.method_name}"
19
+ = form.check_box(field.delete_method, :style => 'display:none;' )
20
+ - if field.cache_method
21
+ = form.hidden_field(field.cache_method)
@@ -0,0 +1,7 @@
1
+ en:
2
+ admin:
3
+ actions:
4
+ crop:
5
+ title: Crop
6
+ menu: Crop
7
+ breadcrumb: Crop
data/config/routes.rb ADDED
@@ -0,0 +1,10 @@
1
+ RailsAdmin::Engine.routes.draw do
2
+ controller "main" do
3
+ scope ":model_name" do
4
+ scope "(:id)/:field" do
5
+ get "/jcrop", :to => 'jcrop#edit', :as => :jcrop
6
+ put "/jcrop", :to => 'jcrop#update'
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module RailsAdminJcrop
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RailsAdmin
4
+
5
+ initializer "RailsAdminJcrop precompile hook" do |app|
6
+ app.config.assets.precompile += ['rails_admin/ra.jcrop.js', 'rails_admin/jquery.Jcrop.js', 'rails_admin/jquery.Jcrop.css']
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module CarrierWave
2
+ module Mount
3
+ module Extension
4
+ def self.included(base)
5
+ base.send :include, RailsAdminJcrop::Orm::ActiveRecord
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,50 @@
1
+ require 'rails_admin/config/actions'
2
+ require 'rails_admin/config/actions/base'
3
+ require 'rails_admin/config/fields'
4
+ require 'rails_admin/config/fields/types/file_upload'
5
+
6
+ module RailsAdmin
7
+ module Config
8
+ module Fields
9
+ module Types
10
+ class Jcrop < RailsAdmin::Config::Fields::Types::FileUpload
11
+ RailsAdmin::Config::Fields::Types::register(self)
12
+
13
+ register_instance_option(:partial) do
14
+ :form_jcrop
15
+ end
16
+
17
+ register_instance_option(:thumb_method) do
18
+ @thumb_method ||= ((versions = bindings[:object].send(name).versions.keys).find{|k| k.in?([:thumb, :thumbnail, 'thumb', 'thumbnail'])} || versions.first.to_s)
19
+ end
20
+
21
+ register_instance_option(:delete_method) do
22
+ "remove_#{name}"
23
+ end
24
+
25
+ register_instance_option(:cache_method) do
26
+ "#{name}_cache"
27
+ end
28
+
29
+ register_instance_option(:jcrop_options) do
30
+ {}
31
+ end
32
+
33
+ def resource_url(thumb = false)
34
+ return nil unless (uploader = bindings[:object].send(name)).present?
35
+ thumb.present? ? uploader.send(thumb).url : uploader.url
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ RailsAdmin::Config::Fields.register_factory do |parent, properties, fields|
44
+ if properties[:name] == :jcrop
45
+ fields << RailsAdmin::Config::Fields::Types::Jcrop.new(parent, properties[:name], properties)
46
+ true
47
+ else
48
+ false
49
+ end
50
+ end