diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb
index 026242b..8ef2500 100644
--- a/app/controllers/answers_controller.rb
+++ b/app/controllers/answers_controller.rb
@@ -6,8 +6,6 @@
# PUT/PATCH /[:locale]/answer/[:id]
def update
# create a new answer based off the passed params
- logger.debug("RAY: update params=")
- logger.debug params.inspect
ans_params = params[:answer]
plan_id = ans_params[:plan_id]
@@ -17,10 +15,8 @@
plan_id: plan_id,
user_id: user_id,
question_id: question_id)
- logger.debug "RAY: found answer=#{@answer.inspect}"
if @answer.nil?
@answer = Answer.new(params[:answer])
- logger.debug "RAY: created answer=#{@answer.inspect}"
end
authorize @answer
diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb
index d116da0..8a400f6 100644
--- a/app/controllers/notes_controller.rb
+++ b/app/controllers/notes_controller.rb
@@ -6,18 +6,30 @@
# POST /notes
def create
@note = Note.new
- logger.debug "RAY: into save note"
- @note.user_id = params[:new_note][:user_id]
- @note.answer_id = params[:new_note][:answer_id]
+ user_id = params[:new_note][:user_id]
+ @note.user_id = user_id
+
+ answer_id = params[:new_note][:answer_id]
+ question_id = params[:new_note][:question_id]
+ plan_id = params[:new_note][:plan_id]
+ if answer_id.present?
+ answer = Answer.find(@note.answer_id)
+ else
+ answer = Answer.new
+ answer.plan_id = plan_id
+ answer.question_id = question_id
+ answer.user_id = user_id
+ answer.save!
+ end
+
+ @note.answer= answer
@note.text = params["#{params[:new_note][:answer_id]}new_note_text"]
authorize @note
- answer = Answer.find(@note.answer_id)
@plan = answer.plan
@phase = answer.question.section.phase
- logger.debug "RAY: saving " + @note.inspect
if @note.save
session[:question_id_notes] = answer.question_id
redirect_to edit_plan_phase_path(@plan, @phase), status: :found, notice: I18n.t("helpers.comments.note_created")
diff --git a/app/controllers/phases_controller.rb b/app/controllers/phases_controller.rb
index 5c08f41..b65f33f 100644
--- a/app/controllers/phases_controller.rb
+++ b/app/controllers/phases_controller.rb
@@ -1,4 +1,5 @@
class PhasesController < ApplicationController
+ require 'pp'
after_action :verify_authorized
@@ -9,7 +10,7 @@
DROPDOWN = QuestionFormat.where(title: "Dropdown").first.id
MULTI = QuestionFormat.where(title: "Multi select box").first.id
- # GET /phases/1/edit
+ # GET /plans/PLANID/phases/PHASEID/edit
def edit
@textarea = TEXTAREA
@@ -21,19 +22,12 @@
@plan = Plan.find(params[:plan_id])
authorize @plan
- @phase = Phase.where(template_id: @plan.template_id, slug: params[:id]).first
- @sections = @phase.sections
- @section_answers = Hash.new
- @phase.sections.each do |section|
- nanswers = 0
- questions = section.questions
- questions.each do |q|
- answers = q.answers.where(plan_id: @plan)
- nanswers += answers.count
- end
- @section_answers[section.id] = nanswers
- end
+ @plan_data = @plan.to_hash
+
+ phase_id = params[:id].to_i
+ @phase = Phase.find(phase_id)
+ @phase_data = @plan_data["template"]["phases"].select {|p| p["id"] == phase_id}.first
if !user_signed_in? then
respond_to do |format|
@@ -42,6 +36,20 @@
end
end
+
+
+ # GET /plans/PLANID/phases/PHASEID/status.json
+ def status
+ @plan = Plan.find(params[:plan_id])
+ authorize @plan
+ if user_signed_in? && @plan.readable_by?(current_user.id) then
+ respond_to do |format|
+ format.json { render json: @plan.status }
+ end
+ else
+ render(:file => File.join(Rails.root, 'public/403.html'), :status => 403, :layout => false)
+ end
+ end
end
diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb
index 3e26fac..edae8f5 100644
--- a/app/controllers/plans_controller.rb
+++ b/app/controllers/plans_controller.rb
@@ -1,4 +1,5 @@
class PlansController < ApplicationController
+ require 'pp'
#Uncomment the line below in order to add authentication to this page - users without permission will not be able to add new plans
#load_and_authorize_resource
#
@@ -109,13 +110,15 @@
# GET /plans/show
def show
- @plan = Plan.includes(template: {phases: :sections} ).find(params[:id])
+ @plan = Plan.find(params[:id])
authorize @plan
+
+ @plan_data = @plan.to_hash
+
@editing = params[:editing] && @plan.administerable_by?(current_user.id)
@selected_guidance_groups = []
- #@selected_guidance_groups = @plan.plan_guidance_groups.map{ |pgg| [pgg.guidance_group.name, pgg.guidance_group.id, :checked => pgg.selected] }
- all_guidance_groups = @plan.plan_guidance_groups.includes(:guidance_group)
- @selected_guidance_groups = all_guidance_groups.map{ |pgg| [pgg.guidance_group.name, pgg.guidance_group.id, :checked => pgg.selected] }
+ all_guidance_groups = @plan_data["plan_guidance_groups"]
+ @selected_guidance_groups = all_guidance_groups.map{ |pgg| [ pgg["guidance_group"]["name"], pgg["guidance_group"]["id"], :checked => pgg["selected"] ] }
@selected_guidance_groups.sort!
if user_signed_in? && @plan.readable_by?(current_user.id) then
@@ -212,6 +215,20 @@
end
end
+ def share
+ @plan = Plan.find(params[:id])
+ authorize @plan
+ @plan_data = @plan.to_hash
+ if !user_signed_in? then
+ respond_to do |format|
+ format.html { redirect_to edit_user_registration_path }
+ end
+ elsif !@plan.editable_by?(current_user.id) then
+ respond_to do |format|
+ format.html { redirect_to plans_url, notice: I18n.t('helpers.settings.plans.errors.no_access_account') }
+ end
+ end
+ end
def destroy
@@ -348,7 +365,7 @@
@plan = Plan.find(params[:id])
authorize @plan
- if user_signed_in? && @plan.readable_by(current_user.id) then
+ if user_signed_in? && @plan.readable_by?(current_user.id) then
@exported_plan = ExportedPlan.new.tap do |ep|
ep.plan = @plan
ep.user = current_user
@@ -356,7 +373,7 @@
ep.format = request.format.to_sym
plan_settings = @plan.settings(:export)
- Settings::Dmptemplate::DEFAULT_SETTINGS.each do |key, value|
+ Settings::Template::DEFAULT_SETTINGS.each do |key, value|
ep.settings(:export).send("#{key}=", plan_settings.send(key))
end
end
@@ -415,4 +432,48 @@
groups.values
end
+
+ def fixup_hash(plan)
+ rollup(plan, "notes", "answer_id", "answers")
+ rollup(plan, "answers", "question_id", "questions")
+ rollup(plan, "questions", "section_id", "sections")
+ rollup(plan, "sections", "phase_id", "phases")
+
+ plan["template"]["phases"] = plan.delete("phases")
+
+ ghash = {}
+ plan["guidance_groups"].map{|g| ghash[g["id"]] = g}
+ plan["plan_guidance_groups"].each do |pgg|
+ pgg["guidance_group"] = ghash[ pgg["guidance_group_id"] ]
+ end
+
+ plan["template"]["org"] = Org.find(plan["template"]["org_id"]).serializable_hash()
+ end
+
+
+ # find all object under src_plan_key
+ # merge them into the items under obj_plan_key using
+ # super_id = id
+ # so we have answers which each have a question_id
+ # rollup(plan, "answers", "quesiton_id", "questions")
+ # will put the answers into the right questions.
+ def rollup(plan, src_plan_key, super_id, obj_plan_key)
+ id_to_obj = Hash.new()
+ plan[src_plan_key].each do |o|
+ id = o[super_id]
+ if !id_to_obj.has_key?(id)
+ id_to_obj[id] = Array.new
+ end
+ id_to_obj[id] << o
+ end
+
+ plan[obj_plan_key].each do |o|
+ id = o["id"]
+ if id_to_obj.has_key?(id)
+ o[src_plan_key] = id_to_obj[ id ]
+ end
+ end
+ plan.delete(src_plan_key)
+ end
+
end
diff --git a/app/models/exported_plan.rb b/app/models/exported_plan.rb
index 05fe0e2..4045298 100644
--- a/app/models/exported_plan.rb
+++ b/app/models/exported_plan.rb
@@ -25,7 +25,9 @@
# Getters to match Settings::Template::VALID_ADMIN_FIELDS
def project_name
- self.plan.title
+ name = self.plan.template.title
+ name += " - #{self.plan.title}" if self.plan.template.phases.count > 1
+ name
end
def project_identifier
diff --git a/app/models/phase.rb b/app/models/phase.rb
index 31e3a57..88429fd 100644
--- a/app/models/phase.rb
+++ b/app/models/phase.rb
@@ -4,7 +4,7 @@
# [+Created:+] 03/09/2014
# [+Copyright:+] Digital Curation Centre and University of California Curation Center
class Phase < ActiveRecord::Base
- #extend FriendlyId
+# extend FriendlyId
##
# Associations
diff --git a/app/models/plan.rb b/app/models/plan.rb
index 3013e10..a32b171 100644
--- a/app/models/plan.rb
+++ b/app/models/plan.rb
@@ -5,6 +5,7 @@
has_many :phases, through: :template
has_many :sections, through: :phases
has_many :questions, through: :sections
+ has_many :themes, through: :questions
has_many :answers, dependent: :destroy
has_many :notes, through: :answers
has_many :roles, dependent: :destroy
@@ -35,7 +36,7 @@
#TODO: work out why this messes up plan creation :
# briley: Removed reliance on :users, its really on :roles (shouldn't have a plan without at least a creator right?) It should be ok like this though now
- validates :template, :title, presence: true
+# validates :template, :title, presence: true
##
# Constants
@@ -113,7 +114,6 @@
def set_possible_guidance_groups
# find all the themes in this plan
# and get the guidance groups they belong to
- logger.debug "RAY: set_possible_guidance_groups"
ggroups = []
self.template.phases.each do |phase|
phase.sections.each do |section|
@@ -140,7 +140,6 @@
# @return array of hashes with orgname, themes and the guidance itself
def guidance_for_question(question)
guidances = []
- logger.debug "RAY: guidance_for_question"
# add in the guidance for the template org
unless self.template.org.nil? then
@@ -599,23 +598,26 @@
end
return self.template.org
end
+=end
##
# returns the funder organisation for the project or nil if none is specified
#
# @return [Organisation, nil] the funder for project, or nil if none exists
def funder
- if self.template.nil? then
+ template = self.template
+ if template.nil? then
return nil
end
- template_org = self.template.org
- if template_org.funder?
- return template_org
+
+ if template.customization_of
+ return template.customization_of.org
else
- return nil
+ return template.org
end
end
+=begin
##
# returns the name of the funder for the project
#
@@ -824,9 +826,172 @@
end
=end
-
+
+
+ ##
+ #
+ # The following method is to help optimise data access.
+ # Even using includes or joins the data access gets performed lazily
+ # and the caching appears to be forgotten at times over view/partial boundaries
+ # To get round it we can pull everything out into a hash and use that
+ # which guarantees no further DB accesses.
+ #
+ # The serializable_hash method only pulls in one level but this includes
+ # attributes which are "through" other attributes. So we do a basic
+ # conversion to hash and then a "fixup" which knits the pieces together into
+ # the structure which we really want.
+ #
+ def to_hash
+ plan_data = self.serializable_hash(
+ include: [ :template, :phases, :sections,
+ :answers, :notes, :roles, :users, :questions,
+ :plan_guidance_groups, :guidance_groups]
+ )
+
+ question_hash = {}
+
+ plan_data["questions"].each do |q|
+ question_hash[q["id"]] = q
+ end
+
+ question_ids = question_hash.keys
+
+ suggested_answers = SuggestedAnswer.where(question_id: question_ids).where.not(text: '')
+ suggested_answers.each do |sa|
+ question_hash[sa.question_id]["suggested_answer"] = sa.serializable_hash
+ end
+
+ qf_hash = {}
+ QuestionFormat.all.each do |qf|
+ qf_hash[qf.id] = qf.serializable_hash
+ end
+ question_hash.values.each do |q|
+ q["question_format"] = qf_hash[q["question_format_id"]]
+ end
+
+ gg_ids = plan_data["plan_guidance_groups"].select{|pgg| pgg["selected"]}.map{|pgg| pgg["guidance_group_id"]}
+ gg_hash = {}
+
+ theme_guidance = {}
+
+ ggs = GuidanceGroup.find(gg_ids).each do |gg|
+ gg_hash[gg.id] = gg.serializable_hash
+ end
+
+ guidances = Guidance.joins(:themes).select('guidances.guidance_group_id, guidances.text, themes.title').where(guidance_group: gg_ids).to_a
+ guidances.each do |g|
+ title = g.title
+ if !theme_guidance.has_key?(title)
+ theme_guidance[title] = Array.new
+ end
+ theme_guidance[title] << {
+ "text" => g.text,
+ "org" => gg_hash[g.guidance_group_id]["name"]
+ }
+ end
+
+ plan_data["questions"].each do |q|
+ qg = {}
+ if q.has_key?("themes")
+ q["themes"].each do |t|
+ title = t["title"]
+ qg[title] = theme_guidance[title] if theme_guidance.has_key?(title)
+ end
+ q["theme_guidance"] = qg
+ end
+ end
+
+ fixup_hash(plan_data)
+
+ return plan_data
+ end
+
+
+
private
+ # reconnect the various parts of the hash so that:
+ # plan = {
+ # template:
+ # phases:
+ # sections:
+ # questions:
+ # answers
+ # }
+ #
+ # becomes a nested structure like so
+ #
+ # plan = { template: {
+ # phases: [ {
+ # sections: [ {
+ # questions: [ {
+ # answers: [...]
+ #
+ def fixup_hash(plan)
+ # sort out guidance first so we can add it to the questions
+ # before rolling up
+ ghash = {}
+ plan["guidance_groups"].map{|g| ghash[g["id"]] = g}
+ plan["plan_guidance_groups"].each do |pgg|
+ pgg["guidance_group"] = ghash[ pgg["guidance_group_id"] ]
+ end
+
+ rollup(plan, "notes", "answer_id", "answers")
+ rollup(plan, "answers", "question_id", "questions")
+ rollup(plan, "questions", "section_id", "sections", true)
+ rollup(plan, "sections", "phase_id", "phases", true)
+
+ plan["template"]["phases"] = plan.delete("phases")
+ plan["template"]["phases"].sort! { |x,y| x["number"].to_i <=> y["number"].to_i }
+
+ plan["template"]["org"] = Org.find(plan["template"]["org_id"]).serializable_hash()
+
+ # when editing phases we want the number of questions answered
+ # so calculate that now
+ plan["template"]["phases"].each do |phase|
+ phase["sections"].each do |section|
+ nanswers = 0
+ section["questions"].each do |question|
+ if question.has_key?("answers") && question["answers"].first["text"].present?
+ nanswers += 1
+ end
+ end
+ section["nanswers"] = nanswers
+ end
+ end
+
+ end
+
+
+ # find all object under src_plan_key
+ # merge them into the items under obj_plan_key using
+ # super_id = id
+ # so we have answers which each have a question_id
+ # rollup(plan, "answers", "quesiton_id", "questions")
+ # will put the answers into the right questions.
+ # sort determines whether the items being rolled up should be sorted by number field
+ def rollup(plan, src_plan_key, super_id, obj_plan_key, sort = false)
+ id_to_obj = Hash.new()
+ plan[src_plan_key].each do |o|
+ id = o[super_id]
+ if !id_to_obj.has_key?(id)
+ id_to_obj[id] = Array.new
+ end
+ id_to_obj[id] << o
+ end
+
+ plan[obj_plan_key].each do |o|
+ id = o["id"]
+ if id_to_obj.has_key?(id)
+ if sort
+ id_to_obj[ id ].sort! { |x,y| x["number"].to_i <=> y["number"].to_i }
+ end
+ o[src_plan_key] = id_to_obj[ id ]
+ end
+ end
+ plan.delete(src_plan_key)
+ end
+
##
# adds a user to the project
# if no flags are specified, the user is given read privleges
diff --git a/app/policies/guidance_policy.rb b/app/policies/guidance_policy.rb
index ee7be10..725326c 100644
--- a/app/policies/guidance_policy.rb
+++ b/app/policies/guidance_policy.rb
@@ -53,7 +53,7 @@
class Scope < Scope
def resolve
- scope = Guidance.includes(:guidance_group, :question, :themes).by_org(user.org_id)
+ scope = Guidance.includes(:guidance_group, :themes).by_org(user.org_id)
end
end
end
\ No newline at end of file
diff --git a/app/policies/plan_policy.rb b/app/policies/plan_policy.rb
index 3f5ba9a..cb43258 100644
--- a/app/policies/plan_policy.rb
+++ b/app/policies/plan_policy.rb
@@ -20,6 +20,10 @@
@plan.editable_by?(@user.id)
end
+ def share?
+ @plan.readable_by?(@user.id)
+ end
+
def export?
@plan.readable_by?(@user.id)
end
diff --git a/app/views/phases/_add_note.html.erb b/app/views/phases/_add_note.html.erb
index 00584fd..41d72f5 100644
--- a/app/views/phases/_add_note.html.erb
+++ b/app/views/phases/_add_note.html.erb
@@ -1,13 +1,21 @@
-<% new_note = Note.new %>
-<% answerid = answer.id %>
+
+<% new_note = Note.new
+ if !answer.nil?
+ answerid = answer["id"]
+ else
+ answerid = answer_obj.id
+ end
+%>
<%= form_for :new_note,
:url => {:controller => :notes, :action => :create },
:html=>{:method=>:post, :id => "new_note_form_#{answerid}", :class => "add_note_form"} do |f| %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.hidden_field :answer_id, :value => answerid %>
+ <%= f.hidden_field :question_id, :value => question["id"] %>
+ <%= f.hidden_field :plan_id, :value => plan_data["id"] %>
<%= text_area_tag("#{answerid}new_note_text".to_sym, "" , class: "tinymce") %>
diff --git a/app/views/phases/_answer_form.html.erb b/app/views/phases/_answer_form.html.erb
index 289ef9b..74ae43a 100644
--- a/app/views/phases/_answer_form.html.erb
+++ b/app/views/phases/_answer_form.html.erb
@@ -7,25 +7,36 @@
- <%= raw suggested_answer.text %> + <%= raw suggested_answer["text"] %>
| - <% user = c.user %> - <%= user.name %> - (<%= l c.updated_at, format: :custom %>) + <% user = c["user"] %> + <% user = User.find(c["user_id"]) %> + <%= user["name"] %> + (<%= l c["updated_at"], format: :custom %>) |
- <% if c.archived %>
+ <% if c["archived"] %>
- <% if c.archived_by == current_user.id %>
+ <% if c["archived_by"] == current_user.id %>
<%= t("helpers.comments.retracted")%>
<% else %>
- <% archived_by_user = User.find(c.archived_by) %>
+ <% archived_by_user = User.find(c["archived_by"]) %>
<%= t("helpers.comments.clear_by")%> <%= archived_by_user.name %>
<%end%>
<%else%>
<%= link_to t("helpers.comments.view_label"),"#", :class => "dmp_table_link view_comment_button" %>
- <%= hidden_field_tag :note_id, c.id, :class => "comment_id" %>
+ <%= hidden_field_tag :note_id, c["id"], :class => "comment_id" %>
- <% if current_user.id == c.user_id %>
+ <% if current_user.id == c["user_id"] %>
<%= link_to t("helpers.comments.edit_label"),"#", :class => "dmp_table_link edit_comment_button" %>
- <%= hidden_field_tag :note_id, c.id, :class => "comment_id" %>
+ <%= hidden_field_tag :note_id, c["id"], :class => "comment_id" %>
<%= link_to t("helpers.comments.retract_label"),"#", :class => "dmp_table_link archive_comment_button" %>
<% end%>
- <% if @plan.administerable_by?(current_user.id) && current_user.id != c.user_id %>
- <%= hidden_field_tag :note_id, c.id, :class => "comment_id" %>
+ <% if plan.administerable_by?(current_user.id) && current_user.id != c["user_id"] %>
+ <%= hidden_field_tag :note_id, c["id"], :class => "comment_id" %>
<%= link_to t("helpers.comments.clear_label"),"#", :class => "dmp_table_link archive_comment_button" %>
<% end%>
<%end%>
@@ -56,30 +60,30 @@
-<% notes_not_archived = notes.where("archived IS NULL") %>
-<% latest_note = notes_not_archived.order("updated_at DESC").first %>
+<% notes_not_archived = notes.select { |n| n["archived"].nil? } %>
+<% latest_note = notes_not_archived.sort { |x,y| y["updated_at"] <=> x["updated_at"] }.first %>
<% if !latest_note.nil? then%>
-
+ " class ="view_comment_class">
<%= render :partial => "view_note", locals: {note: latest_note} %>
<%end%>
-<%notes.order("updated_at DESC").each do |com|%>
+<%notes.each do |com|%>
- |