Skip to content

Commit e89c88f

Browse files
authored
Merge pull request #872 from LeXofLeviafan/flask-admin-v2
migrating to Flask-Admin v2
2 parents 70c2f5b + 01265bd commit e89c88f

File tree

9 files changed

+48
-43
lines changed

9 files changed

+48
-43
lines changed

bukuserver/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ $ # regular/venv install
3636
$ pip3 install "buku[server]"
3737
$ # pipx install
3838
$ pipx install "buku[server]"
39+
$ # with locales
40+
$ pipx install "buku[server,locales]"
3941
```
4042

4143
#### From source
@@ -45,6 +47,8 @@ $ git clone https://github.com/jarun/buku
4547
$ cd buku
4648
$ # regular/venv install
4749
$ pip3 install ".[server]"
50+
$ # with locales
51+
$ pip3 install ".[server,locales]"
4852
```
4953

5054
#### Using Docker
@@ -134,7 +138,7 @@ _**²**_ if input is invalid, the default value will be used if defined
134138

135139
_**³**_ `BUKUSERVER_DB_FILE` can be a DB name (plain filename without extension; cannot contain `.`). The specified DB with `.db` extension is located in default DB directory (which you can override with `BUKU_DEFAULT_DBDIR`).
136140

137-
_****_ `BUKUSERVER_LOCALE` requires either `flask_babel` or `flask_babelex` installed
141+
_****_ `BUKUSERVER_LOCALE` requires buku to be installed with `[locales]`
138142

139143
_****_ the value for `BUKUSERVER_REVERSE_PROXY_PATH` is recommended to include preceding slash and not have trailing slash (i.e. use `/foo` not `/foo/`)
140144

bukuserver/__init__.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
try: # as per Flask-Admin-1.6.1
2-
try:
3-
from flask_babelex import gettext, ngettext, pgettext, lazy_gettext, lazy_pgettext, LazyString
4-
except ImportError:
5-
from flask_babel import gettext, ngettext, pgettext, lazy_gettext, lazy_pgettext, LazyString
1+
try:
2+
from flask_babel import gettext, ngettext, pgettext, lazy_gettext, lazy_pgettext, LazyString
63
except ImportError:
74
from flask_admin.babel import gettext as _gettext, ngettext, lazy_gettext
85
gettext = lambda s, *a, **kw: (s if not kw else _gettext(s, *a, **kw))

bukuserver/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
arrow>=1.2.2
2-
Flask-Admin>=1.6.1,<2
2+
Flask-Admin>=2.0.0
33
flask-paginate>=2022.1.8
44
Flask-WTF>=1.0.1
55
Flask>=2.2.2

bukuserver/server.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from flask import Flask, redirect, request, url_for
1212
from flask.cli import FlaskGroup
1313
from flask_admin import Admin
14+
from flask_admin.theme import Bootstrap4Theme
1415
from flasgger import Swagger
1516

1617
from buku import BukuDb, __version__
@@ -35,13 +36,9 @@ def get_bool_from_env_var(key: str, default_value: bool = False) -> bool:
3536

3637

3738
def init_locale(app, context_processor=lambda: {}):
38-
try: # as per Flask-Admin-1.6.1
39-
try:
40-
from flask_babelex import Babel
41-
Babel(app).localeselector(lambda: app.config['BUKUSERVER_LOCALE'])
42-
except ImportError:
43-
from flask_babel import Babel
44-
Babel().init_app(app, locale_selector=lambda: app.config['BUKUSERVER_LOCALE'])
39+
try:
40+
from flask_babel import Babel
41+
Babel().init_app(app, locale_selector=lambda: app.config['BUKUSERVER_LOCALE'])
4542
app.context_processor(lambda: {'lang': app.config['BUKUSERVER_LOCALE'] or 'en', **context_processor()})
4643
except Exception as e:
4744
app.jinja_env.add_extension('jinja2.ext.i18n')
@@ -99,7 +96,7 @@ def create_app(db_file=None):
9996
app.config['REVERSE_PROXY_PATH'] = reverse_proxy_path
10097
ReverseProxyPrefixFix(app)
10198
bukudb = BukuDb(dbfile=db_file)
102-
app.config['FLASK_ADMIN_SWATCH'] = (os.getenv('BUKUSERVER_THEME') or 'default').lower()
99+
theme = (os.getenv('BUKUSERVER_THEME') or 'default').lower()
103100
app.config['BUKUSERVER_LOCALE'] = os.getenv('BUKUSERVER_LOCALE') or 'en'
104101
_dir = os.path.dirname(os.path.realpath(__file__))
105102
app.config['SWAGGER'] = {'title': 'Bukuserver API', 'doc_dir': os.path.join(_dir, 'apidocs')}
@@ -118,7 +115,7 @@ def shell_context():
118115
app.jinja_env.globals.update(_p=_p, dbfile=bukudb.dbfile, dbname=bukudb.dbname)
119116

