Skip to content

Commit

Permalink
Merge branch 'release/2.8.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
abidibo committed Oct 20, 2023
2 parents a20b2f8 + 9fec23d commit e02de1f
Show file tree
Hide file tree
Showing 21 changed files with 66 additions and 170 deletions.
34 changes: 15 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Login with user `demo` and password `demo`
---
**Last changes**

Baton 2.8.0 updates bootstrap to version 5.3 and introduces the dark theme!
Baton 2.8.* updates bootstrap to version 5.3 and introduces the dark theme!

The theme can be changed by the user (default one is system theme), or can be forced through settings (in this case the theme selector disappears)

Expand Down Expand Up @@ -74,7 +74,7 @@ Everything is styled through CSS and when required, JS is used.
- Optional display of changelist filters in a modal
- Optional use of changelist filters as a form (combine some filters at once and perform the search action)
- Optional index page filled with google analytics widgets
- Customization available for recompiling the js app provided
- Customization available by recompiling the js app provided
- IT translations provided

The following packages are required to manage the Google Analytics index:
Expand Down Expand Up @@ -139,7 +139,7 @@ If you get a "__No crypto library available__" when using the Google Analytics i

### Why two installed apps?

Well, first `baton` has to be placed before the `django.contrib.admin` app, because it overrides 3 templates and resets all CSS.
Well, first `baton` has to be placed before the `django.contrib.admin` app, because it overrides some templates and resets all CSS.
The `baton.autodiscover` entry is needed as the last installed app in order to register all applications for the admin.
I decided to create a custom `AdminSite` class, to allow the customization of some variables the Django way (`site_header`, `index_title`, ...). I think it's a good approach to customize these vars instead of overwriting the orignal templates. The problem is that when creating a custom AdminSite, you have to register all the apps manualy. I didn't like
that so I wrote this `autodiscover` module which automatically registers all the apps registered with the Django's default AdminSite. For this to work, all the apps must be already registered so this app should be the last in `INSTALLED_APPS`.
Expand Down Expand Up @@ -206,15 +206,15 @@ BATON = {
}
```

- `SITE_HEADER`, `COPYRIGHT` and `POWERED_BY` are marked as safe, so you can include img tags and links.
- `SITE_HEADER`, `COPYRIGHT` and `POWERED_BY` are marked as safe, so you can include html (i.e. img tags and links).
- `SUPPORT_HREF` is the URL of the support link. For instance, you can use `mailto:[email protected]`.
- `CONFIRM_UNSAVED_CHANGES`: if set to `True` a confirmation modal appears when leaving a change form or add form with unsaved changes.
The check of a dirty form relies on the jQuery serialize method, so it's not 100% safe. Disabled inputs, particular widgets (ckeditor) can not be detected.
Default value is `True`.
- `SHOW_MULTIPART_UPLOADING`: if set to `True` an overlay with a spinner appears when submitting a `multipart/form-data` form.
- `ENABLE_IMAGES_PREVIEW`: if set to `True` a preview is displayed above all input file fields which contain images. You can control how the preview is displayed by overriding the class `.baton-image-preview`. By default, previews have 100px height and with a box shadow (on "hover").
- `ENABLE_IMAGES_PREVIEW`: if set to `True` a preview is displayed above all input file fields which contain images. You can control how the preview is displayed by overriding the class `.baton-image-preview`. By default, previews are 100px height and have a box shadow on "hover".
- `CHANGELIST_FILTERS_IN_MODAL`: if set to `True` the changelist filters are opened in a centered modal above the document, useful when you set many filters. By default, its value is `False` and the changelist filters appears from the right side of the changelist table.
- `CHANGELIST_FILTERS_ALWAYS_OPEN`: if set to `True` the changelist filters are opened by default. By default, its value is `False` and the changelist filters can be expanded clicking a toggler button. This option is considered only if `CHANGELIST_FILTERS_IN_MODAL` is `False`.
- `CHANGELIST_FILTERS_ALWAYS_OPEN`: if set to `True` the changelist filters are opened by default. By default, its value is `False` and the changelist filters can be expanded clicking a toggle button. This option is considered only if `CHANGELIST_FILTERS_IN_MODAL` is `False`.
- `CHANGELIST_FILTERS_FORM`: if set to `True` the changelist filters are treated as in a form, you can set many of them and then press a filter button. With such option all standard filters are displayed as dropdowns.
- `COLLAPSABLE_USER_AREA`: if set to `True` the sidebar user area is collapsed and can be expanded to show links.
- `MENU_ALWAYS_COLLAPSED`: if set to `True` the menu is hidden at page load, and the navbar toggler is always visible, just click it to show the sidebar menu.
Expand All @@ -229,13 +229,13 @@ Default value is `True`.

### <a name="configuration-menu"></a>MENU

Currently four kind of voices are supported: _title_, _app_, _model_ and _free_.
Currently four kind of items are supported: _title_, _app_, _model_ and _free_.

Title and free voices can have children, which follow the following rules:

- children voices' children are ignored (do not place an app voice as a child)
- children items' children are ignored (do not place an app voice as a child)

Voices with children (title, app, free) can specify a `default_open` key to expand the submenu by default.
Items with children (title, app, free) can specify a `default_open` key to expand the submenu by default.

If you don't define a MENU key in the configuration dictionary, the default MENU is shown.

Expand All @@ -248,7 +248,7 @@ You can also define some perms (OR condition), like this:

{ 'type': 'title', 'label': 'main', 'perms': ('auth.add_user', ) },

Free voices can have children and so you can specify the _default_open_ key.
Title items can have children and so you can specify the _default_open_ key.

#### App

Expand All @@ -265,7 +265,7 @@ You must specify the _type_, _name_ and _app_ keys. Optionally, an icon key.
#### Free

You can specify free voices. You must define a _url_ and if you want some visibility permissions (OR clause). Free voices can have children and so you can specify the _default_open_ key. Free voices also accept a _re_ property, which specifies a regular expression used to decide whether to highlight the voice or not (the regular expression is evaluated against the document location pathname).
You can specify free items. You must define a _url_ and if you want some visibility permissions (OR clause). Free items can have children and so you can specify the _default_open_ key. Free items also accept a _re_ property, which specifies a regular expression used to decide whether to highlight the voice or not (the regular expression is evaluated against the document location pathname).

{
'type': 'free',
Expand Down Expand Up @@ -300,10 +300,6 @@ The autocomplete field will call a custom api at every keyup event. Such api rec
]
}
```

