##// END OF EJS Templates
Wiki links can now refer other project wikis, using this syntax:...
Jean-Philippe Lang -
r637:a5849ee04483
parent child
Show More
@@ -1,290 +1,300
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class RedCloth
19 19 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
20 20 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
21 21 def hard_break( text )
22 22 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
23 23 end
24 24 end
25 25
26 26 module ApplicationHelper
27 27
28 28 # Return current logged in user or nil
29 29 def loggedin?
30 30 @logged_in_user
31 31 end
32 32
33 33 # Return true if user is logged in and is admin, otherwise false
34 34 def admin_loggedin?
35 35 @logged_in_user and @logged_in_user.admin?
36 36 end
37 37
38 38 # Return true if user is authorized for controller/action, otherwise false
39 39 def authorize_for(controller, action)
40 40 # check if action is allowed on public projects
41 41 if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ controller, action ]
42 42 return true
43 43 end
44 44 # check if user is authorized
45 45 if @logged_in_user and (@logged_in_user.admin? or Permission.allowed_to_role( "%s/%s" % [ controller, action ], @logged_in_user.role_for_project(@project) ) )
46 46 return true
47 47 end
48 48 return false
49 49 end
50 50
51 51 # Display a link if user is authorized
52 52 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
53 53 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller], options[:action])
54 54 end
55 55
56 56 # Display a link to user's account page
57 57 def link_to_user(user)
58 58 link_to user.name, :controller => 'account', :action => 'show', :id => user
59 59 end
60 60
61 61 def link_to_issue(issue)
62 62 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
63 63 end
64 64
65 65 def toggle_link(name, id, options={})
66 66 onclick = "Element.toggle('#{id}'); "
67 67 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
68 68 onclick << "return false;"
69 69 link_to(name, "#", :onclick => onclick)
70 70 end
71 71
72 72 def image_to_function(name, function, html_options = {})
73 73 html_options.symbolize_keys!
74 74 tag(:input, html_options.merge({
75 75 :type => "image", :src => image_path(name),
76 76 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
77 77 }))
78 78 end
79 79
80 80 def format_date(date)
81 81 return nil unless date
82 82 @date_format_setting ||= Setting.date_format.to_i
83 83 @date_format_setting == 0 ? l_date(date) : date.strftime("%Y-%m-%d")
84 84 end
85 85
86 86 def format_time(time)
87 87 return nil unless time
88 88 @date_format_setting ||= Setting.date_format.to_i
89 89 time = time.to_time if time.is_a?(String)
90 90 @date_format_setting == 0 ? l_datetime(time) : (time.strftime("%Y-%m-%d") + ' ' + l_time(time))
91 91 end
92 92
93 93 def day_name(day)
94 94 l(:general_day_names).split(',')[day-1]
95 95 end
96 96
97 97 def month_name(month)
98 98 l(:actionview_datehelper_select_month_names).split(',')[month-1]
99 99 end
100 100
101 101 def pagination_links_full(paginator, options={}, html_options={})
102 102 page_param = options.delete(:page_param) || :page
103 103
104 104 html = ''
105 105 html << link_to_remote(('&#171; ' + l(:label_previous)),
106 106 {:update => "content", :url => options.merge(page_param => paginator.current.previous)},
107 107 {:href => url_for(:params => options.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
108 108
109 109 html << (pagination_links_each(paginator, options) do |n|
110 110 link_to_remote(n.to_s,
111 111 {:url => {:params => options.merge(page_param => n)}, :update => 'content'},
112 112 {:href => url_for(:params => options.merge(page_param => n))})
113 113 end || '')
114 114
115 115 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
116 116 {:update => "content", :url => options.merge(page_param => paginator.current.next)},
117 117 {:href => url_for(:params => options.merge(page_param => paginator.current.next))}) if paginator.current.next
118 118 html
119 119 end
120 120
121 121 # textilize text according to system settings and RedCloth availability
122 122 def textilizable(text, options = {})
123 123 return "" if text.blank?
124 124
125 125 # different methods for formatting wiki links
126 126 case options[:wiki_links]
127 127 when :local
128 128 # used for local links to html files
129 format_wiki_link = Proc.new {|title| "#{title}.html" }
129 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
130 130 when :anchor
131 131 # used for single-file wiki export
132 format_wiki_link = Proc.new {|title| "##{title}" }
132 format_wiki_link = Proc.new {|project, title| "##{title}" }
133 133 else
134 if @project
135 format_wiki_link = Proc.new {|title| url_for :controller => 'wiki', :action => 'index', :id => @project, :page => title }
136 else
137 format_wiki_link = Proc.new {|title| title }
138 end
134 format_wiki_link = Proc.new {|project, title| url_for :controller => 'wiki', :action => 'index', :id => project, :page => title }
139 135 end
140 136
141 # turn wiki links into textile links:
137 # turn wiki links into html links
142 138 # example:
143 # [[link]] -> "link":link
144 # [[link|title]] -> "title":link
145 text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) {|m| link_to(($3 || $1), format_wiki_link.call(Wiki.titleize($1)), :class => 'wiki-page') }
139 # [[mypage]]
140 # [[mypage|mytext]]
141 # wiki links can refer other project wikis, using project name or identifier:
142 # [[project:]] -> wiki starting page
143 # [[project:mypage]]
144 # [[project:mypage|mytext]]
145 text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) do |m|
146 project = @project
147 page = $1
148 title = $3
149 if page =~ /^([^\:]+)\:(.*)$/
150 project = Project.find_by_name($1) || Project.find_by_identifier($1)
151 page = $2
152 title = $1 if page.blank?
153 end
154 link_to((title || page), format_wiki_link.call(project, Wiki.titleize(page)), :class => 'wiki-page')
155 end
146 156
147 157 # turn issue ids into links
148 158 # example:
149 159 # #52 -> <a href="/issues/show/52">#52</a>
150 160 text = text.gsub(/#(\d+)(?=\b)/) {|m| link_to "##{$1}", {:controller => 'issues', :action => 'show', :id => $1}, :class => 'issue' }
151 161
152 162 # turn revision ids into links (@project needed)
153 163 # example:
154 164 # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (@project.id is 6)
155 165 text = text.gsub(/(?=\b)r(\d+)(?=\b)/) {|m| link_to "r#{$1}", {:controller => 'repositories', :action => 'revision', :id => @project.id, :rev => $1}, :class => 'changeset' } if @project
156 166
157 167 # when using an image link, try to use an attachment, if possible
158 168 attachments = options[:attachments]
159 169 if attachments
160 170 text = text.gsub(/!([<>=]*)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
161 171 align = $1
162 172 filename = $2
163 173 rf = Regexp.new(filename, Regexp::IGNORECASE)
164 174 # search for the picture in attachments
165 175 if found = attachments.detect { |att| att.filename =~ rf }
166 176 image_url = url_for :controller => 'attachments', :action => 'download', :id => found.id
167 177 "!#{align}#{image_url}!"
168 178 else
169 179 "!#{align}#{filename}!"
170 180 end
171 181 end
172 182 end
173 183
174 184 # finally textilize text
175 185 @do_textilize ||= (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize")
176 186 text = @do_textilize ? auto_link(RedCloth.new(text, [:hard_breaks]).to_html) : simple_format(auto_link(h(text)))
177 187 end
178 188
179 189 # Same as Rails' simple_format helper without using paragraphs
180 190 def simple_format_without_paragraph(text)
181 191 text.to_s.
182 192 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
183 193 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
184 194 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
185 195 end
186 196
187 197 def error_messages_for(object_name, options = {})
188 198 options = options.symbolize_keys
189 199 object = instance_variable_get("@#{object_name}")
190 200 if object && !object.errors.empty?
191 201 # build full_messages here with controller current language
192 202 full_messages = []
193 203 object.errors.each do |attr, msg|
194 204 next if msg.nil?
195 205 msg = msg.first if msg.is_a? Array
196 206 if attr == "base"
197 207 full_messages << l(msg)
198 208 else
199 209 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
200 210 end
201 211 end
202 212 # retrieve custom values error messages
203 213 if object.errors[:custom_values]
204 214 object.custom_values.each do |v|
205 215 v.errors.each do |attr, msg|
206 216 next if msg.nil?
207 217 msg = msg.first if msg.is_a? Array
208 218 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
209 219 end
210 220 end
211 221 end
212 222 content_tag("div",
213 223 content_tag(
214 224 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
215 225 ) +
216 226 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
217 227 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
218 228 )
219 229 else
220 230 ""
221 231 end
222 232 end
223 233
224 234 def lang_options_for_select(blank=true)
225 235 (blank ? [["(auto)", ""]] : []) +
226 236 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.first <=> y.first }
227 237 end
228 238
229 239 def label_tag_for(name, option_tags = nil, options = {})
230 240 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
231 241 content_tag("label", label_text)
232 242 end
233 243
234 244 def labelled_tabular_form_for(name, object, options, &proc)
235 245 options[:html] ||= {}
236 246 options[:html].store :class, "tabular"
237 247 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
238 248 end
239 249
240 250 def check_all_links(form_name)
241 251 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
242 252 " | " +
243 253 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
244 254 end
245 255
246 256 def calendar_for(field_id)
247 257 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
248 258 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
249 259 end
250 260
251 261 def wikitoolbar_for(field_id)
252 262 return '' unless Setting.text_formatting == 'textile'
253 263 javascript_include_tag('jstoolbar') + javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
254 264 end
255 265 end
256 266
257 267 class TabularFormBuilder < ActionView::Helpers::FormBuilder
258 268 include GLoc
259 269
260 270 def initialize(object_name, object, template, options, proc)
261 271 set_language_if_valid options.delete(:lang)
262 272 @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
263 273 end
264 274
265 275 (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
266 276 src = <<-END_SRC
267 277 def #{selector}(field, options = {})
268 278 return super if options.delete :no_label
269 279 label_text = l(options[:label]) if options[:label]
270 280 label_text ||= l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym)
271 281 label_text << @template.content_tag("span", " *", :class => "required") if options.delete(:required)
272 282 label = @template.content_tag("label", label_text,
273 283 :class => (@object && @object.errors[field] ? "error" : nil),
274 284 :for => (@object_name.to_s + "_" + field.to_s))
275 285 label + super
276 286 end
277 287 END_SRC
278 288 class_eval src, __FILE__, __LINE__
279 289 end
280 290
281 291 def select(field, choices, options = {}, html_options = {})
282 292 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
283 293 label = @template.content_tag("label", label_text,
284 294 :class => (@object && @object.errors[field] ? "error" : nil),
285 295 :for => (@object_name.to_s + "_" + field.to_s))
286 296 label + super
287 297 end
288 298
289 299 end
290 300
@@ -1,46 +1,46
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Wiki < ActiveRecord::Base
19 19 belongs_to :project
20 20 has_many :pages, :class_name => 'WikiPage', :dependent => :destroy
21 21
22 22 validates_presence_of :start_page
23 validates_format_of :start_page, :with => /^[^,\.\/\?\;\|]*$/
23 validates_format_of :start_page, :with => /^[^,\.\/\?\;\|\:]*$/
24 24
25 25 # find the page with the given title
26 26 # if page doesn't exist, return a new page
27 27 def find_or_new_page(title)
28 28 title = Wiki.titleize(title || start_page)
29 29 find_page(title) || WikiPage.new(:wiki => self, :title => title)
30 30 end
31 31
32 32 # find the page with the given title
33 33 def find_page(title)
34 34 title = start_page if title.blank?
35 35 pages.find_by_title(Wiki.titleize(title))
36 36 end
37 37
38 38 # turn a string into a valid page title
39 39 def self.titleize(title)
40 40 # replace spaces with _ and remove unwanted caracters
41 title = title.gsub(/\s+/, '_').delete(',./?;|') if title
41 title = title.gsub(/\s+/, '_').delete(',./?;|:') if title
42 42 # upcase the first letter
43 title = title[0..0].upcase + title[1..-1] if title
43 title = (title.length > 1 ? title.first.upcase + title[1..-1] : title.upcase) if title
44 44 title
45 45 end
46 46 end
General Comments 0
You need to be logged in to leave comments. Login now