diff options
author | Joachim Filip Ignacy Bartosik <jbartosik@gmail.com> | 2010-07-12 18:47:01 +0200 |
---|---|---|
committer | Joachim Filip Ignacy Bartosik <jbartosik@gmail.com> | 2010-07-29 19:49:13 +0200 |
commit | 84c29dd7f3fc64ea491f0342476f7bc31a20171f (patch) | |
tree | 6bd86e4dac5bc216de46c5e18569de8deeaf012d /app | |
parent | Allow project leads to add Project Acceptances easily (diff) | |
download | recruiting-webapp-84c29dd7f3fc64ea491f0342476f7bc31a20171f.tar.gz recruiting-webapp-84c29dd7f3fc64ea491f0342476f7bc31a20171f.tar.bz2 recruiting-webapp-84c29dd7f3fc64ea491f0342476f7bc31a20171f.zip |
Email questions
Also make sure users can answer questions with multiple and text
content and can't answer email questions within application. Added
"Gentoo-dev-announce posting" question to seed
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/question_content_emails_controller.rb | 12 | ||||
-rw-r--r-- | app/models/email_answer.rb | 55 | ||||
-rw-r--r-- | app/models/question.rb | 5 | ||||
-rw-r--r-- | app/models/question_content_email.rb | 72 | ||||
-rw-r--r-- | app/models/user.rb | 18 | ||||
-rw-r--r-- | app/models/user_mailer.rb | 6 | ||||
-rw-r--r-- | app/views/question_content_emails/new_for_question.dryml | 4 | ||||
-rw-r--r-- | app/views/questions/show.dryml | 3 | ||||
-rw-r--r-- | app/views/taglibs/detailed.dryml | 20 | ||||
-rw-r--r-- | app/views/taglibs/forms.dryml | 12 | ||||
-rw-r--r-- | app/views/taglibs/views.dryml | 22 |
11 files changed, 227 insertions, 2 deletions
diff --git a/app/controllers/question_content_emails_controller.rb b/app/controllers/question_content_emails_controller.rb new file mode 100644 index 0000000..345f9c3 --- /dev/null +++ b/app/controllers/question_content_emails_controller.rb @@ -0,0 +1,12 @@ +class QuestionContentEmailsController < ApplicationController + + hobo_model_controller + + auto_actions :create, :update + auto_actions_for :question, :new + + def new_for_question + hobo_new QuestionContentEmail.new(:question_id => params[:question_id]) + end + +end diff --git a/app/models/email_answer.rb b/app/models/email_answer.rb new file mode 100644 index 0000000..730f483 --- /dev/null +++ b/app/models/email_answer.rb @@ -0,0 +1,55 @@ +class EmailAnswer < Answer + fields do + correct :boolean + end + + # Users can't change Email answers - app does it internally + multi_permission(:create, :update, :destroy, :edit){ false } + + # Creates new answer/ updates old answer + # expects user to send email from address [s]he has given to app + # and title in format "#{question.id}-#{user.token}" + def self.answer_from_email(email) + user = User.find_by_email_address(email.from) + return if user.nil? + + subject = /^([0-9]+)-(\w+)$/.match(email.subject) + return if subject.nil? + return unless user.token == subject.captures[1] + + question = Question.first :conditions => { :id => subject.captures[0] } + return if question.nil? + return unless question.content.is_a? QuestionContentEmail + + # Fetch existing answer, if it doesn't exist create a new one + # them mark it as incorrect (if it passes all tests it will be correct) + answer = question.answer_of(user) + answer = EmailAnswer.new(:question => question, :owner => user) if answer.nil? + answer.correct = false + answer.save! + + for condition in question.content.req_array + val = email.send(condition[0].downcase.sub('-', '_')) # Value we're testing + cond = /#{condition[1]}/i # Regexp value should match + passed = false # Was test passed + + val = [''] if val.is_a? NilClass + val = [val] if val.is_a? String + + for v in val + if v.match(cond) + # Test passed + passed = true + break + end + end + + return unless passed + end + + # passed all test - mark it and return answer + answer.correct = true + answer.save! + answer + end +end diff --git a/app/models/question.rb b/app/models/question.rb index f5ad800..b126708 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -23,6 +23,7 @@ class Question < ActiveRecord::Base has_many :user_question_groups has_one :question_content_text has_one :question_content_multiple_choice + has_one :question_content_email multi_permission :create, :update, :destroy do # Allow changes if user is administrator @@ -121,7 +122,9 @@ class Question < ActiveRecord::Base end def content - question_content_text || question_content_multiple_choice + question_content_text || + question_content_multiple_choice || + question_content_email end before_create{ |question| diff --git a/app/models/question_content_email.rb b/app/models/question_content_email.rb new file mode 100644 index 0000000..37c1197 --- /dev/null +++ b/app/models/question_content_email.rb @@ -0,0 +1,72 @@ +require 'permissions/inherit.rb' +class QuestionContentEmail < ActiveRecord::Base + + hobo_model # Don't put anything above this + + fields do + requirements :text, :nil => false, :default => "" + description HoboFields::MarkdownString + timestamps + end + + belongs_to :question + attr_readonly :question + never_show :requirements + + inherit_permissions(:question) + + def req_text_view_permitted? + return true if acting_user.try.role.try.is_recruiter? + return true if question.owner_is?(acting_user) && !question.approved + end + + # Returns array. + # Each item of array is array [field, expected value] + def req_array + if requirements.nil? || requirements.empty? + [] + else + ActiveSupport::JSON.decode requirements + end + end + + # Returns easy-human-readable string + # Each line is in format + # field : value + def req_text + res = req_array.inject(String.new) do |res, cur| + # escape colons + cur[0].sub!(':', '\:') + cur[1].sub!(':', '\:') + + res += "#{cur[0]} : #{cur[1]}\n" + end + HoboFields::Text.new(res) + end + + # req_text escaped to display properly as HTML + def req_html + h(req_text).sub("\n", "<br/>\n") + end + + # Converts easy-human-readable string to JSON and saves in requirements + # Ignore improperly formatted lines ( i.e. lines that + def req_text=(str) + # Split to lines + # Split every line at /\s:/, unescape colons, strip white space + # Ignore lines that don't have exactly one /\s:/ + res = str.split(/\n/).inject(Array.new) do |result, line| + + item = line.split(/\s:/).inject(Array.new) do |r,c| + c.sub!('\:', ':') + c.strip! + r.push c + end + + result.push(item) if item.count == 2 + result + end + + self.requirements = res.to_json + end +end diff --git a/app/models/user.rb b/app/models/user.rb index e652e37..a974abb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -10,6 +10,7 @@ class User < ActiveRecord::Base nick :string contributions HoboFields::MarkdownString project_lead :boolean, :default => false + token :string timestamps end @@ -61,6 +62,23 @@ class User < ActiveRecord::Base validates_uniqueness_of :nick, :if => :nick never_show :project_lead + + # Token + never_show :token + + # Generate new token + def token=(more_salt) + # Time.now.to_f.to_s gives enough precision to be considered random + token = Digest::SHA1.hexdigest("#{Time.now.to_f.to_s}#{@salt}#{more_salt}") + write_attribute("token", token) + token + end + + # Give user token on creation + before_create do |u| + u.token = '' + end + # --- Permissions --- # def create_permitted? diff --git a/app/models/user_mailer.rb b/app/models/user_mailer.rb index db60b86..1c9521e 100644 --- a/app/models/user_mailer.rb +++ b/app/models/user_mailer.rb @@ -38,4 +38,10 @@ class UserMailer < ActionMailer::Base common(user, "New comment") @body = { :question_title=> question_title(comment.answer), :id => comment.answer.id } end + + def receive(email) + # For now email answers for questions are only emails app receives + # so try use any received email as answer. + EmailAnswer.answer_from_email(email) + end end diff --git a/app/views/question_content_emails/new_for_question.dryml b/app/views/question_content_emails/new_for_question.dryml new file mode 100644 index 0000000..180f65e --- /dev/null +++ b/app/views/question_content_emails/new_for_question.dryml @@ -0,0 +1,4 @@ +<% + # For unknown reason Hobo does not render a proper new_for page without this file +%> +<new-page/> diff --git a/app/views/questions/show.dryml b/app/views/questions/show.dryml index 0c3fc71..179fbe1 100644 --- a/app/views/questions/show.dryml +++ b/app/views/questions/show.dryml @@ -8,12 +8,13 @@ <ul> <li><a href="&new_question_content_text_for_question_path(this.id)">Add text content</a></li> <li><a href="&new_question_content_multiple_choice_for_question_path(this.id)">Add multiple choice content</a></li> + <li><a href="&new_question_content_email_for_question_path(this.id)">Add email content</a></li> </ul> </if> </if> <else> <if with="&this.answer_of(current_user)"> - <a>View you answer</a>' + <a href="&answer_path(this)">View you answer</a>' </if> <else> <if test="& current_user.signed_up? && this.content.try.new_answer_of(current_user)"> diff --git a/app/views/taglibs/detailed.dryml b/app/views/taglibs/detailed.dryml index f7df48e..ceb8dc9 100644 --- a/app/views/taglibs/detailed.dryml +++ b/app/views/taglibs/detailed.dryml @@ -50,3 +50,23 @@ </answer:> </detailed> </def> + +<def tag="detailed" for="EmailAnswer"> + <h2> + Answer of + <with:owner><name/></with> + for question + "<with:question><name/></with> + <a action="edit" if="&can_edit?">(Edit)</a> + </h2> + <h5>Question:</h5> + <with:question><view:content/></with> + + <h5>Answer:</h5> + <if:correct> + You sent proper email. + </if> + <else> + Email you sent didn't match requirements. + </else> +</def> diff --git a/app/views/taglibs/forms.dryml b/app/views/taglibs/forms.dryml index eddfc0f..013f84c 100644 --- a/app/views/taglibs/forms.dryml +++ b/app/views/taglibs/forms.dryml @@ -37,3 +37,15 @@ </div> </form> </def> + +<def tag="form" for="QuestionContentEmail"> + <form merge param="default"> + <error-messages param/> + <field-list fields="description, req_text" param> + <req_text-label:>Requirements</req_text-label:> + </field-list> + <div param="actions"> + <submit label="#{ht 'question_content_emails.actions.save', :default=>['Save']}" param/><or-cancel param="cancel"/> + </div> + </form> +</def> diff --git a/app/views/taglibs/views.dryml b/app/views/taglibs/views.dryml index fcbec6d..79bd41d 100644 --- a/app/views/taglibs/views.dryml +++ b/app/views/taglibs/views.dryml @@ -19,3 +19,25 @@ <%raise HoboError, "view of non-viewable field '#{this_field}' of #{this_parent.typed_id rescue this_parent}" unless can_view?%> <input disabled/> </def> + +<def tag="view" for="QuestionContentEmail"> + <%= + if this.viewable_by?(current_user, :req_text) + "#{h(this.req_text).sub("\n", "<br/>\n")}<br/>" + else + raise HoboError, "view of non-viewable field '#{this_field}' of #{this_parent.typed_id rescue this_parent}" + end + %> + + <%= + if this.viewable_by?(current_user, :description) + "#{this.description.to_html}" + else + raise HoboError, "view of non-viewable field '#{this_field}' of #{this_parent.typed_id rescue this_parent}" + end + %> + + <if test="¤t_user.signed_up?"> + Your answer should have subject (without quotes) "<%= "#{this.question.id}-#{current_user.try.token}" %>". + </if> +</def> |