Revision 912:5e80956cc792 app/helpers

View differences:

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

  
18 20
module AdminHelper
19 21
  def project_status_options_for_select(selected)
20
    options_for_select([[l(:label_all), ''], 
22
    options_for_select([[l(:label_all), ''],
21 23
                        [l(:status_active), 1]], selected)
22 24
  end
23 25
end
app/helpers/application_helper.rb
1
# encoding: utf-8
2
#
1 3
# Redmine - project management software
2 4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
......
80 82
        subject = truncate(subject, :length => options[:truncate])
81 83
      end
82 84
    end
83
    s = link_to "#{issue.tracker} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
85
    s = link_to "#{h(issue.tracker)} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
84 86
                                                 :class => issue.css_classes,
85 87
                                                 :title => title
86 88
    s << ": #{h subject}" if subject
......
95 97
  def link_to_attachment(attachment, options={})
96 98
    text = options.delete(:text) || attachment.filename
97 99
    action = options.delete(:download) ? 'download' : 'show'
98

  
99
    link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
100
    link_to(h(text),
101
           {:controller => 'attachments', :action => action,
102
            :id => attachment, :filename => attachment.filename },
103
           options)
100 104
  end
101 105

  
102 106
  # Generates a link to a SCM revision
......
106 110
    text = options.delete(:text) || format_revision(revision)
107 111
    rev = revision.respond_to?(:identifier) ? revision.identifier : revision
108 112

  
109
    link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
113
    link_to(h(text), {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
110 114
            :title => l(:label_revision_id, format_revision(revision)))
111 115
  end
112 116

  
......
200 204
      end
201 205
      content << "</ul>\n"
202 206
    end
203
    content
207
    content.html_safe
204 208
  end
205 209

  
206 210
  # Renders flash messages
......
209 213
    flash.each do |k,v|
210 214
      s << content_tag('div', v, :class => "flash #{k}")
211 215
    end
212
    s
216
    s.html_safe
213 217
  end
214 218

  
215 219
  # Renders tabs and their content
......
233 237
        { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
234 238
      end
235 239
      s << '</select>'
236
      s
240
      s.html_safe
237 241
    end
238 242
  end
239 243

  
......
250 254
      tag_options.merge!(yield(project)) if block_given?
251 255
      s << content_tag('option', name_prefix + h(project), tag_options)
252 256
    end
253
    s
257
    s.html_safe
254 258
  end
255 259

  
256 260
  # Yields the given block for each project with its level in the tree
......
281 285
      end
282 286
      s << ("</li></ul>\n" * ancestors.size)
283 287
    end
284
    s
288
    s.html_safe
285 289
  end
286 290

  
287 291
  def principals_check_box_tags(name, principals)
......
289 293
    principals.sort.each do |principal|
290 294
      s << "<label>#{ check_box_tag name, principal.id, false } #{link_to_user principal}</label>\n"
291 295
    end
296
    s.html_safe
297
  end
298

  
299
  # Returns a string for users/groups option tags
300
  def principals_options_for_select(collection, selected=nil)
301
    s = ''
302
    groups = ''
303
    collection.sort.each do |element|
304
      selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
305
      (element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
306
    end
307
    unless groups.empty?
308
      s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
309
    end
292 310
    s
293 311
  end
294 312

  
......
308 326
  end
309 327

  
310 328
  def html_hours(text)
311
    text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
329
    text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
312 330
  end
313 331

  
314 332
  def authoring(created, author, options={})
315
    l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created))
333
    l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
316 334
  end
317 335

  
318 336
  def time_tag(time)
......
339 357

  
340 358
    html = ''
341 359
    if paginator.current.previous
342
      html << link_to_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
360
      # \xc2\xab(utf-8) = &#171;
361
      html << link_to_content_update(
362
                   "\xc2\xab " + l(:label_previous),
363
                   url_param.merge(page_param => paginator.current.previous)) + ' '
343 364
    end
344 365

  
345 366
    html << (pagination_links_each(paginator, options) do |n|
......
347 368
    end || '')
348 369

  
349 370
    if paginator.current.next
350
      html << ' ' + link_to_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
371
      # \xc2\xbb(utf-8) = &#187;
372
      html << ' ' + link_to_content_update(
373
                      (l(:label_next) + " \xc2\xbb"),
374
                      url_param.merge(page_param => paginator.current.next))
351 375
    end
352 376

  
353 377
    unless count.nil?
......
357 381
      end
358 382
    end
359 383

  
360
    html
384
    html.html_safe
361 385
  end
362 386

  
363 387
  def per_page_links(selected=nil)
......
367 391
    links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
368 392
  end
369 393

  
370
  def reorder_links(name, url)
371
    link_to(image_tag('2uparrow.png',   :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
372
    link_to(image_tag('1uparrow.png',   :alt => l(:label_sort_higher)),  url.merge({"#{name}[move_to]" => 'higher'}),  :method => :post, :title => l(:label_sort_higher)) +
373
    link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),   url.merge({"#{name}[move_to]" => 'lower'}),   :method => :post, :title => l(:label_sort_lower)) +
374
    link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),  url.merge({"#{name}[move_to]" => 'lowest'}),  :method => :post, :title => l(:label_sort_lowest))
394
  def reorder_links(name, url, method = :post)
395
    link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
396
            url.merge({"#{name}[move_to]" => 'highest'}),
397
            :method => method, :title => l(:label_sort_highest)) +
