my_forum 0.0.1.beta2 → 0.0.1.beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.rdoc +9 -1
  3. data/app/assets/images/my_forum/smiles/acute.gif +0 -0
  4. data/app/assets/images/my_forum/smiles/aggressive.gif +0 -0
  5. data/app/assets/images/my_forum/smiles/beee.gif +0 -0
  6. data/app/assets/images/my_forum/smiles/blum2.gif +0 -0
  7. data/app/assets/images/my_forum/smiles/blush2.gif +0 -0
  8. data/app/assets/images/my_forum/smiles/cray.gif +0 -0
  9. data/app/assets/images/my_forum/smiles/dirol.gif +0 -0
  10. data/app/assets/images/my_forum/smiles/download.gif +0 -0
  11. data/app/assets/images/my_forum/smiles/i-m_so_happy.gif +0 -0
  12. data/app/assets/images/my_forum/smiles/ireful2.gif +0 -0
  13. data/app/assets/javascripts/my_forum/application.js +3 -0
  14. data/app/assets/javascripts/my_forum/jquery.scrollTo.min.js +7 -0
  15. data/app/assets/javascripts/my_forum/jquery.selection.js +354 -0
  16. data/app/assets/javascripts/my_forum/my_forum.js.coffee +38 -0
  17. data/app/assets/stylesheets/my_forum/application.css.scss +12 -0
  18. data/app/assets/stylesheets/my_forum/posts.css.scss +5 -0
  19. data/app/assets/stylesheets/my_forum/topics.css.scss +4 -0
  20. data/app/assets/stylesheets/my_forum/welcome.css.scss +1 -0
  21. data/app/controllers/my_forum/application_controller.rb +7 -0
  22. data/app/controllers/my_forum/attachments_controller.rb +23 -0
  23. data/app/controllers/my_forum/forums_controller.rb +1 -1
  24. data/app/controllers/my_forum/images_controller.rb +7 -0
  25. data/app/controllers/my_forum/posts_controller.rb +15 -1
  26. data/app/controllers/my_forum/private_messages_controller.rb +1 -1
  27. data/app/controllers/my_forum/topics_controller.rb +3 -0
  28. data/app/helpers/my_forum/posts_helper.rb +15 -6
  29. data/app/helpers/my_forum/users_helper.rb +2 -0
  30. data/app/models/my_forum/attachment.rb +8 -0
  31. data/app/models/my_forum/avatar.rb +4 -0
  32. data/app/models/my_forum/image.rb +5 -0
  33. data/app/models/my_forum/post.rb +2 -0
  34. data/app/models/my_forum/topic.rb +10 -9
  35. data/app/models/my_forum/user.rb +1 -0
  36. data/app/views/layouts/my_forum/_user_navbar.html.haml +1 -0
  37. data/app/views/my_forum/forums/_topic_subject.html.haml +2 -2
  38. data/app/views/my_forum/private_messages/inbox.haml +4 -2
  39. data/app/views/my_forum/topics/_post.haml +6 -2
  40. data/app/views/my_forum/topics/_quick_answer.haml +80 -2
  41. data/app/views/my_forum/topics/show.haml +11 -3
  42. data/config/locales/ru.yml +2 -0
  43. data/config/routes.rb +5 -0
  44. data/db/migrate/20141117122751_create_my_forum_posts.rb +1 -1
  45. data/db/migrate/20141118131215_create_my_forum_users.rb +6 -5
  46. data/db/migrate/20151012095554_create_my_forum_images.rb +12 -0
  47. data/lib/my_forum/engine.rb +1 -1
  48. data/lib/my_forum/version.rb +1 -1
  49. data/lib/tasks/my_forum_tasks.rake +131 -4
  50. data/spec/controllers/my_forum/attachments_controller_spec.rb +7 -0
  51. data/spec/controllers/my_forum/images_controller_controller_spec.rb +7 -0
  52. data/spec/models/my_forum/image_spec.rb +7 -0
  53. metadata +40 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 259bf280807e5379a1207cd076de17a6ae69746d
4
- data.tar.gz: 4d3486d8623e4192e3ada81996bea4a482d351fc
3
+ metadata.gz: 7b4ce3a0448fb5f7fb2ce89f4df7c5aa62b0aa15
4
+ data.tar.gz: 3a2ade18b22f23cf8ea4499f0a6f989f7baf505a
5
5
  SHA512:
6
- metadata.gz: e791b972f66d6546af3214cbe409777845470c0e66a4267a6940b682bb82a16528d1923d6e6148e7470120d128ff0005de981eb7035d55c928e9b29302f3aa3e
7
- data.tar.gz: 87b9229b72c71a0a1dcaac72fad05bf48cd434a13485708c39e9482b0fd7a571ab2ec63be4b4891fd20c3445017f2d8532103d3898e12d5a924e294c1ccfbc49
6
+ metadata.gz: 7f44cb1c67172f7552222a5dababde253b6ac7e757d27f8651ceeb29c678ae0d7e9202135c52135f707579d18607653608b7b4fc089230f7b5a2bde51734a6be
7
+ data.tar.gz: 6d3ad2ed6d8e8ccea838f6b21c1d518be2409a1abaee5a1d857d9f8b59fa21846815fd78d3c269f36bdbcfa28d6fc5396dff56738d207bdbce821d5f803dc47e
data/README.rdoc CHANGED
@@ -15,4 +15,12 @@ MyForum::Engine.use_custom_user_model = true
15
15
 
16
16
  http://w3facility.org/question/ruby-regex-for-stripping-bbcode/
17
17
 
18
- gem build tag_echidna.gemspec
18
+ gem build tag_echidna.gemspec
19
+
20
+ https://github.com/cpjolicoeur/bb-ruby
21
+ http://fortawesome.github.io/Font-Awesome/icons/#text-editor
22
+
23
+
24
+ Used plugins:
25
+
26
+ http://madapaja.github.io/jquery.selection/
@@ -12,5 +12,8 @@
12
12
  //
13
13
  //= require jquery
14
14
  //= require jquery_ujs
15
+ //= require bootstrap-sprockets
15
16
  //= require bootstrap
