aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorJoachim Filip Ignacy Bartosik <jbartosik@gmail.com>2010-07-12 18:47:01 +0200
committerJoachim Filip Ignacy Bartosik <jbartosik@gmail.com>2010-07-29 19:49:13 +0200
commit84c29dd7f3fc64ea491f0342476f7bc31a20171f (patch)
tree6bd86e4dac5bc216de46c5e18569de8deeaf012d /app
parentAllow project leads to add Project Acceptances easily (diff)
downloadrecruiting-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.rb12
-rw-r--r--app/models/email_answer.rb55
-rw-r--r--app/models/question.rb5
-rw-r--r--app/models/question_content_email.rb72
-rw-r--r--app/models/user.rb18
-rw-r--r--app/models/user_mailer.rb6
-rw-r--r--app/views/question_content_emails/new_for_question.dryml4
-rw-r--r--app/views/questions/show.dryml3
-rw-r--r--app/views/taglibs/detailed.dryml20
-rw-r--r--app/views/taglibs/forms.dryml12
-rw-r--r--app/views/taglibs/views.dryml22
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="&current_user.signed_up?">
+ Your answer should have subject (without quotes) "<%= "#{this.question.id}-#{current_user.try.token}" %>".
+ </if>
+</def>