398
    link_to(image_tag('1uparrow.png',   :alt => l(:label_sort_higher)),
399
            url.merge({"#{name}[move_to]" => 'higher'}),
400
           :method => method, :title => l(:label_sort_higher)) +
401
    link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
402
            url.merge({"#{name}[move_to]" => 'lower'}),
403
            :method => method, :title => l(:label_sort_lower)) +
404
    link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
405
            url.merge({"#{name}[move_to]" => 'lowest'}),
406
           :method => method, :title => l(:label_sort_lowest))
375 407
  end
376 408

  
377 409
  def breadcrumb(*args)
378 410
    elements = args.flatten
379
    elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
411
    elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
380 412
  end
381 413

  
382 414
  def other_formats_links(&block)
383
    concat('<p class="other-formats">' + l(:label_export_to))
415
    concat('<p class="other-formats">'.html_safe + l(:label_export_to))
384 416
    yield Redmine::Views::OtherFormatsBuilder.new(self)
385
    concat('</p>')
417
    concat('</p>'.html_safe)
386 418
  end
387 419

  
388 420
  def page_header_title
......
414 446

  
415 447
  def html_title(*args)
416 448
    if args.empty?
417
      title = []
449
      title = @html_title || []
418 450
      title << @project.name if @project
419
      title += @html_title if @html_title
420
      title << Setting.app_title
451
      title << Setting.app_title unless Setting.app_title == title.last
421 452
      title.select {|t| !t.blank? }.join(' - ')
422 453
    else
423 454
      @html_title ||= []
......
463 494
    project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
464 495
    only_path = options.delete(:only_path) == false ? false : true
465 496

  
466
    text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
497
    text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
467 498

  
468 499
    @parsed_headings = []
500
    @current_section = 0 if options[:edit_section_links]
469 501
    text = parse_non_pre_blocks(text) do |text|
470
      [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
502
      [:parse_sections, :parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_macros, :parse_headings].each do |method_name|
471 503
        send method_name, text, project, obj, attr, only_path, options
472 504
      end
473 505
    end
......
505 537
    while tag = tags.pop
506 538
      parsed << "</#{tag}>"
507 539
    end
508
    parsed
540
    parsed.html_safe
509 541
  end
510 542

  
511 543
  def parse_inline_attachments(text, project, obj, attr, only_path, options)
512 544
    # when using an image link, try to use an attachment, if possible
513 545
    if options[:attachments] || (obj && obj.respond_to?(:attachments))
514
      attachments = nil
515
      text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
546
      attachments = options[:attachments] || obj.attachments
547
      text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
516 548
        filename, ext, alt, alttext = $1.downcase, $2, $3, $4
517
        attachments ||= (options[:attachments] || obj.attachments).sort_by(&:created_on).reverse
518 549
        # search for the picture in attachments
519
        if found = attachments.detect { |att| att.filename.downcase == filename }
520
          image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
550
        if found = Attachment.latest_attach(attachments, filename)
551
          image_url = url_for :only_path => only_path, :controller => 'attachments',
552
                              :action => 'download', :id => found
521 553
          desc = found.description.to_s.gsub('"', '')
522 554
          if !desc.blank? && alttext.blank?
523 555
            alt = " title=\"#{desc}\" alt=\"#{desc}\""
524 556
          end
525
          "src=\"#{image_url}\"#{alt}"
557
          "src=\"#{image_url}\"#{alt}".html_safe
526 558
        else
527
          m
559
          m.html_safe
528 560
        end
529 561
      end
530 562
    end
......
557 589
          if page =~ /^(.+?)\#(.+)$/
558 590
            page, anchor = $1, $2
559 591
          end
592
          anchor = sanitize_anchor_name(anchor) if anchor.present?
560 593
          # check if page exists
561 594
          wiki_page = link_project.wiki.find_page(page)
562
          url = case options[:wiki_links]
563
            when :local; "#{title}.html"
564
            when :anchor; "##{title}"   # used for single-file wiki export
595
          url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
596
            "##{anchor}"
597
          else
598
            case options[:wiki_links]
599
            when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
600
            when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
565 601
            else
566 602
              wiki_page_id = page.present? ? Wiki.titleize(page) : nil
567 603
              url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :anchor => anchor)
