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 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