Revision 1297:0a574315af3e .svn/pristine/08

View differences:

.svn/pristine/08/084dba3511eace99e8953d1d7626d2fa107595d7.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2012  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17

  
18
class NewsController < ApplicationController
19
  default_search_scope :news
20
  model_object News
21
  before_filter :find_model_object, :except => [:new, :create, :index]
22
  before_filter :find_project_from_association, :except => [:new, :create, :index]
23
  before_filter :find_project_by_project_id, :only => [:new, :create]
24
  before_filter :authorize, :except => [:index]
25
  before_filter :find_optional_project, :only => :index
26
  accept_rss_auth :index
27
  accept_api_auth :index
28

  
29
  helper :watchers
30
  helper :attachments
31

  
32
  def index
33
    case params[:format]
34
    when 'xml', 'json'
35
      @offset, @limit = api_offset_and_limit
36
    else
37
      @limit =  10
38
    end
39

  
40
    scope = @project ? @project.news.visible : News.visible
41

  
42
    @news_count = scope.count
43
    @news_pages = Paginator.new self, @news_count, @limit, params['page']
44
    @offset ||= @news_pages.current.offset
45
    @newss = scope.all(:include => [:author, :project],
46
                                       :order => "#{News.table_name}.created_on DESC",
47
                                       :offset => @offset,
48
                                       :limit => @limit)
49

  
50
    respond_to do |format|
51
      format.html {
52
        @news = News.new # for adding news inline
53
        render :layout => false if request.xhr?
54
      }
55
      format.api
56
      format.atom { render_feed(@newss, :title => (@project ? @project.name : Setting.app_title) + ": #{l(:label_news_plural)}") }
57
    end
58
  end
59

  
60
  def show
61
    @comments = @news.comments
62
    @comments.reverse! if User.current.wants_comments_in_reverse_order?
63
  end
64

  
65
  def new
66
    @news = News.new(:project => @project, :author => User.current)
67
  end
68

  
69
  def create
70
    @news = News.new(:project => @project, :author => User.current)
71
    @news.safe_attributes = params[:news]
72
    @news.save_attachments(params[:attachments])
73
    if @news.save
74
      render_attachment_warning_if_needed(@news)
75
      flash[:notice] = l(:notice_successful_create)
76
      redirect_to :controller => 'news', :action => 'index', :project_id => @project
77
    else
78
      render :action => 'new'
79
    end
80
  end
81

  
82
  def edit
83
  end
84

  
85
  def update
86
    @news.safe_attributes = params[:news]
87
    @news.save_attachments(params[:attachments])
88
    if @news.save
89
      render_attachment_warning_if_needed(@news)
90
      flash[:notice] = l(:notice_successful_update)
91
      redirect_to :action => 'show', :id => @news
92
    else
93
      render :action => 'edit'
94
    end
95
  end
96

  
97
  def destroy
98
    @news.destroy
99
    redirect_to :action => 'index', :project_id => @project
100
  end
101

  
102
  private
103

  
104
  def find_optional_project
105
    return true unless params[:project_id]
106
    @project = Project.find(params[:project_id])
107
    authorize
108
  rescue ActiveRecord::RecordNotFound
109
    render_404
110
  end
111
end
.svn/pristine/08/085f61120574ccfad3bedefd8a6623102e2bf32b.svn-base
1
<% content_for :header_tags do %>
2
  <%= javascript_include_tag 'repository_navigation' %>
3
<% end %>
4

  
5
<%= link_to l(:label_statistics),
6
            {:action => 'stats', :id => @project, :repository_id => @repository.identifier_param},
7
            :class => 'icon icon-stats' if @repository.supports_all_revisions? %>
8

  
9
<%= form_tag({:action => controller.action_name,
10
             :id => @project,
11
             :repository_id => @repository.identifier_param,
12
             :path => to_path_param(@path),
13
             :rev => nil},
14
            {:method => :get, :id => 'revision_selector'}) do -%>
15
  <!-- Branches Dropdown -->
16
  <% if !@repository.branches.nil? && @repository.branches.length > 0 -%>
17
    | <%= l(:label_branch) %>: 
18
    <%= select_tag :branch,
19
                   options_for_select([''] + @repository.branches, @rev),
20
                   :id => 'branch' %>
21
  <% end -%>
22

  
23
  <% if !@repository.tags.nil? && @repository.tags.length > 0 -%>
24
    | <%= l(:label_tag) %>: 
25
    <%= select_tag :tag,
26
                   options_for_select([''] + @repository.tags, @rev),
27
                   :id => 'tag' %>
28
  <% end -%>
29

  
30
  <% if @repository.supports_all_revisions? %>
31
    | <%= l(:label_revision) %>: 
32
    <%= text_field_tag 'rev', @rev, :size => 8 %>
33
	<% end %>
34
<% end -%>
.svn/pristine/08/08821f4ea63f8913a9301b84afdd1e4c25ebe173.svn-base
1
<h2><%=l(:label_version)%></h2>
2

  
3
<%= labelled_form_for @version do |f| %>
4
<%= render :partial => 'form', :locals => { :f => f } %>
5
<%= submit_tag l(:button_save) %>
6
<% end %>
7

  
.svn/pristine/08/08ad4f57c782749abd613605456ebd0acf953d61.svn-base
1
begin
2
  require 'zlib'
3
  @@__have_zlib = true
4
rescue
5
  @@__have_zlib = false
6
end
7

  
8
require 'rexml/document'
9

  
10
module SVG
11
  module Graph
12
    VERSION = '@ANT_VERSION@'
13

  
14
    # === Base object for generating SVG Graphs
15
    # 
16
    # == Synopsis
17
    #
18
    # This class is only used as a superclass of specialized charts.  Do not
19
    # attempt to use this class directly, unless creating a new chart type.
20
    #
21
    # For examples of how to subclass this class, see the existing specific
22
    # subclasses, such as SVG::Graph::Pie.
23
    #
24
    # == Examples
25
    #
26
    # For examples of how to use this package, see either the test files, or
27
    # the documentation for the specific class you want to use.
28
    #
29
    # * file:test/plot.rb
30
    # * file:test/single.rb
31
    # * file:test/test.rb
32
    # * file:test/timeseries.rb
33
    # 
34
    # == Description
35
    # 
36
    # This package should be used as a base for creating SVG graphs.
37
    #
38
    # == Acknowledgements
39
    #
40
    # Leo Lapworth for creating the SVG::TT::Graph package which this Ruby
41
    # port is based on.
42
    #
43
    # Stephen Morgan for creating the TT template and SVG.
44
    # 
45
    # == See
46
    #
47
    # * SVG::Graph::BarHorizontal
