django-spire 0.16.10__py3-none-any.whl → 0.16.12__py3-none-any.whl

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.
Files changed (68) hide show
  1. django_spire/ai/chat/templates/django_spire/ai/chat/dropdown/ellipsis_dropdown.html +2 -2
  2. django_spire/ai/chat/templates/django_spire/ai/chat/widget/search_chat_widget.html +1 -1
  3. django_spire/auth/group/templates/django_spire/auth/group/element/group_perm_element.html +50 -32
  4. django_spire/auth/group/templates/django_spire/auth/group/element/group_special_role_element.html +51 -34
  5. django_spire/auth/templates/django_spire/auth/page/login_page.html +8 -9
  6. django_spire/auth/templates/django_spire/auth/page/logout_page.html +1 -1
  7. django_spire/auth/templates/django_spire/auth/page/password_set_form_page.html +1 -1
  8. django_spire/comment/templates/django_spire/comment/element/comment_button_element.html +4 -4
  9. django_spire/comment/templates/django_spire/comment/element/comment_textarea_element.html +1 -1
  10. django_spire/comment/templates/django_spire/comment/widget/tagging_widget.html +1 -1
  11. django_spire/consts.py +1 -1
  12. django_spire/contrib/form/templates/django_spire/contrib/form/button/form_submit_button.html +1 -1
  13. django_spire/contrib/help/templates/django_spire/contrib/help/help_modal.html +2 -2
  14. django_spire/core/context_processors.py +0 -3
  15. django_spire/core/static/django_spire/css/app-default.css +1 -0
  16. django_spire/core/static/django_spire/css/bootstrap-extension.css +15 -0
  17. django_spire/core/templates/django_spire/badge/accent_badge.html +1 -1
  18. django_spire/core/templates/django_spire/badge/primary_badge.html +1 -1
  19. django_spire/core/templates/django_spire/badge/secondary_badge.html +1 -1
  20. django_spire/core/templates/django_spire/badge/warning_badge.html +1 -1
  21. django_spire/core/templates/django_spire/card/delete_confirmation_form_card.html +1 -1
  22. django_spire/core/templates/django_spire/card/title_card.html +1 -2
  23. django_spire/core/templates/django_spire/dropdown/dropdown.html +1 -1
  24. django_spire/core/templates/django_spire/dropdown/element/dropdown_link_element.html +16 -16
  25. django_spire/core/templates/django_spire/dropdown/element/ellipsis_dropdown_modal_link_element.html +11 -8
  26. django_spire/core/templates/django_spire/filtering/form/base_session_filter_form.html +2 -2
  27. django_spire/core/templates/django_spire/modal/content/dispatch_modal_delete_confirmation_content.html +1 -1
  28. django_spire/core/templates/django_spire/navigation/accordion/nav_accordion.html +0 -1
  29. django_spire/core/templates/django_spire/navigation/top_navigation.html +13 -0
  30. django_spire/core/templates/django_spire/page/center_card_page.html +1 -1
  31. django_spire/core/templates/django_spire/wizard/wizard.html +4 -4
  32. django_spire/file/apps.py +13 -0
  33. django_spire/file/interfaces.py +42 -9
  34. django_spire/help_desk/apps.py +8 -0
  35. django_spire/help_desk/auth/__init__.py +0 -0
  36. django_spire/help_desk/auth/controller.py +15 -0
  37. django_spire/help_desk/templates/django_spire/help_desk/card/ticket_detail_card.html +3 -9
  38. django_spire/help_desk/templates/django_spire/help_desk/card/ticket_list_card.html +9 -10
  39. django_spire/help_desk/templates/django_spire/help_desk/element/icon.html +3 -0
  40. django_spire/help_desk/templates/django_spire/help_desk/form/ticket_form.html +1 -1
  41. django_spire/help_desk/templates/django_spire/help_desk/item/ticket_item.html +33 -32
  42. django_spire/help_desk/views/form_views.py +3 -3
  43. django_spire/help_desk/views/page_views.py +4 -5
  44. django_spire/knowledge/entry/services/automation_service.py +2 -4
  45. django_spire/knowledge/entry/services/factory_service.py +1 -2
  46. django_spire/knowledge/entry/services/tool_service.py +1 -2
  47. django_spire/knowledge/entry/version/converters/docx_converter.py +1 -1
  48. django_spire/knowledge/entry/version/converters/markdown_converter.py +2 -1
  49. django_spire/knowledge/entry/views/form_views.py +4 -2
  50. django_spire/knowledge/entry/views/json_views.py +1 -1
  51. django_spire/knowledge/templates/django_spire/knowledge/collection/card/context_menu_card.html +5 -5
  52. django_spire/knowledge/templates/django_spire/knowledge/entry/card/context_menu_card.html +5 -5
  53. django_spire/knowledge/templates/django_spire/knowledge/entry/version/block/dropdown/add_dropdown.html +1 -0
  54. django_spire/notification/app/templates/django_spire/notification/app/dropdown/notification_dropdown.html +1 -1
  55. django_spire/notification/app/templates/django_spire/notification/app/dropdown/notification_dropdown_content.html +6 -3
  56. django_spire/settings.py +1 -0
  57. django_spire/theme/templates/django_spire/theme/card/colors_preview_card.html +9 -9
  58. django_spire/theme/templates/django_spire/theme/card/example_preview_card.html +2 -2
  59. django_spire/theme/templates/django_spire/theme/card/preview_card.html +6 -6
  60. django_spire/theme/templates/django_spire/theme/example/item/example_item.html +2 -2
  61. django_spire/theme/templates/django_spire/theme/section/badge_section.html +4 -4
  62. django_spire/theme/templates/django_spire/theme/section/color_section.html +32 -32
  63. {django_spire-0.16.10.dist-info → django_spire-0.16.12.dist-info}/METADATA +5 -1
  64. {django_spire-0.16.10.dist-info → django_spire-0.16.12.dist-info}/RECORD +67 -65
  65. django_spire/knowledge/entry/tests/constants.py +0 -1
  66. {django_spire-0.16.10.dist-info → django_spire-0.16.12.dist-info}/WHEEL +0 -0
  67. {django_spire-0.16.10.dist-info → django_spire-0.16.12.dist-info}/licenses/LICENSE.md +0 -0
  68. {django_spire-0.16.10.dist-info → django_spire-0.16.12.dist-info}/top_level.txt +0 -0
