card 1.93.13 → 1.94.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/config/initializers/01_core_extensions/persistent_identifiers.rb +8 -0
  4. data/config/locales/de.yml +1 -0
  5. data/config/locales/en.yml +1 -0
  6. data/lib/card.rb +2 -1
  7. data/lib/card/cache.rb +1 -1
  8. data/lib/card/content/clean.rb +6 -4
  9. data/lib/card/format/error.rb +1 -1
  10. data/lib/card/format/render.rb +1 -1
  11. data/lib/card/mod.rb +6 -1
  12. data/lib/card/mod/loader.rb +2 -0
  13. data/lib/card/name.rb +25 -5
  14. data/lib/card/query/attributes.rb +13 -8
  15. data/lib/card/set/event.rb +1 -1
  16. data/lib/card/tasks/card.rake +12 -0
  17. data/lib/card/tasks/card/migrate.rake +2 -0
  18. data/lib/card/view/options.rb +2 -1
  19. data/lib/cardio.rb +6 -6
  20. data/lib/cardio/utils.rb +30 -0
  21. data/mod/account/set/self/signin.rb +3 -13
  22. data/mod/account/set/type/signup.rb +1 -1
  23. data/mod/account/spec/set/self/signin_spec.rb +1 -1
  24. data/mod/basic_formats/set/all/base.rb +11 -3
  25. data/mod/basic_formats/set/all/json.rb +1 -0
  26. data/mod/basic_formats/set/self/head.rb +2 -3
  27. data/mod/bootstrap/db/migrate_core_cards/20170719163733_update_bootswatch_themes_to_4_beta.rb +2 -95
  28. data/mod/bootstrap/db/migrate_core_cards/lib/skin.rb +94 -0
  29. data/mod/bootstrap/script/update_skin_thumbnails.rb +9 -0
  30. data/mod/carrierwave/set/type/image.rb +1 -1
  31. data/mod/core/chunk/query_reference.rb +9 -2
  32. data/mod/core/set/all/event_conditions.rb +18 -8
  33. data/mod/core/set/all/fetch_helper.rb +11 -11
  34. data/mod/core/set/all/utils.rb +0 -12
  35. data/mod/core/spec/format/html_format_spec.rb +3 -3
  36. data/mod/core/spec/set/all/event_conditions_spec.rb +15 -0
  37. data/mod/core/spec/set/all/name_spec.rb +7 -0
  38. data/mod/core/spec/set/all/name_validations_spec.rb +0 -10
  39. data/mod/core/spec/set/all/rename_spec.rb +2 -2
  40. data/mod/pointer/set/abstract/02_pointer.rb +4 -0
  41. data/mod/search/set/abstract/00_filter_helper.rb +1 -1
  42. data/mod/search/set/abstract/02_search_params.rb +18 -0
  43. data/mod/search/set/abstract/search.rb +1 -1
  44. data/mod/search/set/self/navbox.rb +2 -2
  45. data/mod/search/set/self/search.rb +1 -19
  46. data/mod/search/spec/set/self/search_spec.rb +1 -1
  47. data/mod/standard/set/all/error.rb +6 -6
  48. data/mod/standard/set/all/rich_html/content.rb +2 -27
  49. data/mod/standard/set/all/rich_html/editing.rb +1 -1
  50. data/mod/standard/set/all/rich_html/title.rb +39 -0
  51. data/mod/standard/set/all/rich_html/toolbar.rb +1 -1
  52. data/mod/standard/set/type/cardtype.rb +8 -2
  53. data/mod/standard/spec/{chunk → content/chunk}/include_spec.rb +5 -5
  54. data/mod/standard/spec/{chunk → content/chunk}/link_spec.rb +1 -1
  55. data/mod/standard/spec/{chunk → content/chunk}/query_reference_spec.rb +0 -0
  56. metadata +11 -7
@@ -77,6 +77,7 @@ format :json do
77
77
  {
78
78
  id: card.id,
79
79
  name: card.name,
80
+ key: card.key,
80
81
  url: path
81
82
  }
82
83
  end
