{% endif %}
- {% endif %}
+ {% endif %}
- {# Type #}
- {% if kind == 'news' %}
+ {# Type #}
+ {% if kind == 'news' %}
{{ instance.type_text }}
- {% endif %}
+ {% endif %}
- {# Author #}
- {# FAQ: Not shown in design #}
- {% comment %}
+ {# Author #}
+ {# FAQ: Not shown in design #}
+ {% comment %}
{{ instance.author_text }}
- {% endcomment %}
+ {% endcomment %}
+
+
+
From d0f4167f78cf7c96138cd34c14181bdde11b6b2f Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Fri, 25 Jun 2021 12:51:14 -0500
Subject: [PATCH 15/56] GH-93: Polish Allocs Article Preview date logic
---
taccsite_cms/contrib/helpers.py | 43 +++++++++++++------
.../cms_plugins.py | 13 ++++--
.../templates/static_article_preview.html | 30 +++++++------
3 files changed, 56 insertions(+), 30 deletions(-)
diff --git a/taccsite_cms/contrib/helpers.py b/taccsite_cms/contrib/helpers.py
index 761245d42..b1bee76c3 100644
--- a/taccsite_cms/contrib/helpers.py
+++ b/taccsite_cms/contrib/helpers.py
@@ -26,7 +26,8 @@ def get_nearest(items, pivot):
# HELP: Can this logic be less verbose?
-def which_date_is_nearest_today(date_a, date_b, preferred_time):
+# HELP: Is the `preferred_time_period` parameter effectual?
+def which_date_is_nearest_today(date_a, date_b, preferred_time_period):
"""
Returns whether each date is today or nearest today, and whether nearest date is past or today or future.
@@ -36,41 +37,55 @@ def which_date_is_nearest_today(date_a, date_b, preferred_time):
:param datetime date_a: a date "A" to compare
:param datetime date_b: a date "B" to compare
- :param str preferred_time: whether to prefer 'future' or 'past' dates
+ :param str preferred_time_period: whether to prefer 'future' or 'past' dates
:returns:
- A tuple containing, respectively:
- - a ``boolean`` (whether ``date_a`` is nearest)
- - a ``boolean`` (whether ``date_b`` is nearest)
- - a ``string`` (whether nearest date/s is/are ``past``, ``today``, or ``future``)
+ A tuple of tuples:
+ (
+ ``boolean`` of whether ``date_a`` is nearest,
+ ``string`` of ``date_a`` time period ``past``/``today``/``future``
+ ),
+ (
+ ``boolean`` of whether ``date_b`` is nearest,
+ ``string`` of ``date_b`` time period ``past``/``today``/``future``
+ ),
:rtype: tuple
"""
today = date.today()
+ is_a = False
+ is_b = False
+ a_time_period = 'today'
+ b_time_period = 'today'
# Match preferred time
if today in {date_a, date_b}:
is_a = True
is_b = True
- actual_time = 'today'
+ a_time_period = 'today'
+ b_time_period = 'today'
- elif preferred_time == 'future':
+ elif preferred_time_period == 'future':
is_a = date_a and date_a >= today
is_b = date_b and date_b >= today
- actual_time = 'future'
+ if is_a: a_time_period = 'future'
+ if is_b: b_time_period = 'future'
if not is_a and not is_b:
is_a = date_a and date_a < today
is_b = date_b and date_b < today
- actual_time = 'past'
+ if is_a: a_time_period = 'past'
+ if is_b: b_time_period = 'past'
- elif preferred_time == 'past':
+ elif preferred_time_period == 'past':
is_a = date_a and date_a < today
is_b = date_b and date_b < today
- actual_time = 'past'
+ if is_a: a_time_period = 'past'
+ if is_b: b_time_period = 'past'
if not is_a and not is_b:
is_a = date_a and date_a >= today
is_b = date_b and date_b >= today
- actual_time = 'future'
+ if is_a: a_time_period = 'future'
+ if is_b: b_time_period = 'future'
# Show nearest date
if is_a and is_b and date_a != date_b:
@@ -81,7 +96,7 @@ def which_date_is_nearest_today(date_a, date_b, preferred_time):
if date_b == nearest_date:
is_a = False
- return (is_a, is_b, actual_time)
+ return ((is_a, a_time_period), (is_b, b_time_period))
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py b/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
index 1da20c9bc..f1db04a9e 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
@@ -173,14 +173,21 @@ def render(self, context, instance, placeholder):
context = super().render(context, instance, placeholder)
request = context['request']
- show_show_dates = which_date_is_nearest_today(
+ dates = which_date_is_nearest_today(
instance.publish_date,
instance.expiry_date,
'future'
)
+ (should_show_open_date, open_date_time_period) = dates[0]
+ (should_show_close_date, close_date_time_period) = dates[1]
context.update({
- 'should_show_open_date': show_show_dates[0],
- 'should_show_close_date': show_show_dates[1]
+ 'open_date': instance.publish_date,
+ 'should_show_open_date': should_show_open_date,
+ 'open_date_time_period': open_date_time_period,
+
+ 'close_date': instance.expiry_date,
+ 'should_show_close_date': should_show_close_date,
+ 'close_date_time_period': close_date_time_period,
})
return context
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html b/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html
index 43e137f3f..08e094b48 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html
@@ -36,26 +36,26 @@
{% if kind == 'news' %}
Published
-
{% endif %}
{% if kind == 'allocs' %}
- {% if instance.publish_date and instance.expiry_date %}
+ {% if open_date and close_date and open_date <= close_date %}
From 9f0a34e362275da1fb80148f66f4d8cbaf6119cf Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Tue, 29 Jun 2021 14:11:20 -0500
Subject: [PATCH 30/56] GH-93: Quick: Docs: Update architecture decision
---
.../contrib/taccsite_static_article_preview/README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/README.md b/taccsite_cms/contrib/taccsite_static_article_preview/README.md
index 58dd6ec4c..0a64962a9 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/README.md
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/README.md
@@ -15,14 +15,18 @@ But this is not available due to constrainst of architecture, time, or ability.
Ideally, the image fields are in the plugin, not a child plugin.
Wesley failed to achieve that functionality:
+
1. Build model so it extends `AbstractPicture` from `djangocms-picture`.
2. Tweak model to sweep bugs under the rug.
3. Quit when he was unable to resolve the error,
`TaccsiteStaticNewsArticlePreview has no field named 'cmsplugin_ptr_id'`
upon saving a plugin instance.
-4. Learn that he [should not try to reduce `AbstractPicture`](https://stackoverflow.com/a/3674714/11817077).
+4. Learn:
+ - [one should not try to reduce `AbstractPicture`](https://stackoverflow.com/a/3674714/11817077)
+ - [one should not subclass a subclass of `CMSPlugin`](https://github.com/django-cms/django-cms/blob/3.7.4/cms/models/pluginmodel.py#L104)
This is the relevant code he abandoned:
+
```python
from djangocms_picture.models import AbstractPicture
From c10917d653f6771d5f28dded5daa321180a16ddd Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Tue, 29 Jun 2021 15:12:48 -0500
Subject: [PATCH 31/56] Doc: How to Handle "Non-Nullable" "Default Value"
---
...ow-to-handle-non-nullable-default-value.md | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 taccsite_cms/contrib/_docs/how-to-handle-non-nullable-default-value.md
diff --git a/taccsite_cms/contrib/_docs/how-to-handle-non-nullable-default-value.md b/taccsite_cms/contrib/_docs/how-to-handle-non-nullable-default-value.md
new file mode 100644
index 000000000..fb558ce69
--- /dev/null
+++ b/taccsite_cms/contrib/_docs/how-to-handle-non-nullable-default-value.md
@@ -0,0 +1,62 @@
+# How to Handle "Non-Nullable" "Default Value"
+
+## Sample Error
+
+```text
+You are trying to add a non-nullable field '...'
+to choice without a default; we can't do that
+(the database needs something to populate existing rows).
+Please select a fix:
+ 1) Provide a one-off default now (will be set on all existing rows)
+ 2) Quit, and let me add a default in models.py
+Select an option:
+```
+
+## Explanations
+
+- (blog post) [What do you do when 'makemigrations' is telling you About your Lack of Default Value](https://chrisbartos.com/articles/what-do-you-do-when-makemigrations-is-telling-you-about-your-lack-of-default-value/)
+- (video) [You are trying to add a non-nullable field ' ' to ' ' without a default; we can't do that](https://www.youtube.com/watch?v=NgaTUEijQSQ)
+
+## Solutions
+
+### For `cmsplugin_ptr`
+
+1. ☑ Select option 1), then see:
+ - [Follow-Up Error](#follow-up-error)
+ - [Notes ▸ `cmsplugin_ptr`](#cmsplugin_ptr)
+
+### For Other Fields
+
+1. ⚠ Select option 1) and hope for the best.
+2. ☑ Select option 2) and provide a sensible default (_not_ `None` a.k.a. null).
+3. ⚠ (blog post) (hack) [Add A Migration For A Non-Null Foreignkey Field In Django](https://jaketrent.com/post/add-migration-nonnull-foreignkey-field-django)
+
+## Follow-Up Error
+
+If you allowed Null to be set as default, then you may have this new error:
+
+```text
+django.db.utils.IntegrityError: column "..." contains null values
+```
+
+Solutions:
+
+1. [delete _relevant_ migration files and rebuild migrations](https://stackoverflow.com/a/37244199/11817077)
+2. [delete _all_ migration files and rebuild migrations](https://stackoverflow.com/a/37242930/11817077)
+
+## Notes
+
+### `cmsplugin_ptr`
+
+ If the field is `cmsplugin_ptr` then know that
+
+ - [it is a database relationship field managed automatically by Django](https://github.com/nephila/djangocms-blog/issues/316#issuecomment-242292787),
+ - you may see it in workarounds for other plugins ([source a](https://github.com/django-cms/djangocms-link/blob/3.0.0/djangocms_link/models.py#L125), [source b](https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/models.py#L208)),
+ - you should __not__ add or overwrite it unless you know what you are doing.
+
+ _W. Bomar learned everything in the intitial version of this document after trying to overwrite `cmsplugin_ptr` while extending its model from [source a](https://github.com/django-cms/djangocms-link/blob/3.0.0/djangocms_link/models.py#L125). His solution was [delete _all_ migration files and rebuild migrations](https://stackoverflow.com/a/37242930/11817077)._
+
+## Appendix
+
+- [Django CMS ▸ How to create Plugins ▸ Handling Relations](https://docs.django-cms.org/en/release-3.7.x/how_to/custom_plugins.html#handling-relations)
+- [[BUG] Plugins with models that don't directly inherit from CMSPlugin or an abstract model cannot be copied](https://github.com/django-cms/django-cms/issues/6987)
From a3234723f67cf09e15bc0bf77aa52cddc23a2ca6 Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Tue, 29 Jun 2021 15:13:49 -0500
Subject: [PATCH 32/56] GH-93: DRY-er Support AbstractLink
---
taccsite_cms/contrib/helpers.py | 53 +++++++++++++++++--
.../migrations/0001_initial.py | 6 +--
.../taccsite_static_article_list/models.py | 37 ++-----------
.../migrations/0001_initial.py | 6 +--
.../taccsite_static_article_preview/models.py | 23 ++++----
5 files changed, 69 insertions(+), 56 deletions(-)
diff --git a/taccsite_cms/contrib/helpers.py b/taccsite_cms/contrib/helpers.py
index caae0f5fe..dce0c3819 100644
--- a/taccsite_cms/contrib/helpers.py
+++ b/taccsite_cms/contrib/helpers.py
@@ -1,7 +1,3 @@
-from datetime import date
-
-
-
# SEE: https://github.com/django-cms/djangocms-bootstrap4/blob/master/djangocms_bootstrap4/helpers.py
def concat_classnames(classes):
"""Concatenates a list of classes (without failing on None)"""
@@ -50,6 +46,9 @@ def add_classname_to_instances(classname, plugin_instances):
# HELP: Can this logic be less verbose?
# HELP: Is the `preferred_time_period` parameter effectual?
+
+from datetime import date
+
def which_date_is_nearest_today(date_a, date_b, preferred_time_period):
"""
Returns whether each date is today or nearest today, and whether nearest date is past or today or future.
@@ -129,8 +128,9 @@ def which_date_is_nearest_today(date_a, date_b, preferred_time_period):
class AbstractMaxChildrenPlugin():
"""
- Abstract extension of `CMSPluginBase` that allows setting maximum amount of nested/child plugins. Usage:
+ Abstract extension of `CMSPluginBase` that allows setting maximum amount of nested/child plugins.
+ Usage:
1. Extend this class,
after extending `CMSPluginBase` or a class that extends `CMSPluginBase`.
2. Set `max_children` to desired limit.
@@ -152,3 +152,46 @@ def add_view(self,request, form_url='', extra_context=None):
'max_children': self.max_children,
})
return super(AbstractMaxChildrenPlugin, self).add_view(request, form_url, extra_context)
+
+
+
+from cms.models.pluginmodel import CMSPlugin
+
+from django.core.exceptions import ValidationError
+from django.utils.translation import gettext_lazy as _
+
+# SEE: https://github.com/django-cms/djangocms-link/blob/3.0.0/djangocms_link/models.py#L48
+def clean_for_abstract_link(model, self):
+ """
+ Intercept and manipulate validation on `AbstractLink` so that it suits TACC's minimal subclassing of it.
+
+ Usage:
+ ```
+ from taccsite_cms.contrib.helpers import clean_for_abstract_link
+
+ def clean(self):
+ clean_for_abstract_link(__class__, self)
+ ```
+ """
+
+ # Bypass irrelevant parent validation
+ # SEE: ./_docs/how-to-override-validation-error-from-parent-model.md
+ try:
+ super(model, self).clean()
+ except ValidationError as err:
+ # Intercept multi-field errors
+ if hasattr(err, 'error_dict'):
+ for field, errors in err.message_dict.items():
+ # Reduce verbosity of original error message
+ # FAQ: Original error message assumes more fields exist
+ indices = get_indices_that_start_with(
+ 'Only one of ', errors
+ )
+ for i in indices:
+ err.error_dict[field] = ValidationError(
+ _('Only one of External link or Internal link may be given.'), code='invalid')
+
+ if len(err.messages) == 0:
+ pass
+ else:
+ raise err
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/migrations/0001_initial.py b/taccsite_cms/contrib/taccsite_static_article_list/migrations/0001_initial.py
index b7326cf89..2831e5de2 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/migrations/0001_initial.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2.16 on 2021-06-28 23:13
+# Generated by Django 2.2.16 on 2021-06-29 19:41
from django.db import migrations, models
import django.db.models.deletion
@@ -12,8 +12,8 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('cms', '0022_auto_20180620_1551'),
('filer', '0012_file_mime_type'),
+ ('cms', '0022_auto_20180620_1551'),
]
operations = [
@@ -27,11 +27,11 @@ class Migration(migrations.Migration):
('mailto', models.EmailField(blank=True, max_length=255, verbose_name='Email address')),
('phone', models.CharField(blank=True, max_length=255, verbose_name='Phone')),
('target', models.CharField(blank=True, choices=[('_blank', 'Open in new window'), ('_self', 'Open in same window'), ('_parent', 'Delegate to parent'), ('_top', 'Delegate to top')], max_length=255, verbose_name='Target')),
+ ('attributes', djangocms_attributes_field.fields.AttributesField(blank=True, default=dict, verbose_name='Attributes')),
('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_static_article_list_taccsitearticlelist', serialize=False, to='cms.CMSPlugin')),
('title_text', models.CharField(blank=True, help_text='The title at the top of the list.', max_length=100, verbose_name='Title Text')),
('layout_type', models.CharField(choices=[('Row Layouts', (('rows-always-N-even', 'Multiple Rows'),)), ('Column Layouts', (('cols-widest-2-even', '2 Equal-Width Columns'), ('cols-widest-2-wide-narr', '2 Columns: 1 Wide, 1 Narrow'), ('cols-widest-2-narr-wide', '2 Columns: 1 Narrow, 1 Wide'), ('cols-widest-3-even', '3 Equal-Width Columns')))], default='Row Layouts', help_text='Layout of the articles within. Notice: All Column Layouts become multiple rows when screen width is narrow.', max_length=255, verbose_name='Layout Option')),
('style_type', models.CharField(blank=True, choices=[('Row Layouts', (('rows-divided', 'Dividers Between Articles'), ('rows-gapless', 'Remove Gaps Between Articles'))), ('Column Layouts', (('cols-gapless', 'Remove Gaps Between Articles'),))], help_text='Optional styles for the list itself.', max_length=255, verbose_name='Style Option')),
- ('attributes', djangocms_attributes_field.fields.AttributesField(default=dict)),
('file_link', filer.fields.file.FilerFileField(blank=True, help_text='If provided links a file from the filer app.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='filer.File', verbose_name='File link')),
('internal_link', models.ForeignKey(blank=True, help_text='If provided, overrides the external link.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.Page', verbose_name='Internal link')),
],
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/models.py b/taccsite_cms/contrib/taccsite_static_article_list/models.py
index bf7e03643..fff301cf9 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/models.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/models.py
@@ -1,16 +1,11 @@
from django.core.exceptions import ValidationError
-
from django.utils.translation import gettext_lazy as _
from django.utils.encoding import force_text
-
from django.db import models
-from djangocms_link.models import AbstractLink
-from djangocms_attributes_field import fields
+from djangocms_link.models import AbstractLink
-from taccsite_cms.contrib.helpers import (
- get_indices_that_start_with
-)
+from taccsite_cms.contrib.helpers import clean_for_abstract_link
# Constants
@@ -69,11 +64,7 @@ class TaccsiteArticleList(AbstractLink):
max_length=255,
)
- attributes = fields.AttributesField()
-
- link_is_optional = True # SEE: AbstractLink
-
-
+ link_is_optional = True
def get_short_description(self):
return self.title_text
@@ -100,27 +91,7 @@ def clean(self):
code='invalid'
)
- # Bypass irrelevant parent validation
- # SEE: ./_docs/how-to-override-validation-error-from-parent-model.md
- try:
- super().clean()
- except ValidationError as err:
- # Intercept multi-field errors
- if hasattr(err, 'error_dict'):
- for field, errors in err.message_dict.items():
- # Reduce verbosity of original error message
- # FAQ: Original error message assumes more fields exist
- indices = get_indices_that_start_with(
- 'Only one of ', errors
- )
- for i in indices:
- err.error_dict[field] = ValidationError(
- _('Only one of External link or Internal link may be given.'), code='invalid')
-
- if len(err.messages) == 0:
- pass
- else:
- raise err
+ clean_for_abstract_link(__class__, self)
class Meta:
abstract = False
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0001_initial.py b/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0001_initial.py
index 469811d58..c9a902311 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0001_initial.py
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2.16 on 2021-06-29 02:47
+# Generated by Django 2.2.16 on 2021-06-29 19:41
from django.db import migrations, models
import django.db.models.deletion
@@ -27,6 +27,7 @@ class Migration(migrations.Migration):
('mailto', models.EmailField(blank=True, max_length=255, verbose_name='Email address')),
('phone', models.CharField(blank=True, max_length=255, verbose_name='Phone')),
('target', models.CharField(blank=True, choices=[('_blank', 'Open in new window'), ('_self', 'Open in same window'), ('_parent', 'Delegate to parent'), ('_top', 'Delegate to top')], max_length=255, verbose_name='Target')),
+ ('attributes', djangocms_attributes_field.fields.AttributesField(blank=True, default=dict, verbose_name='Attributes')),
('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_static_article_preview_taccsitestaticnewsarticlepreview', serialize=False, to='cms.CMSPlugin')),
('media_support', models.CharField(choices=[('nested', 'Nest a single Picture / Image plugin inside this plugin.')], default='nested', max_length=255, verbose_name='How to Add an Image')),
('title_text', models.CharField(default='', help_text='The title for the article.', max_length=50, verbose_name='Title')),
@@ -34,7 +35,6 @@ class Migration(migrations.Migration):
('type_text', models.CharField(blank=True, help_text='The type of the article, ex: "Science News", "Press Release" (manual entry).', max_length=50, verbose_name='Type')),
('author_text', models.CharField(blank=True, help_text='The author of the article (manual entry).', max_length=50, verbose_name='Author')),
('publish_date', models.DateField(blank=True, help_text='The date the article was published (manual entry).', null=True, verbose_name='Date Published')),
- ('attributes', djangocms_attributes_field.fields.AttributesField(default=dict)),
('file_link', filer.fields.file.FilerFileField(blank=True, help_text='If provided links a file from the filer app.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='filer.File', verbose_name='File link')),
('internal_link', models.ForeignKey(blank=True, help_text='If provided, overrides the external link.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.Page', verbose_name='Internal link')),
],
@@ -53,12 +53,12 @@ class Migration(migrations.Migration):
('mailto', models.EmailField(blank=True, max_length=255, verbose_name='Email address')),
('phone', models.CharField(blank=True, max_length=255, verbose_name='Phone')),
('target', models.CharField(blank=True, choices=[('_blank', 'Open in new window'), ('_self', 'Open in same window'), ('_parent', 'Delegate to parent'), ('_top', 'Delegate to top')], max_length=255, verbose_name='Target')),
+ ('attributes', djangocms_attributes_field.fields.AttributesField(blank=True, default=dict, verbose_name='Attributes')),
('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_static_article_preview_taccsitestaticallocsarticlepreview', serialize=False, to='cms.CMSPlugin')),
('media_support', models.CharField(choices=[('nested', 'Nest a single Picture / Image plugin inside this plugin.')], default='nested', max_length=255, verbose_name='How to Add an Image')),
('title_text', models.CharField(default='', help_text='The title for the article.', max_length=50, verbose_name='Title')),
('expiry_date', models.DateField(blank=True, help_text='The date after which submissions are not accepted (manual entry). Format: YYYY-MM-DD', null=True, verbose_name='Submission End Date')),
('publish_date', models.DateField(blank=True, help_text='The date after which submissions are accepted (manual entry). Format: YYYY-MM-DD', null=True, verbose_name='Submission Start Date')),
- ('attributes', djangocms_attributes_field.fields.AttributesField(default=dict)),
('file_link', filer.fields.file.FilerFileField(blank=True, help_text='If provided links a file from the filer app.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='filer.File', verbose_name='File link')),
('internal_link', models.ForeignKey(blank=True, help_text='If provided, overrides the external link.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.Page', verbose_name='Internal link')),
],
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/models.py b/taccsite_cms/contrib/taccsite_static_article_preview/models.py
index 8bd7aec9a..ad8e5bd5e 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/models.py
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/models.py
@@ -1,10 +1,11 @@
from cms.models.pluginmodel import CMSPlugin
-from django.utils.translation import gettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from django.db import models
+
from djangocms_link.models import AbstractLink
-from djangocms_attributes_field import fields
+from taccsite_cms.contrib.helpers import clean_for_abstract_link
# Constants
@@ -100,15 +101,14 @@ class TaccsiteStaticNewsArticlePreview(AbstractLink):
author_text = create_author_text_field()
publish_date = create_publish_date_field()
- attributes = fields.AttributesField()
-
- link_is_optional = True # SEE: AbstractLink
-
-
+ link_is_optional = True
class Meta:
abstract = False
+ def clean(self):
+ clean_for_abstract_link(__class__, self)
+
class TaccsiteStaticAllocsArticlePreview(AbstractLink):
media_support = create_media_support_field(blank=False)
title_text = create_title_text_field(blank=False)
@@ -122,11 +122,10 @@ class TaccsiteStaticAllocsArticlePreview(AbstractLink):
help_text='The date after which submissions are accepted (manual entry).'
)
- attributes = fields.AttributesField()
-
- link_is_optional = True # SEE: AbstractLink
-
-
+ link_is_optional = True
class Meta:
abstract = False
+
+ def clean(self):
+ clean_for_abstract_link(__class__, self)
From c70df4d91ea9ae3b5fcac94ccdf4acf6e1f6b6e6 Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Tue, 29 Jun 2021 15:26:01 -0500
Subject: [PATCH 33/56] GH-93: DRY-er Docs for Static Article Plugins
---
.../contrib/_docs/taccsite_static_article.md | 65 +++++++++++++++++++
.../taccsite_static_article_list/README.md | 10 +--
.../taccsite_static_article_preview/README.md | 57 +---------------
3 files changed, 69 insertions(+), 63 deletions(-)
create mode 100644 taccsite_cms/contrib/_docs/taccsite_static_article.md
diff --git a/taccsite_cms/contrib/_docs/taccsite_static_article.md b/taccsite_cms/contrib/_docs/taccsite_static_article.md
new file mode 100644
index 000000000..5bfe94980
--- /dev/null
+++ b/taccsite_cms/contrib/_docs/taccsite_static_article.md
@@ -0,0 +1,65 @@
+# Static Article Plugins
+
+## Intention
+
+Support static addition of news articles that originate from a Core news site.
+
+A [dynamic solution that pulls form the Core news site](https://github.com/TACC/Core-CMS/issues/69) is preferable.
+
+But this is not available due to constrainst of architecture, time, or ability.
+
+## Architecture
+
+### (Currently) Add Image via Child Plugin Instead of Via Fields
+
+Instead, the image fields should be in the plugin, __not__ via a child plugin, but a solution has not yet been implemented.
+
+#### Hope for the Future
+
+The `AbstractLink` model was successfully extended.
+
+See:
+ - [./how-to-extend-django-cms-plugin.md](./how-to-extend-django-cms-plugin.md)
+ - [../taccsite_static_article_preview](../taccsite_static_article_preview)
+ - [../taccsite_static_article_list](../taccsite_static_article_list)
+
+#### Failed Attempt
+
+1. Build model so it extends `AbstractPicture` from `djangocms-picture`.
+2. Tweak model to sweep bugs under the rug.
+3. Quit when he was unable to resolve the error,
+ `TaccsiteStaticNewsArticlePreview has no field named 'cmsplugin_ptr_id'`
+ upon saving a plugin instance.
+4. Learn:
+ - [one should not try to reduce `AbstractPicture`](https://stackoverflow.com/a/3674714/11817077)
+ - [one should not subclass a subclass of `CMSPlugin`](https://github.com/django-cms/django-cms/blob/3.7.4/cms/models/pluginmodel.py#L104)
+
+#### Abandoned Code
+
+```python
+from djangocms_picture.models import AbstractPicture
+
+# To allow user to not set image
+# FAQ: Emptying the clean() method avoids picture validation
+# SEE: https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/models.py#L278
+def skip_image_validation():
+ pass
+
+class TaccsiteStaticNewsArticlePreview(AbstractPicture):
+ #
+ # …
+ #
+
+ # Remove error-prone attribute from parent class
+ # FAQ: Avoid error when running `makemigrations`:
+ # "You are trying to add a non-nullable field 'cmsplugin_ptr' […]"
+ # SEE: https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/models.py#L212
+ # SEE: https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/models.py#L234
+ cmsplugin_ptr = None
+
+ class Meta:
+ abstract = False
+
+ def clean(self):
+ skip_image_validation()
+```
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/README.md b/taccsite_cms/contrib/taccsite_static_article_list/README.md
index 762de72ec..81f610412 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/README.md
+++ b/taccsite_cms/contrib/taccsite_static_article_list/README.md
@@ -1,9 +1,3 @@
-# Static Articles
+# Static Article List
-## Intention
-
-Support static addition of news articles that originate from a Core news site.
-
-A [dynamic solution that pulls form the Core news site](https://github.com/TACC/Core-CMS/issues/69) is preferable.
-
-But this is not available due to constrainst of architecture, time, or ability.
+See [./_docs/taccsite_static_article.md](./_docs/taccsite_static_article.md).
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/README.md b/taccsite_cms/contrib/taccsite_static_article_preview/README.md
index 0a64962a9..943596354 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/README.md
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/README.md
@@ -1,56 +1,3 @@
-# Static Articles
+# Static Article Preview
-## Intention
-
-Support static addition of news articles that originate from a Core news site.
-
-A [dynamic solution that pulls form the Core news site](https://github.com/TACC/Core-CMS/issues/69) is preferable.
-
-But this is not available due to constrainst of architecture, time, or ability.
-
-## Architectural Decisions
-
-## Add Image via Child Plugin Instead of Via Fields
-
-Ideally, the image fields are in the plugin, not a child plugin.
-
-Wesley failed to achieve that functionality:
-
-1. Build model so it extends `AbstractPicture` from `djangocms-picture`.
-2. Tweak model to sweep bugs under the rug.
-3. Quit when he was unable to resolve the error,
- `TaccsiteStaticNewsArticlePreview has no field named 'cmsplugin_ptr_id'`
- upon saving a plugin instance.
-4. Learn:
- - [one should not try to reduce `AbstractPicture`](https://stackoverflow.com/a/3674714/11817077)
- - [one should not subclass a subclass of `CMSPlugin`](https://github.com/django-cms/django-cms/blob/3.7.4/cms/models/pluginmodel.py#L104)
-
-This is the relevant code he abandoned:
-
-```python
-from djangocms_picture.models import AbstractPicture
-
-# To allow user to not set image
-# FAQ: Emptying the clean() method avoids picture validation
-# SEE: https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/models.py#L278
-def skip_image_validation():
- pass
-
-class TaccsiteStaticNewsArticlePreview(AbstractPicture):
- #
- # …
- #
-
- # Remove error-prone attribute from parent class
- # FAQ: Avoid error when running `makemigrations`:
- # "You are trying to add a non-nullable field 'cmsplugin_ptr' […]"
- # SEE: https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/models.py#L212
- # SEE: https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/models.py#L234
- cmsplugin_ptr = None
-
- class Meta:
- abstract = False
-
- def clean(self):
- skip_image_validation()
-```
+See [./_docs/taccsite_static_article.md](./_docs/taccsite_static_article.md).
From 888af57df0631c52e2782cbfca43c2aa7f053b8c Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Tue, 29 Jun 2021 16:50:54 -0500
Subject: [PATCH 34/56] =?UTF-8?q?GH-93:=20Add"=20Docs"=20Article=20plugins?=
=?UTF-8?q?=20=E2=9A=A0=EF=B8=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
⚠️ Known Issues:
- Lacks support for `rows-gapless` and `row-divided` together.
---
.../cms_plugins.py | 12 ++++++
.../cms_plugins.py | 26 +++++++++++-
.../0002_taccsitestaticdocsarticlepreview.py | 41 +++++++++++++++++++
.../taccsite_static_article_preview/models.py | 12 ++++++
.../templates/static_article_preview.html | 2 +
.../_imports/components/c-article-preview.css | 40 +++++++++++-------
6 files changed, 117 insertions(+), 16 deletions(-)
create mode 100644 taccsite_cms/contrib/taccsite_static_article_preview/migrations/0002_taccsitestaticdocsarticlepreview.py
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py b/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
index ac9e6a376..09a626ead 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
@@ -129,3 +129,15 @@ class TaccsiteAllocsArticleListPlugin(AbstractArticleListPlugin):
child_classes = [
'TaccsiteStaticAllocsArticlePreviewPlugin'
]
+
+@plugin_pool.register_plugin
+class TaccsiteDocsArticleListPlugin(AbstractArticleListPlugin):
+ """
+ Components > "Article List" Plugin
+ https://confluence.tacc.utexas.edu/x/OIAjCQ
+ """
+ name = _('Document Article List (Static)')
+
+ child_classes = [
+ 'TaccsiteStaticDocsArticlePreviewPlugin'
+ ]
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py b/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
index 611d7eb0f..c27ef9640 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
@@ -17,6 +17,7 @@
MEDIA_SUPPORT_CHOICES,
TaccsiteStaticNewsArticlePreview,
TaccsiteStaticAllocsArticlePreview,
+ TaccsiteStaticDocsArticlePreview,
)
# Helpers
@@ -117,7 +118,7 @@ def get_readonly_fields(self, request, obj=None):
return []
# Plugins
-# TODO: Add `TaccsiteStatic___ArticlePreviewPlugin` (Docs, Events)
+# TODO: Add `TaccsiteStaticEventsArticlePreviewPlugin`
@plugin_pool.register_plugin
class TaccsiteStaticNewsArticlePreviewPlugin(AbstractArticlePreviewWithMediaPlugin):
@@ -204,3 +205,26 @@ def render(self, context, instance, placeholder):
'close_date_time_period': close_date_time_period,
})
return context
+
+@plugin_pool.register_plugin
+class TaccsiteStaticDocsArticlePreviewPlugin(AbstractArticlePreviewPlugin):
+ """
+ Components > "(Static) Document Article Preview" Plugin
+ https://confluence.tacc.utexas.edu/x/OYAjCQ
+ """
+ model = TaccsiteStaticDocsArticlePreview
+ name = _('Document Article Preview (Static)')
+
+ parent_classes = [
+ 'TaccsiteDocsArticleListPlugin'
+ ]
+
+ fieldsets = insert_at_position(0, AbstractArticlePreviewPlugin.fieldsets, [
+ (None, {
+ 'fields': ('title_text', 'abstract_text')
+ }),
+ ])
+
+ # Custom
+
+ kind = 'docs'
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0002_taccsitestaticdocsarticlepreview.py b/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0002_taccsitestaticdocsarticlepreview.py
new file mode 100644
index 000000000..f6ebbc681
--- /dev/null
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0002_taccsitestaticdocsarticlepreview.py
@@ -0,0 +1,41 @@
+# Generated by Django 2.2.16 on 2021-06-29 21:15
+
+from django.db import migrations, models
+import django.db.models.deletion
+import djangocms_attributes_field.fields
+import djangocms_link.validators
+import filer.fields.file
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('filer', '0012_file_mime_type'),
+ ('cms', '0022_auto_20180620_1551'),
+ ('taccsite_static_article_preview', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='TaccsiteStaticDocsArticlePreview',
+ fields=[
+ ('template', models.CharField(choices=[('default', 'Default')], default='default', max_length=255, verbose_name='Template')),
+ ('name', models.CharField(blank=True, max_length=255, verbose_name='Display name')),
+ ('external_link', models.CharField(blank=True, help_text='Provide a link to an external source.', max_length=2040, validators=[djangocms_link.validators.IntranetURLValidator(intranet_host_re=None)], verbose_name='External link')),
+ ('anchor', models.CharField(blank=True, help_text='Appends the value only after the internal or external link. Do not include a preceding "#" symbol.', max_length=255, verbose_name='Anchor')),
+ ('mailto', models.EmailField(blank=True, max_length=255, verbose_name='Email address')),
+ ('phone', models.CharField(blank=True, max_length=255, verbose_name='Phone')),
+ ('target', models.CharField(blank=True, choices=[('_blank', 'Open in new window'), ('_self', 'Open in same window'), ('_parent', 'Delegate to parent'), ('_top', 'Delegate to top')], max_length=255, verbose_name='Target')),
+ ('attributes', djangocms_attributes_field.fields.AttributesField(blank=True, default=dict, verbose_name='Attributes')),
+ ('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_static_article_preview_taccsitestaticdocsarticlepreview', serialize=False, to='cms.CMSPlugin')),
+ ('title_text', models.CharField(default='', help_text='The title for the article.', max_length=50, verbose_name='Title')),
+ ('abstract_text', models.TextField(default='', help_text='A summary of the article', verbose_name='Abstract')),
+ ('file_link', filer.fields.file.FilerFileField(blank=True, help_text='If provided links a file from the filer app.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='filer.File', verbose_name='File link')),
+ ('internal_link', models.ForeignKey(blank=True, help_text='If provided, overrides the external link.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.Page', verbose_name='Internal link')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('cms.cmsplugin',),
+ ),
+ ]
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/models.py b/taccsite_cms/contrib/taccsite_static_article_preview/models.py
index ad8e5bd5e..09c061864 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/models.py
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/models.py
@@ -129,3 +129,15 @@ class Meta:
def clean(self):
clean_for_abstract_link(__class__, self)
+
+class TaccsiteStaticDocsArticlePreview(AbstractLink):
+ title_text = create_title_text_field(blank=False)
+ abstract_text = create_abstract_text_field(blank=False)
+
+ link_is_optional = True
+
+ class Meta:
+ abstract = False
+
+ def clean(self):
+ clean_for_abstract_link(__class__, self)
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html b/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html
index 89bf2561d..3b57acc2e 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html
@@ -5,12 +5,14 @@
{{ instance.attributes_str }}
>
{# Media e.g. image thumbnail #}
+ {% if kind == 'news' or kind == 'allocs' %}
{# HACK: Forced to use a wrapper because we cannot control markup #}
{% for plugin_instance in instance.child_plugin_instances %}
{% render_plugin plugin_instance %}
{% endfor %}
{% endif %}
From 4941a469b4c8a89771e04b9c129238640b412f02 Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Wed, 30 Jun 2021 10:52:09 -0500
Subject: [PATCH 39/56] =?UTF-8?q?GH-93:=20Run=20clean=5Ffor=5F=E2=80=A6=20?=
=?UTF-8?q?before=20custom=20validation?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
To catch only parent validation errors, not custom ones:
- Run `clean_for_abstract_link` before any custom validation.
---
taccsite_cms/contrib/helpers.py | 4 +++-
taccsite_cms/contrib/taccsite_static_article_list/models.py | 6 +++---
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/taccsite_cms/contrib/helpers.py b/taccsite_cms/contrib/helpers.py
index dce0c3819..5e54eefd0 100644
--- a/taccsite_cms/contrib/helpers.py
+++ b/taccsite_cms/contrib/helpers.py
@@ -163,7 +163,7 @@ def add_view(self,request, form_url='', extra_context=None):
# SEE: https://github.com/django-cms/djangocms-link/blob/3.0.0/djangocms_link/models.py#L48
def clean_for_abstract_link(model, self):
"""
- Intercept and manipulate validation on `AbstractLink` so that it suits TACC's minimal subclassing of it.
+ Intercept and manipulate validation on `AbstractLink` so that it suits TACC's minimal subclassing of it. (To catch only parent validation errors, not custom ones, run this before any custom validation.)
Usage:
```
@@ -171,6 +171,8 @@ def clean_for_abstract_link(model, self):
def clean(self):
clean_for_abstract_link(__class__, self)
+
+ ...
```
"""
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/models.py b/taccsite_cms/contrib/taccsite_static_article_list/models.py
index 98da59c63..ad6db758c 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/models.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/models.py
@@ -66,9 +66,11 @@ def get_short_description(self):
return self.title_text
def clean(self):
+ clean_for_abstract_link(__class__, self)
+
# If user provided link text, then require link
if self.name and not self.get_link():
- err.error_list[i] = ValidationError(
+ raise ValidationError(
_('Please provide a footer link or delete its display name.'), code='invalid')
# If user mix-and-matched layout and styles, then explain their mistake
@@ -87,7 +89,5 @@ def clean(self):
code='invalid'
)
- clean_for_abstract_link(__class__, self)
-
class Meta:
abstract = False
From f94c39f690d7094a0ed5af7389d0de98d1dd2e33 Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Wed, 30 Jun 2021 11:10:16 -0500
Subject: [PATCH 40/56] GH-93: Fix bad internationalization format
How to Use `_()` with Variables:
https://docs.djangoproject.com/en/2.2/topics/i18n/translation/#internationalization-in-python-code
How to Use `f''` with `_()` and Why Not To:
https://stackoverflow.com/a/56264202/11817077
---
.../taccsite_static_article_list/models.py | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/models.py b/taccsite_cms/contrib/taccsite_static_article_list/models.py
index ad6db758c..9379510ef 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/models.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/models.py
@@ -46,7 +46,7 @@ class TaccsiteArticleList(AbstractLink):
layout_type = models.CharField(
verbose_name=_('Layout Option'),
- help_text=_(f'Layout of the articles within. Notice: All {COLS_CHOICES_NAME} become multiple rows when screen width is narrow.'),
+ help_text=_('Layout of the articles within. Notice: All %(col_layouts)s become multiple rows when screen width is narrow.') % { 'col_layouts': COLS_CHOICES_NAME },
choices=LAYOUT_CHOICES,
default=LAYOUT_CHOICES[0][0],
blank=False,
@@ -71,21 +71,28 @@ def clean(self):
# If user provided link text, then require link
if self.name and not self.get_link():
raise ValidationError(
- _('Please provide a footer link or delete its display name.'), code='invalid')
+ _('Please provide a footer link or delete its display name.'), code='invalid'
+ )
# If user mix-and-matched layout and styles, then explain their mistake
layout_name = force_text(
- self._meta.get_field('layout_type').verbose_name)
+ self._meta.get_field('layout_type').verbose_name )
style_name = force_text(
- self._meta.get_field('style_type').verbose_name)
+ self._meta.get_field('style_type').verbose_name )
if 'cols' in self.layout_type and 'rows' in self.style_type:
raise ValidationError(
- _(f'If you choose a {layout_name} for {ROWS_CHOICES_NAME}, then choose a {style_name} for {ROWS_CHOICES_NAME} (or no {style_name}).'),
+ _('If you choose a %(layout)s from %(row_layouts)s, then choose a %(style)s from %(row_layouts)s (or no %(style)s).') % {
+ 'style': style_name, 'layout': layout_name,
+ 'row_layouts': ROWS_CHOICES_NAME
+ },
code='invalid'
)
if 'rows' in self.layout_type and 'cols' in self.style_type:
raise ValidationError(
- _(f'If you choose a {layout_name} for {COLS_CHOICES_NAME}, then choose a {style_name} for {COLS_CHOICES_NAME} (or no {style_name}).'),
+ _('If you choose a %(layout)s from %(col_layouts)s, then choose a %(style)s from %(col_layouts)s (or no %(style)s).') % {
+ 'style': style_name, 'layout': layout_name,
+ 'col_layouts': COLS_CHOICES_NAME
+ },
code='invalid'
)
From 55616fef3613e922f65ddb5daf36db96feb44224 Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Wed, 30 Jun 2021 11:13:53 -0500
Subject: [PATCH 41/56] GH-93: Add" Events" Article plugins
---
.../cms_plugins.py | 12 ++++++
.../cms_plugins.py | 41 +++++++++++++++++-
...0003_taccsitestaticeventsarticlepreview.py | 43 +++++++++++++++++++
.../taccsite_static_article_preview/models.py | 36 ++++++++++++++++
.../templates/static_article_preview.html | 20 +++++++++
5 files changed, 151 insertions(+), 1 deletion(-)
create mode 100644 taccsite_cms/contrib/taccsite_static_article_preview/migrations/0003_taccsitestaticeventsarticlepreview.py
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py b/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
index be5f4f504..fdb6795ae 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
@@ -140,3 +140,15 @@ class TaccsiteDocsArticleListPlugin(AbstractArticleListPlugin):
child_classes = [
'TaccsiteStaticDocsArticlePreviewPlugin'
]
+
+@plugin_pool.register_plugin
+class TaccsiteEventsArticleListPlugin(AbstractArticleListPlugin):
+ """
+ Components > "Article List" Plugin
+ https://confluence.tacc.utexas.edu/x/OIAjCQ
+ """
+ name = _('Event Article List (Static)')
+
+ child_classes = [
+ 'TaccsiteStaticEventsArticlePreviewPlugin'
+ ]
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py b/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
index 15ef941c3..7abec7b96 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
@@ -18,6 +18,7 @@
TaccsiteStaticNewsArticlePreview,
TaccsiteStaticAllocsArticlePreview,
TaccsiteStaticDocsArticlePreview,
+ TaccsiteStaticEventsArticlePreview,
)
# Helpers
@@ -118,7 +119,6 @@ def get_readonly_fields(self, request, obj=None):
return []
# Plugins
-# TODO: Add `TaccsiteStaticEventsArticlePreviewPlugin`
@plugin_pool.register_plugin
class TaccsiteStaticNewsArticlePreviewPlugin(AbstractArticlePreviewWithMediaPlugin):
@@ -228,3 +228,42 @@ class TaccsiteStaticDocsArticlePreviewPlugin(AbstractArticlePreviewPlugin):
# Custom
kind = 'docs'
+
+@plugin_pool.register_plugin
+class TaccsiteStaticEventsArticlePreviewPlugin(AbstractArticlePreviewPlugin):
+ """
+ Components > "(Static) Event Article Preview" Plugin
+ https://confluence.tacc.utexas.edu/x/OYAjCQ
+ """
+ model = TaccsiteStaticEventsArticlePreview
+ name = _('Event Article Preview (Static)')
+
+ parent_classes = [
+ 'TaccsiteEventsArticleListPlugin'
+ ]
+
+ fieldsets = insert_at_position(0, AbstractArticlePreviewPlugin.fieldsets, [
+ (None, {
+ 'fields': (
+ ('publish_date', 'expiry_date'),
+ 'title_text',
+ 'abstract_text'
+ )
+ }),
+ ])
+
+ # Custom
+
+ kind = 'events'
+
+ # Render
+
+ def render(self, context, instance, placeholder):
+ context = super().render(context, instance, placeholder)
+ request = context['request']
+
+ context.update({
+ 'open_date': instance.publish_date,
+ 'close_date': instance.expiry_date,
+ })
+ return context
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0003_taccsitestaticeventsarticlepreview.py b/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0003_taccsitestaticeventsarticlepreview.py
new file mode 100644
index 000000000..97d80bd3f
--- /dev/null
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0003_taccsitestaticeventsarticlepreview.py
@@ -0,0 +1,43 @@
+# Generated by Django 2.2.16 on 2021-06-30 15:19
+
+from django.db import migrations, models
+import django.db.models.deletion
+import djangocms_attributes_field.fields
+import djangocms_link.validators
+import filer.fields.file
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('filer', '0012_file_mime_type'),
+ ('cms', '0022_auto_20180620_1551'),
+ ('taccsite_static_article_preview', '0002_taccsitestaticdocsarticlepreview'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='TaccsiteStaticEventsArticlePreview',
+ fields=[
+ ('template', models.CharField(choices=[('default', 'Default')], default='default', max_length=255, verbose_name='Template')),
+ ('name', models.CharField(blank=True, max_length=255, verbose_name='Display name')),
+ ('external_link', models.CharField(blank=True, help_text='Provide a link to an external source.', max_length=2040, validators=[djangocms_link.validators.IntranetURLValidator(intranet_host_re=None)], verbose_name='External link')),
+ ('anchor', models.CharField(blank=True, help_text='Appends the value only after the internal or external link. Do not include a preceding "#" symbol.', max_length=255, verbose_name='Anchor')),
+ ('mailto', models.EmailField(blank=True, max_length=255, verbose_name='Email address')),
+ ('phone', models.CharField(blank=True, max_length=255, verbose_name='Phone')),
+ ('target', models.CharField(blank=True, choices=[('_blank', 'Open in new window'), ('_self', 'Open in same window'), ('_parent', 'Delegate to parent'), ('_top', 'Delegate to top')], max_length=255, verbose_name='Target')),
+ ('attributes', djangocms_attributes_field.fields.AttributesField(blank=True, default=dict, verbose_name='Attributes')),
+ ('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_static_article_preview_taccsitestaticeventsarticlepreview', serialize=False, to='cms.CMSPlugin')),
+ ('title_text', models.CharField(default='', help_text='The title for the article.', max_length=50, verbose_name='Title')),
+ ('abstract_text', models.TextField(default='', help_text='A summary of the article', verbose_name='Abstract')),
+ ('expiry_date', models.DateField(blank=True, help_text='The date upon which the event starts (manual entry). Format: YYYY-MM-DD', null=True, verbose_name='Event End Date')),
+ ('publish_date', models.DateField(blank=True, help_text='The date after which the event ends (manual entry). Format: YYYY-MM-DD', null=True, verbose_name='Event Start Date')),
+ ('file_link', filer.fields.file.FilerFileField(blank=True, help_text='If provided links a file from the filer app.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='filer.File', verbose_name='File link')),
+ ('internal_link', models.ForeignKey(blank=True, help_text='If provided, overrides the external link.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.Page', verbose_name='Internal link')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('cms.cmsplugin',),
+ ),
+ ]
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/models.py b/taccsite_cms/contrib/taccsite_static_article_preview/models.py
index 09c061864..7a4a06eb1 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/models.py
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/models.py
@@ -1,6 +1,8 @@
from cms.models.pluginmodel import CMSPlugin
+from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
+from django.utils.encoding import force_text
from django.db import models
from djangocms_link.models import AbstractLink
@@ -141,3 +143,37 @@ class Meta:
def clean(self):
clean_for_abstract_link(__class__, self)
+
+class TaccsiteStaticEventsArticlePreview(AbstractLink):
+ title_text = create_title_text_field(blank=False)
+ abstract_text = create_abstract_text_field(blank=False)
+
+ expiry_date = create_expiry_date_field(
+ verbose_name='Event End Date',
+ help_text='The date upon which the event starts (manual entry).'
+ )
+ publish_date = create_publish_date_field(
+ verbose_name='Event Start Date',
+ help_text='The date after which the event ends (manual entry).'
+ )
+
+ link_is_optional = True
+
+ class Meta:
+ abstract = False
+
+ def clean(self):
+ clean_for_abstract_link(__class__, self)
+
+ # If user provided link text, then require link
+ if not self.publish_date and not self.expiry_date:
+ end_date_name = force_text(
+ self._meta.get_field('expiry_date').verbose_name )
+ start_date_name = force_text(
+ self._meta.get_field('publish_date').verbose_name )
+ raise ValidationError(
+ _('Provide either a %(start_date)s or an %(end_date)s.') % {
+ 'start_date': start_date_name, 'end_date': end_date_name
+ },
+ code='invalid'
+ )
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html b/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html
index c54684681..20e721e56 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/templates/static_article_preview.html
@@ -90,6 +90,26 @@
{% endif %}
+ {% if kind == 'events' %}
+
+ {# WARNING: Dev Idea: A single day range may show as just a single date #}
+ {% if open_date or open_date == close_date %}
+
+ {{ open_date|date:"m/d/y" }}
+
+ {% endif %}
+ {# WARNING: Dev Idea: A lone close date may start with trailing dash #}
+ {% if close_date and close_date != open_date %}
+ —
+
+ {{ close_date|date:"m/d/y" }}
+
+ {% endif %}
+
+ {% endif %}
+
{# Type #}
From 90b759ce83a0ed28d232a4dd4db220ff9a74d93c Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Wed, 30 Jun 2021 11:15:15 -0500
Subject: [PATCH 42/56] GH-93: Design Fix: Bigger space after See All icon
---
.../site_cms/css/src/_imports/components/c-article-list.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/taccsite_cms/static/site_cms/css/src/_imports/components/c-article-list.css b/taccsite_cms/static/site_cms/css/src/_imports/components/c-article-list.css
index 8819e8dcc..70588f335 100644
--- a/taccsite_cms/static/site_cms/css/src/_imports/components/c-article-list.css
+++ b/taccsite_cms/static/site_cms/css/src/_imports/components/c-article-list.css
@@ -132,7 +132,7 @@ Styleguide Components.ArticleList
/* Icon */
.c-article-list__link-icon {
- margin-right: 0.5em;
+ margin-right: 0.75em;
font-size: 1.4rem;
vertical-align: text-bottom;
From 32a3c47b5ded96c376eff65063a7262763b2f3a5 Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Wed, 30 Jun 2021 15:00:24 -0500
Subject: [PATCH 43/56] GH-93: Couple class names and choices
---
taccsite_cms/contrib/helpers.py | 40 ++++++++++++++++++-
.../cms_plugins.py | 22 ++--------
.../taccsite_static_article_list/constants.py | 30 ++++++++++++++
.../taccsite_static_article_list/models.py | 34 ++++++++++------
.../cms_plugins.py | 21 +++++-----
5 files changed, 104 insertions(+), 43 deletions(-)
create mode 100644 taccsite_cms/contrib/taccsite_static_article_list/constants.py
diff --git a/taccsite_cms/contrib/helpers.py b/taccsite_cms/contrib/helpers.py
index 5e54eefd0..be72a13bb 100644
--- a/taccsite_cms/contrib/helpers.py
+++ b/taccsite_cms/contrib/helpers.py
@@ -1,3 +1,39 @@
+def get_choices(choice_dict):
+ """Get a sequence for a Django model field choices from a dictionary.
+
+ :param Dict[str, Dict[str, str]] dictionary: choice as key for dictionary of classnames and descriptions
+ :return: a sequence for django.db.models.CharField.choices
+ :rtype: List[Tuple[str, str], ...]
+ """
+ choices = []
+
+ for key, data in choice_dict.items():
+ choice = (key, data['description'])
+ choices.append(choice)
+
+ return choices
+
+
+
+def filter_choices_by_prefix(choices, prefix):
+ """Reduce sequence of choices to items whose values begin with given string
+
+ :param List[Tuple[str, str], ...] choices: the sequence to filter
+ :param str prefix: the starting text required of an item value to retain it
+ :returns: a sequence for django.db.models.CharField.choices
+ :rtype: List[Tuple[str, str], ...]
+ """
+ new_choices = []
+
+ for choice in choices:
+ should_keep = choice[0].startswith(prefix)
+ if should_keep:
+ new_choices.append(choice)
+
+ return new_choices
+
+
+
# SEE: https://github.com/django-cms/djangocms-bootstrap4/blob/master/djangocms_bootstrap4/helpers.py
def concat_classnames(classes):
"""Concatenates a list of classes (without failing on None)"""
@@ -63,14 +99,14 @@ def which_date_is_nearest_today(date_a, date_b, preferred_time_period):
:returns:
A tuple of tuples:
- (
+ ((
``boolean`` of whether ``date_a`` is nearest,
``string`` of ``date_a`` time period ``past``/``today``/``future``
),
(
``boolean`` of whether ``date_b`` is nearest,
``string`` of ``date_b`` time period ``past``/``today``/``future``
- ),
+ )),
:rtype: tuple
"""
today = date.today()
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py b/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
index fdb6795ae..ec1c7aa43 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
@@ -9,33 +9,17 @@
)
from .models import TaccsiteArticleList
+from .constants import LAYOUT_DICT, STYLE_DICT
# Helpers
def get_layout_classname(value):
"""Get layout class based on value."""
-
- # HELP: Should we couple this map to LAYOUT_CHOICES? If so, how?
- switcher = {
- 'cols-widest-2-even': 'c-article-list--layout-a',
- 'cols-widest-2-wide-narr': 'c-article-list--layout-b',
- 'cols-widest-2-narr-wide': 'c-article-list--layout-c',
- 'cols-widest-3-even': 'c-article-list--layout-d',
- 'rows-always-N-even': 'c-article-list--layout-e'
- ' ' + 'c-article-list--style-gapless',
- }
-
- return switcher.get(value, '')
+ return LAYOUT_DICT.get(value, {}).get('classname')
def get_style_classname(value):
"""Get style class based on value."""
-
- # HELP: Should we couple this map to STYLE_CHOICES? If so, how?
- switcher = {
- 'rows-divided': 'c-article-list--style-divided',
- }
-
- return switcher.get(value, '')
+ return STYLE_DICT.get(value, {}).get('classname')
# Bases
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/constants.py b/taccsite_cms/contrib/taccsite_static_article_list/constants.py
new file mode 100644
index 000000000..1e522a44a
--- /dev/null
+++ b/taccsite_cms/contrib/taccsite_static_article_list/constants.py
@@ -0,0 +1,30 @@
+LAYOUT_DICT = {
+ 'cols-widest-2-even_width': {
+ 'classname': 'c-article-list--layout-a',
+ 'description': '2 Equal-Width Columns',
+ },
+ 'cols-widest-2-wide_narrow': {
+ 'classname': 'c-article-list--layout-b',
+ 'description': '2 Columns: 1 Wide, 1 Narrow',
+ },
+ 'cols-widest-2-narrow_wide': {
+ 'classname': 'c-article-list--layout-c',
+ 'description': '2 Columns: 1 Narrow, 1 Wide',
+ },
+ 'cols-widest-3-even_width': {
+ 'classname': 'c-article-list--layout-d',
+ 'description': '3 Equal-Width Columns',
+ },
+ 'rows-always-N-even_height': {
+ 'classname': 'c-article-list--layout-e'
+ ' ' + 'c-article-list--style-gapless',
+ 'description': 'Multiple Rows',
+ },
+}
+
+STYLE_DICT = {
+ 'rows-divided': {
+ 'classname': 'c-article-list--style-divided',
+ 'description': 'Dividers Between Articles',
+ },
+}
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/models.py b/taccsite_cms/contrib/taccsite_static_article_list/models.py
index 9379510ef..c8e7f1941 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/models.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/models.py
@@ -5,7 +5,13 @@
from djangocms_link.models import AbstractLink
-from taccsite_cms.contrib.helpers import clean_for_abstract_link
+from taccsite_cms.contrib.helpers import (
+ get_choices,
+ filter_choices_by_prefix,
+ clean_for_abstract_link,
+)
+
+from .constants import LAYOUT_DICT, STYLE_DICT
# Constants
@@ -14,20 +20,22 @@
COLS_CHOICES_NAME = _('Column Layouts')
LAYOUT_CHOICES = (
- (ROWS_CHOICES_NAME, (
- ('rows-always-N-even', _('Multiple Rows')),
- )),
- (COLS_CHOICES_NAME, (
- ('cols-widest-2-even', _('2 Equal-Width Columns')),
- ('cols-widest-2-wide-narr', _('2 Columns: 1 Wide, 1 Narrow')),
- ('cols-widest-2-narr-wide', _('2 Columns: 1 Narrow, 1 Wide')),
- ('cols-widest-3-even', _('3 Equal-Width Columns')),
- )),
+ ( ROWS_CHOICES_NAME, filter_choices_by_prefix(
+ get_choices(LAYOUT_DICT), 'row'
+ ) ),
+ ( COLS_CHOICES_NAME, filter_choices_by_prefix(
+ get_choices(LAYOUT_DICT), 'cols'
+ ) ),
)
STYLE_CHOICES = (
- (ROWS_CHOICES_NAME, (
- ('rows-divided', _('Dividers Between Articles')),
- )),
+ ( ROWS_CHOICES_NAME, filter_choices_by_prefix(
+ get_choices(STYLE_DICT), 'rows'
+ ) ),
+ # Disabled until some exist
+ # FAQ: Absent optgroup is easier for user to understand than empty optgroup
+ # ( COLS_CHOICES_NAME, filter_choices_by_prefix(
+ # get_choices(STYLE_DICT), 'cols'
+ # ) ),
)
# Models
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py b/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
index 7abec7b96..7358c8b62 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
@@ -21,19 +21,22 @@
TaccsiteStaticEventsArticlePreview,
)
+# Constants
+
+KIND_DICT = {
+ 'news': 'c-article-preview--news',
+ 'docs': 'c-article-preview--docs',
+ 'allocs': 'c-article-preview--allocs',
+ 'events': 'c-article-preview--events',
+}
+
# Helpers
+# FAQ: This exists to retireve classnames via consistently-named functions
+# SEE: taccsite_cms.contrib.taccsite_static_article_list.cms_plugins
def get_kind_classname(value):
"""Get kind class based on value."""
-
- switcher = {
- 'news': 'c-article-preview--news',
- 'docs': 'c-article-preview--docs',
- 'allocs': 'c-article-preview--allocs',
- 'events': 'c-article-preview--events',
- }
-
- return switcher.get(value, '')
+ return KIND_DICT[value]
# Bases
From a2df8a99bc28f1fdbc09557b4bac009a48f03787 Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Wed, 30 Jun 2021 15:28:52 -0500
Subject: [PATCH 44/56] Quick: Couple DIRECTION class names and choices
---
.../taccsite_blockquote/cms_plugins.py | 2 +-
.../contrib/taccsite_offset/cms_plugins.py | 12 ++++++-
.../contrib/taccsite_offset/models.py | 31 ++++++++-----------
3 files changed, 25 insertions(+), 20 deletions(-)
diff --git a/taccsite_cms/contrib/taccsite_blockquote/cms_plugins.py b/taccsite_cms/contrib/taccsite_blockquote/cms_plugins.py
index b345bfc5b..a5ee6ce65 100644
--- a/taccsite_cms/contrib/taccsite_blockquote/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_blockquote/cms_plugins.py
@@ -3,7 +3,7 @@
from django.utils.translation import gettext_lazy as _
from taccsite_cms.contrib.helpers import concat_classnames
-from taccsite_cms.contrib.taccsite_offset.models import get_direction_classname
+from taccsite_cms.contrib.taccsite_offset.cms_plugins import get_direction_classname
from .models import TaccsiteBlockquote
diff --git a/taccsite_cms/contrib/taccsite_offset/cms_plugins.py b/taccsite_cms/contrib/taccsite_offset/cms_plugins.py
index c2d1da73e..9b32449af 100644
--- a/taccsite_cms/contrib/taccsite_offset/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_offset/cms_plugins.py
@@ -5,7 +5,17 @@
from taccsite_cms.contrib.helpers import concat_classnames
-from .models import TaccsiteOffset, get_direction_classname
+from .models import TaccsiteOffset, DIRECTION_DICT
+
+# Helpers
+
+# FAQ: This exists to retireve classnames via consistently-named functions
+# SEE: taccsite_cms.contrib.taccsite_static_article_list.cms_plugins
+def get_direction_classname(value):
+ """Get direction class based on value."""
+ return DIRECTION_DICT.get(value, {}).get('classname')
+
+# Plugins
@plugin_pool.register_plugin
class TaccsiteOffsetPlugin(CMSPluginBase):
diff --git a/taccsite_cms/contrib/taccsite_offset/models.py b/taccsite_cms/contrib/taccsite_offset/models.py
index 149941b80..51d504a8d 100644
--- a/taccsite_cms/contrib/taccsite_offset/models.py
+++ b/taccsite_cms/contrib/taccsite_offset/models.py
@@ -5,26 +5,21 @@
from djangocms_attributes_field import fields
-# Constants
-
-DIRECTION_CHOICES = (
- ('left', _('Left')),
- # ('center', _('Center')), # GH-66: Support centered offset content
- ('right', _('Right')),
-)
-
-# Helpers
+from taccsite_cms.contrib.helpers import get_choices
-def get_direction_classname(value):
- """Get direction class based on value."""
-
- # TODO: Couple this map to DIRECTION_CHOICES
- switcher = {
- 'right': 'o-offset-content--right',
- 'left': 'o-offset-content--left'
- }
+# Constants
- return switcher.get(value, '')
+DIRECTION_DICT = {
+ 'left': {
+ 'classname': 'o-offset-content--left',
+ 'description': 'Left',
+ },
+ 'right': {
+ 'classname': 'o-offset-content--right',
+ 'description': 'Right',
+ },
+}
+DIRECTION_CHOICES = get_choices(DIRECTION_DICT)
# Models
From 85493a48223d16c4580c18cfd120c7b69afede5a Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Wed, 30 Jun 2021 16:01:50 -0500
Subject: [PATCH 45/56] GH-93: Add "Gapless" style to Columns Layouts
---
.../contrib/taccsite_static_article_list/constants.py | 4 ++++
.../contrib/taccsite_static_article_list/models.py | 8 +++-----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/constants.py b/taccsite_cms/contrib/taccsite_static_article_list/constants.py
index 1e522a44a..1c9c7fbc0 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/constants.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/constants.py
@@ -27,4 +27,8 @@
'classname': 'c-article-list--style-divided',
'description': 'Dividers Between Articles',
},
+ 'cols-gapless': {
+ 'classname': 'c-article-list--style-gapless',
+ 'description': 'Remove Gaps Between Articles',
+ },
}
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/models.py b/taccsite_cms/contrib/taccsite_static_article_list/models.py
index c8e7f1941..ce51e5122 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/models.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/models.py
@@ -31,11 +31,9 @@
( ROWS_CHOICES_NAME, filter_choices_by_prefix(
get_choices(STYLE_DICT), 'rows'
) ),
- # Disabled until some exist
- # FAQ: Absent optgroup is easier for user to understand than empty optgroup
- # ( COLS_CHOICES_NAME, filter_choices_by_prefix(
- # get_choices(STYLE_DICT), 'cols'
- # ) ),
+ ( COLS_CHOICES_NAME, filter_choices_by_prefix(
+ get_choices(STYLE_DICT), 'cols'
+ ) ),
)
# Models
From 64c301eb4697a679a9a32babee4d593094ab3b7c Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Thu, 1 Jul 2021 10:05:29 -0500
Subject: [PATCH 46/56] GH-93: Noop: Consistent Comments & New Lines
---
.../bootstrap4_djangocms_picture/cms_plugins.py | 1 +
.../taccsite_static_article_list/cms_plugins.py | 10 ++++++++--
.../contrib/taccsite_static_article_list/models.py | 4 ++++
.../taccsite_static_article_preview/cms_plugins.py | 10 +++++++++-
.../contrib/taccsite_static_article_preview/models.py | 5 ++++-
5 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py b/taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py
index 939bb5069..99ce38a98 100644
--- a/taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py
+++ b/taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py
@@ -1,5 +1,6 @@
# Reregister unregistered PicturePlugin without uninstalling Bootstrap4's
# FAQ: A Bootstrap picture has superfluous options that are not always desirable
+# TODO: Drop try/except & load non-standard plugin set for migrated legacy sites
# FAQ: If we can import both plugins, then re-register PicturePlugin
# (because Bootstrap4Picture unregistered PicturePlugin)
try:
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py b/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
index ec1c7aa43..afe10e290 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
@@ -11,6 +11,8 @@
from .models import TaccsiteArticleList
from .constants import LAYOUT_DICT, STYLE_DICT
+
+
# Helpers
def get_layout_classname(value):
@@ -21,7 +23,9 @@ def get_style_classname(value):
"""Get style class based on value."""
return STYLE_DICT.get(value, {}).get('classname')
-# Bases
+
+
+# Abstracts
class AbstractArticleListPlugin(LinkPlugin):
"""
@@ -87,7 +91,9 @@ def render(self, context, instance, placeholder):
})
return context
-# Plugin
+
+
+# Plugins
@plugin_pool.register_plugin
class TaccsiteNewsArticleListPlugin(AbstractArticleListPlugin):
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/models.py b/taccsite_cms/contrib/taccsite_static_article_list/models.py
index ce51e5122..4b0f5fb23 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/models.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/models.py
@@ -13,6 +13,8 @@
from .constants import LAYOUT_DICT, STYLE_DICT
+
+
# Constants
ANY_CHOICES_NAME = _('Any Layouts')
@@ -36,6 +38,8 @@
) ),
)
+
+
# Models
class TaccsiteArticleList(AbstractLink):
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py b/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
index 7358c8b62..0e1f0d351 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/cms_plugins.py
@@ -21,6 +21,8 @@
TaccsiteStaticEventsArticlePreview,
)
+
+
# Constants
KIND_DICT = {
@@ -30,6 +32,8 @@
'events': 'c-article-preview--events',
}
+
+
# Helpers
# FAQ: This exists to retireve classnames via consistently-named functions
@@ -38,7 +42,9 @@ def get_kind_classname(value):
"""Get kind class based on value."""
return KIND_DICT[value]
-# Bases
+
+
+# Abstracts
class AbstractArticlePreviewPlugin(LinkPlugin, AbstractMaxChildrenPlugin):
module = 'TACC Site'
@@ -121,6 +127,8 @@ def get_readonly_fields(self, request, obj=None):
else: # i.e. user is creating instance
return []
+
+
# Plugins
@plugin_pool.register_plugin
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/models.py b/taccsite_cms/contrib/taccsite_static_article_preview/models.py
index 7a4a06eb1..8d45dd531 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/models.py
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/models.py
@@ -9,6 +9,8 @@
from taccsite_cms.contrib.helpers import clean_for_abstract_link
+
+
# Constants
MEDIA_SUPPORT_CHOICES = (
@@ -16,6 +18,8 @@
# ('direct', _('Choose / Define an image directly within this plugin.')),
)
+
+
# Helpers
# This field lets us:
@@ -92,7 +96,6 @@ def create_expiry_date_field(blank=True, help_text=None, verbose_name=None):
# Models
-# TODO: Add `TaccsiteStatic____ArticlePreview` (Docs, Events)
class TaccsiteStaticNewsArticlePreview(AbstractLink):
media_support = create_media_support_field(blank=False)
From 14d458bb6014c904dffafe47063c94b5ac9aaa7a Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Fri, 2 Jul 2021 10:25:46 -0500
Subject: [PATCH 47/56] GH-93: Up taccsite-custom, latest from its dev?
---
taccsite_custom | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/taccsite_custom b/taccsite_custom
index 10763d3f2..54855deaa 160000
--- a/taccsite_custom
+++ b/taccsite_custom
@@ -1 +1 @@
-Subproject commit 10763d3f26de748d4085dfee60e3385c9c48cb83
+Subproject commit 54855deaab10e46b6a7fd814e763b58719c484f7
From 2be3f36144f449ef62c1505659f8597c569466f6 Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Fri, 2 Jul 2021 14:13:44 -0500
Subject: [PATCH 48/56] GH-93: hard squash migrations
---
.../migrations/0001_initial.py | 6 +--
.../migrations/0002_auto_20210629_1711.py | 18 -------
.../migrations/0001_initial.py | 48 ++++++++++++++++++-
.../0002_taccsitestaticdocsarticlepreview.py | 41 ----------------
...0003_taccsitestaticeventsarticlepreview.py | 43 -----------------
5 files changed, 50 insertions(+), 106 deletions(-)
delete mode 100644 taccsite_cms/contrib/taccsite_static_article_list/migrations/0002_auto_20210629_1711.py
delete mode 100644 taccsite_cms/contrib/taccsite_static_article_preview/migrations/0002_taccsitestaticdocsarticlepreview.py
delete mode 100644 taccsite_cms/contrib/taccsite_static_article_preview/migrations/0003_taccsitestaticeventsarticlepreview.py
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/migrations/0001_initial.py b/taccsite_cms/contrib/taccsite_static_article_list/migrations/0001_initial.py
index 2831e5de2..521bbd070 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/migrations/0001_initial.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2.16 on 2021-06-29 19:41
+# Generated by Django 2.2.16 on 2021-07-02 19:13
from django.db import migrations, models
import django.db.models.deletion
@@ -30,8 +30,8 @@ class Migration(migrations.Migration):
('attributes', djangocms_attributes_field.fields.AttributesField(blank=True, default=dict, verbose_name='Attributes')),
('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_static_article_list_taccsitearticlelist', serialize=False, to='cms.CMSPlugin')),
('title_text', models.CharField(blank=True, help_text='The title at the top of the list.', max_length=100, verbose_name='Title Text')),
- ('layout_type', models.CharField(choices=[('Row Layouts', (('rows-always-N-even', 'Multiple Rows'),)), ('Column Layouts', (('cols-widest-2-even', '2 Equal-Width Columns'), ('cols-widest-2-wide-narr', '2 Columns: 1 Wide, 1 Narrow'), ('cols-widest-2-narr-wide', '2 Columns: 1 Narrow, 1 Wide'), ('cols-widest-3-even', '3 Equal-Width Columns')))], default='Row Layouts', help_text='Layout of the articles within. Notice: All Column Layouts become multiple rows when screen width is narrow.', max_length=255, verbose_name='Layout Option')),
- ('style_type', models.CharField(blank=True, choices=[('Row Layouts', (('rows-divided', 'Dividers Between Articles'), ('rows-gapless', 'Remove Gaps Between Articles'))), ('Column Layouts', (('cols-gapless', 'Remove Gaps Between Articles'),))], help_text='Optional styles for the list itself.', max_length=255, verbose_name='Style Option')),
+ ('layout_type', models.CharField(choices=[('Row Layouts', [('rows-always-N-even_height', 'Multiple Rows')]), ('Column Layouts', [('cols-widest-2-even_width', '2 Equal-Width Columns'), ('cols-widest-2-wide_narrow', '2 Columns: 1 Wide, 1 Narrow'), ('cols-widest-2-narrow_wide', '2 Columns: 1 Narrow, 1 Wide'), ('cols-widest-3-even_width', '3 Equal-Width Columns')])], default='Row Layouts', help_text='Layout of the articles within. Notice: All Column Layouts become multiple rows when screen width is narrow.', max_length=255, verbose_name='Layout Option')),
+ ('style_type', models.CharField(blank=True, choices=[('Row Layouts', [('rows-divided', 'Dividers Between Articles')]), ('Column Layouts', [('cols-gapless', 'Remove Gaps Between Articles')])], help_text='Optional styles for the list itself.', max_length=255, verbose_name='Style Option')),
('file_link', filer.fields.file.FilerFileField(blank=True, help_text='If provided links a file from the filer app.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='filer.File', verbose_name='File link')),
('internal_link', models.ForeignKey(blank=True, help_text='If provided, overrides the external link.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.Page', verbose_name='Internal link')),
],
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/migrations/0002_auto_20210629_1711.py b/taccsite_cms/contrib/taccsite_static_article_list/migrations/0002_auto_20210629_1711.py
deleted file mode 100644
index be7fe89e5..000000000
--- a/taccsite_cms/contrib/taccsite_static_article_list/migrations/0002_auto_20210629_1711.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2.16 on 2021-06-29 22:11
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('taccsite_static_article_list', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='taccsitearticlelist',
- name='style_type',
- field=models.CharField(blank=True, choices=[('Row Layouts', (('rows-divided', 'Dividers Between Articles'),))], help_text='Optional styles for the list itself.', max_length=255, verbose_name='Style Option'),
- ),
- ]
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0001_initial.py b/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0001_initial.py
index c9a902311..6cb252d59 100644
--- a/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0001_initial.py
+++ b/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2.16 on 2021-06-29 19:41
+# Generated by Django 2.2.16 on 2021-07-02 19:13
from django.db import migrations, models
import django.db.models.deletion
@@ -43,6 +43,52 @@ class Migration(migrations.Migration):
},
bases=('cms.cmsplugin',),
),
+ migrations.CreateModel(
+ name='TaccsiteStaticEventsArticlePreview',
+ fields=[
+ ('template', models.CharField(choices=[('default', 'Default')], default='default', max_length=255, verbose_name='Template')),
+ ('name', models.CharField(blank=True, max_length=255, verbose_name='Display name')),
+ ('external_link', models.CharField(blank=True, help_text='Provide a link to an external source.', max_length=2040, validators=[djangocms_link.validators.IntranetURLValidator(intranet_host_re=None)], verbose_name='External link')),
+ ('anchor', models.CharField(blank=True, help_text='Appends the value only after the internal or external link. Do not include a preceding "#" symbol.', max_length=255, verbose_name='Anchor')),
+ ('mailto', models.EmailField(blank=True, max_length=255, verbose_name='Email address')),
+ ('phone', models.CharField(blank=True, max_length=255, verbose_name='Phone')),
+ ('target', models.CharField(blank=True, choices=[('_blank', 'Open in new window'), ('_self', 'Open in same window'), ('_parent', 'Delegate to parent'), ('_top', 'Delegate to top')], max_length=255, verbose_name='Target')),
+ ('attributes', djangocms_attributes_field.fields.AttributesField(blank=True, default=dict, verbose_name='Attributes')),
+ ('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_static_article_preview_taccsitestaticeventsarticlepreview', serialize=False, to='cms.CMSPlugin')),
+ ('title_text', models.CharField(default='', help_text='The title for the article.', max_length=50, verbose_name='Title')),
+ ('abstract_text', models.TextField(default='', help_text='A summary of the article', verbose_name='Abstract')),
+ ('expiry_date', models.DateField(blank=True, help_text='The date upon which the event starts (manual entry). Format: YYYY-MM-DD', null=True, verbose_name='Event End Date')),
+ ('publish_date', models.DateField(blank=True, help_text='The date after which the event ends (manual entry). Format: YYYY-MM-DD', null=True, verbose_name='Event Start Date')),
+ ('file_link', filer.fields.file.FilerFileField(blank=True, help_text='If provided links a file from the filer app.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='filer.File', verbose_name='File link')),
+ ('internal_link', models.ForeignKey(blank=True, help_text='If provided, overrides the external link.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.Page', verbose_name='Internal link')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('cms.cmsplugin',),
+ ),
+ migrations.CreateModel(
+ name='TaccsiteStaticDocsArticlePreview',
+ fields=[
+ ('template', models.CharField(choices=[('default', 'Default')], default='default', max_length=255, verbose_name='Template')),
+ ('name', models.CharField(blank=True, max_length=255, verbose_name='Display name')),
+ ('external_link', models.CharField(blank=True, help_text='Provide a link to an external source.', max_length=2040, validators=[djangocms_link.validators.IntranetURLValidator(intranet_host_re=None)], verbose_name='External link')),
+ ('anchor', models.CharField(blank=True, help_text='Appends the value only after the internal or external link. Do not include a preceding "#" symbol.', max_length=255, verbose_name='Anchor')),
+ ('mailto', models.EmailField(blank=True, max_length=255, verbose_name='Email address')),
+ ('phone', models.CharField(blank=True, max_length=255, verbose_name='Phone')),
+ ('target', models.CharField(blank=True, choices=[('_blank', 'Open in new window'), ('_self', 'Open in same window'), ('_parent', 'Delegate to parent'), ('_top', 'Delegate to top')], max_length=255, verbose_name='Target')),
+ ('attributes', djangocms_attributes_field.fields.AttributesField(blank=True, default=dict, verbose_name='Attributes')),
+ ('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_static_article_preview_taccsitestaticdocsarticlepreview', serialize=False, to='cms.CMSPlugin')),
+ ('title_text', models.CharField(default='', help_text='The title for the article.', max_length=50, verbose_name='Title')),
+ ('abstract_text', models.TextField(default='', help_text='A summary of the article', verbose_name='Abstract')),
+ ('file_link', filer.fields.file.FilerFileField(blank=True, help_text='If provided links a file from the filer app.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='filer.File', verbose_name='File link')),
+ ('internal_link', models.ForeignKey(blank=True, help_text='If provided, overrides the external link.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.Page', verbose_name='Internal link')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('cms.cmsplugin',),
+ ),
migrations.CreateModel(
name='TaccsiteStaticAllocsArticlePreview',
fields=[
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0002_taccsitestaticdocsarticlepreview.py b/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0002_taccsitestaticdocsarticlepreview.py
deleted file mode 100644
index f6ebbc681..000000000
--- a/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0002_taccsitestaticdocsarticlepreview.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Generated by Django 2.2.16 on 2021-06-29 21:15
-
-from django.db import migrations, models
-import django.db.models.deletion
-import djangocms_attributes_field.fields
-import djangocms_link.validators
-import filer.fields.file
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('filer', '0012_file_mime_type'),
- ('cms', '0022_auto_20180620_1551'),
- ('taccsite_static_article_preview', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='TaccsiteStaticDocsArticlePreview',
- fields=[
- ('template', models.CharField(choices=[('default', 'Default')], default='default', max_length=255, verbose_name='Template')),
- ('name', models.CharField(blank=True, max_length=255, verbose_name='Display name')),
- ('external_link', models.CharField(blank=True, help_text='Provide a link to an external source.', max_length=2040, validators=[djangocms_link.validators.IntranetURLValidator(intranet_host_re=None)], verbose_name='External link')),
- ('anchor', models.CharField(blank=True, help_text='Appends the value only after the internal or external link. Do not include a preceding "#" symbol.', max_length=255, verbose_name='Anchor')),
- ('mailto', models.EmailField(blank=True, max_length=255, verbose_name='Email address')),
- ('phone', models.CharField(blank=True, max_length=255, verbose_name='Phone')),
- ('target', models.CharField(blank=True, choices=[('_blank', 'Open in new window'), ('_self', 'Open in same window'), ('_parent', 'Delegate to parent'), ('_top', 'Delegate to top')], max_length=255, verbose_name='Target')),
- ('attributes', djangocms_attributes_field.fields.AttributesField(blank=True, default=dict, verbose_name='Attributes')),
- ('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_static_article_preview_taccsitestaticdocsarticlepreview', serialize=False, to='cms.CMSPlugin')),
- ('title_text', models.CharField(default='', help_text='The title for the article.', max_length=50, verbose_name='Title')),
- ('abstract_text', models.TextField(default='', help_text='A summary of the article', verbose_name='Abstract')),
- ('file_link', filer.fields.file.FilerFileField(blank=True, help_text='If provided links a file from the filer app.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='filer.File', verbose_name='File link')),
- ('internal_link', models.ForeignKey(blank=True, help_text='If provided, overrides the external link.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.Page', verbose_name='Internal link')),
- ],
- options={
- 'abstract': False,
- },
- bases=('cms.cmsplugin',),
- ),
- ]
diff --git a/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0003_taccsitestaticeventsarticlepreview.py b/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0003_taccsitestaticeventsarticlepreview.py
deleted file mode 100644
index 97d80bd3f..000000000
--- a/taccsite_cms/contrib/taccsite_static_article_preview/migrations/0003_taccsitestaticeventsarticlepreview.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Generated by Django 2.2.16 on 2021-06-30 15:19
-
-from django.db import migrations, models
-import django.db.models.deletion
-import djangocms_attributes_field.fields
-import djangocms_link.validators
-import filer.fields.file
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('filer', '0012_file_mime_type'),
- ('cms', '0022_auto_20180620_1551'),
- ('taccsite_static_article_preview', '0002_taccsitestaticdocsarticlepreview'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='TaccsiteStaticEventsArticlePreview',
- fields=[
- ('template', models.CharField(choices=[('default', 'Default')], default='default', max_length=255, verbose_name='Template')),
- ('name', models.CharField(blank=True, max_length=255, verbose_name='Display name')),
- ('external_link', models.CharField(blank=True, help_text='Provide a link to an external source.', max_length=2040, validators=[djangocms_link.validators.IntranetURLValidator(intranet_host_re=None)], verbose_name='External link')),
- ('anchor', models.CharField(blank=True, help_text='Appends the value only after the internal or external link. Do not include a preceding "#" symbol.', max_length=255, verbose_name='Anchor')),
- ('mailto', models.EmailField(blank=True, max_length=255, verbose_name='Email address')),
- ('phone', models.CharField(blank=True, max_length=255, verbose_name='Phone')),
- ('target', models.CharField(blank=True, choices=[('_blank', 'Open in new window'), ('_self', 'Open in same window'), ('_parent', 'Delegate to parent'), ('_top', 'Delegate to top')], max_length=255, verbose_name='Target')),
- ('attributes', djangocms_attributes_field.fields.AttributesField(blank=True, default=dict, verbose_name='Attributes')),
- ('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_static_article_preview_taccsitestaticeventsarticlepreview', serialize=False, to='cms.CMSPlugin')),
- ('title_text', models.CharField(default='', help_text='The title for the article.', max_length=50, verbose_name='Title')),
- ('abstract_text', models.TextField(default='', help_text='A summary of the article', verbose_name='Abstract')),
- ('expiry_date', models.DateField(blank=True, help_text='The date upon which the event starts (manual entry). Format: YYYY-MM-DD', null=True, verbose_name='Event End Date')),
- ('publish_date', models.DateField(blank=True, help_text='The date after which the event ends (manual entry). Format: YYYY-MM-DD', null=True, verbose_name='Event Start Date')),
- ('file_link', filer.fields.file.FilerFileField(blank=True, help_text='If provided links a file from the filer app.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='filer.File', verbose_name='File link')),
- ('internal_link', models.ForeignKey(blank=True, help_text='If provided, overrides the external link.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.Page', verbose_name='Internal link')),
- ],
- options={
- 'abstract': False,
- },
- bases=('cms.cmsplugin',),
- ),
- ]
From 51b0fca1bd5e353718a0b254bf1912ede3aae2a3 Mon Sep 17 00:00:00 2001
From: Wesley Bomar
Date: Fri, 2 Jul 2021 14:43:15 -0500
Subject: [PATCH 49/56] GH-93: Comments & Noop tweaks
---
.../_docs/how-to-extend-django-cms-plugin.md | 1 -
...ride-validation-error-from-parent-model.md | 1 +
.../contrib/_docs/taccsite_static_article.md | 1 +
.../bootstrap4_djangocms_link/cms_plugins.py | 2 +-
.../cms_plugins.py | 2 +-
taccsite_cms/contrib/helpers.py | 35 +++++++++++++----
.../taccsite_blockquote/cms_plugins.py | 1 -
.../contrib/taccsite_offset/cms_plugins.py | 1 -
.../contrib/taccsite_offset/models.py | 1 +
.../contrib/taccsite_sample/cms_plugins.py | 1 -
.../contrib/taccsite_sample/models.py | 4 +-
.../cms_plugins.py | 4 +-
.../taccsite_static_article_list/constants.py | 1 +
.../taccsite_static_article_list/models.py | 15 ++++---
.../templates/article_list.html | 5 +++
.../cms_plugins.py | 39 ++++++++++++-------
.../taccsite_static_article_preview/models.py | 24 ++++++++++++
.../templates/static_article_preview.html | 12 +++---
18 files changed, 106 insertions(+), 44 deletions(-)
diff --git a/taccsite_cms/contrib/_docs/how-to-extend-django-cms-plugin.md b/taccsite_cms/contrib/_docs/how-to-extend-django-cms-plugin.md
index 07c66b944..f93e07d4e 100644
--- a/taccsite_cms/contrib/_docs/how-to-extend-django-cms-plugin.md
+++ b/taccsite_cms/contrib/_docs/how-to-extend-django-cms-plugin.md
@@ -43,7 +43,6 @@ class ______Plugin(LinkPlugin):
]
# Render
-
def render(self, context, instance, placeholder):
context = super().render(context, instance, placeholder)
request = context['request']
diff --git a/taccsite_cms/contrib/_docs/how-to-override-validation-error-from-parent-model.md b/taccsite_cms/contrib/_docs/how-to-override-validation-error-from-parent-model.md
index 3abbcce5e..a9ccbfc9f 100644
--- a/taccsite_cms/contrib/_docs/how-to-override-validation-error-from-parent-model.md
+++ b/taccsite_cms/contrib/_docs/how-to-override-validation-error-from-parent-model.md
@@ -12,6 +12,7 @@ from taccsite_cms.contrib.helpers import (
)
class OurModelThatUsesXxx(AbstractXxx):
+ # Validate
def clean(self):
# Bypass irrelevant parent validation
try:
diff --git a/taccsite_cms/contrib/_docs/taccsite_static_article.md b/taccsite_cms/contrib/_docs/taccsite_static_article.md
index 5bfe94980..e69d9e300 100644
--- a/taccsite_cms/contrib/_docs/taccsite_static_article.md
+++ b/taccsite_cms/contrib/_docs/taccsite_static_article.md
@@ -60,6 +60,7 @@ class TaccsiteStaticNewsArticlePreview(AbstractPicture):
class Meta:
abstract = False
+ # Validate
def clean(self):
skip_image_validation()
```
diff --git a/taccsite_cms/contrib/bootstrap4_djangocms_link/cms_plugins.py b/taccsite_cms/contrib/bootstrap4_djangocms_link/cms_plugins.py
index 41d0891f1..98ab52d07 100644
--- a/taccsite_cms/contrib/bootstrap4_djangocms_link/cms_plugins.py
+++ b/taccsite_cms/contrib/bootstrap4_djangocms_link/cms_plugins.py
@@ -1,4 +1,4 @@
-# Reregister unregistered LinkPlugin without uninstalling Bootstrap4's
+# Re-register unregistered LinkPlugin without uninstalling Bootstrap4's
# FAQ: A Bootstrap link is undesirable but may be used by migrated legacy sites
# TODO: Drop try/except & load non-standard plugin set for migrated legacy sites
# FAQ: If we can import both plugins, then re-register LinkPlugin
diff --git a/taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py b/taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py
index 99ce38a98..31bcae84e 100644
--- a/taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py
+++ b/taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py
@@ -1,4 +1,4 @@
-# Reregister unregistered PicturePlugin without uninstalling Bootstrap4's
+# Re-register unregistered PicturePlugin without uninstalling Bootstrap4's
# FAQ: A Bootstrap picture has superfluous options that are not always desirable
# TODO: Drop try/except & load non-standard plugin set for migrated legacy sites
# FAQ: If we can import both plugins, then re-register PicturePlugin
diff --git a/taccsite_cms/contrib/helpers.py b/taccsite_cms/contrib/helpers.py
index be72a13bb..9c6d21343 100644
--- a/taccsite_cms/contrib/helpers.py
+++ b/taccsite_cms/contrib/helpers.py
@@ -1,3 +1,4 @@
+# Get Django `models.CharField` `choices`
def get_choices(choice_dict):
"""Get a sequence for a Django model field choices from a dictionary.
@@ -15,6 +16,8 @@ def get_choices(choice_dict):
+# Filter Django `models.CharField` `choices`
+# SEE: get_choices
def filter_choices_by_prefix(choices, prefix):
"""Reduce sequence of choices to items whose values begin with given string
@@ -34,32 +37,42 @@ def filter_choices_by_prefix(choices, prefix):
+# Concatenate a list of CSS classes
# SEE: https://github.com/django-cms/djangocms-bootstrap4/blob/master/djangocms_bootstrap4/helpers.py
def concat_classnames(classes):
- """Concatenates a list of classes (without failing on None)"""
+ """Concatenate a list of classname strings (without failing on None)"""
# SEE: https://stackoverflow.com/a/20271297/11817077
return ' '.join(_class for _class in classes if _class)
+# Create a list clone that has another list shoved into it
# SEE: https://newbedev.com/how-to-insert-multiple-elements-into-a-list
def insert_at_position(position, list, list_to_insert):
- """Insert list at position within another list"""
+ """Insert list at position within another list
+
+ :returns: New list
+ """
return list[:position] + list_to_insert + list[position:]
+# Get the date from a list that is nearest
# SEE: https://stackoverflow.com/a/32237949/11817077
def get_nearest(items, pivot):
- """Get nearest date (or other arithmatic value)"""
+ """Get nearest date (or other arithmatic value)
+
+ :returns: The item value nearest the given "pivot" value
+ """
return min(items, key=lambda x: abs(x - pivot))
+# Get list of indicies of items that start with text
# SEE: https://stackoverflow.com/a/67393343/11817077
def get_indices_that_start_with(text, list):
"""
- Get a list of indexes of list element that starts with given text
+ Get a list of indices of list elements that starts with given text
:rtype: list
"""
@@ -67,10 +80,11 @@ def get_indices_that_start_with(text, list):
+# Populate class attribute of plugin instances
def add_classname_to_instances(classname, plugin_instances):
"""Add class names to class attribute of plugin instances"""
for instance in plugin_instances:
- # A plugin may not have any class set
+ # A plugin must not have any class set
if not hasattr(instance.attributes, 'class'):
instance.attributes['class'] = ''
@@ -80,11 +94,12 @@ def add_classname_to_instances(classname, plugin_instances):
-# HELP: Can this logic be less verbose?
-# HELP: Is the `preferred_time_period` parameter effectual?
+# Get date nearest today
from datetime import date
+# HELP: Can this logic be less verbose?
+# HELP: Is the `preferred_time_period` parameter effectual?
def which_date_is_nearest_today(date_a, date_b, preferred_time_period):
"""
Returns whether each date is today or nearest today, and whether nearest date is past or today or future.
@@ -158,10 +173,11 @@ def which_date_is_nearest_today(date_a, date_b, preferred_time_period):
-# SEE: https://github.com/django-cms/django-cms/issues/5102#issuecomment-597150141
+# Allow plugins to set max number of nested children
from django.shortcuts import render
+# SEE: https://github.com/django-cms/django-cms/issues/5102#issuecomment-597150141
class AbstractMaxChildrenPlugin():
"""
Abstract extension of `CMSPluginBase` that allows setting maximum amount of nested/child plugins.
@@ -191,6 +207,8 @@ def add_view(self,request, form_url='', extra_context=None):
+# Tweak validation on Django CMS `AbstractLink` for TACC
+
from cms.models.pluginmodel import CMSPlugin
from django.core.exceptions import ValidationError
@@ -205,6 +223,7 @@ def clean_for_abstract_link(model, self):
```
from taccsite_cms.contrib.helpers import clean_for_abstract_link
+ # Validate
def clean(self):
clean_for_abstract_link(__class__, self)
diff --git a/taccsite_cms/contrib/taccsite_blockquote/cms_plugins.py b/taccsite_cms/contrib/taccsite_blockquote/cms_plugins.py
index a5ee6ce65..f725e2d63 100644
--- a/taccsite_cms/contrib/taccsite_blockquote/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_blockquote/cms_plugins.py
@@ -51,7 +51,6 @@ class TaccsiteBlockquotePlugin(CMSPluginBase):
]
# Render
-
def render(self, context, instance, placeholder):
context = super().render(context, instance, placeholder)
request = context['request']
diff --git a/taccsite_cms/contrib/taccsite_offset/cms_plugins.py b/taccsite_cms/contrib/taccsite_offset/cms_plugins.py
index 9b32449af..607cb05d8 100644
--- a/taccsite_cms/contrib/taccsite_offset/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_offset/cms_plugins.py
@@ -47,7 +47,6 @@ class TaccsiteOffsetPlugin(CMSPluginBase):
]
# Render
-
def render(self, context, instance, placeholder):
context = super().render(context, instance, placeholder)
request = context['request']
diff --git a/taccsite_cms/contrib/taccsite_offset/models.py b/taccsite_cms/contrib/taccsite_offset/models.py
index d0bdecca8..ccf4abc6d 100644
--- a/taccsite_cms/contrib/taccsite_offset/models.py
+++ b/taccsite_cms/contrib/taccsite_offset/models.py
@@ -11,6 +11,7 @@
# Constants
+# TODO: Consider using an Enum (and an Abstract Enum with `get_choices` method)
DIRECTION_DICT = {
'left': {
'classname': 'o-offset-content--left',
diff --git a/taccsite_cms/contrib/taccsite_sample/cms_plugins.py b/taccsite_cms/contrib/taccsite_sample/cms_plugins.py
index f72e8e5be..f5de7ae8f 100644
--- a/taccsite_cms/contrib/taccsite_sample/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_sample/cms_plugins.py
@@ -44,7 +44,6 @@ def icon_alt(self, instance):
# def text_editor_button_icon(...)
# Render
-
def render(self, context, instance, placeholder):
context = super().render(context, instance, placeholder)
request = context['request']
diff --git a/taccsite_cms/contrib/taccsite_sample/models.py b/taccsite_cms/contrib/taccsite_sample/models.py
index 553d3266b..156065825 100644
--- a/taccsite_cms/contrib/taccsite_sample/models.py
+++ b/taccsite_cms/contrib/taccsite_sample/models.py
@@ -25,7 +25,9 @@ class TaccsiteSample(CMSPlugin):
def get_short_description(self):
return 'Hello, […]'
- # Custom
+
+
+ # Helpers
def get_name(self, user=None):
"""Get name by which to greet the user.
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py b/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
index afe10e290..904f86532 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/cms_plugins.py
@@ -34,8 +34,7 @@ class AbstractArticleListPlugin(LinkPlugin):
"""
module = 'TACC Site'
model = TaccsiteArticleList
- # abstract
- # name = _('______ Article List (Static)')
+ # name = _('______ Article List (Static)') # abstract
render_template = 'article_list.html'
def get_render_template(self, context, instance, placeholder):
return self.render_template
@@ -69,7 +68,6 @@ def get_render_template(self, context, instance, placeholder):
]
# Render
-
def render(self, context, instance, placeholder):
context = super().render(context, instance, placeholder)
request = context['request']
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/constants.py b/taccsite_cms/contrib/taccsite_static_article_list/constants.py
index 1c9c7fbc0..63505cb50 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/constants.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/constants.py
@@ -1,3 +1,4 @@
+# TODO: Consider using an Enum (and an Abstract Enum with `get_choices` method)
LAYOUT_DICT = {
'cols-widest-2-even_width': {
'classname': 'c-article-list--layout-a',
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/models.py b/taccsite_cms/contrib/taccsite_static_article_list/models.py
index 4b0f5fb23..0cd92ccf9 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/models.py
+++ b/taccsite_cms/contrib/taccsite_static_article_list/models.py
@@ -70,11 +70,19 @@ class TaccsiteArticleList(AbstractLink):
max_length=255,
)
- link_is_optional = True
-
def get_short_description(self):
return self.title_text
+
+
+ # Parent
+
+ link_is_optional = True
+
+ class Meta:
+ abstract = False
+
+ # Validate
def clean(self):
clean_for_abstract_link(__class__, self)
@@ -105,6 +113,3 @@ def clean(self):
},
code='invalid'
)
-
- class Meta:
- abstract = False
diff --git a/taccsite_cms/contrib/taccsite_static_article_list/templates/article_list.html b/taccsite_cms/contrib/taccsite_static_article_list/templates/article_list.html
index ac86b4e5f..1f0f98ef9 100644
--- a/taccsite_cms/contrib/taccsite_static_article_list/templates/article_list.html
+++ b/taccsite_cms/contrib/taccsite_static_article_list/templates/article_list.html
@@ -1,15 +1,20 @@
{% load cms_tags %}
+ {# Title #}
{% if instance.title_text %}