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: added re-usable Link widget and JSON endpoint #229

Merged
merged 94 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from 82 commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
bcec5e4
feat: endpoint for urls, link widget
fsbraun Oct 25, 2024
a95a658
Update test action
fsbraun Oct 25, 2024
f7f2fa2
Fix linting issues
fsbraun Oct 25, 2024
f3c56e8
More liniting
fsbraun Oct 25, 2024
c8882ed
fix: v3 compatibility
fsbraun Oct 25, 2024
efc6fd1
Add two step select2 internal link widget
fsbraun Oct 25, 2024
fcfbcdf
Fix linting issues
fsbraun Oct 25, 2024
bbc3004
Fix tests and dependencies
fsbraun Oct 25, 2024
e8aa4dc
Fix migration and link_is_optional
fsbraun Oct 26, 2024
26eb9ff
Remove unused blank parameter
fsbraun Oct 26, 2024
65d2957
Update changelog
fsbraun Oct 26, 2024
ed6fe60
feat: autodetect linkable models through their admin
fsbraun Oct 26, 2024
f3c2b13
Fix v3 compat with GrouperModelAdmin
fsbraun Oct 26, 2024
9f1cc7b
Add tests for endpoint
fsbraun Oct 26, 2024
0943e23
Fix for v3
fsbraun Oct 26, 2024
3a6e214
Try again
fsbraun Oct 26, 2024
63728a6
Update codecov action
fsbraun Oct 26, 2024
c452218
Fix test.yml syntax error
fsbraun Oct 26, 2024
aa8d960
Update tests
fsbraun Oct 26, 2024
4aeb631
Test template tags
fsbraun Oct 26, 2024
991b369
Update validator tests
fsbraun Oct 26, 2024
80d8cd4
Update tag tests
fsbraun Oct 26, 2024
3da1aef
Fix migration
fsbraun Oct 26, 2024
a534cb5
Fix migration test and add tests for Django 5.1
fsbraun Oct 27, 2024
e5794b0
Update migration test for v3
fsbraun Oct 27, 2024
1790799
Remove test with Django 5.1 and django CMS 3.11 (since they are not c…
fsbraun Oct 27, 2024
3d538e3
Add test for migration of anchor-only link.
fsbraun Oct 27, 2024
cbe4266
Add tests for third-party app endpoints
fsbraun Oct 27, 2024
49531b8
Update translations
fsbraun Oct 27, 2024
9f27a4c
Add tests for link widget and compiled translations
fsbraun Oct 27, 2024
0b6e979
Mark untestable lines
fsbraun Oct 27, 2024
c3a3c1f
Simplified manager retrieval
fsbraun Oct 27, 2024
106f4ac
Improved endpoint tests
fsbraun Oct 27, 2024
393d2cb
Allow simplified setting ``DJANGOCMS_LINK_LINKABLE_MODELS``.
fsbraun Oct 27, 2024
efbdf51
Recover django 4.2 compat
fsbraun Oct 27, 2024
1be3cea
Update readme
fsbraun Oct 27, 2024
2252643
Small simplification
fsbraun Oct 27, 2024
3895f46
Add a nocover for current tests
fsbraun Oct 27, 2024
0d3e335
Update readme
fsbraun Oct 27, 2024
a909b0f
Update classifiers
fsbraun Oct 27, 2024
5228814
Add `to_link` template tag
fsbraun Oct 27, 2024
3da1fd8
Set correct default for empty link
fsbraun Oct 27, 2024
c7c31fb
Add ``DJANGOCMS_LINK_ALLOWED_LINK_TYPES`` config
fsbraun Oct 28, 2024
ea83926
feat: Allow for `get_link_queryset` method in model admin
fsbraun Oct 28, 2024
80335b5
Add `DJANGOCMS_LINK_MINIMUM_INPUT_LENGTH` setting
fsbraun Oct 28, 2024
f8e8e5a
Update tests
fsbraun Oct 28, 2024
605c44d
Simplify site detection
fsbraun Oct 28, 2024
35a874e
Update README
fsbraun Oct 28, 2024
9ef96d6
remove legacy code
fsbraun Oct 28, 2024
5c8e79f
Remove more legacy code
fsbraun Oct 28, 2024
edfd76b
Add some type annotations
fsbraun Oct 28, 2024
e89dc2e
Add no cover for an unreachable line in tests
fsbraun Oct 28, 2024
7d9b0e8
Update README.rst
fsbraun Oct 29, 2024
0bf66e2
Update README.rst
fsbraun Oct 29, 2024
84c8e69
Add link icon to plugin (for djangocms_text dropdown)
fsbraun Oct 29, 2024
dbd46f0
fix tests
fsbraun Oct 29, 2024
d360e8c
fix: Empty model list not added to URL endpoint result
fsbraun Oct 30, 2024
6e5ef6f
fix tests
fsbraun Oct 30, 2024
b656183
Add `DJANGOCMS_LINK_PAGINATE_BY` setting
fsbraun Oct 30, 2024
afdb591
fix: LinkField defaulted to `blank=True`
fsbraun Oct 30, 2024
583bb36
Add static files to `MANIFEST.in`
fsbraun Nov 1, 2024
e56d0a6
Remove tests from installation
fsbraun Nov 1, 2024
e30fcec
Optimize `Page` queryset
fsbraun Nov 1, 2024
00c121f
Fix: restore v3 compatibility
fsbraun Nov 1, 2024
049fd87
fix: Update GitHub action versions
fsbraun Nov 4, 2024
929fc88
fix: Run migration test
fsbraun Nov 4, 2024
9c7d0de
fix: Do not initialize `REGISTERED_ADMINS` with model setting
fsbraun Nov 4, 2024
7b7c0ab
Update django-cms dependency
fsbraun Nov 4, 2024
7b636a9
Avoid skipping the migration test
fsbraun Nov 4, 2024
5365009
feat: LinkDict (syntactic sugar)
fsbraun Nov 7, 2024
aae0fac
More detailed link types
fsbraun Nov 7, 2024
6145fe4
feat: improve test coverage and use pytest
fsbraun Nov 8, 2024
7d41f6a
Update checkout action
fsbraun Nov 8, 2024
4255f7b
Remove django-app-helper from dependencies
fsbraun Nov 8, 2024
62eb210
Move to pyproject.toml
fsbraun Nov 8, 2024
95a4d14
fix license description in pyproject.toml
fsbraun Nov 8, 2024
e215635
Update workflows
fsbraun Nov 8, 2024
4ebd75a
Fix flake8 action
fsbraun Nov 8, 2024
152ee99
Fix precommit flake8
fsbraun Nov 8, 2024
73a90ad
fix: Setuptools in py39
fsbraun Nov 8, 2024
7f6340e
Update README
fsbraun Nov 8, 2024
deac3a4
Remove test debug code
fsbraun Nov 8, 2024
fc3c0f7
add __str__ to LinkDict
fsbraun Nov 10, 2024
8c1ab72
Fix pyproject.toml, typo in Changelog
fsbraun Nov 10, 2024
cd98fb8
Fix README.rst
fsbraun Nov 10, 2024
77839bb
Update readme
fsbraun Nov 10, 2024
612573b
Some clarifications in the README
fsbraun Nov 10, 2024
a465770
One more readme clarification
fsbraun Nov 10, 2024
539ca5c
Update pyproject.toml and tox.ini
fsbraun Nov 10, 2024
b25eddb
Update .github/workflows/test.yml
fsbraun Nov 10, 2024
8ad5af0
Move coverage config to pyproject.toml
fsbraun Nov 10, 2024
ba4cae5
Merge branch 'feat/url-mngr' of github.com:django-cms/djangocms-link …
fsbraun Nov 10, 2024
8267ad0
Better slicing of endpoint querysets
fsbraun Nov 11, 2024
221201b
Fix update tests
fsbraun Nov 11, 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
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
with:
python-version: 3.9
- name: Install flake8
run: pip install --upgrade flake8
run: pip install --upgrade flake8 flake8-pyproject
- name: Run flake8
uses: liskin/gh-problem-matcher-wrap@v1
with:
Expand Down
25 changes: 19 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,45 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [ "3.10", "3.11", "3.12"] # latest release minus two
python-version: [ "3.9", "3.10", "3.11", "3.12"] # latest release minus two
fsbraun marked this conversation as resolved.
Show resolved Hide resolved
requirements-file: [
dj42_cms311.txt,
dj42_cms41.txt,
dj50_cms311.txt,
dj50_cms41.txt,
dj51_cms41.txt,
]
os: [
ubuntu-20.04,
]
exclude:
- requirements-file: dj50_cms311.txt
python-version: 3.9
- requirements-file: dj50_cms41.txt
python-version: 3.9
- requirements-file: dj51_cms311.txt
python-version: 3.9
- requirements-file: dj51_cms41.txt
python-version: 3.9

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}

uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -U setuptools
pip install -r tests/requirements/${{ matrix.requirements-file }}
python setup.py install

- name: Run coverage
run: coverage run setup.py test
- name: Run test coverage
run: coverage run -m pytest

- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }} # required
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ repos:
rev: 7.1.0
hooks:
- id: flake8
fsbraun marked this conversation as resolved.
Show resolved Hide resolved
additional_dependencies: [Flake8-pyproject]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
Expand Down
13 changes: 8 additions & 5 deletions .tx/config
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
[main]
host = https://www.transifex.com

[djangocms-link.djangocms_link]
file_filter = djangocms_link/locale/<lang>/LC_MESSAGES/django.po
source_file = djangocms_link/locale/en/LC_MESSAGES/django.po
source_lang = en
type = PO
[o:divio:p:djangocms-link:r:djangocms_link]
file_filter = djangocms_link/locale/<lang>/LC_MESSAGES/django.po
source_file = djangocms_link/locale/en/LC_MESSAGES/django.po
source_lang = en
type = PO
replace_edited_strings = false
keep_translations = false

14 changes: 13 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,20 @@
Changelog
=========

5.0.0 (unreleased)
==================

* Major refactor
* New re-usable LinkWidget, LinkFormField, and LinkField
* New re-usable JSON endpoint for internal links
* New template tags (``get_url`` tag and ``to_url`` filter) to convert link
fields into URLs. This allows multiple LinkFields per model
* Fixed cross-site linking which reduces the number situations of the hostname
needing to be part of a link
* Droped django-select2 dependency in favor of django admin's autocomplete

