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
2012-12-12 21:50:15 +08:00
2013-07-17 21:34:01 +08:00
include SearchTermHelper
2011-02-01 09:57:29 +08:00
belongs_to :wiki , :touch = > true
belongs_to :user
2012-01-11 00:48:48 +08:00
acts_as_url :title , :scope = > [ :wiki_id , :not_deleted ] , :sync_url = > true
2012-12-12 21:50:15 +08:00
2013-05-17 05:02:04 +08:00
validate :validate_front_page_visibility
2012-01-11 00:48:48 +08:00
before_save :set_revised_at
2011-02-01 09:57:29 +08:00
before_validation :ensure_unique_title
2012-08-10 04:20:57 +08:00
2011-08-19 02:43:32 +08:00
TITLE_LENGTH = WikiPage . columns_hash [ 'title' ] . limit rescue 255
2013-08-09 02:47:17 +08:00
SIMPLY_VERSIONED_EXCLUDE_FIELDS = [ :workflow_state , :hide_from_students , :editing_roles , :notify_of_update ]
2012-12-12 21:50:15 +08:00
2013-05-17 05:02:04 +08:00
def validate_front_page_visibility
2013-07-15 22:16:04 +08:00
if self . hide_from_students && self . is_front_page?
2013-05-17 05:02:04 +08:00
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
2012-01-11 00:48:48 +08:00
return if deleted?
2011-02-01 09:57:29 +08:00
self . title || = ( self . url || " page " ) . to_cased_title
2012-01-11 00:48:48 +08:00
return unless self . wiki
2011-07-01 04:36:59 +08:00
# TODO i18n (see wiki.rb)
2011-02-01 09:57:29 +08:00
if self . title == " Front Page " && self . new_record?
2011-04-02 00:59:20 +08:00
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
2011-08-19 02:43:32 +08:00
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 )
2012-12-12 21:50:15 +08:00
2011-08-19 02:43:32 +08:00
self . title = new_title
2011-02-01 09:57:29 +08:00
end
end
2012-12-12 21:50:15 +08:00
2013-08-23 04:04:59 +08:00
# sync hide_from_students with published state
def sync_hidden_and_unpublished
return if ( context rescue nil ) . nil?
2013-11-15 02:22:17 +08:00
WikiPage . skip_callback ( :after_find ) do
if context . feature_enabled? ( :draft_state )
if self . hide_from_students # hide_from_students overrides published
self . hide_from_students = false
self . workflow_state = 'unpublished'
end
else
if self . workflow_state . to_s == 'unpublished' # unpublished overrides hide_from_students
self . workflow_state = 'active'
self . hide_from_students = true
end
2013-08-23 04:04:59 +08:00
end
end
end
before_save :sync_hidden_and_unpublished
alias_method :after_find , :sync_hidden_and_unpublished
private :sync_hidden_and_unpublished
2013-04-20 03:55:00 +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
2011-03-01 08:37:39 +08:00
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
2011-04-02 00:59:20 +08:00
# make stringex scoping a little more useful/flexible... in addition to
# the normal constructed attribute scope(s), it also supports paramater-
2013-03-21 03:38:19 +08:00
# less scopeds. note that there needs to be an instance_method of
2011-04-02 00:59:20 +08:00
# 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
2013-03-19 03:07:47 +08:00
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
2012-07-24 05:29:18 +08:00
copy_authorized_links ( :body ) { [ self . context , self . user ] }
2012-12-12 21:50:15 +08:00
2011-04-02 00:59:20 +08:00
validates_each :title do | record , attr , value |
if value . blank?
2011-06-10 11:51:23 +08:00
record . errors . add ( attr , t ( 'errors.blank_title' , " Title can't be blank " ) )
2011-04-02 00:59:20 +08:00
elsif value . size > maximum_string_length
2011-06-10 11:51:23 +08:00
record . errors . add ( attr , t ( 'errors.title_too_long' , " Title can't exceed %{max_characters} characters " , :max_characters = > maximum_string_length ) )
2011-04-02 00:59:20 +08:00
elsif value . to_url . blank?
2011-06-10 11:51:23 +08:00
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
2011-04-02 00:59:20 +08:00
end
end
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
has_a_broadcast_policy
2013-08-09 02:47:17 +08:00
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
2012-12-12 21:50:15 +08:00
2013-10-03 06:37:45 +08:00
2011-02-01 09:57:29 +08:00
workflow do
2013-05-15 03:53:14 +08:00
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
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
state :deleted
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
end
2012-12-12 21:50:15 +08:00
2013-10-03 06:37:45 +08:00
alias_method :published? , :active?
2011-02-01 09:57:29 +08:00
def restore
2013-12-06 02:46:39 +08:00
self . workflow_state = context . feature_enabled? ( :draft_state ) ? 'unpublished' : 'active'
2011-02-01 09:57:29 +08:00
self . save
end
2012-12-12 21:50:15 +08:00
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
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
def notify_of_update = ( val )
2013-04-20 03:55:00 +08:00
@wiki_page_changed = Canvas :: Plugin . value_to_boolean ( val )
2011-02-01 09:57:29 +08:00
end
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
def notify_of_update
false
end
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
def remove_changed_flag
@wiki_page_changed = false
end
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
def version_history
self . versions . map ( & :model )
end
2012-12-12 21:50:15 +08:00
2013-03-21 03:38:19 +08:00
scope :active , where ( :workflow_state = > 'active' )
2012-12-12 21:50:15 +08:00
2013-03-21 03:38:19 +08:00
scope :deleted_last , order ( " workflow_state='deleted' " )
scope :not_deleted , where ( " wiki_pages.workflow_state<>'deleted' " )
2011-04-02 00:59:20 +08:00
2013-10-08 05:19:00 +08:00
scope :unpublished , where ( " wiki_pages.workflow_state='unpublished' OR (wiki_pages.hide_from_students=? AND wiki_pages.workflow_state<>'deleted') " , true )
2013-08-23 04:04:59 +08:00
# needed for ensure_unique_url
2011-04-02 00:59:20 +08:00
def not_deleted
! deleted?
end
2013-08-23 04:04:59 +08:00
scope :not_hidden , where ( 'wiki_pages.hide_from_students<>?' , true )
2013-03-21 03:38:19 +08:00
scope :order_by_id , order ( :id )
2012-08-10 04:20:57 +08:00
2013-06-04 04:45:42 +08:00
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
2013-06-05 07:51:27 +08:00
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 }
2013-07-11 01:33:58 +08:00
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
2012-12-12 21:50:15 +08:00
2013-07-15 22:16:04 +08:00
def is_front_page?
2013-08-27 06:05:50 +08:00
return false if self . deleted?
2013-11-15 02:22:17 +08:00
self . url == self . wiki . get_front_page_url # wiki.get_front_page_url checks has_front_page? and context.feature_enabled?(:draft_state)
2013-05-17 05:02:04 +08:00
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
2012-07-24 05:29:18 +08:00
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
2012-12-12 21:50:15 +08:00
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
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
set_policy do
2013-07-24 05:56:06 +08:00
given { | user , session | self . wiki . grants_right? ( user , session , :read ) && can_read_page? ( user , session ) }
2011-07-14 00:24:17 +08:00
can :read
2012-12-12 21:50:15 +08:00
2013-07-26 06:14:15 +08:00
given { | user , session | self . can_edit_page? ( user ) }
2011-07-14 00:24:17 +08:00
can :read
2011-02-01 09:57:29 +08:00
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
2012-12-12 21:50:15 +08:00
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 ) }
2013-07-24 05:56:06 +08:00
can :create
2012-12-12 21:50:15 +08:00
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
2012-12-12 21:50:15 +08:00
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
2013-07-24 05:56:06 +08:00
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 ) }
2013-07-24 05:56:06 +08:00
can :delete
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 ) }
2013-07-24 05:56:06 +08:00
can :delete
2011-02-01 09:57:29 +08:00
end
2012-12-12 21:50:15 +08:00
2013-07-24 05:56:06 +08:00
def can_read_page? ( user , session = nil )
self . wiki . grants_right? ( user , session , :manage ) || ( ! hide_from_students && self . active? )
2011-02-23 08:24:52 +08:00
end
2012-12-12 21:50:15 +08:00
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
2013-07-24 05:56:06 +08:00
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 )
2013-07-24 05:56:06 +08:00
# the remaining edit roles all require read access, so just check here
2013-07-26 06:14:15 +08:00
return false unless can_read_page? ( user , session ) && ! self . locked_for? ( user )
2013-07-24 05:56:06 +08:00
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
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
def default_roles
2012-07-24 05:29:18 +08:00
if context . is_a? ( Group )
2011-02-01 09:57:29 +08:00
'members'
2012-07-24 05:29:18 +08:00
elsif context . is_a? ( Course )
2011-02-01 09:57:29 +08:00
'teachers'
else
'members'
end
end
2011-11-09 00:17:32 +08:00
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-11-09 00:17:32 +08:00
2011-02-01 09:57:29 +08:00
def context ( user = nil )
2013-11-13 05:27:39 +08:00
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
2012-07-24 05:29:18 +08:00
2011-02-01 09:57:29 +08:00
def participants
res = [ ]
2012-07-24 05:29:18 +08:00
if context && context . available?
2013-08-23 04:04:59 +08:00
if self . hide_from_students || ! self . active?
2012-07-24 05:29:18 +08:00
res += context . participating_admins
else
res += context . participants
2011-02-01 09:57:29 +08:00
end
end
res . flatten . uniq
end
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
def to_atom ( opts = { } )
context = opts [ :context ]
Atom :: Entry . new do | entry |
2012-07-24 05:29:18 +08:00
entry . title = t ( :atom_entry_title , " Wiki Page, %{course_or_group_name}: %{page_title} " , :course_or_group_name = > context . name , :page_title = > self . title )
2012-04-05 03:06:48 +08:00
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' ,
2012-07-24 05:29:18 +08:00
:href = > " http:// #{ HostUrl . context_host ( context ) } / #{ self . context . class . to_s . downcase . pluralize } / #{ self . context . id } /wiki/ #{ self . url } " )
2011-06-10 11:51:23 +08:00
entry . content = Atom :: Content :: Html . new ( self . body || t ( 'defaults.no_content' , " no content " ) )
2011-02-01 09:57:29 +08:00
end
end
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
def user_name
2012-06-28 05:40:03 +08:00
( user && user . name ) || t ( 'unknown_user_name' , " Unknown " )
2011-02-01 09:57:29 +08:00
end
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
def to_param
url
end
2012-12-12 21:50:15 +08:00
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
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
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
2011-06-18 00:58:18 +08:00
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 |
2012-04-30 23:52:43 +08:00
if ! wiki
ErrorReport . log_error ( :content_migration , :message = > " There was a nil wiki page imported for ContentMigration: #{ migration . id } " )
next
end
2013-05-30 06:41:50 +08:00
next unless migration . import_object? ( " wiki_pages " , wiki [ 'migration_id' ] ) || migration . import_object? ( " wikis " , wiki [ 'migration_id' ] )
2011-06-18 00:58:18 +08:00
begin
import_from_migration ( wiki , migration . context ) if wiki
rescue
2013-04-24 01:12:19 +08:00
migration . add_import_warning ( t ( '#migration.wiki_page_type' , " Wiki Page " ) , wiki [ :title ] , $! )
2011-06-18 00:58:18 +08:00
end
2011-02-01 09:57:29 +08:00
end
end
2012-12-12 21:50:15 +08:00
2011-02-01 09:57:29 +08:00
def self . import_from_migration ( hash , context , item = nil )
hash = hash . with_indifferent_access
2011-06-10 11:51:23 +08:00
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
2011-04-26 05:13:11 +08:00
# 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
2011-02-08 06:57:31 +08:00
if hash [ :root_folder ] && [ 'folder' , 'FOLDER_TYPE' ] . member? ( hash [ :type ] )
2013-05-17 05:02:04 +08:00
front_page = context . wiki . front_page
2011-02-08 06:57:31 +08:00
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
2013-06-06 03:27:10 +08:00
if state = hash [ :workflow_state ]
if state == 'active'
item . workflow_state = 'active'
else
item . workflow_state = 'unpublished'
end
2013-04-24 22:50:45 +08:00
end
2013-08-28 01:32:55 +08:00
item . set_as_front_page! if ! ! hash [ :front_page ]
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 ]
2013-04-04 02:30:57 +08:00
hash [ :missing_links ] = { }
2011-02-01 09:57:29 +08:00
allow_save = true
2011-02-08 08:43:50 +08:00
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 ]
2013-04-04 02:30:57 +08:00
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
2013-04-04 02:30:57 +08:00
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 ]
2013-04-04 02:30:57 +08:00
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'
2013-02-06 05:59:01 +08:00
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'
2011-02-08 08:43:50 +08:00
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 ]
2013-04-04 02:30:57 +08:00
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 ]
2011-06-10 11:51:23 +08:00
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
2011-04-30 06:46:50 +08:00
item . title = hash [ :title ] . presence || item . url . presence || " unnamed page "
2011-08-19 02:43:32 +08:00
if item . title . length > TITLE_LENGTH
2013-04-04 02:30:57 +08:00
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
2011-08-19 02:43:32 +08:00
item . title . splice! ( 0 ... TITLE_LENGTH ) # truncate too-long titles
end
2013-04-04 02:30:57 +08:00
hash [ :missing_links ] [ :body ] = [ ]
item . body = ImportedHtmlConverter . convert ( hash [ :text ] || " " , context , { :missing_links = > hash [ :missing_links ] [ :body ] } )
2012-04-16 21:31:40 +08:00
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
2011-02-08 08:43:50 +08:00
allow_save = false
2011-02-01 09:57:29 +08:00
end
if allow_save && hash [ :migration_id ]
item . save_without_broadcasting!
2011-04-15 11:43:08 +08:00
context . imported_migration_items << item if context . imported_migration_items
2013-04-04 02:30:57 +08:00
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
2012-12-12 21:50:15 +08:00
2013-04-20 03:55:00 +08:00
def increment_view_count ( user , context = nil )
unless self . new_record?
self . with_versioning ( false ) do | p |
context || = p . context
2013-12-10 01:37:38 +08:00
WikiPage . where ( id : p ) . update_all ( " view_count=COALESCE(view_count, 0) + 1 " )
2013-04-20 03:55:00 +08:00
p . context_module_action ( user , context , :read )
end
end
end
2013-08-22 23:43:03 +08:00
def initialize_wiki_page ( user )
2013-11-15 02:22:17 +08:00
unless context . feature_enabled? ( :draft_state )
2013-08-22 23:43:03 +08:00
set_as_front_page! if ! wiki . has_front_page? and url == Wiki :: DEFAULT_FRONT_PAGE_URL
end
is_privileged_user = wiki . grants_right? ( user , :manage )
2013-11-15 02:22:17 +08:00
if is_privileged_user && context . feature_enabled? ( :draft_state ) && ! context . is_a? ( Group )
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 )
end
end
2011-02-01 09:57:29 +08:00
end