Skip to content

How to create a plugin

Noah Settersten edited this page Jan 20, 2023 · 13 revisions

Plugins are a work in progress. Suggestions are welcome!

Plugins allow you to share and reuse common features. In this guide we're going to create a plugin called 'events'.

Final example app

Generate the plugin

From an existing Spina app, run the rails generator for a 'full' rails engine:

rails plugin new vendor/plugins/events --full

This will create an engine inside your 'vendor/plugins' directory. Update the newly created gemspec with your contact information and add Spina as a dependency:

# /vendor/plugins/events/events.gemspec
Gem::Specification.new do |s|
  s.name        = "events"
  s.version     = Events::VERSION
  s.authors     = ["Your name"]
  s.email       = ["[email protected]"]
  s.homepage    = "http://www.spinacms.com/"
  s.summary     = "Summary of Events."
  s.description = "Description of Events."
  s.license     = "MIT"

  s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]
  s.test_files = Dir["test/**/*"]

  s.add_dependency "rails"
  s.add_dependency "spina"

  s.add_development_dependency "sqlite3"
end

In order to register the plugin on Spina configuration, update the vendor/plugins/events/lib/events/engine.rb with the following lines:

require 'spina'

module Events
  class Engine < ::Rails::Engine

    initializer 'spina.plugin.register.events', before: :load_config_initializers do
      ::Spina::Plugin.register do |plugin|
        plugin.name       = 'events'
        plugin.namespace  = 'events'
      end
    end

  end
end

Creating the Event resource

Model

First generate the model events inside the engine. Go to the plugin directory and run the generator:

cd vendor/plugins/events
rails g model spina/event title:string description:text date:datetime 
Routes

We want to be able to manage our new resource inside the Spina admin. Add the following routes:

# /vendor/plugins/events/config/routes.rb
Spina::Engine.routes.draw do
  namespace :admin, path: Spina.config.backend_path do
    resources :events, except: [:show]
  end
end

Notice that you have to change Events::Engine.routes.draw to Spina::Engine.routes.draw. This will add the routes to the existing Spina engine instead of the rails app.

Controller

Create the events controller:

# /vendor/plugins/events/app/controllers/spina/admin/events_controller.rb
module Spina
  module Admin
    class EventsController < AdminController

      before_action :set_breadcrumb
      before_action :set_event, only: [:edit, :update, :destroy]

      admin_section :events

      layout 'spina/admin/admin'

      def index
        @events = Event.all
      end

      def new
        @event = Event.new
      end

      def edit
      end

      def create
        @event = Event.new(event_params)

        if @event.save
          redirect_to admin_events_path, notice: 'Event was successfully created.'
        else
          render :new
        end
      end

      def update
        if @event.update(event_params)
          redirect_to admin_events_path, notice: 'Event was successfully updated.'
        else
          render :edit
        end
      end

      def destroy
        @event.destroy
        respond_to do |format|
          redirect_to admin_events_path, notice: 'Event was successfully destroyed.'
        end
      end

      private

      def event_params
        params.require(:event).permit(:title, :description, :date)
      end

      def set_breadcrumb
        add_breadcrumb 'Events', admin_events_path
      end

      def set_event
        @event = Event.find(params[:id])
        add_breadcrumb @event.title
      end

    end
  end
end
Views

Add the following views:

-# /vendor/plugins/events/app/views/spina/admin/events/index.html.haml
.filters
  = link_to 'New event', spina.new_admin_event_path, class: 'button button-primary', data: {icon: 't'}

.table-container
  %table.table
    %thead
      %tr
        %th Title
        %th Date
        %th

    %tbody
      - if @events.any?
        - @events.each do |event|
          %tr
            %td
              = event.title
            %td
              = l(event.date)
            %td.nowrap.align-right
              = link_to spina.edit_admin_event_path(event), class: 'button button-link' do
                = icon('pencil-outline')

      - else
        %tr
          %td.align-center{colspan: 3}
            %em There are no events
-# /vendor/plugins/events/app/views/spina/admin/events/_form.html.haml
= form_for [:admin, @event], html: {autocomplete: "off"} do |f|
  - content_for :notification_alert do
    = error_explanation!(@event)

  #page_content
    .table-container
      %table.table.table-form
        %tr
          %td
            Title
          %td
            = f.text_field :title
        %tr
          %td
            Description
          %td
            = f.text_area :description
        %tr
          %td
            Date
          %td
            = f.text_field :date, value: (@event.date.strftime("%d-%m-%Y") unless @event.new_record?), class: 'datepicker'

  %button.button.button-primary{type: 'submit', data: {icon: 'o'}}
    Save

  = link_to 'Cancel', admin_events_path, class: 'button button-link'

  - unless @event.new_record?
    .pull-right= link_to 'Delete', admin_event_path(@event), method: :delete, confirm: 'Are you sure?', class: 'button button-link button-danger'
-# /vendor/plugins/events/app/views/spina/admin/events/new.html.haml
= render 'form'
-# /vendor/plugins/events/app/views/spina/admin/events/edit.html.haml
= render 'form'
Menu Navigation

Next we want to add links to our new resource. Spina provides 'hooks' to add your own views to existing layouts. During the rendering of a layout, Spina will search for a view with a specific name in the hooks directory. If such a view exists, then Spina will render the view. For now the following hooks are available:

  • [plugin-root]/app/views/spina/admin/hooks/[plugin-name]/_primary_navigation.html.haml
  • [plugin-root]/app/views/spina/admin/hooks/[plugin-name]/_website_secondary_navigation.html.haml
  • [plugin-root]/app/views/spina/admin/hooks/[plugin-name]/_settings_secondary_navigation.html

For this example we're going to add links to the 'Your website' menu by creating the following view:

-# vendor/plugins/events/app/views/spina/admin/hooks/events/_website_secondary_navigation.html.haml
%li{ class: ('active' if current_admin_path.start_with?('/events')) }
  = link_to spina.admin_events_path do
    = plugin.name

The final result will look like this:

Spina admin events

Hook Into the Application

Add the gem to the Gemfile to your main rails app:

gem 'events', path: 'vendor/plugins'

Now you will be able to install the gem with bundle install, copy and install the migrations and restart your server.

bundle install
rake events_engine:install:migrations
rake db:migrate
touch tmp/restart.txt

and add the plugin to your themes/default.rb file theme.plugins = ['events']