Merge branch 'master' of github.com:collective/collective.contact.core

This commit is contained in:
Cédric Messiant 2013-10-08 11:55:03 +02:00
commit dde54736ca
18 changed files with 156 additions and 16 deletions

View File

@ -4,9 +4,24 @@ Changelog
1.1 (unreleased)
----------------
- Added logo and activity rich field on organization type.
[thomasdesvenain]
- Fixed generate id from title on held positions and persons.
[thomasdesvenain]
- When we get the address of a contact, if the most direct address is empty,
look for the next.
[thomasdesvenain]
- Added Fax and Website fields to IContactDetails and IContactable.
[thomasdesvenain]
- Fixed javascript in @@add-organization view.
[vincentfretin]
- Fixed use parent address if we set Contact Details behaviour on held positions.
[thomasdesvenain]
1.0 (2013-09-13)

10
demo.cfg Normal file
View File

@ -0,0 +1,10 @@
[buildout]
extends = buildout.cfg
auto-checkout +=
collective.contact.facetednav
[instance]
eggs +=
collective.contact.facetednav
zcml +=
collective.contact.facetednav

View File

@ -13,5 +13,6 @@ entrouvert_push = git+ssh://git@repos.entrouvert.org
[sources]
collective.contact.core = git ${remotes:collective}/collective.contact.core.git pushurl=${remotes:collective_push}/collective.contact.core.git
collective.contact.facetednav = git ${remotes:collective}/collective.contact.facetednav.git pushurl=${remotes:collective_push}/collective.contact.facetednav.git
collective.contact.widget = git ${remotes:collective}/collective.contact.widget.git pushurl=${remotes:collective_push}/collective.contact.widget.git
plone.formwidget.masterselect = git ${remotes:collective}/plone.formwidget.masterselect.git pushurl=${remotes:collective_push}/plone.formwidget.masterselect.git

View File

@ -84,8 +84,10 @@ class IContactDetails(model.Schema):
label=_(u'Contact details'),
fields=('phone',
'cell_phone',
'fax',
'email',
'im_handle',
'website',
)
)
fieldset(
@ -119,6 +121,16 @@ class IContactDetails(model.Schema):
required=False,
)
fax = schema.TextLine(
title=_(u"Fax"),
required=False,
)
website = schema.TextLine(
title=_(u"Website"),
required=False,
)
im_handle = schema.TextLine(
title=_('Instant messenger handle'),
required=False,

View File

@ -36,7 +36,7 @@ class Contact(BaseView):
self.person = person
self.fullname = person.Title()
self.title = held_position.Title()
if IBirthday.providedBy(person):
birthday = person.birthday
if birthday is not None:
@ -57,7 +57,9 @@ class Contact(BaseView):
self.email = contact_details['email']
self.phone = contact_details['phone']
self.cell_phone = contact_details['cell_phone']
self.fax = contact_details['fax']
self.im_handle = contact_details['im_handle']
self.website = contact_details['website']
self.address = contact_details['address']
# also show fields that were added TTW

View File

@ -35,7 +35,8 @@ class Contactable(grok.Adapter):
return []
def _get_contactables(self):
"""Build a list of objects which have the IContactDetails behavior
"""
Build a list of objects which have the IContactDetails behavior
for each contact information (email, phone, ...)
we use the one of the first object in this list which have this information
"""
@ -46,7 +47,7 @@ class Contactable(grok.Adapter):
and IContactDetails.providedBy(related_item) \
and related_item not in contactables:
contactables.append(related_item)
return contactables
def _get_address(self, contactables):
@ -55,15 +56,19 @@ class Contactable(grok.Adapter):
if obj.use_parent_address is True:
continue
else:
return get_address(obj)
address = get_address(obj)
if address:
return address
return {}
def get_contact_details(self):
contact_details = {}
contact_details_fields = ['email', 'phone', 'cell_phone', 'im_handle']
contact_details_fields = ['email', 'phone', 'cell_phone', 'fax', 'website', 'im_handle']
contactables = self._get_contactables()
for field in contact_details_fields:
# search the object that carries the field
for obj in self._get_contactables():
for obj in contactables:
obj = aq_base(obj)
value = getattr(obj, field, '') or ''
if value:
@ -71,8 +76,7 @@ class Contactable(grok.Adapter):
break
else:
contact_details[field] = ''
contactables = self._get_contactables()
contact_details['address'] = self._get_address(contactables)
return contact_details
@ -81,10 +85,12 @@ class Contactable(grok.Adapter):
if self.context.is_created and self.context in contactables:
# we don't want self.context address if the object is already created
contactables.remove(self.context)
address = self._get_address(contactables)
if not address:
# Very important to return unicode here, RichTextWidget needs it.
return u''
template_path = os.path.join(TEMPLATES_DIR, 'address.pt')
template = ViewPageTemplateFile(template_path)
self.request = getRequest()

View File

@ -35,6 +35,7 @@ class Organization(BaseView):
parent_organizations = []
sub_organizations = []
positions = []
activity = ''
def update(self):
super(Organization, self).update()
@ -45,6 +46,7 @@ class Organization(BaseView):
factory = getUtility(IVocabularyFactory, "OrganizationTypesOrLevels")
vocabulary = factory(self.context)
self.type = vocabulary.getTerm(organization.organization_type).title
self.activity = self.context.activity
contactable = IContactable(organization)
organizations = contactable.organizations
@ -63,7 +65,9 @@ class Organization(BaseView):
self.email = contact_details['email']
self.phone = contact_details['phone']
self.cell_phone = contact_details['cell_phone']
self.fax = contact_details['fax']
self.im_handle = contact_details['im_handle']
self.website = contact_details['website']
self.address = contact_details['address']
# also show fields that were added TTW

View File

@ -14,7 +14,6 @@ class Person(BaseView):
person_title = ''
gender = ''
held_positions = ''
photo = ''
def update(self):
super(Person, self).update()
@ -43,8 +42,10 @@ class Person(BaseView):
contact_details = contactable.get_contact_details()
self.email = contact_details['email']
self.phone = contact_details['phone']
self.fax = contact_details['fax']
self.cell_phone = contact_details['cell_phone']
self.im_handle = contact_details['im_handle']
self.website = contact_details['website']
self.address = contact_details['address']
# also show fields that were added TTW

View File

@ -28,8 +28,10 @@ class Position(BaseView):
self.email = contact_details['email']
self.phone = contact_details['phone']
self.cell_phone = contact_details['cell_phone']
self.fax = contact_details['fax']
self.im_handle = contact_details['im_handle']
self.address = contact_details['address']
self.website = contact_details['website']
# also show fields that were added TTW
self.ttw_fields = get_ttw_fields(position)

View File

@ -69,12 +69,24 @@
i18n:translate="" />
</div>
<div class="field" tal:condition="view/fax">
<label><tal:block i18n:translate="">Fax number</tal:block>:</label>
<span tal:content="view/fax"
i18n:translate="" />
</div>
<div class="field" tal:condition="view/im_handle">
<label><tal:block i18n:translate="">IM handle</tal:block>:</label>
<span tal:content="view/im_handle"
i18n:translate="" />
</div>
<div class="field" tal:condition="view/website">
<label><tal:block i18n:translate="">Website</tal:block>:</label>
<a tal:content="view/website"
tal:attributes="href view/website" />
</div>
<div id="address"
tal:replace="structure view/render_address" />

View File

@ -7,12 +7,21 @@
<div metal:fill-slot="main">
<h1 tal:content="view/name"></h1>
<div id="logo" class="field" tal:condition="context/logo">
<img tal:replace="structure python: view.widgets['logo'].render()" />
</div>
<div id="type" class="field">
<label><tal:block i18n:translate="">Organization type</tal:block>:</label>
<span tal:content="view/type"
i18n:translate="" />
</div>
<div id="activity" class="field" tal:condition="view/activity">
<label><tal:block i18n:translate="">Activity</tal:block>:</label>
<span tal:content="structure view/activity/output" />
</div>
<div id="organizations" class="field" tal:condition="view/parent_organizations">
<label><tal:block i18n:translate="">Parent organizations</tal:block>:</label>
<ul>
@ -43,12 +52,24 @@
i18n:translate="" />
</div>
<div class="field" tal:condition="view/fax">
<label><tal:block i18n:translate="">Fax number</tal:block>:</label>
<span tal:content="view/fax"
i18n:translate="" />
</div>
<div class="field" tal:condition="view/im_handle">
<label><tal:block i18n:translate="">IM handle</tal:block>:</label>
<span tal:content="view/im_handle"
i18n:translate="" />
</div>
<div class="field" tal:condition="view/website">
<label><tal:block i18n:translate="">Website</tal:block>:</label>
<a tal:content="view/website"
tal:attributes="href view/website" />
</div>
<div id="address"
tal:replace="structure view/render_address" />

View File

@ -35,12 +35,24 @@
i18n:translate="" />
</div>
<div class="field" tal:condition="view/fax">
<label><tal:block i18n:translate="">Fax number</tal:block>:</label>
<span tal:content="view/fax"
i18n:translate="" />
</div>
<div class="field" tal:condition="view/im_handle">
<label><tal:block i18n:translate="">IM handle</tal:block>:</label>
<span tal:content="view/im_handle"
i18n:translate="" />
</div>
<div class="field" tal:condition="view/website">
<label><tal:block i18n:translate="">Website</tal:block>:</label>
<a tal:content="view/website"
tal:attributes="href view/website" />
</div>
<div id="address"
tal:replace="structure view/render_address" />

View File

@ -43,12 +43,24 @@
i18n:translate="" />
</div>
<div class="field" tal:condition="view/fax">
<label><tal:block i18n:translate="">Fax number</tal:block>:</label>
<span tal:content="view/fax"
i18n:translate="" />
</div>
<div class="field" tal:condition="view/im_handle">
<label><tal:block i18n:translate="">IM handle</tal:block>:</label>
<span tal:content="view/im_handle"
i18n:translate="" />
</div>
<div class="field" tal:condition="view/website">
<label><tal:block i18n:translate="">Website</tal:block>:</label>
<a tal:content="view/website"
tal:attributes="href view/website" />
</div>
<div id="address"
tal:replace="structure view/render_address" />

View File

@ -93,6 +93,14 @@ class HeldPosition(Container):
use_parent_address = NO_VALUE
parent_address = NO_VALUE
def set_title(self, val):
return
def get_title(self):
return self.Title()
title = property(get_title, set_title)
def get_person(self):
"""Returns the person who holds the position
"""

View File

@ -2,24 +2,26 @@ from Acquisition import aq_inner, aq_chain
from zope.interface import implements
from zope.interface import Attribute
from zope import schema
from zope.component import getUtility
from zope.intid.interfaces import IIntIds
from z3c.form.interfaces import NO_VALUE
from zc.relation.interfaces import ICatalog
from five import grok
from Products.CMFPlone.utils import base_hasattr
from Products.CMFCore.utils import getToolByName
from plone.dexterity.content import Container
from plone.supermodel import model
from plone.dexterity.schema import DexteritySchemaPolicy
from plone.namedfile.field import NamedImage
from collective.contact.core import _
from collective.contact.core.browser.contactable import Contactable
from collective.contact.widget.interfaces import IContactContent
from zope.component._api import getUtility
from zope.intid.interfaces import IIntIds
from zc.relation.interfaces import ICatalog
from collective.contact.core.content.held_position import IHeldPosition
from Products.CMFCore.utils import getToolByName
from plone.app.textfield import RichText
class IOrganization(model.Schema, IContactContent):
@ -27,11 +29,22 @@ class IOrganization(model.Schema, IContactContent):
is_created = Attribute(u"Marker to know if the object is already created")
activity = RichText(
title=_("Activity"),
required=False,
)
organization_type = schema.Choice(
title=_("Type or level"),
vocabulary="OrganizationTypesOrLevels",
)
logo = NamedImage(
title=_("Logo"),
required=False,
)
def get_organizations_chain(self):
"""Returns the list of organizations and sub-organizations in this organization
e.g. for HR service in Division Bar in Organization Foo :

View File

@ -74,9 +74,14 @@ class Person(Container):
use_parent_address = NO_VALUE
parent_address = NO_VALUE
def set_title(self, val):
return
def get_title(self):
return u' '.join([x for x in (self.person_title, self.firstname, self.lastname) if x])
title = property(get_title, set_title)
def Title(self):
# must return utf8 and not unicode (Title() from basic behavior return utf8)
# attributes are stored as unicode
@ -84,10 +89,10 @@ class Person(Container):
def get_held_positions(self):
return [obj for obj in self.values() if IHeldPosition.providedBy(obj)]
def get_held_positions_titles(self):
return [p.Title() for p in self.get_held_positions()]
class PersonSchemaPolicy(grok.GlobalUtility,
DexteritySchemaPolicy):

View File

@ -48,6 +48,7 @@ class TestBehaviors(unittest.TestCase, BaseTest):
self.assertIsNone(item.getAttributes())
for attr in ('country', 'region', 'zip_code', 'city', 'street',
'number', 'im_handle', 'cell_phone', 'phone', 'email',
'fax', 'website',
'additional_address_details', 'birthday'):
self.assertTrue(hasattr(item, attr))
item.phone = '0655443322'

View File

@ -53,6 +53,7 @@ class TestPerson(TestContentTypes):
self.assertIn('degaulle', self.mydirectory)
degaulle = self.degaulle
self.assertEqual('Général Charles De Gaulle', degaulle.Title())
self.assertEqual(u'Général Charles De Gaulle', degaulle.title)
self.assertEqual('De Gaulle', degaulle.lastname)
self.assertEqual('Charles', degaulle.firstname)
self.assertEqual(datetime.date(1901, 11, 22), degaulle.birthday)
@ -172,6 +173,8 @@ class TestHeldPosition(TestContentTypes):
self.assertIn('adt', degaulle)
self.assertEqual(adt.Title(),
"Armée de terre")
self.assertEqual(adt.title,
"Armée de terre")
self.assertIn('gadt', degaulle)
self.assertEqual(gadt.Title(),
"Général de l'armée de terre (Armée de terre)")