Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions app/controllers/escalated/admin/plugins_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
module Escalated
module Admin
class PluginsController < Escalated::ApplicationController
before_action :require_admin!

def index
plugins = Escalated::Services::PluginService.all_plugins

render inertia: "Escalated/Admin/Plugins/Index", props: {
plugins: plugins.map { |p| plugin_json(p) }
}
end

def upload
unless params[:plugin].present?
redirect_back fallback_location: admin_plugins_path,
alert: "Please select a plugin ZIP file to upload."
return
end

begin
result = Escalated::Services::PluginService.upload_plugin(params[:plugin])

redirect_to admin_plugins_path,
notice: "Plugin uploaded successfully. You can now activate it."
rescue StandardError => e
Rails.logger.error("[Escalated::PluginsController] Upload failed: #{e.message}")
redirect_back fallback_location: admin_plugins_path,
alert: "Failed to upload plugin: #{e.message}"
end
end

def activate
begin
Escalated::Services::PluginService.activate_plugin(params[:id])

redirect_back fallback_location: admin_plugins_path,
notice: "Plugin activated successfully."
rescue StandardError => e
Rails.logger.error("[Escalated::PluginsController] Activation failed: #{e.message}")
redirect_back fallback_location: admin_plugins_path,
alert: "Failed to activate plugin: #{e.message}"
end
end

def deactivate
begin
Escalated::Services::PluginService.deactivate_plugin(params[:id])

redirect_back fallback_location: admin_plugins_path,
notice: "Plugin deactivated successfully."
rescue StandardError => e
Rails.logger.error("[Escalated::PluginsController] Deactivation failed: #{e.message}")
redirect_back fallback_location: admin_plugins_path,
alert: "Failed to deactivate plugin: #{e.message}"
end
end

def destroy
begin
# Check if plugin is gem-sourced before attempting delete
all_plugins = Escalated::Services::PluginService.all_plugins
plugin_data = all_plugins.find { |p| p[:slug] == params[:id] }

if plugin_data && plugin_data[:source] == :composer
redirect_back fallback_location: admin_plugins_path,
alert: "Gem plugins cannot be deleted. Remove the gem via Bundler instead."
return
end

Escalated::Services::PluginService.delete_plugin(params[:id])

redirect_back fallback_location: admin_plugins_path,
notice: "Plugin deleted successfully."
rescue StandardError => e
Rails.logger.error("[Escalated::PluginsController] Deletion failed: #{e.message}")
redirect_back fallback_location: admin_plugins_path,
alert: "Failed to delete plugin: #{e.message}"
end
end

private

def admin_plugins_path
escalated.admin_plugins_path
end

def plugin_json(plugin)
{
slug: plugin[:slug],
name: plugin[:name],
description: plugin[:description],
version: plugin[:version],
author: plugin[:author],
author_url: plugin[:author_url],
requires: plugin[:requires],
is_active: plugin[:is_active],
activated_at: plugin[:activated_at]&.iso8601,
source: (plugin[:source] || :local).to_s,
}
end
end
end
end
14 changes: 11 additions & 3 deletions app/controllers/escalated/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,30 @@ def apply_middleware
end

def set_inertia_shared_data
inertia_share(
shared = {
current_user: current_user_data,
escalated: {
route_prefix: Escalated.configuration.route_prefix,
allow_customer_close: Escalated.configuration.allow_customer_close,
max_attachments: Escalated.configuration.max_attachments,
max_attachment_size_kb: Escalated.configuration.max_attachment_size_kb,
guest_tickets_enabled: Escalated::EscalatedSetting.guest_tickets_enabled?
guest_tickets_enabled: Escalated::EscalatedSetting.guest_tickets_enabled?,
plugins_enabled: Escalated.configuration.plugins_enabled?,
},
flash: {
success: flash[:success],
error: flash[:error],
notice: flash[:notice],
alert: flash[:alert]
}
)
}

# Share plugin UI data when plugin system is enabled
if Escalated.configuration.plugins_enabled?
shared[:plugin_ui] = Escalated.plugin_ui.to_shared_data
end

inertia_share(shared)
end

def current_user_data
Expand Down
16 changes: 16 additions & 0 deletions app/models/escalated/plugin.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Escalated
class Plugin < ApplicationRecord
self.table_name = Escalated.table_name("plugins")

validates :slug, presence: true, uniqueness: true,
format: { with: /\A[a-z0-9]+(?:-[a-z0-9]+)*\z/, message: "must be a lowercase slug (e.g. my-plugin)" }

scope :active, -> { where(is_active: true) }
scope :inactive, -> { where(is_active: false) }
scope :ordered, -> { order(:slug) }

def active?
is_active
end
end
end
9 changes: 9 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@
resources :tags, only: [:index, :create, :update, :destroy]
resources :canned_responses, only: [:index, :create, :update, :destroy]
resources :macros, only: [:index, :create, :update, :destroy]
resources :plugins, only: [:index, :destroy] do
member do
post :activate
post :deactivate
end
collection do
post :upload
end
end
get :reports, to: "reports#index"
get :settings, to: "settings#index"
post :settings, to: "settings#update"
Expand Down
15 changes: 15 additions & 0 deletions db/migrate/018_create_escalated_plugins.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class CreateEscalatedPlugins < ActiveRecord::Migration[7.0]
def change
create_table Escalated.table_name("plugins") do |t|
t.string :slug, null: false
t.boolean :is_active, null: false, default: false
t.datetime :activated_at
t.datetime :deactivated_at

t.timestamps
end

add_index Escalated.table_name("plugins"), :slug, unique: true
add_index Escalated.table_name("plugins"), :is_active
end
end
Loading