hobo 0.8.10 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGES.txt +126 -2
  2. data/Rakefile +4 -1
  3. data/bin/hobo +1 -1
  4. data/doctest/scopes.rdoctest +11 -4
  5. data/dryml_generators/rapid/cards.dryml.erb +8 -2
  6. data/dryml_generators/rapid/forms.dryml.erb +5 -4
  7. data/dryml_generators/rapid/pages.dryml.erb +150 -65
  8. data/lib/hobo.rb +1 -1
  9. data/lib/hobo/accessible_associations.rb +2 -0
  10. data/lib/hobo/authentication_support.rb +1 -1
  11. data/lib/hobo/controller.rb +11 -3
  12. data/lib/hobo/dryml/dryml_doc.rb +1 -1
  13. data/lib/hobo/fake_initializer.rb +14 -0
  14. data/lib/hobo/hobo_helper.rb +94 -6
  15. data/lib/hobo/lifecycles.rb +17 -2
  16. data/lib/hobo/lifecycles/lifecycle.rb +1 -1
  17. data/lib/hobo/lifecycles/transition.rb +12 -4
  18. data/lib/hobo/model.rb +25 -22
  19. data/lib/hobo/model_controller.rb +42 -37
  20. data/lib/hobo/model_router.rb +11 -7
  21. data/lib/hobo/permissions.rb +12 -10
  22. data/lib/hobo/permissions/associations.rb +1 -1
  23. data/lib/hobo/static_tags +21 -0
  24. data/lib/hobo/user.rb +7 -3
  25. data/lib/hobo/user_controller.rb +7 -7
  26. data/lib/hobo/view_hints.rb +10 -3
  27. data/rails_generators/hobo/USAGE +4 -0
  28. data/rails_generators/hobo_admin_site/USAGE +16 -0
  29. data/rails_generators/hobo_front_controller/hobo_front_controller_generator.rb +11 -2
  30. data/rails_generators/hobo_front_controller/templates/controller.rb +6 -0
  31. data/rails_generators/hobo_front_controller/templates/summary.dryml +103 -0
  32. data/rails_generators/hobo_model_resource/USAGE +38 -0
  33. data/rails_generators/hobo_rapid/USAGE +3 -0
  34. data/rails_generators/hobo_rapid/templates/hobo-rapid.js +7 -3
  35. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +4 -0
  36. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +5 -0
  37. data/rails_generators/hobo_subsite/USAGE +16 -0
  38. data/rails_generators/hobo_subsite/hobo_subsite_generator.rb +1 -1
  39. data/rails_generators/hobo_user_controller/templates/controller.rb +2 -2
  40. data/rails_generators/hobo_user_model/templates/model.rb +6 -1
  41. data/taglibs/rapid.dryml +1 -0
  42. data/taglibs/rapid_core.dryml +4 -4
  43. data/taglibs/rapid_forms.dryml +29 -21
  44. data/taglibs/rapid_generics.dryml +3 -1
  45. data/taglibs/rapid_lifecycles.dryml +14 -9
  46. data/taglibs/rapid_navigation.dryml +1 -1
  47. data/taglibs/rapid_plus.dryml +1 -0
  48. data/taglibs/rapid_summary.dryml +300 -0
  49. data/taglibs/rapid_support.dryml +1 -1
  50. data/taglibs/rapid_user_pages.dryml +21 -19
  51. data/test/permissions/test_permissions.rb +1 -1
  52. metadata +12 -4
@@ -16,7 +16,7 @@ class HoboError < RuntimeError; end
16
16
 
17
17
  module Hobo
18
18
 
19
- VERSION = "0.8.10"
19
+ VERSION = "0.9.0"
20
20
 
21
21
  class PermissionDeniedError < RuntimeError; end
22
22
 
@@ -26,6 +26,8 @@ module Hobo
26
26
 
27
27
  def find_or_create_and_update(owner, association_name, finder, record_hash_or_string)
28
28
  if record_hash_or_string.is_a?(String)
29
+ return nil if record_hash_or_string.blank?
30
+
29
31
  # An ID or a name - the passed block will find the record
30
32
  record = find_by_name_or_id(finder, record_hash_or_string)
31
33
 
@@ -67,7 +67,7 @@ module Hobo
67
67
  accepts.xml do
