locomotive_plugins 1.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +26 -0
- data/README.md +229 -0
- data/Rakefile +13 -0
- data/lib/locomotive/plugin.rb +162 -0
- data/lib/locomotive/plugin/config_ui.rb +26 -0
- data/lib/locomotive/plugin/db_model.rb +10 -0
- data/lib/locomotive/plugin/db_model_container.rb +10 -0
- data/lib/locomotive/plugin/db_models.rb +86 -0
- data/lib/locomotive/plugin/liquid.rb +87 -0
- data/lib/locomotive/plugin/liquid/prefixed_filter_module.rb +24 -0
- data/lib/locomotive/plugin/liquid/tag_subclass_methods.rb +22 -0
- data/lib/locomotive_plugins.rb +44 -0
- data/lib/version.rb +3 -0
- data/spec/fixtures/config_template.haml +2 -0
- data/spec/fixtures/config_template.html +3 -0
- data/spec/lib/locomotive/plugin/config_ui_spec.rb +41 -0
- data/spec/lib/locomotive/plugin/db_models_spec.rb +56 -0
- data/spec/lib/locomotive/plugin/liquid_spec.rb +125 -0
- data/spec/lib/locomotive/plugin_spec.rb +46 -0
- data/spec/lib/locomotive_plugins_spec.rb +29 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/support/factories.rb +3 -0
- data/spec/support/plugins/my_other_plugin.rb +16 -0
- data/spec/support/plugins/my_plugin.rb +42 -0
- data/spec/support/plugins/plugin_with_db_model.rb +19 -0
- data/spec/support/plugins/plugin_with_filter.rb +21 -0
- data/spec/support/plugins/plugin_with_many_filter_modules.rb +23 -0
- data/spec/support/plugins/plugin_with_non_string_path.rb +23 -0
- data/spec/support/plugins/plugin_with_tags.rb +31 -0
- data/spec/support/plugins/useless_plugin.rb +6 -0
- metadata +171 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
## 1.0.0
|
2
|
+
|
3
|
+
* Added liquid filters
|
4
|
+
* Added liquid tags
|
5
|
+
* Removed content type scoping (use liquid tags instead)
|
6
|
+
|
7
|
+
## v0.2.1
|
8
|
+
|
9
|
+
* Added custom initialization for plugins
|
10
|
+
|
11
|
+
## v0.2.0
|
12
|
+
|
13
|
+
* Added DBModel functionality
|
14
|
+
|
15
|
+
## v0.1.1
|
16
|
+
|
17
|
+
* Fixed bug with non-string filepaths, such as Rails Pathname object
|
18
|
+
|
19
|
+
## v0.1.0
|
20
|
+
|
21
|
+
* Plugins can supply a config UI in HTML or HAML
|
22
|
+
* Added config variable for plugins
|
23
|
+
* Added before_filters for plugins
|
24
|
+
* Setup plugin registration
|
25
|
+
* Added plugin liquid drops
|
26
|
+
* Added content_type_scope
|
data/README.md
ADDED
@@ -0,0 +1,229 @@
|
|
1
|
+
|
2
|
+
# Locomotive Plugins [![Build Status](https://secure.travis-ci.org/colibri-software/locomotive_plugins.png)](https://secure.travis-ci.org/colibri-software/locomotive_plugins.png)
|
3
|
+
|
4
|
+
Create plugins for [Locomotive CMS](http://locomotivecms.com/).
|
5
|
+
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
To create a Locomotive Plugin, create a ruby gem and then install this gem:
|
10
|
+
|
11
|
+
|
12
|
+
gem install locomotive_plugins
|
13
|
+
|
14
|
+
Alternatively if you're using Bundler, add the following line to your Gemfile:
|
15
|
+
|
16
|
+
gem 'locomotive_plugins'
|
17
|
+
|
18
|
+
and run `bundle install`.
|
19
|
+
|
20
|
+
To install the plugin in LocomotiveCMS, simply [create a LocomotiveCMS
|
21
|
+
app](http://doc.locomotivecms.com/installation/getting_started) and add your
|
22
|
+
plugin gem to the app's Gemfile.
|
23
|
+
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
To create a plugin, create a class which includes the `Locomotive::Plugin`
|
28
|
+
module and register it as a plugin:
|
29
|
+
|
30
|
+
class BasicAuth
|
31
|
+
include Locomotive::Plugin
|
32
|
+
end
|
33
|
+
|
34
|
+
LocomotivePlugins.register_plugin(BasicAuth)
|
35
|
+
|
36
|
+
The plugin will automatically be registered under an ID which is its
|
37
|
+
underscored name, in this case, `basic_auth`. To register it under a
|
38
|
+
different ID, simply supply the ID in the `register_plugin` call:
|
39
|
+
|
40
|
+
LocomotivePlugins::register_plugin(BasicAuth, 'auth')
|
41
|
+
|
42
|
+
### Initialization
|
43
|
+
|
44
|
+
To initialize a plugin object, do not override the `initialize` method because
|
45
|
+
this method is defined by the `Locomotive::Plugin` module and used by
|
46
|
+
Locomotive. Instead, override the `initialize_plugin` method.
|
47
|
+
|
48
|
+
class MyPlugin
|
49
|
+
include Locomotive::Plugin
|
50
|
+
|
51
|
+
def initialize_plugin
|
52
|
+
# Custom initialization code
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
### Before filters
|
57
|
+
|
58
|
+
A plugin may add a before filter which is run before every page load on the
|
59
|
+
website being hosted in Locomotive CMS. The before filter has access to the
|
60
|
+
controller which is being invoked, and a config variable which is set within
|
61
|
+
the Locomotive UI.
|
62
|
+
|
63
|
+
class BasicAuth
|
64
|
+
include Locomotive::Plugin
|
65
|
+
|
66
|
+
before_filter :authenticate
|
67
|
+
|
68
|
+
def authenticate
|
69
|
+
if self.config[:use_basic_auth]
|
70
|
+
self.controller.authenticate_or_request_with_http_basic do |username, password|
|
71
|
+
username = USER_ID && password == PASSWORD
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
### Liquid
|
78
|
+
|
79
|
+
#### Drops
|
80
|
+
|
81
|
+
A plugin can add a liquid drop which can be accessed from page templates in
|
82
|
+
LocomotiveCMS. To do so, override the `to_liquid` method.
|
83
|
+
|
84
|
+
Plugin code:
|
85
|
+
|
86
|
+
class BasicAuth
|
87
|
+
include Locomotive::Plugin
|
88
|
+
|
89
|
+
def to_liquid
|
90
|
+
{ :userid => self.get_authenticated_user_id }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
Liquid code:
|
95
|
+
|
96
|
+
<p>Your User ID is: {{ plugins.basic_auth.userid }}</p>
|
97
|
+
|
98
|
+
This liquid code assumes that the plugin has been registered under the default
|
99
|
+
ID as described above.
|
100
|
+
|
101
|
+
#### Filters
|
102
|
+
|
103
|
+
A plugin can add liquid filters:
|
104
|
+
|
105
|
+
module Filters
|
106
|
+
|
107
|
+
def add_http(input)
|
108
|
+
if input.start_with?('http://')
|
109
|
+
input
|
110
|
+
else
|
111
|
+
"http://#{input}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
class MyPlugin
|
118
|
+
include Locomotive::Plugin
|
119
|
+
|
120
|
+
def self.liquid_filters
|
121
|
+
Filters
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
Locomotive will automatically prefix the filter with the plugin ID in the
|
126
|
+
liquid code:
|
127
|
+
|
128
|
+
<a href="{{ page.link | my_plugin_add_http }}">Click here!</a>
|
129
|
+
|
130
|
+
#### Tags
|
131
|
+
|
132
|
+
A plugin may also supply custom liquid tags. The custom tag class may override
|
133
|
+
the `render_disabled` method to specify what should be rendered if the plugin
|
134
|
+
is not enabled. By default, this will be the empty string. For example:
|
135
|
+
|
136
|
+
# Note that Liquid::Block is a subclass of Liquid::Tag
|
137
|
+
class Paragraph < Liquid::Block
|
138
|
+
def render(context)
|
139
|
+
"<p>#{render_all(@nodelist, context)}</p>"
|
140
|
+
end
|
141
|
+
|
142
|
+
def render_disabled(context)
|
143
|
+
render_all(@nodelist, context)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class Newline < Liquid::Tag
|
148
|
+
def render(context)
|
149
|
+
"<br />"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class MyPlugin
|
154
|
+
include Locomotive::Plugin
|
155
|
+
|
156
|
+
def self.liquid_tags
|
157
|
+
{
|
158
|
+
:paragraph => Paragraph,
|
159
|
+
:newline => Newline
|
160
|
+
}
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
Locomotive will automatically prefix the tag with the plugin ID in the liquid
|
165
|
+
code. Consider the following liquid code:
|
166
|
+
|
167
|
+
{% my_plugin_paragraph %}Some Text{% endmy_plugin_paragraph %}
|
168
|
+
Some Text{% my_plugin_newline %}
|
169
|
+
|
170
|
+
When `MyPlugin` is enabled, the code will be rendered to:
|
171
|
+
|
172
|
+
<p>Some Text</p>
|
173
|
+
Some Text<br />
|
174
|
+
|
175
|
+
When `MyPlugin` is disabled, the code will be rendered to:
|
176
|
+
|
177
|
+
Some Text
|
178
|
+
Some Text
|
179
|
+
|
180
|
+
### Config UI
|
181
|
+
|
182
|
+
Plugins can provide a UI for setting configuration attributes. The UI should be
|
183
|
+
written as a [Handlebars.js](http://handlebarsjs.com/) template. When the
|
184
|
+
template is rendered, it is supplied with the array of content types in the
|
185
|
+
CMS. This can be used, for example, to create a select box for selecting a
|
186
|
+
content type to be acted upon by the plugin.
|
187
|
+
|
188
|
+
A config UI can be specified by a plugin in a few ways. The preferred method is
|
189
|
+
to override the `config_template_file` method on the plugin class. This method
|
190
|
+
must return a path to an HTML or HAML file. For more fine-grained control over
|
191
|
+
how the string is generated, the `config_template_string` can be overridden to
|
192
|
+
directly supply the HTML string to be rendered.
|
193
|
+
|
194
|
+
Here's an example of an HTML config file:
|
195
|
+
|
196
|
+
<li>
|
197
|
+
<label name="my_plugin_config">My Plugin Config</label>
|
198
|
+
<input type="text" name="my_plugin_config">
|
199
|
+
<p class="inline-hints">My Hint</p>
|
200
|
+
</li>
|
201
|
+
<li>
|
202
|
+
<label name="content_type_slugs">Content Types</label>
|
203
|
+
<select name="content_type_slugs" multiple="multiple">
|
204
|
+
{{#each content_types}}
|
205
|
+
<option value="{{ this.slug }}"> {{ this.name }}</option>
|
206
|
+
{{/each}}
|
207
|
+
</select>
|
208
|
+
</li>
|
209
|
+
|
210
|
+
### Database Models
|
211
|
+
|
212
|
+
Plugins can persist data in the database through the use of DBModels. A DBModel
|
213
|
+
has all the functionality of a Mongoid document. For example:
|
214
|
+
|
215
|
+
class VisitCount < Locomotive::Plugin::DBModel
|
216
|
+
field :count, default: 0
|
217
|
+
end
|
218
|
+
|
219
|
+
class VisitCounter
|
220
|
+
include Locomotive::Plugin
|
221
|
+
|
222
|
+
has_one :visit_count, VisitCount
|
223
|
+
before_filter :increment_count
|
224
|
+
|
225
|
+
def increment_count
|
226
|
+
build_visit_count unless visit_count
|
227
|
+
visit_count.count += 1
|
228
|
+
end
|
229
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
5
|
+
require 'version'
|
6
|
+
|
7
|
+
require 'bundler/gem_tasks'
|
8
|
+
Bundler::GemHelper.install_tasks
|
9
|
+
|
10
|
+
require 'rspec/core/rake_task'
|
11
|
+
RSpec::Core::RakeTask.new(:spec)
|
12
|
+
|
13
|
+
task default: :spec
|
@@ -0,0 +1,162 @@
|
|
1
|
+
|
2
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'plugin', '**', '*.rb')) do |f|
|
3
|
+
require f
|
4
|
+
end
|
5
|
+
|
6
|
+
module Locomotive
|
7
|
+
|
8
|
+
# Include this module in a class which should be registered as a Locomotive
|
9
|
+
# plugin. See the documentation for the various methods which can be called
|
10
|
+
# or overridden to describe the plugin
|
11
|
+
module Plugin
|
12
|
+
|
13
|
+
include ConfigUI
|
14
|
+
include DBModels
|
15
|
+
include Liquid
|
16
|
+
|
17
|
+
# @private
|
18
|
+
def self.included(base)
|
19
|
+
self.add_db_model_class_methods(base)
|
20
|
+
self.add_liquid_tag_methods(base)
|
21
|
+
base.extend ClassMethods
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
# Add a before filter to be called by the underlying controller
|
26
|
+
#
|
27
|
+
# @param meth[Symbol] the method to call
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# before_filter :my_method
|
31
|
+
def before_filter(meth)
|
32
|
+
@before_filters ||= []
|
33
|
+
@before_filters << meth
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get list of before filters
|
37
|
+
#
|
38
|
+
# @return [Array<Symbol>] an array of the method symbols
|
39
|
+
def before_filters
|
40
|
+
@before_filters ||= []
|
41
|
+
end
|
42
|
+
|
43
|
+
# Override this method to provide a module or array of modules to include
|
44
|
+
# as liquid filters in the site. All public methods in the module will be
|
45
|
+
# included as filters after being prefixed with the plugin id
|
46
|
+
# (#\\{plugin_id}_#\\{method_name})
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# class MyPlugin
|
50
|
+
# def self.liquid_filters
|
51
|
+
# [ MyFilters, MoreFilters ]
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
def liquid_filters
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
# Override this method to specify the liquid tags supplied by this
|
59
|
+
# plugin. The return value must be a hash whose keys are the tag names
|
60
|
+
# and values are the tag classes. The tag names will be included in
|
61
|
+
# Locomotive's liquid renderer after being prefixed with the plugin id
|
62
|
+
# (#\\{plugin_id}_#\\{tag_name})
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# class MyPlugin
|
66
|
+
# def self.liquid_tags
|
67
|
+
# { :my_tag => MyTag, :other_tag => OtherTag }
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
def liquid_tags
|
71
|
+
{}
|
72
|
+
end
|
73
|
+
|
74
|
+
# Create a +has_many+ mongoid relationship to objects of the given class.
|
75
|
+
# The given class should be derived from Locomotive::Plugin::DBModel.
|
76
|
+
# This will add the following methods to the plugin object:
|
77
|
+
#
|
78
|
+
# [#\\{name}] Returns a list of persisted objects
|
79
|
+
#
|
80
|
+
# [#\\{name}=] Setter for the list of persisted objects
|
81
|
+
#
|
82
|
+
# @param name[String] the name of the relationship
|
83
|
+
#
|
84
|
+
# @param klass[Class] the class of the objects to be stored
|
85
|
+
def has_many(name, klass)
|
86
|
+
self.create_has_many_relationship(name, klass)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Create a +has_one+ mongoid relationship to an object of the given
|
90
|
+
# class. The given class should be derived from
|
91
|
+
# Locomotive::Plugin::DBModel. This will add the following methods to the
|
92
|
+
# plugin object:
|
93
|
+
#
|
94
|
+
# [#\\{name}] Returns the related object
|
95
|
+
#
|
96
|
+
# [#\\{name}=] Setter for the related object
|
97
|
+
#
|
98
|
+
# [build_#\\{name}] Builds the related object
|
99
|
+
#
|
100
|
+
# [create_#\\{name}] Builds and saves the related object
|
101
|
+
#
|
102
|
+
# @param name[String] the name of the relationship
|
103
|
+
#
|
104
|
+
# @param klass[Class] the class of the object to be stored
|
105
|
+
def has_one(name, klass)
|
106
|
+
self.create_has_one_relationship(name, klass)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# This variable is set by LocomotiveCMS. It contains the controller which
|
111
|
+
# is handling the current request
|
112
|
+
attr_accessor :controller
|
113
|
+
|
114
|
+
# This variable is set by LocomotiveCMS. It contains the current
|
115
|
+
# configuration hash for the plugin
|
116
|
+
attr_accessor :config
|
117
|
+
|
118
|
+
# Initialize by supplying the current config parameters. Note that this
|
119
|
+
# method should not be overridden for custom initialization of plugin
|
120
|
+
# objects. Instead, override the initialize_plugin method
|
121
|
+
def initialize(config)
|
122
|
+
self.config = config
|
123
|
+
self.load_or_create_db_model_container!
|
124
|
+
self.save_db_model_container
|
125
|
+
self.initialize_plugin
|
126
|
+
end
|
127
|
+
|
128
|
+
# Override this method to supply custom initialization code for the plugin
|
129
|
+
# object. <b>Do not override the normal +initialize+ method</b>
|
130
|
+
def initialize_plugin
|
131
|
+
end
|
132
|
+
|
133
|
+
# Get all before filters which have been added to the controller
|
134
|
+
def before_filters
|
135
|
+
self.class.before_filters
|
136
|
+
end
|
137
|
+
|
138
|
+
# Override this method to provide a liquid drop which should be available
|
139
|
+
# in the CMS
|
140
|
+
def to_liquid
|
141
|
+
nil
|
142
|
+
end
|
143
|
+
|
144
|
+
# Override this method to supply a path to the config UI template file.
|
145
|
+
# This file should be an HTML or HAML file using the Handlebars.js
|
146
|
+
# templating language.
|
147
|
+
def config_template_file
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
|
151
|
+
# This method may be overridden to supply the raw HTML string to be used
|
152
|
+
# for the config UI. The HTML string may be a Handlebars.js template. By
|
153
|
+
# default, this method will use the file supplied by the
|
154
|
+
# +config_template_file+ method to construct the string (see
|
155
|
+
# #config_template_file)
|
156
|
+
def config_template_string
|
157
|
+
self.default_config_template_string
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
module Locomotive
|
3
|
+
module Plugin
|
4
|
+
# @private
|
5
|
+
module ConfigUI
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def default_config_template_string
|
10
|
+
filepath = self.config_template_file
|
11
|
+
|
12
|
+
if filepath
|
13
|
+
filepath = filepath.to_s
|
14
|
+
if filepath.end_with?('haml')
|
15
|
+
Haml::Engine.new(IO.read(filepath)).render
|
16
|
+
else
|
17
|
+
IO.read(filepath)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|