django_spire/file/apps.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from django.apps import AppConfig
2
+ from django.conf import settings
2
3
 
3
4
  from django_spire.utils import check_required_apps
4
5
 
@@ -13,4 +14,16 @@ class FileConfig(AppConfig):
13
14
  URLPATTERNS_NAMESPACE = 'file'
14
15
 
15
16
  def ready(self) -> None:
17
+ if not hasattr(settings, 'BASE_FOLDER_NAME'):
18
+ raise ValueError(
19
+ f'"BASE_FOLDER_NAME" must be set in the django settings when '
20
+ f'using "{self.label}".'
21
+ )
22
+
23
+ elif not isinstance(getattr(settings, 'BASE_FOLDER_NAME'), str):
24
+ raise ValueError(
25
+ f'"BASE_FOLDER_NAME" must be a string in the django settings when '
26
+ f'using "{self.label}".'
27
+ )
28
+
16
29
  check_required_apps(self.label)
@@ -2,6 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  from abc import ABC, abstractmethod
4
4
  from dataclasses import dataclass
5
+
6
+ from django.conf import settings
7
+ from django.core.files.base import ContentFile
5
8
  from typing_extensions import TYPE_CHECKING
6
9
 
7
10
  from django.contrib.contenttypes.models import ContentType
@@ -35,16 +38,27 @@ class FileFormatter:
35
38
 
36
39
  @property
37
40
  def location(self) -> str:
38
- return'django-spire/' + str(self.related_field) + '/' + '/' + str(self.app_name) + '/' + random_64_char_token() + '/' + self.name
41
+ location = settings.BASE_FOLDER_NAME + '/'
42
+ location += str(self.app_name) + '/'
43
+
44
+ if self.related_field is not None:
45
+ location += str(self.related_field) + '/'
46
+
47
+ location += random_64_char_token() + '/'
48
+ location += self.name
49
+
50
+ return location
39
51
 
40
52
  def null_file_obj(self) -> File:
