locomotive_plugins 1.0.0.beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 [](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
|