68
68
  headers["Status"] = "Unauthorized"
69
69
  headers["WWW-Authenticate"] = %(Basic realm="Web Password")
70
- render :text => "Couldn't authenticate you", :status => '401 Unauthorized'
70
+ render :text => ht("hobo.messages.unauthenticated", :default=>["Couldn't authenticate you"], :status => '401 Unauthorized')
71
71
  end
72
72
  end
73
73
  false
@@ -23,6 +23,7 @@ module Hobo
23
23
  Thread.current['Hobo.current_controller'] = nil # should avoid memory-leakage
24
24
  end
25
25
  @included_taglibs = []
26
+ rescue_from ActionController::RoutingError, :with => :not_found
26
27
  end
27
28
  Hobo::HoboHelper.add_to_controller(klass)
28
29
  end
@@ -108,9 +109,10 @@ module Hobo
108
109
 
109
110
  def render_tags(objects, tag, options={})
110
111
  for_type = options.delete(:for_type)
112
+ base_tag = tag
111
113
 
112
114
  results = objects.map do |o|
113
- tag = tag_renderer.find_polymorphic_tag(tag, o.class) if for_type
115
+ tag = tag_renderer.find_polymorphic_tag(base_tag, o.class) if for_type
114
116
  tag_renderer.send(tag, options.merge(:with => o))
115
117
  end.join
116
118
 
@@ -154,8 +156,14 @@ module Hobo
154
156
  request.env['HTTP_CACHE_CONTROL'] =~ /max-age=\s*0/
155
157
  end
156
158
 
157
- def not_found
158
-
159
+ def not_found(error)
160
+ if "not_found_response".in?(self.class.superclass.instance_methods)
161
+ super
162
+ elsif render_tag("not-found-page", {}, :status => 404)
163
+ # cool
164
+ else
165
+ render(:text => ht(:"hobo.messages.not_found", :default=>["The page you requested cannot be found."]) , :status => 404)
166
+ end
159
167
  end
160
168
 
161
169
  end
@@ -156,4 +156,4 @@ module Hobo
156
156
 
157
157
  end
158
158
 
159
- end
159
+ end
@@ -0,0 +1,14 @@
1
+ # really what we want is a reference to the Initializer used in
2
+ # config/boot.rb. But since we can't monkey patch that file, we'll
3
+ # use a fake instead.
4
+
5
+ # this is used by the rapid_summary tag with_plugins
6
+ module Hobo
7
+ class FakeInitializer
8
+ attr_reader :configuration
9
+
10
+ def initialize(config = Rails.configuration)
11
+ @configuration = config
12
+ end
13
+ end
14
+ end
@@ -45,7 +45,7 @@ module Hobo
45
45
 
46
46
 
47
47
  def subsite
