refactor feature xml access, extend tests
[opensuse:openfate.git] / app / controllers / feature_controller.rb
1 class FeatureController < ApplicationController
2
3   include Leftbee::HTMLHelpers
4
5   before_filter :require_auth, :except => [:index, :vote_statistics, :votes_graph]
6   before_filter :set_products
7   before_filter :validate_params, :except => [:auto_complete_for_feature_tag]
8   before_filter :add_boxes, :get_feature, :except => [:new, :create, :revert, :get_unsavedbox, :new_comment, :auto_complete_for_feature_tag, :tags, :votes]
9   before_filter :load_tags, :only => [:index, :tags, :add_comment, :edit_title, :edit_description, :revert, :save, :edit_usecase, :edit_testcase, :remove_actor, :add_myself_as]
10   before_filter :load_votes, :only => [:index, :votes, :vote_statistics, :add_comment, :edit_title, :edit_description, :revert, :save, :edit_usecase, :edit_testcase, :remove_actor, :add_myself_as]
11   skip_before_filter :verify_authenticity_token, :only => [:votes, :votes_graph, :vote_statistics, :tags, :get_unsavedbox, :get_editbox]
12   before_filter :require_xhr_post, :only => [:add_comment, :vote_up, :vote_neutral, :vote_down, :tag, :tags, :remove_tag, :add_product, :edit_description, :edit_product, :edit_relation, :edit_stakeholder, :edit_testcase, :edit_title, :edit_usecase]
13
14   auto_complete_for :feature, :tag
15
16
17   # Render a single feature
18   def index
19     if @feature.nil?
20       flash[:notice] = "Feature ##{params[:id]} does either not exist or you are not authorized to access it."
21       redirect_to :controller => "main", :action => "index"
22       return
23     end
24     
25     if !session[:user].isAuthenticated then
26       @skip_flash_sanitize = true
27       flash.now[:info] = "Please <a href='#' onClick='Effect.BlindDown(\"flash_login_form\");return false;'>login</a> or <a href='/ICSLogin/?%22" + @return_to + "%22'>register</a> to be able to edit or vote this feature." + 
28              "<div id='flash_login_form' style='display:none'>" + render_to_string(:partial=>'main/login_form') + "</div>"
29     end
30     
31     if (params[:contenttype] == 'text/xml')
32       render(:text => @feature.xml, :content_type => 'text/xml' )
33     elsif (params[:contenttype] == 'text/plain')
34       render(:text => @feature.render('text', session[:user], @client), :content_type => 'text/plain' )
35     elsif (params[:contenttype] == 'text/print')
36       @css = 'feature-print.css'
37       render :template => 'feature/index', :layout => false
38     end
39   end
40   
41   
42   def votes
43     if @votes.nil? then
44       render :text => "No voting data available."
45     elsif (@votes.class == Hash && @votes["status"] && @votes["status"] != "0")
46       render :text => "There was an error: #{@votes["status"]} - #{@votes["message"]}"
47     else
48       render :partial => "voting"
49     end
50   end
51
52
53   def vote_statistics
54     render :partial => "vote_statistics"
55   end
56
57
58   def votes_graph
59     send_data(Feature.graph(params[:id]), :disposition => 'inline', :type => 'image/png', :filename => "#{params[:id]}.png")
60   end
61
62
63   def vote_up
64     logger.info( "#{session[:user].uid} is voting up  #{params[:id]} " )
65     Feature.vote_up(params[:id], session[:user].uid)
66     redirect_to :action => :votes, :id => params[:id]
67   end
68
69
70   def vote_neutral
71     logger.info( "#{session[:user].uid} is voting neutral  #{params[:id]} " )
72     Feature.vote_neutral(params[:id], session[:user].uid)
73     redirect_to :action => :votes, :id => params[:id]
74   end
75   
76   
77   def vote_down
78     logger.info( "#{session[:user].uid} is voting down  #{params[:id]} " )
79     Feature.vote_down(params[:id], session[:user].uid)
80     redirect_to :action => :votes, :id => params[:id]
81   end
82   
83   
84   def tags
85     if @tags.nil?
86       render :text => "No tagging data availble."
87     elsif (@tags.class == Hash && !@tags["status"].nil? && @tags["status"] != "0")
88       render :text => "There was an error: #{@tags["status"]} - #{@tags["message"]}"
89     else
90       render :partial => "tagging"
91     end
92   end
93   
94   
95   def tag
96     if !params[:tag].blank?
97       logger.info( "#{session[:user].uid} is tagging #{params[:id]} with #{params[:tag]}" )
98       params[:tag].split(' ').each do |t|
99         Feature.tag(params[:id], t)
100       end
101     end
102     render :partial => "tagging"
103   end
104   
105   
106   def remove_tag
107     if !params[:tag].blank?
108       logger.info( "#{session[:user].uid} is removing tag #{params[:tag]} from #{params[:id]}" )
109       Feature.remove_tag(params[:id], params[:tag])
110     end
111     render :partial => "tagging"
112   end
113   
114
115   def auto_complete_for_feature_tag
116     @thetags = get_all_tags.sort.collect(&:first).select { |t| /^#{params[:tag]}/.match( t ) }
117     render :inline => "<%= content_tag(:ul, @thetags.map { |t| content_tag(:li, h(t)) }) %>"
118   end
119
120
121   def attachment
122   end
123
124
125   def edit_product
126     if( @feature.change_product( params[:productid], params[:reqpriority], params[:status], 
127          params[:duplicate_details], params[:rejected_details], session[:user].uid ) )
128       session[:editedFeatures][params[:id]] = @feature.xml.to_s
129       @scrollup = true
130       flash.now[:info] = "Product #{params[:productname]} updated."
131       flash.now[:fade] = true
132     end
133     render :partial => "featurehtml"
134   end
135
136
137   def edit_stakeholder
138      sb = richtextify(params[:stakeholder_benefit])
139     if( validate_richtext( sb ) )
140       if( sb == "<p></p>" ) then sb = nil end
141       if( params[:nda_date] == "" ) then params[:nda_date] = nil end
142       if( params[:external_id] == "" ) then params[:external_id] = nil end
143       if (@feature.set_stakeholder_data( CGI.escapeHTML(params[:external_id]), CGI.escapeHTML(params[:nda_date]), sb) )
144         session[:editedFeatures][params[:id]] = @feature.xml.to_s
145         @scrollup = true
146         flash.now[:info] = "Stakeholder data updated."
147         flash.now[:fade] = true
148       end
149     else
150       logger.debug "Validation failed: " + params[:stakeholder_benefit] + sb
151       @stakeholder_error = @validation_error
152       logger.debug @validation_error
153     end
154     @stakeholder_benefit = sb
155     render :partial => "featurehtml"
156   end
157
158
159   def remove_product
160     if (@feature.products.size == 1)
161       flash.now[:notice] = "You cannot remove all products from a feature."
162       flash.now[:fade] = true      
163     elsif( @feature.delete_product( params[:productid] ) )
164       @scrollup = true
165       session[:editedFeatures][params[:id]] = @feature.xml.to_s
166       flash.now[:info] = "Product #{params[:productname]} has been removed."
167       flash.now[:fade] = true
168     end
169     render :partial => "featurehtml"
170   end
171   
172
173   def add_product
174     if( @feature.add_product( params[:productid], params[:reqpriority] ) )
175        session[:editedFeatures][params[:id]] = @feature.xml.to_s
176       @scrollup = true
177       flash.now[:info] = "Your product has been added."
178       flash.now[:fade] = true
179     end
180     render :partial => "featurehtml"
181   end
182
183   
184   def new
185     if @newproducts.length == 0 then
186       flash[:error] = "There are currently no products open for new features."
187       redirect_to :controller => :main, :action => "index"
188       return
189     end
190   end
191
192   
193   def create
194     errors = Array.new
195     if !params[:title] || params[:title].empty? then
196       errors << "Please enter a title"
197     end
198     if !params[:description] || params[:description].empty? then
199       errors << "Please enter a description"
200     else
201       params[:description] = richtextify(params[:description])
202     end
203     if !params[:products] || params[:products].length == 0 then
204       errors << "Please select at least one product"
205     end
206     if !params[:priority] || params[:priority].empty? then
207       errors << "Please select at a priority"
208     end
209     
210     if( !validate_richtext( params[:description] ) ) then
211       errors << "The description is not valid richtext: " + @validation_error
212     end
213     
214     if params[:usecase] && !params[:usecase].empty? then
215       params[:usecase] = richtextify(params[:usecase])
216       if( !validate_richtext( params[:usecase] ) ) then
217         errors << "The usecase is not valid richtext: " + @validation_error
218       end
219     end
220     
221     if params[:testcase] && !params[:testcase].empty? then
222       params[:testcase] = richtextify(params[:testcase])
223       if( !validate_richtext( params[:testcase] ) ) then
224         errors << "The testcase is not valid richtext: " + @validation_error
225       end
226     end
227
228     if params[:partner_benefit] && !params[:partner_benefit].empty? then
229       params[:partner_benefit] = richtextify(params[:partner_benefit])
230       if( !validate_richtext( params[:partner_benefit] ) ) then
231         errors << "The partner benefit is not valid richtext: " + @validation_error
232       end
233     end
234     
235     if !errors.empty? then 
236       flash.now[:error] = "There were errors:<br/><ul>"
237       errors.each do |e|
238         flash.now[:error] << "<li>#{e}</li>"
239       end
240       flash.now[:error] << "</ul>"
241       render :action => :new
242       return
243     end
244     
245     begin
246       xml = %Q[
247         <feature xmlns:k="http://inttools.suse.de/sxkeeper/schema/keeper" k:schemarevision="12" >
248         <title>#{CGI.escapeHTML(params[:title])}</title>
249         <description><richtext>#{params[:description]}</richtext></description>
250         <partnercontext><organization>#{get_user_org()}</organization>]
251        if params[:external_id] && !params[:external_id].empty? then
252         xml += %Q[
253           <externalid>#{CGI.escapeHTML(params[:external_id])}</externalid>]
254       end
255        if params[:partner_benefit] && !params[:partner_benefit].empty? then
256         xml += %Q[
257           <partnerbenefit><richtext>#{params[:partner_benefit]}</richtext></partnerbenefit>]
258       end
259        if params[:nda_date] && !params[:nda_date].empty? then
260         xml += %Q[
261           <nda><expires>#{CGI.escapeHTML(params[:nda_date])}</expires></nda>]
262       end
263         xml += %Q[</partnercontext>]
264       params[:products].each do |p|
265         xml += %Q[
266           <productcontext>
267             <product>
268               <productid>#{p}</productid>
269             </product>
270             <status><unconfirmed/></status>
271             <priority>
272               <#{params[:priority]}/>
273               <owner><role>requester</role></owner>
274             </priority>
275           </productcontext>]
276       end
277       xml += create_requester_element get_user_org()
278       
279       if params[:testcase] && !params[:testcase].empty? then
280         xml += %Q[
281           <testcase><richtext>#{params[:testcase]}</richtext></testcase>]
282       end
283       if params[:usecase] && !params[:usecase].empty? then
284         xml += %Q[
285           <usecase><richtext>#{params[:usecase]}</richtext></usecase>]
286       end
287       
288       xml += "</feature>"
289       logger.debug xml
290       
291       f = Feature.new xml
292       response = f.save(session[:user].uid, @client)
293       if response.code == "201" then
294         xml = XML::Smart.string(response.body)
295         id = xml.find("k:docchange/k:id", {"k" => "http://inttools.suse.de/sxkeeper/schema/keeper"}).first.text
296         flash[:success] = "Your feature has been created with the id #{id}.<br/>" +
297           "Please subscribe at <u><a href='http://hermes.opensuse.org'>hermes</a></u> " +
298           "if you want to receive notifications on feature changes."
299         redirect_to :action => :index, :id => id
300         return
301       else
302         raise Exception, response.body
303       end
304     rescue Exception => e
305       logger.error "Creating Feature ##{params[:id]} failed: \n#{extract_errormessage(e.to_s)}"
306       flash.now[:error] = "Creating Feature ##{params[:id]} failed: \n#{extract_errormessage(e.to_s)}"
307       ExceptionNotifier.deliver_exception_notification(e, self, request, {})
308     end
309     render :template => 'feature/new'
310   end
311   
312   
313   def add_comment
314     comment = richtextify(params[:comment])
315     if( validate_richtext( comment ) && validate_content( comment ) )
316       if (@feature.add_comment( comment, session[:user], params[:replyto] )) 
317         session[:editedFeatures][params[:id]] = @feature.xml.to_s
318       end
319       @scrollup = true
320       flash.now[:info] = "Your comment has been added."
321       flash.now[:fade] = true
322     else
323       @replyto = params[:replyto]
324       @oldcomment = comment
325       @comment_error = @validation_error
326     end
327     render :partial => "featurehtml"
328   end
329   
330   
331   def edit_title
332     logger.debug "Setting title: " + CGI.escapeHTML(params[:newtitle])
333     if (@feature.set_field( "title", CGI.escapeHTML(params[:newtitle]) ) )
334       session[:editedFeatures][params[:id]] = @feature.xml.to_s
335       flash.now[:info] = "The title was updated."
336       flash.now[:fade] = true
337     end
338     render :partial => "featurehtml"
339   end
340   
341   
342   def edit_description
343     desc = richtextify(params[:newdescription])
344     if( validate_richtext( desc ) )
345       logger.debug "Setting description: " + params[:newdescription]
346       if (@feature.set_richtextfield("description", desc) )
347         session[:editedFeatures][params[:id]] = @feature.xml.to_s
348       end
349       @scrollup = true
350       flash.now[:info] = "The description was updated."
351       flash.now[:fade] = true
352     else
353       logger.debug "Validation failed: " + params[:newdescription] + desc
354       @newdescription_value = desc
355       @newdescription_error = @validation_error
356       logger.debug @newdescription_error
357     end
358     render :partial => "featurehtml"
359   end
360   
361   
362   def edit_usecase
363     uc = richtextify(params[:newusecase])
364     if( validate_richtext( uc ) )
365       if( uc == "<p></p>" ) then uc = nil end
366       logger.debug "Setting usecase: #{uc}"
367       if (@feature.set_richtextfield("usecase", uc) ) 
368         session[:editedFeatures][params[:id]] = @feature.xml.to_s
369       end
370       @scrollup = true
371       flash.now[:info] = "The usecase was updated."
372       flash.now[:fade] = true
373     else
374       logger.debug "Validation failed: " + params[:newusecase] + uc
375       @newusecase_value = uc
376       @newusecase_error = @validation_error
377       logger.debug @newusecase_error
378     end
379     render :partial => "featurehtml"
380   end
381   
382   
383   def edit_testcase
384     tc = richtextify(params[:newtestcase])
385     if( validate_richtext( tc ) )
386       if( tc == "<p></p>" ) then tc = nil end
387       logger.debug "Setting testcase: #{tc}"
388       if (@feature.set_richtextfield("testcase", tc) )
389         session[:editedFeatures][params[:id]] = @feature.xml.to_s
390       end
391       @scrollup = true
392       flash.now[:info] = "The testcase was updated."
393       flash.now[:fade] = true
394     else
395       logger.debug "Validation failed: " + params[:newtestcase] + tc
396       @newtestcase_value = tc
397       @newtestcase_error = @validation_error
398       logger.debug @newtestcase_error
399     end
400     render :partial => "featurehtml"
401   end
402   
403   
404   def remove_actor
405     @scrollup = true
406     @feature.remove_actor(params[:uid], params[:role])
407     session[:editedFeatures][params[:id]] = @feature.xml.to_s
408     flash.now[:info] = "You have been removed with role #{params[:role]}."
409     flash.now[:fade] = true
410     render :partial => "featurehtml"
411   end
412   
413   
414   def add_myself_as
415     if params[:role] != "developer" && params[:role] != "interested" then
416       flash.now[:error] = "Invalid role"
417     elsif (@feature.actors(session[:user].uid, params[:role]).size > 0)
418       logger.error("Tried to add user #{session[:user].uid} twice to #{params[:role]}")
419     else
420       @feature.add_actor(session[:user].uid, params[:role])
421        session[:editedFeatures][params[:id]] = @feature.xml.to_s
422       flash.now[:info] = "You have been added with role #{params[:role]}."
423       flash.now[:fade] = true
424        @scrollup = true
425     end
426     render :partial => "featurehtml"
427   end
428   
429   
430   def remove_relation
431     @scrollup = true
432     @feature.remove_relation(params[:relation])
433     session[:editedFeatures][params[:id]] = @feature.xml.to_s
434     render :partial => "featurehtml"
435   end
436   
437   
438   def edit_relation
439     @scrollup = true
440     if $editablerelationtypes.index(params[:type]).nil? then
441       flash.now[:error] = "Invalid relation type."
442       render :partial => "featurehtml"
443       return
444     end
445     if params[:title].nil? || params[:title].empty? then
446       flash.now[:error] = "Please enter a title for the relation."
447       render :partial => "featurehtml"
448       return
449     end
450     if params[:target].nil? || params[:target].empty? then
451       flash.now[:error] = "Please enter a target for the relation."
452       render :partial => "featurehtml"
453       return
454     end
455     if params[:wasrelation].nil? || params[:wasrelation].empty? then
456        @feature.add_relation CGI.escapeHTML(params[:title]), CGI.escapeHTML(params[:target]), params[:type]
457     else
458        @feature.edit_relation params[:wasrelation], CGI.escapeHTML(params[:title]), CGI.escapeHTML(params[:target]), params[:type]
459     end
460     session[:editedFeatures][params[:id]] = @feature.xml.to_s
461     render :partial => "featurehtml"
462   end
463   
464   
465   def save
466     if request.post?
467       begin
468         @feature.save( session[:user].uid, @client )
469         session[:editedFeatures].delete(params[:id])
470         flash.now[:success] = "Feature ##{params[:id]} saved.<br/>" +
471           "Please subscribe at <u><a href='http://hermes.opensuse.org'>hermes</a></u> " +
472           "if you want to receive notifications on feature changes."
473         get_feature
474         @force_box_refresh = true
475         @scrollup = true
476       rescue Exception=>e
477         logger.error "Saving Feature ##{params[:id]} failed: \n#{extract_errormessage(e.to_s)}"
478         allowed_codes = [ '452', '409' ]
479         if (allowed_codes.include?(extract_errorcode(e.to_s)))
480           flash.now[:error] = "Saving Feature ##{params[:id]} failed: " + extract_errormessage(e.to_s)
481         else 
482           ExceptionNotifier.deliver_exception_notification(e, self, request, {})
483           flash.now[:error] = "Saving Feature ##{params[:id]} failed due to an internal error. The development team has been notified."
484         end
485       end
486       render :partial => "featurehtml"
487     end
488     if request.get?
489       then
490         flash[:error] = "You entered an malformed url!"
491         redirect_to :controller => "feature", :id => params[:id]
492       end
493   end
494   
495   
496   def revert
497     if session[:editedFeatures].has_key?(params[:id]) then 
498       session[:editedFeatures].delete(params[:id])
499       flash.now[:notice] = "Your changes to feature ##{params[:id]} have been reverted."
500       flash.now[:fade] = true
501       @force_box_refresh = true
502       @scrollup = true
503     end
504     get_feature
505     render :partial => "featurehtml"
506   end
507   
508   
509   def get_unsavedbox
510     render :partial => "layouts/unsavedbox"
511   end  
512   
513   
514   def get_editbox
515     render :partial => "editbox"
516   end
517   
518
519   private
520   
521   def require_xhr_post
522     if !request.post? || !request.xhr?
523       logger.error "Not a POST request to #{request}"
524       flash[:error] = "You entered an malformed url!"
525       redirect_to :controller => "feature", :id => params[:id]
526     end
527   end
528   
529
530   def load_tags
531     @tags = Feature.tags(params[:id])
532   end
533
534
535   def load_votes
536     @votes = Feature.votes_for_object(params[:id], session[:user].uid)
537     if !@votes.nil? then
538       @voting_total = @votes["total"]
539       @voting_sum = @votes["sum"]
540       @voting_up = @votes["up"]
541       @voting_neutral = @votes["neutral"]
542       @voting_down = @votes["down"]
543       @voting_user= @votes["has_voted"]
544     end
545   end
546   
547
548   def add_boxes
549     if isOpenFate 
550       if (session[:user].isAuthenticated)
551         @partials = [ ['feature/voting', {}], 
552                    ['feature/editbox', {}], 
553                    ['feature/exportbox', {}], 
554                    ['layouts/unsavedbox', {}],
555                    ['feature/hermesbox', {}]]
556       else 
557         @partials = [ ['feature/voting', {}], 
558                     ['feature/exportbox', {}] ]
559       end
560     elsif isPartnerFate
561         @partials = [ ['feature/editbox', {}], 
562                    ['feature/exportbox', {}], 
563                    ['layouts/unsavedbox', {}] ]       
564     else
565         @partials = [ ['feature/voting', {}], 
566                    ['feature/editbox', {}], 
567                    ['feature/exportbox', {}], 
568                    ['layouts/unsavedbox', {}] ]
569     end
570   end
571   
572   
573   def validate_params
574     if params.has_key?(:id) and /\A\d*\z/.match(params[:id]).nil? then
575       flash[:error] = "'#{params[:id]}' is not a valid id."
576       redirect_to :controller => "main", :action => :index
577       return
578     end
579     if params.has_key?(:replyto) and /\A\d*\z/.match(params[:replyto]).nil? then
580       flash[:error] = "'#{params[:replyto]}' is not a valid comment number."
581       redirect_to :controller => "main", :action => :index
582       return
583     end
584   end
585
586   
587   # is called on incoming field values to create a valid richtext from user input
588   def richtextify(content)
589     logger.debug content
590
591     # WYSIWYG is too smart, so decode the encoded entities
592     content.gsub!( /&gt;/, "&amp;gt;" ); # Escape XML-Tags
593     content.gsub!( /&lt;/, "&amp;lt;" ); # Escape XML-Tags
594     content = decode_entities(content)
595
596     content.gsub!( /&/, "&amp;" );
597     # Escape all < and > that are not part of an element declaration
598     content.gsub!( />/, "&gt;" );
599     content.gsub!( /</, "&lt;" );
600     content.gsub!( /&lt;(\/?\w+)(\s\w+=['"][^'"]*['"])*[\/]?&gt;/, "<\\1\\2>" );
601     
602     # now fix the entities that we just broke... this is getting a mess
603     content.gsub!( /&amp;gt;/, "&gt;" );
604     content.gsub!( /&amp;lt;/, "&lt;" );
605     
606     # if the text starts and ends with an element or starts/ends with <p>, don't add elements
607    if( content.index(/\A<.*>\Z/m).nil? && 
608         content.index(/\A[^<]*<p>/m).nil? && content.index(/<\/p>[^>]*\Z/m).nil? ) then
609       content = "<p>" + content + "</p>"
610    end
611     # make a new paragraph on double newlines
612     content.gsub!( /(<p>[^<]*)\n\n/, "\\1</p>\n<p>" );
613     logger.debug "Richtextified content: " + content
614     return content
615   end
616
617   
618   def validate_richtext(content)
619     dtd = LibXML::XML::Dtd.new(<<EOF)
620 <!ELEMENT richtext ( p | a | b | ul | ol | em | pre | h3 | tt )*>
621
622 <!ELEMENT p (#PCDATA | b | ul | ol | a | tt | em | pre)*>
623 <!ELEMENT a (#PCDATA)>
624 <!ATTLIST a
625           href CDATA #REQUIRED>
626 <!ELEMENT b (#PCDATA | b | ul | ol | a | tt | em | p | pre)*>
627 <!ATTLIST b
628           class CDATA #IMPLIED>
629 <!ELEMENT ul (li+)>
630 <!ELEMENT ol (li)+>
631 <!ELEMENT li (#PCDATA | b | tt | em | a | p | ul)*>
632 <!ELEMENT em (#PCDATA)>
633 <!ELEMENT pre (#PCDATA | tt | b | em | a )*>
634 <!ATTLIST pre
635           class CDATA #IMPLIED>
636 <!ELEMENT h3 (#PCDATA)>
637 <!ELEMENT tt (#PCDATA | b | ul | ol | a | tt | em | p | pre)*>
638 <!ATTLIST tt
639           class CDATA #IMPLIED>
640 EOF
641
642     begin
643       doc = LibXML::XML::Document.string( "<richtext>" + content + "</richtext>" )
644       doc.validate(dtd)
645     rescue => e
646       @validation_error = e.to_s
647       return false
648     else
649       return true
650     end
651   end
652
653
654   def validate_content(content)
655     if( content.length <= 15 && ( content.include?( '+1' ) || content.include?( '-1' )) )
656       @validation_error = "Your comment is too short. Please use the voting widget in the upper right corner " +
657         "to vote for a feature, so the discussion stays readable."
658       return false
659     end
660     return true
661   end
662
663
664   def check_member
665     if (isOpenFate && !session[:user].isMember && !session[:user].isAdmin?)
666       flash[:notice] = "Sorry, only approved openSUSE members can create new feature requests at the moment. \n" + 
667                        "Please check <a href='http://en.opensuse.org/Members'>http://en.opensuse.org/Members</a> for information about openSUSE membership."
668       redirect_to :controller => "main", :action => "index"
669     end
670   end
671   
672   # for partnerfate, we need to set the requester to the tam
673   def create_requester_element partnerorg
674     xml = "<actor><role>requester</role><person>"
675     if isPartnerFate
676       if $partner.items[partnerorg]["tam"].nil?
677         raise "There is a problem with your assigned TAM. Please contact your TAM or featureadmin@suse.de."
678       end
679       xml += "<email>#{$partner.items[partnerorg]["tam"]}</email>"
680     else
681       xml += "<userid>#{session[:user].uid}</userid>"
682     end
683     return xml + "</person></actor>"
684   end
685
686   def self.logger 
687     RAILS_DEFAULT_LOGGER
688   end
689
690 end