From 657d054685431cf9568d590bd60addb2d9e34bed Mon Sep 17 00:00:00 2001 From: Martin Denizet Date: Wed, 12 Oct 2011 16:08:35 +0800 Subject: [PATCH] Original plugin, version 0.1.0 --- LICENSE | 24 +++++ README.rdoc | 10 ++ .../attach_screenshot_controller.rb | 38 ++++++++ app/helpers/attach_screenshot_helper.rb | 2 + app/views/attachments/_form.rhtml | 90 ++++++++++++++++++ assets/javascripts/attach_screenshot.js | 15 +++ assets/javascripts/uploader_applet.jar | Bin 0 -> 9321 bytes assets/stylesheets/attach_screenshot.css | 30 ++++++ config/locales/en.yml | 10 ++ config/locales/ru.yml | 10 ++ init.rb | 17 ++++ lib/application_patch.rb | 24 +++++ lib/attachment_hook.rb | 76 +++++++++++++++ lib/cleanup_tmp.rb | 37 +++++++ 14 files changed, 383 insertions(+) create mode 100644 LICENSE create mode 100644 README.rdoc create mode 100644 app/controllers/attach_screenshot_controller.rb create mode 100644 app/helpers/attach_screenshot_helper.rb create mode 100644 app/views/attachments/_form.rhtml create mode 100644 assets/javascripts/attach_screenshot.js create mode 100644 assets/javascripts/uploader_applet.jar create mode 100644 assets/stylesheets/attach_screenshot.css create mode 100644 config/locales/en.yml create mode 100644 config/locales/ru.yml create mode 100644 init.rb create mode 100644 lib/application_patch.rb create mode 100644 lib/attachment_hook.rb create mode 100644 lib/cleanup_tmp.rb diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c0d8d8c --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2010 Konstantin Zaitsev, Sergei Vasiliev, Alexandr Poplavsky, Axmor Software, Jens Alfke +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Axmor Software nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000..002e400 --- /dev/null +++ b/README.rdoc @@ -0,0 +1,10 @@ += redmine_attach_screenshot + +This plugin allows attaching several screenshots from clipboard directly to a Redmine issue. +Requires RMagick (http://rmagick.rubyforge.org/) to show screenshot thumbnails when adding/editing an issue. +Compatibility: Redmine 1.1.0 + +Credits: +Authors: Konstantin Zaitcev (kos@axmor.com), Sergei Vasilyev (vsv@axmor.com), Alexandr Poplavsky (poplavsky@axmor.com), +Inspired by Atlassian JIRA (http://www.atlassian.com/software/jira) +Work sponsored by Axmor Software (http://www.axmor.com) diff --git a/app/controllers/attach_screenshot_controller.rb b/app/controllers/attach_screenshot_controller.rb new file mode 100644 index 0000000..d593ab2 --- /dev/null +++ b/app/controllers/attach_screenshot_controller.rb @@ -0,0 +1,38 @@ +#require 'RMagick' + +class AttachScreenshotController < ApplicationController + unloadable + skip_before_filter :require_login + skip_before_filter :verify_authenticity_token + accept_key_auth :index + + def index + path = "#{RAILS_ROOT}/tmp/" + if request.post? + date = DateTime.now.strftime("%H%M%S") + @fname = make_tmpname(date) + file = File.new(path + make_tmpname(date), "wb"); + file.write(params[:attachments].read); + file.close(); + if (Object.const_defined?(:Magick)) + img = Magick::Image::read(file.path()).first + thumb = img.resize_to_fit(150, 150) + @fname = make_tmpname(date, "thumb.png") + thumb.write path + @fname + end + render :inline => "<%= @fname %>" + else + @fname = params[:id]; + send_file(path + @fname, + :disposition => 'inline', + :type => 'image/png', + :filename => "screenshot.png"); + end + end + + private + + def make_tmpname(date, name = "screenshot.png") + sprintf('%d_%d%s', User.current.id, date, name) + end +end diff --git a/app/helpers/attach_screenshot_helper.rb b/app/helpers/attach_screenshot_helper.rb new file mode 100644 index 0000000..9e6494f --- /dev/null +++ b/app/helpers/attach_screenshot_helper.rb @@ -0,0 +1,2 @@ +module AttachScreenshotHelper +end diff --git a/app/views/attachments/_form.rhtml b/app/views/attachments/_form.rhtml new file mode 100644 index 0000000..d63a700 --- /dev/null +++ b/app/views/attachments/_form.rhtml @@ -0,0 +1,90 @@ +<% content_for :header_tags do %> + <%= stylesheet_link_tag 'attach_screenshot', :plugin => 'redmine_attach_screenshot' %> + <%= javascript_include_tag 'attach_screenshot', :plugin => 'redmine_attach_screenshot' %> +<% end %> + +<%= file_field_tag 'attachments[1][file]', :size => 30, :id => nil -%> + +
+<%= link_to l(:label_add_another_file), '#', :onclick => 'addFileField(); return false;' %> +(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>) + +
+
+<% if @issue %> +<% if @issue.new_record? %> + +<% else %> +
+<% end %> + + +<%= submit_tag l(:button_add), :onclick => 'showAttachScreen(); return false;'%> + +<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %> + +
+ +
+ + +<% end %> diff --git a/assets/javascripts/attach_screenshot.js b/assets/javascripts/attach_screenshot.js new file mode 100644 index 0000000..754552b --- /dev/null +++ b/assets/javascripts/attach_screenshot.js @@ -0,0 +1,15 @@ +//var screenshotFieldCount = 1; + +function hideAttachScreen() { + document.getElementById('attach_applet').style.display = 'none'; + document.getElementById('attach_applet').innerHTML = ''; +} +function deleteAttachScreen(fileId) { + p.removeChild(document.getElementById(fileId)); +} +function addLinkToAttachScreen(fileId) { + if (wikiToolbar){ + fileId = fileId.replace("thumb", "screenshot") + wikiToolbar.encloseSelection("!"+ fileId +"!",""); + } +} diff --git a/assets/javascripts/uploader_applet.jar b/assets/javascripts/uploader_applet.jar new file mode 100644 index 0000000000000000000000000000000000000000..e8d394a21916ccee517d86a2d60bf883b709fe7a GIT binary patch literal 9321 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 F{vY8>BQXE~ literal 0 HcmV?d00001 diff --git a/assets/stylesheets/attach_screenshot.css b/assets/stylesheets/attach_screenshot.css new file mode 100644 index 0000000..3311ef3 --- /dev/null +++ b/assets/stylesheets/attach_screenshot.css @@ -0,0 +1,30 @@ +#screenshots_fields input[type=text] {margin-left: 8px; } +img.scr_attach_icon { + text-align: center; + vertical-align: middle; + border: 1px solid grey; +} +span.screen_thumb { + display: block; + position: relative; + float: left; + text-align: center; + vertical-align: bottom; + margin: 3px; +} +span.screen_thumb .st1 { + display: table-cell; + position: relative; + width: 160px; + height: 160px; + text-align: center; + vertical-align: middle; +} +span.screen_thumb .st1 im { + vertical-align: middle; +} +span.screen_thumb .st2 { + width: 160px; + display: block; + text-align: center; +} diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000..ead3385 --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,10 @@ +en: + label_screenshots: Screenshots + label_add_another_screenshot: Add another screenshot + label_button_paste: Paste image from clipboard + label_image: Screenshot + button_attach: Attach + error_close_msg: Can not close applet, {0} + error_close_title: Error + error_attach_msg: Error, {0} + error_attach_title: Attaching error diff --git a/config/locales/ru.yml b/config/locales/ru.yml new file mode 100644 index 0000000..a7ba58f --- /dev/null +++ b/config/locales/ru.yml @@ -0,0 +1,10 @@ +ru: + label_screenshots: Скриншоты + label_add_another_screenshot: Добавить еще один скриншот + label_button_paste: Вставить скриншот из буфера обмена + label_image: Скриншот + button_attach: Добавить + error_close_msg: Невозможно закрыть аплет, {0} + error_close_title: Ошибка + error_attach_msg: Ошибка, {0} + error_attach_title: Ошибка при загрузке diff --git a/init.rb b/init.rb new file mode 100644 index 0000000..2178138 --- /dev/null +++ b/init.rb @@ -0,0 +1,17 @@ +require 'redmine' +require 'dispatcher' +require 'application_patch' +require 'cleanup_tmp' + +require_dependency 'attachment_hook' + +Redmine::Plugin.register :redmine_attach_screenshot do + name 'Redmine Attach Screenshot plugin' + author 'Konstantin Zaitsev, Sergei Vasiliev, Alexandr Poplavsky, Jens Alfke' + description 'Attach screenshots from clipboard directly to a Redmine issue' + version '0.1.0' + Dispatcher.to_prepare do + ApplicationController.send(:include, AttachScreenshotPlugin::ApplicationControllerPatch) + AccountController.send(:include, AttachScreenshotPlugin::CleanupTmp) + end +end diff --git a/lib/application_patch.rb b/lib/application_patch.rb new file mode 100644 index 0000000..d372980 --- /dev/null +++ b/lib/application_patch.rb @@ -0,0 +1,24 @@ +require 'account_controller' +require 'ftools' + +module AttachScreenshotPlugin + module ApplicationControllerPatch + def self.included(base) # :nodoc: + base.send(:include, InstanceMethods) + base.class_eval do + unloadable + alias_method_chain :find_current_user, :screenshot + end + end + + module InstanceMethods + def find_current_user_with_screenshot + if params[:controller] == "attach_screenshot" && request.post? && params[:key].present? + User.find_by_rss_key(params[:key]) + else + find_current_user_without_screenshot + end + end + end + end +end diff --git a/lib/attachment_hook.rb b/lib/attachment_hook.rb new file mode 100644 index 0000000..4d246db --- /dev/null +++ b/lib/attachment_hook.rb @@ -0,0 +1,76 @@ +require 'redmine' + +module AttachmentHook + class Hooks < Redmine::Hook::ViewListener + + def attach_screenshots (context ={}) + issue = context[:issue] + journal = context[:journal] + params = context[:params] + + unsaved = [] + screenshots = params[:screenshots] + if screenshots && screenshots.is_a?(Hash) + screenshots.each_pair do |key, screenshot| + key = key.gsub("thumb", "screenshot") + path = "#{RAILS_ROOT}/tmp/" + key + file = File.open(path, "rb") + + def file.init(name) + @screenshot_name = name + end + + def file.content_type + "image/png" + end + + def file.size + File.size(path()) + end + + def file.original_filename + @screenshot_name + end + + file.init(key) + + next unless file && file.size > 0 + a = Attachment.create(:container => issue, + :file => file, + :description => screenshot['description'].to_s.strip, + :author => User.current) + + file.close() + File.delete(path) + key = key.gsub("screenshot", "thumb") + path = "#{RAILS_ROOT}/tmp/" + key + begin + File.delete(path) + rescue + end + if a.new_record? + unsaved << a + else + if journal + journal.details << JournalDetail.new(:property => 'attachment', + :prop_key => a.id, + :value => a.filename) + end + end + end + if unsaved.any? + flash[:warning] = l(:warning_attachments_not_saved, unsaved.size) + end + end + end + + def controller_issues_edit_before_save (context ={}) + attach_screenshots(context) + end + + def controller_issues_new_after_save (context ={}) + attach_screenshots(context) + end + + end +end \ No newline at end of file diff --git a/lib/cleanup_tmp.rb b/lib/cleanup_tmp.rb new file mode 100644 index 0000000..19e895e --- /dev/null +++ b/lib/cleanup_tmp.rb @@ -0,0 +1,37 @@ +require 'account_controller' +require 'ftools' +require 'find' + +module AttachScreenshotPlugin + module CleanupTmp + def self.included(base) # :nodoc: + base.send(:include, InstanceMethods) + + base.class_eval do + alias_method_chain :logout, :cleanup + end + end + + module InstanceMethods + class ApplicationControllerPatchRoutes < Redmine::Hook::Listener + def controller_account_success_authentication_after(params) + InstanceMethods.cleanup + end + end + + def self.cleanup + ss = sprintf('%d_', User.current.id) + Find.find("#{RAILS_ROOT}/tmp/") do |f| + if (f[ss]!=nil) + File.delete(f) + end + end + end + + def logout_with_cleanup + InstanceMethods.cleanup + logout_without_cleanup + end + end + end +end