card 1.16.8 → 1.16.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/db/migrate_core_cards/{20150611203506_rails_inflection_updates.rb → 20120611203506_rails_inflection_updates.rb} +0 -0
  4. data/db/migrate_core_cards/20150326205655_bootswatch_themes.rb +0 -1
  5. data/db/migrate_core_cards/20150429090551_search_card_context.rb +1 -1
  6. data/db/migrate_core_cards/20150708224756_add_list_cards.rb +22 -0
  7. data/db/seed/new/card_actions.yml +789 -509
  8. data/db/seed/new/card_acts.yml +1 -1
  9. data/db/seed/new/card_changes.yml +2618 -1920
  10. data/db/seed/new/card_references.yml +1034 -901
  11. data/db/seed/new/cards.yml +2303 -1675
  12. data/db/seed/test/fixtures/card_actions.yml +1926 -1606
  13. data/db/seed/test/fixtures/card_acts.yml +354 -324
  14. data/db/seed/test/fixtures/card_changes.yml +5950 -5175
  15. data/db/seed/test/fixtures/card_references.yml +1861 -1630
  16. data/db/seed/test/fixtures/cards.yml +3768 -3048
  17. data/db/seed/test/seed.rb +121 -107
  18. data/lib/card.rb +2 -3
  19. data/lib/card/active_record_helper.rb +44 -0
  20. data/lib/card/auth.rb +51 -47
  21. data/lib/card/cache.rb +7 -3
  22. data/lib/card/codename.rb +7 -7
  23. data/lib/card/format.rb +2 -1
  24. data/lib/card/migration.rb +17 -16
  25. data/lib/card/name.rb +71 -20
  26. data/lib/card/set.rb +202 -166
  27. data/lib/card/simplecov_helper.rb +11 -7
  28. data/lib/card/subcards.rb +249 -0
  29. data/mod/01_core/set/all/collection.rb +1 -2
  30. data/mod/01_core/set/all/fetch.rb +167 -92
  31. data/mod/01_core/set/all/initialize.rb +8 -22
  32. data/mod/01_core/set/all/name.rb +128 -79
  33. data/mod/01_core/set/all/phases.rb +93 -95
  34. data/mod/01_core/set/all/subcards.rb +70 -0
  35. data/mod/01_core/set/all/tracked_attributes.rb +83 -59
  36. data/mod/01_core/set/all/trash.rb +14 -12
  37. data/mod/01_core/set/all/type.rb +3 -24
  38. data/mod/01_core/spec/set/all/initialize_spec.rb +44 -14
  39. data/mod/01_core/spec/set/all/permissions_spec.rb +206 -185
  40. data/mod/01_core/spec/set/all/tracked_attributes_spec.rb +0 -10
  41. data/mod/01_core/spec/set/all/trash_spec.rb +38 -13
  42. data/mod/01_core/spec/set/all/type_spec.rb +0 -19
  43. data/mod/01_history/set/all/content_history.rb +5 -3
  44. data/mod/01_history/set/all/history.rb +117 -82
  45. data/mod/02_basic_types/set/all/base.rb +50 -49
  46. data/mod/03_machines/lib/card/machine.rb +2 -1
  47. data/mod/03_machines/lib/javascript/wagn_mod.js.coffee +55 -17
  48. data/mod/03_machines/spec/set/type/javascript_spec.rb +18 -12
  49. data/mod/05_email/set/right/followers.rb +5 -5
  50. data/mod/05_email/set/right/following.rb +1 -1
  51. data/mod/05_email/set/type_plus_right/user/follow.rb +1 -1
  52. data/mod/05_standard/lib/carrier_wave/cardmount.rb +19 -11
  53. data/mod/05_standard/lib/file_uploader.rb +1 -1
  54. data/mod/05_standard/set/abstract/attachment.rb +20 -8
  55. data/mod/05_standard/set/all/list_changes.rb +43 -0
  56. data/mod/05_standard/set/all/rich_html/form.rb +21 -11
  57. data/mod/05_standard/set/all/rich_html/menu.rb +1 -1
  58. data/mod/05_standard/set/right/account.rb +5 -5
  59. data/mod/05_standard/set/self/head.rb +0 -1
  60. data/mod/05_standard/set/self/signin.rb +43 -35
  61. data/mod/05_standard/set/type/file.rb +9 -2
  62. data/mod/05_standard/set/type/list.rb +134 -0
  63. data/mod/05_standard/set/type/listed_by.rb +94 -0
  64. data/mod/05_standard/set/type/search_type.rb +62 -61
  65. data/mod/05_standard/set/type/signup.rb +94 -63
  66. data/mod/05_standard/set/type/user.rb +48 -39
  67. data/mod/05_standard/spec/set/all/account_spec.rb +1 -1
  68. data/mod/05_standard/spec/set/all/rich_html/form_spec.rb +2 -2
  69. data/mod/05_standard/spec/set/self/signin_spec.rb +23 -27
  70. data/mod/05_standard/spec/set/type/email_template_spec.rb +0 -2
  71. data/mod/05_standard/spec/set/type/list_spec.rb +140 -0
  72. data/mod/05_standard/spec/set/type/listed_by_spec.rb +157 -0
  73. data/mod/05_standard/spec/set/type/signup_spec.rb +38 -32
  74. data/spec/lib/card/subcards_spec.rb +126 -0
  75. metadata +14 -3