41
- return File(
42
- file=self.file,
53
+ file_obj = File(
43
54
  name=self.name,
44
55
  size=self.size_verbose(),
45
56
  type=self.type,
46
- related_field=self.related_field
57
+ related_field=self.related_field,
47
58
  )
59
+ self.save_file(file_obj)
60
+
61
+ return file_obj
48
62
 
49
63
  def size_verbose(self) -> str:
50
64
  if self.file.size < 512000:
@@ -62,6 +76,10 @@ class FileFormatter:
62
76
 
63
77
  return str(value) + ext
64
78
 
79
+ def save_file(self, file_obj: File):
80
+ file_path = self.location + '.' + self.type
81
+ file_obj.file.save(file_path, ContentFile(self.file.read()), save=False)
82
+
65
83
 
66
84
  @dataclass
67
85
  class FileContentObjectFormatter(FileFormatter):
@@ -69,26 +87,41 @@ class FileContentObjectFormatter(FileFormatter):
69
87
 
70
88
  @property
71
89
  def location(self) -> str:
72
- return 'django-spire/' + '/' + str(self.content_object._meta.app_label) + '/' + random_64_char_token() + '/' + self.name
90
+ location = settings.BASE_FOLDER_NAME + '/'
91
+ location += str(self.content_object._meta.app_label) + '/'
92
+
93
+ if self.related_field is not None:
94
+ location += str(self.related_field) + '/'
95
+
96
+ location += random_64_char_token() + '/'
97
+ location += self.name
98
+ return location
73
99
 
74
100
  def null_file_obj(self) -> File:
75
- return File(
101
+ file_obj = File(
76
102
  content_type=ContentType.objects.get_for_model(self.content_object),
77
103
  object_id=self.content_object.id,
78
- file=self.file,
79
104
  name=self.name,
80
105
  size=self.size_verbose(),
81
106
  type=self.type,
82
- related_field=self.related_field
107
+ related_field=self.related_field,
83
108
  )
109
+ self.save_file(file_obj)
110
+
111
+ return file_obj
84
112
 
85
113
 
86
114
  @dataclass
87
115
  class FileUploader(ABC):
88
116
  related_field: str | None
117
+ app_name: str = 'Uncategorized'
89
118
 
90
119
  def null_file_obj(self, file):
91
- formatted_file = FileFormatter(file, self.related_field)
120
+ formatted_file = FileFormatter(
121
+ file=file,
122
+ related_field=self.related_field,
123
+ app_name=self.app_name
124
+ )
92
125
  return formatted_file.null_file_obj()
93
126
 
94
127
  @abstractmethod
@@ -7,6 +7,14 @@ class HelpDeskConfig(AppConfig):
7
7
  default_auto_field = 'django.db.models.BigAutoField'
8
8
  label = 'django_spire_help_desk'
9
9
  name = 'django_spire.help_desk'
10
+ MODEL_PERMISSIONS = (
11
+ {
12
+ 'name': 'help_desk',
13
+ 'verbose_name': 'Help Desk',
14
+ 'model_class_path': 'django_spire.help_desk.models.HelpDeskTicket',
15
+ 'is_proxy_model': False,
16
+ },
17
+ )
10
18
 
11
19
  REQUIRED_APPS = ('django_spire_core',)
12
20
  URLPATTERNS_INCLUDE = 'django_spire.help_desk.urls'
File without changes
@@ -0,0 +1,15 @@
1
+ from django_spire.auth.controller.controller import BaseAuthController
2
+
3
+
4
+ class BaseHelpDeskAuthController(BaseAuthController):
5
+ def can_add(self):
6
+ return self.request.user.has_perm('django_spire_help_desk.add_helpdeskticket')
7
+
8
+ def can_change(self):
9
+ return self.request.user.has_perm('django_spire_help_desk.change_helpdeskticket')
10
+
11
+ def can_delete(self):
12
+ return self.request.user.has_perm('django_spire_help_desk.delete_helpdeskticket')
13
+
14
+ def can_view(self):
15
+ return self.request.user.has_perm('django_spire_help_desk.view_helpdeskticket')
@@ -7,15 +7,9 @@
7
7
  {% endblock %}
8
8
 
9
9
  {% block card_button %}
10
- {% if perms.django_spire_help_desk.change_helpdeskticket %}
11
- <div class="pe-3">
12
- {% url 'django_spire:help_desk:form:update' pk=ticket.pk as edit_url %}
13
- {% include 'django_spire/button/primary_button.html' with button_text='Edit' button_href=edit_url button_icon='bi bi-pencil' %}
14
- </div>
15
- {% endif %}
16
- {% if perms.django_spire_help_desk.delete_helpdeskticket %}
17
- {% url 'django_spire:help_desk:page:delete' pk=ticket.pk as delete_url %}
18
- {% include 'django_spire/button/danger_button.html' with button_text='Delete' button_href=delete_url button_icon='bi bi-trash' %}
10
+ {% if AuthController.help_desk.can_change %}
11
+ {% url 'django_spire:help_desk:form:update' pk=ticket.pk as edit_url %}
12
+ {% include 'django_spire/button/primary_button.html' with button_text='Edit' button_href=edit_url button_icon='bi bi-pencil' %}
19
13
  {% endif %}
20
14
  {% endblock %}
21
15
 
@@ -3,17 +3,16 @@
3
3
  {% block card_title_content_style %}{% endblock %}
4
4
 
5
5
  {% block card_title %}
6
- <div class="row justify-content-between">
7
- <div class="col text-start">
8
- Tickets
6
+ Tickets
7
+ {% endblock %}
8
+
9
+ {% block card_button %}
10
+ {% if AuthController.help_desk.can_add %}
11
+ <div class="col text-end mb-1">
12
+ {% url 'django_spire:help_desk:form:create' as create_url %}
13
+ {% include 'django_spire/button/primary_button.html' with button_text='Add' button_href=create_url button_icon="bi bi-plus" %}
9
14
  </div>
10
- {% if perms.django_spire_help_desk.add_helpdeskticket %}
11
- <div class="col text-end mb-1">
12
- {% url 'django_spire:help_desk:form:create' as create_url %}
13
- {% include 'django_spire/button/primary_button.html' with button_text='Add' button_href=create_url button_icon="bi bi-plus" %}
14
- </div>
15
- {% endif %}
16
- </div>
15
+ {% endif %}
17
16
  {% endblock %}
18
17
 
19
18
  {% block card_title_content %}
@@ -0,0 +1,3 @@
1
+ <a href="{% url 'django_spire:help_desk:page:list' %}">
2
+ <i class="bi bi-headset fs-2 mx-2 text-app-default-text-color"></i>
3
+ </a>
@@ -25,7 +25,7 @@
25
25
  <div class="col-12 mb-1 mt-3">
26
26
  {% include 'django_glue/form/field/text_field.html' with glue_model_field='ticket.description' %}
27
27
  </div>
28
- <div class="text-end my-2">
28
+ <div class="my-2">
29
29
  {% include 'django_spire/contrib/form/button/form_submit_button.html' %}
30
30
  </div>
31
31
  </form>
@@ -1,37 +1,38 @@
1
1
  {% extends 'django_spire/item/item.html' %}
2
2
 
3
- {% block item_title_col %}d-none{% endblock %}
3
+ {% block item_title_col %}{% endblock %}
4
4
 
5
5
  {% block item_row_content %}
6
- <a class="col-12 col-sm-4 mb-2 mb-sm-0" href="{% url 'django_spire:help_desk:page:detail' pk=ticket.pk %}">
7
- {% include 'django_spire/element/attribute_element.html' with attribute_title=ticket attribute_value=ticket.description|truncatechars:50 %}
8
- </a>
9
- <div class="col-6 col-sm-2 mb-2 mb-sm-0 text-start">
10
- {% include 'django_spire/element/attribute_element.html' with attribute_value=ticket.created_by.get_full_name attribute_title='Created By' %}
11
- </div>
12
- <div class="col-6 col-sm-2 mb-2 mb-sm-0 text-start">
13
- {% include 'django_spire/element/attribute_element.html' with attribute_value=ticket.created_datetime|date:'M d, Y' attribute_title='Created At' %}
14
- </div>
15
- <div class="col">
16
- {% include 'django_spire/element/attribute_element.html' with attribute_title='Purpose' %}
17
- {% include 'django_spire/help_desk/element/ticket_purpose_badge.html' %}
18
- </div>
19
- <div class="col">
20
- {% include 'django_spire/element/attribute_element.html' with attribute_title='Priority' %}
21
- {% include 'django_spire/help_desk/element/ticket_priority_badge.html' %}
22
- </div>
23
- <div class="col">
24
- {% include 'django_spire/element/attribute_element.html' with attribute_title='Status' %}
25
- {% include 'django_spire/help_desk/element/ticket_status_badge.html' %}
26
- </div>
27
- <div class="col-6 col-sm-1 text-end">
28
- {% url 'django_spire:help_desk:page:detail' pk=ticket.pk as detail_url %}
29
- {% if perms.django_spire_help_desk.change_helpdeskticket %}
30
- {% url 'django_spire:help_desk:form:update' pk=ticket.pk as update_url %}
31
- {% endif %}
32
- {% if perms.django_spire_help_desk.delete_helpdeskticket %}
33
- {% url 'django_spire:help_desk:page:delete' pk=ticket.pk as delete_url %}
34
- {% endif %}
35
- {% include 'django_spire/dropdown/ellipsis_dropdown.html' with view_url=detail_url edit_url=update_url delete_url=delete_url %}
36
- </div>
6
+ <a class="col-12 col-sm-4 mb-2 mb-sm-0" href="{% url 'django_spire:help_desk:page:detail' pk=ticket.pk %}">
7
+ {% include 'django_spire/element/attribute_element.html' with attribute_value=ticket.description|truncatechars:50 %}
8
+ </a>
9
+ <div class="col-6 col-sm-2 mb-2 mb-sm-0 text-start">
10
+ {% include 'django_spire/element/attribute_element.html' with attribute_value=ticket.created_by.get_full_name attribute_title='Created By' %}
11
+ </div>
12
+ <div class="col-6 col-sm-2 mb-2 mb-sm-0 text-start">
13
+ {% include 'django_spire/element/attribute_element.html' with attribute_value=ticket.created_datetime|date:'M d, Y' attribute_title='Created At' %}
14
+ </div>
15
+ <div class="col">
16
+ {% include 'django_spire/element/attribute_element.html' with attribute_title='Purpose' %}
17
+ {% include 'django_spire/help_desk/element/ticket_purpose_badge.html' %}
18
+ </div>
19
+ <div class="col">
20
+ {% include 'django_spire/element/attribute_element.html' with attribute_title='Priority' %}
21
+ {% include 'django_spire/help_desk/element/ticket_priority_badge.html' %}
22
+ </div>
23
+ <div class="col">
24
+ {% include 'django_spire/element/attribute_element.html' with attribute_title='Status' %}
25
+ {% include 'django_spire/help_desk/element/ticket_status_badge.html' %}
26
+ </div>
27
+ {% endblock %}
28
+
29
+ {% block item_button %}
30
+ {% url 'django_spire:help_desk:page:detail' pk=ticket.pk as detail_url %}
31
+ {% if AuthController.help_desk.can_change %}
32
+ {% url 'django_spire:help_desk:form:update' pk=ticket.pk as update_url %}
33
+ {% endif %}
34
+ {% if AuthController.help_desk.can_delete %}
35
+ {% url 'django_spire:help_desk:page:delete' pk=ticket.pk as delete_url %}
36
+ {% endif %}
37
+ {% include 'django_spire/dropdown/ellipsis_dropdown.html' with view_url=detail_url edit_url=update_url delete_url=delete_url %}
37
38
  {% endblock %}
@@ -2,14 +2,14 @@ import django_glue as dg
2
2
  from django.shortcuts import redirect, get_object_or_404
3
3
  from django.urls import reverse
4
4
 
5
- from django_spire.auth.permissions.decorators import permission_required
5
+ from django_spire.auth.controller.controller import AppAuthController
6
6
  from django_spire.contrib.form.utils import show_form_errors
7
7
  from django_spire.contrib.generic_views import portal_views
8
8
  from django_spire.help_desk import forms
9
9
  from django_spire.help_desk.models import HelpDeskTicket
10
10
 
11
11
 
12
- @permission_required('django_spire_help_desk.add_helpdeskticket')
12
+ @AppAuthController('help_desk').permission_required('can_add')
13
13
  def ticket_create_form_view(request):
14
14
  ticket = HelpDeskTicket()
15
15
 
@@ -42,7 +42,7 @@ def ticket_create_form_view(request):
42
42
  )
43
43
 
44
44
 
45
- @permission_required('django_spire_help_desk.change_helpdeskticket')
45
+ @AppAuthController('help_desk').permission_required('can_change')
46
46
  def ticket_update_form_view(request, pk: int):
47
47
  ticket = get_object_or_404(HelpDeskTicket, pk=pk)
48
48
 
@@ -1,13 +1,12 @@
1
- from django.contrib.auth.decorators import login_required
2
1
  from django.shortcuts import get_object_or_404
3
2
  from django.urls import reverse
4
3
 
5
- from django_spire.auth.permissions.decorators import permission_required
4
+ from django_spire.auth.controller.controller import AppAuthController
6
5
  from django_spire.contrib.generic_views import portal_views
7
6
  from django_spire.help_desk.models import HelpDeskTicket
8
7
 
9
8
 
10
- @permission_required('django_spire_help_desk.delete_helpdeskticket')
9
+ @AppAuthController('help_desk').permission_required('can_delete')
11
10
  def ticket_delete_view(request, pk):
12
11
  ticket = get_object_or_404(HelpDeskTicket, pk=pk)
13
12
 
@@ -21,7 +20,7 @@ def ticket_delete_view(request, pk):
21
20
  )