48
- params[:controller].match(/([^\/]+)\//)._?[1]
48
+ params[:controller]._?.match(/([^\/]+)\//)._?[1]
49
49
  end
50
50
 
51
51
 
@@ -192,12 +192,20 @@ module Hobo
192
192
 
193
193
 
194
194
  def first_item?
195
- this == scope.repeat_collection.first
195
+ if scope.repeat_collection.respond_to? :each_pair
196
+ this == scope.repeat_collection.first.try.last
197
+ else
198
+ this == scope.repeat_collection.first
199
+ end
196
200
  end
197
201
 
198
202
 
199
203
  def last_item?
200
- this == scope.repeat_collection.last
204
+ if scope.repeat_collection.respond_to? :each_pair
205
+ this == scope.repeat_collection.last.try.last
206
+ else
207
+ this == scope.repeat_collection.last
208
+ end
201
209
  end
202
210
 
203
211
 
@@ -476,12 +484,16 @@ module Hobo
476
484
 
477
485
  # --- ViewHint Helpers --- #
478
486
 
479
- def this_field_name
480
- this_parent.class.try.view_hints.try.field_name(this_field) || this_field
487
+ def this_field_name
488
+ key = "#{this_parent.class.try.name.underscore}.#{this_field.to_s}"
489
+ default = this_parent.class.try.view_hints.try.field_name(this_field) || this_field
490
+ I18n.t(key, :default => default, :scope => [:activerecord, :attributes], :count => 1)
481
491
  end
482
492
 
483
493
  def this_field_help
484
- this_parent.class.try.view_hints.try.field_help[this_field.to_sym]
494
+ key = "#{this_parent.class.try.name.pluralize.underscore}.hints.#{this_field.to_s}"
495
+ default = this_parent.class.try.view_hints.try.field_help[this_field.to_sym] || ""
496
+ I18n.t(key,:default=>default)
485
497
  end
486
498
 
487
499
 
@@ -499,6 +511,82 @@ module Hobo
499
511
  logger.debug("###################\n")
500
512
  args.first unless args.empty?
501
513
  end
514
+
515
+ # --- Translation Helper --- #
516
+ #
517
+ # Uses RoR native I18n.translate.
518
+ #
519
+ # Adds some conventions for easier hobo translation.
520
+ # 1. Assumes the first part of the key to be a model name (e.g.: users.index.title -> user)
521
+ # 2. Tries to translate the model by lookup for: (e.g.: user-> activerecord.models.user)
522
+ # 3. Adds a default fallback to the beginning of the fallback chain
523
+ # by replacing the first part of the key with "hobo" and using the translated model name
524
+ # as additional attribute. This allows us to have default translations
525
+ # (e.g.: hobo.index.title: "{{model}} Index")
526
+ #
527
+ # Is also used as a tag in the dryml-view files. The syntax is:
528
+ # <ht key="my.app">My Application</ht>
529
+ # --> Will lookup the "my.app"-key for your locale and replaces the "My Application" content
530
+ # if found.
531
+ #
532
+ # <ht key="my" app="Program">My Application</ht>
533
+ # --> Will look up both the "my"- and "app"-key for your locale, and replaces the
534
+ # "My Application" with the "my"-key contents (interpolated using the "app"-key.
535
+ # sample.en.yml-file:
536
+ # "no":
537
+ # my: "Mitt {{app}}"
538
+ # The output should be: Mitt Program
539
+ #
540
+ # Otherwise with features as the ht method, step 1, 2 and 3 above.
541
+ def ht(key, options={})
542
+
543
+ # Check if called as a tag, i.e. like this <ht></ht>
544
+ if (key.class == Hash)
545
+ if key.has_key?(:default) && !key[:default].blank?
546
+ logger.warn "hobo-i18n: 'default' should not be used as an attribute on the ht-tag. If used, then you need to make sure that the tags inner-contents are not used. These are normally treated as defaults automatically, but if there is a default attribute then that inner-content will be hidden from this method - and will not be replaced with the translation found."
547
+ end
548
+ defaults = options[:default];
549
+ # Swap key and options, remove options[:key]
550
+ options = key
551
+ key = options.delete(:key) # returns value for options[:key] as well as deleting it
552
+ # Set options[:default] to complete the tag-argument-conversion process.
553
+ options[:default] = (defaults.class == Proc) ? [defaults.call(options)] : (options[:default].blank? ? [] : [options[:default]])
554
+ else
555
+ # Not called as a tag. Prepare options[:default].
556
+ if options[:default].blank?
557
+ options[:default]=[]
558
+ elsif options[:default].class != Array
559
+ options[:default] = [options[:default]]
560
+ end
561
+ end
562
+
563
+ # assume the first part of the key to be the model
564
+ keys = key.to_s.split(".")
565
+ if keys.length > 1
566
+ model = keys.shift()
567
+ subkey = keys.join(".")
568
+ else
569
+ subkey = key
570
+ end
571
+
572
+ # add :"hobo.#{key}" as the first fallback
573
+ options[:default].unshift("hobo.#{subkey}".to_sym)
574
+
575
+ # translate the model
576
+ unless model.blank?
577
+ translated_model = I18n.translate( "activerecord.models.#{model.singularize.underscore}", :default=>model).titleize
578
+ options[:model] = translated_model
579
+ end
580
+
581
+ begin
582
+ key_prefix = "<span class='translation-key'>#{key}</span>" if HOBO_SHOW_LOCALE_KEYS
583
+ rescue
584
+ puts "Set HOBO_SHOW_LOCALE_KEYS=true in your environment to get all locale keys displayed"
585
+ end
586
+
587
+ logger.info "..translate(#{key}, #{options.inspect}) to #{I18n.locale}"
588
+ I18n.translate(key.to_sym, options)+(key_prefix ? key_prefix:"")
589
+ end
502
590
 
503
591
  end
504
592
 
@@ -20,7 +20,9 @@ module Hobo
20
20
  :key_timestamp_field => :key_timestamp,
21
21
  :key_timeout => 999.years)
22
22
 
23
- if defined? self::Lifecycle
23
+ # use const_defined so that subclasses can define lifecycles
24
+ # TODO: figure out how to merge with parent, if desired
25
+ if self.const_defined?(:Lifecycle)
24
26
  lifecycle = self::Lifecycle
25
27
  else
26
28
  # First call
@@ -35,6 +37,10 @@ module Hobo
35
37
 
36
38
  default = lifecycle.default_state ? { :default => lifecycle.default_state.name.to_s } : {}
37
39
  declare_field(options[:state_field], :string, default)
40
+ unless options[:index] == false
41
+ index_options = { :name => options[:index] } unless options[:index] == true
42
+ index(options[:state_field], index_options || {})
43
+ end
38
44
  never_show options[:state_field]
39
45
  attr_protected options[:state_field]
40
46
 
@@ -76,7 +82,16 @@ module Hobo
76
82
 
77
83
 
78
84
  def lifecycle
79
- @lifecycle ||= self.class::Lifecycle.new(self)
85
+ @lifecycle ||= if defined? self.class::Lifecycle
86
+ self.class::Lifecycle.new(self)
87
+ else
88
+ # search through superclasses
89
+ current = self.class.superclass
90
+ until (defined?(current::Lifecycle) || current.nil? || !current.respond_to?(:lifecycle)) do
91
+ current = current.superclass
92
+ end
93
+ current.class::Lifecycle.new(self) if defined? current::Lifecycle
94
+ end
80
95
  end
81
96
 
82
97
  end
@@ -126,7 +126,7 @@ module Hobo
126
126
 
127
127
  def transition(name, user, attributes)
128
128
  transition = find_transition(name, user)
129
- transition.run!(record, user, attributes)
129
+ transition.run!(record, user, attributes) unless transition.nil?
130
130
  end
131
131
 
132
132
 
@@ -20,11 +20,19 @@ module Hobo
20
20
 
21
21
 
22
22
  def extract_attributes(attributes)
23
- update_attributes = options.fetch(:params, [])
24
- attributes & update_attributes
23
+ model = lifecycle.model
24
+ params = options.fetch(:params, [])
25
+ allowed = params.dup
26
+ params.each do |p|
27
+ if (refl = model.reflections[p]) && refl.macro == :belongs_to
28
+ allowed << refl.primary_key_name.to_s
29
+ allowed << refl.options[:foreign_type] if refl.options[:polymorphic]
30
+ end
31
+ end
32
+ attributes & allowed
25
33
  end
26
-
27
-
34
+
35
+
28
36
  def change_state(record)
29
37
  record.lifecycle.become(get_state(record, end_state))
30
38
  end
@@ -85,6 +85,7 @@ module Hobo
85
85
  @models_loaded = true
86
86
  end
87
87
 
88
+ @model_names ||= Set.new
88
89
  # ...but only return the ones that registered themselves
89
90
  @model_names.map do |name|
90
91
  name.safe_constantize || (@model_names.delete name; nil)
@@ -137,8 +138,18 @@ module Hobo
137
138
  require 'active_record/viewhints_validations_interceptor'
138
139
  include Hobo::ViewHintsValidationsInterceptor
139
140
 
141
+ # TODO: should this be an inheriting_cattr_accessor as well? Probably.
140
142
  attr_accessor :creator_attribute
141
- attr_writer :name_attribute, :primary_content_attribute
143
+ inheriting_cattr_accessor :name_attribute => Proc.new { |c|
144
+ names = c.columns.*.name + c.public_instance_methods
145
+ NAME_FIELD_GUESS.detect {|f| f.in? names }
146
+ }
147
+
148
+ inheriting_cattr_accessor :primary_content_attribute => Proc.new { |c|
149
+ names = c.columns.*.name + c.public_instance_methods
150
+ PRIMARY_CONTENT_GUESS.detect {|f| f.in? names }
151
+ }
152
+
142
153
 
143
154
  def named(*args)
144
155
  raise NoNameError, "Model #{name} has no name attribute" unless name_attribute
@@ -157,21 +168,6 @@ module Hobo
157
168
  end
158
169
 
159
170
 
160
- def name_attribute
161
- @name_attribute ||= begin
162
- names = columns.*.name + public_instance_methods
163
- NAME_FIELD_GUESS.detect {|f| f.in? names }
164
- end
165
- end
166
-
167
-
168
- def primary_content_attribute
169
- @primary_content_attribute ||= begin
170
- names = columns.*.name + public_instance_methods
171
- PRIMARY_CONTENT_GUESS.detect {|f| f.in? names }
172
- end
173
- end
174
-
175
171
  def dependent_collections
176
172
  reflections.values.select do |refl|
177
173
  refl.macro == :has_many && refl.options[:dependent]
@@ -203,6 +199,9 @@ module Hobo
203
199
  belongs_to_without_test_methods(name, options, &block)
204
200
  refl = reflections[name]
205
201
  if options[:polymorphic]
202
+ # TODO: the class lookup in _is? below is incomplete; a polymorphic association to an STI base class
203
+ # will fail to match an object of a derived type
204
+ # (ie X belongs_to Y (polymorphic), Z is a subclass of Y; @x.y_is?(some_z) will never pass)
206
205
  class_eval %{
207
206
  def #{name}_is?(target)
208
207
  target.class.name == self.#{refl.options[:foreign_type]} && target.id == self.#{refl.primary_key_name}
@@ -214,7 +213,7 @@ module Hobo
214
213
  else
215
214
  class_eval %{
216
215
  def #{name}_is?(target)
217
- target.class == ::#{refl.klass.name} && target.id == self.#{refl.primary_key_name}
216
+ target.class <= ::#{refl.klass.name} && target.id == self.#{refl.primary_key_name}
218
217
  end
219
218
  def #{name}_changed?
220
219
  #{refl.primary_key_name}_changed?
@@ -345,7 +344,7 @@ module Hobo
345
344
 
346
345
  refl.klass.reflections.values.find do |r|
347
346
  r.macro.in?(reverse_macros) &&
348
- r.klass == self &&
347
+ r.klass >= self &&
349
348
  !r.options[:conditions] &&
350
349
  r.primary_key_name == refl.primary_key_name
351
350
  end
@@ -434,14 +433,18 @@ module Hobo
434
433
  attr = self.class.creator_attribute
435
434
  return unless attr
436
435
 
437
- # Is creator a string field or an association?
438
- if self.class.attr_type(attr)._? <= String
436
+ attr_type = self.class.creator_type
437
+
438
+ # Is creator an instance, a string field or an association?
439
+ if !attr_type.is_a?(Class)
440
+ # attr_type is an instance - typically AssociationReflection for a polymorphic association
441
+ self.send("#{attr}=", user)
442
+ elsif self.class.attr_type(attr)._? <= String
439
443
  # Set it to the name of the current user
440
444
  self.send("#{attr}=", user.to_s) unless user.guest?
441
445
  else
442
446
  # Assume user is a user object, but don't set if we've got a type mismatch
443
- t = self.class.creator_type
444
- self.send("#{attr}=", user) if t.nil? || user.is_a?(t)
447
+ self.send("#{attr}=", user) if attr_type.nil? || user.is_a?(attr_type)
445
448
  end
446
449
  end
447
450
 
@@ -383,7 +383,7 @@ module Hobo
383
383
  (!destroyed && object_url(@this)) ||
384
384
 
385
385
  # Then the show page of the 'owning' object if there is one
386
- (@this.class.default_dependent_on && object_url(@this.send(@this.class.default_dependent_on))) ||
386
+ object_url(owning_object) ||
387
387
 
388
388
  # Last try - the index page for this model
389
389
  object_url(@this.class) ||
@@ -391,6 +391,11 @@ module Hobo
391
391
  # Give up
392
392
  home_page
393
393
  end
394
+
395
+ def owning_object
396
+ method = @this.class.dependent_on.detect {|m| !@this.send(m).nil?}
397
+ method ? @this.send(method) : nil
398
+ end
394
399
 
395
400
 
396
401
  def url_for_page_path
@@ -457,9 +462,9 @@ module Hobo
457
462
 
458
463
  def find_owner_and_association(owner_association)
459
464
  refl = model.reflections[owner_association]
460
- klass = refl.klass
461
- id = params["#{owner_association}_id"]
462
- owner = klass.find(id)
465
+ owner_name = refl.macro == :has_many ? owner_association.to_s.singularize : owner_association
466
+ id = params["#{owner_name}_id"]
467
+ owner = refl.klass.find(id)
463
468
  instance_variable_set("@#{owner_association}", owner)
464
469
  [owner, owner.send(model.reverse_reflection(owner_association).name)]
465
470
  end
@@ -486,7 +491,7 @@ module Hobo
486
491
 
487
492
  def hobo_show(*args, &b)
488
493
  options = args.extract_options!
489
- self.this ||= find_instance(options)
494
+ self.this ||= args.first || find_instance(options)
490
495
  response_block(&b)
491
496
  end
492
497
 
@@ -544,7 +549,7 @@ module Hobo
544
549
 
545
550
 
546
551
  def create_response(new_action, options={}, &b)
547
- flash_notice "The #{@this.class.name.titleize.downcase} was created successfully" if valid?
552
+ flash_notice (ht( :"#{@this.class.name.pluralize.underscore}.messages.create.success", :default=>["The #{@this.class.name.titleize.downcase} was created successfully"])) if valid?
548
553
 
549
554
  response_block(&b) or
550
555
  if valid?
@@ -554,10 +559,12 @@ module Hobo
554
559
  end
555
560
  else
556
561
  respond_to do |wants|
562
+ # errors is used by the translation helper, ht, below.
563
+ errors = this.errors.full_messages.join("\n")
557
564
  wants.html { re_render_form(new_action) }
558
565
  wants.js { render(:status => 500,
559
- :text => ("Couldn't create the #{this.class.name.titleize.downcase}.\n" +
560
- this.errors.full_messages.join("\n"))) }
566
+ :text => ht( :"#{this.class.name.pluralize.underscore}.messages.create.error", :errors=>errors,:default=>["Couldn't create the #{this.class.name.titleize.downcase}.\n #{errors}"])
567
+ )}
561
568
  end
562
569
  end
563
570
  end
@@ -567,7 +574,7 @@ module Hobo
567
574
  options = args.extract_options!
568
575
 
569
576
  self.this ||= args.first || find_instance
570
- changes = options[:attributes] || attribute_parameters or raise RuntimeError, "No update specified in params"
577
+ changes = options[:attributes] || attribute_parameters or raise RuntimeError, ht(:"hobo.messages.update.no_attribute_error", :default=>["No update specified in params"])
571
578
  this.user_update_attributes(current_user, changes)
572
579
 
573
580
  # Ensure current_user isn't out of date
@@ -579,7 +586,8 @@ module Hobo
579
586
 
580
587
 
581
588
  def update_response(in_place_edit_field=nil, options={}, &b)
582
- flash_notice "Changes to the #{@this.class.name.titleize.downcase} were saved" if valid?
589
+
590
+ flash_notice (ht(:"#{@this.class.name.pluralize.underscore}.messages.update.success", :default=>["Changes to the #{@this.class.name.titleize.downcase} were saved"])) if valid?
583
591
 
584
592
  response_block(&b) or
585
593
  if valid?
@@ -602,10 +610,12 @@ module Hobo
602
610
  end
603
611
  else
604
612
  respond_to do |wants|
613
+ # errors is used by the translation helper, ht, below.
614
+ errors = @this.errors.full_messages.join("\n")
605
615
  wants.html { re_render_form(:edit) }
606
616
  wants.js { render(:status => 500,
607
- :text => ("There was a problem with that change.\n" +
608
- @this.errors.full_messages.join("\n"))) }
617
+ :text => ht(:"#{@this.class.name.pluralize.underscore}.messages.update.error",:default=>["There was a problem with that change.\n#{errors}"], :errors=>errors)
618
+ ) }
609
619
  end
610
620
  end
611
621
  end
@@ -615,7 +625,7 @@ module Hobo
615
625
  options = args.extract_options!
616
626
  self.this ||= args.first || find_instance
617
627
  this.user_destroy(current_user)
618
- flash_notice "The #{model.name.titleize.downcase} was deleted"
628
+ flash_notice ht( :"#{model.name.pluralize.underscore}.messages.destroy.success", :default=>["The #{model.name.titleize.downcase} was deleted"])
619
629
  destroy_response(options, &b)
620
630
  end
621
631
 
@@ -652,10 +662,12 @@ module Hobo
652
662
  else
653
663
  this.exempt_from_edit_checks = true
654
664
  respond_to do |wants|
665
+ # errors is used by the translation helper, ht, below.
666
+ errors = this.errors.full_messages.join("\n")
655
667
  wants.html { re_render_form(name) }
656
668
  wants.js { render(:status => 500,
657
- :text => ("Couldn't do creator #{name}.\n" +
658
- this.errors.full_messages.join("\n"))) }
669
+ :text => ht(:"#{@this.class.name.pluralize.underscore}.messages.creator.error", :default=>["Couldn't do creator #{name}.\n#{errors}"], :name=>name, :errors=>errors)
670
+ )}
659
671
  end
660
672
  end
661
673
  end
@@ -663,12 +675,13 @@ module Hobo
663
675
 
664
676
  def prepare_transition(name, options)
665
677
  key = options.delete(:key) || params[:key]
666
-
667
- self.this = find_instance do |record|
668
- # The block allows us to perform actions on the records before the permission check
669
- record.exempt_from_edit_checks = true
670
- record.lifecycle.provided_key = key
671
- end
678
+
679
+ # we don't use find_instance here, as it fails for key_holder transitions on objects that Guest can't view
680
+ record = model.find(params[:id])
681
+ record.exempt_from_edit_checks = true
682
+ record.lifecycle.provided_key = key
683
+ self.this = record
684
+
672
685
  this.lifecycle.find_transition(name, current_user) or raise Hobo::PermissionDeniedError
673
686
  end
674
687
 
@@ -691,10 +704,12 @@ module Hobo
691
704
  end
692
705
  else
693
706
  respond_to do |wants|
707
+ # errors is used by the translation helper, ht, below.
708
+ errors = this.errors.full_messages.join("\n")
694
709
  wants.html { re_render_form(name) }
695
710
  wants.js { render(:status => 500,
696
- :text => ("Couldn't do transition #{name}.\n" +
697
- this.errors.full_messages.join("\n"))) }
711
+ :text => ht(:"#{@this.class.name.pluralize.underscore}.messages.transition.error", :default=>["Couldn't do transition #{name}.\n#{errors}"], :name=>name, :errors=>errors)
712
+ )}
698
713
  end
699
714
  end
700
715
  end
@@ -748,28 +763,17 @@ module Hobo
748
763
  if render_tag("permission-denied-page", { }, :status => 403)
749
764
  # job done
750
765
  else
751
- render :text => "Permission Denied", :status => 403
766
+ render :text => ht(:"hobo.messages.permission_denied", :default=>["Permission Denied"]), :status => 403
752
767
  end
753
768
  end
754
769
  wants.js do
755
- render :text => "Permission Denied", :status => 403
770
+ render :text => ht(:"hobo.messages.permission_denied", :default=>["Permission Denied"]), :status => 403
756
771
  end
757
772
  end
758
773
  end
759
774
  end
760
775
 
761
776
 
762
- def not_found(error)
763
- if "not_found_response".in?(self.class.superclass.instance_methods)
764
- super
765
- elsif render_tag("not-found-page", {}, :status => 404)
766
- # cool
767
- else
768
- render(:text => "The page you requested cannot be found.", :status => 404)
769
- end
770
- end
771
-
772
-
773
777
  def this
774
778
  @this ||= (instance_variable_get("@#{model.name.underscore}") ||
775
779
  instance_variable_get("@#{model.name.underscore.pluralize}"))
@@ -794,7 +798,8 @@ module Hobo
794
798
  def render_with_hobo_model(*args, &block)
795
799
  options = args.extract_options!
796
800
  self.this = options[:object] if options[:object]
797
- this.user_view(current_user) if this && this.respond_to?(:user_view)
801
+ # this causes more problems than it solves, and Tom says it's not supposed to be here
802
+ # this.user_view(current_user) if this && this.respond_to?(:user_view)
798
803
  render_without_hobo_model(*args + [options], &block)
799
804
  end
800
805