48
    # * SVG::Graph::Bar
49
    # * SVG::Graph::Line
50
    # * SVG::Graph::Pie
51
    # * SVG::Graph::Plot
52
    # * SVG::Graph::TimeSeries
53
    #
54
    # == Author
55
    #
56
    # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
57
    #
58
    # Copyright 2004 Sean E. Russell
59
    # This software is available under the Ruby license[LICENSE.txt]
60
    #
61
    class Graph
62
      include REXML
63

  
64
      # Initialize the graph object with the graph settings.  You won't
65
      # instantiate this class directly; see the subclass for options.
66
      # [width] 500
67
      # [height] 300
68
      # [show_x_guidelines] false
69
      # [show_y_guidelines] true
70
      # [show_data_values] true
71
      # [min_scale_value] 0
72
      # [show_x_labels] true
73
      # [stagger_x_labels] false
74
      # [rotate_x_labels] false
75
      # [step_x_labels] 1
76
      # [step_include_first_x_label] true
77
      # [show_y_labels] true
78
      # [rotate_y_labels] false
79
      # [scale_integers] false
80
      # [show_x_title] false
81
      # [x_title] 'X Field names'
82
      # [show_y_title] false
83
      # [y_title_text_direction] :bt
84
      # [y_title] 'Y Scale'
85
      # [show_graph_title] false
86
      # [graph_title] 'Graph Title'
87
      # [show_graph_subtitle] false
88
      # [graph_subtitle] 'Graph Sub Title'
89
      # [key] true,
90
      # [key_position] :right, # bottom or righ
91
      # [font_size] 12
92
      # [title_font_size] 16
93
      # [subtitle_font_size] 14
94
      # [x_label_font_size] 12
95
      # [x_title_font_size] 14
96
      # [y_label_font_size] 12
97
      # [y_title_font_size] 14
98
      # [key_font_size] 10
99
      # [no_css] false
100
      # [add_popups] false
101
      def initialize( config )
102
        @config = config
103

  
104
        self.top_align = self.top_font = self.right_align = self.right_font = 0
105

  
106
        init_with({
107
          :width                => 500,
108
          :height                => 300,
109
          :show_x_guidelines    => false,
110
          :show_y_guidelines    => true,
111
          :show_data_values     => true,
112

  
113
#          :min_scale_value      => 0,
114

  
115
          :show_x_labels        => true,
116
          :stagger_x_labels     => false,
117
          :rotate_x_labels      => false,
118
          :step_x_labels        => 1,
119
          :step_include_first_x_label => true,
120

  
121
          :show_y_labels        => true,
122
          :rotate_y_labels      => false,
123
          :stagger_y_labels     => false,
124
          :scale_integers       => false,
125

  
126
          :show_x_title         => false,
127
          :x_title              => 'X Field names',
128

  
129
          :show_y_title         => false,
130
          :y_title_text_direction => :bt,
131
          :y_title              => 'Y Scale',
132

  
133
          :show_graph_title      => false,
134
          :graph_title          => 'Graph Title',
135
          :show_graph_subtitle  => false,
136
          :graph_subtitle        => 'Graph Sub Title',
137
          :key                  => true, 
138
          :key_position          => :right, # bottom or right
139

  
140
          :font_size            =>12,
141
          :title_font_size      =>16,
142
          :subtitle_font_size   =>14,
143
          :x_label_font_size    =>12,
144
          :x_title_font_size    =>14,
145
          :y_label_font_size    =>12,
146
          :y_title_font_size    =>14,
147
          :key_font_size        =>10,
148
          
149
          :no_css               =>false,
150
          :add_popups           =>false,
151
        })
152

  
153
				set_defaults if respond_to? :set_defaults
154

  
155
        init_with config
156
      end
157

  
158
      
159
      # This method allows you do add data to the graph object.
160
      # It can be called several times to add more data sets in.
161
      #
162
      #   data_sales_02 = [12, 45, 21];
163
      #   
164
      #   graph.add_data({
165
      #     :data => data_sales_02,
166
      #     :title => 'Sales 2002'
167
      #   })
168
      def add_data conf
169
        @data = [] unless defined? @data
170

  
171
        if conf[:data] and conf[:data].kind_of? Array
172
          @data << conf
173
        else
174
          raise "No data provided by #{conf.inspect}"
175
        end
176
      end
177

  
178

  
179
      # This method removes all data from the object so that you can
180
      # reuse it to create a new graph but with the same config options.
181
      #
182
      #   graph.clear_data
183
      def clear_data 
184
        @data = []
185
      end
186

  
187

  
188
      # This method processes the template with the data and
189
      # config which has been set and returns the resulting SVG.
190
      #
191
      # This method will croak unless at least one data set has
192
      # been added to the graph object.
193
      #
194
      #   print graph.burn
195
      def burn
196
        raise "No data available" unless @data.size > 0
197
        
198
        calculations if respond_to? :calculations
199

  
200
        start_svg
201
        calculate_graph_dimensions
202
        @foreground = Element.new( "g" )
203
        draw_graph
204
        draw_titles
205
        draw_legend
206
        draw_data
207
        @graph.add_element( @foreground )
208
        style
209

  
210
        data = ""
211
        @doc.write( data, 0 )
212

  
213
        if @config[:compress]
214
          if @@__have_zlib
215
            inp, out = IO.pipe
216
            gz = Zlib::GzipWriter.new( out )
217
            gz.write data
218
            gz.close
219
            data = inp.read
220
          else
221
            data << "<!-- Ruby Zlib not available for SVGZ -->";
222
          end
223
        end
224
        
225
        return data
226
      end
227

  
228

  
229
      #   Set the height of the graph box, this is the total height
230
      #   of the SVG box created - not the graph it self which auto
231
      #   scales to fix the space.
232
      attr_accessor :height
233
      #   Set the width of the graph box, this is the total width
234
      #   of the SVG box created - not the graph it self which auto
235
      #   scales to fix the space.
236
      attr_accessor :width
237
      #   Set the path to an external stylesheet, set to '' if
238
      #   you want to revert back to using the defaut internal version.
239
      #
240
      #   To create an external stylesheet create a graph using the
241
      #   default internal version and copy the stylesheet section to
242
      #   an external file and edit from there.
243
      attr_accessor :style_sheet
244
      #   (Bool) Show the value of each element of data on the graph
245
      attr_accessor :show_data_values
246
      #   The point at which the Y axis starts, defaults to '0',
247
      #   if set to nil it will default to the minimum data value.
248
      attr_accessor :min_scale_value
249
      #   Whether to show labels on the X axis or not, defaults
250
      #   to true, set to false if you want to turn them off.
