govuk_content_models 8.1.0 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/app/models/action.rb CHANGED
@@ -4,17 +4,19 @@ class Action
4
4
  include Mongoid::Document
5
5
 
6
6
  STATUS_ACTIONS = [
7
- CREATE = "create",
8
- REQUEST_REVIEW = "request_review",
9
- APPROVE_REVIEW = "approve_review",
10
- APPROVE_FACT_CHECK = "approve_fact_check",
11
- REQUEST_AMENDMENTS = "request_amendments",
12
- SEND_FACT_CHECK = "send_fact_check",
13
- RECEIVE_FACT_CHECK = "receive_fact_check",
14
- SKIP_FACT_CHECK = "skip_fact_check",
15
- PUBLISH = "publish",
16
- ARCHIVE = "archive",
17
- NEW_VERSION = "new_version",
7
+ CREATE = "create",
8
+ REQUEST_REVIEW = "request_review",
9
+ APPROVE_REVIEW = "approve_review",
10
+ APPROVE_FACT_CHECK = "approve_fact_check",
11
+ REQUEST_AMENDMENTS = "request_amendments",
12
+ SEND_FACT_CHECK = "send_fact_check",
13
+ RECEIVE_FACT_CHECK = "receive_fact_check",
14
+ SKIP_FACT_CHECK = "skip_fact_check",
15
+ SCHEDULE_FOR_PUBLISHING = "schedule_for_publishing",
16
+ CANCEL_SCHEDULED_PUBLISHING = "cancel_scheduled_publishing",
17
+ PUBLISH = "publish",
18
+ ARCHIVE = "archive",
19
+ NEW_VERSION = "new_version",
18
20
  ]
19
21
 
