# encoding: utf-8 # # Redmine - project management software # Copyright (C) 2006-2013 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. require 'forwardable' require 'cgi' module ApplicationHelper include Redmine::WikiFormatting::Macros::Definitions include Redmine::I18n include GravatarHelper::PublicMethods include Redmine::Pagination::Helper include AvatarHelper ## added by william include PraiseTreadHelper include CoursesHelper extend Forwardable def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter # Time 2015-03-24 15:27:29 # Author lizanle # Description 从硬盘上删除对应的资源文件 def delete_kindeditor_assets_from_disk owner_id,owner_type assets = Kindeditor::Asset.where(["owner_id = ? and owner_type = ?",owner_id,owner_type]) if !assets.nil? && !assets.blank? assets.all.each do |asset| next if asset.nil? filepath = File.join(Rails.root,"public","files","uploads", asset[:created_at].to_s.gsub("+0800","").to_datetime.strftime("%Y%m").to_s, asset[:asset].to_s) File.delete(filepath) if File.exist?filepath end end end # Time 2015-03-24 16:38:05 # Author lizanle # Description after save后需要进行资源记录的更新 # owner_type = 1 对应的是 memo # owner_type = 2 对应的是forum # owner_type = 3 对应的是message # owner_type = 4 对应的是news # owner_type = 5 对应的是comment def update_kindeditor_assets_owner ids,owner_id,owner_type ids.each do |id| asset = Kindeditor::Asset.find(id.to_i) asset.owner_id = owner_id asset.owner_type = owner_type asset.save end end # Added by young # Define the course menu's link class # 不是数组的转化成数组,然后判断当前menu_item是否在给定的列表 # REVIEW: 目测menu的机制,貌似不是很需要转换,再说 def link_class(label) labels = label.is_a?(Array) ? label : ([] << label) #a = current_menu_item labels.include?(current_menu_item) ? 'selected' : '' end #Ended by young # Return true if user is authorized for controller/action, otherwise false def authorize_for(controller, action) User.current.allowed_to?({:controller => controller, :action => action}, @project) end # add by nwb def authorize_for_course(controller, action) User.current.allowed_to?({:controller => controller, :action => action}, @course) end def authorize_for_contest(controller, action) User.current.allowed_to?({:controller => controller, :action => action}, @contest) end # Display a link if user is authorized # # @param [String] name Anchor text (passed to link_to) # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized # @param [optional, Hash] html_options Options passed to link_to # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference) link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action]) end def link_to_if_authorized_course(name, options = {}, html_options = nil, *parameters_for_method_reference) link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for_course(options[:controller] || params[:controller], options[:action]) end def link_to_if_authorized_contest(name, options = {}, html_options = nil, *parameters_for_method_reference) link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for_contest(options[:controller] || params[:controller], options[:action]) end # Displays a link to user's account page if active def link_to_user(user, canShowRealName = false, options={}) if user.is_a?(User) if canShowRealName name = h(user.realname(options[:format])) else name = h(user.name(options[:format])) end #if user.active? || (User.current.admin? && user.logged?) # link_to name, {:controller=> 'users', :action => 'show', id: user.id, host: Setting.host_user}, :class => user.css_classes #else # name #end link_to name, {:controller=> 'users', :action => 'show', id: user.id, host: Setting.host_user}, :class => user.css_classes else h(user.to_s) end end def link_to_isuue_user(user, options={}) if user.is_a?(User) name = h(user.name(options[:format])) link_to name, {:controller=> 'users', :action => 'show', id: user.id, host: Setting.host_user}, :class => "pro_info_p" else h(user.to_s) end end def link_to_settings_user(user, options={}) if user.is_a?(User) name = h(user.name(options[:format])) link_to name, {:controller=> 'users', :action => 'show', id: user.id, host: Setting.host_user}, :class => "w90 c_orange fl" else h(user.to_s) end end #重载上面方法,增加样式显示 def link_to_user_header user,canShowRealName=false,options={} if user.is_a?(User) if canShowRealName name = user.show_name name = user.login if name == "" else name = user.login end link_to name, {:controller=> 'users', :action => 'show', id: user.id, host: Setting.host_user}, :class => options[:class] else h(user.to_s) end end # Displays a link to +issue+ with its subject. # Examples: # # link_to_issue(issue) # => Defect #6: This is the subject # link_to_issue(issue, :truncate => 6) # => Defect #6: This i... # link_to_issue(issue, :subject => false) # => Defect #6 # link_to_issue(issue, :project => true) # => Foo - Defect #6 # link_to_issue(issue, :subject => false, :tracker => false) # => #6 # def link_to_issue(issue, options={}) title = nil subject = nil text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}" if options[:subject] == false title = truncate(issue.subject, :length => 60) else subject = issue.subject if options[:truncate] subject = truncate(subject, :length => options[:truncate]) end end s = link_to text, issue_path(issue), :class => issue.css_classes, :title => title s << h(": #{subject}") if subject s = h("#{issue.project} - ") + s if options[:project] s end def link_to_issue_version(issue, options={}) title = nil subject = nil text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}" if options[:subject] == false title = truncate(issue.subject, :length => 60) else subject = issue.subject if options[:truncate] subject = truncate(subject, :length => 60) end end if issue.status_id == 5 s = link_to text, issue_path(issue), :class => "text_line_s", :title => title else s = link_to text, issue_path(issue), :class => "c_blue", :title => title end s << h(": #{subject}") if subject s = h("#{issue.project} - ") + s if options[:project] s end # Generates a link to an attachment. # Options: # * :text - Link text (default to attachment filename) # * :download - Force download (default: false) def link_to_short_attachment(attachment, options={}) length = options[:length] ? options[:length]:23 text = h(truncate(options.delete(:text) || attachment.filename, length: length, omission: '...')) route_method = options.delete(:download) ? :download_named_attachment_path : :named_attachment_path html_options = options.slice!(:only_path) url = send(route_method, attachment, attachment.filename, options) link_to text, url, html_options end # Generates a link to an attachment. # Options: # * :text - Link text (default to attachment filename) # * :download - Force download (default: false) def link_to_attachment(attachment, options={}) token = options[:token] if options[:token] text = options.delete(:text) || attachment.filename route_method = options.delete(:download) ? :download_named_attachment_path : :named_attachment_path html_options = options.slice!(:only_path) url = send(route_method, attachment, attachment.filename, options) url << "?token=#{token}" unless token.nil? link_to text, url, html_options end def link_to_attachment_img(attachment, options={}) text = options.delete(:text) || attachment.filename route_method = options.delete(:download) ? :download_named_attachment_path : :named_attachment_path html_options = options.slice!(:only_path) url = send(route_method, attachment, attachment.filename, options) image_tag url, html_options end # Generates a link to a SCM revision # Options: # * :text - Link text (default to the formatted revision) def link_to_revision(revision, repository, options={}) if repository.is_a?(Project) repository = repository.repository end text = options.delete(:text) || format_revision(revision) rev = revision.respond_to?(:identifier) ? revision.identifier : revision link_to( h(text), {:controller => 'repositories', :action => 'revision', :id => repository.project, :repository_id => repository.identifier_param, :rev => rev}, :title => l(:label_revision_id, format_revision(revision)) ) end # Generates a link to a message def link_to_message(message, options={}, html_options = nil) link_to( truncate(message.subject, :length => 60), board_message_path(message.board_id, message.parent_id || message.id, { :r => (message.parent_id && message.id), :anchor => (message.parent_id ? "message-#{message.id}" : nil) }.merge(options)), html_options ) end # Generates a link to a project if active # Examples: # # link_to_project(project) # => link to the specified project overview # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options # link_to_project(project, {}, :class => "project") # => html options with default url (project overview) # def link_to_project(project, options={}, html_options = nil) if project.archived? h(project.name) elsif options.key?(:action) ActiveSupport::Deprecation.warn "#link_to_project with :action option is deprecated and will be removed in Redmine 3.0." url = {:controller => 'projects', :action => 'show', :id => project}.merge(options) link_to project.name, url, html_options else link_to project.name, project_path(project, options), html_options end end def link_to_course(course, options={}, html_options = nil) if course.archived? h(course.name) elsif options.key?(:action) ActiveSupport::Deprecation.warn "#link_to_course with :action option is deprecated and will be removed in Redmine 3.0." url = {:controller => 'courses', :action => 'show', :id => project}.merge(options) link_to course.name, url, html_options else link_to course.name, course_path(course, options), html_options end end # Generates a link to a project settings if active def link_to_project_settings(project, options={}, html_options=nil) if project.active? link_to project.name, settings_project_path(project, options), html_options elsif project.archived? h(project.name) else link_to project.name, project_path(project, options), html_options end end def wiki_page_path(page, options={}) url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options)) end def thumbnail_tag(attachment) link_to image_tag(thumbnail_path(attachment)), named_attachment_path(attachment, attachment.filename), :title => attachment.filename end def thumbnail_issue_tag(attachment) imagesize = attachment.thumbnail(:size => "200*200") imagepath = named_attachment_path(attachment, attachment.filename) if imagesize link_to image_tag(thumbnail_path(attachment), height: '73', width: '100', class: 'issue_attachment_picture'), imagepath, :title => attachment.filename else link_to image_tag(imagepath , height: '73', width: '100', class: 'issue_attachment_picture'), imagepath, :title => attachment.filename end end # 图片缩略图链接 def thumbnail_small_tag(attachment) imagesize = attachment.thumbnail(:size => "200*200") imagepath = named_attachment_path(attachment, attachment.filename) if imagesize link_to image_tag(imagesize), imagepath, :title => attachment.filename else link_to image_tag(imagepath , height: '200', width: '250'), imagepath, :title => attachment.filename end end def toggle_link(name, id, options={}) onclick = "$('##{id}').slideToggle(); " onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ") onclick << "return false;" link_to(name, "javascript:void(0)", :onclick => onclick,:class => options[:class]) end def image_to_function(name, function, html_options = {}) html_options.symbolize_keys! tag(:input, html_options.merge({ :type => "image", :src => image_path(name), :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};" })) end def format_activity_title(text) h(truncate_single_line(text, :length => 100)) end def format_activity_day(date) date == User.current.today ? l(:label_today).titleize : format_date(date) end def format_activity_description(text) h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "
").html_safe #h(truncate(text.to_s, :length => 120).gsub(/<\/?.*?>/,"")).html_safe end def format_version_name(version) if version.project == @project h(truncate(version.name,:length=>20)) else h("#{version.project} - #{truncate(version.name,:length=>20)}") end end def due_date_distance_in_words(date) if date l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date)) end end # Renders a tree of projects as a nested set of unordered lists # The given collection may be a subset of the whole project tree # (eg. some intermediate nodes are private and can not be seen) #Modified by nie. def render_project_nested_lists(projects) s = '' if projects.any? ancestors = [] original_project = @project #modified by nie projects.each do |project| # set the project environment to please macros. @project = project if (ancestors.empty? || project.is_descendant_of?(ancestors.last)) # s << "\n" * ancestors.size) @project = original_project end s.html_safe end def render_course_nested_lists(courses) s = '' if courses.any? ancestors = [] original_course = @course #modified by nie courses.each do |course| # set the project environment to please macros. @course = course if (ancestors.empty? )#|| course.is_descendant_of?(ancestors.last)) s << "\n" end end classes = (ancestors.empty? ? 'root' : 'child') s << "
  • " s << (render :partial => 'courses/course', :locals => {:course => course}).to_s s << "
    \n" ancestors << course end s << ("
  • \n" * ancestors.size) @course = original_course end s.html_safe end #added by young def render_project_nested_lists_new(projects) s = '' if projects.any? ancestors = [] original_project = @project projects.sort_by(&:lft).each do |project| # set the project environment to please macros. @project = project if (ancestors.empty? || project.is_descendant_of?(ancestors.last)) # s << "\n" * ancestors.size) @project = original_project end s.html_safe end #end def render_page_hierarchy(pages, node=nil, options={}) content = '' if pages[node] content << "\n" end content.html_safe end # Renders flash messages def render_flash_messages s = '' flash.each do |k,v| s << content_tag('div', v.html_safe, :class => "flash #{k}", :id => "flash_#{k}") end s.html_safe end # Renders tabs and their content def render_tabs(tabs) if tabs.any? render :partial => 'common/tabs', :locals => {:tabs => tabs} else content_tag 'p', l(:label_no_data), :class => "nodata" end end def render_project_settings_tabs(tabs) if tabs.any? render :partial => 'common/project_tab', :locals => {:tabs => tabs} else content_tag 'p', l(:label_no_data), :class => "nodata" end end # Renders the project quick-jump box def render_project_jump_box return unless User.current.logged? projects = User.current.memberships.collect(&:project).compact.select(&:active?).uniq if projects.any? options = ("" + '').html_safe options << project_tree_options_for_select(projects, :selected => @project) do |p| { :value => project_path(:id => p, :jump => current_menu_item) } end select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }') end end def project_tree_options_for_select(projects, options = {}) s = '' project_tree(projects) do |project, level| name_prefix = (level > 0 ? ' ' * 2 * level + '» ' : '').html_safe tag_options = {:value => project.id} tag_options[:title] = project.name if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project)) tag_options[:selected] = 'selected' else tag_options[:selected] = nil end tag_options.merge!(yield(project)) if block_given? s << content_tag('option', name_prefix + h(project), tag_options) end s.html_safe end # Yields the given block for each project with its level in the tree # # Wrapper for Project#project_tree def project_tree(projects, &block) Project.project_tree(projects, &block) end # 项目版本库可见权限判断 # 条件:1、modules中设置不可见或项目没有版本库;2、如果项目是私有或者项目版本库隐藏则必须是项目成员才可见 def visible_repository?(project) @result = false unless project.enabled_modules.where("name = 'repository'").empty? || project.repositories.count == 0 if (project.hidden_repo || !project.is_public?) if User.current.member_of?(project) @result = true end else @result = true end end return @result end # 判断当前用户是否为项目管理员 def is_project_manager?(user_id, project_id) @result = false mem = Member.where("user_id = ? and project_id = ?",user_id, project_id) unless mem.blank? @result = mem.first.roles.to_s.include?("Manager") ? true : false end return @result end # 公开项目资源可以引用,admin和管理员和资源上传者拥有设置公开私有权限 def authority_pubilic_for_files(project, file) @result = false if (is_project_manager?(User.current.id, @project.id) || file.author_id == User.current.id || User.current.admin) && project_contains_attachment?(project,file) && file.container_id == project.id && file.container_type == "Project" @result = true end return @result end def principals_check_box_tags(name, principals) s = '' principals.each do |principal| s << "\n" end s.html_safe end #项目成员列表复选框生成 def project_member_check_box_tags_ex name, principals s = '' principals.each do |principal| s << "
  • #{ check_box_tag name, principal.id, false, :id => nil } #{h link_to principal.userInfo, user_path( principal.id)}
  • \n" end s.html_safe end #缺陷追踪者列表复选框生成 def issue_watcher_check_box_tags_ex name, principals s = '' principals.each do |principal| s << "
  • #{ check_box_tag name, principal.id, false, :id => nil } #{h link_to principal.userInfo, user_path( principal.id)}
  • \n" end s.html_safe end #扩展的checkbox生成 def principals_check_box_tags_ex(name, principals) s = '' principals.each do |principal| s << "\n" end s.html_safe end # li标签checkbos扩展 def principals_check_box_tags_li(name, principals) s = '' principals.each do |principal| s << "
  • #{ check_box_tag name, principal.id, false, :id => nil } #{h link_to principal.userInfo, user_path( principal.id) }
  • \n" end s.html_safe end #扩展的checkbox生成 def principals_radio_box_tags_ex(name, principals) s = '' principals.each do |principal| s << "\n" end s.html_safe end # Returns a string for users/groups option tags def principals_options_for_select(collection, selected=nil) s = '' if collection.include?(User.current) s << content_tag('option', "<< #{l(:label_me)} >>", :value => User.current.id) end groups = '' collection.sort.each do |element| selected_attribute = ' selected="selected"' if option_value_selected?(element, selected) (element.is_a?(Group) ? groups : s) << %() end unless groups.empty? s << %(#{groups}) end s.html_safe end # Options for the new membership projects combo-box def options_for_membership_project_select(principal, projects) options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---") options << project_tree_options_for_select(projects) do |p| {:disabled => principal.projects.to_a.include?(p)} end options end # Truncates and returns the string as a single line def truncate_single_line(string, *args) truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ') end # Truncates at line break after 250 characters or options[:length] def truncate_lines(string, options={}) length = options[:length] || 250 if string.to_s =~ /\A(.{#{length}}.*?)$/m "#{$1}..." else string end end def anchor(text) text.to_s.gsub(' ', '_') end def html_hours(text) text.gsub(%r{(\d+)\.(\d+)}, '\1.\2').html_safe end def authoring(created, author, options={}) l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe end def added_time(created) l(:label_added_time, :age => time_tag(created)).html_safe end def user_url_and_time(user_name, user_url, created) unless user_name.nil? || user_name == '' l(:label_added_time_by, :author => link_to(user_name, user_url), :age => time_tag(created)).html_safe else l(:label_added_time, :age => time_tag(created)).html_safe end end #huang def betweentime(enddate) ss=(DateTime.parse("#{enddate.to_date}")-DateTime.parse("#{DateTime.now.to_date}")).to_i return ss end def time_tag(time, *args) options = args.extract_options! text = distance_of_time_in_words(Time.now, time) if @project link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => User.current.time_to_date(time)},options.reverse_merge(:title => format_time(time))) else content_tag('acronym', text, options.reverse_merge(:title => format_time(time))) end end def syntax_highlight_lines(name, content) lines = [] syntax_highlight(name, content).each_line { |line| lines << line } lines end def syntax_highlight(name, content) Redmine::SyntaxHighlighting.highlight_by_filename(content, name) end def to_path_param(path) str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/") str.blank? ? nil : str end def reorder_links(name, url, method = :post) link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => method, :title => l(:label_sort_highest)) + link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => method, :title => l(:label_sort_higher)) + link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => method, :title => l(:label_sort_lower)) + link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => method, :title => l(:label_sort_lowest)) end def breadcrumb(*args) elements = args.flatten elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'wiki_con_tit"') : nil end def other_formats_links(&block) concat('

    '.html_safe + l(:label_export_to)) yield Redmine::Views::OtherFormatsBuilder.new(self) concat('

    '.html_safe) end def page_header_title if @project.nil? || @project.new_record? h(Setting.app_title) else b = [] ancestors = (@project.root? ? [] : @project.ancestors.visible.all) if ancestors.any? root = ancestors.shift b << link_to_project(root, {:jump => current_menu_item}, :class => 'root') if ancestors.size > 2 b << "\xe2\x80\xa6" ancestors = ancestors[-2, 2] end b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') } end b << h(@project) b.join(" \xc2\xbb ").html_safe end end def html_title(*args) #點擊項目版本庫 多觸發一次 字符串為"/" #暫時解決方法 直接判斷 if(args == ["/"]) args = [] end first_page = FirstPage.find_by_page_type('project') if args.empty? title = @html_title || [] title << @project.name if @project if first_page.nil? || first_page.web_title.nil? title << Setting.app_title unless Setting.app_title == title.last else title << first_page.web_title unless first_page.web_title == title.last end title.select {|t| !t.blank? }.join(' - ') else @html_title ||= [] @html_title += args end end # Returns the theme, controller name, and action as css classes for the # HTML body. def body_css_classes css = [] if theme = Redmine::Themes.theme(Setting.ui_theme) css << 'theme-' + theme.name end css << 'controller-' + controller_name css << 'action-' + action_name css.join(' ') end def accesskey(s) @used_accesskeys ||= [] key = Redmine::AccessKeys.key_for(s) return nil if @used_accesskeys.include?(key) @used_accesskeys << key key end # Formats text according to system settings. # 2 ways to call this method: # * with a String: textilizable(text, options) # * with an object and one of its attribute: textilizable(issue, :description, options) def textilizable(*args) options = args.last.is_a?(Hash) ? args.pop : {} case args.size when 1 obj = options[:object] text = args.shift when 2 obj = args.shift attr = args.shift text = obj.send(attr).to_s else raise ArgumentError, 'invalid arguments to textilizable' end return '' if text.blank? project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil) only_path = options.delete(:only_path) == false ? false : true text = text.dup macros = catch_macros(text) text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) @parsed_headings = [] @heading_anchors = {} @current_section = 0 if options[:edit_section_links] parse_sections(text, project, obj, attr, only_path, options) text = parse_non_pre_blocks(text, obj, macros) do |text| [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name| send method_name, text, project, obj, attr, only_path, options end end parse_headings(text, project, obj, attr, only_path, options) if @parsed_headings.any? replace_toc(text, @parsed_headings) end text.html_safe end # #格式化字符串,不转义html代码 def textAreailizable(*args) options = args.last.is_a?(Hash) ? args.pop : {} case args.size when 1 obj = options[:object] text = args.shift when 2 obj = args.shift attr = args.shift text = obj.send(attr).to_s else raise ArgumentError, 'invalid arguments to textilizable' end return '' if text.blank? project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil) only_path = options.delete(:only_path) == false ? false : true text = text.dup macros = catch_macros(text) #text = Redmine::WikiFormatting.to_html("CKEditor", text, :object => obj, :attribute => attr) @parsed_headings = [] @heading_anchors = {} @current_section = 0 if options[:edit_section_links] parse_sections(text, project, obj, attr, only_path, options) text = parse_non_pre_blocks(text, obj, macros) do |text| [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name| send method_name, text, project, obj, attr, only_path, options end end parse_headings(text, project, obj, attr, only_path, options) if @parsed_headings.any? replace_toc(text, @parsed_headings) end text.html_safe end def parse_non_pre_blocks(text, obj, macros) s = StringScanner.new(text) tags = [] parsed = '' while !s.eos? s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im) text, full_tag, closing, tag = s[1], s[2], s[3], s[4] if tags.empty? yield text inject_macros(text, obj, macros) if macros.any? else inject_macros(text, obj, macros, false) if macros.any? end parsed << text if tag if closing if tags.last == tag.downcase tags.pop end else tags << tag.downcase end parsed << full_tag end end # Close any non closing tags while tag = tags.pop parsed << "" end parsed end def parse_inline_attachments(text, project, obj, attr, only_path, options) # when using an image link, try to use an attachment, if possible attachments = options[:attachments] || [] attachments += obj.attachments if obj.respond_to?(:attachments) if attachments.present? text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m| filename, ext, alt, alttext = $1.downcase, $2, $3, $4 # search for the picture in attachments if found = Attachment.latest_attach(attachments, filename) image_url = download_named_attachment_path(found, found.filename, :only_path => only_path) desc = found.description.to_s.gsub('"', '') if !desc.blank? && alttext.blank? alt = " title=\"#{desc}\" alt=\"#{desc}\"" end "src=\"#{image_url}\"#{alt}" else m end end end end # Wiki links # # Examples: # [[mypage]] # [[mypage|mytext]] # wiki links can refer other project wikis, using project name or identifier: # [[project:]] -> wiki starting page # [[project:|mytext]] # [[project:mypage]] # [[project:mypage|mytext]] def parse_wiki_links(text, project, obj, attr, only_path, options) text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m| link_project = project esc, all, page, title = $1, $2, $3, $5 if esc.nil? if page =~ /^([^\:]+)\:(.*)$/ identifier, page = $1, $2 link_project = Project.find_by_identifier(identifier) || Project.find_by_name(identifier) title ||= identifier if page.blank? end if link_project && link_project.wiki # extract anchor anchor = nil if page =~ /^(.+?)\#(.+)$/ page, anchor = $1, $2 end anchor = sanitize_anchor_name(anchor) if anchor.present? # check if page exists wiki_page = link_project.wiki.find_page(page) url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page "##{anchor}" else case options[:wiki_links] when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '') when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export else wiki_page_id = page.present? ? Wiki.titleize(page) : nil parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :version => nil, :anchor => anchor, :parent => parent) end end link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new'))) else # project or wiki doesn't exist all end else all end end end def select_option_helper option tmp = Hash.new tmp={"" => ""} if option.nil? else option.each do |project| tmp[project.name] = project.id end end tmp end # Redmine links # # Examples: # Issues: # #52 -> Link to issue #52 # Changesets: # r52 -> Link to revision 52 # commit:a85130f -> Link to scmid starting with a85130f # Documents: # document#17 -> Link to document with id 17 # document:Greetings -> Link to the document with title "Greetings" # document:"Some document" -> Link to the document with title "Some document" # Versions: # version#3 -> Link to version with id 3 # version:1.0.0 -> Link to version named "1.0.0" # version:"1.0 beta 2" -> Link to version named "1.0 beta 2" # Attachments: # attachment:file.zip -> Link to the attachment of the current object named file.zip # Source files: # source:some/file -> Link to the file located at /some/file in the project's repository # source:some/file@52 -> Link to the file's revision 52 # source:some/file#L120 -> Link to line 120 of the file # source:some/file@52#L120 -> Link to line 120 of the file's revision 52 # export:some/file -> Force the download of the file # Forum messages: # message#1218 -> Link to message with id 1218 # # Links can refer other objects from other projects, using project identifier: # identifier:r52 # identifier:document:"Some document" # identifier:version:1.0.0 # identifier:source:some/file def parse_redmine_links(text, default_project, obj, attr, only_path, options) text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-_]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m| leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $18, $14 || $19, $15, $17 link = nil project = default_project if project_identifier project = Project.visible.find_by_identifier(project_identifier) end if esc.nil? if prefix.nil? && sep == 'r' if project repository = nil if repo_identifier repository = project.repositories.detect {|repo| repo.identifier == repo_identifier} else repository = project.repository end # project.changesets.visible raises an SQL error because of a double join on repositories if repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(repository.id, identifier)) link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.revision}, :class => 'changeset', :title => truncate_single_line(changeset.comments, :length => 100)) end end elsif sep == '#' oid = identifier.to_i case prefix when nil if oid.to_s == identifier && issue = Issue.visible.find_by_id(oid, :include => :status) anchor = comment_id ? "note-#{comment_id}" : nil link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor}, :class => issue.css_classes, :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})") end when 'document' if document = Document.visible.find_by_id(oid) link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document}, :class => 'document' end when 'version' if version = Version.visible.find_by_id(oid) link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version}, :class => 'version' end when 'message' if message = Message.visible.find_by_id(oid, :include => :parent) link = link_to_message(message, {:only_path => only_path}, :class => 'message') end when 'forum' if board = Board.visible.find_by_id(oid) link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project}, :class => 'board' end when 'news' if news = News.visible.find_by_id(oid) link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news}, :class => 'news' end when 'project' if p = Project.visible.find_by_id(oid) link = link_to_project(p, {:only_path => only_path}, :class => 'project') end end elsif sep == ':' # removes the double quotes if any name = identifier.gsub(%r{^"(.*)"$}, "\\1") case prefix when 'document' if project && document = project.documents.visible.find_by_title(name) link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document}, :class => 'document' end when 'version' if project && version = project.versions.visible.find_by_name(name) link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version}, :class => 'version' end when 'forum' if project && board = project.boards.visible.find_by_name(name) link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project}, :class => 'board' end when 'news' if project && news = project.news.visible.find_by_title(name) link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news}, :class => 'news' end when 'commit', 'source', 'export' if project repository = nil if name =~ %r{^(([a-z0-9\-_]+)\|)(.+)$} repo_prefix, repo_identifier, name = $1, $2, $3 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier} else repository = project.repository end if prefix == 'commit' if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first) link = link_to h("#{project_prefix}#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.identifier}, :class => 'changeset', :title => truncate_single_line(changeset.comments, :length => 100) end else if repository && User.current.allowed_to?(:browse_repository, project) name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$} path, rev, anchor = $1, $3, $5 link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param, :path => to_path_param(path), :rev => rev, :anchor => anchor}, :class => (prefix == 'export' ? 'source download' : 'source') end end repo_prefix = nil end when 'attachment' attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil) if attachments && attachment = Attachment.latest_attach(attachments, name) link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment') end when 'project' if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first link = link_to_project(p, {:only_path => only_path}, :class => 'project') end end end end (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}")) end end HEADING_RE = /(]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE) def parse_sections(text, project, obj, attr, only_path, options) return unless options[:edit_section_links] text.gsub!(HEADING_RE) do heading = $1 @current_section += 1 if @current_section > 1 content_tag('div', link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)), :class => 'contextual', :title => l(:button_edit_section)) + heading.html_safe else heading end end end # Headings and TOC # Adds ids and links to headings unless options[:headings] is set to false def parse_headings(text, project, obj, attr, only_path, options) return if options[:headings] == false text.gsub!(HEADING_RE) do level, attrs, content = $2.to_i, $3, $4 item = strip_tags(content).strip anchor = sanitize_anchor_name(item) # used for single-file wiki export anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) @heading_anchors[anchor] ||= 0 idx = (@heading_anchors[anchor] += 1) if idx > 1 anchor = "#{anchor}-#{idx}" end @parsed_headings << [level, anchor, item] "\n#{content}" end end MACROS_RE = /( (!)? # escaping ( \{\{ # opening tag ([\w]+) # macro name (\(([^\n\r]*?)\))? # optional arguments ([\n\r].*?[\n\r])? # optional block of text \}\} # closing tag ) )/mx unless const_defined?(:MACROS_RE) MACRO_SUB_RE = /( \{\{ macro\((\d+)\) \}\} )/x unless const_defined?(:MACRO_SUB_RE) # Extracts macros from text def catch_macros(text) macros = {} text.gsub!(MACROS_RE) do all, macro = $1, $4.downcase if macro_exists?(macro) || all =~ MACRO_SUB_RE index = macros.size macros[index] = all "{{macro(#{index})}}" else all end end macros end # Executes and replaces macros in text def inject_macros(text, obj, macros, execute=true) text.gsub!(MACRO_SUB_RE) do all, index = $1, $2.to_i orig = macros.delete(index) if execute && orig && orig =~ MACROS_RE esc, all, macro, args, block = $2, $3, $4.downcase, $6.to_s, $7.try(:strip) if esc.nil? h(exec_macro(macro, obj, args, block) || all) else h(all) end elsif orig h(orig) else h(all) end end end TOC_RE = /

    \{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE) # Renders the TOC with given headings def replace_toc(text, headings) text.gsub!(TOC_RE) do # Keep only the 4 first levels headings = headings.select{|level, anchor, item| level <= 4} if headings.empty? '' else div_class = 'toc' div_class << ' right' if $1 == '>' div_class << ' left' if $1 == '<' out = "

    ' * (current - root) out << '' end end end # Same as Rails' simple_format helper without using paragraphs def simple_format_without_paragraph(text) text.to_s. gsub(/\r\n?/, "\n"). # \r\n and \r -> \n gsub(/\n\n+/, "

    "). # 2+ newline -> 2 br gsub(/([^\n]\n)(?=[^\n])/, '\1
    '). # 1 newline -> br html_safe end def wiki_simple_format_without_paragraph(text) text.to_s. gsub(/\r\n?/, "\n"). # \r\n and \r -> \n gsub(/\n\n+/, "

    "). # 2+ newline -> 2 br gsub(/([^\n]\n)(?=[^\n])/, '\1
    '). # 1 newline -> br gsub("&nbsp", " "). #gsub(/<\/?.*?>/,""). gsub(/<\/?.*?>/, ""). gsub(""", "'"). html_safe end def lang_options_for_select(blank=true) { 'Chinese简体中文 '=> 'zh', :English => :en} end def label_tag_for(name, option_tags = nil, options = {}) label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") content_tag("label", label_text) end def labelled_form_for(*args, &proc) args << {} unless args.last.is_a?(Hash) options = args.last if args.first.is_a?(Symbol) options.merge!(:as => args.shift) end options.merge!({:builder => Redmine::Views::LabelledFormBuilder}) form_for(*args, &proc) end def labelled_fields_for(*args, &proc) args << {} unless args.last.is_a?(Hash) options = args.last options.merge!({:builder => Redmine::Views::LabelledFormBuilder}) fields_for(*args, &proc) end def labelled_remote_form_for(*args, &proc) ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_remote_form_for is deprecated and will be removed in Redmine 2.2." args << {} unless args.last.is_a?(Hash) options = args.last options.merge!({:builder => Redmine::Views::LabelledFormBuilder, :remote => true}) form_for(*args, &proc) end def error_messages_for(*objects) html = "" # modified by fq if objects.first.is_a?(Array) objects = objects.first end # end if objects != nil objects = objects.map {|o| o.is_a?(String) ? instance_variable_get("@#{o}") : o}.compact errors = objects.map {|o| o.errors.full_messages}.flatten if errors.any? html << "
    \n" end end html.html_safe end def delete_link(url, options={}) options = { :method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => 'icon icon-del' }.merge(options) link_to l(:button_delete), url, options end def delete_link_version(url, options={}) options = { :method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => 'c_purple' }.merge(options) link_to l(:button_delete), url, options end def delete_new_link(url, options={}) options = { :method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => "c_purple" }.merge(options) link_to l(:button_delete), url, options end def preview_link(url, form, target='preview', options={}) content_tag 'a', l(:label_preview), { :href => "#", :onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|, :accesskey => accesskey(:preview) }.merge(options) end def link_to_function(name, function, html_options={}) content_tag(:a, name, {:href => '#', :onclick => "#{function}; return false;"}.merge(:class => " c_purple")) end # Helper to render JSON in views def raw_json(arg) arg.to_json.to_s.gsub('/', '\/').html_safe end def back_url url = params[:back_url] if url.nil? && referer = request.env['HTTP_REFERER'] url = CGI.unescape(referer.to_s) end url end def back_url_hidden_field_tag url = back_url hidden_field_tag('back_url', url, :id => nil) unless url.blank? end def check_all_links(form_name) link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") + "  ".html_safe + " | "+ "  ".html_safe + link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)") end def progress_bar(pcts, options={}) pcts = [pcts, pcts] unless pcts.is_a?(Array) pcts = pcts.collect(&:round) pcts[1] = pcts[1] - pcts[0] pcts << (100 - pcts[1] - pcts[0]) width = options[:width] || '100px;' legend = options[:legend] || '' content_tag('table', content_tag('tr', (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) + (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) + (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe) ), :class => 'progress', :style => "width: #{width};").html_safe + content_tag('p', legend, :class => 'percent').html_safe end def checked_image(checked=true) if checked image_tag 'toggle_check.png' end end def context_menu(url) unless @context_menu_included content_for :header_tags do javascript_include_tag('context_menu') + stylesheet_link_tag('context_menu') end if l(:direction) == 'rtl' content_for :header_tags do stylesheet_link_tag('context_menu_rtl') end end @context_menu_included = true end javascript_tag "contextMenuInit('#{ url_for(url) }')" end def calendar_for(field_id,start_day=nil) include_calendar_headers_tags(start_day) javascript_tag("$(function() { $('##{field_id}').datepicker(datepickerOptions); });") end def include_calendar_headers_tags(start_day=nil) if start_day.nil? unless @calendar_headers_tags_included @calendar_headers_tags_included = true content_for :header_tags do start_of_week = Setting.start_of_week start_of_week = l(:general_first_day_of_week, :default => '1') if start_of_week.blank? # Redmine uses 1..7 (monday..sunday) in settings and locales # JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0 start_of_week = start_of_week.to_i % 7 tags = javascript_tag( "var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " + "showOn: 'button', buttonImageOnly: true, buttonImage: '" + path_to_image('/images/public_icon.png') + "', showButtonPanel: true, showWeek: true, showOtherMonths: true, selectOtherMonths: true};") jquery_locale = l('jquery.locale', :default => current_language.to_s) unless jquery_locale == 'en' tags << javascript_include_tag("i18n/jquery.ui.datepicker-#{jquery_locale}.js") end tags end end else unless @calendar_headers_tags_included @calendar_headers_tags_included = true content_for :header_tags do start_of_week = Setting.start_of_week start_of_week = l(:general_first_day_of_week, :default => '1') if start_of_week.blank? # Redmine uses 1..7 (monday..sunday) in settings and locales # JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0 start_of_week = start_of_week.to_i % 7 tags = javascript_tag( "var datepickerOptions={dateFormat: 'yy-mm-dd',minDate: new Date(), firstDay: #{start_of_week}, " + "showOn: 'button', buttonImageOnly: true, buttonImage: '" + path_to_image('/images/public_icon.png') + "', showButtonPanel: true, showWeek: true, showOtherMonths: true, selectOtherMonths: true, onClose: function(dateText, inst) {TimeClose(dateText,inst);}, beforeShow : function(input){TimeBeforeShow(input);} };") jquery_locale = l('jquery.locale', :default => current_language.to_s) unless jquery_locale == 'en' tags << javascript_include_tag("i18n/jquery.ui.datepicker-#{jquery_locale}.js") end tags end end end end # Overrides Rails' stylesheet_link_tag with themes and plugins support. # Examples: # stylesheet_link_tag('styles') # => picks styles.css from the current theme or defaults # stylesheet_link_tag('styles', :plugin => 'foo) # => picks styles.css from plugin's assets # def stylesheet_link_tag(*sources) options = sources.last.is_a?(Hash) ? sources.pop : {} plugin = options.delete(:plugin) sources = sources.map do |source| if plugin "/plugin_assets/#{plugin}/stylesheets/#{source}" elsif current_theme && current_theme.stylesheets.include?(source) current_theme.stylesheet_path(source) else source end end super sources, options end # Overrides Rails' image_tag with themes and plugins support. # Examples: # image_tag('image.png') # => picks image.png from the current theme or defaults # image_tag('image.png', :plugin => 'foo) # => picks image.png from plugin's assets # def image_tag(source, options={}) if plugin = options.delete(:plugin) source = "/plugin_assets/#{plugin}/images/#{source}" elsif current_theme && current_theme.images.include?(source) source = current_theme.image_path(source) end super source, options end # Overrides Rails' javascript_include_tag with plugins support # Examples: # javascript_include_tag('scripts') # => picks scripts.js from defaults # javascript_include_tag('scripts', :plugin => 'foo) # => picks scripts.js from plugin's assets # def javascript_include_tag(*sources) options = sources.last.is_a?(Hash) ? sources.pop : {} if plugin = options.delete(:plugin) sources = sources.map do |source| if plugin "/plugin_assets/#{plugin}/javascripts/#{source}" else source end end end super sources, options end def content_for(name, content = nil, &block) @has_content ||= {} @has_content[name] = true super(name, content, &block) end def has_content?(name) (@has_content && @has_content[name]) || false end def sidebar_content? has_content?(:sidebar) || view_layouts_base_sidebar_hook_response.present? end def view_layouts_base_sidebar_hook_response @view_layouts_base_sidebar_hook_response ||= call_hook(:view_layouts_base_sidebar) end def email_delivery_enabled? !!ActionMailer::Base.perform_deliveries end # Returns the avatar image tag for the given +user+ if avatars are enabled # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe ') def avatar(user, options = { }) if Setting.gravatar_enabled? options.merge!({:ssl => (request && request.ssl?), :default => Setting.gravatar_default}) email = nil if user.respond_to?(:mail) email = user.mail elsif user.to_s =~ %r{<(.+?)>} email = $1 end return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil #options ={"class" => ["avatar2"],"width" =>["80px"],"height" =>["80px"]} #return image_tag url_to_avatar(user), options else '' end end def sanitize_anchor_name(anchor) if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java' anchor.gsub(%r{[^\s\-\p{Word}]}, '').gsub(%r{\s+(\-+\s*)?}, '-') else # TODO: remove when ruby1.8 is no longer supported anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-') end end # Returns the javascript tags that are included in the html layout head def javascript_heads tags = javascript_include_tag('jquery-1.8.3-ui-1.9.2-ujs-2.0.3', 'application', 'jquery.colorbox-min') unless User.current.pref.warn_on_leaving_unsaved == '0' tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });") end tags end def hubspot_head tags = javascript_include_tag('hubspot/messenger.min', 'hubspot/messenger-theme-future') tags << stylesheet_link_tag('hubspot/messenger', 'hubspot/messenger-theme-future', 'hubspot/messenger-theme-flat') end def bootstrap_head tags = stylesheet_link_tag('bootstrap/bootstrap.min', 'bootstrap/bootstrap-theme.min') tags << javascript_include_tag('bootstrap/affix') tags << javascript_include_tag('bootstrap/alert') tags << javascript_include_tag('bootstrap/button') tags << javascript_include_tag('bootstrap/carousel') tags << javascript_include_tag('bootstrap/collapse') tags << javascript_include_tag('bootstrap/dropdown') tags << javascript_include_tag('bootstrap/modal') tags << javascript_include_tag('bootstrap/popover') tags << javascript_include_tag('bootstrap/scrollspy') tags << javascript_include_tag('bootstrap/tab') tags << javascript_include_tag('bootstrap/tooltip') tags << javascript_include_tag('bootstrap/transition') tags end def favicon "".html_safe end def robot_exclusion_tag ''.html_safe end # Returns true if arg is expected in the API response def include_in_api_response?(arg) unless @included_in_api_response param = params[:include] @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',') @included_in_api_response.collect!(&:strip) end @included_in_api_response.include?(arg.to_s) end # Returns options or nil if nometa param or X-Redmine-Nometa header # was set in the request def api_meta(options) if params[:nometa].present? || request.headers['X-Redmine-Nometa'] # compatibility mode for activeresource clients that raise # an error when unserializing an array with attributes nil else options end end # Add by Tao def url_to_avatar(source) source = nil if source.kind_of?(String) get_avatar(source) end # Endof Tao's code def date_format_local(time) date = time.strftime("%Y年%m月%d日") end #当TAG数量过多时,更多链接 #1代表是user类型 2代表是project类型 3代表是issue类型 4代表需求 9代表课程 def more_tags id,object_flag a= 1 case object_flag when "1" s = link_to l(:label_more_tags),:controller => "users", :action => "show", :id => id when "2" s = link_to l(:label_more_tags),:controller => "projects", :action => "show", :id => id when "3" s = link_to l(:label_more_tags),:controller => "issues", :action => "show", :id => id when "4" s = link_to l(:label_more_tags),:controller => "bids", :action => "show", :id => id when "9" s = link_to l(:label_more_tags),:controller => "courses", :action => "show", :id => id end s end def get_memo @new_memo = Memo.new @public_forum = Forum.find(1) rescue ActiveRecord::RecordNotFound end #获取用户未过期的课程 def get_user_course user courses_doing = [] user.courses.each do |course| if !course_endTime_timeout?(course) courses_doing.push course end end courses_doing end def attachment_candown attachment candown = false if attachment.container if attachment.container.class.to_s != "HomeworkAttach" && (attachment.container.has_attribute?(:project) || attachment.container.has_attribute?(:project_id)) && attachment.container.project project = attachment.container.project candown= User.current.member_of?(project) || (project.is_public && attachment.is_public == 1) elsif attachment.container.is_a?(Project) project = attachment.container candown= User.current.member_of?(project) || (project.is_public && attachment.is_public == 1) elsif (attachment.container.has_attribute?(:board) || attachment.container.has_attribute?(:board_id)) && attachment.container.board && attachment.container.board.project project = attachment.container.board.project candown = User.current.member_of?(project) || (project.is_public && attachment.is_public == 1) elsif (attachment.container.has_attribute?(:course) ||attachment.container.has_attribute?(:course_id) ) && attachment.container.course course = attachment.container.course candown = User.current.member_of_course?(course) || (course.is_public==1 && attachment.is_public == 1) elsif attachment.container.is_a?(Course) course = attachment.container candown= User.current.member_of_course?(course) || (course.is_public==1 && attachment.is_public == 1) elsif (attachment.container.has_attribute?(:board) || attachment.container.has_attribute?(:board_id)) && attachment.container.board && attachment.container.board.course course = attachment.container.board.course candown= User.current.member_of_course?(course) || (course.is_public==1 && attachment.is_public == 1) elsif attachment.container.class.to_s=="HomeworkAttach" && attachment.container.bid.reward_type == 3 candown = true elsif attachment.container_type == "Bid" && attachment.container && attachment.container.courses course = attachment.container.courses.first candown = User.current.member_of_course?(attachment.container.courses.first) || (course.is_public == 1 && attachment.is_public == 1) else candown = (attachment.is_public == 1 || attachment.is_public == true) end end candown end def project_type_link(text, value) if value == 1 link_to "#{text}".html_safe,"javascript:void(0)" ,:onClick => "show_window();", :class => "pr_join_a",:id => "setting_project_type" elsif value == 2 link_to "#{text}".html_safe,"javascript:void(0)" ,:onClick => "show_window();", :class => "pr_join_a",:id => "setting_project_type" else link_to "#{text}".html_safe,"javascript:void(0)" ,:onClick => "show_window();", :class => "pr_join_a",:id => "setting_project_type" end end private def wiki_helper helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting) extend helper return self end def link_to_content_update(text, url_params = {}, html_options = {}) link_to(text, url_params, html_options) end #added by nie # Display watcher picture def show_more_watchers?(obj) if User.watched_by(obj.id).count > 6 return true else return false end end def show_watcher_profile(obj) count = 0 html = '' if User.watched_by(obj.id).count == 0 html << (content_tag "span", l(:label_no_current_watchers)) end for user in User.watched_by(obj.id) html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => "#{user.name}") count = count + 1 if count >= 12 break end end html.html_safe end #display bid project def show_more_bid_project?(bid) if bid.projects.where('is_public = 1').count > 12 return true else return false end end def show_bid_project(bid) html = '' if bid.projects.where('is_public = 1').count == 0 html << (content_tag "p", l(:label_no_bid_project), :class => "font_lighter") else bid.projects.where('is_public = 1').take(12).each do |project| html << (link_to image_tag(url_to_avatar(project), :class => "avatar", :title => project.name), project_path(project), :class => "avatar") end end html.html_safe end def show_bid_fans_picture(obj) html = '' if obj.watcher_users.count == 0 html << (content_tag "span", l(:label_project_no_follow)) else obj.watcher_users.take(12).each do |user| html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => user.name) end end html.html_safe end #display contest project def show_more_contest_project?(contest) if contest.projects.where('is_public = 1').count > 12 return true else return false end end def show_more_contest_softapplication?(contest) if contest.softapplications.where('is_public = 1').count > 12 return true else return false end end def show_contest_project(bid) html = '' if contest.projects.where('is_public = 1').count == 0 html << (content_tag "p", l(:label_no_bid_project), :class => "font_lighter") else contest.projects.where('is_public = 1').take(12).each do |project| html << (link_to image_tag(url_to_avatar(project), :class => "avatar", :title => project.name), project_path(project), :class => "avatar") end end html.html_safe end def show_contest_project(contest) html = '' if contest.projects.where('is_public = 1').count == 0 html << (content_tag "p", l(:label_no_bid_project), :class => "font_lighter") else contest.projects.where('is_public = 1').take(12).each do |project| html << (link_to image_tag(url_to_avatar(project), :class => "avatar", :title => project.name), project_path(project), :class => "avatar") end end html.html_safe end def show_contest_softapplication(contest) html = '' if contest.softapplications.where('is_public = 1').count == 0 html << (content_tag "p", l(:label_no_contest_softapplication), :class => "font_lighter") else contest.softapplications.where('is_public = 1').take(12).each do |softapplication| html << (link_to image_tag(url_to_avatar(project), :class => "avatar", :title => project.name), project_path(project), :class => "avatar") end end html.html_safe end def show_contest_fans_picture(obj) html = '' if obj.watcher_users.count == 0 html << (content_tag "span", l(:label_project_no_follow)) else obj.watcher_users.take(12).each do |user| html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => user.name) end end html.html_safe end #display fans picture def show_more_fans?(obj) if obj.watcher_users.count > 12 return true else return false end end def show_fans_picture(obj) html = '' if obj.watcher_users.count == 0 html << (content_tag "span", l(:label_no_current_fans)) else obj.watcher_users.take(12).each do |user| html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => user.name) end end html.html_safe end # added by bai def show_more_participate?(obj) if obj.join_in_contests.count > 12 return true else return false end end def show_participate_picture(obj) html = '' count = 0 if obj.join_in_contests.count == 0 html << (content_tag "span", l(:label_no_current_participate)) end for temp in obj.join_in_contests html << (link_to image_tag(url_to_avatar(temp.user), :class => "avatar"), user_path(temp.user), :class => "avatar", :title => "#{temp.user.name}") count = count + 1 if count >= 12 break end end html.html_safe end #end # add by huang def show_watcher_list(user) html = '' count = 0 for user in User.watched_by(user.id) html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => "#{user.name}") count = count + 1 if count >= 12 break end end html.html_safe end # end #added by william def get_fans_num(user) user.watcher_users.count end #end def hadcommittedhomework(cur,curb) bid = Bid.find_by_id(curb) return true if bid.nil? case bid.homework_type when Bid::HomeworkFile attaches = HomeworkAttach.where(bid_id: curb) attaches.map(&:user_id).include? cur when Bid::HomeworkProject attaches = BidingProject.where(user_id: User.current, bid_id: bid) attaches.count > 0 # > 0 则有提交记录 else true end end def render_dynamic_nav home_link = link_to l(:field_homepage), {:controller => 'welcome', :action => 'index'} home_link = "
  • " << home_link << "
  • " # bootstrap_render_dynamic_nav content_tag :ul, (home_link.html_safe+bootstrap_render_dynamic_nav) end def bootstrap_render_dynamic_nav hidden_non_project = Setting.find_by_name("hidden_non_project") visiable = !(hidden_non_project && hidden_non_project.value == "0") main_course_link = link_to l(:label_course_practice), {:controller => 'welcome', :action => 'index', :host => Setting.host_course} main_project_link = link_to l(:label_project_deposit), {:controller => 'welcome', :action => 'index', :host => Setting.host_name} main_contest_link = link_to l(:label_contest_innovate), {:controller => 'welcome', :action => 'index', :host => Setting.host_contest} # course_all_course_link = link_to l(:label_course_all), {:controller => 'courses', :action => 'index'} course_teacher_all_link = link_to l(:label_teacher_all), {:controller => 'users', :action => 'index', :role => 'teacher', :host => Setting.host_course} # courses_link = link_to l(:label_course_practice), {:controller => 'courses', :action => 'index'} #users_link = link_to l(:label_software_user), {:controller => 'users', :action => 'index', :host => Setting.host_user} # contest_link = link_to l(:label_contest_innovate), {:controller => 'contests', :action => 'index'} bids_link = link_to l(:label_requirement_enterprise), {:controller => 'bids', :action => 'index'} forum_link = link_to l(:label_forum_all), {:controller => "forums", :action => "index"} stores_link = link_to l(:label_stores_index), {:controller => 'stores', :action=> 'index'} school_all_school_link = link_to l(:label_school_all), {:controller => 'school', :action => 'index'} project_new_link = link_to l(:label_project_new), {:controller => 'projects', :action => 'new', :host => Setting.host_name} # project_mine_link = link_to l(:label_my_project), {:controller => 'users', :action => 'user_projects', :host => Setting.host_name} #@nav_dispaly_project_label nav_list = Array.new nav_list.push(school_all_school_link) if @nav_dispaly_course_all_label && @show_course == 1 && visiable # nav_list.push(course_all_course_link) if @nav_dispaly_course_all_label && @show_course == 1 nav_list.push(course_teacher_all_link) if @nav_dispaly_teacher_all_label && @show_course == 1 && visiable nav_list.push(main_project_link) if @nav_dispaly_main_project_label nav_list.push(main_course_link) if @nav_dispaly_main_course_label && @show_course == 1 && visiable nav_list.push(main_contest_link) if @nav_dispaly_main_contest_label && @show_contest == 1 && visiable nav_list.push(courses_link) if @nav_dispaly_course_label && @show_course == 1 && visiable nav_list.push(project_new_link) if @nav_dispaly_project_label # nav_list.push(project_mine_link) if @nav_dispaly_main_project_label # nav_list.push(projects_link) if @nav_dispaly_project_label #nav_list.push(users_link) if @nav_dispaly_user_label # nav_list.push(contest_link) if @nav_dispaly_contest_label && @show_contest == 1 nav_list.push(bids_link) if @nav_dispaly_bid_label && visiable nav_list.push(forum_link) if @nav_dispaly_forum_label && visiable nav_list.push(stores_link) if @nav_dispaly_store_all_label && visiable content_li = '' nav_list.collect do |nav_item| content_li << content_tag(:li, nav_item, :class => 'topnav_a fl') end content_li.html_safe end def current_user User.current end # def hadcommittedforcontest(curu) # message = JournalsForMessage.find_by_sql("select * from journals_for_messages where jour_type = 'Softapplication' ") # message.each do |createmessage| # if createmessage.user_id == curu # return true # end # end # end def footer_logo(ul_class=nil, li_class=nil) logos = [] logos.push(link_to image_tag('/images/footer_logo/nudt.png',:alt=>"nudt"),"http://www.nudt.edu.cn/special.asp?classid=12" ) logos.push(link_to image_tag('/images/footer_logo/peking_eecs.png', :alt=>"peking_eecs"), "http://eecs.pku.edu.cn" ) logos.push(link_to image_tag('/images/footer_logo/buaa_scse.png', :alt=>"buaa_scse"), "http://scse.buaa.edu.cn/" ) logos.push(link_to image_tag('/images/footer_logo/iscas.png', :alt=>"iscas"), "http://www.iscas.ac.cn" ) logos.push(link_to image_tag('/images/footer_logo/inforbus.png', :alt=>"inforbus"), "http://www.inforbus.com" ) logos.collect! { |logo| content_tag(:li, logo.html_safe, :class => li_class.to_s) } content_tag(:ul, logos.join("").html_safe, :class => ul_class.to_s).html_safe end def sort_homework_path(bid, sort, direction) case self.action_name when 'show_courseEx' get_not_batch_homework_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: 'asc') when 'get_not_batch_homework' get_not_batch_homework_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: direction) when 'get_batch_homeworks' get_batch_homeworks_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: direction) when 'get_homeworks' get_homeworks_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: direction) else '#' end end def anonymous_comment_link(bid, course) link = case bid.comment_status when 0 confirm_info = "开启匿评后学生将不能对作品进行提交、修改、删除等操作\n" confirm_info += anonymous_comment_notice(bid,course) confirm_info += '是否确定开启匿评?' link_to '启动匿评', start_anonymous_comment_bid_path(bid), id: "#{bid.id}_start_anonymous_comment", remote: true, :confirm => confirm_info, disable_with: '加载中...' when 1 confirm_info = "关闭匿评后所有同学将不能继续进行匿评,且将公开已提交作品列表\n" confirm_info += anonymous_comment_notice(bid,course) confirm_info += '是否确定关闭匿评?' link_to '关闭匿评', stop_anonymous_comment_bid_path(bid), id: "#{bid.id}_stop_anonymous_comment", remote: true, :confirm => confirm_info when 2 '匿评结束' end content_tag('span', link, id: "#{bid.id}_anonymous_comment") end def anonymous_comment_notice(bid, course) case bid.comment_status when 0 @student_size ||= searchStudent(course).size @homework_size = bid.homeworks.size percent = @homework_size.to_f / (@student_size == 0 ? 1 : @student_size) confirm_info = "目前#{@student_size}个学生,总共提交了#{@homework_size}份作品,占#{number_to_percentage(percent * 100, precision: 1)}\n" when 1 @homework_evaluations = 0 bid.homeworks.map { |homework| @homework_evaluations += homework.homework_evaluations.count} teachers = "(" teacher_members = searchTeacherAndAssistant(course) teacher_members.each do |member| if member == teacher_members.last teachers += member.user_id.to_s + ")" else teachers += member.user_id.to_s + "," end end @has_evaluations = 0 bid.homeworks.map { |homework| @has_evaluations += homework.rates(:quality).where("seems_rateable_rates.rater_id not in #{teachers}").count} percent = @has_evaluations.to_f / (@homework_evaluations == 0 ? 1 : @homework_evaluations) confirm_info = "目前总共分配了#{@homework_evaluations}份匿评作品,已评价#{@has_evaluations}份作品,占#{number_to_percentage(percent * 100, precision: 1)}\n" end confirm_info end def get_technical_title user if user.user_extensions.technical_title == "Professor" || user.user_extensions.technical_title == "教授" technical_title = l(:label_technicl_title_professor) elsif user.user_extensions.technical_title == "Associate professor" || user.user_extensions.technical_title == "副教授" technical_title = l(:label_technicl_title_associate_professor) elsif user.user_extensions.technical_title == "Lecturer" || user.user_extensions.technical_title == "讲师" technical_title = l(:label_technicl_title_lecturer) elsif user.user_extensions.technical_title == "Teaching assistant" || user.user_extensions.technical_title == "助教" technical_title = l(:label_technicl_title_teaching_assistant) end technical_title end def ie8? request.env["HTTP_USER_AGENT"] =~ /MSIE 8.0/ end #获取指定资源列表的TAG的集合以及每个TAG的数量,降序排序 def attachment_tag_list attachments tag_list = Hash.new attachments.each do |attachment| attachment.tag_list.map{|tag| tag_list.has_key?(tag) ? tag_list[tag] = tag_list[tag] + 1 : tag_list[tag] = 1} end tag_list.sort {|a,b| b[1]<=>a[1]} end #获取课程资源的TAG云 def get_course_tag_list course all_attachments = course.attachments.select{|attachment| attachment.is_public? || (attachment.container_type == "Course" && User.current.member_of_course?(course))|| attachment.author_id == User.current.id } tag_list = attachment_tag_list all_attachments tag_list end end