diff --git a/app/controllers/admin/notes_controller.rb b/app/controllers/admin/notes_controller.rb new file mode 100644 index 00000000000..4c0310ac55e --- /dev/null +++ b/app/controllers/admin/notes_controller.rb @@ -0,0 +1,53 @@ +class Admin::NotesController < AdminController + include Admin::TagHelper + include TranslatableParams + + def new + @note = Note.new + @note.build_all_translations + end + + def create + @note = Note.new(note_params) + if @note.save + notice = 'Note successfully created.' + redirect_to admin_note_parent_path(@note), notice: notice + else + @note.build_all_translations + render :new + end + end + + def edit + @note = Note.find(params[:id]) + @note.build_all_translations + end + + def update + @note = Note.find(params[:id]) + if @note.update(note_params) + notice = 'Note successfully updated.' + redirect_to admin_note_parent_path(@note), notice: notice + else + @note.build_all_translations + render :edit + end + end + + def destroy + @note = Note.find(params[:id]) + @note.destroy + notice = 'Note successfully destroyed.' + redirect_to admin_note_parent_path(@note), notice: notice + end + + private + + def note_params + translatable_params( + params.require(:note), + translated_keys: [:locale, :body], + general_keys: [:notable_id, :notable_type] + ) + end +end diff --git a/app/models/note.rb b/app/models/note.rb new file mode 100644 index 00000000000..13ed304fc8c --- /dev/null +++ b/app/models/note.rb @@ -0,0 +1,24 @@ +# == Schema Information +# Schema version: 20220720085105 +# +# Table name: notes +# +# id :bigint not null, primary key +# notable_type :string +# notable_id :bigint +# notable_tag :string +# created_at :datetime not null +# updated_at :datetime not null +# body :text +# + +class Note < ApplicationRecord + include AdminColumn + + translates :body + include Translatable + + belongs_to :notable, polymorphic: true + + validates :body, presence: true +end diff --git a/app/views/admin/notes/_form.html.erb b/app/views/admin/notes/_form.html.erb new file mode 100644 index 00000000000..f22f4e245b3 --- /dev/null +++ b/app/views/admin/notes/_form.html.erb @@ -0,0 +1,27 @@ +<%= foi_error_messages_for :note %> + +
+ + +
+ <% @note.ordered_translations.each do |translation| %> + <% if AlaveteliLocalization.default_locale?(translation.locale) %> + <%= fields_for('note', @note) do |t| %> + <%= render partial: 'locale_fields', locals: { t: t, locale: translation.locale } %> + <% end %> + <% else %> + <%= f.fields_for(:translations, translation, child_index: translation.locale) do |t| %> + <%= render partial: 'locale_fields', locals: { t: t, locale: translation.locale } %> + <% end %> + <% end %> + <% end %> +
+
diff --git a/app/views/admin/notes/_locale_fields.html.erb b/app/views/admin/notes/_locale_fields.html.erb new file mode 100644 index 00000000000..ebc0b5dcc17 --- /dev/null +++ b/app/views/admin/notes/_locale_fields.html.erb @@ -0,0 +1,16 @@ +
+
+ <%= t.hidden_field :locale, :value => locale %> + + <%= t.label :body, class: 'control-label' %> +
+ <% if AlaveteliLocalization.default_locale?(locale) && t.object.errors[:body].any? %> + + <% end %> + <%= t.text_area :body, class: 'span6', rows: 10 %> + <% if AlaveteliLocalization.default_locale?(locale) && t.object.errors[:body].any? %> + + <%end %> +
+
+
diff --git a/app/views/admin/notes/_note.html.erb b/app/views/admin/notes/_note.html.erb new file mode 100644 index 00000000000..0607754d93c --- /dev/null +++ b/app/views/admin/notes/_note.html.erb @@ -0,0 +1,24 @@ +
+
+ + <%= link_to chevron_right, "##{dom_id(note)}", data: { toggle: 'collapse', parent: 'notes' } %> + <%= link_to(note.body, edit_admin_note_path(note), title: 'view full details') %> + + + +
+ <%= tag.div id: dom_id(note), class: 'item-detail accordion-body collapse row' do %> + <% note.for_admin_column do |name, value, type| %> +
+ + <%= name %> + + + <%= admin_value(value) %> + +
+ <% end %> + <% end %> +
diff --git a/app/views/admin/notes/edit.erb b/app/views/admin/notes/edit.erb new file mode 100644 index 00000000000..0ad11316c60 --- /dev/null +++ b/app/views/admin/notes/edit.erb @@ -0,0 +1,21 @@ +
+
+ +
+
+ +<%= form_for [:admin, @note], class: 'form form-horizontal' do |f| %> + <%= render partial: 'form', locals: { f: f } %> +
+ <%= submit_tag 'Save', class: 'btn btn-success' %> +
+<% end %> + +<%= form_tag [:admin, @note], class: 'form form-inline', method: 'delete' do %> + <%= submit_tag 'Destroy note', + class: 'btn btn-danger', + data: { confirm: 'Are you sure? This is irreversible.' } %> + (this is permanent!) +<% end %> diff --git a/app/views/admin/notes/new.html.erb b/app/views/admin/notes/new.html.erb new file mode 100644 index 00000000000..3dce3850c25 --- /dev/null +++ b/app/views/admin/notes/new.html.erb @@ -0,0 +1,14 @@ +
+
+ +
+
+ +<%= form_for [:admin, @note], class: 'form form-horizontal' do |f| %> + <%= render partial: 'form', locals: { f: f } %> +
+ <%= submit_tag 'Create', class: 'btn btn-success' %> +
+<% end %> diff --git a/config/routes.rb b/config/routes.rb index 81b44e1c3f6..0c03b659a62 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -485,6 +485,16 @@ end #### + #### AdminNote controller + namespace :admin do + resources :notes, except: [:index, :show] + end + + direct :admin_note_parent do |note| + admin_general_index_path + end + #### + #### AdminPublicBody controller scope '/admin', :as => 'admin' do resources :bodies, diff --git a/db/migrate/20220720085105_create_notes.rb b/db/migrate/20220720085105_create_notes.rb new file mode 100644 index 00000000000..5e50edb3b82 --- /dev/null +++ b/db/migrate/20220720085105_create_notes.rb @@ -0,0 +1,19 @@ +class CreateNotes < ActiveRecord::Migration[6.1] + def change + create_table :notes do |t| + t.references :notable, polymorphic: true + t.string :notable_tag + t.timestamps + end + + reversible do |dir| + dir.up do + Note.create_translation_table!(body: :text) + end + + dir.down do + Note.drop_translation_table! + end + end + end +end diff --git a/spec/controllers/admin/notes_controller_spec.rb b/spec/controllers/admin/notes_controller_spec.rb new file mode 100644 index 00000000000..6e5134004bd --- /dev/null +++ b/spec/controllers/admin/notes_controller_spec.rb @@ -0,0 +1,153 @@ +require 'spec_helper' + +RSpec.describe Admin::NotesController do + before(:each) { basic_auth_login(@request) } + + describe 'GET new' do + before { get :new } + + it 'returns a successful response' do + expect(response).to be_successful + end + + it 'assigns the note' do + expect(assigns[:note]).to be_a(Note) + end + + it 'renders the correct template' do + expect(response).to render_template(:new) + end + end + + describe 'POST #create' do + before do + post :create, params: params + end + + context 'on a successful create' do + let(:params) do + { note: { body: 'New body' } } + end + + it 'assigns the note' do + expect(assigns[:note]).to be_a(Note) + end + + it 'creates the note' do + expect(assigns[:note].body).to eq('New body') + end + + it 'sets a notice' do + expect(flash[:notice]).to eq('Note successfully created.') + end + + it 'redirects to the general index' do + expect(response).to redirect_to(admin_general_index_path) + end + end + + context 'on an unsuccessful create' do + let(:params) do + { note: { body: '' } } + end + + it 'assigns the note' do + expect(assigns[:note]).to be_a(Note) + end + + it 'does not create the note' do + expect(assigns[:note]).to be_new_record + end + + it 'renders the form again' do + expect(response).to render_template(:new) + end + end + end + + describe 'GET edit' do + let!(:note) { FactoryBot.create(:note) } + + before { get :edit, params: { id: note.id } } + + it 'returns a successful response' do + expect(response).to be_successful + end + + it 'assigns the note' do + expect(assigns[:note]).to eq(note) + end + + it 'renders the correct template' do + expect(response).to render_template(:edit) + end + end + + describe 'PATCH #update' do + let!(:note) { FactoryBot.create(:note) } + + before do + patch :update, params: params + end + + context 'on a successful update' do + let(:params) do + { id: note.id, note: { body: 'New body' } } + end + + it 'assigns the note' do + expect(assigns[:note]).to eq(note) + end + + it 'updates the note' do + expect(note.reload.body).to eq('New body') + end + + it 'sets a notice' do + expect(flash[:notice]).to eq('Note successfully updated.') + end + + it 'redirects to the general index' do + expect(response).to redirect_to(admin_general_index_path) + end + end + + context 'on an unsuccessful update' do + let(:params) do + { id: note.id, note: { body: '' } } + end + + it 'assigns the note' do + expect(assigns[:note]).to eq(note) + end + + it 'does not update the note' do + expect(note.reload.body).not_to be_blank + end + + it 'renders the form again' do + expect(response).to render_template(:edit) + end + end + end + + describe 'DELETE #destroy' do + let!(:note) { FactoryBot.create(:note) } + + it 'destroys the note' do + allow(Note).to receive(:find).and_return(note) + expect(note).to receive(:destroy) + delete :destroy, params: { id: note.id } + end + + it 'sets a notice' do + delete :destroy, params: { id: note.id } + expect(flash[:notice]).to eq('Note successfully destroyed.') + end + + it 'redirects to the general index' do + delete :destroy, params: { id: note.id } + expect(response).to redirect_to(admin_general_index_path) + end + end +end diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb new file mode 100644 index 00000000000..291dc8a5421 --- /dev/null +++ b/spec/factories/notes.rb @@ -0,0 +1,23 @@ +# == Schema Information +# Schema version: 20220720085105 +# +# Table name: notes +# +# id :bigint not null, primary key +# notable_type :string +# notable_id :bigint +# notable_tag :string +# created_at :datetime not null +# updated_at :datetime not null +# body :text +# + +FactoryBot.define do + factory :note do + body { 'Test note' } + + trait :for_public_body do + association :notable, factory: :public_body + end + end +end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb new file mode 100644 index 00000000000..952605250fc --- /dev/null +++ b/spec/models/note_spec.rb @@ -0,0 +1,48 @@ +# == Schema Information +# Schema version: 20220720085105 +# +# Table name: notes +# +# id :bigint not null, primary key +# notable_type :string +# notable_id :bigint +# notable_tag :string +# created_at :datetime not null +# updated_at :datetime not null +# body :text +# + +require 'spec_helper' + +RSpec.describe Note, type: :model do + let(:note) { FactoryBot.build(:note) } + + describe 'validations' do + specify { expect(note).to be_valid } + + it 'requires body' do + note.body = nil + expect(note).not_to be_valid + end + end + + describe 'translations' do + before { note.save! } + + it 'adds translated body' do + expect(note.body_translations).to_not include(es: 'body') + AlaveteliLocalization.with_locale(:es) { note.body = 'body' } + expect(note.body_translations).to include(es: 'body') + end + end + + describe 'associations' do + context 'when info request cited' do + let(:note) { FactoryBot.build(:note, :for_public_body) } + + it 'belongs to a public body via polymorphic notable' do + expect(note.notable).to be_a PublicBody + end + end + end +end