card 1.16.3 → 1.16.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/card.gemspec +1 -1
  4. data/db/migrate_core_cards/20150724123438_update_file_and_image_cards.rb +1 -1
  5. data/db/migrate_core_cards/20150824135418_update_file_history.rb +20 -0
  6. data/db/migrate_core_cards/20150903130006_attachment_upload_cards.rb +13 -0
  7. data/db/seed/new/card_actions.yml +16 -0
  8. data/db/seed/new/card_acts.yml +1 -1
  9. data/db/seed/new/card_changes.yml +56 -80
  10. data/db/seed/new/card_references.yml +282 -58
  11. data/db/seed/new/cards.yml +1348 -1312
  12. data/db/seed/test/fixtures/card_actions.yml +884 -868
  13. data/db/seed/test/fixtures/card_acts.yml +250 -250
  14. data/db/seed/test/fixtures/card_changes.yml +1935 -1959
  15. data/db/seed/test/fixtures/card_references.yml +1024 -800
  16. data/db/seed/test/fixtures/cards.yml +2402 -2366
  17. data/db/version_core_cards.txt +1 -1
  18. data/lib/card.rb +3 -1
  19. data/lib/card/cache.rb +5 -5
  20. data/lib/card/chunk.rb +2 -0
  21. data/lib/card/env.rb +1 -1
  22. data/lib/card/query/card_clause.rb +59 -58
  23. data/lib/card/set.rb +7 -0
  24. data/lib/card/success.rb +143 -0
  25. data/mod/01_core/chunk/query_reference.rb +16 -7
  26. data/mod/01_core/chunk/reference.rb +3 -3
  27. data/mod/01_core/set/all/collection.rb +1 -1
  28. data/mod/01_core/set/all/name.rb +2 -1
  29. data/mod/01_core/set/all/phases.rb +12 -2
  30. data/mod/01_core/set/all/type.rb +5 -5
  31. data/mod/01_history/set/all/actions.rb +6 -2
  32. data/mod/01_history/set/all/content_history.rb +17 -2
  33. data/mod/01_history/set/all/history.rb +1 -1
  34. data/mod/02_basic_types/set/all/file.rb +0 -31
  35. data/mod/03_machines/lib/javascript/jquery.fileupload.js +539 -182
  36. data/mod/03_machines/lib/javascript/wagn.js.coffee +3 -0
  37. data/mod/03_machines/lib/javascript/wagn_mod.js.coffee +20 -18
  38. data/mod/03_machines/lib/stylesheets/style_cards.scss +28 -1
  39. data/mod/05_email/set/all/notify.rb +1 -1
  40. data/mod/05_standard/file/favicon/image-icon.png +0 -0
  41. data/mod/05_standard/file/favicon/image-large.png +0 -0
  42. data/mod/05_standard/file/favicon/image-medium.png +0 -0
  43. data/mod/05_standard/file/favicon/image-original.png +0 -0
  44. data/mod/05_standard/file/favicon/image-small.png +0 -0
  45. data/mod/05_standard/lib/carrier_wave/cardmount.rb +25 -6
  46. data/mod/05_standard/lib/file_uploader.rb +27 -11
  47. data/mod/05_standard/lib/image_uploader.rb +7 -4
  48. data/mod/05_standard/set/abstract/attachment.rb +132 -14
  49. data/mod/05_standard/set/right/account.rb +2 -2
  50. data/mod/05_standard/set/self/signin.rb +0 -1
  51. data/mod/05_standard/set/type/file.rb +48 -19
  52. data/mod/05_standard/set/type/image.rb +9 -12
  53. data/mod/05_standard/spec/chunk/include_spec.rb +13 -12
  54. data/mod/05_standard/spec/chunk/query_reference_spec.rb +50 -0
  55. data/mod/05_standard/spec/set/right/account_spec.rb +24 -25
  56. data/mod/05_standard/spec/set/type/file_spec.rb +1 -1
  57. data/spec/lib/card/reference_spec.rb +14 -0
  58. data/spec/lib/card/success_spec.rb +142 -0
  59. data/tmpsets/set/mod001-01_core/all/collection.rb +1 -1
  60. data/tmpsets/set/mod001-01_core/all/name.rb +2 -1
  61. data/tmpsets/set/mod001-01_core/all/phases.rb +12 -2
  62. data/tmpsets/set/mod001-01_core/all/type.rb +5 -5
  63. data/tmpsets/set/mod002-01_history/all/actions.rb +6 -2
  64. data/tmpsets/set/mod002-01_history/all/content_history.rb +17 -2
  65. data/tmpsets/set/mod002-01_history/all/history.rb +1 -1
  66. data/tmpsets/set/mod003-02_basic_types/all/file.rb +0 -24
  67. data/tmpsets/set/mod003-02_basic_types/all/rss.rb +8 -5
  68. data/tmpsets/set/mod003-02_basic_types/type/pointer.rb +2 -2
  69. data/tmpsets/set/mod005-04_settings/right/structure.rb +7 -2
  70. data/tmpsets/set/mod006-05_email/all/notify.rb +1 -1
  71. data/tmpsets/set/mod007-05_standard/abstract/attachment.rb +132 -14
  72. data/tmpsets/set/mod007-05_standard/all/links.rb +8 -0
  73. data/tmpsets/set/mod007-05_standard/all/rich_html/header.rb +5 -7
  74. data/tmpsets/set/mod007-05_standard/right/account.rb +2 -2
  75. data/tmpsets/set/mod007-05_standard/self/signin.rb +0 -1
  76. data/tmpsets/set/mod007-05_standard/type/file.rb +49 -20
  77. data/tmpsets/set/mod007-05_standard/type/image.rb +9 -12
  78. data/tmpsets/set/mod007-05_standard/type/search_type.rb +40 -22
  79. data/tmpsets/set/mod008-06_bootstrap/self/bootswatch_shared.rb +1 -1
  80. metadata +10 -4
