##// END OF EJS Templates
Added metadata to the gallery. Added links to search the image by the online search engines
neko259 -
r460:dce9cedd 1.5-dev
parent child Browse files
Show More
@@ -1,29 +1,33 b''
1 .ui-button {
1 .ui-button {
2 display: none;
2 display: none;
3 }
3 }
4
4
5 .ui-dialog-content {
5 .ui-dialog-content {
6 padding: 0;
6 padding: 0;
7 min-height: 0;
7 min-height: 0;
8 }
8 }
9
9
10 .mark_btn {
10 .mark_btn {
11 cursor: pointer;
11 cursor: pointer;
12 }
12 }
13
13
14 .img-full {
14 .img-full {
15 position: fixed;
15 position: fixed;
16 z-index: 9999;
16 z-index: 9999;
17 background-color: #CCC;
17 background-color: #CCC;
18 border: 1px solid #000;
18 border: 1px solid #000;
19 cursor: pointer;
19 cursor: pointer;
20 }
20 }
21
21
22 .strikethrough {
22 .strikethrough {
23 text-decoration: line-through;
23 text-decoration: line-through;
24 }
24 }
25
25
26 .post_preview {
26 .post_preview {
27 z-index: 300;
27 z-index: 300;
28 position:absolute;
28 position:absolute;
29 }
29 }
30
31 .gallery_image {
32 display: inline-block;
33 } No newline at end of file
@@ -1,361 +1,368 b''
1 html {
1 html {
2 background: #555;
2 background: #555;
3 color: #ffffff;
3 color: #ffffff;
4 }
4 }
5
5
6 #admin_panel {
6 #admin_panel {
7 background: #FF0000;
7 background: #FF0000;
8 color: #00FF00
8 color: #00FF00
9 }
9 }
10
10
11 .input_field {
11 .input_field {
12
12
13 }
13 }
14
14
15 .input_field_name {
15 .input_field_name {
16
16
17 }
17 }
18
18
19 .input_field_error {
19 .input_field_error {
20 color: #FF0000;
20 color: #FF0000;
21 }
21 }
22
22
23 .title {
23 .title {
24 font-weight: bold;
24 font-weight: bold;
25 color: #ffcc00;
25 color: #ffcc00;
26 font-size: 2ex;
26 font-size: 2ex;
27 }
27 }
28
28
29 .link, a {
29 .link, a {
30 color: #afdcec;
30 color: #afdcec;
31 }
31 }
32
32
33 .block {
33 .block {
34 display: inline-block;
34 display: inline-block;
35 vertical-align: top;
35 vertical-align: top;
36 }
36 }
37
37
38 .tag {
38 .tag {
39 color: #b4cfec;
39 color: #b4cfec;
40 }
40 }
41
41
42 .post_id {
42 .post_id {
43 color: #fff380;
43 color: #fff380;
44 }
44 }
45
45
46 .post, .dead_post, #posts-table {
46 .post, .dead_post, #posts-table {
47 background: #333;
47 background: #333;
48 margin: 5px;
48 margin: 5px;
49 padding: 10px;
49 padding: 10px;
50 border: solid 1px #888;
50 border: solid 1px #888;
51 clear: left;
51 clear: left;
52 word-wrap: break-word;
52 word-wrap: break-word;
53 }
53 }
54
54
55 .metadata {
55 .metadata {
56 padding-top: 5px;
56 padding-top: 5px;
57 margin-top: 10px;
57 margin-top: 10px;
58 border-top: solid 1px #666;
58 border-top: solid 1px #666;
59 font-size: 0.9em;
59 font-size: 0.9em;
60 color: #ddd;
60 color: #ddd;
61 }
61 }
62
62
63 .navigation_panel, .tag_info {
63 .navigation_panel, .tag_info {
64 background: #444;
64 background: #444;
65 margin: 5px;
65 margin: 5px;
66 padding: 10px;
66 padding: 10px;
67 border: solid 1px #888;
67 border: solid 1px #888;
68 color: #eee;
68 color: #eee;
69 }
69 }
70
70
71 .navigation_panel .link {
71 .navigation_panel .link {
72 border-right: 1px solid #fff;
72 border-right: 1px solid #fff;
73 font-weight: bold;
73 font-weight: bold;
74 margin-right: 1ex;
74 margin-right: 1ex;
75 padding-right: 1ex;
75 padding-right: 1ex;
76 }
76 }
77 .navigation_panel .link:last-child {
77 .navigation_panel .link:last-child {
78 border-left: 1px solid #fff;
78 border-left: 1px solid #fff;
79 border-right: none;
79 border-right: none;
80 float: right;
80 float: right;
81 margin-left: 1ex;
81 margin-left: 1ex;
82 margin-right: 0;
82 margin-right: 0;
83 padding-left: 1ex;
83 padding-left: 1ex;
84 padding-right: 0;
84 padding-right: 0;
85 }
85 }
86
86
87 .navigation_panel::after, .post::after {
87 .navigation_panel::after, .post::after {
88 clear: both;
88 clear: both;
89 content: ".";
89 content: ".";
90 display: block;
90 display: block;
91 height: 0;
91 height: 0;
92 line-height: 0;
92 line-height: 0;
93 visibility: hidden;
93 visibility: hidden;
94 }
94 }
95
95
96 p {
96 p {
97 margin-top: .5em;
97 margin-top: .5em;
98 margin-bottom: .5em;
98 margin-bottom: .5em;
99 }
99 }
100
100
101 .post-form-w {
101 .post-form-w {
102 display: table;
102 display: table;
103 background: #333344;
103 background: #333344;
104 border: solid 1px #888;
104 border: solid 1px #888;
105 color: #fff;
105 color: #fff;
106 padding: 10px;
106 padding: 10px;
107 margin: 5px;
107 margin: 5px;
108 }
108 }
109
109
110 .form-row {
110 .form-row {
111 display: table-row;
111 display: table-row;
112 }
112 }
113
113
114 .form-label, .form-input, .form-errors {
114 .form-label, .form-input, .form-errors {
115 display: table-cell;
115 display: table-cell;
116 }
116 }
117
117
118 .form-label {
118 .form-label {
119 padding: .25em 1ex .25em 0;
119 padding: .25em 1ex .25em 0;
120 vertical-align: top;
120 vertical-align: top;
121 }
121 }
122
122
123 .form-input {
123 .form-input {
124 padding: .25em 0;
124 padding: .25em 0;
125 }
125 }
126
126
127 .form-errors {
127 .form-errors {
128 font-weight: bolder;
128 font-weight: bolder;
129 vertical-align: middle;
129 vertical-align: middle;
130 }
130 }
131
131
132 .post-form input, .post-form textarea {
132 .post-form input, .post-form textarea {
133 background: #333;
133 background: #333;
134 color: #fff;
134 color: #fff;
135 border: solid 1px;
135 border: solid 1px;
136 padding: 0;
136 padding: 0;
137 width: 100%;
137 width: 100%;
138 font: medium sans;
138 font: medium sans;
139 }
139 }
140
140
141 .form-submit {
141 .form-submit {
142 display: table;
142 display: table;
143 margin-bottom: 1ex;
143 margin-bottom: 1ex;
144 }
144 }
145
145
146 .form-title {
146 .form-title {
147 font-weight: bold;
147 font-weight: bold;
148 font-size: 2.5ex;
148 font-size: 2.5ex;
149 text-decoration: underline;
149 text-decoration: underline;
150 }
150 }
151
151
152 input[type="submit"] {
152 input[type="submit"] {
153 background: #222;
153 background: #222;
154 border: solid 2px #fff;
154 border: solid 2px #fff;
155 color: #fff;
155 color: #fff;
156 padding: 0.5ex;
156 padding: 0.5ex;
157 }
157 }
158
158
159 input[type="submit"]:hover {
159 input[type="submit"]:hover {
160 background: #060;
160 background: #060;
161 }
161 }
162
162
163 blockquote {
163 blockquote {
164 border-left: solid 2px;
164 border-left: solid 2px;
165 padding-left: 5px;
165 padding-left: 5px;
166 color: #B1FB17;
166 color: #B1FB17;
167 margin: 0;
167 margin: 0;
168 }
168 }
169
169
170 .post > .image {
170 .post > .image {
171 float: left;
171 float: left;
172 margin: 0 1ex .5ex 0;
172 margin: 0 1ex .5ex 0;
173 min-width: 1px;
173 min-width: 1px;
174 text-align: center;
174 text-align: center;
175 display: table-row;
175 display: table-row;
176 }
176 }
177
177
178 .post > .metadata {
178 .post > .metadata {
179 clear: left;
179 clear: left;
180 }
180 }
181
181
182 .get {
182 .get {
183 font-weight: bold;
183 font-weight: bold;
184 color: #d55;
184 color: #d55;
185 }
185 }
186
186
187 * {
187 * {
188 text-decoration: none;
188 text-decoration: none;
189 }
189 }
190
190
191 .dead_post {
191 .dead_post {
192 background-color: #442222;
192 background-color: #442222;
193 }
193 }
194
194
195 .mark_btn {
195 .mark_btn {
196 border: 1px solid;
196 border: 1px solid;
197 min-width: 2ex;
197 min-width: 2ex;
198 padding: 2px 2ex;
198 padding: 2px 2ex;
199 }
199 }
200
200
201 .mark_btn:hover {
201 .mark_btn:hover {
202 background: #555;
202 background: #555;
203 }
203 }
204
204
205 .quote {
205 .quote {
206 color: #92cf38;
206 color: #92cf38;
207 font-style: italic;
207 font-style: italic;
208 }
208 }
209
209
210 .spoiler {
210 .spoiler {
211 background: white;
211 background: white;
212 color: white;
212 color: white;
213 }
213 }
214
214
215 .spoiler:hover {
215 .spoiler:hover {
216 color: black;
216 color: black;
217 }
217 }
218
218
219 .comment {
219 .comment {
220 color: #eb2;
220 color: #eb2;
221 font-style: italic;
221 font-style: italic;
222 }
222 }
223
223
224 a:hover {
224 a:hover {
225 text-decoration: underline;
225 text-decoration: underline;
226 }
226 }
227
227
228 .last-replies {
228 .last-replies {
229 margin-left: 3ex;
229 margin-left: 3ex;
230 }
230 }
231
231
232 .thread {
232 .thread {
233 margin-bottom: 3ex;
233 margin-bottom: 3ex;
234 }
234 }
235
235
236 .post:target {
236 .post:target {
237 border: solid 2px white;
237 border: solid 2px white;
238 }
238 }
239
239
240 pre{
240 pre{
241 white-space:pre-wrap
241 white-space:pre-wrap
242 }
242 }
243
243
244 li {
244 li {
245 list-style-position: inside;
245 list-style-position: inside;
246 }
246 }
247
247
248 .fancybox-skin {
248 .fancybox-skin {
249 position: relative;
249 position: relative;
250 background-color: #fff;
250 background-color: #fff;
251 color: #ddd;
251 color: #ddd;
252 text-shadow: none;
252 text-shadow: none;
253 }
253 }
254
254
255 .fancybox-image {
255 .fancybox-image {
256 border: 1px solid black;
256 border: 1px solid black;
257 }
257 }
258
258
259 .image-mode-tab {
259 .image-mode-tab {
260 background: #444;
260 background: #444;
261 color: #eee;
261 color: #eee;
262 display: table;
262 display: table;
263 margin: 5px;
263 margin: 5px;
264 padding: 5px;
264 padding: 5px;
265 border: 1px solid #888;
265 border: 1px solid #888;
266 }
266 }
267
267
268 .image-mode-tab > label {
268 .image-mode-tab > label {
269 margin: 0 1ex;
269 margin: 0 1ex;
270 }
270 }
271
271
272 .image-mode-tab > label > input {
272 .image-mode-tab > label > input {
273 margin-right: .5ex;
273 margin-right: .5ex;
274 }
274 }
275
275
276 #posts-table {
276 #posts-table {
277 margin: 5px;
277 margin: 5px;
278 }
278 }
279
279
280 .tag_info {
280 .tag_info {
281 display: table;
281 display: table;
282 }
282 }
283
283
284 .tag_info > h2 {
284 .tag_info > h2 {
285 margin: 0;
285 margin: 0;
286 }
286 }
287
287
288 .post-info {
288 .post-info {
289 color: #ddd;
289 color: #ddd;
290 }
290 }
291
291
292 .moderator_info {
292 .moderator_info {
293 color: #e99d41;
293 color: #e99d41;
294 border: dashed 1px;
294 border: dashed 1px;
295 padding: 3px;
295 padding: 3px;
296 }
296 }
297
297
298 .refmap {
298 .refmap {
299 font-size: 0.9em;
299 font-size: 0.9em;
300 color: #ccc;
300 color: #ccc;
301 margin-top: 1em;
301 margin-top: 1em;
302 }
302 }
303
303
304 .fav {
304 .fav {
305 color: yellow;
305 color: yellow;
306 }
306 }
307
307
308 .not_fav {
308 .not_fav {
309 color: #ccc;
309 color: #ccc;
310 }
310 }
311
311
312 .role {
312 .role {
313 text-decoration: underline;
313 text-decoration: underline;
314 }
314 }
315
315
316 .form-email {
316 .form-email {
317 display: none;
317 display: none;
318 }
318 }
319
319
320 .footer {
320 .footer {
321 margin: 5px;
321 margin: 5px;
322 }
322 }
323
323
324 .bar-value {
324 .bar-value {
325 background: rgba(50, 55, 164, 0.45);
325 background: rgba(50, 55, 164, 0.45);
326 font-size: 0.9em;
326 font-size: 0.9em;
327 height: 1.5em;
327 height: 1.5em;
328 }
328 }
329
329
330 .bar-bg {
330 .bar-bg {
331 position: relative;
331 position: relative;
332 border: solid 1px #888;
332 border: solid 1px #888;
333 margin: 5px;
333 margin: 5px;
334 overflow: hidden;
334 overflow: hidden;
335 }
335 }
336
336
337 .bar-text {
337 .bar-text {
338 padding: 2px;
338 padding: 2px;
339 position: absolute;
339 position: absolute;
340 left: 0;
340 left: 0;
341 top: 0;
341 top: 0;
342 }
342 }
343
343
344 .page_link {
344 .page_link {
345 display: table;
345 display: table;
346 background: #444;
346 background: #444;
347 margin: 5px;
347 margin: 5px;
348 border: solid 1px #888;
348 border: solid 1px #888;
349 padding: 5px;
349 padding: 5px;
350 font-weight: bolder;
350 font-weight: bolder;
351 color: #eee;
351 color: #eee;
352 }
352 }
353
353
354 .skipped_replies {
354 .skipped_replies {
355 margin: 5px;
355 margin: 5px;
356 }
356 }
357
357
358 .current_page, .current_mode {
358 .current_page, .current_mode {
359 border: solid 1px #afdcec;
359 border: solid 1px #afdcec;
360 padding: 2px;
360 padding: 2px;
361 }
362
363 .gallery_image {
364 border: solid 1px;
365 padding: 0.5ex;
366 margin: 0.5ex;
367 text-align: center;
361 } No newline at end of file
368 }
@@ -1,353 +1,357 b''
1 * {
1 * {
2 font-size: inherit;
2 font-size: inherit;
3 margin: 0;
3 margin: 0;
4 padding: 0;
4 padding: 0;
5 }
5 }
6 html {
6 html {
7 background: #fff;
7 background: #fff;
8 color: #000;
8 color: #000;
9 font: medium sans-serif;
9 font: medium sans-serif;
10 }
10 }
11 a {
11 a {
12 color: inherit;
12 color: inherit;
13 text-decoration: underline;
13 text-decoration: underline;
14 }
14 }
15 li {
15 li {
16 list-style-position: inside;
16 list-style-position: inside;
17 }
17 }
18
18
19 #admin_panel {
19 #admin_panel {
20 background: #182F6F;
20 background: #182F6F;
21 color: #fff;
21 color: #fff;
22 padding: .5ex 1ex .5ex 1ex;
22 padding: .5ex 1ex .5ex 1ex;
23 }
23 }
24
24
25 .navigation_panel {
25 .navigation_panel {
26 background: #182F6F;
26 background: #182F6F;
27 color: #B4CFEC;
27 color: #B4CFEC;
28 margin-bottom: 1em;
28 margin-bottom: 1em;
29 padding: .5ex 1ex 1ex 1ex;
29 padding: .5ex 1ex 1ex 1ex;
30 }
30 }
31 .navigation_panel::after {
31 .navigation_panel::after {
32 clear: both;
32 clear: both;
33 content: ".";
33 content: ".";
34 display: block;
34 display: block;
35 height: 0;
35 height: 0;
36 line-height: 0;
36 line-height: 0;
37 visibility: hidden;
37 visibility: hidden;
38 }
38 }
39
39
40 .navigation_panel a:link, .navigation_panel a:visited, .navigation_panel a:hover {
40 .navigation_panel a:link, .navigation_panel a:visited, .navigation_panel a:hover {
41 text-decoration: none;
41 text-decoration: none;
42 }
42 }
43
43
44 .navigation_panel .link {
44 .navigation_panel .link {
45 border-right: 1px solid #fff;
45 border-right: 1px solid #fff;
46 color: #fff;
46 color: #fff;
47 font-weight: bold;
47 font-weight: bold;
48 margin-right: 1ex;
48 margin-right: 1ex;
49 padding-right: 1ex;
49 padding-right: 1ex;
50 }
50 }
51 .navigation_panel .link:last-child {
51 .navigation_panel .link:last-child {
52 border-left: 1px solid #fff;
52 border-left: 1px solid #fff;
53 border-right: none;
53 border-right: none;
54 float: right;
54 float: right;
55 margin-left: 1ex;
55 margin-left: 1ex;
56 margin-right: 0;
56 margin-right: 0;
57 padding-left: 1ex;
57 padding-left: 1ex;
58 padding-right: 0;
58 padding-right: 0;
59 }
59 }
60
60
61 .navigation_panel .tag {
61 .navigation_panel .tag {
62 color: #fff;
62 color: #fff;
63 }
63 }
64
64
65 .input_field {
65 .input_field {
66
66
67 }
67 }
68
68
69 .input_field_name {
69 .input_field_name {
70
70
71 }
71 }
72
72
73 .input_field_error {
73 .input_field_error {
74 color: #FF0000;
74 color: #FF0000;
75 }
75 }
76
76
77
77
78 .title {
78 .title {
79 color: #182F6F;
79 color: #182F6F;
80 font-weight: bold;
80 font-weight: bold;
81 }
81 }
82
82
83 .post-form-w {
83 .post-form-w {
84 background: #182F6F;
84 background: #182F6F;
85 border-radius: 1ex;
85 border-radius: 1ex;
86 color: #fff;
86 color: #fff;
87 margin: 1em 1ex;
87 margin: 1em 1ex;
88 padding: 1ex;
88 padding: 1ex;
89 }
89 }
90 .post-form {
90 .post-form {
91 display: table;
91 display: table;
92 border-collapse: collapse;
92 border-collapse: collapse;
93 width: 100%;
93 width: 100%;
94
94
95 }
95 }
96 .form-row {
96 .form-row {
97 display: table-row;
97 display: table-row;
98 }
98 }
99 .form-label, .form-input {
99 .form-label, .form-input {
100 display: table-cell;
100 display: table-cell;
101 vertical-align: top;
101 vertical-align: top;
102 }
102 }
103 .form-label {
103 .form-label {
104 padding: .25em 1ex .25em 0;
104 padding: .25em 1ex .25em 0;
105 }
105 }
106 .form-input {
106 .form-input {
107 padding: .25em 0;
107 padding: .25em 0;
108 }
108 }
109 .form-input > * {
109 .form-input > * {
110 background: #fff;
110 background: #fff;
111 color: #000;
111 color: #000;
112 border: none;
112 border: none;
113 padding: 0;
113 padding: 0;
114 resize: vertical;
114 resize: vertical;
115 width: 100%;
115 width: 100%;
116 }
116 }
117 .form-submit {
117 .form-submit {
118 border-bottom: 1px solid #666;
118 border-bottom: 1px solid #666;
119 margin-bottom: .5em;
119 margin-bottom: .5em;
120 padding-bottom: .5em;
120 padding-bottom: .5em;
121 }
121 }
122 .form-title {
122 .form-title {
123 font-weight: bold;
123 font-weight: bold;
124 margin-bottom: .5em;
124 margin-bottom: .5em;
125 }
125 }
126 .post-form .settings_item {
126 .post-form .settings_item {
127 margin: .5em 0;
127 margin: .5em 0;
128 }
128 }
129 .form-submit input {
129 .form-submit input {
130 margin-top: .5em;
130 margin-top: .5em;
131 padding: .2em 1ex;
131 padding: .2em 1ex;
132 }
132 }
133 .form-label {
133 .form-label {
134 text-align: right;
134 text-align: right;
135 }
135 }
136
136
137 .block {
137 .block {
138 display: inline-block;
138 display: inline-block;
139 vertical-align: top;
139 vertical-align: top;
140 }
140 }
141
141
142 .post_id {
142 .post_id {
143 color: #a00;
143 color: #a00;
144 }
144 }
145
145
146 .post {
146 .post {
147 clear: left;
147 clear: left;
148 margin: 0 1ex 1em 1ex;
148 margin: 0 1ex 1em 1ex;
149 overflow-x: auto;
149 overflow-x: auto;
150 word-wrap: break-word;
150 word-wrap: break-word;
151 background: #FFF;
151 background: #FFF;
152 padding: 1ex;
152 padding: 1ex;
153 border: 1px solid #666;
153 border: 1px solid #666;
154 box-shadow: 1px 1px 2px 1px #666;
154 box-shadow: 1px 1px 2px 1px #666;
155 }
155 }
156
156
157 #posts > .post:last-child {
157 #posts > .post:last-child {
158 border-bottom: none;
158 border-bottom: none;
159 padding-bottom: 0;
159 padding-bottom: 0;
160 }
160 }
161
161
162 .metadata {
162 .metadata {
163 background: #C0E4E8;
163 background: #C0E4E8;
164 border: 1px solid #7F9699;
164 border: 1px solid #7F9699;
165 border-radius: .4ex;
165 border-radius: .4ex;
166 display: table;
166 display: table;
167 margin-top: .5em;
167 margin-top: .5em;
168 padding: .4em;
168 padding: .4em;
169 }
169 }
170
170
171 .post ul, .post ol {
171 .post ul, .post ol {
172 margin: .5em 0 .5em 3ex;
172 margin: .5em 0 .5em 3ex;
173 }
173 }
174 .post li {
174 .post li {
175 margin: .2em 0;
175 margin: .2em 0;
176 }
176 }
177 .post p {
177 .post p {
178 margin: .5em 0;
178 margin: .5em 0;
179 }
179 }
180 .post blockquote {
180 .post blockquote {
181 border-left: 3px solid #182F6F;
181 border-left: 3px solid #182F6F;
182 margin: .5em 0 .5em 3ex;
182 margin: .5em 0 .5em 3ex;
183 padding-left: 1ex;
183 padding-left: 1ex;
184 }
184 }
185 .post blockquote > blockquote {
185 .post blockquote > blockquote {
186 padding-top: .1em;
186 padding-top: .1em;
187 }
187 }
188
188
189 .post > .image {
189 .post > .image {
190 float: left;
190 float: left;
191 margin-right: 1ex;
191 margin-right: 1ex;
192 }
192 }
193 .post > .metadata {
193 .post > .metadata {
194 clear: left;
194 clear: left;
195 }
195 }
196
196
197 .post > .message .get {
197 .post > .message .get {
198 color: #182F6F; font-weight: bold;
198 color: #182F6F; font-weight: bold;
199 }
199 }
200
200
201 .dead_post > .metadata {
201 .dead_post > .metadata {
202 background: #eee;
202 background: #eee;
203 }
203 }
204
204
205 .quote {
205 .quote {
206 color: #182F6F;
206 color: #182F6F;
207 }
207 }
208
208
209 .spoiler {
209 .spoiler {
210 background: black;
210 background: black;
211 color: black;
211 color: black;
212 }
212 }
213
213
214 .spoiler:hover {
214 .spoiler:hover {
215 background: #ffffff;
215 background: #ffffff;
216 }
216 }
217
217
218 .comment {
218 .comment {
219 color: #557055;
219 color: #557055;
220 }
220 }
221
221
222 .last-replies {
222 .last-replies {
223 margin-left: 6ex;
223 margin-left: 6ex;
224 }
224 }
225
225
226 .thread > .post > .message > .post-info {
226 .thread > .post > .message > .post-info {
227 border-bottom: 1px solid #ccc;
227 border-bottom: 1px solid #ccc;
228 padding-bottom: .5em;
228 padding-bottom: .5em;
229 }
229 }
230
230
231 :target .post_id {
231 :target .post_id {
232 background: #182F6F;
232 background: #182F6F;
233 color: #FFF;
233 color: #FFF;
234 text-decoration: none;
234 text-decoration: none;
235 }
235 }
236
236
237 .image-mode-tab {
237 .image-mode-tab {
238 background: #182F6F;
238 background: #182F6F;
239 color: #FFF;
239 color: #FFF;
240 display: table;
240 display: table;
241 margin: 1em auto 1em 0;
241 margin: 1em auto 1em 0;
242 padding: .2em .5ex;
242 padding: .2em .5ex;
243 }
243 }
244
244
245 .image-mode-tab > label {
245 .image-mode-tab > label {
246 margin: 0 1ex;
246 margin: 0 1ex;
247 }
247 }
248
248
249 .image-mode-tab > label > input {
249 .image-mode-tab > label > input {
250 margin-right: .5ex;
250 margin-right: .5ex;
251 }
251 }
252
252
253 .tag_info, .page_link {
253 .tag_info, .page_link {
254 margin: 1em 0;
254 margin: 1em 0;
255 text-align: center;
255 text-align: center;
256 }
256 }
257
257
258 .form-errors {
258 .form-errors {
259 margin-left: 1ex;
259 margin-left: 1ex;
260 }
260 }
261
261
262 .moderator_info {
262 .moderator_info {
263 font-weight: bold;
263 font-weight: bold;
264 float: right;
264 float: right;
265 }
265 }
266
266
267 .refmap {
267 .refmap {
268 border: 1px dashed #aaa;
268 border: 1px dashed #aaa;
269 padding: 0.5em;
269 padding: 0.5em;
270 display: table;
270 display: table;
271 }
271 }
272
272
273 .fav {
273 .fav {
274 color: blue;
274 color: blue;
275 }
275 }
276
276
277 .not_fav {
277 .not_fav {
278 color: #ccc;
278 color: #ccc;
279 }
279 }
280
280
281 .role {
281 .role {
282 text-decoration: underline;
282 text-decoration: underline;
283 }
283 }
284
284
285 .form-email {
285 .form-email {
286 display: none;
286 display: none;
287 }
287 }
288
288
289 .bar-value {
289 .bar-value {
290 background: #E3E7F2;
290 background: #E3E7F2;
291 padding: .1em 1ex;
291 padding: .1em 1ex;
292 moz-box-sizing: border-box;
292 moz-box-sizing: border-box;
293 box-sizing: border-box;
293 box-sizing: border-box;
294 height: 1.5em;
294 height: 1.5em;
295 }
295 }
296
296
297 .bar-bg {
297 .bar-bg {
298 background: #EA4649;
298 background: #EA4649;
299 border: 1px solid #666;
299 border: 1px solid #666;
300 margin: 0 1ex 1em 1ex;
300 margin: 0 1ex 1em 1ex;
301 position: relative;
301 position: relative;
302 overflow: hidden;
302 overflow: hidden;
303 }
303 }
304
304
305 .bar-text {
305 .bar-text {
306 padding: 2px;
306 padding: 2px;
307 position: absolute;
307 position: absolute;
308 left: 0;
308 left: 0;
309 top: 0;
309 top: 0;
310 }
310 }
311
311
312 .skipped_replies {
312 .skipped_replies {
313 margin: 1ex;
313 margin: 1ex;
314 }
314 }
315
315
316 #mark-panel {
316 #mark-panel {
317 background: #eee;
317 background: #eee;
318 border-bottom: 1px solid #182F6F;
318 border-bottom: 1px solid #182F6F;
319 }
319 }
320
320
321 .mark_btn {
321 .mark_btn {
322 display: inline-block;
322 display: inline-block;
323 padding: .2em 1ex;
323 padding: .2em 1ex;
324 border-left: 1px solid #182F6F;
324 border-left: 1px solid #182F6F;
325 }
325 }
326
326
327 .mark_btn:first-child {
327 .mark_btn:first-child {
328 border-left: none;
328 border-left: none;
329 }
329 }
330
330
331 .mark_btn:last-child {
331 .mark_btn:last-child {
332 border-right: 1px solid #182F6F;
332 border-right: 1px solid #182F6F;
333 }
333 }
334
334
335 .current_page {
335 .current_page {
336 border-bottom: 1px solid #FFF;
336 border-bottom: 1px solid #FFF;
337 padding: 0px 0.5ex;
337 padding: 0px 0.5ex;
338 }
338 }
339
339
340 .image-mode-tab a {
340 .image-mode-tab a {
341 text-decoration: none;
341 text-decoration: none;
342 }
342 }
343 .image-mode-tab .current_mode::before {
343 .image-mode-tab .current_mode::before {
344 content: "βœ“ ";
344 content: "βœ“ ";
345 padding: 0 0 0 .5ex;
345 padding: 0 0 0 .5ex;
346 color: #182F6F;
346 color: #182F6F;
347 background: #FFF;
347 background: #FFF;
348 }
348 }
349 .image-mode-tab .current_mode {
349 .image-mode-tab .current_mode {
350 padding: 0 .5ex 0 0;
350 padding: 0 .5ex 0 0;
351 color: #182F6F;
351 color: #182F6F;
352 background: #FFF;
352 background: #FFF;
353 }
354
355 .gallery_image_metadata {
356 margin-bottom: 1em;
353 } No newline at end of file
357 }
@@ -1,58 +1,66 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load cache %}
4 {% load cache %}
5 {% load static from staticfiles %}
5 {% load static from staticfiles %}
6 {% load board %}
6 {% load board %}
7
7
8 {% block head %}
8 {% block head %}
9 <title>Neboard - {{ thread.get_opening_post.get_title }}</title>
9 <title>Neboard - {{ thread.get_opening_post.get_title }}</title>
10 {% endblock %}
10 {% endblock %}
11
11
12 {% block content %}
12 {% block content %}
13 {% spaceless %}
13 {% spaceless %}
14 {% get_current_language as LANGUAGE_CODE %}
14 {% get_current_language as LANGUAGE_CODE %}
15
15
16 <script src="{% static 'js/thread.js' %}"></script>
16 <script src="{% static 'js/thread.js' %}"></script>
17
17
18 {% cache 600 thread_gallery_view thread.id thread.last_edit_time LANGUAGE_CODE %}
18 {% cache 600 thread_gallery_view thread.id thread.last_edit_time LANGUAGE_CODE %}
19 <div class="image-mode-tab">
19 <div class="image-mode-tab">
20 <a href="{% url 'thread' thread.get_opening_post.id %}">{% trans 'Normal mode' %}</a>,
20 <a href="{% url 'thread' thread.get_opening_post.id %}">{% trans 'Normal mode' %}</a>,
21 <a class="current_mode" href="{% url 'thread_mode' thread.get_opening_post.id 'gallery' %}">{% trans 'Gallery mode' %}</a>
21 <a class="current_mode" href="{% url 'thread_mode' thread.get_opening_post.id 'gallery' %}">{% trans 'Gallery mode' %}</a>
22 </div>
22 </div>
23
23
24 <div id="posts-table">
24 <div id="posts-table">
25 {% for post in thread.get_replies %}
25 {% for post in thread.get_replies %}
26 {% if post.image %}
26 {% if post.image %}
27 <div class="gallery_image">
28 <div>
27 <a
29 <a
28 class="thumb"
30 class="thumb"
29 href="{{ post.image.url }}"><img
31 href="{{ post.image.url }}"><img
30 src="{{ post.image.url_200x150 }}"
32 src="{{ post.image.url_200x150 }}"
31 alt="{{ post.id }}"
33 alt="{{ post.id }}"
32 width="{{ post.image_pre_width }}"
34 width="{{ post.image_pre_width }}"
33 height="{{ post.image_pre_height }}"
35 height="{{ post.image_pre_height }}"
34 data-width="{{ post.image_width }}"
36 data-width="{{ post.image_width }}"
35 data-height="{{ post.image_height }}"/>
37 data-height="{{ post.image_height }}"/>
36 </a>
38 </a>
39 </div>
40 <div class="gallery_image_metadata">
41 {{ post.image_width }}x{{ post.image_height }}
42 {% image_actions post.image.url request.get_host %}
43 </div>
44 </div>
37 {% endif %}
45 {% endif %}
38 {% endfor %}
46 {% endfor %}
39 </div>
47 </div>
40 {% endcache %}
48 {% endcache %}
41
49
42 {% endspaceless %}
50 {% endspaceless %}
43 {% endblock %}
51 {% endblock %}
44
52
45 {% block metapanel %}
53 {% block metapanel %}
46
54
47 {% get_current_language as LANGUAGE_CODE %}
55 {% get_current_language as LANGUAGE_CODE %}
48
56
49 <span class="metapanel" data-last-update="{{ last_update }}">
57 <span class="metapanel" data-last-update="{{ last_update }}">
50 {% cache 600 thread_meta thread.last_edit_time moderator LANGUAGE_CODE %}
58 {% cache 600 thread_meta thread.last_edit_time moderator LANGUAGE_CODE %}
51 <span id="reply-count">{{ thread.get_reply_count }}</span> {% trans 'replies' %},
59 <span id="reply-count">{{ thread.get_reply_count }}</span> {% trans 'replies' %},
52 <span id="image-count">{{ thread.get_images_count }}</span> {% trans 'images' %}.
60 <span id="image-count">{{ thread.get_images_count }}</span> {% trans 'images' %}.
53 {% trans 'Last update: ' %}{{ thread.last_edit_time }}
61 {% trans 'Last update: ' %}{{ thread.last_edit_time }}
54 [<a href="rss/">RSS</a>]
62 [<a href="rss/">RSS</a>]
55 {% endcache %}
63 {% endcache %}
56 </span>
64 </span>
57
65
58 {% endblock %}
66 {% endblock %}
@@ -1,23 +1,49 b''
1 from django.core.urlresolvers import reverse
1 from django.core.urlresolvers import reverse
2 from django.shortcuts import get_object_or_404
2 from django.shortcuts import get_object_or_404
3 from boards.models import Post
3 from boards.models import Post
4 from boards.views import thread
4 from boards.views import thread
5 from django import template
5 from django import template
6
6
7 register = template.Library()
7 register = template.Library()
8
8
9 actions = [
10 {
11 'name': 'google',
12 'link': 'http://google.com/searchbyimage?image_url=%s',
13 },
14 {
15 'name': 'iqdb',
16 'link': 'http://iqdb.org/?url=%s',
17 },
18 ]
19
9
20
10 @register.simple_tag(name='post_url')
21 @register.simple_tag(name='post_url')
11 def post_url(*args, **kwargs):
22 def post_url(*args, **kwargs):
12 post_id = args[0]
23 post_id = args[0]
13
24
14 post = get_object_or_404(Post, id=post_id)
25 post = get_object_or_404(Post, id=post_id)
15
26
16 if not post.is_opening():
27 if not post.is_opening():
17 link = reverse(thread, kwargs={
28 link = reverse(thread, kwargs={
18 'post_id': post.thread_new.get_opening_post().id}) + '#' + str(
29 'post_id': post.thread_new.get_opening_post().id}) + '#' + str(
19 post_id)
30 post_id)
20 else:
31 else:
21 link = reverse(thread, kwargs={'post_id': post_id})
32 link = reverse(thread, kwargs={'post_id': post_id})
22
33
23 return link
34 return link
35
36
37 @register.simple_tag(name='image_actions')
38 def image_actions(*args, **kwargs):
39 image_link = args[0]
40 if len(args) > 1:
41 image_link = 'http://' + args[1] + image_link # TODO https?
42
43 result = ''
44
45 for action in actions:
46 result += '[<a href="' + action['link'] % image_link + '">' + \
47 action['name'] + '</a>]'
48
49 return result
@@ -1,558 +1,559 b''
1 __author__ = 'neko259'
1 __author__ = 'neko259'
2
2
3 import hashlib
3 import hashlib
4 import string
4 import string
5 import time
5 import time
6 import re
6 import re
7
7
8 from django.core import serializers
8 from django.core import serializers
9 from django.core.urlresolvers import reverse
9 from django.core.urlresolvers import reverse
10 from django.http import HttpResponseRedirect
10 from django.http import HttpResponseRedirect, Http404
11 from django.http.response import HttpResponse
11 from django.http.response import HttpResponse
12 from django.template import RequestContext
12 from django.template import RequestContext
13 from django.shortcuts import render, redirect, get_object_or_404
13 from django.shortcuts import render, redirect, get_object_or_404
14 from django.utils import timezone
14 from django.utils import timezone
15 from django.db import transaction
15 from django.db import transaction
16 from django.views.decorators.cache import cache_page
16 from django.views.decorators.cache import cache_page
17 from django.views.i18n import javascript_catalog
17 from django.views.i18n import javascript_catalog
18
18
19 from boards import forms
19 from boards import forms
20 import boards
20 import boards
21 from boards import utils
21 from boards import utils
22 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
22 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
23 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
23 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
24 from boards.models import Post, Tag, Ban, User
24 from boards.models import Post, Tag, Ban, User
25 from boards.models.post import SETTING_MODERATE, REGEX_REPLY
25 from boards.models.post import SETTING_MODERATE, REGEX_REPLY
26 from boards.models.user import RANK_USER
26 from boards.models.user import RANK_USER
27 from boards import authors
27 from boards import authors
28 from boards.utils import get_client_ip
28 from boards.utils import get_client_ip
29 import neboard
29 import neboard
30
30
31
31
32 BAN_REASON_SPAM = 'Autoban: spam bot'
32 BAN_REASON_SPAM = 'Autoban: spam bot'
33 MODE_GALLERY = 'gallery'
34 MODE_NORMAL = 'normal'
33
35
34
36
35 def index(request, page=0):
37 def index(request, page=0):
36 context = _init_default_context(request)
38 context = _init_default_context(request)
37
39
38 if utils.need_include_captcha(request):
40 if utils.need_include_captcha(request):
39 threadFormClass = ThreadCaptchaForm
41 threadFormClass = ThreadCaptchaForm
40 kwargs = {'request': request}
42 kwargs = {'request': request}
41 else:
43 else:
42 threadFormClass = ThreadForm
44 threadFormClass = ThreadForm
43 kwargs = {}
45 kwargs = {}
44
46
45 if request.method == 'POST':
47 if request.method == 'POST':
46 form = threadFormClass(request.POST, request.FILES,
48 form = threadFormClass(request.POST, request.FILES,
47 error_class=PlainErrorList, **kwargs)
49 error_class=PlainErrorList, **kwargs)
48 form.session = request.session
50 form.session = request.session
49
51
50 if form.is_valid():
52 if form.is_valid():
51 return _new_post(request, form)
53 return _new_post(request, form)
52 if form.need_to_ban:
54 if form.need_to_ban:
53 # Ban user because he is suspected to be a bot
55 # Ban user because he is suspected to be a bot
54 _ban_current_user(request)
56 _ban_current_user(request)
55 else:
57 else:
56 form = threadFormClass(error_class=PlainErrorList, **kwargs)
58 form = threadFormClass(error_class=PlainErrorList, **kwargs)
57
59
58 threads = []
60 threads = []
59 for thread_to_show in Post.objects.get_threads(page=int(page)):
61 for thread_to_show in Post.objects.get_threads(page=int(page)):
60 threads.append(_get_template_thread(thread_to_show))
62 threads.append(_get_template_thread(thread_to_show))
61
63
62 # TODO Make this generic for tag and threads list pages
64 # TODO Make this generic for tag and threads list pages
63 context['threads'] = None if len(threads) == 0 else threads
65 context['threads'] = None if len(threads) == 0 else threads
64 context['form'] = form
66 context['form'] = form
65 context['current_page'] = int(page)
67 context['current_page'] = int(page)
66
68
67 page_count = Post.objects.get_thread_page_count()
69 page_count = Post.objects.get_thread_page_count()
68 context['pages'] = range(page_count)
70 context['pages'] = range(page_count)
69 page = int(page)
71 page = int(page)
70 if page < page_count - 1:
72 if page < page_count - 1:
71 context['next_page'] = str(page + 1)
73 context['next_page'] = str(page + 1)
72 if page > 0:
74 if page > 0:
73 context['prev_page'] = str(page - 1)
75 context['prev_page'] = str(page - 1)
74
76
75 return render(request, 'boards/posting_general.html',
77 return render(request, 'boards/posting_general.html',
76 context)
78 context)
77
79
78
80
79 @transaction.atomic
81 @transaction.atomic
80 def _new_post(request, form, opening_post=None):
82 def _new_post(request, form, opening_post=None):
81 """Add a new post (in thread or as a reply)."""
83 """Add a new post (in thread or as a reply)."""
82
84
83 ip = get_client_ip(request)
85 ip = get_client_ip(request)
84 is_banned = Ban.objects.filter(ip=ip).exists()
86 is_banned = Ban.objects.filter(ip=ip).exists()
85
87
86 if is_banned:
88 if is_banned:
87 return redirect(you_are_banned)
89 return redirect(you_are_banned)
88
90
89 data = form.cleaned_data
91 data = form.cleaned_data
90
92
91 title = data['title']
93 title = data['title']
92 text = data['text']
94 text = data['text']
93
95
94 text = _remove_invalid_links(text)
96 text = _remove_invalid_links(text)
95
97
96 if 'image' in data.keys():
98 if 'image' in data.keys():
97 image = data['image']
99 image = data['image']
98 else:
100 else:
99 image = None
101 image = None
100
102
101 tags = []
103 tags = []
102
104
103 if not opening_post:
105 if not opening_post:
104 tag_strings = data['tags']
106 tag_strings = data['tags']
105
107
106 if tag_strings:
108 if tag_strings:
107 tag_strings = tag_strings.split(' ')
109 tag_strings = tag_strings.split(' ')
108 for tag_name in tag_strings:
110 for tag_name in tag_strings:
109 tag_name = string.lower(tag_name.strip())
111 tag_name = string.lower(tag_name.strip())
110 if len(tag_name) > 0:
112 if len(tag_name) > 0:
111 tag, created = Tag.objects.get_or_create(name=tag_name)
113 tag, created = Tag.objects.get_or_create(name=tag_name)
112 tags.append(tag)
114 tags.append(tag)
113 post_thread = None
115 post_thread = None
114 else:
116 else:
115 post_thread = opening_post.thread_new
117 post_thread = opening_post.thread_new
116
118
117 post = Post.objects.create_post(title=title, text=text, ip=ip,
119 post = Post.objects.create_post(title=title, text=text, ip=ip,
118 thread=post_thread, image=image,
120 thread=post_thread, image=image,
119 tags=tags, user=_get_user(request))
121 tags=tags, user=_get_user(request))
120
122
121 thread_to_show = (opening_post.id if opening_post else post.id)
123 thread_to_show = (opening_post.id if opening_post else post.id)
122
124
123 if opening_post:
125 if opening_post:
124 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
126 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
125 '#' + str(post.id))
127 '#' + str(post.id))
126 else:
128 else:
127 return redirect(thread, post_id=thread_to_show)
129 return redirect(thread, post_id=thread_to_show)
128
130
129
131
130 def tag(request, tag_name, page=0):
132 def tag(request, tag_name, page=0):
131 """
133 """
132 Get all tag threads. Threads are split in pages, so some page is
134 Get all tag threads. Threads are split in pages, so some page is
133 requested. Default page is 0.
135 requested. Default page is 0.
134 """
136 """
135
137
136 tag = get_object_or_404(Tag, name=tag_name)
138 tag = get_object_or_404(Tag, name=tag_name)
137 threads = []
139 threads = []
138 for thread_to_show in Post.objects.get_threads(page=int(page), tag=tag):
140 for thread_to_show in Post.objects.get_threads(page=int(page), tag=tag):
139 threads.append(_get_template_thread(thread_to_show))
141 threads.append(_get_template_thread(thread_to_show))
140
142
141 if request.method == 'POST':
143 if request.method == 'POST':
142 form = ThreadForm(request.POST, request.FILES,
144 form = ThreadForm(request.POST, request.FILES,
143 error_class=PlainErrorList)
145 error_class=PlainErrorList)
144 form.session = request.session
146 form.session = request.session
145
147
146 if form.is_valid():
148 if form.is_valid():
147 return _new_post(request, form)
149 return _new_post(request, form)
148 if form.need_to_ban:
150 if form.need_to_ban:
149 # Ban user because he is suspected to be a bot
151 # Ban user because he is suspected to be a bot
150 _ban_current_user(request)
152 _ban_current_user(request)
151 else:
153 else:
152 form = forms.ThreadForm(initial={'tags': tag_name},
154 form = forms.ThreadForm(initial={'tags': tag_name},
153 error_class=PlainErrorList)
155 error_class=PlainErrorList)
154
156
155 context = _init_default_context(request)
157 context = _init_default_context(request)
156 context['threads'] = None if len(threads) == 0 else threads
158 context['threads'] = None if len(threads) == 0 else threads
157 context['tag'] = tag
159 context['tag'] = tag
158 context['current_page'] = int(page)
160 context['current_page'] = int(page)
159
161
160 page_count = Post.objects.get_thread_page_count(tag=tag)
162 page_count = Post.objects.get_thread_page_count(tag=tag)
161 context['pages'] = range(page_count)
163 context['pages'] = range(page_count)
162 page = int(page)
164 page = int(page)
163 if page < page_count - 1:
165 if page < page_count - 1:
164 context['next_page'] = str(page + 1)
166 context['next_page'] = str(page + 1)
165 if page > 0:
167 if page > 0:
166 context['prev_page'] = str(page - 1)
168 context['prev_page'] = str(page - 1)
167
169
168 context['form'] = form
170 context['form'] = form
169
171
170 return render(request, 'boards/posting_general.html',
172 return render(request, 'boards/posting_general.html',
171 context)
173 context)
172
174
173
175
174 def thread(request, post_id, mode='normal'):
176 def thread(request, post_id, mode=MODE_NORMAL):
175 """Get all thread posts"""
177 """Get all thread posts"""
176
178
177 if utils.need_include_captcha(request):
179 if utils.need_include_captcha(request):
178 postFormClass = PostCaptchaForm
180 postFormClass = PostCaptchaForm
179 kwargs = {'request': request}
181 kwargs = {'request': request}
180 else:
182 else:
181 postFormClass = PostForm
183 postFormClass = PostForm
182 kwargs = {}
184 kwargs = {}
183
185
184 if request.method == 'POST':
186 if request.method == 'POST':
185 form = postFormClass(request.POST, request.FILES,
187 form = postFormClass(request.POST, request.FILES,
186 error_class=PlainErrorList, **kwargs)
188 error_class=PlainErrorList, **kwargs)
187 form.session = request.session
189 form.session = request.session
188
190
189 opening_post = get_object_or_404(Post, id=post_id)
191 opening_post = get_object_or_404(Post, id=post_id)
190 if form.is_valid():
192 if form.is_valid():
191 return _new_post(request, form, opening_post)
193 return _new_post(request, form, opening_post)
192 if form.need_to_ban:
194 if form.need_to_ban:
193 # Ban user because he is suspected to be a bot
195 # Ban user because he is suspected to be a bot
194 _ban_current_user(request)
196 _ban_current_user(request)
195 else:
197 else:
196 form = postFormClass(error_class=PlainErrorList, **kwargs)
198 form = postFormClass(error_class=PlainErrorList, **kwargs)
197
199
198 thread_to_show = get_object_or_404(Post, id=post_id).thread_new
200 thread_to_show = get_object_or_404(Post, id=post_id).thread_new
199
201
200 context = _init_default_context(request)
202 context = _init_default_context(request)
201
203
202 posts = thread_to_show.get_replies()
204 posts = thread_to_show.get_replies()
203 context['form'] = form
205 context['form'] = form
204 context['bumpable'] = thread_to_show.can_bump()
206 context['bumpable'] = thread_to_show.can_bump()
205 if context['bumpable']:
207 if context['bumpable']:
206 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - posts \
208 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - posts \
207 .count()
209 .count()
208 context['bumplimit_progress'] = str(
210 context['bumplimit_progress'] = str(
209 float(context['posts_left']) /
211 float(context['posts_left']) /
210 neboard.settings.MAX_POSTS_PER_THREAD * 100)
212 neboard.settings.MAX_POSTS_PER_THREAD * 100)
211 context["last_update"] = _datetime_to_epoch(thread_to_show.last_edit_time)
213 context["last_update"] = _datetime_to_epoch(thread_to_show.last_edit_time)
212 context["thread"] = thread_to_show
214 context["thread"] = thread_to_show
213
215
214 if 'normal' == mode:
216 if MODE_NORMAL == mode:
215 document = 'boards/thread.html'
217 document = 'boards/thread.html'
216 elif 'gallery' == mode:
218 elif MODE_GALLERY == mode:
217 document = 'boards/thread_gallery.html'
219 document = 'boards/thread_gallery.html'
218 else:
220 else:
219 pass
221 raise Http404
220 # TODO raise 404 error
221
222
222 return render(request, document, context)
223 return render(request, document, context)
223
224
224
225
225 def login(request):
226 def login(request):
226 """Log in with user id"""
227 """Log in with user id"""
227
228
228 context = _init_default_context(request)
229 context = _init_default_context(request)
229
230
230 if request.method == 'POST':
231 if request.method == 'POST':
231 form = LoginForm(request.POST, request.FILES,
232 form = LoginForm(request.POST, request.FILES,
232 error_class=PlainErrorList)
233 error_class=PlainErrorList)
233 form.session = request.session
234 form.session = request.session
234
235
235 if form.is_valid():
236 if form.is_valid():
236 user = User.objects.get(user_id=form.cleaned_data['user_id'])
237 user = User.objects.get(user_id=form.cleaned_data['user_id'])
237 request.session['user_id'] = user.id
238 request.session['user_id'] = user.id
238 return redirect(index)
239 return redirect(index)
239
240
240 else:
241 else:
241 form = LoginForm()
242 form = LoginForm()
242
243
243 context['form'] = form
244 context['form'] = form
244
245
245 return render(request, 'boards/login.html', context)
246 return render(request, 'boards/login.html', context)
246
247
247
248
248 def settings(request):
249 def settings(request):
249 """User's settings"""
250 """User's settings"""
250
251
251 context = _init_default_context(request)
252 context = _init_default_context(request)
252 user = _get_user(request)
253 user = _get_user(request)
253 is_moderator = user.is_moderator()
254 is_moderator = user.is_moderator()
254
255
255 if request.method == 'POST':
256 if request.method == 'POST':
256 with transaction.atomic():
257 with transaction.atomic():
257 if is_moderator:
258 if is_moderator:
258 form = ModeratorSettingsForm(request.POST,
259 form = ModeratorSettingsForm(request.POST,
259 error_class=PlainErrorList)
260 error_class=PlainErrorList)
260 else:
261 else:
261 form = SettingsForm(request.POST, error_class=PlainErrorList)
262 form = SettingsForm(request.POST, error_class=PlainErrorList)
262
263
263 if form.is_valid():
264 if form.is_valid():
264 selected_theme = form.cleaned_data['theme']
265 selected_theme = form.cleaned_data['theme']
265
266
266 user.save_setting('theme', selected_theme)
267 user.save_setting('theme', selected_theme)
267
268
268 if is_moderator:
269 if is_moderator:
269 moderate = form.cleaned_data['moderate']
270 moderate = form.cleaned_data['moderate']
270 user.save_setting(SETTING_MODERATE, moderate)
271 user.save_setting(SETTING_MODERATE, moderate)
271
272
272 return redirect(settings)
273 return redirect(settings)
273 else:
274 else:
274 selected_theme = _get_theme(request)
275 selected_theme = _get_theme(request)
275
276
276 if is_moderator:
277 if is_moderator:
277 form = ModeratorSettingsForm(initial={'theme': selected_theme,
278 form = ModeratorSettingsForm(initial={'theme': selected_theme,
278 'moderate': context['moderator']},
279 'moderate': context['moderator']},
279 error_class=PlainErrorList)
280 error_class=PlainErrorList)
280 else:
281 else:
281 form = SettingsForm(initial={'theme': selected_theme},
282 form = SettingsForm(initial={'theme': selected_theme},
282 error_class=PlainErrorList)
283 error_class=PlainErrorList)
283
284
284 context['form'] = form
285 context['form'] = form
285
286
286 return render(request, 'boards/settings.html', context)
287 return render(request, 'boards/settings.html', context)
287
288
288
289
289 def all_tags(request):
290 def all_tags(request):
290 """All tags list"""
291 """All tags list"""
291
292
292 context = _init_default_context(request)
293 context = _init_default_context(request)
293 context['all_tags'] = Tag.objects.get_not_empty_tags()
294 context['all_tags'] = Tag.objects.get_not_empty_tags()
294
295
295 return render(request, 'boards/tags.html', context)
296 return render(request, 'boards/tags.html', context)
296
297
297
298
298 def jump_to_post(request, post_id):
299 def jump_to_post(request, post_id):
299 """Determine thread in which the requested post is and open it's page"""
300 """Determine thread in which the requested post is and open it's page"""
300
301
301 post = get_object_or_404(Post, id=post_id)
302 post = get_object_or_404(Post, id=post_id)
302
303
303 if not post.thread:
304 if not post.thread:
304 return redirect(thread, post_id=post.id)
305 return redirect(thread, post_id=post.id)
305 else:
306 else:
306 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
307 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
307 + '#' + str(post.id))
308 + '#' + str(post.id))
308
309
309
310
310 def authors(request):
311 def authors(request):
311 """Show authors list"""
312 """Show authors list"""
312
313
313 context = _init_default_context(request)
314 context = _init_default_context(request)
314 context['authors'] = boards.authors.authors
315 context['authors'] = boards.authors.authors
315
316
316 return render(request, 'boards/authors.html', context)
317 return render(request, 'boards/authors.html', context)
317
318
318
319
319 @transaction.atomic
320 @transaction.atomic
320 def delete(request, post_id):
321 def delete(request, post_id):
321 """Delete post"""
322 """Delete post"""
322
323
323 user = _get_user(request)
324 user = _get_user(request)
324 post = get_object_or_404(Post, id=post_id)
325 post = get_object_or_404(Post, id=post_id)
325
326
326 if user.is_moderator():
327 if user.is_moderator():
327 # TODO Show confirmation page before deletion
328 # TODO Show confirmation page before deletion
328 Post.objects.delete_post(post)
329 Post.objects.delete_post(post)
329
330
330 if not post.thread:
331 if not post.thread:
331 return _redirect_to_next(request)
332 return _redirect_to_next(request)
332 else:
333 else:
333 return redirect(thread, post_id=post.thread.id)
334 return redirect(thread, post_id=post.thread.id)
334
335
335
336
336 @transaction.atomic
337 @transaction.atomic
337 def ban(request, post_id):
338 def ban(request, post_id):
338 """Ban user"""
339 """Ban user"""
339
340
340 user = _get_user(request)
341 user = _get_user(request)
341 post = get_object_or_404(Post, id=post_id)
342 post = get_object_or_404(Post, id=post_id)
342
343
343 if user.is_moderator():
344 if user.is_moderator():
344 # TODO Show confirmation page before ban
345 # TODO Show confirmation page before ban
345 ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
346 ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
346 if created:
347 if created:
347 ban.reason = 'Banned for post ' + str(post_id)
348 ban.reason = 'Banned for post ' + str(post_id)
348 ban.save()
349 ban.save()
349
350
350 return _redirect_to_next(request)
351 return _redirect_to_next(request)
351
352
352
353
353 def you_are_banned(request):
354 def you_are_banned(request):
354 """Show the page that notifies that user is banned"""
355 """Show the page that notifies that user is banned"""
355
356
356 context = _init_default_context(request)
357 context = _init_default_context(request)
357
358
358 ban = get_object_or_404(Ban, ip=utils.get_client_ip(request))
359 ban = get_object_or_404(Ban, ip=utils.get_client_ip(request))
359 context['ban_reason'] = ban.reason
360 context['ban_reason'] = ban.reason
360 return render(request, 'boards/staticpages/banned.html', context)
361 return render(request, 'boards/staticpages/banned.html', context)
361
362
362
363
363 def page_404(request):
364 def page_404(request):
364 """Show page 404 (not found error)"""
365 """Show page 404 (not found error)"""
365
366
366 context = _init_default_context(request)
367 context = _init_default_context(request)
367 return render(request, 'boards/404.html', context)
368 return render(request, 'boards/404.html', context)
368
369
369
370
370 @transaction.atomic
371 @transaction.atomic
371 def tag_subscribe(request, tag_name):
372 def tag_subscribe(request, tag_name):
372 """Add tag to favorites"""
373 """Add tag to favorites"""
373
374
374 user = _get_user(request)
375 user = _get_user(request)
375 tag = get_object_or_404(Tag, name=tag_name)
376 tag = get_object_or_404(Tag, name=tag_name)
376
377
377 if not tag in user.fav_tags.all():
378 if not tag in user.fav_tags.all():
378 user.add_tag(tag)
379 user.add_tag(tag)
379
380
380 return _redirect_to_next(request)
381 return _redirect_to_next(request)
381
382
382
383
383 @transaction.atomic
384 @transaction.atomic
384 def tag_unsubscribe(request, tag_name):
385 def tag_unsubscribe(request, tag_name):
385 """Remove tag from favorites"""
386 """Remove tag from favorites"""
386
387
387 user = _get_user(request)
388 user = _get_user(request)
388 tag = get_object_or_404(Tag, name=tag_name)
389 tag = get_object_or_404(Tag, name=tag_name)
389
390
390 if tag in user.fav_tags.all():
391 if tag in user.fav_tags.all():
391 user.remove_tag(tag)
392 user.remove_tag(tag)
392
393
393 return _redirect_to_next(request)
394 return _redirect_to_next(request)
394
395
395
396
396 def static_page(request, name):
397 def static_page(request, name):
397 """Show a static page that needs only tags list and a CSS"""
398 """Show a static page that needs only tags list and a CSS"""
398
399
399 context = _init_default_context(request)
400 context = _init_default_context(request)
400 return render(request, 'boards/staticpages/' + name + '.html', context)
401 return render(request, 'boards/staticpages/' + name + '.html', context)
401
402
402
403
403 def api_get_post(request, post_id):
404 def api_get_post(request, post_id):
404 """
405 """
405 Get the JSON of a post. This can be
406 Get the JSON of a post. This can be
406 used as and API for external clients.
407 used as and API for external clients.
407 """
408 """
408
409
409 post = get_object_or_404(Post, id=post_id)
410 post = get_object_or_404(Post, id=post_id)
410
411
411 json = serializers.serialize("json", [post], fields=(
412 json = serializers.serialize("json", [post], fields=(
412 "pub_time", "_text_rendered", "title", "text", "image",
413 "pub_time", "_text_rendered", "title", "text", "image",
413 "image_width", "image_height", "replies", "tags"
414 "image_width", "image_height", "replies", "tags"
414 ))
415 ))
415
416
416 return HttpResponse(content=json)
417 return HttpResponse(content=json)
417
418
418
419
419 def get_post(request, post_id):
420 def get_post(request, post_id):
420 """Get the html of a post. Used for popups."""
421 """Get the html of a post. Used for popups."""
421
422
422 post = get_object_or_404(Post, id=post_id)
423 post = get_object_or_404(Post, id=post_id)
423 thread = post.thread_new
424 thread = post.thread_new
424
425
425 context = RequestContext(request)
426 context = RequestContext(request)
426 context["post"] = post
427 context["post"] = post
427 context["can_bump"] = thread.can_bump()
428 context["can_bump"] = thread.can_bump()
428 if "truncated" in request.GET:
429 if "truncated" in request.GET:
429 context["truncated"] = True
430 context["truncated"] = True
430
431
431 return render(request, 'boards/post.html', context)
432 return render(request, 'boards/post.html', context)
432
433
433 @cache_page(86400)
434 @cache_page(86400)
434 def cached_js_catalog(request, domain='djangojs', packages=None):
435 def cached_js_catalog(request, domain='djangojs', packages=None):
435 return javascript_catalog(request, domain, packages)
436 return javascript_catalog(request, domain, packages)
436
437
437
438
438 def _get_theme(request, user=None):
439 def _get_theme(request, user=None):
439 """Get user's CSS theme"""
440 """Get user's CSS theme"""
440
441
441 if not user:
442 if not user:
442 user = _get_user(request)
443 user = _get_user(request)
443 theme = user.get_setting('theme')
444 theme = user.get_setting('theme')
444 if not theme:
445 if not theme:
445 theme = neboard.settings.DEFAULT_THEME
446 theme = neboard.settings.DEFAULT_THEME
446
447
447 return theme
448 return theme
448
449
449
450
450 def _init_default_context(request):
451 def _init_default_context(request):
451 """Create context with default values that are used in most views"""
452 """Create context with default values that are used in most views"""
452
453
453 context = RequestContext(request)
454 context = RequestContext(request)
454
455
455 user = _get_user(request)
456 user = _get_user(request)
456 context['user'] = user
457 context['user'] = user
457 context['tags'] = user.get_sorted_fav_tags()
458 context['tags'] = user.get_sorted_fav_tags()
458 context['posts_per_day'] = float(Post.objects.get_posts_per_day())
459 context['posts_per_day'] = float(Post.objects.get_posts_per_day())
459
460
460 theme = _get_theme(request, user)
461 theme = _get_theme(request, user)
461 context['theme'] = theme
462 context['theme'] = theme
462 context['theme_css'] = 'css/' + theme + '/base_page.css'
463 context['theme_css'] = 'css/' + theme + '/base_page.css'
463
464
464 # This shows the moderator panel
465 # This shows the moderator panel
465 moderate = user.get_setting(SETTING_MODERATE)
466 moderate = user.get_setting(SETTING_MODERATE)
466 if moderate == 'True':
467 if moderate == 'True':
467 context['moderator'] = user.is_moderator()
468 context['moderator'] = user.is_moderator()
468 else:
469 else:
469 context['moderator'] = False
470 context['moderator'] = False
470
471
471 return context
472 return context
472
473
473
474
474 def _get_user(request):
475 def _get_user(request):
475 """
476 """
476 Get current user from the session. If the user does not exist, create
477 Get current user from the session. If the user does not exist, create
477 a new one.
478 a new one.
478 """
479 """
479
480
480 session = request.session
481 session = request.session
481 if not 'user_id' in session:
482 if not 'user_id' in session:
482 request.session.save()
483 request.session.save()
483
484
484 md5 = hashlib.md5()
485 md5 = hashlib.md5()
485 md5.update(session.session_key)
486 md5.update(session.session_key)
486 new_id = md5.hexdigest()
487 new_id = md5.hexdigest()
487
488
488 time_now = timezone.now()
489 time_now = timezone.now()
489 user = User.objects.create(user_id=new_id, rank=RANK_USER,
490 user = User.objects.create(user_id=new_id, rank=RANK_USER,
490 registration_time=time_now)
491 registration_time=time_now)
491
492
492 session['user_id'] = user.id
493 session['user_id'] = user.id
493 else:
494 else:
494 user = User.objects.get(id=session['user_id'])
495 user = User.objects.get(id=session['user_id'])
495
496
496 return user
497 return user
497
498
498
499
499 def _redirect_to_next(request):
500 def _redirect_to_next(request):
500 """
501 """
501 If a 'next' parameter was specified, redirect to the next page. This is
502 If a 'next' parameter was specified, redirect to the next page. This is
502 used when the user is required to return to some page after the current
503 used when the user is required to return to some page after the current
503 view has finished its work.
504 view has finished its work.
504 """
505 """
505
506
506 if 'next' in request.GET:
507 if 'next' in request.GET:
507 next_page = request.GET['next']
508 next_page = request.GET['next']
508 return HttpResponseRedirect(next_page)
509 return HttpResponseRedirect(next_page)
509 else:
510 else:
510 return redirect(index)
511 return redirect(index)
511
512
512
513
513 @transaction.atomic
514 @transaction.atomic
514 def _ban_current_user(request):
515 def _ban_current_user(request):
515 """Add current user to the IP ban list"""
516 """Add current user to the IP ban list"""
516
517
517 ip = utils.get_client_ip(request)
518 ip = utils.get_client_ip(request)
518 ban, created = Ban.objects.get_or_create(ip=ip)
519 ban, created = Ban.objects.get_or_create(ip=ip)
519 if created:
520 if created:
520 ban.can_read = False
521 ban.can_read = False
521 ban.reason = BAN_REASON_SPAM
522 ban.reason = BAN_REASON_SPAM
522 ban.save()
523 ban.save()
523
524
524
525
525 def _remove_invalid_links(text):
526 def _remove_invalid_links(text):
526 """
527 """
527 Replace invalid links in posts so that they won't be parsed.
528 Replace invalid links in posts so that they won't be parsed.
528 Invalid links are links to non-existent posts
529 Invalid links are links to non-existent posts
529 """
530 """
530
531
531 for reply_number in re.finditer(REGEX_REPLY, text):
532 for reply_number in re.finditer(REGEX_REPLY, text):
532 post_id = reply_number.group(1)
533 post_id = reply_number.group(1)
533 post = Post.objects.filter(id=post_id)
534 post = Post.objects.filter(id=post_id)
534 if not post.exists():
535 if not post.exists():
535 text = string.replace(text, '>>' + post_id, post_id)
536 text = string.replace(text, '>>' + post_id, post_id)
536
537
537 return text
538 return text
538
539
539
540
540 def _datetime_to_epoch(datetime):
541 def _datetime_to_epoch(datetime):
541 return int(time.mktime(timezone.localtime(
542 return int(time.mktime(timezone.localtime(
542 datetime,timezone.get_current_timezone()).timetuple())
543 datetime,timezone.get_current_timezone()).timetuple())
543 * 1000000 + datetime.microsecond)
544 * 1000000 + datetime.microsecond)
544
545
545
546
546 def _get_template_thread(thread_to_show):
547 def _get_template_thread(thread_to_show):
547 """Get template values for thread"""
548 """Get template values for thread"""
548
549
549 last_replies = thread_to_show.get_last_replies()
550 last_replies = thread_to_show.get_last_replies()
550 skipped_replies_count = thread_to_show.get_replies().count() \
551 skipped_replies_count = thread_to_show.get_replies().count() \
551 - len(last_replies) - 1
552 - len(last_replies) - 1
552 return {
553 return {
553 'thread': thread_to_show,
554 'thread': thread_to_show,
554 'op': thread_to_show.get_replies()[0],
555 'op': thread_to_show.get_replies()[0],
555 'bumpable': thread_to_show.can_bump(),
556 'bumpable': thread_to_show.can_bump(),
556 'last_replies': last_replies,
557 'last_replies': last_replies,
557 'skipped_replies': skipped_replies_count,
558 'skipped_replies': skipped_replies_count,
558 }
559 }
General Comments 0
You need to be logged in to leave comments. Login now