card 1.93.13 → 1.94.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/config/initializers/01_core_extensions/persistent_identifiers.rb +8 -0
- data/config/locales/de.yml +1 -0
- data/config/locales/en.yml +1 -0
- data/lib/card.rb +2 -1
- data/lib/card/cache.rb +1 -1
- data/lib/card/content/clean.rb +6 -4
- data/lib/card/format/error.rb +1 -1
- data/lib/card/format/render.rb +1 -1
- data/lib/card/mod.rb +6 -1
- data/lib/card/mod/loader.rb +2 -0
- data/lib/card/name.rb +25 -5
- data/lib/card/query/attributes.rb +13 -8
- data/lib/card/set/event.rb +1 -1
- data/lib/card/tasks/card.rake +12 -0
- data/lib/card/tasks/card/migrate.rake +2 -0
- data/lib/card/view/options.rb +2 -1
- data/lib/cardio.rb +6 -6
- data/lib/cardio/utils.rb +30 -0
- data/mod/account/set/self/signin.rb +3 -13
- data/mod/account/set/type/signup.rb +1 -1
- data/mod/account/spec/set/self/signin_spec.rb +1 -1
- data/mod/basic_formats/set/all/base.rb +11 -3
- data/mod/basic_formats/set/all/json.rb +1 -0
- data/mod/basic_formats/set/self/head.rb +2 -3
- data/mod/bootstrap/db/migrate_core_cards/20170719163733_update_bootswatch_themes_to_4_beta.rb +2 -95
- data/mod/bootstrap/db/migrate_core_cards/lib/skin.rb +94 -0
- data/mod/bootstrap/script/update_skin_thumbnails.rb +9 -0
- data/mod/carrierwave/set/type/image.rb +1 -1
- data/mod/core/chunk/query_reference.rb +9 -2
- data/mod/core/set/all/event_conditions.rb +18 -8
- data/mod/core/set/all/fetch_helper.rb +11 -11
- data/mod/core/set/all/utils.rb +0 -12
- data/mod/core/spec/format/html_format_spec.rb +3 -3
- data/mod/core/spec/set/all/event_conditions_spec.rb +15 -0
- data/mod/core/spec/set/all/name_spec.rb +7 -0
- data/mod/core/spec/set/all/name_validations_spec.rb +0 -10
- data/mod/core/spec/set/all/rename_spec.rb +2 -2
- data/mod/pointer/set/abstract/02_pointer.rb +4 -0
- data/mod/search/set/abstract/00_filter_helper.rb +1 -1
- data/mod/search/set/abstract/02_search_params.rb +18 -0
- data/mod/search/set/abstract/search.rb +1 -1
- data/mod/search/set/self/navbox.rb +2 -2
- data/mod/search/set/self/search.rb +1 -19
- data/mod/search/spec/set/self/search_spec.rb +1 -1
- data/mod/standard/set/all/error.rb +6 -6
- data/mod/standard/set/all/rich_html/content.rb +2 -27
- data/mod/standard/set/all/rich_html/editing.rb +1 -1
- data/mod/standard/set/all/rich_html/title.rb +39 -0
- data/mod/standard/set/all/rich_html/toolbar.rb +1 -1
- data/mod/standard/set/type/cardtype.rb +8 -2
- data/mod/standard/spec/{chunk → content/chunk}/include_spec.rb +5 -5
- data/mod/standard/spec/{chunk → content/chunk}/link_spec.rb +1 -1
- data/mod/standard/spec/{chunk → content/chunk}/query_reference_spec.rb +0 -0
- metadata +11 -7
@@ -23,10 +23,9 @@ format :html do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def head_title
|
26
|
-
title = root.
|
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
|
data/mod/bootstrap/db/migrate_core_cards/20170719163733_update_bootswatch_themes_to_4_beta.rb
CHANGED
@@ -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
|
@@ -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 #{
|
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(&:
|
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
|
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 =
|
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"
|
data/mod/core/set/all/utils.rb
CHANGED
@@ -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="
|
36
|
-
with_tag 'a.nav-link.internal-link[href="
|
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="
|
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 / 5 <script>")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
60
67
|
describe "codename" do
|
61
68
|
before do
|
62
69
|
@card = Card["a"]
|