FlaskBB 2.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. celery_worker.py +18 -0
  2. flaskbb/__init__.py +19 -0
  3. flaskbb/app.py +551 -0
  4. flaskbb/auth/__init__.py +7 -0
  5. flaskbb/auth/forms.py +170 -0
  6. flaskbb/auth/plugins.py +119 -0
  7. flaskbb/auth/services/__init__.py +56 -0
  8. flaskbb/auth/services/activation.py +60 -0
  9. flaskbb/auth/services/authentication.py +188 -0
  10. flaskbb/auth/services/factories.py +52 -0
  11. flaskbb/auth/services/password.py +67 -0
  12. flaskbb/auth/services/reauthentication.py +87 -0
  13. flaskbb/auth/services/registration.py +261 -0
  14. flaskbb/auth/views.py +501 -0
  15. flaskbb/cli/__init__.py +24 -0
  16. flaskbb/cli/db.py +40 -0
  17. flaskbb/cli/main.py +705 -0
  18. flaskbb/cli/plugins.py +214 -0
  19. flaskbb/cli/themes.py +109 -0
  20. flaskbb/cli/translations.py +105 -0
  21. flaskbb/cli/users.py +113 -0
  22. flaskbb/cli/utils.py +203 -0
  23. flaskbb/configs/__init__.py +0 -0
  24. flaskbb/configs/config.cfg.template +281 -0
  25. flaskbb/configs/default.py +291 -0
  26. flaskbb/configs/testing.py +60 -0
  27. flaskbb/core/__init__.py +0 -0
  28. flaskbb/core/auth/__init__.py +7 -0
  29. flaskbb/core/auth/activation.py +60 -0
  30. flaskbb/core/auth/authentication.py +301 -0
  31. flaskbb/core/auth/password.py +54 -0
  32. flaskbb/core/auth/registration.py +112 -0
  33. flaskbb/core/changesets.py +96 -0
  34. flaskbb/core/exceptions.py +85 -0
  35. flaskbb/core/tokens.py +142 -0
  36. flaskbb/core/user/__init__.py +0 -0
  37. flaskbb/core/user/update.py +93 -0
  38. flaskbb/deprecation.py +106 -0
  39. flaskbb/display/__init__.py +0 -0
  40. flaskbb/display/navigation.py +109 -0
  41. flaskbb/email.py +89 -0
  42. flaskbb/exceptions.py +28 -0
  43. flaskbb/extensions.py +83 -0
  44. flaskbb/fixtures/__init__.py +0 -0
  45. flaskbb/fixtures/groups.py +137 -0
  46. flaskbb/fixtures/settings.py +325 -0
  47. flaskbb/forum/__init__.py +3 -0
  48. flaskbb/forum/forms.py +212 -0
  49. flaskbb/forum/locals.py +59 -0
  50. flaskbb/forum/models.py +1664 -0
  51. flaskbb/forum/utils.py +39 -0
  52. flaskbb/forum/views.py +1327 -0
  53. flaskbb/management/__init__.py +20 -0
  54. flaskbb/management/forms.py +516 -0
  55. flaskbb/management/models.py +150 -0
  56. flaskbb/management/plugins.py +46 -0
  57. flaskbb/management/views.py +1480 -0
  58. flaskbb/markup.py +148 -0
  59. flaskbb/migrations/201501082314_8ad96e49dc6_init.py +321 -0
  60. flaskbb/migrations/201503222157_514ca0a3282c_private_messages.py +99 -0
  61. flaskbb/migrations/201504082225_127be3fb000_added_m2m_forumgroups_table.py +37 -0
  62. flaskbb/migrations/201606061345_221d918aa9f0_add_user_authentication_infos.py +35 -0
  63. flaskbb/migrations/201606210939_d9530a529b3f_add_timezone_awareness_for_datetime.py +244 -0
  64. flaskbb/migrations/201611190919_d87cea4e995d_remove_timezone_info_from_birthday_field.py +38 -0
  65. flaskbb/migrations/201705041144_933bd7d807c4_add_more_non_nullables.py +192 -0
  66. flaskbb/migrations/201706300917_881dd22cab94_add_date_modified_to_conversations.py +50 -0
  67. flaskbb/migrations/201709041519_d0ffadc3ea48_add_hidden_columns.py +127 -0
  68. flaskbb/migrations/201711180953_7c3fcf8a3335_add_plugin_tables.py +66 -0
  69. flaskbb/migrations/201802021027_af3f5579c84d_add_cascades.py +483 -0
  70. flaskbb/migrations/201802282131_232e68a03aa2_change_emoji_shortcodes_to_characters.py +949 -0
  71. flaskbb/migrations/201803012138_5945d8081a95_remove_conversations.py +55 -0
  72. flaskbb/migrations/__init__.py +0 -0
  73. flaskbb/migrations/script.py.mako +24 -0
  74. flaskbb/plugins/__init__.py +10 -0
  75. flaskbb/plugins/manager.py +243 -0
  76. flaskbb/plugins/models.py +139 -0
  77. flaskbb/plugins/spec.py +1141 -0
  78. flaskbb/plugins/utils.py +86 -0
  79. flaskbb/static/app.css +19 -0
  80. flaskbb/static/app.css.map +1 -0
  81. flaskbb/static/app.js +2 -0
  82. flaskbb/static/app.js.map +1 -0
  83. flaskbb/static/avatar.svg +4 -0
  84. flaskbb/static/avatar100x100.png +0 -0
  85. flaskbb/static/avatar150x150.png +0 -0
  86. flaskbb/static/avatar400x400.png +0 -0
  87. flaskbb/static/avatar80x80.png +0 -0
  88. flaskbb/static/fa-regular-400.eot +0 -0
  89. flaskbb/static/fa-regular-400.svg +801 -0
  90. flaskbb/static/fa-regular-400.ttf +0 -0
  91. flaskbb/static/fa-regular-400.woff +0 -0
  92. flaskbb/static/fa-regular-400.woff2 +0 -0
  93. flaskbb/static/fa-solid-900.eot +0 -0
  94. flaskbb/static/fa-solid-900.svg +5034 -0
  95. flaskbb/static/fa-solid-900.ttf +0 -0
  96. flaskbb/static/fa-solid-900.woff +0 -0
  97. flaskbb/static/fa-solid-900.woff2 +0 -0
  98. flaskbb/static/favicon.ico +0 -0
  99. flaskbb/static/kuyN6Bh.jpg +0 -0
  100. flaskbb/static/vendors.js +3 -0
  101. flaskbb/static/vendors.js.LICENSE.txt +7 -0
  102. flaskbb/static/vendors.js.map +1 -0
  103. flaskbb/templates/_macros/form.html +218 -0
  104. flaskbb/templates/_macros/navigation.html +83 -0
  105. flaskbb/templates/_macros/pagination.html +109 -0
  106. flaskbb/templates/_macros/utils.html +11 -0
  107. flaskbb/templates/_partials/confirm_dialog.html +17 -0
  108. flaskbb/templates/_partials/editor_help.html +60 -0
  109. flaskbb/templates/_partials/flashed_messages.html +19 -0
  110. flaskbb/templates/auth/account_activation.html +22 -0
  111. flaskbb/templates/auth/forgot_password.html +27 -0
  112. flaskbb/templates/auth/login.html +35 -0
  113. flaskbb/templates/auth/reauth.html +22 -0
  114. flaskbb/templates/auth/register.html +34 -0
  115. flaskbb/templates/auth/request_account_activation.html +23 -0
  116. flaskbb/templates/auth/reset_password.html +25 -0
  117. flaskbb/templates/email/activate_account.html +10 -0
  118. flaskbb/templates/email/activate_account.txt +11 -0
  119. flaskbb/templates/email/reset_password.html +9 -0
  120. flaskbb/templates/email/reset_password.txt +11 -0
  121. flaskbb/templates/errors/forbidden_page.html +14 -0
  122. flaskbb/templates/errors/page_not_found.html +14 -0
  123. flaskbb/templates/errors/server_error.html +13 -0
  124. flaskbb/templates/errors/too_many_logins.html +14 -0
  125. flaskbb/templates/forum/_forum_meta.html +11 -0
  126. flaskbb/templates/forum/_forum_row.html +73 -0
  127. flaskbb/templates/forum/category.html +16 -0
  128. flaskbb/templates/forum/category_layout.html +124 -0
  129. flaskbb/templates/forum/edit_forum.html +109 -0
  130. flaskbb/templates/forum/forum.html +72 -0
  131. flaskbb/templates/forum/index.html +32 -0
  132. flaskbb/templates/forum/memberlist.html +62 -0
  133. flaskbb/templates/forum/new_post.html +49 -0
  134. flaskbb/templates/forum/new_topic.html +50 -0
  135. flaskbb/templates/forum/online_users.html +25 -0
  136. flaskbb/templates/forum/report_post.html +23 -0
  137. flaskbb/templates/forum/search_form.html +27 -0
  138. flaskbb/templates/forum/search_result.html +340 -0
  139. flaskbb/templates/forum/topic.html +244 -0
  140. flaskbb/templates/forum/topic_controls.html +119 -0
  141. flaskbb/templates/forum/topic_horizontal.html +201 -0
  142. flaskbb/templates/forum/topictracker.html +121 -0
  143. flaskbb/templates/layout.html +298 -0
  144. flaskbb/templates/management/banned_users.html +129 -0
  145. flaskbb/templates/management/category_form.html +46 -0
  146. flaskbb/templates/management/forum_form.html +51 -0
  147. flaskbb/templates/management/forums.html +191 -0
  148. flaskbb/templates/management/group_form.html +62 -0
  149. flaskbb/templates/management/groups.html +97 -0
  150. flaskbb/templates/management/management_layout.html +23 -0
  151. flaskbb/templates/management/overview.html +178 -0
  152. flaskbb/templates/management/plugins.html +96 -0
  153. flaskbb/templates/management/reports.html +116 -0
  154. flaskbb/templates/management/settings.html +74 -0
  155. flaskbb/templates/management/user_form.html +65 -0
  156. flaskbb/templates/management/users.html +165 -0
  157. flaskbb/templates/user/all_posts.html +48 -0
  158. flaskbb/templates/user/all_topics.html +56 -0
  159. flaskbb/templates/user/change_email.html +19 -0
  160. flaskbb/templates/user/change_password.html +19 -0
  161. flaskbb/templates/user/change_user_details.html +28 -0
  162. flaskbb/templates/user/general_settings.html +18 -0
  163. flaskbb/templates/user/profile.html +47 -0
  164. flaskbb/templates/user/profile_layout.html +124 -0
  165. flaskbb/templates/user/settings_layout.html +32 -0
  166. flaskbb/themes/aurora/Makefile +21 -0
  167. flaskbb/themes/aurora/README.md +41 -0
  168. flaskbb/themes/aurora/build_emoji_set.py +32 -0
  169. flaskbb/themes/aurora/info.json +11 -0
  170. flaskbb/themes/aurora/package-lock.json +4904 -0
  171. flaskbb/themes/aurora/package.json +60 -0
  172. flaskbb/themes/aurora/preview.png +0 -0
  173. flaskbb/themes/aurora/src/app/confirm_modal.js +45 -0
  174. flaskbb/themes/aurora/src/app/editor.js +142 -0
  175. flaskbb/themes/aurora/src/app/emoji.js +3515 -0
  176. flaskbb/themes/aurora/src/app/flaskbb.js +271 -0
  177. flaskbb/themes/aurora/src/app/utils.js +11 -0
  178. flaskbb/themes/aurora/src/app.js +26 -0
  179. flaskbb/themes/aurora/src/assets/avatar.svg +4 -0
  180. flaskbb/themes/aurora/src/assets/avatar100x100.png +0 -0
  181. flaskbb/themes/aurora/src/assets/avatar150x150.png +0 -0
  182. flaskbb/themes/aurora/src/assets/avatar400x400.png +0 -0
  183. flaskbb/themes/aurora/src/assets/avatar80x80.png +0 -0
  184. flaskbb/themes/aurora/src/assets/favicon.ico +0 -0
  185. flaskbb/themes/aurora/src/scss/_button.scss +39 -0
  186. flaskbb/themes/aurora/src/scss/_category.scss +67 -0
  187. flaskbb/themes/aurora/src/scss/_forum.scss +56 -0
  188. flaskbb/themes/aurora/src/scss/_management.scss +205 -0
  189. flaskbb/themes/aurora/src/scss/_misc.scss +181 -0
  190. flaskbb/themes/aurora/src/scss/_navigation.scss +130 -0
  191. flaskbb/themes/aurora/src/scss/_page.scss +51 -0
  192. flaskbb/themes/aurora/src/scss/_profile.scss +121 -0
  193. flaskbb/themes/aurora/src/scss/_pygments.scss +65 -0
  194. flaskbb/themes/aurora/src/scss/_text.scss +43 -0
  195. flaskbb/themes/aurora/src/scss/_topic.scss +172 -0
  196. flaskbb/themes/aurora/src/scss/_variables.scss +122 -0
  197. flaskbb/themes/aurora/src/scss/styles.scss +26 -0
  198. flaskbb/themes/aurora/tsconfig.json +24 -0
  199. flaskbb/themes/aurora/webpack.common.js +132 -0
  200. flaskbb/themes/aurora/webpack.dev.js +7 -0
  201. flaskbb/themes/aurora/webpack.prod.js +7 -0
  202. flaskbb/tokens/__init__.py +12 -0
  203. flaskbb/tokens/serializer.py +93 -0
  204. flaskbb/tokens/verifiers.py +34 -0
  205. flaskbb/translations/da/LC_MESSAGES/messages.po +3324 -0
  206. flaskbb/translations/de/LC_MESSAGES/messages.po +3359 -0
  207. flaskbb/translations/en/LC_MESSAGES/messages.po +4126 -0
  208. flaskbb/translations/es/LC_MESSAGES/messages.po +3363 -0
  209. flaskbb/translations/fr/LC_MESSAGES/messages.po +3398 -0
  210. flaskbb/translations/nb_NO/LC_MESSAGES/messages.po +3493 -0
  211. flaskbb/translations/pl/LC_MESSAGES/messages.po +3350 -0
  212. flaskbb/translations/pt/LC_MESSAGES/messages.po +3488 -0
  213. flaskbb/translations/pt_BR/LC_MESSAGES/messages.po +3350 -0
  214. flaskbb/translations/ru/LC_MESSAGES/messages.po +3335 -0
  215. flaskbb/translations/sv_SE/LC_MESSAGES/messages.po +3347 -0
  216. flaskbb/translations/ta/LC_MESSAGES/messages.po +3528 -0
  217. flaskbb/translations/uk/LC_MESSAGES/messages.po +3505 -0
  218. flaskbb/translations/zh_CN/LC_MESSAGES/messages.po +3325 -0
  219. flaskbb/translations/zh_TW/LC_MESSAGES/messages.po +3324 -0
  220. flaskbb/user/__init__.py +19 -0
  221. flaskbb/user/forms.py +131 -0
  222. flaskbb/user/models.py +527 -0
  223. flaskbb/user/plugins.py +87 -0
  224. flaskbb/user/services/__init__.py +0 -0
  225. flaskbb/user/services/factories.py +89 -0
  226. flaskbb/user/services/update.py +88 -0
  227. flaskbb/user/services/validators.py +107 -0
  228. flaskbb/user/views.py +235 -0
  229. flaskbb/utils/__init__.py +3 -0
  230. flaskbb/utils/alembic.py +79 -0
  231. flaskbb/utils/database.py +189 -0
  232. flaskbb/utils/datastructures.py +31 -0
  233. flaskbb/utils/forms.py +209 -0
  234. flaskbb/utils/helpers.py +940 -0
  235. flaskbb/utils/http.py +81 -0
  236. flaskbb/utils/populate.py +445 -0
  237. flaskbb/utils/queries.py +154 -0
  238. flaskbb/utils/requirements.py +352 -0
  239. flaskbb/utils/search.py +143 -0
  240. flaskbb/utils/settings.py +55 -0
  241. flaskbb/utils/translations.py +229 -0
  242. flaskbb-2.2.0.dist-info/METADATA +120 -0
  243. flaskbb-2.2.0.dist-info/RECORD +247 -0
  244. flaskbb-2.2.0.dist-info/WHEEL +4 -0
  245. flaskbb-2.2.0.dist-info/entry_points.txt +2 -0
  246. flaskbb-2.2.0.dist-info/licenses/LICENSE +33 -0
  247. wsgi.py +11 -0
