Reworked ZClasses based CV product as Architypes product.
1 parent 4b27220 commit 5ae375661af6aa334bbdbca378556b5ed6ec3ea7
@Alex Tucker Alex Tucker authored on 24 Jul 2007
Showing 13 changed files
View
45
Extensions/Install.py 0 → 100644
# Python imports
from StringIO import StringIO
 
# CMF imports
from Products.CMFCore.utils import getToolByName
 
# Archetypes imports
from Products.Archetypes import listTypes
from Products.Archetypes.Extensions.utils import installTypes, install_subskin
 
# Products imports
from Products.CV.config import GLOBALS, PROJECTNAME
 
def install(self):
"""Install content types, skin layer, enable the portal factory
"""
 
out = StringIO()
 
print >> out, "Installing CV"
 
# Install types
classes = listTypes(PROJECTNAME)
installTypes(self, out,
classes,
PROJECTNAME)
print >> out, "Installed types"
 
# Install skin
install_subskin(self, out, GLOBALS)
print >> out, "Installed skin"
 
# Register types with portal_factory
factory = getToolByName(self, 'portal_factory')
types = factory.getFactoryTypes().keys()
if 'CV' not in types:
types.append('CV')
factory.manage_setPortalFactoryTypes(listOfTypeIds = types)
 
print >> out, "Added CV to portal_factory"
 
return out.getvalue()
View
36
__init__.py 0 → 100644
from Globals import package_home
from Products.CMFCore import utils, CMFCorePermissions, DirectoryView
from Products.CMFPlone.PloneUtilities import ToolInit
from Products.Archetypes.public import *
from Products.Archetypes import listTypes
from Products.Archetypes.utils import capitalize
 
import os, os.path, sys, content
 
# Get configuration data, permissions
from Products.CV.config import *
 
# Register skin directories so they can be added to portal_skins
DirectoryView.registerDirectory('skins', product_globals)
DirectoryView.registerDirectory('skins/cv_images', product_globals)
DirectoryView.registerDirectory('skins/cv_styles', product_globals)
DirectoryView.registerDirectory('skins/cv_templates', product_globals)
 
def initialize(context):
 
# Import the type, which results in registerType() being called
from content import CV, Employment, Education, Publication, Skill
 
# initialize the content, including types and add permissions
content_types, constructors, ftis = process_types(
listTypes(PROJECTNAME),
PROJECTNAME)
 
utils.ContentInit(
PROJECTNAME + ' Content',
content_types = content_types,
permission = DEFAULT_ADD_CONTENT_PERMISSION,
extra_constructors = constructors,
fti = ftis,
).initialize(context)
View
8
config.py 0 → 100644
from Products.CMFCore.CMFCorePermissions import setDefaultRoles
 
PROJECTNAME = "CV"
DEFAULT_ADD_CONTENT_PERMISSION = "Add CV"
product_globals=GLOBALS=globals()
 
setDefaultRoles(DEFAULT_ADD_CONTENT_PERMISSION, ('Manager', 'Owner',))
View
2
■■■
content/__init__.py 0 → 100644
from cv import CV, Education, Employment, Publication, Skill
View
405
content/cv.py 0 → 100644
from Products.Archetypes.atapi import *
from Products.ATContentTypes.content.folder import ATFolder
from Products.ATContentTypes.content.file import ATFile
from Products.ATContentTypes.content.base import ATCTContent
from Products.ATContentTypes.content.schemata import ATContentTypeSchema, finalizeATCTSchema
from Products.CMFPlone.interfaces import INonStructuralFolder
from Products.CMFCore import permissions
from Products.CV.config import PROJECTNAME
from string import split, join
 
CVSchema = ATFolder.schema.copy() + Schema((
ComputedField(
name='title',
searchable=1,
expression='context._computeTitle()',
accessor='Title',
widget=ComputedWidget(mode='view')
),
ComputedField(
name='description',
searchable=1,
expression='context._computeDescription()',
accessor='Description',
widget=ComputedWidget(mode='view')
),
StringField(
name='fullName',
widget=StringWidget(label='Full name'),
required=True,
),
LinesField(
name='qualifications',
widget=LinesWidget(label='Qualifications'),
required=True,
),
LinesField(
name='address',
widget=LinesWidget(label='Address'),
required=True,
),
StringField(
name='homePhone',
widget=StringWidget(label='Home phone number'),
required=True,
),
StringField(
name='mobilePhone',
widget=StringWidget(label='Mobile phone number'),
),
StringField(
name='workPhone',
widget=StringWidget(label='Work phone number'),
),
StringField(
name='email',
widget=StringWidget(label='Email address'),
required=True,
),
DateTimeField(
name='birthDate',
widget=CalendarWidget(label='Date of birth',
show_hm=False,
starting_year=1970,
format='%Y-%m-%d'),
required=True,
),
StringField(
name='maritalStatus',
widget=SelectionWidget(label='Marital status'),
vocabulary=['Single', 'Married', 'Divorced'],
required=True,
),
StringField(
name='drivingLicence',
widget=StringWidget(label='Driving licence'),
required=True,
),
TextField(
name='interests',
widget=RichWidget(label='Interests'),
required=True,
)
),)
 
EducationSchema = ATContentTypeSchema.copy() + Schema((
ComputedField(
name='title',
searchable=1,
expression='context._computeTitle()',
accessor='Title'),
DateTimeField(
name='fromDate',
widget=CalendarWidget(label='From date',
show_hm=False,
starting_year=1980,
format='%Y-%m-%d'),
required=True,
),
DateTimeField(
name='toDate',
widget=CalendarWidget(label='To date',
show_hm=False,
starting_year=1980,
format='%Y-%m-%d'),
),
StringField(
name='qualification',
widget=StringWidget(label='Qualifiction'),
),
StringField(
name='institute',
widget=StringWidget(label='Institute'),
required=True,
),
TextField(
name='description',
widget=RichWidget(label='Description'),
required=True,
default_content_type='text/html',
allowable_content_types=('text/html',),
default_output_type='text/html',
)
),)
 
EmploymentSchema = ATContentTypeSchema.copy() + Schema((
ComputedField(
name='title',
searchable=1,
expression='context._computeTitle()',
accessor='Title'),
DateTimeField(
name='fromDate',
widget=CalendarWidget(label='From date',
show_hm=False,
starting_year=1980,
format='%Y-%m-%d'),
required=True,
),
DateTimeField(
name='toDate',
widget=CalendarWidget(label='To date',
show_hm=False,
starting_year=1980,
format='%Y-%m-%d'),
),
StringField(
name='position',
widget=StringWidget(label='Position'),
),
StringField(
name='company',
widget=StringWidget(label='Company'),
required=True,
),
StringField(
name='location',
widget=StringWidget(label='Location'),
required=True,
),
TextField(
name='description',
widget=RichWidget(label='Description'),
required=True,
default_content_type='text/html',
allowable_content_types=('text/html',),
default_output_type='text/html',
)
),)
 
PublicationSchema = ATFile.schema.copy() + Schema((
StringField(
name='publicationType',
widget=SelectionWidget(label='Publication type'),
vocabulary=['article', 'book', 'booklet', 'conference',
'inBook', 'inCollection', 'inProceedings',
'manual', 'mastersThesis', 'misc', 'phdThesis',
'proceedings', 'techreport', 'unpublished'],
required=True,
),
StringField(
name='address',
widget=StringWidget(label='Address')
),
LinesField(
name='authors',
widget=LinesWidget(label='Authors'),
required=True,
),
StringField(
name='booktitle',
widget=StringWidget(label='Book title'),
),
StringField(
name='chapter',
widget=StringWidget(label='Chapter'),
),
StringField(
name='edition',
widget=StringWidget(label='Edition'),
),
StringField(
name='editor',
widget=StringWidget(label='Editor'),
),
StringField(
name='howpublished',
widget=StringWidget(label='How published'),
),
StringField(
name='institution',
widget=StringWidget(label='Institution'),
),
StringField(
name='journal',
widget=StringWidget(label='Journal'),
),
StringField(
name='key',
widget=StringWidget(label='Key'),
),
StringField(
name='month',
widget=StringWidget(label='Month'),
),
StringField(
name='note',
widget=StringWidget(label='Note'),
),
StringField(
name='number',
widget=StringWidget(label='Number'),
),
StringField(
name='organization',
widget=StringWidget(label='Organization'),
),
StringField(
name='pages',
widget=StringWidget(label='Pages'),
),
StringField(
name='publisher',
widget=StringWidget(label='Publisher'),
),
StringField(
name='school',
widget=StringWidget(label='School'),
),
StringField(
name='series',
widget=StringWidget(label='Series'),
),
StringField(
name='techReportType',
widget=StringWidget(label='Technical report type'),
),
StringField(
name='volume',
widget=StringWidget(label='Volume'),
),
StringField(
name='year',
widget=StringWidget(label='Year'),
required=True,
),
StringField(
name='onlineUrl',
widget=StringWidget(label='URL'),
),
StringField(
name='onlineSize',
widget=StringWidget(label='Size'),
)
),)
 
SkillSchema = ATContentTypeSchema.copy() + Schema((
ComputedField(
name='title',
searchable=1,
expression='context._computeTitle()',
accessor='Title'),
StringField(
name='category',
widget=StringWidget(label='Category'),
required=True,
),
TextField(
name='description',
widget=RichWidget(label='Description'),
default_content_type='text/html',
allowable_content_types=('text/html',),
default_output_type='text/html',
)
),)
 
finalizeATCTSchema(CVSchema)
finalizeATCTSchema(EducationSchema)
finalizeATCTSchema(EmploymentSchema)
finalizeATCTSchema(PublicationSchema)
finalizeATCTSchema(SkillSchema)
 
class CV(ATFolder):
"""
A CV lists details of someone's education and employment histories
as well as key skills and any publications, along with generic
properties like address, name, etc.
"""
 
portal_type = meta_type = 'CV'
archetype_name = 'Curriculum Vitae'
content_icon = 'cv.png'
schema = CVSchema
typeDescription = 'A Curriculum Vitae for someone.'
allowed_content_types = ['Education', 'Employment', 'Publication', 'Skill']
default_view = 'cv_view'
immediate_view = 'cv_view'
# __implements__ = ATFolder.__implements__ + [INonStructuralFolder]
actions = ATFolder.actions
 
def canSetDefaultPage(self):
return False
 
def _computeTitle(self):
return 'Curriculum Vitae for ' + self.getFullName();
 
def _computeDescription(self):
return "%s\n%s\n%s\n%s" % (self.getFullName(),
self.getAddress(),
self.getEmail(),
self.getBirthDate())
 
registerType(CV)
 
class Education(ATCTContent):
portal_type = meta_type = 'Education'
archetype_name = 'Education'
content_icon = 'education.png'
schema = EducationSchema
typeDescription = 'Details of education from some institute.'
# __implements__ = ATCTContent.__implements__
actions = ATCTContent.actions
global_allow = False
 
def _computeTitle(self):
return 'Education at ' + self.getInstitute();
 
registerType(Education)
 
class Employment(ATCTContent):
portal_type = meta_type = 'Employment'
archetype_name = 'Employment'
content_icon = 'employment.png'
schema = EmploymentSchema
typeDescription = 'Details of employment with some employer.'
# __implements__ = ATCTContent.__implements__
actions = ATCTContent.actions
global_allow = False
 
def _computeTitle(self):
return 'Employment at ' + self.getCompany();
 
registerType(Employment)
 
class Publication(ATFile):
portal_type = meta_type = 'Publication'
archetype_name = 'Publication'
content_icon = 'publication.png'
schema = PublicationSchema
typeDescription = 'A publication with citation details.'
# __implements__ = ATFile.__implements__
actions = ATFile.actions
global_allow = False
 
def listAuthors(self):
name_list = []
for full_name in self.authors:
names = split(full_name, ' ')
surname = names[-1]
backward_name = surname
for name in names[:-1]:
backward_name = backward_name + ' ' + name[0] + '.'
name_list.append(backward_name)
if len(name_list) > 1:
return join(name_list[0:-1], ',') + ' and ' + name_list[-1]
else:
return name_list[0]
registerType(Publication)
 
class Skill(ATCTContent):
portal_type = meta_type = 'Skill'
archetype_name = 'Skill'
content_icon = 'skill.png'
schema = SkillSchema
typeDescription = 'Details of a skill.'
# __implements__ = ATCTContent.__implements__
actions = ATCTContent.actions
global_allow = False
 
def _computeTitle(self):
return 'Skill in ' + self.getCategory();
 
registerType(Skill)
View
skins/cv_images/cv.png 0 → 100644
View
skins/cv_images/education.png 0 → 100644
View
skins/cv_images/employment.png 0 → 100644
View
skins/cv_images/publication.png 0 → 100644
View
skins/cv_images/skill.png 0 → 100644
View
90
skins/cv_styles/cv_main.css 0 → 100644
.cv {
color: black;
background: beige;
margin-left: 5%;
margin-right: 5%;
margin-top: 0px;
margin-bottom: 0px;
font-family: "Georgia", serif;
}
 
.cv h1, .cv h2, .cv h3 {
font-family: "Georgia", serif;
}
 
.cv dl {
text-align: justify;
}
 
.cv dt {
font-weight: bold;
}
 
.cv p {
text-align: justify;
margin: 0px 0px 0px 0px;
padding-bottom: 1ex;
line-height: 120%;
}
 
.cv div.modified {
font-size: small;
}
 
.cv div.headMatter {
text-align: center;
vertical-align: middle;
}
 
.cv h2 {
font-size: 18pt;
font-weight: bold;
padding: 1ex;
}
 
.cv div.name {
font-size: 14pt;
font-weight: bold;
padding: 0.5ex;
}
 
.cv div.addressBlock {
font-style: italic;
}
 
.cv div.personal {
text-align: center;
}
 
.cv th {
font-weight: bold;
text-align: right;
vertical-align: top;
}
 
.cv td {
text-align: justify;
vertical-align: top;
}
 
.cv div.jobHead {
font-weight: bold;
}
 
.cv th.date {
width: 16%;
text-align: center;
}
 
.cv h3 {
font-size: 14pt;
font-weight: bold;
text-align: center;
padding-top: 2ex;
padding-bottom: 1ex;
}
 
.cv div.reference {
text-indent: -2em;
margin-left: 2em;
}
View
184
skins/cv_templates/cv_view.pt 0 → 100644
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en"
metal:use-macro="here/main_template/macros/master"
i18n:domain="plone">
 
<head>
<metal:block fill-slot="style_slot">
<style type="text/css" media="all">
<!-- @import url(cv_main.css); -->
</style>
</metal:block>
</head>
 
<body>
<metal:main fill-slot="main">
<div class="cv">
<div class="headMatter">
<h2>Curriculum Vit&#230;</h2>
<div class="name">
<span tal:replace="here/fullName"/>
<span tal:replace="python: modules['string'].join(here.qualifications, ' ')"/>
</div>
<div class="addressBlock">
<div class="street_address">
<span tal:replace="python: modules['string'].join(here.address, ', ')"/>
</div>
<div class="phone_numbers">
<tal:block tal:repeat="phone python: ['Home', 'Work', 'Mobile']">
<span tal:condition="python: here[phone.lower() + 'Phone']"
tal:replace="python: phone + ': ' + here[phone.lower() + 'Phone'] + '.'"/>
</tal:block>
</div>
<div class="email" tal:condition="here/email"
tal:content="python:'Email: ' + here.getEmail()"/>
</div>
</div>
<table align="center" summary="Personal details">
<tr>
<th>Date of birth:</th>
<td>
<span tal:replace="here/birthDate/day"/><sup tal:condition="python: here.getBirthDate().day() in [1,21,31]">st</sup><sup tal:condition="python: here.getBirthDate().day() in [2,22]">nd</sup><sup tal:condition="python: here.getBirthDate().day() in [3,23]">rd</sup><sup tal:condition="python: here.getBirthDate().day() not in [1,2,3,21,22,23,31]">th</sup>
<span tal:replace="here/birthDate/Month"/>, <span tal:replace="here/birthDate/year"/>
</td>
</tr>
<tr>
<th>Marital status:</th>
<td tal:content="here/maritalStatus"/>
</tr>
<tr tal:condition="here/drivingLicence">
<th>Driving licence:</th>
<td tal:content="here/drivingLicence"/>
</tr>
</table>
<tal:block tal:define="emps python:sequence.sort(here.objectValues('Employment'), [('fromDate', 'cmp', 'desc')]);
edus python:sequence.sort(here.objectValues('Education'), (('fromDate', 'cmp', 'desc'),))">
<tal:block tal:condition="python: len(emps) > 0 and len(edus) > 0">
<tal:block tal:condition="python: emps[0].fromDate > edus[0].fromDate"
metal:define-macro="employment">
<h3>Employment</h3>
<div class="employment">
<table summary="Employment details to date">
<tr tal:repeat="emp emps">
<th tal:condition="python: emp.getToDate() != None and emp.getFromDate().year() == emp.getToDate().year()">
<span tal:replace="emp/fromDate/Month"/> &ndash; <span tal:replace="emp/toDate/Month"/>&nbsp;<span tal:replace="emp/toDate/year"/>
</th>
<th tal:condition="python: emp.getToDate() == None or emp.getFromDate().year() != emp.getToDate().year()">
<span tal:replace="emp/fromDate/aMonth"/>&nbsp;<span tal:replace="emp/fromDate/year"/> &ndash;
<tal:block tal:condition="emp/toDate">
<span tal:replace="emp/toDate/aMonth"/>&nbsp;<span tal:replace="emp/toDate/year"/>
</tal:block>
<tal:block tal:condition="not: emp/toDate">Current</tal:block>
</th>
<td>
<div class="jobHead" tal:content="string: ${emp/position} for ${emp/company}, ${emp/location}"/>
<div tal:replace="structure emp/description"/>
</td>
</tr>
</table>
</div>
</tal:block>
<tal:block tal:condition="python: emps[0].toDate &lt;= edus[0].toDate"
metal:define-macro="education">
<h3>Education</h3>
<div class="education">
<table summary="Education details to date">
<tr tal:repeat="edu edus">
<th tal:condition="python: edu.getToDate() != None and edu.getFromDate().year() == edu.getToDate().year()">
<span tal:replace="edu/fromDate/Month"/> &ndash; <span tal:replace="edu/toDate/Month"/>&nbsp;<span tal:replace="edu/toDate/year"/>
</th>
<th tal:condition="python: edu.getToDate() == None or edu.getFromDate().year() != edu.getToDate().year()">
<span tal:replace="edu/fromDate/aMonth"/>&nbsp;<span tal:replace="edu/fromDate/year"/> &ndash;
<tal:block tal:condition="edu/toDate">
<span tal:replace="edu/toDate/aMonth"/>&nbsp;<span tal:replace="edu/toDate/year"/>
</tal:block>
<tal:block tal:condition="not: edu/toDate">Current</tal:block>
</th>
<td>
<div class="jobHead"
tal:condition="edu/qualification"
tal:content="string: ${edu/qualification}, ${edu/institute}."/>
<div class="jobHead"
tal:condition="not: edu/qualification"
tal:content="string: ${edu/institute}."/>
<div tal:replace="structure edu/description"/>
</td>
</tr>
</table>
</div>
</tal:block>
</tal:block>
<tal:block tal:condition="python: (len(edus) == 0) and (len(emps) > 0)">
<metal:employment metal:use-macro="template/macros/employment" />
</tal:block>
<tal:block tal:condition="python: (len(emps) == 0) and (len(edus) > 0)">
<metal:education metal:use-macro="template/macros/education" />
</tal:block>
</tal:block>
 
<tal:block tal:condition="python: len(here.objectIds('Skill')) > 0">
<h3>Skills</h3>
<div class="skills">
<dl>
<tal:block tal:repeat="skill python: here.objectValues('Skill')">
<dt tal:content="skill/category"/>
<dd tal:content="structure skill/description"/>
</tal:block>
</dl>
</div>
</tal:block>
 
<tal:block tal:condition="here/interests">
<h3>Interests</h3>
<p tal:content="structure here/interests"/>
</tal:block>
 
<tal:block tal:condition="python: len(here.objectIds('Publication')) > 0">
<h3>Publications</h3>
<div class="publications">
<div class="reference"
tal:repeat="ref python:sequence.sort(here.objectValues('Publication'), [('year', 'cmp', 'desc')])">
<span tal:replace="ref/listAuthors" /> (<span tal:replace="ref/year"/>)
<tal:block tal:condition="python: ref.publicationType == 'phdThesis'">
<span style="font-style: italic;" tal:content="ref/title" /> PhD Thesis.
<span tal:replace="string: ${ref/school} ${ref/institution}" />
</tal:block>
<tal:block tal:condition="python: ref.publicationType == 'article'">
<span tal:replace="ref/title" />
<span style="font-style: italic;"
tal:content="ref/journal" />,
<span tal:replace="ref/volume" />.
</tal:block>
<tal:block tal:condition="python: ref.publicationType == 'inProceedings'">
<span style="font-style: italic;"
tal:content="ref/title" />,
<span tal:replace="ref/booktitle" />
</tal:block>
<a tal:attributes="href ref/absolute_url">
<img tal:attributes="src string: /${ref/getIcon}" />
</a>
</div>
</div>
</tal:block>
<!--
<dtml-if expr="_.len(objectValues('Skill')) > 0">
<div class="sectionHeader">Skills</div>
<dtml-var expr="list_skills(this())">
</dtml-if>
 
<dtml-if expr="_.has_key('interests') and interests != ''">
<div class="sectionHeader">Interests</div>
<dtml-var expr="interests">
</dtml-if>
 
<dtml-if expr="_.len(objectValues('Reference')) > 0">
<div class="sectionHeader">Publications</div>
<dtml-var expr="list_publications(this())">
</dtml-if>
 
<dtml-var standard_html_footer>
-->
</div>
</metal:main>
</body>
</html>
View
2
■■■
version 0 → 100644
0.8