17
+ //= jquery.selection.js
18
+ //= jquery.scrollTo.min.js
16
19
  //= require_tree .
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Copyright (c) 2007-2015 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
3
+ * Licensed under MIT
4
+ * @author Ariel Flesler
5
+ * @version 2.1.2
6
+ */
7
+ ;(function(f){"use strict";"function"===typeof define&&define.amd?define(["jquery"],f):"undefined"!==typeof module&&module.exports?module.exports=f(require("jquery")):f(jQuery)})(function($){"use strict";function n(a){return!a.nodeName||-1!==$.inArray(a.nodeName.toLowerCase(),["iframe","#document","html","body"])}function h(a){return $.isFunction(a)||$.isPlainObject(a)?a:{top:a,left:a}}var p=$.scrollTo=function(a,d,b){return $(window).scrollTo(a,d,b)};p.defaults={axis:"xy",duration:0,limit:!0};$.fn.scrollTo=function(a,d,b){"object"=== typeof d&&(b=d,d=0);"function"===typeof b&&(b={onAfter:b});"max"===a&&(a=9E9);b=$.extend({},p.defaults,b);d=d||b.duration;var u=b.queue&&1<b.axis.length;u&&(d/=2);b.offset=h(b.offset);b.over=h(b.over);return this.each(function(){function k(a){var k=$.extend({},b,{queue:!0,duration:d,complete:a&&function(){a.call(q,e,b)}});r.animate(f,k)}if(null!==a){var l=n(this),q=l?this.contentWindow||window:this,r=$(q),e=a,f={},t;switch(typeof e){case "number":case "string":if(/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(e)){e= h(e);break}e=l?$(e):$(e,q);case "object":if(e.length===0)return;if(e.is||e.style)t=(e=$(e)).offset()}var v=$.isFunction(b.offset)&&b.offset(q,e)||b.offset;$.each(b.axis.split(""),function(a,c){var d="x"===c?"Left":"Top",m=d.toLowerCase(),g="scroll"+d,h=r[g](),n=p.max(q,c);t?(f[g]=t[m]+(l?0:h-r.offset()[m]),b.margin&&(f[g]-=parseInt(e.css("margin"+d),10)||0,f[g]-=parseInt(e.css("border"+d+"Width"),10)||0),f[g]+=v[m]||0,b.over[m]&&(f[g]+=e["x"===c?"width":"height"]()*b.over[m])):(d=e[m],f[g]=d.slice&& "%"===d.slice(-1)?parseFloat(d)/100*n:d);b.limit&&/^\d+$/.test(f[g])&&(f[g]=0>=f[g]?0:Math.min(f[g],n));!a&&1<b.axis.length&&(h===f[g]?f={}:u&&(k(b.onAfterFirst),f={}))});k(b.onAfter)}})};p.max=function(a,d){var b="x"===d?"Width":"Height",h="scroll"+b;if(!n(a))return a[h]-$(a)[b.toLowerCase()]();var b="client"+b,k=a.ownerDocument||a.document,l=k.documentElement,k=k.body;return Math.max(l[h],k[h])-Math.min(l[b],k[b])};$.Tween.propHooks.scrollLeft=$.Tween.propHooks.scrollTop={get:function(a){return $(a.elem)[a.prop]()}, set:function(a){var d=this.get(a);if(a.options.interrupt&&a._last&&a._last!==d)return $(a.elem).stop();var b=Math.round(a.now);d!==b&&($(a.elem)[a.prop](b),a._last=this.get(a))}};return p});
@@ -0,0 +1,354 @@
1
+ /*!
2
+ * jQuery.selection - jQuery Plugin
3
+ *
4
+ * Copyright (c) 2010-2014 IWASAKI Koji (@madapaja).
5
+ * http://blog.madapaja.net/
6
+ * Under The MIT License
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining
9
+ * a copy of this software and associated documentation files (the
10
+ * "Software"), to deal in the Software without restriction, including
11
+ * without limitation the rights to use, copy, modify, merge, publish,
12
+ * distribute, sublicense, and/or sell copies of the Software, and to
13
+ * permit persons to whom the Software is furnished to do so, subject to
14
+ * the following conditions:
15
+ *
16
+ * The above copyright notice and this permission notice shall be
17
+ * included in all copies or substantial portions of the Software.
18
+ *
19
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
+ */
27
+ (function($, win, doc) {
28
+ /**
29
+ * get caret status of the selection of the element
30
+ *
31
+ * @param {Element} element target DOM element
32
+ * @return {Object} return
33
+ * @return {String} return.text selected text
34
+ * @return {Number} return.start start position of the selection
35
+ * @return {Number} return.end end position of the selection
36
+ */
37
+ var _getCaretInfo = function(element){
38
+ var res = {
39
+ text: '',
40
+ start: 0,
41
+ end: 0
42
+ };
43
+
44
+ if (!element.value) {
45
+ /* no value or empty string */
46
+ return res;
47
+ }
48
+
49
+ try {
50
+ if (win.getSelection) {
51
+ /* except IE */
52
+ res.start = element.selectionStart;
53
+ res.end = element.selectionEnd;
54
+ res.text = element.value.slice(res.start, res.end);
55
+ } else if (doc.selection) {
56
+ /* for IE */
57
+ element.focus();
58
+
59
+ var range = doc.selection.createRange(),
60
+ range2 = doc.body.createTextRange();
61
+
62
+ res.text = range.text;
63
+
64
+ try {
65
+ range2.moveToElementText(element);
66
+ range2.setEndPoint('StartToStart', range);
67
+ } catch (e) {
68
+ range2 = element.createTextRange();
69
+ range2.setEndPoint('StartToStart', range);
70
+ }
71
+
72
+ res.start = element.value.length - range2.text.length;
73
+ res.end = res.start + range.text.length;
74
+ }
75
+ } catch (e) {
76
+ /* give up */
77
+ }
78
+
79
+ return res;
80
+ };
81
+
82
+ /**
83
+ * caret operation for the element
84
+ * @type {Object}
85
+ */
86
+ var _CaretOperation = {
87
+ /**
88
+ * get caret position
89
+ *
90
+ * @param {Element} element target element
91
+ * @return {Object} return
92
+ * @return {Number} return.start start position for the selection
93
+ * @return {Number} return.end end position for the selection
94
+ */
95
+ getPos: function(element) {
96
+ var tmp = _getCaretInfo(element);
97
+ return {start: tmp.start, end: tmp.end};
98
+ },
99
+
100
+ /**
101
+ * set caret position
102
+ *
103
+ * @param {Element} element target element
104
+ * @param {Object} toRange caret position
105
+ * @param {Number} toRange.start start position for the selection
106
+ * @param {Number} toRange.end end position for the selection
107
+ * @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
108
+ */
109
+ setPos: function(element, toRange, caret) {
110
+ caret = this._caretMode(caret);
111
+
112
+ if (caret === 'start') {
113
+ toRange.end = toRange.start;
114
+ } else if (caret === 'end') {
115
+ toRange.start = toRange.end;
116
+ }
117
+
118
+ element.focus();
119
+ try {
120
+ if (element.createTextRange) {
121
+ var range = element.createTextRange();
122
+
123
+ if (win.navigator.userAgent.toLowerCase().indexOf("msie") >= 0) {
124
+ toRange.start = element.value.substr(0, toRange.start).replace(/\r/g, '').length;
125
+ toRange.end = element.value.substr(0, toRange.end).replace(/\r/g, '').length;
126
+ }
127
+
128
+ range.collapse(true);
129
+ range.moveStart('character', toRange.start);
130
+ range.moveEnd('character', toRange.end - toRange.start);
131
+
132
+ range.select();
133
+ } else if (element.setSelectionRange) {
134
+ element.setSelectionRange(toRange.start, toRange.end);
135
+ }
136
+ } catch (e) {
137
+ /* give up */
138
+ }
139
+ },
140
+
141
+ /**
142
+ * get selected text
143
+ *
144
+ * @param {Element} element target element
145
+ * @return {String} return selected text
146
+ */
147
+ getText: function(element) {
148
+ return _getCaretInfo(element).text;
149
+ },
150
+
151
+ /**
152
+ * get caret mode
153
+ *
154
+ * @param {String} caret caret mode
155
+ * @return {String} return any of the following: "keep" | "start" | "end"
156
+ */
157
+ _caretMode: function(caret) {
158
+ caret = caret || "keep";
159
+ if (caret === false) {
160
+ caret = 'end';
161
+ }
162
+
163
+ switch (caret) {
164
+ case 'keep':
165
+ case 'start':
166
+ case 'end':
167
+ break;
168
+
169
+ default:
170
+ caret = 'keep';
171
+ }
172
+
173
+ return caret;
174
+ },
175
+
176
+ /**
177
+ * replace selected text
178
+ *
179
+ * @param {Element} element target element
180
+ * @param {String} text replacement text
181
+ * @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
182
+ */
183
+ replace: function(element, text, caret) {
184
+ var tmp = _getCaretInfo(element),
185
+ orig = element.value,
186
+ pos = $(element).scrollTop(),
187
+ range = {start: tmp.start, end: tmp.start + text.length};
188
+
189
+ element.value = orig.substr(0, tmp.start) + text + orig.substr(tmp.end);
190
+
191
+ $(element).scrollTop(pos);
192
+ this.setPos(element, range, caret);
193
+ },
194
+
195
+ /**
196
+ * insert before the selected text
197
+ *
198
+ * @param {Element} element target element
199
+ * @param {String} text insertion text
200
+ * @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
201
+ */
202
+ insertBefore: function(element, text, caret) {
203
+ var tmp = _getCaretInfo(element),
204
+ orig = element.value,
205
+ pos = $(element).scrollTop(),
206
+ range = {start: tmp.start + text.length, end: tmp.end + text.length};
207
+
208
+ element.value = orig.substr(0, tmp.start) + text + orig.substr(tmp.start);
209
+
210
+ $(element).scrollTop(pos);
211
+ this.setPos(element, range, caret);
212
+ },
213
+
214
+ /**
215
+ * insert after the selected text
216
+ *
217
+ * @param {Element} element target element
218
+ * @param {String} text insertion text
219
+ * @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
220
+ */
221
+ insertAfter: function(element, text, caret) {
222
+ var tmp = _getCaretInfo(element),
223
+ orig = element.value,
224
+ pos = $(element).scrollTop(),
225
+ range = {start: tmp.start, end: tmp.end};
226
+
227
+ element.value = orig.substr(0, tmp.end) + text + orig.substr(tmp.end);
228
+
229
+ $(element).scrollTop(pos);
230
+ this.setPos(element, range, caret);
231
+ }
232
+ };
233
+
234
+ /* add jQuery.selection */
235
+ $.extend({
236
+ /**
237
+ * get selected text on the window
238
+ *
239
+ * @param {String} mode selection mode: any of the following: "text" | "html"
240
+ * @return {String} return
241
+ */
242
+ selection: function(mode) {
243
+ var getText = ((mode || 'text').toLowerCase() === 'text');
244
+
245
+ try {
246
+ if (win.getSelection) {
247
+ if (getText) {
248
+ // get text
249
+ return win.getSelection().toString();
250
+ } else {
251
+ // get html
252
+ var sel = win.getSelection(), range;
253
+
254
+ if (sel.getRangeAt) {
255
+ range = sel.getRangeAt(0);
256
+ } else {
257
+ range = doc.createRange();
258
+ range.setStart(sel.anchorNode, sel.anchorOffset);
259
+ range.setEnd(sel.focusNode, sel.focusOffset);
260
+ }
261
+
262
+ return $('<div></div>').append(range.cloneContents()).html();
263
+ }
264
+ } else if (doc.selection) {
265
+ if (getText) {
266
+ // get text
267
+ return doc.selection.createRange().text;
268
+ } else {
269
+ // get html
270
+ return doc.selection.createRange().htmlText;
271
+ }
272
+ }
273
+ } catch (e) {
274
+ /* give up */
275
+ }
276
+
277
+ return '';
278
+ }
279
+ });
280
+
281
+ /* add selection */
282
+ $.fn.extend({
283
+ selection: function(mode, opts) {
284
+ opts = opts || {};
285
+
286
+ switch (mode) {
287
+ /**
288
+ * selection('getPos')
289
+ * get caret position
290
+ *
291
+ * @return {Object} return
292
+ * @return {Number} return.start start position for the selection
293
+ * @return {Number} return.end end position for the selection
294
+ */
295
+ case 'getPos':
296
+ return _CaretOperation.getPos(this[0]);
297
+
298
+ /**
299
+ * selection('setPos', opts)
300
+ * set caret position
301
+ *
302
+ * @param {Number} opts.start start position for the selection
303
+ * @param {Number} opts.end end position for the selection
304
+ */
305
+ case 'setPos':
306
+ return this.each(function() {
307
+ _CaretOperation.setPos(this, opts);
308
+ });
309
+
310
+ /**
311
+ * selection('replace', opts)
312
+ * replace the selected text
313
+ *
314
+ * @param {String} opts.text replacement text
315
+ * @param {String} opts.caret caret mode: any of the following: "keep" | "start" | "end"
316
+ */
317
+ case 'replace':
318
+ return this.each(function() {
319
+ _CaretOperation.replace(this, opts.text, opts.caret);
320
+ });
321
+
322
+ /**
323
+ * selection('insert', opts)
324
+ * insert before/after the selected text
325
+ *
326
+ * @param {String} opts.text insertion text
327
+ * @param {String} opts.caret caret mode: any of the following: "keep" | "start" | "end"
328
+ * @param {String} opts.mode insertion mode: any of the following: "before" | "after"
329
+ */
330
+ case 'insert':
331
+ return this.each(function() {
332
+ if (opts.mode === 'before') {
333
+ _CaretOperation.insertBefore(this, opts.text, opts.caret);
334
+ } else {
335
+ _CaretOperation.insertAfter(this, opts.text, opts.caret);
336
+ }
337
+ });
338
+
339
+ /**
340
+ * selection('get')
341
+ * get selected text
342
+ *
343
+ * @return {String} return
344
+ */
345
+ case 'get':
346
+ /* falls through */
347
+ default:
348
+ return _CaretOperation.getText(this[0]);
349
+ }
350
+
351
+ return this;
352
+ }
353
+ });
354
+ })(jQuery, window, window.document);
@@ -1,4 +1,42 @@
1
1
  ready = ->
