diff --git a/app/views/super_admin/notifications/new.html.erb b/app/views/super_admin/notifications/new.html.erb
new file mode 100644
index 0000000..0e4b494
--- /dev/null
+++ b/app/views/super_admin/notifications/new.html.erb
@@ -0,0 +1,3 @@
+
<%= _('New notification') %>
+
+<%= render 'form' %>
diff --git a/config/routes.rb b/config/routes.rb
index 5e2376b..19a1333 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -61,6 +61,7 @@
put 'update_email_preferences'
put 'org_swap', constraints: {format: [:json]}
end
+ post '/acknowledge_notification', to: 'users#acknowledge_notification'
end
#organisation admin area
@@ -271,6 +272,10 @@
resources :themes, only: [] do
get 'index/:page', action: :index, on: :collection, as: :index
end
+ # Paginable actions for notifications
+ resources :notifications, only: [] do
+ get 'index/:page', action: :index, on: :collection, as: :index
+ end
# Paginable actions for templates
resources :templates, only: [] do
get 'all/:page', action: :all, on: :collection, as: :all
@@ -315,5 +320,6 @@
resources :orgs, only: [:index, :new, :create, :edit, :update, :destroy]
resources :themes, only: [:index, :new, :create, :edit, :update, :destroy]
resources :users, only: [:edit, :update]
+ resources :notifications
end
end
diff --git a/db/migrate/20180123161959_change_long_strings_to_text.rb b/db/migrate/20180123161959_change_long_strings_to_text.rb
index 47cef6d..c2ef100 100644
--- a/db/migrate/20180123161959_change_long_strings_to_text.rb
+++ b/db/migrate/20180123161959_change_long_strings_to_text.rb
@@ -1,9 +1,8 @@
class ChangeLongStringsToText < ActiveRecord::Migration
def change
- change_column :orgs, :links, :text
- change_column :templates, :links, :text
- change_column :identifier_schemes, :logo_url, :text
- change_column :identifier_schemes, :user_landing_url, :text
+ # change_column :orgs, :links, :text
+ # change_column :templates, :links, :text
+ # change_column :identifier_schemes, :logo_url, :text
+ # change_column :identifier_schemes, :user_landing_url, :text
end
end
-
diff --git a/db/migrate/20180328115455_create_notifications.rb b/db/migrate/20180328115455_create_notifications.rb
new file mode 100644
index 0000000..c6be87e
--- /dev/null
+++ b/db/migrate/20180328115455_create_notifications.rb
@@ -0,0 +1,15 @@
+class CreateNotifications < ActiveRecord::Migration
+ def change
+ create_table :notifications do |t|
+ t.integer :notification_type
+ t.string :title
+ t.integer :level
+ t.text :body
+ t.boolean :dismissable
+ t.date :starts_at
+ t.date :expires_at
+
+ t.timestamps null: false
+ end
+ end
+end
diff --git a/db/migrate/20180412092647_create_notification_acknowledgements.rb b/db/migrate/20180412092647_create_notification_acknowledgements.rb
new file mode 100644
index 0000000..bbb06cf
--- /dev/null
+++ b/db/migrate/20180412092647_create_notification_acknowledgements.rb
@@ -0,0 +1,10 @@
+class CreateNotificationAcknowledgements < ActiveRecord::Migration
+ def change
+ create_table :notification_acknowledgements do |t|
+ t.belongs_to :user, foreign_key: true, index: true
+ t.belongs_to :notification, foreign_key: true, index: true
+
+ t.timestamps null: true
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index ca41822..475283f 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,10 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180315161757) do
-
- # These are extensions that must be enabled in order to support this database
- enable_extension "plpgsql"
+ActiveRecord::Schema.define(version: 20180412092647) do
create_table "annotations", force: :cascade do |t|
t.integer "question_id"
@@ -80,7 +77,7 @@
create_table "friendly_id_slugs", force: :cascade do |t|
t.string "slug", null: false
t.integer "sluggable_id", null: false
- t.string "sluggable_type", limit: 40
+ t.string "sluggable_type"
t.datetime "created_at"
end
@@ -139,6 +136,28 @@
add_index "notes", ["answer_id"], name: "index_notes_on_answer_id"
+ create_table "notification_acknowledgements", force: :cascade do |t|
+ t.integer "user_id"
+ t.integer "notification_id"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "notification_acknowledgements", ["notification_id"], name: "index_notification_acknowledgements_on_notification_id"
+ add_index "notification_acknowledgements", ["user_id"], name: "index_notification_acknowledgements_on_user_id"
+
+ create_table "notifications", force: :cascade do |t|
+ t.integer "notification_type"
+ t.string "title"
+ t.integer "level"
+ t.text "body"
+ t.boolean "dismissable"
+ t.date "starts_at"
+ t.date "expires_at"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
create_table "org_identifiers", force: :cascade do |t|
t.string "identifier"
t.string "attrs"
@@ -384,11 +403,11 @@
create_table "users", force: :cascade do |t|
t.string "firstname"
t.string "surname"
- t.string "email", default: "", null: false
+ t.string "email", default: "", null: false
t.string "orcid_id"
t.string "shibboleth_id"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.string "encrypted_password", default: ""
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
@@ -426,42 +445,4 @@
add_index "users_perms", ["user_id"], name: "index_users_perms_on_user_id"
- add_foreign_key "annotations", "orgs"
- add_foreign_key "annotations", "questions"
- add_foreign_key "answers", "plans"
- add_foreign_key "answers", "questions"
- add_foreign_key "answers", "users"
- add_foreign_key "answers_question_options", "answers"
- add_foreign_key "answers_question_options", "question_options"
- add_foreign_key "guidance_groups", "orgs"
- add_foreign_key "guidances", "guidance_groups"
- add_foreign_key "notes", "answers"
- add_foreign_key "notes", "users"
- add_foreign_key "org_identifiers", "identifier_schemes"
- add_foreign_key "org_identifiers", "orgs"
- add_foreign_key "org_token_permissions", "orgs"
- add_foreign_key "org_token_permissions", "token_permission_types"
- add_foreign_key "orgs", "languages"
- add_foreign_key "orgs", "regions"
- add_foreign_key "phases", "templates"
- add_foreign_key "plans", "templates"
- add_foreign_key "plans_guidance_groups", "guidance_groups"
- add_foreign_key "plans_guidance_groups", "plans"
- add_foreign_key "question_options", "questions"
- add_foreign_key "questions", "question_formats"
- add_foreign_key "questions", "sections"
- add_foreign_key "questions_themes", "questions"
- add_foreign_key "questions_themes", "themes"
- add_foreign_key "roles", "plans"
- add_foreign_key "roles", "users"
- add_foreign_key "sections", "phases"
- add_foreign_key "templates", "orgs"
- add_foreign_key "themes_in_guidance", "guidances"
- add_foreign_key "themes_in_guidance", "themes"
- add_foreign_key "user_identifiers", "identifier_schemes"
- add_foreign_key "user_identifiers", "users"
- add_foreign_key "users", "languages"
- add_foreign_key "users", "orgs"
- add_foreign_key "users_perms", "perms"
- add_foreign_key "users_perms", "users"
end
diff --git a/db/seeds.rb b/db/seeds.rb
index 6257841..f41156e 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -5,8 +5,8 @@
# Identifier Schemes
# -------------------------------------------------------
identifier_schemes = [
- {name: 'orcid', description: 'ORCID', active: true,
- logo_url:'http://orcid.org/sites/default/files/images/orcid_16x16.png',
+ {name: 'orcid', description: 'ORCID', active: true,
+ logo_url:'http://orcid.org/sites/default/files/images/orcid_16x16.png',
user_landing_url:'https://orcid.org' },
{name: 'shibboleth', description: 'Your institutional credentials', active: true,
},
@@ -57,7 +57,7 @@
Dir.entries("#{Rails.root.join("config", "locales").to_s}").each do |f|
if f[-4..-1] == '.yml'
lang = f.gsub('.yml', '')
-
+
if Language.where(abbreviation: lang).empty?
Language.create!({
abbreviation: lang,
@@ -72,10 +72,10 @@
# Regions (create the super regions first and then create the rest)
# -------------------------------------------------------
regions = [
- {abbreviation: 'horizon',
+ {abbreviation: 'horizon',
description: 'European super region',
name: 'Horizon2020',
-
+
sub_regions: [
{abbreviation: 'uk',
description: 'United Kingdom',
@@ -90,7 +90,7 @@
description: 'Spain',
name: 'ES'}
]},
- {abbreviation: 'us',
+ {abbreviation: 'us',
description: 'United States of America',
name: 'US'}
]
@@ -99,9 +99,9 @@
regions.each do |r|
srs = r[:sub_regions]
r.delete(:sub_regions) unless r[:sub_regions].nil?
-
+
if Region.find_by(abbreviation: r[:abbreviation]).nil?
- region = Region.create!(r)
+ region = Region.create!(r)
unless srs.nil?
srs.each do |sr|
@@ -111,7 +111,7 @@
end
end
end
-
+
end
end
@@ -179,7 +179,7 @@
]
orgs.map{ |o| Org.create!(o) if Org.find_by(abbreviation: o[:abbreviation]).nil? }
-# Create a Super Admin associated with our generic organisation,
+# Create a Super Admin associated with our generic organisation,
# an Org Admin for our funder and an Org Admin and User for our University
# -------------------------------------------------------
users = [
@@ -229,7 +229,7 @@
users.map{ |u| User.create!(u) if User.find_by(email: u[:email]).nil? }
# Create a Guidance Group for our organisation and the funder
-# -------------------------------------------------------
+# -------------------------------------------------------
guidance_groups = [
{name: "Generic Guidance (provided by the example curation centre)",
org: Org.find_by(abbreviation: Rails.configuration.branding[:organisation][:abbreviation]),
@@ -243,17 +243,17 @@
guidance_groups.map{ |gg| GuidanceGroup.create!(gg) if GuidanceGroup.find_by(name: gg[:name]).nil? }
# Initialize with the generic Roadmap guidance and a sample funder guidance
-# -------------------------------------------------------
+# -------------------------------------------------------
guidances = [
{text: "● Give a summary of the data you will collect or create, noting the content, coverage and data type, e.g., tabular data, survey data, experimental measurements, models, software, audiovisual data, physical samples, etc.
-● Consider how your data could complement and integrate with existing data, or whether there are any existing data or methods that you could reuse.
+● Consider how your data could complement and integrate with existing data, or whether there are any existing data or methods that you could reuse.
● If purchasing or reusing existing data, explain how issues such as copyright and IPR have been addressed. You should aim to m
inimise any restrictions on the reuse (and subsequent sharing) of third-party data.",
guidance_group: GuidanceGroup.first,
published: true,
themes: [Theme.find_by(title: 'Data Description')]},
- {text: "● Clearly note what format(s) your data will be in, e.g., plain text (.txt), comma-separated values (.csv), geo-referenced TIFF (.tif, .tfw).
-● Explain why you have chosen certain formats. Decisions may be based on staff expertise, a preference for open formats, the standards accepted by data centres or widespread usage within a given community.
+ {text: "● Clearly note what format(s) your data will be in, e.g., plain text (.txt), comma-separated values (.csv), geo-referenced TIFF (.tif, .tfw).
+● Explain why you have chosen certain formats. Decisions may be based on staff expertise, a preference for open formats, the standards accepted by data centres or widespread usage within a given community.
● Using standardised, interchangeable or open formats ensures the long-term usability of data; these are recommended for sharing and archiving.
● See UK Data Service guidance on recommended formats or DataONE Best Practices for file formats",
guidance_group: GuidanceGroup.first,
@@ -265,9 +265,9 @@
guidance_group: GuidanceGroup.first,
published: true,
themes: [Theme.find_by(title: 'Data Volume')]},
- {text: "● Outline how the data will be collected and processed. This should cover relevant standards or methods, quality assurance and data organisation.
+ {text: "● Outline how the data will be collected and processed. This should cover relevant standards or methods, quality assurance and data organisation.
● Indicate how the data will be organised during the project, mentioning, e.g., naming conventions, version control and folder structures. Consistent, well-ordered research data will be easier to find, understand and reuse
-● Explain how the consistency and quality of data collection will be controlled and documented. This may include processes such as calibration, repeat samples or measurements, standardised data capture, data entry validation, peer review of data or representation with controlled vocabularies.
+● Explain how the consistency and quality of data collection will be controlled and documented. This may include processes such as calibration, repeat samples or measurements, standardised data capture, data entry validation, peer review of data or representation with controlled vocabularies.
● See the DataOne Best Practices for data quality",
guidance_group: GuidanceGroup.first,
published: true,
@@ -287,23 +287,23 @@
guidance_group: GuidanceGroup.first,
published: true,
themes: [Theme.find_by(title: 'Ethics & Privacy')]},
- {text: "● State who will own the copyright and IPR of any new data that you will generate. For multi-partner projects, IPR ownership should be covered in the consortium agreement.
-● Outline any restrictions needed on data sharing, e.g., to protect proprietary or patentable data.
+ {text: "● State who will own the copyright and IPR of any new data that you will generate. For multi-partner projects, IPR ownership should be covered in the consortium agreement.
+● Outline any restrictions needed on data sharing, e.g., to protect proprietary or patentable data.
● Explain how the data will be licensed for reuse. See the DCC guide on How to license research data and EUDAT’s data and software licensing wizard.",
guidance_group: GuidanceGroup.first,
published: true,
themes: [Theme.find_by(title: 'Intellectual Property Rights')]},
{text: "● Describe where the data will be stored and backed up during the course of research activities. This may vary if you are doing fieldwork or working across multiple sites so explain each procedure.
-● Identify who will be responsible for backup and how often this will be performed. The use of robust, managed storage with automatic backup, for example, that provided by university IT teams, is preferable. Storing data on laptops, computer hard drives or external storage devices alone is very risky.
+● Identify who will be responsible for backup and how often this will be performed. The use of robust, managed storage with automatic backup, for example, that provided by university IT teams, is preferable. Storing data on laptops, computer hard drives or external storage devices alone is very risky.
● See UK Data Service Guidance on data storage or DataONE Best Practices for storage
-● Also consider data security, particularly if your data is sensitive e.g., detailed personal data, politically sensitive information or trade secrets. Note the main risks and how these will be managed.
+● Also consider data security, particularly if your data is sensitive e.g., detailed personal data, politically sensitive information or trade secrets. Note the main risks and how these will be managed.
● Identify any formal standards that you will comply with, e.g., ISO 27001. See the DCC Briefing Paper on Information Security Management -ISO 27000 and UK Data Service guidance on data security",
guidance_group: GuidanceGroup.first,
published: true,
themes: [Theme.find_by(title: 'Storage & Security')]},
- {text: "● How will you share the data e.g. deposit in a data repository, use a secure data service, handle data requests directly or use another mechanism? The methods used will depend on a number of factors such as the type, size, complexity and sensitivity of the data.
-● When will you make the data available? Research funders expect timely release. They typically allow embargoes but not prolonged exclusive use.
-● Who will be able to use your data? If you need to restricted access to certain communities or apply data sharing agreements, explain why.
+ {text: "● How will you share the data e.g. deposit in a data repository, use a secure data service, handle data requests directly or use another mechanism? The methods used will depend on a number of factors such as the type, size, complexity and sensitivity of the data.
+● When will you make the data available? Research funders expect timely release. They typically allow embargoes but not prolonged exclusive use.
+● Who will be able to use your data? If you need to restricted access to certain communities or apply data sharing agreements, explain why.
● Consider strategies to minimise restrictions on sharing. These may include anonymising or aggregating data, gaining participant consent for data sharing, gaining copyright permissions, and agreeing a limited embargo period.
● How might your data be reused in other contexts? Where there is potential for reuse, you should use standards and formats that facilitate this, and ensure that appropriate metadata is available online so your data can be discovered. Persistent identifiers should be applied so people can reliably and efficiently find your data. They also help you to track citations and reuse.",
guidance_group: GuidanceGroup.first,
@@ -321,19 +321,19 @@
guidance_group: GuidanceGroup.first,
published: true,
themes: [Theme.find_by(title: 'Preservation')]},
- {text: "● Outline the roles and responsibilities for all activities, e.g., data capture, metadata production, data quality, storage and backup, data archiving & data sharing. Individuals should be named where possible.
+ {text: "● Outline the roles and responsibilities for all activities, e.g., data capture, metadata production, data quality, storage and backup, data archiving & data sharing. Individuals should be named where possible.
● For collaborative projects you should explain the coordination of data management responsibilities across partners.
● See UK Data Service guidance on data management roles and responsibilities or DataONE Best Practices: Define roles and assign responsibilities for data management",
guidance_group: GuidanceGroup.first,
published: true,
themes: [Theme.find_by(title: 'Roles & Responsibilities')]},
{text: "● Carefully consider and justify any resources needed to deliver the plan. These may include storage costs, hardware, staff time, costs of preparing data for deposit and repository charges.
-● Outline any relevant technical expertise, support and training that is likely to be required and how it will be acquired.
+● Outline any relevant technical expertise, support and training that is likely to be required and how it will be acquired.
● If you are not depositing in a data repository, ensure you have appropriate resources and systems in place to share and preserve the data. See UK Data Service guidance on costing data management",
guidance_group: GuidanceGroup.first,
published: true,
themes: [Theme.find_by(title: 'Budget')]},
- {text: "● Consider whether there are any existing procedures that you can base your approach on. If your group/department has local guidelines that you work to, point to them here.
+ {text: "● Consider whether there are any existing procedures that you can base your approach on. If your group/department has local guidelines that you work to, point to them here.
● List any other relevant funder, institutional, departmental or group policies on data management, data sharing and data security. ",
guidance_group: GuidanceGroup.first,
published: true,
@@ -346,7 +346,7 @@
guidances.map{ |g| Guidance.create!(g) if Guidance.find_by(text: g[:text]).nil? }
# Create a default template for the curation centre and one for the example funder
-# -------------------------------------------------------
+# -------------------------------------------------------
templates = [
{title: "My Curation Center's Default Template",
description: "The default template",
@@ -358,7 +358,7 @@
dmptemplate_id: 1,
visibility: Template.visibilities[:publicly_visible],
links: {"funder":[],"sample_plan":[]}},
-
+
{title: "OLD - Department of Testing Award",
published: false,
org: Org.find_by(abbreviation: 'GA'),
@@ -368,7 +368,7 @@
visibility: Template.visibilities[:organisationally_visible],
dmptemplate_id: 2,
links: {"funder":[],"sample_plan":[]}},
-
+
{title: "Department of Testing Award",
published: true,
org: Org.find_by(abbreviation: 'GA'),
@@ -379,11 +379,11 @@
dmptemplate_id: 3,
links: {"funder":[],"sample_plan":[]}}
]
-# Template creation calls defaults handler which sets is_default and
+# Template creation calls defaults handler which sets is_default and
# published to false automatically, so update them after creation
-templates.map do |t|
- if Template.find_by(title: t[:title]).nil?
- tmplt = Template.create!(t)
+templates.map do |t|
+ if Template.find_by(title: t[:title]).nil?
+ tmplt = Template.create!(t)
tmplt.published = t[:published]
tmplt.is_default = t[:is_default]
tmplt.visibility = t[:visibility]
@@ -392,18 +392,18 @@
end
# Create 2 phases for the funder's template and one for our generic template
-# -------------------------------------------------------
+# -------------------------------------------------------
phases = [
{title: "Generic Data Management Planning Template",
number: 1,
modifiable: false,
template: Template.find_by(title: "My Curation Center's Default Template")},
-
+
{title: "Detailed Overview",
number: 1,
modifiable: false,
template: Template.find_by(title: "OLD - Department of Testing Award")},
-
+
{title: "Preliminary Statement of Work",
number: 1,
modifiable: true,
@@ -420,7 +420,7 @@
funder_template_phase_2 = Phase.find_by(title: "Detailed Overview")
# Create sections for the 2 templates and their phases
-# -------------------------------------------------------
+# -------------------------------------------------------
sections = [
# Sections for the Generic Template
{title: "Data Collection",
@@ -464,7 +464,7 @@
number: 1,
published: false,
modifiable: true,
- phase: Phase.find_by(title: "Detailed Overview")},
+ phase: Phase.find_by(title: "Detailed Overview")},
# Sections for the Funder Template's Preliminary Phase
{title: "Data Overview",
@@ -510,7 +510,7 @@
text_area = QuestionFormat.find_by(title: "Text area")
# Create questions for the 2 templates and their phases
-# -------------------------------------------------------
+# -------------------------------------------------------
questions = [
# Questions for the Generic Template
{text: "What data will you collect or create?",
@@ -588,7 +588,7 @@
modifiable: false,
section: Section.find_by(title: "Responsibilities and Resources"),
question_format: text_area},
-
+
# Questions for old version of Funder Template
{text: "What data will you collect and how will it be obtained?",
number: 1,
@@ -600,7 +600,7 @@
modifiable: false,
section: Section.find_by(title: "Data Collection and Preservation"),
question_format: text_area},
-
+
# Questions for the Funder Template's Preliminary Phase
{text: "Provide an overview of the dataset.",
number: 1,
@@ -620,7 +620,7 @@
section: Section.find_by(title: "Data Description"),
question_format: text_area,
themes: [Theme.find_by(title: "Data Collection")]},
-
+
# Questions for the Funder Template's Detailed Phase
{text: "What is your policy for long term access to your dataset?",
number: 1,
@@ -689,11 +689,11 @@
questions.map{ |q| Question.create!(q) if Question.find_by(text: q[:text]).nil? }
drop_down_question = Question.find_by(text: "Where will you store your data during the research period?")
-multi_select_question = Question.find_by(text: "What type(s) of data will you collect?")
+multi_select_question = Question.find_by(text: "What type(s) of data will you collect?")
radio_button_question = Question.find_by(text: "Please select the appropriate formats.")
# Create suggested answers for a few questions
-# -------------------------------------------------------
+# -------------------------------------------------------
annotations = [
{text: "We will preserve it in Dryad or a similar data repository service.",
type: Annotation.types[:example_answer],
@@ -707,7 +707,7 @@
annotations.map{ |s| Annotation.create!(s) if Annotation.find_by(text: s[:text]).nil? }
# Create options for the dropdown, multi-select and radio buttons
-# -------------------------------------------------------
+# -------------------------------------------------------
question_options = [
{text: "csv files",
number: 1,
@@ -721,7 +721,7 @@
number: 3,
question: radio_button_question,
is_default: false},
-
+
{text: "local hard drive",
number: 1,
question: drop_down_question,
@@ -734,7 +734,7 @@
number: 3,
question: drop_down_question,
is_default: false},
-
+
{text: "statistical",
number: 1,
question: multi_select_question,
@@ -755,7 +755,7 @@
question_options.map{ |q| QuestionOption.create!(q) if QuestionOption.find_by(text: q[:text]).nil? }
# Create plans
-# -------------------------------------------------------
+# -------------------------------------------------------
=begin
plans = [
{title: "Sample plan",
@@ -787,7 +787,7 @@
plan: plan,
user: user,
question: Question.find_by(text: "How will you store the data and how will it be preserved?")},
-
+
{text: "We want people to be able to access it. ",
plan: plan,
user: user,
@@ -799,7 +799,7 @@
{plan: plan,
user: user,
question: multi_select_question,
- question_options: [QuestionOption.find_by(text: "image/video"),
+ question_options: [QuestionOption.find_by(text: "image/video"),
QuestionOption.find_by(text: "other")]},
{plan: plan,
user: user,
diff --git a/lib/assets/javascripts/application.js b/lib/assets/javascripts/application.js
index 17f32e2..0262849 100644
--- a/lib/assets/javascripts/application.js
+++ b/lib/assets/javascripts/application.js
@@ -49,3 +49,4 @@
import './views/usage/index';
import './views/users/notification_preferences';
import './views/users/admin_grant_permissions';
+import './views/super_admin/notifications/edit';
diff --git a/lib/assets/javascripts/views/super_admin/notifications/edit.js b/lib/assets/javascripts/views/super_admin/notifications/edit.js
new file mode 100644
index 0000000..7ec8378
--- /dev/null
+++ b/lib/assets/javascripts/views/super_admin/notifications/edit.js
@@ -0,0 +1,11 @@
+import ariatiseForm from '../../../utils/ariatiseForm';
+import { Tinymce } from '../../../utils/tinymce';
+
+$(() => {
+ Tinymce.init({
+ selector: '#notification_body',
+ forced_root_block: '',
+ toolbar: 'bold italic underline | link',
+ });
+ ariatiseForm({ selector: 'form.notification' });
+});
diff --git a/lib/tasks/notifications.rake b/lib/tasks/notifications.rake
new file mode 100644
index 0000000..c536e06
--- /dev/null
+++ b/lib/tasks/notifications.rake
@@ -0,0 +1,7 @@
+namespace :notifications do
+ desc "Create some notifications types"
+ task create_types: :environment do
+ NotificationType.create(name: 'global')
+ end
+
+end
diff --git a/test/functional/notifications_controller_test.rb b/test/functional/notifications_controller_test.rb
new file mode 100644
index 0000000..8616c5d
--- /dev/null
+++ b/test/functional/notifications_controller_test.rb
@@ -0,0 +1,83 @@
+require 'test_helper'
+module SuperAdmin
+ class NotificationsControllerTest < ActionController::TestCase
+ include Devise::Test::ControllerHelpers
+
+ setup do
+ @super_admin = User.find_by(email: 'super_admin@example.com')
+ scaffold_org_admin(Org.last)
+
+ @notification_attributes = {
+ notification_type: Notification.notification_types[:global],
+ title: 'notification_1',
+ level: Notification.levels[:info],
+ body: 'notification 1',
+ dismissable: true,
+ starts_at: Date.today,
+ expires_at: Date.tomorrow
+ }
+ @notification = Notification.create!(@notification_attributes)
+ end
+
+ test 'should get index' do
+ sign_in @super_admin
+ get :index
+ assert_response :success
+ assert_not_nil assigns(:notifications)
+ end
+
+ test 'should get new' do
+ sign_in @super_admin
+ get :new
+ assert_response :success
+ end
+
+ test 'should create notification' do
+ sign_in @super_admin
+ assert_difference('Notification.count') do
+ @notification_attributes[:level] = :info #controller is expecting the symbol instead of the numerical value
+ post :create, notification: @notification_attributes
+ end
+ assert_redirected_to super_admin_notifications_url
+ end
+
+ test 'should get edit' do
+ sign_in @super_admin
+ get :edit, id: @notification
+ assert_response :success
+ assert_not_nil assigns(:notification)
+ end
+
+ test 'should update notification' do
+ sign_in @super_admin
+ @notification_attributes[:title] = 'notification_2'
+ @notification_attributes[:level] = :info #controller is expecting the symbol instead of the numerical value
+ patch :update, id: @notification, notification: @notification_attributes
+ assert_redirected_to super_admin_notifications_url
+ end
+
+ test 'should destroy notification' do
+ sign_in @super_admin
+ assert_difference('Notification.count', -1) do
+ delete :destroy, id: @notification
+ end
+ assert_redirected_to super_admin_notifications_url
+ end
+
+ test 'unauthorized redirections' do
+ sign_in @user
+ get :index
+ assert_redirected_to(plans_url)
+ get :new
+ assert_redirected_to(plans_url)
+ post :create, notification: @notification_attributes
+ assert_redirected_to(plans_url)
+ get :edit, id: @notification
+ assert_redirected_to(plans_url)
+ patch :update, id: @notification, notification: @notification_attributes
+ assert_redirected_to(plans_url)
+ delete :destroy, id: @notification
+ assert_redirected_to(plans_url)
+ end
+ end
+end
diff --git a/test/unit/notification_test.rb b/test/unit/notification_test.rb
new file mode 100644
index 0000000..7be2e1b
--- /dev/null
+++ b/test/unit/notification_test.rb
@@ -0,0 +1,52 @@
+require 'test_helper'
+
+class NotificationTest < ActiveSupport::TestCase
+
+ setup do
+ @super_admin = User.find_by(email: 'super_admin@example.com')
+
+ @notification = Notification.create!(
+ notification_type: Notification.notification_types[:global],
+ title: 'notification_1',
+ level: Notification.levels[:info],
+ body: 'notification 1',
+ dismissable: true,
+ starts_at: Time.now,
+ expires_at: Time.now + 1.days)
+ end
+
+ # Validity
+ test 'validations valid' do
+ 1.upto(10) { |i| assert(@notification.valid?) }
+ end
+
+ # Date validation
+ test 'validations inconsistent dates' do
+ @notification.expires_at = Date.today - 1.days
+ assert_not(@notification.valid?)
+ end
+
+ # Missing parameters
+ test 'validations missing params' do
+ @notification.dismissable = nil
+ assert(@notification.valid?)
+ @notification.dismissable = false
+ @notification.notification_type = nil
+ assert_not(@notification.valid?)
+ @notification.notification_type = Notification.notification_types[:global]
+ @notification.title = nil
+ assert_not(@notification.valid?)
+ @notification.title = "Testing"
+ @notification.body = nil
+ assert_not(@notification.valid?)
+ @notification.body = "Testing"
+ @notification.level = nil
+ assert_not(@notification.valid?)
+ @notification.level = Notification.levels[:info]
+ @notification.starts_at = nil
+ assert_not(@notification.valid?)
+ @notification.starts_at = Time.now
+ @notification.expires_at = nil
+ assert_not(@notification.valid?)
+ end
+end
diff --git a/test/unit/perm_test.rb b/test/unit/perm_test.rb
index a40c704..a651315 100644
--- a/test/unit/perm_test.rb
+++ b/test/unit/perm_test.rb
@@ -1,7 +1,6 @@
require 'test_helper'
class PermTest < ActiveSupport::TestCase
-
setup do
@user = User.last
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
index 0fa96b6..9a1dbce 100644
--- a/test/unit/user_test.rb
+++ b/test/unit/user_test.rb
@@ -15,6 +15,15 @@
org: Org.last,
api_token: 'ABC123',
language: Language.find_by(abbreviation: I18n.locale))
+
+ @notification = Notification.create!(
+ notification_type: Notification.notification_types[:global],
+ title: 'notification_1',
+ level: Notification.levels[:info],
+ body: 'notification 1',
+ dismissable: false,
+ starts_at: Date.today,
+ expires_at: Date.tomorrow)
end
# ---------------------------------------------------
@@ -349,4 +358,16 @@
assert_equal(previous_api_token, @user.api_token)
assert_equal(previous_perms, @user.perms)
end
+
+ # Cannot dismiss Notifications that are non-dismissable
+ test 'cannot acknowledge a notification that is not dismissable' do
+ @user.acknowledge(@notification)
+ assert_not(@notification.acknowledged?(@user))
+ end
+ # Can dismiss Notifications that are dismissable
+ test 'can acknowledge a notification' do
+ @notification.update!(dismissable: true)
+ @user.acknowledge(@notification)
+ assert(@notification.acknowledged?(@user))
+ end
end