├── .gitignore
├── README.md
├── account
├── __init__.py
├── admin.py
├── locale
│ └── zh_CN
│ │ └── LC_MESSAGES
│ │ └── django.po
├── models.py
├── templates
│ └── account
│ │ ├── change-password.html
│ │ ├── login.html
│ │ ├── reg.html
│ │ ├── reset-password-confirm.html
│ │ ├── reset-password-email.html
│ │ ├── reset-password-subject.txt
│ │ ├── reset-password.html
│ │ ├── user-avatar.html
│ │ ├── user-info.html
│ │ ├── user-mention.html
│ │ └── user-setting.html
├── tests.py
├── urls.py
└── views.py
├── fairy
├── __init__.py
├── conf.py
├── middleware.py
├── settings.py
├── urls.py
└── wsgi.py
├── forum
├── __init__.py
├── admin.py
├── api.py
├── locale
│ └── zh_CN
│ │ └── LC_MESSAGES
│ │ └── django.po
├── models.py
├── templates
│ └── forum
│ │ ├── append.html
│ │ ├── create-topic.html
│ │ ├── edit-topic.html
│ │ ├── index.html
│ │ ├── node-all.html
│ │ ├── node-view.html
│ │ └── topic.html
├── templatetags
│ ├── __init__.py
│ └── settingsvalue.py
├── tests.py
├── urls.py
└── views.py
├── locale
└── zh_CN
│ └── LC_MESSAGES
│ └── django.po
├── manage.py
├── panel
├── __init__.py
├── admin.py
├── locale
│ └── zh_CN
│ │ └── LC_MESSAGES
│ │ └── django.po
├── models.py
├── templates
│ └── panel
│ │ ├── base.html
│ │ ├── index.html
│ │ ├── login.html
│ │ ├── node-create.html
│ │ ├── node-edit.html
│ │ ├── node-manage.html
│ │ ├── notifications.html
│ │ ├── panels-wells.html
│ │ ├── tables.html
│ │ ├── topic-edit.html
│ │ ├── topic-manage.html
│ │ ├── user-edit.html
│ │ └── user-manage.html
├── tests.py
├── urls.py
└── views.py
├── requirements.txt
├── static
├── css
│ ├── base.css
│ ├── bootstrap-theme.css
│ ├── bootstrap-theme.css.map
│ ├── bootstrap-theme.min.css
│ ├── bootstrap.css.map
│ ├── bootstrap.min.css
│ └── codehilite.css
├── fonts
│ ├── .directory
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ └── glyphicons-halflings-regular.woff
├── image
│ ├── Connect_logo_7.png
│ ├── bg.png
│ └── pixels.png
├── js
│ ├── bootstrap.min.js
│ ├── html5shiv.js
│ ├── jquery.min.js
│ ├── mention.js
│ ├── previewer.js
│ ├── respond.min.js
│ └── search.js
└── panel
│ ├── css
│ ├── bootstrap.css
│ ├── bootstrap.min.css
│ ├── plugins
│ │ ├── dataTables
│ │ │ └── dataTables.bootstrap.css
│ │ ├── morris
│ │ │ └── morris-0.4.3.min.css
│ │ ├── social-buttons
│ │ │ └── social-buttons.css
│ │ └── timeline
│ │ │ └── timeline.css
│ └── sb-admin.css
│ ├── font-awesome
│ ├── css
│ │ ├── font-awesome.css
│ │ └── font-awesome.min.css
│ ├── fonts
│ │ ├── FontAwesome.otf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.svg
│ │ ├── fontawesome-webfont.ttf
│ │ └── fontawesome-webfont.woff
│ ├── less
│ │ ├── bordered-pulled.less
│ │ ├── core.less
│ │ ├── fixed-width.less
│ │ ├── font-awesome.less
│ │ ├── icons.less
│ │ ├── larger.less
│ │ ├── list.less
│ │ ├── mixins.less
│ │ ├── path.less
│ │ ├── rotated-flipped.less
│ │ ├── spinning.less
│ │ ├── stacked.less
│ │ └── variables.less
│ └── scss
│ │ ├── _bordered-pulled.scss
│ │ ├── _core.scss
│ │ ├── _fixed-width.scss
│ │ ├── _icons.scss
│ │ ├── _larger.scss
│ │ ├── _list.scss
│ │ ├── _mixins.scss
│ │ ├── _path.scss
│ │ ├── _rotated-flipped.scss
│ │ ├── _spinning.scss
│ │ ├── _stacked.scss
│ │ ├── _variables.scss
│ │ └── font-awesome.scss
│ ├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ └── glyphicons-halflings-regular.woff
│ └── js
│ ├── autocomplete.js
│ ├── bootstrap.js
│ ├── bootstrap.min.js
│ ├── demo
│ ├── dashboard-demo.js
│ ├── flot-demo.js
│ └── morris-demo.js
│ ├── jquery-1.10.2.js
│ ├── plugins
│ ├── dataTables
│ │ ├── dataTables.bootstrap.js
│ │ └── jquery.dataTables.js
│ ├── flot
│ │ ├── excanvas.min.js
│ │ ├── jquery.flot.js
│ │ ├── jquery.flot.pie.js
│ │ ├── jquery.flot.resize.js
│ │ └── jquery.flot.tooltip.min.js
│ ├── metisMenu
│ │ └── jquery.metisMenu.js
│ └── morris
│ │ ├── morris.js
│ │ └── raphael-2.1.0.min.js
│ ├── sb-admin.js
│ └── tabletools.js
└── template
├── common
├── 403.html
├── 404.html
├── 500.html
├── base-with-sidebar.html
├── base.html
└── error.html
├── pagination
└── pagination.html
└── widget
├── links.html
├── node-info.html
├── node.html
├── stat.html
├── topic-list.html
└── user-panel.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *.swp
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | bin/
10 | build/
11 | develop-eggs/
12 | dist/
13 | eggs/
14 | lib/
15 | lib64/
16 | parts/
17 | sdist/
18 | venv/
19 | var/
20 | static/upload/
21 | *.egg-info/
22 | .installed.cfg
23 | *.egg
24 | *.sublime*
25 |
26 | # Installer logs
27 | pip-log.txt
28 | pip-delete-this-directory.txt
29 |
30 | # Unit test / coverage reports
31 | .tox/
32 | .coverage
33 | .cache
34 | nosetests.xml
35 | coverage.xml
36 |
37 | # Translations
38 | *.mo
39 |
40 | # Mr Developer
41 | .mr.developer.cfg
42 | .project
43 | .pydevproject
44 | .settings
45 | # Rope
46 | .ropeproject
47 |
48 | # Django stuff:
49 | *.log
50 | *.pot
51 |
52 | # Sphinx documentation
53 | docs/_build/
54 |
55 | db.sqlite3
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NOTICE
2 |
3 | This project is disconinued due to low code quality.
4 |
5 | A refactored version is available at [https://github.com/ericls/niji](https://github.com/ericls/niji)
6 |
--------------------------------------------------------------------------------
/account/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/account/__init__.py
--------------------------------------------------------------------------------
/account/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from account.models import profile, social
3 | # Register your models here.
4 | admin.site.register(profile)
5 | admin.site.register(social)
6 |
--------------------------------------------------------------------------------
/account/locale/zh_CN/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , 2014.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2014-07-15 14:08+0800\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 |
20 | #: templates/account/change-password.html:6
21 | #: templates/account/user-setting.html:29 views.py:148
22 | msgid "change password"
23 | msgstr "修改密码"
24 |
25 | #: templates/account/change-password.html:20
26 | msgid "current password"
27 | msgstr "原始密码"
28 |
29 | #: templates/account/change-password.html:26
30 | msgid "new password"
31 | msgstr "新密码"
32 |
33 | #: templates/account/change-password.html:33
34 | msgid "new password again"
35 | msgstr "重复"
36 |
37 | #: templates/account/change-password.html:42
38 | #: templates/account/user-avatar.html:29
39 | #: templates/account/user-setting.html:27
40 | msgid "update"
41 | msgstr "更新"
42 |
43 | #: templates/account/login.html:6 templates/account/login.html.py:29
44 | #: views.py:91
45 | msgid "sign in"
46 | msgstr "登陆"
47 |
48 | #: templates/account/login.html:18 templates/account/reg.html:18
49 | msgid "username"
50 | msgstr "用户名"
51 |
52 | #: templates/account/login.html:23 templates/account/reg.html:28
53 | msgid "password"
54 | msgstr "密码"
55 |
56 | #: templates/account/login.html:31
57 | msgid "reg?"
58 | msgstr "注册?"
59 |
60 | #: templates/account/login.html:32
61 | msgid "forget password?"
62 | msgstr "忘记密码?"
63 |
64 | #: templates/account/reg.html:6 views.py:58
65 | msgid "register"
66 | msgstr "注册"
67 |
68 | #: templates/account/reg.html:18 templates/account/reg.html.py:19
69 | msgid "letters,digits,underscore"
70 | msgstr "字母,数字,下划线"
71 |
72 | #: templates/account/reg.html:23
73 | msgid "Email"
74 | msgstr "Email"
75 |
76 | #: templates/account/reg.html:33
77 | msgid "repeat password"
78 | msgstr "重复密码"
79 |
80 | #: templates/account/reg.html:39
81 | #: templates/account/reset-password-confirm.html:44
82 | #: templates/account/reset-password.html:43
83 | msgid "submit"
84 | msgstr "提交"
85 |
86 | #: templates/account/reg.html:41
87 | msgid "sign in?"
88 | msgstr "已有账户?"
89 |
90 | #: templates/account/reset-password-confirm.html:5
91 | #: templates/account/reset-password-confirm.html:35
92 | #: templates/account/reset-password.html:5
93 | #: templates/account/reset-password.html:35
94 | msgid "reset password"
95 | msgstr "密码重置"
96 |
97 | #: templates/account/user-avatar.html:6 templates/account/user-setting.html:30
98 | msgid "avatar settings"
99 | msgstr "头像设置"
100 |
101 | #: templates/account/user-avatar.html:11
102 | msgid "upload new avatar(leave blank will not change your current avatar)"
103 | msgstr "上传新头像(留空不修改)"
104 |
105 | #: templates/account/user-avatar.html:13
106 | msgid "maximum 512K, png,jpg and gif only"
107 | msgstr "最大512k,png,jpg和gif格式"
108 |
109 | #: templates/account/user-avatar.html:15
110 | msgid "Use Gravatar"
111 | msgstr "使用Gravatar"
112 |
113 | #: templates/account/user-setting.html:6
114 | msgid "user settings"
115 | msgstr "用户设置"
116 |
117 | #: templates/account/user-setting.html:11
118 | msgid "website"
119 | msgstr "网站"
120 |
121 | #: views.py:49
122 | msgid "user info"
123 | msgstr "用户信息"
124 |
125 | #: views.py:52
126 | #, python-format
127 | msgid "%s's topics"
128 | msgstr "%s的话题"
129 |
130 | #: views.py:69
131 | msgid "username can only contain letters digits and underscore"
132 | msgstr "用户名只能包含字母,数字和下划线"
133 |
134 | #: views.py:73
135 | msgid "username already exists"
136 | msgstr "用户名已经存在"
137 |
138 | #: views.py:77 views.py:155
139 | msgid "passwords don't match, or are blank"
140 | msgstr "密码不匹配或为空"
141 |
142 | #: views.py:99
143 | msgid "username does not exist"
144 | msgstr "用户名不存在"
145 |
146 | #: views.py:103
147 | msgid "password is invalid"
148 | msgstr "密码不正确"
149 |
150 | #: views.py:114 views.py:123
151 | msgid "settings"
152 | msgstr "设置"
153 |
154 | #: views.py:138
155 | msgid "mentions"
156 | msgstr "提醒"
157 |
158 | #: views.py:161
159 | msgid "password updated successfully"
160 | msgstr "密码修改成功"
161 |
162 | #: views.py:164
163 | msgid "unable to change your password, the current password may be invalid"
164 | msgstr "错误,可能是原始密码不对"
165 |
166 | #: views.py:171
167 | #| msgid "avatar settings"
168 | msgid "avatar setting"
169 | msgstr "头像设置"
170 |
171 | #: views.py:181
172 | msgid "file too big"
173 | msgstr "文件太大"
174 |
175 | #: views.py:183
176 | msgid "file type not permitted"
177 | msgstr "文件类型不允许"
178 |
--------------------------------------------------------------------------------
/account/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 | import urllib
4 | import hashlib
5 |
6 |
7 | class profile(models.Model):
8 | user = models.OneToOneField(User)
9 | nickname = models.CharField(max_length=12, blank=True, null=True)
10 | use_gravatar = models.BooleanField(default=True)
11 | location = models.CharField(max_length=20, blank=True, null=True)
12 | avatar_url = models.URLField(blank=True, null=True)
13 | website = models.URLField(blank=True, null=True)
14 |
15 | def unread_mention(self):
16 | return self.user.received_mentions.filter(read=False)
17 |
18 | def old_mention(self):
19 | return self.user.received_mentions.filter(read=True)[0:5]
20 |
21 | def username(self):
22 | if self.nickname:
23 | return self.nickname
24 | else:
25 | return self.user.username
26 |
27 | def latest_activity(self):
28 | d = {}
29 | d['topic'] = self.user.topics.all().filter(deleted=False).order_by('-time_created')[0:10]
30 | d['post'] = self.user.posts.all().filter(deleted=False).order_by('-time_created')[0:10]
31 | return d
32 |
33 | def __unicode__(self):
34 | return self.user.username
35 |
36 | def avatar(self):
37 | da = '' # default avatar
38 | dic = {}
39 | if self.use_gravatar:
40 | mail = self.user.email.lower()
41 | gravatar_url = "http://www.gravatar.com/avatar/"
42 | base_url = gravatar_url + hashlib.md5(mail).hexdigest() + "?"
43 | dic['small'] = base_url + urllib.urlencode({'d': da, 's': '40'})
44 | dic['middle'] = base_url + urllib.urlencode({'d': da, 's': '48'})
45 | dic['large'] = base_url + urllib.urlencode({'d': da, 's': '80'})
46 | return dic
47 | elif self.avatar_url:
48 | dic['small'] = self.avatar_url
49 | dic['middle'] = self.avatar_url
50 | dic['large'] = self.avatar_url
51 | return dic
52 |
53 |
54 | class social(models.Model):
55 | user = models.OneToOneField(User)
56 | access_token = models.CharField(max_length=255)
57 | openid = models.CharField(max_length=255)
58 | avatar = models.URLField()
59 |
60 | def __unicode__(self):
61 | return self.user.username
62 |
63 |
--------------------------------------------------------------------------------
/account/templates/account/change-password.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% load i18n %}
3 | {% block left %}
4 |
5 |
6 |
{% trans 'change password' %}
7 |
8 |
47 |
48 | {% endblock %}
49 | {% block right %}
50 | {% include 'user-panel.html' %}
51 | {% include 'node.html' %}
52 | {% include 'links.html' %}
53 | {% endblock %}
54 |
--------------------------------------------------------------------------------
/account/templates/account/login.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 | {% block inside_row %}
4 |
36 | {% endblock %}
--------------------------------------------------------------------------------
/account/templates/account/reg.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n %}
3 | {% block inside_row %}
4 |
45 | {% endblock %}
--------------------------------------------------------------------------------
/account/templates/account/reset-password-confirm.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% load i18n %}
3 | {% load settingsvalue %}
4 | {% block title %}
5 | {% trans 'reset password'%}-{% conf_value "sitename" %}
6 | {% endblock %}
7 | {% block left %}
8 |
33 |
34 |
35 |
{% trans 'reset password'%}
36 |
37 | {% if validlink %}
38 |
49 | {% else %}
50 |
This reset link is no longer valid!
51 | {% endif %}
52 |
53 | {% endblock %}
54 | {% block right %}
55 | {% include 'user-panel.html' %}
56 | {% endblock %}
--------------------------------------------------------------------------------
/account/templates/account/reset-password-email.html:
--------------------------------------------------------------------------------
1 | password reset for email {{ email }}. Follow the link below:
2 | {{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
--------------------------------------------------------------------------------
/account/templates/account/reset-password-subject.txt:
--------------------------------------------------------------------------------
1 | Password Reset
2 |
--------------------------------------------------------------------------------
/account/templates/account/reset-password.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% block title %}
3 | {% load i18n %}
4 | {% load settingsvalue %}
5 | {% trans 'reset password'%}-{% conf_value "sitename" %}
6 | {% endblock %}
7 | {% block left %}
8 |
33 |
34 |
35 |
{% trans 'reset password'%}
36 |
37 |
48 |
49 | {% endblock %}
50 | {% block right %}
51 | {% include 'user-panel.html' %}
52 | {% endblock %}
53 |
--------------------------------------------------------------------------------
/account/templates/account/user-avatar.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% load i18n %}
3 | {% block left %}
4 |
5 |
6 |
{% trans 'avatar settings' %}
7 |
8 |
34 |
35 | {% endblock %}
36 | {% block right %}
37 | {% include 'user-panel.html' %}
38 | {% include 'node.html' %}
39 | {% include 'links.html' %}
40 | {% endblock %}
41 |
--------------------------------------------------------------------------------
/account/templates/account/user-info.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% load humanize %}
3 | {% block left %}
4 |
5 |
6 |
7 |

