voluntary 0.2.2 → 0.2.3

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 (29) hide show
  1. data/CHANGELOG.md +88 -0
  2. data/README.rdoc +2 -2
  3. data/app/assets/javascripts/voluntary/application.js +3 -0
  4. data/app/assets/javascripts/voluntary/base.js.coffee +47 -28
  5. data/app/assets/javascripts/voluntary/lib/jquery.multisortable.js +254 -0
  6. data/app/assets/stylesheets/voluntary/application.css +2 -0
  7. data/app/assets/stylesheets/voluntary/base.css.sass +7 -1
  8. data/app/assets/stylesheets/voluntary/bootstrap_and_overrides.css.sass +5 -1
  9. data/app/controllers/concerns/voluntary/v1/base_controller.rb +6 -1
  10. data/app/controllers/voluntary/api/v1/base_controller.rb +11 -0
  11. data/app/helpers/voluntary/show_helper.rb +2 -2
  12. data/app/models/concerns/likeable.rb +11 -0
  13. data/app/models/user.rb +4 -0
  14. data/app/views/layouts/application.html.erb +7 -1
  15. data/app/views/shared/_modal_javascript_response.js.erb +31 -0
  16. data/app/views/shared/layouts/twitter_bootstrap/_control_group.html.erb +11 -0
  17. data/app/views/shared/layouts/twitter_bootstrap/_modal.html.erb +12 -0
  18. data/app/views/shared/layouts/twitter_bootstrap/control_group/_boolean.html.erb +17 -0
  19. data/app/views/shared/resource/_actions.html.erb +4 -2
  20. data/config/locales/general/en.yml +14 -2
  21. data/config/routes/api.rb +6 -0
  22. data/config/routes.rb +3 -1
  23. data/lib/generators/voluntary/install/templates/app/views/layouts/application.html.erb +7 -1
  24. data/lib/generators/voluntary/product_dummy/product_dummy_generator.rb +0 -1
  25. data/lib/generators/voluntary/product_dummy/templates/app/views/layouts/application.html.erb +7 -1
  26. data/lib/voluntary/version.rb +1 -1
  27. data/lib/voluntary.rb +3 -1
  28. metadata +90 -84
  29. data/app/controllers/voluntary/api/v1/api_controller.rb +0 -9