568 604
            end
569
          link_to((title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
605
          end
606
          link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
570 607
        else
571 608
          # project or wiki doesn't exist
572
          all
609
          all.html_safe
573 610
        end
574 611
      else
575
        all
612
        all.html_safe
576 613
      end
577 614
    end
578 615
  end
......
610 647
  #     identifier:version:1.0.0
611 648
  #     identifier:source:some/file
612 649
  def parse_redmine_links(text, project, obj, attr, only_path, options)
613
    text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
650
    text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|forum|news|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
614 651
      leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $7 || $9, $8 || $10
615 652
      link = nil
616 653
      if project_identifier
......
620 657
        if prefix.nil? && sep == 'r'
621 658
          # project.changesets.visible raises an SQL error because of a double join on repositories
622 659
          if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier))
623
            link = link_to("#{project_prefix}r#{identifier}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
660
            link = link_to(h("#{project_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
624 661
                                      :class => 'changeset',
625 662
                                      :title => truncate_single_line(changeset.comments, :length => 100))
626 663
          end
......
647 684
            if message = Message.visible.find_by_id(oid, :include => :parent)
648 685
              link = link_to_message(message, {:only_path => only_path}, :class => 'message')
649 686
            end
687
          when 'forum'
688
            if board = Board.visible.find_by_id(oid)
689
              link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
690
                                             :class => 'board'
691
            end
692
          when 'news'
693
            if news = News.visible.find_by_id(oid)
694
              link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
695
                                            :class => 'news'
696
            end
650 697
          when 'project'
651 698
            if p = Project.visible.find_by_id(oid)
652 699
              link = link_to_project(p, {:only_path => only_path}, :class => 'project')
......
666 713
              link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
667 714
                                              :class => 'version'
668 715
            end
716
          when 'forum'
717
            if project && board = project.boards.visible.find_by_name(name)
718
              link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
719
                                             :class => 'board'
720
            end
721
          when 'news'
722
            if project && news = project.news.visible.find_by_title(name)
723
              link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
724
                                            :class => 'news'
725
            end
669 726
          when 'commit'
670 727
            if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
671 728
              link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier},
672 729
                                           :class => 'changeset',
673
                                           :title => truncate_single_line(changeset.comments, :length => 100)
730
                                           :title => truncate_single_line(h(changeset.comments), :length => 100)
674 731
            end
675 732
          when 'source', 'export'
676 733
            if project && project.repository && User.current.allowed_to?(:browse_repository, project)
......
696 753
          end
697 754
        end
698 755
      end
699
      leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")
756
      (leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")).html_safe
700 757
    end
701 758
  end
702 759

  
703
  HEADING_RE = /<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>/i unless const_defined?(:HEADING_RE)
760
  HEADING_RE = /(<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>)/i unless const_defined?(:HEADING_RE)
761

  
762
  def parse_sections(text, project, obj, attr, only_path, options)
763
    return unless options[:edit_section_links]
764
    text.gsub!(HEADING_RE) do
765
      @current_section += 1
766
      if @current_section > 1
767
        content_tag('div',
768
          link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
769
          :class => 'contextual',
770
          :title => l(:button_edit_section)) + $1
771
      else
772
        $1
773
      end
774
    end
775
  end
704 776

  
705 777
  # Headings and TOC
706 778
  # Adds ids and links to headings unless options[:headings] is set to false
......
708 780
    return if options[:headings] == false
709 781

  
710 782
    text.gsub!(HEADING_RE) do
711
      level, attrs, content = $1.to_i, $2, $3
783
      level, attrs, content = $2.to_i, $3, $4
712 784
      item = strip_tags(content).strip
713
      anchor = item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
785
      anchor = sanitize_anchor_name(item)
786
      # used for single-file wiki export
787
      anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
714 788
      @parsed_headings << [level, anchor, item]
715 789
      "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
716 790
    end
717 791
  end
718 792

  
793
  MACROS_RE = /
794
                (!)?                        # escaping
795
                (
796
                \{\{                        # opening tag
797
                ([\w]+)                     # macro name
798
                (\(([^\}]*)\))?             # optional arguments
799
                \}\}                        # closing tag
800
                )
801
              /x unless const_defined?(:MACROS_RE)
802

  
803
  # Macros substitution
804
  def parse_macros(text, project, obj, attr, only_path, options)
805
    text.gsub!(MACROS_RE) do
806
      esc, all, macro = $1, $2, $3.downcase
807
      args = ($5 || '').split(',').each(&:strip)
808
      if esc.nil?
809
        begin
810
          exec_macro(macro, obj, args)
811
        rescue => e
812
          "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
813
        end || all
814
      else
815
        all
816
      end
817
    end
818
  end
819

  
719 820
  TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
720 821

  
721 822
  # Renders the TOC with given headings
......
754 855
    text.to_s.
755 856
      gsub(/\r\n?/, "\n").                    # \r\n and \r -> \n
756 857
      gsub(/\n\n+/, "<br /><br />").          # 2+ newline  -> 2 br
757
      gsub(/([^\n]\n)(?=[^\n])/, '\1<br />')  # 1 newline   -> br
858
      gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline   -> br
859
      html_safe
758 860
  end
759 861

  
760 862
  def lang_options_for_select(blank=true)
......
767 869
    content_tag("label", label_text)
768 870
  end
769 871

  
770
  def labelled_tabular_form_for(name, object, options, &proc)
872
  def labelled_tabular_form_for(*args, &proc)
873
    args << {} unless args.last.is_a?(Hash)
874
    options = args.last
771 875
    options[:html] ||= {}
772 876
    options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
773
    form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
877
    options.merge!({:builder => TabularFormBuilder})
878
    form_for(*args, &proc)
879
  end
880

  
881
  def labelled_form_for(*args, &proc)
882
    args << {} unless args.last.is_a?(Hash)
883
    options = args.last
884
    options.merge!({:builder => TabularFormBuilder})
885
    form_for(*args, &proc)
774 886
  end
775 887

  
776 888
  def back_url_hidden_field_tag
......
781 893

  
782 894
  def check_all_links(form_name)
783 895
    link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
784
    " | " +
896
    " | ".html_safe +
785 897
    link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
786 898
  end
787 899

  
......
794 906
    legend = options[:legend] || ''
795 907
    content_tag('table',
796 908
      content_tag('tr',
797
        (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : '') +
798
        (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : '') +
799
        (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : '')
800
      ), :class => 'progress', :style => "width: #{width};") +
801
      content_tag('p', legend, :class => 'pourcent')
909
        (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
910
        (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
911
        (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
912
      ), :class => 'progress', :style => "width: #{width};").html_safe +
913
      content_tag('p', legend, :class => 'pourcent').html_safe
802 914
  end
803 915

  
804 916
  def checked_image(checked=true)
......
836 948
      options[:class] << ' disabled'
837 949
      url = '#'
838 950
    end
839
    link_to name, url, options
951
    link_to h(name), url, options
840 952
  end
841 953

  
842 954
  def calendar_for(field_id)
......
879 991
    (@has_content && @has_content[name]) || false
880 992
  end
881 993

  
994
  def email_delivery_enabled?
995
    !!ActionMailer::Base.perform_deliveries
996
  end
997

  
882 998
  # Returns the avatar image tag for the given +user+ if avatars are enabled
883 999
  # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
884 1000
  def avatar(user, options = { })
......
896 1012
    end
897 1013
  end
898 1014

  
1015
  def sanitize_anchor_name(anchor)
1016
    anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1017
  end
1018

  
899 1019
  # Returns the javascript tags that are included in the html layout head
900 1020
  def javascript_heads
901 1021
    tags = javascript_include_tag(:defaults)
902 1022
    unless User.current.pref.warn_on_leaving_unsaved == '0'
903
      tags << "\n" + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
1023
      tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
904 1024
    end
905 1025
    tags
906 1026
  end
907 1027

  
908 1028
  def favicon
909
    "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />"
1029
    "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
910 1030
  end
911 1031

  
912 1032
  def robot_exclusion_tag
913
    '<meta name="robots" content="noindex,follow,noarchive" />'
1033
    '<meta name="robots" content="noindex,follow,noarchive" />'.html_safe
914 1034
  end
915 1035

  
916 1036
  def stylesheet_platform_font_tag
app/helpers/attachments_helper.rb
1
# encoding: utf-8
2
#
1 3
# Redmine - project management software
2 4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
......
28 30
    end
29 31
  end
30 32

  
31
  def to_utf8(str)
32
    if str.respond_to?(:force_encoding)
33
      str.force_encoding('UTF-8')
34
      return str if str.valid_encoding?
35
    else
36
      return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
37
    end
38

  
39
    begin
40
      Iconv.conv('UTF-8//IGNORE', 'UTF-8', str + '  ')[0..-3]
41
    rescue Iconv::InvalidEncoding
42
      # "UTF-8//IGNORE" is not supported on some OS
43
      str
33
  def render_api_attachment(attachment, api)
34
    api.attachment do
35
      api.id attachment.id
36
      api.filename attachment.filename
37
      api.filesize attachment.filesize
38
      api.content_type attachment.content_type
39
      api.description attachment.description
40
      api.content_url url_for(:controller => 'attachments', :action => 'download', :id => attachment, :filename => attachment.filename, :only_path => false)
41
      api.author(:id => attachment.author.id, :name => attachment.author.name) if attachment.author
42
      api.created_on attachment.created_on
44 43
    end
45 44
  end
46 45
end
app/helpers/auth_sources_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/boards_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006-2007  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/calendars_helper.rb
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
1 20
module CalendarsHelper
2 21
  def link_to_previous_month(year, month, options={})
3 22
    target_year, target_month = if month == 1
......
5 24
                                else
6 25
                                  [year, month - 1]
7 26
                                end
8
    
27

  
9 28
    name = if target_month == 12
10 29
             "#{month_name(target_month)} #{target_year}"
11 30
           else
12 31
             "#{month_name(target_month)}"
13 32
           end
14 33

  
15
    link_to_month(('&#171; ' + name), target_year, target_month, options)
34
    # \xc2\xab(utf-8) = &#171;
35
    link_to_month(("\xc2\xab " + name), target_year, target_month, options)
16 36
  end
17 37

  
18 38
  def link_to_next_month(year, month, options={})
......
28 48
             "#{month_name(target_month)}"
29 49
           end
30 50

  
31
    link_to_month((name + ' &#187;'), target_year, target_month, options)
51
    # \xc2\xbb(utf-8) = &#187;
52
    link_to_month((name + " \xc2\xbb"), target_year, target_month, options)
32 53
  end
33 54

  
34 55
  def link_to_month(link_name, year, month, options={})
35
    link_to_content_update(link_name, params.merge(:year => year, :month => month))
56
    link_to_content_update(h(link_name), params.merge(:year => year, :month => month))
36 57
  end
37 58
end
app/helpers/custom_fields_helper.rb
1
# encoding: utf-8
2
#
1 3
# Redmine - project management software
2 4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
......
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
29 31
            {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
30 32
            ]
31 33
  end
32
  
34

  
33 35
  # Return custom field html tag corresponding to its format
34 36
  def custom_field_tag(name, custom_value)	
35 37
    custom_field = custom_value.custom_field
......
39 41
    field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
40 42
    case field_format.try(:edit_as)
41 43
    when "date"
42
      text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) + 
44
      text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
43 45
      calendar_for(field_id)
44 46
    when "text"
45 47
      text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
......
47 49
      hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
48 50
    when "list"
49 51
      blank_option = custom_field.is_required? ?
50
                       (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') : 
52
                       (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
51 53
                       '<option></option>'
52 54
      select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id)
53 55
    else
54 56
      text_field_tag(field_name, custom_value.value, :id => field_id)
55 57
    end
56 58
  end
57
  
59

  
58 60
  # Return custom field label tag
59 61
  def custom_field_label_tag(name, custom_value)
60
    content_tag "label", custom_value.custom_field.name +
61
	(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>" : ""),
62
    content_tag "label", h(custom_value.custom_field.name) +
63
	(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
62 64
	:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}",
63 65
	:class => (custom_value.errors.empty? ? nil : "error" )
64 66
  end
65
  
67

  
66 68
  # Return custom field tag with its label tag
67 69
  def custom_field_tag_with_label(name, custom_value)
68 70
    custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
69 71
  end
70
  
72

  
71 73
  def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
72 74
    field_name = "#{name}[custom_field_values][#{custom_field.id}]"
73 75
    field_id = "#{name}_custom_field_values_#{custom_field.id}"
74 76
    field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
75 77
    case field_format.try(:edit_as)
76 78
      when "date"
77
        text_field_tag(field_name, '', :id => field_id, :size => 10) + 
79
        text_field_tag(field_name, '', :id => field_id, :size => 10) +
78 80
        calendar_for(field_id)
79 81
      when "text"
80 82
        text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
......
94 96
    return "" unless custom_value
95 97
    format_value(custom_value.value, custom_value.custom_field.field_format)
96 98
  end
97
  
99

  
98 100
  # Return a string used to display a custom value
99 101
  def format_value(value, field_format)
100 102
    Redmine::CustomFieldFormat.format_value(value, field_format) # Proxy
......
104 106
  def custom_field_formats_for_select(custom_field)
105 107
    Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
106 108
  end
107
  
109

  
108 110
  # Renders the custom_values in api views
109 111
  def render_api_custom_values(custom_values, api)
110 112
    api.array :custom_fields do
app/helpers/documents_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/enumerations_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/gantt_helper.rb
1
# encoding: utf-8
2
#
1 3
# Redmine - project management software
2 4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
......
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
25 27
          params.merge(gantt.params.merge(:zoom => (gantt.zoom+1))),
26 28
          :class => 'icon icon-zoom-in'
27 29
      else
28
        content_tag('span', l(:text_zoom_in), :class => 'icon icon-zoom-in')
30
        content_tag('span', l(:text_zoom_in), :class => 'icon icon-zoom-in').html_safe
29 31
      end
30
      
32

  
31 33
    when :out
32 34
      if gantt.zoom > 1
33 35
        link_to_content_update l(:text_zoom_out),
34 36
          params.merge(gantt.params.merge(:zoom => (gantt.zoom-1))),
35 37
          :class => 'icon icon-zoom-out'
36 38
      else
37
        content_tag('span', l(:text_zoom_out), :class => 'icon icon-zoom-out')
39
        content_tag('span', l(:text_zoom_out), :class => 'icon icon-zoom-out').html_safe
38 40
      end
39 41
    end
40 42
  end
app/helpers/groups_helper.rb
1
# encoding: utf-8
2
#
1 3
# Redmine - project management software
2
# Copyright (C) 2006-2009  Jean-Philippe Lang
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
24 26
    end
25 27
    options
26 28
  end
27
  
29

  
28 30
  def group_settings_tabs
29 31
    tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general},
30 32
            {:name => 'users', :partial => 'groups/users', :label => :label_user_plural},
app/helpers/issue_categories_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/issue_moves_helper.rb
1
# encoding: utf-8
2
#
1 3
module IssueMovesHelper
2 4
end
app/helpers/issue_relations_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006-2007  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/issue_statuses_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/issues_helper.rb
1
# encoding: utf-8
2
#
1 3
# Redmine - project management software
2 4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
......
46 48
    @cached_label_priority ||= l(:field_priority)
47 49
    @cached_label_project ||= l(:field_project)
48 50

  
49
    link_to_issue(issue) + "<br /><br />" +
51
    (link_to_issue(issue) + "<br /><br />" +
50 52
      "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
51
      "<strong>#{@cached_label_status}</strong>: #{issue.status.name}<br />" +
53
      "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />" +
52 54
      "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
53 55
      "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
54
      "<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
55
      "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
56
      "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />" +
57
      "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}").html_safe
56 58
  end
57 59

  
58 60
  def issue_heading(issue)
......
72 74
    end
73 75
    s << content_tag('h3', subject)
74 76
    s << '</div>' * (ancestors.size + 1)
75
    s
77
    s.html_safe
76 78
  end
77 79

  
78 80
  def render_descendants_tree(issue)
......
87 89
             :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
88 90
    end
89 91
    s << '</form></table>'
90
    s
92
    s.html_safe
91 93
  end
92 94

  
93 95
  def render_custom_fields_rows(issue)
......
106 108
      n += 1
107 109
    end
108 110
    s << "</tr>\n"
109
    s
111
    s.html_safe
110 112
  end
111 113

  
112 114
  def issues_destroy_confirmation_message(issues)
......
145 147
    # links to #index on issues/show
146 148
    url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
147 149

  
148
    content_tag('h3', title) +
150
    content_tag('h3', h(title)) +
149 151
      queries.collect {|query|
150 152
          link_to(h(query.name), url_params.merge(:query_id => query))
151 153
        }.join('<br />')
......
206 208
    unless no_html
207 209
      label = content_tag('strong', label)
208 210
      old_value = content_tag("i", h(old_value)) if detail.old_value
209
      old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
211
      old_value = content_tag("strike", old_value) if detail.old_value and detail.value.blank?
210 212
      if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
211 213
        # Link to the attachment if it has not been removed
212 214
        value = link_to_attachment(a)
......
263 265
    end
264 266
  end
265 267

  
266
  def issues_to_csv(issues, project = nil)
267
    ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
268
  def issues_to_csv(issues, project, query, options={})
268 269
    decimal_separator = l(:general_csv_decimal_separator)
270
    encoding = l(:general_csv_encoding)
271
    columns = (options[:columns] == 'all' ? query.available_columns : query.columns)
272

  
269 273
    export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
270 274
      # csv header fields
271
      headers = [ "#",
272
                  l(:field_status),
273
                  l(:field_project),
274
                  l(:field_tracker),
275
                  l(:field_priority),
276
                  l(:field_subject),
277
                  l(:field_assigned_to),
278
                  l(:field_category),
279
                  l(:field_fixed_version),
280
                  l(:field_author),
281
                  l(:field_start_date),
282
                  l(:field_due_date),
283
                  l(:field_done_ratio),
284
                  l(:field_estimated_hours),
285
                  l(:field_parent_issue),
286
                  l(:field_created_on),
287
                  l(:field_updated_on)
288
                  ]
289
      # Export project custom fields if project is given
290
      # otherwise export custom fields marked as "For all projects"
291
      custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
292
      custom_fields.each {|f| headers << f.name}
293
      # Description in the last column
294
      headers << l(:field_description)
295
      csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
275
      csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) } +