@@ -0,0 +1,43 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ event :trunk_cardtype_of_a_list_relation_changed,
4
+ changed: :type, after: :store, on: :update,
5
+ when: proc { Codename[:list] } do
6
+ type_key_was = (tk = Card.fetch(type_id_was)) && tk.key
7
+ if (list_cards = Card.search(left: name, type_id: Card::ListID))
8
+ list_cards.each do |card|
9
+ card.update_listed_by_cache_for card.item_keys, type_key: type_key_was
10
+ card.update_listed_by_cache_for card.item_keys
11
+ end
12
+ end
13
+ if (listed_by_cards = Card.search(left: name, type_id: Card::ListedByID))
14
+ listed_by_cards.each(&:update_cached_list)
15
+ end
16
+ end
17
+
18
+ event :trunk_name_of_a_list_relation_changed,
19
+ changed: :name, after: :store, on: :update,
20
+ when: proc { Codename[:list] } do
21
+ if (list_cards = Card.search(left: name, type_id: Card::ListID))
22
+ list_cards.each do |card|
23
+ card.update_listed_by_cache_for card.item_keys
24
+ end
25
+ end
26
+ if (listed_by_cards = Card.search(left: name, type_id: Card::ListedByID))
27
+ listed_by_cards.each(&:update_cached_list)
28
+ end
29
+ end
30
+
31
+ event :cardtype_of_list_item_changed,
32
+ changed: :type, before: :approve, on: :save,
33
+ when: proc { Codename[:list] } do
34
+ Card.search(type_id: Card::ListID, link_to: name).each do |card|
35
+ if card.item_type_id != type_id
36
+ errors.add(
37
+ :type,
38
+ "can't be changed because #{name} " \
39
+ "is referenced by list card #{card.name}"
40
+ )
41
+ end
42
+ end
43
+ end
@@ -1,20 +1,22 @@
1
1
  format :html do
2
2
  def edit_slot args={}
3
- #note: @mode should already be :edit here...
3
+ # note: @mode should already be :edit here...
4
4
  if args[:structure] || card.structure
5
5
  # multi-card editing
6
6
 
7
7
  if args[:core_edit] #need better name
8
8
  _render_core args
9
9
  else
10
- process_relative_tags optional_toolbar: :hide, structure: args[:structure]
10
+ process_relative_tags optional_toolbar: :hide,
11
+ structure: args[:structure]
11
12
  end
12
13
 
13
14
  else
14
15
  # single-card edit mode
15
16
  field = content_field form, args
16
17
 
17
- if [ args[:optional_type_formgroup], args[:optional_name_formgroup] ].member? :show
18
+ if [args[:optional_type_formgroup], args[:optional_name_formgroup]]
19
+ .member? :show
18
20
  # display content field in formgroup for consistency with other fields
19
21
  formgroup '', field, editor: :content
