From d4b1ccca17707870599ca4b08d7727cf127df92a Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Sat, 15 Apr 2023 15:40:52 +0100 Subject: [PATCH 01/11] add categories controller --- .../admin/categories_controller.rb | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 app/controllers/admin/categories_controller.rb diff --git a/app/controllers/admin/categories_controller.rb b/app/controllers/admin/categories_controller.rb new file mode 100644 index 000000000..4f766a84f --- /dev/null +++ b/app/controllers/admin/categories_controller.rb @@ -0,0 +1,107 @@ +class Admin::CategoriesController < ApplicationController + + layout :determine_layout + before_action :unescape_id, only: [:edit, :show, :update, :destroy] + before_action :authorize_admin + + CATEGORIES_URL = "#{LinkedData::Client.settings.rest_url}/categories" + + def index + response = _categories + render :json => response + end + + def new + @category = LinkedData::Client::Models::Category.new + + respond_to do |format| + format.html { render "new", :layout => false } + end + end + + def edit + @category = LinkedData::Client::Models::Category.find_by_acronym(params[:id]).first + + respond_to do |format| + format.html { render "edit", :layout => false } + end + end + + def create + response = { errors: '', success: '' } + start = Time.now + begin + category = LinkedData::Client::Models::Category.new(values: category_params) + category_saved = category.save + if category_saved && category_saved.errors + response[:errors] = response_errors(category_saved) + else + response[:success] = "category successfully created in #{Time.now - start}s" + end + rescue Exception => e + response[:errors] = "Problem creating the category - #{e.message}" + end + render json: response, status: (response[:errors] == '' ? :created : :internal_server_error) + + end + + def update + response = { errors: '', success: ''} + start = Time.now + begin + category = LinkedData::Client::Models::Category.find_by_acronym(params[:id]).first + category.update_from_params(category_params) + category_update = category.update + if category_update && category_update.errors + response[:errors] = response_errors(category_update) + else + response[:success] = "category successfully updated in #{Time.now - start}s" + end + rescue Exception => e + response[:errors] = "Problem updating the category - #{e.message}" + end + render json: response, status: (response[:errors] == '' ? :ok : :internal_server_error) + end + + def destroy + response = { errors: '', success: ''} + start = Time.now + begin + category = LinkedData::Client::Models::Category.find_by_acronym(params[:id]).first + error_response = category.delete + + if error_response + response[:errors] = response_errors(error_response) + else + response[:success] = "category successfully deleted in #{Time.now - start}s" + end + rescue Exception => e + response[:errors] = "Problem deleting the category - #{e.message}" + end + render json: response, status: (response[:errors] == '' ? :ok : :internal_server_error) + end + + private + + def unescape_id + params[:id] = CGI.unescape(params[:id]) + end + + def category_params + params.require(:category).permit(:acronym, :name, :description, :parentCategory).to_h + end + + def _categories + response = { categories: Hash.new, errors: '', success: '' } + start = Time.now + begin + response[:categories] = JSON.parse(LinkedData::Client::HTTP.get(CATEGORIES_URL, { include: 'all' }, raw: true)) + + response[:success] = "categories successfully retrieved in #{Time.now - start}s" + LOG.add :debug, "Categories - retrieved #{response[:categories].length} groups in #{Time.now - start}s" + rescue Exception => e + response[:errors] = "Problem retrieving categories - #{e.message}" + end + response + end +end From bb4c82e1c31b777044d066ca068360953ee7c32b Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Sat, 15 Apr 2023 15:41:21 +0100 Subject: [PATCH 02/11] add categories administration views --- app/views/admin/categories/_form.html.haml | 43 ++++++++++++++++++++++ app/views/admin/categories/edit.html.haml | 6 +++ app/views/admin/categories/new.html.haml | 6 +++ 3 files changed, 55 insertions(+) create mode 100644 app/views/admin/categories/_form.html.haml create mode 100644 app/views/admin/categories/edit.html.haml create mode 100644 app/views/admin/categories/new.html.haml diff --git a/app/views/admin/categories/_form.html.haml b/app/views/admin/categories/_form.html.haml new file mode 100644 index 000000000..1d72c6362 --- /dev/null +++ b/app/views/admin/categories/_form.html.haml @@ -0,0 +1,43 @@ +- new_record = @category.acronym.nil? +%div.alert-box.error{style: @errors.nil? ? "display: none" : nil } + %ul + - unless @errors.nil? + - for error in @errors + %li + = error +%div{:style => "width:500px"} + %span.asterik{:style => "float:right;"} * fields are required + %h3 #{title_text} + %table#category_form.form + %colgroup + %col + %col{style: "width: 100%"} + %tr + %th + Acronym: + %span.asterik * + %td.top + - if new_record + = f.text_field :acronym, class: "form-control" + - else + = f.text_field :acronym, class: "form-control", readonly: true + %tr + %th + Name: + %span.asterik * + %td.top + = f.text_field :name, class: "form-control" + %tr + %th + Description: + %td.top + = f.text_area :description, class: "form-control" + - unless new_record + %tr + %th + Created: + %td.top + = f.text_field :created, readonly: true, class: "form-control" + %div.mt-2 + = f.submit button_text, class: "btn btn-primary mr-sm-2 group-form-accept" + = link_to "Cancel", "javascript:;", class: "btn btn-primary dismiss-dialog" \ No newline at end of file diff --git a/app/views/admin/categories/edit.html.haml b/app/views/admin/categories/edit.html.haml new file mode 100644 index 000000000..31511a88a --- /dev/null +++ b/app/views/admin/categories/edit.html.haml @@ -0,0 +1,6 @@ +- @title = "Edit category" + +%div + = form_for :category, url: admin_categories_path + "/" + URI::escape(@category.acronym), method: "PATCH", remote: true, data: { collection: "categories"}, html: {class: "admin-collection-form" } do |f| + = render partial: "form", locals: {f: f, title_text: "Edit category", + button_text: "Edit category", button_class: "edit-category"} diff --git a/app/views/admin/categories/new.html.haml b/app/views/admin/categories/new.html.haml new file mode 100644 index 000000000..4d3b3082d --- /dev/null +++ b/app/views/admin/categories/new.html.haml @@ -0,0 +1,6 @@ +- @title = "Create new category" + +%div + = form_for :category, url: admin_categories_path, method: "POST", remote: true, data: { collection: "categories"}, html: {class: "admin-collection-form" } do |f| + = render partial: "form", locals: {f: f, title_text: "Create category", + button_text: "Create category" } From 7b00542c534af9cc185739e45e04e48fd7d22c89 Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Sat, 15 Apr 2023 15:41:49 +0100 Subject: [PATCH 03/11] add categories administration route --- config/routes.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/routes.rb b/config/routes.rb index 057297ac7..963fe753d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -50,6 +50,7 @@ namespace :admin do resources :licenses, only: [:index, :create, :new] + resources :categories, only: [:index, :create, :new, :edit, :update, :destroy] end resources :subscriptions From 8da991b4ad86ff8d9e55afb0d0d6d5b3cc1d3621 Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Sat, 15 Apr 2023 15:42:20 +0100 Subject: [PATCH 04/11] add groups controller --- app/controllers/admin/groups_controller.rb | 107 +++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 app/controllers/admin/groups_controller.rb diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb new file mode 100644 index 000000000..48e861e03 --- /dev/null +++ b/app/controllers/admin/groups_controller.rb @@ -0,0 +1,107 @@ +class Admin::GroupsController < ApplicationController + + layout :determine_layout + before_action :unescape_id, only: [:edit, :show, :update, :destroy] + before_action :authorize_admin + + GROUPS_URL = "#{LinkedData::Client.settings.rest_url}/groups" + + def index + response = _groups + render :json => response + end + + def new + @group = LinkedData::Client::Models::Group.new + + respond_to do |format| + format.html { render "new", :layout => false } + end + end + + def edit + @group = LinkedData::Client::Models::Group.find_by_acronym(params[:id]).first + + respond_to do |format| + format.html { render "edit", :layout => false } + end + end + + def create + response = { errors: '', success: '' } + start = Time.now + begin + group = LinkedData::Client::Models::Group.new(values: group_params) + group_saved = group.save + if group_saved && group_saved.errors + response[:errors] = response_errors(group_saved) + else + response[:success] = "group successfully created in #{Time.now - start}s" + end + rescue Exception => e + response[:errors] = "Problem creating the group - #{e.message}" + end + render json: response, status: (response[:errors] == '' ? :created : :internal_server_error) + + end + + def update + response = { errors: '', success: ''} + start = Time.now + begin + group = LinkedData::Client::Models::Group.find_by_acronym(params[:id]).first + group.update_from_params(group_params) + group_updated = group.update + if group_updated && group_updated.errors + response[:errors] = response_errors(group_updated) + else + response[:success] = "group successfully updated in #{Time.now - start}s" + end + rescue Exception => e + response[:errors] = "Problem updating the group - #{e.message}" + end + render json: response, status: (response[:errors] == '' ? :ok : :internal_server_error) + end + + def destroy + response = { errors: '', success: ''} + start = Time.now + begin + group = LinkedData::Client::Models::Group.find_by_acronym(params[:id]).first + error_response = group.delete + + if error_response + response[:errors] = response_errors(error_response) + else + response[:success] = "group successfully deleted in #{Time.now - start}s" + end + rescue Exception => e + response[:errors] = "Problem deleting the group - #{e.message}" + end + render json: response, status: (response[:errors] == '' ? :ok : :internal_server_error) + end + + private + + def unescape_id + params[:id] = CGI.unescape(params[:id]) + end + + def group_params + params.require(:group).permit(:acronym, :name, :description).to_h() + end + + def _groups + response = { groups: Hash.new, errors: '', success: '' } + start = Time.now + begin + response[:groups] = JSON.parse(LinkedData::Client::HTTP.get(GROUPS_URL, { include: 'all' }, raw: true)) + + response[:success] = "groups successfully retrieved in #{Time.now - start}s" + LOG.add :debug, "Groups - retrieved #{response[:groups].length} groups in #{Time.now - start}s" + rescue Exception => e + response[:errors] = "Problem retrieving groups - #{e.message}" + end + response + end +end From 51a28594fdbff7e00b2a87d34234333da2e0bbdc Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Sat, 15 Apr 2023 15:42:34 +0100 Subject: [PATCH 05/11] add groups administration views --- app/views/admin/groups/_form.html.haml | 43 ++++++++++++++++++++++++++ app/views/admin/groups/edit.html.haml | 6 ++++ app/views/admin/groups/new.html.haml | 6 ++++ 3 files changed, 55 insertions(+) create mode 100644 app/views/admin/groups/_form.html.haml create mode 100644 app/views/admin/groups/edit.html.haml create mode 100644 app/views/admin/groups/new.html.haml diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml new file mode 100644 index 000000000..628f648ae --- /dev/null +++ b/app/views/admin/groups/_form.html.haml @@ -0,0 +1,43 @@ +- new_record = @group.acronym.nil? +%div.alert-box.error{style: @errors.nil? ? "display: none" : nil } + %ul + - unless @errors.nil? + - for error in @errors + %li + = error +%div{:style => "width:500px"} + %span.asterik{:style => "float:right;"} * fields are required + %h3 #{title_text} + %table#group_form.form + %colgroup + %col + %col{style: "width: 100%"} + %tr + %th + Acronym: + %span.asterik * + %td.top + - if new_record + = f.text_field :acronym, class: "form-control" + - else + = f.text_field :acronym, class: "form-control", readonly: true + %tr + %th + Name: + %span.asterik * + %td.top + = f.text_field :name, class: "form-control" + %tr + %th + Description: + %td.top + = f.text_area :description, class: "form-control" + - unless new_record + %tr + %th + Created: + %td.top + = f.text_field :created, readonly: true, class: "form-control" + %div.mt-2 + = f.submit button_text, class: "btn btn-primary mr-sm-2 group-form-accept" + = link_to "Cancel", "javascript:;", class: "btn btn-primary dismiss-dialog" \ No newline at end of file diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml new file mode 100644 index 000000000..76909dcc4 --- /dev/null +++ b/app/views/admin/groups/edit.html.haml @@ -0,0 +1,6 @@ +- @title = "Edit group" + +%div + = form_for :group, url: admin_groups_path + "/" + URI::escape(@group.acronym), method: "PATCH", remote: true, data: { collection: "groups"}, html: {class: "admin-collection-form" } do |f| + = render partial: "form", locals: {f: f, title_text: "Edit group", + button_text: "Edit group", button_class: "edit-group"} diff --git a/app/views/admin/groups/new.html.haml b/app/views/admin/groups/new.html.haml new file mode 100644 index 000000000..2a8bf5f42 --- /dev/null +++ b/app/views/admin/groups/new.html.haml @@ -0,0 +1,6 @@ +- @title = "Create new group" + +%div + = form_for :group, url: admin_groups_path, method: "POST", remote: true, data: { collection: "groups"}, html: {class: "admin-collection-form" } do |f| + = render partial: "form", locals: {f: f, title_text: "Create group", + button_text: "Create group" } From 4f59b1912201af528febe1094f8613df2faeacb8 Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Sat, 15 Apr 2023 15:42:49 +0100 Subject: [PATCH 06/11] add groups administration route --- config/routes.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/routes.rb b/config/routes.rb index 963fe753d..81b907f21 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -50,6 +50,7 @@ namespace :admin do resources :licenses, only: [:index, :create, :new] + resources :groups, only: [:index, :create, :new, :edit, :update, :destroy] resources :categories, only: [:index, :create, :new, :edit, :update, :destroy] end From fca4a8a7fa57f94789124309b078645e66f030ac Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Sat, 15 Apr 2023 15:45:10 +0100 Subject: [PATCH 07/11] add groups and categories tabs to the admin panel --- app/views/admin/index.html.haml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/views/admin/index.html.haml b/app/views/admin/index.html.haml index 38bc1a3e5..03faaa9f6 100644 --- a/app/views/admin/index.html.haml +++ b/app/views/admin/index.html.haml @@ -1,4 +1,4 @@ -- @title = "BioPortal Administration" +- @title = "Administration" %div.row %div.col @@ -25,6 +25,10 @@ =link_to("Users", "#users", id: "users-admin-tab", class: "nav-link", role: "tab", data: { toggle: "tab", href: users_path() }, aria: { controls: "users", selected: "false" }) %li.nav-item =link_to("Metadata Administration", "#ontologies_metadata_curator", id: "ontologies_metadata_curator-admin-tab", class: "nav-link", role: "tab", data: { toggle: "tab"}, aria: { controls: "ontologies_metadata_curator", selected: "false" }) + %li.nav-item + =link_to("Groups", "#groups", id: "groups-admin-tab", class: "nav-link", role: "tab", data: { toggle: "tab", href: "groups" }, aria: { controls: "groups", selected: "false" }) + %li.nav-item + =link_to("Categories", "#categories", id: "categories-admin-tab", class: "nav-link", role: "tab", data: { toggle: "tab", href: "categories" }, aria: { controls: "categories", selected: "false" }) %div#adminTabContent.tab-content -# Site Administration tab @@ -95,6 +99,17 @@ %div.tab-pane.fade{id: "ontologies_metadata_curator", role: "tabpanel", aria: { labelledby: "ontologies_metadata_curator-admin-tab" }} = render partial: 'ontologies_metadata_curator/metadata_tab' + -# Groups tab + %div.tab-pane.fade{id: "groups", role: "tabpanel", aria: { labelledby: "groups-admin-tab" }} + %div.ontologies_list_container.mt-3 + %table#adminGroups.zebra{:cellpadding => "0", :cellspacing => "0", :width => "100%"} + + -# Categories tab + %div.tab-pane.fade{id: "categories", role: "tabpanel", aria: { labelledby: "categories-admin-tab" }} + %div.ontologies_list_container.mt-3 + %table#adminCategories.zebra{:cellpadding => "0", :cellspacing => "0", :width => "100%"} + + \ No newline at end of file From 4fb787a48584e38c015b11a58e64e1368a70ad89 Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Sat, 15 Apr 2023 16:06:42 +0100 Subject: [PATCH 08/11] add groups and categories administration JS --- app/assets/javascripts/bp_admin.js | 532 ++++++++++++++++++++++++++++- 1 file changed, 531 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/bp_admin.js b/app/assets/javascripts/bp_admin.js index 63ac8ae9e..744a297e9 100644 --- a/app/assets/javascripts/bp_admin.js +++ b/app/assets/javascripts/bp_admin.js @@ -865,6 +865,220 @@ jQuery(".admin.index").ready(function() { }); // end: BUTTON onclick actions ----------------------------------- + + //============================================================== + // GROUPS MANAGEMENT + //============================================================== + displayGroups({}); + + // allow selecting of rows, except on link clicks + jQuery('#adminGroups tbody').on('click', 'tr', function(event) { + if (event.target.tagName.toLowerCase() != 'a') { + jQuery(this).toggleClass('selected'); + } + }); + + jQuery("div.groups_nav").html(` + + + New + + + Apply to Selected Rows:     + +      + + Go + + `); + + jQuery('#group_admin_action_submit').on('click', function(event) { + var action = jQuery('#group_admin_action').val(); + + if (!action) { + alertify.alert("Please choose an action to perform on the selected groups."); + return; + } + + switch(action) { + case "delete": + DeleteGroups.act(); + break; + } + }); + + jQuery(document).on("reveal.facebox", function (event) { + jQuery("#facebox form[data-collection=groups]").validate({ + errorClass: "groupFormError", + errorElement: "div", + rules: { + "group[name]": "required", + "group[acronym]": "required", + }, + messages: { + "group[name]": "Please enter a name", + "group[acronym]": "Please enter an acronym", + }, + }); + + }); + + jQuery('#group_new_action').on('click', function (event) { + jQuery.facebox({ + ajax: "/admin/groups/new?time=" + new Date().getTime() + }); + }); + + jQuery('#adminGroups').on('click', 'a.edit-group', function(event) { + jQuery.facebox({ + ajax: "/admin/groups/" + encodeURIComponent(event.target.dataset.groupName) + "/edit?time=" + new Date().getTime() + }); + }); + + + //============================================================== + // CATEGORIES MANAGEMENT + //============================================================== + displayCategories({}); + + // allow selecting of rows, except on link clicks + jQuery('#adminCategories tbody').on('click', 'tr', function(event) { + if (event.target.tagName.toLowerCase() != 'a') { + jQuery(this).toggleClass('selected'); + } + }); + + jQuery("div.categories_nav").html(` + + + New + + + Apply to Selected Rows:     + +      + + Go + + `); + + jQuery('#category_admin_action_submit').on('click', function(event) { + var action = jQuery('#category_admin_action').val(); + + if (!action) { + alertify.alert("Please choose an action to perform on the selected categories."); + return; + } + + switch(action) { + case "delete": + DeleteCategories.act(); + break; + } + }); + + jQuery(document).on("reveal.facebox", function (event) { + jQuery("#facebox form[data-collection=categories]").validate({ + errorClass: "categoryFormError", + errorElement: "div", + rules: { + "category[name]": "required", + "category[acronym]": "required", + }, + messages: { + "category[name]": "Please enter a name", + "category[acronym]": "Please enter an acronym", + }, + }); + + }); + + jQuery('#category_new_action').on('click', function (event) { + jQuery.facebox({ + ajax: "/admin/categories/new?time=" + new Date().getTime() + }); + }); + + jQuery('#adminCategories').on('click', 'a.edit-category', function(event) { + jQuery.facebox({ + ajax: "/admin/categories/" + encodeURIComponent(event.target.dataset.categoryName) + "/edit?time=" + new Date().getTime() + }); + }); + + //============================================================== + // MANAGEMENT COMMONS + //============================================================== + + jQuery(document).on("click", "#facebox a.dismiss-dialog", function (event) { + jQuery(document).trigger('close.facebox'); + }); + + jQuery(document).on('ajax:success', "#facebox form.admin-collection-form", (event, response, status, xhr) => { + jQuery(document).trigger('close.facebox'); + if (response && response.success) { + _showStatusMessages([response.success], [], [], false); + } + refreshCollection(event.target.dataset.collection); + }); + jQuery(document).on('ajax:error', "#facebox form.admin-collection-form", (event, xhr, status, error) => { + if (xhr.responseJSON) { + displayDialogErrorMessages(xhr.responseJSON) + } else { + displayDialogErrorMessages(status); + } + }); + + function refreshCollection(collectionName) { + switch (collectionName) { + case "groups": + displayGroups({}); + break; + case "categories": + displayCategories({}); + break; + default: + alertify.alert("Unable to refresh unknown collection '" + collectionName + "'"); + } + } + + function displayDialogErrorMessages(data, settings) { + settings ||= {} + + let append = settings.append || false; + + let errorListNode = jQuery("#facebox .alert-box ul"); + + if (!append) { + errorListNode.empty(); + } + + let messages = []; + if (typeof data == "string" || data instanceof String) { + messages.push(data) + } + if (typeof data == "object" && data.errors) { + messages.push.apply(messages, Object.values(data.errors)); + } + if (typeof data == "object" && data.status && data.status / 200 != 1) { + messages.push("Request error: " + data.statusText); + } + + for (let msg of messages) { + errorListNode.append(jQuery("
  • ").text(msg)) + } + + if (messages.length == 0) { + errorListNode.parents(".alert-box").hide(); + } else { + errorListNode.parents(".alert-box").show(); + } + } }); @@ -1040,4 +1254,320 @@ DeleteUsers.prototype.ajaxCall = function (username){ } DeleteUsers.act = function(user) { new DeleteUsers(user).ajaxCall(user); -}; \ No newline at end of file +}; + +/* groups part */ +function displayGroups(data, group) { + let ontTable = null; + let allRows + if (jQuery.fn.dataTable.isDataTable('#adminGroups')) { + ontTable = jQuery('#adminGroups').DataTable().ajax.reload(); + } else { + ontTable = jQuery("#adminGroups").DataTable({ + "ajax": { + "url": "/admin/groups", + "contentType": "application/json", + "dataSrc": function (json) { + return populateGroupRows(json); + } + }, + "rowCallback": function(row, data, index) { + var acronym = jQuery('td:nth-child(4)', row).text(); + + jQuery(row).attr("id", "tr_" + acronym); + }, + "initComplete": function(settings, json) { + }, + "columnDefs": [ + { + "targets": 0, + "searchable": true, + "title": "Name", + }, + { + "targets": 1, + "searchable": true, + "title": "Description", + }, + { + "targets": 2, + "searchable": true, + "title": "Created", + }, + { + "targets": 3, + "searchable": true, + "title": "Id", + }, + { + "targets": 4, + "searchable": false, + "orderable": false, + "title": "Actions", + "width": "210px" + } + ], + "autoWidth": false, + "lengthChange": false, + "searching": true, + "language": { + "search": "Filter: ", + "emptyTable": "No groups available" + }, + "info": true, + "paging": true, + "pageLength": 100, + "ordering": true, + "responsive": true, + "dom": '<"groups_nav"><"top"fi>rtip', + "stripeClasses": ["", "alt"], + }); + } + return ontTable; +} + +function populateGroupRows(data) { + let groups = data['groups']; + let allRows = groups.map(group => { + let name = group['name']; + let description = group['description'] + let created = group['created']; + let id = group['acronym']; + let actions = [ + 'Edit', + ] + return [name, description, created, id , actions.join('|')]; + }) + + return allRows; +} + +function DeleteGroups() { +} + +DeleteGroups.act = function(groupName) { + let group2delete = jQuery("#adminGroups tr.selected td:nth-child(4)").map(function(index, value) { return value.textContent.trim();}).toArray(); + let confirmMsg = "You are about to delete the following groups:
    " + group2delete.join(",") + "

    Should I proceed?"; + alertify.confirm(confirmMsg, (e) => { + if (e) { + _clearStatusMessages(); + let success = []; + let errors = []; + let notices = []; + let errorState = false; + let deferredObj = jQuery.Deferred(); + let initialDeferredObj = deferredObj; + + for (let group of group2delete) { + fun = () => { return jQuery.ajax("/admin/groups/" + encodeURIComponent(group), { + method: "DELETE", + dataType: "json", + success: function(data, msg) { + var reg = /\s*,\s*/g; + + if (data.errors) { + errorState = true; + errors.push.apply(errors, data.errors); + } + + if (data.success) { + success.push(data.success); + } + + if (data.notices) { + notices.push.apply(notices, data.notices); + } + + _showStatusMessages(success, errors, notices, false); + }, + error: function(request, textStatus, errorThrown) { + errorState = true; + errors.push(request.status + ": " + errorThrown); + _showStatusMessages(success, errors, notices, false); + }, + complete: function(request, textStatus) { + if (errorState) { + jQuery("#tr_" + group).removeClass('selected'); + } + } + }) + }; + deferredObj = deferredObj.then(fun, fun); + } + // hide progress message and deselect rows after ALL operations have completed + deferredObj.always(function () { + jQuery("#adminGroups").DataTable().ajax.reload(); + }); + + initialDeferredObj.resolve(); + } + }); +} +/* categories part */ +function displayCategories(data, category) { + let ontTable = null; + let allRows + if (jQuery.fn.dataTable.isDataTable('#adminCategories')) { + ontTable = jQuery('#adminCategories').DataTable().ajax.reload(); + } else { + ontTable = jQuery("#adminCategories").DataTable({ + "ajax": { + "url": "/admin/categories", + "contentType": "application/json", + "dataSrc": function (json) { + return populateCategoryRows(json); + } + }, + "rowCallback": function(row, data, index) { + var acronym = jQuery('td:nth-child(4)', row).text(); + + jQuery(row).attr("id", "tr_" + acronym); + }, + "initComplete": function(settings, json) { + }, + "columnDefs": [ + { + "targets": 0, + "searchable": true, + "title": "Name", + }, + { + "targets": 1, + "searchable": true, + "title": "Description", + }, + { + "targets": 2, + "searchable": true, + "title": "Created", + }, + { + "targets": 3, + "searchable": true, + "title": "Id", + }, + { + "targets": 4, + "searchable": false, + "orderable": false, + "title": "Parent", + }, + { + "targets": 5, + "searchable": false, + "orderable": false, + "title": "Actions", + "width": "210px" + } + ], + "autoWidth": false, + "lengthChange": false, + "searching": true, + "language": { + "search": "Filter: ", + "emptyTable": "No categories available" + }, + "info": true, + "paging": true, + "pageLength": 100, + "ordering": true, + "responsive": true, + "dom": '<"categories_nav"><"top"fi>rtip', + "stripeClasses": ["", "alt"], + }); + } + return ontTable; +} + +function populateCategoryRows(data) { + let categories = data['categories']; + let allRows = categories.map(category => { + let name = category['name']; + let description = category['description'] + let created = category['created']; + let id = category['acronym']; + let parentCategory = category['parentCategory']; + let actions = [ + 'Edit', + ] + return [name, description, created, id , parentCategory, actions.join('|')]; + }) + + return allRows; +} + +function DeleteCategories() { +} + +DeleteCategories.act = function(groupName) { + let category2delete = jQuery("#adminCategories tr.selected td:nth-child(4)").map(function(index, value) { return value.textContent.trim();}).toArray(); + let confirmMsg = "You are about to delete the following categories:
    " + category2delete.join(",") + "

    Should I proceed?"; + alertify.confirm(confirmMsg, (e) => { + if (e) { + _clearStatusMessages(); + let success = []; + let errors = []; + let notices = []; + let errorState = false; + let deferredObj = jQuery.Deferred(); + let initialDeferredObj = deferredObj; + + for (let category of category2delete) { + fun = () => { return jQuery.ajax("/admin/categories/" + encodeURIComponent(category), { + method: "DELETE", + dataType: "json", + success: function(data, msg) { + var reg = /\s*,\s*/g; + + if (data.errors) { + errorState = true; + errors.push.apply(errors, data.errors); + } + + if (data.success) { + success.push(data.success); + } + + if (data.notices) { + notices.push.apply(notices, data.notices); + } + + _showStatusMessages(success, errors, notices, false); + }, + error: function(request, textStatus, errorThrown) { + errorState = true; + errors.push(request.status + ": " + errorThrown); + _showStatusMessages(success, errors, notices, false); + }, + complete: function(request, textStatus) { + if (errorState) { + jQuery("#tr_" + category).removeClass('selected'); + } + } + }) + }; + deferredObj = deferredObj.then(fun, fun); + } + // hide progress message and deselect rows after ALL operations have completed + deferredObj.always(function () { + jQuery("#adminCategories").DataTable().ajax.reload(); + }); + + initialDeferredObj.resolve(); + } + }); +} + +/***************************** + * COMMON FUNCTIONS + *****************************/ +function _clearStatusMessages() { + jQuery("#progress_message").hide(); + jQuery("#success_message").hide(); + jQuery("#error_message").hide(); + jQuery("#info_message").hide(); + jQuery("#progress_message").html(""); + jQuery("#success_message").html(""); + jQuery("#error_message").html(""); + jQuery("#info_message").html(""); +} \ No newline at end of file From 6a305bf52433a5b80b09d75ad7f863be89fe81ae Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Sun, 16 Apr 2023 10:15:49 +0100 Subject: [PATCH 09/11] add group from error style --- app/assets/stylesheets/admin.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 181e46e11..ad58c2c44 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -16,6 +16,7 @@ .error { background: #ffecec image-url("error.png") no-repeat 10px 50%; border: 1px solid #f5aca6; + padding-left: 30px; } .success { @@ -139,3 +140,8 @@ table.dataTable tbody tr.selected { margin-right: 8px; } } + +div.groupFormError { + color: red; + padding-top: 3px; +} From 706463de835ee260f53fe52f681bfcae1e1b2608 Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Mon, 8 May 2023 05:12:56 +0200 Subject: [PATCH 10/11] migrate URI::escape to the escape helper --- app/views/admin/categories/edit.html.haml | 2 +- app/views/admin/groups/edit.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/categories/edit.html.haml b/app/views/admin/categories/edit.html.haml index 31511a88a..b3706314d 100644 --- a/app/views/admin/categories/edit.html.haml +++ b/app/views/admin/categories/edit.html.haml @@ -1,6 +1,6 @@ - @title = "Edit category" %div - = form_for :category, url: admin_categories_path + "/" + URI::escape(@category.acronym), method: "PATCH", remote: true, data: { collection: "categories"}, html: {class: "admin-collection-form" } do |f| + = form_for :category, url: admin_categories_path + "/" + escape(@category.acronym), method: "PATCH", remote: true, data: { collection: "categories"}, html: {class: "admin-collection-form" } do |f| = render partial: "form", locals: {f: f, title_text: "Edit category", button_text: "Edit category", button_class: "edit-category"} diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml index 76909dcc4..d51cf7123 100644 --- a/app/views/admin/groups/edit.html.haml +++ b/app/views/admin/groups/edit.html.haml @@ -1,6 +1,6 @@ - @title = "Edit group" %div - = form_for :group, url: admin_groups_path + "/" + URI::escape(@group.acronym), method: "PATCH", remote: true, data: { collection: "groups"}, html: {class: "admin-collection-form" } do |f| + = form_for :group, url: admin_groups_path + "/" + escape(@group.acronym), method: "PATCH", remote: true, data: { collection: "groups"}, html: {class: "admin-collection-form" } do |f| = render partial: "form", locals: {f: f, title_text: "Edit group", button_text: "Edit group", button_class: "edit-group"} From d2bce043314a1e7e72ab153b9c3af8601a96a03b Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Mon, 8 May 2023 05:13:30 +0200 Subject: [PATCH 11/11] update categories and groups controllers to use response_error? helper --- app/controllers/admin/categories_controller.rb | 7 ++++--- app/controllers/admin/groups_controller.rb | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/controllers/admin/categories_controller.rb b/app/controllers/admin/categories_controller.rb index 4f766a84f..2af3f9143 100644 --- a/app/controllers/admin/categories_controller.rb +++ b/app/controllers/admin/categories_controller.rb @@ -33,7 +33,7 @@ def create begin category = LinkedData::Client::Models::Category.new(values: category_params) category_saved = category.save - if category_saved && category_saved.errors + if response_error?(category_saved) response[:errors] = response_errors(category_saved) else response[:success] = "category successfully created in #{Time.now - start}s" @@ -52,7 +52,8 @@ def update category = LinkedData::Client::Models::Category.find_by_acronym(params[:id]).first category.update_from_params(category_params) category_update = category.update - if category_update && category_update.errors + + if response_error?(category_update) response[:errors] = response_errors(category_update) else response[:success] = "category successfully updated in #{Time.now - start}s" @@ -70,7 +71,7 @@ def destroy category = LinkedData::Client::Models::Category.find_by_acronym(params[:id]).first error_response = category.delete - if error_response + if response_error?(error_response) response[:errors] = response_errors(error_response) else response[:success] = "category successfully deleted in #{Time.now - start}s" diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 48e861e03..842982957 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -33,7 +33,7 @@ def create begin group = LinkedData::Client::Models::Group.new(values: group_params) group_saved = group.save - if group_saved && group_saved.errors + if response_error?(group_saved) response[:errors] = response_errors(group_saved) else response[:success] = "group successfully created in #{Time.now - start}s" @@ -52,7 +52,7 @@ def update group = LinkedData::Client::Models::Group.find_by_acronym(params[:id]).first group.update_from_params(group_params) group_updated = group.update - if group_updated && group_updated.errors + if response_error?(group_updated) response[:errors] = response_errors(group_updated) else response[:success] = "group successfully updated in #{Time.now - start}s" @@ -70,7 +70,7 @@ def destroy group = LinkedData::Client::Models::Group.find_by_acronym(params[:id]).first error_response = group.delete - if error_response + if response_error?(error_response) response[:errors] = response_errors(error_response) else response[:success] = "group successfully deleted in #{Time.now - start}s"