canvas-lms/app/models/wiki_page.rb

560 lines
22 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, :hide_from_students, :editing_roles, :notify_of_update
attr_readonly :wiki_id
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 :cloned_item
belongs_to :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
TITLE_LENGTH = WikiPage.columns_hash['title'].limit rescue 255
SIMPLY_VERSIONED_EXCLUDE_FIELDS = [:workflow_state, :hide_from_students, :editing_roles, :notify_of_update]
def validate_front_page_visibility
if self.hide_from_students && 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?
2011-02-01 09:57:29 +08:00
self.title ||= (self.url || "page").to_cased_title
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" }
2011-02-01 09:57:29 +08:00
baddies.each{|p| p.title = p.url.to_cased_title; p.save_without_broadcasting! }
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 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, Instructure::SanitizeField::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
2011-02-01 09:57:29 +08:00
state :deleted
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def restore
self.workflow_state = 'active'
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'")
def not_deleted
!deleted?
end
scope :visible_to_students, where(:hide_from_students => false)
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
context = opts[:context]
context ||= self.context if self.respond_to?(:context)
m = context_module_tag_for(context).context_module rescue nil
2011-02-01 09:57:29 +08:00
locked = false
if (m && !m.available_for?(user))
locked = {:asset_string => self.asset_string, :context_module => m.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?
!self.deleted? && self.wiki.has_front_page? && self.url == self.wiki.get_front_page_url
end
def set_as_front_page!
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"))
return false
end
self.wiki.set_front_page_url!(self.url)
end
def context_module_tag_for(context)
@tag ||= self.context_module_tags.find_by_context_id_and_context_type(context.id, context.class.to_s)
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def context_module_action(user, context, action)
tag = self.context_module_tags.find_by_context_id_and_context_type(context.id, context.class.to_s)
tag.context_module_action(user, action) if tag
end
2011-02-01 09:57:29 +08:00
set_policy do
given {|user, session| self.wiki.grants_right?(user, session, :read) && can_read_page?(user, session)}
can :read
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| self.can_edit_page?(user)}
can :read
2011-02-01 09:57:29 +08:00
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)}
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
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.active? && 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
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.active? && self.wiki.grants_right?(user, session, :delete_page)}
can :delete
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.workflow_state == '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)
self.wiki.grants_right?(user, session, :manage) || (!hide_from_students && self.active?)
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)
2011-02-01 09:57:29 +08:00
context_roles = context.default_wiki_editing_roles rescue nil
roles = (editing_roles || context_roles || default_roles).split(",")
# managers are always allowed to edit
return true if wiki.grants_right?(user, session, :manage)
2011-02-01 09:57:29 +08:00
return true if roles.include?('teachers') && context.respond_to?(:teachers) && context.teachers.include?(user)
# the remaining edit roles all require read access, so just check here
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
return false unless can_read_page?(user, session) && !self.locked_for?(user)
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
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 { |record|
record.created_at < Time.now - (30*60) &&
((
record.active? && @wiki_page_changed && record.prior_version
) ||
(
record.changed_state(:active)
))
}
end
2011-02-01 09:57:29 +08:00
def context(user=nil)
@context ||= Course.find_by_wiki_id(self.wiki_id) || Group.find_by_wiki_id(self.wiki_id)
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.hide_from_students
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
2011-02-01 09:57:29 +08:00
attr_accessor :clone_updated
def clone_for(context, dup=nil, options={}) #migrate=true)
options[:migrate] = true if options[:migrate] == nil
if !self.cloned_item && !self.new_record?
self.cloned_item ||= ClonedItem.create(:original_item => self)
self.save!
end
existing = context.wiki.wiki_pages.active.find_by_id(self.id)
existing ||= context.wiki.wiki_pages.active.find_by_cloned_item_id(self.cloned_item_id || 0)
return existing if existing && !options[:overwrite]
dup ||= WikiPage.new
dup = existing if existing && options[:overwrite]
self.attributes.delete_if{|k,v| [:id, :wiki_id].include?(k.to_sym) }.each do |key, val|
dup.send("#{key}=", val)
end
dup.wiki = context.wiki
dup.body = context.migrate_content_links(self.body, options[:old_context] || self.context) if options[:migrate]
context.log_merge_result(t('notices.wiki_page_created', 'Wiki Page "%{title}" created', :title => dup.title))
2011-02-01 09:57:29 +08:00
context.may_have_links_to_migrate(dup)
dup.updated_at = Time.now
dup.clone_updated = true
dup
end
def self.process_migration_course_outline(data, migration)
outline = data['course_outline'] ? data['course_outline']: nil
return unless outline
to_import = migration.to_import 'outline_folders'
outline['root_folder'] = true
begin
import_from_migration(outline.merge({:outline_folders_to_import => to_import}), migration.context)
rescue
migration.add_warning("Error importing the course outline.", $!)
end
2011-02-01 09:57:29 +08:00
end
def self.process_migration(data, migration)
wikis = data['wikis'] ? data['wikis']: []
wikis.each do |wiki|
if !wiki
ErrorReport.log_error(:content_migration, :message => "There was a nil wiki page imported for ContentMigration:#{migration.id}")
next
end
next unless migration.import_object?("wiki_pages", wiki['migration_id']) || migration.import_object?("wikis", wiki['migration_id'])
begin
import_from_migration(wiki, migration.context) if wiki
rescue
migration.add_import_warning(t('#migration.wiki_page_type', "Wiki Page"), wiki[:title], $!)
end
2011-02-01 09:57:29 +08:00
end
end
2011-02-01 09:57:29 +08:00
def self.import_from_migration(hash, context, item=nil)
hash = hash.with_indifferent_access
item ||= find_by_wiki_id_and_id(context.wiki.id, hash[:id])
item ||= find_by_wiki_id_and_migration_id(context.wiki.id, hash[:migration_id])
2011-02-01 09:57:29 +08:00
item ||= context.wiki.wiki_pages.new
# force the url to be the same as the url_name given, since there are
# likely other resources in the import that link to that url
if hash[:url_name].present?
item.url = hash[:url_name]
item.only_when_blank = true
end
if hash[:root_folder] && ['folder', 'FOLDER_TYPE'].member?(hash[:type])
front_page = context.wiki.front_page
if front_page.id
hash[:root_folder] = false
else
# If there is no id there isn't a front page yet
item = front_page
end
end
if state = hash[:workflow_state]
if state == 'active'
item.workflow_state = 'active'
else
item.workflow_state = 'unpublished'
end
end
2011-02-01 09:57:29 +08:00
context.imported_migration_items << item if context.imported_migration_items && item.new_record?
item.migration_id = hash[:migration_id]
(hash[:contents] || []).each do |sub_item|
next if sub_item[:type] == 'embedded_content'
WikiPage.import_from_migration(sub_item.merge({
:outline_folders_to_import => hash[:outline_folders_to_import]
}), context)
end
return if hash[:type] && ['folder', 'FOLDER_TYPE'].member?(hash[:type]) && hash[:linked_resource_id]
hash[:missing_links] = {}
2011-02-01 09:57:29 +08:00
allow_save = true
if hash[:type] == 'linked_resource' || hash[:type] == "URL_TYPE"
2011-02-01 09:57:29 +08:00
allow_save = false
elsif ['folder', 'FOLDER_TYPE'].member? hash[:type]
item.title = hash[:title] unless hash[:root_folder]
description = ""
if hash[:header]
hash[:missing_links][:field] = []
description += hash[:header][:is_html] ? ImportedHtmlConverter.convert(hash[:header][:body] || "", context, {:missing_links => hash[:missing_links][:header]}) : ImportedHtmlConverter.convert_text(hash[:header][:body] || [""], context)
2011-02-01 09:57:29 +08:00
end
hash[:missing_links][:description] = []
description += ImportedHtmlConverter.convert(hash[:description], context, {:missing_links => hash[:missing_links][:description]}) if hash[:description]
2011-02-01 09:57:29 +08:00
contents = ""
allow_save = false if hash[:migration_id] && hash[:outline_folders_to_import] && !hash[:outline_folders_to_import][hash[:migration_id]]
hash[:contents].each do |sub_item|
sub_item = sub_item.with_indifferent_access
if ['folder', 'FOLDER_TYPE'].member? sub_item[:type]
obj = context.wiki.wiki_pages.find_by_migration_id(sub_item[:migration_id])
contents += " <li><a href='/courses/#{context.id}/wiki/#{obj.url}'>#{obj.title}</a></li>\n" if obj
elsif sub_item[:type] == 'embedded_content'
if contents && contents.length > 0
description += "<ul>\n#{contents}\n</ul>"
contents = ""
end
description += "\n<h2>#{sub_item[:title]}</h2>\n" if sub_item[:title]
hash[:missing_links][:sub_item] = []
description += ImportedHtmlConverter.convert(sub_item[:description], context, {:missing_links => hash[:missing_links][:sub_item]}) if sub_item[:description]
2011-02-01 09:57:29 +08:00
elsif sub_item[:type] == 'linked_resource'
case sub_item[:linked_resource_type]
when 'TOC_TYPE'
obj = context.context_modules.not_deleted.find_by_migration_id(sub_item[:linked_resource_id])
2011-02-01 09:57:29 +08:00
contents += " <li><a href='/courses/#{context.id}/modules'>#{obj.name}</a></li>\n" if obj
when 'ASSESSMENT_TYPE'
obj = context.quizzes.find_by_migration_id(sub_item[:linked_resource_id])
contents += " <li><a href='/courses/#{context.id}/quizzes/#{obj.id}'>#{obj.title}</a></li>\n" if obj
when /PAGE_TYPE|WIKI_TYPE/
obj = context.wiki.wiki_pages.find_by_migration_id(sub_item[:linked_resource_id])
contents += " <li><a href='/courses/#{context.id}/wiki/#{obj.url}'>#{obj.title}</a></li>\n" if obj
when 'FILE_TYPE'
file = context.attachments.find_by_migration_id(sub_item[:linked_resource_id])
if file
name = sub_item[:linked_resource_title] || file.name
contents += " <li><a href=\"/courses/#{context.id}/files/#{file.id}/download\">#{name}</a></li>"
end
when 'DISCUSSION_TOPIC_TYPE'
obj = context.discussion_topics.find_by_migration_id(sub_item[:linked_resource_id])
contents += " <li><a href='/courses/#{context.id}/discussion_topics/#{obj.id}'>#{obj.title}</a></li>\n" if obj
when 'URL_TYPE'
if sub_item['title'] && sub_item['description'] && sub_item['title'] != '' && sub_item['description'] != ''
contents += " <li><a href='#{sub_item['url']}'>#{sub_item['title']}</a><ul><li>#{sub_item['description']}</li></ul></li>\n"
else
contents += " <li><a href='#{sub_item['url']}'>#{sub_item['title'] || sub_item['description']}</a></li>\n"
end
2011-02-01 09:57:29 +08:00
end
end
end
description += "<ul>\n#{contents}\n</ul>" if contents && contents.length > 0
if hash[:footer]
hash[:missing_links][:footer] = []
description += hash[:footer][:is_html] ? ImportedHtmlConverter.convert(hash[:footer][:body] || "", context, {:missing_links => hash[:missing_links][:footer]}) : ImportedHtmlConverter.convert_text(hash[:footer][:body] || [""], context)
2011-02-01 09:57:29 +08:00
end
item.body = description
allow_save = false if !description || description.empty?
elsif hash[:page_type] == 'module_toc'
elsif hash[:topics]
item.title = t('title_for_topics_category', '%{category} Topics', :category => hash[:category_name])
2011-02-01 09:57:29 +08:00
description = "#{hash[:category_description]}"
description += "\n\n<ul>\n"
topic_count = 0
hash[:topics].each do |topic|
topic = DiscussionTopic.import_from_migration(topic.merge({
:topics_to_import => hash[:topics_to_import],
:topic_entries_to_import => hash[:topic_entries_to_import]
}), context)
if topic
topic_count += 1
description += " <li><a href='/#{context.class.to_s.downcase.pluralize}/#{context.id}/discussion_topics/#{topic.id}'>#{topic.title}</a></li>\n"
end
end
description += "</ul>"
item.body = description
return nil if topic_count == 0
elsif hash[:title] and hash[:text]
#it's an actual wiki page
item.title = hash[:title].presence || item.url.presence || "unnamed page"
if item.title.length > TITLE_LENGTH
if context.respond_to?(:content_migration) && context.content_migration
context.content_migration.add_warning(t('warnings.truncated_wiki_title', "The title of the following wiki page was truncated: %{title}", :title => item.title))
end
item.title.splice!(0...TITLE_LENGTH) # truncate too-long titles
end
hash[:missing_links][:body] = []
item.body = ImportedHtmlConverter.convert(hash[:text] || "", context, {:missing_links => hash[:missing_links][:body]})
item.editing_roles = hash[:editing_roles] if hash[:editing_roles].present?
item.hide_from_students = hash[:hide_from_students] if !hash[:hide_from_students].nil?
item.notify_of_update = hash[:notify_of_update] if !hash[:notify_of_update].nil?
2011-02-01 09:57:29 +08:00
else
allow_save = false
2011-02-01 09:57:29 +08:00
end
if allow_save && hash[:migration_id]
item.save_without_broadcasting!
context.imported_migration_items << item if context.imported_migration_items
if context.respond_to?(:content_migration) && context.content_migration
hash[:missing_links].each do |field, missing_links|
context.content_migration.add_missing_content_links(:class => item.class.to_s,
:id => item.id, :field => field, :missing_links => missing_links,
:url => "/#{context.class.to_s.underscore.pluralize}/#{context.id}/wiki/#{item.url}")
end
end
2011-02-01 09:57:29 +08:00
return item
end
end
def increment_view_count(user, context = nil)
unless self.new_record?
self.with_versioning(false) do |p|
context ||= p.context
p.connection.execute("UPDATE wiki_pages SET view_count=COALESCE(view_count, 0) + 1 WHERE id=#{p.id}")
p.context_module_action(user, context, :read)
end
end
end
2011-02-01 09:57:29 +08:00
end