20
22
  NON_STATUS_ACTIONS = [
@@ -1,4 +1,3 @@
1
- require "slug_validator"
2
1
  require "plek"
3
2
  require "traits/taggable"
4
3
  require "artefact_action" # Require this when running outside Rails
@@ -1,4 +1,3 @@
1
- require "slug_validator"
2
1
  require "traits/taggable"
3
2
  require "safe_html"
4
3
 
@@ -13,6 +13,7 @@ class Edition
13
13
 
14
14
  field :title, type: String
15
15
  field :created_at, type: DateTime, default: lambda { Time.zone.now }
16
+ field :publish_at, type: DateTime
16
17
  field :overview, type: String
17
18
  field :alternative_title, type: String
18
19
  field :slug, type: String
@@ -30,16 +31,12 @@ class Edition
30
31
 
31
32
  belongs_to :assigned_to, class_name: "User"
32
33
 
33
- scope :draft, where(state: "draft")
34
- scope :amends_needed, where(state: "amends_needed")
35
- scope :in_review, where(state: "in_review")
36
- scope :fact_check, where(state: "fact_check")
37
- scope :fact_check_received, where(state: "fact_check_received")
38
- scope :ready, where(state: "ready")
39
- scope :published, where(state: "published")
40
- scope :archived, where(state: "archived")
41
- scope :in_progress, where(:state.nin => ["archived", "published"])
42
- scope :assigned_to, lambda { |user|
34
+ # state_machine comes from Workflow
35
+ state_machine.states.map(&:name).each do |state|
36
+ scope state, where(state: state)
37
+ end
38
+ scope :in_progress, where(:state.nin => ["archived", "published"])
39
+ scope :assigned_to, lambda { |user|
43
40
  if user
44
41
  where(assigned_to_id: user.id)
45
42
  else
@@ -50,6 +47,7 @@ class Edition
50
47
  validates :title, presence: true
51
48
  validates :version_number, presence: true
52
49
  validates :panopticon_id, presence: true
50
+ validate :publish_at_is_in_the_future
53
51
  validates_with SafeHtml
54
52
 
55
53
  before_save :check_for_archived_artefact
@@ -286,4 +284,10 @@ class Edition
286
284
  Artefact.find(self.panopticon_id).destroy
287
285
  end
288
286
  end
287
+
288
+ private
289
+
290
+ def publish_at_is_in_the_future
291
+ errors.add(:publish_at, "can't be a time in the past") if publish_at.present? && publish_at < Time.zone.now
292
+ end
289
293
  end
@@ -22,6 +22,14 @@ module Workflow
22
22
  edition.mark_as_rejected
23
23
  end
24
24
 
25
+ before_transition on: :schedule_for_publishing do |edition, transition|
26
+ edition.publish_at = transition.args.first
27
+ end
28
+
29
+ before_transition on: [:publish, :cancel_scheduled_publishing] do |edition, transition|
30
+ edition.publish_at = nil
31
+ end
32
+
25
33
  after_transition on: :publish do |edition, transition|
26
34
  edition.was_published
27
35
  end
@@ -60,8 +68,16 @@ module Workflow
60
68
  transition fact_check: :fact_check_received
61
69
  end
62
70
 
71
+ event :schedule_for_publishing do
72
+ transition ready: :scheduled_for_publishing
73
+ end
74
+
75
+ event :cancel_scheduled_publishing do
76
+ transition scheduled_for_publishing: :ready
77
+ end
78
+
63
79
  event :publish do
64
- transition ready: :published
80
+ transition [:ready, :scheduled_for_publishing] => :published
65
81
  end
66
82
 
67
83
  event :emergency_publish do
@@ -71,6 +87,10 @@ module Workflow
71
87
  event :archive do
72
88
  transition all => :archived, :unless => :archived?
73
89
  end
90
+
91
+ state :scheduled_for_publishing do
92
+ validates_presence_of :publish_at
93
+ end
74
94
  end
75
95
  end
76
96
 
@@ -78,8 +98,10 @@ module Workflow
78
98
  (self.actions.where(request_type: Action::APPROVE_FACT_CHECK).count > 0)
79
99
  end
80
100
 
81
- def capitalized_state_name
82
- self.human_state_name.capitalize
101
+ def status_text
102
+ text = human_state_name.capitalize
103
+ text += ' on ' + publish_at.strftime("%d/%m/%Y %H:%M") if scheduled_for_publishing?
104
+ text
83
105
  end
84
106
 
85
107
  def update_user_action(property, statuses)
@@ -146,26 +168,8 @@ module Workflow
146
168
  self.actions.sort_by(&:created_at).reverse.find(&blk)
147
169
  end
148
170
 
149
- def not_editing_published_item
150
- if changed? and ! state_changed?
151
- if archived?
152
- errors.add(:base, "Archived editions can't be edited")
153
- end
154
- if published?
155
- changes_allowed_when_published = ["slug", "section",
156
- "department", "business_proposition"]
157
- illegal_changes = changes.keys - changes_allowed_when_published
158
- if illegal_changes.empty?
159
- # Allow it
160
- else
161
- errors.add(:base, "Published editions can't be edited")
162
- end
163
- end
164
- end
165
- end
166
-
167
171
  def can_destroy?
168
- ! published? and ! archived?
172
+ ! scheduled_for_publishing? && ! published? && ! archived?
169
173
  end
170
174
 
171
175
  def check_can_delete_and_notify
@@ -206,4 +210,25 @@ module Workflow
206
210
  def in_progress?
207
211
  ! ["archived", "published"].include? self.state
208
212
  end
213
+
214
+ private
215
+
216
+ def not_editing_published_item
217
+ if changed? and ! state_changed?
218
+ if archived?
219
+ errors.add(:base, "Archived editions can't be edited")
220
+ end
221
+ if scheduled_for_publishing? || published?
222
+ changes_allowed_when_published = ["slug", "section",
223
+ "department", "business_proposition"]
224
+ illegal_changes = changes.keys - changes_allowed_when_published
225
+ if illegal_changes.empty?
226
+ # Allow it
227
+ else
228
+ edition_description = published? ? 'Published editions' : 'Editions scheduled for publishing'
229
+ errors.add(:base, "#{edition_description} can't be edited")
230
+ end
231
+ end
232
+ end
233
+ end
209
234
  end
@@ -6,8 +6,14 @@ require "programme_edition"
6
6
  require "transaction_edition"
7
7
 
8
8
  module WorkflowActor
9
- SIMPLE_WORKFLOW_ACTIONS = %W[request_review
10
- request_amendments approve_review approve_fact_check archive]
9
+ SIMPLE_WORKFLOW_ACTIONS = %w(
10
+ request_review
11
+ request_amendments
12
+ approve_review
13
+ approve_fact_check
14
+ archive
15
+ cancel_scheduled_publishing
16
+ )
11
17
 
12
18
  def record_action(edition, type, options={})
13
19
  type = Action.const_get(type.to_s.upcase)
@@ -27,8 +33,8 @@ module WorkflowActor
27
33
  respond_to?(:"can_#{action}?") ? __send__(:"can_#{action}?", edition) : true
28
34
  end
29
35
 
30
- def take_action(edition, action, details = {})
31
- if can_take_action(action, edition) and edition.send(action)
36
+ def take_action(edition, action, details = {}, action_parameters = [])
37
+ if can_take_action(action, edition) && edition.send(action, *action_parameters)
32
38
  record_action(edition, action, details)
33
39
  edition
34
40
  else
@@ -36,8 +42,8 @@ module WorkflowActor
36
42
  end
37
43
  end
38
44
 
39
- def take_action!(edition, action, details = {})
40
- edition = take_action(edition, action, details)
45
+ def take_action!(edition, action, details = {}, action_parameters = [])
46
+ edition = take_action(edition, action, details, action_parameters)
41
47
  edition.save if edition
42
48
  end
43
49
 
@@ -109,6 +115,11 @@ module WorkflowActor
109
115
  end
110
116
  end
111
117
 
118
+ def schedule_for_publishing(edition, details)
119
+ publish_at = details.delete(:publish_at)
120
+ take_action(edition, __method__, details, [publish_at])
121
+ end
122
+
112
123
  def publish(edition, details)
113
124
  if edition.published_edition
114
125
  details.merge!({ diff: edition.edition_changes })
@@ -118,16 +129,16 @@ module WorkflowActor
118
129
  end
119
130
 
120
131
  def can_approve_review?(edition)
121
- # To accommodate latest_status_action being nil, we'll always return true in
122
- # those cases
123
- # This is intended as a v.temporary fix until we can remedy the root cause
124
- if edition.latest_status_action
125
- edition.latest_status_action.requester_id != self.id
132
+ requester_different?(edition)
133
+ end
134
+
135
+ def can_request_amendments?(edition)
136
+ if edition.in_review?
137
+ requester_different?(edition)
126
138
  else
127
139
  true
128
140
  end
129
141
  end
130
- alias :can_request_amendments? :can_approve_review?
131
142
 
132
143
  def assign(edition, recipient)
133
144
  edition.assigned_to_id = recipient.id
@@ -138,4 +149,17 @@ module WorkflowActor
138
149
  edition.save! and edition.reload
139
150
  record_action edition, __method__, recipient: recipient
140
151
  end
152
+
153
+ private
154
+
155
+ def requester_different?(edition)
156
+ # To accommodate latest_status_action being nil, we'll always return true in
157
+ # those cases
158
+ # This is intended as a v.temporary fix until we can remedy the root cause
159
+ if edition.latest_status_action
160
+ edition.latest_status_action.requester_id != self.id
161
+ else
162
+ true
163
+ end
164
+ end
141
165
  end
@@ -3,13 +3,12 @@ require "active_model"
3
3
  require "mongoid"
4
4
  require "govuk_content_models"
5
5
 
6
- %w[ app/models app/validators app/repositories app/traits lib ].each do |path|
7
- full_path = File.expand_path(
8
- "#{File.dirname(__FILE__)}/../../#{path}", __FILE__)
6
+ root_path = "#{File.dirname(__FILE__)}/../.."
7
+ %w[ app/models app/validators app/traits lib ].each do |path|
8
+ full_path = File.expand_path("#{root_path}/#{path}")
9
9
  $LOAD_PATH.unshift full_path unless $LOAD_PATH.include?(full_path)
10
10
  end
11
11
 
12
- # Require everything under app
13
- Dir.glob("#{File.dirname(__FILE__)}/../../app/**/*.rb").each do |file|
14
- require file
15
- end
12
+ # Require validators first, then other files in app
13
+ Dir.glob("#{root_path}/app/validators/*.rb").each {|f| require f }
14
+ Dir.glob("#{root_path}/app/**/*.rb").each {|f| require f }
@@ -81,6 +81,11 @@ FactoryGirl.define do
81
81
  section "test:subsection test"
82
82
 
83
83
  association :assigned_to, factory: :user
84
+
85
+ trait :scheduled_for_publishing do
86
+ state 'scheduled_for_publishing'
87
+ publish_at 1.day.from_now
88
+ end
84
89
  end
85
90
  factory :answer_edition, parent: :edition do
86
91
  end
@@ -1,4 +1,4 @@
1
1
  module GovukContentModels
2
2
  # Changing this causes Jenkins to tag and release the gem into the wild
3
- VERSION = "8.1.0"
3
+ VERSION = "8.2.0"
4
4
  end
@@ -0,0 +1,84 @@
1
+ require "test_helper"
2
+
3
+ class EditionScheduledForPublishingTest < ActiveSupport::TestCase
4
+ context "#schedule_for_publishing" do
5
+ context "when publish_at is not specified" do
6
+ setup do
7
+ @edition = FactoryGirl.create(:edition, state: 'ready')
8
+ @edition.schedule_for_publishing
9
+ @edition.reload
10
+ end
11
+
12
+ should "return an error" do
13
+ assert_includes @edition.errors[:publish_at], "can't be blank"
14
+ end
15
+
16
+ should "not complete the transition to scheduled_for_publishing" do
17
+ assert_equal 'ready', @edition.state
18
+ end
19
+ end
20
+
21
+ context "when publish_at is specified" do
22
+ setup do
23
+ @edition = FactoryGirl.create(:edition, state: 'ready')
24
+ @publish_when = 1.day.from_now
25
+ @edition.schedule_for_publishing(@publish_when)
26
+ @edition.reload
27
+ end
28
+
29
+ should "save publish_at against the edition" do
30
+ assert_equal @publish_when.to_i, @edition.publish_at.to_i
31
+ end
32
+
33
+ should "complete the transition to scheduled_for_publishing" do
34
+ assert_equal 'scheduled_for_publishing', @edition.state
35
+ end
36
+ end
37
+ end
38
+
39
+ context "when scheduled_for_publishing" do
40
+ should "not allow editing fields like title" do
41
+ edition = FactoryGirl.create(:edition, :scheduled_for_publishing)
42
+
43
+ edition.title = 'a new title'
44
+
45
+ refute edition.valid?
46
+ assert_includes edition.errors.full_messages, "Editions scheduled for publishing can't be edited"
47
+ end
48
+
49
+ should "allow editing fields like section" do
50
+ edition = FactoryGirl.create(:edition, :scheduled_for_publishing)
51
+
52
+ edition.section = 'new section'
53
+
54
+ assert edition.save
55
+ assert_equal edition.reload.section, 'new section'
56
+ end
57
+
58
+ should "return false for #can_destroy?" do
59
+ edition = FactoryGirl.create(:edition, :scheduled_for_publishing)
60
+ refute edition.can_destroy?
61
+ end
62
+
63
+ should "allow transition to published state" do
64
+ edition = FactoryGirl.create(:edition, :scheduled_for_publishing)
65
+ assert edition.can_publish?
66
+ end
67
+ end
68
+
69
+ context "#cancel_scheduled_publishing" do
70
+ setup do
71
+ @edition = FactoryGirl.create(:edition, :scheduled_for_publishing)
72
+ @edition.cancel_scheduled_publishing
73
+ @edition.reload
74
+ end
75
+
76
+ should "remove the publish_at stored against the edition" do
77
+ assert_nil @edition.publish_at
78
+ end
79
+
80
+ should "complete the transition back to ready" do
81
+ assert_equal 'ready', @edition.state
82
+ end
83
+ end
84
+ end
@@ -46,6 +46,15 @@ class EditionTest < ActiveSupport::TestCase
46
46
  assert a.errors[:title].any?
47
47
  end
48
48
 
49
+ context "#publish_at" do
50
+ should "not be a time in the past" do
51
+ edition = FactoryGirl.build(:edition, publish_at: 1.minute.ago)
52
+
53
+ refute edition.valid?
54
+ assert_includes edition.errors[:publish_at], "can't be a time in the past"
55
+ end
56
+ end
57
+
49
58
  test "it should give a friendly (legacy supporting) description of its format" do
50
59
  a = LocalTransactionEdition.new
51
60
  assert_equal "LocalTransaction", a.format
@@ -629,6 +638,15 @@ class EditionTest < ActiveSupport::TestCase
629
638
  assert_equal 2, GuideEdition.where(panopticon_id: edition.panopticon_id, state: "archived").count
630
639
  end
631
640
 
641
+ test "when an edition is published, publish_at is cleared" do
642
+ user = FactoryGirl.create(:user)
643
+ edition = FactoryGirl.create(:edition, :scheduled_for_publishing)
644
+
645
+ user.publish edition, comment: "First publication"
646
+
647
+ assert_nil edition.reload.publish_at
648
+ end
649
+
632
650
  test "edition can return latest status action of a specified request type" do
633
651
  edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "draft")
634
652
  user = User.create(name: "George")
@@ -58,4 +58,32 @@ class WorkflowActorTest < ActiveSupport::TestCase
58
58
  end
59
59
  end
60
60
 
61
+ context "#schedule_for_publishing" do
62
+ setup do
63
+ @user = FactoryGirl.build(:user)
64
+ @publish_at = 1.day.from_now
65
+ @activity_details = { publish_at: @publish_at, comment: "Go schedule !" }
66
+ end
67
+
68
+ should "return false when scheduling an already published edition" do
69
+ edition = FactoryGirl.create(:edition, state: 'published')
70
+ refute @user.schedule_for_publishing(edition, @activity_details)
71
+ end
72
+
73
+ should "schedule an edition for publishing if it is ready" do
74
+ edition = FactoryGirl.create(:edition, state: 'ready')
75
+
76
+ edition = @user.schedule_for_publishing(edition, @activity_details)
77
+
78
+ assert edition.scheduled_for_publishing?
79
+ assert_equal @publish_at.to_i, edition.publish_at.to_i
80
+ end
81
+
82
+ should "record the action" do
83
+ edition = FactoryGirl.create(:edition, state: 'ready')
84
+ @user.expects(:record_action).with(edition, :schedule_for_publishing, { comment: "Go schedule !" })
85
+
86
+ @user.schedule_for_publishing(edition, @activity_details)
87
+ end
88
+ end
61
89
  end
@@ -56,6 +56,19 @@ class WorkflowTest < ActiveSupport::TestCase
56
56
  return user, transaction
57
57
  end
58
58
 
59
+ context "#status_text" do
60
+ should "return a capitalized text representation of the state" do
61
+ assert_equal 'Ready', FactoryGirl.build(:edition, state: 'ready').status_text
62
+ end
63
+
64
+ should "also return scheduled publishing time when the state is scheduled for publishing" do
65
+ edition = FactoryGirl.build(:edition, :scheduled_for_publishing)
66
+ expected_status_text = 'Scheduled for publishing on ' + edition.publish_at.strftime("%d/%m/%Y %H:%M")
67
+
68
+ assert_equal expected_status_text, edition.status_text
69
+ end
70
+ end
71
+
59
72
  test "permits the creation of new editions" do
60
73
  user, transaction = template_user_and_published_transaction
61
74
  assert transaction.persisted?
@@ -219,6 +232,7 @@ class WorkflowTest < ActiveSupport::TestCase
219
232
  assert edition.can_request_review?
220
233
  user.request_review(edition,{comment: "Review this guide please."})
221
234
  refute user.request_amendments(edition, {comment: "Well Done, but work harder"})
235
+ refute user.can_request_amendments?(edition)
222
236
  end
223
237
 
224
238
  test "user should not be able to okay a guide they requested review for" do
@@ -347,4 +361,19 @@ class WorkflowTest < ActiveSupport::TestCase
347
361
  bs.valid?
348
362
  assert_equal "Published editions can't be edited", bs.errors[:base].first
349
363
  end
364
+
365
+ test "User can request amendments for an edition they just approved" do
366
+ user_1, user_2 = template_users
367
+ edition = user_1.create_edition(:answer, panopticon_id: @artefact.id, title: "Answer foo", slug: "answer-foo")
368
+ edition.body = "body content"
369
+ user_1.assign(edition, user_2)
370
+ user_1.request_review(edition,{comment: "Review this guide please."})
371
+ assert edition.in_review?
372
+
373
+ user_2.approve_review(edition, {comment: "Looks good just now"})
374
+ assert edition.ready?
375
+
376
+ user_2.request_amendments(edition, {comment: "More work needed"})
377
+ assert edition.amends_needed?
378
+ end
350
379
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govuk_content_models
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.0
4
+ version: 8.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-04 00:00:00.000000000 Z
12
+ date: 2014-03-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bson_ext
@@ -417,6 +417,7 @@ files:
417
417
  - test/models/business_support_edition_test.rb
418
418
  - test/models/campaign_edition_test.rb
419
419
  - test/models/curated_list_test.rb
420
+ - test/models/edition_scheduled_for_publishing_test.rb
420
421
  - test/models/edition_test.rb
421
422
  - test/models/fact_check_address_test.rb
422
423
  - test/models/help_page_edition_test.rb
@@ -457,7 +458,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
457
458
  version: '0'
458
459
  segments:
459
460
  - 0
460
- hash: 484371821541707473
461
+ hash: 4108751101128219238
461
462
  required_rubygems_version: !ruby/object:Gem::Requirement
462
463
  none: false
463
464
  requirements:
@@ -466,7 +467,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
466
467
  version: '0'
467
468
  segments:
468
469
  - 0
469
- hash: 484371821541707473
470
+ hash: 4108751101128219238
470
471
  requirements: []
471
472
  rubyforge_project:
472
473
  rubygems_version: 1.8.23
@@ -490,6 +491,7 @@ test_files:
490
491
  - test/models/business_support_edition_test.rb
491
492
  - test/models/campaign_edition_test.rb
492
493
  - test/models/curated_list_test.rb
494
+ - test/models/edition_scheduled_for_publishing_test.rb
493
495
  - test/models/edition_test.rb
494
496
  - test/models/fact_check_address_test.rb
495
497
  - test/models/help_page_edition_test.rb