251
      attr_accessor :show_x_labels
252
      #   This puts the X labels at alternative levels so if they
253
      #   are long field names they will not overlap so easily.
254
      #   Default it false, to turn on set to true.
255
      attr_accessor :stagger_x_labels
256
      #   This puts the Y labels at alternative levels so if they
257
      #   are long field names they will not overlap so easily.
258
      #   Default it false, to turn on set to true.
259
      attr_accessor :stagger_y_labels
260
      #   This turns the X axis labels by 90 degrees.
261
      #   Default it false, to turn on set to true.
262
      attr_accessor :rotate_x_labels
263
      #   This turns the Y axis labels by 90 degrees.
264
      #   Default it false, to turn on set to true.
265
      attr_accessor :rotate_y_labels
266
      #   How many "steps" to use between displayed X axis labels,
267
      #   a step of one means display every label, a step of two results
268
      #   in every other label being displayed (label <gap> label <gap> label),
269
      #   a step of three results in every third label being displayed
270
      #   (label <gap> <gap> label <gap> <gap> label) and so on.
271
      attr_accessor :step_x_labels
272
      #   Whether to (when taking "steps" between X axis labels) step from 
273
      #   the first label (i.e. always include the first label) or step from
274
      #   the X axis origin (i.e. start with a gap if step_x_labels is greater
275
      #   than one).
276
      attr_accessor :step_include_first_x_label
277
      #   Whether to show labels on the Y axis or not, defaults
278
      #   to true, set to false if you want to turn them off.
279
      attr_accessor :show_y_labels
280
      #   Ensures only whole numbers are used as the scale divisions.
281
      #   Default it false, to turn on set to true. This has no effect if 
282
      #   scale divisions are less than 1.
283
      attr_accessor :scale_integers
284
      #   This defines the gap between markers on the Y axis,
285
      #   default is a 10th of the max_value, e.g. you will have
286
      #   10 markers on the Y axis. NOTE: do not set this too
287
      #   low - you are limited to 999 markers, after that the
288
      #   graph won't generate.
289
      attr_accessor :scale_divisions
290
      #   Whether to show the title under the X axis labels,
291
      #   default is false, set to true to show.
292
      attr_accessor :show_x_title
293
      #   What the title under X axis should be, e.g. 'Months'.
294
      attr_accessor :x_title
295
      #   Whether to show the title under the Y axis labels,
296
      #   default is false, set to true to show.
297
      attr_accessor :show_y_title
298
      #   Aligns writing mode for Y axis label. 
299
      #   Defaults to :bt (Bottom to Top).
300
      #   Change to :tb (Top to Bottom) to reverse.
301
      attr_accessor :y_title_text_direction
302
      #   What the title under Y axis should be, e.g. 'Sales in thousands'.
303
      attr_accessor :y_title
304
      #   Whether to show a title on the graph, defaults
305
      #   to false, set to true to show.
306
      attr_accessor :show_graph_title
307
      #   What the title on the graph should be.
308
      attr_accessor :graph_title
309
      #   Whether to show a subtitle on the graph, defaults
310
      #   to false, set to true to show.
311
      attr_accessor :show_graph_subtitle
312
      #   What the subtitle on the graph should be.
313
      attr_accessor :graph_subtitle
314
      #   Whether to show a key, defaults to false, set to
315
      #   true if you want to show it.
316
      attr_accessor :key
317
      #   Where the key should be positioned, defaults to
318
      #   :right, set to :bottom if you want to move it.
319
      attr_accessor :key_position
320
      # Set the font size (in points) of the data point labels
321
      attr_accessor :font_size
322
      # Set the font size of the X axis labels
323
      attr_accessor :x_label_font_size
324
      # Set the font size of the X axis title
325
      attr_accessor :x_title_font_size
326
      # Set the font size of the Y axis labels
327
      attr_accessor :y_label_font_size
328
      # Set the font size of the Y axis title
329
      attr_accessor :y_title_font_size
330
      # Set the title font size
331
      attr_accessor :title_font_size
332
      # Set the subtitle font size
333
      attr_accessor :subtitle_font_size
334
      # Set the key font size
335
      attr_accessor :key_font_size
336
      # Show guidelines for the X axis
337
      attr_accessor :show_x_guidelines
338
      # Show guidelines for the Y axis
339
      attr_accessor :show_y_guidelines
340
      # Do not use CSS if set to true.  Many SVG viewers do not support CSS, but
341
      # not using CSS can result in larger SVGs as well as making it impossible to
342
      # change colors after the chart is generated.  Defaults to false.
343
      attr_accessor :no_css
344
      # Add popups for the data points on some graphs
345
      attr_accessor :add_popups
346

  
347

  
348
      protected
349

  
350
      def sort( *arrys )
351
        sort_multiple( arrys )
352
      end
353

  
354
      # Overwrite configuration options with supplied options.  Used
355
      # by subclasses.
356
      def init_with config
357
        config.each { |key, value|
358
          self.send((key.to_s+"=").to_sym, value ) if respond_to? key.to_sym
359
        }
360
      end
361

  
362
      attr_accessor :top_align, :top_font, :right_align, :right_font
363

  
364
      KEY_BOX_SIZE = 12
365

  
366
      # Override this (and call super) to change the margin to the left
367
      # of the plot area.  Results in @border_left being set.
368
      def calculate_left_margin
369
        @border_left = 7
370
        # Check for Y labels
371
        max_y_label_height_px = rotate_y_labels ? 
372
          y_label_font_size :
373
          get_y_labels.max{|a,b| 
374
            a.to_s.length<=>b.to_s.length
375
          }.to_s.length * y_label_font_size * 0.6
376
        @border_left += max_y_label_height_px if show_y_labels
377
        @border_left += max_y_label_height_px + 10 if stagger_y_labels
378
        @border_left += y_title_font_size + 5 if show_y_title
379
      end
380

  
381

  
382
      # Calculates the width of the widest Y label.  This will be the
383
      # character height if the Y labels are rotated
384
      def max_y_label_width_px
385
        return font_size if rotate_y_labels
386
      end
387

  
388

  
389
      # Override this (and call super) to change the margin to the right
390
      # of the plot area.  Results in @border_right being set.
391
      def calculate_right_margin
392
        @border_right = 7
393
        if key and key_position == :right
394
          val = keys.max { |a,b| a.length <=> b.length }
395
          @border_right += val.length * key_font_size * 0.6 
396
          @border_right += KEY_BOX_SIZE
397
          @border_right += 10    # Some padding around the box
398
        end
399
      end
400

  
401

  
402
      # Override this (and call super) to change the margin to the top
403
      # of the plot area.  Results in @border_top being set.
404
      def calculate_top_margin
405
        @border_top = 5
406
        @border_top += title_font_size if show_graph_title
407
        @border_top += 5
408
        @border_top += subtitle_font_size if show_graph_subtitle
409
      end
410

  
411

  
412
      # Adds pop-up point information to a graph.
413
      def add_popup( x, y, label )
414
        txt_width = label.length * font_size * 0.6 + 10
415
        tx = (x+txt_width > width ? x-5 : x+5)
416
        t = @foreground.add_element( "text", {
417
          "x" => tx.to_s,
418
          "y" => (y - font_size).to_s,
419
          "visibility" => "hidden",
420
        })
421
        t.attributes["style"] = "fill: #000; "+
422
          (x+txt_width > width ? "text-anchor: end;" : "text-anchor: start;")
423
        t.text = label.to_s
424
        t.attributes["id"] = t.object_id.to_s
425

  
426
        @foreground.add_element( "circle", {
427
          "cx" => x.to_s,
428
          "cy" => y.to_s,
429
          "r" => "10",
430
          "style" => "opacity: 0",
431
          "onmouseover" => 
432
            "document.getElementById(#{t.object_id}).setAttribute('visibility', 'visible' )",
433
          "onmouseout" => 
434
            "document.getElementById(#{t.object_id}).setAttribute('visibility', 'hidden' )",
435
        })
436

  
437
      end
438

  
439
      
440
      # Override this (and call super) to change the margin to the bottom
441
      # of the plot area.  Results in @border_bottom being set.
442
      def calculate_bottom_margin
443
        @border_bottom = 7
444
        if key and key_position == :bottom
445
          @border_bottom += @data.size * (font_size + 5)
446
          @border_bottom += 10
447
        end
448
        if show_x_labels
449
		  max_x_label_height_px = (not rotate_x_labels) ? 
450
            x_label_font_size :
451
            get_x_labels.max{|a,b| 
452
              a.to_s.length<=>b.to_s.length
453
            }.to_s.length * x_label_font_size * 0.6
454
          @border_bottom += max_x_label_height_px
455
          @border_bottom += max_x_label_height_px + 10 if stagger_x_labels
456
        end
457
        @border_bottom += x_title_font_size + 5 if show_x_title
458
      end
459

  
460

  
461
      # Draws the background, axis, and labels.
462
      def draw_graph
463
        @graph = @root.add_element( "g", {
464
          "transform" => "translate( #@border_left #@border_top )"
465
        })
466

  
467
        # Background
468
        @graph.add_element( "rect", {
469
          "x" => "0",
470
          "y" => "0",
471
          "width" => @graph_width.to_s,
472
          "height" => @graph_height.to_s,
473
          "class" => "graphBackground"
474
        })
475

  
476
        # Axis
477
        @graph.add_element( "path", {
478
          "d" => "M 0 0 v#@graph_height",
479
          "class" => "axis",
480
          "id" => "xAxis"
481
        })
482
        @graph.add_element( "path", {
483
          "d" => "M 0 #@graph_height h#@graph_width",
484
          "class" => "axis",
485
          "id" => "yAxis"
486
        })
487

  
488
        draw_x_labels
489
        draw_y_labels
490
      end
491

  
492

  
493
      # Where in the X area the label is drawn
494
      # Centered in the field, should be width/2.  Start, 0.
495
      def x_label_offset( width )
496
        0
497
      end
498

  
499
      def make_datapoint_text( x, y, value, style="" )
500
        if show_data_values
501
          @foreground.add_element( "text", {
502
            "x" => x.to_s,
503
            "y" => y.to_s,
504
            "class" => "dataPointLabel",
505
            "style" => "#{style} stroke: #fff; stroke-width: 2;"
506
          }).text = value.to_s
507
          text = @foreground.add_element( "text", {
508
            "x" => x.to_s,
509
            "y" => y.to_s,
510
            "class" => "dataPointLabel"
511
          })
512
          text.text = value.to_s
513
          text.attributes["style"] = style if style.length > 0
514
        end
515
      end
516

  
517

  
518
      # Draws the X axis labels
519
      def draw_x_labels
520
        stagger = x_label_font_size + 5
521
        if show_x_labels
522
          label_width = field_width
523

  
524
          count = 0
525
          for label in get_x_labels
526
            if step_include_first_x_label == true then
527
              step = count % step_x_labels
528
            else
529
              step = (count + 1) % step_x_labels
530
            end
531

  
532
            if step == 0 then
533
              text = @graph.add_element( "text" )
534
              text.attributes["class"] = "xAxisLabels"
535
              text.text = label.to_s
536

  
537
              x = count * label_width + x_label_offset( label_width )
538
              y = @graph_height + x_label_font_size + 3
539
              t = 0 - (font_size / 2)
540

  
541
              if stagger_x_labels and count % 2 == 1
542
                y += stagger
543
                @graph.add_element( "path", {
544
                  "d" => "M#{x} #@graph_height v#{stagger}",
545
                  "class" => "staggerGuideLine"
546
                })
547
              end
548

  
549
              text.attributes["x"] = x.to_s
550
              text.attributes["y"] = y.to_s
551
              if rotate_x_labels
552
                text.attributes["transform"] = 
553
                  "rotate( 90 #{x} #{y-x_label_font_size} )"+
554
                  " translate( 0 -#{x_label_font_size/4} )"
555
                text.attributes["style"] = "text-anchor: start"
556
              else
557
                text.attributes["style"] = "text-anchor: middle"
558
              end
559
            end
560

  
561
            draw_x_guidelines( label_width, count ) if show_x_guidelines
562
            count += 1
563
          end
564
        end
565
      end
566

  
567

  
568
      # Where in the Y area the label is drawn
569
      # Centered in the field, should be width/2.  Start, 0.
570
      def y_label_offset( height )
571
        0
572
      end
573

  
574

  
575
      def field_width
576
        (@graph_width.to_f - font_size*2*right_font) /
577
           (get_x_labels.length - right_align)
578
      end
579

  
580

  
581
      def field_height
582
        (@graph_height.to_f - font_size*2*top_font) /
583
           (get_y_labels.length - top_align)
584
      end
585

  
586

  
587
      # Draws the Y axis labels
588
      def draw_y_labels
589
        stagger = y_label_font_size + 5
590
        if show_y_labels
591
          label_height = field_height
592

  
593
          count = 0
594
          y_offset = @graph_height + y_label_offset( label_height )
595
          y_offset += font_size/1.2 unless rotate_y_labels
596
          for label in get_y_labels
597
            y = y_offset - (label_height * count)
598
            x = rotate_y_labels ? 0 : -3
599

  
600
            if stagger_y_labels and count % 2 == 1
601
              x -= stagger
602
              @graph.add_element( "path", {
603
                "d" => "M#{x} #{y} h#{stagger}",
604
                "class" => "staggerGuideLine"
605
              })
606
            end
607

  
608
            text = @graph.add_element( "text", {
609
              "x" => x.to_s,
610
              "y" => y.to_s,
611
              "class" => "yAxisLabels"
612
            })
613
            text.text = label.to_s
614
            if rotate_y_labels
615
              text.attributes["transform"] = "translate( -#{font_size} 0 ) "+
616
                "rotate( 90 #{x} #{y} ) "
617
              text.attributes["style"] = "text-anchor: middle"
618
            else
619
              text.attributes["y"] = (y - (y_label_font_size/2)).to_s
620
              text.attributes["style"] = "text-anchor: end"
621
            end
622
            draw_y_guidelines( label_height, count ) if show_y_guidelines
623
            count += 1
624
          end
625
        end
626
      end
627

  
628

  
629
      # Draws the X axis guidelines
630
      def draw_x_guidelines( label_height, count )
631
        if count != 0
632
          @graph.add_element( "path", {
633
            "d" => "M#{label_height*count} 0 v#@graph_height",
634
            "class" => "guideLines"
635
          })
636
        end
637
      end
638

  
639

  
640
      # Draws the Y axis guidelines
641
      def draw_y_guidelines( label_height, count )
642
        if count != 0
643
          @graph.add_element( "path", {
644
            "d" => "M0 #{@graph_height-(label_height*count)} h#@graph_width",
645
            "class" => "guideLines"
646
          })
647
        end
648
      end
649

  
650

  
651
      # Draws the graph title and subtitle
652
      def draw_titles
653
        if show_graph_title
654
          @root.add_element( "text", {
655
            "x" => (width / 2).to_s,
656
            "y" => (title_font_size).to_s,
657
            "class" => "mainTitle"
658
          }).text = graph_title.to_s
659
        end
660

  
661
        if show_graph_subtitle
662
          y_subtitle = show_graph_title ? 
663
            title_font_size + 10 :
664
            subtitle_font_size
665
          @root.add_element("text", {
666
            "x" => (width / 2).to_s,
667
            "y" => (y_subtitle).to_s,
668
            "class" => "subTitle"
669
          }).text = graph_subtitle.to_s
670
        end
671

  
672
        if show_x_title
673
          y = @graph_height + @border_top + x_title_font_size
674
          if show_x_labels
675
            y += x_label_font_size + 5 if stagger_x_labels
676
            y += x_label_font_size + 5
677
          end
678
          x = width / 2
679

  
680
          @root.add_element("text", {
681
            "x" => x.to_s,
682
            "y" => y.to_s,
683
            "class" => "xAxisTitle",
684
          }).text = x_title.to_s
685
        end
686

  
687
        if show_y_title
688
          x = y_title_font_size + (y_title_text_direction==:bt ? 3 : -3)
689
          y = height / 2
690

  
691
          text = @root.add_element("text", {
692
            "x" => x.to_s,
693
            "y" => y.to_s,
694
            "class" => "yAxisTitle",
695
          })
696
          text.text = y_title.to_s
697
          if y_title_text_direction == :bt
698
            text.attributes["transform"] = "rotate( -90, #{x}, #{y} )"
699
          else
700
            text.attributes["transform"] = "rotate( 90, #{x}, #{y} )"
701
          end
702
        end
703
      end
704

  
705
      def keys 
706
        return @data.collect{ |d| d[:title] }
707
      end
708

  
709
      # Draws the legend on the graph
710
      def draw_legend
711
        if key
712
          group = @root.add_element( "g" )
713

  
714
          key_count = 0
715
          for key_name in keys
716
            y_offset = (KEY_BOX_SIZE * key_count) + (key_count * 5)
717
            group.add_element( "rect", {
718
              "x" => 0.to_s,
719
              "y" => y_offset.to_s,
720
              "width" => KEY_BOX_SIZE.to_s,
721
              "height" => KEY_BOX_SIZE.to_s,
722
              "class" => "key#{key_count+1}"
723
            })
724
            group.add_element( "text", {
725
              "x" => (KEY_BOX_SIZE + 5).to_s,
726
              "y" => (y_offset + KEY_BOX_SIZE).to_s,
727
              "class" => "keyText"
728
            }).text = key_name.to_s
729
            key_count += 1
730
          end
731

  
732
          case key_position
733
          when :right
734
            x_offset = @graph_width + @border_left + 10
735
            y_offset = @border_top + 20
736
          when :bottom
737
            x_offset = @border_left + 20
738
            y_offset = @border_top + @graph_height + 5
739
            if show_x_labels
740
			  max_x_label_height_px = (not rotate_x_labels) ? 
741
				x_label_font_size :
742
				get_x_labels.max{|a,b| 
743
				  a.to_s.length<=>b.to_s.length
744
				}.to_s.length * x_label_font_size * 0.6
745
                x_label_font_size
746
              y_offset += max_x_label_height_px
747
              y_offset += max_x_label_height_px + 5 if stagger_x_labels
748
            end
749
            y_offset += x_title_font_size + 5 if show_x_title
750
          end
751
          group.attributes["transform"] = "translate(#{x_offset} #{y_offset})"
752
        end
753
      end
754

  
755

  
756
      private
757

  
758
      def sort_multiple( arrys, lo=0, hi=arrys[0].length-1 )
759
        if lo < hi
760
          p = partition(arrys,lo,hi)
761
          sort_multiple(arrys, lo, p-1)
762
          sort_multiple(arrys, p+1, hi)
763
        end
764
        arrys 
765
      end
766

  
767
      def partition( arrys, lo, hi )
768
        p = arrys[0][lo]
769
        l = lo
770
        z = lo+1
771
        while z <= hi
772
          if arrys[0][z] < p
773
            l += 1
774
            arrys.each { |arry| arry[z], arry[l] = arry[l], arry[z] }
775
          end
776
          z += 1
777
        end
778
        arrys.each { |arry| arry[lo], arry[l] = arry[l], arry[lo] }
779
        l
780
      end
781

  
782
      def style
783
        if no_css
784
          styles = parse_css
785
          @root.elements.each("//*[@class]") { |el|
786
            cl = el.attributes["class"]
787
            style = styles[cl]
788
            style += el.attributes["style"] if el.attributes["style"]
789
            el.attributes["style"] = style
790
          }
791
        end
792
      end
793

  
794
      def parse_css
795
        css = get_style
796
        rv = {}
797
        while css =~ /^(\.(\w+)(?:\s*,\s*\.\w+)*)\s*\{/m
798
          names_orig = names = $1
799
          css = $'
800
          css =~ /([^}]+)\}/m
801
          content = $1
802
          css = $'
803

  
804
          nms = []
805
          while names =~ /^\s*,?\s*\.(\w+)/
806
            nms << $1
807
            names = $'
808
          end
809

  
810
          content = content.tr( "\n\t", " ")
811
          for name in nms
812
            current = rv[name]
813
            current = current ? current+"; "+content : content
814
            rv[name] = current.strip.squeeze(" ")
815
          end
816
        end
817
        return rv
818
      end
819

  
820

  
821
      # Override and place code to add defs here
822
      def add_defs defs
823
      end
824

  
825

  
826
      def start_svg
827
        # Base document
828
        @doc = Document.new
829
        @doc << XMLDecl.new
830
        @doc << DocType.new( %q{svg PUBLIC "-//W3C//DTD SVG 1.0//EN" } +
831
          %q{"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"} )
832
        if style_sheet && style_sheet != ''
833
          @doc << Instruction.new( "xml-stylesheet",
834
            %Q{href="#{style_sheet}" type="text/css"} )
835
        end
836
        @root = @doc.add_element( "svg", {
837
          "width" => width.to_s,
838
          "height" => height.to_s,
839
          "viewBox" => "0 0 #{width} #{height}",
840
          "xmlns" => "http://www.w3.org/2000/svg",
841
          "xmlns:xlink" => "http://www.w3.org/1999/xlink",
842
          "xmlns:a3" => "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/",
843
          "a3:scriptImplementation" => "Adobe"
844
        })
845
        @root << Comment.new( " "+"\\"*66 )
846
        @root << Comment.new( " Created with SVG::Graph " )
847
        @root << Comment.new( " SVG::Graph by Sean E. Russell " )
848
        @root << Comment.new( " Losely based on SVG::TT::Graph for Perl by"+
849
        " Leo Lapworth & Stephan Morgan " )
850
        @root << Comment.new( " "+"/"*66 )
851

  
852
        defs = @root.add_element( "defs" )
853
        add_defs defs
854
        if not(style_sheet && style_sheet != '') and !no_css
855
          @root << Comment.new(" include default stylesheet if none specified ")
856
          style = defs.add_element( "style", {"type"=>"text/css"} )
857
          style << CData.new( get_style )
858
        end
859

  
860
        @root << Comment.new( "SVG Background" )
861
        @root.add_element( "rect", {
862
          "width" => width.to_s,
863
          "height" => height.to_s,
864
          "x" => "0",
865
          "y" => "0",
866
          "class" => "svgBackground"
867
        })
868
      end
869

  
870

  
871
      def calculate_graph_dimensions
872
        calculate_left_margin
873
        calculate_right_margin
874
        calculate_bottom_margin
875
        calculate_top_margin
876
        @graph_width = width - @border_left - @border_right
877
        @graph_height = height - @border_top - @border_bottom
878
      end
879

  
880
      def get_style
881
        return <<EOL
882
/* Copy from here for external style sheet */
883
.svgBackground{
884
  fill:#ffffff;
885
}
886
.graphBackground{
887
  fill:#f0f0f0;
888
}
889

  
890
/* graphs titles */
891
.mainTitle{
892
  text-anchor: middle;
893
  fill: #000000;
894
  font-size: #{title_font_size}px;
895
  font-family: "Arial", sans-serif;
896
  font-weight: normal;
897
}
898
.subTitle{
899
  text-anchor: middle;
900
  fill: #999999;
901
  font-size: #{subtitle_font_size}px;
902
  font-family: "Arial", sans-serif;
903
  font-weight: normal;
904
}
905

  
906
.axis{
907
  stroke: #000000;
908
  stroke-width: 1px;
909
}
910

  
911
.guideLines{
912
  stroke: #666666;
913
  stroke-width: 1px;
914
  stroke-dasharray: 5 5;
915
}
916

  
917
.xAxisLabels{
918
  text-anchor: middle;
919
  fill: #000000;
920
  font-size: #{x_label_font_size}px;
921
  font-family: "Arial", sans-serif;
922
  font-weight: normal;
923
}
924

  
925
.yAxisLabels{
926
  text-anchor: end;
927
  fill: #000000;
928
  font-size: #{y_label_font_size}px;
929
  font-family: "Arial", sans-serif;
930
  font-weight: normal;
931
}
932

  
933
.xAxisTitle{
934
  text-anchor: middle;
935
  fill: #ff0000;
936
  font-size: #{x_title_font_size}px;
937
  font-family: "Arial", sans-serif;
938
  font-weight: normal;
939
}
940

  
941
.yAxisTitle{
942
  fill: #ff0000;
943
  text-anchor: middle;
944
  font-size: #{y_title_font_size}px;
945
  font-family: "Arial", sans-serif;
946
  font-weight: normal;
947
}
948

  
949
.dataPointLabel{
950
  fill: #000000;
951
  text-anchor:middle;
952
  font-size: 10px;
953
  font-family: "Arial", sans-serif;
954
  font-weight: normal;
955
}
956

  
957
.staggerGuideLine{
958
  fill: none;
959
  stroke: #000000;
960
  stroke-width: 0.5px;  
961
}
962

  
963
#{get_css}
964

  
965
.keyText{
966
  fill: #000000;
967
  text-anchor:start;
968
  font-size: #{key_font_size}px;
969
  font-family: "Arial", sans-serif;
970
  font-weight: normal;
971
}
972
/* End copy for external style sheet */
973
EOL
974
      end
975

  
976
    end
977
  end
978
end
.svn/pristine/08/08ba366b8c38c360363f9f7f4597d533502606b0.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2012  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17

  
18
require File.expand_path('../../test_helper', __FILE__)
19
require 'workflows_controller'
20

  
21
# Re-raise errors caught by the controller.
22
class WorkflowsController; def rescue_action(e) raise e end; end
23

  
24
class WorkflowsControllerTest < ActionController::TestCase
25
  fixtures :roles, :trackers, :workflows, :users, :issue_statuses
26

  
27
  def setup
28
    @controller = WorkflowsController.new
29
    @request    = ActionController::TestRequest.new
30
    @response   = ActionController::TestResponse.new
31
    User.current = nil
32
    @request.session[:user_id] = 1 # admin
33
  end
34

  
35
  def test_index
36
    get :index
37
    assert_response :success
38
    assert_template 'index'
39

  
40
    count = WorkflowTransition.count(:all, :conditions => 'role_id = 1 AND tracker_id = 2')
41
    assert_tag :tag => 'a', :content => count.to_s,
42
                            :attributes => { :href => '/workflows/edit?role_id=1&amp;tracker_id=2' }
43
  end
44

  
45
  def test_get_edit
46
    get :edit
47
    assert_response :success
48
    assert_template 'edit'
49
    assert_not_nil assigns(:roles)
50
    assert_not_nil assigns(:trackers)
51
  end
52

  
53
  def test_get_edit_with_role_and_tracker
54
    WorkflowTransition.delete_all
55
    WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 2, :new_status_id => 3)
56
    WorkflowTransition.create!(:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 5)
57

  
58
    get :edit, :role_id => 2, :tracker_id => 1
59
    assert_response :success
60
    assert_template 'edit'
61

  
62
    # used status only
63
    assert_not_nil assigns(:statuses)
64
    assert_equal [2, 3, 5], assigns(:statuses).collect(&:id)
65

  
66
    # allowed transitions
67
    assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
68
                                                 :name => 'issue_status[3][5][]',
69
                                                 :value => 'always',
70
                                                 :checked => 'checked' }
71
    # not allowed
72
    assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
73
                                                 :name => 'issue_status[3][2][]',
74
                                                 :value => 'always',
75
                                                 :checked => nil }
76
    # unused
77
    assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox',
78
                                                    :name => 'issue_status[1][1][]' }
79
  end
80

  
81
  def test_get_edit_with_role_and_tracker_and_all_statuses
82
    WorkflowTransition.delete_all
83

  
84
    get :edit, :role_id => 2, :tracker_id => 1, :used_statuses_only => '0'
85
    assert_response :success
86
    assert_template 'edit'
87

  
88
    assert_not_nil assigns(:statuses)
89
    assert_equal IssueStatus.count, assigns(:statuses).size
90

  
91
    assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
92
                                                 :name => 'issue_status[1][1][]',
93
                                                 :value => 'always',
94
                                                 :checked => nil }
95
  end
96

  
97
  def test_post_edit
98
    post :edit, :role_id => 2, :tracker_id => 1,
99
      :issue_status => {
100
        '4' => {'5' => ['always']},
101
        '3' => {'1' => ['always'], '2' => ['always']}
102
      }
103
    assert_redirected_to '/workflows/edit?role_id=2&tracker_id=1'
104

  
105
    assert_equal 3, WorkflowTransition.count(:conditions => {:tracker_id => 1, :role_id => 2})
106
    assert_not_nil  WorkflowTransition.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 2})
107
    assert_nil      WorkflowTransition.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4})
108
  end
109

  
110
  def test_post_edit_with_additional_transitions
111
    post :edit, :role_id => 2, :tracker_id => 1,
112
      :issue_status => {
113
        '4' => {'5' => ['always']},
114
        '3' => {'1' => ['author'], '2' => ['assignee'], '4' => ['author', 'assignee']}
115
      }
116
    assert_redirected_to '/workflows/edit?role_id=2&tracker_id=1'
117

  
118
    assert_equal 4, WorkflowTransition.count(:conditions => {:tracker_id => 1, :role_id => 2})
119

  
120
    w = WorkflowTransition.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 4, :new_status_id => 5})
121
    assert ! w.author
122
    assert ! w.assignee
123
    w = WorkflowTransition.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 1})
124
    assert w.author
125
    assert ! w.assignee
126
    w = WorkflowTransition.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 2})
127
    assert ! w.author
128
    assert w.assignee
129
    w = WorkflowTransition.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 4})
130
    assert w.author
131
    assert w.assignee
132
  end
133

  
134
  def test_clear_workflow
135
    assert WorkflowTransition.count(:conditions => {:tracker_id => 1, :role_id => 2}) > 0
136

  
137
    post :edit, :role_id => 2, :tracker_id => 1
138
    assert_equal 0, WorkflowTransition.count(:conditions => {:tracker_id => 1, :role_id => 2})
139
  end
140

  
141
  def test_get_permissions
142
    get :permissions
143

  
144
    assert_response :success
145
    assert_template 'permissions'
146
    assert_not_nil assigns(:roles)
147
    assert_not_nil assigns(:trackers)
148
  end
149

  
150
  def test_get_permissions_with_role_and_tracker
151
    WorkflowPermission.delete_all
152
    WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :field_name => 'assigned_to_id', :rule => 'required')
153
    WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :field_name => 'fixed_version_id', :rule => 'required')
154
    WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 3, :field_name => 'fixed_version_id', :rule => 'readonly')
155

  
156
    get :permissions, :role_id => 1, :tracker_id => 2
157
    assert_response :success
158
    assert_template 'permissions'
159

  
160
    assert_select 'input[name=role_id][value=1]'
161
    assert_select 'input[name=tracker_id][value=2]'
162

  
163
    # Required field
164
    assert_select 'select[name=?]', 'permissions[assigned_to_id][2]' do
165
      assert_select 'option[value=]'
166
      assert_select 'option[value=][selected=selected]', 0
167
      assert_select 'option[value=readonly]', :text => 'Read-only'
168
      assert_select 'option[value=readonly][selected=selected]', 0
169
      assert_select 'option[value=required]', :text => 'Required'
170
      assert_select 'option[value=required][selected=selected]'
171
    end
172

  
173
    # Read-only field
174
    assert_select 'select[name=?]', 'permissions[fixed_version_id][3]' do
175
      assert_select 'option[value=]'
176
      assert_select 'option[value=][selected=selected]', 0
177
      assert_select 'option[value=readonly]', :text => 'Read-only'
178
      assert_select 'option[value=readonly][selected=selected]'
179
      assert_select 'option[value=required]', :text => 'Required'
180
      assert_select 'option[value=required][selected=selected]', 0
181
    end
182

  
183
    # Other field
184
    assert_select 'select[name=?]', 'permissions[due_date][3]' do
185
      assert_select 'option[value=]'
186
      assert_select 'option[value=][selected=selected]', 0
187
      assert_select 'option[value=readonly]', :text => 'Read-only'
188
      assert_select 'option[value=readonly][selected=selected]', 0
189
      assert_select 'option[value=required]', :text => 'Required'
190
      assert_select 'option[value=required][selected=selected]', 0
191
    end
192
  end
193

  
194
  def test_get_permissions_with_required_custom_field_should_not_show_required_option
195
    cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :tracker_ids => [1], :is_required => true)
196

  
197
    get :permissions, :role_id => 1, :tracker_id => 1
198
    assert_response :success
199
    assert_template 'permissions'
200

  
201
    # Custom field that is always required
202
    # The default option is "(Required)"
203
    assert_select 'select[name=?]', "permissions[#{cf.id}][3]" do
