locomotive_cms 0.0.3.1 → 0.0.3.3

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.
@@ -7,6 +7,7 @@ class ContentInstance
7
7
  include CustomFields::ProxyClassEnabler
8
8
 
9
9
  ## fields (dynamic fields) ##
10
+ field :_slug
10
11
  field :_position_in_list, :type => Integer, :default => 0
11
12
 
12
13
  ## validations ##
@@ -16,6 +17,7 @@ class ContentInstance
16
17
  embedded_in :content_type, :inverse_of => :contents
17
18
 
18
19
  ## callbacks ##
20
+ before_save :set_slug
19
21
  before_create :add_to_list_bottom
20
22
 
21
23
  ## named scopes ##
@@ -29,16 +31,29 @@ class ContentInstance
29
31
 
30
32
  protected
31
33
 
34
+ def set_slug
35
+ _alias = self.highlighted_field_alias
36
+ self._slug = self.send(_alias).parameterize('_')
37
+ end
38
+
32
39
  def add_to_list_bottom
33
40
  Rails.logger.debug "add_to_list_bottom"
34
41
  self._position_in_list = self.content_type.contents.size
35
42
  end
36
43
 
37
44
  def require_highlighted_field
38
- _alias = self.content_type.highlighted_field._alias.to_sym
45
+ _alias = self.highlighted_field_alias
39
46
  if self.send(_alias).blank?
40
47
  self.errors.add(_alias, :blank)
41
48
  end
42
49
  end
43
50
 
51
+ def highlighted_field_value
52
+ self.send(self.content_type.highlighted_field._name)
53
+ end
54
+
55
+ def highlighted_field_alias
56
+ self.content_type.highlighted_field._alias.to_sym
57
+ end
58
+
44
59
  end
@@ -0,0 +1,32 @@
1
+ module Models
2
+ module Extensions
3
+ module Page
4
+ module Templatized
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+
10
+ belongs_to_related :content_type
11
+
12
+ field :templatized, :type => Boolean, :default => false
13
+
14
+ field :content_type_visible_column
15
+
16
+ before_validate :set_slug_if_templatized
17
+ end
18
+
19
+ module InstanceMethods
20
+
21
+ def set_slug_if_templatized
22
+ self.slug = 'content_type_template' if self.templatized?
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+
@@ -6,6 +6,7 @@ class Page
6
6
  include Models::Extensions::Page::Tree
7
7
  include Models::Extensions::Page::Parts
8
8
  include Models::Extensions::Page::Render
9
+ include Models::Extensions::Page::Templatized
9
10
 
10
11
  ## fields ##
11
12
  field :title
@@ -31,8 +32,8 @@ class Page
31
32
 
32
33
  ## named scopes ##
33
34
  named_scope :latest_updated, :order_by => [[:updated_at, :desc]], :limit => Locomotive.config.lastest_items_nb
34
- named_scope :index, :where => { :slug => 'index', :depth => 0, :published => true }
35
- named_scope :not_found, :where => { :slug => '404', :depth => 0, :published => true }
35
+ named_scope :index, :where => { :slug => 'index', :depth => 0 }
36
+ named_scope :not_found, :where => { :slug => '404', :depth => 0 }
36
37
  named_scope :published, :where => { :published => true }
37
38
 
38
39
  ## behaviours ##
@@ -45,8 +45,6 @@ class AssetUploader < CarrierWave::Uploader::Base
45
45
  end
46
46
  end
47
47
 
48
- puts "content_type = #{value}"
49
-
50
48
  model.content_type = value
51
49
  end
52
50
 
@@ -1,7 +1,6 @@
1
1
  - if contents.empty?
2
2
  %p.no-items= t('.no_items', :url => new_admin_content_url(@content_type.slug))
3
3
  - else
4
- - puts contents.inspect
5
4
  %ul{ :id => 'contents-list', :class => "list #{'sortable' if @content_type.order_by == '_position_in_list'}" }
6
5
  - contents.each do |content|
7
6
  %li.content{ :id => "content-#{content._id}" }
@@ -1,5 +1,8 @@
1
1
  - title link_to(@account.name.blank? ? @account.name_was : @account.name, '#', :rel => 'my_account_name', :title => t('.ask_for_name'), :class => 'editable')
2
2
 
3
+ - content_for :head do
4
+ = javascript_include_tag 'admin/account'
5
+
3
6
  - content_for :submenu do
4
7
  = render 'admin/shared/menu/settings'
5
8
 
@@ -11,8 +11,13 @@
11
11
  - if not @page.index? and not @page.not_found?
12
12
  = f.input :parent_id, :as => :select, :collection => parent_pages_options, :include_blank => false