2
+ # BBCode editor
3
+ $('.text-editor-buttons').click (event) ->
4
+ return false if !$(event.target).is('a') and !$(event.target).is('i')
5
+
6
+ event.preventDefault()
7
+
8
+ apply_to = $('.text-editor-buttons').data('apply-to')
9
+ apply_to = $('#' + apply_to)
10
+ button = if $(event.target).is('a') then $(event.target).find('i') else $(event.target)
11
+ action = button.attr('class').replace('fa fa-', '');
12
+
13
+ text = apply_to.val()
14
+
15
+ switch action
16
+ when 'bold'
17
+ bbcode = '[b] [/b]'
18
+ when 'italic'
19
+ bbcode = '[i] [/i]'
20
+ when 'strikethrough'
21
+ bbcode = '[s] [/s]'
22
+ when 'underline'
23
+ bbcode = '[u] [/u]'
24
+ when 'link', 'video-camera', 'camera-retro'
25
+ $('#add_photo').modal()
26
+
27
+ else
28
+ bbcode = ''
29
+ console.log 'Unknown tag'
30
+
31
+ if bbcode
32
+ if (selected_text = apply_to.selection()).length > 0
33
+ open_tag = bbcode.split(' ')[0]
34
+ close_tag = bbcode.split(' ')[1]
35
+ apply_to.selection('replace', { text: open_tag + selected_text + close_tag })
36
+ else
37
+ apply_to.val(text + bbcode + ' ')
38
+
39
+ # Autocomplete
2
40
  $('.autocomplete').keyup (elm) ->
