Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
1,125 additions
and
1 deletion.
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
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,203 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Generate view.""" | ||
|
||
from bobtemplates.plone.base import base_prepare_renderer | ||
from bobtemplates.plone.base import git_commit | ||
from bobtemplates.plone.base import update_file | ||
from lxml import etree | ||
from mrbob.bobexceptions import SkipQuestion | ||
from mrbob.bobexceptions import ValidationError | ||
|
||
import os | ||
import stringcase | ||
|
||
|
||
def get_view_name_from_python_class(configurator, question): | ||
"""Generate view default name from python class""" | ||
if configurator.variables['view_python_class']: | ||
view_class_name = configurator.variables['view_python_class_name'] | ||
view_generated_name = stringcase.snakecase(view_class_name).replace('_', '-') # NOQA: E501 | ||
question.default = view_generated_name | ||
else: | ||
question.default = 'my-view' | ||
|
||
|
||
def get_template_name_default(configurator, question): | ||
if configurator.variables['view_template']: | ||
view_url = configurator.variables['view_name'] | ||
template_default_name = stringcase.snakecase(view_url) | ||
question.default = template_default_name | ||
else: | ||
question.default = 'view' | ||
|
||
|
||
def check_python_class_answer(configurator, question): | ||
if not configurator.variables['view_python_class']: | ||
raise SkipQuestion(u'No python class, so we skip python class name question.') # NOQA: E501 | ||
|
||
|
||
def check_view_template_answer(configurator, question): | ||
if not configurator.variables['view_template'] and not configurator.variables['view_python_class']: # NOQA: E501 | ||
raise ValidationError(u'View must at least have a template or a python class') # NOQA: E501 | ||
elif not configurator.variables['view_template']: | ||
raise SkipQuestion(u'No view template, so we skip view template name question.') # NOQA: E501 | ||
|
||
|
||
def _update_views_configure_zcml(configurator): | ||
file_name = u'configure.zcml' | ||
directory_path = configurator.variables['package_folder'] + '/views/' | ||
file_path = directory_path + file_name | ||
configure_example_file_path = configurator.variables['package_folder'] + '/views/configure.zcml.example' # NOQA: E501 | ||
file_list = os.listdir(os.path.dirname(directory_path)) | ||
if file_name not in file_list: | ||
os.rename(configure_example_file_path, file_path) | ||
namespaces = '{http://namespaces.zope.org/zope}' | ||
|
||
with open(file_path, 'r') as xml_file: | ||
parser = etree.XMLParser(remove_blank_text=True) | ||
tree = etree.parse(xml_file, parser) | ||
tree_root = tree.getroot() | ||
view_xpath = "{0}browser:page[@name='{1}']".format( | ||
namespaces, | ||
configurator.variables['view_name'], | ||
) | ||
if len(tree_root.findall(view_xpath)): | ||
print( | ||
'{0} already in configure.zcml, skip adding!'.format( | ||
configurator.variables['view_name'], | ||
), | ||
) | ||
return | ||
|
||
match_str = '-*- extra stuff goes here -*-' | ||
|
||
if configurator.variables['view_template'] and configurator.variables['view_python_class']: # NOQA: E501 | ||
insert_str = """ | ||
<browser:page | ||
name="{0}" | ||
for="Products.CMFCore.interfaces.IFolderish" | ||
class=".{1}.{2}" | ||
template="{3}.pt" | ||
permission="zope2.View" | ||
/> | ||
""".format( | ||
configurator.variables['view_name'], | ||
configurator.variables['view_python_file_name'], | ||
configurator.variables['view_python_class_name'], | ||
configurator.variables['view_template_name'], | ||
) | ||
|
||
if configurator.variables['view_template'] and not configurator.variables['view_python_class']: # NOQA: E501 | ||
insert_str = """ | ||
<browser:page | ||
name="{0}" | ||
for="Products.CMFCore.interfaces.IFolderish" | ||
template="{1}.pt" | ||
permission="zope2.View" | ||
/> | ||
""".format( | ||
configurator.variables['view_name'], | ||
configurator.variables['view_template_name'], | ||
) | ||
|
||
if not configurator.variables['view_template'] and configurator.variables['view_python_class']: # NOQA: E501 | ||
insert_str = """ | ||
<browser:page | ||
name="{0}" | ||
for="Products.CMFCore.interfaces.IFolderish" | ||
class=".{1}.{2}" | ||
permission="zope2.View" | ||
/> | ||
""".format( | ||
configurator.variables['view_name'], | ||
configurator.variables['view_python_file_name'], | ||
configurator.variables['view_python_class_name'], | ||
) | ||
|
||
update_file(configurator, file_path, match_str, insert_str) | ||
|
||
|
||
def _update_configure_zcml(configurator): | ||
file_name = u'configure.zcml' | ||
file_path = configurator.variables['package_folder'] + '/' + file_name | ||
namespaces = '{http://namespaces.zope.org/zope}' | ||
|
||
with open(file_path, 'r') as xml_file: | ||
parser = etree.XMLParser(remove_blank_text=True) | ||
tree = etree.parse(xml_file, parser) | ||
tree_root = tree.getroot() | ||
view_xpath = "{0}include[@package='.views']".format(namespaces) | ||
if len(tree_root.findall(view_xpath)): | ||
print( | ||
'.views already in configure.zcml, skip adding!', | ||
) | ||
return | ||
|
||
match_str = '<!--<includeDependencies package="." />-->' | ||
insert_str = """ | ||
<include package=".views" /> | ||
""" | ||
update_file(configurator, file_path, match_str, insert_str) | ||
|
||
|
||
def _delete_unwanted_files(configurator): | ||
directory_path = configurator.variables['package_folder'] + '/views/' | ||
if not configurator.variables['view_template']: | ||
file_name = u'{0}.pt'.format( | ||
configurator.variables['view_template_name'], | ||
) | ||
file_path = directory_path + file_name | ||
os.remove(file_path) | ||
|
||
elif not configurator.variables['view_python_class']: | ||
file_name = u'{0}.py'.format( | ||
configurator.variables['view_python_file_name'], | ||
) | ||
file_path = directory_path + file_name | ||
os.remove(file_path) | ||
|
||
file_name = u'configure.zcml.example' | ||
file_list = os.listdir(os.path.dirname(directory_path)) | ||
if file_name in file_list: | ||
file_path = directory_path + file_name | ||
os.remove(file_path) | ||
|
||
|
||
def prepare_renderer(configurator): | ||
"""Prepare rendering.""" | ||
configurator = base_prepare_renderer(configurator) | ||
configurator.variables['template_id'] = 'view' | ||
view_name = configurator.variables['view_name'].strip('_') | ||
normalized_view_name = stringcase.snakecase(view_name) | ||
configurator.variables['view_name_normalized'] = normalized_view_name | ||
if configurator.variables['view_python_class']: | ||
python_class_name = configurator.variables['view_python_class_name'].strip('_') # NOQA: E501 | ||
configurator.variables['view_python_class_name'] = stringcase.pascalcase( # NOQA: E501 | ||
python_class_name, | ||
) | ||
view_python_file_name = stringcase.snakecase(python_class_name) | ||
configurator.variables['view_python_file_name'] = view_python_file_name | ||
view_name_from_input = normalized_view_name.replace('_', '-') | ||
view_name_from_python_class = view_python_file_name.replace('_', '-') | ||
if view_name_from_input != view_name_from_python_class: | ||
configurator.variables['view_name'] = view_name_from_input | ||
else: | ||
configurator.variables['view_python_file_name'] = view_name | ||
|
||
if not configurator.variables['view_template']: | ||
configurator.variables['view_template_name'] = view_name | ||
|
||
configurator.target_directory = configurator.variables['package_folder'] | ||
|
||
|
||
def post_renderer(configurator): | ||
"""Post rendering.""" | ||
_update_configure_zcml(configurator) | ||
_update_views_configure_zcml(configurator) | ||
_delete_unwanted_files(configurator) | ||
git_commit( | ||
configurator, | ||
'Add view: {0}'.format( | ||
configurator.variables['view_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,49 @@ | ||
[questions] | ||
subtemplate_warning.question = Please commit your changes, before using a sub-template! Continue anyway? [n/y] | ||
subtemplate_warning.required = True | ||
subtemplate_warning.default = n | ||
subtemplate_warning.pre_ask_question = bobtemplates.plone.base:git_clean_state_check | ||
subtemplate_warning.post_ask_question = mrbob.hooks:validate_choices bobtemplates.plone.base:subtemplate_warning_post_question | ||
subtemplate_warning.choices = y|n | ||
subtemplate_warning.choices_delimiter = | | ||
|
||
view_python_class.question = Should the view have a Python class? | ||
view_python_class.help = Do you want Python class for this view? | ||
view_python_class.required = True | ||
view_python_class.default = y | ||
view_python_class.pre_ask_question = bobtemplates.plone.base:check_root_folder | ||
view_python_class.post_ask_question = mrbob.hooks:validate_choices mrbob.hooks:to_boolean | ||
view_python_class.choices = y|n | ||
view_python_class.choices_delimiter = | | ||
|
||
view_python_class_name.question = Python class name | ||
view_python_class_name.help = Shold be something like 'MyView' | ||
view_python_class_name.required = False | ||
view_python_class_name.default = MyView | ||
view_python_class_name.pre_ask_question = bobtemplates.plone.view:check_python_class_answer | ||
|
||
view_name.question = View name (part of the URL) | ||
view_name.help = Should be something like 'my-view' (no special characters!) | ||
view_name.required = True | ||
view_name.default = my-view | ||
view_name.pre_ask_question = bobtemplates.plone.view:get_view_name_from_python_class | ||
|
||
|
||
view_template.question = Should the View have a template file? | ||
view_template.help = Do you want a template file for this view? | ||
view_template.required = True | ||
view_template.default = y | ||
view_template.post_ask_question = mrbob.hooks:validate_choices mrbob.hooks:to_boolean | ||
view_template.choices = y|n | ||
view_template.choices_delimiter = | | ||
|
||
view_template_name.question = Template name (without extension) | ||
view_template_name.help = name of the template file for this view | ||
view_template_name.default = my_view | ||
view_template_name.required = False | ||
view_template_name.pre_ask_question = bobtemplates.plone.view:check_view_template_answer bobtemplates.plone.view:get_template_name_default | ||
|
||
[template] | ||
post_ask = bobtemplates.plone.base:set_global_vars | ||
pre_render = bobtemplates.plone.view:prepare_renderer | ||
post_render = bobtemplates.plone.view:post_renderer |
59 changes: 59 additions & 0 deletions
59
bobtemplates/plone/view/tests/test_view_+view_name_normalized+.py.bob
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,59 @@ | ||
# -*- coding: utf-8 -*- | ||
from {{{package.dottedname}}}.testing import {{{package.uppercasename}}}_FUNCTIONAL_TESTING | ||
from {{{package.dottedname}}}.testing import {{{package.uppercasename}}}_INTEGRATION_TESTING | ||
from plone import api | ||
from plone.app.testing import setRoles | ||
from plone.app.testing import TEST_USER_ID | ||
from zope.component import getMultiAdapter | ||
from zope.component.interfaces import ComponentLookupError | ||
|
||
import unittest | ||
|
||
|
||
class ViewsIntegrationTest(unittest.TestCase): | ||
|
||
layer = {{{package.uppercasename}}}_INTEGRATION_TESTING | ||
|
||
def setUp(self): | ||
self.portal = self.layer['portal'] | ||
setRoles(self.portal, TEST_USER_ID, ['Manager']) | ||
api.content.create(self.portal, 'Folder', 'other-folder') | ||
api.content.create(self.portal, 'Collection', 'my-collection') | ||
|
||
def test_{{{view_name_normalized}}}_is_registered(self): | ||
view = getMultiAdapter( | ||
(self.portal['other-folder'], self.portal.REQUEST), | ||
name='{{{ view_name }}}' | ||
) | ||
self.assertTrue(view(), '{{{ view_name }}} is not found') | ||
{{% if view_python_class and view_template %}} | ||
self.assertTrue( | ||
'Sample View' in view(), | ||
'Sample View is not found in {{{ view_name }}}' | ||
) | ||
self.assertTrue( | ||
'Sample View' in view(), | ||
'A small message is not found in {{{ view_name }}}' | ||
) | ||
{{% else %}} | ||
self.assertTrue( | ||
'Sample View' in view(), | ||
'Sample View is not found in {{{ view_name }}}' | ||
) | ||
{{% endif %}} | ||
|
||
def test_{{{view_name_normalized}}}_in_my_collection(self): | ||
with self.assertRaises(ComponentLookupError): | ||
getMultiAdapter( | ||
(self.portal['my-collection'], self.portal.REQUEST), | ||
name='{{{ view_name }}}' | ||
) | ||
|
||
|
||
class ViewsFunctionalTest(unittest.TestCase): | ||
|
||
layer = {{{package.uppercasename}}}_FUNCTIONAL_TESTING | ||
|
||
def setUp(self): | ||
self.portal = self.layer['portal'] | ||
setRoles(self.portal, TEST_USER_ID, ['Manager']) |
29 changes: 29 additions & 0 deletions
29
bobtemplates/plone/view/views/+view_python_file_name+.py.bob
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,29 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
{{% if view_template %}} | ||
from {{{ package.dottedname }}} import _ | ||
{{% endif %}} | ||
from Products.Five.browser import BrowserView | ||
{{% if view_template %}} | ||
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile | ||
{{% endif %}} | ||
|
||
|
||
class {{{ view_python_class_name }}}(BrowserView): | ||
{{% if view_template %}} | ||
template = ViewPageTemplateFile('{{{ view_template_name }}}.pt') | ||
|
||
def __init__(self, context, request): | ||
self.context = context | ||
self.request = request | ||
|
||
def __call__(self): | ||
self.msg = _(u'A small message') | ||
return self.template() | ||
{{% else %}} | ||
def __call__(self): | ||
template = '''<li class="heading" i18n:translate=""> | ||
Sample View | ||
</li>''' | ||
return template | ||
{{% endif %}} |
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,20 @@ | ||
<html xmlns="http://www.w3.org/1999/xhtml" | ||
xmlns:metal="http://xml.zope.org/namespaces/metal" | ||
xmlns:tal="http://xml.zope.org/namespaces/tal" | ||
xmlns:i18n="http://xml.zope.org/namespaces/i18n" | ||
i18n:domain="{{{ package.dottedname }}}" | ||
metal:use-macro="context/main_template/macros/master"> | ||
<body> | ||
<metal:block fill-slot="content-core"> | ||
<div class="heading"> | ||
<li i18n:translate=""> | ||
Sample View | ||
</li> | ||
{{% if view_python_class %}} | ||
<span tal:content="view/msg">this gets replaced</span> | ||
{{% endif %}} | ||
</div> | ||
</metal:block> | ||
|
||
<body> | ||
</html> |
Empty file.
Oops, something went wrong.