@@ -151,12 +151,15 @@ jQuery.fn.extend {
151
151
 
152
152
  setInterval (-> $('.card-form').setContentFieldsFromMap()), 20000
153
153
 
154
+
155
+
154
156
  $(window).ready ->
155
157
  $.ajaxSetup cache: false
156
158
 
157
159
  setTimeout (-> wagn.initializeEditors $('body')), 10
158
160
  # dislike the timeout, but without this forms with multiple TinyMCE editors were failing to load properly
159
161
 
162
+
160
163
  $('body').on 'ajax:success', '.slotter', (event, data, c, d) ->
161
164
  unless event.slotSuccessful
162
165
  slot_top_pos = $(this).slot().offset().top
@@ -22,7 +22,7 @@ $.extend wagn,
22
22
  '.ace-editor-textarea' : -> wagn.initAce $(this)
23
23
  '.tinymce-textarea' : -> wagn.initTinyMCE @[0].id
24
24
  '.pointer-list-editor' : -> @sortable({handle: '.handle', cancel: ''}); wagn.initPointerList @find('input')
25
- '.file-upload' : -> @fileupload( add: wagn.chooseFile )#, forceIframeTransport: true )
25
+ '.file-upload' : -> @fileupload( dataType: 'html', done: wagn.doneFile, add: wagn.chooseFile, progressall: wagn.progressallFile )#, forceIframeTransport: true )
26
26
  '.etherpad-textarea' : -> $(this).closest('form').find('.edit-submit-button').attr('class', 'etherpad-submit-button')
27
27
  }
28
28
 
@@ -105,23 +105,23 @@ $.extend wagn,
105
105
  # initfunc()
106
106
 
107
107
  chooseFile: (e, data) ->
108
- file = data.files[0]
109
- # $(this).fileupload '_normalizeFile', 0, file # so file objects have same fields in all browsers
110
- $(this).closest('form').data 'file-data', data # stores data on form for use at submission time
111
-
112
- if name_field = $(this).slot().find( '.name-editor input' )
113
- # populates card name if blank
114
- if name_field[0] and name_field.val() == ''
115
- name_field.val file.name.replace( /\..*$/, '' ).replace( /_/g, ' ')
116
-
108
+ data.form.find('button[type=submit]').attr('disabled',true)
117
109
  editor = $(this).closest '.card-editor'
110
+ $('#progress').show()
111
+ editor.append '<input type="hidden" class="extra_upload_param" value="true" name="attachment_upload">'
112
+ editor.append '<input type="hidden" class="extra_upload_param" value="preview_editor" name="view">'
113
+ data.submit()
118
114
  editor.find('.choose-file').hide()
