diff --git a/Extensions/Install.py b/Extensions/Install.py new file mode 100644 index 0000000..6fe4926 --- /dev/null +++ b/Extensions/Install.py @@ -0,0 +1,44 @@ +# 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() + diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..f2cac0e --- /dev/null +++ b/__init__.py @@ -0,0 +1,35 @@ +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) diff --git a/config.py b/config.py new file mode 100644 index 0000000..c797881 --- /dev/null +++ b/config.py @@ -0,0 +1,7 @@ +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',)) diff --git a/content/__init__.py b/content/__init__.py new file mode 100644 index 0000000..2e5fbd1 --- /dev/null +++ b/content/__init__.py @@ -0,0 +1 @@ +from cv import CV, Education, Employment, Publication, Skill diff --git a/content/cv.py b/content/cv.py new file mode 100644 index 0000000..b386f8b --- /dev/null +++ b/content/cv.py @@ -0,0 +1,404 @@ +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) diff --git a/skins/cv_images/cv.png b/skins/cv_images/cv.png new file mode 100644 index 0000000..b85a467 --- /dev/null +++ b/skins/cv_images/cv.png Binary files differ diff --git a/skins/cv_images/education.png b/skins/cv_images/education.png new file mode 100644 index 0000000..8852ff8 --- /dev/null +++ b/skins/cv_images/education.png Binary files differ diff --git a/skins/cv_images/employment.png b/skins/cv_images/employment.png new file mode 100644 index 0000000..b6eb792 --- /dev/null +++ b/skins/cv_images/employment.png Binary files differ diff --git a/skins/cv_images/publication.png b/skins/cv_images/publication.png new file mode 100644 index 0000000..8b7968a --- /dev/null +++ b/skins/cv_images/publication.png Binary files differ diff --git a/skins/cv_images/skill.png b/skins/cv_images/skill.png new file mode 100644 index 0000000..4bd0c07 --- /dev/null +++ b/skins/cv_images/skill.png Binary files differ diff --git a/skins/cv_styles/cv_main.css b/skins/cv_styles/cv_main.css new file mode 100644 index 0000000..d0d368a --- /dev/null +++ b/skins/cv_styles/cv_main.css @@ -0,0 +1,90 @@ +.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; +} \ No newline at end of file diff --git a/skins/cv_templates/cv_view.pt b/skins/cv_templates/cv_view.pt new file mode 100644 index 0000000..08701fa --- /dev/null +++ b/skins/cv_templates/cv_view.pt @@ -0,0 +1,184 @@ + + + + + + + + + + +
+
+

Curriculum Vitæ

+
+ + +
+
+
+ +
+
+ + + +
+ +
+ + + + + + + + + + + +
Date of birth: + stndrdth + , +
Marital status: +
Driving licence: +
+ + + +

Employment

+
+ + + + + + +
+   + +   – + +   + + Current + +
+
+
+
+
+ +

Education

+
+ + + + + + +
+   + +   – + +   + + Current + +
+
+
+
+
+
+
+ + + + + + +
+ + +

Skills

+
+
+ +
+
+ +
+
+
+ + +

Interests

+

+ + + +

Publications

+
+
+ () + + PhD Thesis. + + + + + , + . + + + , + + + + + +
+
+
+ +
+ + + \ No newline at end of file diff --git a/version b/version new file mode 100644 index 0000000..aec258d --- /dev/null +++ b/version @@ -0,0 +1 @@ +0.8