data/CHANGELOG.md ADDED
@@ -0,0 +1,88 @@
1
+ ## unreleased ##
2
+
3
+ ## 0.2.3 (March 15, 2015) ##
4
+
5
+ * #16 Implements scope liked_by for likeables.
6
+ * #45 Rewrite of API base controller and initialization of API V1 routes.
7
+ * Introduces Likeable.likes_or_dislikes_for.
8
+ * Introduces new window function jquery_ui_tabs_initialization.
9
+ * #37 Integrates selectize plugin. Used at new tournament form of competition product.
10
+ * Make .remote_modal_link click handler work with other elements than links through data attribute url.
11
+ * Disables closing of modal on pressing escape key.
12
+ * Introduces modal JavaScript response partial. See protip https://coderwall.com/p/eu9sqa/multi-step-form-wizard-responses-for-ajax-modals-and-html-requests
13
+ * Option to yield sidebar in application layout.
14
+ * Introduces User#best_available_name.
15
+ * #26 Add event handler for toggleable checkboxes.
16
+ * Sets GitHub ribbon to voluntary repository in application layout which can be overridden in each product through controller method voluntary_application_repository_path.
17
+ * #20 #23 #32 #33 #34 Adds JavaScript Plugin CompetitiveList used at music metadata enrichment music year in review top albums and songs.
18
+ * Adds jquery.multisortable.js used at music metadata enrichment music year in review top albums and songs.
19
+ * #19 Adds Twitter Bootstrap datetime picker.
20
+ * Implements shared twitter bootstrap layout 'boolean control group'.
21
+ * Sets width of #bootstrap_modal to 800px.
22
+ * Implements shared twitter bootstrap layout partials 'control_group' ...
23
+ * Implements new option :namespace for Voluntary::ShowHelper#show_actions.
24
+
25
+ ## 0.2.2 (February 5, 2015) ##
26
+
27
+ * #17 Show progress spinner gif for Ajax tabs.
28
+ * Implements option to render custom breadcrumbs.
29
+ * Makes stylesheets and javascripts of application layout variable.
30
+ * Adds new view helper name_with_apostrophe.
31
+ * Yield page title and search in application layout.
32
+ * Disable scrolling in modal windows because of autocomplete lists cut off before.
33
+ * Sets global font size from 10px to 12px.
34
+ * Fixes ui tab paging links. Implements remote links and remote modal link handler.
35
+ * #14 last.fm authentication.
36
+ * #13 Facebook authentication.
37
+ * #12 Google authentication.
38
+
39
+ ## 0.2.1 (January 6, 2015) ##
40
+
41
+ * Small changes not worth mentioning.
42
+
43
+ ## 0.2.0 (March 22, 2014) ##
44
+
45
+ * Rails 4 upgrade.
46
+ * Adds new resource organization.
47
+ * after initialize callback for authorization ability.rb
48
+ * after_resource_has_many callback for main navigation
49
+ * Several bugfixes and small features.
50
+
51
+ ## 0.1.0 (October 13, 2013) ##
52
+
53
+ * voluntary_translation support with new model column.
54
+
55
+ ## 0.1.0.rc4 (September 9, 2013) ##
56
+
57
+ * Select product in /workflow/user/products/:product_id/areas/:id
58
+
59
+ ## 0.1.0.rc3 (September 6, 2013) ##
60
+
61
+ * Fixes invalid user update form by initializing presenter.
62
+
63
+ ## 0.1.0.rc2 (July 31, 2013) ##
64
+
65
+ * Support nesting of areas. #1
66
+
67
+ ## 0.1.0.rc1 (June 7, 2013) ##
68
+
69
+ * Updates twitter bootstrap and jquery ui.
70
+ * Removes rails_info dependency.
71
+ * Paints navigation black and more responsive.
72
+ * Allow mixin of cancan authorization abilities in products and client applications.
73
+ * Several small bug fixes and cleanup.
74
+ * Removes "twitter" (auth) dependencies
75
+
76
+ ## 0.0.3 (November 19, 2012) ##
77
+
78
+ * Adds vendor_extensions to gem specification's directories.
79
+
80
+ ## 0.0.2 (November 19, 2012) ##
81
+
82
+ * Fixes some bugs regarding integration of product engines.
83
+
84
+ * Adds product dummy generator.
85
+
86
+ ## 0.0.1 (November 11, 2012) ##
87
+
88
+ * Initial version.
data/README.rdoc CHANGED
@@ -12,7 +12,7 @@ Run this in your console:
12
12
 
13
13
  rvm --create use 1.9.3@your_crowdsourcing_platform_name
14
14
  gem update bundler
15
- gem install rails -v 3.2.14 --no-rdoc --no-ri
15
+ gem install rails -v 4.0.13 --no-rdoc --no-ri
16
16
  rails new your_crowdsourcing_platform_name
17
17
  cd your_crowdsourcing_platform_name
18
18
 
@@ -74,9 +74,9 @@ Run this in your console:
74
74
  bundle install
75
75
  # change config/boot.rb to require bundler like here: https://github.com/volontariat/voluntary_scholarship/blob/master/dummy/config/boot.rb
76
76
  # change database names to #{product_name}_#{environment} and customize user credentials in config/database.yml
77
- # add gitignore file from voluntary: https://github.com/volontariat/voluntary/blob/master/.gitignore
78
77
  bundle exec rake db:create:all && bundle exec rails g voluntary:product_dummy # confirm all overwrite questions except of Gemfile
79
78
  cd ..
79
+ # add gitignore file from voluntary: https://github.com/volontariat/voluntary/blob/master/.gitignore
80
80
  rails g migration add_product_name_product
81
81
  # fill migration file with template: https://github.com/volontariat/voluntary_scholarship/blob/master/db/migrate/20140306201232_add_scholarship_product.rb
82
82
  cd dummy
@@ -2,5 +2,8 @@
2
2
  //= require jquery_ujs
3
3
  //= require jquery-ui-bootstrap
4
4
  //= require twitter/bootstrap
5
+ //= require bootstrap-datetimepicker
5
6
  //= require jquery.tokeninput
7
+ //= require selectize
8
+ //= require competitive_list
6
9
  //= require_tree .
@@ -5,33 +5,37 @@ $(document).ready ->
5
5
 
6
6
  $( '.accordions' ).each (k, v) ->
7
7
  $(v).accordion({ autoHeight: false });
