cards: add category (#48111)
This commit is contained in:
parent
6a0c783587
commit
27b8916657
|
@ -23,7 +23,7 @@ from wcs.qommon.ident.password_accounts import PasswordAccount
|
|||
from wcs.qommon.http_request import HTTPRequest
|
||||
from wcs.qommon.template import get_current_theme
|
||||
from wcs.admin.settings import UserFieldsFormDef
|
||||
from wcs.categories import Category
|
||||
from wcs.categories import Category, CardDefCategory
|
||||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.wscalls import NamedWsCall
|
||||
from wcs.roles import Role
|
||||
|
@ -641,6 +641,7 @@ def test_settings_export_import(pub, studio):
|
|||
Workflow.wipe()
|
||||
Role.wipe()
|
||||
Category.wipe()
|
||||
CardDefCategory.wipe()
|
||||
NamedDataSource.wipe()
|
||||
NamedWsCall.wipe()
|
||||
|
||||
|
@ -672,6 +673,7 @@ def test_settings_export_import(pub, studio):
|
|||
carddef.name = 'bar'
|
||||
carddef.store()
|
||||
Category(name='baz').store()
|
||||
CardDefCategory(name='foobar').store()
|
||||
Role(name='qux').store()
|
||||
NamedDataSource(name='quux').store()
|
||||
NamedWsCall(name='corge').store()
|
||||
|
@ -708,10 +710,11 @@ def test_settings_export_import(pub, studio):
|
|||
assert 'models/export_to_model-1.upload' not in filelist
|
||||
assert 'roles/1' in filelist
|
||||
assert 'categories/1' in filelist
|
||||
assert 'carddef_categories/1' in filelist
|
||||
assert 'datasources/1' in filelist
|
||||
assert 'wscalls/corge' in filelist
|
||||
for filename in filelist:
|
||||
assert not '.indexes' in filename
|
||||
assert '.indexes' not in filename
|
||||
|
||||
wipe()
|
||||
assert FormDef.count() == 0
|
||||
|
|
|
@ -4,6 +4,7 @@ import pytest
|
|||
|
||||
from wcs import fields
|
||||
from wcs.admin.settings import UserFieldsFormDef
|
||||
from wcs.categories import CardDefCategory
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.qommon.http_request import HTTPRequest
|
||||
|
@ -42,6 +43,47 @@ def teardown_module(module):
|
|||
clean_temporary_pub()
|
||||
|
||||
|
||||
def test_cards_list(pub, studio):
|
||||
create_superuser(pub)
|
||||
|
||||
CardDef.wipe()
|
||||
carddef = CardDef()
|
||||
carddef.name = 'card title'
|
||||
carddef.fields = []
|
||||
carddef.store()
|
||||
|
||||
carddef2 = CardDef()
|
||||
carddef2.name = 'card title 2'
|
||||
carddef2.fields = []
|
||||
carddef2.store()
|
||||
|
||||
CardDefCategory.wipe()
|
||||
cat = CardDefCategory(name='Foo')
|
||||
cat.store()
|
||||
cat2 = CardDefCategory(name='Bar')
|
||||
cat2.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/cards/')
|
||||
assert '<h2>Misc</h2>' not in resp.text
|
||||
assert '<h2>Foo</h2>' not in resp.text
|
||||
assert '<h2>Bar</h2>' not in resp.text
|
||||
|
||||
carddef.category = cat2
|
||||
carddef.store()
|
||||
resp = app.get('/backoffice/cards/')
|
||||
assert '<h2>Misc</h2>' in resp.text
|
||||
assert '<h2>Foo</h2>' not in resp.text
|
||||
assert '<h2>Bar</h2>' in resp.text
|
||||
|
||||
carddef2.category = cat
|
||||
carddef2.store()
|
||||
resp = app.get('/backoffice/cards/')
|
||||
assert '<h2>Misc</h2>' not in resp.text
|
||||
assert '<h2>Foo</h2>' in resp.text
|
||||
assert '<h2>Bar</h2>' in resp.text
|
||||
|
||||
|
||||
def test_cards_new(pub, studio):
|
||||
CardDef.wipe()
|
||||
create_superuser(pub)
|
||||
|
@ -197,6 +239,45 @@ def test_card_digest_template(pub, studio):
|
|||
assert 'Existing cards will be updated in the background.' not in resp.text
|
||||
|
||||
|
||||
def test_card_category(pub, studio):
|
||||
create_superuser(pub)
|
||||
|
||||
CardDef.wipe()
|
||||
carddef = CardDef()
|
||||
carddef.name = 'card title'
|
||||
carddef.fields = []
|
||||
carddef.store()
|
||||
|
||||
CardDefCategory.wipe()
|
||||
cat = CardDefCategory(name='Foo')
|
||||
cat.store()
|
||||
cat = CardDefCategory(name='Bar')
|
||||
cat.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/cards/1/')
|
||||
assert '<span class="label">Category</span> <span class="value">None</span>' in resp.text
|
||||
assert '<span class="label">Category</span> <span class="value">Foo</span>' not in resp.text
|
||||
assert '<span class="label">Category</span> <span class="value">Bar</span>' not in resp.text
|
||||
resp = resp.click(href='category')
|
||||
resp.forms[0].submit('cancel')
|
||||
assert CardDef.get(carddef.id).category_id is None
|
||||
|
||||
resp = app.get('/backoffice/cards/1/')
|
||||
assert '<span class="label">Category</span> <span class="value">None</span>' in resp.text
|
||||
assert '<span class="label">Category</span> <span class="value">Foo</span>' not in resp.text
|
||||
assert '<span class="label">Category</span> <span class="value">Bar</span>' not in resp.text
|
||||
resp = resp.click(href='category')
|
||||
resp.forms[0]['category_id'] = cat.id
|
||||
resp.forms[0].submit('submit')
|
||||
assert CardDef.get(carddef.id).category_id == cat.id
|
||||
|
||||
resp = app.get('/backoffice/cards/1/')
|
||||
assert '<span class="label">Category</span> <span class="value">None</span>' not in resp.text
|
||||
assert '<span class="label">Category</span> <span class="value">Foo</span>' not in resp.text
|
||||
assert '<span class="label">Category</span> <span class="value">Bar</span>' in resp.text
|
||||
|
||||
|
||||
def test_card_custom_view_data_source(pub, studio):
|
||||
user = create_superuser(pub)
|
||||
Role.wipe()
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pytest
|
||||
|
||||
from wcs.qommon.http_request import HTTPRequest
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.categories import CardDefCategory
|
||||
|
||||
from utilities import get_app, login, create_temporary_pub, clean_temporary_pub
|
||||
from .test_all import create_superuser
|
||||
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
if 'pub' in metafunc.fixturenames:
|
||||
metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates'], indirect=True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pub(request):
|
||||
pub = create_temporary_pub(
|
||||
sql_mode=bool('sql' in request.param),
|
||||
templates_mode=bool('templates' in request.param)
|
||||
)
|
||||
|
||||
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
|
||||
pub.set_app_dir(req)
|
||||
pub.cfg['identification'] = {'methods': ['password']}
|
||||
pub.cfg['language'] = {'language': 'en'}
|
||||
pub.write_cfg()
|
||||
|
||||
return pub
|
||||
|
||||
|
||||
def teardown_module(module):
|
||||
clean_temporary_pub()
|
||||
|
||||
|
||||
def test_categories(pub):
|
||||
create_superuser(pub)
|
||||
app = login(get_app(pub))
|
||||
app.get('/backoffice/cards/categories/')
|
||||
|
||||
|
||||
def test_categories_new(pub):
|
||||
create_superuser(pub)
|
||||
CardDefCategory.wipe()
|
||||
app = login(get_app(pub))
|
||||
|
||||
# go to the page and cancel
|
||||
resp = app.get('/backoffice/cards/categories/')
|
||||
resp = resp.click('New Category')
|
||||
resp = resp.forms[0].submit('cancel')
|
||||
assert resp.location == 'http://example.net/backoffice/cards/categories/'
|
||||
|
||||
# go to the page and add a category
|
||||
resp = app.get('/backoffice/cards/categories/')
|
||||
resp = resp.click('New Category')
|
||||
resp.forms[0]['name'] = 'a new category'
|
||||
resp.forms[0]['description'] = 'description of the category'
|
||||
resp = resp.forms[0].submit('submit')
|
||||
assert resp.location == 'http://example.net/backoffice/cards/categories/'
|
||||
resp = resp.follow()
|
||||
assert 'a new category' in resp.text
|
||||
resp = resp.click('a new category')
|
||||
assert '<h2>a new category' in resp.text
|
||||
|
||||
assert CardDefCategory.get(1).name == 'a new category'
|
||||
assert CardDefCategory.get(1).description == 'description of the category'
|
||||
|
||||
|
||||
def test_categories_edit(pub):
|
||||
create_superuser(pub)
|
||||
CardDefCategory.wipe()
|
||||
category = CardDefCategory(name='foobar')
|
||||
category.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/cards/categories/1/')
|
||||
assert 'no card model associated to this category' in resp.text
|
||||
|
||||
resp = resp.click(href='edit')
|
||||
assert resp.forms[0]['name'].value == 'foobar'
|
||||
resp.forms[0]['description'] = 'category description'
|
||||
resp = resp.forms[0].submit('submit')
|
||||
assert resp.location == 'http://example.net/backoffice/cards/categories/'
|
||||
resp = resp.follow()
|
||||
resp = resp.click('foobar')
|
||||
assert '<h2>foobar' in resp.text
|
||||
|
||||
assert CardDefCategory.get(1).description == 'category description'
|
||||
|
||||
|
||||
def test_categories_edit_duplicate_name(pub):
|
||||
CardDefCategory.wipe()
|
||||
category = CardDefCategory(name='foobar')
|
||||
category.store()
|
||||
category = CardDefCategory(name='foobar2')
|
||||
category.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/cards/categories/1/')
|
||||
|
||||
resp = resp.click(href='edit')
|
||||
assert resp.forms[0]['name'].value == 'foobar'
|
||||
resp.forms[0]['name'] = 'foobar2'
|
||||
resp = resp.forms[0].submit('submit')
|
||||
assert 'This name is already used' in resp.text
|
||||
|
||||
resp = resp.forms[0].submit('cancel')
|
||||
assert resp.location == 'http://example.net/backoffice/cards/categories/'
|
||||
|
||||
|
||||
def test_categories_with_carddefs(pub):
|
||||
CardDefCategory.wipe()
|
||||
category = CardDefCategory(name='foobar')
|
||||
category.store()
|
||||
|
||||
CardDef.wipe()
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/cards/categories/1/')
|
||||
assert 'form bar' not in resp.text
|
||||
|
||||
formdef = CardDef()
|
||||
formdef.name = 'form bar'
|
||||
formdef.fields = []
|
||||
formdef.category_id = category.id
|
||||
formdef.store()
|
||||
|
||||
resp = app.get('/backoffice/cards/categories/1/')
|
||||
assert 'form bar' in resp.text
|
||||
assert 'no card model associated to this category' not in resp.text
|
||||
|
||||
|
||||
def test_categories_delete(pub):
|
||||
CardDefCategory.wipe()
|
||||
category = CardDefCategory(name='foobar')
|
||||
category.store()
|
||||
|
||||
CardDef.wipe()
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/cards/categories/1/')
|
||||
|
||||
resp = resp.click(href='delete')
|
||||
resp = resp.forms[0].submit('cancel')
|
||||
assert resp.location == 'http://example.net/backoffice/cards/categories/1/'
|
||||
assert CardDefCategory.count() == 1
|
||||
|
||||
resp = app.get('/backoffice/cards/categories/1/')
|
||||
resp = resp.click(href='delete')
|
||||
resp = resp.forms[0].submit()
|
||||
assert resp.location == 'http://example.net/backoffice/cards/categories/'
|
||||
resp = resp.follow()
|
||||
assert CardDefCategory.count() == 0
|
||||
|
||||
|
||||
def test_categories_edit_description(pub):
|
||||
CardDefCategory.wipe()
|
||||
category = CardDefCategory(name='foobar')
|
||||
category.description = 'category description'
|
||||
category.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
# this URL is used for editing from the frontoffice, there's no link
|
||||
# pointing to it in the admin.
|
||||
resp = app.get('/backoffice/cards/categories/1/description')
|
||||
assert resp.forms[0]['description'].value == 'category description'
|
||||
resp.forms[0]['description'] = 'updated description'
|
||||
|
||||
# check cancel doesn't save the change
|
||||
resp2 = resp.forms[0].submit('cancel')
|
||||
assert resp2.location == 'http://example.net/backoffice/cards/categories/1/'
|
||||
assert CardDefCategory.get(1).description == 'category description'
|
||||
|
||||
# check submit does it properly
|
||||
resp2 = resp.forms[0].submit('submit')
|
||||
assert resp2.location == 'http://example.net/backoffice/cards/categories/1/'
|
||||
resp2 = resp2.follow()
|
||||
assert CardDefCategory.get(1).description == 'updated description'
|
||||
|
||||
|
||||
def test_categories_new_duplicate_name(pub):
|
||||
CardDefCategory.wipe()
|
||||
category = CardDefCategory(name='foobar')
|
||||
category.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/cards/categories/')
|
||||
resp = resp.click('New Category')
|
||||
resp.forms[0]['name'] = 'foobar'
|
||||
resp = resp.forms[0].submit('submit')
|
||||
assert 'This name is already used' in resp.text
|
||||
|
||||
|
||||
def test_categories_reorder(pub):
|
||||
CardDefCategory.wipe()
|
||||
category = CardDefCategory(name='foo')
|
||||
category.store()
|
||||
category = CardDefCategory(name='bar')
|
||||
category.store()
|
||||
category = CardDefCategory(name='baz')
|
||||
category.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
app.get('/backoffice/cards/categories/update_order?order=1;2;3;')
|
||||
categories = CardDefCategory.select()
|
||||
CardDefCategory.sort_by_position(categories)
|
||||
assert [x.id for x in categories] == ['1', '2', '3']
|
||||
|
||||
app.get('/backoffice/cards/categories/update_order?order=3;1;2;')
|
||||
categories = CardDefCategory.select()
|
||||
CardDefCategory.sort_by_position(categories)
|
||||
assert [x.id for x in categories] == ['3', '1', '2']
|
|
@ -46,7 +46,7 @@ from wcs.wf.resubmit import ResubmitWorkflowStatusItem
|
|||
from wcs.wf.create_formdata import CreateFormdataWorkflowStatusItem, Mapping
|
||||
from wcs.wf.create_carddata import CreateCarddataWorkflowStatusItem
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.categories import Category
|
||||
from wcs.categories import Category, CardDefCategory
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.logged_errors import LoggedError
|
||||
from wcs import fields
|
||||
|
@ -6220,7 +6220,7 @@ def test_carddata_management(pub, studio):
|
|||
user = create_user(pub)
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/')
|
||||
assert not 'Cards' in resp.text
|
||||
assert 'Cards' not in resp.text
|
||||
carddef = CardDef()
|
||||
carddef.name = 'foo'
|
||||
carddef.fields = [
|
||||
|
@ -6232,7 +6232,7 @@ def test_carddata_management(pub, studio):
|
|||
carddef.data_class().wipe()
|
||||
|
||||
resp = app.get('/backoffice/')
|
||||
assert not 'Cards' in resp.text
|
||||
assert 'Cards' not in resp.text
|
||||
|
||||
carddef.backoffice_submission_roles = user.roles
|
||||
carddef.store()
|
||||
|
@ -6288,6 +6288,51 @@ def test_carddata_management(pub, studio):
|
|||
assert resp.text.count('<tr') == 2 # header + row of data
|
||||
|
||||
|
||||
def test_carddata_management_categories(pub, studio):
|
||||
user = create_user(pub)
|
||||
|
||||
CardDef.wipe()
|
||||
carddef = CardDef()
|
||||
carddef.name = 'foo'
|
||||
carddef.fields = []
|
||||
carddef.backoffice_submission_roles = None
|
||||
carddef.workflow_roles = {'_editor': user.roles[0]}
|
||||
carddef.store()
|
||||
|
||||
carddef2 = CardDef()
|
||||
carddef2.name = 'card title 2'
|
||||
carddef2.fields = []
|
||||
carddef2.backoffice_submission_roles = None
|
||||
carddef2.workflow_roles = {'_editor': user.roles[0]}
|
||||
carddef2.store()
|
||||
|
||||
CardDefCategory.wipe()
|
||||
cat = CardDefCategory(name='Foo')
|
||||
cat.store()
|
||||
cat2 = CardDefCategory(name='Bar')
|
||||
cat2.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/data/')
|
||||
assert '<h3>Misc</h3>' not in resp.text
|
||||
assert '<h3>Foo</h3>' not in resp.text
|
||||
assert '<h3>Bar</h3>' not in resp.text
|
||||
|
||||
carddef.category = cat2
|
||||
carddef.store()
|
||||
resp = app.get('/backoffice/data/')
|
||||
assert '<h3>Misc</h3>' in resp.text
|
||||
assert '<h3>Foo</h3>' not in resp.text
|
||||
assert '<h3>Bar</h3>' in resp.text
|
||||
|
||||
carddef2.category = cat
|
||||
carddef2.store()
|
||||
resp = app.get('/backoffice/data/')
|
||||
assert '<h3>Misc</h3>' not in resp.text
|
||||
assert '<h3>Foo</h3>' in resp.text
|
||||
assert '<h3>Bar</h3>' in resp.text
|
||||
|
||||
|
||||
def test_studio_card_item_link(pub, studio):
|
||||
user = create_user(pub)
|
||||
CardDef.wipe()
|
||||
|
|
|
@ -31,7 +31,7 @@ from wcs.roles import Role
|
|||
from wcs.carddef import CardDef
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.formdata import Evolution
|
||||
from wcs.categories import Category
|
||||
from wcs.categories import Category, CardDefCategory
|
||||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.workflows import Workflow, EditableWorkflowStatusItem, WorkflowBackofficeFieldsFormDef, WorkflowVariablesFieldsFormDef
|
||||
from wcs.wf.jump import JumpWorkflowStatusItem
|
||||
|
@ -3312,6 +3312,11 @@ def test_cards(pub, local_user):
|
|||
local_user.roles = [role.id]
|
||||
local_user.store()
|
||||
|
||||
CardDefCategory.wipe()
|
||||
category = CardDefCategory()
|
||||
category.name = 'Category A'
|
||||
category.store()
|
||||
|
||||
CardDef.wipe()
|
||||
carddef = CardDef()
|
||||
carddef.name = 'test'
|
||||
|
@ -3355,11 +3360,21 @@ def test_cards(pub, local_user):
|
|||
resp = get_app(pub).get(sign_uri('/api/cards/@list'))
|
||||
assert len(resp.json['data']) == 1
|
||||
assert resp.json['data'][0]['slug'] == 'test'
|
||||
assert resp.json['data'][0]['category_slug'] is None
|
||||
assert resp.json['data'][0]['category_name'] is None
|
||||
assert resp.json['data'][0]['custom_views'] == [
|
||||
{'id': 'datasource-carddef-custom-view', 'text': 'datasource carddef custom view'},
|
||||
{'id': 'shared-carddef-custom-view', 'text': 'shared carddef custom view'},
|
||||
]
|
||||
|
||||
carddef.category = category
|
||||
carddef.store()
|
||||
resp = get_app(pub).get(sign_uri('/api/cards/@list'))
|
||||
assert len(resp.json['data']) == 1
|
||||
assert resp.json['data'][0]['slug'] == 'test'
|
||||
assert resp.json['data'][0]['category_slug'] == 'category-a'
|
||||
assert resp.json['data'][0]['category_name'] == 'Category A'
|
||||
|
||||
resp = get_app(pub).get(sign_uri('/api/cards/test/list'), status=403)
|
||||
|
||||
resp = get_app(pub).get(sign_uri(
|
||||
|
|
|
@ -4,7 +4,9 @@ import xml.etree.ElementTree as ET
|
|||
from django.utils.six import BytesIO
|
||||
|
||||
from wcs.qommon.http_request import HTTPRequest
|
||||
from wcs.qommon.misc import indent_xml as indent
|
||||
from wcs.qommon.template import Template
|
||||
from wcs.categories import CardDefCategory
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.fields import ItemField
|
||||
from wcs.fields import StringField
|
||||
|
@ -31,6 +33,28 @@ def teardown_module(module):
|
|||
clean_temporary_pub()
|
||||
|
||||
|
||||
def export_to_indented_xml(carddef, include_id=False):
|
||||
carddef_xml = ET.fromstring(ET.tostring(carddef.export_to_xml(include_id=include_id)))
|
||||
indent(carddef_xml)
|
||||
return carddef_xml
|
||||
|
||||
|
||||
def assert_compare_carddef(carddef1, carddef2, include_id=False):
|
||||
assert (
|
||||
ET.tostring(export_to_indented_xml(carddef1, include_id=include_id)) ==
|
||||
ET.tostring(export_to_indented_xml(carddef2, include_id=include_id)))
|
||||
assert (
|
||||
carddef1.export_to_json(include_id=include_id, indent=2) ==
|
||||
carddef2.export_to_json(include_id=include_id, indent=2))
|
||||
|
||||
|
||||
def assert_xml_import_export_works(carddef, include_id=False):
|
||||
carddef_xml = carddef.export_to_xml(include_id=include_id)
|
||||
carddef2 = CardDef.import_from_xml_tree(carddef_xml, include_id=include_id)
|
||||
assert_compare_carddef(carddef, carddef2, include_id=include_id)
|
||||
return carddef2
|
||||
|
||||
|
||||
def test_basics(pub):
|
||||
carddef = CardDef()
|
||||
carddef.name = 'foo'
|
||||
|
@ -159,6 +183,40 @@ def test_xml_export_import(pub):
|
|||
assert custom_views[1].formdef_type == 'carddef'
|
||||
|
||||
|
||||
def test_xml_export_import_category_reference(pub):
|
||||
CardDefCategory.wipe()
|
||||
CardDef.wipe()
|
||||
|
||||
cat = CardDefCategory()
|
||||
cat.name = 'test category'
|
||||
cat.store()
|
||||
|
||||
carddef = CardDef()
|
||||
carddef.name = 'foo'
|
||||
carddef.category_id = cat.id
|
||||
f2 = assert_xml_import_export_works(carddef)
|
||||
assert f2.category_id == carddef.category_id
|
||||
|
||||
f2 = assert_xml_import_export_works(carddef, include_id=True)
|
||||
assert f2.category_id == carddef.category_id
|
||||
|
||||
carddef_xml_with_id = carddef.export_to_xml(include_id=True)
|
||||
|
||||
# check there's no reference to a non-existing category
|
||||
CardDefCategory.wipe()
|
||||
assert CardDef.import_from_xml_tree(carddef_xml_with_id, include_id=False).category_id is None
|
||||
assert CardDef.import_from_xml_tree(carddef_xml_with_id, include_id=True).category_id is None
|
||||
|
||||
# check an import that is not using id fields will find the category by its
|
||||
# name
|
||||
cat = CardDefCategory()
|
||||
cat.id = '2'
|
||||
cat.name = 'test category'
|
||||
cat.store()
|
||||
assert CardDef.import_from_xml_tree(carddef_xml_with_id, include_id=False).category_id == '2'
|
||||
assert CardDef.import_from_xml_tree(carddef_xml_with_id, include_id=True).category_id is None
|
||||
|
||||
|
||||
def test_template_access(pub):
|
||||
CardDef.wipe()
|
||||
carddef = CardDef()
|
||||
|
|
|
@ -6,9 +6,8 @@ import pytest
|
|||
|
||||
from django.utils.six import BytesIO
|
||||
from quixote import cleanup
|
||||
from wcs import publisher
|
||||
|
||||
from wcs.categories import Category
|
||||
from wcs.categories import Category, CardDefCategory
|
||||
|
||||
from utilities import create_temporary_pub
|
||||
|
||||
|
@ -25,49 +24,53 @@ def teardown_module(module):
|
|||
shutil.rmtree(pub.APP_DIR)
|
||||
|
||||
|
||||
def test_store():
|
||||
Category.wipe()
|
||||
test = Category()
|
||||
@pytest.mark.parametrize('category_class', [Category, CardDefCategory])
|
||||
def test_store(category_class):
|
||||
category_class.wipe()
|
||||
test = category_class()
|
||||
test.name = 'Test'
|
||||
test.description = 'Hello world'
|
||||
test.store()
|
||||
test2 = Category.get(1)
|
||||
test2 = category_class.get(1)
|
||||
assert test.id == test2.id
|
||||
assert test.name == test2.name
|
||||
assert test.description == test2.description
|
||||
|
||||
|
||||
def test_urlname():
|
||||
Category.wipe()
|
||||
test = Category()
|
||||
@pytest.mark.parametrize('category_class', [Category, CardDefCategory])
|
||||
def test_urlname(category_class):
|
||||
category_class.wipe()
|
||||
test = category_class()
|
||||
test.name = 'Test'
|
||||
test.description = 'Hello world'
|
||||
test.store()
|
||||
test = Category.get(1)
|
||||
test = category_class.get(1)
|
||||
assert test.url_name == 'test'
|
||||
|
||||
|
||||
def test_duplicate_urlname():
|
||||
Category.wipe()
|
||||
test = Category()
|
||||
@pytest.mark.parametrize('category_class', [Category, CardDefCategory])
|
||||
def test_duplicate_urlname(category_class):
|
||||
category_class.wipe()
|
||||
test = category_class()
|
||||
test.name = 'Test'
|
||||
test.store()
|
||||
test = Category.get(1)
|
||||
test = category_class.get(1)
|
||||
assert test.url_name == 'test'
|
||||
|
||||
test2 = Category()
|
||||
test2 = category_class()
|
||||
test2.name = 'Test'
|
||||
test2.store()
|
||||
test2 = Category.get(2)
|
||||
test2 = category_class.get(2)
|
||||
assert test2.url_name == 'test-2'
|
||||
|
||||
|
||||
def test_sort_positions():
|
||||
Category.wipe()
|
||||
@pytest.mark.parametrize('category_class', [Category, CardDefCategory])
|
||||
def test_sort_positions(category_class):
|
||||
category_class.wipe()
|
||||
|
||||
categories = []
|
||||
for i in range(10):
|
||||
test = Category()
|
||||
test = category_class()
|
||||
test.name = 'Test %s' % i
|
||||
test.position = 10-i
|
||||
categories.append(test)
|
||||
|
@ -76,35 +79,37 @@ def test_sort_positions():
|
|||
for i in range(8, 10):
|
||||
categories[i].position = None
|
||||
|
||||
Category.sort_by_position(categories)
|
||||
category_class.sort_by_position(categories)
|
||||
assert categories[0].name == 'Test 7'
|
||||
assert categories[-1].name in ('Test 8', 'Test 9')
|
||||
|
||||
|
||||
def test_xml_export():
|
||||
Category.wipe()
|
||||
test = Category()
|
||||
@pytest.mark.parametrize('category_class', [Category, CardDefCategory])
|
||||
def test_xml_export(category_class):
|
||||
category_class.wipe()
|
||||
test = category_class()
|
||||
test.id = 1
|
||||
test.name = 'Test'
|
||||
test.description = 'Hello world'
|
||||
test.store()
|
||||
test = Category.get(1)
|
||||
test = category_class.get(1)
|
||||
|
||||
assert b'<name>Test</name>' in test.export_to_xml_string(include_id=True)
|
||||
assert b' id="1"' in test.export_to_xml_string(include_id=True)
|
||||
assert b' id="1"' not in test.export_to_xml_string(include_id=False)
|
||||
|
||||
|
||||
def test_xml_import():
|
||||
Category.wipe()
|
||||
test = Category()
|
||||
@pytest.mark.parametrize('category_class', [Category, CardDefCategory])
|
||||
def test_xml_import(category_class):
|
||||
category_class.wipe()
|
||||
test = category_class()
|
||||
test.name = 'Test'
|
||||
test.description = 'Hello world'
|
||||
test.store()
|
||||
test = Category.get(1)
|
||||
test = category_class.get(1)
|
||||
|
||||
fd = BytesIO(test.export_to_xml_string(include_id=True))
|
||||
test2 = Category.import_from_xml(fd, include_id=True)
|
||||
test2 = category_class.import_from_xml(fd, include_id=True)
|
||||
assert test.id == test2.id
|
||||
assert test.name == test2.name
|
||||
assert test.description == test2.description
|
||||
|
@ -129,40 +134,43 @@ def test_load_old_pickle():
|
|||
assert test.description == test2.description
|
||||
|
||||
|
||||
def test_get_by_urlname():
|
||||
Category.wipe()
|
||||
test = Category()
|
||||
@pytest.mark.parametrize('category_class', [Category, CardDefCategory])
|
||||
def test_get_by_urlname(category_class):
|
||||
category_class.wipe()
|
||||
test = category_class()
|
||||
test.id = 1
|
||||
test.name = 'Test'
|
||||
test.description = 'Hello world'
|
||||
test.store()
|
||||
test = Category.get(1)
|
||||
test2 = Category.get_by_urlname('test')
|
||||
test = category_class.get(1)
|
||||
test2 = category_class.get_by_urlname('test')
|
||||
assert test.id == test2.id
|
||||
|
||||
|
||||
def test_has_urlname():
|
||||
Category.wipe()
|
||||
test = Category()
|
||||
@pytest.mark.parametrize('category_class', [Category, CardDefCategory])
|
||||
def test_has_urlname(category_class):
|
||||
category_class.wipe()
|
||||
test = category_class()
|
||||
test.id = 1
|
||||
test.name = 'Test'
|
||||
test.description = 'Hello world'
|
||||
test.store()
|
||||
test = Category.get(1)
|
||||
test = category_class.get(1)
|
||||
|
||||
assert Category.has_urlname('test')
|
||||
assert not Category.has_urlname('foobar')
|
||||
assert category_class.has_urlname('test')
|
||||
assert not category_class.has_urlname('foobar')
|
||||
|
||||
|
||||
def test_remove_self():
|
||||
Category.wipe()
|
||||
test = Category()
|
||||
@pytest.mark.parametrize('category_class', [Category, CardDefCategory])
|
||||
def test_remove_self(category_class):
|
||||
category_class.wipe()
|
||||
test = category_class()
|
||||
test.id = 1
|
||||
test.name = 'Test'
|
||||
test.description = 'Hello world'
|
||||
test.store()
|
||||
test = Category.get(1)
|
||||
test = category_class.get(1)
|
||||
test.remove_self()
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
Category.get(1)
|
||||
category_class.get(1)
|
||||
|
|
|
@ -18,19 +18,22 @@ from quixote import redirect
|
|||
from quixote.directory import Directory
|
||||
from quixote.html import TemplateIO, htmltext
|
||||
|
||||
from wcs.qommon import _
|
||||
from wcs.categories import Category
|
||||
from wcs.qommon import _, N_
|
||||
from wcs.categories import Category, CardDefCategory
|
||||
from wcs.qommon.form import *
|
||||
from wcs.qommon.backoffice.menu import html_top
|
||||
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.formdef import FormDef
|
||||
|
||||
|
||||
class CategoryUI(object):
|
||||
category_class = Category
|
||||
|
||||
def __init__(self, category):
|
||||
self.category = category
|
||||
if self.category is None:
|
||||
self.category = Category()
|
||||
self.category = self.category_class()
|
||||
|
||||
def get_form(self):
|
||||
form = Form(enctype='multipart/form-data')
|
||||
|
@ -39,10 +42,11 @@ class CategoryUI(object):
|
|||
form.add(WysiwygTextWidget, 'description', title=_('Description'),
|
||||
cols=80, rows=10,
|
||||
value=self.category.description)
|
||||
form.add(StringWidget, 'redirect_url', size=32,
|
||||
title=_('URL Redirection'),
|
||||
hint=_('If set, redirect the site category page to the given URL.'),
|
||||
value=self.category.redirect_url)
|
||||
if self.category_class == Category:
|
||||
form.add(StringWidget, 'redirect_url', size=32,
|
||||
title=_('URL Redirection'),
|
||||
hint=_('If set, redirect the site category page to the given URL.'),
|
||||
value=self.category.redirect_url)
|
||||
form.add_submit('submit', _('Submit'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
return form
|
||||
|
@ -51,22 +55,32 @@ class CategoryUI(object):
|
|||
self.category.name = form.get_widget('name').parse()
|
||||
|
||||
name = form.get_widget('name').parse()
|
||||
category_names = [x.name for x in Category.select() if x.id != self.category.id]
|
||||
category_names = [x.name for x in self.category_class.select() if x.id != self.category.id]
|
||||
if name in category_names:
|
||||
form.get_widget('name').set_error(_('This name is already used'))
|
||||
raise ValueError()
|
||||
|
||||
self.category.description = form.get_widget('description').parse()
|
||||
self.category.redirect_url = form.get_widget('redirect_url').parse()
|
||||
if form.get_widget('redirect_url'):
|
||||
self.category.redirect_url = form.get_widget('redirect_url').parse()
|
||||
self.category.store()
|
||||
|
||||
|
||||
class CardDefCategoryUI(CategoryUI):
|
||||
category_class = CardDefCategory
|
||||
|
||||
|
||||
class CategoryPage(Directory):
|
||||
category_class = Category
|
||||
category_ui_class = CategoryUI
|
||||
formdef_class = FormDef
|
||||
usage_title = N_('Forms in this category')
|
||||
empty_message = N_('no form associated to this category')
|
||||
_q_exports = ['', 'edit', 'delete', 'description']
|
||||
|
||||
def __init__(self, component):
|
||||
self.category = Category.get(component)
|
||||
self.category_ui = CategoryUI(self.category)
|
||||
self.category = self.category_class.get(component)
|
||||
self.category_ui = self.category_ui_class(self.category)
|
||||
get_response().breadcrumb.append((component + '/', self.category.name))
|
||||
|
||||
def _q_index(self):
|
||||
|
@ -86,17 +100,17 @@ class CategoryPage(Directory):
|
|||
r += self.category.get_description_html_text()
|
||||
r += htmltext('</div>')
|
||||
|
||||
formdefs = FormDef.select(order_by='name')
|
||||
formdefs = self.formdef_class.select(order_by='name')
|
||||
formdefs = [x for x in formdefs if x.category_id == self.category.id]
|
||||
r += htmltext('<div class="bo-block">')
|
||||
r += htmltext('<h3>%s</h3>') % _('Forms in this category')
|
||||
r += htmltext('<h3>%s</h3>') % _(self.usage_title)
|
||||
r += htmltext('<ul>')
|
||||
for formdef in formdefs:
|
||||
r += htmltext('<li><a href="../../%s/">') % str(formdef.id)
|
||||
r += formdef.name
|
||||
r += htmltext('</a></li>')
|
||||
if not formdefs:
|
||||
r += htmltext('<li>%s</li>') % _('no form associated to this category')
|
||||
r += htmltext('<li>%s</li>') % _(self.empty_message)
|
||||
r += htmltext('</ul>')
|
||||
r += htmltext('</div>')
|
||||
return r.getvalue()
|
||||
|
@ -165,8 +179,20 @@ class CategoryPage(Directory):
|
|||
return r.getvalue()
|
||||
|
||||
|
||||
class CardDefCategoryPage(CategoryPage):
|
||||
category_class = CardDefCategory
|
||||
category_ui_class = CardDefCategoryUI
|
||||
formdef_class = CardDef
|
||||
usage_title = N_('Card models in this category')
|
||||
empty_message = N_('no card model associated to this category')
|
||||
|
||||
|
||||
class CategoriesDirectory(Directory):
|
||||
_q_exports = ['', 'new', 'update_order']
|
||||
category_class = Category
|
||||
category_ui_class = CategoryUI
|
||||
category_page_class = CategoryPage
|
||||
category_explanation = N_('Categories are used to sort the different forms.')
|
||||
|
||||
def _q_index(self):
|
||||
get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js',
|
||||
|
@ -180,11 +206,10 @@ class CategoriesDirectory(Directory):
|
|||
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Category')
|
||||
r += htmltext('</span>')
|
||||
r += htmltext('</div>')
|
||||
r += htmltext('<div class="explanation bo-block"><p>%s</p></div>') % \
|
||||
_('Categories are used to sort the different forms.')
|
||||
categories = Category.select()
|
||||
r += htmltext('<div class="explanation bo-block"><p>%s</p></div>') % _(self.category_explanation)
|
||||
categories = self.category_class.select()
|
||||
r += htmltext('<ul class="biglist sortable" id="category-list">')
|
||||
Category.sort_by_position(categories)
|
||||
self.category_class.sort_by_position(categories)
|
||||
for category in categories:
|
||||
r += htmltext('<li class="biglistitem" id="itemId_%s">') % category.id
|
||||
r += htmltext('<strong class="label"><a href="%s/">%s</a></strong>') % (
|
||||
|
@ -196,7 +221,7 @@ class CategoriesDirectory(Directory):
|
|||
def update_order(self):
|
||||
request = get_request()
|
||||
new_order = request.form['order'].strip(';').split(';')
|
||||
categories = Category.select()
|
||||
categories = self.category_class.select()
|
||||
dict = {}
|
||||
for l in categories:
|
||||
dict[str(l.id)] = l
|
||||
|
@ -207,7 +232,7 @@ class CategoriesDirectory(Directory):
|
|||
|
||||
def new(self):
|
||||
get_response().breadcrumb.append( ('new', _('New')) )
|
||||
category_ui = CategoryUI(None)
|
||||
category_ui = self.category_ui_class(None)
|
||||
form = category_ui.get_form()
|
||||
if form.get_widget('cancel').parse():
|
||||
return redirect('.')
|
||||
|
@ -227,8 +252,15 @@ class CategoriesDirectory(Directory):
|
|||
return r.getvalue()
|
||||
|
||||
def _q_lookup(self, component):
|
||||
return CategoryPage(component)
|
||||
return self.category_page_class(component)
|
||||
|
||||
def _q_traverse(self, path):
|
||||
get_response().breadcrumb.append( ('categories/', _('Categories')) )
|
||||
return super(CategoriesDirectory, self)._q_traverse(path)
|
||||
return super()._q_traverse(path)
|
||||
|
||||
|
||||
class CardDefCategoriesDirectory(CategoriesDirectory):
|
||||
category_class = CardDefCategory
|
||||
category_ui_class = CardDefCategoryUI
|
||||
category_page_class = CardDefCategoryPage
|
||||
category_explanation = N_('Categories are used to sort the different card models.')
|
||||
|
|
|
@ -60,19 +60,20 @@ from .logged_errors import LoggedErrorsDirectory
|
|||
from wcs.backoffice.snapshots import SnapshotsDirectory
|
||||
|
||||
|
||||
def get_categories():
|
||||
t = sorted([(misc.simplify(x.name), x.id, x.name, x.id) for x in Category.select()])
|
||||
def get_categories(category_class):
|
||||
t = sorted([(misc.simplify(x.name), x.id, x.name, x.id) for x in category_class.select()])
|
||||
return [x[1:] for x in t]
|
||||
|
||||
|
||||
class FormDefUI(object):
|
||||
formdef_class = FormDef
|
||||
category_class = Category
|
||||
|
||||
def __init__(self, formdef):
|
||||
self.formdef = formdef
|
||||
|
||||
def get_categories(self):
|
||||
return get_categories()
|
||||
return get_categories(self.category_class)
|
||||
|
||||
@classmethod
|
||||
def get_workflows(cls, condition=lambda x: True):
|
||||
|
@ -165,6 +166,8 @@ class FieldsDirectory(FieldsDirectory):
|
|||
|
||||
|
||||
class OptionsDirectory(Directory):
|
||||
category_class = Category
|
||||
category_empty_choice = N_('Select a category for this form')
|
||||
_q_exports = ['confirmation', 'only_allow_one',
|
||||
'always_advertise', 'tracking_code', 'online_status', 'captcha',
|
||||
'description', 'keywords', 'category', 'management',
|
||||
|
@ -265,9 +268,9 @@ class OptionsDirectory(Directory):
|
|||
return self.handle(form, _('Keywords'))
|
||||
|
||||
def category(self):
|
||||
categories = get_categories()
|
||||
categories = get_categories(self.category_class)
|
||||
form = Form(enctype='multipart/form-data')
|
||||
form.widgets.append(HtmlWidget('<p>%s</p>' % _('Select a category for this form')))
|
||||
form.widgets.append(HtmlWidget('<p>%s</p>' % _(self.category_empty_choice)))
|
||||
form.add(SingleSelectWidget, 'category_id', title=_('Category'),
|
||||
value=self.formdef.category_id,
|
||||
options=[(None, '---', '')] + categories)
|
||||
|
@ -421,6 +424,7 @@ class FormDefPage(Directory):
|
|||
formdef_export_prefix = 'form'
|
||||
formdef_ui_class = FormDefUI
|
||||
formdef_default_workflow = '_default'
|
||||
options_directory_class = OptionsDirectory
|
||||
|
||||
delete_message = N_('You are about to irrevocably delete this form.')
|
||||
delete_title = N_('Deleting Form:')
|
||||
|
@ -442,7 +446,7 @@ class FormDefPage(Directory):
|
|||
self.fields.html_top = self.html_top
|
||||
self.role = WorkflowRoleDirectory(self.formdef)
|
||||
self.role.html_top = self.html_top
|
||||
self.options = OptionsDirectory(self.formdef)
|
||||
self.options = self.options_directory_class(self.formdef)
|
||||
self.logged_errors_dir = LoggedErrorsDirectory(parent_dir=self, formdef_id=self.formdef.id)
|
||||
self.snapshots_dir = SnapshotsDirectory(self.formdef)
|
||||
|
||||
|
@ -714,31 +718,6 @@ class FormDefPage(Directory):
|
|||
r += htmltext('</div>')
|
||||
return r.getvalue()
|
||||
|
||||
def category(self):
|
||||
categories = get_categories()
|
||||
form = Form(enctype='multipart/form-data')
|
||||
form.add(SingleSelectWidget, 'category_id',
|
||||
value=self.formdef.category_id,
|
||||
options=[(None, '---', '')] + categories)
|
||||
if not self.formdef.is_readonly():
|
||||
form.add_submit('submit', _('Submit'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
if form.get_widget('cancel').parse():
|
||||
return redirect('.')
|
||||
|
||||
if not form.is_submitted() or form.has_errors():
|
||||
get_response().breadcrumb.append( ('category', _('Category')) )
|
||||
self.html_top(title = self.formdef.name)
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s</h2>') % _('Category')
|
||||
r += htmltext('<p>%s</p>') % _('Select a category for this form')
|
||||
r += form.render()
|
||||
return r.getvalue()
|
||||
else:
|
||||
self.formdef.category_id = form.get_widget('category_id').parse()
|
||||
self.formdef.store(comment=_('Change of category'))
|
||||
return redirect('.')
|
||||
|
||||
def _roles_selection(self, title, attribute, description=None,
|
||||
include_logged_users_role=True):
|
||||
form = Form(enctype='multipart/form-data')
|
||||
|
@ -1564,6 +1543,7 @@ class FormsDirectory(AccessControlled, Directory):
|
|||
_q_exports = ['', 'new', ('import', 'p_import'),
|
||||
'blocks', 'categories', ('data-sources', 'data_sources')]
|
||||
|
||||
category_class = Category
|
||||
categories = CategoriesDirectory()
|
||||
blocks = BlocksDirectory(section='forms')
|
||||
data_sources = NamedDataSourcesDirectoryInForms()
|
||||
|
@ -1571,6 +1551,7 @@ class FormsDirectory(AccessControlled, Directory):
|
|||
formdef_page_class = FormDefPage
|
||||
formdef_ui_class = FormDefUI
|
||||
|
||||
top_title = N_('Forms')
|
||||
import_title = N_('Import Form')
|
||||
import_submit_label = N_('Import Form')
|
||||
import_paragraph = N_(
|
||||
|
@ -1593,30 +1574,13 @@ class FormsDirectory(AccessControlled, Directory):
|
|||
return super()._q_traverse(path)
|
||||
|
||||
def _q_index(self):
|
||||
self.html_top(title = _('Forms'))
|
||||
self.html_top(title=_(self.top_title))
|
||||
r = TemplateIO(html=True)
|
||||
get_response().add_javascript(['jquery.js', 'widget_list.js'])
|
||||
has_roles = bool(Role.count())
|
||||
r += self.form_actions()
|
||||
|
||||
r += htmltext('<div id="appbar">')
|
||||
r += htmltext('<h2>%s</h2>') % _('Forms')
|
||||
if has_roles:
|
||||
r += htmltext('<span class="actions">')
|
||||
r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources')
|
||||
if get_publisher().has_site_option('fields-blocks'):
|
||||
r += htmltext('<a href="blocks/">%s</a>') % _('Fields blocks')
|
||||
if get_publisher().get_backoffice_root().is_accessible('categories'):
|
||||
r += htmltext('<a href="categories/">%s</a>') % _('Categories')
|
||||
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import')
|
||||
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Form')
|
||||
r += htmltext('</span>')
|
||||
r += htmltext('</div>')
|
||||
|
||||
if not has_roles:
|
||||
r += htmltext('<p>%s</p>') % _('You first have to define roles.')
|
||||
|
||||
cats = Category.select()
|
||||
Category.sort_by_position(cats)
|
||||
cats = self.category_class.select()
|
||||
self.category_class.sort_by_position(cats)
|
||||
one = False
|
||||
formdefs = self.formdef_class.select(order_by='name', ignore_errors=True, lightweight=True)
|
||||
for c in cats:
|
||||
|
@ -1638,6 +1602,27 @@ class FormsDirectory(AccessControlled, Directory):
|
|||
r += self.form_list(l2, title = title)
|
||||
return r.getvalue()
|
||||
|
||||
def form_actions(self):
|
||||
r = TemplateIO(html=True)
|
||||
has_roles = bool(Role.count())
|
||||
r += htmltext('<div id="appbar">')
|
||||
r += htmltext('<h2>%s</h2>') % _('Forms')
|
||||
if has_roles:
|
||||
r += htmltext('<span class="actions">')
|
||||
r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources')
|
||||
if get_publisher().has_site_option('fields-blocks'):
|
||||
r += htmltext('<a href="blocks/">%s</a>') % _('Fields blocks')
|
||||
if get_publisher().get_backoffice_root().is_accessible('categories'):
|
||||
r += htmltext('<a href="categories/">%s</a>') % _('Categories')
|
||||
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import')
|
||||
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Form')
|
||||
r += htmltext('</span>')
|
||||
r += htmltext('</div>')
|
||||
|
||||
if not has_roles:
|
||||
r += htmltext('<p>%s</p>') % _('You first have to define roles.')
|
||||
return r.getvalue()
|
||||
|
||||
def form_list(self, formdefs, title):
|
||||
r = TemplateIO(html=True)
|
||||
if title:
|
||||
|
|
|
@ -867,6 +867,7 @@ class SettingsDirectory(QommonSettingsDirectory):
|
|||
if not get_cfg('sp', {}).get('idp-manage-roles'):
|
||||
form.add(CheckboxWidget, 'roles', title = _('Roles'), value = True)
|
||||
form.add(CheckboxWidget, 'categories', title = _('Categories'), value = True)
|
||||
form.add(CheckboxWidget, 'carddef_categories', title = _('Card Model Categories'), value = True)
|
||||
form.add(CheckboxWidget, 'settings', title = _('Settings'), value = False)
|
||||
form.add(CheckboxWidget, 'datasources', title=_('Data sources'), value=True)
|
||||
form.add(CheckboxWidget, 'mail-templates', title=_('Mail templates'), value=True)
|
||||
|
@ -894,7 +895,7 @@ class SettingsDirectory(QommonSettingsDirectory):
|
|||
c = BytesIO()
|
||||
z = zipfile.ZipFile(c, 'w')
|
||||
for d in self.dirs:
|
||||
if d not in ('roles', 'categories', 'datasources', 'wscalls', 'mail-templates'):
|
||||
if d not in ('roles', 'categories', 'carddef_categories', 'datasources', 'wscalls', 'mail-templates'):
|
||||
continue
|
||||
path = os.path.join(self.app_dir, d)
|
||||
if not os.path.exists(path):
|
||||
|
@ -942,8 +943,8 @@ class SettingsDirectory(QommonSettingsDirectory):
|
|||
job.store()
|
||||
|
||||
dirs = []
|
||||
for w in ('formdefs', 'carddefs', 'workflows', 'roles', 'categories',
|
||||
'datasources', 'wscalls', 'mail-templates', 'blockdefs'):
|
||||
for w in ('formdefs', 'carddefs', 'workflows', 'roles', 'categories', 'carddef_categories',
|
||||
'datasources', 'wscalls', 'mail-templates', 'blockdefs'):
|
||||
if form.get_widget(w) and form.get_widget(w).parse():
|
||||
dirs.append(w)
|
||||
if not dirs and not form.get_widget('settings').parse():
|
||||
|
|
|
@ -364,6 +364,8 @@ class ApiCardsDirectory(Directory):
|
|||
'title': x.name,
|
||||
'slug': x.url_name,
|
||||
'url': x.get_url(),
|
||||
'category_slug': x.category.url_name if x.category else None,
|
||||
'category_name': x.category.name if x.category else None,
|
||||
'description': x.description or '',
|
||||
'keywords': x.keywords_list,
|
||||
'custom_views': get_custom_views(x),
|
||||
|
|
|
@ -16,29 +16,32 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
|
||||
from quixote import get_publisher, get_request, get_response, get_session, redirect
|
||||
from quixote import get_publisher, get_response, get_session, redirect
|
||||
from quixote.directory import Directory
|
||||
from quixote.html import TemplateIO, htmltext
|
||||
|
||||
from ..qommon import _, N_, misc
|
||||
from ..qommon import _, N_
|
||||
from ..qommon.misc import C_
|
||||
from ..qommon.storage import NotEqual, Null
|
||||
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.categories import CardDefCategory
|
||||
from wcs.roles import Role
|
||||
from wcs.workflows import Workflow
|
||||
from wcs.admin.forms import FormsDirectory, FormDefPage, FormDefUI, html_top
|
||||
from wcs.admin.categories import CardDefCategoriesDirectory
|
||||
from wcs.admin.forms import FormsDirectory, FormDefPage, FormDefUI, html_top, OptionsDirectory
|
||||
from wcs.admin.logged_errors import LoggedErrorsDirectory
|
||||
from wcs.admin import utils
|
||||
|
||||
|
||||
class CardDefUI(FormDefUI):
|
||||
formdef_class = CardDef
|
||||
category_class = CardDefCategory
|
||||
|
||||
def get_categories(self):
|
||||
return []
|
||||
|
||||
class CardDefOptionsDirectory(OptionsDirectory):
|
||||
category_class = CardDefCategory
|
||||
category_empty_choice = N_('Select a category for this card model')
|
||||
|
||||
|
||||
class CardDefPage(FormDefPage):
|
||||
|
@ -47,6 +50,8 @@ class CardDefPage(FormDefPage):
|
|||
formdef_ui_class = CardDefUI
|
||||
formdef_default_workflow = '_carddef_default'
|
||||
|
||||
options_directory_class = CardDefOptionsDirectory
|
||||
|
||||
delete_message = N_('You are about to irrevocably delete this card model.')
|
||||
delete_title = N_('Deleting Card Model:')
|
||||
overwrite_message = N_(
|
||||
|
@ -86,6 +91,17 @@ class CardDefPage(FormDefPage):
|
|||
'label': label,
|
||||
'current_value': current_value})
|
||||
|
||||
r += htmltext('<div class="bo-block">')
|
||||
r += htmltext('<h3>%s</h3>') % _('Information')
|
||||
r += htmltext('<ul class="biglist optionslist">')
|
||||
|
||||
r += add_option_line(
|
||||
'options/category', _('Category'),
|
||||
self.formdef.category_id and self.formdef.category and
|
||||
self.formdef.category.name or C_('category|None'))
|
||||
r += htmltext('</ul>')
|
||||
r += htmltext('</div>')
|
||||
|
||||
r += htmltext('<div class="splitcontent-left">')
|
||||
r += htmltext('<div class="bo-block">')
|
||||
r += htmltext('<h3>%s</h3>') % _('Workflow')
|
||||
|
@ -214,11 +230,15 @@ class CardDefPage(FormDefPage):
|
|||
|
||||
|
||||
class CardsDirectory(FormsDirectory):
|
||||
_q_exports = ['', 'new', ('import', 'p_import')]
|
||||
_q_exports = ['', 'new', ('import', 'p_import'), 'categories']
|
||||
|
||||
category_class = CardDefCategory
|
||||
categories = CardDefCategoriesDirectory()
|
||||
formdef_class = CardDef
|
||||
formdef_page_class = CardDefPage
|
||||
formdef_ui_class = CardDefUI
|
||||
|
||||
top_title = N_('Card Models')
|
||||
import_title = N_('Import Card Model')
|
||||
import_submit_label = N_('Import Card Model')
|
||||
import_paragraph = N_(
|
||||
|
@ -238,20 +258,16 @@ class CardsDirectory(FormsDirectory):
|
|||
get_response().breadcrumb.append(('cards/', _('Card Models')))
|
||||
return Directory._q_traverse(self, path)
|
||||
|
||||
def _q_index(self):
|
||||
self.html_top(title=_('Card Models'))
|
||||
def form_actions(self):
|
||||
r = TemplateIO(html=True)
|
||||
|
||||
r += htmltext('<div id="appbar">')
|
||||
r += htmltext('<h2>%s</h2>') % _('Card Models')
|
||||
r += htmltext('<span class="actions">')
|
||||
r += htmltext('<a href="categories/">%s</a>') % _('Categories')
|
||||
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import')
|
||||
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Card Model')
|
||||
r += htmltext('</span>')
|
||||
r += htmltext('</div>')
|
||||
|
||||
formdefs = self.formdef_class.select(order_by='name', ignore_errors=True, lightweight=True)
|
||||
r += self.form_list(formdefs, title='')
|
||||
return r.getvalue()
|
||||
|
||||
def new(self):
|
||||
|
|
|
@ -33,6 +33,7 @@ from ..qommon import template
|
|||
from ..qommon.afterjobs import AfterJob
|
||||
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.categories import CardDefCategory
|
||||
from wcs import fields
|
||||
|
||||
from .management import ManagementDirectory, FormPage, FormFillPage, FormBackOfficeStatusPage
|
||||
|
@ -60,16 +61,23 @@ class DataManagementDirectory(ManagementDirectory):
|
|||
|
||||
def get_carddefs(self):
|
||||
user = get_request().user
|
||||
if user:
|
||||
for formdef in CardDef.select(order_by='name', ignore_errors=True, lightweight=True):
|
||||
if user.is_admin or formdef.is_of_concern_for_user(user):
|
||||
yield formdef
|
||||
if not user:
|
||||
return
|
||||
carddefs = CardDef.select(order_by='name', ignore_errors=True, lightweight=True)
|
||||
carddefs = [c for c in carddefs if user.is_admin or c.is_of_concern_for_user(user)]
|
||||
cats = CardDefCategory.select()
|
||||
CardDefCategory.sort_by_position(cats)
|
||||
for c in cats + [None]:
|
||||
for carddef in carddefs:
|
||||
if c is None and not carddef.category_id:
|
||||
yield carddef
|
||||
if c is not None and carddef.category_id == c.id:
|
||||
yield carddef
|
||||
|
||||
def _q_index(self):
|
||||
html_top('data_management', _('Cards'))
|
||||
if CardDef.count() == 0:
|
||||
return self.empty_site_message(_('Cards'))
|
||||
|
||||
return template.QommonTemplateResponse(
|
||||
templates=['wcs/backoffice/data-management.html'],
|
||||
context={'view': self})
|
||||
|
|
|
@ -23,6 +23,7 @@ from .qommon.storage import Contains, Equal, NotEqual, ILike
|
|||
from .qommon.template import Template
|
||||
|
||||
from wcs.carddata import CardData
|
||||
from wcs.categories import CardDefCategory
|
||||
from wcs.formdef import FormDef, get_formdefs_of_all_kinds
|
||||
|
||||
if not hasattr(types, 'ClassType'):
|
||||
|
@ -39,6 +40,8 @@ class CardDef(FormDef):
|
|||
|
||||
confirmation = False
|
||||
|
||||
category_class = CardDefCategory
|
||||
|
||||
def data_class(self, mode=None):
|
||||
if not 'carddef' in sys.modules:
|
||||
sys.modules['carddef'] = sys.modules[__name__]
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from quixote import get_publisher, get_response
|
||||
from quixote import get_publisher
|
||||
from quixote.html import htmltext
|
||||
|
||||
from .qommon import N_
|
||||
|
@ -34,10 +34,11 @@ class Category(XmlStorableObject):
|
|||
redirect_url = None
|
||||
|
||||
# declarations for serialization
|
||||
XML_NODES = [('name', 'str'), ('url_name', 'str'), ('description', 'str'),
|
||||
('redirect_url', 'str'), ('position', 'int')]
|
||||
XML_NODES = [
|
||||
('name', 'str'), ('url_name', 'str'), ('description', 'str'),
|
||||
('redirect_url', 'str'), ('position', 'int')]
|
||||
|
||||
def __init__(self, name = None):
|
||||
def __init__(self, name=None):
|
||||
StorableObject.__init__(self)
|
||||
self.name = name
|
||||
|
||||
|
@ -110,6 +111,14 @@ class Category(XmlStorableObject):
|
|||
return htmltext(text)
|
||||
|
||||
|
||||
class CardDefCategory(Category):
|
||||
_names = 'carddef_categories'
|
||||
xml_root_node = 'carddef_category'
|
||||
|
||||
# declarations for serialization
|
||||
XML_NODES = [('name', 'str'), ('url_name', 'str'), ('description', 'str'), ('position', 'int')]
|
||||
|
||||
|
||||
Substitutions.register('category_name', category=N_('General'), comment=N_('Category Name'))
|
||||
Substitutions.register('category_description', category=N_('General'), comment=N_('Category Description'))
|
||||
Substitutions.register('category_id', category=N_('General'), comment=N_('Category Identifier'))
|
||||
|
|
|
@ -142,6 +142,8 @@ class FormDef(StorableObject):
|
|||
'always_advertise', 'include_download_all_button',
|
||||
'has_captcha', 'skip_from_360_view']
|
||||
|
||||
category_class = Category
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FormDef, self).__init__(*args, **kwargs)
|
||||
self.fields = []
|
||||
|
@ -416,7 +418,7 @@ class FormDef(StorableObject):
|
|||
def get_category(self):
|
||||
if self.category_id:
|
||||
try:
|
||||
return Category.get(self.category_id)
|
||||
return self.category_class.get(self.category_id)
|
||||
except KeyError:
|
||||
return None
|
||||
else:
|
||||
|
@ -1147,11 +1149,11 @@ class FormDef(StorableObject):
|
|||
category_node = tree.find('category')
|
||||
if include_id and category_node.attrib.get('category_id'):
|
||||
category_id = str(category_node.attrib.get('category_id'))
|
||||
if Category.has_key(category_id):
|
||||
if cls.category_class.has_key(category_id):
|
||||
formdef.category_id = category_id
|
||||
else:
|
||||
category = xml_node_text(category_node)
|
||||
for c in Category.select():
|
||||
for c in cls.category_class.select():
|
||||
if c.name == category:
|
||||
formdef.category_id = c.id
|
||||
break
|
||||
|
|
|
@ -4,9 +4,13 @@
|
|||
{% block appbar-title %}{% trans "Cards" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<ul class="objects-list single-links">
|
||||
{% for carddef in view.get_carddefs %}
|
||||
<li><a href="{{ carddef.url_name }}/">{{ carddef.name }}</a></li>
|
||||
{% regroup view.get_carddefs by category.id as category_list %}
|
||||
<ul class="biglist">
|
||||
{% for category_id, object_list in category_list %}
|
||||
{% if not forloop.first or category_id %}<li><h3>{{ object_list.0.category.name|default:_('Misc') }}</h3></li>{% endif %}
|
||||
{% for carddef in object_list %}
|
||||
<li><a href="{{ carddef.url_name }}/">{{ carddef.name }}</a></li>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in New Issue