276
        (options[:description] ? [Redmine::CodesetUtil.from_utf8(l(:field_description), encoding)] : [])
277

  
296 278
      # csv lines
297 279
      issues.each do |issue|
298
        fields = [issue.id,
299
                  issue.status.name,
300
                  issue.project.name,
301
                  issue.tracker.name,
302
                  issue.priority.name,
303
                  issue.subject,
304
                  issue.assigned_to,
305
                  issue.category,
306
                  issue.fixed_version,
307
                  issue.author.name,
308
                  format_date(issue.start_date),
309
                  format_date(issue.due_date),
310
                  issue.done_ratio,
311
                  issue.estimated_hours.to_s.gsub('.', decimal_separator),
312
                  issue.parent_id,
313
                  format_time(issue.created_on),
314
                  format_time(issue.updated_on)
315
                  ]
316
        custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
317
        fields << issue.description
318
        csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
280
        col_values = columns.collect do |column|
281
          s = if column.is_a?(QueryCustomFieldColumn)
282
            cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
283
            show_value(cv)
284
          else
285
            value = issue.send(column.name)
286
            if value.is_a?(Date)
287
              format_date(value)
288
            elsif value.is_a?(Time)
289
              format_time(value)
290
            elsif value.is_a?(Float)
291
              value.to_s.gsub('.', decimal_separator)