13
13
 
14
- = f.input :slug, :required => false, :hint => @page.slug.blank? ? '&nbsp;' : @page.url, :input_html => { :data_url => get_path_admin_pages_url, :disabled => @page.index? || @page.not_found? }
15
-
14
+ = f.input :slug, :required => false, :hint => @page.slug.blank? ? '&nbsp;' : @page.url, :input_html => { :data_url => get_path_admin_pages_url, :disabled => @page.index? || @page.not_found? }, :wrapper_html => { :style => "#{'display: none' if @page.templatized?}" }
15
+
16
+ = f.custom_input :templatized, :css => 'toggle' do
17
+ = f.check_box :templatized
18
+
19
+ = f.input :content_type_id, :as => :select, :collection => current_site.content_types.all.to_a, :include_blank => false, :wrapper_html => { :style => "#{'display: none' unless @page.templatized?}" }
20
+
16
21
  = f.custom_input :published, :css => 'toggle' do
17
22
  = f.check_box :published
18
23
 
@@ -1,4 +1,4 @@
1
- %li{ :id => "item-#{page.id}", :class => "#{'not-found' if page.not_found? }"}
1
+ %li{ :id => "item-#{page.id}", :class => "#{'not-found' if page.not_found? } #{'templatized' if page.templatized?}"}
2
2
  - if not page.index? and not page.children.empty?
3
3
  = image_tag 'admin/list/icons/node_closed.png', :class => 'toggler'
4
4
  %em
@@ -10,7 +10,7 @@
10
10
 
11
11
  = stylesheet_link_tag 'admin/layout', 'admin/jquery/ui', 'admin/plugins/toggle', 'admin/menu', 'admin/buttons', 'admin/formtastic', 'admin/formtastic_changes', 'admin/application', :media => 'screen', :cache => Rails.env.production? && !Locomotive.heroku?
12
12
 
13
- = javascript_include_tag 'admin/jquery', 'admin/jquery.ui', 'admin/rails', 'admin/utils', 'admin/plugins/shortcut', 'admin/plugins/toggle', 'admin/plugins/growl', 'admin/plugins/cookie', 'admin/application', 'admin/locales/datepicker_fr', :cache => Rails.env.production? && !Locomotive.heroku?
13
+ = javascript_include_tag 'admin/jquery', 'admin/jquery.ui', 'admin/rails', 'admin/utils', 'admin/plugins/subscribe', 'admin/plugins/shortcut', 'admin/plugins/toggle', 'admin/plugins/growl', 'admin/plugins/cookie', 'admin/application', 'admin/locales/datepicker_fr', :cache => Rails.env.production? && !Locomotive.heroku?
14
14
 
15
15
  %script{ :type => 'text/javascript' }
16
16
  = find_and_preserve(growl_message)
@@ -268,6 +268,7 @@ en:
268
268
  page:
269
269
  published: "Only authenticated accounts can view unpublished pages."
270
270
  cache_strategy: "Cache the page for better performance. The 'Simple' choice is a good compromise."
271
+ templatized: "Use the page as a template for a model you defined."
271
272
  snippet:
272
273
  slug: "You need to know it in order to insert the snippet inside a page or a layout"
273
274
  site:
@@ -290,6 +290,7 @@ fr:
290
290
  page:
291
291
  published: "Seuls les administrateurs authentifiés peuvent voir une page non publiée."
292
292
  cache_strategy: "Cache la page pour de meilleure performance. L'option 'Simple' est le meilleur compromis."
293
+ templatized: "Utilise la page comme un template pour un modèle défini."
293
294
  snippet:
294
295
  slug: "Utilisé pour insérer le snippet dans une page ou un gabarit."
295
296
  site:
@@ -10,7 +10,6 @@ require 'locomotive/httparty'
10
10
  require 'locomotive/inherited_resources'
11
11
  require 'locomotive/admin_responder'
12
12
 
13
- require 'redcloth'
14
13
  require 'mongo_session_store/mongoid'
15
14
 
16
15
  module Locomotive
@@ -8,6 +8,8 @@ require 'formtastic'
8
8
  require 'mongoid'
9
9
  require 'mongoid_acts_as_tree'
10
10
  require 'httparty'
11
+ require 'redcloth'
12
+ require 'actionmailer_with_request'
11
13
 
12
14
  # FIXME: get rid of it once custom_fields is a gem
13
15
  require File.dirname(__FILE__) + '/../../vendor/plugins/custom_fields/init.rb'
@@ -11,7 +11,9 @@ module Locomotive
11
11
  end
12
12
  end
13
13
 
