Skip to content

Commit eee6258

Browse files
authored
feat: Add abstract base model AbstractFrontendUIItem (#195)
1 parent c16c4bf commit eee6258

File tree

7 files changed

+95
-18
lines changed

7 files changed

+95
-18
lines changed

djangocms_frontend/contrib/icon/cms_plugins.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,15 @@ class IconPlugin(
3131
model = models.Icon
3232
form = forms.IconForm
3333
text_enabled = True
34-
34+
text_icon = (
35+
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" '
36+
'class="bi bi-emoji-sunglasses" viewBox="0 0 16 16"><path d="M4.968 9.75a.5.5 0 1 0-.866.5A4.5 4.5 '
37+
'0 0 0 8 12.5a4.5 4.5 0 0 0 3.898-2.25.5.5 0 1 0-.866-.5A3.5 3.5 0 0 1 8 11.5a3.5 3.5 0 0 1-3.032-1.75M7 '
38+
'5.116V5a1 1 0 0 0-1-1H3.28a1 1 0 0 0-.97 1.243l.311 1.242A2 2 0 0 0 4.561 8H5a2 2 0 0 0 1.994-1.839A3 '
39+
'3 0 0 1 8 6c.393 0 .74.064 1.006.161A2 2 0 0 0 11 8h.438a2 2 0 0 0 1.94-1.515l.311-1.242A1 1 0 0 0 '
40+
'12.72 4H10a1 1 0 0 0-1 1v.116A4.2 4.2 0 0 0 8 5c-.35 0-.69.04-1 .116"/><path d="M16 8A8 8 0 1 1 0 8a8 8 '
41+
'0 0 1 16 0m-1 0A7 7 0 1 0 1 8a7 7 0 0 0 14 0"/></svg>'
42+
)
3543
fieldsets = [
3644
(
3745
None,

djangocms_frontend/contrib/image/cms_plugins.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ class ImagePlugin(
3434
form = forms.ImageForm
3535

3636
text_enabled = True
37+
text_icon = (
38+
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-image" '
39+
'viewBox="0 0 16 16"><path d="M6.002 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0"/>'
40+
'<path d="M2.002 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2zm12 1a1 1 0 0 1 1 '
41+
'1v6.5l-3.777-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12V3a1 1 0 0 1 '
42+
'1-1z"/></svg>'
43+
)
3744

3845
change_form_template = "djangocms_frontend/admin/image.html"
3946

djangocms_frontend/contrib/link/cms_plugins.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ class LinkPlugin(
100100
form = forms.LinkForm
101101
change_form_template = "djangocms_frontend/admin/link.html"
102102
text_enabled = True
103+
text_icon = (
104+
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-link-45deg" '
105+
'viewBox="0 0 16 16"><path d="M4.715 6.542 3.343 7.914a3 3 0 1 0 4.243 4.243l1.828-1.829A3 3 0 0 0 8.586 '
106+
'5.5L8 6.086a1 1 0 0 0-.154.199 2 2 0 0 1 .861 3.337L6.88 11.45a2 2 0 1 1-2.83-2.83l.793-.792a4 4 0 0 '
107+
'1-.128-1.287z"/><path d="M6.586 4.672A3 3 0 0 0 7.414 9.5l.775-.776a2 2 0 0 1-.896-3.346L9.12 3.55a2 2 0 '
108+
'1 1 2.83 2.83l-.793.792c.112.42.155.855.128 1.287l1.372-1.372a3 3 0 1 0-4.243-4.243z"/></svg>'
109+
)
103110
allow_children = True
104111

105112
fieldsets = UILINK_FIELDSET

djangocms_frontend/contrib/link/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ def __init__(self, *args, **kwargs):
224224
def get_choices(self):
225225
if MINIMUM_INPUT_LENGTH == 0:
226226
return get_choices(self.request)
227-
if not self.is_bound: # find inital value
227+
if not self.is_bound: # find initial value
228228
int_link_field = self.fields["internal_link"]
229229
initial = self.get_initial_for_field(int_link_field, "internal_link")
230230
if initial: # Initial set?

djangocms_frontend/contrib/link/models.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def get_link(self):
5353
cms_page = self.placeholder.page if self.placeholder_id else None
5454

5555
# first, we check if the placeholder the plugin is attached to
56-
# has a page. Thus the check "is not None":
56+
# has a page. Thus, the check "is not None":
5757
if cms_page is not None:
5858
if getattr(cms_page, "node", None):
5959
cms_page_site_id = getattr(cms_page.node, "site_id", None)
@@ -67,9 +67,9 @@ def get_link(self):
6767

6868
# now we do the same for the reference page the plugin links to
6969
# in order to compare them later
70-
if getattr(ref_page, "node", None) and cms_page is not None:
70+
if getattr(ref_page, "node", None):
7171
ref_page_site_id = ref_page.node.site_id
72-
elif getattr(ref_page, "site_id", None) and cms_page is not None:
72+
elif getattr(ref_page, "site_id", None):
7373
ref_page_site_id = ref_page.site_id
7474
# if no external reference is found the plugin links to the
7575
# current page

djangocms_frontend/models.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,35 @@
1414
JSONField = models.JSONField
1515

1616

17-
class FrontendUIItem(CMSPlugin):
17+
class AbstractFrontendUIItem(CMSPlugin):
1818
"""
19-
Generic plugin model to store all frontend items. Plugin-specific information is stored in a JSON field
20-
called "config".
19+
The `AbstractFrontendUIItem` class is an abstract base class that provides common functionality
20+
for frontend UI items in a CMS plugin. It is a subclass of `CMSPlugin` class.
21+
22+
Use this class as a base class for custom plugins that add their own database fields.
23+
24+
Attributes:
25+
- ui_item: A CharField that represents the UI item name (max length 30).
26+
- tag_type: A TagTypeField (custom field) that represents the type of HTML tag for the UI item.
27+
- config: A JSONField that stores additional configuration for the UI item.
28+
29+
Methods:
30+
- __init__(*args, **kwargs): Constructor method that initializes the object and sets additional classes.
31+
- __getattr__(item): Allows properties of the plugin config to be accessed as plugin properties.
32+
- __str__(): Returns a string representation of the UI item.
33+
- add_classes(*args): Adds additional classes to the UI item.
34+
- add_attribute(attr, value=None): Adds an attribute to the configuration attributes.
35+
- get_attributes(): Returns the attributes as a string for rendering the UI item.
36+
- save(*args, **kwargs): Saves the UI item to the database.
37+
- initialize_from_form(form=None): Populates the config JSON field based on initial values from a form.
38+
- get_short_description(): Returns a plugin-specific short description.
39+
- framework_info: Returns the framework information for the UI item.
40+
41+
Note: This is an abstract base class and should not be used directly.
2142
"""
2243

2344
class Meta:
45+
abstract = True
2446
verbose_name = gettext("UI item")
2547

2648
ui_item = models.CharField(max_length=30)
@@ -95,3 +117,28 @@ def get_short_description(self):
95117
@property
96118
def framework_info(self):
97119
return FRAMEWORK_PLUGIN_INFO.get(self.__class__.__name__, None)
120+
121+
122+
class FrontendUIItem(AbstractFrontendUIItem):
123+
"""
124+
125+
Class: FrontendUIItem
126+
127+
Inherits From: AbstractFrontendUIItem
128+
129+
Description:
130+
This class represents a UI item in the frontend. It is used to define the behavior and attributes
131+
of a UI item in the user interface.
132+
133+
Use this class as a base class for custom plugins that do not add their own database fields but
134+
use the entangled form fields instead.
135+
136+
Attributes:
137+
- verbose_name (str): The verbose name of the UI item.
138+
139+
Methods:
140+
None
141+
142+
"""
143+
class Meta:
144+
verbose_name = gettext("UI item")

docs/source/how-to/add-frontend-plugins.rst

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ How to add your own frontend plugin
44
Creating the plugin
55
-------------------
66

7-
To add custom plugins to Django CMS Frontend, you can follow these steps with in
8-
your Django app:
7+
In order to integrate your custom plugins with Django CMS Frontend within your Django app, you should follow these steps.
98

10-
1. **Define a Plugin Model**: In ``models.py`` of your new app, define a model for your
11-
plugin. Data will be stored in a JSON field of the ``FrontendUIItem`` class. The
12-
model will in many cases not do much, except defining a short description of
13-
an instance.
9+
1. **Define a Plugin Model**:As the first step, you need to define a model for your plugin. This is done in the `models.py` of your Django app. The Plugin model needs to be a **proxy model** of the Django CMS Frontend's `FrontendUIItem` class.
10+
11+
The plugin model defines what kind information will be associated with instances of your plugin.
12+
13+
Here is an example of a hypothetical plugin model:
1414

1515
.. code-block:: python
1616
@@ -20,16 +20,17 @@ your Django app:
2020
2121
class YourPluginModel(FrontendUIItem):
2222
class Meta:
23-
proxy = True # Only a proxy model, if NO new fields are added
23+
proxy = True # MUST be a proxy model
2424
verbose_name = _("Your Plugin")
2525
2626
def short_description(self):
2727
return f"'{self.field_name}'"
2828
29+
In this example, the `YourPluginModel` is a proxy of the `FrontendUIItem`, which is the base class for all Django CMS Frontend plugins. It includes a short description method that provides a description for each instance of the plugin.
30+
2931
.. note::
3032

31-
When adding new fields to the model, you need to remove the ``proxy = True``
32-
statement in the model's ``Meta`` class.
33+
Keep in mind proxy models don't allow adding fields to the model. If your plugin needs to include additional fields, consider using ``AbstractFrontendUIItem`` as the base class and remove ``proxy = True`` from the Meta class.
3334

3435
2. **Define a Plugin Form**: This form will declare which data to store in the
3536
``FrontendUIItem``'s JSON field. The ``EntangledModelForm`` will automatically
@@ -40,6 +41,10 @@ your Django app:
4041

4142
It will be used in the frontend to create and edit plugin instances.
4243

44+
2. **Define a Plugin Form**: You should also define a form that will instruct Django on how to handle the input for creating and editing instances of your plugin. The form should specify which data will be stored in the `FrontendUIItem`'s JSON field.
45+
46+
Here is an example of a form for the `YourPluginModel`:
47+
4348
.. code-block:: python
4449
4550
# forms.py
@@ -59,7 +64,7 @@ your Django app:
5964
field_name = forms.CharField(max_length=50)
6065
6166
3. **Create a Plugin Class**: In the same app, create a file named ``cms_plugins.py``.
62-
Inside this file, define a class for your plugin by extending `CMSPluginBase`.
67+
Inside this file, define a class for your plugin by extending ``CMSPluginBase``.
6368

6469
.. code-block:: python
6570
@@ -110,6 +115,8 @@ Remember, developing custom plugins requires a good understanding of Django's an
110115
CMS's architecture. Additionally, consider the security implications of your plugin,
111116
especially if it handles user input.
112117

118+
119+
113120
Extending the plugin
114121
--------------------
115122

@@ -181,3 +188,4 @@ and images in your plugin. These mixins are:
181188
class YourPluginModel(ImageMixin, FrontendUIItem):
182189
image_field = "image" # The name of the image field in the config JSON
183190
...
191+

0 commit comments

Comments
 (0)