8 |
9 |
10 |
{{user.profile.username}}
11 |
#{{user.id}} @ {{user.date_joined|naturaltime}}
12 | {% if user.profile.website %}
13 |
{{user.profile.website}}
14 | {% endif %}
15 |
16 |
17 |
18 | {% include 'topic-list.html' %}
19 | {% endblock %}
20 | {% block right %}
21 | {% include 'user-panel.html' %}
22 | {% include 'node.html' %}
23 | {% include 'links.html' %}
24 | {% endblock %}
--------------------------------------------------------------------------------
/account/templates/account/user-mention.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% block left %}
3 | {% load humanize %}
4 |
5 |
6 |
7 |
8 | Unread Mentions
9 |
10 |
11 |
12 | {% for m in new %}
13 |
14 |
15 |

16 |
32 |
33 |
34 | {% endfor %}
35 |
36 |
37 |
38 |
39 |
40 |
41 | Old Mentions
42 |
43 |
44 |
45 | {% for m in old %}
46 |
47 |
48 |

49 |
65 |
66 |
67 | {% endfor %}
68 |
69 |
70 |
71 |
72 | {% endblock %}
73 | {% block right %}
74 | {% include 'user-panel.html' %}
75 | {% include 'node.html' %}
76 | {% include 'links.html' %}
77 | {% endblock %}
78 |
79 |
--------------------------------------------------------------------------------
/account/templates/account/user-setting.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% load i18n %}
3 | {% block left %}
4 |
5 |
6 |
{% trans 'user settings' %}
7 |
8 |
34 |
35 | {% endblock %}
36 | {% block right %}
37 | {% include 'user-panel.html' %}
38 | {% include 'node.html' %}
39 | {% include 'links.html' %}
40 | {% endblock %}
41 |
--------------------------------------------------------------------------------
/account/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/account/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 |
3 | urlpatterns = patterns('account.views',
4 | url(r'^(?P\d+)/info/$', 'user_info', name='user_info'),
5 | url(r'^reg/$', 'reg', name='reg'),
6 | url(r'^signin/$', 'user_login', name='signin'),
7 | url(r'^setting/$', 'setting', name='user_setting'),
8 | url(r'^signout/$', 'user_logout', name='signout'),
9 | url(r'^mention/$', 'view_mention', name='mention'),
10 | url(r'^oauth/qq/$', 'qq_oauth', name='qq_oauth'),
11 | url(r'password/$', 'change_password', name='change_password'),
12 | url(r'^avatar/$', 'user_avatar', name='user_avatar'),
13 | url(r'^reset/confirm/(?P[0-9A-Za-z]+)-(?P.+)/$',
14 | 'reset_confirm', name='password_reset_confirm'),
15 | url(r'^reset/$', 'reset', name='password_reset'),
16 | # url(r'^set_lang/$', 'set_lang', name='set_lang'),
17 | )
18 |
--------------------------------------------------------------------------------
/fairy/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/fairy/__init__.py
--------------------------------------------------------------------------------
/fairy/conf.py:
--------------------------------------------------------------------------------
1 | #encoding=utf-8
2 | from account.models import profile
3 | from fairy import settings
4 | from forum.models import node, topic, post
5 | import os
6 | sitename = u'FairyBBS'
7 | logoname = u'FairyBBS'
8 |
9 | links = {
10 | #'description': 'url',
11 | }
12 | nodes = node.objects.all()
13 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
14 | UPLOAD_PATH = os.path.join(BASE_DIR, 'static/upload')
15 | user_count = profile.objects.count()
16 | topic_count = topic.objects.filter(deleted=False).count()
17 | post_count = post.objects.filter(deleted=False).count()
18 |
19 | site_off = False
20 |
--------------------------------------------------------------------------------
/fairy/middleware.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from fairy.conf import site_off
3 | from forum.views import error
4 | from django.core.urlresolvers import reverse
5 |
6 | class SiteOff(object):
7 |
8 | def process_request(self, request):
9 | if (site_off) and (request.path != reverse('signin')) and (not request.user.is_superuser):
10 | return error(request, 'down for maintenace')
11 |
--------------------------------------------------------------------------------
/fairy/settings.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Django settings for fairy project.
4 | For more information on this file, see
5 | https://docs.djangoproject.com/en/1.6/topics/settings/
6 |
7 | For the full list of settings and their values, see
8 | https://docs.djangoproject.com/en/1.6/ref/settings/
9 | """
10 |
11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
12 | import os
13 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
14 |
15 |
16 | # Quick-start development settings - unsuitable for production
17 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
18 |
19 | # SECURITY WARNING: keep the secret key used in production secret!
20 | SECRET_KEY = 'SECRET_KEY!!!!!!!!!!'
21 |
22 | # SECURITY WARNING: don't run with debug turned on in production!
23 | DEBUG = True
24 |
25 | TEMPLATE_DEBUG = True
26 |
27 | ALLOWED_HOSTS = ['']
28 |
29 |
30 | # Application definition
31 |
32 | INSTALLED_APPS = (
33 | 'account',
34 | 'forum',
35 | 'pagination',
36 | 'panel',
37 | 'django.contrib.humanize',
38 | 'django.contrib.admin',
39 | 'django.contrib.auth',
40 | 'django.contrib.contenttypes',
41 | 'django.contrib.sessions',
42 | 'django.contrib.messages',
43 | 'django.contrib.staticfiles',
44 | #'south',
45 | )
46 |
47 | MIDDLEWARE_CLASSES = (
48 | 'django.contrib.sessions.middleware.SessionMiddleware',
49 | 'django.middleware.common.CommonMiddleware',
50 | 'django.middleware.csrf.CsrfViewMiddleware',
51 | 'django.middleware.locale.LocaleMiddleware',
52 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
53 | 'fairy.middleware.SiteOff',
54 | 'django.contrib.messages.middleware.MessageMiddleware',
55 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
56 | 'pagination.middleware.PaginationMiddleware',
57 | )
58 |
59 | ROOT_URLCONF = 'fairy.urls'
60 |
61 | WSGI_APPLICATION = 'fairy.wsgi.application'
62 |
63 |
64 | # Database
65 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases
66 |
67 | DATABASES = {
68 | 'default': {
69 | 'ENGINE': 'django.db.backends.sqlite3',
70 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
71 | 'USER': '',
72 | 'PASSWORD': '',
73 | }
74 | }
75 |
76 | # Internationalization
77 | # https://docs.djangoproject.com/en/1.6/topics/i18n/
78 |
79 | LANGUAGE_CODE = 'en-us'
80 |
81 | TIME_ZONE = 'Asia/Shanghai'
82 |
83 | USE_I18N = True
84 |
85 | USE_L10N = True
86 |
87 | USE_TZ = True
88 |
89 |
90 | # Static files (CSS, JavaScript, Images)
91 | # https://docs.djangoproject.com/en/1.6/howto/static-files/
92 |
93 | STATIC_URL = '/static/'
94 |
95 | STATIC_ROOT = os.path.join(BASE_DIR, '/static/')
96 |
97 | STATICFILES_DIRS = (
98 | os.path.join(BASE_DIR, "static"),
99 | )
100 |
101 | TEMPLATE_DIRS = (
102 | os.path.join(BASE_DIR, 'template'),
103 | os.path.join(BASE_DIR, 'template/common'),
104 | os.path.join(BASE_DIR, 'template/widget'),
105 | )
106 |
107 | MAX_UPLOAD_SIZE = "524288"
108 |
109 | EMAIL_USE_TLS = True
110 | EMAIL_HOST = ''
111 | EMAIL_PORT = 465
112 | EMAIL_HOST_USER = ''
113 | EMAIL_HOST_PASSWORD = ''
114 |
115 | DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
116 | SERVER_EMAIL = EMAIL_HOST_USER
117 |
118 | LOCALE_PATHS = (os.path.join(BASE_DIR, 'locale'),)
119 |
--------------------------------------------------------------------------------
/fairy/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 | from fairy import settings
3 | from django.contrib import admin
4 | from django.conf.urls.static import static
5 | import forum
6 |
7 | admin.autodiscover()
8 |
9 | urlpatterns = patterns('',
10 | # Examples:
11 | # url(r'^$', 'fairy.views.home', name='home'),
12 | # url(r'^blog/', include('blog.urls')),
13 |
14 | url(r'^admin/', include(admin.site.urls)),
15 | url(r'', include('forum.urls')),
16 | url(r'^user/', include('account.urls')),
17 | url(r'^api/forum/', include(forum.urls.api_urlpatterns)),
18 | url(r'^panel/', include('panel.urls', namespace='panel', app_name='panel')),
19 | )
20 |
21 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
22 |
--------------------------------------------------------------------------------
/fairy/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for fairy project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "fairy.settings")
12 |
13 | from django.core.wsgi import get_wsgi_application
14 | application = get_wsgi_application()
15 |
--------------------------------------------------------------------------------
/forum/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/forum/__init__.py
--------------------------------------------------------------------------------
/forum/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from forum.models import topic, mention, notification, post, node
3 | # Register your models here.
4 |
5 |
6 | class post_inline(admin.StackedInline):
7 | model = post
8 | exclude = ['user', 'content_rendered']
9 | extra = 0
10 |
11 |
12 | class topic_admin(admin.ModelAdmin):
13 | list_display = ('title', 'time_created', 'user', 'click', 'last_replied')
14 | list_filter = ('time_created',)
15 | search_fields = ['title', 'user__username']
16 | inlines = [post_inline]
17 |
18 | admin.site.register(topic, topic_admin)
19 | admin.site.register(node)
20 |
--------------------------------------------------------------------------------
/forum/api.py:
--------------------------------------------------------------------------------
1 | from django.http import HttpResponse
2 | from django.core.files.storage import FileSystemStorage
3 | from fairy.settings import BASE_DIR
4 | from forum.models import topic, post, node
5 | import json
6 | import os
7 |
8 | UPLOAD_TO = os.path.join(BASE_DIR, 'static/upload')
9 | storage = FileSystemStorage(
10 | location=UPLOAD_TO,
11 | base_url='static/upload'
12 | )
13 |
14 |
15 | def topic_data(topic_id):
16 | t = topic.objects.get(id=topic_id)
17 | data=dict(id=t.id,
18 | title=t.title,
19 | content=t.content,
20 | create_time=t.time_created.isoformat(),
21 | user=dict(id=t.user.id,
22 | username=t.user.username),
23 | reply_count=t.reply_count,
24 | node_id=t.node.id,
25 | node_name=t.node.title,
26 | reply_id=[p.id for p in t.post_set.all()])
27 | return data
28 |
29 |
30 | def post_api(request ,post_id):
31 | p = post.objects.get(id=post_id)
32 | data = dict(id=p.id,
33 | content=p.content,
34 | topic_id=p.topic.id,
35 | user=dict(id=p.user.id,
36 | username=p.user.username),
37 | create_time=p.time_created.isoformat())
38 | return HttpResponse(json.dumps(data))
39 |
40 |
41 | def topics_api(request):
42 | data = {}
43 | for t in topic.objects.all()[:30]:
44 | data[str(t.id)] = topic_data(t.id)
45 | return HttpResponse(json.dumps(data))
46 |
47 |
48 | def topic_api(request, topic_id):
49 | data = topic_data(topic_id)
50 | return HttpResponse(json.dumps(data))
51 |
52 |
53 | def simditor_upload(request):
54 | f = request.FILES['upload_file']
55 | name = storage.save(storage.get_available_name(f.name), f)
56 | url = storage.url(name)
57 | data = {}
58 | data['file_path'] = url
59 | return HttpResponse(json.dumps(data), content_type="application/json")
60 |
--------------------------------------------------------------------------------
/forum/locale/zh_CN/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , 2014.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2014-07-06 13:28+0800\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 |
20 | #: templates/forum/append.html:7
21 | #, python-format
22 | msgid ""
23 | "\n"
24 | "\t\t\tcreate appendix to topic:%(topic)s \n"
26 | "\t\t\t"
27 | msgstr ""
28 | "\n"
29 | "\t\t\t创建主题 %(topic)s 的附言
9 | {% endblocktrans %}
10 |
11 |
12 |
13 | {% if messages %}
14 |
15 | {% for message in messages %}
16 |
17 | {{ message }}
18 |
19 | {% endfor %}
20 |
21 | {% endif %}
22 |
35 |
36 |
37 |
38 |
39 |
40 | {% endblock %}
41 | {% block right %}
42 | {% include 'node-info.html' %}
43 | {% include 'user-panel.html' %}
44 | {% include 'node.html' %}
45 | {% include 'links.html' %}
46 | {% endblock %}
47 | {% block footer_ext %}
48 |
49 | {% endblock %}
--------------------------------------------------------------------------------
/forum/templates/forum/create-topic.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% load i18n %}
3 | {% block left %}
4 |
5 |
10 |
11 | {% if messages %}
12 |
13 | {% for message in messages %}
14 |
15 | {{ message }}
16 |
17 | {% endfor %}
18 |
19 | {% endif %}
20 | {% if request.user.is_authenticated %}
21 |
40 |
41 |
42 |
43 | {% else %}
44 |
45 | {% trans 'please sign in before post ' %}
46 |
47 |
{% trans 'sign in' %}
48 | {% endif %}
49 |
50 |
51 | {% endblock %}
52 | {% block right %}
53 | {% include 'node-info.html' %}
54 | {% include 'user-panel.html' %}
55 | {% include 'node.html' %}
56 | {% include 'links.html' %}
57 | {% endblock %}
58 | {% block footer_ext %}
59 |
60 | {% endblock %}
--------------------------------------------------------------------------------
/forum/templates/forum/edit-topic.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% load i18n %}
3 | {% block left %}
4 |
5 |
6 |
7 | {% trans 'edit topic' %}-{{topic.title}}
8 |
9 |
10 |
11 | {% if messages %}
12 |
13 | {% for message in messages %}
14 |
15 | {{ message }}
16 |
17 | {% endfor %}
18 |
19 | {% endif %}
20 | {% if request.user.is_authenticated %}
21 |
40 |
41 |
42 |
43 | {% endif %}
44 |
45 |
46 | {% endblock %}
47 | {% block right %}
48 | {% include 'user-panel.html' %}
49 | {% include 'links.html' %}
50 | {% endblock %}
51 | {% block footer_ext %}
52 |
53 | {% endblock %}
--------------------------------------------------------------------------------
/forum/templates/forum/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% block header_ext%}
3 |
4 |
5 | {% endblock %}
6 | {% block left %}
7 | {% include 'topic-list.html' %}
8 | {% endblock %}
9 | {% block right %}
10 | {% include 'user-panel.html' %}
11 | {% include 'node.html' %}
12 | {% include 'links.html' %}
13 | {% include 'stat.html' %}
14 | {% endblock %}
--------------------------------------------------------------------------------
/forum/templates/forum/node-all.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% load i18n %}
3 | {% block left %}
4 |
5 |
6 |
{% trans 'node navigation' %}
7 |
8 |
9 |
10 | {% for category,node_list in nodes.items %}
11 |
12 |
13 |
14 | {{category}} |
15 |
16 | {% for node in node_list %}
17 |
18 |
19 | {{ node.title }}
20 |
21 |
22 | {% endfor %}
23 | |
24 |
25 |
26 |
27 | {% endfor %}
28 |
29 |
30 |
31 | {% endblock %}
32 | {% block right %}
33 | {% include 'user-panel.html' %}
34 | {% include 'node.html' %}
35 | {% include 'links.html' %}
36 | {% endblock %}
37 | {% block footer_ext %}
38 |
39 |
40 |
41 | {% endblock %}
--------------------------------------------------------------------------------
/forum/templates/forum/node-view.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% block left %}
3 | {% include 'topic-list.html' %}
4 | {% endblock %}
5 | {% block right %}
6 | {% include 'node-info.html' %}
7 | {% include 'user-panel.html' %}
8 | {% include 'node.html' %}
9 | {% include 'links.html' %}
10 | {% endblock %}
11 | {% block footer_ext %}
12 |
13 |
21 | {% endblock %}
--------------------------------------------------------------------------------
/forum/templates/forum/topic.html:
--------------------------------------------------------------------------------
1 | {% extends 'base-with-sidebar.html' %}
2 | {% load i18n %}
3 | {% block left %}
4 | {% load humanize %}
5 | {% load pagination_tags %}
6 | {% load settingsvalue %}
7 |
8 |
9 |
10 |

11 |
30 |
31 |
32 |
33 | {{topic.content_rendered | safe}}
34 |
35 | {% if topic.appendix_set.all %}
36 |
46 | {% endif %}
47 |
48 |
49 |
50 |
51 |
52 | {% if topic.reply_count %}
53 | {% blocktrans count reply_count=topic.reply_count %}
54 | {{reply_count}} reply
55 | {% plural %}
56 | {{reply_count}} replies
57 | {% endblocktrans %}
58 | {% else %}
59 | {% trans 'no replies yet' %}
60 | {% endif %}
61 |
62 |
63 | {% if topic.reply_count != 0 %}
64 |
65 | {% autopaginate posts 30 %}
66 | {% for r in posts %}
67 |
68 |
69 |

70 |
71 |
72 |
79 |
80 | {% if request.user.is_authenticated %}
81 |
reply
82 | {% endif %}
83 |
84 |
85 |
86 |
87 | {{r.content_rendered | safe}}
88 |
89 |
90 |
91 |
92 |
93 | {% endfor %}
94 |
95 |
98 | {% else %}
99 |
100 |
101 | {% trans 'no replies yet' %}
102 |
103 |
104 | {% endif %}
105 |
106 |
107 |
108 |
109 |
110 | {% trans 'reply' %}
111 |
112 |
113 |
114 | {% if messages %}
115 |
116 | {% for message in messages %}
117 |
118 | {{ message }}
119 |
120 | {% endfor %}
121 |
122 | {% endif %}
123 | {% if request.user.is_authenticated %}
124 |
138 |
139 |
140 |
141 | {% else %}
142 |
143 | {% trans 'please sign in before reply' %}
144 |
145 |
{% trans 'sign in' %}
146 | {% endif %}
147 |
148 |
149 |
150 | {% endblock %}
151 | {% block right %}
152 | {% include 'node-info.html' %}
153 | {% include 'user-panel.html' %}
154 | {% include 'node.html' %}
155 | {% include 'links.html' %}
156 | {% endblock %}
157 | {% block footer_ext %}
158 |
159 |
160 | {% endblock %}
161 |
--------------------------------------------------------------------------------
/forum/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/forum/templatetags/__init__.py
--------------------------------------------------------------------------------
/forum/templatetags/settingsvalue.py:
--------------------------------------------------------------------------------
1 | __author__ = 'TianShuo'
2 | from django import template
3 | from fairy import settings
4 | from fairy import conf
5 |
6 | register = template.Library()
7 |
8 |
9 | '''
10 | usage:
11 | first
12 | {% load settingsvalue %}
13 | then
14 | {% settings_value "MAX_UPLOAD_SIZE" %}
15 | or
16 | {% settings_value "logoname" %}
17 |
18 | '''
19 |
20 | @register.simple_tag
21 | def settings_value(name):
22 | return getattr(settings, name, "")
23 |
24 | @register.simple_tag
25 | def conf_value(name):
26 | return getattr(conf, name, "")
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/forum/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/forum/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 |
3 |
4 | api_urlpatterns = patterns('forum.api',
5 | url(r'^topic/(?P\d+)/$', 'topic_api', name='topic_api'),
6 | url(r'^topics/$', 'topics_api', name='topics_api'),
7 | url(r'^post/(?P\d+)/$', 'post_api', name='post_api'),
8 | url(r'^/simditor-upload/$', 'simditor_upload', name='simditor_upload'),
9 | )
10 |
11 |
12 | urlpatterns = patterns('forum.views',
13 | url(r'^$', 'index', name='index'),
14 |
15 | url(r'^topic/(?P\d+)/$', 'topic_view', name='topic_view'),
16 | url(r'^topic/(?P\d+)/reply/$', 'create_reply', name='create_reply'),
17 | url(r'^topic/(?P\d+)/append/$', 'add_appendix', name='add_appendix'),
18 | url(r'^topic/(?P\d+)/delete/$', 'del_topic', name='delete_topic'),
19 | url(r'^topic/(?P\d+)/edit/$', 'edit_topic', name='edit_topic'),
20 |
21 | url(r'^post/(?P\d+)/delete/$', 'del_reply', name='delete_post'),
22 |
23 | url(r'^node/$', 'node_all', name='node_all'),
24 | url(r'^node/(?P\d+)/$', 'node_view', name='node_view'),
25 | url(r'^node/(?P\d+)/create/$', 'create_topic', name='create_topic'),
26 |
27 | url(r'^search/(?P.*?)/$', 'search', name='search'),
28 |
29 | url(r'^recent/$', 'recent', name='recent'),
30 |
31 | url(r'^previewer/$', 'previewer', name='previewer'),
32 | )
33 |
--------------------------------------------------------------------------------
/forum/views.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from account.models import profile
3 | from django.contrib.admin.views.decorators import staff_member_required
4 | from django.contrib.auth.models import User
5 | from django.contrib import messages
6 | from django.core.context_processors import csrf
7 | from django.utils.translation import ugettext as _
8 | from django.core.urlresolvers import reverse
9 | from django.db.models import Q
10 | from django.http import HttpResponseRedirect, HttpResponse
11 | from django.shortcuts import render_to_response
12 | from django.template import RequestContext
13 | from fairy import conf
14 | from forum.models import topic, post, node, appendix
15 | import json
16 | import markdown
17 | import operator
18 | # Create your views here.
19 |
20 |
21 | def error(request, msg, back=None):
22 | return render_to_response('error.html', {'conf': conf, 'title': _('notice'),
23 | 'msg': msg,
24 | 'back': back,
25 | 'request': request, })
26 |
27 |
28 | def previewer(request):
29 | c = request.REQUEST['content']
30 | md = {}
31 | md['marked'] = markdown.markdown(c, ['codehilite'], safe_mode='escape')
32 | return HttpResponse(json.dumps(md))
33 |
34 |
35 | def index(request):
36 | conf.nodes = node.objects.all()
37 | conf.user_count = profile.objects.count()
38 | conf.topic_count = topic.objects.count()
39 | conf.post_count = post.objects.count()
40 | topics = topic.objects.all().filter(deleted=False).order_by('-last_replied')[0:30]
41 | post_list_title = _('latest topics')
42 | return render_to_response('forum/index.html', {'topics': topics, 'title': _('home'),
43 | 'request': request,
44 | 'post_list_title': post_list_title,
45 | 'conf': conf})
46 |
47 |
48 | def topic_view(request, topic_id):
49 | t = topic.objects.get(id=topic_id)
50 | t.click += 1
51 | t.save()
52 | n = t.node
53 | posts = t.post_set.filter(deleted=False)
54 | try:
55 | page = request.GET['page']
56 | except:
57 | page = None
58 | if page == '1':
59 | page = None
60 | return render_to_response('forum/topic.html', {'conf': conf, 'title': t.title,
61 | 'request': request,
62 | 'topic': t,
63 | 'node': n,
64 | 'pager': page,
65 | 'posts': posts
66 | },
67 | context_instance=RequestContext(request))
68 |
69 |
70 | def create_reply(request, topic_id):
71 | if request.method == 'POST':
72 | t = topic.objects.get(id=topic_id)
73 | r = post()
74 | r.topic = t
75 | if request.POST['content']:
76 | r.content = request.POST['content']
77 | else:
78 | messages.add_message(request, messages.WARNING, _('content cannot be empty'))
79 | return HttpResponseRedirect(reverse('topic_view', kwargs={'topic_id':topic_id}))
80 | r.user = request.user
81 | r.save()
82 | return HttpResponseRedirect(reverse('topic_view', kwargs={'topic_id': t.id}))
83 | elif request.method == 'GET':
84 | return error(request, 'don\'t get')
85 |
86 |
87 | def node_view(request, node_id):
88 | try:
89 | page = request.GET['page']
90 | except:
91 | page = None
92 | if page == '1':
93 | page = None
94 | n = node.objects.get(id=node_id)
95 | topics = topic.objects.filter(node=n,deleted=False)
96 | return render_to_response('forum/node-view.html', {'request': request, 'title': n.title,
97 | 'conf': conf,
98 | 'topics': topics,
99 | 'node': n,
100 | 'node_view': True,
101 | 'pager': page,})
102 |
103 |
104 | def create_topic(request, node_id):
105 | n = node.objects.get(id=node_id)
106 | if request.method == 'GET':
107 | return render_to_response('forum/create-topic.html', {'node': n, 'title': _('create topic'),
108 | 'request': request,
109 | 'conf': conf},
110 | context_instance=RequestContext(request))
111 | elif request.method == 'POST':
112 | t = topic()
113 | t.content = request.POST.get('content') or ''
114 | t.node = n
115 | t.title = request.POST['title']
116 | if not t.title:
117 | messages.add_message(request, messages.WARNING, _('title cannot be empty'))
118 | return HttpResponseRedirect(reverse('create_topic', kwargs={'node_id':node_id}))
119 | if not request.user.is_authenticated():
120 | return error(request, '请登陆', reverse('signin'))
121 | t.user = request.user
122 | t.save()
123 | return HttpResponseRedirect(reverse('topic_view',
124 | kwargs={'topic_id': t.id}))
125 |
126 |
127 | def search(request, keyword):
128 | keys = keyword.split(' ')
129 | condition = reduce(operator.and_,
130 | (Q(title__contains=x) for x in keys))
131 | topics = topic.objects.filter(condition)
132 | try:
133 | page = request.GET['page']
134 | except:
135 | page = None
136 | if page == '1':
137 | page = None
138 | return render_to_response('forum/index.html', {'request': request, 'title': _('%s-search result') % (keyword),
139 | 'conf': conf, 'pager': page,
140 | 'topics': topics,
141 | 'post_list_title': _('search %s') % (keyword), })
142 |
143 |
144 | def recent(request):
145 | try:
146 | page = request.GET['page']
147 | except:
148 | page = None
149 | if page == '1':
150 | page = None
151 | topics = topic.objects.all().filter(deleted=False)
152 | return render_to_response('forum/index.html', {'request': request, 'title': _('latest topics'),
153 | 'conf': conf,
154 | 'topics': topics,
155 | 'recent': 'reccent',
156 | 'pager': page,
157 | 'post_list_title': _('latest posted topics'), })
158 |
159 |
160 | @staff_member_required
161 | def del_reply(request, post_id):
162 | p = post.objects.get(id=post_id)
163 | t_id = p.topic.id
164 | p.deleted = True
165 | p.save()
166 | p.topic.save()
167 | return HttpResponseRedirect(reverse('topic_view', kwargs={'topic_id': t_id}))
168 |
169 |
170 | def del_topic(request, topic_id):
171 | t = topic.objects.get(id=topic_id)
172 | if request.user != t.user and (not request.user.is_superuser):
173 | return HttpResponseRedirect(reverse('topic_view', kwargs={'topic_id': t.id}))
174 | n_id = t.node.id
175 | t.deleted = True
176 | t.save()
177 | return HttpResponseRedirect(reverse('node_view', kwargs={'node_id': n_id}))
178 |
179 |
180 | def edit_topic(request, topic_id):
181 | t = topic.objects.get(id=topic_id)
182 | if request.user != t.user and (not request.user.is_superuser):
183 | return HttpResponseRedirect(reverse('topic_view', kwargs={'topic_id': t.id}))
184 | if request.method == 'GET':
185 | return render_to_response('forum/edit-topic.html',{'request': request, 'conf': conf,
186 | 'topic': t,
187 | 'title': _('topic edit')},
188 | context_instance=RequestContext(request))
189 | elif request.method == 'POST':
190 | t.title = request.POST['title']
191 | t.content = request.POST['content']
192 | if not t.title:
193 | messages.add_message(request, messages.WARNING, _('title cannot be empty'))
194 | return HttpResponseRedirect(reverse('edit_topic', kwargs={'topic_id': t.id}))
195 | t.save()
196 | return HttpResponseRedirect(reverse('topic_view', kwargs={'topic_id': t.id}))
197 |
198 |
199 | def add_appendix(request, topic_id):
200 | t = topic.objects.get(id=topic_id)
201 | n = t.node
202 | if request.user != t.user:
203 | return error(request, _('you cannot add appendix to other people\'s topic' ))
204 | if request.method == 'GET':
205 | return render_to_response('forum/append.html', {'request': request, 'title': _('add appendix'),
206 | 'node': n, 'conf': conf,
207 | 'topic': t, },
208 | context_instance=RequestContext(request))
209 | elif request.method == 'POST':
210 | a = appendix()
211 | a.content = request.POST['content']
212 | if not a.content:
213 | messages.add_message(request, messages.WARNING, _('content cannot be empty'))
214 | return HttpResponseRedirect(reverse('add_appendix', kwargs={'topic_id': t.id}))
215 | a.topic = t
216 | a.save()
217 | return HttpResponseRedirect(reverse('topic_view', kwargs={'topic_id': t.id}))
218 |
219 |
220 | def node_all(request):
221 | nodes = {}
222 | nodes[u'分类1'] = list(node.objects.filter(id__in=[1]).all())
223 | return render_to_response('forum/node-all.html', {'request': request, 'title': _('all nodes'),
224 | 'conf': conf,
225 | 'nodes': nodes, })
226 |
--------------------------------------------------------------------------------
/locale/zh_CN/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2014-07-05 17:14+0800\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 |
20 | #: template/common/base.html:17
21 | #, python-format
22 | msgid "page %(pager)s"
23 | msgstr "第%(pager)s页"
24 |
25 | #: template/common/base.html:49
26 | msgid "recent post"
27 | msgstr "最近主题"
28 |
29 | #: template/common/base.html:50
30 | msgid "all nodes"
31 | msgstr "所有节点"
32 |
33 | #: template/common/base.html:55
34 | msgid "enter key words"
35 | msgstr "输入关键词"
36 |
37 | #: template/common/base.html:58
38 | msgid "Search"
39 | msgstr "搜索"
40 |
41 | #: template/widget/links.html:5
42 | msgid "links"
43 | msgstr "链接"
44 |
45 | #: template/widget/node.html:5
46 | msgid "nodes"
47 | msgstr "节点"
48 |
49 | #: template/widget/stat.html:5
50 | msgid "site stat"
51 | msgstr "站点统计"
52 |
53 | #: template/widget/stat.html:10
54 | msgid "number of users"
55 | msgstr "用户数量"
56 |
57 | #: template/widget/stat.html:13
58 | msgid "number of topics"
59 | msgstr "话题数量"
60 |
61 | #: template/widget/stat.html:16
62 | msgid "number of replies"
63 | msgstr "回复数目"
64 |
65 | #: template/widget/topic-list.html:11
66 | msgid "home"
67 | msgstr "主页"
68 |
69 | #: template/widget/topic-list.html:15
70 | msgid "new topic"
71 | msgstr "新话题"
72 |
73 | #: template/widget/topic-list.html:21
74 | msgid "sorry, but we can't find the topics you need"
75 | msgstr "抱歉,没有找到你想要的内容"
76 |
77 | #: template/widget/topic-list.html:36
78 | #, python-format
79 | msgid "%(clicks)s clicks"
80 | msgstr "%(clicks)s次点击"
81 |
82 | #: template/widget/user-panel.html:16
83 | msgid "info"
84 | msgstr "信息"
85 |
86 | #: template/widget/user-panel.html:19
87 | msgid "settings"
88 | msgstr "设置"
89 |
90 | #: template/widget/user-panel.html:22
91 | msgid "log out"
92 | msgstr "登出"
93 |
94 | #: template/widget/user-panel.html:33
95 | msgid "sign in"
96 | msgstr "登陆"
97 |
98 | #: template/widget/user-panel.html:36
99 | msgid "reg"
100 | msgstr "注册"
101 |
102 | #: template/widget/user-panel.html:54
103 | msgid "new messages"
104 | msgstr "未读消息"
105 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "fairy.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
--------------------------------------------------------------------------------
/panel/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/panel/__init__.py
--------------------------------------------------------------------------------
/panel/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/panel/locale/zh_CN/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , 2014.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2014-07-05 17:14+0800\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 |
20 | #: panel/templates/panel/base.html:11 panel/templates/panel/base.html.py:39
21 | msgid "admin"
22 | msgstr "后台"
23 |
24 | #: panel/templates/panel/base.html:55 panel/templates/panel/user-manage.html:6
25 | #: panel/views.py:22
26 | msgid "user management"
27 | msgstr "用户管理"
28 |
29 | #: panel/templates/panel/base.html:58
30 | #: panel/templates/panel/user-manage.html:14
31 | msgid "all users"
32 | msgstr "所有用户"
33 |
34 | #: panel/templates/panel/base.html:65 panel/templates/panel/node-manage.html:6
35 | #: panel/templates/panel/node-manage.html:14 panel/views.py:108
36 | msgid "node management"
37 | msgstr "节点管理"
38 |
39 | #: panel/templates/panel/base.html:68
40 | msgid "all nodes"
41 | msgstr "所有节点"
42 |
43 | #: panel/templates/panel/base.html:72 panel/templates/panel/node-create.html:7
44 | #: panel/views.py:167
45 | msgid "create node"
46 | msgstr "创建节点"
47 |
48 | #: panel/templates/panel/base.html:79
49 | msgid "content management"
50 | msgstr "内容管理"
51 |
52 | #: panel/templates/panel/base.html:82
53 | msgid "all topics"
54 | msgstr "所有话题"
55 |
56 | #: panel/templates/panel/node-create.html:16
57 | msgid "new node"
58 | msgstr "新节点"
59 |
60 | #: panel/templates/panel/node-create.html:24
61 | #: panel/templates/panel/node-edit.html:24
62 | #: panel/templates/panel/topic-manage.html:24
63 | msgid "node title"
64 | msgstr "节点标题"
65 |
66 | #: panel/templates/panel/node-create.html:28
67 | #: panel/templates/panel/node-edit.html:28
68 | msgid "node description"
69 | msgstr "节点描述"
70 |
71 | #: panel/templates/panel/node-create.html:37
72 | #: panel/templates/panel/node-edit.html:37
73 | #: panel/templates/panel/topic-edit.html:49
74 | #: panel/templates/panel/user-edit.html:98
75 | msgid "submit"
76 | msgstr "提交"
77 |
78 | #: panel/templates/panel/node-edit.html:7 panel/views.py:152
79 | msgid "edit node"
80 | msgstr "编辑节点"
81 |
82 | #: panel/templates/panel/node-manage.html:22
83 | #: panel/templates/panel/topic-edit.html:24
84 | #: panel/templates/panel/topic-manage.html:22
85 | msgid "title"
86 | msgstr "标题"
87 |
88 | #: panel/templates/panel/node-manage.html:23
89 | #: panel/templates/panel/topic-manage.html:25
90 | #: panel/templates/panel/user-manage.html:24 panel/views.py:65
91 | #: panel/views.py:141 panel/views.py:238
92 | msgid "edit"
93 | msgstr "编辑"
94 |
95 | #: panel/templates/panel/topic-edit.html:7 panel/views.py:191
96 | msgid "edit topic"
97 | msgstr "编辑话题"
98 |
99 | #: panel/templates/panel/topic-edit.html:28
100 | msgid "author"
101 | msgstr "作者"
102 |
103 | #: panel/templates/panel/topic-edit.html:32
104 | msgid "node"
105 | msgstr "节点"
106 |
107 | #: panel/templates/panel/topic-edit.html:36
108 | msgid "content"
109 | msgstr "内容"
110 |
111 | #: panel/templates/panel/topic-edit.html:40
112 | msgid "order(0-99,default:99)"
113 | msgstr "排序"
114 |
115 | #: panel/templates/panel/topic-manage.html:6
116 | #: panel/templates/panel/topic-manage.html:14 panel/views.py:182
117 | msgid "topic management"
118 | msgstr "话题管理"
119 |
120 | #: panel/templates/panel/topic-manage.html:23
121 | #: panel/templates/panel/user-edit.html:25
122 | #: panel/templates/panel/user-manage.html:22
123 | msgid "username"
124 | msgstr "用户名"
125 |
126 | #: panel/templates/panel/topic-manage.html:32
127 | msgid "delete selected"
128 | msgstr "删除选中项"
129 |
130 | #: panel/templates/panel/user-edit.html:7 panel/views.py:77
131 | msgid "edit user"
132 | msgstr "编辑用户"
133 |
134 | #: panel/templates/panel/user-edit.html:23
135 | msgid "identity"
136 | msgstr "身份信息"
137 |
138 | #: panel/templates/panel/user-edit.html:29
139 | msgid "password"
140 | msgstr "密码"
141 |
142 | #: panel/templates/panel/user-edit.html:30
143 | msgid "leave empty will not change the current password"
144 | msgstr "留空不修改"
145 |
146 | #: panel/templates/panel/user-edit.html:33
147 | #: panel/templates/panel/user-manage.html:23
148 | msgid "email"
149 | msgstr "Email"
150 |
151 | #: panel/templates/panel/user-edit.html:40
152 | msgid "user status"
153 | msgstr "用户状态"
154 |
155 | #: panel/templates/panel/user-edit.html:44
156 | #: panel/templates/panel/user-edit.html:46
157 | msgid "active"
158 | msgstr "激活的"
159 |
160 | #: panel/templates/panel/user-edit.html:53
161 | #: panel/templates/panel/user-edit.html:55
162 | msgid "superuser"
163 | msgstr "超级用户"
164 |
165 | #: panel/templates/panel/user-edit.html:63
166 | msgid "user profile"
167 | msgstr "用户资料"
168 |
169 | #: panel/templates/panel/user-edit.html:65
170 | msgid "nickname"
171 | msgstr "昵称"
172 |
173 | #: panel/templates/panel/user-edit.html:69
174 | msgid "location"
175 | msgstr "位置"
176 |
177 | #: panel/templates/panel/user-edit.html:73
178 | msgid "website"
179 | msgstr "网站"
180 |
181 | #: panel/templates/panel/user-edit.html:83
182 | #: panel/templates/panel/user-edit.html:87
183 | #: panel/templates/panel/user-edit.html:89
184 | msgid "use Gravatar"
185 | msgstr "使用Gravatar"
186 |
187 | #: panel/views.py:17
188 | msgid "home"
189 | msgstr "主页"
190 |
--------------------------------------------------------------------------------
/panel/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/panel/templates/panel/base.html:
--------------------------------------------------------------------------------
1 | {% load settingsvalue %}
2 | {% load i18n %}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {{title}}-{% trans 'admin' %}-{% conf_value "sitename" %}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
95 |
96 |
97 | {% block main %}
98 | {% endblock %}
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | {% block footer_ext %}{% endblock %}
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/panel/templates/panel/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'panel/base.html' %}
2 |
3 |
--------------------------------------------------------------------------------
/panel/templates/panel/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Start Bootstrap - SB Admin Version 2.0 Demo
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
Please Sign In
28 |
29 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/panel/templates/panel/node-create.html:
--------------------------------------------------------------------------------
1 | {% extends 'panel/base.html' %}
2 | {% load i18n %}
3 |
4 | {% block main %}
5 |
11 |
12 |
13 |
14 |
15 |
16 | {% trans 'new node' %}
17 |
18 |
19 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | {% endblock %}
47 |
--------------------------------------------------------------------------------
/panel/templates/panel/node-edit.html:
--------------------------------------------------------------------------------
1 | {% extends 'panel/base.html' %}
2 |
3 | {% load i18n %}
4 | {% block main %}
5 |
11 |
12 |
13 |
14 |
15 |
16 | {{node.title}}
17 |
18 |
19 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | {% endblock %}
47 |
--------------------------------------------------------------------------------
/panel/templates/panel/node-manage.html:
--------------------------------------------------------------------------------
1 | {% extends 'panel/base.html' %}
2 | {% load i18n %}
3 | {% block main %}
4 |
9 |
10 |
11 |
12 |
13 |
14 | {% trans 'node management' %}
15 |
16 |
17 |
18 |
19 |
20 |
21 | id |
22 | {% trans 'title' %} |
23 | {% trans 'edit' %} |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | {% endblock %}
35 |
36 | {% block footer_ext %}
37 |
38 |
39 |
40 |
41 |
70 | {% endblock %}
71 |
--------------------------------------------------------------------------------
/panel/templates/panel/topic-edit.html:
--------------------------------------------------------------------------------
1 | {% extends 'panel/base.html' %}
2 | {% load i18n %}
3 |
4 | {% block main %}
5 |
11 |
12 |
13 |
14 |
15 |
16 | {{topic.title}}
17 |
18 |
19 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | {% endblock %}
59 |
60 | {% block footer_ext %}
61 |
67 |
82 | {% endblock %}
83 |
--------------------------------------------------------------------------------
/panel/templates/panel/topic-manage.html:
--------------------------------------------------------------------------------
1 | {% extends 'panel/base.html' %}
2 | {% load i18n %}
3 | {% block main %}
4 |
9 |
10 |
11 |
12 |
13 |
14 | {% trans 'topic management' %}
15 |
16 |
17 |
18 |
19 |
20 |
21 | id |
22 | {% trans 'title' %} |
23 | {% trans 'username' %} |
24 | {% trans 'node title' %} |
25 | {% trans 'edit' %} |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
{% trans 'delete selected' %}
33 |
34 |
35 |
36 |
37 | {% endblock %}
38 |
39 | {% block footer_ext %}
40 |
41 |
42 |
43 |
44 |
92 | {% endblock %}
93 |
--------------------------------------------------------------------------------
/panel/templates/panel/user-edit.html:
--------------------------------------------------------------------------------
1 | {% extends 'panel/base.html' %}
2 | {% load i18n %}
3 |
4 | {% block main %}
5 |
11 |
12 |
13 |
14 |
15 |
16 | {{user.username}}
17 |
18 |
19 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | {% endblock %}
108 |
--------------------------------------------------------------------------------
/panel/templates/panel/user-manage.html:
--------------------------------------------------------------------------------
1 | {% extends 'panel/base.html' %}
2 | {% load i18n %}
3 | {% block main %}
4 |
9 |
10 |
11 |
12 |
13 |
14 | {% trans 'all users' %}
15 |
16 |
17 |
18 |
19 |
20 |
21 | id |
22 | {% trans 'username' %} |
23 | {% trans 'email' %} |
24 | {% trans 'edit' %} |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | {% endblock %}
36 |
37 | {% block footer_ext %}
38 |
39 |
40 |
41 |
42 |
72 | {% endblock %}
73 |
--------------------------------------------------------------------------------
/panel/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/panel/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, url
2 |
3 | urlpatterns = patterns('panel.views',
4 | url(r'^$', 'index', name='index'),
5 |
6 | url(r'^user/all/$', 'user_manage', name="user_manage"),
7 | url(r'^user/(?P\d+)/edit/$', 'user_edit', name="user_edit"),
8 | url(r'^user/all/data-ss/$', 'user_table_ss', name="user_table_ss"),
9 |
10 | url(r'^node/all/$', 'node_manage', name="node_manage"),
11 | url(r'^node/create/$', 'node_create', name="node_create"),
12 | url(r'^node/all/data-ss/$', 'node_table_ss', name="node_table_ss"),
13 | url(r'^node/(?P\d+)/edit/$', 'node_edit', name="node_edit"),
14 |
15 | url(r'^topic/all/$', 'topic_manage', name="topic_manage"),
16 | #url(r'^topic/(?P\d+)/edit/$', 'topic_edit', name="topic_edit"),
17 | url(r'^topic/all/data-ss/$', 'topic_table_ss', name="topic_table_ss"),
18 | url(r'^topic/(?P\d+)/$', 'topic_edit', name="topic_edit"),
19 |
20 | url(r'^ajax/node/$', 'node_title_ajax', name="node_title_ajax"),
21 | url(r'^ajax/topic/bulk-delete/$', 'topic_bulk_delete', name="topic_bulk_delete"),
22 | )
23 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Markdown==2.4
2 | Pygments==1.6
3 | django-pagination==1.0.7
4 | pillow==2.4.0
--------------------------------------------------------------------------------
/static/css/base.css:
--------------------------------------------------------------------------------
1 | a {
2 | text-decoration: none !important;
3 | }
4 |
5 | .list-post-title, .list-meta {
6 | margin: 0px;
7 | }
8 |
9 | .list-post-title {
10 | font-size: 120%;
11 | padding-top: 5px;
12 | }
13 |
14 | .avatar-middle {
15 | width: 48px;
16 | height: 48px;
17 | }
18 |
19 | .avatar-large {
20 | width: 80px;
21 | height: 80px;
22 | }
23 |
24 | .list-reply-count {
25 | margin-top: 15px;
26 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
27 | }
28 |
29 | /*.label {*/
30 | /*border: 1px solid #000;*/
31 | /*}*/
32 |
33 | .label a {
34 | color: #F5F5F5;
35 | }
36 |
37 | .label-node {
38 | display: inline-block;
39 | line-height: 1.6;
40 | background: #999;
41 | margin-bottom: 10px;
42 | }
43 |
44 | h3.list-post-title a {
45 | text-decoration: none;
46 | color: #333;
47 | }
48 |
49 | .list-topic-item:hover {
50 | background-color: #f5f5f5;
51 | }
52 |
53 | .list-count::before, .list-time::before, .list-username::before, .list-reply-count-xs::before {
54 | content: '\2022';
55 | margin-right: 5px;
56 | }
57 |
58 | .list-username a {
59 | color: #666;
60 | }
61 |
62 | .list-meta {
63 | color: #aaa;
64 | }
65 |
66 | footer {
67 | padding-top: 20px;
68 | }
69 |
70 | #wrap {
71 | /**background-image: url("/static/image/bg.png");**/
72 | background-color: #E0E0E0;
73 | padding-top: 70px;
74 | }
75 |
76 | ul.pager {
77 | margin: 0px;
78 | }
79 |
80 | .padtop {
81 | margin-top: 5px;
82 | }
83 |
84 | .list-time {
85 | color: #aaa
86 | }
87 |
88 | /* Sticky footer styles
89 | -------------------------------------------------- */
90 |
91 | html, body {
92 | height: 100%;
93 | /* The html and body elements cannot have any padding or margin. */
94 | }
95 |
96 | /* Wrapper for page content to push down footer */
97 | #wrap {
98 | min-height: 100%;
99 | height: auto;
100 | /* Negative indent footer by its height */
101 | margin-bottom: -50px;
102 | /* Pad bottom by footer height */
103 | padding-bottom: 50px;
104 | }
105 |
106 | /* Set the fixed height of the footer here */
107 | footer {
108 | height: 50px;
109 | background-color: #FEFEFE;
110 | }
111 |
112 | #content {
113 | height: 120px;
114 | }
115 |
116 | .pagination {
117 | margin: 0px;
118 | }
119 |
120 | .appendix-meta {
121 | font-size: 12px;
122 | line-height: 120%;
123 | text-align: left;
124 | color: #ccc;
125 | }
126 |
127 | .list-appendix-item {
128 | background: #FFFFF9;
129 | border-left: 3px solid #FFFBC1 !important;
130 | }
131 |
132 | .panel-body {
133 | padding: 15px;
134 | }
135 |
136 | .panel-body:before, .panel-body:after {
137 | display: table;
138 | content: " ";
139 | }
140 |
141 | .panel-body:after {
142 | clear: both;
143 | }
144 |
145 | .panel-body:before, .panel-body:after {
146 | display: table;
147 | content: " ";
148 | }
149 |
150 | .panel-body:after {
151 | clear: both;
152 | }
153 |
154 | .list-meta-node {
155 | background-color: #F5F5F5;
156 | }
157 |
158 | .list-meta-node a {
159 | color: #AAA;
160 | }
161 |
162 | .list-reply-count {
163 | background-color: #C0C0C0;
164 | }
165 |
166 | .topic-heading {
167 | background-color: #FFF !important;
168 | }
169 |
170 | .list-post-title-in-topic {
171 | font-size: 180%;
172 | padding-top: 5px;
173 | padding-bottom: 5px;
174 | margin-top: 0px;
175 | }
176 |
177 | @media (max-width: 768px) {
178 | .list-reply-count-xs {
179 | display: inline-block !important;
180 | }
181 | .list-username {
182 | display: none;
183 | }
184 | }
185 |
186 | .topic-breadcrumb, .topic-breadcrumb a {
187 | margin: 0px;
188 | color: #AAAAAA;
189 | }
190 |
191 | body {
192 | font-family: "WenQuanYi Micro Hei", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;
193 | }
194 |
195 | html, html a {
196 | -webkit-font-smoothing: antialiased;
197 | text-shadow: 1px 1px 1px rgba(0,0,0,0.004);
198 | }
199 |
200 | .title-breadcrumb a {
201 | color: #428bca;
202 | }
203 |
204 | .title-breadcrumb span:before {
205 | content: "/\00a0";
206 | padding: 0 5px;
207 | color: #ccc;
208 | }
209 |
210 | a.add-new-btn {
211 | line-height: 12px;
212 | color: #FFF;
213 | }
214 |
215 | p > img {
216 | max-width: 100%;
217 | }
218 |
--------------------------------------------------------------------------------
/static/css/codehilite.css:
--------------------------------------------------------------------------------
1 | .hll { background-color: #ffffcc }
2 | .c { color: #408080; font-style: italic } /* Comment */
3 | .err { border: 1px solid #FF0000 } /* Error */
4 | .k { color: #008000; font-weight: bold } /* Keyword */
5 | .o { color: #666666 } /* Operator */
6 | .cm { color: #408080; font-style: italic } /* Comment.Multiline */
7 | .cp { color: #BC7A00 } /* Comment.Preproc */
8 | .c1 { color: #408080; font-style: italic } /* Comment.Single */
9 | .cs { color: #408080; font-style: italic } /* Comment.Special */
10 | .gd { color: #A00000 } /* Generic.Deleted */
11 | .ge { font-style: italic } /* Generic.Emph */
12 | .gr { color: #FF0000 } /* Generic.Error */
13 | .gh { color: #000080; font-weight: bold } /* Generic.Heading */
14 | .gi { color: #00A000 } /* Generic.Inserted */
15 | .go { color: #888888 } /* Generic.Output */
16 | .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
17 | .gs { font-weight: bold } /* Generic.Strong */
18 | .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
19 | .gt { color: #0044DD } /* Generic.Traceback */
20 | .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
21 | .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
22 | .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
23 | .kp { color: #008000 } /* Keyword.Pseudo */
24 | .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
25 | .kt { color: #B00040 } /* Keyword.Type */
26 | .m { color: #666666 } /* Literal.Number */
27 | .s { color: #BA2121 } /* Literal.String */
28 | .na { color: #7D9029 } /* Name.Attribute */
29 | .nb { color: #008000 } /* Name.Builtin */
30 | .nc { color: #0000FF; font-weight: bold } /* Name.Class */
31 | .no { color: #880000 } /* Name.Constant */
32 | .nd { color: #AA22FF } /* Name.Decorator */
33 | .ni { color: #999999; font-weight: bold } /* Name.Entity */
34 | .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
35 | .nf { color: #0000FF } /* Name.Function */
36 | .nl { color: #A0A000 } /* Name.Label */
37 | .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
38 | .nt { color: #008000; font-weight: bold } /* Name.Tag */
39 | .nv { color: #19177C } /* Name.Variable */
40 | .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
41 | .w { color: #bbbbbb } /* Text.Whitespace */
42 | .mf { color: #666666 } /* Literal.Number.Float */
43 | .mh { color: #666666 } /* Literal.Number.Hex */
44 | .mi { color: #666666 } /* Literal.Number.Integer */
45 | .mo { color: #666666 } /* Literal.Number.Oct */
46 | .sb { color: #BA2121 } /* Literal.String.Backtick */
47 | .sc { color: #BA2121 } /* Literal.String.Char */
48 | .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
49 | .s2 { color: #BA2121 } /* Literal.String.Double */
50 | .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
51 | .sh { color: #BA2121 } /* Literal.String.Heredoc */
52 | .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
53 | .sx { color: #008000 } /* Literal.String.Other */
54 | .sr { color: #BB6688 } /* Literal.String.Regex */
55 | .s1 { color: #BA2121 } /* Literal.String.Single */
56 | .ss { color: #19177C } /* Literal.String.Symbol */
57 | .bp { color: #008000 } /* Name.Builtin.Pseudo */
58 | .vc { color: #19177C } /* Name.Variable.Class */
59 | .vg { color: #19177C } /* Name.Variable.Global */
60 | .vi { color: #19177C } /* Name.Variable.Instance */
61 | .il { color: #666666 } /* Literal.Number.Integer.Long */
62 |
--------------------------------------------------------------------------------
/static/fonts/.directory:
--------------------------------------------------------------------------------
1 | [Dolphin]
2 | PreviewsShown=true
3 | Timestamp=2014,2,19,23,27,30
4 | Version=3
5 |
--------------------------------------------------------------------------------
/static/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/static/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/static/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/static/image/Connect_logo_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/image/Connect_logo_7.png
--------------------------------------------------------------------------------
/static/image/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/image/bg.png
--------------------------------------------------------------------------------
/static/image/pixels.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/image/pixels.png
--------------------------------------------------------------------------------
/static/js/html5shiv.js:
--------------------------------------------------------------------------------
1 | /*
2 | HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x";
6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);
8 | if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d #mq-test-1 { width: 42px; }',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b li {
19 | position: relative;
20 | margin-bottom: 20px;
21 | }
22 |
23 | .timeline > li:before,
24 | .timeline > li:after {
25 | content: " ";
26 | display: table;
27 | }
28 |
29 | .timeline > li:after {
30 | clear: both;
31 | }
32 |
33 | .timeline > li:before,
34 | .timeline > li:after {
35 | content: " ";
36 | display: table;
37 | }
38 |
39 | .timeline > li:after {
40 | clear: both;
41 | }
42 |
43 | .timeline > li > .timeline-panel {
44 | float: left;
45 | position: relative;
46 | width: 46%;
47 | padding: 20px;
48 | border: 1px solid #d4d4d4;
49 | border-radius: 2px;
50 | -webkit-box-shadow: 0 1px 6px rgba(0,0,0,0.175);
51 | box-shadow: 0 1px 6px rgba(0,0,0,0.175);
52 | }
53 |
54 | .timeline > li > .timeline-panel:before {
55 | content: " ";
56 | display: inline-block;
57 | position: absolute;
58 | top: 26px;
59 | right: -15px;
60 | border-top: 15px solid transparent;
61 | border-right: 0 solid #ccc;
62 | border-bottom: 15px solid transparent;
63 | border-left: 15px solid #ccc;
64 | }
65 |
66 | .timeline > li > .timeline-panel:after {
67 | content: " ";
68 | display: inline-block;
69 | position: absolute;
70 | top: 27px;
71 | right: -14px;
72 | border-top: 14px solid transparent;
73 | border-right: 0 solid #fff;
74 | border-bottom: 14px solid transparent;
75 | border-left: 14px solid #fff;
76 | }
77 |
78 | .timeline > li > .timeline-badge {
79 | z-index: 100;
80 | position: absolute;
81 | top: 16px;
82 | left: 50%;
83 | width: 50px;
84 | height: 50px;
85 | margin-left: -25px;
86 | border-radius: 50% 50% 50% 50%;
87 | text-align: center;
88 | font-size: 1.4em;
89 | line-height: 50px;
90 | color: #fff;
91 | background-color: #999999;
92 | }
93 |
94 | .timeline > li.timeline-inverted > .timeline-panel {
95 | float: right;
96 | }
97 |
98 | .timeline > li.timeline-inverted > .timeline-panel:before {
99 | right: auto;
100 | left: -15px;
101 | border-right-width: 15px;
102 | border-left-width: 0;
103 | }
104 |
105 | .timeline > li.timeline-inverted > .timeline-panel:after {
106 | right: auto;
107 | left: -14px;
108 | border-right-width: 14px;
109 | border-left-width: 0;
110 | }
111 |
112 | .timeline-badge.primary {
113 | background-color: #2e6da4 !important;
114 | }
115 |
116 | .timeline-badge.success {
117 | background-color: #3f903f !important;
118 | }
119 |
120 | .timeline-badge.warning {
121 | background-color: #f0ad4e !important;
122 | }
123 |
124 | .timeline-badge.danger {
125 | background-color: #d9534f !important;
126 | }
127 |
128 | .timeline-badge.info {
129 | background-color: #5bc0de !important;
130 | }
131 |
132 | .timeline-title {
133 | margin-top: 0;
134 | color: inherit;
135 | }
136 |
137 | .timeline-body > p,
138 | .timeline-body > ul {
139 | margin-bottom: 0;
140 | }
141 |
142 | .timeline-body > p + p {
143 | margin-top: 5px;
144 | }
--------------------------------------------------------------------------------
/static/panel/css/sb-admin.css:
--------------------------------------------------------------------------------
1 | /* Global Styles */
2 |
3 | /* ------------------------------- */
4 |
5 | body {
6 | background-color: #f8f8f8;
7 | padding-top: 100px;
8 | }
9 |
10 | @media(min-width:768px) {
11 | body {
12 | padding-top: 50px;
13 | }
14 | }
15 |
16 | /* Wrappers */
17 |
18 | /* ------------------------------- */
19 |
20 | #wrapper {
21 | width: 100%;
22 | }
23 |
24 | #page-wrapper {
25 | padding: 0 15px;
26 | min-height: 568px;
27 | background-color: #fff;
28 | }
29 |
30 | @media(min-width:768px) {
31 | #page-wrapper {
32 | position: inherit;
33 | margin: 0 0 0 250px;
34 | padding: 0 30px;
35 | min-height: 1300px;
36 | border-left: 1px solid #e7e7e7;
37 | }
38 | }
39 |
40 | .navbar-static-side ul li {
41 | border-bottom: 1px solid #e7e7e7;
42 | }
43 |
44 | /* Navigation */
45 |
46 | /* ------------------------------- */
47 |
48 | /* Top Right Navigation Dropdown Styles */
49 |
50 | .navbar-top-links li {
51 | display: inline-block;
52 | }
53 |
54 | .navbar-top-links li:last-child {
55 | margin-right: 15px;
56 | }
57 |
58 | .navbar-top-links li a {
59 | padding: 15px;
60 | min-height: 50px;
61 | }
62 |
63 | .navbar-top-links .dropdown-menu li {
64 | display: block;
65 | }
66 |
67 | .navbar-top-links .dropdown-menu li:last-child {
68 | margin-right: 0;
69 | }
70 |
71 | .navbar-top-links .dropdown-menu li a {
72 | padding: 3px 20px;
73 | min-height: 0;
74 | }
75 |
76 | .navbar-top-links .dropdown-menu li a div {
77 | white-space: normal;
78 | }
79 |
80 | .navbar-top-links .dropdown-messages,
81 | .navbar-top-links .dropdown-tasks,
82 | .navbar-top-links .dropdown-alerts {
83 | width: 310px;
84 | min-width: 0;
85 | }
86 |
87 | .navbar-top-links .dropdown-messages {
88 | margin-left: 5px;
89 | }
90 |
91 | .navbar-top-links .dropdown-tasks {
92 | margin-left: -59px;
93 | }
94 |
95 | .navbar-top-links .dropdown-alerts {
96 | margin-left: -123px;
97 | }
98 |
99 | .navbar-top-links .dropdown-user {
100 | right: 0;
101 | left: auto;
102 | }
103 |
104 | /* Sidebar Menu Styles */
105 |
106 | .sidebar-search {
107 | padding: 15px;
108 | }
109 |
110 | .arrow {
111 | float: right;
112 | }
113 |
114 | .fa.arrow:before {
115 | content: "\f104";
116 | }
117 |
118 | .active > a > .fa.arrow:before {
119 | content: "\f107";
120 | }
121 |
122 | .nav-second-level li,
123 | .nav-third-level li {
124 | border-bottom: none !important;
125 | }
126 |
127 | .nav-second-level li a {
128 | padding-left: 37px;
129 | }
130 |
131 | .nav-third-level li a {
132 | padding-left: 52px;
133 | }
134 |
135 | @media(min-width:768px) {
136 | .navbar-static-side {
137 | z-index: 1;
138 | position: absolute;
139 | width: 250px;
140 | margin-top: 51px;
141 | }
142 |
143 | .navbar-top-links .dropdown-messages,
144 | .navbar-top-links .dropdown-tasks,
145 | .navbar-top-links .dropdown-alerts {
146 | margin-left: auto;
147 | }
148 | }
149 |
150 | /* Buttons */
151 |
152 | /* ------------------------------- */
153 |
154 | .btn-outline {
155 | color: inherit;
156 | background-color: transparent;
157 | transition: all .5s;
158 | }
159 |
160 | .btn-primary.btn-outline {
161 | color: #428bca;
162 | }
163 |
164 | .btn-success.btn-outline {
165 | color: #5cb85c;
166 | }
167 |
168 | .btn-info.btn-outline {
169 | color: #5bc0de;
170 | }
171 |
172 | .btn-warning.btn-outline {
173 | color: #f0ad4e;
174 | }
175 |
176 | .btn-danger.btn-outline {
177 | color: #d9534f;
178 | }
179 |
180 | .btn-primary.btn-outline:hover,
181 | .btn-success.btn-outline:hover,
182 | .btn-info.btn-outline:hover,
183 | .btn-warning.btn-outline:hover,
184 | .btn-danger.btn-outline:hover {
185 | color: #fff;
186 | }
187 |
188 | /* Pages */
189 |
190 | /* ------------------------------- */
191 |
192 | /* Dashboard Chat */
193 |
194 | .chat {
195 | margin: 0;
196 | padding: 0;
197 | list-style: none;
198 | }
199 |
200 | .chat li {
201 | margin-bottom: 10px;
202 | padding-bottom: 5px;
203 | border-bottom: 1px dotted #B3A9A9;
204 | }
205 |
206 | .chat li.left .chat-body {
207 | margin-left: 60px;
208 | }
209 |
210 | .chat li.right .chat-body {
211 | margin-right: 60px;
212 | }
213 |
214 | .chat li .chat-body p {
215 | margin: 0;
216 | color: #777777;
217 | }
218 |
219 | .panel .slidedown .glyphicon,
220 | .chat .glyphicon {
221 | margin-right: 5px;
222 | }
223 |
224 | .chat-panel .panel-body {
225 | height: 350px;
226 | overflow-y: scroll;
227 | }
228 |
229 | /* Login Page */
230 |
231 | .login-panel {
232 | margin-top: 25%;
233 | }
234 |
235 | /* Flot Chart Containers */
236 |
237 | .flot-chart {
238 | display: block;
239 | height: 400px;
240 | }
241 |
242 | .flot-chart-content {
243 | width: 100%;
244 | height: 100%;
245 | }
246 |
247 | /* DataTables Overrides */
248 |
249 | table.dataTable thead .sorting,
250 | table.dataTable thead .sorting_asc,
251 | table.dataTable thead .sorting_desc,
252 | table.dataTable thead .sorting_asc_disabled,
253 | table.dataTable thead .sorting_desc_disabled {
254 | background: transparent;
255 | }
256 |
257 | table.dataTable thead .sorting_asc:after {
258 | content: "\f0de";
259 | float: right;
260 | font-family: fontawesome;
261 | }
262 |
263 | table.dataTable thead .sorting_desc:after {
264 | content: "\f0dd";
265 | float: right;
266 | font-family: fontawesome;
267 | }
268 |
269 | table.dataTable thead .sorting:after {
270 | content: "\f0dc";
271 | float: right;
272 | font-family: fontawesome;
273 | color: rgba(50,50,50,.5);
274 | }
275 |
276 | /* Circle Buttons */
277 |
278 | .btn-circle {
279 | width: 30px;
280 | height: 30px;
281 | padding: 6px 0;
282 | border-radius: 15px;
283 | text-align: center;
284 | font-size: 12px;
285 | line-height: 1.428571429;
286 | }
287 |
288 | .btn-circle.btn-lg {
289 | width: 50px;
290 | height: 50px;
291 | padding: 10px 16px;
292 | border-radius: 25px;
293 | font-size: 18px;
294 | line-height: 1.33;
295 | }
296 |
297 | .btn-circle.btn-xl {
298 | width: 70px;
299 | height: 70px;
300 | padding: 10px 16px;
301 | border-radius: 35px;
302 | font-size: 24px;
303 | line-height: 1.33;
304 | }
305 |
306 | .show-grid [class^="col-"] {
307 | padding-top: 10px;
308 | padding-bottom: 10px;
309 | border: 1px solid #ddd;
310 | background-color: #eee !important;
311 | }
312 |
313 | .show-grid {
314 | margin: 15px 0;
315 | }
--------------------------------------------------------------------------------
/static/panel/font-awesome/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/panel/font-awesome/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/static/panel/font-awesome/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/panel/font-awesome/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/static/panel/font-awesome/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/panel/font-awesome/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/static/panel/font-awesome/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/panel/font-awesome/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/static/panel/font-awesome/less/bordered-pulled.less:
--------------------------------------------------------------------------------
1 | // Bordered & Pulled
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-border {
5 | padding: .2em .25em .15em;
6 | border: solid .08em @fa-border-color;
7 | border-radius: .1em;
8 | }
9 |
10 | .pull-right { float: right; }
11 | .pull-left { float: left; }
12 |
13 | .@{fa-css-prefix} {
14 | &.pull-left { margin-right: .3em; }
15 | &.pull-right { margin-left: .3em; }
16 | }
17 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/less/core.less:
--------------------------------------------------------------------------------
1 | // Base Class Definition
2 | // -------------------------
3 |
4 | .@{fa-css-prefix} {
5 | display: inline-block;
6 | font-family: FontAwesome;
7 | font-style: normal;
8 | font-weight: normal;
9 | line-height: 1;
10 | -webkit-font-smoothing: antialiased;
11 | -moz-osx-font-smoothing: grayscale;
12 | }
13 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/less/fixed-width.less:
--------------------------------------------------------------------------------
1 | // Fixed Width Icons
2 | // -------------------------
3 | .@{fa-css-prefix}-fw {
4 | width: (18em / 14);
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/less/font-awesome.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */
5 |
6 | @import "variables";
7 | @import "mixins";
8 | @import "path";
9 | @import "core";
10 | @import "larger";
11 | @import "fixed-width";
12 | @import "list";
13 | @import "bordered-pulled";
14 | @import "spinning";
15 | @import "rotated-flipped";
16 | @import "stacked";
17 | @import "icons";
18 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/less/larger.less:
--------------------------------------------------------------------------------
1 | // Icon Sizes
2 | // -------------------------
3 |
4 | /* makes the font 33% larger relative to the icon container */
5 | .@{fa-css-prefix}-lg {
6 | font-size: (4em / 3);
7 | line-height: (3em / 4);
8 | vertical-align: -15%;
9 | }
10 | .@{fa-css-prefix}-2x { font-size: 2em; }
11 | .@{fa-css-prefix}-3x { font-size: 3em; }
12 | .@{fa-css-prefix}-4x { font-size: 4em; }
13 | .@{fa-css-prefix}-5x { font-size: 5em; }
14 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/less/list.less:
--------------------------------------------------------------------------------
1 | // List Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-ul {
5 | padding-left: 0;
6 | margin-left: @fa-li-width;
7 | list-style-type: none;
8 | > li { position: relative; }
9 | }
10 | .@{fa-css-prefix}-li {
11 | position: absolute;
12 | left: -@fa-li-width;
13 | width: @fa-li-width;
14 | top: (2em / 14);
15 | text-align: center;
16 | &.@{fa-css-prefix}-lg {
17 | left: -@fa-li-width + (4em / 14);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/less/mixins.less:
--------------------------------------------------------------------------------
1 | // Mixins
2 | // --------------------------
3 |
4 | .fa-icon-rotate(@degrees, @rotation) {
5 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation);
6 | -webkit-transform: rotate(@degrees);
7 | -moz-transform: rotate(@degrees);
8 | -ms-transform: rotate(@degrees);
9 | -o-transform: rotate(@degrees);
10 | transform: rotate(@degrees);
11 | }
12 |
13 | .fa-icon-flip(@horiz, @vert, @rotation) {
14 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1);
15 | -webkit-transform: scale(@horiz, @vert);
16 | -moz-transform: scale(@horiz, @vert);
17 | -ms-transform: scale(@horiz, @vert);
18 | -o-transform: scale(@horiz, @vert);
19 | transform: scale(@horiz, @vert);
20 | }
21 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/less/path.less:
--------------------------------------------------------------------------------
1 | /* FONT PATH
2 | * -------------------------- */
3 |
4 | @font-face {
5 | font-family: 'FontAwesome';
6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}');
7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'),
8 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'),
9 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'),
10 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg');
11 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
12 | font-weight: normal;
13 | font-style: normal;
14 | }
15 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/less/rotated-flipped.less:
--------------------------------------------------------------------------------
1 | // Rotated & Flipped Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); }
5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); }
6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); }
7 |
8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); }
9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); }
10 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/less/spinning.less:
--------------------------------------------------------------------------------
1 | // Spinning Icons
2 | // --------------------------
3 |
4 | .@{fa-css-prefix}-spin {
5 | -webkit-animation: spin 2s infinite linear;
6 | -moz-animation: spin 2s infinite linear;
7 | -o-animation: spin 2s infinite linear;
8 | animation: spin 2s infinite linear;
9 | }
10 |
11 | @-moz-keyframes spin {
12 | 0% { -moz-transform: rotate(0deg); }
13 | 100% { -moz-transform: rotate(359deg); }
14 | }
15 | @-webkit-keyframes spin {
16 | 0% { -webkit-transform: rotate(0deg); }
17 | 100% { -webkit-transform: rotate(359deg); }
18 | }
19 | @-o-keyframes spin {
20 | 0% { -o-transform: rotate(0deg); }
21 | 100% { -o-transform: rotate(359deg); }
22 | }
23 | @-ms-keyframes spin {
24 | 0% { -ms-transform: rotate(0deg); }
25 | 100% { -ms-transform: rotate(359deg); }
26 | }
27 | @keyframes spin {
28 | 0% { transform: rotate(0deg); }
29 | 100% { transform: rotate(359deg); }
30 | }
31 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/less/stacked.less:
--------------------------------------------------------------------------------
1 | // Stacked Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-stack {
5 | position: relative;
6 | display: inline-block;
7 | width: 2em;
8 | height: 2em;
9 | line-height: 2em;
10 | vertical-align: middle;
11 | }
12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x {
13 | position: absolute;
14 | left: 0;
15 | width: 100%;
16 | text-align: center;
17 | }
18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; }
19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; }
20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; }
21 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/scss/_bordered-pulled.scss:
--------------------------------------------------------------------------------
1 | // Bordered & Pulled
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-border {
5 | padding: .2em .25em .15em;
6 | border: solid .08em $fa-border-color;
7 | border-radius: .1em;
8 | }
9 |
10 | .pull-right { float: right; }
11 | .pull-left { float: left; }
12 |
13 | .#{$fa-css-prefix} {
14 | &.pull-left { margin-right: .3em; }
15 | &.pull-right { margin-left: .3em; }
16 | }
17 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/scss/_core.scss:
--------------------------------------------------------------------------------
1 | // Base Class Definition
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix} {
5 | display: inline-block;
6 | font-family: FontAwesome;
7 | font-style: normal;
8 | font-weight: normal;
9 | line-height: 1;
10 | -webkit-font-smoothing: antialiased;
11 | -moz-osx-font-smoothing: grayscale;
12 | }
13 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/scss/_fixed-width.scss:
--------------------------------------------------------------------------------
1 | // Fixed Width Icons
2 | // -------------------------
3 | .#{$fa-css-prefix}-fw {
4 | width: (18em / 14);
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/scss/_larger.scss:
--------------------------------------------------------------------------------
1 | // Icon Sizes
2 | // -------------------------
3 |
4 | /* makes the font 33% larger relative to the icon container */
5 | .#{$fa-css-prefix}-lg {
6 | font-size: (4em / 3);
7 | line-height: (3em / 4);
8 | vertical-align: -15%;
9 | }
10 | .#{$fa-css-prefix}-2x { font-size: 2em; }
11 | .#{$fa-css-prefix}-3x { font-size: 3em; }
12 | .#{$fa-css-prefix}-4x { font-size: 4em; }
13 | .#{$fa-css-prefix}-5x { font-size: 5em; }
14 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/scss/_list.scss:
--------------------------------------------------------------------------------
1 | // List Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-ul {
5 | padding-left: 0;
6 | margin-left: $fa-li-width;
7 | list-style-type: none;
8 | > li { position: relative; }
9 | }
10 | .#{$fa-css-prefix}-li {
11 | position: absolute;
12 | left: -$fa-li-width;
13 | width: $fa-li-width;
14 | top: (2em / 14);
15 | text-align: center;
16 | &.#{$fa-css-prefix}-lg {
17 | left: -$fa-li-width + (4em / 14);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 | // Mixins
2 | // --------------------------
3 |
4 | @mixin fa-icon-rotate($degrees, $rotation) {
5 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);
6 | -webkit-transform: rotate($degrees);
7 | -moz-transform: rotate($degrees);
8 | -ms-transform: rotate($degrees);
9 | -o-transform: rotate($degrees);
10 | transform: rotate($degrees);
11 | }
12 |
13 | @mixin fa-icon-flip($horiz, $vert, $rotation) {
14 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);
15 | -webkit-transform: scale($horiz, $vert);
16 | -moz-transform: scale($horiz, $vert);
17 | -ms-transform: scale($horiz, $vert);
18 | -o-transform: scale($horiz, $vert);
19 | transform: scale($horiz, $vert);
20 | }
21 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/scss/_path.scss:
--------------------------------------------------------------------------------
1 | /* FONT PATH
2 | * -------------------------- */
3 |
4 | @font-face {
5 | font-family: 'FontAwesome';
6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}');
7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'),
8 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'),
9 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'),
10 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg');
11 | //src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
12 | font-weight: normal;
13 | font-style: normal;
14 | }
15 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/scss/_rotated-flipped.scss:
--------------------------------------------------------------------------------
1 | // Rotated & Flipped Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); }
5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); }
6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); }
7 |
8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); }
9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); }
10 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/scss/_spinning.scss:
--------------------------------------------------------------------------------
1 | // Spinning Icons
2 | // --------------------------
3 |
4 | .#{$fa-css-prefix}-spin {
5 | -webkit-animation: spin 2s infinite linear;
6 | -moz-animation: spin 2s infinite linear;
7 | -o-animation: spin 2s infinite linear;
8 | animation: spin 2s infinite linear;
9 | }
10 |
11 | @-moz-keyframes spin {
12 | 0% { -moz-transform: rotate(0deg); }
13 | 100% { -moz-transform: rotate(359deg); }
14 | }
15 | @-webkit-keyframes spin {
16 | 0% { -webkit-transform: rotate(0deg); }
17 | 100% { -webkit-transform: rotate(359deg); }
18 | }
19 | @-o-keyframes spin {
20 | 0% { -o-transform: rotate(0deg); }
21 | 100% { -o-transform: rotate(359deg); }
22 | }
23 | @-ms-keyframes spin {
24 | 0% { -ms-transform: rotate(0deg); }
25 | 100% { -ms-transform: rotate(359deg); }
26 | }
27 | @keyframes spin {
28 | 0% { transform: rotate(0deg); }
29 | 100% { transform: rotate(359deg); }
30 | }
31 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/scss/_stacked.scss:
--------------------------------------------------------------------------------
1 | // Stacked Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-stack {
5 | position: relative;
6 | display: inline-block;
7 | width: 2em;
8 | height: 2em;
9 | line-height: 2em;
10 | vertical-align: middle;
11 | }
12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x {
13 | position: absolute;
14 | left: 0;
15 | width: 100%;
16 | text-align: center;
17 | }
18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; }
19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; }
20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; }
21 |
--------------------------------------------------------------------------------
/static/panel/font-awesome/scss/font-awesome.scss:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */
5 |
6 | @import "variables";
7 | @import "mixins";
8 | @import "path";
9 | @import "core";
10 | @import "larger";
11 | @import "fixed-width";
12 | @import "list";
13 | @import "bordered-pulled";
14 | @import "spinning";
15 | @import "rotated-flipped";
16 | @import "stacked";
17 | @import "icons";
18 |
--------------------------------------------------------------------------------
/static/panel/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/panel/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/static/panel/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/panel/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/static/panel/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericls/FairyBBS/27480168d6598d6768110df7a443ed58195ff855/static/panel/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/static/panel/js/autocomplete.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Ajax Autocomplete for jQuery, version 1.2.9
3 | * (c) 2013 Tomas Kirda
4 | *
5 | * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
6 | * For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
7 | *
8 | */
9 | (function(d){"function"===typeof define&&define.amd?define(["jquery"],d):d(jQuery)})(function(d){function g(a,b){var c=function(){},c={autoSelectFirst:!1,appendTo:"body",serviceUrl:null,lookup:null,onSelect:null,width:"auto",minChars:1,maxHeight:300,deferRequestBy:0,params:{},formatResult:g.formatResult,delimiter:null,zIndex:9999,type:"GET",noCache:!1,onSearchStart:c,onSearchComplete:c,onSearchError:c,containerClass:"autocomplete-suggestions",tabDisabled:!1,dataType:"text",currentRequest:null,triggerSelectOnValidInput:!0,
10 | lookupFilter:function(a,b,c){return-1!==a.value.toLowerCase().indexOf(c)},paramName:"query",transformResult:function(a){return"string"===typeof a?d.parseJSON(a):a}};this.element=a;this.el=d(a);this.suggestions=[];this.badQueries=[];this.selectedIndex=-1;this.currentValue=this.element.value;this.intervalId=0;this.cachedResponse={};this.onChange=this.onChangeInterval=null;this.isLocal=!1;this.suggestionsContainer=null;this.options=d.extend({},c,b);this.classes={selected:"autocomplete-selected",suggestion:"autocomplete-suggestion"};
11 | this.hint=null;this.hintValue="";this.selection=null;this.initialize();this.setOptions(b)}var k=function(){return{escapeRegExChars:function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},createNode:function(a){var b=document.createElement("div");b.className=a;b.style.position="absolute";b.style.display="none";return b}}}();g.utils=k;d.Autocomplete=g;g.formatResult=function(a,b){var c="("+k.escapeRegExChars(b)+")";return a.value.replace(RegExp(c,"gi"),"$1")};g.prototype=
12 | {killerFn:null,initialize:function(){var a=this,b="."+a.classes.suggestion,c=a.classes.selected,e=a.options,f;a.element.setAttribute("autocomplete","off");a.killerFn=function(b){0===d(b.target).closest("."+a.options.containerClass).length&&(a.killSuggestions(),a.disableKillerFn())};a.suggestionsContainer=g.utils.createNode(e.containerClass);f=d(a.suggestionsContainer);f.appendTo(e.appendTo);"auto"!==e.width&&f.width(e.width);f.on("mouseover.autocomplete",b,function(){a.activate(d(this).data("index"))});
13 | f.on("mouseout.autocomplete",function(){a.selectedIndex=-1;f.children("."+c).removeClass(c)});f.on("click.autocomplete",b,function(){a.select(d(this).data("index"))});a.fixPosition();a.fixPositionCapture=function(){a.visible&&a.fixPosition()};d(window).on("resize.autocomplete",a.fixPositionCapture);a.el.on("keydown.autocomplete",function(b){a.onKeyPress(b)});a.el.on("keyup.autocomplete",function(b){a.onKeyUp(b)});a.el.on("blur.autocomplete",function(){a.onBlur()});a.el.on("focus.autocomplete",function(){a.onFocus()});
14 | a.el.on("change.autocomplete",function(b){a.onKeyUp(b)})},onFocus:function(){this.fixPosition();if(this.options.minChars<=this.el.val().length)this.onValueChange()},onBlur:function(){this.enableKillerFn()},setOptions:function(a){var b=this.options;d.extend(b,a);if(this.isLocal=d.isArray(b.lookup))b.lookup=this.verifySuggestionsFormat(b.lookup);d(this.suggestionsContainer).css({"max-height":b.maxHeight+"px",width:b.width+"px","z-index":b.zIndex})},clearCache:function(){this.cachedResponse={};this.badQueries=
15 | []},clear:function(){this.clearCache();this.currentValue="";this.suggestions=[]},disable:function(){this.disabled=!0;this.currentRequest&&this.currentRequest.abort()},enable:function(){this.disabled=!1},fixPosition:function(){var a;"body"===this.options.appendTo&&(a=this.el.offset(),a={top:a.top+this.el.outerHeight()+"px",left:a.left+"px"},"auto"===this.options.width&&(a.width=this.el.outerWidth()-2+"px"),d(this.suggestionsContainer).css(a))},enableKillerFn:function(){d(document).on("click.autocomplete",
16 | this.killerFn)},disableKillerFn:function(){d(document).off("click.autocomplete",this.killerFn)},killSuggestions:function(){var a=this;a.stopKillSuggestions();a.intervalId=window.setInterval(function(){a.hide();a.stopKillSuggestions()},50)},stopKillSuggestions:function(){window.clearInterval(this.intervalId)},isCursorAtEnd:function(){var a=this.el.val().length,b=this.element.selectionStart;return"number"===typeof b?b===a:document.selection?(b=document.selection.createRange(),b.moveStart("character",
17 | -a),a===b.text.length):!0},onKeyPress:function(a){if(!this.disabled&&!this.visible&&40===a.which&&this.currentValue)this.suggest();else if(!this.disabled&&this.visible){switch(a.which){case 27:this.el.val(this.currentValue);this.hide();break;case 39:if(this.hint&&this.options.onHint&&this.isCursorAtEnd()){this.selectHint();break}return;case 9:if(this.hint&&this.options.onHint){this.selectHint();return}case 13:if(-1===this.selectedIndex){this.hide();return}this.select(this.selectedIndex);if(9===a.which&&
18 | !1===this.options.tabDisabled)return;break;case 38:this.moveUp();break;case 40:this.moveDown();break;default:return}a.stopImmediatePropagation();a.preventDefault()}},onKeyUp:function(a){var b=this;if(!b.disabled){switch(a.which){case 38:case 40:return}clearInterval(b.onChangeInterval);if(b.currentValue!==b.el.val())if(b.findBestHint(),0f&&(b.suggestions=b.suggestions.slice(0,f));return b},getSuggestions:function(a){var b,c=this,e=c.options,f=e.serviceUrl,l,g;e.params[e.paramName]=a;l=e.ignoreParams?null:e.params;
21 | c.isLocal?b=c.getSuggestionsLocal(a):(d.isFunction(f)&&(f=f.call(c.element,a)),g=f+"?"+d.param(l||{}),b=c.cachedResponse[g]);b&&d.isArray(b.suggestions)?(c.suggestions=b.suggestions,c.suggest()):c.isBadQuery(a)||!1===e.onSearchStart.call(c.element,e.params)||(c.currentRequest&&c.currentRequest.abort(),c.currentRequest=d.ajax({url:f,data:l,type:e.type,dataType:e.dataType}).done(function(b){c.currentRequest=null;c.processResponse(b,a,g);e.onSearchComplete.call(c.element,a)}).fail(function(b,d,f){e.onSearchError.call(c.element,
22 | a,b,d,f)}))},isBadQuery:function(a){for(var b=this.badQueries,c=b.length;c--;)if(0===a.indexOf(b[c]))return!0;return!1},hide:function(){this.visible=!1;this.selectedIndex=-1;d(this.suggestionsContainer).hide();this.signalHint(null)},suggest:function(){if(0===this.suggestions.length)this.hide();else{var a=this.options,b=a.formatResult,c=this.getQuery(this.currentValue),e=this.classes.suggestion,f=this.classes.selected,g=d(this.suggestionsContainer),k=a.beforeRender,m="",h;if(a.triggerSelectOnValidInput&&
23 | (h=this.findSuggestionIndex(c),-1!==h)){this.select(h);return}d.each(this.suggestions,function(a,d){m+=''+b(d,c)+"
"});"auto"===a.width&&(h=this.el.outerWidth()-2,g.width(0this.selectedIndex?(a=e.get(this.selectedIndex),d(a).addClass(b),a):null},selectHint:function(){var a=d.inArray(this.hint,this.suggestions);
26 | this.select(a)},select:function(a){this.hide();this.onSelect(a)},moveUp:function(){-1!==this.selectedIndex&&(0===this.selectedIndex?(d(this.suggestionsContainer).children().first().removeClass(this.classes.selected),this.selectedIndex=-1,this.el.val(this.currentValue),this.findBestHint()):this.adjustScroll(this.selectedIndex-1))},moveDown:function(){this.selectedIndex!==this.suggestions.length-1&&this.adjustScroll(this.selectedIndex+1)},adjustScroll:function(a){var b=this.activate(a),c,e;b&&(b=b.offsetTop,
27 | c=d(this.suggestionsContainer).scrollTop(),e=c+this.options.maxHeight-25,be&&d(this.suggestionsContainer).scrollTop(b-this.options.maxHeight+25),this.el.val(this.getValue(this.suggestions[a].value)),this.signalHint(null))},onSelect:function(a){var b=this.options.onSelect;a=this.suggestions[a];this.currentValue=this.getValue(a.value);this.el.val(this.currentValue);this.signalHint(null);this.suggestions=[];this.selection=a;d.isFunction(b)&&b.call(this.element,
28 | a)},getValue:function(a){var b=this.options.delimiter,c;if(!b)return a;c=this.currentValue;b=c.split(b);return 1===b.length?a:c.substr(0,c.length-b[b.length-1].length)+a},dispose:function(){this.el.off(".autocomplete").removeData("autocomplete");this.disableKillerFn();d(window).off("resize.autocomplete",this.fixPositionCapture);d(this.suggestionsContainer).remove()}};d.fn.autocomplete=function(a,b){return 0===arguments.length?this.first().data("autocomplete"):this.each(function(){var c=d(this),e=
29 | c.data("autocomplete");if("string"===typeof a){if(e&&"function"===typeof e[a])e[a](b)}else e&&e.dispose&&e.dispose(),e=new g(this,a),c.data("autocomplete",e)})}});
--------------------------------------------------------------------------------
/static/panel/js/demo/dashboard-demo.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | Morris.Area({
4 | element: 'morris-area-chart',
5 | data: [{
6 | period: '2010 Q1',
7 | iphone: 2666,
8 | ipad: null,
9 | itouch: 2647
10 | }, {
11 | period: '2010 Q2',
12 | iphone: 2778,
13 | ipad: 2294,
14 | itouch: 2441
15 | }, {
16 | period: '2010 Q3',
17 | iphone: 4912,
18 | ipad: 1969,
19 | itouch: 2501
20 | }, {
21 | period: '2010 Q4',
22 | iphone: 3767,
23 | ipad: 3597,
24 | itouch: 5689
25 | }, {
26 | period: '2011 Q1',
27 | iphone: 6810,
28 | ipad: 1914,
29 | itouch: 2293
30 | }, {
31 | period: '2011 Q2',
32 | iphone: 5670,
33 | ipad: 4293,
34 | itouch: 1881
35 | }, {
36 | period: '2011 Q3',
37 | iphone: 4820,
38 | ipad: 3795,
39 | itouch: 1588
40 | }, {
41 | period: '2011 Q4',
42 | iphone: 15073,
43 | ipad: 5967,
44 | itouch: 5175
45 | }, {
46 | period: '2012 Q1',
47 | iphone: 10687,
48 | ipad: 4460,
49 | itouch: 2028
50 | }, {
51 | period: '2012 Q2',
52 | iphone: 8432,
53 | ipad: 5713,
54 | itouch: 1791
55 | }],
56 | xkey: 'period',
57 | ykeys: ['iphone', 'ipad', 'itouch'],
58 | labels: ['iPhone', 'iPad', 'iPod Touch'],
59 | pointSize: 2,
60 | hideHover: 'auto',
61 | resize: true
62 | });
63 |
64 | Morris.Donut({
65 | element: 'morris-donut-chart',
66 | data: [{
67 | label: "Download Sales",
68 | value: 12
69 | }, {
70 | label: "In-Store Sales",
71 | value: 30
72 | }, {
73 | label: "Mail-Order Sales",
74 | value: 20
75 | }],
76 | resize: true
77 | });
78 |
79 | Morris.Bar({
80 | element: 'morris-bar-chart',
81 | data: [{
82 | y: '2006',
83 | a: 100,
84 | b: 90
85 | }, {
86 | y: '2007',
87 | a: 75,
88 | b: 65
89 | }, {
90 | y: '2008',
91 | a: 50,
92 | b: 40
93 | }, {
94 | y: '2009',
95 | a: 75,
96 | b: 65
97 | }, {
98 | y: '2010',
99 | a: 50,
100 | b: 40
101 | }, {
102 | y: '2011',
103 | a: 75,
104 | b: 65
105 | }, {
106 | y: '2012',
107 | a: 100,
108 | b: 90
109 | }],
110 | xkey: 'y',
111 | ykeys: ['a', 'b'],
112 | labels: ['Series A', 'Series B'],
113 | hideHover: 'auto',
114 | resize: true
115 | });
116 |
117 | });
118 |
--------------------------------------------------------------------------------
/static/panel/js/demo/morris-demo.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | Morris.Area({
4 | element: 'morris-area-chart',
5 | data: [{
6 | period: '2010 Q1',
7 | iphone: 2666,
8 | ipad: null,
9 | itouch: 2647
10 | }, {
11 | period: '2010 Q2',
12 | iphone: 2778,
13 | ipad: 2294,
14 | itouch: 2441
15 | }, {
16 | period: '2010 Q3',
17 | iphone: 4912,
18 | ipad: 1969,
19 | itouch: 2501
20 | }, {
21 | period: '2010 Q4',
22 | iphone: 3767,
23 | ipad: 3597,
24 | itouch: 5689
25 | }, {
26 | period: '2011 Q1',
27 | iphone: 6810,
28 | ipad: 1914,
29 | itouch: 2293
30 | }, {
31 | period: '2011 Q2',
32 | iphone: 5670,
33 | ipad: 4293,
34 | itouch: 1881
35 | }, {
36 | period: '2011 Q3',
37 | iphone: 4820,
38 | ipad: 3795,
39 | itouch: 1588
40 | }, {
41 | period: '2011 Q4',
42 | iphone: 15073,
43 | ipad: 5967,
44 | itouch: 5175
45 | }, {
46 | period: '2012 Q1',
47 | iphone: 10687,
48 | ipad: 4460,
49 | itouch: 2028
50 | }, {
51 | period: '2012 Q2',
52 | iphone: 8432,
53 | ipad: 5713,
54 | itouch: 1791
55 | }],
56 | xkey: 'period',
57 | ykeys: ['iphone', 'ipad', 'itouch'],
58 | labels: ['iPhone', 'iPad', 'iPod Touch'],
59 | pointSize: 2,
60 | hideHover: 'auto',
61 | resize: true
62 | });
63 |
64 | Morris.Donut({
65 | element: 'morris-donut-chart',
66 | data: [{
67 | label: "Download Sales",
68 | value: 12
69 | }, {
70 | label: "In-Store Sales",
71 | value: 30
72 | }, {
73 | label: "Mail-Order Sales",
74 | value: 20
75 | }],
76 | resize: true
77 | });
78 |
79 | Morris.Bar({
80 | element: 'morris-bar-chart',
81 | data: [{
82 | y: '2006',
83 | a: 100,
84 | b: 90
85 | }, {
86 | y: '2007',
87 | a: 75,
88 | b: 65
89 | }, {
90 | y: '2008',
91 | a: 50,
92 | b: 40
93 | }, {
94 | y: '2009',
95 | a: 75,
96 | b: 65
97 | }, {
98 | y: '2010',
99 | a: 50,
100 | b: 40
101 | }, {
102 | y: '2011',
103 | a: 75,
104 | b: 65
105 | }, {
106 | y: '2012',
107 | a: 100,
108 | b: 90
109 | }],
110 | xkey: 'y',
111 | ykeys: ['a', 'b'],
112 | labels: ['Series A', 'Series B'],
113 | hideHover: 'auto',
114 | resize: true
115 | });
116 |
117 | Morris.Line({
118 | element: 'morris-line-chart',
119 | data: [{
120 | y: '2006',
121 | a: 100,
122 | b: 90
123 | }, {
124 | y: '2007',
125 | a: 75,
126 | b: 65
127 | }, {
128 | y: '2008',
129 | a: 50,
130 | b: 40
131 | }, {
132 | y: '2009',
133 | a: 75,
134 | b: 65
135 | }, {
136 | y: '2010',
137 | a: 50,
138 | b: 40
139 | }, {
140 | y: '2011',
141 | a: 75,
142 | b: 65
143 | }, {
144 | y: '2012',
145 | a: 100,
146 | b: 90
147 | }],
148 | xkey: 'y',
149 | ykeys: ['a', 'b'],
150 | labels: ['Series A', 'Series B'],
151 | hideHover: 'auto',
152 | resize: true
153 | });
154 |
155 | });
156 |
--------------------------------------------------------------------------------
/static/panel/js/plugins/dataTables/dataTables.bootstrap.js:
--------------------------------------------------------------------------------
1 | /* Set the defaults for DataTables initialisation */
2 | $.extend(true, $.fn.dataTable.defaults, {
3 | "sDom": "<'row'<'col-sm-6'l><'col-sm-6'f>r>" + "t" + "<'row'<'col-sm-6'i><'col-sm-6'p>>",
4 | "oLanguage": {
5 | "sLengthMenu": "_MENU_ records per page"
6 | }
7 | });
8 |
9 |
10 | /* Default class modification */
11 | $.extend($.fn.dataTableExt.oStdClasses, {
12 | "sWrapper": "dataTables_wrapper form-inline",
13 | "sFilterInput": "form-control input-sm",
14 | "sLengthSelect": "form-control input-sm"
15 | });
16 |
17 | // In 1.10 we use the pagination renderers to draw the Bootstrap paging,
18 | // rather than custom plug-in
19 | if ($.fn.dataTable.Api) {
20 | $.fn.dataTable.defaults.renderer = 'bootstrap';
21 | $.fn.dataTable.ext.renderer.pageButton.bootstrap = function(settings, host, idx, buttons, page, pages) {
22 | var api = new $.fn.dataTable.Api(settings);
23 | var classes = settings.oClasses;
24 | var lang = settings.oLanguage.oPaginate;
25 | var btnDisplay, btnClass;
26 |
27 | var attach = function(container, buttons) {
28 | var i, ien, node, button;
29 | var clickHandler = function(e) {
30 | e.preventDefault();
31 | if (e.data.action !== 'ellipsis') {
32 | api.page(e.data.action).draw(false);
33 | }
34 | };
35 |
36 | for (i = 0, ien = buttons.length; i < ien; i++) {
37 | button = buttons[i];
38 |
39 | if ($.isArray(button)) {
40 | attach(container, button);
41 | } else {
42 | btnDisplay = '';
43 | btnClass = '';
44 |
45 | switch (button) {
46 | case 'ellipsis':
47 | btnDisplay = '…';
48 | btnClass = 'disabled';
49 | break;
50 |
51 | case 'first':
52 | btnDisplay = lang.sFirst;
53 | btnClass = button + (page > 0 ?
54 | '' : ' disabled');
55 | break;
56 |
57 | case 'previous':
58 | btnDisplay = lang.sPrevious;
59 | btnClass = button + (page > 0 ?
60 | '' : ' disabled');
61 | break;
62 |
63 | case 'next':
64 | btnDisplay = lang.sNext;
65 | btnClass = button + (page < pages - 1 ?
66 | '' : ' disabled');
67 | break;
68 |
69 | case 'last':
70 | btnDisplay = lang.sLast;
71 | btnClass = button + (page < pages - 1 ?
72 | '' : ' disabled');
73 | break;
74 |
75 | default:
76 | btnDisplay = button + 1;
77 | btnClass = page === button ?
78 | 'active' : '';
79 | break;
80 | }
81 |
82 | if (btnDisplay) {
83 | node = $('', {
84 | 'class': classes.sPageButton + ' ' + btnClass,
85 | 'aria-controls': settings.sTableId,
86 | 'tabindex': settings.iTabIndex,
87 | 'id': idx === 0 && typeof button === 'string' ? settings.sTableId + '_' + button : null
88 | })
89 | .append($('', {
90 | 'href': '#'
91 | })
92 | .html(btnDisplay)
93 | )
94 | .appendTo(container);
95 |
96 | settings.oApi._fnBindAction(
97 | node, {
98 | action: button
99 | }, clickHandler
100 | );
101 | }
102 | }
103 | }
104 | };
105 |
106 | attach(
107 | $(host).empty().html('').children('ul'),
108 | buttons
109 | );
110 | }
111 | } else {
112 | // Integration for 1.9-
113 | $.fn.dataTable.defaults.sPaginationType = 'bootstrap';
114 |
115 | /* API method to get paging information */
116 | $.fn.dataTableExt.oApi.fnPagingInfo = function(oSettings) {
117 | return {
118 | "iStart": oSettings._iDisplayStart,
119 | "iEnd": oSettings.fnDisplayEnd(),
120 | "iLength": oSettings._iDisplayLength,
121 | "iTotal": oSettings.fnRecordsTotal(),
122 | "iFilteredTotal": oSettings.fnRecordsDisplay(),
123 | "iPage": oSettings._iDisplayLength === -1 ? 0 : Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength),
124 | "iTotalPages": oSettings._iDisplayLength === -1 ? 0 : Math.ceil(oSettings.fnRecordsDisplay() / oSettings._iDisplayLength)
125 | };
126 | };
127 |
128 | /* Bootstrap style pagination control */
129 | $.extend($.fn.dataTableExt.oPagination, {
130 | "bootstrap": {
131 | "fnInit": function(oSettings, nPaging, fnDraw) {
132 | var oLang = oSettings.oLanguage.oPaginate;
133 | var fnClickHandler = function(e) {
134 | e.preventDefault();
135 | if (oSettings.oApi._fnPageChange(oSettings, e.data.action)) {
136 | fnDraw(oSettings);
137 | }
138 | };
139 |
140 | $(nPaging).append(
141 | ''
145 | );
146 | var els = $('a', nPaging);
147 | $(els[0]).bind('click.DT', {
148 | action: "previous"
149 | }, fnClickHandler);
150 | $(els[1]).bind('click.DT', {
151 | action: "next"
152 | }, fnClickHandler);
153 | },
154 |
155 | "fnUpdate": function(oSettings, fnDraw) {
156 | var iListLength = 5;
157 | var oPaging = oSettings.oInstance.fnPagingInfo();
158 | var an = oSettings.aanFeatures.p;
159 | var i, ien, j, sClass, iStart, iEnd, iHalf = Math.floor(iListLength / 2);
160 |
161 | if (oPaging.iTotalPages < iListLength) {
162 | iStart = 1;
163 | iEnd = oPaging.iTotalPages;
164 | } else if (oPaging.iPage <= iHalf) {
165 | iStart = 1;
166 | iEnd = iListLength;
167 | } else if (oPaging.iPage >= (oPaging.iTotalPages - iHalf)) {
168 | iStart = oPaging.iTotalPages - iListLength + 1;
169 | iEnd = oPaging.iTotalPages;
170 | } else {
171 | iStart = oPaging.iPage - iHalf + 1;
172 | iEnd = iStart + iListLength - 1;
173 | }
174 |
175 | for (i = 0, ien = an.length; i < ien; i++) {
176 | // Remove the middle elements
177 | $('li:gt(0)', an[i]).filter(':not(:last)').remove();
178 |
179 | // Add the new list items and their event handlers
180 | for (j = iStart; j <= iEnd; j++) {
181 | sClass = (j == oPaging.iPage + 1) ? 'class="active"' : '';
182 | $('' + j + '')
183 | .insertBefore($('li:last', an[i])[0])
184 | .bind('click', function(e) {
185 | e.preventDefault();
186 | oSettings._iDisplayStart = (parseInt($('a', this).text(), 10) - 1) * oPaging.iLength;
187 | fnDraw(oSettings);
188 | });
189 | }
190 |
191 | // Add / remove disabled classes from the static elements
192 | if (oPaging.iPage === 0) {
193 | $('li:first', an[i]).addClass('disabled');
194 | } else {
195 | $('li:first', an[i]).removeClass('disabled');
196 | }
197 |
198 | if (oPaging.iPage === oPaging.iTotalPages - 1 || oPaging.iTotalPages === 0) {
199 | $('li:last', an[i]).addClass('disabled');
200 | } else {
201 | $('li:last', an[i]).removeClass('disabled');
202 | }
203 | }
204 | }
205 | }
206 | });
207 | }
208 |
209 |
210 | /*
211 | * TableTools Bootstrap compatibility
212 | * Required TableTools 2.1+
213 | */
214 | if ($.fn.DataTable.TableTools) {
215 | // Set the classes that TableTools uses to something suitable for Bootstrap
216 | $.extend(true, $.fn.DataTable.TableTools.classes, {
217 | "container": "DTTT btn-group",
218 | "buttons": {
219 | "normal": "btn btn-default",
220 | "disabled": "disabled"
221 | },
222 | "collection": {
223 | "container": "DTTT_dropdown dropdown-menu",
224 | "buttons": {
225 | "normal": "",
226 | "disabled": "disabled"
227 | }
228 | },
229 | "print": {
230 | "info": "DTTT_print_info modal"
231 | },
232 | "select": {
233 | "row": "active"
234 | }
235 | });
236 |
237 | // Have the collection use a bootstrap compatible dropdown
238 | $.extend(true, $.fn.DataTable.TableTools.DEFAULTS.oTags, {
239 | "collection": {
240 | "container": "ul",
241 | "button": "li",
242 | "liner": "a"
243 | }
244 | });
245 | }
246 |
--------------------------------------------------------------------------------
/static/panel/js/plugins/flot/jquery.flot.resize.js:
--------------------------------------------------------------------------------
1 | /* Flot plugin for automatically redrawing plots as the placeholder resizes.
2 |
3 | Copyright (c) 2007-2013 IOLA and Ole Laursen.
4 | Licensed under the MIT license.
5 |
6 | It works by listening for changes on the placeholder div (through the jQuery
7 | resize event plugin) - if the size changes, it will redraw the plot.
8 |
9 | There are no options. If you need to disable the plugin for some plots, you
10 | can just fix the size of their placeholders.
11 |
12 | */
13 |
14 | /* Inline dependency:
15 | * jQuery resize event - v1.1 - 3/14/2010
16 | * http://benalman.com/projects/jquery-resize-plugin/
17 | *
18 | * Copyright (c) 2010 "Cowboy" Ben Alman
19 | * Dual licensed under the MIT and GPL licenses.
20 | * http://benalman.com/about/license/
21 | */
22 |
23 | (function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this);
24 |
25 | (function ($) {
26 | var options = { }; // no options
27 |
28 | function init(plot) {
29 | function onResize() {
30 | var placeholder = plot.getPlaceholder();
31 |
32 | // somebody might have hidden us and we can't plot
33 | // when we don't have the dimensions
34 | if (placeholder.width() == 0 || placeholder.height() == 0)
35 | return;
36 |
37 | plot.resize();
38 | plot.setupGrid();
39 | plot.draw();
40 | }
41 |
42 | function bindEvents(plot, eventHolder) {
43 | plot.getPlaceholder().resize(onResize);
44 | }
45 |
46 | function shutdown(plot, eventHolder) {
47 | plot.getPlaceholder().unbind("resize", onResize);
48 | }
49 |
50 | plot.hooks.bindEvents.push(bindEvents);
51 | plot.hooks.shutdown.push(shutdown);
52 | }
53 |
54 | $.plot.plugins.push({
55 | init: init,
56 | options: options,
57 | name: 'resize',
58 | version: '1.0'
59 | });
60 | })(jQuery);
--------------------------------------------------------------------------------
/static/panel/js/plugins/flot/jquery.flot.tooltip.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jquery.flot.tooltip
3 | *
4 | * description: easy-to-use tooltips for Flot charts
5 | * version: 0.6.2
6 | * author: Krzysztof Urbas @krzysu [myviews.pl]
7 | * website: https://github.com/krzysu/flot.tooltip
8 | *
9 | * build on 2013-09-30
10 | * released under MIT License, 2012
11 | */
12 | (function(t){var o={tooltip:!1,tooltipOpts:{content:"%s | X: %x | Y: %y",xDateFormat:null,yDateFormat:null,shifts:{x:10,y:20},defaultTheme:!0,onHover:function(){}}},i=function(t){this.tipPosition={x:0,y:0},this.init(t)};i.prototype.init=function(o){function i(t){var o={};o.x=t.pageX,o.y=t.pageY,s.updateTooltipPosition(o)}function e(t,o,i){var e=s.getDomElement();if(i){var n;n=s.stringFormat(s.tooltipOptions.content,i),e.html(n),s.updateTooltipPosition({x:o.pageX,y:o.pageY}),e.css({left:s.tipPosition.x+s.tooltipOptions.shifts.x,top:s.tipPosition.y+s.tooltipOptions.shifts.y}).show(),"function"==typeof s.tooltipOptions.onHover&&s.tooltipOptions.onHover(i,e)}else e.hide().html("")}var s=this;o.hooks.bindEvents.push(function(o,n){s.plotOptions=o.getOptions(),s.plotOptions.tooltip!==!1&&void 0!==s.plotOptions.tooltip&&(s.tooltipOptions=s.plotOptions.tooltipOpts,s.getDomElement(),t(o.getPlaceholder()).bind("plothover",e),t(n).bind("mousemove",i))}),o.hooks.shutdown.push(function(o,s){t(o.getPlaceholder()).unbind("plothover",e),t(s).unbind("mousemove",i)})},i.prototype.getDomElement=function(){var o;return t("#flotTip").length>0?o=t("#flotTip"):(o=t("").attr("id","flotTip"),o.appendTo("body").hide().css({position:"absolute"}),this.tooltipOptions.defaultTheme&&o.css({background:"#fff","z-index":"100",padding:"0.4em 0.6em","border-radius":"0.5em","font-size":"0.8em",border:"1px solid #111",display:"none","white-space":"nowrap"})),o},i.prototype.updateTooltipPosition=function(o){var i=t("#flotTip").outerWidth()+this.tooltipOptions.shifts.x,e=t("#flotTip").outerHeight()+this.tooltipOptions.shifts.y;o.x-t(window).scrollLeft()>t(window).innerWidth()-i&&(o.x-=i),o.y-t(window).scrollTop()>t(window).innerHeight()-e&&(o.y-=e),this.tipPosition.x=o.x,this.tipPosition.y=o.y},i.prototype.stringFormat=function(t,o){var i=/%p\.{0,1}(\d{0,})/,e=/%s/,s=/%x\.{0,1}(?:\d{0,})/,n=/%y\.{0,1}(?:\d{0,})/;return"function"==typeof t&&(t=t(o.series.label,o.series.data[o.dataIndex][0],o.series.data[o.dataIndex][1],o)),o.series.percent!==void 0&&(t=this.adjustValPrecision(i,t,o.series.percent)),o.series.label!==void 0&&(t=t.replace(e,o.series.label)),this.isTimeMode("xaxis",o)&&this.isXDateFormat(o)&&(t=t.replace(s,this.timestampToDate(o.series.data[o.dataIndex][0],this.tooltipOptions.xDateFormat))),this.isTimeMode("yaxis",o)&&this.isYDateFormat(o)&&(t=t.replace(n,this.timestampToDate(o.series.data[o.dataIndex][1],this.tooltipOptions.yDateFormat))),"number"==typeof o.series.data[o.dataIndex][0]&&(t=this.adjustValPrecision(s,t,o.series.data[o.dataIndex][0])),"number"==typeof o.series.data[o.dataIndex][1]&&(t=this.adjustValPrecision(n,t,o.series.data[o.dataIndex][1])),o.series.xaxis.tickFormatter!==void 0&&(t=t.replace(s,o.series.xaxis.tickFormatter(o.series.data[o.dataIndex][0],o.series.xaxis))),o.series.yaxis.tickFormatter!==void 0&&(t=t.replace(n,o.series.yaxis.tickFormatter(o.series.data[o.dataIndex][1],o.series.yaxis))),t},i.prototype.isTimeMode=function(t,o){return o.series[t].options.mode!==void 0&&"time"===o.series[t].options.mode},i.prototype.isXDateFormat=function(){return this.tooltipOptions.xDateFormat!==void 0&&null!==this.tooltipOptions.xDateFormat},i.prototype.isYDateFormat=function(){return this.tooltipOptions.yDateFormat!==void 0&&null!==this.tooltipOptions.yDateFormat},i.prototype.timestampToDate=function(o,i){var e=new Date(o);return t.plot.formatDate(e,i)},i.prototype.adjustValPrecision=function(t,o,i){var e,s=o.match(t);return null!==s&&""!==RegExp.$1&&(e=RegExp.$1,i=i.toFixed(e),o=o.replace(t,i)),o};var e=function(t){new i(t)};t.plot.plugins.push({init:e,options:o,name:"tooltip",version:"0.6.1"})})(jQuery);
--------------------------------------------------------------------------------
/static/panel/js/plugins/metisMenu/jquery.metisMenu.js:
--------------------------------------------------------------------------------
1 | ;(function ($, window, document, undefined) {
2 |
3 | var pluginName = "metisMenu",
4 | defaults = {
5 | toggle: true
6 | };
7 |
8 | function Plugin(element, options) {
9 | this.element = element;
10 | this.settings = $.extend({}, defaults, options);
11 | this._defaults = defaults;
12 | this._name = pluginName;
13 | this.init();
14 | }
15 |
16 | Plugin.prototype = {
17 | init: function () {
18 |
19 | var $this = $(this.element),
20 | $toggle = this.settings.toggle;
21 |
22 | $this.find('li.active').has('ul').children('ul').addClass('collapse in');
23 | $this.find('li').not('.active').has('ul').children('ul').addClass('collapse');
24 |
25 | $this.find('li').has('ul').children('a').on('click', function (e) {
26 | e.preventDefault();
27 |
28 | $(this).parent('li').toggleClass('active').children('ul').collapse('toggle');
29 |
30 | if ($toggle) {
31 | $(this).parent('li').siblings().removeClass('active').children('ul.in').collapse('hide');
32 | }
33 | });
34 | }
35 | };
36 |
37 | $.fn[ pluginName ] = function (options) {
38 | return this.each(function () {
39 | if (!$.data(this, "plugin_" + pluginName)) {
40 | $.data(this, "plugin_" + pluginName, new Plugin(this, options));
41 | }
42 | });
43 | };
44 |
45 | })(jQuery, window, document);
46 |
--------------------------------------------------------------------------------
/static/panel/js/sb-admin.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | $('#side-menu').metisMenu();
4 |
5 | });
6 |
7 | //Loads the correct sidebar on window load,
8 | //collapses the sidebar on window resize.
9 | $(function() {
10 | $(window).bind("load resize", function() {
11 | width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width;
12 | if (width < 768) {
13 | $('div.sidebar-collapse').addClass('collapse')
14 | } else {
15 | $('div.sidebar-collapse').removeClass('collapse')
16 | }
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/template/common/403.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% block title %}
3 | 错误信息
4 | {% endblock %}
5 | {% block inside_row %}
6 |
12 | {% endblock %}
--------------------------------------------------------------------------------
/template/common/404.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% block title %}
3 | 404 Not Found-页面未找到
4 | {% endblock %}
5 | {% block inside_row %}
6 |
12 | {% endblock %}
--------------------------------------------------------------------------------
/template/common/500.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% block title %}
3 | 500 Internal Error
4 | {% endblock %}
5 | {% block inside_row %}
6 |
7 |
8 |
Internal Error Detected
9 |
10 |
Go back
11 |
12 | {% endblock %}
--------------------------------------------------------------------------------
/template/common/base-with-sidebar.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% block inside_row %}
3 |
4 | {% block left %}{% endblock %}
5 |
6 |
7 | {% block right %}{% endblock %}
8 |
9 | {% endblock %}
--------------------------------------------------------------------------------
/template/common/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 | {% load settingsvalue %}
3 | {% load i18n %}
4 |
5 |
6 |
7 |
8 |
9 |
10 | {% block header_ext%}
11 | {% endblock %}
12 |
13 | {% spaceless %}
14 | {% block title %}
15 | {{title}}-
16 | {% if pager %}
17 | {% blocktrans %}page {{ pager }}{% endblocktrans %}
18 | -
19 | {% endif %}
20 | {% conf_value "sitename" %}
21 | {% endblock %}
22 | {% endspaceless %}
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
35 |
36 |
37 |
64 |
65 |
66 |
67 |
68 | {% block inside_row %}
69 | {% endblock %}
70 |
71 |
72 |
73 |
74 |
89 |
90 |
91 |
92 |
93 | {% block footer_ext %}{% endblock %}
94 |
95 |
96 |
--------------------------------------------------------------------------------
/template/common/error.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% block inside_row %}
3 |
15 | {% endblock %}
--------------------------------------------------------------------------------
/template/pagination/pagination.html:
--------------------------------------------------------------------------------
1 | {% if is_paginated %}
2 |
39 | {% endif %}
--------------------------------------------------------------------------------
/template/widget/links.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 |
5 | {% trans 'links' %}
6 |
7 |
8 |
9 |
12 | {% for key,val in conf.links.items %}
13 | -
14 | {{key}}
15 |
16 | {% endfor %}
17 |
18 |
19 |
--------------------------------------------------------------------------------
/template/widget/node-info.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{node.title}}
5 |
6 |
7 |
8 | {{node.description}}
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/template/widget/node.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 |
5 | {% trans 'nodes' %}
6 |
7 |
8 |
9 | {% for node in conf.nodes %}
10 |
{{node.title}}
11 | {% endfor %}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/template/widget/stat.html:
--------------------------------------------------------------------------------
1 | {% load i18n%}
2 |
3 |
4 |
5 | {% trans 'site stat' %}
6 |
7 |
8 |
9 | -
10 | {% trans 'number of users'%}:{{conf.user_count}}
11 |
12 | -
13 | {% trans 'number of topics'%}:{{conf.topic_count}}
14 |
15 | -
16 | {% trans 'number of replies'%}:{{conf.post_count}}
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/template/widget/topic-list.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 | {% load humanize %}
3 | {% load pagination_tags %}
4 |
5 |
6 |
7 |
8 | {% if post_list_title %}
9 | {{post_list_title}}
10 | {% else %}
11 |
{% trans 'home' %} {{node.title}}
12 | {% endif %}
13 |
14 | {% if node_view %}
15 |
{% trans 'new topic' %}
16 | {% endif %}
17 |
18 | {% if not topics %}
19 |
20 |
21 | {% trans "sorry, but we can't find the topics you need" %}
22 |
23 |
24 | {% endif %}
25 |
26 | {% autopaginate topics 30 %}
27 | {% for t in topics %}
28 |
29 |
30 |

31 |
32 |
33 |
34 | {{t.node.title}}
35 | {% with clicks=t.click %}
36 | {% blocktrans %}{{clicks}} clicks{% endblocktrans %}
37 | {% endwith %}
38 | {{t.time_created | naturaltime}}
39 | {{t.user.profile.username}}
40 | {{reply_count}}
41 |
42 |
43 |
44 | {{t.reply_count}}
45 |
46 |
47 |
48 | {% endfor %}
49 |
50 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/template/widget/user-panel.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 |
{% if request.user.is_authenticated %}
5 | {{request.user.profile.username}}
6 | {% else %}
7 | 什么是FairyBBS
8 | {% endif %}
9 |
10 |
11 | {% if request.user.is_authenticated %}
12 |
13 |

14 |
25 |
26 |
27 | {% else %}
28 |
29 | FairyBBS是使用Django框架建立的小型论坛系统
30 |
31 |
39 | {% comment %}
40 |
41 |
48 | {% endcomment %}
49 | {% endif %}
50 |
51 | {% if request.user.profile.unread_mention %}
52 |
55 | {% endif %}
56 |
57 |
--------------------------------------------------------------------------------