@@ -23,10 +23,9 @@ format :html do
23
23
  end
24
24
 
25
25
  def head_title
26
- title = root.card && root.card.name
27
- title = nil if title.blank?
26
+ title = root.safe_name
28
27
  title = params[:action] if title == "*placeholder"
29
- %(<title>#{title ? "#{title} - " : ''}#{Card.global_setting :title}</title>)
28
+ %(<title>#{title.present? ? "#{title} - " : ''}#{Card.global_setting :title}</title>)
30
29
  end
31
30
 
32
31
  def head_buttons
@@ -1,5 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
+ require_relative "lib/skin"
4
+
3
5
  class UpdateBootswatchThemesTo4Beta < Card::Migration::Core
4
6
  def up
5
7
  remove_old_stuff
@@ -49,101 +51,6 @@ class UpdateBootswatchThemesTo4Beta < Card::Migration::Core
49
51
  Card["themeless bootstrap skin"].add_item! name.tr("_", " ")
50
52
  end
51
53
  end
52
-
53
- class Skin
54
- include ::Card::Model::SaveHelper
55
-
56
- class << self
57
- def vendor_path
58
- File.expand_path "../../../vendor", __FILE__
59
- end
60
-
61
- def bootstrap_scss_path filename
62
- File.join vendor_path, "bootstrap", "scss", "_#{filename}.scss"
63
- end
64
-
65
- def themes
66
- json = File.read File.join(vendor_path, "bootswatch", "docs", "api", "4.json")
67
- JSON.parse(json)["themes"].map { |theme| theme["name"] }
68
- end
69
- end
70
-
71
- def initialize theme_name
72
- @theme_name = theme_name.downcase
73
- @skin_name = "#{theme_name} skin"
74
- @skin_codename = @skin_name.downcase.tr(" ", "_")
75
- end
76
-
77
- def create_or_update
78
- Card.exists?(@skin_name) ? update_skin : create_skin
79
- end
80
-
81
- def create_skin
82
- Card.create! name: @skin_name,
83
- codename: @skin_codename,
84
- type_id: Card::SkinID,
85
- content: "[[themeless bootstrap skin]]\n[[+bootswatch theme]]",
86
- subcards: create_subcard_args
87
- end
88
-
89
- def update_skin
90
- update_css file_name: "bootstrap.css", field_name: "bootswatch theme"
91
- update_tumbnail
92
- end
93
-
94
- def update_scss file_name:, field_name: file_name
95
- update_card "#{@skin_name}+#{field_name}", style_args(file_name)
96
- end
97
-
98
- def update_css file_name:, field_name: file_name
99
- ensure_card "#{@skin_name}+#{field_name}", style_args(file_name, Card::CssID)
100
- end
101
-
102
- def update_tumbnail
103
- update_card "#{@skin_name}+Image", thumbnail_args
104
- end
105
-
106
- private
107
-
108
- def create_subcard_args
109
- {
110
- "+bootswatch theme" => style_args("bootstrap.css", Card::CssID),
111
- "+Image" => thumbnail_args
112
- }
113
- end
114
-
115
- def style_args file_name, type_id=Card::ScssID
116
- paths = Array.wrap(file_name).map { |fn| resource_path(fn) }
117
- content = paths.map { |p| File.read(p) }.join "\n"
118
- { type_id: type_id, content: content }
119
- end
120
-
121
- def thumbnail_args
122
- {
123
- codename: "#{@skin_codename}_image",
124
- type_id: Card::ImageID,
125
- mod: :bootstrap, storage_type: :coded,
126
- image: File.open(thumbnail_path)
127
- }
128
- end
129
-
130
- def resource_path resource
131
- resource = "_#{resource}.scss" unless resource.include? "."
132
- if resource.include? File::SEPARATOR
133
- resource
134
- else
135
- File.join base_resource_dir, resource
136
- end
137
- end
138
-
139
- def thumbnail_path
140
- File.join Skin.vendor_path, "bootswatch", "docs", @theme_name, "thumbnail.png"
141
- end
142
-
143
- def base_resource_dir
144
- File.join Skin.vendor_path, "bootswatch", "dist", @theme_name
145
- end
146
- end
147
54
  end
148
55
 
149
56
 
@@ -0,0 +1,94 @@
1
+ # Update or create a bootstrap skin
2
+ class Skin
3
+ include ::Card::Model::SaveHelper
4
+
5
+ class << self
6
+ def vendor_path
7
+ File.expand_path "../../../../vendor", __FILE__
8
+ end
9
+
10
+ def bootstrap_scss_path filename
11
+ File.join vendor_path, "bootstrap", "scss", "_#{filename}.scss"
12
+ end
13
+
14
+ def themes
15
+ json = File.read File.join(vendor_path, "bootswatch", "docs", "api", "4.json")
16
+ JSON.parse(json)["themes"].map { |theme| theme["name"] }
17
+ end
18
+ end
19
+
20
+ def initialize theme_name
21
+ @theme_name = theme_name.downcase
22
+ @skin_name = "#{theme_name} skin"
23
+ @skin_codename = @skin_name.downcase.tr(" ", "_")
24
+ end
25
+
26
+ def create_or_update
27
+ Card.exists?(@skin_name) ? update_skin : create_skin
28
+ end
29
+
30
+ def create_skin
31
+ Card.create! name: @skin_name,
32
+ codename: @skin_codename,
33
+ type_id: Card::SkinID,
34
+ content: "[[themeless bootstrap skin]]\n[[+bootswatch theme]]",
35
+ subcards: create_subcard_args
36
+ end
37
+
38
+ def update_skin
39
+ update_css file_name: "bootstrap.css", field_name: "bootswatch theme"
40
+ # don't update thumbnails since they are stored as coded files in the gem
41
+ # there's a script for doing that
42
+ # update_tumbnail
43
+ end
44
+
45
+ def update_scss file_name:, field_name: file_name
46
+ update_card "#{@skin_name}+#{field_name}", style_args(file_name)
47
+ end
48
+
49
+ def update_css file_name:, field_name: file_name
50
+ ensure_card "#{@skin_name}+#{field_name}", style_args(file_name, Card::CssID)
51
+ end
52
+
53
+ def update_thumbnail
54
+ update_card "#{@skin_name}+Image", thumbnail_args
55
+ end
56
+
57
+ private
58
+
59
+ def create_subcard_args
60
+ { "+bootswatch theme" => style_args("bootstrap.css", Card::CssID) }
61
+ end
62
+
63
+ def style_args file_name, type_id=Card::ScssID
64
+ paths = Array.wrap(file_name).map { |fn| resource_path(fn) }
65
+ content = paths.map { |p| File.read(p) }.join "\n"
66
+ { type_id: type_id, content: content }
67
+ end
68
+
69
+ def thumbnail_args
70
+ {
71
+ codename: "#{@skin_codename}_image",
72
+ type_id: Card::ImageID,
73
+ mod: :bootstrap, storage_type: :coded,
74
+ image: File.open(thumbnail_path)
75
+ }
76
+ end
77
+
78
+ def resource_path resource
79
+ resource = "_#{resource}.scss" unless resource.include? "."
80
+ if resource.include? File::SEPARATOR
81
+ resource
82
+ else
83
+ File.join base_resource_dir, resource
84
+ end
85
+ end
86
+
87
+ def thumbnail_path
88
+ File.join Skin.vendor_path, "bootswatch", "docs", @theme_name, "thumbnail.png"
89
+ end
90
+
91
+ def base_resource_dir
92
+ File.join Skin.vendor_path, "bootswatch", "dist", @theme_name
93
+ end
94
+ end
@@ -0,0 +1,9 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require_relative "../db/migrate_core_cards/lib/skin"
4
+
5
+ puts "Updating bootstrap themes ..."
6
+ Skin.themes.each do |theme_name|
7
+ puts theme_name
8
+ Skin.new(theme_name).update_thumbnail
9
+ end
@@ -63,7 +63,7 @@ format :html do
63
63
  if source.blank? || source == "missing"
64
64
  # FIXME - these images should be "broken", not "missing"
65
65
  # ("missing" is the view for "unknown" now, so we shouldn't further confuse things)
66
- "<!-- image missing #{@card.name} -->"
66
+ "<!-- image missing #{safe_name} -->"
67
67
  else
68
68
  image_tag source
69
69
  end
@@ -23,7 +23,7 @@ module Card::Content::Chunk
23
23
  Card::Query::ATTRIBUTES.keys +
24
24
  Card::Query::CONJUNCTIONS.keys +
25
25
  %w[desc asc count]
26
- ).map(&:to_name)
26
+ ).map(&:to_s)
27
27
  )
