canvas-lms/app/models/wiki_page.rb

437 lines
16 KiB
Ruby
Raw Normal View History

2011-02-01 09:57:29 +08:00
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
class WikiPage < ActiveRecord::Base
attr_accessible :title, :body, :url, :user_id, :editing_roles, :notify_of_update
attr_readonly :wiki_id, :hide_from_students
2011-02-01 09:57:29 +08:00
validates_length_of :body, :maximum => maximum_long_text_length, :allow_nil => true, :allow_blank => true
validates_presence_of :wiki_id
include Workflow
include HasContentTags
include CopyAuthorizedLinks
allow using an item in modules more than once closes #8769 An item can be added to multiple modules, or even the same module more than once. This is especially useful for attachment items, but is also useful for allowing multiple paths through a course, with say an assignment in two different modules and the user only has to complete one of the two modules. test plan: For an item in only one module, verify that the module navigation still appears if you go straight to that item's page, without going through the modules page. Add an item to more than one module. If you visit that item from the modules page, you'll see the right nav depending on which instance of the item you clicked on. If you visit the item directly without going through the modules page, you'll see no nav. Lock one instance of the item by adding a prerequisite, but leave the other unlocked. You can still see the item as a student. Lock all instances of the item with prerequisites. The item will now be locked and you can't see it as a student. Add completion requirements to the item, such as a minimum score on a quiz. Make the requirements different -- 3 points in one instance and 5 in the other, for instance. Verify that if you get 3 points on the quiz, one item is marked as completed but the other isn't, as expected. Rename the item. Verify that all instances of it in modules get renamed. Change-Id: I4f1b2f6f033062ec47ac34fe5eb973a950c17b0c Reviewed-on: https://gerrit.instructure.com/11671 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
2012-06-19 06:18:43 +08:00
include ContextModuleItem
include SearchTermHelper
2011-02-01 09:57:29 +08:00
belongs_to :wiki, :touch => true
belongs_to :user
EXPORTABLE_ATTRIBUTES = [
:id, :wiki_id, :title, :body, :workflow_state, :recent_editors, :user_id, :created_at, :updated_at, :url, :delayed_post_at, :protected_editing, :hide_from_students,
:editing_roles, :view_count, :revised_at, :could_be_locked, :cloned_item_id, :wiki_page_comments_count
]
EXPORTABLE_ASSOCIATIONS = [:wiki, :user]
acts_as_url :title, :scope => [:wiki_id, :not_deleted], :sync_url => true
validate :validate_front_page_visibility
before_save :set_revised_at
2011-02-01 09:57:29 +08:00
before_validation :ensure_unique_title
after_save :touch_wiki_context
TITLE_LENGTH = WikiPage.columns_hash['title'].limit rescue 255
SIMPLY_VERSIONED_EXCLUDE_FIELDS = [:workflow_state, :hide_from_students, :editing_roles, :notify_of_update]
def touch_wiki_context
self.wiki.touch_context if self.wiki && self.wiki.context
end
def validate_front_page_visibility
if !published? && self.is_front_page?
self.errors.add(:hide_from_students, t(:cannot_hide_page, "cannot hide front page"))
end
end
2011-02-01 09:57:29 +08:00
def ensure_unique_title
return if deleted?
to_cased_title = ->(string) { string.gsub(/[^\w]+/, " ").gsub(/\b('?[a-z])/){$1.capitalize}.strip }
self.title ||= to_cased_title.call(self.url || "page")
return unless self.wiki
# TODO i18n (see wiki.rb)
2011-02-01 09:57:29 +08:00
if self.title == "Front Page" && self.new_record?
baddies = self.wiki.wiki_pages.not_deleted.find_all_by_title("Front Page").select{|p| p.url != "front-page" }
baddies.each{|p| p.title = to_cased_title.call(p.url); p.save_without_broadcasting! }
2011-02-01 09:57:29 +08:00
end
if existing = self.wiki.wiki_pages.not_deleted.find_by_title(self.title)
return if existing == self
real_title = self.title.gsub(/-(\d*)\z/, '') # remove any "-#" at the end
n = $1 ? $1.to_i + 1 : 2
begin
mod = "-#{n}"
new_title = real_title[0...(TITLE_LENGTH - mod.length)] + mod
n = n.succ
end while self.wiki.wiki_pages.not_deleted.find_by_title(new_title)
self.title = new_title
2011-02-01 09:57:29 +08:00
end
end
def normalize_hide_from_students
workflow_state = self.read_attribute('workflow_state')
hide_from_students = self.read_attribute('hide_from_students')
if !workflow_state.nil? && !hide_from_students.nil?
self.workflow_state = 'unpublished' if hide_from_students && workflow_state == 'active'
self.write_attribute('hide_from_students', nil)
remove hide_from_students from the new pages UI test plan: - start with draft state disabled - for each step, exercise the api (as a teacher) to confirm the expected values 1) create pages with different hide_from_students values a) as a teacher hidden pages should be visible; the hidden flag should persist b) as a student hidden pages should not be visible 2) using the api, update published and hide_from_students (together and separately) a) the published value should not be affected b) the hide_from_students value should be updated when set 3) enable draft state a) as a teacher previously hidden pages should be visible, but unpublished b) as a student previously hidden pages should not be visible 4) edit a page a) ensure the "Hide from students" option is gone 5) publishing pages should behave as expected a) published pages should be visible to students b) unpublished pages should not be visible to students 6) using the api, update published and hide_from_students (together and separately) a) the hide_from_students value should not be affected b) the published value should be updated when set 7) create new pages with different published states a) as a teacher unpublished pages should be visible, but unpublished b) as a student unpublished pages should not be visible 8) disable draft state a) as a teacher previously unpublished pages should be marked hidden b) as a student previously unpublished pages should not be visible fixes CNVS-7617 Change-Id: I395e0b2639543a64d9e2bc8d9377c78cf36f42d6 Reviewed-on: https://gerrit.instructure.com/23618 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: August Thornton <august@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
2013-08-23 04:04:59 +08:00
end
end
if CANVAS_RAILS2
alias_method :after_find, :normalize_hide_from_students
else
after_find :normalize_hide_from_students
end
private :normalize_hide_from_students
def hide_from_students
self.workflow_state == 'unpublished'
end
def hide_from_students=(v)
self.workflow_state = 'unpublished' if v && self.workflow_state == 'active'
self.workflow_state = 'active' if !v && self.workflow_state = 'unpublished'
hide_from_students
end
remove hide_from_students from the new pages UI test plan: - start with draft state disabled - for each step, exercise the api (as a teacher) to confirm the expected values 1) create pages with different hide_from_students values a) as a teacher hidden pages should be visible; the hidden flag should persist b) as a student hidden pages should not be visible 2) using the api, update published and hide_from_students (together and separately) a) the published value should not be affected b) the hide_from_students value should be updated when set 3) enable draft state a) as a teacher previously hidden pages should be visible, but unpublished b) as a student previously hidden pages should not be visible 4) edit a page a) ensure the "Hide from students" option is gone 5) publishing pages should behave as expected a) published pages should be visible to students b) unpublished pages should not be visible to students 6) using the api, update published and hide_from_students (together and separately) a) the hide_from_students value should not be affected b) the published value should be updated when set 7) create new pages with different published states a) as a teacher unpublished pages should be visible, but unpublished b) as a student unpublished pages should not be visible 8) disable draft state a) as a teacher previously unpublished pages should be marked hidden b) as a student previously unpublished pages should not be visible fixes CNVS-7617 Change-Id: I395e0b2639543a64d9e2bc8d9377c78cf36f42d6 Reviewed-on: https://gerrit.instructure.com/23618 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: August Thornton <august@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
2013-08-23 04:04:59 +08:00
def self.title_order_by_clause
best_unicode_collation_key('wiki_pages.title')
end
2011-02-01 09:57:29 +08:00
def ensure_unique_url
url_attribute = self.class.url_attribute
base_url = self.send(url_attribute)
base_url = self.send(self.class.attribute_to_urlify).to_s.to_url if base_url.blank? || !self.only_when_blank
conditions = [wildcard("#{url_attribute}", base_url, :type => :right)]
2011-02-01 09:57:29 +08:00
unless new_record?
conditions.first << " and id != ?"
conditions << id
end
# make stringex scoping a little more useful/flexible... in addition to
# the normal constructed attribute scope(s), it also supports paramater-
# less scopeds. note that there needs to be an instance_method of
# the same name for this to work
scopes = self.class.scope_for_url ? Array(self.class.scope_for_url) : []
base_scope = self.class
scopes.each do |scope|
next unless self.respond_to?(scope)
if base_scope.respond_to?(scope)
return unless send(scope)
base_scope = base_scope.send(scope)
else
conditions.first << " and #{connection.quote_column_name(scope)} = ?"
conditions << send(scope)
end
2011-02-01 09:57:29 +08:00
end
url_owners = base_scope.where(conditions).all
2011-02-01 09:57:29 +08:00
# This is the part in stringex that messed us up, since it will never allow
# a url of "front-page" once "front-page-1" or "front-page-2" is created
# We modify it to allow "front-page" and start the indexing at "front-page-2"
# instead of "front-page-1"
if url_owners.size > 0 && url_owners.detect{|u| u.send(url_attribute) == base_url}
n = 2
while url_owners.detect{|u| u.send(url_attribute) == "#{base_url}-#{n}"}
n = n.succ
end
write_attribute url_attribute, "#{base_url}-#{n}"
else
write_attribute url_attribute, base_url
end
end
sanitize_field :body, CanvasSanitize::SANITIZE
copy_authorized_links(:body) { [self.context, self.user] }
validates_each :title do |record, attr, value|
if value.blank?
record.errors.add(attr, t('errors.blank_title', "Title can't be blank"))
elsif value.size > maximum_string_length
record.errors.add(attr, t('errors.title_too_long', "Title can't exceed %{max_characters} characters", :max_characters => maximum_string_length))
elsif value.to_url.blank?
record.errors.add(attr, t('errors.title_characters', "Title must contain at least one letter or number")) # it's a bit more liberal than this, but let's not complicate things
end
end
2011-02-01 09:57:29 +08:00
has_a_broadcast_policy
simply_versioned :exclude => SIMPLY_VERSIONED_EXCLUDE_FIELDS, :when => Proc.new { |wp|
# :user_id and :updated_at do not merit creating a version, but should be saved
exclude_fields = [:user_id, :updated_at].concat(SIMPLY_VERSIONED_EXCLUDE_FIELDS).map(&:to_s)
(wp.changes.keys.map(&:to_s) - exclude_fields).present?
}
2011-02-01 09:57:29 +08:00
after_save :remove_changed_flag
2011-02-01 09:57:29 +08:00
workflow do
state :active do
event :unpublish, :transitions_to => :unpublished
end
state :unpublished do
event :publish, :transitions_to => :active
end
2011-02-01 09:57:29 +08:00
state :post_delayed do
event :delayed_post, :transitions_to => :active
end
state :deleted
end
alias_method :published?, :active?
2011-02-01 09:57:29 +08:00
def restore
self.workflow_state = context.feature_enabled?(:draft_state) ? 'unpublished' : 'active'
2011-02-01 09:57:29 +08:00
self.save
end
2011-02-01 09:57:29 +08:00
def set_revised_at
self.revised_at ||= Time.now
self.revised_at = Time.now if self.body_changed?
@page_changed = self.body_changed? || self.title_changed?
true
end
2011-02-01 09:57:29 +08:00
def notify_of_update=(val)
@wiki_page_changed = Canvas::Plugin.value_to_boolean(val)
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def notify_of_update
false
end
2011-02-01 09:57:29 +08:00
def remove_changed_flag
@wiki_page_changed = false
end
2011-02-01 09:57:29 +08:00
def version_history
self.versions.map(&:model)
end
scope :active, -> { where(:workflow_state => 'active') }
scope :deleted_last, -> { order("workflow_state='deleted'") }
scope :not_deleted, -> { where("wiki_pages.workflow_state<>'deleted'") }
scope :published, -> { where("wiki_pages.workflow_state='active' AND (wiki_pages.hide_from_students=? OR wiki_pages.hide_from_students IS NULL)", false) }
scope :unpublished, -> { where("wiki_pages.workflow_state='unpublished' OR (wiki_pages.hide_from_students=? AND wiki_pages.workflow_state<>'deleted')", true) }
2013-10-08 05:19:00 +08:00
remove hide_from_students from the new pages UI test plan: - start with draft state disabled - for each step, exercise the api (as a teacher) to confirm the expected values 1) create pages with different hide_from_students values a) as a teacher hidden pages should be visible; the hidden flag should persist b) as a student hidden pages should not be visible 2) using the api, update published and hide_from_students (together and separately) a) the published value should not be affected b) the hide_from_students value should be updated when set 3) enable draft state a) as a teacher previously hidden pages should be visible, but unpublished b) as a student previously hidden pages should not be visible 4) edit a page a) ensure the "Hide from students" option is gone 5) publishing pages should behave as expected a) published pages should be visible to students b) unpublished pages should not be visible to students 6) using the api, update published and hide_from_students (together and separately) a) the hide_from_students value should not be affected b) the published value should be updated when set 7) create new pages with different published states a) as a teacher unpublished pages should be visible, but unpublished b) as a student unpublished pages should not be visible 8) disable draft state a) as a teacher previously unpublished pages should be marked hidden b) as a student previously unpublished pages should not be visible fixes CNVS-7617 Change-Id: I395e0b2639543a64d9e2bc8d9377c78cf36f42d6 Reviewed-on: https://gerrit.instructure.com/23618 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: August Thornton <august@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
2013-08-23 04:04:59 +08:00
# needed for ensure_unique_url
def not_deleted
!deleted?
end
scope :order_by_id, -> { order(:id) }
def locked_for?(user, opts={})
2011-02-01 09:57:29 +08:00
return false unless self.could_be_locked
allow using an item in modules more than once closes #8769 An item can be added to multiple modules, or even the same module more than once. This is especially useful for attachment items, but is also useful for allowing multiple paths through a course, with say an assignment in two different modules and the user only has to complete one of the two modules. test plan: For an item in only one module, verify that the module navigation still appears if you go straight to that item's page, without going through the modules page. Add an item to more than one module. If you visit that item from the modules page, you'll see the right nav depending on which instance of the item you clicked on. If you visit the item directly without going through the modules page, you'll see no nav. Lock one instance of the item by adding a prerequisite, but leave the other unlocked. You can still see the item as a student. Lock all instances of the item with prerequisites. The item will now be locked and you can't see it as a student. Add completion requirements to the item, such as a minimum score on a quiz. Make the requirements different -- 3 points in one instance and 5 in the other, for instance. Verify that if you get 3 points on the quiz, one item is marked as completed but the other isn't, as expected. Rename the item. Verify that all instances of it in modules get renamed. Change-Id: I4f1b2f6f033062ec47ac34fe5eb973a950c17b0c Reviewed-on: https://gerrit.instructure.com/11671 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
2012-06-19 06:18:43 +08:00
Rails.cache.fetch(locked_cache_key(user), :expires_in => 1.minute) do
2011-02-01 09:57:29 +08:00
locked = false
if item = locked_by_module_item?(user, opts[:deep_check_if_needed])
locked = {:asset_string => self.asset_string, :context_module => item.context_module.attributes}
locked[:unlock_at] = locked[:context_module]["unlock_at"] if locked[:context_module]["unlock_at"]
2011-02-01 09:57:29 +08:00
end
locked
end
end
def is_front_page?
return false if self.deleted?
self.url == self.wiki.get_front_page_url # wiki.get_front_page_url checks has_front_page? and context.feature_enabled?(:draft_state)
end
def set_as_front_page!
can_set_front_page = true
if self.unpublished?
self.errors.add(:front_page, t(:cannot_set_unpublished_front_page, 'could not set as front page because it is unpublished'))
can_set_front_page = false
end
if self.hide_from_students
self.errors.add(:front_page, t(:cannot_set_hidden_front_page, 'could not set as front page because it is hidden'))
can_set_front_page = false
end
return false unless can_set_front_page
self.wiki.set_front_page_url!(self.url)
end
def context_module_tag_for(context)
@tag ||= self.context_module_tags.where(context_id: context, context_type: context.class.base_ar_class.name).first
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def context_module_action(user, context, action)
self.context_module_tags.where(context_id: context, context_type: context.class.base_ar_class.name).each do |tag|
tag.context_module_action(user, action)
end
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
set_policy do
given {|user, session| self.can_read_page?(user, session)}
can :read
given {|user| self.can_edit_page?(user)}
can :read
2011-02-01 09:57:29 +08:00
given {|user| user && self.can_edit_page?(user)}
page revisions api test plan: 1. consult the Pages API documentation, and notice (a) the new "List revisions" endpoint and the PageRevision data type it returns (b) the "Show revision" endpoint (which can accept an id or "latest") (c) the "Revert to revision" endpoint 2. exercise these endpoints as a teacher (a) note that reverting a page should not change editing roles, hidden, or published state 3. exercise these endpoints as a student (a) students should not be able to list, or retrieve prior revisions unless they have edit rights to the page (b) all students (who can read the page) have permission to get the latest version (but edit rights are required to get a specific revision number) (c) for the revert action, the course permissions must allow students to edit wiki pages; if the course does not grant wiki edit rights to the student, but the page does, the student can change page content only (cannot revert or rename) (d) for the show revision action, the permissions of the current version of the page are applied; students with edit rights to it can view or revert to previous versions that may have been hidden or unpublished (in other words, "hidden" and "unpublished" apply only to the current state of the page, not historical versions of it) fixes CNVS-7301 Change-Id: Ie4948617e24154a61f3111e08fc3f89b9265da6d Reviewed-on: https://gerrit.instructure.com/23088 Reviewed-by: Mark Severson <markse@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Hannah Bottalla <hannah@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
2013-08-07 03:43:48 +08:00
can :update_content and can :read_revisions
change new pages to show/hide elements correctly test plan (in the course and group wikis): * enable draft state for the account (enable_draft) - general * Pages course tab navigates to new pages - front page - index page (if no front page is set) * Pages breadcrumb (in pages) navigates to new pages index - index page * New Page button is hidden unless user has create rights * Publish icon & settings cog hidden unless user is a teacher or admin - show page * Published button is hidden unless user is a teacher or admin * Published button functions properly (publishes and unpublishes) * Edit button is hidden unless user has edit rights to the page * Settings cog/Delete button is hidden unless the user has delete rights to the page * Page content is restricted if the page is locked by a module - edit page * Published indicator is hidden unless user is a teacher or admin * Settings cog/Delete button is hidden unless the user has delete rights to the page * Title field is hidden unless user has full edit rights (course setting) * Hide this page from students is hidden unless user is a teacher or admin * ... can edit this page is hidden unless user has full edit rights (course setting) * Unauthorized error if page is locked by a module - pages api * Unauthorized error when updating page if page is locked by a module fixes #CNVS-6859 Change-Id: I12239e58a5f267c43fd2bcb912c7b485693de2c1 Reviewed-on: https://gerrit.instructure.com/22677 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: August Thornton <august@instructure.com> Reviewed-by: Sterling Cobb <sterling@instructure.com> Product-Review: Matt Goodwin <mattg@instructure.com>
2013-07-26 06:14:15 +08:00
given {|user, session| user && self.can_edit_page?(user) && self.wiki.grants_right?(user, session, :create_page)}
can :create
change new pages to show/hide elements correctly test plan (in the course and group wikis): * enable draft state for the account (enable_draft) - general * Pages course tab navigates to new pages - front page - index page (if no front page is set) * Pages breadcrumb (in pages) navigates to new pages index - index page * New Page button is hidden unless user has create rights * Publish icon & settings cog hidden unless user is a teacher or admin - show page * Published button is hidden unless user is a teacher or admin * Published button functions properly (publishes and unpublishes) * Edit button is hidden unless user has edit rights to the page * Settings cog/Delete button is hidden unless the user has delete rights to the page * Page content is restricted if the page is locked by a module - edit page * Published indicator is hidden unless user is a teacher or admin * Settings cog/Delete button is hidden unless the user has delete rights to the page * Title field is hidden unless user has full edit rights (course setting) * Hide this page from students is hidden unless user is a teacher or admin * ... can edit this page is hidden unless user has full edit rights (course setting) * Unauthorized error if page is locked by a module - pages api * Unauthorized error when updating page if page is locked by a module fixes #CNVS-6859 Change-Id: I12239e58a5f267c43fd2bcb912c7b485693de2c1 Reviewed-on: https://gerrit.instructure.com/22677 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: August Thornton <august@instructure.com> Reviewed-by: Sterling Cobb <sterling@instructure.com> Product-Review: Matt Goodwin <mattg@instructure.com>
2013-07-26 06:14:15 +08:00
given {|user, session| user && self.can_edit_page?(user) && self.wiki.grants_right?(user, session, :update_page)}
page revisions api test plan: 1. consult the Pages API documentation, and notice (a) the new "List revisions" endpoint and the PageRevision data type it returns (b) the "Show revision" endpoint (which can accept an id or "latest") (c) the "Revert to revision" endpoint 2. exercise these endpoints as a teacher (a) note that reverting a page should not change editing roles, hidden, or published state 3. exercise these endpoints as a student (a) students should not be able to list, or retrieve prior revisions unless they have edit rights to the page (b) all students (who can read the page) have permission to get the latest version (but edit rights are required to get a specific revision number) (c) for the revert action, the course permissions must allow students to edit wiki pages; if the course does not grant wiki edit rights to the student, but the page does, the student can change page content only (cannot revert or rename) (d) for the show revision action, the permissions of the current version of the page are applied; students with edit rights to it can view or revert to previous versions that may have been hidden or unpublished (in other words, "hidden" and "unpublished" apply only to the current state of the page, not historical versions of it) fixes CNVS-7301 Change-Id: Ie4948617e24154a61f3111e08fc3f89b9265da6d Reviewed-on: https://gerrit.instructure.com/23088 Reviewed-by: Mark Severson <markse@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Hannah Bottalla <hannah@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
2013-08-07 03:43:48 +08:00
can :update and can :read_revisions
given {|user, session| user && self.can_edit_page?(user) && self.published? && self.wiki.grants_right?(user, session, :update_page_content)}
page revisions api test plan: 1. consult the Pages API documentation, and notice (a) the new "List revisions" endpoint and the PageRevision data type it returns (b) the "Show revision" endpoint (which can accept an id or "latest") (c) the "Revert to revision" endpoint 2. exercise these endpoints as a teacher (a) note that reverting a page should not change editing roles, hidden, or published state 3. exercise these endpoints as a student (a) students should not be able to list, or retrieve prior revisions unless they have edit rights to the page (b) all students (who can read the page) have permission to get the latest version (but edit rights are required to get a specific revision number) (c) for the revert action, the course permissions must allow students to edit wiki pages; if the course does not grant wiki edit rights to the student, but the page does, the student can change page content only (cannot revert or rename) (d) for the show revision action, the permissions of the current version of the page are applied; students with edit rights to it can view or revert to previous versions that may have been hidden or unpublished (in other words, "hidden" and "unpublished" apply only to the current state of the page, not historical versions of it) fixes CNVS-7301 Change-Id: Ie4948617e24154a61f3111e08fc3f89b9265da6d Reviewed-on: https://gerrit.instructure.com/23088 Reviewed-by: Mark Severson <markse@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Hannah Bottalla <hannah@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
2013-08-07 03:43:48 +08:00
can :update_content and can :read_revisions
given {|user, session| user && self.can_edit_page?(user) && self.published? && self.wiki.grants_right?(user, session, :delete_page)}
can :delete
given {|user, session| user && self.can_edit_page?(user) && self.unpublished? && self.wiki.grants_right?(user, session, :delete_unpublished_page)}
can :delete
2011-02-01 09:57:29 +08:00
end
def can_read_page?(user, session=nil)
return true if self.wiki.grants_right?(user, session, :manage)
return true if self.unpublished? && self.wiki.grants_right?(user, session, :view_unpublished_items)
self.published? && self.wiki.grants_right?(user, session, :read)
end
change new pages to show/hide elements correctly test plan (in the course and group wikis): * enable draft state for the account (enable_draft) - general * Pages course tab navigates to new pages - front page - index page (if no front page is set) * Pages breadcrumb (in pages) navigates to new pages index - index page * New Page button is hidden unless user has create rights * Publish icon & settings cog hidden unless user is a teacher or admin - show page * Published button is hidden unless user is a teacher or admin * Published button functions properly (publishes and unpublishes) * Edit button is hidden unless user has edit rights to the page * Settings cog/Delete button is hidden unless the user has delete rights to the page * Page content is restricted if the page is locked by a module - edit page * Published indicator is hidden unless user is a teacher or admin * Settings cog/Delete button is hidden unless the user has delete rights to the page * Title field is hidden unless user has full edit rights (course setting) * Hide this page from students is hidden unless user is a teacher or admin * ... can edit this page is hidden unless user has full edit rights (course setting) * Unauthorized error if page is locked by a module - pages api * Unauthorized error when updating page if page is locked by a module fixes #CNVS-6859 Change-Id: I12239e58a5f267c43fd2bcb912c7b485693de2c1 Reviewed-on: https://gerrit.instructure.com/22677 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: August Thornton <august@instructure.com> Reviewed-by: Sterling Cobb <sterling@instructure.com> Product-Review: Matt Goodwin <mattg@instructure.com>
2013-07-26 06:14:15 +08:00
def can_edit_page?(user, session=nil)
# wiki managers are always allowed to edit
return true if wiki.grants_right?(user, session, :manage)
roles = effective_roles
# teachers implies all course admins (teachers, TAs, etc)
return true if roles.include?('teachers') && context.respond_to?(:admins) && context.admins.include?(user)
# the page must be available for users of the following roles
return false unless available_for?(user, session)
return true if roles.include?('students') && context.respond_to?(:students) && context.includes_student?(user)
return true if roles.include?('members') && context.respond_to?(:users) && context.users.include?(user)
return true if roles.include?('public')
2011-02-01 09:57:29 +08:00
false
end
def effective_roles
context_roles = context.default_wiki_editing_roles rescue nil
roles = (editing_roles || context_roles || default_roles).split(',')
roles == %w(teachers) ? [] : roles # "Only teachers" option doesn't grant rights excluded by RoleOverrides
end
def available_for?(user, session=nil)
return true if wiki.grants_right?(user, session, :manage)
return false unless published? || (unpublished? && wiki.grants_right?(user, session, :view_unpublished_items))
return false if locked_for?(user)
true
end
2011-02-01 09:57:29 +08:00
def default_roles
if context.is_a?(Group)
2011-02-01 09:57:29 +08:00
'members'
elsif context.is_a?(Course)
2011-02-01 09:57:29 +08:00
'teachers'
else
'members'
end
end
2011-02-01 09:57:29 +08:00
set_broadcast_policy do |p|
p.dispatch :updated_wiki_page
p.to { participants }
p.whenever do |record|
return false unless record.created_at < Time.now - 30.minutes
(record.published? && @wiki_page_changed && record.prior_version) || record.changed_state(:active)
end
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def context(user=nil)
shard.activate do
@context ||= Course.find_by_wiki_id(self.wiki_id) || Group.find_by_wiki_id(self.wiki_id)
end
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def participants
res = []
if context && context.available?
if !self.active?
res += context.participating_admins
else
res += context.participants
2011-02-01 09:57:29 +08:00
end
end
res.flatten.uniq
end
2011-02-01 09:57:29 +08:00
def to_atom(opts={})
context = opts[:context]
Atom::Entry.new do |entry|
entry.title = t(:atom_entry_title, "Wiki Page, %{course_or_group_name}: %{page_title}", :course_or_group_name => context.name, :page_title => self.title)
entry.authors << Atom::Person.new(:name => t(:atom_author, "Wiki Page"))
2011-02-01 09:57:29 +08:00
entry.updated = self.updated_at
entry.published = self.created_at
entry.id = "tag:#{HostUrl.default_host},#{self.created_at.strftime("%Y-%m-%d")}:/wiki_pages/#{self.feed_code}_#{self.updated_at.strftime("%Y-%m-%d")}"
entry.links << Atom::Link.new(:rel => 'alternate',
:href => "http://#{HostUrl.context_host(context)}/#{self.context.class.to_s.downcase.pluralize}/#{self.context.id}/wiki/#{self.url}")
entry.content = Atom::Content::Html.new(self.body || t('defaults.no_content', "no content"))
2011-02-01 09:57:29 +08:00
end
end
2011-02-01 09:57:29 +08:00
def user_name
(user && user.name) || t('unknown_user_name', "Unknown")
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def to_param
url
end
2011-02-01 09:57:29 +08:00
def last_revision_at
res = self.revised_at || self.updated_at
res = Time.now if res.is_a?(String)
res
end
def increment_view_count(user, context = nil)
unless self.new_record?
self.with_versioning(false) do |p|
context ||= p.context
WikiPage.where(id: p).update_all("view_count=COALESCE(view_count, 0) + 1")
p.context_module_action(user, context, :read)
end
end
end
allow PUT requests to create wiki pages implicitly testing note: this change affects all wiki page endpoints: api, draft state, and non-draft state. basic functionality for all of these pages should be verified, specifically when dealing with non-existent or deleted pages. test plan: * PUT to /api/v1/courses/:course_id/front_page - variations: * wiki_page[title] - provided/not provided * wiki_page[published] - true/false/not provided * wiki_page[hide_from_students] - true/false/not provided (hiding/unpublishing the front page is not allowed) * PUT to /api/v1/courses/:course_id/pages/non-existent-page - same variations as front_page (above) * navigating to a non-existent/deleted page (draft state/non-draft state) - should redirect teachers to the edit page - should redirect students to the index page (with an alert message indicating why) * navigating to the 'Pages' tab (non-draft state) - if the 'Front Page' exists - shows the page - if the 'Front Page' has been deleted (with draft state enabled) - shows the edit page - if the 'Front Page' has never existed - shows the edit page * all other pages functionality should remain unchanged - non-draft state UI - show page - edit page - draft state UI - index page - show page - edit page - api - .../pages - GET (index) - POST (create page) - .../front_page - GET (show front page) - PUT (create/update front page) - .../pages/page-url - GET (show page) - PUT (create/update page) - DELETE (destroy page) fixes CNVS-8488 Change-Id: I563e6944e1602e0b21ab69c6fe2dcd643c06611d Reviewed-on: https://gerrit.instructure.com/23590 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Matt Fairbourn <mfairbourn@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
2013-08-22 23:43:03 +08:00
def can_unpublish?
!is_front_page?
end
allow PUT requests to create wiki pages implicitly testing note: this change affects all wiki page endpoints: api, draft state, and non-draft state. basic functionality for all of these pages should be verified, specifically when dealing with non-existent or deleted pages. test plan: * PUT to /api/v1/courses/:course_id/front_page - variations: * wiki_page[title] - provided/not provided * wiki_page[published] - true/false/not provided * wiki_page[hide_from_students] - true/false/not provided (hiding/unpublishing the front page is not allowed) * PUT to /api/v1/courses/:course_id/pages/non-existent-page - same variations as front_page (above) * navigating to a non-existent/deleted page (draft state/non-draft state) - should redirect teachers to the edit page - should redirect students to the index page (with an alert message indicating why) * navigating to the 'Pages' tab (non-draft state) - if the 'Front Page' exists - shows the page - if the 'Front Page' has been deleted (with draft state enabled) - shows the edit page - if the 'Front Page' has never existed - shows the edit page * all other pages functionality should remain unchanged - non-draft state UI - show page - edit page - draft state UI - index page - show page - edit page - api - .../pages - GET (index) - POST (create page) - .../front_page - GET (show front page) - PUT (create/update front page) - .../pages/page-url - GET (show page) - PUT (create/update page) - DELETE (destroy page) fixes CNVS-8488 Change-Id: I563e6944e1602e0b21ab69c6fe2dcd643c06611d Reviewed-on: https://gerrit.instructure.com/23590 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Matt Fairbourn <mfairbourn@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
2013-08-22 23:43:03 +08:00
def initialize_wiki_page(user)
is_privileged_user = wiki.grants_right?(user, :manage)
if is_privileged_user && context.feature_enabled?(:draft_state) && !context.is_a?(Group)
allow PUT requests to create wiki pages implicitly testing note: this change affects all wiki page endpoints: api, draft state, and non-draft state. basic functionality for all of these pages should be verified, specifically when dealing with non-existent or deleted pages. test plan: * PUT to /api/v1/courses/:course_id/front_page - variations: * wiki_page[title] - provided/not provided * wiki_page[published] - true/false/not provided * wiki_page[hide_from_students] - true/false/not provided (hiding/unpublishing the front page is not allowed) * PUT to /api/v1/courses/:course_id/pages/non-existent-page - same variations as front_page (above) * navigating to a non-existent/deleted page (draft state/non-draft state) - should redirect teachers to the edit page - should redirect students to the index page (with an alert message indicating why) * navigating to the 'Pages' tab (non-draft state) - if the 'Front Page' exists - shows the page - if the 'Front Page' has been deleted (with draft state enabled) - shows the edit page - if the 'Front Page' has never existed - shows the edit page * all other pages functionality should remain unchanged - non-draft state UI - show page - edit page - draft state UI - index page - show page - edit page - api - .../pages - GET (index) - POST (create page) - .../front_page - GET (show front page) - PUT (create/update front page) - .../pages/page-url - GET (show page) - PUT (create/update page) - DELETE (destroy page) fixes CNVS-8488 Change-Id: I563e6944e1602e0b21ab69c6fe2dcd643c06611d Reviewed-on: https://gerrit.instructure.com/23590 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Matt Fairbourn <mfairbourn@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
2013-08-22 23:43:03 +08:00
self.workflow_state = 'unpublished'
else
self.workflow_state = 'active'
end
self.editing_roles = (context.default_wiki_editing_roles rescue nil) || default_roles
if is_front_page?
self.body = t "#application.wiki_front_page_default_content_course", "Welcome to your new course wiki!" if context.is_a?(Course)
self.body = t "#application.wiki_front_page_default_content_group", "Welcome to your new group wiki!" if context.is_a?(Group)
self.workflow_state = 'active'
allow PUT requests to create wiki pages implicitly testing note: this change affects all wiki page endpoints: api, draft state, and non-draft state. basic functionality for all of these pages should be verified, specifically when dealing with non-existent or deleted pages. test plan: * PUT to /api/v1/courses/:course_id/front_page - variations: * wiki_page[title] - provided/not provided * wiki_page[published] - true/false/not provided * wiki_page[hide_from_students] - true/false/not provided (hiding/unpublishing the front page is not allowed) * PUT to /api/v1/courses/:course_id/pages/non-existent-page - same variations as front_page (above) * navigating to a non-existent/deleted page (draft state/non-draft state) - should redirect teachers to the edit page - should redirect students to the index page (with an alert message indicating why) * navigating to the 'Pages' tab (non-draft state) - if the 'Front Page' exists - shows the page - if the 'Front Page' has been deleted (with draft state enabled) - shows the edit page - if the 'Front Page' has never existed - shows the edit page * all other pages functionality should remain unchanged - non-draft state UI - show page - edit page - draft state UI - index page - show page - edit page - api - .../pages - GET (index) - POST (create page) - .../front_page - GET (show front page) - PUT (create/update front page) - .../pages/page-url - GET (show page) - PUT (create/update page) - DELETE (destroy page) fixes CNVS-8488 Change-Id: I563e6944e1602e0b21ab69c6fe2dcd643c06611d Reviewed-on: https://gerrit.instructure.com/23590 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Matt Fairbourn <mfairbourn@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
2013-08-22 23:43:03 +08:00
end
end
2011-02-01 09:57:29 +08:00
end