3
41
  ajax_url = $(@).data('autocomplete-path')
4
42
  return unless ajax_url
@@ -16,6 +16,8 @@
16
16
 
17
17
  @import "bootstrap-sprockets";
18
18
  @import "bootstrap";
19
+ @import "font-awesome-sprockets";
20
+ @import "font-awesome";
19
21
 
20
22
  .buttons_for_new_topic {
21
23
  margin-top: 10px;
@@ -50,4 +52,14 @@
50
52
  }
51
53
  }
52
54
 
55
+ }
56
+ .text-editor-buttons {
57
+ margin-bottom: -1px;
58
+
59
+ .btn-group.smiles {
60
+ img {
61
+ max-width: 16px;
62
+ height: 16px;
63
+ }
64
+ }
53
65
  }
@@ -24,6 +24,11 @@
24
24
  line-height: 36px;
25
25
  margin: 0;
26
26
  background-color: #4e61a8;
27
+
28
+ .post_number a {
29
+ color: #fff;
30
+ font-size: 9px;
31
+ }
27
32
  }
28
33
 
29
34
  .created_at {
@@ -10,3 +10,7 @@
10
10
  margin-right: 8px;
11
11
  }
12
12
  }
13
+
14
+ textarea.quick-answer {
15
+ height: 100px !important;
16
+ }
@@ -63,4 +63,5 @@
63
63
 