120117
admin = Admin(
121-
app, name='buku server', template_mode='bootstrap4',
118+
app, name='buku server', theme=Bootstrap4Theme(swatch=theme),
122119
index_view=views.CustomAdminIndexView(
123120
template='bukuserver/home.html', url='/'
124121
)

bukuserver/templates/bukuserver/lib.html

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,11 @@
143143
{% macro page_size_custom() %}
144144
<script>
145145
$(document).ready(function() {
146-
const SIZES = [20, 50, 100]; // hardcoded list; see page_size_form() in admin/model/layout.html
147146
let pageSize = url => new URL(url || location.host).searchParams.get('page_size');
148147
$(`.nav.nav-tabs .dropdown-menu`).each(function () {
149148
let _sizes = $(`a.dropdown-item`, this).map(function () {return pageSize(this.href)}).get();
150-
if (SIZES.every((x, i) => x == _sizes[i]))
151-
$('a', this).last().clone().text({{ _('custom')|tojson }}).attr('href', `#`).on('click', () => {
149+
if (_sizes.length > 2 && _sizes.length == new Set(_sizes).size) // 3+ links; each link has different pagesize
150+
$('a', this).last().clone().text({{ _('custom')|tojson }}).removeClass('active').attr('href', `#`).on('click', () => {
152151
let page = prompt({{ _('Set custom page size (empty for default)')|tojson }}, pageSize(location) || '');
153152
if (Number(page) || (page == "")) {
154153
let search = new URL(location).searchParams;

bukuserver/views.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,20 @@ def __init__(self, bukudb: buku.BukuDb, *args, **kwargs):
195195
custom_model = types.SimpleNamespace(bukudb=bukudb, __name__='bookmark')
196196
super().__init__(custom_model, *args, **kwargs)
197197

198+
@property
199+
def url_render_mode(self):
200+
return app_param('URL_RENDER_MODE', DEFAULT_URL_RENDER_MODE)
201+
198202
@property
199203
def page_size(self):
200204
return app_param('PER_PAGE', DEFAULT_PER_PAGE)
201205

202206
@property
203-
def url_render_mode(self):
204-
return app_param('URL_RENDER_MODE', DEFAULT_URL_RENDER_MODE)
207+
def page_size_options(self):
208+
return tuple(sorted(set([self.page_size] + list(super().page_size_options))))
209+
210+
def get_safe_page_size(self, page_size): # un-enforcing the restriction
211+
return (page_size if self.can_set_page_size and page_size > 0 else self.page_size)
205212

206213
def create_form(self, obj=None):
207214
form = super().create_form(obj)
@@ -464,6 +471,13 @@ def __init__(self, bukudb, *args, **kwargs):
464471
def page_size(self):
465472
return app_param('PER_PAGE', DEFAULT_PER_PAGE)
466473

474+
@property
475+
def page_size_options(self):
476+
return tuple(sorted(set([self.page_size] + list(super().page_size_options))))
477+
478+
def get_safe_page_size(self, page_size): # un-enforcing the restriction
479+
return (page_size if self.can_set_page_size and page_size > 0 else self.page_size)
480+
467481
@expose('/refresh', methods=['POST'])
468482
def refresh(self):
469483
self._refresh()

setup.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
with open('README.md', encoding='utf-8') as f:
1111
long_description = f.read()
1212

13+
server_locales_require = ['Flask-Admin[translation]']
14+
1315
tests_require = [
1416
'attrs>=17.4.0',
1517
'beautifulsoup4>=4.6.0',
@@ -27,13 +29,12 @@
2729
'setuptools>=41.0.1',
2830
'vcrpy>=1.13.0',
2931
'lxml',
30-
'flask_babel',
31-
]
32+
] + server_locales_require
3233

3334

3435
server_require = [
3536
"arrow>=1.2.2",
36-
"Flask-Admin>=1.6.1,<2",
37+
"Flask-Admin>=2.0.0",
3738
"flask-paginate>=2022.1.8",
3839
"Flask-WTF>=1.0.1",
3940
"Flask>=2.2.2",
@@ -72,6 +73,7 @@
7273
extras_require={
7374
"tests": tests_require + server_require,
7475
"server": server_require,
76+
"locales": server_locales_require,
7577
"docs": [
7678
"myst-parser>=0.17.0",
7779
"sphinx-rtd-theme>=1.0.0",

tests/test_setup.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,12 @@ def m_setup(**_):
1313

1414
return setup
1515

16+
_reqs = lambda path: [s for s in pathlib.Path(path).read_text(encoding='utf8', errors='surrogateescape').splitlines()
17+
if not s.startswith('#') and s != 'setuptools']
1618

17-
def test_bukuserver_requirement(setup_obj):
18-
assert [
19-
x
20-
for x in pathlib.Path("bukuserver/requirements.txt").read_text(encoding="utf8", errors="surrogateescape").splitlines()
21-
if "flask-reverse-proxy-fix" not in x
22-
] == setup_obj.server_require
2319

20+
def test_bukuserver_requirement(setup_obj):
21+
assert sorted(_reqs('bukuserver/requirements.txt')) == sorted(setup_obj.server_require)
2422

2523
def test_buku_requirement(setup_obj):
26-
assert sorted(
27-
[
28-
x
29-
for x in pathlib.Path("requirements.txt").read_text(encoding="utf8", errors="surrogateescape").splitlines()
30-
if not x.startswith('#') and x != 'setuptools'
31-
]
32-
) == sorted(setup_obj.install_requires)
24+
assert sorted(_reqs('requirements.txt')) == sorted(setup_obj.install_requires)

tests/test_views.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ def test_update(bukudb, client, override):
250250
@pytest.mark.slow
251251
@pytest.mark.parametrize('redirect, uri, args', [
252252
('_add_another', '/bookmark/new/', {'url': '/bookmark/'}),
253-
('_continue_editing', '/bookmark/edit/', {'id': '1'}),
253+
('_continue_editing', '/bookmark/edit/', {'id': '1', 'url': '/bookmark/'}),
254254
])
255255
def test_update_redirect(bukudb, client, redirect, uri, args):
256256
_add_rec(bukudb, 'http://example.org')
@@ -412,22 +412,22 @@ def test_env_theme(theme, client):
412412
'en': {f'//ul{xpath_cls("nav navbar-nav")}/li/a/text()': ['Home', 'Bookmarks', 'Tags', 'Statistic'],
413413
f'//ul{xpath_cls("nav nav-tabs")}/li/a/text()': ['List (1)', 'Create', 'Random', 'Reorder', 'Add Filter', '10 items'],
414414
f'//td{xpath_cls("list-buttons-column")}/a/@title': ['View Record', 'Edit Record'],
415-
f'//td{xpath_cls("list-buttons-column")}/form/button/@title': ['Delete record']},
415+
f'//td{xpath_cls("list-buttons-column")}/form/button/@title': ['Delete Record']},
416416
'de': {f'//ul{xpath_cls("nav navbar-nav")}/li/a/text()': ['Start', 'Lesezeichen', 'Schilder', 'Statistik'],
417417
f'//ul{xpath_cls("nav nav-tabs")}/li/a/text()':
418418
['Liste (1)', 'Erstellen', 'Zufälliger', 'Neu anordnen', 'Filter hinzufügen', '10 Elemente'],
419419
f'//td{xpath_cls("list-buttons-column")}/a/@title': ['Eintrag ansehen', 'Eintrag bearbeiten'],
420-
f'//td{xpath_cls("list-buttons-column")}/form/button/@title': ['Delete record']}, # ['Datensatz löschen']},
420+
f'//td{xpath_cls("list-buttons-column")}/form/button/@title': ['Datenzatz löschen']},
421421
'fr': {f'//ul{xpath_cls("nav navbar-nav")}/li/a/text()': ['Accueil', 'Signets', 'Étiquettes', 'Statistique'],
422422
f'//ul{xpath_cls("nav nav-tabs")}/li/a/text()':
423-
['Liste (1)', 'Créer', 'Aléatoire', 'Réorganiser', 'Ajouter un filtre', '10 items'],
423+
['Liste (1)', 'Créer', 'Aléatoire', 'Réorganiser', 'Ajouter un filtre', '10 articles'],
424424
f'//td{xpath_cls("list-buttons-column")}/a/@title': ['Afficher L\'enregistrement', 'Modifier enregistrement'],
425-
f'//td{xpath_cls("list-buttons-column")}/form/button/@title': ['Delete record']}, # ['Supprimer l\'enregistrement']},
425+
f'//td{xpath_cls("list-buttons-column")}/form/button/@title': ['Supprimer l\'enregistrement']},
426426
'ru': {f'//ul{xpath_cls("nav navbar-nav")}/li/a/text()': ['Главная', 'Закладки', 'Теги', 'Статистика'],
427427
f'//ul{xpath_cls("nav nav-tabs")}/li/a/text()':
428428
['Список (1)', 'Создать', 'Случайная', 'Изменить порядок', 'Добавить Фильтр', '10 элементы'],
429429
f'//td{xpath_cls("list-buttons-column")}/a/@title': ['Просмотр записи', 'Редактировать запись'],
430-
f'//td{xpath_cls("list-buttons-column")}/form/button/@title': ['Delete record']}, # ['Удалить запись']},
430+
f'//td{xpath_cls("list-buttons-column")}/form/button/@title': ['Удалить запись']},
431431
}
432432
locale = env_fixture('BUKUSERVER_LOCALE', params=['en', 'de', 'fr', 'ru', None])
433433

0 commit comments

Comments
 (0)