22
21
 
23
22
 
24
- @login_required()
23
+ @AppAuthController('help_desk').permission_required('can_view')
25
24
  def ticket_detail_view(request, pk):
26
25
  ticket = get_object_or_404(HelpDeskTicket, pk=pk)
27
26
 
@@ -35,7 +34,7 @@ def ticket_detail_view(request, pk):
35
34
  )
36
35
 
37
36
 
38
- @login_required()
37
+ @AppAuthController('help_desk').permission_required('can_view')
39
38
  def ticket_list_view(request):
40
39
  tickets = HelpDeskTicket.objects.order_by('-created_datetime').active()
41
40
 
@@ -35,11 +35,9 @@ class EntryAutomationService(BaseDjangoModelService['Entry']):
35
35
  )
36
36
  except Exception as e:
37
37
  errored.append({'file': file_object.name, 'error': str(e)})
38
- file_object.file.delete()
39
- file_object.delete()
38
+ file_object.set_deleted()
40
39
  else:
41
- file_object.file.delete()
42
- file_object.delete()
40
+ file_object.set_deleted()
43
41
 
44
42
  message = f'Files Converted: {len(file_objects) - len(errored)}'
45
43
  if errored:
@@ -7,7 +7,6 @@ from django.contrib.contenttypes.models import ContentType
7
7
  from django_spire.auth.user.models import AuthUser