64
64
  #online_users {
65
65
  margin: 20px 0;
66
+ font-size: 10px
66
67
  }
@@ -3,6 +3,8 @@ module MyForum
3
3
 
4
4
  before_filter :user_activity
5
5
 
6
+ helper_method :attachment_img_path
7
+
6
8
  def authenticate_user!
7
9
  redirect_to admin_signin_path unless current_user
8
10
  end
@@ -52,5 +54,10 @@ module MyForum
52
54
 
53
55
  redirect_to root_path if (category_user_groups.map(&:name) & current_user_groups).blank?
54
56
  end
57
+
58
+ def attachment_img_path(attachment_id)
59
+ attachment = Attachment.find_by_id(attachment_id.to_i)
60
+ File.join(Attachment::UPLOAD_PATH, attachment.user_id.to_s, attachment.file_name)
61
+ end
55
62
  end
56
63
  end
@@ -0,0 +1,23 @@
1
+ require_dependency "my_forum/application_controller"
2
+
3
+ module MyForum
4
+ class AttachmentsController < ApplicationController
5
+
6
+ def create
7
+ upload_path = File.join(Attachment::UPLOAD_PATH, current_user.id.to_s)
8
+
9
+ # Create dir of not exists
10
+ FileUtils::mkdir_p upload_path
11
+
12
+ uploaded_io = params[:file]
13
+ File.open(File.join(upload_path, uploaded_io.original_filename), 'wb') do |file|
14
+ file.write(uploaded_io.read)
15
+ end
16
+
17
+ attachment = Attachment.create(user_id: current_user.id, file_name: uploaded_io.original_filename)
18
+
19
+ render json: { success: true, attachment_id: attachment.id }
20
+ end
21
+
22
+ end
23
+ end
@@ -6,7 +6,7 @@ module MyForum
6
6
 
7
7
  def show
8
8
  check_access_permissions(@forum)
9
- @forum_topics = @forum.topics_with_latest_post_info(page: params[:page], per_page: 30)
9
+ @forum_topics = @forum.topics_with_latest_post_info(page: params[:page], per_page: Post::PER_PAGE)
10
10
  end
11
11
 
12
12
  def unread_topics
