hobo 0.8.10 → 0.9.0

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