-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Task/gh 67 tacc sample plugin
- Loading branch information
Showing
12 changed files
with
378 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
from cms.plugin_base import CMSPluginBase | ||
from cms.plugin_pool import plugin_pool | ||
from cms.models.pluginmodel import CMSPlugin | ||
from django.utils.translation import gettext_lazy as _ | ||
from django.utils.encoding import force_text | ||
|
||
from .models import TaccsiteSample | ||
|
||
from .defaults import user_name as default_name | ||
from .utils import has_proper_name | ||
|
||
# SEE: http://docs.django-cms.org/en/release-3.7.x/reference/plugins.html | ||
@plugin_pool.register_plugin | ||
class TaccsiteSamplePlugin(CMSPluginBase): | ||
""" | ||
Components > "Sample (Greet User)" Plugin | ||
https://url.to/docs/components/sample/ | ||
""" | ||
module = 'TACC Site' | ||
model = TaccsiteSample | ||
name = _('Sample (Greet User)') | ||
render_template = 'sample.html' | ||
|
||
cache = False | ||
text_enabled = True | ||
# NOTE: Use case is unclear | ||
# admin_preview = True | ||
# NOTE: To change for all TACC plugins add taccsite_cms/templates/admin/... | ||
# change_form_template = 'templates/plugin_change_form.html' | ||
# NOTE: To change field widget and other attribute beyond `models.…Field` | ||
# form = TaccsiteSamplePluginForm # TODO: Provide example | ||
|
||
# FAQ: Sets tooltip of preview of this plugin within a Text plugin | ||
def icon_alt(self, instance): | ||
super_value = force_text(super().icon_alt(instance)) | ||
return f'Hello, […] ({super_value})' | ||
# NOTE: Our previews (see `icon_alt`) are rich and have no icon... | ||
# TODO: Confirm whether these are ever necessary | ||
# def icon_src(self, instance) | ||
# def text_editor_button_icon(...) | ||
|
||
def render(self, context, instance, placeholder): | ||
context = super().render(context, instance, placeholder) | ||
request = context['request'] | ||
|
||
context.update({ | ||
'name': instance.get_name(request.user), | ||
'has_proper_name': has_proper_name(request.user), | ||
'is_authenticated': request.user.is_authenticated | ||
}) | ||
return context |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
user_name = 'Guest' |
27 changes: 27 additions & 0 deletions
27
taccsite_cms/contrib/taccsite_sample/migrations/0001_initial.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Generated by Django 2.2.16 on 2021-04-13 20:16 | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
('cms', '0022_auto_20180620_1551'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='TaccsiteSample', | ||
fields=[ | ||
('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_sample_taccsitesample', serialize=False, to='cms.CMSPlugin')), | ||
('guest_name', models.CharField(blank=True, default='Guest', help_text='If user is logged in they are greeted by their name. If not logged in, they are greeted as this value. If this value is blank, they are greeted as "Guest".', max_length=50)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
bases=('cms.cmsplugin',), | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
from cms.models.pluginmodel import CMSPlugin | ||
|
||
from django.db import models | ||
|
||
from .defaults import user_name as default_name | ||
from .utils import has_proper_name, get_proper_name | ||
|
||
class TaccsiteSample(CMSPlugin): | ||
# Overwrites | ||
|
||
def get_short_description(self): | ||
return 'Hello, […]' | ||
|
||
# Fields | ||
|
||
""" | ||
Components > "Sample (Greet User)" Model | ||
https://url.to/docs/components/sample/ | ||
""" | ||
guest_name = models.CharField( | ||
max_length=50, | ||
default=default_name, | ||
help_text=f'If user is logged in they are greeted by their name. If not logged in, they are greeted as this value. If this value is blank, they are greeted as "{default_name}".', | ||
# To change the widget, a new Form class is required | ||
# FAQ: Wesley B searched for hours to find this important information | ||
# SEE: http://disq.us/p/210zgp2 | ||
# SEE: [`TaccsiteSamplePlugin.form`](./cms_plugins.py) | ||
# widget=forms.TextInput(attrs={'placeholder': 'Search'}), | ||
blank=True | ||
) | ||
|
||
# Custom | ||
|
||
def get_name(self, user=None): | ||
"""Get name by which to greet the user. | ||
:param user: Django user object | ||
:rtype: str | ||
:returns: Name of authenticated user or the name for any guest | ||
""" | ||
if has_proper_name(user): | ||
name = get_proper_name(user) | ||
elif user.is_authenticated: | ||
name = user.username | ||
elif bool(self.guest_name): | ||
name = self.guest_name | ||
else: | ||
name = default_name | ||
|
||
return name |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<h1>{% spaceless %} | ||
{% if has_proper_name or not is_authenticated %} | ||
Hello, {{name}}. | ||
{% elif is_authenticated %} | ||
Hello, <samp>{{name}}</samp>. | ||
{% endif %} | ||
{% endspaceless %}</h1> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
from django.test import TestCase | ||
from django.test.client import RequestFactory | ||
|
||
from django.contrib.auth.models import AnonymousUser, User | ||
|
||
from cms.api import add_plugin | ||
from cms.models import Placeholder | ||
from cms.plugin_rendering import ContentRenderer | ||
|
||
from taccsite_cms.contrib.taccsite_sample.cms_plugins import TaccsiteSamplePlugin | ||
from taccsite_cms.contrib.taccsite_sample.models import TaccsiteSample | ||
|
||
# TODO: Isolate model test from plugin test | ||
class TaccsiteSampleTests(TestCase): | ||
def setUp(self): | ||
self.factory = RequestFactory() | ||
self.auth_user = None # set via _create_user() | ||
self.anon_user = AnonymousUser() | ||
self.context = None # set via _create_user() | ||
self.placeholder = Placeholder.objects.create(slot='test') | ||
self.plugin = None # set via _populate_plugin_model() | ||
|
||
|
||
|
||
# Helpers | ||
|
||
def _create_auth_user(self, username='test', first_name='', last_name=''): | ||
self.auth_user = User.objects.create_user( | ||
username=username, | ||
first_name=first_name, | ||
last_name=last_name, | ||
email='[email protected]', | ||
password='top_secret' | ||
) | ||
self.context = { 'request': self.factory.get('/test/user') } | ||
self.context['request'].user = self.auth_user | ||
|
||
def _create_anon_user(self, guest_name='Guest'): | ||
self.auth_user = AnonymousUser() | ||
self.context = { 'request': self.factory.get('/test/user') } | ||
self.context['request'].user = self.anon_user | ||
|
||
def _populate_plugin_model(self, guest_name=None): | ||
data = {'guest_name': guest_name} if bool(guest_name) else {} | ||
self.plugin = add_plugin( | ||
self.placeholder, | ||
TaccsiteSamplePlugin, | ||
'en', | ||
**data | ||
) | ||
|
||
def _get_data(self): | ||
"""Return context necessary for testing plugin logic""" | ||
plugin_instance = self.plugin.get_plugin_class_instance() | ||
data = plugin_instance.render(self.context, self.plugin, None) | ||
return data | ||
|
||
def _get_html(self): | ||
"""Return markup necessary for testing plugin template""" | ||
renderer = ContentRenderer(request=self.factory) | ||
html = renderer.render_plugin(self.plugin, self.context) | ||
return html | ||
|
||
def _test_data(self, assertDict): | ||
"""Reusable plugin logic test""" | ||
context = self._get_data() | ||
for key in assertDict.keys(): | ||
self.assertIn(key, context) | ||
self.assertEqual(context[key], assertDict[key], msg=f"key='{key}'") | ||
|
||
def _test_html(self, assertDict): | ||
"""Reusable plugin markup test""" | ||
html = self._get_html() | ||
self.assertTrue(html.find('Hello') > -1) | ||
self.assertTrue(html.find(assertDict['name']) > -1) | ||
|
||
|
||
|
||
# Tests | ||
|
||
# Tests: Guest User | ||
|
||
def test_anon_user_default(self): | ||
"""Test guest user with plugin default value(s)""" | ||
self._create_anon_user() | ||
self._populate_plugin_model() | ||
assertDict = { | ||
'name': 'Guest', | ||
'has_proper_name': None, | ||
'is_authenticated': False | ||
} | ||
|
||
self._test_data(assertDict) | ||
self._test_html(assertDict) | ||
|
||
def test_anon_user_custom(self): | ||
"""Test guest user with plugin custom value(s)""" | ||
self._create_anon_user(guest_name='Friend') | ||
self._populate_plugin_model(guest_name='Friend') | ||
assertDict = { | ||
'name': 'Friend', | ||
'has_proper_name': None, | ||
'is_authenticated': False | ||
} | ||
|
||
self._test_data(assertDict) | ||
self._test_html(assertDict) | ||
|
||
# Tests: Auth'd User | ||
|
||
def test_auth_user_username(self): | ||
"""Test logged-in user with no first nor last name""" | ||
self._create_auth_user(username='fred') | ||
self._populate_plugin_model() | ||
assertDict = { | ||
'name': 'fred', | ||
'has_proper_name': False, | ||
'is_authenticated': True | ||
} | ||
|
||
self._test_data(assertDict) | ||
self._test_html(assertDict) | ||
|
||
def test_auth_user_lastname(self): | ||
"""Test logged-in user with only last name""" | ||
self._create_auth_user(username='fred', last_name='Flintstone') | ||
self._populate_plugin_model() | ||
assertDict = { | ||
'name': 'fred', | ||
'has_proper_name': False, | ||
'is_authenticated': True | ||
} | ||
|
||
self._test_data(assertDict) | ||
self._test_html(assertDict) | ||
|
||
def test_auth_user_firstname(self): | ||
"""Test logged-in user with only first name""" | ||
self._create_auth_user(username='fred', first_name='Fred') | ||
self._populate_plugin_model() | ||
assertDict = { | ||
'name': 'Fred', | ||
'has_proper_name': True, | ||
'is_authenticated': True | ||
} | ||
|
||
self._test_data(assertDict) | ||
self._test_html(assertDict) | ||
|
||
def test_auth_user_fullname(self): | ||
"""Test logged-in user with first and last name""" | ||
self._create_auth_user( | ||
username='fred', first_name='Fred', last_name='Flintstone') | ||
self._populate_plugin_model() | ||
assertDict = { | ||
'name': 'Fred Flintstone', | ||
'has_proper_name': True, | ||
'is_authenticated': True | ||
} | ||
|
||
self._test_data(assertDict) | ||
self._test_html(assertDict) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
""" | ||
.. module:: taccsite_sample.utils | ||
:synopsis: Utilities to process user name. | ||
""" | ||
|
||
def has_proper_name(user=None): | ||
"""Whether user has enough data with which to form a proper name. | ||
:param user: Django user object | ||
:returns: True, False, or None (if unknown) | ||
:rtype: bool | None | ||
.. note:: | ||
User must be authenticated or function will return None. | ||
""" | ||
ret = None | ||
|
||
if user and user.is_authenticated: | ||
if user.first_name: | ||
ret = True | ||
else: | ||
ret = False | ||
|
||
return ret | ||
|
||
def get_proper_name(user=None): | ||
"""Get proper name of an authenticated user. | ||
:param user: Django user object (authenticated) | ||
:returns: Proper name of user | ||
:rtype: str | None | ||
.. note:: | ||
If user is not authenticated, we do not know any name. | ||
If user has no first name, we give up (no name prefix logic). | ||
""" | ||
name = None | ||
|
||
if has_proper_name(user): | ||
if bool(user.first_name): | ||
name = user.first_name | ||
if bool(user.last_name): | ||
name = name + ' ' + user.last_name | ||
|
||
return name |
Oops, something went wrong.