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.
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/.travis.yml +14 -0
- data/CONTRIBUTING.md +22 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +5 -0
- data/Rakefile +46 -0
- data/app/models/action.rb +60 -0
- data/app/models/answer_edition.rb +13 -0
- data/app/models/artefact.rb +341 -0
- data/app/models/artefact_action.rb +27 -0
- data/app/models/artefact_external_link.rb +15 -0
- data/app/models/business_support/business_size.rb +14 -0
- data/app/models/business_support/business_type.rb +14 -0
- data/app/models/business_support/location.rb +14 -0
- data/app/models/business_support/purpose.rb +14 -0
- data/app/models/business_support/sector.rb +14 -0
- data/app/models/business_support/stage.rb +14 -0
- data/app/models/business_support/support_type.rb +14 -0
- data/app/models/business_support_edition.rb +69 -0
- data/app/models/campaign_edition.rb +72 -0
- data/app/models/completed_transaction_edition.rb +14 -0
- data/app/models/curated_list.rb +32 -0
- data/app/models/edition.rb +286 -0
- data/app/models/expectant.rb +21 -0
- data/app/models/expectation.rb +12 -0
- data/app/models/guide_edition.rb +19 -0
- data/app/models/help_page_edition.rb +13 -0
- data/app/models/licence_edition.rb +35 -0
- data/app/models/local_authority.rb +58 -0
- data/app/models/local_interaction.rb +20 -0
- data/app/models/local_service.rb +49 -0
- data/app/models/local_transaction_edition.rb +49 -0
- data/app/models/overview_dashboard.rb +25 -0
- data/app/models/part.rb +28 -0
- data/app/models/parted.rb +32 -0
- data/app/models/place_edition.rb +20 -0
- data/app/models/programme_edition.rb +26 -0
- data/app/models/simple_smart_answer_edition.rb +66 -0
- data/app/models/simple_smart_answer_edition/node.rb +40 -0
- data/app/models/simple_smart_answer_edition/node/option.rb +31 -0
- data/app/models/tag.rb +88 -0
- data/app/models/transaction_edition.rb +28 -0
- data/app/models/travel_advice_edition.rb +177 -0
- data/app/models/user.rb +54 -0
- data/app/models/video_edition.rb +24 -0
- data/app/models/workflow.rb +217 -0
- data/app/models/workflow_actor.rb +141 -0
- data/app/traits/attachable.rb +60 -0
- data/app/traits/govspeak_smart_quotes_fixer.rb +19 -0
- data/app/traits/taggable.rb +113 -0
- data/app/validators/safe_html.rb +33 -0
- data/app/validators/slug_validator.rb +53 -0
- data/config/mongoid.yml +5 -0
- data/govuk_content_models.gemspec +42 -0
- data/jenkins.sh +7 -0
- data/lib/fact_check_address.rb +36 -0
- data/lib/govuk_content_models.rb +12 -0
- data/lib/govuk_content_models/require_all.rb +14 -0
- data/lib/govuk_content_models/test_helpers/factories.rb +213 -0
- data/lib/govuk_content_models/test_helpers/local_services.rb +24 -0
- data/lib/govuk_content_models/version.rb +4 -0
- data/test/fixtures/contactotron_api_response.json +1 -0
- data/test/fixtures/uploads/image.jpg +0 -0
- data/test/models/artefact_action_test.rb +123 -0
- data/test/models/artefact_external_link_test.rb +32 -0
- data/test/models/artefact_tag_test.rb +52 -0
- data/test/models/artefact_test.rb +583 -0
- data/test/models/business_support/business_size_test.rb +25 -0
- data/test/models/business_support/business_type_test.rb +25 -0
- data/test/models/business_support/location_test.rb +25 -0
- data/test/models/business_support/purpose_test.rb +29 -0
- data/test/models/business_support/sector_test.rb +25 -0
- data/test/models/business_support/stage_test.rb +25 -0
- data/test/models/business_support/support_type_test.rb +25 -0
- data/test/models/business_support_edition_test.rb +186 -0
- data/test/models/campaign_edition_test.rb +90 -0
- data/test/models/curated_list_test.rb +32 -0
- data/test/models/edition_test.rb +826 -0
- data/test/models/fact_check_address_test.rb +36 -0
- data/test/models/help_page_edition_test.rb +38 -0
- data/test/models/licence_edition_test.rb +104 -0
- data/test/models/local_authority_test.rb +113 -0
- data/test/models/local_service_test.rb +199 -0
- data/test/models/local_transaction_edition_test.rb +78 -0
- data/test/models/overview_dashboard_test.rb +47 -0
- data/test/models/simple_smart_answer_edition_test.rb +169 -0
- data/test/models/simple_smart_answer_node_test.rb +134 -0
- data/test/models/simple_smart_answer_option_test.rb +90 -0
- data/test/models/tag_test.rb +92 -0
- data/test/models/time_zone_test.rb +48 -0
- data/test/models/transaction_edition_test.rb +20 -0
- data/test/models/travel_advice_edition_test.rb +480 -0
- data/test/models/user_test.rb +114 -0
- data/test/models/video_edition_test.rb +64 -0
- data/test/models/workflow_actor_test.rb +61 -0
- data/test/models/workflow_test.rb +307 -0
- data/test/test_helper.rb +47 -0
- data/test/traits/attachable_test.rb +143 -0
- data/test/traits/taggable_test.rb +114 -0
- data/test/validators/safe_html_validator_test.rb +86 -0
- data/test/validators/slug_validator_test.rb +42 -0
- metadata +511 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require "edition"
|
|
2
|
+
|
|
3
|
+
class SimpleSmartAnswerEdition < Edition
|
|
4
|
+
class Node
|
|
5
|
+
include Mongoid::Document
|
|
6
|
+
embedded_in :edition, :class_name => "SimpleSmartAnswerEdition"
|
|
7
|
+
embeds_many :options, :class_name => "SimpleSmartAnswerEdition::Node::Option"
|
|
8
|
+
|
|
9
|
+
accepts_nested_attributes_for :options, :allow_destroy => true
|
|
10
|
+
|
|
11
|
+
field :slug, type: String
|
|
12
|
+
field :title, type: String
|
|
13
|
+
field :body, type: String
|
|
14
|
+
field :order, type: Integer
|
|
15
|
+
field :kind, type: String
|
|
16
|
+
|
|
17
|
+
default_scope order_by([:order, :asc])
|
|
18
|
+
|
|
19
|
+
GOVSPEAK_FIELDS = [:body]
|
|
20
|
+
|
|
21
|
+
KINDS = [
|
|
22
|
+
'question',
|
|
23
|
+
'outcome'
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
validates :title, :kind, presence: true
|
|
27
|
+
validates :kind, inclusion: { :in => KINDS }
|
|
28
|
+
validates :slug, :presence => true, :format => {:with => /\A[a-z0-9-]+\z/}
|
|
29
|
+
|
|
30
|
+
validate :outcomes_have_no_options
|
|
31
|
+
|
|
32
|
+
validates_with SafeHtml
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def outcomes_have_no_options
|
|
37
|
+
errors.add(:options, "cannot be added for an outcome") if options.present? and options.any? and kind == "outcome"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require "edition"
|
|
2
|
+
|
|
3
|
+
class SimpleSmartAnswerEdition < Edition
|
|
4
|
+
class Node
|
|
5
|
+
class Option
|
|
6
|
+
include Mongoid::Document
|
|
7
|
+
|
|
8
|
+
embedded_in :node, :class_name => "SimpleSmartAnswerEdition::Node"
|
|
9
|
+
|
|
10
|
+
field :label, type: String
|
|
11
|
+
field :slug, type: String
|
|
12
|
+
field :next_node, type: String
|
|
13
|
+
field :order, type: Integer
|
|
14
|
+
|
|
15
|
+
default_scope order_by([:order, :asc])
|
|
16
|
+
|
|
17
|
+
validates :label, :next_node, presence: true
|
|
18
|
+
validates :slug, :format => {:with => /\A[a-z0-9-]+\z/}
|
|
19
|
+
|
|
20
|
+
before_validation :populate_slug_if_blank
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def populate_slug_if_blank
|
|
25
|
+
if self.slug.blank? and self.label.present?
|
|
26
|
+
self.slug = ActiveSupport::Inflector.parameterize(self.label)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/app/models/tag.rb
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require "safe_html"
|
|
2
|
+
|
|
3
|
+
class Tag
|
|
4
|
+
include Mongoid::Document
|
|
5
|
+
field :tag_id, type: String
|
|
6
|
+
field :title, type: String
|
|
7
|
+
field :tag_type, type: String #TODO: list of accepted types?
|
|
8
|
+
field :description, type: String
|
|
9
|
+
field :short_description, type: String
|
|
10
|
+
|
|
11
|
+
field :parent_id, type: String
|
|
12
|
+
|
|
13
|
+
GOVSPEAK_FIELDS = []
|
|
14
|
+
|
|
15
|
+
index :tag_id
|
|
16
|
+
index [ [:tag_id, Mongo::ASCENDING], [:tag_type, Mongo::ASCENDING] ], unique: true
|
|
17
|
+
index :tag_type
|
|
18
|
+
|
|
19
|
+
validates_presence_of :tag_id, :title, :tag_type
|
|
20
|
+
validates_with SafeHtml
|
|
21
|
+
|
|
22
|
+
class MissingTags < RuntimeError
|
|
23
|
+
attr_reader :tag_ids
|
|
24
|
+
|
|
25
|
+
def initialize(tag_ids)
|
|
26
|
+
super("Missing tags: #{tag_ids.join(", ")}")
|
|
27
|
+
@tag_ids = tag_ids
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# This doesn't get set automatically: the code that loads tags
|
|
32
|
+
# should go through them and set this attribute manually
|
|
33
|
+
attr_accessor :uniquely_named
|
|
34
|
+
|
|
35
|
+
def as_json(options = {})
|
|
36
|
+
{
|
|
37
|
+
id: self.tag_id,
|
|
38
|
+
title: self.title,
|
|
39
|
+
type: self.tag_type,
|
|
40
|
+
description: self.description,
|
|
41
|
+
short_description: self.short_description
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def has_parent?
|
|
46
|
+
parent_id.present?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def parent
|
|
50
|
+
Tag.by_tag_id(parent_id, self.tag_type) if has_parent?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def unique_title
|
|
54
|
+
self.uniquely_named ? self.title : "#{self.title} [#{self.tag_id}]"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def to_s
|
|
58
|
+
title
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.by_tag_id(tag_id, tag_type = nil)
|
|
62
|
+
scope = tag_type ? Tag.where(tag_type: tag_type) : Tag
|
|
63
|
+
scope.where(tag_id: tag_id).first
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Retrieve a list of tags by tag ID. Any missing tags become `nil`.
|
|
67
|
+
def self.by_tag_ids(tag_id_list, tag_type = nil)
|
|
68
|
+
scope = tag_type ? Tag.where(tag_type: tag_type) : Tag
|
|
69
|
+
|
|
70
|
+
# Load up all the tags in a single query
|
|
71
|
+
tags = scope.any_in(tag_id: tag_id_list).to_a
|
|
72
|
+
tag_id_list.map { |tag_id| tags.find { |t| t.tag_id == tag_id } }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Retrieve a list of tags by tag ID. Any missing tags raise an exception.
|
|
76
|
+
def self.by_tag_ids!(tag_id_list, tag_type = nil)
|
|
77
|
+
tags = by_tag_ids(tag_id_list, tag_type)
|
|
78
|
+
if tags.any?(&:nil?)
|
|
79
|
+
# Find the tag IDs for which the resulting tag is nil
|
|
80
|
+
missing_ids = tag_id_list.zip(tags).select { |tag_id, tag|
|
|
81
|
+
tag.nil?
|
|
82
|
+
}.map(&:first)
|
|
83
|
+
raise MissingTags, missing_ids
|
|
84
|
+
else
|
|
85
|
+
tags
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require "edition"
|
|
2
|
+
require "expectant"
|
|
3
|
+
|
|
4
|
+
class TransactionEdition < Edition
|
|
5
|
+
|
|
6
|
+
include Expectant
|
|
7
|
+
|
|
8
|
+
field :introduction, type: String
|
|
9
|
+
field :will_continue_on, type: String
|
|
10
|
+
field :link, type: String
|
|
11
|
+
field :more_information, type: String
|
|
12
|
+
field :alternate_methods, type: String
|
|
13
|
+
|
|
14
|
+
GOVSPEAK_FIELDS = Edition::GOVSPEAK_FIELDS + [:introduction, :more_information, :alternate_methods]
|
|
15
|
+
|
|
16
|
+
@fields_to_clone = [:introduction, :will_continue_on, :link,
|
|
17
|
+
:more_information, :alternate_methods,
|
|
18
|
+
:minutes_to_complete, :uses_government_gateway,
|
|
19
|
+
:expectation_ids]
|
|
20
|
+
|
|
21
|
+
def indexable_content
|
|
22
|
+
"#{super} #{Govspeak::Document.new(introduction).to_text} #{Govspeak::Document.new(more_information).to_text}".strip
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def whole_body
|
|
26
|
+
[ self.link, self.introduction, self.more_information ].join("\n\n")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
require 'attachable'
|
|
2
|
+
require 'parted'
|
|
3
|
+
require 'state_machine'
|
|
4
|
+
require 'safe_html'
|
|
5
|
+
require 'govspeak_smart_quotes_fixer'
|
|
6
|
+
|
|
7
|
+
class TravelAdviceEdition
|
|
8
|
+
include Mongoid::Document
|
|
9
|
+
include Mongoid::Timestamps
|
|
10
|
+
include Parted
|
|
11
|
+
include Attachable
|
|
12
|
+
|
|
13
|
+
field :country_slug, type: String
|
|
14
|
+
field :title, type: String
|
|
15
|
+
field :overview, type: String
|
|
16
|
+
field :version_number, type: Integer
|
|
17
|
+
field :state, type: String, default: "draft"
|
|
18
|
+
field :alert_status, type: Array, default: [ ]
|
|
19
|
+
field :summary, type: String
|
|
20
|
+
field :change_description, type: String
|
|
21
|
+
field :minor_update, type: Boolean, default: false
|
|
22
|
+
field :synonyms, type: Array, default: [ ]
|
|
23
|
+
# This is the publicly presented publish time. For minor updates, this will be the publish time of the previous version
|
|
24
|
+
field :published_at, type: Time
|
|
25
|
+
field :reviewed_at, type: Time
|
|
26
|
+
|
|
27
|
+
embeds_many :actions
|
|
28
|
+
|
|
29
|
+
index [[:country_slug, Mongo::ASCENDING], [:version_number, Mongo::DESCENDING]], :unique => true
|
|
30
|
+
|
|
31
|
+
attaches :image, :document
|
|
32
|
+
|
|
33
|
+
GOVSPEAK_FIELDS = [:summary]
|
|
34
|
+
ALERT_STATUSES = [
|
|
35
|
+
"avoid_all_but_essential_travel_to_parts",
|
|
36
|
+
"avoid_all_but_essential_travel_to_whole_country",
|
|
37
|
+
"avoid_all_travel_to_parts",
|
|
38
|
+
"avoid_all_travel_to_whole_country",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
include GovspeakSmartQuotesFixer
|
|
42
|
+
before_validation :populate_version_number, :on => :create
|
|
43
|
+
|
|
44
|
+
validates_presence_of :country_slug, :title
|
|
45
|
+
validate :state_for_slug_unique
|
|
46
|
+
validates :version_number, :presence => true, :uniqueness => { :scope => :country_slug }
|
|
47
|
+
validate :alert_status_contains_valid_values
|
|
48
|
+
validate :first_version_cant_be_minor_update
|
|
49
|
+
validates_with SafeHtml
|
|
50
|
+
|
|
51
|
+
scope :published, where(:state => "published")
|
|
52
|
+
|
|
53
|
+
class << self; attr_accessor :fields_to_clone end
|
|
54
|
+
@fields_to_clone = [:title, :country_slug, :overview, :alert_status, :summary, :image_id, :document_id, :synonyms]
|
|
55
|
+
|
|
56
|
+
state_machine initial: :draft do
|
|
57
|
+
before_transition :draft => :published do |edition, transition|
|
|
58
|
+
if edition.minor_update
|
|
59
|
+
previous = edition.previous_version
|
|
60
|
+
edition.published_at = previous.published_at
|
|
61
|
+
edition.reviewed_at = previous.reviewed_at
|
|
62
|
+
edition.change_description = previous.change_description
|
|
63
|
+
else
|
|
64
|
+
edition.published_at = Time.zone.now.utc
|
|
65
|
+
edition.reviewed_at = edition.published_at
|
|
66
|
+
end
|
|
67
|
+
edition.class.where(country_slug: edition.country_slug, state: 'published').each do |ed|
|
|
68
|
+
ed.archive
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
event :publish do
|
|
73
|
+
transition draft: :published
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
event :archive do
|
|
77
|
+
transition all => :archived, :unless => :archived?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
state :published do
|
|
81
|
+
validate :cannot_edit_published
|
|
82
|
+
validates_presence_of :change_description, :unless => :minor_update, :message => "can't be blank on publish"
|
|
83
|
+
end
|
|
84
|
+
state :archived do
|
|
85
|
+
validate :cannot_edit_archived
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def indexable_content
|
|
90
|
+
strings = [Govspeak::Document.new(self.summary).to_text]
|
|
91
|
+
parts.each do |part|
|
|
92
|
+
strings << part.title << Govspeak::Document.new(part.body).to_text
|
|
93
|
+
end
|
|
94
|
+
strings.join(" ").strip
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def build_clone
|
|
99
|
+
new_edition = self.class.new
|
|
100
|
+
self.class.fields_to_clone.each do |attr|
|
|
101
|
+
new_edition[attr] = self.read_attribute(attr)
|
|
102
|
+
end
|
|
103
|
+
new_edition.parts = self.parts.map(&:dup)
|
|
104
|
+
new_edition
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def build_action_as(user, action_type, comment = nil)
|
|
108
|
+
actions.build(:requester => user, :request_type => action_type, :comment => comment)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def publish_as(user)
|
|
112
|
+
comment = self.minor_update ? 'Minor update' : Govspeak::Document.new(self.change_description).to_text
|
|
113
|
+
build_action_as(user, Action::PUBLISH, comment) && publish
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def previous_version
|
|
117
|
+
self.class.where(:country_slug => self.country_slug, :version_number.lt => self.version_number).order_by([:version_number, :desc]).first
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
def state_for_slug_unique
|
|
123
|
+
if %w(published draft).include?(self.state) and
|
|
124
|
+
self.class.where(:_id.ne => id,
|
|
125
|
+
:country_slug => country_slug,
|
|
126
|
+
:state => state).any?
|
|
127
|
+
errors.add(:state, :taken)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def populate_version_number
|
|
132
|
+
if self.version_number.nil? and ! self.country_slug.nil? and ! self.country_slug.empty?
|
|
133
|
+
if latest_edition = self.class.where(:country_slug => self.country_slug).order_by([:version_number, :desc]).first
|
|
134
|
+
self.version_number = latest_edition.version_number + 1
|
|
135
|
+
else
|
|
136
|
+
self.version_number = 1
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def cannot_edit_published
|
|
142
|
+
if anything_other_than_state_changed?('reviewed_at') and self.state_was != 'draft'
|
|
143
|
+
errors.add(:state, "must be draft to modify")
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def cannot_edit_archived
|
|
148
|
+
if anything_other_than_state_changed?
|
|
149
|
+
errors.add(:state, "must be draft to modify")
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def anything_other_than_state_changed?(*additional_allowed_fields)
|
|
154
|
+
self.changed? and ((real_fields_changed - ['state'] - additional_allowed_fields) != [] or self.parts.any?(&:changed?))
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def real_fields_changed
|
|
158
|
+
# There's an issue with dirty-tracking of Array fields. Merely accessing them will mark
|
|
159
|
+
# them as changed, but with no changes. This recifies that.
|
|
160
|
+
# this also allows changes when the change is something changing from nil to an empty array
|
|
161
|
+
self.changes.reject { |k, v|
|
|
162
|
+
v.nil? || v == [nil, []]
|
|
163
|
+
}.keys
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def alert_status_contains_valid_values
|
|
167
|
+
self.alert_status.each do |status|
|
|
168
|
+
errors.add(:alert_status, "is not in the list") unless ALERT_STATUSES.include?(status)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def first_version_cant_be_minor_update
|
|
173
|
+
if self.minor_update and self.version_number == 1
|
|
174
|
+
errors.add(:minor_update, "can't be set for first version")
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
data/app/models/user.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require "digest/md5"
|
|
2
|
+
require "cgi"
|
|
3
|
+
require "gds-sso/user"
|
|
4
|
+
require "workflow_actor"
|
|
5
|
+
require "safe_html"
|
|
6
|
+
|
|
7
|
+
class User
|
|
8
|
+
include Mongoid::Document
|
|
9
|
+
include Mongoid::Timestamps
|
|
10
|
+
include GDS::SSO::User
|
|
11
|
+
include WorkflowActor
|
|
12
|
+
|
|
13
|
+
# Let an app configure the collection name to use, e.g. set a constant in an
|
|
14
|
+
# initializer
|
|
15
|
+
def self.collection_name
|
|
16
|
+
defined?(USER_COLLECTION_NAME) ? USER_COLLECTION_NAME : "users"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
field "name", type: String
|
|
20
|
+
field "uid", type: String
|
|
21
|
+
field "version", type: Integer
|
|
22
|
+
field "email", type: String
|
|
23
|
+
field "permissions", type: Array
|
|
24
|
+
field "remotely_signed_out", type: Boolean, default: false
|
|
25
|
+
|
|
26
|
+
GOVSPEAK_FIELDS = []
|
|
27
|
+
|
|
28
|
+
# Setup accessible (or protected) attributes for your model
|
|
29
|
+
attr_accessible :email, :name, :uid
|
|
30
|
+
attr_accessible :email, :name, :uid, :permissions, as: :oauth
|
|
31
|
+
|
|
32
|
+
scope :alphabetized, order_by(name: :asc)
|
|
33
|
+
|
|
34
|
+
validates_with SafeHtml
|
|
35
|
+
|
|
36
|
+
# GDS::SSO specifically looks for find_by_uid within warden
|
|
37
|
+
# when loading authentication user from session
|
|
38
|
+
def self.find_by_uid(uid)
|
|
39
|
+
where(uid: uid).first
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def to_s
|
|
43
|
+
name || email || ""
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def gravatar_url(opts = {})
|
|
47
|
+
opts.symbolize_keys!
|
|
48
|
+
"%s.gravatar.com/avatar/%s%s" % [
|
|
49
|
+
opts[:ssl] ? "https://secure" : "http://www",
|
|
50
|
+
Digest::MD5.hexdigest(email.downcase),
|
|
51
|
+
opts[:s] ? "?s=#{CGI.escape(opts[:s])}" : ""
|
|
52
|
+
]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require "edition"
|
|
2
|
+
require "attachable"
|
|
3
|
+
|
|
4
|
+
class VideoEdition < Edition
|
|
5
|
+
include Attachable
|
|
6
|
+
|
|
7
|
+
field :video_url, type: String
|
|
8
|
+
field :video_summary, type: String
|
|
9
|
+
field :body, type: String
|
|
10
|
+
|
|
11
|
+
GOVSPEAK_FIELDS = Edition::GOVSPEAK_FIELDS + [:body]
|
|
12
|
+
|
|
13
|
+
@fields_to_clone = [:video_url, :video_summary, :body]
|
|
14
|
+
|
|
15
|
+
attaches :caption_file
|
|
16
|
+
|
|
17
|
+
def has_video?
|
|
18
|
+
video_url.present?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def whole_body
|
|
22
|
+
[video_summary, video_url, body].join("\n\n")
|
|
23
|
+
end
|
|
24
|
+
end
|