govuk_content_models 6.0.2

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.
Files changed (104) hide show
  1. data/.gitignore +17 -0
  2. data/.ruby-version +1 -0
  3. data/.travis.yml +14 -0
  4. data/CONTRIBUTING.md +22 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +20 -0
  7. data/README.md +5 -0
  8. data/Rakefile +46 -0
  9. data/app/models/action.rb +60 -0
  10. data/app/models/answer_edition.rb +13 -0
  11. data/app/models/artefact.rb +341 -0
  12. data/app/models/artefact_action.rb +27 -0
  13. data/app/models/artefact_external_link.rb +15 -0
  14. data/app/models/business_support/business_size.rb +14 -0
  15. data/app/models/business_support/business_type.rb +14 -0
  16. data/app/models/business_support/location.rb +14 -0
  17. data/app/models/business_support/purpose.rb +14 -0
  18. data/app/models/business_support/sector.rb +14 -0
  19. data/app/models/business_support/stage.rb +14 -0
  20. data/app/models/business_support/support_type.rb +14 -0
  21. data/app/models/business_support_edition.rb +69 -0
  22. data/app/models/campaign_edition.rb +72 -0
  23. data/app/models/completed_transaction_edition.rb +14 -0
  24. data/app/models/curated_list.rb +32 -0
  25. data/app/models/edition.rb +286 -0
  26. data/app/models/expectant.rb +21 -0
  27. data/app/models/expectation.rb +12 -0
  28. data/app/models/guide_edition.rb +19 -0
  29. data/app/models/help_page_edition.rb +13 -0
  30. data/app/models/licence_edition.rb +35 -0
  31. data/app/models/local_authority.rb +58 -0
  32. data/app/models/local_interaction.rb +20 -0
  33. data/app/models/local_service.rb +49 -0
  34. data/app/models/local_transaction_edition.rb +49 -0
  35. data/app/models/overview_dashboard.rb +25 -0
  36. data/app/models/part.rb +28 -0
  37. data/app/models/parted.rb +32 -0
  38. data/app/models/place_edition.rb +20 -0
  39. data/app/models/programme_edition.rb +26 -0
  40. data/app/models/simple_smart_answer_edition.rb +66 -0
  41. data/app/models/simple_smart_answer_edition/node.rb +40 -0
  42. data/app/models/simple_smart_answer_edition/node/option.rb +31 -0
  43. data/app/models/tag.rb +88 -0
  44. data/app/models/transaction_edition.rb +28 -0
  45. data/app/models/travel_advice_edition.rb +177 -0
  46. data/app/models/user.rb +54 -0
  47. data/app/models/video_edition.rb +24 -0
  48. data/app/models/workflow.rb +217 -0
  49. data/app/models/workflow_actor.rb +141 -0
  50. data/app/traits/attachable.rb +60 -0
  51. data/app/traits/govspeak_smart_quotes_fixer.rb +19 -0
  52. data/app/traits/taggable.rb +113 -0
  53. data/app/validators/safe_html.rb +33 -0
  54. data/app/validators/slug_validator.rb +53 -0
  55. data/config/mongoid.yml +5 -0
  56. data/govuk_content_models.gemspec +42 -0
  57. data/jenkins.sh +7 -0
  58. data/lib/fact_check_address.rb +36 -0
  59. data/lib/govuk_content_models.rb +12 -0
  60. data/lib/govuk_content_models/require_all.rb +14 -0
  61. data/lib/govuk_content_models/test_helpers/factories.rb +213 -0
  62. data/lib/govuk_content_models/test_helpers/local_services.rb +24 -0
  63. data/lib/govuk_content_models/version.rb +4 -0
  64. data/test/fixtures/contactotron_api_response.json +1 -0
  65. data/test/fixtures/uploads/image.jpg +0 -0
  66. data/test/models/artefact_action_test.rb +123 -0
  67. data/test/models/artefact_external_link_test.rb +32 -0
  68. data/test/models/artefact_tag_test.rb +52 -0
  69. data/test/models/artefact_test.rb +583 -0
  70. data/test/models/business_support/business_size_test.rb +25 -0
  71. data/test/models/business_support/business_type_test.rb +25 -0
  72. data/test/models/business_support/location_test.rb +25 -0
  73. data/test/models/business_support/purpose_test.rb +29 -0
  74. data/test/models/business_support/sector_test.rb +25 -0
  75. data/test/models/business_support/stage_test.rb +25 -0
  76. data/test/models/business_support/support_type_test.rb +25 -0
  77. data/test/models/business_support_edition_test.rb +186 -0
  78. data/test/models/campaign_edition_test.rb +90 -0
  79. data/test/models/curated_list_test.rb +32 -0
  80. data/test/models/edition_test.rb +826 -0
  81. data/test/models/fact_check_address_test.rb +36 -0
  82. data/test/models/help_page_edition_test.rb +38 -0
  83. data/test/models/licence_edition_test.rb +104 -0
  84. data/test/models/local_authority_test.rb +113 -0
  85. data/test/models/local_service_test.rb +199 -0
  86. data/test/models/local_transaction_edition_test.rb +78 -0
  87. data/test/models/overview_dashboard_test.rb +47 -0
  88. data/test/models/simple_smart_answer_edition_test.rb +169 -0
  89. data/test/models/simple_smart_answer_node_test.rb +134 -0
  90. data/test/models/simple_smart_answer_option_test.rb +90 -0
  91. data/test/models/tag_test.rb +92 -0
  92. data/test/models/time_zone_test.rb +48 -0
  93. data/test/models/transaction_edition_test.rb +20 -0
  94. data/test/models/travel_advice_edition_test.rb +480 -0
  95. data/test/models/user_test.rb +114 -0
  96. data/test/models/video_edition_test.rb +64 -0
  97. data/test/models/workflow_actor_test.rb +61 -0
  98. data/test/models/workflow_test.rb +307 -0
  99. data/test/test_helper.rb +47 -0
  100. data/test/traits/attachable_test.rb +143 -0
  101. data/test/traits/taggable_test.rb +114 -0
  102. data/test/validators/safe_html_validator_test.rb +86 -0
  103. data/test/validators/slug_validator_test.rb +42 -0
  104. metadata +511 -0