8
-
9
- $('.tabs').each (k, v) ->
10
- $(v).tabs
11
- autoHeight: false
12
- beforeActivate: (event, ui) ->
13
- unless ui.newTab.find('a').attr('href').indexOf('#') == 0
14
- ui.newTab.find('.ajax_spinner').show()
15
- ui.newPanel.empty()
16
-
17
- beforeLoad: (event, ui) ->
18
- ui.jqXHR.error ->
8
+
9
+ window.jquery_ui_tabs_initialization = ->
10
+ $('.tabs').each (k, v) ->
11
+ $(v).tabs
12
+ autoHeight: false
13
+ beforeActivate: (event, ui) ->
14
+ unless ui.newTab.find('a').attr('href').indexOf('#') == 0
15
+ ui.newTab.find('.ajax_spinner').show()
16
+ ui.newPanel.empty()
17
+
18
+ beforeLoad: (event, ui) ->
19
+ ui.jqXHR.error ->
20
+ unless ui.tab.find('a').attr('href').indexOf('#') == 0
21
+ ui.tab.find('.ajax_spinner').hide()
22
+
23
+ json = null
24
+
25
+ try
26
+ json = jQuery.parseJSON(ui.jqXHR.responseText)
27
+ catch e
28
+
29
+ error = if json && json['error'] then json['error'] else 'Something went wrong'
30
+
31
+ ui.panel.html error
32
+
33
+ load: (event, ui) ->
19
34
  unless ui.tab.find('a').attr('href').indexOf('#') == 0
20
35
  ui.tab.find('.ajax_spinner').hide()
21
-
22
- json = null
23
-
24
- try
25
- json = jQuery.parseJSON(ui.jqXHR.responseText)
26
- catch e
27
-
28
- error = if json && json['error'] then json['error'] else 'Something went wrong'
29
-
30
- ui.panel.html error
31
-
32
- load: (event, ui) ->
33
- unless ui.tab.find('a').attr('href').indexOf('#') == 0
34
- ui.tab.find('.ajax_spinner').hide()
36
+ window.jquery_ui_tabs_initialization()
37
+
38
+ window.jquery_ui_tabs_initialization()
35
39
 
36
40
  $(document).on "click", ".ui-tabs-panel .pagination a", (event) ->
37
41
  event.preventDefault()
@@ -82,6 +86,8 @@ $(document).ready ->
82
86
  $( ".datepicker" ).each (k, v) ->
83
87
  $(v).datepicker({ dateFormat: "yy-mm-dd", changeYear: true, yearRange: "c-100:c+10" });
84
88
 
89
+ $('.datetime_picker').datetimepicker()
90
+
85
91
  $(document.body).on "click", ".remote_links", (event) ->
86
92
  $this = $(this)
87
93
 
@@ -93,13 +99,26 @@ $(document).ready ->
93
99
  $(document.body).on "click", ".remote_modal_link", (event) ->
94
100
  $this = $(this)
95
101
 
96
- $.ajax(url: $this.attr('href'), type: "GET", dataType: "html").success (data) ->
102
+ url = if $this.data('url') then $this.data('url') else $this.attr('href')
103
+
104
+ $.ajax(url: url, type: "GET", dataType: "html").success (data) ->
97
105
  $('#bootstrap_modal').html(data)
98
- $('#bootstrap_modal').modal('show')
106
+ $('#bootstrap_modal').modal(show: true, keyboard: false)
99
107
 
100
108
  event.preventDefault()
101
109
 
102
110
  $(document.body).on "click", "#close_bootstrap_modal_button", (event) ->
103
111
  $('#bootstrap_modal').modal('hide')
104
112
  event.preventDefault()