4.0.0 (2024-07-22)
================
==================

* Added support for django CMS 4.1
* Added support for python 3.10 to 3.12
Expand Down
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ include LICENSE
include README.rst
recursive-include djangocms_link/locale *
recursive-include djangocms_link/templates *
recursive-include djangocms_link/static *
recursive-exclude * *.py[co]
recursive-exclude tests *
215 changes: 174 additions & 41 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@
django CMS Link
===============

|pypi| |build| |coverage|
|pypi| |coverage| |python| |django| |djangocms| |djangocms4|

**django CMS Link** is a plugin for `django CMS <http://django-cms.org>`_ that
**django CMS Link** is a plugin for `django CMS <https://django-cms.org>`_ that
allows you to add links on your site.

This plugin supports child plugins. If you add an other plugin as a
child it will take this content instead of the link name as the content of the link.

This addon is compatible with `Divio Cloud <http://divio.com>`_ and is also available on the
`django CMS Marketplace <https://marketplace.django-cms.org/en/addons/browse/djangocms-link/>`_
for easy installation.
This addon is compatible with `Divio Cloud <http://divio.com>`_.

.. image:: preview.gif

Expand All @@ -39,12 +37,13 @@ Documentation
See ``REQUIREMENTS`` in the `setup.py <https://github.com/divio/djangocms-link/blob/master/setup.py>`_
file for additional dependencies:

|python| |django| |djangocms|
django CMS Link has a weak dependency on django Filer. If
`django Filer <http://django-filer.readthedocs.io/en/latest/installation.html>`_
is installed and configured appropriately, django CMS Link will allow linking
files.

* Django Filer 1.7 or higher

Make sure `django Filer <http://django-filer.readthedocs.io/en/latest/installation.html>`_
is installed and configured appropriately.
* djangocms-atrributes-field 1.0 or higher


Installation
Expand All @@ -60,6 +59,9 @@ For a manual install:
Configuration
-------------

Link templates
..............

Note that the provided templates are very minimal by design. You are encouraged
to adapt and override them to your project's requirements.

Expand All @@ -73,11 +75,89 @@ setting:
('feature', _('Featured Version')),
]

You'll need to create the `feature` folder inside ``templates/djangocms_link/``
You'll need to create the ``feature`` folder inside ``templates/djangocms_link/``
otherwise you will get a *template does not exist* error. You can do this by
copying the ``default`` folder inside that directory and renaming it to
``feature``.

Link types
...........

By default, django CMS Link provides three major link types: internal, external,
and file link (if django-filer is installed).

Phone links or email links can be entered by using the ``tel:`` or ``mailto:``
scheme, respectively, in the external link field.

By changing the ``DJANGOCMS_LINK_ALLOWED_LINK_TYPES`` setting you can limit
the type of links accepted. The default is::

