BS5 Theme

This commit is contained in:
Aaron Kable
2023-10-07 08:20:22 +00:00
committed by Ariel Rin
parent 567d97f38a
commit 2e78aa5f26
161 changed files with 3198 additions and 1655 deletions

View File

@@ -0,0 +1,19 @@
# Theme Hooks
The theme hook allows custom themes to be loaded dynamically by AAs CSS/JS Bundles, as selected by Users.
To register a ThemeHook class you would do the following:
```Python
@hooks.register('theme_hook')
def register_darkly_hook():
return ThemeHook()
```
The `ThemeHook` class specifies some parameters/instance variables required.
```eval_rst
.. autoclass:: allianceauth.theme.hooks.ThemeHook
:members: __init__
:undoc-members:
```

View File

@@ -1,12 +1,13 @@
# Custom apps and services
This section describes how to extend **Alliance Auth** with custom apps and services.
This section describes how to extend **Alliance Auth** with custom apps, services and themes.
```eval_rst
.. toctree::
:maxdepth: 1
integrating-services
custom-themes
menu-hooks
url-hooks
logging

View File

@@ -20,19 +20,21 @@ Typically a service will contain 5 key components:
The architecture looks something like this:
urls -------▶ Views
▲ |
| |
|
ServiceHook ----▶ Tasks ----▶ Manager
|
|
AllianceAuth
```none
urls -------▶ Views
|
| |
|
ServiceHook ----▶ Tasks ----▶ Manager
|
|
AllianceAuth
Where:
Module --▶ Dependency/Import
Where:
Module --▶ Dependency/Import
```
While this is the typical structure of the existing services modules, there is no enforcement of this structure and you are, effectively, free to create whatever architecture may be necessary. A service module need not even communicate with an external service, for example, if similar triggers such as validate_user, delete_user are required for a module it may be convenient to masquerade as a service. Ideally though, using the common structure improves the maintainability for other developers.
@@ -40,9 +42,11 @@ While this is the typical structure of the existing services modules, there is n
In order to integrate with Alliance Auth service modules must provide a `services_hook`. This hook will be a function that returns an instance of the `services.hooks.ServiceHook` class and decorated with the `@hooks.registerhook` decorator. For example:
@hooks.register('services_hook')
def register_service():
return ExampleService()
```python
@hooks.register('services_hook')
def register_service():
return ExampleService()
```
This would register the ExampleService class which would need to be a subclass of `services.hooks.ServiceHook`.
@@ -53,15 +57,17 @@ This would register the ExampleService class which would need to be a subclass o
A subclassed `ServiceHook` might look like this:
class ExampleService(ServicesHook):
def __init__(self):
ServicesHook.__init__(self)
self.urlpatterns = urlpatterns
self.service_url = 'http://exampleservice.example.com'
```python
class ExampleService(ServicesHook):
def __init__(self):
ServicesHook.__init__(self)
self.urlpatterns = urlpatterns
self.service_url = 'http://exampleservice.example.com'
"""
Overload base methods here to implement functionality
"""
```
### The ServiceHook class
@@ -71,9 +77,9 @@ You will need to subclass `services.hooks.ServiceHook` in order to provide imple
Instance Variables:
- [self.name](#self-name)
- [self.urlpatterns](#self-url-patterns)
- [self.service_ctrl_template](#self-service-ctrl-template)
- [self.name](#selfname)
- [self.urlpatterns](#selfurlpatterns)
- [self.service_ctrl_template](#selfservice-ctrl-template)
Properties:
@@ -88,37 +94,47 @@ Functions:
- [update_groups](#update_groups)
- [update_groups_bulk](#update_groups_bulk)
- [update_all_groups](#update_all_groups)
- [service_enabled_members](#service_enabled_members)
- [service_enabled_blues](#service_enabled_blues)
- [service_active_for_user](#service_active_for_user)
- [show_service_ctrl](#show_service_ctrl)
- [render_service_ctrl](#render_service_ctrl)
### Variables
#### self.name
Internal name of the module, should be unique amongst modules.
#### self.service-ctrl-template
The template used to render
#### self.urlpatterns
You should define all of your service URLs internally, usually in `urls.py`. Then you can import them and set `self.urlpatterns` to your defined urlpatterns.
from . import urls
...
class MyService(ServiceHook):
def __init__(self):
...
self.urlpatterns = urls.urlpatterns
```python
from . import urls
...
class MyService(ServiceHook):
def __init__(self):
...
self.urlpatterns = urls.urlpatterns
```
All of your apps defined urlpatterns will then be included in the `URLconf` when the core application starts.
#### self.service_ctrl_template
This is provided as a courtesy and defines the default template to be used with [render_service_ctrl](#render-service-ctrl). You are free to redefine or not use this variable at all.
### Properties
#### title
This is a property which provides a user friendly display of your service's name. It will usually do a reasonably good job unless your service name has punctuation or odd capitalization. If this is the case you should override this method and return a string.
### Functions
#### self.service_ctrl_template
This is provided as a courtesy and defines the default template to be used with [render_service_ctrl](#render-service-ctrl). You are free to redefine or not use this variable at all.
#### delete_user
`def delete_user(self, user, notify_user=False):`
@@ -134,12 +150,12 @@ The function should return a boolean, `True` if successfully disabled, `False` o
Validate the users service account, deleting it if they should no longer have access. The `user` parameter should be a Django User object.
An implementation will probably look like the following:
def validate_user(self, user):
logger.debug('Validating user %s %s account' % (user, self.name))
if ExampleTasks.has_account(user) and not self.service_active_for_user(user):
self.delete_user(user, notify_user=True)
```python
def validate_user(self, user):
logger.debug('Validating user %s %s account' % (user, self.name))
if ExampleTasks.has_account(user) and not self.service_active_for_user(user):
self.delete_user(user, notify_user=True)
```
No return value is expected.
This function will be called periodically on all users to validate that the given user should have their current service accounts.
@@ -207,37 +223,39 @@ Should the service be shown for the given `user` with the given `state`? The `us
Usually you wont need to override this function.
For more information see the [render_service_ctrl](#render-service-ctrl) section.
For more information see the [render_service_ctrl](#render_service_ctrl) section.
#### render_service_ctrl
`def render_services_ctrl(self, request):`
Render the services control row. This will be called for all active services when a user visits the `/services/` page and [show_service_ctrl](#show-service-ctrl) returns `True` for the given user.
Render the services control row. This will be called for all active services when a user visits the `/services/` page and [show_service_ctrl](#show_service_ctrl) returns `True` for the given user.
It should return a string (usually from `render_to_string`) of a table row (`<tr>`) with 4 columns (`<td>`). Column #1 is the service name, column #2 is the users username for this service, column #3 is the services URL, and column #4 is the action buttons.
You may either define your own service template or use the default one provided. The default can be used like this example:
def render_services_ctrl(self, request):
"""
Example for rendering the service control panel row
You can override the default template and create a
custom one if you wish.
:param request:
:return:
"""
urls = self.Urls()
urls.auth_activate = 'auth_example_activate'
urls.auth_deactivate = 'auth_example_deactivate'
urls.auth_reset_password = 'auth_example_reset_password'
urls.auth_set_password = 'auth_example_set_password'
return render_to_string(self.service_ctrl_template, {
'service_name': self.title,
'urls': urls,
'service_url': self.service_url,
'username': 'example username'
}, request=request)
```python
def render_services_ctrl(self, request):
"""
Example for rendering the service control panel row
You can override the default template and create a
custom one if you wish.
:param request:
:return:
"""
urls = self.Urls()
urls.auth_activate = 'auth_example_activate'
urls.auth_deactivate = 'auth_example_deactivate'
urls.auth_reset_password = 'auth_example_reset_password'
urls.auth_set_password = 'auth_example_set_password'
return render_to_string(self.service_ctrl_template, {
'service_name': self.title,
'urls': urls,
'service_url': self.service_url,
'username': 'example username'
}, request=request)
```
the `Urls` class defines the available URL names for the 4 actions available in the default template:

View File

@@ -1,7 +1,9 @@
# Logging from Custom Apps
Alliance Auth provides a logger for use with custom apps to make everyone's life a little easier.
## Using the Extensions Logger
AllianceAuth provides a helper function to get the logger for the current module to reduce the amount of
code you need to write.
@@ -15,17 +17,20 @@ This works by creating a child logger of the extension logger which propagates a
to the parent (extensions) logger.
## Changing the Logging Level
By default, the extension logger's level is set to `DEBUG`.
To change this, uncomment (or add) the following line in `local.py`.
```python
LOGGING['handlers']['extension_file']['level'] = 'INFO'
```
*(Remember to restart your supervisor workers after changes to `local.py`)*
This will change the logger's level to the level you define.
Options are: *(all options accept entries of levels listed below them)*
* `DEBUG`
* `INFO`
* `WARNING`

View File

@@ -4,37 +4,21 @@ The menu hooks allow you to dynamically specify menu items from your plugin app
To register a MenuItemHook class you would do the following:
```Python
```python
@hooks.register('menu_item_hook')
def register_menu():
return MenuItemHook('Example Item', 'glyphicon glyphicon-heart', 'example_url_name',150)
return MenuItemHook('Example Item', 'fas fa-user fa-fw"', 'example_url_name', 150)
```
The `MenuItemHook` class specifies some parameters/instance variables required for menu item display.
## MenuItemHook(text, classes, url_name, order=None)
```eval_rst
.. autoclass:: allianceauth.services.hooks.MenuItemHook
:members: __init__
:undoc-members:
```
### text
The text shown as menu item, e.g. usually the name of the app.
### classes
The classes that should be applied to the bootstrap menu item icon
### url_name
The name of the Django URL to use
### order
An integer which specifies the order of the menu item, lowest to highest. Community apps are free ot use an oder above `1000`. Numbers below are served for Auth.
### navactive
A list of views or namespaces the link should be highlighted on. See [django-navhelper](https://github.com/geelweb/django-navhelper#navactive) for usage. Defaults to the supplied `url_name`.
### count
## count
`count` is an integer shown next to the menu item as badge when `count` is not `None`.
@@ -49,10 +33,18 @@ This is a great feature to signal the user, that he has some open issues to take
To use it set count the `render()` function of your subclass in accordance to the current user. Here is an example:
```eval_rst
.. automodule:: allianceauth.services.hooks.MenuItemHook
:members: render
:noindex:
:undoc-members:
```
```Python
def render(self, request):
# ...
self.count = calculate_count_for_user(request.user)
return MenuItemHook.render(self, request)
# ...
```

View File

@@ -12,6 +12,18 @@ def register_urls():
return UrlHook(app_name.urls, 'app_name', r^'app_name/')
```
#### urls
The urls module to include. See [the Django docs](https://docs.djangoproject.com/en/dev/topics/http/urls/#example) for designing urlpatterns.
#### namespace
The URL namespace to apply. This is usually just the app name.
#### base_url
The URL prefix to match against in regex form. Example `r'^app_name/'`. This prefix will be applied in front of all URL patterns included. It is possible to use the same prefix as existing apps (or no prefix at all) but [standard URL resolution](https://docs.djangoproject.com/en/dev/topics/http/urls/#how-django-processes-a-request) ordering applies (hook URLs are the last ones registered).
### Public views
In addition is it possible to make views public. Normally, all views are automatically decorated with the `main_character_required` decorator. That decorator ensures a user needs to be logged in and have a main before he can access that view. This feature protects against a community app sneaking in a public view without the administrator knowing about it.
@@ -22,7 +34,6 @@ An app can opt-out of this feature by adding a list of views to be excluded when
.. note::
Note that for a public view to work, administrators need to also explicitly allow apps to have public views in their AA installation, by adding the apps label to ``APPS_WITH_PUBLIC_VIEWS`` setting.
```
## Examples
An app called `plugin` provides a single view: