##// END OF EJS Templates
Adds url and author_url plugin attributes (#2162)....
Jean-Philippe Lang -
r2039:5362a85f2bd7
parent child
Show More
@@ -1,17 +1,19
1 1 <h2><%= l(:label_plugins) %></h2>
2 2
3 3 <% if @plugins.any? %>
4 <table class="list">
4 <table class="list plugins">
5 5 <% @plugins.each do |plugin| %>
6 6 <tr class="<%= cycle('odd', 'even') %>">
7 <td><%=h plugin.name %></td>
8 <td><%=h plugin.description %></td>
9 <td><%=h plugin.author %></td>
10 <td><%=h plugin.version %></td>
11 <td><%= link_to(l(:button_configure), :controller => 'settings', :action => 'plugin', :id => plugin.id) if plugin.configurable? %></td>
7 <td><span class="name"><%=h plugin.name %></span>
8 <%= content_tag('span', h(plugin.description), :class => 'description') unless plugin.description.blank? %>
9 <%= content_tag('span', link_to(h(plugin.url), plugin.url), :class => 'url') unless plugin.url.blank? %>
10 </td>
11 <td class="author"><%= plugin.author_url.blank? ? h(plugin.author) : link_to(h(plugin.author), plugin.author_url) %></td>
12 <td class="version"><%=h plugin.version %></td>
13 <td class="configure"><%= link_to(l(:button_configure), :controller => 'settings', :action => 'plugin', :id => plugin.id) if plugin.configurable? %></td>
12 14 </tr>
13 15 <% end %>
14 16 </table>
15 17 <% else %>
16 18 <p class="nodata"><%= l(:label_no_data) %></p>
17 19 <% end %>
@@ -1,197 +1,197
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 module Redmine #:nodoc:
19 19
20 20 class PluginNotFound < StandardError; end
21 21
22 22 # Base class for Redmine plugins.
23 23 # Plugins are registered using the <tt>register</tt> class method that acts as the public constructor.
24 24 #
25 25 # Redmine::Plugin.register :example do
26 26 # name 'Example plugin'
27 27 # author 'John Smith'
28 28 # description 'This is an example plugin for Redmine'
29 29 # version '0.0.1'
30 30 # settings :default => {'foo'=>'bar'}, :partial => 'settings/settings'
31 31 # end
32 32 #
33 33 # === Plugin attributes
34 34 #
35 35 # +settings+ is an optional attribute that let the plugin be configurable.
36 36 # It must be a hash with the following keys:
37 37 # * <tt>:default</tt>: default value for the plugin settings
38 38 # * <tt>:partial</tt>: path of the configuration partial view, relative to the plugin <tt>app/views</tt> directory
39 39 # Example:
40 40 # settings :default => {'foo'=>'bar'}, :partial => 'settings/settings'
41 41 # In this example, the settings partial will be found here in the plugin directory: <tt>app/views/settings/_settings.rhtml</tt>.
42 42 #
43 43 # When rendered, the plugin settings value is available as the local variable +settings+
44 44 class Plugin
45 45 @registered_plugins = {}
46 46 class << self
47 47 attr_reader :registered_plugins
48 48 private :new
49 49
50 50 def def_field(*names)
51 51 class_eval do
52 52 names.each do |name|
53 53 define_method(name) do |*args|
54 54 args.empty? ? instance_variable_get("@#{name}") : instance_variable_set("@#{name}", *args)
55 55 end
56 56 end
57 57 end
58 58 end
59 59 end
60 def_field :name, :description, :author, :version, :settings
60 def_field :name, :description, :url, :author, :author_url, :version, :settings
61 61 attr_reader :id
62 62
63 63 # Plugin constructor
64 64 def self.register(id, &block)
65 65 p = new(id)
66 66 p.instance_eval(&block)
67 67 # Set a default name if it was not provided during registration
68 68 p.name(id.to_s.humanize) if p.name.nil?
69 69 registered_plugins[id] = p
70 70 end
71 71
72 72 # Returns an array off all registered plugins
73 73 def self.all
74 74 registered_plugins.values.sort
75 75 end
76 76
77 77 # Finds a plugin by its id
78 78 # Returns a PluginNotFound exception if the plugin doesn't exist
79 79 def self.find(id)
80 80 registered_plugins[id.to_sym] || raise(PluginNotFound)
81 81 end
82 82
83 83 # Clears the registered plugins hash
84 84 # It doesn't unload installed plugins
85 85 def self.clear
86 86 @registered_plugins = {}
87 87 end
88 88
89 89 def initialize(id)
90 90 @id = id.to_sym
91 91 end
92 92
93 93 def <=>(plugin)
94 94 self.id.to_s <=> plugin.id.to_s
95 95 end
96 96
97 97 # Adds an item to the given +menu+.
98 98 # The +id+ parameter (equals to the project id) is automatically added to the url.
99 99 # menu :project_menu, :plugin_example, { :controller => 'example', :action => 'say_hello' }, :caption => 'Sample'
100 100 #
101 101 # +name+ parameter can be: :top_menu, :account_menu, :application_menu or :project_menu
102 102 #
103 103 def menu(menu, item, url, options={})
104 104 Redmine::MenuManager.map(menu).push(item, url, options)
105 105 end
106 106 alias :add_menu_item :menu
107 107
108 108 # Removes +item+ from the given +menu+.
109 109 def delete_menu_item(menu, item)
110 110 Redmine::MenuManager.map(menu).delete(item)
111 111 end
112 112
113 113 # Defines a permission called +name+ for the given +actions+.
114 114 #
115 115 # The +actions+ argument is a hash with controllers as keys and actions as values (a single value or an array):
116 116 # permission :destroy_contacts, { :contacts => :destroy }
117 117 # permission :view_contacts, { :contacts => [:index, :show] }
118 118 #
119 119 # The +options+ argument can be used to make the permission public (implicitly given to any user)
120 120 # or to restrict users the permission can be given to.
121 121 #
122 122 # Examples
123 123 # # A permission that is implicitly given to any user
124 124 # # This permission won't appear on the Roles & Permissions setup screen
125 125 # permission :say_hello, { :example => :say_hello }, :public => true
126 126 #
127 127 # # A permission that can be given to any user
128 128 # permission :say_hello, { :example => :say_hello }
129 129 #
130 130 # # A permission that can be given to registered users only
131 131 # permission :say_hello, { :example => :say_hello }, :require => loggedin
132 132 #
133 133 # # A permission that can be given to project members only
134 134 # permission :say_hello, { :example => :say_hello }, :require => member
135 135 def permission(name, actions, options = {})
136 136 if @project_module
137 137 Redmine::AccessControl.map {|map| map.project_module(@project_module) {|map|map.permission(name, actions, options)}}
138 138 else
139 139 Redmine::AccessControl.map {|map| map.permission(name, actions, options)}
140 140 end
141 141 end
142 142
143 143 # Defines a project module, that can be enabled/disabled for each project.
144 144 # Permissions defined inside +block+ will be bind to the module.
145 145 #
146 146 # project_module :things do
147 147 # permission :view_contacts, { :contacts => [:list, :show] }, :public => true
148 148 # permission :destroy_contacts, { :contacts => :destroy }
149 149 # end
150 150 def project_module(name, &block)
151 151 @project_module = name
152 152 self.instance_eval(&block)
153 153 @project_module = nil
154 154 end
155 155
156 156 # Registers an activity provider.
157 157 #
158 158 # Options:
159 159 # * <tt>:class_name</tt> - one or more model(s) that provide these events (inferred from event_type by default)
160 160 # * <tt>:default</tt> - setting this option to false will make the events not displayed by default
161 161 #
162 162 # A model can provide several activity event types.
163 163 #
164 164 # Examples:
165 165 # register :news
166 166 # register :scrums, :class_name => 'Meeting'
167 167 # register :issues, :class_name => ['Issue', 'Journal']
168 168 #
169 169 # Retrieving events:
170 170 # Associated model(s) must implement the find_events class method.
171 171 # ActiveRecord models can use acts_as_activity_provider as a way to implement this class method.
172 172 #
173 173 # The following call should return all the scrum events visible by current user that occured in the 5 last days:
174 174 # Meeting.find_events('scrums', User.current, 5.days.ago, Date.today)
175 175 # Meeting.find_events('scrums', User.current, 5.days.ago, Date.today, :project => foo) # events for project foo only
176 176 #
177 177 # Note that :view_scrums permission is required to view these events in the activity view.
178 178 def activity_provider(*args)
179 179 Redmine::Activity.register(*args)
180 180 end
181 181
182 182 # Registers a wiki formatter.
183 183 #
184 184 # Parameters:
185 185 # * +name+ - human-readable name
186 186 # * +formatter+ - formatter class, which should have an instance method +to_html+
187 187 # * +helper+ - helper module, which will be included by wiki pages
188 188 def wiki_format_provider(name, formatter, helper)
189 189 Redmine::WikiFormatting.register(name, formatter, helper)
190 190 end
191 191
192 192 # Returns +true+ if the plugin can be configured.
193 193 def configurable?
194 194 settings && settings.is_a?(Hash) && !settings[:partial].blank?
195 195 end
196 196 end
197 197 end
@@ -1,665 +1,671
1 1 body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; }
2 2
3 3 h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;}
4 4 h1 {margin:0; padding:0; font-size: 24px;}
5 5 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
6 6 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
7 7 h4, .wiki h3 {font-size: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
8 8
9 9 /***** Layout *****/
10 10 #wrapper {background: white;}
11 11
12 12 #top-menu {background: #2C4056; color: #fff; height:1.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;}
13 13 #top-menu ul {margin: 0; padding: 0;}
14 14 #top-menu li {
15 15 float:left;
16 16 list-style-type:none;
17 17 margin: 0px 0px 0px 0px;
18 18 padding: 0px 0px 0px 0px;
19 19 white-space:nowrap;
20 20 }
21 21 #top-menu a {color: #fff; padding-right: 8px; font-weight: bold;}
22 22 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
23 23
24 24 #account {float:right;}
25 25
26 26 #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
27 27 #header a {color:#f8f8f8;}
28 28 #quick-search {float:right;}
29 29
30 30 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;}
31 31 #main-menu ul {margin: 0; padding: 0;}
32 32 #main-menu li {
33 33 float:left;
34 34 list-style-type:none;
35 35 margin: 0px 2px 0px 0px;
36 36 padding: 0px 0px 0px 0px;
37 37 white-space:nowrap;
38 38 }
39 39 #main-menu li a {
40 40 display: block;
41 41 color: #fff;
42 42 text-decoration: none;
43 43 font-weight: bold;
44 44 margin: 0;
45 45 padding: 4px 10px 4px 10px;
46 46 }
47 47 #main-menu li a:hover {background:#759FCF; color:#fff;}
48 48 #main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
49 49
50 50 #main {background-color:#EEEEEE;}
51 51
52 52 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
53 53 * html #sidebar{ width: 17%; }
54 54 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
55 55 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
56 56 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
57 57
58 58 #content { width: 80%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; height:600px; min-height: 600px;}
59 59 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
60 60 html>body #content { height: auto; min-height: 600px; overflow: auto; }
61 61
62 62 #main.nosidebar #sidebar{ display: none; }
63 63 #main.nosidebar #content{ width: auto; border-right: 0; }
64 64
65 65 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
66 66
67 67 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
68 68 #login-form table td {padding: 6px;}
69 69 #login-form label {font-weight: bold;}
70 70
71 71 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
72 72
73 73 /***** Links *****/
74 74 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
75 75 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
76 76 a img{ border: 0; }
77 77
78 78 a.issue.closed { text-decoration: line-through; }
79 79
80 80 /***** Tables *****/
81 81 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
82 82 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
83 83 table.list td { vertical-align: top; }
84 84 table.list td.id { width: 2%; text-align: center;}
85 85 table.list td.checkbox { width: 15px; padding: 0px;}
86 86
87 87 tr.issue { text-align: center; white-space: nowrap; }
88 88 tr.issue td.subject, tr.issue td.category, td.assigned_to { white-space: normal; }
89 89 tr.issue td.subject { text-align: left; }
90 90 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
91 91
92 92 tr.entry { border: 1px solid #f8f8f8; }
93 93 tr.entry td { white-space: nowrap; }
94 94 tr.entry td.filename { width: 30%; }
95 95 tr.entry td.size { text-align: right; font-size: 90%; }
96 96 tr.entry td.revision, tr.entry td.author { text-align: center; }
97 97 tr.entry td.age { text-align: right; }
98 98
99 99 tr.entry span.expander {background-image: url(../images/bullet_toggle_plus.png); padding-left: 8px; margin-left: 0; cursor: pointer;}
100 100 tr.entry.open span.expander {background-image: url(../images/bullet_toggle_minus.png);}
101 101 tr.entry.file td.filename a { margin-left: 16px; }
102 102
103 103 tr.changeset td.author { text-align: center; width: 15%; }
104 104 tr.changeset td.committed_on { text-align: center; width: 15%; }
105 105
106 106 tr.message { height: 2.6em; }
107 107 tr.message td.last_message { font-size: 80%; }
108 108 tr.message.locked td.subject a { background-image: url(../images/locked.png); }
109 109 tr.message.sticky td.subject a { background-image: url(../images/sticky.png); font-weight: bold; }
110 110
111 111 tr.user td { width:13%; }
112 112 tr.user td.email { width:18%; }
113 113 tr.user td { white-space: nowrap; }
114 114 tr.user.locked, tr.user.registered { color: #aaa; }
115 115 tr.user.locked a, tr.user.registered a { color: #aaa; }
116 116
117 117 tr.time-entry { text-align: center; white-space: nowrap; }
118 118 tr.time-entry td.subject, tr.time-entry td.comments { text-align: left; white-space: normal; }
119 119 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
120 120 td.hours .hours-dec { font-size: 0.9em; }
121 121
122 table.plugins td { vertical-align: middle; }
123 table.plugins td.configure { text-align: right; padding-right: 1em; }
124 table.plugins span.name { font-weight: bold; display: block; margin-bottom: 6px; }
125 table.plugins span.description { display: block; font-size: 0.9em; }
126 table.plugins span.url { display: block; font-size: 0.9em; }
127
122 128 table.list tbody tr:hover { background-color:#ffffdd; }
123 129 table td {padding:2px;}
124 130 table p {margin:0;}
125 131 .odd {background-color:#f6f7f8;}
126 132 .even {background-color: #fff;}
127 133
128 134 .highlight { background-color: #FCFD8D;}
129 135 .highlight.token-1 { background-color: #faa;}
130 136 .highlight.token-2 { background-color: #afa;}
131 137 .highlight.token-3 { background-color: #aaf;}
132 138
133 139 .box{
134 140 padding:6px;
135 141 margin-bottom: 10px;
136 142 background-color:#f6f6f6;
137 143 color:#505050;
138 144 line-height:1.5em;
139 145 border: 1px solid #e4e4e4;
140 146 }
141 147
142 148 div.square {
143 149 border: 1px solid #999;
144 150 float: left;
145 151 margin: .3em .4em 0 .4em;
146 152 overflow: hidden;
147 153 width: .6em; height: .6em;
148 154 }
149 155 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
150 156 .contextual input {font-size:0.9em;}
151 157
152 158 .splitcontentleft{float:left; width:49%;}
153 159 .splitcontentright{float:right; width:49%;}
154 160 form {display: inline;}
155 161 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
156 162 fieldset {border: 1px solid #e4e4e4; margin:0;}
157 163 legend {color: #484848;}
158 164 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
159 165 blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;}
160 166 blockquote blockquote { margin-left: 0;}
161 167 textarea.wiki-edit { width: 99%; }
162 168 li p {margin-top: 0;}
163 169 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
164 170 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
165 171 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
166 172 p.footnote { font-size: 0.9em; margin-top: 0px; margin-bottom: 0px; }
167 173
168 174 fieldset#filters, fieldset#date-range { padding: 0.7em; margin-bottom: 8px; }
169 175 fieldset#filters p { margin: 1.2em 0 0.8em 2px; }
170 176 fieldset#filters table { border-collapse: collapse; }
171 177 fieldset#filters table td { padding: 0; vertical-align: middle; }
172 178 fieldset#filters tr.filter { height: 2em; }
173 179 fieldset#filters td.add-filter { text-align: right; vertical-align: top; }
174 180 .buttons { font-size: 0.9em; }
175 181
176 182 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
177 183 div#issue-changesets .changeset { padding: 4px;}
178 184 div#issue-changesets .changeset { border-bottom: 1px solid #ddd; }
179 185 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
180 186
181 187 div#activity dl, #search-results { margin-left: 2em; }
182 188 div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
183 189 div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
184 190 div#activity dt.me .time { border-bottom: 1px solid #999; }
185 191 div#activity dt .time { color: #777; font-size: 80%; }
186 192 div#activity dd .description, #search-results dd .description { font-style: italic; }
187 193 div#activity span.project:after, #search-results span.project:after { content: " -"; }
188 194 div#activity dd span.description, #search-results dd span.description { display:block; }
189 195
190 196 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
191 197 div#search-results-counts {float:right;}
192 198 div#search-results-counts ul { margin-top: 0.5em; }
193 199 div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; }
194 200
195 201 dt.issue { background-image: url(../images/ticket.png); }
196 202 dt.issue-edit { background-image: url(../images/ticket_edit.png); }
197 203 dt.issue-closed { background-image: url(../images/ticket_checked.png); }
198 204 dt.issue-note { background-image: url(../images/ticket_note.png); }
199 205 dt.changeset { background-image: url(../images/changeset.png); }
200 206 dt.news { background-image: url(../images/news.png); }
201 207 dt.message { background-image: url(../images/message.png); }
202 208 dt.reply { background-image: url(../images/comments.png); }
203 209 dt.wiki-page { background-image: url(../images/wiki_edit.png); }
204 210 dt.attachment { background-image: url(../images/attachment.png); }
205 211 dt.document { background-image: url(../images/document.png); }
206 212 dt.project { background-image: url(../images/projects.png); }
207 213
208 214 div#roadmap fieldset.related-issues { margin-bottom: 1em; }
209 215 div#roadmap fieldset.related-issues ul { margin-top: 0.3em; margin-bottom: 0.3em; }
210 216 div#roadmap .wiki h1:first-child { display: none; }
211 217 div#roadmap .wiki h1 { font-size: 120%; }
212 218 div#roadmap .wiki h2 { font-size: 110%; }
213 219
214 220 div#version-summary { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
215 221 div#version-summary fieldset { margin-bottom: 1em; }
216 222 div#version-summary .total-hours { text-align: right; }
217 223
218 224 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
219 225 table#time-report tbody tr { font-style: italic; color: #777; }
220 226 table#time-report tbody tr.last-level { font-style: normal; color: #555; }
221 227 table#time-report tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
222 228 table#time-report .hours-dec { font-size: 0.9em; }
223 229
224 230 ul.properties {padding:0; font-size: 0.9em; color: #777;}
225 231 ul.properties li {list-style-type:none;}
226 232 ul.properties li span {font-style:italic;}
227 233
228 234 .total-hours { font-size: 110%; font-weight: bold; }
229 235 .total-hours span.hours-int { font-size: 120%; }
230 236
231 237 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;}
232 238 #user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
233 239
234 240 .pagination {font-size: 90%}
235 241 p.pagination {margin-top:8px;}
236 242
237 243 /***** Tabular forms ******/
238 244 .tabular p{
239 245 margin: 0;
240 246 padding: 5px 0 8px 0;
241 247 padding-left: 180px; /*width of left column containing the label elements*/
242 248 height: 1%;
243 249 clear:left;
244 250 }
245 251
246 252 html>body .tabular p {overflow:hidden;}
247 253
248 254 .tabular label{
249 255 font-weight: bold;
250 256 float: left;
251 257 text-align: right;
252 258 margin-left: -180px; /*width of left column*/
253 259 width: 175px; /*width of labels. Should be smaller than left column to create some right
254 260 margin*/
255 261 }
256 262
257 263 .tabular label.floating{
258 264 font-weight: normal;
259 265 margin-left: 0px;
260 266 text-align: left;
261 267 width: 270px;
262 268 }
263 269
264 270 input#time_entry_comments { width: 90%;}
265 271
266 272 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
267 273
268 274 .tabular.settings p{ padding-left: 300px; }
269 275 .tabular.settings label{ margin-left: -300px; width: 295px; }
270 276
271 277 .required {color: #bb0000;}
272 278 .summary {font-style: italic;}
273 279
274 280 #attachments_fields input[type=text] {margin-left: 8px; }
275 281
276 282 div.attachments { margin-top: 12px; }
277 283 div.attachments p { margin:4px 0 2px 0; }
278 284 div.attachments img { vertical-align: middle; }
279 285 div.attachments span.author { font-size: 0.9em; color: #888; }
280 286
281 287 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
282 288 .other-formats span + span:before { content: "| "; }
283 289
284 290 a.feed { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
285 291
286 292 /***** Flash & error messages ****/
287 293 #errorExplanation, div.flash, .nodata, .warning {
288 294 padding: 4px 4px 4px 30px;
289 295 margin-bottom: 12px;
290 296 font-size: 1.1em;
291 297 border: 2px solid;
292 298 }
293 299
294 300 div.flash {margin-top: 8px;}
295 301
296 302 div.flash.error, #errorExplanation {
297 303 background: url(../images/false.png) 8px 5px no-repeat;
298 304 background-color: #ffe3e3;
299 305 border-color: #dd0000;
300 306 color: #550000;
301 307 }
302 308
303 309 div.flash.notice {
304 310 background: url(../images/true.png) 8px 5px no-repeat;
305 311 background-color: #dfffdf;
306 312 border-color: #9fcf9f;
307 313 color: #005f00;
308 314 }
309 315
310 316 .nodata, .warning {
311 317 text-align: center;
312 318 background-color: #FFEBC1;
313 319 border-color: #FDBF3B;
314 320 color: #A6750C;
315 321 }
316 322
317 323 #errorExplanation ul { font-size: 0.9em;}
318 324
319 325 /***** Ajax indicator ******/
320 326 #ajax-indicator {
321 327 position: absolute; /* fixed not supported by IE */
322 328 background-color:#eee;
323 329 border: 1px solid #bbb;
324 330 top:35%;
325 331 left:40%;
326 332 width:20%;
327 333 font-weight:bold;
328 334 text-align:center;
329 335 padding:0.6em;
330 336 z-index:100;
331 337 filter:alpha(opacity=50);
332 338 opacity: 0.5;
333 339 }
334 340
335 341 html>body #ajax-indicator { position: fixed; }
336 342
337 343 #ajax-indicator span {
338 344 background-position: 0% 40%;
339 345 background-repeat: no-repeat;
340 346 background-image: url(../images/loading.gif);
341 347 padding-left: 26px;
342 348 vertical-align: bottom;
343 349 }
344 350
345 351 /***** Calendar *****/
346 352 table.cal {border-collapse: collapse; width: 100%; margin: 0px 0 6px 0;border: 1px solid #d7d7d7;}
347 353 table.cal thead th {width: 14%;}
348 354 table.cal tbody tr {height: 100px;}
349 355 table.cal th { background-color:#EEEEEE; padding: 4px; }
350 356 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
351 357 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
352 358 table.cal td.odd p.day-num {color: #bbb;}
353 359 table.cal td.today {background:#ffffdd;}
354 360 table.cal td.today p.day-num {font-weight: bold;}
355 361
356 362 /***** Tooltips ******/
357 363 .tooltip{position:relative;z-index:24;}
358 364 .tooltip:hover{z-index:25;color:#000;}
359 365 .tooltip span.tip{display: none; text-align:left;}
360 366
361 367 div.tooltip:hover span.tip{
362 368 display:block;
363 369 position:absolute;
364 370 top:12px; left:24px; width:270px;
365 371 border:1px solid #555;
366 372 background-color:#fff;
367 373 padding: 4px;
368 374 font-size: 0.8em;
369 375 color:#505050;
370 376 }
371 377
372 378 /***** Progress bar *****/
373 379 table.progress {
374 380 border: 1px solid #D7D7D7;
375 381 border-collapse: collapse;
376 382 border-spacing: 0pt;
377 383 empty-cells: show;
378 384 text-align: center;
379 385 float:left;
380 386 margin: 1px 6px 1px 0px;
381 387 }
382 388
383 389 table.progress td { height: 0.9em; }
384 390 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
385 391 table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
386 392 table.progress td.open { background: #FFF none repeat scroll 0%; }
387 393 p.pourcent {font-size: 80%;}
388 394 p.progress-info {clear: left; font-style: italic; font-size: 80%;}
389 395
390 396 /***** Tabs *****/
391 397 #content .tabs {height: 2.6em; border-bottom: 1px solid #bbbbbb; margin-bottom:1.2em; position:relative;}
392 398 #content .tabs ul {margin:0; position:absolute; bottom:-2px; padding-left:1em;}
393 399 #content .tabs>ul { bottom:-1px; } /* others */
394 400 #content .tabs ul li {
395 401 float:left;
396 402 list-style-type:none;
397 403 white-space:nowrap;
398 404 margin-right:8px;
399 405 background:#fff;
400 406 }
401 407 #content .tabs ul li a{
402 408 display:block;
403 409 font-size: 0.9em;
404 410 text-decoration:none;
405 411 line-height:1.3em;
406 412 padding:4px 6px 4px 6px;
407 413 border: 1px solid #ccc;
408 414 border-bottom: 1px solid #bbbbbb;
409 415 background-color: #eeeeee;
410 416 color:#777;
411 417 font-weight:bold;
412 418 }
413 419
414 420 #content .tabs ul li a:hover {
415 421 background-color: #ffffdd;
416 422 text-decoration:none;
417 423 }
418 424
419 425 #content .tabs ul li a.selected {
420 426 background-color: #fff;
421 427 border: 1px solid #bbbbbb;
422 428 border-bottom: 1px solid #fff;
423 429 }
424 430
425 431 #content .tabs ul li a.selected:hover {
426 432 background-color: #fff;
427 433 }
428 434
429 435 /***** Diff *****/
430 436 .diff_out { background: #fcc; }
431 437 .diff_in { background: #cfc; }
432 438
433 439 /***** Wiki *****/
434 440 div.wiki table {
435 441 border: 1px solid #505050;
436 442 border-collapse: collapse;
437 443 margin-bottom: 1em;
438 444 }
439 445
440 446 div.wiki table, div.wiki td, div.wiki th {
441 447 border: 1px solid #bbb;
442 448 padding: 4px;
443 449 }
444 450
445 451 div.wiki .external {
446 452 background-position: 0% 60%;
447 453 background-repeat: no-repeat;
448 454 padding-left: 12px;
449 455 background-image: url(../images/external.png);
450 456 }
451 457
452 458 div.wiki a.new {
453 459 color: #b73535;
454 460 }
455 461
456 462 div.wiki pre {
457 463 margin: 1em 1em 1em 1.6em;
458 464 padding: 2px;
459 465 background-color: #fafafa;
460 466 border: 1px solid #dadada;
461 467 width:95%;
462 468 overflow-x: auto;
463 469 }
464 470
465 471 div.wiki ul.toc {
466 472 background-color: #ffffdd;
467 473 border: 1px solid #e4e4e4;
468 474 padding: 4px;
469 475 line-height: 1.2em;
470 476 margin-bottom: 12px;
471 477 margin-right: 12px;
472 478 margin-left: 0;
473 479 display: table
474 480 }
475 481 * html div.wiki ul.toc { width: 50%; } /* IE6 doesn't autosize div */
476 482
477 483 div.wiki ul.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
478 484 div.wiki ul.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
479 485 div.wiki ul.toc li { list-style-type:none;}
480 486 div.wiki ul.toc li.heading2 { margin-left: 6px; }
481 487 div.wiki ul.toc li.heading3 { margin-left: 12px; font-size: 0.8em; }
482 488
483 489 div.wiki ul.toc a {
484 490 font-size: 0.9em;
485 491 font-weight: normal;
486 492 text-decoration: none;
487 493 color: #606060;
488 494 }
489 495 div.wiki ul.toc a:hover { color: #c61a1a; text-decoration: underline;}
490 496
491 497 a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
492 498 a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
493 499 h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
494 500
495 501 /***** My page layout *****/
496 502 .block-receiver {
497 503 border:1px dashed #c0c0c0;
498 504 margin-bottom: 20px;
499 505 padding: 15px 0 15px 0;
500 506 }
501 507
502 508 .mypage-box {
503 509 margin:0 0 20px 0;
504 510 color:#505050;
505 511 line-height:1.5em;
506 512 }
507 513
508 514 .handle {
509 515 cursor: move;
510 516 }
511 517
512 518 a.close-icon {
513 519 display:block;
514 520 margin-top:3px;
515 521 overflow:hidden;
516 522 width:12px;
517 523 height:12px;
518 524 background-repeat: no-repeat;
519 525 cursor:pointer;
520 526 background-image:url('../images/close.png');
521 527 }
522 528
523 529 a.close-icon:hover {
524 530 background-image:url('../images/close_hl.png');
525 531 }
526 532
527 533 /***** Gantt chart *****/
528 534 .gantt_hdr {
529 535 position:absolute;
530 536 top:0;
531 537 height:16px;
532 538 border-top: 1px solid #c0c0c0;
533 539 border-bottom: 1px solid #c0c0c0;
534 540 border-right: 1px solid #c0c0c0;
535 541 text-align: center;
536 542 overflow: hidden;
537 543 }
538 544
539 545 .task {
540 546 position: absolute;
541 547 height:8px;
542 548 font-size:0.8em;
543 549 color:#888;
544 550 padding:0;
545 551 margin:0;
546 552 line-height:0.8em;
547 553 }
548 554
549 555 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
550 556 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
551 557 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
552 558 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
553 559
554 560 /***** Icons *****/
555 561 .icon {
556 562 background-position: 0% 40%;
557 563 background-repeat: no-repeat;
558 564 padding-left: 20px;
559 565 padding-top: 2px;
560 566 padding-bottom: 3px;
561 567 }
562 568
563 569 .icon22 {
564 570 background-position: 0% 40%;
565 571 background-repeat: no-repeat;
566 572 padding-left: 26px;
567 573 line-height: 22px;
568 574 vertical-align: middle;
569 575 }
570 576
571 577 .icon-add { background-image: url(../images/add.png); }
572 578 .icon-edit { background-image: url(../images/edit.png); }
573 579 .icon-copy { background-image: url(../images/copy.png); }
574 580 .icon-del { background-image: url(../images/delete.png); }
575 581 .icon-move { background-image: url(../images/move.png); }
576 582 .icon-save { background-image: url(../images/save.png); }
577 583 .icon-cancel { background-image: url(../images/cancel.png); }
578 584 .icon-file { background-image: url(../images/file.png); }
579 585 .icon-folder { background-image: url(../images/folder.png); }
580 586 .open .icon-folder { background-image: url(../images/folder_open.png); }
581 587 .icon-package { background-image: url(../images/package.png); }
582 588 .icon-home { background-image: url(../images/home.png); }
583 589 .icon-user { background-image: url(../images/user.png); }
584 590 .icon-mypage { background-image: url(../images/user_page.png); }
585 591 .icon-admin { background-image: url(../images/admin.png); }
586 592 .icon-projects { background-image: url(../images/projects.png); }
587 593 .icon-help { background-image: url(../images/help.png); }
588 594 .icon-attachment { background-image: url(../images/attachment.png); }
589 595 .icon-index { background-image: url(../images/index.png); }
590 596 .icon-history { background-image: url(../images/history.png); }
591 597 .icon-time { background-image: url(../images/time.png); }
592 598 .icon-stats { background-image: url(../images/stats.png); }
593 599 .icon-warning { background-image: url(../images/warning.png); }
594 600 .icon-fav { background-image: url(../images/fav.png); }
595 601 .icon-fav-off { background-image: url(../images/fav_off.png); }
596 602 .icon-reload { background-image: url(../images/reload.png); }
597 603 .icon-lock { background-image: url(../images/locked.png); }
598 604 .icon-unlock { background-image: url(../images/unlock.png); }
599 605 .icon-checked { background-image: url(../images/true.png); }
600 606 .icon-details { background-image: url(../images/zoom_in.png); }
601 607 .icon-report { background-image: url(../images/report.png); }
602 608 .icon-comment { background-image: url(../images/comment.png); }
603 609
604 610 .icon22-projects { background-image: url(../images/22x22/projects.png); }
605 611 .icon22-users { background-image: url(../images/22x22/users.png); }
606 612 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
607 613 .icon22-role { background-image: url(../images/22x22/role.png); }
608 614 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
609 615 .icon22-options { background-image: url(../images/22x22/options.png); }
610 616 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
611 617 .icon22-authent { background-image: url(../images/22x22/authent.png); }
612 618 .icon22-info { background-image: url(../images/22x22/info.png); }
613 619 .icon22-comment { background-image: url(../images/22x22/comment.png); }
614 620 .icon22-package { background-image: url(../images/22x22/package.png); }
615 621 .icon22-settings { background-image: url(../images/22x22/settings.png); }
616 622 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
617 623
618 624 img.gravatar {
619 625 padding: 2px;
620 626 border: solid 1px #d5d5d5;
621 627 background: #fff;
622 628 }
623 629
624 630 div.issue img.gravatar {
625 631 float: right;
626 632 margin: 0 0 0 1em;
627 633 padding: 5px;
628 634 }
629 635
630 636 div.issue table img.gravatar {
631 637 height: 14px;
632 638 width: 14px;
633 639 padding: 2px;
634 640 float: left;
635 641 margin: 0 0.5em 0 0;
636 642 }
637 643
638 644 #history img.gravatar {
639 645 padding: 3px;
640 646 margin: 0 1.5em 1em 0;
641 647 float: left;
642 648 }
643 649
644 650 td.username img.gravatar {
645 651 float: left;
646 652 margin: 0 1em 0 0;
647 653 }
648 654
649 655 #activity dt img.gravatar {
650 656 float: left;
651 657 margin: 0 1em 1em 0;
652 658 }
653 659
654 660 #activity dt,
655 661 .journal {
656 662 clear: left;
657 663 }
658 664
659 665
660 666 /***** Media print specific styles *****/
661 667 @media print {
662 668 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
663 669 #main { background: #fff; }
664 670 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; }
665 671 }
@@ -1,124 +1,124
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19 require 'admin_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class AdminController; def rescue_action(e) raise e end; end
23 23
24 24 class AdminControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :users, :roles
26 26
27 27 def setup
28 28 @controller = AdminController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 @request.session[:user_id] = 1 # admin
33 33 end
34 34
35 35 def test_index
36 36 get :index
37 37 assert_no_tag :tag => 'div',
38 38 :attributes => { :class => /nodata/ }
39 39 end
40 40
41 41 def test_index_with_no_configuration_data
42 42 delete_configuration_data
43 43 get :index
44 44 assert_tag :tag => 'div',
45 45 :attributes => { :class => /nodata/ }
46 46 end
47 47
48 48 def test_projects
49 49 get :projects
50 50 assert_response :success
51 51 assert_template 'projects'
52 52 assert_not_nil assigns(:projects)
53 53 # active projects only
54 54 assert_nil assigns(:projects).detect {|u| !u.active?}
55 55 end
56 56
57 57 def test_projects_with_name_filter
58 58 get :projects, :name => 'store', :status => ''
59 59 assert_response :success
60 60 assert_template 'projects'
61 61 projects = assigns(:projects)
62 62 assert_not_nil projects
63 63 assert_equal 1, projects.size
64 64 assert_equal 'OnlineStore', projects.first.name
65 65 end
66 66
67 67 def test_load_default_configuration_data
68 68 delete_configuration_data
69 69 post :default_configuration, :lang => 'fr'
70 70 assert IssueStatus.find_by_name('Nouveau')
71 71 end
72 72
73 73 def test_test_email
74 74 get :test_email
75 75 assert_redirected_to 'settings/edit'
76 76 mail = ActionMailer::Base.deliveries.last
77 77 assert_kind_of TMail::Mail, mail
78 78 user = User.find(1)
79 79 assert_equal [user.mail], mail.bcc
80 80 end
81 81
82 82 def test_no_plugins
83 83 Redmine::Plugin.clear
84 84
85 85 get :plugins
86 86 assert_response :success
87 87 assert_template 'plugins'
88 88 end
89 89
90 90 def test_plugins
91 91 # Register a few plugins
92 92 Redmine::Plugin.register :foo do
93 93 name 'Foo plugin'
94 94 author 'John Smith'
95 95 description 'This is a test plugin'
96 96 version '0.0.1'
97 97 settings :default => {'sample_setting' => 'value', 'foo'=>'bar'}, :partial => 'foo/settings'
98 98 end
99 99 Redmine::Plugin.register :bar do
100 100 end
101 101
102 102 get :plugins
103 103 assert_response :success
104 104 assert_template 'plugins'
105 105
106 assert_tag :td, :content => 'Foo plugin'
107 assert_tag :td, :content => 'Bar'
106 assert_tag :td, :child => { :tag => 'span', :content => 'Foo plugin' }
107 assert_tag :td, :child => { :tag => 'span', :content => 'Bar' }
108 108 end
109 109
110 110 def test_info
111 111 get :info
112 112 assert_response :success
113 113 assert_template 'info'
114 114 end
115 115
116 116 private
117 117
118 118 def delete_configuration_data
119 119 Role.delete_all('builtin = 0')
120 120 Tracker.delete_all
121 121 IssueStatus.delete_all
122 122 Enumeration.delete_all
123 123 end
124 124 end
@@ -1,51 +1,55
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../../../test_helper'
19 19
20 20 class Redmine::PluginTest < Test::Unit::TestCase
21 21
22 22 def setup
23 23 @klass = Redmine::Plugin
24 24 # In case some real plugins are installed
25 25 @klass.clear
26 26 end
27 27
28 28 def teardown
29 29 @klass.clear
30 30 end
31 31
32 32 def test_register
33 33 @klass.register :foo do
34 34 name 'Foo plugin'
35 url 'http://example.net/plugins/foo'
35 36 author 'John Smith'
37 author_url 'http://example.net/jsmith'
36 38 description 'This is a test plugin'
37 39 version '0.0.1'
38 40 settings :default => {'sample_setting' => 'value', 'foo'=>'bar'}, :partial => 'foo/settings'
39 41 end
40 42
41 43 assert_equal 1, @klass.all.size
42 44
43 45 plugin = @klass.find('foo')
44 46 assert plugin.is_a?(Redmine::Plugin)
45 47 assert_equal :foo, plugin.id
46 48 assert_equal 'Foo plugin', plugin.name
49 assert_equal 'http://example.net/plugins/foo', plugin.url
47 50 assert_equal 'John Smith', plugin.author
51 assert_equal 'http://example.net/jsmith', plugin.author_url
48 52 assert_equal 'This is a test plugin', plugin.description
49 53 assert_equal '0.0.1', plugin.version
50 54 end
51 55 end
General Comments 0
You need to be logged in to leave comments. Login now