DJANGOCMS_LINK_ALLOWED_LINK_TYPES = [
'internal_link', # Pages and other models
'external_link', # Hand-typed URLs
'file_link', # Files from django-filer
'tel', # Phone numbers as external links using the tel: scheme
'mailto', # Email addresses as external links using the mailto: scheme
'anchor', # Anchors in the current page as external links using #
]

Linkable models
...............

*Changed in version 5:*

By default, django CMS Link will autodetect which Django or Django CMS models it
can create internal links to. To make a model appear in the list of internal
links, you need to

* register a model admin for the model and provide a ``search_fields``
attribute. django CMS Link uses the same search logic as the Django admin.
* provide a ``get_absolute_url()`` method on the model. This method should
return the URL of the model instance.

If you do not want to use auto detection, you can provide a list of models
in the ``DJANGOCMS_LINKABLE_MODELS`` setting using dotted strings::

DJANGOCMS_LINKABLE_MODELS = [
'myapp.mymodel',
]

Attention: ``Page`` objects are always linkable.

django CMS Link will use the model admin's ``get_queryset`` method to retrieve
the list of objects. If you want to add custom filters, sorting or site
handling, you can add a ``get_link_queryset`` method to the model admin::

class MyModelAdmin(admin.ModelAdmin):
def get_link_queryset(self, request, site_id):
"""Only used by djangocms-link: returns queryset to select link targets from."""
qs = self.get_queryset(request)
return qs.filter(is_public=True)

Large search-sets
..................

If you have a large number of internally linkable models, you can use the
``DJANGOCMS_LINK_MINIMUM_INPUT_LENGTH`` setting to require a minimum number of
characters typed before the search is triggered. The higher the number, the
smaller the average result set size. The default is 0::

# Require at least 2 characters to be typed before searching for pages
DJANGOCMS_LINK_MINIMUM_INPUT_LENGTH = 2

By default django CMS Link will paginate the search results. You can change the
page size by setting the ``DJANGOCMS_LINK_PAGINATE_BY`` setting.
The default is 100::

# Show 5 results per page
DJANGOCMS_LINK_PAGINATE_BY = 100

Note, that in the admin paginated search results repeat the modle verbose name.


Non-standard hostnames
......................

To support environments where non-standard URLs would otherwise work, this
project supports the defining of an additional RegEx pattern for validating the
host-portion of the URL.
Expand All @@ -98,16 +178,59 @@ Either of these might accept a URL such as:
If left undefined, the normal Django URLValidator will be used.


Django Select2
~~~~~~~~~~~~~~
Link fields
-----------

This plugin supports `django-select2 <https://github.com/applegrew/django-select2#installation>`_
for simpler use of internal links. You need to manually enable it by:
As of version 5, django CMS Link provides a re-usable link model field,
form field and form widget. This allows you to use the link field in your own
models or forms.

* run ``pip install django-select2``
* add ``django_select2`` to your ``INSTALLED_APPS``
* add ``url(r'^select2/', include('django_select2.urls')),`` to your ``urls.py``
* set ``DJANGOCMS_LINK_USE_SELECT2 = True`` in your ``settings.py``
.. code-block:: python

from djangocms_link.fields import LinkField, LinkFormField, LinkWidget

class MyModel(models.Model):
link = LinkField() # or LinkField(blank=True) for optional links

class MyForm(forms.Form):
link = LinkFormField(required=False)

``LinkField`` is a subclass of ``JSONField`` and stores the link data as
``djangocms_link.helpers.LinkDict``, a direct subclass of ``dict``.
(An empty link will be ``{}``.)

To render the link field in a template, use the ``LinkDict`` property ``url`` or
the new template tag ``to_url``. The ``type`` property returns the link type::