In order to activate this functionality you should add the BATON configuration:


You should provide the results length and the data as an array of objects which must contain the `label` and `url` keys. The `icon` key is optional and is treated as css class given to an `i` element.

Let's see an example:
Expand Down Expand Up @@ -387,7 +383,7 @@ The available page types are the following: `dashboard`, `admindocs`, `login`, `
## <a name="signals"></a>Signals

Baton provides a dispatcher that can be used to register function that will be called when some events occurr.
Currently, Baton emits four types of events:
Currently, Baton emits five types of events:

- `onNavbarReady`: dispatched when the navbar is fully rendered
- `onMenuReady`: dispatched when the menu is fully rendered (probably the last event fired, since the menu contents are retrieved async)
Expand Down Expand Up @@ -418,7 +414,7 @@ Baton comes with a number of exported js modules you can use to enhance your adm

### Dispatcher

Baton Dispatcher singleton module lets you subscribe to event and dispatch them, making use of the Mediator pattern.
Baton Dispatcher singleton module lets you subscribe to events and dispatch them, making use of the Mediator pattern.

Example:

Expand Down Expand Up @@ -485,8 +481,8 @@ There are some circustamces in which Baton will print to screen some js message.
However you can provide or add your own translations by attaching an object to the `Baton` namespace:

``` javascript
// these are the default translations, you can just edit the one you need, or add some locales. Baton engione will always
// pick up your custom translation first, if it find them.
// these are the default translations, you can just edit the one you need, or add some locales. Baton engine will always
// pick up your custom translation first, if it finds them.
// you can define the object before Baton.init in the base_site template
Baton.translations = {
unsavedChangesAlert: 'You have some unsaved changes.',
Expand Down
2 changes: 1 addition & 1 deletion baton/static/baton/app/dist/baton.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions baton/static/baton/app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion baton/static/baton/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "baton",
"version": "2.8.0",
"version": "2.8.1",
"description": "Django Baton App",
"main": "index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion baton/static/baton/app/src/core/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class Modal {
}