20
22
  else
@@ -23,16 +25,13 @@ format :html do
23
25
  end
24
26
  end
25
27
 
26
-
27
28
  def form_for_multi
28
- card.name = card.name.gsub(/^#{Regexp.escape(root.card.name)}\+/, '+') if root.card.new_card? ##FIXME -- need to match other relative inclusions.
29
+ instantiate_builder("card#{subcard_input_names}", card, {})
30
+ end
29
31
 
30
- # doesn't work anymore in Rails 4
31
- # TODO -- check whether forms work with the new instantiate_builder call
32
- # block = Proc.new {}
33
- # builder = ActionView::Base.default_form_builder
34
- # builder.new("card[subcards][#{card.relative_name}]", card, template, {}, block)
35
- builder = instantiate_builder("card[subcards][#{card.relative_name}]", card, {})
32
+ def subcard_input_names
33
+ return '' if !form_root_format || form_root_format == self
34
+ "#{@parent.subcard_input_names}[subcards][#{card.contextual_name}]"
36
35
  end
37
36
 
38
37
  def form
@@ -40,6 +39,7 @@ format :html do
40
39
  end
41
40
 
42
41
  def card_form action, opts={}
42
+ @form_root = true
43
43
  hidden_args = opts.delete :hidden
44
44
  form_for card, card_form_opts(action, opts) do |form|
45
45
  @form = form
@@ -50,6 +50,16 @@ format :html do
50
50
  end
51
51
  end
52
52
 
53
+ def form_root_format
54
+ if @form_root
55
+ self
56
+ elsif !@parent
57
+ nil
58
+ else
59
+ @parent.form_root_format
60
+ end
61
+ end
62
+
53
63
  def card_form_opts action, html={}
54
64
  url, action = case action
55
65
  when Symbol ; [ path(action: action) , action ]
@@ -108,7 +108,7 @@ format :html do
108
108
  def show_menu_items
109
109
  disc_tagname = Card.fetch(:discussion, skip_modules: true).cardname
110
110
  disc_card = unless card.new_card? or card.junction? && card.cardname.tag_name.key == disc_tagname.key
111
- Card.fetch "#{card.name}+#{disc_tagname}", skip_virtual: true, skip_modules: true, new: {}
111
+ Card.fetch "#{card.name}+#{disc_tagname}", skip_modules: true, new: {}
112
112
  end
113
113
 
114
114
  res = {
@@ -65,7 +65,7 @@ event :validate_accountability, on: :create, before: :approve do
65
65
  end
66
66
 
67
67
  event :require_email, on: :create, after: :approve do
68
- unless subcards["+#{Card[:email].name}"]
68
+ unless subfield(:email)
69
69
  errors.add :email, 'required'
70
70
  end
71
71
  end
@@ -74,20 +74,20 @@ end
74
74
  event :set_default_salt, on: :create, before: :process_subcards do
75
75
  salt = Digest::SHA1.hexdigest "--#{Time.now.to_s}--"
76
76
  Env[:salt] = salt # HACK!!! need viable mechanism to get this to password
77
- subcards["+#{Card[:salt].name}"] ||= { content: salt }
77
+ add_subfield :salt, content: salt
78
78
  end
79
79
 
80
80
  event :set_default_status, on: :create, before: :process_subcards do
81
81
  default_status = ( Auth.needs_setup? ? 'active' : 'pending' )
82
- subcards["+#{Card[:status].name}"] = { content: default_status }
82
+ add_subfield :status, content: default_status
83
83
  end
84
84
 
85
85
  def confirm_ok?
86
86
  Card.new( type_id: Card.default_accounted_type_id ).ok? :create
87
87
  end
88
88
 
89
- event :generate_confirmation_token, on: :create, before: :process_subcards, when: proc{ |c| c.confirm_ok? } do
90
- subcards["+#{Card[:token].name}"] = {content: generate_token }
89
+ event :generate_confirmation_token, :on=>:create, :before=>:process_subcards, :when=>proc{ |c| c.confirm_ok? } do
90
+ add_subfield :token, content: generate_token
91
91
  end
92
92
 
93
93
  event :reset_password, on: :update, before: :approve, when: proc{ |c| c.has_reset_token? } do
@@ -84,7 +84,6 @@ format :html do
84
84
  elsif script_card
85
85
  javascript_include_tag script_card.machine_output_url
86
86
  end
87
-
88
87
  ie9_card = Card[:script_html5shiv_printshiv]
89
88
  %(#{ javascript_tag do varvals * ';' end }
90
89
  #{ @js_tag if @js_tag }
@@ -1,6 +1,5 @@
1
1
 
2
2
  format :html do
3
-
4
3
  view :open do |args|
5
4
  args.merge! optional_help: :show
6
5
  super args
@@ -12,84 +11,93 @@ format :html do
12
11
 
13
12
  view :open_content do |args|
14
13
  # annoying step designed to avoid table of contents. sigh
15
- _render_core( args )
14
+ _render_core(args)
16
15
  end
17
16
 
18
- view :closed_content do |args|
17
+ view :closed_content do |_args|
19
18
  ''
20
19
  end
21
20
 
22
21
  def default_core_args args={}
23
22
  args[:buttons] = button_tag 'Sign in', situation: 'primary'
24
23
  if Card.new(type_id: Card::SignupID).ok? :create
25
- args[:buttons] += link_to( '...or sign up!', card_path("account/signup"))
24
+ args[:buttons] += link_to('...or sign up!', card_path('account/signup'))
26
25
  end
27
- args[:buttons] += raw("<div style='float:right'>#{ view_link 'RESET PASSWORD', :edit, path_opts: {slot: {hide: :toolbar}} }</div>") #FIXME - hardcoded styling
26
+ args[:buttons] += raw(
27
+ "<div style='float:right'>" \
28
+ "#{ view_link 'RESET PASSWORD', :edit,
29
+ path_opts: { slot: { hide: :toolbar } } }" \
30
+ '</div>') # FIXME: hardcoded styling
28
31
  args
29
32
  end
30
33
 
31
34
  view :core do |args|
32
- account = card.fetch trait: :account, new: {}
33
35
  form_args = {
34
36
  hidden: { success: "REDIRECT: #{Env.interrupted_action || '*previous'}" },
35
37
  recaptcha: :off
36
38
  }
37
-
38
39
  with_inclusion_mode :edit do
39
40
  card_form :update, form_args do
40
41
  [
41
- Auth.as_bot do
42
- subformat(account)._render :content_formgroup, structure: true, items: {autocomplete: 'on'}
43
- end,
44
- _optional_render( :button_formgroup, args )
42
+ _optional_render(:content_formgroup, args.merge(structure: true)),
43
+ _optional_render(:button_formgroup, args)
45
44
  ].join
46
45
  end
47
46
  end
48
47
  end
49
48
 
50
- #FORGOT PASSWORD
49
+ # FORGOT PASSWORD
51
50
  view :edit do |args|
52
- args.merge!( {
51
+ @forgot_password = true
52
+ args.merge!(
53
53
  title: 'Forgot Password',
54
54
  optional_help: :hide,
55
- buttons: button_tag( 'Reset my password', situation: 'primary' ),
55
+ buttons: button_tag('Reset my password', situation: 'primary'),
56
56
  structure: true,
57
57
  hidden: {
58
58
  reset_password: true,
59
59
  success: { view: :reset_password_success }
60
60
  }
61
- } )
61
+ )
62
62
 
63
63
  Auth.as_bot { super args }
64
64
  end
65
65
 
66
- view :raw do |args|
67
- '{{+*email|title:email;type:Phrase}}'
66
+ view :raw do |_args|
67
+ if @forgot_password
68
+ "{{+#{Card[:email].name}|title:email;type:Phrase}}"
69
+ else
70
+ %(
71
+ {{+#{Card[:email].name}|titled;title:email}}
72
+ {{+#{Card[:password].name}|titled;title:password}}
73
+ )
74
+ end
68
75
  end
69
76
 
70
- view :reset_password_success do |args|
77
+ view :reset_password_success do |_args|
71
78
  frame { 'Check your email for a link to reset your password' }
72
79
  end
73
-
74
80
  end
75
81
 
76
82
  event :signin, before: :approve, on: :update do
77
- email = subcards["+#{Card[:email ].name}"]
78
- email &&= email['content']
79
- pword = subcards["+#{Card[:password].name}"]
80
- pword &&= pword['content']
83
+ email = subfield :email
84
+ email &&= email.content
85
+ pword = subfield :password
86
+ pword &&= pword.content
81
87
 
82
88
  abort :failure, 'bad signin args' unless email && pword
83
89
 
84
- if signin_id = Auth.authenticate( email, pword )
90
+ if (signin_id = Auth.authenticate(email, pword))
85
91
  Auth.signin signin_id
86
92
  else
87
- accted = Auth[ email.strip.downcase ]
88
- errors.add :signin, case
89
- when accted.nil? ; "Unrecognized email."
90
- when !accted.account.active? ; "Sorry, that account is not active."
91
- else ; "Wrong password"
93
+ accted = Auth[email.strip.downcase]
94
+ error_msg =
95
+ case
96
+ when accted.nil? then 'Unrecognized email.'
97
+ when !accted.account.active? then 'Sorry, that account is not active.'
98
+ else 'Wrong password'
92
99
  end
100
+ errors.add :signin, error_msg
93
101
  abort :failure
94
102
  end
95
103
  end
@@ -98,11 +106,13 @@ event :signin_success, after: :signin do
98
106
  abort :success
99
107
  end
100
108
 
101
- event :send_reset_password_token, before: :signin, on: :update, when: proc{ |c| Env.params[:reset_password] } do
102
- email = subcards["+#{Card[:email].name}"]
103
- email &&= email['content']
109
+ event :send_reset_password_token,
110
+ before: :signin, on: :update,
111
+ when: proc { Env.params[:reset_password] } do
112
+ email = subfield :email
113
+ email &&= email.content
104
114
 
105
- if accted = Auth[ email.strip.downcase ] and accted.account.active?
115
+ if (accted = Auth[email.strip.downcase]) && accted.account.active?
106
116
  accted.account.send_reset_password_token
107
117
  abort :success
108
118
  else
@@ -119,5 +129,3 @@ event :signout, before: :approve, on: :delete do
119
129
  Auth.signin nil
120
130
  abort :success
121
131
  end
122
-
123
-
@@ -87,9 +87,11 @@ format :html do
87
87
  end
88
88
 
89
89
  view :preview_editor, tags: :unknown_ok do |args|
90
+ cached_upload_card_name = Card::Env.params[:attachment_upload]
91
+ cached_upload_card_name.gsub!(/\[\w+\]$/, "[cached_upload]")
90
92
  <<-HTML
91
93
  <div class="chosen-file">
92
- <input type="hidden" name="cached_upload" value="#{card.selected_action_id}">
94
+ <input type="hidden" name="#{cached_upload_card_name}" value="#{card.selected_action_id}">
93
95
  <table role="presentation" class="table table-striped"><tbody class="files">
94
96
  <tr class="template-download fade in">
95
97
  <td>
@@ -126,7 +128,12 @@ format :html do
126
128
  <span>
127
129
  #{card.new_card? ? 'Add' : 'Replace'} #{card.attachment_name}...
128
130
  </span>
129
- #{file_field card.attachment_name, class: 'file-upload slotter'}
131
+ <input class="file-upload slotter form-control" type="file"
132
+ name="card[#{card.type_code}]" id="card_#{card.type_code}">
133
+ #{hidden_field_tag 'attachment_type_id', card.type_id}
134
+ #{hidden_field card.attachment_name, class: "attachment_card_name",
135
+ value: ''}
136
+ #{hidden_field_tag 'file_card_name', card.cardname.url_key}
130
137
  </span>
131
138
  </div>
132
139
  <div id="progress" class="progress" style="display: none;">
@@ -0,0 +1,134 @@
1
+ event :validate_list_name, before: :validate, on: :save, changed: :name do
2
+ if !junction? || !right || right.type_id != CardtypeID
3
+ errors.add :name, 'must have a cardtype name as right part'
4
+ end
5
+ end
6
+
7
+ event :validate_list_item_type_change,
8
+ before: :validate, on: :save, changed: :name do
9
+ item_cards.each do |item_card|
10
+ if item_card.type_cardname.key != item_type_name.key
11
+ errors.add :name,
12
+ "name conflicts with list items' type; " \
13
+ 'delete content first'
14
+ end
15
+ end
16
+ end
17
+
18
+ event :validate_list_content, before: :validate, on: :save, changed: :content do
19
+ item_cards.each do |item_card|
20
+ if item_card.type_cardname.key != item_type_name.key
21
+ errors.add :content,
22
+ "#{item_card.name} has wrong cardtype; " \
23
+ "only cards of type #{cardname.right} are allowed"
24
+ end
25
+ end
26
+ end
27
+
28
+ event :create_listed_by_cards,
29
+ before: :approve, on: :save, changed: :content do
30
+ item_names.each do |item_name|
31
+ listed_by_name = "#{item_name}+#{left.type_name}"
32
+ if !Card[listed_by_name]
33
+ add_subcard listed_by_name, type_id: ListedByID
34
+ else
35
+ Card[listed_by_name].update_references
36
+ end
37
+ end
38
+ end
39
+
40
+ event :update_related_listed_by_card_on_create,
41
+ after: :store, on: :create do
42
+ update_listed_by_cache_for item_keys
43
+ end
44
+
45
+ event :update_related_listed_by_card_on_content_update,
46
+ after: :store, on: :update, changed: :content do
47
+ new_items = item_keys
48
+ changed_items =
49
+ if db_content_was
50
+ old_items = item_keys(content: db_content_was)
51
+ old_items + new_items - (old_items & new_items)
52
+ else
53
+ new_items
54
+ end
55
+ update_listed_by_cache_for changed_items
56
+ end
57
+
58
+ event :update_related_listed_by_card_on_name_update,
59
+ after: :store, on: :update, changed: :name do
60
+ update_all_items
61
+ end
62
+
63
+ event :update_related_listed_by_card_on_type_update,
64
+ after: :store, on: :update, changed: :type_id do
65
+ update_all_items
66
+ end
67
+
68
+ event :update_related_listed_by_card_on_delete,
69
+ after: :store, on: :delete, when: proc { |c| c.junction? } do
70
+ update_listed_by_cache_for item_keys, type_key: @left_type_key
71
+ end
72
+
73
+ event :cache_type_key,
74
+ before: :store, on: :delete, when: proc { |c| c.junction? } do
75
+ @left_type_key = left.type_card.key
76
+ end
77
+
78
+ def update_all_items
79
+ current_items = item_keys
80
+ if db_content_was
81
+ old_items = item_keys(content: db_content_was)
82
+ update_listed_by_cache_for old_items
83
+ end
84
+ update_listed_by_cache_for current_items
85
+ end
86
+
87
+ def update_listed_by_cache_for item_keys, args={}
88
+ type_key = args[:type_key] || left.type_card.key
89
+
90
+ item_keys.each do |item_key|
91
+ key = "#{item_key}+#{type_key}"
92
+ if Card::Cache[Card::Set::Type::ListedBy].exist? key
93
+ if (card = Card.fetch(key))
94
+ card.update_cached_list
95
+ card.update_references
96
+ else
97
+ Card::Cache[Card::Set::Type::ListedBy].delete key
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ include Pointer
104
+ format do
105
+ include Pointer::Format
106
+ end
107
+ format :html do
108
+ include Pointer::HtmlFormat
109
+ end
110
+ format :css do
111
+ include Pointer::CssFormat
112
+ end
113
+ format :js do
114
+ include Pointer::JsFormat
115
+ end
116
+ format :data do
117
+ include Pointer::DataFormat
118
+ end
119
+
120
+ def item_type
121
+ cardname.right
122
+ end
123
+
124
+ def item_type_name
125
+ cardname.right_name
126
+ end
127
+
128
+ def item_type_card
129
+ cardname.right
130
+ end
131
+
132
+ def item_type_id
133
+ right.id
134
+ end