105
-
113
+
114
+ $(document.body).on "click", "#toggle_checkboxes_checkbox", (event) ->
115
+ if $('.toggleable_checkbox:checked').length == 0
116
+ $('.toggleable_checkbox').prop('checked', true)
117
+ else
118
+ $('.toggleable_checkbox').prop('checked', false)
119
+
120
+ $('.bootstrap_tooltip').tooltip()
121
+
122
+ $('.selectize_select').selectize
123
+ create: true,
124
+ sortField: 'text'
@@ -0,0 +1,254 @@
1
+ /**
2
+ * jquery.multisortable.js - v0.2
3
+ * https://github.com/shvetsgroup/jquery.multisortable
4
+ *
5
+ * Author: Ethan Atlakson, Jay Hayes, Gabriel Such, Alexander Shvets
6
+ * Last Revision 3/16/2012
7
+ * multi-selectable, multi-sortable jQuery plugin
8
+ */
9
+
10
+ !function($) {
11
+
12
+ $.fn.multiselectable = function(options) {
13
+ if (!options) {
14
+ options = {}
15
+ }
16
+ options = $.extend({}, $.fn.multiselectable.defaults, options);
17
+
18
+ function mouseDown(e) {
19
+ var item = $(this),
20
+ parent = item.parent(),
21
+ myIndex = item.index();
22
+
23
+ var prev = parent.find('.multiselectable-previous');
24
+ // If no previous selection found, start selecting from first selected item.
25
+ prev = prev.length ? prev : $(parent.find('.' + options.selectedClass)[0]).addClass('multiselectable-previous');
26
+ var prevIndex = prev.index();
27
+
28
+ if (e.ctrlKey || e.metaKey) {
29
+ if (item.hasClass(options.selectedClass)) {
30
+ item.removeClass(options.selectedClass).removeClass('multiselectable-previous')
31
+ if (item.not('.child').length) {
32
+ item.nextUntil(':not(.child)').removeClass(options.selectedClass);
33
+ }
34
+ }
35
+ else {
36
+ parent.find('.multiselectable-previous').removeClass('multiselectable-previous');
37
+ item.addClass(options.selectedClass).addClass('multiselectable-previous')
38
+ if (item.not('.child').length) {
39
+ item.nextUntil(':not(.child)').addClass(options.selectedClass);
40
+ }
41
+ }
42
+ }
43
+
44
+ if (e.shiftKey) {
45
+ var last_shift_range = parent.find('.multiselectable-shift');
46
+ last_shift_range.removeClass(options.selectedClass).removeClass('multiselectable-shift');
47
+
48
+ var shift_range;
49
+ if (prevIndex < myIndex) {
50
+ shift_range = item.prevUntil('.multiselectable-previous').add(prev).add(item);
51
+ }
52
+ else if (prevIndex > myIndex) {
53
+ shift_range = item.nextUntil('.multiselectable-previous').add(prev).add(item);
54
+ }
55
+ shift_range.addClass(options.selectedClass).addClass('multiselectable-shift');
56
+ }
57
+ else {
58
+ parent.find('.multiselectable-shift').removeClass('multiselectable-shift');
59
+ }
60
+
61
+ if (!e.ctrlKey && !e.metaKey && !e.shiftKey) {
62
+ parent.find('.multiselectable-previous').removeClass('multiselectable-previous');
63
+ if (!item.hasClass(options.selectedClass)) {
64
+ parent.find('.' + options.selectedClass).removeClass(options.selectedClass);
65
+ item.addClass(options.selectedClass).addClass('multiselectable-previous');
66
+ if (item.not('.child').length) {
67
+ item.nextUntil(':not(.child)').addClass(options.selectedClass);
68
+ }
69
+ }
70
+ }
71
+
72
+ options.mousedown(e, item);
73
+ }
74
+
75
+ function click(e) {
76
+ if ( $(this).is('.ui-draggable-dragging') ) {
77
+ return;
78
+ }
79
+
80
+ var item = $(this), parent = item.parent();
81
+
82
+ // If item wasn't draged and is not multiselected, it should reset selection for other items.
83
+ if (!e.ctrlKey && !e.metaKey && !e.shiftKey) {
84
+ parent.find('.multiselectable-previous').removeClass('multiselectable-previous');
85
+ parent.find('.' + options.selectedClass).removeClass(options.selectedClass);
86
+ item.addClass(options.selectedClass).addClass('multiselectable-previous');
87
+ if (item.not('.child').length) {
88
+ item.nextUntil(':not(.child)').addClass(options.selectedClass);
89
+ }
90
+ }
91
+
92
+ options.click(e, item);
93
+ }
94
+
95
+ return this.each(function() {
96
+ var list = $(this);
97
+
98
+ if (!list.data('multiselectable')) {
99
+ list.data('multiselectable', true)
100
+ .delegate(options.items, 'mousedown', mouseDown)
101
+ .delegate(options.items, 'click', click)
102
+ .disableSelection();
103
+ }
104
+ })
105
+ };
106
+
107
+ $.fn.multiselectable.defaults = {
108
+ click: function(event, elem) {},
109
+ mousedown: function(event, elem) {},
110
+ selectedClass: 'selected',
111
+ items: 'li'
112
+ };
113
+
114
+
115
+ $.fn.multisortable = function(options) {
116
+ if (!options) {
117
+ options = {}
118
+ }
119
+ var settings = $.extend({}, $.fn.multisortable.defaults, options);
120
+
121
+ function regroup(item, list) {
122
+ if (list.find('.' + settings.selectedClass).length > 0) {
123
+ var myIndex = item.data('i');
124
+
125
+ var itemsBefore = list.find('.' + settings.selectedClass).filter(function() {
126
+ return $(this).data('i') < myIndex
127
+ }).css({
128
+ position: '',
129
+ width: '',
130
+ left: '',
131
+ top: '',
132
+ zIndex: ''
133
+ });
134
+
135
+ item.before(itemsBefore);
136
+
137
+ var itemsAfter = list.find('.' + settings.selectedClass).filter(function() {
138
+ return $(this).data('i') > myIndex
139
+ }).css({
140
+ position: '',
141
+ width: '',
142
+ left: '',
143
+ top: '',
144
+ zIndex: ''
145
+ });
146
+
147
+ item.after(itemsAfter);
148
+
149
+ setTimeout(function() {
150
+ itemsAfter.add(itemsBefore).addClass(settings.selectedClass);
151
+ }, 0);
152
+ }
153
+ }
154
+
155
+ return this.each(function() {
156
+ var list = $(this);
157
+
158
+ //enable multi-selection
159
+ list.multiselectable({
160
+ selectedClass: settings.selectedClass,
161
+ click: settings.click,
162
+ items: settings.items,
163
+ mousedown: settings.mousedown
164
+ });
165
+
166
+ //enable sorting
167
+ options.cancel = settings.items + ':not(.' + settings.selectedClass + ')';
168
+ options.placeholder = settings.placeholder;
169
+ options.start = function(event, ui) {
170
+ if (ui.item.hasClass(settings.selectedClass)) {
171
+ var parent = ui.item.parent();
172
+
173
+ //assign indexes to all selected items
174
+ parent.find('.' + settings.selectedClass).each(function(i) {
175
+ $(this).data('i', i);
176
+ });
177
+
178
+ // adjust placeholder size to be size of items
179
+ var height = parent.find('.' + settings.selectedClass).length * ui.item.outerHeight();
180
+ ui.placeholder.height(height);
181
+ }
182
+
183
+ settings.start(event, ui);
184
+ };
185
+
186
+ options.stop = function(event, ui) {
187
+ regroup(ui.item, ui.item.parent());
188
+ settings.stop(event, ui);
189
+ };
190
+
191
+ options.sort = function(event, ui) {
192
+ var parent = ui.item.parent(),
193
+ myIndex = ui.item.data('i'),
194
+ top = parseInt(ui.item.css('top').replace('px', '')),
195
+ left = parseInt(ui.item.css('left').replace('px', ''));
196
+
197
+ // fix to keep compatibility using prototype.js and jquery together
198
+ $.fn.reverse = Array.prototype._reverse || Array.prototype.reverse
199
+
200
+ var height = 0;
201
+ $('.' + settings.selectedClass, parent).filter(function() {
202
+ return $(this).data('i') < myIndex;
203
+ }).reverse().each(function() {
204
+ height += $(this).outerHeight();
205
+ $(this).css({
206
+ left: left,
207
+ top: top - height,
208
+ position: 'absolute',
209
+ zIndex: 1000,
210
+ width: ui.item.width()
211
+ })
212
+ });
213
+
214
+ height = ui.item.outerHeight();
215
+ $('.' + settings.selectedClass, parent).filter(function() {
216
+ return $(this).data('i') > myIndex;
217
+ }).each(function() {
218
+ var item = $(this);
219
+ item.css({
220
+ left: left,
221
+ top: top + height,
222
+ position: 'absolute',
223
+ zIndex: 1000,
224
+ width: ui.item.width()
225
+ });
226
+
227
+ height += item.outerHeight();
228
+ });
229
+
230
+ settings.sort(event, ui);
231
+ };
232
+
233
+ options.receive = function(event, ui) {
234
+ regroup(ui.item, ui.sender);
235
+ settings.receive(event, ui);
236
+ };
237
+
238
+ list.sortable(options).disableSelection();
239
+ })
240
+ };
241
+
242
+ $.fn.multisortable.defaults = {
243
+ start: function(event, ui) {},
244
+ stop: function(event, ui) {},
245
+ sort: function(event, ui) {},
246
+ receive: function(event, ui) {},
247
+ click: function(event, elem) {},
248
+ mousedown: function(event, elem) {},
249
+ selectedClass: 'selected',
250
+ placeholder: 'placeholder',
251
+ items: 'li'
252
+ };
253
+
254
+ }(jQuery);
@@ -13,6 +13,8 @@
13
13
  *= require token-input-facebook