celery_worker.py ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ flaskbb.celery_worker
4
+ ~~~~~~~~~~~~~~~~~~~~~
5
+
6
+ Prepares the celery app for the celery worker.
7
+ To start celery, enter this in the console::
8
+
9
+ celery -A celery_worker.celery --loglevel=info worker
10
+
11
+ :copyright: (c) 2016 by the FlaskBB Team.
12
+ :license: BSD, see LICENSE for more details.
13
+ """
14
+
15
+ from flaskbb.app import create_app
16
+ from flaskbb.extensions import celery # noqa
17
+
18
+ app = create_app()
flaskbb/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ flaskbb
4
+ ~~~~~~~
5
+
6
+ FlaskBB is a forum software written in python using the
7
+ microframework Flask.
8
+
9
+ :copyright: (c) 2014 by the FlaskBB Team.
10
+ :license: BSD, see LICENSE for more details.
11
+ """
12
+
13
+ __version__ = "2.2.0"
14
+
15
+ import logging
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ from flaskbb.app import create_app # noqa
flaskbb/app.py ADDED
@@ -0,0 +1,551 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ flaskbb.app
4
+ ~~~~~~~~~~~
5
+
6
+ manages the app creation and configuration process
7
+
8
+ :copyright: (c) 2014 by the FlaskBB Team.
9
+ :license: BSD, see LICENSE for more details.
10
+ """
11
+
12
+ import logging
13
+ import logging.config
14
+ import os
15
+ import sys
16
+ import time
17
+ import warnings
18
+ from datetime import UTC, datetime
19
+ from typing import Any, Callable
20
+
21
+ from celery import Celery
22
+ from flask import Flask, request
23
+ from flask_login import current_user
24
+ from sqlalchemy import event
25
+ from sqlalchemy.engine import Engine
26
+ from sqlalchemy.exc import OperationalError, ProgrammingError
27
+
28
+ # extensions
29
+ from flaskbb.extensions import (
30
+ alembic,
31
+ allows,
32
+ babel,
33
+ cache,
34
+ celery,
35
+ csrf,
36
+ db,
37
+ debugtoolbar,
38
+ limiter,
39
+ login_manager,
40
+ mail,
41
+ pluggy,
42
+ redis_store,
43
+ themes,
44
+ whooshee,
45
+ )
46
+ from flaskbb.plugins import spec
47
+ from flaskbb.plugins.models import PluginRegistry
48
+ from flaskbb.plugins.utils import remove_zombie_plugins_from_db, template_hook
49
+
50
+ # models
51
+ from flaskbb.user.models import Guest, User
52
+
53
+ # various helpers
54
+ from flaskbb.utils.helpers import (
55
+ app_config_from_env,
56
+ crop_title,
57
+ format_date,
58
+ format_datetime,
59
+ format_time,
60
+ forum_is_unread,
61
+ get_alembic_locations,
62
+ get_flaskbb_config,
63
+ is_online,
64
+ mark_online,
65
+ render_template,
66
+ time_since,
67
+ time_utcnow,
68
+ topic_is_unread,
69
+ )
70
+
71
+ # permission checks (here they are used for the jinja filters)
72
+ from flaskbb.utils.requirements import (
73
+ CanBanUser,
74
+ CanEditUser,
75
+ IsAdmin,
76
+ IsAtleastModerator,
77
+ can_delete_topic,
78
+ can_edit_post,
79
+ can_moderate,
80
+ can_post_reply,
81
+ can_post_topic,
82
+ has_permission,
83
+ permission_with_identity,
84
+ )
85
+
86
+ # whooshees
87
+ from flaskbb.utils.search import (
88
+ ForumWhoosheer,
89
+ PostWhoosheer,
90
+ TopicWhoosheer,
91
+ UserWhoosheer,
92
+ )
93
+
94
+ # app specific configurations
95
+ from flaskbb.utils.settings import flaskbb_config
96
+ from flaskbb.utils.translations import FlaskBBDomain
97
+
98
+ from . import markup # noqa
99
+ from .auth import views as auth_views # noqa
100
+ from .deprecation import FlaskBBDeprecation
101
+ from .display.navigation import NavigationContentType
102
+ from .forum import views as forum_views # noqa
103
+ from .management import views as management_views # noqa
104
+ from .user import views as user_views # noqa
105
+
106
+ logger = logging.getLogger(__name__)
107
+
108
+
109
+ def create_app(config: object | None = None, instance_path: str | None = None):
110
+ """Creates the app.
111
+
112
+ :param instance_path: An alternative instance path for the application.
113
+ By default the folder ``'instance'`` next to the
114
+ package or module is assumed to be the instance
115
+ path.
116
+ See :ref:`Instance Folders <flask:instance-folders>`.
117
+ :param config: The configuration file or object.
118
+ The environment variable is weightet as the heaviest.
119
+ For example, if the config is specified via an file
120
+ and a ENVVAR, it will load the config via the file and
121
+ later overwrite it from the ENVVAR.
122
+ If no config is provided, FlaskBB will try to load the
123
+ config named ``flaskbb.cfg`` from the instance path.
124
+ """
125
+
126
+ app = Flask("flaskbb", instance_path=instance_path, instance_relative_config=True)
127
+
128
+ # instance folders are not automatically created by flask
129
+ if not os.path.exists(app.instance_path):
130
+ os.makedirs(app.instance_path)
131
+
132
+ configure_app(app, config)
133
+ configure_celery_app(app, celery)
134
+ configure_extensions(app)
135
+
136
+ load_plugins(app)
137
+ configure_blueprints(app)
138
+ configure_template_filters(app)
139
+ configure_context_processors(app)
140
+ configure_before_handlers(app)
141
+ configure_errorhandlers(app)
142
+ configure_migrations(app)
143
+ configure_translations(app)
144
+ pluggy.hook.flaskbb_additional_setup(app=app, pluggy=pluggy)
145
+
146
+ return app
147
+
148
+
149
+ def configure_app(app: Flask, config: Any):
150
+ """Configures FlaskBB."""
151
+ # Use the default config and override it afterwards
152
+ app.config.from_object("flaskbb.configs.default.DefaultConfig")
153
+ config = get_flaskbb_config(app, config)
154
+ # Path
155
+ if isinstance(config, str):
156
+ app.config.from_pyfile(config)
157
+ # Module
158
+ else:
159
+ # try to update the config from the object
160
+ app.config.from_object(config)
161
+
162
+ # Add the location of the config to the config
163
+ app.config["CONFIG_PATH"] = config
164
+
165
+ # Environment
166
+ # Parse the env for FLASKBB_ prefixed env variables and set
167
+ # them on the config object
168
+ app_config_from_env(app, prefix="FLASKBB_")
169
+
170
+ # Migrate Celery 4.x config to Celery 6.x
171
+ old_celery_config = app.config.get_namespace("CELERY_")
172
+ celery_config = {}
173
+ for key, value in old_celery_config.items():
174
+ # config is the new format
175
+ if key != "config":
176
+ config_key = f"CELERY_{key.upper()}"
177
+ celery_config[key] = value
178
+ try:
179
+ del app.config[config_key]
180
+ except KeyError:
181
+ pass
182
+
183
+ # merge the new config with the old one
184
+ new_celery_config = app.config.get("CELERY_CONFIG")
185
+ new_celery_config.update(celery_config)
186
+ app.config.update({"CELERY_CONFIG": new_celery_config})
187
+
188
+ # Setting up logging as early as possible
189
+ configure_logging(app)
190
+
191
+ if not isinstance(config, str) and config is not None:
192
+ config_name = "{}.{}".format(config.__module__, config.__name__)
193
+ else:
194
+ config_name = config
195
+
196
+ logger.info("Using config from: {}".format(config_name))
197
+
198
+ deprecation_level = app.config.get("DEPRECATION_LEVEL", "default")
199
+
200
+ # never set the deprecation level during testing, pytest will handle it
201
+ if not app.testing: # pragma: no branch
202
+ warnings.simplefilter(deprecation_level, FlaskBBDeprecation)
203
+
204
+ # Filter Flask-Limiter in-memory warnings when running in debug mode oder test mode
205
+ if app.debug or app.testing:
206
+ warnings.filterwarnings(
207
+ action="ignore",
208
+ message=".*Using the in-memory storage for tracking rate limits.*",
209
+ )
210
+
211
+ debug_panels = app.config.setdefault(
212
+ "DEBUG_TB_PANELS",
213
+ [
214
+ "flask_debugtoolbar.panels.versions.VersionDebugPanel",
215
+ "flask_debugtoolbar.panels.timer.TimerDebugPanel",
216
+ "flask_debugtoolbar.panels.headers.HeaderDebugPanel",
217
+ "flask_debugtoolbar.panels.request_vars.RequestVarsDebugPanel",
218
+ "flask_debugtoolbar.panels.config_vars.ConfigVarsDebugPanel",
219
+ "flask_debugtoolbar.panels.template.TemplateDebugPanel",
220
+ "flask_debugtoolbar.panels.sqlalchemy.SQLAlchemyDebugPanel",
221
+ "flask_debugtoolbar.panels.logger.LoggingPanel",
222
+ "flask_debugtoolbar.panels.route_list.RouteListDebugPanel",
223
+ "flask_debugtoolbar.panels.profiler.ProfilerDebugPanel",
224
+ ],
225
+ )
226
+
227
+ if all("WarningsPanel" not in p for p in debug_panels):
228
+ debug_panels.append("flask_debugtoolbar_warnings.WarningsPanel")
229
+
230
+
231
+ def configure_celery_app(app: Flask, celery: Celery):
232
+ """Configures the celery app."""
233
+ celery.conf.update(app.config.get("CELERY_CONFIG"))
234
+
235
+ TaskBase = celery.Task
236
+
237
+ class ContextTask(TaskBase):
238
+ def __call__(self, *args, **kwargs):
239
+ with app.app_context():
240
+ return TaskBase.__call__(self, *args, **kwargs)
241
+
242
+ celery.Task = ContextTask
243
+
244
+
245
+ def configure_blueprints(app: Flask):
246
+ pluggy.hook.flaskbb_load_blueprints(app=app)
247
+
248
+
249
+ def configure_extensions(app: Flask):
250
+ """Configures the extensions."""
251
+ # Flask-Allows
252
+ allows.init_app(app)
253
+ allows.identity_loader(lambda: current_user)
254
+
255
+ # Flask-WTF CSRF
256
+ csrf.init_app(app)
257
+
258
+ # Flask-SQLAlchemy
259
+ db.init_app(app)
260
+
261
+ # Flask-Alembic
262
+ alembic.init_app(app)
263
+
264
+ # Flask-Mail
265
+ mail.init_app(app)
266
+
267
+ # Flask-Cache
268
+ cache.init_app(app)
269
+
270
+ # Flask-Debugtoolbar
271
+ debugtoolbar.init_app(app)
272
+
273
+ # Flask-Themes
274
+ themes.init_themes(app, app_identifier="flaskbb")
275
+
276
+ # Flask-And-Redis
277
+ redis_store.init_app(app)
278
+
279
+ # Flask-Limiter
280
+ limiter.init_app(app)
281
+
282
+ # Flask-Whooshee
283
+ whooshee.init_app(app)
284
+ # not needed for unittests - and it will speed up testing A LOT
285
+ if not app.testing:
286
+ whooshee.register_whoosheer(PostWhoosheer)
287
+ whooshee.register_whoosheer(TopicWhoosheer)
288
+ whooshee.register_whoosheer(ForumWhoosheer)
289
+ whooshee.register_whoosheer(UserWhoosheer)
290
+
291
+ # Flask-Login
292
+ login_manager.login_view = app.config["LOGIN_VIEW"]
293
+ login_manager.refresh_view = app.config["REAUTH_VIEW"]
294
+ login_manager.login_message_category = app.config["LOGIN_MESSAGE_CATEGORY"]
295
+ login_manager.needs_refresh_message_category = app.config[
296
+ "REFRESH_MESSAGE_CATEGORY"
297
+ ]
298
+ login_manager.anonymous_user = Guest
299
+
300
+ @login_manager.user_loader
301
+ def load_user(user_id: int):
302
+ """Loads the user. Required by the `login` extension."""
303
+ user = db.session.execute(
304
+ db.select(User).filter_by(id=user_id)
305
+ ).scalar_one_or_none()
306
+ pluggy.hook.flaskbb_current_user(app=app, user=user)
307
+ return user
308
+
309
+ login_manager.init_app(app)
310
+
311
+
312
+ def configure_template_filters(app: Flask):
313
+ """Configures the template filters."""
314
+ filters: dict[str, Callable[..., Any]] = {}
315
+
316
+ filters["crop_title"] = crop_title
317
+ filters["format_date"] = format_date
318
+ filters["format_time"] = format_time
319
+ filters["format_datetime"] = format_datetime
320
+ filters["forum_is_unread"] = forum_is_unread
321
+ filters["is_online"] = is_online
322
+ filters["time_since"] = time_since
323
+ filters["topic_is_unread"] = topic_is_unread
324
+
325
+ permissions = [
326
+ ("is_admin", IsAdmin),
327
+ ("is_moderator", IsAtleastModerator),
328
+ ("is_admin_or_moderator", IsAtleastModerator),
329
+ ("can_edit_user", CanEditUser),
330
+ ("can_ban_user", CanBanUser),
331
+ ]
332
+
333
+ filters.update(
334
+ (name, permission_with_identity(perm, name=name)) for name, perm in permissions
335
+ )
336
+
337
+ filters["can_moderate"] = can_moderate
338
+ filters["post_reply"] = can_post_reply
339
+ filters["edit_post"] = can_edit_post
340
+ filters["delete_post"] = can_edit_post
341
+ filters["post_topic"] = can_post_topic
342
+ filters["delete_topic"] = can_delete_topic
343
+ filters["has_permission"] = has_permission
344
+
345
+ app.jinja_env.filters.update(filters)
346
+
347
+ jinja_globals: dict[str, Callable[..., Any]] = {}
348
+ jinja_globals["run_hook"] = template_hook
349
+ jinja_globals["NavigationContentType"] = NavigationContentType
350
+ app.jinja_env.globals.update(jinja_globals)
351
+
352
+ pluggy.hook.flaskbb_jinja_directives(app=app)
353
+
354
+
355
+ def configure_context_processors(app: Flask):
356
+ """Configures the context processors."""
357
+
358
+ @app.context_processor
359
+ def inject_flaskbb_config():
360
+ """Injects the ``flaskbb_config`` config variable into the
361
+ templates.
362
+ """
363
+ return dict(flaskbb_config=flaskbb_config, format_date=format_date)
364
+
365
+ @app.context_processor
366
+ def inject_now():
367
+ """Injects the current time."""
368
+ return dict(now=datetime.now(UTC))
369
+
370
+
371
+ def configure_before_handlers(app: Flask):
372
+ """Configures the before request handlers."""
373
+
374
+ @app.before_request
375
+ def update_lastseen():
376
+ """Updates `lastseen` before every reguest if the user is
377
+ authenticated."""
378
+ if current_user.is_authenticated:
379
+ current_user.lastseen = time_utcnow()
380
+ db.session.add(current_user)
381
+ db.session.commit()
382
+
383
+ if app.config["REDIS_ENABLED"]:
384
+
385
+ @app.before_request
386
+ def mark_current_user_online():
387
+ if current_user.is_authenticated:
388
+ mark_online(current_user.username)
389
+ else:
390
+ mark_online(request.remote_addr, guest=True)
391
+
392
+ pluggy.hook.flaskbb_request_processors(app=app)
393
+
394
+
395
+ def configure_errorhandlers(app: Flask):
396
+ """Configures the error handlers."""
397
+
398
+ @app.errorhandler(403)
399
+ def forbidden_page(error):
400
+ return render_template("errors/forbidden_page.html"), 403
401
+
402
+ @app.errorhandler(404)
403
+ def page_not_found(error):
404
+ return render_template("errors/page_not_found.html"), 404
405
+
406
+ @app.errorhandler(500)
407
+ def server_error_page(error):
408
+ return render_template("errors/server_error.html"), 500
409
+
410
+ pluggy.hook.flaskbb_errorhandlers(app=app)
411
+
412
+
413
+ def configure_migrations(app: Flask):
414
+ """Configure migrations."""
415
+ plugin_dirs = pluggy.hook.flaskbb_load_migrations()
416
+ version_locations = get_alembic_locations(plugin_dirs)
417
+
418
+ app.config["ALEMBIC"]["version_locations"] = version_locations
419
+
420
+
421
+ def configure_translations(app: Flask):
422
+ """Configure translations."""
423
+
424
+ # we have to initialize the extension after we have loaded the plugins
425
+ # because we of the 'flaskbb_load_translations' hook
426
+ babel.init_app(app=app, default_domain=FlaskBBDomain(app))
427
+
428
+ @babel.localeselector
429
+ def get_locale():
430
+ # if a user is logged in, use the locale from the user settings
431
+ if current_user and current_user.is_authenticated and current_user.language:
432
+ return current_user.language
433
+ # otherwise we will just fallback to the default language
434
+ return flaskbb_config["DEFAULT_LANGUAGE"]
435
+
436
+
437
+ def configure_logging(app: Flask):
438
+ """Configures logging."""
439
+ if app.config.get("USE_DEFAULT_LOGGING"):
440
+ configure_default_logging(app)
441
+
442
+ if app.config.get("LOG_CONF_FILE"):
443
+ logging.config.fileConfig(
444
+ app.config["LOG_CONF_FILE"], disable_existing_loggers=False
445
+ )
446
+
447
+ if app.config["SQLALCHEMY_ECHO"]:
448
+ # Ref: http://stackoverflow.com/a/8428546
449
+ @event.listens_for(Engine, "before_cursor_execute")
450
+ def before_cursor_execute(
451
+ conn, cursor, statement, parameters, context, executemany
452
+ ):
453
+ conn.info.setdefault("query_start_time", []).append(time.time())
454
+
455
+ @event.listens_for(Engine, "after_cursor_execute")
456
+ def after_cursor_execute(
457
+ conn, cursor, statement, parameters, context, executemany
458
+ ):
459
+ total = time.time() - conn.info["query_start_time"].pop(-1)
460
+ app.logger.debug("Total Time: %f", total)
461
+
462
+
463
+ def configure_default_logging(app: Flask):
464
+ # Load default logging config
465
+ logging.config.dictConfig(app.config["LOG_DEFAULT_CONF"])
466
+
467
+ if app.config["SEND_LOGS"]:
468
+ configure_mail_logs(app)
469
+
470
+
471
+ def configure_mail_logs(app: Flask, formatter: logging.Formatter | None = None):
472
+ from logging.handlers import SMTPHandler
473
+
474
+ if formatter is None:
475
+ formatter = logging.Formatter(
476
+ "%(asctime)s %(levelname)-7s %(name)-25s %(message)s"
477
+ )
478
+ mail_handler = SMTPHandler(
479
+ app.config["MAIL_SERVER"],
480
+ app.config["MAIL_DEFAULT_SENDER"],
481
+ app.config["ADMINS"],
482
+ "application error, no admins specified",
483
+ (app.config["MAIL_USERNAME"], app.config["MAIL_PASSWORD"]),
484
+ )
485
+
486
+ mail_handler.setLevel(logging.ERROR)
487
+ mail_handler.setFormatter(formatter)
488
+ app.logger.addHandler(mail_handler)
489
+
490
+
491
+ def load_plugins(app: Flask):
492
+ pluggy.add_hookspecs(spec)
493
+
494
+ # have to find all the flaskbb modules that are loaded this way
495
+ # otherwise sys.modules might change while we're iterating it
496
+ # because of imports and that makes Python very unhappy
497
+ # we are not interested in duplicated plugins or invalid ones
498
+ # ('None' - appears on py2) and thus using a set
499
+ flaskbb_modules = set(
500
+ module for name, module in sys.modules.items() if name.startswith("flaskbb")
501
+ )
502
+ for module in flaskbb_modules:
503
+ pluggy.register(module, internal=True)
504
+
505
+ try:
506
+ with app.app_context():
507
+ plugins = db.session.execute(db.select(PluginRegistry)).scalars().all()
508
+
509
+ except (OperationalError, ProgrammingError) as exc:
510
+ logger.debug(
511
+ "Database is not setup correctly or has not been setup yet.",
512
+ exc_info=exc,
513
+ )
514
+ # load plugins even though the database isn't setup correctly
515
+ # i.e. when creating the initial database and wanting to install
516
+ # the plugins migration as well
517
+ pluggy.load_setuptools_entrypoints("flaskbb_plugins")
518
+ return
519
+
520
+ for plugin in plugins:
521
+ if not plugin.enabled:
522
+ pluggy.set_blocked(plugin.name)
523
+
524
+ pluggy.load_setuptools_entrypoints("flaskbb_plugins")
525
+ pluggy.hook.flaskbb_extensions(app=app)
526
+
527
+ loaded_names = set([p[0] for p in pluggy.list_name_plugin()])
528
+ registered_names: set[str] = set([p.name for p in plugins])
529
+ unregistered = [
530
+ PluginRegistry(name=name)
531
+ for name in loaded_names - registered_names
532
+ # ignore internal FlaskBB modules
533
+ if not name.startswith("flaskbb.") and name != "flaskbb"
534
+ ]
535
+ with app.app_context():
536
+ db.session.add_all(unregistered)
537
+ db.session.commit()
538
+
539
+ removed = 0
540
+ if app.config["REMOVE_DEAD_PLUGINS"]:
541
+ removed = remove_zombie_plugins_from_db()
542
+ logger.info("Removed Plugins: {}".format(removed))
543
+
544
+ # we need a copy of it because of
545
+ # RuntimeError: dictionary changed size during iteration
546
+ tasks = celery.tasks.copy()
547
+ disabled_plugins = [p.__package__ for p in pluggy.get_disabled_plugins()]
548
+ for task_name, task in tasks.items():
549
+ if task.__module__.split(".")[0] in disabled_plugins:
550
+ logger.debug("Unregistering task: '{}'".format(task))
551
+ celery.tasks.unregister(task_name)
@@ -0,0 +1,7 @@
1
+ import logging
2
+
3
+ from pluggy import HookimplMarker
4
+
5
+ impl = HookimplMarker("flaskbb")
6
+
7
+ logger = logging.getLogger(__name__)