{# Variant 1 #}
{% if obj.link %}
<a href="{{ obj.link.url }}">Link available</a>
{% endif %}

{# Variant 2 #}
{% load djangocms_link_tags %}
{% if obj.link %}
<a href="{{ obj.link|to_url }}">Link</a>
{% endif %}

{# Variant 3 #}
{% with url=obj.link|to_url %}
{% if url %}
<a href="{{ url }}">Link available</a>
{% endif %}
{% endwith %}

{% if obj.link.type == "external_link" %}
<a href="{{ obj.link.url }}">External link</a>
{% endif %}


To turn the ``LinkField``'s ``LinkDict`` dictionary into a URL in python code,
use the ``url`` property. (It will hit the database when needed. Results are
cached.)::

obj = MyModel.objects.first()
url = obj.link.url


Running Tests
Expand All @@ -118,35 +241,45 @@ You can run tests by executing::
virtualenv env
source env/bin/activate
pip install -r tests/requirements.txt
python setup.py test
pytest


.. |pypi| image:: https://badge.fury.io/py/djangocms-link.svg
:target: http://badge.fury.io/py/djangocms-link
.. |build| image:: https://travis-ci.org/divio/djangocms-link.svg?branch=master
:target: https://travis-ci.org/divio/djangocms-link
.. |coverage| image:: https://codecov.io/gh/divio/djangocms-link/branch/master/graph/badge.svg
:target: https://codecov.io/gh/divio/djangocms-link

.. |python| image:: https://img.shields.io/badge/python-3.5+-blue.svg
:target: https://pypi.org/project/djangocms-link/
.. |django| image:: https://img.shields.io/badge/django-2.2,%203.0,%203.1-blue.svg
:target: https://www.djangoproject.com/
.. |djangocms| image:: https://img.shields.io/badge/django%20CMS-3.7%2B-blue.svg
:target: https://www.django-cms.org/

Upgrading from version 4 or lower
--------------------------------

Updating from `cmsplugin-filer <https://github.com/django-cms/cmsplugin-filer>`_
--------------------------------------------------------------------------------
django CMS Link 5 is a rewrite of the plugin. If you are updating from
version 4 or lower, you will notice

Historically, `cmsplugin-filer` was used to create file, folder, image, link, teaser & video plugins on your django CMS projects. Now `cmsplugin-filer` has been archived, you can still migrate your old instances without having to copy them manually to the new `djangocms-<file|picture|link|...>` plugins.
* the **new re-usable link widget**, greatly simplifying the user interface
* an **improved management of multi-site situations**, essentially avoiding the
unnecessary additon of the host name to the URL in plugin instances that
are not in a page placeholder (such as links on aliases or static placeholder)
* a **re-usable admin endpoint** for querying available links which can be used
by other apps such as djangocms-text.
* Links are generated by template tags or template filters instead of the
model's ``get_link()`` method. This allows multiple links in future models. The
``get_link()`` method on the plugin's model is still available for backwards
compatibility.

There's a third-party management command that supports your migration:
Migrations should automatically existing plugin instances to the new model
fields.

`migrate_cmsplugin_filer.py <https://gist.github.com/corentinbettiol/84a6ea7e4d047fc01861b0af15fd60f0>`_
**WARNING:** We strongly recommend to backup your database before updating to
version 5. The migration is tested but they do remove unused fields from
the database. If you encounter any issues, please report them on
`GitHub <https://github.com/django-cms/djangocms-link/issues>`_.

This management command is only a starting point. It *has* worked out of the box for some people, but we encourage you to read the code, understand what it does, and test it on a development environment before running it on your production server.
.. |pypi| image:: https://badge.fury.io/py/djangocms-link.svg
:target: http://badge.fury.io/py/djangocms-link
.. |coverage| image:: https://codecov.io/gh/django-cms/djangocms-link/branch/master/graph/badge.svg
:target: https://codecov.io/gh/django-cms/djangocms-link

The management command is only configured to transfer your `cmsplugin_link`, `cmsplugin_file`, `cmsplugin_folder` and `cmsplugin_image` plugins to modern `djangocms_*` plugins. If you need to transfer other `cmsplugin_*` plugins, you'll have to write your own code.
.. |python| image:: https://img.shields.io/badge/python-3.10+-blue.svg
:target: https://pypi.org/project/djangocms-link/
.. |django| image:: https://img.shields.io/badge/django-4.2,%205.0,%205.1-blue.svg
:target: https://www.djangoproject.com/
.. |djangocms| image:: https://img.shields.io/badge/django%20CMS-3.11%2B-blue.svg
:target: https://www.django-cms.org/
.. |djangocms4| image:: https://img.shields.io/badge/django%20CMS-4-blue.svg
:target: https://www.django-cms.org/

Alternatively you can use the `deprecate_cmsplugin_filer <https://github.com/ImaginaryLandscape/deprecate_cmsplugin_filer>`_ app, which only adds a small migration that transfer the old `cmsplugin-filer` plugins instances to the new `djangocms-<file|picture|link|...>` plugins.
Loading
Loading