my_forum 0.0.1.beta2 → 0.0.1.beta3

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 (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>" }