From ecab6eb8136634b071669f32b5e3a21677d9e2e2 Mon Sep 17 00:00:00 2001 From: Milos Mosic Date: Wed, 14 May 2014 15:33:23 +0200 Subject: [PATCH] Added versioning of data --- Gemfile | 2 + Gemfile.lock | 4 ++ .../rails_admin/custom/ui.js.coffee | 15 ++++++ .../rails_admin/custom/theming.css.scss | 4 ++ app/controllers/cameras_controller.rb | 6 +++ app/controllers/versions_controller.rb | 13 +++++ app/helpers/application_helper.rb | 1 + app/models/camera.rb | 1 + app/models/manufacturer.rb | 3 +- app/views/rails_admin/main/history.html.haml | 54 +++++++++++++++++++ config/initializers/rails_admin.rb | 8 +-- config/routes.rb | 3 ++ db/migrate/20140512121824_create_versions.rb | 13 +++++ ...12121825_add_object_changes_to_versions.rb | 5 ++ db/schema.rb | 14 ++++- lib/rails_admin_approve_change.rb | 15 ++++++ public/api-docs/api/v1/cameras.json | 2 +- 17 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 app/assets/javascripts/rails_admin/custom/ui.js.coffee create mode 100644 app/assets/stylesheets/rails_admin/custom/theming.css.scss create mode 100644 app/controllers/versions_controller.rb create mode 100644 app/views/rails_admin/main/history.html.haml create mode 100644 db/migrate/20140512121824_create_versions.rb create mode 100644 db/migrate/20140512121825_add_object_changes_to_versions.rb create mode 100644 lib/rails_admin_approve_change.rb diff --git a/Gemfile b/Gemfile index 582f53b..6620e0f 100644 --- a/Gemfile +++ b/Gemfile @@ -44,6 +44,8 @@ gem 'aws-sdk' gem 'stringex' +gem 'paper_trail', '~> 3.0.1' + group :production do gem 'rails_12factor' # gem 'unicorn' diff --git a/Gemfile.lock b/Gemfile.lock index 8667c28..a4dde3d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -180,6 +180,9 @@ GEM nokogiri (1.6.1-x86-mingw32) mini_portile (~> 0.5.0) orm_adapter (0.5.0) + paper_trail (3.0.1) + activerecord (>= 3.0, < 5.0) + activesupport (>= 3.0, < 5.0) paperclip (4.1.1) activemodel (>= 3.0.0) activesupport (>= 3.0.0) @@ -337,6 +340,7 @@ DEPENDENCIES jquery-turbolinks kaminari launchy + paper_trail (~> 3.0.1) paperclip pg rails (= 4.1.0) diff --git a/app/assets/javascripts/rails_admin/custom/ui.js.coffee b/app/assets/javascripts/rails_admin/custom/ui.js.coffee new file mode 100644 index 0000000..3a8f31a --- /dev/null +++ b/app/assets/javascripts/rails_admin/custom/ui.js.coffee @@ -0,0 +1,15 @@ +$(document).on 'rails_admin.dom_ready', -> + console.log 'loaded' + + $('.history_approve_contribution').on 'click', (e) -> + version_id = $(this).attr('data-id') + console.log 'clicked' + $.ajax + type: "POST" + url: "/versions/" + data: + version_id: version_id + dataType: "json" + success: (msg) -> + alert "Data Saved: " + msg + return diff --git a/app/assets/stylesheets/rails_admin/custom/theming.css.scss b/app/assets/stylesheets/rails_admin/custom/theming.css.scss new file mode 100644 index 0000000..7321d4f --- /dev/null +++ b/app/assets/stylesheets/rails_admin/custom/theming.css.scss @@ -0,0 +1,4 @@ +#history > tbody > tr > td:nth-child(3){ + white-space: pre; + max-width: none; +} diff --git a/app/controllers/cameras_controller.rb b/app/controllers/cameras_controller.rb index 5623da7..72e6d1f 100644 --- a/app/controllers/cameras_controller.rb +++ b/app/controllers/cameras_controller.rb @@ -1,5 +1,6 @@ class CamerasController < ApplicationController before_action :set_camera, only: [:show, :edit, :update, :destroy] + after_action :rollback_to_previous_version, only: [:update] # GET /cameras # GET /cameras.json @@ -92,6 +93,11 @@ def set_camera @camera = Camera.find_by_camera_slug(params[:id]) end + def rollback_to_previous_version + @camera.versions.last.reify.save! + flash[:notice] = "Your changes will be reflected once an admin has reviewed them" + end + # Never trust parameters from the scary internet, only allow the white list through. def camera_params params.require(:camera).permit(:model, diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb new file mode 100644 index 0000000..ec52701 --- /dev/null +++ b/app/controllers/versions_controller.rb @@ -0,0 +1,13 @@ +class VersionsController < ApplicationController + + def change + @version = PaperTrail::Version.find(params[:version_id]).next + if @version.reify + @version.reify.save! + else + @version.item.destroy + end + render :inline => "Success" + end + +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8143587..c06516e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -38,3 +38,4 @@ def avatar_url(user) "http://gravatar.com/avatar/#{gravatar_id}.png?s=100" end end + diff --git a/app/models/camera.rb b/app/models/camera.rb index 1eeba63..b551ad1 100644 --- a/app/models/camera.rb +++ b/app/models/camera.rb @@ -3,6 +3,7 @@ class Camera < ActiveRecord::Base has_many :documents, as: :owner belongs_to :manufacturer before_save :make_slug + has_paper_trail accepts_nested_attributes_for :images, allow_destroy: true, reject_if: :all_blank diff --git a/app/models/manufacturer.rb b/app/models/manufacturer.rb index 1dedc30..f5eeacf 100644 --- a/app/models/manufacturer.rb +++ b/app/models/manufacturer.rb @@ -1,7 +1,8 @@ class Manufacturer < ActiveRecord::Base has_one :image, as: :owner - has_many :cameras, dependent: :destroy + has_many :cameras before_save :make_slug + has_paper_trail accepts_nested_attributes_for :image, allow_destroy: true, reject_if: :all_blank diff --git a/app/views/rails_admin/main/history.html.haml b/app/views/rails_admin/main/history.html.haml new file mode 100644 index 0000000..5ffece8 --- /dev/null +++ b/app/views/rails_admin/main/history.html.haml @@ -0,0 +1,54 @@ +- params = request.params.except(:action, :controller, :model_name) +- query = params[:query] +- filter = params[:filter] +- sort = params[:sort] +- sort_reverse = params[:sort_reverse] +- path_method = params[:id] ? "history_show_path" : "history_index_path" + += form_tag("", method: "get", class: "search pjax-form form-inline") do + .well + %input{name: "query", type: "search", value: query, placeholder: "#{t("admin.misc.filter")}", class: 'input-small'} + %button.btn.btn-primary{type: "submit", :'data-disable-with' => " ".html_safe + t("admin.misc.refresh")} + %i.icon-white.icon-refresh + = t("admin.misc.refresh") + +%table#history.table.table-striped.table-condensed + %thead + %tr + - columns = [] + - columns << { property_name: "created_at", css_class: "created_at",link_text: t('admin.table_headers.created_at') } + - columns << { property_name: "username", css_class: "username", link_text: t('admin.table_headers.username') } + - columns << { property_name: "item", css_class: "item", link_text: t('admin.table_headers.item') } if @general + - columns << { property_name: "changes", css_class: "changes", link_text: t('admin.table_headers.changes') } + - columns << { property_name: "approve", css_class: "approve", link_text: t('admin.table_headers.approve_change') } + + - columns.each do |column| + - property_name = column[:property_name] + - selected = (sort == property_name) + - sort_direction = (sort_reverse ? "headerSortUp" : "headerSortDown" if selected) + - sort_location = send(path_method, params.except("sort_reverse").merge(model_name: @abstract_model.to_param, sort: property_name).merge(selected && sort_reverse != "true" ? {sort_reverse: "true"} : {})) + %th{class: "header pjax #{column[:css_class]} #{sort_direction if selected}", :'data-href' => sort_location}= column[:link_text] + %tbody + - @history.each_with_index do |object, index| + %tr + - unless object.created_at.nil? + %td= l(object.created_at, format: :long, default: l(object.created_at, format: :long, locale: :en)) + %td= object.username + - if @general + - if o = @abstract_model.get(object.item) + - label = o.send(@abstract_model.config.object_label_method) + - if show_action = action(:show, @abstract_model, o) + %td= link_to(label, url_for(action: show_action.action_name, model_name: @abstract_model.to_param, id: o.id), class: 'pjax') + - else + %td= label + - else + %td= "#{@abstract_model.config.label} ##{object.item}" + %td= object.changes + - if index == 0 + %td + - else + %td= link_to("##{object.id}", "#", :class => 'history_approve_contribution', :'data-id' => "#{object.id}") + +- unless params[:all] || !@history.respond_to?(:current_page) + = paginate(@history, theme: 'twitter-bootstrap', remote: true) + = link_to(t("admin.misc.show_all"), send(path_method, params.merge(all: true)), class: "show-all btn pjax") unless (tc = @history.total_count) <= @history.size || tc > 100 diff --git a/config/initializers/rails_admin.rb b/config/initializers/rails_admin.rb index ca52321..0a188da 100644 --- a/config/initializers/rails_admin.rb +++ b/config/initializers/rails_admin.rb @@ -1,3 +1,5 @@ +require Rails.root.join('lib', 'rails_admin_approve_change.rb') + RailsAdmin.config do |config| ### Popular gems integration @@ -12,7 +14,7 @@ # config.authorize_with :cancan ## == PaperTrail == - # config.audit_with :paper_trail, 'User', 'PaperTrail::Version' # PaperTrail >= 3.0.0 + config.audit_with :paper_trail, 'User', 'PaperTrail::Version' # PaperTrail >= 3.0.0 ### More at https://github.com/sferik/rails_admin/wiki/Base-configuration @@ -28,7 +30,7 @@ show_in_app ## With an audit adapter, you can add: - # history_index - # history_show + history_index + history_show end end diff --git a/config/routes.rb b/config/routes.rb index 88a8451..79982ad 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -9,6 +9,9 @@ get '/contact-us' => 'pages#contact' get '/api-docs' => 'pages#api_docs' + post '/admin/history', to: 'cameras#history', as: :cameras_history + post "/versions", to: "versions#change", :as => "change_version" + devise_for :users mount RailsAdmin::Engine => '/admin', as: 'rails_admin' diff --git a/db/migrate/20140512121824_create_versions.rb b/db/migrate/20140512121824_create_versions.rb new file mode 100644 index 0000000..23be970 --- /dev/null +++ b/db/migrate/20140512121824_create_versions.rb @@ -0,0 +1,13 @@ +class CreateVersions < ActiveRecord::Migration + def change + create_table :versions do |t| + t.string :item_type, :null => false + t.integer :item_id, :null => false + t.string :event, :null => false + t.string :whodunnit + t.text :object + t.datetime :created_at + end + add_index :versions, [:item_type, :item_id] + end +end diff --git a/db/migrate/20140512121825_add_object_changes_to_versions.rb b/db/migrate/20140512121825_add_object_changes_to_versions.rb new file mode 100644 index 0000000..2d723e0 --- /dev/null +++ b/db/migrate/20140512121825_add_object_changes_to_versions.rb @@ -0,0 +1,5 @@ +class AddObjectChangesToVersions < ActiveRecord::Migration + def change + add_column :versions, :object_changes, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index c92e7d5..229d255 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20140507122914) do +ActiveRecord::Schema.define(version: 20140512121825) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -98,4 +98,16 @@ add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree add_index "users", ["username"], name: "index_users_on_username", unique: true, using: :btree + create_table "versions", force: true do |t| + t.string "item_type", null: false + t.integer "item_id", null: false + t.string "event", null: false + t.string "whodunnit" + t.text "object" + t.datetime "created_at" + t.text "object_changes" + end + + add_index "versions", ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id", using: :btree + end diff --git a/lib/rails_admin_approve_change.rb b/lib/rails_admin_approve_change.rb new file mode 100644 index 0000000..596db4f --- /dev/null +++ b/lib/rails_admin_approve_change.rb @@ -0,0 +1,15 @@ +module RailsAdmin + module Extensions + module PaperTrail + class VersionProxy + def id + @version.id + end + + def changes + @changes = @version.changeset.to_a.collect {|c| c[0] + " = " + c[1][1].to_s}.join(", \n") + end + end + end + end +end diff --git a/public/api-docs/api/v1/cameras.json b/public/api-docs/api/v1/cameras.json index be23bb8..273fcac 100644 --- a/public/api-docs/api/v1/cameras.json +++ b/public/api-docs/api/v1/cameras.json @@ -71,4 +71,4 @@ } ], "resourcePath": "cameras" -} \ No newline at end of file +}