@@ -0,0 +1,27 @@
1
+ require "safe_html"
2
+
3
+ class ArtefactAction
4
+ include Mongoid::Document
5
+ include Mongoid::Timestamps::Created
6
+
7
+ field "action_type", type: String
8
+ field "snapshot", type: Hash
9
+
10
+ GOVSPEAK_FIELDS = []
11
+
12
+ embedded_in :artefact
13
+
14
+ validates_with SafeHtml
15
+
16
+ # Ideally we would like to use the UID field here, since that will be the
17
+ # same across all applications, but Mongoid doesn't yet support using a
18
+ # custom primary key on a related field
19
+ belongs_to :user
20
+
21
+ # Not validating presence of a user just yet, since there may be some
22
+ # circumstances where we can't reliably determine the user. As an example
23
+ # of this, requests made through the API are not yet tied to a user. If we
24
+ # find out that there are no such circumstances in practice, we can add a
25
+ # validator for :user.
26
+ validates_presence_of :action_type, :snapshot
27
+ end
@@ -0,0 +1,15 @@
1
+ class ArtefactExternalLink
2
+ include Mongoid::Document
3
+
4
+ field "title", type: String
5
+ field "url", type: String
6
+
7
+ GOVSPEAK_FIELDS = []
8
+
9
+ embedded_in :artefact
10
+
11
+ validates_with SafeHtml
12
+
13
+ validates_presence_of :title
14
+ validates :url, :presence => true, :format => { :with => URI::regexp(%w{http https}) }
15
+ end
@@ -0,0 +1,14 @@
1
+ module BusinessSupport
2
+ class BusinessSize
3
+ include Mongoid::Document
4
+
5
+ field :name, type: String
6
+ field :slug, type: String
7
+ index :slug => 1
8
+
9
+ validates_presence_of :name
10
+ validates_uniqueness_of :name
11
+ validates_presence_of :slug
12
+ validates_uniqueness_of :slug
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module BusinessSupport
2
+ class BusinessType
3
+ include Mongoid::Document
4
+
5
+ field :name, type: String
6
+ field :slug, type: String
7
+ index :slug => 1
8
+
9
+ validates_presence_of :name
10
+ validates_uniqueness_of :name
11
+ validates_presence_of :slug
12
+ validates_uniqueness_of :slug
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module BusinessSupport
2
+ class Location
3
+ include Mongoid::Document
4
+
5
+ field :name, type: String
6
+ field :slug, type: String
7
+ index :slug => 1
8
+
9
+ validates_presence_of :name
10
+ validates_uniqueness_of :name
11
+ validates_presence_of :slug
12
+ validates_uniqueness_of :slug
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module BusinessSupport
2
+ class Purpose
3
+ include Mongoid::Document
4
+
5
+ field :name, type: String
6
+ field :slug, type: String
7
+ index :slug => 1
8
+
9
+ validates_presence_of :name
10
+ validates_uniqueness_of :name
11
+ validates_presence_of :slug
12
+ validates_uniqueness_of :slug
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module BusinessSupport
2
+ class Sector
3
+ include Mongoid::Document
4
+
5
+ field :name, type: String
6
+ field :slug, type: String
7
+ index :slug => 1
8
+
9
+ validates_presence_of :name
10
+ validates_uniqueness_of :name
11
+ validates_presence_of :slug
12
+ validates_uniqueness_of :slug
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module BusinessSupport
2
+ class Stage
3
+ include Mongoid::Document
4
+
5
+ field :name, type: String
6
+ field :slug, type: String
7
+ index :slug => 1
8
+
9
+ validates_presence_of :name
10
+ validates_uniqueness_of :name
11
+ validates_presence_of :slug
12
+ validates_uniqueness_of :slug
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module BusinessSupport
2
+ class SupportType
3
+ include Mongoid::Document
4
+
5
+ field :name, type: String
6
+ field :slug, type: String
7
+ index :slug => 1
8
+
9
+ validates_presence_of :name
10
+ validates_uniqueness_of :name
11
+ validates_presence_of :slug
12
+ validates_uniqueness_of :slug
13
+ end
14
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: utf-8
2
+ require "edition"
3
+
4
+ class BusinessSupportEdition < Edition
5
+
6
+ include Mongoid::MultiParameterAttributes
7
+
8
+ field :short_description, type: String
9
+ field :body, type: String
10
+ field :min_value, type: Integer
11
+ field :max_value, type: Integer
12
+ field :max_employees, type: Integer
13
+ field :organiser, type: String
14
+ field :eligibility, type: String
15
+ field :evaluation, type: String
16
+ field :additional_information, type: String
17
+ field :continuation_link, type: String
18
+ field :will_continue_on, type: String
19
+ field :contact_details, type: String
20
+ field :business_support_identifier, type: String
21
+
22
+ field :priority, type: Integer, default: 1
23
+ field :business_types, type: Array, default: []
24
+ field :business_sizes, type: Array, default: []
25
+ field :locations, type: Array, default: []
26
+ field :purposes, type: Array, default: []
27
+ field :sectors, type: Array, default: []
28
+ field :stages, type: Array, default: []
29
+ field :support_types, type: Array, default: []
30
+ field :start_date, type: Date
31
+ field :end_date, type: Date
32
+
33
+ index :business_support_identifier
34
+
35
+ GOVSPEAK_FIELDS = Edition::GOVSPEAK_FIELDS + [:body, :eligibility, :evaluation, :additional_information]
36
+
37
+ validate :min_must_be_less_than_max
38
+ validates :business_support_identifier, :presence => true
39
+ validate :business_support_identifier_unique
40
+ validates_format_of :continuation_link, :with => URI::regexp(%w(http https)), :allow_blank => true
41
+
42
+ # https://github.com/mongoid/mongoid/issues/1735 Really Mongoid‽
43
+ validates :min_value, :max_value, :max_employees, :numericality => {:allow_nil => true, :only_integer => true}
44
+
45
+ @fields_to_clone = [:body, :min_value, :max_value, :max_employees, :organiser,
46
+ :eligibility, :evaluation, :additional_information, :continuation_link,
47
+ :will_continue_on, :contact_details, :short_description,
48
+ :business_support_identifier]
49
+
50
+ def whole_body
51
+ [short_description, body].join("\n\n")
52
+ end
53
+
54
+ private
55
+
56
+ def min_must_be_less_than_max
57
+ if !min_value.nil? && !max_value.nil? && min_value > max_value
58
+ errors[:min_value] << "Min value must be smaller than max value"
59
+ errors[:max_value] << "Max value must be larger than min value"
60
+ end
61
+ end
62
+
63
+ def business_support_identifier_unique
64
+ if self.class.without_state('archived').where(:business_support_identifier => business_support_identifier,
65
+ :panopticon_id.ne => panopticon_id).any?
66
+ errors.add(:business_support_identifier, :taken)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,72 @@
1
+ require 'attachable'
2
+ require 'edition'
3
+
4
+ class CampaignEdition < Edition
5
+ include Attachable
6
+
7
+ field :body, type: String
8
+ field :organisation_formatted_name, type: String
9
+ field :organisation_url, type: String
10
+ field :organisation_brand_colour, type: String
11
+ field :organisation_crest, type: String
12
+
13
+ attaches :large_image, :medium_image, :small_image
14
+
15
+ GOVSPEAK_FIELDS = Edition::GOVSPEAK_FIELDS + [:body]
16
+ @fields_to_clone = [
17
+ :body, :large_image_id, :medium_image_id, :small_image_id,
18
+ :organisation_formatted_name, :organisation_url, :organisation_brand_colour, :organisation_crest
19
+ ]
20
+
21
+ BRAND_COLOURS = [
22
+ "attorney-generals-office",
23
+ "cabinet-office",
24
+ "department-for-business-innovation-skills",
25
+ "department-for-communities-and-local-government",
26
+ "department-for-culture-media-sport",
27
+ "department-for-education",
28
+ "department-for-environment-food-rural-affairs",
29
+ "department-for-international-development",
30
+ "department-for-transport",
31
+ "department-for-work-pensions",
32
+ "department-of-energy-climate-change",
33
+ "department-of-health",
34
+ "foreign-commonwealth-office",
35
+ "hm-government",
36
+ "hm-revenue-customs",
37
+ "hm-treasury",
38
+ "home-office",
39
+ "ministry-of-defence",
40
+ "ministry-of-justice",
41
+ "northern-ireland-office",
42
+ "office-of-the-advocate-general-for-scotland",
43
+ "office-of-the-leader-of-the-house-of-lords",
44
+ "scotland-office",
45
+ "the-office-of-the-leader-of-the-house-of-commons",
46
+ "uk-export-finance",
47
+ "uk-trade-investment",
48
+ "wales-office"
49
+ ]
50
+ CRESTS = {
51
+ "No identity" => "no-identity",
52
+ "Single identity" => "single-identity",
53
+ "Department for Business, Innovation and Skills" => "bis",
54
+ "Scotland Office" => "so",
55
+ "Home Office" => "ho",
56
+ "Ministry of Defence" => "mod",
57
+ "Wales Office" => "wales",
58
+ "HM Coastguard" => "coastguard",
59
+ "Portcullis" => "portcullis",
60
+ "UK Hydrographic Office" => "ukho",
61
+ "Executive Office" => "eo",
62
+ "HM Revenue and Customs" => "hmrc",
63
+ "UK Atomic Energy Authority" => "ukaea"
64
+ }
65
+
66
+ validates :organisation_brand_colour, :inclusion => { :in => BRAND_COLOURS, :allow_blank => true }
67
+ validates :organisation_crest, :inclusion => { :in => CRESTS.values, :allow_blank => true }
68
+
69
+ def whole_body
70
+ self.body
71
+ end
72
+ end
@@ -0,0 +1,14 @@
1
+ require "edition"
2
+
3
+ class CompletedTransactionEdition < Edition
4
+ field :body, type: String
5
+
6
+ GOVSPEAK_FIELDS = Edition::GOVSPEAK_FIELDS + [:body]
7
+
8
+ @fields_to_clone = [:body]
9
+
10
+ def whole_body
11
+ self.body
12
+ end
13
+
14
+ end
@@ -0,0 +1,32 @@
1
+ require "slug_validator"
2
+ require "traits/taggable"
3
+ require "safe_html"
4
+
5
+ class CuratedList
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+
9
+ include Taggable
10
+ stores_tags_for :sections
11
+
12
+ field "slug", type: String
13
+ has_and_belongs_to_many :artefacts, class_name: "Artefact"
14
+
15
+ index "slug"
16
+
17
+ GOVSPEAK_FIELDS = []
18
+
19
+ validates :slug, presence: true, uniqueness: true, slug: true
20
+ validates_with SafeHtml
21
+
22
+ def self.find_by_slug(slug)
23
+ where(slug: slug).first
24
+ end
25
+
26
+ # Returns the artefacts in order, skipping missing artefacts
27
+ def artefacts
28
+ Artefact.where(:_id.in => artefact_ids).sort_by do |artefact|
29
+ artefact_ids.index(artefact.id)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,286 @@
1
+ require "workflow"
2
+ require "fact_check_address"
3
+
4
+ class Edition
5
+ include Mongoid::Document
6
+ include Mongoid::Timestamps
7
+ include Workflow
8
+
9
+ field :panopticon_id, type: String
10
+ field :version_number, type: Integer, default: 1
11
+ field :sibling_in_progress, type: Integer, default: nil
12
+ field :business_proposition, type: Boolean, default: false
13
+
14
+ field :title, type: String
15
+ field :created_at, type: DateTime, default: lambda { Time.zone.now }
16
+ field :overview, type: String
17
+ field :alternative_title, type: String
18
+ field :slug, type: String
19
+ field :section, type: String
20
+ field :department, type: String
21
+ field :rejected_count, type: Integer, default: 0
22
+ field :tags, type: String
23
+
24
+ field :assignee, type: String
25
+ field :creator, type: String
26
+ field :publisher, type: String
27
+ field :archiver, type: String
28
+
29
+ GOVSPEAK_FIELDS = []
30
+
31
+ belongs_to :assigned_to, class_name: "User"
32
+
33
+ scope :lined_up, where(state: "lined_up")
34
+ scope :draft, where(state: "draft")
35
+ scope :amends_needed, where(state: "amends_needed")
36
+ scope :in_review, where(state: "in_review")
37
+ scope :fact_check, where(state: "fact_check")
38
+ scope :fact_check_received, where(state: "fact_check_received")
39
+ scope :ready, where(state: "ready")
40
+ scope :published, where(state: "published")
41
+ scope :archived, where(state: "archived")
42
+ scope :in_progress, where(:state.nin => ["archived", "published"])
43
+ scope :assigned_to, lambda { |user|
44
+ if user
45
+ where(assigned_to_id: user.id)
46
+ else
47
+ where(:assigned_to_id.exists => false)
48
+ end
49
+ }
50
+
51
+ validates :title, presence: true
52
+ validates :version_number, presence: true
53
+ validates :panopticon_id, presence: true
54
+ validates_with SafeHtml
55
+
56
+ before_save :check_for_archived_artefact
57
+ before_destroy :destroy_artefact
58
+
59
+ index "assigned_to_id"
60
+ index "panopticon_id"
61
+ index "state"
62
+
63
+ class << self; attr_accessor :fields_to_clone end
64
+ @fields_to_clone = []
65
+
66
+ alias_method :admin_list_title, :title
67
+
68
+ def series
69
+ Edition.where(panopticon_id: panopticon_id)
70
+ end
71
+
72
+ def history
73
+ series.order([:version_number, :desc])
74
+ end
75
+
76
+ def siblings
77
+ series.excludes(id: id)
78
+ end
79
+
80
+ def previous_siblings
81
+ siblings.where(:version_number.lt => version_number)
82
+ end
83
+
84
+ def subsequent_siblings
85
+ siblings.where(:version_number.gt => version_number)
86
+ end
87
+
88
+ def latest_edition?
89
+ subsequent_siblings.empty?
90
+ end
91
+
92
+ def published_edition
93
+ series.where(state: "published").order(version_number: "desc").first
94
+ end
95
+
96
+ def previous_published_edition
97
+ series.where(state: "published").order(version_number: "desc").second
98
+ end
99
+
100
+ def in_progress_sibling
101
+ subsequent_siblings.in_progress.order(version_number: "desc").first
102
+ end
103
+
104
+ def can_create_new_edition?
105
+ subsequent_siblings.in_progress.empty?
106
+ end
107
+
108
+ def meta_data
109
+ PublicationMetadata.new self
110
+ end
111
+
112
+ def fact_check_email_address
113
+ FactCheckAddress.new.for_edition(self)
114
+ end
115
+
116
+ def get_next_version_number
117
+ latest_version = series.order(version_number: "desc").first.version_number
118
+ latest_version + 1
119
+ end
120
+
121
+ def indexable_content
122
+ respond_to?(:parts) ? indexable_content_with_parts : indexable_content_without_parts
123
+ end
124
+
125
+ def indexable_content_without_parts
126
+ if respond_to?(:body)
127
+ "#{alternative_title} #{Govspeak::Document.new(body).to_text}".strip
128
+ else
129
+ alternative_title
130
+ end
131
+ end
132
+
133
+ def indexable_content_with_parts
134
+ content = indexable_content_without_parts
135
+ return content unless published_edition
136
+ parts.inject([content]) { |acc, part|
137
+ acc.concat([part.title, Govspeak::Document.new(part.body).to_text])
138
+ }.compact.join(" ").strip
139
+ end
140
+
141
+ # If the new clone is of the same type, we can copy all its fields over; if
142
+ # we are changing the type of the edition, any fields other than the base
143
+ # fields will likely be meaningless.
144
+ def fields_to_copy(edition_class)
145
+ edition_class == self.class ? self.class.fields_to_clone : []
146
+ end
147
+
148
+ def build_clone(edition_class=nil)
149
+ unless state == "published"
150
+ raise "Cloning of non published edition not allowed"
151
+ end
152
+ unless can_create_new_edition?
153
+ raise "Cloning of a published edition when an in-progress edition exists
154
+ is not allowed"
155
+ end
156
+
157
+ edition_class = self.class unless edition_class
158
+ new_edition = edition_class.new(title: self.title,
159
+ version_number: get_next_version_number)
160
+
161
+ real_fields_to_merge = fields_to_copy(edition_class) +
162
+ [:panopticon_id, :overview, :alternative_title,
163
+ :slug, :section, :department]
164
+
165
+ real_fields_to_merge.each do |attr|
166
+ new_edition[attr] = read_attribute(attr)
167
+ end
168
+
169
+ if edition_class == AnswerEdition and self.class == GuideEdition
170
+ new_edition.body = whole_body
171
+ end
172
+
173
+ if edition_class == GuideEdition and self.class == AnswerEdition
174
+ new_edition.parts.build(title: "Part One", body: whole_body,
175
+ slug: "part-one")
176
+ end
177
+
178
+ new_edition
179
+ end
180
+
181
+ def self.find_or_create_from_panopticon_data(panopticon_id,
182
+ importing_user, api_credentials)
183
+ existing_publication = Edition.where(panopticon_id: panopticon_id)
184
+ .order_by([:version_number, :desc]).first
185
+ return existing_publication if existing_publication
186
+
187
+ raise "Artefact not found" unless metadata = Artefact.find(panopticon_id)
188
+
189
+ importing_user.create_edition(metadata.kind.to_sym,
190
+ panopticon_id: metadata.id,
191
+ slug: metadata.slug,
192
+ title: metadata.name,
193
+ section: metadata.section,
194
+ department: metadata.department,
195
+ business_proposition: metadata.business_proposition)
196
+ end
197
+
198
+ def self.find_and_identify(slug, edition)
199
+ scope = where(slug: slug)
200
+
201
+ if edition.present? and edition == "latest"
202
+ scope.order_by(:version_number).last
203
+ elsif edition.present?
204
+ scope.where(version_number: edition).first
205
+ else
206
+ scope.where(state: "published").order_by(:created_at).last
207
+ end
208
+ end
209
+
210
+ def panopticon_uri
211
+ Plek.current.find("panopticon") + "/artefacts/" + (panopticon_id || slug).to_s
212
+ end
213
+
214
+ def format
215
+ self.class.to_s.gsub("Edition", "")
216
+ end
217
+
218
+ def format_name
219
+ format
220
+ end
221
+
222
+ def has_video?
223
+ false
224
+ end
225
+
226
+ def safe_to_preview?
227
+ true
228
+ end
229
+
230
+ def has_sibling_in_progress?
231
+ ! sibling_in_progress.nil?
232
+ end
233
+
234
+ # Stop broadcasting a delete message unless there are no siblings.
235
+ def broadcast_action(callback_action)
236
+ unless callback_action == "destroyed" and self.siblings.any?
237
+ super(callback_action)
238
+ end
239
+ end
240
+
241
+ def was_published
242
+ previous_siblings.all.each(&:archive)
243
+ notify_siblings_of_published_edition
244
+ end
245
+
246
+ def update_from_artefact(artefact)
247
+ self.title = artefact.name unless published?
248
+ self.slug = artefact.slug
249
+ self.section = artefact.section
250
+ self.department = artefact.department
251
+ self.business_proposition = artefact.business_proposition
252
+ self.save!
253
+ end
254
+
255
+ def check_for_archived_artefact
256
+ if panopticon_id
257
+ a = Artefact.find(panopticon_id)
258
+ if a.state == "archived" and changed_attributes.any?
259
+ # If we're only changing the state to archived, that's ok
260
+ # Any other changes are not allowed
261
+ allowed_keys = ["state", "updated_at"]
262
+ unless ((changed_attributes.keys - allowed_keys).empty?) and state == "archived"
263
+ raise "Editing of an edition with an Archived artefact is not allowed"
264
+ end
265
+ end
266
+ end
267
+ end
268
+
269
+ def artefact
270
+ Artefact.find(panopticon_id)
271
+ end
272
+
273
+ # When we delete an edition is the only one in its series
274
+ # we delete the associated artefact to remove all trace of the
275
+ # item from the system.
276
+ #
277
+ # We don't do this by notifying panopticon as this will only ever
278
+ # happen for artefacts representing editions that haven't been
279
+ # published (and therefore aren't registered in the rest of the)
280
+ # system.
281
+ def destroy_artefact
282
+ if can_destroy? && siblings.empty?
283
+ Artefact.find(self.panopticon_id).destroy
284
+ end
285
+ end
286
+ end