14
14
  *= require twitter/bootstrap
15
15
  *= require jquery-ui-bootstrap
16
+ *= require selectize
17
+ *= require selectize.bootstrap2
16
18
  *= require 'voluntary/bootstrap_and_overrides'
17
19
  *= require 'voluntary/base'
18
20
  */
@@ -63,4 +63,10 @@ html, body, div
63
63
 
64
64
  .likes_and_dislikes_table td
65
65
  background-color: transparent !important
66
- border-top: 0px !important
66
+ border-top: 0px !important
67
+
68
+ .multisortable li.selected
69
+ outline: 1px solid red
70
+
71
+ .selectize-input
72
+ width: 216px
@@ -1,4 +1,5 @@
1
1
  @import "twitter/bootstrap/responsive"
2
+ @import 'bootstrap-datetimepicker'
2
3
 
3
4
  .list-striped
4
5
  li:nth-child(odd)
@@ -11,4 +12,7 @@
11
12
  overflow-y: visible
12
13
 
13
14
  .scrollable-modal-body
14
- overflow-y: auto
15
+ overflow-y: auto
16
+
17
+ #bootstrap_modal
18
+ width: 800px !important
@@ -8,7 +8,8 @@ module Voluntary
8
8
  rescue_from ActiveRecord::RecordNotFound, with: :not_found