update(config) {
this.options = $.extend({}, this.opts, config)
this.options = $.extend({}, this.options ? this.options : this.opts, config)
this.setSize()
this.setHeader()
this.setTitle()
Expand Down
4 changes: 3 additions & 1 deletion baton/static/baton/app/src/core/Tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ const Tabs = {
$('a[data-bs-toggle="tab"]').on('shown.bs.tab', function (e) {
// add hash to stay in same tab when save and continue
const hash = $(e.target).attr('data-bs-target')
window.location.replace(hash) // adding with replace won't add an history entry
e.preventDefault()
history.replaceState({}, '', hash);
// window.location.replace(hash) // adding with replace won't add an history entry
const tooltipTriggerList = [].slice.call($('[title]:not(iframe)'))
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
Expand Down
1 change: 1 addition & 0 deletions baton/static/baton/app/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,4 @@ window.Baton = {
}

window.jQuery = jQuery
window.bootstrap = bootstrap
2 changes: 0 additions & 2 deletions baton/templatetags/baton_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ def baton_config():
"forceTheme": get_config('FORCE_THEME'),
}

print(conf)

return conf


Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@
# built documents.
#
# The short X.Y version.
version = u'2.8.0'
release = u'2.8.0'
version = u'2.8.1'
release = u'2.8.1'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

setup(
name='django-baton',
version='2.8.0',
version='2.8.1',
packages=['baton', 'baton.autodiscover', 'baton.templatetags'],
include_package_data=True,
license='MIT License',
Expand Down
17 changes: 2 additions & 15 deletions testapp/app/app/tests/test_e2e_cl_includes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,17 @@
import time

from django.test import TestCase
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager

from .utils import element_has_css_class
from .utils import element_has_css_class, make_driver

os.environ['WDM_LOG_LEVEL'] = '0'


class TestBatonClIncludes(TestCase):
def setUp(self):
service = Service(ChromeDriverManager(version='114.0.5735.90').install())
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-extensions')
chrome_options.add_argument('--disable-dev-shm-usage')
self.driver = webdriver.Chrome(
service=service,
options=chrome_options,
)
self.driver = make_driver()
self.driver.set_window_size(1920, 1080)
self.driver.implicitly_wait(10)
self.login()
Expand Down
20 changes: 3 additions & 17 deletions testapp/app/app/tests/test_e2e_index.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
import time

from django.test import TestCase, override_settings
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.chrome.options import Options
from django.test import TestCase
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager

from .utils import element_has_css_class
from .utils import element_has_css_class, make_driver

import os
os.environ['WDM_LOG_LEVEL'] = '0'


class TestBatonIndex(TestCase):
def setUp(self):
service = Service(ChromeDriverManager(version='114.0.5735.90').install())
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-extensions')
chrome_options.add_argument('--disable-dev-shm-usage')
self.driver = webdriver.Chrome(
service=service,
options=chrome_options,
)
self.driver = make_driver()
self.driver.set_window_size(1920, 1080)
self.driver.implicitly_wait(10)
self.login()
Expand Down
17 changes: 2 additions & 15 deletions testapp/app/app/tests/test_e2e_index_mobile.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,18 @@
import time

from django.test import TestCase
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager

from .utils import element_has_css_class
from .utils import element_has_css_class, make_driver

import os
os.environ['WDM_LOG_LEVEL'] = '0'


class TestBatonIndexMobile(TestCase):
def setUp(self):
service = Service(ChromeDriverManager(version='114.0.5735.90').install())
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-extensions')
chrome_options.add_argument('--disable-dev-shm-usage')
self.driver = webdriver.Chrome(
service=service,
options=chrome_options,
)
self.driver = make_driver()
self.driver.set_window_size(480, 600)
self.driver.implicitly_wait(10)
self.login()
Expand Down
18 changes: 2 additions & 16 deletions testapp/app/app/tests/test_e2e_input_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,17 @@
import time

from django.test import TestCase
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager

from .utils import element_has_css_class
from .utils import element_has_css_class, make_driver

os.environ['WDM_LOG_LEVEL'] = '0'


class TestBatonInputFilter(TestCase):
def setUp(self):
service = Service(ChromeDriverManager(version='114.0.5735.90').install())
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-extensions')
chrome_options.add_argument('--disable-dev-shm-usage')
self.driver = webdriver.Chrome(
service=service,
options=chrome_options,
)
self.driver = make_driver()
self.driver.set_window_size(1920, 1080)
self.driver.implicitly_wait(10)
self.login()
Expand Down
19 changes: 4 additions & 15 deletions testapp/app/app/tests/test_e2e_login.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
import time

from django.test import TestCase
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager
from testapp.app.app.tests.utils import make_driver

import os

os.environ['WDM_LOG_LEVEL'] = '0'


class TestBatonLogin(TestCase):
def setUp(self):
service = Service(ChromeDriverManager(version='114.0.5735.90').install())
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-extensions')
chrome_options.add_argument('--disable-dev-shm-usage')
self.driver = webdriver.Chrome(
service=service,
options=chrome_options,
)
self.driver = make_driver()

def tearDown(self):
self.driver.quit()
Expand Down
Loading

0 comments on commit e02de1f

Please sign in to comment.