14
-
14
+ def highlighted_field_value
15
+ @source.highlighted_field_value
16
+ end
15
17
 
16
18
  end
17
19
  end
@@ -3,7 +3,15 @@ module Locomotive
3
3
  module Drops
4
4
  class Page < Base
5
5
 
6
- liquid_attributes << :title << :slug
6
+ # liquid_attributes << :title << :slug
7
+
8
+ def title
9
+ @source.templatized? ? @context['content_instance'].highlighted_field_value : @source.title
10
+ end
11
+
12
+ def slug
13
+ @source.templatized? ? @source.content_type.slug.singularize : @source.slug
14
+ end
7
15
 
8
16
  def children
9
17
  @children ||= liquify(*@source.children)
@@ -29,10 +29,12 @@ module Locomotive
29
29
 
30
30
  source = context.registers[@site_or_page.to_sym]
31
31
 
32
+ puts "#{@site_or_page.to_sym} / source = #{source.inspect}"
33
+
32
34
  if source.respond_to?(:name) # site ?
33
- source = source.pages.first # start from home page
35
+ source = source.pages.index.first # start from home page
34
36
  else
35
- source = source.parent
37
+ source = source.parent || source
36
38
  end
37
39
 
38
40
  output = %{<ul id="nav">}
@@ -23,13 +23,28 @@ module Locomotive
23
23
  path.gsub!(/^\//, '')
24
24
  path = 'index' if path.blank?
25
25
 
26
- if page = current_site.pages.where(:fullpath => path).first
26
+ if path != 'index'
27
+ dirname = File.dirname(path).gsub(/^\.$/, '') # also look for templatized page path
28
+ path = [path, File.join(dirname, 'content_type_template').gsub(/^\//, '')]
29
+ end
30
+
31
+ # TODO: path is not correctly built + find content instance in order to render a 404 page if not found
32
+
33
+ if page = current_site.pages.any_in(:fullpath => [*path]).first
27
34
  if not page.published? and current_admin.nil?
28
35
  page = nil
36
+ else
37
+ if page.templatized?
38
+ @content_instance = page.content_type.contents.where(:_slug => File.basename(path.first)).first
39
+
40
+ if @content_instance.nil? # content instance not found
41
+ page = nil
42
+ end
43
+ end
29
44
  end
30
45
  end
31
46
 
32
- page || current_site.pages.not_found.first
47
+ page || current_site.pages.not_found.published.first
33
48
  end
34
49
 
35
50
  def locomotive_context
@@ -43,6 +58,11 @@ module Locomotive
43
58
  'current_page' => self.params[:page]
44
59
  }
45
60
 
61
+ if @page.templatized? # add instance from content type
62
+ assigns['content_instance'] = @content_instance
63
+ assigns[@page.content_type.slug.singularize] = @content_instance # just here to help to write readable liquid code
64
+ end
65
+
46
66
  registers = { :controller => self, :site => current_site, :page => @page }
47
67
 
48
68
  ::Liquid::Context.new(assigns, registers)
@@ -0,0 +1,5 @@
1
+ $(document).ready(function() {
2
+ $.subscribe('form.saved.success', function(event, data) {
3
+ $('#global-actions-bar a:first').html($('#my_account_name').val());
4
+ }, []);
5
+ });
@@ -31,6 +31,19 @@ $(document).ready(function() {
31
31
  }
32
32
  });
33
33
 
34
+ // templatized feature
35
+
36
+ $.subscribe('toggle.page_templatized.checked', function(event, data) {
37
+ $('#page_slug_input').hide();
38
+ $('#page_content_type_id_input').show();
39
+ }, []);
40
+
41
+ $.subscribe('toggle.page_templatized.unchecked', function(event, data) {
42
+ $('#page_slug_input').show();
43
+ $('#page_slug').val(makeSlug($('#page_title').val())).addClass('touched');
44
+ $('#page_content_type_id_input').hide();
45
+ }, []);
46
+
34
47
  // automatic slug from page title
35
48
  $('#page_title').keypress(function() {
36
49
  var input = $(this);
@@ -36,6 +36,7 @@ jQuery.fn.saveWithShortcut = function() {
36
36
  form.find('li.error input').eq(0).focus();
37
37
  } else {
38
38
  $.growl('success', data.notice);
39
+ $.publish('form.saved.success', [data]);
39
40
  }
40
41
  };
41
42
 
@@ -0,0 +1,367 @@
1
+ /*
2
+ * jquery.subscribe.1.1
3
+ *
4
+ * Implementation of publish/subcription framework for jQuery
5
+ * Requires use of jQuery. Tested with jQuery 1.3 and above
6
+ *
7
+ *
8
+ * Copyright (c) 2008 Eric Chijioke (obinna a-t g mail dot c o m)
9
+ *
10
+ *
11
+ * Dual licensed under the MIT and GPL licenses:
12
+ * http://www.opensource.org/licenses/mit-license.php
13
+ * http://www.gnu.org/licenses/gpl.html
14
+ *
15
+ * Release Notes:
16
+ *
17
+ * version 1.1:
18
+ *
19
+ * Fixed unexpected behavior which can occur when a script in a embedded page (page loaded in div,tab etc.) subscribes a handler for a topic using
20
+ * the jQuery subscribe ($.subscribe) or a no-id element but this subscribe plugin is not reloaded within that embedded page (for example, when
21
+ * script is included in containing page) . In this case, if the embedded page is reloaded without reloading the entire page (and plugin), the
22
+ * subscription could be made multiple times for the topic, which will call the handler multiple times each time the topic is published.
23
+ * Code has been added to prevent this when the subscription is made using the non-element subscribe ($.subscribe()), which assures that only one
24
+ * subscription is made for a topic for a given window/frame. To prevent this from happening for an element subscription ($elem.subscribe()), make
25
+ * sure that the element has an id attribute.
26
+ */
27
+
28
+
29
+ (function($){
30
+
31
+ _subscribe_topics = {};
32
+ _subscribe_handlers = {};
33
+
34
+ _subscribe_getDocumentWindow = function(document){
35
+
36
+ return document.parentWindow || document.defaultView;
37
+ };
38
+
39
+ $.fn.extend({
40
+
41
+ /**
42
+ * Creates a new topic without any subscribers.
43
+ * Not usually used explicitly
44
+ */
45
+ createTopic : function(topic) {
46
+ if(topic && !_subscribe_topics[topic]) {
47
+
48
+ _subscribe_topics[topic] = {};
49
+ _subscribe_topics[topic].objects = {};
50
+ _subscribe_topics[topic].objects['__noId__'] = [];
51
+ }
52
+
53
+ return this;
54
+ },
55
+
56
+ /**
57
+ * Destroy an existing topic and unsubscribe all subscribers
58
+ */
59
+ destroyTopic : function(topic) {
60
+
61
+ if(topic && _subscribe_topics[topic]) {
62
+
63
+ for(i in _subscribe_topics[topic].objects) {
64
+
65
+ var object = _subscribe_topics[topic].objects[i];
66
+
67
+ if($.isArray(object)) { // handle '__noId__' elements
68
+
69
+ if(object.length > 0) {
70
+
71
+ for(j in object) {
72
+
73
+ object[j].unbind(topic);
74
+ }
75
+ }
76
+
77
+ } else {
78
+
79
+ object.unbind(topic,data);
80
+ }
81
+ }
82
+ }
83
+
84
+ delete _subscribe_topics[topic];
85
+
86
+ return this;
87
+ },
88
+
89
+ /**
90
+ * Subscribes an object to particular topic with a handler.
91
+ * When the topic is published, this handler will be executed.
92
+ *
93
+ * Parameters:
94
+ * -topic- is the string name of the topic
95
+ * -handler- is a handler function and is of the form function(event, data), in which the 'this' refers to the element itself.
96
+ * handler can be a function or can be a string referring to a function previously registered using the $.subscribeHandler() function
97
+ * Note: returning 'false' from the handler will prevent subsequent handlers from being executed on this element during
98
+ * this call.
99
+ * -data- (optional) is additional data that is passed to the event handler as event.data when the topic is published
100
+ *
101
+ * Note: Unexpected behavior can occur when a script in a embedded page (page loaded in div,tab etc.) subscribes a handler for a topic using
102
+ * the jQuery subscribe ($.subscribe) or a no-id element but this subscribe plugin is not reloaded within that embedded page (for example, when
103
+ * script is included in containing page) . In this case, if the embedded page is reloaded without reloading the entire page (and plugin), the
104
+ * subscription could be made multiple times for the topic, which will call the handler multiple times each time the topic is published.
105
+ * Code has been added to prevent this when the subscription is made using the non-element subscribe ($.subscribe()), which assures that only one
106
+ * subscription is made for a topic for a given window/frame. To prevent this from happening for an element subscription ($elem.subscribe()), make
107
+ * sure that the element has an id attribute.
108
+ */
109
+ subscribe : function(topic, handler, data) {
110
+
111
+ if(this[0] && topic && handler) {
112
+
113
+ this.createTopic(topic);
114
+
115
+ if(this.attr('id')) {
116
+
117
+ _subscribe_topics[topic].objects[this.attr('id')] = this;
118
+
119
+ } else {
120
+
121
+ //do not subscribe the same window/frame document multiple times, this causes unexpected behavior of executing embedded scripts multiple times
122
+ var noIdObjects = _subscribe_topics[topic].objects['__noId__'];
123
+
124
+ if(this[0].nodeType == 9) { //if document is being bound (the case for non-element jQuery subscribing ($.subscribe)
125
+
126
+ for ( var index in noIdObjects) {
127
+
128
+ var noIdObject = noIdObjects[index];
129
+
130
+ if(noIdObject[0].nodeType == 9 && _subscribe_getDocumentWindow(this[0]).frameElement == _subscribe_getDocumentWindow(noIdObject[0]).frameElement ) {
131
+
132
+ return this;
133
+ }
134
+ }
135
+ }
136
+
137
+ var exists = false;
138
+ for(var i = 0; i < noIdObjects.length; i++){
139
+ if(noIdObjects[i] == this){
140
+ exists = true;
141
+ break;
142
+ }
143
+ }
144
+
145
+ if(!exists) {
146
+
147
+ _subscribe_topics[topic].objects['__noId__'].push(this);
148
+ }
149
+ }
150
+
151
+ if(typeof(handler) == 'function') {
152
+
153
+ this.bind(topic, data, handler);
154
+
155
+ } else if(typeof(handler) == 'string' && typeof(_subscribe_handlers[handler]) == 'function') {
156
+
157
+ this.bind(topic, data, _subscribe_handlers[handler]);
158
+ }
159
+ }
160
+
161
+ return this;
162
+ },
163
+
164
+ /**
165
+ * Remove a subscription of an element to a topic.
166
+ * This will unbind stop all handlers from executing on this element when the topic
167
+ * is published
168
+ */
169
+ unsubscribe : function(topic) {
170
+
171
+ if(topic) {
172
+
173
+ if(_subscribe_topics[topic]) {
174
+
175
+ if(this.attr('id')) {
176
+
177
+ var object = _subscribe_topics[topic].objects[this.attr('id')];
178
+
179
+ if(object) {
180
+
181
+ delete _subscribe_topics[topic].objects[this.attr('id')];
182
+ }
183
+
184
+ } else {
185
+
186
+ var noIdObjects = _subscribe_topics[topic].objects['__noId__'];
187
+
188
+ for(var i = 0; i < noIdObjects.length; i++){
189
+
190
+ if(noIdObjects[i] == this){
191
+
192
+ subscribe_topics[topic].objects['__noId__'].splice(index,1);
193
+ break;
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ this.unbind(topic);
200
+ }
201
+
202
+ return this;
203
+ },
204
+
205
+ /**
206
+ * Publishes a topic (triggers handlers on all topic subscribers)
207
+ * This ends up calling any subscribed handlers which are functions of the form function (event, data)
208
+ * where: event - is a standard jQuery event object
209
+ * data - is the data parameter that was passed to this publish() method
210
+ * event.data - is the data parameter passed to the subscribe() function when this published topic was subscribed to
211
+ * event.target - is the dom element that subscribed to the event (or the document element if $.subscribe() was used)
212
+ *
213
+ * Parameters:
214
+ * -topic- is the string name of the topic
215
+ * -data- (optional) is additional data that is passed to the event handler 'data' parameter when the topic is published
216
+ * handler can be a function or can be a string referring to a function previously registered using the $.subscribeHandler() function
217
+ * -originalEvent- (optional) may be passed in a reference to an event which triggered this publishing. This will be passed as the
218
+ * 'originalEvent' field of the triggered event which will allow for controlling the propagation of higher level events
219
+ * from within the topic handler. In other words, this allows one to cancel execution of all subsequent handlers on the originalEvent
220
+ * for this element by return 'false' from a handler that is subscribed to the topic published here. This can be especially useful
221
+ * in conjunction with publishOnEvent(), where a topic is published when an event executes (such as a click) and we want our
222
+ * handler logic prevent additional topics from being published (For example if our topic displays a 'delete confirm' dialog on click and
223
+ * the user cancels, we may want to prevent subsequent topics bound to the original click event from being published).
224
+ */
225
+ publish : function(topic, data, originalEvent) {
226
+
227
+ if(topic) {
228
+
229
+ this.createTopic(topic);
230
+
231
+ //if an orginal event exists, need to modify the event object to prevent execution of all
232
+ //other handlers if the result of the handler is false (which calls stopPropagation())
233
+
234
+ var subscriberStopPropagation = function(){
235
+
236
+ this.isImmediatePropagationStopped = function(){
237
+ return true;
238
+ };
239
+
240
+ (new $.Event).stopPropagation();
241
+
242
+ if(this.originalEvent) {
243
+
244
+ this.originalEvent.isImmediatePropagationStopped = function(){
245
+ return true;
246
+ };
247
+
248
+ this.originalEvent.stopPropagation = subscriberStopPropagation;
249
+ }
250
+ }
251
+
252
+ var event = jQuery.Event(topic);
253
+ $.extend(event,{originalEvent: originalEvent, stopPropagation: subscriberStopPropagation});
254
+
255
+ for(i in _subscribe_topics[topic].objects) {
256
+
257
+ var object = _subscribe_topics[topic].objects[i];
258
+
259
+ if($.isArray(object)) { // handle '__noId__' elements (if any)
260
+
261
+ if(object.length > 0) {
262
+
263
+ for(j in object) {
264
+
265
+ object[j].trigger( event,data);
266
+ }
267
+ }
268
+
269
+ } else {
270
+
271
+ object.trigger( event,data);
272
+ }
273
+ }
274
+
275
+ }
276
+
277
+ return this;
278
+ },
279
+
280
+ /**
281
+ * Binds an objects event handler to a publish call
282
+ *
283
+ * Upon the event triggering, this ends up calling any subscribed handlers which are functions of the form function (event, data)
284
+ * where: event- is a standard jQuery event object
285
+ * event.data- is the data parameter passed to the subscribe() function when this published topic was subscribed to
286
+ * data- is the data parameter that was passed to this publishOnEvent() method
287
+ * Parameters:
288
+ * -event- is the string name of the event upon which to publish the topic
289
+ * -topic- is the string name of the topic to publish when the event occurs
290
+ * -data- (optional) is additional data which will be passed in to the publish() method ant hen available as the second ('data')
291
+ * parameter to the topic handler
292
+ */
293
+ publishOnEvent : function(event, topic, data) {
294
+
295
+ if(event && topic) {
296
+
297
+ this.createTopic(topic);
298
+
299
+ this.bind(event, data, function (e) {
300
+
301
+ $(this).publish(topic, e.data, e);
302
+ });
303
+ }
304
+
305
+ return this;
306
+ }
307
+ });
308
+
309
+ /**
310
+ * Make publish(), createTopic() and destroyTopic() callable without an element context
311
+ * Often don't need a context to subscribe, publish, create or destroy a topic.
312
+ * We will call from the document context
313
+ */
314
+ $.extend({
315
+
316
+ /**
317
+ * Subscribe an event handler to a topic without an element context
318
+ *
319
+ * Note: Caution about subscribing using same document to topic multiple time (maybe by loading subscribe script multiple times)
320
+ *
321
+ */
322
+ subscribe : function(topic, handler, data) {
323
+ return $(window).subscribe(topic, handler, data);
324
+
325
+ },
326
+
327
+ /**
328
+ * Unsubscribe an event handler for a topic without an element context
329
+ *
330
+ */
331
+ unsubscribe : function(topic, handler, data) {
332
+
333
+ return $(window).unsubscribe(topic, handler, data);
334
+
335
+ },
336
+
337
+ /**
338
+ * Register a handler function which can then be referenced by name when calling subscribe()
339
+ */
340
+ subscribeHandler: function(name, handler) {
341
+
342
+ if(name && handler && typeof(handler) == "function") {
343
+
344
+ _subscribe_handlers[name] = handler;
345
+ }
346
+
347
+ return $(window);
348
+ },
349
+
350
+ publish: function(topic, data) {
351
+
352
+ return $(window).publish(topic,data);
353
+ },
354
+
355
+ createTopic: function(topic) {
356
+
357
+ return $(window).createTopic(topic);
358
+ },
359
+
360
+ destroyTopic: function(topic) {
361
+
362
+ return $(window).destroyTopic(topic);
363
+ }
364
+
365
+ });
366
+
367
+ })(jQuery);
@@ -64,6 +64,8 @@
64
64
  $(element).parent().css("background-color", settings.off_bg_color);
65
65
  $(element).parent().parent().prev().removeAttr("checked");
66
66
  $(element).removeClass("left").addClass("right");
67
+
68
+ $.publish('toggle.' + $(element).parent().parent().prev().attr('id') + '.unchecked', []);
67
69
  });
68
70
 
69
71
  }else{
@@ -78,6 +80,7 @@
78
80
  $(element).parent().parent().prev().attr("checked","checked");
79
81
  $(element).removeClass("right").addClass("left");
80
82
 
83
+ $.publish('toggle.' + $(element).parent().parent().prev().attr('id') + '.checked', []);
81
84
  });
82
85
 
83
86
  }
@@ -1,7 +1,7 @@
1
1
  $(document).ready(function() {
2
2
 
3
3
  var defaultValue = $('fieldset.editable-list li.template input[type=text]').val();
4
-
4
+
5
5
  /* __ fields ___ */
6
6
  $('fieldset.editable-list li.template input[type=text]').focus(function() {
7
7
  if ($(this).hasClass('void') && $(this).parents('li').hasClass('template'))
@@ -32,5 +32,8 @@ $(document).ready(function() {
32
32
  e.preventDefault();
33
33
  e.stopPropagation();
34
34
  });
35
-
35
+
36
+ $.subscribe('form.saved.success', function(event, data) {
37
+ $('#header h1 a').html($('#current_site_name').val());
38
+ }, []);
36
39
  });
@@ -117,7 +117,7 @@ ul.assets li.asset.last {
117
117
  margin-right: 0px;
118
118
  }
119
119
 
120
- ul.assets li.asset h4 { margin: 0px; height: 30px; }
120
+ ul.assets li.asset h4 { margin: 0px; height: 30px; border-bottom: 1px solid #ccced7; }
121
121
 
122
122
  ul.assets li.asset h4 a {
123
123
  position: relative;
@@ -205,6 +205,11 @@ div#uploadAssetsInputQueue { display: none; }
205
205
  cursor: move;
206
206
  }
207
207
 
208
+ #pages-list ul.folder li.templatized em {
209
+ background-position: left -62px;
210
+ cursor: pointer;
211
+ }
212
+
208
213
  #pages-list li .toggler {
209
214
  position: absolute;
210
215
  top: 9px;
@@ -60,7 +60,7 @@ form.formtastic fieldset.foldable.folded legend span em {
60
60
  form.formtastic fieldset.foldable ol {
61
61
  clear: both;
62
62
  width: 100%;
63
- overflow: hidden;
63
+ overflow: hidden;
64
64
  }
65
65
 
66
66
  form.formtastic fieldset.foldable.folded ol { display: none; }
@@ -80,6 +80,7 @@ form.formtastic fieldset.inputs ol {
80
80
  padding-top: 15px;
81
81
  padding-bottom: 5px;
82
82
  background: #ebedf4 url(/images/admin/form/footer.png) no-repeat 0 bottom;
83
+ border-top: 1px solid #ccced7;
83
84
  }
84
85
 
85
86
  @media screen and (-webkit-min-device-pixel-ratio:0) {
@@ -2,6 +2,7 @@
2
2
  background: #ebedf4 url(/images/admin/form/footer.png) no-repeat 0 bottom;
3
3
  width: 880px;
4
4
  padding: 20px 20px;
5
+ border-top: 1px solid #ccced7;
5
6
  }
6
7
 
7
8
  #page-parts {
@@ -12,6 +13,12 @@
12
13
  height: 30px;
13
14
  }
14
15
 
16
+ #page-parts .nav {
17
+ position: relative;
18
+ top: 1px;
19
+ z-index: 990;
20
+ }
21
+
15
22
  #page-parts .nav a {
16
23
  float: left;
17
24
  display: block;
@@ -52,19 +52,19 @@ describe 'Locomotive rendering system' do
52
52
 
53
53
  it 'should retrieve the index page /' do
54
54
  @controller.request.fullpath = '/'
55
- @controller.current_site.pages.expects(:where).with({ :fullpath => 'index' }).returns([@page])
55
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{index} }).returns([@page])
56
56
  @controller.send(:locomotive_page).should_not be_nil