9
9
  rescue_from Mongoid::Errors::DocumentNotFound, with: :not_found
10
10
 
11
- helper_method :parent, :application_navigation, :navigation_product_path, :navigation_product_name, :voluntary_application_stylesheets, :voluntary_application_javascripts
11
+ helper_method :parent, :application_navigation, :navigation_product_path, :navigation_product_name, :voluntary_application_stylesheets
12
+ helper_method :voluntary_application_javascripts, :voluntary_application_repository_path
12
13
  end
13
14
 
14
15
  def voluntary_application_stylesheets
@@ -18,6 +19,10 @@ module Voluntary
18
19
  def voluntary_application_javascripts
19
20
  ['voluntary/application', 'application']
20
21
  end
22
+
23
+ def voluntary_application_repository_path
24
+ 'volontariat/voluntary'
25
+ end
21
26
 
22
27
  def parent
23
28
  @parent
@@ -0,0 +1,11 @@
1
+ module Voluntary
2
+ module Api
3
+ module V1
4
+ class BaseController < ActionController::Base #ActionController::Metal
5
+ #include ActionController::Rendering # enables rendering
6
+ #include ActionController::MimeResponds # enables serving different content types like :xml or :json
7
+ #include AbstractController::Callbacks # callbacks for your authentication logic
8
+ end
9
+ end
10
+ end
11
+ end
@@ -53,11 +53,11 @@ module Voluntary
53
53
  end
54
54
  end
55
55
 
56
- def show_actions
56
+ def show_actions(options = {})
57
57
  result = content_tag :dt, raw('&nbsp')
58
58
  result += content_tag :dd, render(
59
59
  partial: 'shared/resource/actions', locals: {
60
- type: root_model_class_name(resource).tableize, resource: resource
60
+ type: root_model_class_name(resource).tableize, resource: resource, namespace: options[:namespace]
61
61
  }
62
62
  )
63
63
 
@@ -8,9 +8,20 @@ module Likeable
8
8
  has_many :likers, class_name: 'User', through: :likes, source: :user
9
9
  has_many :dislikes, -> { where(positive: false) }, class_name: 'Like', dependent: :delete_all, as: :target
10
10
  has_many :dislikers, class_name: 'User', through: :dislikes, source: :user
11
+
12
+ scope :liked_by, ->(user_id) do
13
+ select('music_videos.*, likes.created_at AS liked_at').joins('RIGHT JOIN likes ON likes.positive = 1 AND likes.target_type = "MusicVideo" AND likes.target_id = music_videos.id').
14
+ where('likes.user_id = ? AND music_videos.id IS NOT NULL', user_id)
15
+ end
11
16
  end