28
28
 
29
29
  Card::Content::Chunk.register_class(
@@ -53,10 +53,17 @@ module Card::Content::Chunk
53
53
 
54
54
  class << self
55
55
  def full_match content, prefix
56
+ # matches cardnames that are not keywords
57
+ # FIXME: would not match cardnames that are keywords
56
58
  match, offset = super(content, prefix)
57
- return unless match && !QUERY_KEYWORDS.include?(match[1].to_name)
59
+ return if !match || keyword?(match[1])
58
60
  [match, offset]
59
61
  end
62
+
63
+ def keyword? str
64
+ return unless str
65
+ QUERY_KEYWORDS.include?(str.tr(" ", "_").downcase)
66
+ end
60
67
  end
61
68
 
62
69
  def interpret match, _content
@@ -1,30 +1,30 @@
1
- EVENT_CONDITIONS = %i[set on changed when].freeze
1
+ EVENT_CONDITIONS = %i[set on changed when optional].freeze
2
2
 
3
- def event_applies? opts
3
+ def event_applies? event, opts
4
4
  EVENT_CONDITIONS.all? do |key|
5
- send "#{key}_condition_applies?", opts[key]
5
+ send "#{key}_condition_applies?", event, opts[key]
6
6
  end
7
7
  end
8
8
 
9
9
  private
10
10
 
11
- def set_condition_applies? set_module
11
+ def set_condition_applies? _event, set_module
12
12
  singleton_class.include?(set_module)
13
13
  end
14
14
 
15
- def on_condition_applies? actions
15
+ def on_condition_applies? _event, actions
16
16
  actions = Array(actions).compact
17
17
  return true if actions.empty?
18
18
  actions.include? @action
19
19
  end
20
20
 
21
- def changed_condition_applies? db_columns
21
+ def changed_condition_applies? _event, db_columns
22
22
  db_columns = Array(db_columns).compact
23
23
  return true if db_columns.empty?
24
24
  db_columns.any? { |col| single_changed_condition_applies? col }
25
25
  end
26
26
 
27
- def when_condition_applies? block
27
+ def when_condition_applies? _event, block
28
28
  case block
29
29
  when Proc then block.call(self)
30
30
  when Symbol then send block
@@ -32,6 +32,11 @@ def when_condition_applies? block
32
32
  end
33
33
  end
34
34
 
35
+ def optional_condition_applies? event, optional
36
+ return true unless optional
37
+ skip_event? event
38
+ end
39
+
35
40
  def single_changed_condition_applies? db_column
36
41
  return true unless db_column
37
42
  db_column =
@@ -53,6 +58,11 @@ def wrong_stage opts
53
58
  end
54
59
 
55
60
  def wrong_action action
56
- return false if on_condition_applies? action
61
+ return false if on_condition_applies?(nil, action)
57
62
  "on: #{action} method #{method} called on #{@action}"
58
63
  end
64
+
65
+ def skip_event? event
66
+ @names_of_skipped_events ||= ::Set.new(Array.wrap(skip_event).map(&:to_sym))
67
+ !@names_of_skipped_events.include? event
68
+ end
@@ -13,6 +13,16 @@ module ClassMethods
13
13
  end
14
14
  end
15
15
 
16
+ def safe_param param
17
+ if param.respond_to? :to_unsafe_h
18
+ # clone doesn't work for Parameters
19
+ param.to_unsafe_h
20
+ else
21
+ # clone so that original params remain unaltered. need deeper clone?
22
+ (param || {}).clone
23
+ end
24
+ end
25
+
16
26
  private
17
27
 
18
28
  def standard_controller_fetch args, card_opts
@@ -23,23 +33,13 @@ module ClassMethods
23
33
  end
24
34
 
25
35
  def controller_fetch_opts args
26
- opts = safe_card_opts args[:card]
36
+ opts = safe_param args[:card]
27
37
  opts[:type] ||= args[:type] if args[:type]
28
38
  # for /new/:type shortcut. we should handle in routing and deprecate this
29
39
  opts[:name] ||= Card::Name.url_key_to_standard(args[:id])
30
40
  opts
31
41
  end
32
42
 
33
- def safe_card_opts card_opts
34
- if card_opts.respond_to? :to_unsafe_h
35
- # clone doesn't work for Parameters
36
- card_opts.to_unsafe_h
37
- else
38
- # clone so that original params remain unaltered. need deeper clone?
39
- (card_opts || {}).clone
40
- end
41
- end
42
-
43
43
  def validate_fetch_opts! opts
44
44
  return unless opts[:new] && opts[:skip_virtual]
45
45
  raise Card::Error, "fetch called with new args and skip_virtual"
@@ -1,12 +1,4 @@
1
1
  module ClassMethods
2
- def delete_tmp_files id=nil
3
- dir = Cardio.paths["files"].existent.first + "/tmp"
4
- dir += "/#{id}" if id
5
- FileUtils.rm_rf dir, secure: true
6
- rescue
7
- Rails.logger.info "failed to remove tmp files"
8
- end
9
-
10
2
  def merge_list attribs, opts={}
11
3
  unmerged = []
12
4
  attribs.each do |row|
@@ -49,8 +41,4 @@ module ClassMethods
49
41
  card.update_attributes! attribs
50
42
  end
51
43
  end
52
-
53
- def seed_test_db
54
- system "env RAILS_ENV=test bundle exec rake db:fixtures:load"
55
- end
56
44
  end
@@ -32,10 +32,10 @@ RSpec.describe Card::Format::HtmlFormat do
32
32
 
33
33
  it "renders top menu" do
34
34
  is_expected.to have_tag "header" do
35
- with_tag 'a.nav-link.internal-link[href="/"]', text: "Home"
36
- with_tag 'a.nav-link.internal-link[href="/:recent"]', text: "Recent"
35
+ with_tag 'a.nav-link.internal-link[href=""]', text: "Home"
36
+ with_tag 'a.nav-link.internal-link[href=":recent"]', text: "Recent"
37
37
  with_tag 'form.navbox-form[action="/*search"]' do
38
- with_tag 'input[name="_keyword"]'
38
+ with_tag 'input[name="query[keyword]"]'
39
39
  end
40
40
  end
41
41
  end
@@ -50,5 +50,20 @@ describe Card::Set::All::EventConditions, "event" do
50
50
  expect(@log).to contain_exactly(*content_before_change)
51
51
  end
52
52
  end
53
+
54
+ specify "skip event condition" do
55
+ with_test_events do
56
+ test_event :validate, on: :update, optional: true, for: "A" do
57
+ add_to_log "not skipped"
58
+ end
59
+ Card["A"].update_attributes! content: "changed content", skip_event: :test_event_0
60
+
61
+ aggregate_failures do
62
+ expect(@log).to be_empty
63
+ Card["A"].update_attributes! content: "changed content"
64
+ expect(@log).to contain_exactly "not skipped"
65
+ end
66
+ end
67
+ end
53
68
  end
54
69
  end
@@ -57,6 +57,13 @@ describe Card::Set::All::Name do
57
57
  end
58
58
  end
59
59
 
60
+ describe "event: escape_name" do
61
+ it "escapes invalid characters" do
62
+ c = Card.create! name: "8 / 5 <script>"
63
+ expect(c.name).to eq("8 &#47; 5 &#60;script&#62;")
64
+ end
65
+ end
66
+
60
67
  describe "codename" do
61
68
  before do
62
69
  @card = Card["a"]