119
- editor.find('.chosen-filename').text file.name
120
- editor.find('.chosen-file').show()
115
+ editor.find('.extra_upload_param').remove()
121
116
 
122
- contentFieldName = this.name.replace( /attach\]$/, 'content]' )
123
- editor.append '<input type="hidden" value="CHOSEN" class="upload-card-content" name="' + contentFieldName + '">'
124
- # we add and remove the contentField to insure that nothing is added / updated when nothing is chosen.
117
+ progressallFile: (e, data) ->
118
+ progress = parseInt(data.loaded / data.total * 100, 10)
119
+ $('#progress .progress-bar').css('width', progress + '%')
120
+
121
+ doneFile: (e, data) ->
122
+ editor = $(this).closest '.card-editor'
123
+ editor.find('.chosen-file').replaceWith data.result
124
+ data.form.find('button[type=submit]').attr('disabled',false)
125
125
 
126
126
  isTouchDevice: ->
127
127
  if 'ontouchstart' of window or window.DocumentTouch and document instanceof DocumentTouch
@@ -141,10 +141,12 @@ $(window).ready ->
141
141
 
142
142
  $('body').on 'click', '.cancel-upload', ->
143
143
  editor = $(this).closest '.card-editor'
144
- editor.find('.chosen-file').hide()
145
144
  editor.find('.choose-file').show()
146
- $(this).closest('form').data 'file-data', null
147
- contentField = editor.find( '.upload-card-content' ).remove()
145
+ editor.find('.chosen-file').empty()
146
+ editor.find('.progress').show()
147
+ editor.find('#progress .progress-bar').css('width', '0%')
148
+ editor.find('#progress').hide()
149
+
148
150
 
149
151
  #navbox mod
150
152
  $('.navbox').autocomplete {
@@ -206,6 +206,26 @@ Choosing (b) will mean your CSS will not be affected by automated updates.
206
206
  }
207
207
  }
208
208
 
209
+ /* -- file upload -- */
210
+ .fileupload-buttonbar .btn, .fileupload-buttonbar .toggle {
211
+ margin-bottom: 5px;
212
+ }
213
+ .fileinput-button {
214
+ position: relative;
215
+ overflow: hidden;
216
+ display: inline-block;
217
+ }
218
+ .fileinput-button input {
219
+ position: absolute;
220
+ top: 0;
221
+ right: 0;
222
+ margin: 0;
223
+ opacity: 0;
224
+ -ms-filter: 'alpha(opacity=0)';
225
+ font-size: 200px;
226
+ direction: ltr;
227
+ cursor: pointer;
228
+ }
209
229
 
210
230
 
211
231
  /* -- template-editor (editing template rule inclusions inline) -- */
@@ -606,9 +626,16 @@ $act-gray: #b7b7b7;
606
626
  .diff-red {
607
627
  text-decoration: line-through;
608
628
  color: $diff-red;
629
+ img {
630
+ border: 2px solid $diff-red;
631
+ }
609
632
  }
610
633
  .diff-green {
611
- color: $diff-green;
634
+ color: $diff-green;
635
+ img {
636
+ margin: 0px 4px 0px 4px;
637
+ border: 2px solid $diff-green;
638
+ }
612
639
  }
613
640
  .diff-invisible {
614
641
  color: $action-white;
@@ -63,7 +63,7 @@ def followable?
63
63
  end
64
64
 
65
65
  def notable_change?
66
- !supercard && current_act && Card::Auth.current_id != WagnBotID && followable?
66
+ !silent_change && !supercard && current_act && Card::Auth.current_id != WagnBotID && followable?
67
67
  end
68
68
 
69
69
  event :notify_followers_after_save, :after=>:subsequent, :on=>:save, :when=>proc{ |ca| ca.notable_change? } do
@@ -18,9 +18,10 @@ module CarrierWave
18
18
  super
19
19
 
20
20
  class_eval <<-RUBY, __FILE__, __LINE__+1
21
- event :store_#{column}_event, :on=>:save, :before=>:store do
21
+ event :store_#{column}_event, :on=>:save, :after=>:store do
22
22
  store_#{column}!
23
23
  end
24
+
24
25
  event :remove_#{column}_event, :on =>:delete, :after=>:stored do
25
26
  remove_#{column}!
26
27
  end
@@ -35,6 +36,14 @@ module CarrierWave
35
36
  #{column}
36
37
  end
37
38
 
39
+ def store_attachment!
40
+ store_#{column}!
41
+ end
42
+
43
+ def attachment_name
44
+ "#{column}".to_sym
45
+ end
46
+
38
47
  def read_uploader *args
39
48
  read_attribute *args
40
49
  end
@@ -42,14 +51,16 @@ module CarrierWave
42
51
  write_attribute *args
43
52
  end
44
53
  def #{column}=(new_file)
45
- column = _mounter(:#{column}).serialization_column
46
- send(:"\#{column}_will_change!")
54
+ send(:"#{column}_will_change!")
55
+ db_column = _mounter(:#{column}).serialization_column
56
+ send(:"\#{db_column}_will_change!")
47
57
  super
48
58
  end
49
59
 
50
60
  def remote_#{column}_url=(url)
51
- column = _mounter(:#{column}).serialization_column
52
- send(:"\#{column}_will_change!")
61
+ send(:"#{column}_will_change!")
62
+ db_column = _mounter(:#{column}).serialization_column
63
+ send(:"\#{db_column}_will_change!")
53
64
  super
54
65
  end
55
66
 
@@ -59,6 +70,14 @@ module CarrierWave
59
70
  _mounter(:#{column}).write_identifier
60
71
  end
61
72
 
73
+ def #{column}_will_change!
74
+ @#{column}_changed = true
75
+ end
76
+
77
+ def #{column}_changed?
78
+ @#{column}_changed
79
+ end
80
+
62
81
  def serializable_hash(options=nil)
63
82
  hash = {}
64
83
 
@@ -76,4 +95,4 @@ module CarrierWave
76
95
  end
77
96
 
78
97
  end
79
- end
98
+ end
@@ -66,13 +66,18 @@ class FileUploader < CarrierWave::Uploader::Base
66
66
  end.downcase
67
67
  end
68
68
 
69
- # the identifier gets stored in the card's db_content field
70
- def db_content(args={})
69
+ # generate identifier that gets stored in the card's db_content field
70
+ def db_content opts={}
71
+ @mod = opts[:mod] || model.load_from_mod # don't use mod_file? here
72
+ # mod_file? looks at the identifier in the db
73
+ # to figure out whether it's a mod file
74
+ # so it wouldn't be possible to turn a mod file card into a regular file card
75
+
71
76
  basename =
72
- if (args[:mod])
73
- "#{args[:mod]}#{extension}"
77
+ if @mod
78
+ "#{@mod}#{extension}"
74
79
  else
75
- filename
80
+ "#{action_id}#{extension}"
76
81
  end
77
82
  "%s/%s" % [file_dir, basename]
78
83
  end
@@ -82,35 +87,46 @@ class FileUploader < CarrierWave::Uploader::Base
82
87
  end
83
88
 
84
89
  def file_dir
85
- if (mod = mod_file?)
90
+ if mod_file?
86
91
  ":#{model.codename}"
87
92
  elsif model.id
88
93
  "~#{model.id}"
89
94
  else
90
- "#{model.key}" # FIXME what if the card has not a name yet?
95
+ "~#{model.upload_cache_card.id}"
91
96
  end
92
97
  end
93
98
 
94
99
  def cache_dir
95
- Cardio.paths['files'].existent.first + '/tmp'
100
+ Cardio.paths['files'].existent.first + '/cache'
96
101
  end
97
102
 
98
- # Carrierwave usually store the filename as identifier in the database
103
+ # Carrierwave usually stores the filename as identifier in the database
99
104
  # and retrieve_from_store! calls store_path with the identifier from the db
100
105
  # In our case the first part of our identifier is not part of the path
101
- # but we construct the filename from db data. So we don't need the identifier.
106
+ # but we can construct the filename from db data. So we don't need the identifier.
102
107
  # We can just call store_path always with the filename
103
108
  def store_path(for_file=filename) #
104
109
  super(filename)
105
110
  end
106
111
 
112
+ def tmp_path
113
+ if !Dir.exists? model.tmp_store_dir
114
+ Dir.mkdir model.tmp_store_dir
115
+ end
116
+ File.join model.tmp_store_dir, filename
117
+ end
118
+
119
+ def create_versions? new_file
120
+ model.create_versions?
121
+ end
122
+
107
123
  # paperclip compatibility used in type/file.rb#core (base format)
108
124
  def path(version=nil)
109
125
  version ? versions[version].path : super()
110
126
  end
111
127
 
112
128
  def original_filename
113
- @original_filename || model.selected_action.comment
129
+ @original_filename || (model.selected_action && model.selected_action.comment)
114
130
  end
115
131
 
116
132
  def store_dir
@@ -7,19 +7,22 @@ class ImageUploader < FileUploader
7
7
  (version && version != :original) ? versions[version].path : super()
8
8
  end
9
9
 
10
- version :icon do #, :from_version=>:small do
10
+ version :icon, :if=>:create_versions?, :from_version=>:small do
11
11
  process :resize_and_pad => [16,16]
12
12
  end
13
- version :small do #, :from_version=>:medium do
13
+ version :small, :if=>:create_versions?, :from_version=>:medium do
14
14
  process :resize_to_fit => [75,75]
15
15
  end
16
- version :medium do
16
+ version :medium, :if=>:create_versions? do
17
17
  process :resize_to_fit => [200,200]
18
18
  end
19
- version :large do
19
+ version :large, :if=>:create_versions? do
20
20
  process :resize_to_fit => [500,500]
21
21
  end
22
22
 
23
+ def identifier
24
+ full_filename(super())
25
+ end
23
26
  # add 'original' if no version is given
24
27
  def full_filename(for_file)
25
28
  name = super(for_file)
@@ -1,32 +1,85 @@
1
1
  require 'carrier_wave/cardmount'
2
+
2
3
  def self.included host_class
3
4
  host_class.extend CarrierWave::CardMount
4
5
  end
5
6
 
7
+ event :select_file_revision, :after=>:select_action do
8
+ attachment.retrieve_from_store!(attachment.identifier)
9
+ end
6
10
 
7
- event :write_identifier, :after=>:assign_action do
8
- self.content = attachment.db_content
11
+ event :determine_store_place, :before=>:prepare, :on=>:update do
12
+ @store_place = load_from_mod || :deck
9
13
  end
10
14
 
11
- event :save_original_filename, :before=>:finalize_action do
12
- if @current_action
13
- @current_action.update_attributes! :comment=>original_filename
15
+ event :upload_attachment, :before=>:validate_name, :on=>:save, :when=>proc { |c| c.preliminary_upload? } do
16
+ save_original_filename # save original filename as comment in action
17
+ write_identifier # set db_content (needs original filename to determine extension)
18
+ store_attachment!
19
+ finalize_action # create Card::Change entry for db_content
20
+ @current_action.update_attributes! :draft => true, :card_id => (new_card? ? upload_cache_card.id : id)
21
+
22
+ success << {
23
+ :target => (new_card? ? upload_cache_card : '_self'),
24
+ :type=> type_name,
25
+ :view => 'preview_editor',
26
+ :rev_id => current_action.id
27
+ }
28
+ abort :success
29
+ end
30
+
31
+ event :assign_attachment_on_create, :after=>:prepare, :on=>:create do
32
+ if save_preliminary_upload? && (action = Card::Action.fetch(Card::Env.params[:cached_upload]))
33
+ upload_cache_card.selected_action_id = action.id
34
+ upload_cache_card.select_file_revision
35
+ assign_attachment upload_cache_card.attachment.file, action.comment
36
+ action.delete # TODO: delete files too
14
37
  end
15
38
  end
16
39
 
17
- event :move_file_to_store_dir, :after=>:store, :on=>:create do
18
- if ::File.exist? tmp_store_dir
19
- if ::File.exist? store_dir
20
- FileUtils.rm_rf store_dir
21
- end
22
- FileUtils.mv tmp_store_dir, store_dir
40
+ event :assign_attachment_on_update, :after=>:prepare, :on=>:update do
41
+ if save_preliminary_upload? && (action = Card::Action.fetch(Card::Env.params[:cached_upload]))
42
+ uploaded_file =
43
+ with_selected_action_id(action.id) do
44
+ attachment.file
45
+ end
46
+ assign_attachment uploaded_file, action.comment
47
+ action.delete
23
48
  end
24
- if !(content =~ /^[:~]/)
25
- update_column(:db_content,attachment.db_content)
26
- expire
49
+ end
50
+
51
+ def assign_attachment file, original_filename
52
+ send "#{attachment_name}=", file
53
+ write_identifier
54
+ @current_action.update_attributes! :comment=>original_filename
55
+ end
56
+
57
+ # we need a card id for the path so we have to update db_content when we got an id
58
+ event :correct_identifier, :after=>:store, :on=>:create do
59
+ update_column(:db_content,attachment.db_content(:mod=>load_from_mod))
60
+ expire
61
+ end
62
+
63
+
64
+ event :save_original_filename, :after=>:validate_name, :when => proc {|c| !c.preliminary_upload? && !c.save_preliminary_upload? && c.attachment_changed?} do
65
+ if @current_action
66
+ @current_action.update_attributes! :comment=>original_filename
27
67
  end
28
68
  end
29
69
 
70
+ event :write_identifier, :after=>:save_original_filename do
71
+ self.content = attachment.db_content(:mod=>load_from_mod)
72
+ end
73
+
74
+
75
+ def upload_cache_card
76
+ @upload_cache_card ||= Card["new_#{attachment_name}".to_sym ]
77
+ end
78
+
79
+ def tmp_store_dir action_id=nil
80
+ "#{ Card.paths['files'].existent.first }/#{upload_cache_card.id}"
81
+ end
82
+
30
83
  def item_names(args={}) # needed for flexmail attachments. hacky.
31
84
  [self.cardname]
32
85
  end
@@ -35,6 +88,60 @@ def original_filename
35
88
  attachment.original_filename
36
89
  end
37
90
 
91
+ def preliminary_upload?
92
+ Card::Env && Card::Env.params[:attachment_upload]
93
+ end
94
+
95
+ def save_preliminary_upload?
96
+ Card::Env.params[:cached_upload].present?
97
+ end
98
+
99
+ def attachment_changed?
100
+ send "#{attachment_name}_changed?"
101
+ end
102
+
103
+ def create_versions?
104
+ true
105
+ end
106
+
107
+ def store_place
108
+ @store_place ||= mod_file? || :deck
109
+ end
110
+
111
+ def load_from_mod= value
112
+ @mod = value
113
+ end
114
+
115
+ def load_from_mod
116
+ @mod
117
+ end
118
+
119
+ def store_dir
120
+ if (store_place == :deck)
121
+ if id
122
+ "#{ Card.paths['files'].existent.first }/#{id}"
123
+ else
124
+ tmp_store_dir
125
+ end
126
+ else
127
+ "#{ Cardio.gem_root}/mod/#{store_place}/file/#{codename}"
128
+ end
129
+ end
130
+
131
+ def mod_file?
132
+ # when db_content was changed assume that it's no longer a mod file
133
+ if @store_place != :deck && !db_content_changed? && content.present?
134
+ case content
135
+ when /^:[^\/]+\/([^.]+)/ ; $1 # current mod_file format
136
+ when /^\~/ ; false # current id file format
137
+ else
138
+ if lines = content.split("\n") and lines.size == 4 # old format, still used in card_changes.
139
+ lines.last
140
+ end
141
+ end
142
+ end
143
+ end
144
+
38
145
 
39
146
  def assign_set_specific_attributes
40
147
  if @set_specific && @set_specific.present?
@@ -43,6 +150,17 @@ def assign_set_specific_attributes
43
150
  super
44
151
  end
45
152
 
153
+ def clear_upload_tmp_dir
154
+ Dir.entries(tmp_store_dir).each do |filename|
155
+ if filename =~/^\d+/
156
+ path = File.join(tmp_store_dir, filename )
157
+ older_than_five_days = ( DateTime.now - File.ctime(path) > 432000)
158
+ if older_than_five_days
159
+ FileUtils.rm path
160
+ end
161
+ end
162
+ end
163
+ end
46
164
 
47
165
  def symlink_to(prior_action_id) # create filesystem links to files from prior action
48
166
  if prior_action_id != last_action_id