Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add abstract base model AbstractFrontendUIItem #195

Merged
merged 36 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4245adc
Fix: Update locales and add floating options for images
fsbraun Nov 7, 2023
e66e618
Merge branch 'master' of github.com:fsbraun/djangocms-frontend
fsbraun Nov 7, 2023
cbaccdd
Merge branch 'master' of github.com:fsbraun/djangocms-frontend
fsbraun Nov 28, 2023
66e053e
fix: icons not showing in ckeditor
fsbraun Nov 28, 2023
fec0ece
Merge branch 'django-cms:master' into master
fsbraun Nov 28, 2023
49e4acc
Merge branch 'django-cms:master' into master
fsbraun Nov 28, 2023
a9ead97
Add image drag and drop
fsbraun Nov 28, 2023
9ac7126
Merge branch 'master' of github.com:fsbraun/djangocms-frontend
fsbraun Nov 28, 2023
f5f27b6
Update docs
fsbraun Nov 28, 2023
6d5fac4
Add tests
fsbraun Nov 28, 2023
18d60ab
Fix tests for v4
fsbraun Nov 28, 2023
44d5035
Bump version
fsbraun Nov 28, 2023
210cccd
Update changelog
fsbraun Nov 28, 2023
510494e
Merge branch 'django-cms:master' into master
fsbraun Nov 28, 2023
297d436
Merge branch 'django-cms:master' into master
fsbraun Dec 13, 2023
fc20a5a
Merge branch 'django-cms:master' into master
fsbraun Dec 15, 2023
606cc91
fix/remove-unused-css
fsbraun Dec 15, 2023
1af2ef9
Merge branch 'django-cms:master' into master
fsbraun Jan 4, 2024
60f810f
Merge branch 'django-cms:master' into master
fsbraun Jan 8, 2024
05a0939
Merge branch 'django-cms:master' into master
fsbraun Jan 8, 2024
a8e5f2d
Doc typos
fsbraun Jan 8, 2024
c7c8bcf
Merge branch 'django-cms:master' into master
fsbraun Jan 9, 2024
ad90c57
Fix: Missing space in auto column short description
fsbraun Jan 11, 2024
d2a2e78
Merge branch 'master' of github.com:fsbraun/djangocms-frontend
fsbraun Jan 11, 2024
41e58bc
Merge branch 'master' into master
fsbraun Jan 11, 2024
ef87c91
Unlist removed iconsets from the docs
fsbraun Jan 12, 2024
8d63c99
Merge branch 'django-cms:master' into master
fsbraun Jan 12, 2024
a165dcd
Merge branch 'master' into master
fsbraun Jan 13, 2024
c7e34a2
Merge branch 'django-cms:master' into master
fsbraun Feb 14, 2024
aa0aff9
Merge branch 'django-cms:master' into master
fsbraun Feb 16, 2024
1017581
Merge branch 'django-cms:master' into master
fsbraun Feb 28, 2024
87e4b51
Add abstract base model
fsbraun Mar 7, 2024
f91de87
Update docs
fsbraun Mar 7, 2024
507dd25
Fix #196
fsbraun Mar 14, 2024
784d676
Add icons for selected text-enabled plugins
fsbraun Mar 21, 2024
0e7fd74
Merge branch 'master' into feat/abstract-frontenduiitem
fsbraun Mar 21, 2024
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
10 changes: 9 additions & 1 deletion djangocms_frontend/contrib/icon/cms_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ class IconPlugin(
model = models.Icon
form = forms.IconForm
text_enabled = True

text_icon = (
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" '
'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 '
'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 '
'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 '
'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 '
'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 '
'0 0 1 16 0m-1 0A7 7 0 1 0 1 8a7 7 0 0 0 14 0"/></svg>'
)
fieldsets = [
(
None,
Expand Down
7 changes: 7 additions & 0 deletions djangocms_frontend/contrib/image/cms_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ class ImagePlugin(
form = forms.ImageForm

text_enabled = True
text_icon = (
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-image" '
'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"/>'
'<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 '
'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 '
'1-1z"/></svg>'
)

change_form_template = "djangocms_frontend/admin/image.html"

Expand Down
7 changes: 7 additions & 0 deletions djangocms_frontend/contrib/link/cms_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ class LinkPlugin(
form = forms.LinkForm
change_form_template = "djangocms_frontend/admin/link.html"
text_enabled = True
text_icon = (
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-link-45deg" '
'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 '
'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 '
'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 '
'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>'
)
allow_children = True

fieldsets = UILINK_FIELDSET
Expand Down
2 changes: 1 addition & 1 deletion djangocms_frontend/contrib/link/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ def __init__(self, *args, **kwargs):
def get_choices(self):
if MINIMUM_INPUT_LENGTH == 0:
return get_choices(self.request)
if not self.is_bound: # find inital value
if not self.is_bound: # find initial value
int_link_field = self.fields["internal_link"]
initial = self.get_initial_for_field(int_link_field, "internal_link")
if initial: # Initial set?
Expand Down
6 changes: 3 additions & 3 deletions djangocms_frontend/contrib/link/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def get_link(self):
cms_page = self.placeholder.page if self.placeholder_id else None

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

# now we do the same for the reference page the plugin links to
# in order to compare them later
if getattr(ref_page, "node", None) and cms_page is not None:
if getattr(ref_page, "node", None):
ref_page_site_id = ref_page.node.site_id
elif getattr(ref_page, "site_id", None) and cms_page is not None:
elif getattr(ref_page, "site_id", None):
ref_page_site_id = ref_page.site_id
# if no external reference is found the plugin links to the
# current page
Expand Down
53 changes: 50 additions & 3 deletions djangocms_frontend/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,35 @@
JSONField = models.JSONField


class FrontendUIItem(CMSPlugin):
class AbstractFrontendUIItem(CMSPlugin):
"""
Generic plugin model to store all frontend items. Plugin-specific information is stored in a JSON field
called "config".
The `AbstractFrontendUIItem` class is an abstract base class that provides common functionality
for frontend UI items in a CMS plugin. It is a subclass of `CMSPlugin` class.

Use this class as a base class for custom plugins that add their own database fields.

Attributes:
- ui_item: A CharField that represents the UI item name (max length 30).
- tag_type: A TagTypeField (custom field) that represents the type of HTML tag for the UI item.
- config: A JSONField that stores additional configuration for the UI item.

Methods:
- __init__(*args, **kwargs): Constructor method that initializes the object and sets additional classes.
- __getattr__(item): Allows properties of the plugin config to be accessed as plugin properties.
- __str__(): Returns a string representation of the UI item.
- add_classes(*args): Adds additional classes to the UI item.
- add_attribute(attr, value=None): Adds an attribute to the configuration attributes.
- get_attributes(): Returns the attributes as a string for rendering the UI item.
- save(*args, **kwargs): Saves the UI item to the database.
- initialize_from_form(form=None): Populates the config JSON field based on initial values from a form.
- get_short_description(): Returns a plugin-specific short description.
- framework_info: Returns the framework information for the UI item.

Note: This is an abstract base class and should not be used directly.
"""

class Meta:
abstract = True
verbose_name = gettext("UI item")

ui_item = models.CharField(max_length=30)
Expand Down Expand Up @@ -95,3 +117,28 @@ def get_short_description(self):
@property
def framework_info(self):
return FRAMEWORK_PLUGIN_INFO.get(self.__class__.__name__, None)


class FrontendUIItem(AbstractFrontendUIItem):
"""

Class: FrontendUIItem

Inherits From: AbstractFrontendUIItem

Description:
This class represents a UI item in the frontend. It is used to define the behavior and attributes
of a UI item in the user interface.

Use this class as a base class for custom plugins that do not add their own database fields but
use the entangled form fields instead.

Attributes:
- verbose_name (str): The verbose name of the UI item.

Methods:
None

"""
class Meta:
verbose_name = gettext("UI item")
28 changes: 18 additions & 10 deletions docs/source/how-to/add-frontend-plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ How to add your own frontend plugin
Creating the plugin
-------------------

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

1. **Define a Plugin Model**: In ``models.py`` of your new app, define a model for your
plugin. Data will be stored in a JSON field of the ``FrontendUIItem`` class. The
model will in many cases not do much, except defining a short description of
an instance.
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.

The plugin model defines what kind information will be associated with instances of your plugin.

Here is an example of a hypothetical plugin model:

.. code-block:: python

Expand All @@ -20,16 +20,17 @@ your Django app:

class YourPluginModel(FrontendUIItem):
class Meta:
proxy = True # Only a proxy model, if NO new fields are added
proxy = True # MUST be a proxy model
verbose_name = _("Your Plugin")

def short_description(self):
return f"'{self.field_name}'"

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.

.. note::

When adding new fields to the model, you need to remove the ``proxy = True``
statement in the model's ``Meta`` class.
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.

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

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

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.

Here is an example of a form for the `YourPluginModel`:

.. code-block:: python

# forms.py
Expand All @@ -59,7 +64,7 @@ your Django app:
field_name = forms.CharField(max_length=50)

3. **Create a Plugin Class**: In the same app, create a file named ``cms_plugins.py``.
Inside this file, define a class for your plugin by extending `CMSPluginBase`.
Inside this file, define a class for your plugin by extending ``CMSPluginBase``.

.. code-block:: python

Expand Down Expand Up @@ -110,6 +115,8 @@ Remember, developing custom plugins requires a good understanding of Django's an
CMS's architecture. Additionally, consider the security implications of your plugin,
especially if it handles user input.



Extending the plugin
--------------------

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