wbwiki 2.2.1__tar.gz
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.
- wbwiki-2.2.1/.gitignore +181 -0
- wbwiki-2.2.1/PKG-INFO +5 -0
- wbwiki-2.2.1/pyproject.toml +29 -0
- wbwiki-2.2.1/wbwiki/__init__.py +1 -0
- wbwiki-2.2.1/wbwiki/admin.py +18 -0
- wbwiki-2.2.1/wbwiki/apps.py +5 -0
- wbwiki-2.2.1/wbwiki/dynamic_preferences_registry.py +16 -0
- wbwiki-2.2.1/wbwiki/factories.py +30 -0
- wbwiki-2.2.1/wbwiki/migrations/0001_initial_squashed_0004_alter_wikiarticle_tags.py +33 -0
- wbwiki-2.2.1/wbwiki/migrations/0005_wikiarticlerelationship_and_more.py +52 -0
- wbwiki-2.2.1/wbwiki/migrations/0006_auto_20240103_0954.py +43 -0
- wbwiki-2.2.1/wbwiki/migrations/__init__.py +0 -0
- wbwiki-2.2.1/wbwiki/models.py +76 -0
- wbwiki-2.2.1/wbwiki/preferences.py +10 -0
- wbwiki-2.2.1/wbwiki/serializers.py +91 -0
- wbwiki-2.2.1/wbwiki/tests/__init__.py +0 -0
- wbwiki-2.2.1/wbwiki/tests/conftest.py +13 -0
- wbwiki-2.2.1/wbwiki/tests/signals.py +13 -0
- wbwiki-2.2.1/wbwiki/tests/test_initial.py +2 -0
- wbwiki-2.2.1/wbwiki/tests/tests.py +17 -0
- wbwiki-2.2.1/wbwiki/urls.py +26 -0
- wbwiki-2.2.1/wbwiki/viewsets/__init__.py +5 -0
- wbwiki-2.2.1/wbwiki/viewsets/buttons/__init__.py +4 -0
- wbwiki-2.2.1/wbwiki/viewsets/buttons/wikis.py +51 -0
- wbwiki-2.2.1/wbwiki/viewsets/display/__init__.py +1 -0
- wbwiki-2.2.1/wbwiki/viewsets/display/wikis.py +75 -0
- wbwiki-2.2.1/wbwiki/viewsets/endpoints/__init__.py +4 -0
- wbwiki-2.2.1/wbwiki/viewsets/endpoints/wikis.py +20 -0
- wbwiki-2.2.1/wbwiki/viewsets/menu/__init__.py +1 -0
- wbwiki-2.2.1/wbwiki/viewsets/menu/wikis.py +21 -0
- wbwiki-2.2.1/wbwiki/viewsets/preview/__init__.py +0 -0
- wbwiki-2.2.1/wbwiki/viewsets/titles/init.py +0 -0
- wbwiki-2.2.1/wbwiki/viewsets/wikis.py +107 -0
wbwiki-2.2.1/.gitignore
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
~
|
|
2
|
+
|
|
3
|
+
# Docker volumes
|
|
4
|
+
volumes/
|
|
5
|
+
# Poetry auth file
|
|
6
|
+
auth.toml
|
|
7
|
+
|
|
8
|
+
media/*
|
|
9
|
+
media/
|
|
10
|
+
mediafiles/
|
|
11
|
+
mediafiles/*
|
|
12
|
+
test/*
|
|
13
|
+
staticfiles/*
|
|
14
|
+
staticfiles/
|
|
15
|
+
#
|
|
16
|
+
# Byte-compiled / optimized / DLL files
|
|
17
|
+
__pycache__/
|
|
18
|
+
*.py[cod]
|
|
19
|
+
*$py.class
|
|
20
|
+
|
|
21
|
+
# C extensions
|
|
22
|
+
*.so
|
|
23
|
+
|
|
24
|
+
# Distribution / packaging
|
|
25
|
+
.Python
|
|
26
|
+
build/
|
|
27
|
+
develop-eggs/
|
|
28
|
+
dist/
|
|
29
|
+
info/
|
|
30
|
+
downloads/
|
|
31
|
+
eggs/
|
|
32
|
+
.eggs/
|
|
33
|
+
lib/
|
|
34
|
+
lib64/
|
|
35
|
+
parts/
|
|
36
|
+
sdist/
|
|
37
|
+
var/
|
|
38
|
+
wheels/
|
|
39
|
+
share/python-wheels/
|
|
40
|
+
*.egg-info/
|
|
41
|
+
.installed.cfg
|
|
42
|
+
*.egg
|
|
43
|
+
MANIFEST
|
|
44
|
+
|
|
45
|
+
# PyInstaller
|
|
46
|
+
# Usually these files are written by a python script from a template
|
|
47
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
48
|
+
*.manifest
|
|
49
|
+
*.spec
|
|
50
|
+
|
|
51
|
+
# Installer logs
|
|
52
|
+
pip-log.txt
|
|
53
|
+
pip-delete-this-directory.txt
|
|
54
|
+
|
|
55
|
+
# Unit test / coverage reports
|
|
56
|
+
htmlcov/
|
|
57
|
+
.tox/
|
|
58
|
+
.nox/
|
|
59
|
+
.coverage
|
|
60
|
+
.coverage.*
|
|
61
|
+
.cache
|
|
62
|
+
.dccache
|
|
63
|
+
nosetests.xml
|
|
64
|
+
coverage.xml
|
|
65
|
+
*.cover
|
|
66
|
+
*.py,cover
|
|
67
|
+
.hypothesis/
|
|
68
|
+
.pytest_cache/
|
|
69
|
+
cover/
|
|
70
|
+
report.xml
|
|
71
|
+
*/report.xml
|
|
72
|
+
|
|
73
|
+
# Translations
|
|
74
|
+
*.mo
|
|
75
|
+
*.pot
|
|
76
|
+
|
|
77
|
+
# Django stuff:
|
|
78
|
+
*.log
|
|
79
|
+
local_settings.py
|
|
80
|
+
*.sqlite3
|
|
81
|
+
db.sqlite3-journal
|
|
82
|
+
|
|
83
|
+
# Flask stuff:
|
|
84
|
+
instance/
|
|
85
|
+
.webassets-cache
|
|
86
|
+
|
|
87
|
+
# Scrapy stuff:
|
|
88
|
+
.scrapy
|
|
89
|
+
|
|
90
|
+
# Sphinx documentation
|
|
91
|
+
docs/_build/
|
|
92
|
+
|
|
93
|
+
# PyBuilder
|
|
94
|
+
.pybuilder/
|
|
95
|
+
target/
|
|
96
|
+
|
|
97
|
+
# Jupyter Notebook
|
|
98
|
+
.ipynb_checkpoints
|
|
99
|
+
*.ipynb
|
|
100
|
+
# IPython
|
|
101
|
+
profile_default/
|
|
102
|
+
ipython_config.py
|
|
103
|
+
|
|
104
|
+
# pyenv
|
|
105
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
106
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
107
|
+
# .python-version
|
|
108
|
+
|
|
109
|
+
# pipenv
|
|
110
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
111
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
112
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
113
|
+
# install all needed dependencies.
|
|
114
|
+
#Pipfile.lock
|
|
115
|
+
|
|
116
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
117
|
+
__pypackages__/
|
|
118
|
+
|
|
119
|
+
# Celery stuff
|
|
120
|
+
celerybeat-schedule
|
|
121
|
+
celerybeat.pid
|
|
122
|
+
|
|
123
|
+
# SageMath parsed files
|
|
124
|
+
*.sage.py
|
|
125
|
+
|
|
126
|
+
# Environments
|
|
127
|
+
.env
|
|
128
|
+
.envrc
|
|
129
|
+
.venv
|
|
130
|
+
env/
|
|
131
|
+
venv/
|
|
132
|
+
ENV/
|
|
133
|
+
env.bak/
|
|
134
|
+
venv.bak/
|
|
135
|
+
.vscode/
|
|
136
|
+
.idea/
|
|
137
|
+
.idea.bkp/
|
|
138
|
+
|
|
139
|
+
# Spyder project settings
|
|
140
|
+
.spyderproject
|
|
141
|
+
.spyproject
|
|
142
|
+
|
|
143
|
+
# Rope project settings
|
|
144
|
+
.ropeproject
|
|
145
|
+
|
|
146
|
+
# mkdocs documentation
|
|
147
|
+
/site
|
|
148
|
+
|
|
149
|
+
# mypy
|
|
150
|
+
.mypy_cache/
|
|
151
|
+
.dmypy.json
|
|
152
|
+
dmypy.json
|
|
153
|
+
|
|
154
|
+
# Pyre type checker
|
|
155
|
+
.pyre/
|
|
156
|
+
|
|
157
|
+
# pytype static type analyzer
|
|
158
|
+
.pytype/
|
|
159
|
+
crm/
|
|
160
|
+
# Cython debug symbols
|
|
161
|
+
cython_debug/
|
|
162
|
+
|
|
163
|
+
# Gitlab Runner
|
|
164
|
+
builds
|
|
165
|
+
builds/
|
|
166
|
+
|
|
167
|
+
# Integrator Office 365 : reverse proxy tunnel for outlook365
|
|
168
|
+
ngrok
|
|
169
|
+
*/ngrok
|
|
170
|
+
/modules/**/system/
|
|
171
|
+
|
|
172
|
+
/modules/wbmailing/files/*
|
|
173
|
+
/modules/wbmailing/mailing/*
|
|
174
|
+
|
|
175
|
+
/projects/*/requirements.txt
|
|
176
|
+
public
|
|
177
|
+
|
|
178
|
+
# Ignore archive localization generated folder
|
|
179
|
+
backend/modules/**/archive/*
|
|
180
|
+
**/**/requirements.txt
|
|
181
|
+
CHANGELOG-*
|
wbwiki-2.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "wbwiki"
|
|
3
|
+
description = ""
|
|
4
|
+
authors = [{ name = "Christopher Wittlinger", email = "c.wittlinger@stainly.com"}]
|
|
5
|
+
dynamic = ["version"]
|
|
6
|
+
|
|
7
|
+
dependencies = [
|
|
8
|
+
"wbcore",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
[tool.uv.sources]
|
|
12
|
+
wbcore = { workspace = true }
|
|
13
|
+
|
|
14
|
+
[tool.uv]
|
|
15
|
+
package = true
|
|
16
|
+
|
|
17
|
+
[tool.hatch.version]
|
|
18
|
+
path = "../../pyproject.toml"
|
|
19
|
+
|
|
20
|
+
[tool.hatch.build.targets.sdist]
|
|
21
|
+
include = ["wbwiki/*"]
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["wbwiki"]
|
|
25
|
+
only-packages = true
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["hatchling"]
|
|
29
|
+
build-backend = "hatchling.build"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
from wbwiki.models import WikiArticle, WikiArticleRelationship
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class WikiArticleRelationshipInline(admin.TabularInline):
|
|
6
|
+
model = WikiArticleRelationship
|
|
7
|
+
extra = 0
|
|
8
|
+
raw_id_fields = ["content_type", "wiki"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@admin.register(WikiArticleRelationship)
|
|
12
|
+
class WikiArticleRelationshipAdmin(admin.ModelAdmin):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@admin.register(WikiArticle)
|
|
17
|
+
class WikiArticleModelAdmin(admin.ModelAdmin):
|
|
18
|
+
inlines = [WikiArticleRelationshipInline]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from django.contrib.contenttypes.models import ContentType
|
|
2
|
+
from django.utils.translation import gettext as _
|
|
3
|
+
from dynamic_preferences.preferences import Section
|
|
4
|
+
from dynamic_preferences.registries import global_preferences_registry
|
|
5
|
+
from dynamic_preferences.types import ModelMultipleChoicePreference
|
|
6
|
+
|
|
7
|
+
mailing_section = Section("wbwiki")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@global_preferences_registry.register
|
|
11
|
+
class AllowedTypeWikiRelationshipPreference(ModelMultipleChoicePreference):
|
|
12
|
+
section = mailing_section
|
|
13
|
+
name = "allowed_type_wiki_relationship"
|
|
14
|
+
queryset = ContentType.objects.all()
|
|
15
|
+
default = []
|
|
16
|
+
verbose_name = _("Allowed Type Wiki Relationship")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import factory
|
|
2
|
+
from django.contrib.contenttypes.models import ContentType
|
|
3
|
+
from wbwiki.models import WikiArticle, WikiArticleRelationship
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class WikiArticleFactory(factory.django.DjangoModelFactory):
|
|
7
|
+
class Meta:
|
|
8
|
+
model = WikiArticle
|
|
9
|
+
|
|
10
|
+
title = factory.Faker("pystr")
|
|
11
|
+
summary = factory.Faker("pystr")
|
|
12
|
+
content = factory.Faker("pystr")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AbstractPublicationFactory(factory.django.DjangoModelFactory):
|
|
16
|
+
object_id = factory.SelfAttribute("content_object.id")
|
|
17
|
+
content_type = factory.LazyAttribute(lambda o: ContentType.objects.get_for_model(o.content_object))
|
|
18
|
+
|
|
19
|
+
class Meta:
|
|
20
|
+
exclude = ["content_object"]
|
|
21
|
+
abstract = True
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class WikiArticleRelationshipFactory(AbstractPublicationFactory):
|
|
25
|
+
class Meta:
|
|
26
|
+
model = WikiArticleRelationship
|
|
27
|
+
|
|
28
|
+
wiki = factory.SubFactory(WikiArticleFactory)
|
|
29
|
+
content_object = factory.SubFactory("wbcore.contrib.authentication.factories.AuthenticatedPersonFactory")
|
|
30
|
+
# content_object = factory.SubFactory(WikiArticleFactory)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Generated by Django 4.1.8 on 2023-04-19 05:27
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
initial = True
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
("tags", "0001_initial"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.CreateModel(
|
|
15
|
+
name="WikiArticle",
|
|
16
|
+
fields=[
|
|
17
|
+
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
|
18
|
+
("tag_detail_endpoint", models.CharField(blank=True, max_length=255, null=True)),
|
|
19
|
+
("tag_representation", models.CharField(blank=True, max_length=512, null=True)),
|
|
20
|
+
("title", models.CharField(max_length=1024)),
|
|
21
|
+
("content", models.TextField(default="")),
|
|
22
|
+
("summary", models.TextField(default="")),
|
|
23
|
+
(
|
|
24
|
+
"tags",
|
|
25
|
+
models.ManyToManyField(blank=True, related_name="%(app_label)s_%(class)s_items", to="tags.tag"),
|
|
26
|
+
),
|
|
27
|
+
],
|
|
28
|
+
options={
|
|
29
|
+
"verbose_name": "Wiki Article",
|
|
30
|
+
"verbose_name_plural": "Wiki Articles",
|
|
31
|
+
},
|
|
32
|
+
),
|
|
33
|
+
]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Generated by Django 4.1.9 on 2023-07-11 10:27
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
dependencies = [
|
|
9
|
+
("contenttypes", "0002_remove_content_type_name"),
|
|
10
|
+
("wbwiki", "0001_initial_squashed_0004_alter_wikiarticle_tags"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.CreateModel(
|
|
15
|
+
name="WikiArticleRelationship",
|
|
16
|
+
fields=[
|
|
17
|
+
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
|
18
|
+
("computed_str", models.CharField(blank=True, max_length=512, null=True, verbose_name="Name")),
|
|
19
|
+
("object_id", models.PositiveIntegerField(verbose_name="Related Item")),
|
|
20
|
+
(
|
|
21
|
+
"content_type",
|
|
22
|
+
models.ForeignKey(
|
|
23
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
24
|
+
to="contenttypes.contenttype",
|
|
25
|
+
verbose_name="Item Type",
|
|
26
|
+
),
|
|
27
|
+
),
|
|
28
|
+
(
|
|
29
|
+
"wiki",
|
|
30
|
+
models.ForeignKey(
|
|
31
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
32
|
+
related_name="relationships",
|
|
33
|
+
to="wbwiki.wikiarticle",
|
|
34
|
+
),
|
|
35
|
+
),
|
|
36
|
+
],
|
|
37
|
+
options={
|
|
38
|
+
"verbose_name": "Wiki Relationship",
|
|
39
|
+
"verbose_name_plural": "Wiki Relationships",
|
|
40
|
+
},
|
|
41
|
+
),
|
|
42
|
+
migrations.AddIndex(
|
|
43
|
+
model_name="wikiarticlerelationship",
|
|
44
|
+
index=models.Index(fields=["content_type", "object_id"], name="wbwiki_wiki_content_9ba127_idx"),
|
|
45
|
+
),
|
|
46
|
+
migrations.AddConstraint(
|
|
47
|
+
model_name="wikiarticlerelationship",
|
|
48
|
+
constraint=models.UniqueConstraint(
|
|
49
|
+
fields=("wiki", "content_type", "object_id"), name="unique_wiki_article_relationship"
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Generated by Django 4.2.8 on 2024-01-03 08:54
|
|
2
|
+
|
|
3
|
+
from contextlib import suppress
|
|
4
|
+
|
|
5
|
+
from django.apps import apps
|
|
6
|
+
from django.contrib.contenttypes.models import ContentType
|
|
7
|
+
from django.db import migrations
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def migrate_content_type(apps, schema_editor):
|
|
11
|
+
WikiArticleRelationship = apps.get_model("wbwiki", "WikiArticleRelationship")
|
|
12
|
+
|
|
13
|
+
for model_name in [
|
|
14
|
+
"Classification",
|
|
15
|
+
"Instrument",
|
|
16
|
+
"ClassificationGroup",
|
|
17
|
+
"Deal",
|
|
18
|
+
"Exchange",
|
|
19
|
+
"InstrumentClassificationRelatedInstrument",
|
|
20
|
+
"InstrumentClassificationThroughModel",
|
|
21
|
+
"InstrumentFavoriteGroup",
|
|
22
|
+
"InstrumentList",
|
|
23
|
+
"InstrumentListThroughModel",
|
|
24
|
+
"InstrumentRequest",
|
|
25
|
+
"RelatedInstrumentThroughModel",
|
|
26
|
+
"InstrumentPrice",
|
|
27
|
+
]:
|
|
28
|
+
with suppress(ContentType.DoesNotExist):
|
|
29
|
+
old_ct = ContentType.objects.get(app_label="wbportfolio", model=model_name.lower())
|
|
30
|
+
new_ct = ContentType.objects.get(app_label="wbfdm", model=model_name.lower())
|
|
31
|
+
WikiArticleRelationship.objects.filter(content_type_id=old_ct.id).update(content_type_id=new_ct.id)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Migration(migrations.Migration):
|
|
35
|
+
dependencies = (
|
|
36
|
+
[
|
|
37
|
+
("wbwiki", "0005_wikiarticlerelationship_and_more"),
|
|
38
|
+
("wbfdm", "0012_instrumentprice_created_instrumentprice_modified"),
|
|
39
|
+
]
|
|
40
|
+
if apps.is_installed("wbfdm")
|
|
41
|
+
else [("wbwiki", "0005_wikiarticlerelationship_and_more")]
|
|
42
|
+
)
|
|
43
|
+
operations = [migrations.RunPython(migrate_content_type)]
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import reversion
|
|
2
|
+
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
3
|
+
from django.contrib.contenttypes.models import ContentType
|
|
4
|
+
from django.db import models
|
|
5
|
+
from django.utils.translation import gettext_lazy as _
|
|
6
|
+
from rest_framework.reverse import reverse
|
|
7
|
+
from wbcore.contrib.tags.models import TagModelMixin
|
|
8
|
+
from wbcore.models import WBModel
|
|
9
|
+
from wbcore.utils.models import ComplexToStringMixin
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@reversion.register()
|
|
13
|
+
class WikiArticle(TagModelMixin, WBModel):
|
|
14
|
+
title = models.CharField(max_length=1024)
|
|
15
|
+
summary = models.TextField(default="")
|
|
16
|
+
content = models.TextField(default="")
|
|
17
|
+
|
|
18
|
+
def __str__(self) -> str:
|
|
19
|
+
return f"{self.title}"
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def get_endpoint_basename(cls):
|
|
23
|
+
return "wbwiki:wikiarticle"
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def get_representation_endpoint(cls):
|
|
27
|
+
return "wbwiki:wikiarticle-list"
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get_representation_value_key(cls):
|
|
31
|
+
return "id"
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def get_representation_label_key(cls):
|
|
35
|
+
return "{{ title }}"
|
|
36
|
+
|
|
37
|
+
def get_tag_detail_endpoint(self):
|
|
38
|
+
return reverse("wbwiki:wikiarticle-detail", [self.id])
|
|
39
|
+
|
|
40
|
+
def get_tag_representation(self):
|
|
41
|
+
return self.title
|
|
42
|
+
|
|
43
|
+
class Meta:
|
|
44
|
+
verbose_name = "Wiki Article"
|
|
45
|
+
verbose_name_plural = "Wiki Articles"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class WikiArticleRelationship(ComplexToStringMixin, models.Model):
|
|
49
|
+
wiki = models.ForeignKey(to="wbwiki.WikiArticle", related_name="relationships", on_delete=models.CASCADE)
|
|
50
|
+
content_type = models.ForeignKey(ContentType, verbose_name=_("Item Type"), on_delete=models.CASCADE)
|
|
51
|
+
object_id = models.PositiveIntegerField(verbose_name=_("Related Item"))
|
|
52
|
+
content_object = GenericForeignKey("content_type", "object_id")
|
|
53
|
+
|
|
54
|
+
def __str__(self) -> str:
|
|
55
|
+
return f"{self.wiki} -> {self.content_object}"
|
|
56
|
+
|
|
57
|
+
def compute_str(self) -> str:
|
|
58
|
+
return f"{self.content_object}"
|
|
59
|
+
|
|
60
|
+
class Meta:
|
|
61
|
+
verbose_name = _("Wiki Relationship")
|
|
62
|
+
verbose_name_plural = _("Wiki Relationships")
|
|
63
|
+
constraints = [
|
|
64
|
+
models.UniqueConstraint(
|
|
65
|
+
name="unique_wiki_article_relationship", fields=["wiki", "content_type", "object_id"]
|
|
66
|
+
),
|
|
67
|
+
]
|
|
68
|
+
indexes = [models.Index(fields=["content_type", "object_id"])]
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def get_representation_value_key(cls):
|
|
72
|
+
return "id"
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def get_representation_label_key(cls):
|
|
76
|
+
return "{{wiki}} -> {{computed_str}}"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from django.contrib.contenttypes.models import ContentType
|
|
2
|
+
from django.db.utils import ProgrammingError
|
|
3
|
+
from dynamic_preferences.registries import global_preferences_registry
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_allowed_type_wiki_relationship():
|
|
7
|
+
try:
|
|
8
|
+
return global_preferences_registry.manager()["wbwiki__allowed_type_wiki_relationship"]
|
|
9
|
+
except (RuntimeError, ProgrammingError):
|
|
10
|
+
return ContentType.objects.none()
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from django.utils.translation import gettext as _
|
|
2
|
+
from rest_framework import serializers as rf_serializers
|
|
3
|
+
from rest_framework.reverse import reverse
|
|
4
|
+
from wbcore import serializers
|
|
5
|
+
from wbcore.content_type.serializers import (
|
|
6
|
+
ContentTypeRepresentationSerializer,
|
|
7
|
+
DynamicObjectIDRepresentationSerializer,
|
|
8
|
+
)
|
|
9
|
+
from wbcore.contrib.tags.serializers import TagSerializerMixin
|
|
10
|
+
from wbwiki.models import WikiArticle, WikiArticleRelationship
|
|
11
|
+
|
|
12
|
+
from .preferences import get_allowed_type_wiki_relationship
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class WikiArticleRepresentationSerializer(serializers.RepresentationSerializer):
|
|
16
|
+
class Meta:
|
|
17
|
+
model = WikiArticle
|
|
18
|
+
fields = ("id", "title")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class WikiArticleModelSerializer(TagSerializerMixin, serializers.ModelSerializer):
|
|
22
|
+
@serializers.register_only_instance_resource()
|
|
23
|
+
def relationship_resources(self, instance, request, user, **kwargs):
|
|
24
|
+
resources = {"relationship": reverse("wiki:wiki-relationship-list", args=[instance.id], request=request)}
|
|
25
|
+
return resources
|
|
26
|
+
|
|
27
|
+
class Meta:
|
|
28
|
+
model = WikiArticle
|
|
29
|
+
fields = (
|
|
30
|
+
"id",
|
|
31
|
+
"title",
|
|
32
|
+
"summary",
|
|
33
|
+
"content",
|
|
34
|
+
"tags",
|
|
35
|
+
"_tags",
|
|
36
|
+
"_additional_resources",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class WikiArticleRelationshipModelSerializer(serializers.ModelSerializer):
|
|
41
|
+
_wiki = WikiArticleRepresentationSerializer(source="wiki")
|
|
42
|
+
_content_type = ContentTypeRepresentationSerializer(
|
|
43
|
+
source="content_type",
|
|
44
|
+
label_key="{{model_title}}",
|
|
45
|
+
allowed_types=get_allowed_type_wiki_relationship(),
|
|
46
|
+
)
|
|
47
|
+
_object_id = DynamicObjectIDRepresentationSerializer(
|
|
48
|
+
source="object_id",
|
|
49
|
+
optional_get_parameters={"content_type": "content_type"},
|
|
50
|
+
depends_on=[{"field": "content_type", "options": {}}],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@serializers.register_only_instance_resource()
|
|
54
|
+
def additional_resources(self, instance, request, user, view, **kwargs):
|
|
55
|
+
if instance and hasattr(instance.content_object, "get_endpoint_basename"):
|
|
56
|
+
return {
|
|
57
|
+
"object_endpoint": reverse(
|
|
58
|
+
f"{instance.content_object.get_endpoint_basename()}-detail",
|
|
59
|
+
args=[instance.object_id],
|
|
60
|
+
request=request,
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class Meta:
|
|
65
|
+
model = WikiArticleRelationship
|
|
66
|
+
dependency_map = {"object_id": ["content_type"]}
|
|
67
|
+
fields = (
|
|
68
|
+
"id",
|
|
69
|
+
"content_type",
|
|
70
|
+
"_content_type",
|
|
71
|
+
"object_id",
|
|
72
|
+
"_object_id",
|
|
73
|
+
"wiki",
|
|
74
|
+
"_wiki",
|
|
75
|
+
"computed_str",
|
|
76
|
+
"_additional_resources",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def validate(self, validated_data):
|
|
80
|
+
content_type = validated_data.get("content_type", self.instance.content_type if self.instance else None)
|
|
81
|
+
wiki = validated_data.get("wiki", self.instance.wiki if self.instance else None)
|
|
82
|
+
object_id = validated_data.get("object_id", self.instance.object_id if self.instance else None)
|
|
83
|
+
|
|
84
|
+
if content_type and wiki and object_id:
|
|
85
|
+
qs = WikiArticleRelationship.objects.filter(wiki=wiki, content_type=content_type, object_id=object_id)
|
|
86
|
+
qs = qs.exclude(id=self.instance.id) if self.instance else qs
|
|
87
|
+
if qs.exists():
|
|
88
|
+
raise rf_serializers.ValidationError(
|
|
89
|
+
{"object_id": _("Relationship with wiki: {} already exists").format(wiki)}
|
|
90
|
+
)
|
|
91
|
+
return super().validate(validated_data)
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from django.apps import apps
|
|
2
|
+
from django.db import connection
|
|
3
|
+
from django.db.models.signals import pre_migrate
|
|
4
|
+
from pytest_factoryboy import register
|
|
5
|
+
from wbcore.contrib.geography.tests.signals import app_pre_migration
|
|
6
|
+
from wbwiki.factories import WikiArticleFactory, WikiArticleRelationshipFactory
|
|
7
|
+
|
|
8
|
+
register(WikiArticleFactory)
|
|
9
|
+
register(WikiArticleRelationshipFactory)
|
|
10
|
+
|
|
11
|
+
from .signals import *
|
|
12
|
+
|
|
13
|
+
pre_migrate.connect(app_pre_migration, sender=apps.get_app_config("wbwiki"))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from django.contrib.contenttypes.models import ContentType
|
|
2
|
+
from django.dispatch import receiver
|
|
3
|
+
from wbcore.test.signals import custom_update_kwargs
|
|
4
|
+
from wbwiki.factories import WikiArticleRelationshipFactory
|
|
5
|
+
from wbwiki.viewsets import ContentObjectWikiArticleModelViewSet
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@receiver(custom_update_kwargs, sender=ContentObjectWikiArticleModelViewSet)
|
|
9
|
+
def receive_kwargs_content_object(sender, *args, **kwargs):
|
|
10
|
+
if wiki := kwargs.get("obj_factory"):
|
|
11
|
+
WikiArticleRelationshipFactory(wiki=wiki, content_object=wiki)
|
|
12
|
+
return {"content_type": ContentType.objects.get_for_model(wiki).id, "content_id": wiki.id}
|
|
13
|
+
return {}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from wbcore.test import GenerateTest, default_config
|
|
3
|
+
|
|
4
|
+
config = {}
|
|
5
|
+
for key, value in default_config.items():
|
|
6
|
+
config[key] = list(
|
|
7
|
+
filter(
|
|
8
|
+
lambda x: x.__module__.startswith("wbwiki") and x.__name__ not in ["WikiArticleRelationshipModelViewSet"],
|
|
9
|
+
value,
|
|
10
|
+
)
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.django_db
|
|
15
|
+
@GenerateTest(config)
|
|
16
|
+
class TestProject:
|
|
17
|
+
pass
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from django.urls import include, path
|
|
2
|
+
from wbcore.routers import WBCoreRouter
|
|
3
|
+
from wbwiki import viewsets
|
|
4
|
+
|
|
5
|
+
router = WBCoreRouter()
|
|
6
|
+
router.register(r"wikiarticle", viewsets.WikiArticleModelViewSet, basename="wikiarticle")
|
|
7
|
+
|
|
8
|
+
wiki_router = WBCoreRouter()
|
|
9
|
+
wiki_router.register(
|
|
10
|
+
r"relationship",
|
|
11
|
+
viewsets.WikiArticleRelationshipModelViewSet,
|
|
12
|
+
basename="wiki-relationship",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
content_object_wiki_router = WBCoreRouter()
|
|
16
|
+
content_object_wiki_router.register(
|
|
17
|
+
r"contentobjectwiki",
|
|
18
|
+
viewsets.ContentObjectWikiArticleModelViewSet,
|
|
19
|
+
basename="contentobjectwiki",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
urlpatterns = [
|
|
23
|
+
path("", include(router.urls)),
|
|
24
|
+
path("wiki/<wiki_id>/", include(wiki_router.urls)),
|
|
25
|
+
path("contentobjectwiki/<int:content_type>/<int:content_id>/", include(content_object_wiki_router.urls)),
|
|
26
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from django.utils.translation import gettext as _
|
|
2
|
+
from rest_framework.reverse import reverse
|
|
3
|
+
from wbcore import serializers as wb_serializers
|
|
4
|
+
from wbcore.contrib.icons import WBIcon
|
|
5
|
+
from wbcore.enums import RequestType
|
|
6
|
+
from wbcore.metadata.configs import buttons as bt
|
|
7
|
+
from wbcore.metadata.configs.buttons.view_config import ButtonViewConfig
|
|
8
|
+
from wbcore.metadata.configs.display.instance_display.shortcuts import (
|
|
9
|
+
create_simple_display,
|
|
10
|
+
)
|
|
11
|
+
from wbwiki.models import WikiArticle
|
|
12
|
+
from wbwiki.serializers import WikiArticleRepresentationSerializer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class WikiArticleRelationshipButtonConfig(ButtonViewConfig):
|
|
16
|
+
def get_custom_instance_buttons(self):
|
|
17
|
+
return self.get_custom_list_instance_buttons()
|
|
18
|
+
|
|
19
|
+
def get_custom_list_instance_buttons(self):
|
|
20
|
+
return {
|
|
21
|
+
bt.WidgetButton(key="object_endpoint", label="{{computed_str}}", icon=WBIcon.CLIPBOARD.icon),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ContentObjectWikiArticleButtonConfig(ButtonViewConfig):
|
|
26
|
+
def get_custom_buttons(self):
|
|
27
|
+
if not self.view.kwargs.get("pk", None):
|
|
28
|
+
|
|
29
|
+
class WikiArticleSerializer(wb_serializers.Serializer):
|
|
30
|
+
wiki = wb_serializers.PrimaryKeyRelatedField(
|
|
31
|
+
queryset=WikiArticle.objects.all(),
|
|
32
|
+
required=True,
|
|
33
|
+
label=_("Wiki"),
|
|
34
|
+
)
|
|
35
|
+
_wiki = WikiArticleRepresentationSerializer(source="wiki")
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
bt.ActionButton(
|
|
39
|
+
identifiers=("wbwiki:wikiarticle",),
|
|
40
|
+
method=RequestType.PATCH,
|
|
41
|
+
endpoint=f"{reverse('wbwiki:wikiarticle-linkwiki', args=[], request=self.request)}?content_type={self.view.kwargs['content_type']}&content_id={self.view.kwargs['content_id']}",
|
|
42
|
+
action_label=_("Add Existing Wiki"),
|
|
43
|
+
title=_("Add Existing Wiki"),
|
|
44
|
+
label=_("Add Existing Wiki"),
|
|
45
|
+
icon=WBIcon.LINK.icon,
|
|
46
|
+
serializer=WikiArticleSerializer,
|
|
47
|
+
description_fields="<p>Link item to Existing Wiki</p><p>Are you sure you want to proceed?</p>",
|
|
48
|
+
instance_display=create_simple_display([["wiki"]]),
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
return {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .wikis import WikiArticleDisplayConfig, WikiArticleRelationshipDisplayConfig
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from django.utils.translation import gettext as _
|
|
2
|
+
from wbcore.metadata.configs import display as dp
|
|
3
|
+
from wbcore.metadata.configs.display.instance_display import (
|
|
4
|
+
Display,
|
|
5
|
+
Inline,
|
|
6
|
+
Layout,
|
|
7
|
+
Page,
|
|
8
|
+
Style,
|
|
9
|
+
create_simple_display,
|
|
10
|
+
)
|
|
11
|
+
from wbcore.metadata.configs.display.instance_display.operators import default
|
|
12
|
+
from wbcore.metadata.configs.display.view_config import DisplayViewConfig
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class WikiArticleRelationshipDisplayConfig(DisplayViewConfig):
|
|
16
|
+
def get_list_display(self) -> dp.ListDisplay:
|
|
17
|
+
return dp.ListDisplay(
|
|
18
|
+
fields=[
|
|
19
|
+
dp.Field(key="content_type", label="Item Type"),
|
|
20
|
+
dp.Field(key="computed_str", label="Related Item"),
|
|
21
|
+
]
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def get_instance_display(self) -> Display:
|
|
25
|
+
return create_simple_display([["content_type", "object_id"]])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class WikiArticleDisplayConfig(DisplayViewConfig):
|
|
29
|
+
def get_list_display(self) -> dp.ListDisplay:
|
|
30
|
+
return dp.ListDisplay(
|
|
31
|
+
fields=[
|
|
32
|
+
dp.Field(key="title", label="Title"),
|
|
33
|
+
dp.Field(key="summary", label="Summary"),
|
|
34
|
+
dp.Field(key="tags", label="Tags"),
|
|
35
|
+
]
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def _get_custom_instance_display(self) -> Display:
|
|
39
|
+
return Display(
|
|
40
|
+
pages=[
|
|
41
|
+
Page(
|
|
42
|
+
title=_("Main Information"),
|
|
43
|
+
layouts={
|
|
44
|
+
default(): Layout(
|
|
45
|
+
grid_template_areas=[["title"], ["summary"], ["tags"], ["content"]],
|
|
46
|
+
grid_template_columns=[
|
|
47
|
+
"minmax(min-content, 1fr)",
|
|
48
|
+
],
|
|
49
|
+
),
|
|
50
|
+
},
|
|
51
|
+
),
|
|
52
|
+
Page(
|
|
53
|
+
title=_("Relationship"),
|
|
54
|
+
layouts={
|
|
55
|
+
default(): Layout(
|
|
56
|
+
grid_template_areas=[["relationship_key"]],
|
|
57
|
+
inlines=[Inline(key="relationship_key", endpoint="relationship")],
|
|
58
|
+
grid_template_columns=[
|
|
59
|
+
"minmax(min-content, 1fr)",
|
|
60
|
+
],
|
|
61
|
+
grid_auto_rows=Style.MIN_CONTENT,
|
|
62
|
+
),
|
|
63
|
+
},
|
|
64
|
+
),
|
|
65
|
+
]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def get_instance_display(self) -> Display:
|
|
69
|
+
display = (
|
|
70
|
+
self._get_custom_instance_display()
|
|
71
|
+
if "pk" in self.view.kwargs
|
|
72
|
+
else create_simple_display([["title", "summary", "tags", "content"]])
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return display
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from rest_framework.reverse import reverse
|
|
2
|
+
from wbcore.metadata.configs.endpoints import EndpointViewConfig
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class WikiArticleRelationshipEndpointConfig(EndpointViewConfig):
|
|
6
|
+
def get_endpoint(self, **kwargs):
|
|
7
|
+
return reverse(
|
|
8
|
+
"wiki:wiki-relationship-list",
|
|
9
|
+
args=[self.view.kwargs["wiki_id"]],
|
|
10
|
+
request=self.request,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ContentObjectWikiArticleEndpointConfig(EndpointViewConfig):
|
|
15
|
+
def get_endpoint(self, **kwargs):
|
|
16
|
+
return reverse(
|
|
17
|
+
"wiki:contentobjectwiki-list",
|
|
18
|
+
args=[self.view.kwargs["content_type"], self.view.kwargs["content_id"]],
|
|
19
|
+
request=self.request,
|
|
20
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .wikis import WIKI_MENU, WIKI_MENU_ITEM
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from wbcore.menus import ItemPermission, Menu, MenuItem
|
|
2
|
+
|
|
3
|
+
WIKI_MENU_ITEM = MenuItem(
|
|
4
|
+
label="WIKI",
|
|
5
|
+
endpoint="wbwiki:wikiarticle-list",
|
|
6
|
+
add=MenuItem(
|
|
7
|
+
label="Create a wiki article",
|
|
8
|
+
endpoint="wbwiki:wikiarticle-list",
|
|
9
|
+
permission=ItemPermission(
|
|
10
|
+
permissions=["wbwiki.create_wikiarticle"],
|
|
11
|
+
),
|
|
12
|
+
),
|
|
13
|
+
permission=ItemPermission(
|
|
14
|
+
permissions=["wbwiki.view_wikiarticle"],
|
|
15
|
+
),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
WIKI_MENU = Menu(
|
|
19
|
+
label="WIKI",
|
|
20
|
+
items=[WIKI_MENU_ITEM],
|
|
21
|
+
)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from django.utils.translation import gettext_lazy as _
|
|
2
|
+
from rest_framework import status
|
|
3
|
+
from rest_framework.decorators import action
|
|
4
|
+
from rest_framework.response import Response
|
|
5
|
+
from reversion.views import RevisionMixin
|
|
6
|
+
from wbcore import viewsets
|
|
7
|
+
from wbcore.contrib.authentication.authentication import JWTCookieAuthentication
|
|
8
|
+
from wbwiki.models import WikiArticle, WikiArticleRelationship
|
|
9
|
+
from wbwiki.serializers import (
|
|
10
|
+
WikiArticleModelSerializer,
|
|
11
|
+
WikiArticleRelationshipModelSerializer,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from .buttons import (
|
|
15
|
+
ContentObjectWikiArticleButtonConfig,
|
|
16
|
+
WikiArticleRelationshipButtonConfig,
|
|
17
|
+
)
|
|
18
|
+
from .display import WikiArticleDisplayConfig, WikiArticleRelationshipDisplayConfig
|
|
19
|
+
from .endpoints import (
|
|
20
|
+
ContentObjectWikiArticleEndpointConfig,
|
|
21
|
+
WikiArticleRelationshipEndpointConfig,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class WikiArticleModelViewSet(RevisionMixin, viewsets.ModelViewSet):
|
|
26
|
+
queryset = WikiArticle.objects.all()
|
|
27
|
+
serializer_class = WikiArticleModelSerializer
|
|
28
|
+
# search_fields = ("title", "summary", "content", "tags__title")
|
|
29
|
+
filterset_fields = {
|
|
30
|
+
"title": ["icontains"],
|
|
31
|
+
"summary": ["icontains"],
|
|
32
|
+
"tags": ["exact"],
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
display_config_class = WikiArticleDisplayConfig
|
|
36
|
+
|
|
37
|
+
@action(detail=False, methods=["PATCH"], authentication_classes=[JWTCookieAuthentication])
|
|
38
|
+
def linkwiki(self, request, pk=None):
|
|
39
|
+
content_type_id = request.GET.get("content_type")
|
|
40
|
+
object_id = request.GET.get("content_id")
|
|
41
|
+
wiki_id = request.POST.get("wiki")
|
|
42
|
+
|
|
43
|
+
if content_type_id and object_id:
|
|
44
|
+
if wiki_id:
|
|
45
|
+
wiki_relationship, created = WikiArticleRelationship.objects.get_or_create(
|
|
46
|
+
wiki_id=wiki_id,
|
|
47
|
+
content_type_id=content_type_id,
|
|
48
|
+
object_id=object_id,
|
|
49
|
+
)
|
|
50
|
+
if created:
|
|
51
|
+
message = _("Relationship Item has been added to wiki")
|
|
52
|
+
_status = status.HTTP_200_OK
|
|
53
|
+
else:
|
|
54
|
+
message = _("Wiki has already been linked to this Item")
|
|
55
|
+
_status = status.HTTP_400_BAD_REQUEST
|
|
56
|
+
else:
|
|
57
|
+
message = _("Wiki is mandatory")
|
|
58
|
+
_status = status.HTTP_400_BAD_REQUEST
|
|
59
|
+
else:
|
|
60
|
+
message = _("Relationship Item could not be linked to wiki")
|
|
61
|
+
_status = status.HTTP_400_BAD_REQUEST
|
|
62
|
+
|
|
63
|
+
return Response(
|
|
64
|
+
{"__notification": {"title": message}},
|
|
65
|
+
status=_status,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ContentObjectWikiArticleModelViewSet(WikiArticleModelViewSet):
|
|
70
|
+
endpoint_config_class = ContentObjectWikiArticleEndpointConfig
|
|
71
|
+
button_config_class = ContentObjectWikiArticleButtonConfig
|
|
72
|
+
|
|
73
|
+
def get_queryset(self):
|
|
74
|
+
if (content_type_id := self.kwargs.get("content_type")) and (object_id := self.kwargs.get("content_id")):
|
|
75
|
+
return (
|
|
76
|
+
super()
|
|
77
|
+
.get_queryset()
|
|
78
|
+
.filter(relationships__content_type=content_type_id, relationships__object_id=object_id)
|
|
79
|
+
)
|
|
80
|
+
return WikiArticle.objects.none()
|
|
81
|
+
|
|
82
|
+
def create(self, request, *args, **kwargs):
|
|
83
|
+
response = super().create(request, *args, **kwargs)
|
|
84
|
+
|
|
85
|
+
if (content_type_id := self.kwargs.get("content_type")) and (object_id := self.kwargs.get("content_id")):
|
|
86
|
+
WikiArticleRelationship.objects.get_or_create(
|
|
87
|
+
wiki_id=response.data["instance"]["id"],
|
|
88
|
+
content_type_id=content_type_id,
|
|
89
|
+
object_id=object_id,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return response
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class WikiArticleRelationshipModelViewSet(viewsets.ModelViewSet):
|
|
96
|
+
serializer_class = WikiArticleRelationshipModelSerializer
|
|
97
|
+
display_config_class = WikiArticleRelationshipDisplayConfig
|
|
98
|
+
endpoint_config_class = WikiArticleRelationshipEndpointConfig
|
|
99
|
+
button_config_class = WikiArticleRelationshipButtonConfig
|
|
100
|
+
queryset = WikiArticleRelationship.objects.all()
|
|
101
|
+
search_fields = ("computed_str", "content_type__model")
|
|
102
|
+
filterset_fields = {
|
|
103
|
+
"computed_str": ["icontains", "exact"],
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
def get_queryset(self):
|
|
107
|
+
return super().get_queryset().filter(wiki__id=self.kwargs["wiki_id"])
|