8
8
  from django_spire.contrib.service import BaseDjangoModelService
9
9
  from django_spire.file.models import File
10
- from django_spire.knowledge.entry.tests.constants import ENTRY_IMPORT_RELATED_FIELD
11
10
 
12
11
  if TYPE_CHECKING:
13
12
  from django_spire.knowledge.entry.models import Entry
@@ -34,7 +33,7 @@ class EntryFactoryService(BaseDjangoModelService['Entry']):
34
33
 
35
34
  file.content_type = ContentType.objects.get_for_model(entry.__class__)
36
35
  file.object_id = entry.id
37
- file.related_field = ENTRY_IMPORT_RELATED_FIELD
36
+ file.related_field = None
38
37
 
39
38
  entry.ordering_services.processor.move_to_position(
40
39
  destination_objects=collection.entries.active(),
@@ -7,7 +7,6 @@ from django.contrib.contenttypes.models import ContentType
7
7
 
8
8
  from django_spire.contrib.service import BaseDjangoModelService
9
9
  from django_spire.file.models import File
10
- from django_spire.knowledge.entry.tests.constants import ENTRY_IMPORT_RELATED_FIELD
11
10
 
12
11
  if TYPE_CHECKING:
13
12
  from django.db.models import QuerySet
@@ -19,7 +18,7 @@ class EntryToolService(BaseDjangoModelService['Entry']):
19
18
 
20
19
  def get_files_to_convert(self) -> QuerySet[File]:
21
20
  return (
22
- File.objects.related_field(field_name=ENTRY_IMPORT_RELATED_FIELD)
21
+ File.objects
23
22
  .filter(content_type=ContentType.objects.get_for_model(self.obj_class))
24
23
  .active()
25
24
  .order_by('object_id')
@@ -29,7 +29,7 @@ class DocxConverter(BaseConverter):
29
29
 
30
30
  def convert_file_to_blocks(self, file: File) -> list[models.EntryVersionBlock]:
31
31
  markitdown = MarkItDown()
32
- markdown_result = markitdown.convert(file.file.path)
32
+ markdown_result = markitdown.convert(file.file.url)
33
33
  markdown_content = markdown_result.markdown
34
34
 
35
35
  markdown_converter = MarkdownConverter(entry_version=self.entry_version)
@@ -7,6 +7,7 @@ import marko
7
7
 
8
8
  from typing import TYPE_CHECKING
9
9
 
10
+ from django.core.files.storage import default_storage
10
11
  from marko.element import Element
11
12
  from marko.block import Heading, List, ListItem
12
13
 
@@ -31,7 +32,7 @@ class MarkdownConverter(BaseConverter):
31
32
  self._order = 0
32
33
 
33
34
  def convert_file_to_blocks(self, file: File) -> list[models.EntryVersionBlock]:
34
- with open(file.file.path, 'r') as f:
35
+ with default_storage.open(file.file.name, 'r') as f:
35
36
  return self.convert_markdown_to_blocks(f.read())
36
37
 
37
38
  def _convert_heading_block(
@@ -84,9 +84,11 @@ def import_form_view(
84
84
  file_form = EntryFilesForm(request.POST, request.FILES)
85
85
 
86
86
  if file_form.is_valid():
87
- file_objects = MultiFileUploader(related_field=None).upload(
88
- request.FILES.getlist('import_files')
87
+ file_uploader = MultiFileUploader(
88
+ related_field=None,
89
+ app_name='knowledge'
89
90
  )
91
+ file_objects = file_uploader.upload(request.FILES.getlist('import_files'))
90
92
 
91
93
  _ = Entry.services.factory.create_from_files(
92
94
  author=request.user,
@@ -41,7 +41,7 @@ def reorder_view(request: WSGIRequest) -> JsonResponse:
41
41
  destination_objects=(
42
42
  collection.entries
43
43
  .has_current_version()
44
- .request_user_has_access(user=request.user)
44
+ .user_has_access(user=request.user)
45
45
  .active()
46
46
  ),
47
47
  position=order,
@@ -7,23 +7,23 @@
7
7
  top: y + 'px',
8
8
  left: x + 'px',
9
9
  }"
10
- class="position-fixed card shadow-lg z-3 vstack cursor-pointer bg-app-layer-one"
10
+ class="position-fixed card shadow-lg z-3 vstack cursor-pointer bg-app-layer-one border"
11
11
  @click.away="hide_menu()"
12
12
  >
13
13
  <span class="px-2 py-1" x-text="title"></span>
14
14
 
15
15
  {% if AuthController.knowledge.can_change %}
16
- <span class="border-top bg-app-layer-one-hover px-3 text-app-default-link-color" @click="edit_collection()">
16
+ <span class="border-top bg-app-layer-three-hover px-3 text-app-default-link-color dropdown-item" @click="edit_collection()">
17
17
  <i class="bi bi-pencil pe-1"></i>
18
18
  Edit Collection
19
19
  </span>
20
20
  {% endif %}
21
21
  {% if AuthController.knowledge.can_add %}
22
- <span class="border-top bg-app-layer-one-hover px-3 text-app-default-link-color" @click="add_entry()">
22
+ <span class="bg-app-layer-three-hover px-3 text-app-default-link-color dropdown-item" @click="add_entry()">
23
23
  <i class="bi bi-plus pe-1"></i>
24
24
  Add Entry
25
25
  </span>
26
- <span class="border-top bg-app-layer-one-hover px-3 text-app-default-link-color" @click="import_entry()">
26
+ <span class="bg-app-layer-three-hover px-3 text-app-default-link-color dropdown-item" @click="import_entry()">
27
27
  <i class="bi bi-upload pe-1">
28
28
  </i>
29
29
  Import Entry
@@ -31,7 +31,7 @@
31
31
  {% endif %}
32
32
 
33
33
  {% if AuthController.knowledge.can_delete %}
34
- <span class="border-top bg-app-layer-one-hover px-3 text-app-danger" @click="delete_collection()">
34
+ <span class="bg-app-layer-three-hover px-3 text-app-danger dropdown-item" @click="delete_collection()">
35
35
  <i class="bi bi-trash3 pe-1"></i>
36
36
  <span>Delete Collection</span>
37
37
  </span>
@@ -7,31 +7,31 @@
7
7
  top: y + 'px',
8
8
  left: x + 'px',
9
9
  }"
10
- class="position-fixed card shadow-lg z-3 vstack cursor-pointer bg-app-layer-one"
10
+ class="position-fixed card shadow-lg z-3 vstack cursor-pointer bg-app-layer-one border"
11
11
  @click.away="hide_menu()"
12
12
  >
13
13
  <span class="px-2 py-1" x-text="title"></span>
14
14
 
15
15
  {% if AuthController.knowledge.can_view %}
16
- <span class="border-top bg-app-layer-three-hover px-3 text-app-default-link-color" @click="view_entry()">
16
+ <span class="border-top bg-app-layer-three-hover px-3 text-app-default-link-color dropdown-item" @click="view_entry()">
17
17
  <i class="bi bi-eye pe-1"></i>
18
18
  <span>View</span>
19
19
  </span>
20
20
  {% endif %}
21
21
 
22
22
  {% if AuthController.knowledge.can_change %}
23
- <span class="border-top bg-app-layer-three-hover px-3 text-app-default-link-color" @click="edit_entry()">
23
+ <span class="bg-app-layer-three-hover px-3 text-app-default-link-color dropdown-item" @click="edit_entry()">
24
24
  <i class="bi bi-input-cursor-text pe-1"></i>
25
25
  <span>Rename</span>
26
26
  </span>
27
- <span class="border-top bg-app-layer-three-hover px-3 text-app-default-link-color" @click="edit_entry_version()">
27
+ <span class="bg-app-layer-three-hover px-3 text-app-default-link-color dropdown-item" @click="edit_entry_version()">
28
28
  <i class="bi bi-pencil pe-1"></i>
29
29
  <span>Edit</span>
30
30
  </span>
31
31
  {% endif %}
32
32
 
33
33
  {% if AuthController.knowledge.can_delete %}
34
- <span class="border-top bg-app-layer-three-hover px-3 text-app-danger" @click="delete_entry()">
34
+ <span class="bg-app-layer-three-hover px-3 text-app-danger dropdown-item" @click="delete_entry()">
35
35
  <i class="bi bi-trash3 pe-1"></i>
36
36
  <span>Delete</span>
37
37
  </span>
@@ -12,6 +12,7 @@
12
12
  {% block dropdown_content %}
13
13
  {% for block_type in block_types %}
14
14
  <div
15
+ class="bg-app-layer-one bg-app-layer-three-hover dropdown-item"
15
16
  x-data="{
16
17
  create_block() {
17
18
  create_blank_block({block_type: '{{ block_type.value }}', order: version_block.order + 1})
@@ -32,7 +32,7 @@
32
32
  class="position-relative">
33
33
  <span @click='await render_dropdown()'>
34
34
  {% block notification_dropdown_trigger %}
35
- <i x-bind='trigger' class="bi bi-bell-fill text-white fs-4 cursor-pointer position-absolute"></i>
35
+ <i x-bind='trigger' class="bi bi-bell-fill fs-4 cursor-pointer position-absolute"></i>
36
36
  <span x-show="new_notification" x-cloak style="width: 10px; height: 10px; top: -1px; right: -24px;" class="bg-danger rounded-circle position-absolute"></span>
37
37
  {% endblock %}
38
38
  </span>
@@ -2,14 +2,17 @@
2
2
  :style="window.innerWidth < 768
3
3
  ? 'width: 350px; position: fixed; top: 5%; left: 50%; transform: translateX(-50%);'
4
4
  : 'width: 350px'"
5
- class="container border rounded-2"
5
+ class="container border rounded-2 bg-app-layer-two"
6
6
  >
7
7
  <div class="col-12 w-100">
8
- <h6 class="mt-2 ms-2 text-app-secondary text-center">Notifications</h6>
8
+ <h6 class="mt-2 ms-2 text-center">Notifications</h6>
9
9
  <hr class="m-0">
10
10
  </div>
11
11
 
12
- <div style="max-height: 300px !important; overflow-x: hidden !important; overflow-y: scroll !important;" class="text-start">
12
+ <div
13
+ style="max-height: 300px !important;"
14
+ class="text-start overflow-y-scroll overflow-x-hidden"
15
+ >
13
16
  {% for app_notification in app_notification_list %}
14
17
  <div class="col-12">
15
18
  {% include app_notification.template %}
django_spire/settings.py CHANGED
@@ -1,5 +1,6 @@
1
1
  DJANGO_SPIRE_AUTH_CONTROLLERS = {
2
2
  'ai_chat': 'django_spire.ai.chat.auth.controller.BaseAiChatAuthController',
3
+ 'help_desk': 'django_spire.help_desk.auth.controller.BaseHelpDeskAuthController',
3
4
  'knowledge': 'django_spire.knowledge.auth.controller.BaseKnowledgeAuthController',
4
5
  }
5
6
 
@@ -22,19 +22,19 @@
22
22
  <div class="preview-sample">
23
23
  <div class="d-flex flex-wrap">
24
24
  <!-- Primary colors -->
25
- <div class="p-2 m-1 bg-app-primary text-white" style="width: 40px; height: 40px;"></div>
26
- <div class="p-2 m-1 bg-app-primary-soft text-dark" style="width: 40px; height: 40px;"></div>
27
- <div class="p-2 m-1 bg-app-primary-dark text-white" style="width: 40px; height: 40px;"></div>
25
+ <div class="p-2 m-1 bg-app-primary" style="width: 40px; height: 40px;"></div>
26
+ <div class="p-2 m-1 bg-app-primary-soft" style="width: 40px; height: 40px;"></div>
27
+ <div class="p-2 m-1 bg-app-primary-dark" style="width: 40px; height: 40px;"></div>
28
28
 
29
29
  <!-- Secondary colors -->
30
- <div class="p-2 m-1 bg-app-secondary text-white" style="width: 40px; height: 40px;"></div>
31
- <div class="p-2 m-1 bg-app-secondary-soft text-dark" style="width: 40px; height: 40px;"></div>
32
- <div class="p-2 m-1 bg-app-secondary-dark text-white" style="width: 40px; height: 40px;"></div>
30
+ <div class="p-2 m-1 bg-app-secondary" style="width: 40px; height: 40px;"></div>
31
+ <div class="p-2 m-1 bg-app-secondary-soft" style="width: 40px; height: 40px;"></div>
32
+ <div class="p-2 m-1 bg-app-secondary-dark" style="width: 40px; height: 40px;"></div>
33
33
 
34
34
  <!-- Accent colors -->
35
- <div class="p-2 m-1 bg-app-accent text-white" style="width: 40px; height: 40px;"></div>
36
- <div class="p-2 m-1 bg-app-accent-soft text-dark" style="width: 40px; height: 40px;"></div>
37
- <div class="p-2 m-1 bg-app-accent-dark text-white" style="width: 40px; height: 40px;"></div>
35
+ <div class="p-2 m-1 bg-app-accent" style="width: 40px; height: 40px;"></div>
36
+ <div class="p-2 m-1 bg-app-accent-soft" style="width: 40px; height: 40px;"></div>
37
+ <div class="p-2 m-1 bg-app-accent-dark" style="width: 40px; height: 40px;"></div>
38
38
  </div>
39
39
  </div>
40
40
  {% endblock %}
@@ -24,11 +24,11 @@
24
24
  </div>
25
25
  <div class="col text-center">
26
26
  {% include 'django_spire/element/attribute_element.html' with attribute_title='Dropdown' %}
27
- {% include 'django_spire/dropdown/ellipsis_dropdown.html' with view_url='#' edit_url='#' delete_url='#' %}
27
+ {% include 'django_spire/dropdown/ellipsis_dropdown.html' with view_url='#' edit_url='#' delete_url='#' link_css='text-start' %}
28
28
  </div>
29
29
  <div class="col text-center">
30
30
  {% include 'django_spire/element/attribute_element.html' with attribute_title='Modal' %}
31
- {% include 'django_spire/dropdown/ellipsis_modal_dropdown.html' with view_url='#' edit_url='#' delete_url='#' %}
31
+ {% include 'django_spire/dropdown/ellipsis_modal_dropdown.html' with view_url='#' edit_url='#' delete_url='#' link_css='text-start' %}
32
32
  </div>
33
33
  </div>
34
34
  {% endblock %}