12
17
 
13
18
  def update_likes_counter
14
19
  self.class.where(id: self.id).update_all likes_count: self.likes.count, dislikes_count: self.dislikes.count
15
20
  end
21
+
22
+ module ClassMethods
23
+ def likes_or_dislikes_for(user, ids)
24
+ user.likes_or_dislikes.for_targets(name, ids).index_by(&:target_id)
25
+ end
26
+ end
16
27
  end
data/app/models/user.rb CHANGED
@@ -101,6 +101,10 @@ class User < ActiveRecord::Base
101
101
  [first_name, last_name].join(' ')
102
102
  end
103
103
 
104
+ def best_available_name
105
+ lastfm_user_name || full_name
106
+ end
107
+
104
108
  private
105
109
 
106
110
  def set_main_role
@@ -13,6 +13,10 @@
13
13
 
14
14
  <%= render 'layouts/shared/navigation' %>
15
15
 
16
+ <% if voluntary_application_repository_path.present? %>
17
+ <a href="https://github.com/<%= voluntary_application_repository_path %>"><img style="position: absolute; top: 41px; right: 0; border: 0; z-index: 100;" src="https://camo.githubusercontent.com/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"></a>
18
+ <% end %>
19
+
16
20
  <section id="dialog">
17
21
  <img alt="Ajax-loader-small" class="hide " id="dialog_body_spinner" src="<%=image_path('voluntary/spinner.gif')%>"/>
18
22
  <div id="dialog_body"/>
@@ -27,7 +31,7 @@
27
31
  </div>
28
32
  <% end %>
29
33
  <div class="row-fluid">
30
- <% if sidenav(@sidenav_links_count).present? || content_for?(:search) %>
34
+ <% if sidenav(@sidenav_links_count).present? || content_for?(:search) || content_for?(:sidebar) %>
31
35
  <div class="span9">
32
36
  <% if content_for?(:breadcrumbs) %>
33
37
  <div class="nav">
@@ -43,6 +47,8 @@
43
47
  <%= yield :search %>
44
48
 
45
49
  <%= sidenav(@sidenav_links_count) %>
50
+
51
+ <%= yield :sidebar %>
46
52
  </div>
47
53
  <% else %>
48
54
  <div class="span12">
@@ -0,0 +1,31 @@
1
+ <% namespace ||= nil %>
2
+ <% message = flash[:notice].present? ? flash[:notice].clone : nil %>
3
+ <% flash.delete(:notice) %>
4
+ <% message = flash[:alert].present? ? flash[:alert].clone : message %>
5
+ <% flash.delete(:alert) %>
6
+ <% ajax_data ||= {} %>
7
+ <% ajax_method ||= :get %>
8
+
9
+ <% if @path.present? %>
10
+ $.ajax({ url: "<%= @path %>", data: <%= raw ajax_data.to_json %>, type: "<%= ajax_method.to_s.upcase %>", dataType: "script"}).done(function(data) {
11
+ eval(data);
12
+ <%= message.present? ? raw('alert("' + message + '");') : '' %>
13
+ })
14
+ .fail(function(data) {
15
+ <%= message.present? ? raw('alert("' + message + '");') : '' %>
16
+ alert("Failed to load <%= @path %>!");
17
+ });
18
+ <% elsif @template.present? %>
19
+ $("#bootstrap_modal").html("<%= escape_javascript(
20
+ render(
21
+ partial: 'shared/layouts/twitter_bootstrap/modal',
22
+ locals: {
23
+ title: title,
24
+ body: render(template: "#{namespace}#{@template}.html")
25
+ }
26
+ )
27
+ ) %>");
28
+ <%= message.present? ? raw('alert("' + message + '");') : '' %>
29
+ <% elsif message.present? %>
30
+ <%= message.present? ? raw('alert("' + message + '");') : '' %>
31
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <% id ||= field.match(/id="([^"]*)"/)[1] %>
2
+ <% required ||= false %>
3
+ <% required_class = required ? ' required' : ' optional' %>
4
+ <div class="control-group string<%= required_class %> <%= id %>">
5
+ <label for="<%= id %>" class="string<%= required_class %> control-label">
6
+ <% if required %><abbr title="required">*</abbr><% end %> <%= label %>
7
+ </label>
8
+ <div class="controls">
9
+ <%= field %>
10
+ </div>
11
+ </div>