Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
f90b6e3
initial add config app with Plugin model
MyPyDavid Sep 30, 2025
fc11cd2
core: add rdmo.config to settings
MyPyDavid Sep 30, 2025
061ce24
config: add plugin_settings JSONField
MyPyDavid Oct 15, 2025
7e07fd1
config(admin): update fields and re-use ElementAdminForm
MyPyDavid Oct 15, 2025
f764449
config(tests): initial add tests for plugins
MyPyDavid Oct 16, 2025
bf829e7
config(migrations): add 0001_initial again
MyPyDavid Oct 16, 2025
0eba427
config(viewsets): add v1-config to api and plugins viewset
MyPyDavid Oct 20, 2025
921c9ec
config(plugin): add managers and plugin_type property
MyPyDavid Oct 20, 2025
2856b20
config(commands): add check_plugins
MyPyDavid Oct 20, 2025
8d29e6f
config(tests): add model, admin, validator, viewset tests
MyPyDavid Oct 20, 2025
fdb4ca7
questions(migrations): fix 0032_meta by adding dependency to domain.0038
MyPyDavid Oct 21, 2025
2bf6779
testing: add mock plugins and config fixtures
MyPyDavid Oct 27, 2025
4ebc212
config(tests): update and fix tests
MyPyDavid Oct 27, 2025
38cd0a9
config(plugin): add initialize_class method
MyPyDavid Oct 27, 2025
864e0fa
rules: add config.plugin model and object perms
MyPyDavid Oct 27, 2025
52a9975
style: add eof newline
MyPyDavid Oct 27, 2025
7309a45
style: add newline
MyPyDavid Oct 27, 2025
5d464bf
config(apps): call it Config
MyPyDavid Oct 27, 2025
8e740d7
core(settings): add PLUGINS setting with default list
MyPyDavid Nov 4, 2025
1ba1ad1
config(commands): add setup_plugins
MyPyDavid Nov 6, 2025
147ea53
testing: add config fixture and mock plugins
MyPyDavid Nov 6, 2025
1617950
config(checks): add plugin settings check
MyPyDavid Nov 6, 2025
0702045
config(plugins): refactor from core and update utils
MyPyDavid Nov 6, 2025
dd365ba
core(settings): remove deprecated plugin settings
MyPyDavid Nov 6, 2025
ba1bfea
core(plugins): revert refactor to config
MyPyDavid Nov 6, 2025
e75704f
Move mock SimpleProviders to testing
MyPyDavid Nov 6, 2025
90f6b7a
options(viewsets): handle missing provider setting
MyPyDavid Nov 6, 2025
41feaa0
management(rules): fix plugin toggle site pred
MyPyDavid Nov 7, 2025
07ee628
config(plugins): add support for xml import
MyPyDavid Nov 7, 2025
f67c629
core(managers): add filter-for-... mixins and refactor
MyPyDavid Nov 11, 2025
50140f6
core(constants): add config.plugin to PERMISSIONS
MyPyDavid Nov 11, 2025
fbf7a38
config(plugin): add PluginManager to model
MyPyDavid Nov 11, 2025
6859dfd
Add python_path and plugin_type to PluginSerializer
MyPyDavid Nov 11, 2025
8935833
Fix mocked plugin in test
MyPyDavid Nov 11, 2025
c44ad89
testing: add rdmo internal classes to plugin setting and fixtures
MyPyDavid Nov 11, 2025
9c9f73d
Add url_name field to Plugin model
MyPyDavid Nov 12, 2025
08b9527
Add default to url_name field
MyPyDavid Nov 12, 2025
7f05adb
Update and refactor checks and commands
MyPyDavid Nov 12, 2025
57c5e90
testing: update plugin settings and fixtures
MyPyDavid Nov 12, 2025
b47bff9
Add url_name and plugin_settings to PluginSerializer
MyPyDavid Nov 12, 2025
d4e2d1b
Update and refactor config utils and methods
MyPyDavid Nov 12, 2025
64c18f5
Use new plugins in projects
MyPyDavid Nov 12, 2025
287feed
config: refactor plugin related parts
MyPyDavid Nov 14, 2025
2b3e81b
config(commands): fix get or initialize in setup_plugins
MyPyDavid Nov 14, 2025
ccaab58
config(plugin): add for_context to PluginManager
MyPyDavid Nov 14, 2025
460c62c
Add plugin_type field to Plugin
MyPyDavid Nov 14, 2025
bbe0fcf
config(tests): update tests
MyPyDavid Nov 14, 2025
545a4cf
Add plugins m2m field to OptionSet
MyPyDavid Nov 14, 2025
88a6b0d
Remove args from core.plugins.Plugin and add plugin_type to inheritin…
MyPyDavid Nov 14, 2025
c6693c5
Update initialize_class method on Plugin
MyPyDavid Nov 14, 2025
fa930db
Add support for new Plugin objects in projects
MyPyDavid Nov 14, 2025
7990ba5
testing: remove legacy plugin settings from base.py settings
MyPyDavid Nov 14, 2025
a38a302
config(checks): update output and add tests
MyPyDavid Nov 19, 2025
9f9dff4
config(tests): add fixture to enable legacy settings
MyPyDavid Nov 19, 2025
3469914
update Plugin model, use string import for Catalog
MyPyDavid Nov 19, 2025
ce9c820
add url_name to Plugin import
MyPyDavid Nov 19, 2025
627df20
update detect plugin type
MyPyDavid Nov 19, 2025
29daaf5
move legacy Plugin base class from core to config
MyPyDavid Nov 19, 2025
b585b86
use plugins instead of OptionSet.provider
MyPyDavid Nov 19, 2025
14de0fd
testing(config): fix fixtures and add plugin_type
MyPyDavid Nov 24, 2025
aaf9609
testing(config): add URLImport plugin
MyPyDavid Nov 24, 2025
317e07b
testing(settings): add URLImport plugin
MyPyDavid Nov 24, 2025
394114d
testing(fixtures): add simple plugin to optionset
MyPyDavid Nov 24, 2025
d0b01e2
Add filters for format and settings to Plugin manager
MyPyDavid Nov 24, 2025
5e47f1e
Add PluginPythonPathValidator
MyPyDavid Nov 24, 2025
2d4a56a
style: rename unused var
MyPyDavid Nov 24, 2025
0491e35
Update project imports test
MyPyDavid Nov 24, 2025
f07c4d6
Use Plugin queryset for project imports action
MyPyDavid Nov 24, 2025
5f5fe9c
Use Plugin filter query for export view
MyPyDavid Nov 24, 2025
a6cead2
Use Plugin filter query for snapshot export
MyPyDavid Nov 24, 2025
cecf9a3
Use Plugin filter for integration issue provider
MyPyDavid Nov 24, 2025
8e79f79
testing(fixtures): add simple issue provider to config
MyPyDavid Nov 24, 2025
4bbad43
Use only ForSiteQuerySet mixin in PluginQuerySet
MyPyDavid Dec 8, 2025
6ec2d43
Clean up in config app
MyPyDavid Dec 8, 2025
d2ca0dc
Update Plugin manager
MyPyDavid Dec 8, 2025
707f566
Use for_context filter for Plugin in projects
MyPyDavid Dec 8, 2025
6754001
tests: add catalog 1 to plugin fixtures
MyPyDavid Dec 8, 2025
a523400
Update get export plugin in project and snaphsot export view
MyPyDavid Dec 8, 2025
0d7c42b
tests: add catalog 1 to all plugin fixtures
MyPyDavid Dec 8, 2025
2a77e49
tests: fix assert in prune projects test
MyPyDavid Dec 8, 2025
8aabf3f
Use Plugin filter in integration form
MyPyDavid Dec 8, 2025
32d408c
Use Plugin filter in export projects command
MyPyDavid Dec 8, 2025
42a54e6
Use Plugin filter in oauth callbck view
MyPyDavid Dec 8, 2025
d3f2663
Clean up legacy get_plugin
MyPyDavid Dec 8, 2025
07f8414
Add Plugin to management UI
MyPyDavid Dec 8, 2025
106d178
Fix typo in config managers
MyPyDavid Dec 9, 2025
b6260c9
Fix mocked plugin in test
MyPyDavid Dec 9, 2025
a68adca
Fix timestamps in config fixtures
MyPyDavid Dec 9, 2025
03faa2d
Add get_plugin_python_paths util func
MyPyDavid Dec 10, 2025
bc1d19f
Use get_plugin_python_paths as validation in admin and api
MyPyDavid Dec 10, 2025
1f2ecfd
Use Select for python_path field in EditPlugin
MyPyDavid Dec 10, 2025
6f3a3bd
Use enum constant for internal plugin types
MyPyDavid Dec 19, 2025
0ffb48a
Clean up use of plugin types
MyPyDavid Dec 22, 2025
35b3bdd
Update setup plugins script
MyPyDavid Dec 22, 2025
8f66bac
Rename function
MyPyDavid Dec 22, 2025
9108f28
Use variables directly from options kwargs
MyPyDavid Dec 22, 2025
0ffdc3b
Clean up comment
MyPyDavid Dec 22, 2025
d4e590a
Clean up newline
MyPyDavid Dec 22, 2025
ed97b32
Add newline
MyPyDavid Dec 22, 2025
3445ee2
Clean up and refactor config app
MyPyDavid Dec 23, 2025
b176e23
Clean up and refactor plugins in projects
MyPyDavid Dec 23, 2025
92ee088
Remove queryset that uses OPTIONSET_PROVIDERS
MyPyDavid Dec 23, 2025
7b7b2f5
Clean up and refactor plugins in services
MyPyDavid Dec 23, 2025
06a503e
Add queryset from plugins to ProviderViewSet and add test
MyPyDavid Dec 23, 2025
5c1ae6e
Clean up in config.legacy
MyPyDavid Dec 23, 2025
9ecf292
Add support for default uri prefix to plugins
MyPyDavid Dec 23, 2025
ce6e187
Use meta model name instead
MyPyDavid Dec 23, 2025
3f7b5aa
Remove CSV and JSON exports from PLUGINS
MyPyDavid Dec 23, 2025
4d46f64
Clean up config.helpers and imported annotations
MyPyDavid Dec 23, 2025
366c7ad
Do not raise from Exception in config
MyPyDavid Dec 23, 2025
1b178d1
Make str return just uri for Plugin
MyPyDavid Dec 23, 2025
af00d09
Improve error handling for setup plugins
MyPyDavid Dec 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def fixtures():
'sites',
'tasks',
'users',
'views'
'views',
'config'
}
fixtures = []
for fixture_dir in settings.FIXTURE_DIRS:
Expand Down
54 changes: 48 additions & 6 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
},
"dependencies": {
"@codemirror/lang-html": "^6.4.2",
"@codemirror/lang-json": "^6.0.2",
"@codemirror/lang-javascript": "^6.2.2",
"@uiw/react-codemirror": "^4.25.1",
"bootstrap-sass": "^3.4.1",
Expand Down
2 changes: 0 additions & 2 deletions rdmo/accounts/checks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

from django.conf import settings
from django.core.checks import Tags, Warning, register

Expand Down
2 changes: 0 additions & 2 deletions rdmo/accounts/tests/test_checks_shibboleth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

from rdmo.accounts.checks import W_FIX_DISABLED, W_MISSING_FIX, check_shibboleth_remoteuser

from .helpers import enable_shibboleth, fake_shibboleth # noqa: F401
Expand Down
Empty file added rdmo/config/__init__.py
Empty file.
35 changes: 35 additions & 0 deletions rdmo/config/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django import forms
from django.contrib import admin

from rdmo.core.admin import ElementAdminForm
from rdmo.core.utils import get_language_fields, get_plugin_python_paths

from .models import Plugin
from .validators import PluginLockedValidator, PluginPythonPathValidator, PluginUniqueURIValidator


class PluginAdminForm(ElementAdminForm):

python_path = forms.ChoiceField(choices=[(plugin, plugin) for plugin in get_plugin_python_paths()])


class Meta:
model = Plugin
fields = '__all__'

def clean(self):
PluginUniqueURIValidator(self.instance)(self.cleaned_data)
PluginLockedValidator(self.instance)(self.cleaned_data)
PluginPythonPathValidator(self.instance)(self.cleaned_data)


@admin.register(Plugin)
class PluginAdmin(admin.ModelAdmin):
form = PluginAdminForm

search_fields = ['uri', 'python_path', *get_language_fields('title'), *get_language_fields('help')]
list_display = ('uri', 'python_path', 'plugin_type', 'available')
readonly_fields = ('uri', 'plugin_type')
list_filter = ('available', 'python_path', 'sites' , 'groups', 'catalogs')
filter_horizontal = ('catalogs', 'sites', 'editors', 'groups')
ordering = ('python_path','order')
10 changes: 10 additions & 0 deletions rdmo/config/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _


class ConfigConfig(AppConfig):
name = 'rdmo.config'
verbose_name = _('Config')

def ready(self):
from . import checks # noqa: F401
60 changes: 60 additions & 0 deletions rdmo/config/checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from collections import defaultdict

from django.core.checks import Warning, register


@register()
def deprecated_plugin_settings_check(app_configs, **kwargs):
from django.conf import settings
legacy_settings = [
"PROJECT_EXPORTS",
"PROJECT_SNAPSHOT_EXPORTS",
"PROJECT_IMPORTS",
"PROJECT_ISSUE_PROVIDERS",
"PROJECT_IMPORTS_LIST",
"OPTIONSET_PROVIDERS",
]
issues = []
legacy_settings_used_keys = {
i for i in legacy_settings if hasattr(settings, i)
}
if legacy_settings_used_keys:
_verb = "are" if len(legacy_settings_used_keys) > 1 else "is"
legacy_settings = {
i: getattr(settings, i) for i in legacy_settings_used_keys
}
_legacy_settings_plugins = defaultdict(list)
_python_paths = set()
for name,entries in legacy_settings.items():
for entry in entries:
_legacy_settings_plugins[name].append(entry)
if len(entry) == 3:
_python_paths.add(entry[-1])
issues.append(Warning(
f"{', '.join(legacy_settings_used_keys)} {_verb} deprecated as of RDMO 2.5.0; "
f"use PLUGINS = ['python.dotted.paths', ...] instead.",
id="rdmo.config.W001",
hint="Define the legacy plugin settings in PLUGINS and remove the legacy settings."
f"\n{repr_new_settings(_python_paths)}",
))
# If both PLUGINS and any legacy key exist
if hasattr(settings, "PLUGINS"):
issues.append(Warning(
"PLUGINS is set in addition to legacy settings; the legacy settings are ignored.",
id="rdmo.config.W002",
hint="Remove the following legacy settings to avoid confusion: "
f"{', '.join(legacy_settings_used_keys)}."
))
return issues


def repr_new_settings(python_paths) -> str:
if not python_paths:
return ""
elif len(python_paths) == 1:
return f"PLUGINS = ['{python_paths}']"
msg = "PLUGINS = ["
for python_path in sorted(python_paths):
msg += f"\n\t{python_path}, "
msg += "\n]"
return msg
9 changes: 9 additions & 0 deletions rdmo/config/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.db.models import TextChoices


class PLUGIN_TYPES(TextChoices):
PROJECT_EXPORT = "project_export", "Project export"
PROJECT_SNAPSHOT_EXPORT = "project_snapshot_export", "Project snapshot export"
PROJECT_IMPORT = "project_import", "Project import"
PROJECT_ISSUE_PROVIDER = "project_issue_provider", "Project issue provider"
OPTIONSET_PROVIDER = "optionset_provider", "Optionset provider"
20 changes: 20 additions & 0 deletions rdmo/config/imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from rdmo.core.import_helpers import ElementImportHelper, ExtraFieldHelper

from .models import Plugin
from .validators import PluginLockedValidator, PluginUniqueURIValidator

import_helper_plugin = ElementImportHelper(
model=Plugin,
validators=(PluginLockedValidator, PluginUniqueURIValidator),
lang_fields=('title', 'help'),
extra_fields=(
ExtraFieldHelper(field_name='python_path'),
ExtraFieldHelper(field_name='plugin_settings', overwrite_in_element=True),
ExtraFieldHelper(field_name='available', overwrite_in_element=True),
ExtraFieldHelper(field_name='locked'),
ExtraFieldHelper(field_name='order'),
ExtraFieldHelper(field_name='url_name'),

),
add_current_site_sites = True,
)
44 changes: 44 additions & 0 deletions rdmo/config/legacy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from django.conf import settings

from rdmo.config.constants import PLUGIN_TYPES

PLUGIN_TYPE_TO_SETTING_KEY = {
PLUGIN_TYPES.PROJECT_IMPORT: "PROJECT_IMPORTS",
PLUGIN_TYPES.PROJECT_EXPORT: "PROJECT_EXPORTS",
PLUGIN_TYPES.PROJECT_SNAPSHOT_EXPORT: "PROJECT_SNAPSHOT_EXPORTS",
PLUGIN_TYPES.PROJECT_ISSUE_PROVIDER: "PROJECT_ISSUE_PROVIDERS",
PLUGIN_TYPES.OPTIONSET_PROVIDER: "OPTIONSET_PROVIDERS",
}

def get_plugins_from_legacy_settings(select_plugin_type=None) -> list[dict]:
"""Read 3-tuples (key, label, python-path) from legacy settings."""
plugin_definitions: list[dict] = []
for plugin_type, legacy_setting in PLUGIN_TYPE_TO_SETTING_KEY.items():
if not hasattr(settings, legacy_setting):
continue
if select_plugin_type is not None and select_plugin_type != plugin_type:
continue

legacy_plugins = getattr(settings, legacy_setting, None)
if not legacy_plugins:
continue

for entry in legacy_plugins:
try:
key, label, dotted = entry
except ValueError as exc:
raise ValueError(
f"{legacy_setting} must be a sequence of 3-tuples "
f"(key, label, python-path); got {entry!r}"
) from exc

plugin_definitions.append({
"uri_prefix": settings.DEFAULT_URI_PREFIX,
"uri_path": f"{legacy_setting.lower()}/{key}",
"title": label,
"python_path": dotted,
"plugin_type": plugin_type,
"url_name": key,
})

return plugin_definitions
Empty file.
27 changes: 27 additions & 0 deletions rdmo/config/management/commands/check_plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django.core.management.base import BaseCommand
from django.utils.module_loading import import_string

from rdmo.config.models import Plugin


class Command(BaseCommand):
help = "Check that all configured plugins can be imported"

def handle(self, *args, **options):
if not Plugin.objects.exists():
self.stdout.write(self.style.SUCCESS("No plugins found."))

for plugin in Plugin.objects.order_by('python_path').all():
try:
import_string(plugin.python_path)
url_name = f" url_name='{plugin.url_name}'" if plugin.url_name else ""
self.stdout.write(self.style.SUCCESS(f"✔ {plugin.python_path}, type={plugin.plugin_type}{url_name}."))
except ImportError as e:
if plugin.available:
self.stdout.write(self.style.ERROR(
f"✖ {plugin.python_path}, type={plugin.plugin_type} failed: {e}")
)
else:
self.stdout.write(self.style.WARNING(
f"!! {plugin.python_path}, type={plugin.plugin_type} (=unavailable) failed: {e}")
)
Loading