Skip to content

Commit b718302

Browse files
committed
Squashed merge of task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts of the following:
commit d452772 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Thu Oct 7 16:59:43 2021 -0500 GH-93: Fix bug from intro of `x-overlay--` mixin Bug Source: - #368 - #361 - TACC/Core-CMS-Resources@6d253e1...522c296#diff-5894bbc commit 17d5174 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Thu Oct 7 13:30:24 2021 -0500 Major: Deprecate `.x-overlay--` classes Affects Frontera and UTRC. Both are updated in this commit. commit 9525e25 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Thu Oct 7 13:18:53 2021 -0500 GH-93: Fix: Do NOT load migrate CSS on homepage commit 23abf1d Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Thu Oct 7 12:54:04 2021 -0500 GH-93: Frontera homepage banner fixes/improvements commit 7ffbd47 Merge: 265fec0 666bf95 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Thu Oct 7 12:32:14 2021 -0500 Merge branch 'main' into task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts commit 265fec0 Merge: fc21621 c117ee6 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Tue Sep 28 17:01:07 2021 -0500 Merge branch 'main' into task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts commit fc21621 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Tue Sep 28 17:00:43 2021 -0500 GH-93-ETC: Update submod to have its latest main commit cb9c1a1 Merge: c4186df 1248117 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Thu Sep 2 14:24:54 2021 -0500 Merge branch 'main' into task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts commit c4186df Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Wed Sep 1 16:22:57 2021 -0500 GH-93/GH-142/GH-133: Remove errant submod commits commit 7563f7c Merge: 579f5c6 97f7df0 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Tue Aug 31 13:59:51 2021 -0500 Merge branch 'main' into task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts commit 579f5c6 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Mon Aug 23 20:25:31 2021 -0500 Docs: Corrections to "How to Extend Plugin" doc commit 3bdde39 Merge: 266a9ad 1d50a7b Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Mon Aug 16 18:18:22 2021 -0500 Merge branch 'main' into task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts commit 266a9ad Merge: 647d394 a66033a Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Mon Aug 16 18:09:17 2021 -0500 Merge branch 'main' into task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts commit 647d394 Merge: dcf606c ac6d873 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Mon Aug 16 17:19:26 2021 -0500 Merge branch 'main' into task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts commit dcf606c Merge: 74b9a9c e0e420d Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Mon Aug 16 17:04:52 2021 -0500 Merge branch 'main' into task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts commit 74b9a9c Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Fri Aug 13 05:52:08 2021 -0500 GH-93: Avoid title color bug with upcoming PR #312 Ensure heading turns white. commit 1729430 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Fri Jul 30 13:43:29 2021 -0500 GH-142, GH-133: Article List Plugin Helpers This code was lost during re-creation: - of #264 - as #280. commit eea0768 Merge: 89b1136 374b78e Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Fri Jul 30 12:46:27 2021 -0500 Merge branch 'main' into task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts commit 89b1136 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Tue Jul 20 17:24:37 2021 -0500 GH-93: Frontera: Plugin-less style … — Real Fix commit 5bd6484 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Tue Jul 20 17:23:30 2021 -0500 GH-93: Frontera: Plugin-less style … snippet — Fix commit f6b8f3a Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Tue Jul 20 17:03:17 2021 -0500 GH-93: Frontera: Plugin-less style updates snippet commit b531503 Merge: 2b66f11 302fe3b Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Tue Jul 20 16:58:15 2021 -0500 Merge branch 'main' into task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts commit 2b66f11 Merge: 2bdf7e7 d0a5151 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Mon Jul 12 21:38:53 2021 -0500 Merge branch 'main' into task/GH-93-GH-142-GH-133-article-list-plugins-and-styles--no-reverts commit 2bdf7e7 Author: Wesley Bomar <wbomar@tacc.utexas.edu> Date: Mon Jul 12 20:55:42 2021 -0500 GH-93, GH-142, GH-133: Article List Plugins+Styles
1 parent 15723f0 commit b718302

31 files changed

Lines changed: 1953 additions & 538 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# How to Conditionally Render Child Plugins
2+
3+
```handlebars
4+
{% for plugin_instance in instance.child_plugin_instances %}
5+
{% if plugin_instance.plugin_type == 'LinkPlugin' %}
6+
<a href="{{ link }}" <!-- ... -->>
7+
<!-- ... -->
8+
</a>
9+
{% endif %}
10+
{% endfor %}
11+
```
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# How to Handle "Non-Nullable" "Default Value"
2+
3+
## Sample Error
4+
5+
```text
6+
You are trying to add a non-nullable field '...'
7+
to choice without a default; we can't do that
8+
(the database needs something to populate existing rows).
9+
Please select a fix:
10+
1) Provide a one-off default now (will be set on all existing rows)
11+
2) Quit, and let me add a default in models.py
12+
Select an option:
13+
```
14+
15+
## Explanations
16+
17+
- (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/)
18+
- (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)
19+
20+
## Solutions
21+
22+
### For `cmsplugin_ptr`
23+
24+
1. ☑ Select option 1), then see:
25+
- [Follow-Up Error](#follow-up-error)
26+
- [Notes ▸ `cmsplugin_ptr`](#cmsplugin_ptr)
27+
28+
### For Other Fields
29+
30+
1. ⚠ Select option 1) and hope for the best.
31+
2. ☑ Select option 2) and provide a sensible default (_not_ `None` a.k.a. null).
32+
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)
33+
34+
## Follow-Up Error
35+
36+
If you allowed Null to be set as default, then you may have this new error:
37+
38+
```text
39+
django.db.utils.IntegrityError: column "..." contains null values
40+
```
41+
42+
Solutions:
43+
44+
1. [delete _relevant_ migration files and rebuild migrations](https://stackoverflow.com/a/37244199/11817077)
45+
2. [delete _all_ migration files and rebuild migrations](https://stackoverflow.com/a/37242930/11817077)
46+
47+
## Notes
48+
49+
### `cmsplugin_ptr`
50+
51+
If the field is `cmsplugin_ptr` then know that
52+
53+
- [it is a database relationship field managed automatically by Django](https://github.com/nephila/djangocms-blog/issues/316#issuecomment-242292787),
54+
- 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)),
55+
- you should __not__ add or overwrite it unless you know what you are doing.
56+
57+
_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)._
58+
59+
## Appendix
60+
61+
- [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)
62+
- [[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)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Static Article Plugins
2+
3+
## Intention
4+
5+
Support static addition of news articles that originate from a Core news site.
6+
7+
A [dynamic solution that pulls form the Core news site](https://github.com/TACC/Core-CMS/issues/69) is preferable.
8+
9+
But this is not available due to constrainst of architecture, time, or ability.
10+
11+
## Architecture
12+
13+
### (Currently) Add Image via Child Plugin Instead of Via Fields
14+
15+
Instead, the image fields should be in the plugin, __not__ via a child plugin, but a solution has not yet been implemented.
16+
17+
#### Hope for the Future
18+
19+
The `AbstractLink` model was successfully extended.
20+
21+
See:
22+
- [./how-to-extend-django-cms-plugin.md](./how-to-extend-django-cms-plugin.md)
23+
- [../taccsite_static_article_preview](../taccsite_static_article_preview)
24+
- [../taccsite_static_article_list](../taccsite_static_article_list)
25+
26+
#### Failed Attempt
27+
28+
1. Build model so it extends `AbstractPicture` from `djangocms-picture`.
29+
2. Tweak model to sweep bugs under the rug.
30+
3. Quit when he was unable to resolve the error,
31+
`TaccsiteStaticNewsArticlePreview has no field named 'cmsplugin_ptr_id'`
32+
upon saving a plugin instance.
33+
4. Learn:
34+
- [one should not try to reduce `AbstractPicture`](https://stackoverflow.com/a/3674714/11817077)
35+
- [one should not subclass a subclass of `CMSPlugin`](https://github.com/django-cms/django-cms/blob/3.7.4/cms/models/pluginmodel.py#L104)
36+
37+
#### Abandoned Code
38+
39+
```python
40+
from djangocms_picture.models import AbstractPicture
41+
42+
# To allow user to not set image
43+
# FAQ: Emptying the clean() method avoids picture validation
44+
# SEE: https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/models.py#L278
45+
def skip_image_validation():
46+
pass
47+
48+
class TaccsiteStaticNewsArticlePreview(AbstractPicture):
49+
#
50+
#
51+
#
52+
53+
# Remove error-prone attribute from parent class
54+
# FAQ: Avoid error when running `makemigrations`:
55+
# "You are trying to add a non-nullable field 'cmsplugin_ptr' […]"
56+
# SEE: https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/models.py#L212
57+
# SEE: https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/models.py#L234
58+
cmsplugin_ptr = None
59+
60+
class Meta:
61+
abstract = False
62+
63+
# Validate
64+
def clean(self):
65+
skip_image_validation()
66+
```

taccsite_cms/contrib/helpers.py

Lines changed: 162 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,23 @@ def get_choices(choice_dict):
1515

1616

1717

18-
# GH-93, GH-142, GH-133: Upcoming functions here (ease merge conflict, maybe)
18+
# Filter Django `models.CharField` `choices`
19+
# SEE: get_choices
20+
def filter_choices_by_prefix(choices, prefix):
21+
"""Reduce sequence of choices to items whose values begin with given string
22+
:param List[Tuple[str, str], ...] choices: the sequence to filter
23+
:param str prefix: the starting text required of an item value to retain it
24+
:returns: a sequence for django.db.models.CharField.choices
25+
:rtype: List[Tuple[str, str], ...]
26+
"""
27+
new_choices = []
28+
29+
for choice in choices:
30+
should_keep = choice[0].startswith(prefix)
31+
if should_keep:
32+
new_choices.append(choice)
33+
34+
return new_choices
1935

2036

2137

@@ -28,7 +44,26 @@ def concat_classnames(classes):
2844

2945

3046

31-
# GH-93, GH-142, GH-133: Upcoming functions here (ease merge conflict, maybe)
47+
# Create a list clone that has another list shoved into it
48+
# SEE: https://newbedev.com/how-to-insert-multiple-elements-into-a-list
49+
def insert_at_position(position, list, list_to_insert):
50+
"""Insert list at position within another list
51+
:returns: New list
52+
"""
53+
return list[:position] + list_to_insert + list[position:]
54+
55+
56+
57+
# Get the date from a list that is nearest
58+
# SEE: https://stackoverflow.com/a/32237949/11817077
59+
def get_nearest(items, pivot):
60+
"""Get nearest date (or other arithmatic value)
61+
:returns: The item value nearest the given "pivot" value
62+
"""
63+
return min(items, key=lambda x: abs(x - pivot))
64+
65+
66+
3267
# Get list of indicies of items that start with text
3368
# SEE: https://stackoverflow.com/a/67393343/11817077
3469
def get_indices_that_start_with(text, list):
@@ -39,6 +74,129 @@ def get_indices_that_start_with(text, list):
3974
return [i for i in range(len(list)) if list[i].startswith(text)]
4075

4176

77+
78+
# Populate class attribute of plugin instances
79+
def add_classname_to_instances(classname, plugin_instances):
80+
"""Add class names to class attribute of plugin instances"""
81+
for instance in plugin_instances:
82+
# A plugin must not have any class set
83+
if not hasattr(instance.attributes, 'class'):
84+
instance.attributes['class'] = ''
85+
86+
# The class should occur before any CMS or user classes
87+
# FAQ: This keeps plugin author classes together
88+
instance.attributes['class'] = instance.attributes['class'] + classname
89+
90+
91+
92+
# Get date nearest today
93+
94+
from datetime import date
95+
96+
# HELP: Can this logic be less verbose?
97+
# HELP: Is the `preferred_time_period` parameter effectual?
98+
def which_date_is_nearest_today(date_a, date_b, preferred_time_period):
99+
"""
100+
Returns whether each date is today or nearest today, and whether nearest date is past or today or future.
101+
Only two dates are supported. You may prefer 'future' or 'past' date(s).
102+
If both dates are the same date, then both are reported as True.
103+
:param datetime date_a: a date "A" to compare
104+
:param datetime date_b: a date "B" to compare
105+
:param str preferred_time_period: whether to prefer 'future' or 'past' dates
106+
:returns:
107+
A tuple of tuples:
108+
((
109+
``boolean`` of whether ``date_a`` is nearest,
110+
``string`` of ``date_a`` time period ``past``/``today``/``future``
111+
),
112+
(
113+
``boolean`` of whether ``date_b`` is nearest,
114+
``string`` of ``date_b`` time period ``past``/``today``/``future``
115+
)),
116+
:rtype: tuple
117+
"""
118+
today = date.today()
119+
is_a = False
120+
is_b = False
121+
a_time_period = 'today'
122+
b_time_period = 'today'
123+
124+
# Match preferred time
125+
126+
if today in {date_a, date_b}:
127+
is_a = True
128+
is_b = True
129+
a_time_period = 'today'
130+
b_time_period = 'today'
131+
132+
elif preferred_time_period == 'future':
133+
is_a = date_a and date_a >= today
134+
is_b = date_b and date_b >= today
135+
if is_a: a_time_period = 'future'
136+
if is_b: b_time_period = 'future'
137+
if not is_a and not is_b:
138+
is_a = date_a and date_a < today
139+
is_b = date_b and date_b < today
140+
if is_a: a_time_period = 'past'
141+
if is_b: b_time_period = 'past'
142+
143+
elif preferred_time_period == 'past':
144+
is_a = date_a and date_a < today
145+
is_b = date_b and date_b < today
146+
if is_a: a_time_period = 'past'
147+
if is_b: b_time_period = 'past'
148+
if not is_a and not is_b:
149+
is_a = date_a and date_a >= today
150+
is_b = date_b and date_b >= today
151+
if is_a: a_time_period = 'future'
152+
if is_b: b_time_period = 'future'
153+
154+
# Show nearest date
155+
if is_a and is_b and date_a != date_b:
156+
nearest_date = get_nearest((date_a, date_b), today)
157+
158+
if date_a == nearest_date:
159+
is_b = False
160+
if date_b == nearest_date:
161+
is_a = False
162+
163+
return ((is_a, a_time_period), (is_b, b_time_period))
164+
165+
166+
167+
# Allow plugins to set max number of nested children
168+
169+
from django.shortcuts import render
170+
171+
# SEE: https://github.com/django-cms/django-cms/issues/5102#issuecomment-597150141
172+
class AbstractMaxChildrenPlugin():
173+
"""
174+
Abstract extension of `CMSPluginBase` that allows setting maximum amount of nested/child plugins.
175+
Usage:
176+
1. Extend this class,
177+
after extending `CMSPluginBase` or a class that extends `CMSPluginBase`.
178+
2. Set `max_children` to desired limit.
179+
"""
180+
181+
max_children = None
182+
183+
def add_view(self,request, form_url='', extra_context=None):
184+
185+
if self.max_children:
186+
# FAQ: Placeholders do not have a parent, only plugins do
187+
if self._cms_initial_attributes['parent']:
188+
num_allowed = len([v for v in self._cms_initial_attributes['parent'].get_children() if v.get_plugin_instance()[0] is not None])
189+
else:
190+
num_allowed = len([v for v in self.placeholder.get_plugins() if v.get_plugin_instance()[0] is not None and v.get_plugin_name() == self.name])
191+
192+
if num_allowed >= self.max_children:
193+
return render(request , "path/to/your/max_reached_template.html", {
194+
'max_children': self.max_children,
195+
})
196+
return super(AbstractMaxChildrenPlugin, self).add_view(request, form_url, extra_context)
197+
198+
199+
42200
# Tweak validation on Django CMS `AbstractLink` for TACC
43201

44202
from cms.models.pluginmodel import CMSPlugin
@@ -82,8 +240,9 @@ def clean(self):
82240
if len(err.messages):
83241
raise err
84242

85-
# Get name of field from a given model
86243

244+
245+
# Get name of field from a given model
87246
# SEE: https://stackoverflow.com/a/14498938/11817077
88247
def get_model_field_name(model, field_name):
89248
model_field_name = model._meta.get_field(field_name).verbose_name.title()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Static Article List
2+
3+
See [./_docs/taccsite_static_article.md](./_docs/taccsite_static_article.md).

taccsite_cms/contrib/taccsite_static_article_list/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)