57
57
  end
58
58
 
59
59
  it 'should also retrieve the index page (index.html)' do
60
60
  @controller.request.fullpath = '/index.html'
61
- @controller.current_site.pages.expects(:where).with({ :fullpath => 'index' }).returns([@page])
61
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{index} }).returns([@page])
62
62
  @controller.send(:locomotive_page).should_not be_nil
63
63
  end
64
64
 
65
65
  it 'should retrieve it based on the full path' do
66
66
  @controller.request.fullpath = '/about_us/team.html'
67
- @controller.current_site.pages.expects(:where).with({ :fullpath => 'about_us/team' }).returns([@page])
67
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{about_us/team about_us/content_type_template} }).returns([@page])
68
68
  @controller.send(:locomotive_page).should_not be_nil
69
69
  end
70
70
 
@@ -74,6 +74,31 @@ describe 'Locomotive rendering system' do
74
74
  @controller.send(:locomotive_page).should be_true
75
75
  end
76
76
 
77
+ context 'templatized page' do
78
+
79
+ before(:each) do
80
+ @content_type = Factory.build(:content_type, :site => nil)
81
+ @page.templatized = true
82
+ @page.content_type = @content_type
83
+ @controller.request.fullpath = '/projects/edeneo.html'
84
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{projects/edeneo projects/content_type_template} }).returns([@page])
85
+ end
86
+
87
+ it 'sets the content_instance variable' do
88
+ @content_type.contents.stubs(:where).returns([42])
89
+ @controller.send(:locomotive_page).should_not be_nil
90
+ @controller.instance_variable_get(:@content_instance).should == 42
91
+ end
92
+
93
+ it 'returns the 404 page if the instance does not exist' do
94
+ @content_type.contents.stubs(:where).returns([])
95
+ @controller.current_site.pages.expects(:not_found).returns([true])
96
+ @controller.send(:locomotive_page).should be_true
97
+ @controller.instance_variable_get(:@content_instance).should be_nil
98
+ end
99
+
100
+ end
101
+
77
102
  context 'non published page' do
78
103
 
79
104
  before(:each) do
@@ -83,7 +108,7 @@ describe 'Locomotive rendering system' do
83
108
 
84
109
  it 'should return the 404 page if the page has not been published yet' do
85
110
  @controller.request.fullpath = '/contact'
86
- @controller.current_site.pages.expects(:where).with({ :fullpath => 'contact' }).returns([@page])
111
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{contact content_type_template} }).returns([@page])
87
112
  @controller.current_site.pages.expects(:not_found).returns([true])
88
113
  @controller.send(:locomotive_page).should be_true
89
114
  end
@@ -91,7 +116,7 @@ describe 'Locomotive rendering system' do
91
116
  it 'should not return the 404 page if the page has not been published yet and admin is logged in' do
92
117
  @controller.current_admin = true
93
118
  @controller.request.fullpath = '/contact'
94
- @controller.current_site.pages.expects(:where).with({ :fullpath => 'contact' }).returns([@page])
119
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{contact content_type_template} }).returns([@page])
95
120
  @controller.send(:locomotive_page).should == @page
96
121
  end
97
122
 
@@ -333,4 +333,26 @@ describe Page do
333
333
  end
334
334
 
335
335
  end
336
+
337
+ describe 'templatized extension' do
338
+
339
+ before(:each) do
340
+ @page = Factory.build(:page, :site => nil, :templatized => true, :content_type_id => 42)
341
+ ContentType.stubs(:find).returns(Factory.build(:content_type, :site => nil))
342
+ end
343
+
344
+ it 'is considered as a templatized page' do
345
+ @page.templatized?.should be_true
346
+ end
347
+
348
+ it 'fills in the slug field' do
349
+ @page.valid?
350
+ @page.slug.should == 'content_type_template'
351
+ end
352
+
353
+ it 'does forget to set the content type id' do
354
+ @page.content_type_id.should == '42'
355
+ end
356
+
357
+ end
336
358
  end
metadata CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
6
6
  - 0
7
7
  - 0
8
8
  - 3
9
- - 1
10
- version: 0.0.3.1
9
+ - 3
10
+ version: 0.0.3.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Didier Lafforgue
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-07-15 00:00:00 +02:00
18
+ date: 2010-07-16 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -345,6 +345,7 @@ files:
345
345
  - app/models/extensions/asset/vignette.rb
346
346
  - app/models/extensions/page/parts.rb
347
347
  - app/models/extensions/page/render.rb
348
+ - app/models/extensions/page/templatized.rb
348
349
  - app/models/extensions/page/tree.rb
349
350
  - app/models/layout.rb
350
351
  - app/models/liquid_template.rb
@@ -812,6 +813,7 @@ files:
812
813
  - public/images/admin/plugins/toggle_handle_right-bg.png
813
814
  - public/images/admin/plugins/toggle_shadow-bg.png
814
815
  - public/images/admin/rails.png
816
+ - public/javascripts/admin/account.js
815
817
  - public/javascripts/admin/application.js
816
818
  - public/javascripts/admin/asset_collections.js
817
819
  - public/javascripts/admin/assets.js
@@ -859,6 +861,7 @@ files:
859
861
  - public/javascripts/admin/plugins/plupload/plupload.silverlight.xap
860
862
  - public/javascripts/admin/plugins/scrollTo.js
861
863
  - public/javascripts/admin/plugins/shortcut.js
864
+ - public/javascripts/admin/plugins/subscribe.js
862
865
  - public/javascripts/admin/plugins/tiny_mce/langs/en.js
863
866
  - public/javascripts/admin/plugins/tiny_mce/license.txt
864
867
  - public/javascripts/admin/plugins/tiny_mce/plugins/advhr/css/advhr.css