@@ -0,0 +1,7 @@
1
+ require_dependency "my_forum/application_controller"
2
+
3
+ module MyForum
4
+ class ImagesController < ApplicationController
5
+
6
+ end
7
+ end
@@ -9,11 +9,25 @@ module MyForum
9
9
  post = @topic.posts.build(post_params)
10
10
  post.user = current_user
11
11
  post.save
12
- redirect_to forum_topic_path(@forum, @topic)
12
+
13
+ process_attachments(post)
14
+
15
+ last_page = @topic.posts.count / Post::PER_PAGE
16
+ last_page = 1 if last_page == 0
17
+ redirect_to forum_topic_path(@forum, @topic, page: last_page)
13
18
  end
14
19
 
15
20
  private
16
21
 
22
+ def process_attachments(post)
23
+ return unless matches = post_params.to_s.match(/\[attachment=([0-9]+)\]/i)
24
+
25
+ matches.captures.map(&:to_i).each do |attachment_id|
26
+ attachment = Attachment.where(id: attachment_id).first
27
+ attachment.update(post: post) if attachment and post.user == attachment.user
28
+ end
29
+ end
30
+
17
31
  def find_topic
18
32
  @topic = Topic.find(params[:topic_id])
19
33
  end
@@ -6,7 +6,7 @@ module MyForum
6
6
  before_filter :only_for_registered_users
7
7
 
8
8
  def index
9
- @private_messages = PrivateMessage.inbox_for(current_user)
9
+ @private_messages = PrivateMessage.inbox_for(current_user).paginate(per_page: 16, page: params[:page])
10
10
  render :inbox
11
11
  end
12
12
 
@@ -27,6 +27,9 @@ module MyForum
27
27
 
28
28
  topic.save
29
29
  post.save
30
+
31
+ topic.mark_as_read(current_user, post)
32
+
30
33
  redirect_to forum_path(@forum)
31
34
  end
32
35
 
@@ -9,16 +9,24 @@ module MyForum
9
9
  text.gsub!(/\[img\]/i, '<img src="')
10
10
  text.gsub!(/\[\/img\]/i, '" />')
11
11
 
12
+ # Youtube
13
+ text.gsub!(/(http|www)(.*youtu*.+\/)(watch\?v=|embed\/watch\?|\/)(?<video_code>[a-z1-9]+)/i) do |match|
14
+ "<iframe width='560' height='315' src='https://www.youtube.com/embed/#{$~[:video_code]}' frameborder='0' allowfullscreen></iframe>"
15
+ end
16
+
17
+ # Attachments
18
+ text.gsub!(/\[attachment=([0-9]+)\]/i) do |match|
19
+ "<img src='#{attachment_img_path($1)}' />"
20
+ end
21
+
12
22
  # Bold text
13
- text.gsub!(/\[b\]/i, '<strong>')
14
- text.gsub!(/\[\/b\]/i, '</strong>')
23
+ text.gsub!(/(\[b\])(?<bold_text>.*)(\[\/b\])/i) { |match| "<strong>#{$1}</strong>" }
15
24
 
16
- text.gsub!(/\[i\]/i, '<i>')
17
- text.gsub!(/\[\/i\]/i, '</i>')
25
+ # Italic
26
+ text.gsub!(/(\[i\])(?<italic_text>.*)(\[\/i\])/i) { |match| "<i>#{$1}</i>" }
18
27
 
19
28
  # Cut
20
- text.gsub!(/\[cut\]/i, '<pre>')
21
- text.gsub!(/\[\/cut\]/i, '</pre>')
29
+ text.gsub!(/(\[cut\])(?<cut_text>.*)(\[\/cut\])/i) { |match| "<pre>#{$1}</pre>" }
22
30
 
23
31
  # Color
24
32
  text.gsub!(/\[color=(.*?)\](.*?)\[\/color\]/i) { "<span style='color: #{$1}'>#{$2}</span>" }
@@ -29,6 +37,7 @@ module MyForum
29
37
  # Quote
30
38
  text.gsub!(/\[quote author=(.*?) link=(.*?) date=(.*?)\]/i) { bbquote(author: $1, date: $3) }
31
39
  text.gsub!(/\[\/quote\]/i, '</div>')
40
+ text.gsub!(/\[quote(.*)\]/i, "<div class='bbqoute'>")
32
41
 
33
42
  # Link
34
43
  text.gsub!(/\[url=(.*?)\](.*?)\[\/url\]/i) { "<a href='#{$1}'>#{$2}</a>" }