292
            else
293
              value
294
            end
295
          end
296
          s.to_s
297
        end
298
        csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } +
299
          (options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : [])
319 300
      end
320 301
    end
321 302
    export
app/helpers/journals_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006-2008  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
......
24 26
      links << link_to_remote(image_tag('comment.png'),
25 27
                              { :url => {:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal} },
26 28
                              :title => l(:button_quote)) if options[:reply_links]
27
      links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes", 
29
      links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
28 30
                                             { :controller => 'journals', :action => 'edit', :id => journal },
29 31
                                                :title => l(:button_edit)) if editable
30 32
    end
......
34 36
    css_classes << " editable" if editable
35 37
    content_tag('div', content, :id => "journal-#{journal.id}-notes", :class => css_classes)
36 38
  end
37
  
39

  
38 40
  def link_to_in_place_notes_editor(text, field_id, url, options={})
39 41
    onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;"
40 42
    link_to text, '#', options.merge(:onclick => onclick)
app/helpers/mail_handler_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006-2008  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/members_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/messages_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006-2007  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/my_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/news_helper.rb
1
# redMine - project management software
2
# Copyright (C) 2006  Jean-Philippe Lang
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
4 6
# This program is free software; you can redistribute it and/or
5 7
# modify it under the terms of the GNU General Public License
6 8
# as published by the Free Software Foundation; either version 2
7 9
# of the License, or (at your option) any later version.
8
# 
10
#
9 11
# This program is distributed in the hope that it will be useful,
10 12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 14
# GNU General Public License for more details.
13
# 
15
#
14 16
# You should have received a copy of the GNU General Public License
15 17
# along with this program; if not, write to the Free Software
16 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
app/helpers/projects_helper.rb
1
# encoding: utf-8
2
#
1 3
# Redmine - project management software
2 4
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 5
#
......
46 48
    options = ''