204
      assert_select 'option[value=]'
205
      assert_select 'option[value=readonly]', :text => 'Read-only'
206
      assert_select 'option[value=required]', 0
207
    end
208
  end
209

  
210
  def test_get_permissions_with_role_and_tracker_and_all_statuses
211
    WorkflowTransition.delete_all
212

  
213
    get :permissions, :role_id => 1, :tracker_id => 2, :used_statuses_only => '0'
214
    assert_response :success
215
    assert_equal IssueStatus.sorted.all, assigns(:statuses)
216
  end
217

  
218
  def test_post_permissions
219
    WorkflowPermission.delete_all
220

  
221
    post :permissions, :role_id => 1, :tracker_id => 2, :permissions => {
222
      'assigned_to_id' => {'1' => '', '2' => 'readonly', '3' => ''},
223
      'fixed_version_id' => {'1' => 'required', '2' => 'readonly', '3' => ''},
224
      'due_date' => {'1' => '', '2' => '', '3' => ''},
225
    }
226
    assert_redirected_to '/workflows/permissions?role_id=1&tracker_id=2'
227

  
228
    workflows = WorkflowPermission.all
229
    assert_equal 3, workflows.size
230
    workflows.each do |workflow|
231
      assert_equal 1, workflow.role_id
232
      assert_equal 2, workflow.tracker_id
233
    end
234
    assert workflows.detect {|wf| wf.old_status_id == 2 && wf.field_name == 'assigned_to_id' && wf.rule == 'readonly'}
235
    assert workflows.detect {|wf| wf.old_status_id == 1 && wf.field_name == 'fixed_version_id' && wf.rule == 'required'}
236
    assert workflows.detect {|wf| wf.old_status_id == 2 && wf.field_name == 'fixed_version_id' && wf.rule == 'readonly'}
237
  end
238

  
239
  def test_post_permissions_should_clear_permissions
240
    WorkflowPermission.delete_all
241
    WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :field_name => 'assigned_to_id', :rule => 'required')
242
    WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :field_name => 'fixed_version_id', :rule => 'required')
243
    wf1 = WorkflowPermission.create!(:role_id => 1, :tracker_id => 3, :old_status_id => 2, :field_name => 'fixed_version_id', :rule => 'required')
244
    wf2 = WorkflowPermission.create!(:role_id => 2, :tracker_id => 2, :old_status_id => 3, :field_name => 'fixed_version_id', :rule => 'readonly')
245

  
246
    post :permissions, :role_id => 1, :tracker_id => 2
247
    assert_redirected_to '/workflows/permissions?role_id=1&tracker_id=2'
248

  
249
    workflows = WorkflowPermission.all
250
    assert_equal 2, workflows.size
251
    assert wf1.reload
252
    assert wf2.reload
253
  end
254

  
255
  def test_get_copy
256
    get :copy
257
    assert_response :success
258
    assert_template 'copy'
259
    assert_select 'select[name=source_tracker_id]' do
260
      assert_select 'option[value=1]', :text => 'Bug'
261
    end
262
    assert_select 'select[name=source_role_id]' do
263
      assert_select 'option[value=2]', :text => 'Developer'
264
    end
265
    assert_select 'select[name=?]', 'target_tracker_ids[]' do
266
      assert_select 'option[value=3]', :text => 'Support request'
267
    end
268
    assert_select 'select[name=?]', 'target_role_ids[]' do
269
      assert_select 'option[value=1]', :text => 'Manager'
270
    end
271
  end
272

  
273
  def test_post_copy_one_to_one
274
    source_transitions = status_transitions(:tracker_id => 1, :role_id => 2)
275

  
276
    post :copy, :source_tracker_id => '1', :source_role_id => '2',
277
                :target_tracker_ids => ['3'], :target_role_ids => ['1']
278
    assert_response 302
279
    assert_equal source_transitions, status_transitions(:tracker_id => 3, :role_id => 1)
280
  end
281

  
282
  def test_post_copy_one_to_many
283
    source_transitions = status_transitions(:tracker_id => 1, :role_id => 2)
284

  
285
    post :copy, :source_tracker_id => '1', :source_role_id => '2',
286
                :target_tracker_ids => ['2', '3'], :target_role_ids => ['1', '3']
287
    assert_response 302
288
    assert_equal source_transitions, status_transitions(:tracker_id => 2, :role_id => 1)
289
    assert_equal source_transitions, status_transitions(:tracker_id => 3, :role_id => 1)
290
    assert_equal source_transitions, status_transitions(:tracker_id => 2, :role_id => 3)
291
    assert_equal source_transitions, status_transitions(:tracker_id => 3, :role_id => 3)
292
  end
293

  
294
  def test_post_copy_many_to_many
295
    source_t2 = status_transitions(:tracker_id => 2, :role_id => 2)
296
    source_t3 = status_transitions(:tracker_id => 3, :role_id => 2)
297

  
298
    post :copy, :source_tracker_id => 'any', :source_role_id => '2',
299
                :target_tracker_ids => ['2', '3'], :target_role_ids => ['1', '3']
300
    assert_response 302
301
    assert_equal source_t2, status_transitions(:tracker_id => 2, :role_id => 1)
302
    assert_equal source_t3, status_transitions(:tracker_id => 3, :role_id => 1)
303
    assert_equal source_t2, status_transitions(:tracker_id => 2, :role_id => 3)
304
    assert_equal source_t3, status_transitions(:tracker_id => 3, :role_id => 3)
305
  end
306

  
307
  # Returns an array of status transitions that can be compared
308
  def status_transitions(conditions)
309
    WorkflowTransition.find(:all, :conditions => conditions,
310
                        :order => 'tracker_id, role_id, old_status_id, new_status_id').collect {|w| [w.old_status, w.new_status_id]}
311
  end
312
end
.svn/pristine/08/08cbe25d569ad9b66dc5e2416984f568601a717d.svn-base
1
<%= labelled_form_for @group do |f| %>
2
<%= render :partial => 'form', :locals => { :f => f } %>
3
<%= submit_tag l(:button_save) %>
4
<% end %>
.svn/pristine/08/08df2c90c0fda275003e285b2727284ba737baa3.svn-base
1
Redmine::Plugin.register :<%= plugin_name %> do
2
  name '<%= plugin_pretty_name %> plugin'
3
  author 'Author name'
4
  description 'This is a plugin for Redmine'
5
  version '0.0.1'
6
  url 'http://example.com/path/to/plugin'
7
  author_url 'http://example.com/about'
8
end
.svn/pristine/08/08f914636b6cd922b89f9ce8cb2268cea53be558.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2012  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff