Skip to content

Commit

Permalink
Original plugin, version 0.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-denizet committed Oct 12, 2011
0 parents commit 657d054
Show file tree
Hide file tree
Showing 14 changed files with 383 additions and 0 deletions.
24 changes: 24 additions & 0 deletions 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.
10 changes: 10 additions & 0 deletions 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)
38 changes: 38 additions & 0 deletions 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
2 changes: 2 additions & 0 deletions app/helpers/attach_screenshot_helper.rb
@@ -0,0 +1,2 @@
module AttachScreenshotHelper
end
90 changes: 90 additions & 0 deletions 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 %>
<span id="attachments_fields">
<%= file_field_tag 'attachments[1][file]', :size => 30, :id => nil -%><label class="inline"><span id="attachment_description_label_content"><%= l(:label_optional_description) %></span><%= text_field_tag 'attachments[1][description]', '', :size => 60, :id => nil %>
</label>
</span>
<br />
<small><%= 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) %>)
</small>
<br />
<br />
<% if @issue %>
<% if @issue.new_record? %>
<label>
<% end %>
<%= l(:label_screenshots) %>
<% if @issue.new_record? %>
</label>
<% else %>
<br />
<% end %>
<span id="attach_applet" style="display: none; position: fixed; top: 50px; left: 10px; border: solid 1px #BBB;"></span>
<span id="screenshots_fields">
<%= submit_tag l(:button_add), :onclick => 'showAttachScreen(); return false;'%>
</span>
<small><%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>
</small>
<br />
<span id="screenshots"></span>
<br />

<script type="text/javascript">
function showAttachScreen() {
document.getElementById('attach_applet').style.display = 'block';
document.getElementById('attach_applet').innerHTML = '<applet codebase="<%= url_for(:only_path => false, :controller => 'welcome')%>plugin_assets/redmine_attach_screenshot/javascripts/" code="com.axmor.redmine.uploader.Uploader.class" archive="uploader_applet.jar" height="600" width="700" MAYSCRIPT>'+
'<param name="label.image" value="<%= l(:label_image) %>">' +
'<param name="label.button.paste" value="<%= l(:label_button_paste) %>">' +
'<param name="label.button.attach" value="<%= l(:button_attach) %>">' +
'<param name="label.button.cancel" value="<%= l(:button_cancel) %>">' +
'<param name="error.close.msg" value="<%= l(:error_close_msg) %>">' +
'<param name="error.close.title" value="<%= l(:error_close_title) %>">' +
'<param name="attach.url" value="<%= url_for(:only_path => false, :controller => 'welcome')%>attach_screenshot">' +
'<param name="error.attach.msg" value="<%= l(:error_attach_msg) %>">' +
'<param name="error.attach.title" value="<%= l(:error_attach_title) %>">' +
'<param name="rss.key" value="<%= User.current.rss_key %>">' +
'Applet</applet>';
}
function addAttachScreen(fileId) {
var s = document.createElement("span");
s.id = fileId;
s.className = "screen_thumb";

var s1 = document.createElement("span");
s1.className = "st1";
var s2 = document.createElement("span");
s2.className = "st2";

var im = document.createElement("img");
im.src = "<%= url_for(:only_path => false, :controller => 'welcome')%>attach_screenshot?id=" + fileId;

var b = document.createElement("input");
b.type = "image";
b.src = "<%= url_for(:only_path => false, :controller => 'welcome')%>images/delete.png"
b.setAttribute("onClick", "deleteAttachScreen('" + fileId + "'); return false;");

var i = document.createElement("input");
i.type = "image";
i.src = "<%= url_for(:only_path => false, :controller => 'welcome')%>images/lightning.png";
i.setAttribute("onClick", "addLinkToAttachScreen('" + fileId + "'); return false;");

var d = document.createElement("input");
d.name = "screenshots[" + fileId + "][description]";
d.type = "text";
d.size = 15;

s1.appendChild(im);
s.appendChild(s1);
s2.appendChild(d);
s2.appendChild(b);
s2.appendChild(i);
s.appendChild(s2);

p = document.getElementById("screenshots");
p.appendChild(s);
}
</script>
<% end %>
15 changes: 15 additions & 0 deletions 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 +"!","");
}
}
Binary file added assets/javascripts/uploader_applet.jar
Binary file not shown.
30 changes: 30 additions & 0 deletions 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;
}
10 changes: 10 additions & 0 deletions 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
10 changes: 10 additions & 0 deletions 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: Ошибка при загрузке
17 changes: 17 additions & 0 deletions 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
24 changes: 24 additions & 0 deletions 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
76 changes: 76 additions & 0 deletions 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
37 changes: 37 additions & 0 deletions 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

0 comments on commit 657d054

Please sign in to comment.