47 49
    options << "<option value=''></option>" if project.allowed_parents.include?(nil)
48 50
    options << project_tree_options_for_select(project.allowed_parents.compact, :selected => selected)
49
    content_tag('select', options, :name => 'project[parent_id]', :id => 'project_parent_id')
51
    content_tag('select', options.html_safe, :name => 'project[parent_id]', :id => 'project_parent_id')
50 52
  end
51 53

  
52 54
  def render_project_short_description(project)
......
90 92
      s << ("</li></ul>\n" * ancestors.size)
91 93
      @project = original_project
92 94
    end
93
    s
95
    s.html_safe
94 96
  end
95 97

  
96 98

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

  
18 20
module QueriesHelper
19
  
21

  
20 22
  def operators_for_select(filter_type)
21 23
    Query.operators_by_filter_type[filter_type].collect {|o| [l(Query.operators[o]), o]}
22 24
  end
23
  
25

  
24 26
  def column_header(column)
25 27
    column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
26
                                                        :default_order => column.default_order) : 
27
                      content_tag('th', column.caption)
28
                                                        :default_order => column.default_order) :
29
                      content_tag('th', h(column.caption))
28 30
  end
29
  
31

  
30 32
  def column_content(column, issue)
31 33
    value = column.value(issue)
32
    
34

  
33 35
    case value.class.name
34 36
    when 'String'
35 37
      if column.name == :subject
......
45 47
      if column.name == :done_ratio
46 48
        progress_bar(value, :width => '80px')
47 49
      else
48
        value.to_s
50
        h(value.to_s)
49 51
      end
50 52
    when 'User'
51 53
      link_to_user value
......
74 76
      @query.project = @project
75 77
      session[:query] = {:id => @query.id, :project_id => @query.project_id}
76 78
      sort_clear
79
    elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
80
      # Give it a name, required to be valid
81
      @query = Query.new(:name => "_")
82
      @query.project = @project
83
      build_query_from_params
84
      session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
77 85
    else
78
      if api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
79
        # Give it a name, required to be valid
80
        @query = Query.new(:name => "_")
81
        @query.project = @project
82
        if params[:fields] || params[:f]
83
          @query.filters = {}
84
          @query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v])
85
        else
86
          @query.available_filters.keys.each do |field|
87
            @query.add_short_filter(field, params[field]) if params[field]
88
          end
89
        end
90
        @query.group_by = params[:group_by]
91
        @query.column_names = params[:c] || (params[:query] && params[:query][:column_names])
92
        session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff