xmlresume2x 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README ADDED
@@ -0,0 +1,57 @@
1
+ xmlresume2x: converts an xml resume to various output formats
2
+
3
+ Copyright (C) 2004 Thomas Leitner
4
+
5
+ = Description
6
+
7
+ The "XML Resume Library" at http://xmlresume.sourceforge.net provides a way for storing one's resume
8
+ data in a consistent way. The XML r�sum� can be transformed to various output formats, including
9
+ text, HTML and PDF by utilizing XSLT.
10
+
11
+ I came across the European Curriculum Vitae format at http://www.cedefop.eu.int/transparency/cv.asp
12
+ which provides a standard for CVs in Europe. Nicola Vitacolonna has used this information to provide
13
+ a LaTeX class - europecv (http://www.ctan.org/tex-archive/help/Catalogue/entries/europecv.html) -
14
+ which (nearly) conforms to this standard. As the XML R�sum� Library does not have a converter for
15
+ this, I wrote this program to convert an XML r�sum� to a LaTeX file which uses the europecv class.
16
+
17
+ During the development process I realized that it would be useful to add support for other output
18
+ formats. Therefore I changed the conversion process to use configuration files so that this program
19
+ can be used to transform a r�sum� to any output format.
20
+
21
+ = Dependencies
22
+
23
+ No Ruby dependencies but if you want to convert the resulting LaTeX file to a PDF file, you need to
24
+ have LaTeX and the europecv class installed.
25
+
26
+ = Installation
27
+
28
+ $ ruby setup.rb config
29
+ $ ruby setup.rb setup
30
+ $ ruby setup.rb install
31
+
32
+ Or you could use Rake and substitute the above commands with this:
33
+ $ rake install
34
+
35
+ Or you could use the RPA way:
36
+ $ rpa install xmlresume2x
37
+
38
+ Or you could use the RubyGem way:
39
+ $ gem install xmlresume2x
40
+
41
+ = Documentation
42
+
43
+ The documentation is located in the "doc/output" directory and has to be generated with webgen
44
+ (http://webgen.rubyforge.org) by issuing the command <code>rake doc</code>. If you do not want to do
45
+ this, you can still view the whole documentation for the latest version at
46
+ http://xmlresume2x.rubyforge.org.
47
+
48
+ = Example
49
+
50
+ Example files are provided in the "testfiles" directory.
51
+
52
+ = Contact
53
+
54
+ Author: Thomas Leitner
55
+ * Web: xmlresume2x.rubyforge.org
56
+ * e-Mail: t_leitner@gmx.at
57
+ * GPG Key-Id: 0xD942E7F6
@@ -0,0 +1,275 @@
1
+ # -*- ruby -*-
2
+ #
3
+ # $Id$
4
+ #
5
+ # xmlresume2x: converts an xml resume to various output formats
6
+ # Copyright (C) 2004 Thomas Leitner
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify it under the terms of the GNU
9
+ # General Public License as published by the Free Software Foundation; either version 2 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
13
+ # even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along with this program; if not,
17
+ # write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ #
19
+
20
+
21
+ begin
22
+ require 'rubygems'
23
+ require 'rake/gempackagetask'
24
+ rescue Exception
25
+ nil
26
+ end
27
+
28
+ require 'rake/clean'
29
+ require 'rake/packagetask'
30
+ require 'rake/rdoctask'
31
+ require 'rake/testtask'
32
+
33
+ # General actions ##############################################################
34
+
35
+ $:.push "lib"
36
+ require 'xmlresume2x/converter'
37
+
38
+ PKG_NAME = "xmlresume2x"
39
+ PKG_VERSION = XMLResume2x::VERSION.join( '.' )
40
+ PKG_FULLNAME = PKG_NAME + "-" + PKG_VERSION
41
+ PKG_SUMMARY = "Converts an xml resume to various output formats"
42
+ PKG_DESCRIPTION = "xmlresume2x can convert CVs written in the XML Resume
43
+ Library format (http://xmlresume.sourceforge.net) to a number
44
+ of formats, including LaTeX markup which uses the europecv
45
+ (http://www.ctan.org/tex-archive/help/Catalogue/entries/europecv.html)
46
+ class which is based on the 'standard' European Curriculum Vitae
47
+ format at http://www.cedefop.eu.int/transparency/cv.asp."
48
+
49
+ SRC_RB = FileList['lib/**/*.rb', 'bin/**/*', 'data/**/*.cfg']
50
+
51
+ # The default task is run if rake is given no explicit arguments.
52
+
53
+ desc "Default Task"
54
+ task :default => :version
55
+
56
+
57
+ # End user tasks ################################################################
58
+
59
+ desc "Prepares for installation"
60
+ task :prepare do
61
+ ruby "setup.rb config"
62
+ ruby "setup.rb setup"
63
+ end
64
+
65
+
66
+ desc "Installs the package #{PKG_NAME}"
67
+ task :install => [:prepare] do
68
+ ruby "setup.rb install"
69
+ end
70
+
71
+
72
+ task :clean do
73
+ ruby "setup.rb clean"
74
+ end
75
+
76
+
77
+ CLOBBER << "doc/output" << FileList['texput.*'].to_a
78
+ desc "Builds the documentation (needs webgen and LaTeX with latex-europecv package installed)"
79
+ task :doc => [:rdoc, :gen_version] do
80
+ chdir "doc" do
81
+ sh "webgen -V 3"
82
+ end
83
+ ruby "-Ilib bin/xmlresume2x -d data/xmlresume2x -f xhtml -l en testfiles/example1.xml > doc/output/examples/example1.html"
84
+ ruby "-Ilib bin/xmlresume2x -d data/xmlresume2x -f xhtml -l en testfiles/example2.xml > doc/output/examples/example2.html"
85
+ ruby "-Ilib bin/xmlresume2x -d data/xmlresume2x -f latex-europecv -l en testfiles/example1.xml | latex"
86
+ sh "dvipdf texput.dvi doc/output/examples/example1.pdf"
87
+ ruby "-Ilib bin/xmlresume2x -d data/xmlresume2x -f latex-europecv -l en testfiles/example2.xml | latex"
88
+ sh "dvipdf texput.dvi doc/output/examples/example2.pdf"
89
+ sh "cp testfiles/example*.xml doc/output/examples"
90
+ end
91
+
92
+ rd = Rake::RDocTask.new do |rdoc|
93
+ rdoc.rdoc_dir = 'doc/output/rdoc'
94
+ rdoc.title = PKG_NAME
95
+ rdoc.options << '--line-numbers' << '--inline-source' << '-m README'
96
+ rdoc.rdoc_files.include( 'README' )
97
+ rdoc.rdoc_files.include( 'lib/**/*.rb' )
98
+ end
99
+
100
+ # Developer tasks ##############################################################
101
+
102
+ PKG_FILES = FileList.new( [
103
+ 'setup.rb',
104
+ 'TODO',
105
+ 'COPYING',
106
+ 'README',
107
+ 'Rakefile',
108
+ 'ChangeLog',
109
+ 'VERSION',
110
+ 'install.rb',
111
+ 'bin/**/*',
112
+ 'lib/**/*.rb',
113
+ 'doc/**/*',
114
+ 'test/**/*',
115
+ 'data/**/*'
116
+ ]) do |fl|
117
+ fl.exclude( /\bsvn\b/ )
118
+ fl.exclude( 'doc/output' )
119
+ end
120
+
121
+ task :package => [:gen_files] do
122
+ chdir 'pkg' do
123
+ sh "rpaadmin packport #{PKG_NAME}-#{PKG_VERSION}"
124
+ end
125
+ end
126
+
127
+ task :gen_changelog do
128
+ sh "svn log -r HEAD:1 -v > ChangeLog"
129
+ end
130
+
131
+ task :gen_version do
132
+ puts "Generating VERSION file"
133
+ File.open( 'VERSION', 'w+' ) do |file| file.write( PKG_VERSION + "\n" ) end
134
+ end
135
+
136
+ task :gen_installrb do
137
+ puts "Generating install.rb file"
138
+ File.open( 'install.rb', 'w+' ) do |file|
139
+ file.write "
140
+ require 'rpa/install'
141
+
142
+ class Install_#{PKG_NAME} < RPA::Install::FullInstaller
143
+ name '#{PKG_NAME}'
144
+ version '#{PKG_VERSION}-1'
145
+ classification Application
146
+ build do
147
+ installdocs %w[COPYING ChangeLog TODO]
148
+ installdocs 'docs'
149
+ installrdoc %w[README] + Dir['lib/**/*.rb']
150
+ installdata
151
+ end
152
+ description <<-EOF
153
+ #{PKG_SUMMARY}
154
+
155
+ #{PKG_DESCRIPTION}
156
+ EOF
157
+ end
158
+ "
159
+ end
160
+ end
161
+
162
+ task :gen_files => [:gen_changelog, :gen_version, :gen_installrb]
163
+ CLOBBER << "ChangeLog" << "VERSION" << "install.rb"
164
+
165
+ Rake::PackageTask.new( PKG_NAME, PKG_VERSION ) do |p|
166
+ p.need_tar = true
167
+ p.need_zip = true
168
+ p.package_files = PKG_FILES
169
+ end
170
+
171
+ if defined? Gem
172
+ spec = Gem::Specification.new do |s|
173
+
174
+ #### Basic information
175
+
176
+ s.name = PKG_NAME
177
+ s.version = PKG_VERSION
178
+ s.summary = PKG_SUMMARY
179
+ s.description = PKG_DESCRIPTION
180
+
181
+ #### Dependencies, requirements and files
182
+
183
+ s.files = PKG_FILES.to_a
184
+
185
+ s.require_path = 'lib'
186
+ s.autorequire = nil
187
+ s.executables = ['xmlresume2x']
188
+ s.default_executable = 'xmlresume2x'
189
+
190
+ #### Documentation
191
+
192
+ s.has_rdoc = true
193
+ s.extra_rdoc_files = rd.rdoc_files.reject do |fn| fn =~ /\.rb$/ end.to_a
194
+ s.rdoc_options = ['--line-numbers', '-m README']
195
+
196
+ #### Author and project details
197
+
198
+ s.author = "Thomas Leitner"
199
+ s.email = "t_leitner@gmx.at"
200
+ s.homepage = "xmlresume2x.rubyforge.org"
201
+ s.rubyforge_project = "xmlresume2x"
202
+ end
203
+
204
+ Rake::GemPackageTask.new( spec ) do |pkg|
205
+ pkg.need_zip = true
206
+ pkg.need_tar = true
207
+ end
208
+ end
209
+
210
+
211
+ desc "Creates a tag in the repository"
212
+ task :tag do
213
+ repositoryPath = File.dirname( $1 ) if `svn info` =~ /^URL: (.*)$/
214
+ fail "Tag already created in repository " if /#{PKG_FULLNAME}/ =~ `svn ls #{repositoryPath}/versions`
215
+ sh "svn cp -m 'Created version #{PKG_FULLNAME}' #{repositoryPath}/trunk #{repositoryPath}/versions/#{PKG_FULLNAME}"
216
+ end
217
+
218
+ desc "Upload documentation to homepage"
219
+ task :uploaddoc => [:doc] do
220
+ Dir.chdir('doc/output')
221
+ sh "scp -r * gettalong@rubyforge.org:/var/www/gforge-projects/#{PKG_NAME}/"
222
+ end
223
+
224
+
225
+ # Misc tasks ###################################################################
226
+
227
+
228
+ def count_lines( filename )
229
+ lines = 0
230
+ codelines = 0
231
+ open( filename ) do |f|
232
+ f.each do |line|
233
+ lines += 1
234
+ next if line =~ /^\s*$/
235
+ next if line =~ /^\s*#/
236
+ codelines += 1
237
+ end
238
+ end
239
+ [lines, codelines]
240
+ end
241
+
242
+
243
+ def show_line( msg, lines, loc )
244
+ printf "%6s %6s %s\n", lines.to_s, loc.to_s, msg
245
+ end
246
+
247
+ desc "Show statistics"
248
+ task :statistics do
249
+ total_lines = 0
250
+ total_code = 0
251
+ show_line( "File Name", "Lines", "LOC" )
252
+ SRC_RB.each do |fn|
253
+ lines, codelines = count_lines fn
254
+ show_line( fn, lines, codelines )
255
+ total_lines += lines
256
+ total_code += codelines
257
+ end
258
+ show_line( "Total", total_lines, total_code )
259
+ end
260
+
261
+
262
+ desc "Show unprocessed elements"
263
+ task :missing do
264
+ require 'set'
265
+ elements = File.read( 'misc/dtd/resume.dtd' ).scan( /^(<!ELEMENT\s)(\w+)\s(?!\(#PCDATA\))/ ).collect {|i| i[1]}.to_set
266
+ Dir['data/xmlresume2x/format/*.cfg'].each do |file|
267
+ processor = XMLResume2x::ResumeProcessor.new( 'en' )
268
+ processor.load_config( File.read( file ) )
269
+ processed = processor.assignments.keys
270
+ e = elements.dup
271
+ e.subtract( processed )
272
+ puts "Missing elements for format file #{file}:"
273
+ puts e.to_a.sort.join( ", " )
274
+ end
275
+ end
data/TODO ADDED
@@ -0,0 +1,37 @@
1
+ * add some css files to distribution
2
+ * add TOC-menu to xhtml format
3
+ * generalize formating of publications, look at pub.xsl
4
+ * add support for multiple pubs, memberships, ...???
5
+ * most words in lang files should be lowercase, config files should capitalize words correctly
6
+ * add Spanish and Esperanto language support
7
+ * add output filter for text (use Text::Format)
8
+
9
+ ---- FINISHED ----
10
+
11
+ * add OptionParser for reading in template and xml file
12
+ * add 'calculated' stub
13
+ * add basic ERB functionality so that the template can be translated
14
+ * combine Element2Latex and Text2Latex as they process text parts (%inlines; or #PCDATA)
15
+ * combine Text2Latex and CalculatedEntries and use uniform interface
16
+ * refactor Text2Latex (ev. use subclasses, ie. base class Element, then AddressElement, LocationElement, ...)
17
+ * think about moving the formatting of the calculated parts to a YAML file
18
+ * add calculated items for period, periodOrDate
19
+ * add more element processors to configuration file (move most of the processing to the configuration file)
20
+ * add more blocks to the template file
21
+ * BUG: whitespace should be squeezed sometimes and sometimes not (ie. always in para, not in address...)
22
+ * externalize strings for i18n
23
+ * change name of program to xmlresume2x
24
+ * make a list of all processed and not processed elements (put it into Rakefile)
25
+ * add processors for the rest of the xml resume dtd
26
+ * add "free form" output for addresses (i.e. convert \n to \newline{} but only here, not everywhere!)
27
+ * BUG: list for membership, referees, awards,...
28
+ * BUG: two history sections result in two "Employment History" captions, same with academics, pubs,...
29
+ * BUG: latex output has to be in latin1 format, how to convert?
30
+ * finish LaTeX output
31
+ * incorporate changed install.rb for RPA from Mauricio
32
+ * add French
33
+ * make RubyGems version by using information/code supplied at ruby-talk by Mauricio
34
+ * provide Array#separate_by( normal, last ) for use with major/minor list
35
+ * provide sample output on homepage for test r�sum�s
36
+ * BUG: iconv not available on windows -> use other tool (eventually #pack and #unpack)
37
+ * add output filter for HTML
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # $Id: xmlresume2europecv.rb 6 2004-10-14 10:36:22Z thomas $
4
+ #
5
+ # xmlresume2europecv: converts an xml resume to a europecv LaTeX file
6
+ # Copyright (C) 2004 Thomas Leitner
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify it under the terms of the GNU
9
+ # General Public License as published by the Free Software Foundation; either version 2 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
13
+ # even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along with this program; if not,
17
+ # write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ #
19
+
20
+ require 'optparse'
21
+ require 'ostruct'
22
+ require 'xmlresume2x/converter'
23
+
24
+ converter = XMLResume2x::Converter.new
25
+
26
+ # Try to parse data dir independently to show correct help messages
27
+ begin
28
+ OptionParser.new do |opt|
29
+ opt.on( "-d", "--data-dir DATADIR" ) {|converter.data_dir|}
30
+ opt.on( "-h", "--help" ) {}
31
+ opt.on( "-v", "--version" ) {}
32
+ end.permute( ARGV )
33
+ rescue OptionParser::ParseError
34
+ end
35
+
36
+ options = OpenStruct.new
37
+
38
+ opt = OptionParser.new do |opt|
39
+ opt.banner = "Usage: xmlresume2x [options] RESUME.XML "
40
+ opt.separator "Options:"
41
+ opt.separator ""
42
+ opt.on( "-f", "--format FORMAT", converter.available_formats, "Use FORMAT as the output format (#{converter.available_formats.join(', ')})" ) {|options.format|}
43
+ opt.on( "-l", "--lang LANG", converter.available_langs, "Use LANG as the resume language (#{converter.available_langs.join(', ')})" ) {|options.language|}
44
+ opt.on( "-c", "--config CONFIG", "Use CONFIG as additional configuration file (can be used to override default behaviour)" ) {|options.userconfig|}
45
+ opt.on( "-d", "--data-dir DATADIR", "Use the directory DATADIR as data directory" ) {|converter.data_dir|}
46
+ opt.on( "-h", "--help" ) { puts opt; exit 1}
47
+ end
48
+
49
+ files = opt.permute( ARGV )
50
+ raise OptionParser::NeedlessArgument.new( *files[1..-1] ) if files.length > 1
51
+ if options.format.nil? || options.language.nil?
52
+ $stderr.puts "Error: You need to specify the FORMAT and the LANGUAGE!!!"
53
+ $stderr.puts opt;
54
+ exit 1
55
+ end
56
+ converter.load_config( options.format, options.language, options.userconfig )
57
+ puts converter.convert( files[0] )
@@ -0,0 +1,154 @@
1
+ # Common configuration for the resume converter -*- ruby -*-
2
+ # - Includes assignments for simple processors
3
+ # - Includes common processors for
4
+ # - name, location, present, date, from, to, period, birth
5
+ # - address
6
+ # - contact
7
+
8
+ #####################################################
9
+ # Assignments for simple processors
10
+
11
+ add_processor 'DefaultProcessor' do |element|
12
+ element.process_children!
13
+ end
14
+
15
+ processor ['description', 'note', 'misc', 'legalnotice', 'objective'] => 'SubParaProcessor'
16
+
17
+ processor ['achievement', 'artTitle', 'bookTitle', 'employer', 'institution',
18
+ 'organization', 'para', 'publisher', 'skill'] => 'DefaultProcessor'
19
+
20
+
21
+ #####################################################
22
+ # Name, location, ... processors
23
+
24
+ processor 'name' => 'NameProcessor' do |n|
25
+ [n.title, n.firstname, (n.middlenames.join(' ') if n.middlenames), n.surname, n.suffix].compact.join( ' ' )
26
+ end
27
+
28
+ processor 'location' => 'LocationProcessor' do |l|
29
+ [l.city, l.state, l.province, l.county, l.country].compact.join( ', ' )
30
+ end
31
+
32
+ processor 'present' => 'PresentProcessor' do |present|
33
+ keyword( :Present )
34
+ end
35
+
36
+ processor 'date' => 'DateProcessor' do |date|
37
+ day = (date.dayOfMonth.nil? ? '' : "#{date.dayOfMonth}. ")
38
+ month = (date.month.nil? ? '' : "#{date.month} ")
39
+ "#{day}#{month}#{date.year}"
40
+ end
41
+
42
+ processor ['from', 'to'] => 'FromToProcessor' do |element|
43
+ [element.date, element.present].compact.join( '' )
44
+ end
45
+
46
+ processor 'birth' => 'BirthProcessor' do |birth|
47
+ birth.date
48
+ end
49
+
50
+
51
+ #####################################################
52
+ # Address processor and help methods
53
+ #
54
+ # !You have to define the newline method before using this address processor!
55
+
56
+ def cityDivision( address )
57
+ address.suburb || address.ward
58
+ end
59
+
60
+ def adminDivision( address )
61
+ address.state || address.province || address.county
62
+ end
63
+
64
+ def postCode( address )
65
+ address.zip || address.postalCode
66
+ end
67
+
68
+ def formatStandardAddress( address )
69
+ arr = []
70
+ arr << address.street.join( newline ) << newline if address.street
71
+ arr << cityDivision( address ).to_s << newline if cityDivision( address )
72
+ arr << address.city.to_s
73
+ arr << ", #{adminDivision( address )}" if adminDivision( address )
74
+ arr << " #{postCode( address )}" if postCode( address )
75
+ arr << newline
76
+ arr << address.country.to_s
77
+ arr
78
+ end
79
+
80
+ def formatEuropeanAddress( address )
81
+ arr = []
82
+ arr << address.street.join( newline ) << newline if address.street
83
+ arr << cityDivision( address ).to_s << newline if cityDivision( address )
84
+ arr << "#{postCode( address )} " if postCode( address )
85
+ arr << address.city.to_s
86
+ arr << newline << adminDivision( address ).to_s if adminDivision( address )
87
+ arr << newline
88
+ arr << address.country.to_s
89
+ arr
90
+ end
91
+
92
+ def formatItalianAddress( address )
93
+ arr = []
94
+ arr << address.street.join( newline ) << newline if address.street
95
+ arr << "#{address.postalCode} " if address.postalCode
96
+ arr << address.city.to_s
97
+ arr << " (#{address.province})" if address.province
98
+ arr << newline
99
+ arr << address.country.to_s
100
+ arr
101
+ end
102
+
103
+ processor 'address' => 'AddressProcessor' do |address|
104
+ if address.__element.has_elements?
105
+ format = address._format.nil? ? 'standard' : address._format
106
+ send( "format#{format.capitalize}Address", address )
107
+ else
108
+ get_children_value( address, :verbatim => true )
109
+ end
110
+ end
111
+
112
+
113
+ #####################################################
114
+ # Contact processor and help methods
115
+
116
+ def phone_location( location )
117
+ case location
118
+ when 'home' then keyword( :Phone_Home )
119
+ when 'work' then keyword( :Phone_Work )
120
+ when 'mobile' then keyword( :Phone_Mobile )
121
+ else location.nil? ? keyword( :Phone ) : location
122
+ end
123
+ end
124
+
125
+ def fax_location( location )
126
+ case location
127
+ when 'home' then keyword( :Fax_Home )
128
+ when 'work' then keyword( :Fax_Work )
129
+ else location.nil? ? keyword( :Fax ) : location
130
+ end
131
+ end
132
+
133
+ def im_service( service )
134
+ case service
135
+ when 'aim' then keyword( :IM_Aim )
136
+ when 'icq' then keyword( :IM_Icq )
137
+ when 'irc' then keyword( :IM_Irc )
138
+ when 'jabber' then keyword( :IM_Jabber )
139
+ when 'msn' then keyword( :IM_Msn )
140
+ when 'yahoo' then keyword( :IM_Yahoo )
141
+ else service.nil? ? '' : service
142
+ end
143
+ end
144
+
145
+ processor 'contact' => 'ContactProcessor' do |contact|
146
+ arr = []
147
+ arr << contact.phone.collect {|phone| ["#{phone_location(phone._location)}: ", phone] }.join( newline ) if contact.phone
148
+ arr << newline << contact.fax.collect {|fax| ["#{fax_location(fax._location)}: ", fax] }.join( newline ) if contact.fax
149
+ arr << newline << contact.pager.collect {|pager| ["#{keyword(:Pager)}: ", pager] }.join( newline ) if contact.pager
150
+ arr << newline << contact.email.collect {|email| ["#{keyword(:Email)}: ", email] }.join( newline ) if contact.email
151
+ arr << newline << contact.url.collect {|url| ["#{keyword(:Url)}: ", url] }.join( newline ) if contact.url
152
+ arr << newline << contact.instantMessage.collect {|im| ["#{im_service(im._service)}: ", im] }.join( newline ) if contact.instantMessage
153
+ arr
154
+ end