Initial import

This commit is contained in:
Arnaud Ysmal 2018-05-07 00:58:03 +02:00
commit a5c24c4e46
15 changed files with 1907 additions and 0 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "resume-schema"]
path = resume-schema
url = https://github.com/jsonresume/resume-schema/

7
README.md Normal file
View File

@ -0,0 +1,7 @@
jsonresume written in go
------------------------
kendall and kendallfr themes are based on https://github.com/LinuxBozo/jsonresume-theme-kendall.
`$ go build -o jsonresume ./cli`
`$ ./jsonresume -t kendall -r resume-schema/examples/valid/complete.json -o resume.html && cp themes/kendall/*.css .`

74
cli/resume.go Normal file
View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2018 Arnaud Ysmal. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package main
import (
"flag"
"fmt"
"log"
"os"
"jsonresume/model"
"jsonresume/themes"
_ "jsonresume/themes/kendall"
_ "jsonresume/themes/kendallfr"
)
func main() {
var r *model.Resume
var resume string
var theme string
var err error
var outf string
var f *os.File
flag.StringVar(&resume, "resume", "resume.json", "JSON of resume")
flag.StringVar(&resume, "r", "resume.json", "JSON of resume")
flag.StringVar(&theme, "theme", "", "Theme of resume")
flag.StringVar(&theme, "t", "", "Theme of resume")
flag.StringVar(&outf, "out", "-", "Output file")
flag.StringVar(&outf, "o", "-", "Output file")
flag.Parse()
if outf == "-" {
f = os.Stdout
} else {
if f, err = os.OpenFile(outf, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755); err != nil {
log.Fatal(err)
}
}
if r, err = model.Parse(resume); err != nil {
log.Fatal(err)
}
if t, exists := themes.Themes[theme]; exists {
if err = t.Render(r, f); err != nil {
log.Fatal(err)
}
} else {
fmt.Printf("No such theme\n")
}
}

199
model/json.go Normal file
View File

@ -0,0 +1,199 @@
/*
* Copyright (c) 2018 Arnaud Ysmal. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package model
import (
"encoding/json"
"io/ioutil"
"strings"
"time"
)
const ISO8601DateLayout = "2006-01-02"
type ResumeDate struct {
time.Time
}
func (ct *ResumeDate) UnmarshalJSON(b []byte) (err error) {
s := strings.Trim(string(b), "\"")
if s == "null" {
ct.Time = time.Time{}
return
}
ct.Time, err = time.Parse(ISO8601DateLayout, s)
return
}
// Resume represent Curriculum Vitae
type Resume struct {
Title string `json:"title"`
Basic Basic `json:"basics"`
Work []Work `json:"work"`
Volunteer []Volunteer `json:"volunteer"`
Education []Education `json:"education"`
Awards []Award `json:"awards"`
Publications []Publication `json:"publications"`
Skills []Skill `json:"skills"`
Languages []Language `json:"languages"`
Interests []Interest `json:"interests"`
References []Reference `json:"references"`
Projects []Project `json:"projects"`
}
// Basic is the basic information for a resume
type Basic struct {
Name string `json:"name"`
Label string `json:"label"`
Image string `json:"image"`
Email string `json:"email"`
Phone string `json:"phone"`
URL string `json:"url"`
Summary string `json:"summary"`
ResumeLocation Location `json:"location"`
Profiles []SocialProfile `json:"profiles"`
}
// Location is the location details of a resume owner.
type Location struct {
Address string `json:"address"`
PostalCode string `json:"postalCode"`
City string `json:"city"`
CountryCode string `json:"countryCode"`
Region string `json:"region"`
}
// SocialProfile is the profile of the resume owner.
type SocialProfile struct {
Network string `json:"network"`
UserName string `json:"username"`
URL string `json:"url"`
}
// Work is the work details of the resume owner.
type Work struct {
Name string `json:"name"`
WorkLocation string `json:"location"`
Description string `json:"description"`
Position string `json:"position"`
URL string `json:"url"`
StartDate ResumeDate `json:"startDate"`
EndDate ResumeDate `json:"endDate"`
Summary string `json:"summary"`
Highlights []string `json:"highlights"`
}
// Volunteer is the volunteer details of the resume owner.
type Volunteer struct {
Organization string `json:"organization"`
Position string `json:"position"`
URL string `json:"url"`
StartDate ResumeDate `json:"startDate"`
EndDate ResumeDate `json:"endDate"`
Summary string `json:"summary"`
Highlights []string `json:"highlights"`
}
// Education is the education details of the resume owner.
type Education struct {
Institution string `json:"institution"`
Area string `json:"area"`
StudyType string `json:"studyType"`
StartDate ResumeDate `json:"startDate"`
EndDate ResumeDate `json:"endDate"`
GPA string `json:"gpa"`
Courses []string `json:"courses"`
}
// Award is the award details of the resume owner.
type Award struct {
Title string `json:"title"`
Date ResumeDate `json:"date"`
Awarder string `json:"awarder"`
Summary string `json:"summary"`
}
// Publication is the publication details of the resume owner.
type Publication struct {
Name string `json:"name"`
Publisher string `json:"publisher"`
ReleaseDate ResumeDate `json:"releaseDate"`
URL string `json:"url"`
Summary string `json:"summary"`
}
// Skill is the skill details of the resume owner.
type Skill struct {
Name string `json:"name"`
Level string `json:"level"`
Keywords []string `json:"keywords"`
}
// Language is the language details of the resume owner.
type Language struct {
Language string `json:"language"`
Fluency string `json:"fluency"`
}
// Interest is the interest details of the resume owner.
type Interest struct {
Name string `json:"name"`
Keywords []string `json:"keywords"`
}
// Referee is the details about a person who is refereeing for the resume owner.
type Reference struct {
Name string `json:"name"`
Reference string `json:"reference"`
}
// Volunteer is the volunteer details of the resume owner.
type Project struct {
Name string `json:"name"`
Description string `json:"description"`
Highlights []string `json:"highlights"`
Keywords []string `json:"keywords"`
StartDate ResumeDate `json:"startDate"`
EndDate ResumeDate `json:"endDate"`
URL string `json:"url"`
Roles []string `json:"roles"`
Entity string `json:"entity"`
Type string `json:"type"`
}
func Parse(f string) (*Resume, error) {
var content []byte
var err error
var r Resume
if content, err = ioutil.ReadFile(f); err != nil {
return nil, err
}
if err = json.Unmarshal(content, &r); err != nil {
return nil, err
}
return &r, nil
}

1
resume-schema Submodule

@ -0,0 +1 @@
Subproject commit 8558e9e6e4badade1c871d5cfd253a740dbf3b34

1
themes/README.md Normal file
View File

@ -0,0 +1 @@
kendall and kendallfr are based on https://github.com/LinuxBozo/jsonresume-theme-kendall

108
themes/kendall/kendall.go Normal file
View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2018 Arnaud Ysmal. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package kendall
import (
"fmt"
"jsonresume/model"
"jsonresume/themes"
"strings"
"text/template"
)
var Theme = themes.Theme{
Name: "kendall",
Functions: template.FuncMap{
"iconClass": iconClass,
"formatDateWork": formatDateWork,
"formatDateEdu": formatDateEdu,
"formatDatePub": formatDatePub,
},
Template: "themes/kendall/resume.template",
}
func init() {
(&Theme).Register()
}
func iconClass(network string) string {
network = strings.ToLower(network)
switch network {
// special cases
case "google-plus":
case "googleplus":
return "fa fa-google-plus"
case "flickr":
case "flicker":
return "fa fa-flickr"
case "dribbble":
case "dribble":
return "fa fa-dribbble"
case "codepen":
return "fa fa-codepen"
case "soundcloud":
return "fa fa-soundcloud"
case "reddit":
return "fa fa-reddit"
case "tumblr":
case "tumbler":
return "fa fa-tumblr"
case "stack-overflow":
case "stackoverflow":
return "fa fa-stack-overflow"
case "blog":
case "rss":
return "fa fa-rss"
case "gitlab":
return "fa fa-gitlab"
case "keybase":
return "fa fa-key"
default:
return "fa fa-" + network
}
return "fa fa-" + network
}
func formatDateWork(date model.ResumeDate) string {
if date.IsZero() {
return "Present"
}
return fmt.Sprintf("%s %d", date.Month().String(), date.Year())
}
func formatDateEdu(date model.ResumeDate) string {
if date.IsZero() {
return "Present"
}
return fmt.Sprintf("%d", date.Year())
}
func formatDatePub(date model.ResumeDate) string {
if date.IsZero() {
return "Present"
}
return fmt.Sprintf("%d %s %d", date.Day(), date.Month().String(), date.Year())
}

91
themes/kendall/print.css Normal file
View File

@ -0,0 +1,91 @@
body {
font-size: .95em;
-webkit-print-color-adjust: exact;
}
a[href]:after {
content: none !important;
}
#photo{
display: none;
}
.box {
margin-bottom: -10px;
}
blockquote,
#education,
#awards,
.contact-item,
.publication,
.skills,
.interests {
page-break-inside: avoid;
}
.col-sm-5{
width: 40%;
padding: 0 15px;
}
.col-sm-7{
width: 60%;
padding: 0 15px;
}
.skills .col-sm-offset-1,
.interests .col-sm-offset-1{
margin-top: -10px;
margin-bottom: 5px;
}
#education {
margin: 0;
margin-bottom: -20px;
}
#awards:before,
#education:before {
background: none;
}
#awards .description,
#education .description,
.job .details {
border: 1px solid #eee;
}
.publication,
.publication .panel-heading,
.publication .name{
margin: 0;
padding: 0 5px;
border: none;
}
.publication .panel-body {
padding: 0 10px;
margin: 0;
}
.badge {
margin: 0;
}
.list-group-item{
border: none;
margin: 0;
padding: 5px 15px;
}
.list-group-item:after{
content: '';
position: absolute;
top: 8px;
right: 0;
left: -1px;
height: 0;
width: 0;
border: solid transparent;
border-right-color: #999;
border-width: 4px;
pointer-events: none;
}

View File

@ -0,0 +1,292 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Resume of {{.Basic.Name}}</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="style.css" media="screen">
<link rel="stylesheet" type="text/css" href="print.css" media="print">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-12">
<div id="photo-header" class="text-center">
<!-- PHOTO (AVATAR) -->
{{if .Basic.Image -}}
<div id="photo">
<img src="{{.Basic.Image}}" alt="avatar">
</div>
{{end -}}
<div id="text-header" {{if not .Basic.Image}}style="margin-top: 90px;"{{end -}}>
<h1>{{.Basic.Name}}<br>{{if .Basic.Label}}<span>{{.Basic.Label}}</span>{{end -}}</h1>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-7">
{{if .Basic.Summary -}}
<!-- ABOUT ME -->
<div class="box">
<h2><i class="fa fa-user ico"></i> About</h2>
<p>{{.Basic.Summary}}</p>
</div>
{{end -}}
{{if .Work -}}
<!-- WORK EXPERIENCE -->
<div class="box">
<h2><i class= "fa fa-suitcase ico"></i> Work Experience</h2>
{{range .Work -}}
<div class="job clearfix">
<div class="row">
<div class="details">
<div class="where">{{.Name}}</div>
{{if .URL -}}
<div class="address">
<a href="{{.URL}}" target= "_blank"><i class="fa fa-globe ico"></i> {{.URL}}</a>
</div>
{{end -}}
<div class="year">{{formatDateWork .StartDate}} {{formatDateWork .EndDate}}</div>
</div>
</div>
<div class="row">
<div class="job-details col-xs-11">
<div class="profession">{{.Position}}</div>
<div class="description">
{{.Summary -}}
{{if .Highlights -}}
<div class="highlights"></div>
<ul class="list-group">
{{range .Highlights -}}
<li class="list-group-item">{{.}}</li>
{{end -}}
</ul>
{{end -}}
</div>
</div>
</div>
</div>
{{end -}}
</div>
{{end -}}
{{if .Awards -}}
<!-- AWARDS -->
<div class="box">
<h2><i class="fa fa-certificate ico"></i> Awards</h2>
<ul id="awards" class="clearfix">
{{range .Awards -}}
<li>
<div class="year pull-left">{{formatDatePub .Date}}</div>
<div class="description pull-right">
<h3>{{.Awarder}}</h3>
<p><i class="fa fa-trophy ico"></i> {{.Title}}</p>
<p>{{.Summary}}</p>
</div>
</li>
{{end -}}
</ul>
</div>
{{end -}}
{{if .Volunteer -}}
<!-- VOLUNTEER -->
<div class="box">
<h2><i class= "fa fa-group ico"></i> Volunteer</h2>
{{range .Volunteer -}}
<div class="job clearfix">
<div class="row">
<div class="details">
<div class="where">{{.Organization}}</div>
{{if .URL -}}
<div class="address">
<a href="{{.URL}}" target= "_blank"><i class="fa fa-globe ico"></i> {{.URL}}</a>
</div>
{{end -}}
<div class="year">{{formatDateWork .StartDate}} {{formatDateWork .EndDate}}</div>
</div>
</div>
<div class="row">
<div class="job-details col-xs-11">
<div class="profession">{{.Position}}</div>
<div class="description">
{{.Summary -}}
{{if .Highlights -}}
<div class="highlights"></div>
<ul class="list-group">
{{range .Highlights -}}
<li class="list-group-item">{{.}}</li>
{{end -}}
</ul>
{{end -}}
</div>
</div>
</div>
</div>
{{end -}}
</div>
{{end -}}
</div>
<div class="col-xs-12 col-sm-5">
<!-- CONTACT -->
<div class="box clearfix">
<h2><i class="fa fa-bullseye ico"></i> Contact</h2>
{{if .Basic.ResumeLocation.City -}}
<div class="contact-item">
<div class="icon pull-left text-center"><span class="fa fa-map-marker fa-fw"></span></div>
{{if .Basic.ResumeLocation.Address}}<div class="title pull-right">{{.Basic.ResumeLocation.Address}}</div>{{end -}}
<div class="title {{if not .Basic.ResumeLocation.Address}}only {{end -}} pull-right">{{.Basic.ResumeLocation.City}}{{if .Basic.ResumeLocation.Region}}, {{.Basic.ResumeLocation.Region}}{{end -}}{{if .Basic.ResumeLocation.PostalCode}} {{.Basic.ResumeLocation.PostalCode}}{{end -}}{{if .Basic.ResumeLocation.CountryCode}} {{.Basic.ResumeLocation.CountryCode}}{{end -}}</div>
</div>
{{end -}}
{{if .Basic.Phone -}}
<div class="contact-item">
<div class="icon pull-left text-center"><span class="fa fa-phone fa-fw"></span></div>
<div class="title only pull-right">{{.Basic.Phone}}</div>
</div>
{{end -}}
{{if .Basic.Email -}}
<div class="contact-item">
<div class="icon pull-left text-center"><span class="fa fa-envelope fa-fw"></span></div>
<div class="title only pull-right"><a href="mailto:{{.Basic.Email}}" target="_blank">{{.Basic.Email}}</a></div>
</div>
{{end -}}
{{if .Basic.URL -}}
<div class="contact-item">
<div class="icon pull-left text-center"><span class="fa fa-globe fa-fw"></span></div>
<div class="title only pull-right"><a href="{{.Basic.URL}}" target="_blank">{{.Basic.URL}}</a></div>
</div>
{{end -}}
{{range .Basic.Profiles -}}
<div class="contact-item">
<div class="icon pull-left text-center"><span class="{{iconClass .Network}} fa-fw"></span></div>
<div class="title pull-right">{{.Network}}</div>
<div class="description pull-right"><a href="{{.URL}}" target="_blank">{{if .UserName}}{{.UserName}}{{end -}}{{if not .UserName}}{{.URL}}{{end -}}</a></div>
</div>
{{end -}}
</div>
{{if .Education -}}
<!-- EDUCATION -->
<div class="box">
<h2><i class="fa fa-university ico"></i> Education</h2>
<ul id="education" class="clearfix">
{{range .Education -}}
<li>
<div class="year pull-left">{{formatDateEdu .StartDate}} {{formatDateEdu .EndDate}}</div>
<div class="description pull-right">
<h3>{{.Institution}}</h3>
{{if .StudyType}}<p><i class= "fa fa-graduation-cap ico"></i> {{.StudyType}}</p>{{end}}
<p>{{.Area}}</p>
{{if .GPA -}}
<p>
GPA: {{.GPA}}
</p>
{{end -}}
{{if .Courses -}}
<div>Courses</div>
<ul class="list-group">
{{range .Courses -}}
<li class="list-group-item">{{.}}</li>
{{end -}}
</ul>
{{end -}}
</div>
</li>
{{end -}}
</ul>
</div>
{{end -}}
{{if .Skills -}}
<!-- SKILLS -->
<div class="box">
<h2><i class="fa fa-tasks ico"></i> Skills</h2>
{{range .Skills -}}
<div class="skills clearfix">
<div class="item-skills">
{{.Name}}
{{if .Level}}<span class="skill-level">{{.Level}}</span>{{end -}}
</div>
<div class="col-sm-offset-1 col-sm-12 clearfix">
{{range .Keywords -}}
<span class= "skill badge">{{.}}</span>
{{end -}}
</div>
</div>
{{end -}}
</div>
{{end -}}
{{if .Publications -}}
<!-- PUBLICATIONS -->
<div class="box">
<h2><i class="fa fa-book ico"></i> Publications</h2>
{{range .Publications -}}
<div class="publication panel panel-default">
<div class="panel-heading">
<div class="name panel-title">{{.Name}}</div>
</div>
<div class="panel-body">
{{if .Publisher -}}
<div class="publisher"><i class= "fa fa-bookmark ico"></i> {{.Publisher}}</div>
{{end -}}
<div class="year">{{formatDatePub .ReleaseDate}}</div>
{{if .URL -}}
<div class="address">
<a href="{{.URL}}" target= "_blank"><i class="fa fa-globe ico"></i> {{.URL}}</a>
</div>
{{end -}}
{{if .Summary -}}
<p>{{.Summary}}</p>
{{end -}}
</div>
</div>
{{end -}}
</div>
{{end -}}
{{if .Languages -}}
<!-- LANGUAGES -->
<div class="box">
<h2><i class="fa fa-language ico"></i> Languages</h2>
<ul class="list-group">
{{range .Languages -}}
<li class=" list-group-item">{{.Language}}<span class="skill badge pull-right">{{.Fluency}}</span></li>
{{end -}}
</ul>
</div>
{{end -}}
{{if .Interests -}}
<!-- HOBBIES -->
<div class="box">
<h2><i class="fa fa-heart ico"></i> Interests</h2>
{{range .Interests -}}
<div class="interests clearfix">
<div class="item-interests">
{{.Name -}}
</div>
<div class="col-sm-offset-1 col-sm-12 clearfix">
{{range .Keywords -}}
<span class= "interest badge">{{.}}</span>
{{end -}}
</div>
</div>
{{end -}}
</div>
{{end -}}
{{if .References -}}
<div class="box">
<h2><i class= "fa fa-check-square ico"></i> References</h2>
{{range .References -}}
<blockquote>
<div>{{.Reference}}</div>
<footer>{{.Name}}</footer>
</blockquote><br>
{{end -}}
</div>
{{end -}}
</div>
</div>
</div>
</body>
</html>

276
themes/kendall/style.css Normal file
View File

@ -0,0 +1,276 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800);
@charset "utf-8";
@-webkit-viewport { width: device-width; }
@-moz-viewport { width: device-width; }
@-ms-viewport { width: device-width; }
@-o-viewport { width: device-width; }
@viewport { width: device-width; }
body{
font-family: 'Open Sans', Arial, Tahoma;
font-weight: 400;
color: #363636;
background: #334960;
}
blockquote {
font-size: 1em;
}
.container{
margin-top: 80px;
margin-bottom: 15px;
background: #fff;
}
#photo-header{
margin-top: -75px;
}
#photo{
width: 160px;
height: 160px;
border-radius: 50%;
overflow: hidden;
padding: 5px;
background: #334960;
display: inline-block;
}
#photo img{
width: 150px;
height: 150px;
border-radius: 50%;
}
#text-header h1{
margin: 0;
padding: 0;
font-size: 1.5em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: -1px;
}
#text-header h1::first-line{
font-size: 1.5em;
font-weight: 800;
line-height: 1.5em;
}
#text-header h1 span{
color: #334960;
opacity: 0.7;
}
#text-header h1 sup{
opacity: 0.5;
}
#text-header:after{
width: 100%;
height: 3px;
border-bottom: 1px solid #ddd;
margin-top: 15px;
content: '';
display: block;
}
.box{
padding-bottom: 10px;
margin-bottom: 25px;
}
.box h2{
color: #227c74;
font-size: 1.5em;
font-weight: 700;
text-transform: uppercase;
}
#awards,
#education{
margin-top: 20px;
margin-bottom: 0;
position: relative;
padding: 1em 0;
list-style: none;
}
#awards:before,
#education:before {
width: 5px;
height: 100%;
position: absolute;
left: 35px;
top: 0;
content: ' ';
display: block;
background: #32475c;
background: -moz-linear-gradient(top, #ffffff 0%, #32475c 7%, #32475c 89%, #ffffff 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(7%,#32475c), color-stop(89%,#32475c), color-stop(100%,#ffffff));
background: -webkit-linear-gradient(top, #ffffff 0%,#32475c 7%,#32475c 89%,#ffffff 100%);
background: -o-linear-gradient(top, #ffffff 0%,#32475c 7%,#32475c 89%,#ffffff 100%);
background: -ms-linear-gradient(top, #ffffff 0%,#32475c 7%,#32475c 89%,#ffffff 100%);
background: linear-gradient(to bottom, #ffffff 0%,#32475c 7%,#32475c 89%,#ffffff 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ffffff',GradientType=0 );
}
#awards li,
#education li{
width: 100%;
z-index: 2;
position: relative;
float: left;
}
#awards .year,
#education .year{
width: 14%;
background: #fff;
padding: 10px;
font-weight: 700;
display: inline-block;
}
#awards .description,
#education .description{
width: 83%;
display: inline-block;
background: #eee;
margin-bottom: 10px;
position: relative;
padding: 10px;
border-bottom: 1px solid #ccc;
border-right: 1px solid #ccc;
}
#awards .description:after,
#education .description:after {
content: '';
position: absolute;
top: 15px;
right: 0;
left: -16px;
height: 0;
width: 0;
border: solid transparent;
border-right-color: #eee;
border-width: 8px;
pointer-events: none;
}
#awards .description h3,
#education .description h3{
font-size: 1.2em;
margin: 0;
padding: 0;
font-weight: 700;
}
#awards .description p,
#education .description p{
margin-top: 5px;
padding: 0;
}
.job{
margin-bottom: 15px;
}
.job .details {
margin-left: 3%;
width: 95%;
padding: 10px;
margin-bottom: 10px;
background: #eee;
border-bottom: 1px solid #ccc;
border-right: 1px solid #ccc;
}
.job .where{
font-size: 1.2em;
font-weight: bold;
}
.job .year{
opacity: 0.7;
}
.job .profession{
font-size: 1.2em;
font-weight: bold;
}
.job .description{
line-height: 1.5em;
}
.job .highlights{
padding: 5px 0;
font-weight: bold;
}
.job .job-details {
padding-left: 5%;
width: 100%;
}
.publication {
margin-bottom: 0;
}
.publication .name{
font-size: 1em;
font-weight: bold;
}
.publication .year{
opacity: 0.7;
}
.publication p{
margin: 0;
padding-top: 10px;
}
.contact-item{
width: 100%;
float: left;
}
.contact-item .icon{
padding: 10px;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
color: #32475c;
background: #eee;
}
.contact-item:last-child .icon{
border-bottom: none;
}
.contact-item .title{
width: 80%;
width: calc(100% - 55px);
font-weight: 700;
opacity: 0.9;
}
.contact-item .title.only{
margin-top: 10px;
}
.contact-item .description{
width: 80%;
width: calc(100% - 55px);
color: #334960;
}
.item-interests,
.item-skills{
height: 30px;
color: #334960;
padding: 5px 10px;
margin-bottom: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 1.1em;
font-weight: 600;
}
.interest,
.skill{
color: #fff;
display: inline-block;
margin-right: 5px;
margin-bottom: 5px;
padding: 5px 10px;
background: #32475c;
position: relative;
font-size: .85em;
}
.skill-level {
background-color: #227c74;
border-radius: 4px;
color: #fff;
padding: 1px 8px;
font-size: .75em;
position: absolute;
margin: 1px 10px;
}
#language-skills .skill{
margin: 10px 0;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}

View File

@ -0,0 +1,137 @@
/*
* Copyright (c) 2018 Arnaud Ysmal. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package kendallfr
import (
"fmt"
"jsonresume/model"
"jsonresume/themes"
"strings"
"text/template"
)
var Theme = themes.Theme{
Name: "kendallfr",
Functions: template.FuncMap{
"iconClass": iconClass,
"formatDateWork": formatDateWork,
"formatDateEdu": formatDateEdu,
"formatDatePub": formatDatePub,
},
Template: "themes/kendallfr/resume.template",
}
func init() {
(&Theme).Register()
}
func iconClass(network string) string {
network = strings.ToLower(network)
switch network {
// special cases
case "google-plus":
case "googleplus":
return "fa fa-google-plus"
case "flickr":
case "flicker":
return "fa fa-flickr"
case "dribbble":
case "dribble":
return "fa fa-dribbble"
case "codepen":
return "fa fa-codepen"
case "soundcloud":
return "fa fa-soundcloud"
case "reddit":
return "fa fa-reddit"
case "tumblr":
case "tumbler":
return "fa fa-tumblr"
case "stack-overflow":
case "stackoverflow":
return "fa fa-stack-overflow"
case "blog":
case "rss":
return "fa fa-rss"
case "gitlab":
return "fa fa-gitlab"
case "keybase":
return "fa fa-key"
default:
return "fa fa-" + network
}
return "fa fa-" + network
}
func getMonth(date model.ResumeDate) string {
switch date.Month() {
case 1:
return "Janvier"
case 2:
return "Février"
case 3:
return "Mars"
case 4:
return "Avril"
case 5:
return "Mai"
case 6:
return "Juin"
case 7:
return "Juillet"
case 8:
return "Août"
case 9:
return "Septembre"
case 10:
return "Octobre"
case 11:
return "Novembre"
case 12:
return "Décembre"
}
return ""
}
func formatDateWork(date model.ResumeDate) string {
if date.IsZero() {
return "Présent"
}
return fmt.Sprintf("%s %d", getMonth(date), date.Year())
}
func formatDateEdu(date model.ResumeDate) string {
if date.IsZero() {
return "Présent"
}
return fmt.Sprintf("%d", date.Year())
}
func formatDatePub(date model.ResumeDate) string {
if date.IsZero() {
return "Présent"
}
return fmt.Sprintf("%d %s %d", date.Day(), getMonth(date), date.Year())
}

View File

@ -0,0 +1,91 @@
body {
font-size: .95em;
-webkit-print-color-adjust: exact;
}
a[href]:after {
content: none !important;
}
#photo{
display: none;
}
.box {
margin-bottom: -10px;
}
blockquote,
#education,
#awards,
.contact-item,
.publication,
.skills,
.interests {
page-break-inside: avoid;
}
.col-sm-5{
width: 40%;
padding: 0 15px;
}
.col-sm-7{
width: 60%;
padding: 0 15px;
}
.skills .col-sm-offset-1,
.interests .col-sm-offset-1{
margin-top: -10px;
margin-bottom: 5px;
}
#education {
margin: 0;
margin-bottom: -20px;
}
#awards:before,
#education:before {
background: none;
}
#awards .description,
#education .description,
.job .details {
border: 1px solid #eee;
}
.publication,
.publication .panel-heading,
.publication .name{
margin: 0;
padding: 0 5px;
border: none;
}
.publication .panel-body {
padding: 0 10px;
margin: 0;
}
.badge {
margin: 0;
}
.list-group-item{
border: none;
margin: 0;
padding: 5px 15px;
}
.list-group-item:after{
content: '';
position: absolute;
top: 8px;
right: 0;
left: -1px;
height: 0;
width: 0;
border: solid transparent;
border-right-color: #999;
border-width: 4px;
pointer-events: none;
}

View File

@ -0,0 +1,292 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CV de {{.Basic.Name}}</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="style.css" media="screen">
<link rel="stylesheet" type="text/css" href="print.css" media="print">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-12">
<div id="photo-header" class="text-center">
<!-- PHOTO (AVATAR) -->
{{if .Basic.Image -}}
<div id="photo">
<img src="{{.Basic.Image}}" alt="avatar">
</div>
{{end -}}
<div id="text-header" {{if not .Basic.Image}}style="margin-top: 90px;"{{end -}}>
<h1>{{.Basic.Name}}<br>{{if .Basic.Label}}<span>{{.Basic.Label}}</span>{{end -}}</h1>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-7">
{{if .Basic.Summary -}}
<!-- ABOUT ME -->
<div class="box">
<h2><i class="fa fa-user ico"></i> À propos</h2>
<p>{{.Basic.Summary}}</p>
</div>
{{end -}}
{{if .Work -}}
<!-- WORK EXPERIENCE -->
<div class="box">
<h2><i class= "fa fa-suitcase ico"></i> Expérience Professionnelle</h2>
{{range .Work -}}
<div class="job clearfix">
<div class="row">
<div class="details">
<div class="where">{{.Name}}</div>
{{if .URL -}}
<div class="address">
<a href="{{.URL}}" target= "_blank"><i class="fa fa-globe ico"></i> {{.URL}}</a>
</div>
{{end -}}
<div class="year">{{formatDateWork .StartDate}} {{formatDateWork .EndDate}}</div>
</div>
</div>
<div class="row">
<div class="job-details col-xs-11">
<div class="profession">{{.Position}}</div>
<div class="description">
{{.Summary -}}
{{if .Highlights -}}
<div class="highlights"></div>
<ul class="list-group">
{{range .Highlights -}}
<li class="list-group-item">{{.}}</li>
{{end -}}
</ul>
{{end -}}
</div>
</div>
</div>
</div>
{{end -}}
</div>
{{end -}}
{{if .Awards -}}
<!-- AWARDS -->
<div class="box">
<h2><i class="fa fa-certificate ico"></i> Awards</h2>
<ul id="awards" class="clearfix">
{{range .Awards -}}
<li>
<div class="year pull-left">{{formatDatePub .Date}}</div>
<div class="description pull-right">
<h3>{{.Awarder}}</h3>
<p><i class="fa fa-trophy ico"></i> {{.Title}}</p>
<p>{{.Summary}}</p>
</div>
</li>
{{end -}}
</ul>
</div>
{{end -}}
{{if .Volunteer -}}
<!-- VOLUNTEER -->
<div class="box">
<h2><i class= "fa fa-group ico"></i> Bénévolat</h2>
{{range .Volunteer -}}
<div class="job clearfix">
<div class="row">
<div class="details">
<div class="where">{{.Organization}}</div>
{{if .URL -}}
<div class="address">
<a href="{{.URL}}" target= "_blank"><i class="fa fa-globe ico"></i> {{.URL}}</a>
</div>
{{end -}}
<div class="year">{{formatDateWork .StartDate}} {{formatDateWork .EndDate}}</div>
</div>
</div>
<div class="row">
<div class="job-details col-xs-11">
<div class="profession">{{.Position}}</div>
<div class="description">
{{.Summary -}}
{{if .Highlights -}}
<div class="highlights"></div>
<ul class="list-group">
{{range .Highlights -}}
<li class="list-group-item">{{.}}</li>
{{end -}}
</ul>
{{end -}}
</div>
</div>
</div>
</div>
{{end -}}
</div>
{{end -}}
</div>
<div class="col-xs-12 col-sm-5">
<!-- CONTACT -->
<div class="box clearfix">
<h2><i class="fa fa-bullseye ico"></i> Contact</h2>
{{if .Basic.ResumeLocation.City -}}
<div class="contact-item">
<div class="icon pull-left text-center"><span class="fa fa-map-marker fa-fw"></span></div>
{{if .Basic.ResumeLocation.Address}}<div class="title pull-right">{{.Basic.ResumeLocation.Address}}</div>{{end -}}
<div class="title {{if not .Basic.ResumeLocation.Address}}only {{end -}} pull-right">{{.Basic.ResumeLocation.City}}{{if .Basic.ResumeLocation.Region}}, {{.Basic.ResumeLocation.Region}}{{end -}}{{if .Basic.ResumeLocation.PostalCode}} {{.Basic.ResumeLocation.PostalCode}}{{end -}}{{if .Basic.ResumeLocation.CountryCode}} {{.Basic.ResumeLocation.CountryCode}}{{end -}}</div>
</div>
{{end -}}
{{if .Basic.Phone -}}
<div class="contact-item">
<div class="icon pull-left text-center"><span class="fa fa-phone fa-fw"></span></div>
<div class="title only pull-right">{{.Basic.Phone}}</div>
</div>
{{end -}}
{{if .Basic.Email -}}
<div class="contact-item">
<div class="icon pull-left text-center"><span class="fa fa-envelope fa-fw"></span></div>
<div class="title only pull-right"><a href="mailto:{{.Basic.Email}}" target="_blank">{{.Basic.Email}}</a></div>
</div>
{{end -}}
{{if .Basic.URL -}}
<div class="contact-item">
<div class="icon pull-left text-center"><span class="fa fa-globe fa-fw"></span></div>
<div class="title only pull-right"><a href="{{.Basic.URL}}" target="_blank">{{.Basic.URL}}</a></div>
</div>
{{end -}}
{{range .Basic.Profiles -}}
<div class="contact-item">
<div class="icon pull-left text-center"><span class="{{iconClass .Network}} fa-fw"></span></div>
<div class="title pull-right">{{.Network}}</div>
<div class="description pull-right"><a href="{{.URL}}" target="_blank">{{if .UserName}}{{.UserName}}{{end -}}{{if not .UserName}}{{.URL}}{{end -}}</a></div>
</div>
{{end -}}
</div>
{{if .Education -}}
<!-- EDUCATION -->
<div class="box">
<h2><i class="fa fa-university ico"></i> Formation</h2>
<ul id="education" class="clearfix">
{{range .Education -}}
<li>
<div class="year pull-left">{{formatDateEdu .StartDate}} {{formatDateEdu .EndDate}}</div>
<div class="description pull-right">
<h3>{{.Institution}}</h3>
{{if .StudyType}}<p><i class= "fa fa-graduation-cap ico"></i> {{.StudyType}}</p>{{end}}
<p>{{.Area}}</p>
{{if .GPA -}}
<p>
GPA: {{.GPA}}
</p>
{{end -}}
{{if .Courses -}}
<div>Courses</div>
<ul class="list-group">
{{range .Courses -}}
<li class="list-group-item">{{.}}</li>
{{end -}}
</ul>
{{end -}}
</div>
</li>
{{end -}}
</ul>
</div>
{{end -}}
{{if .Skills -}}
<!-- SKILLS -->
<div class="box">
<h2><i class="fa fa-tasks ico"></i> Compétences</h2>
{{range .Skills -}}
<div class="skills clearfix">
<div class="item-skills">
{{.Name}}
{{if .Level}}<span class="skill-level">{{.Level}}</span>{{end -}}
</div>
<div class="col-sm-offset-1 col-sm-12 clearfix">
{{range .Keywords -}}
<span class= "skill badge">{{.}}</span>
{{end -}}
</div>
</div>
{{end -}}
</div>
{{end -}}
{{if .Publications -}}
<!-- PUBLICATIONS -->
<div class="box">
<h2><i class="fa fa-book ico"></i> Publications</h2>
{{range .Publications -}}
<div class="publication panel panel-default">
<div class="panel-heading">
<div class="name panel-title">{{.Name}}</div>
</div>
<div class="panel-body">
{{if .Publisher -}}
<div class="publisher"><i class= "fa fa-bookmark ico"></i> {{.Publisher}}</div>
{{end -}}
<div class="year">{{formatDatePub .ReleaseDate}}</div>
{{if .URL -}}
<div class="address">
<a href="{{.URL}}" target= "_blank"><i class="fa fa-globe ico"></i> {{.URL}}</a>
</div>
{{end -}}
{{if .Summary -}}
<p>{{.Summary}}</p>
{{end -}}
</div>
</div>
{{end -}}
</div>
{{end -}}
{{if .Languages -}}
<!-- LANGUAGES -->
<div class="box">
<h2><i class="fa fa-language ico"></i> Langues</h2>
<ul class="list-group">
{{range .Languages -}}
<li class=" list-group-item">{{.Language}}<span class="skill badge pull-right">{{.Fluency}}</span></li>
{{end -}}
</ul>
</div>
{{end -}}
{{if .Interests -}}
<!-- HOBBIES -->
<div class="box">
<h2><i class="fa fa-heart ico"></i> Intérêts</h2>
{{range .Interests -}}
<div class="interests clearfix">
<div class="item-interests">
{{.Name -}}
</div>
<div class="col-sm-offset-1 col-sm-12 clearfix">
{{range .Keywords -}}
<span class= "interest badge">{{.}}</span>
{{end -}}
</div>
</div>
{{end -}}
</div>
{{end -}}
{{if .References -}}
<div class="box">
<h2><i class= "fa fa-check-square ico"></i> Références</h2>
{{range .References -}}
<blockquote>
<div>{{.Reference}}</div>
<footer>{{.Name}}</footer>
</blockquote><br>
{{end -}}
</div>
{{end -}}
</div>
</div>
</div>
</body>
</html>

276
themes/kendallfr/style.css Normal file
View File

@ -0,0 +1,276 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800);
@charset "utf-8";
@-webkit-viewport { width: device-width; }
@-moz-viewport { width: device-width; }
@-ms-viewport { width: device-width; }
@-o-viewport { width: device-width; }
@viewport { width: device-width; }
body{
font-family: 'Open Sans', Arial, Tahoma;
font-weight: 400;
color: #363636;
background: #334960;
}
blockquote {
font-size: 1em;
}
.container{
margin-top: 80px;
margin-bottom: 15px;
background: #fff;
}
#photo-header{
margin-top: -75px;
}
#photo{
width: 160px;
height: 160px;
border-radius: 50%;
overflow: hidden;
padding: 5px;
background: #334960;
display: inline-block;
}
#photo img{
width: 150px;
height: 150px;
border-radius: 50%;
}
#text-header h1{
margin: 0;
padding: 0;
font-size: 1.5em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: -1px;
}
#text-header h1::first-line{
font-size: 1.5em;
font-weight: 800;
line-height: 1.5em;
}
#text-header h1 span{
color: #334960;
opacity: 0.7;
}
#text-header h1 sup{
opacity: 0.5;
}
#text-header:after{
width: 100%;
height: 3px;
border-bottom: 1px solid #ddd;
margin-top: 15px;
content: '';
display: block;
}
.box{
padding-bottom: 10px;
margin-bottom: 25px;
}
.box h2{
color: #227c74;
font-size: 1.5em;
font-weight: 700;
text-transform: uppercase;
}
#awards,
#education{
margin-top: 20px;
margin-bottom: 0;
position: relative;
padding: 1em 0;
list-style: none;
}
#awards:before,
#education:before {
width: 5px;
height: 100%;
position: absolute;
left: 35px;
top: 0;
content: ' ';
display: block;
background: #32475c;
background: -moz-linear-gradient(top, #ffffff 0%, #32475c 7%, #32475c 89%, #ffffff 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(7%,#32475c), color-stop(89%,#32475c), color-stop(100%,#ffffff));
background: -webkit-linear-gradient(top, #ffffff 0%,#32475c 7%,#32475c 89%,#ffffff 100%);
background: -o-linear-gradient(top, #ffffff 0%,#32475c 7%,#32475c 89%,#ffffff 100%);
background: -ms-linear-gradient(top, #ffffff 0%,#32475c 7%,#32475c 89%,#ffffff 100%);
background: linear-gradient(to bottom, #ffffff 0%,#32475c 7%,#32475c 89%,#ffffff 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ffffff',GradientType=0 );
}
#awards li,
#education li{
width: 100%;
z-index: 2;
position: relative;
float: left;
}
#awards .year,
#education .year{
width: 14%;
background: #fff;
padding: 10px;
font-weight: 700;
display: inline-block;
}
#awards .description,
#education .description{
width: 83%;
display: inline-block;
background: #eee;
margin-bottom: 10px;
position: relative;
padding: 10px;
border-bottom: 1px solid #ccc;
border-right: 1px solid #ccc;
}
#awards .description:after,
#education .description:after {
content: '';
position: absolute;
top: 15px;
right: 0;
left: -16px;
height: 0;
width: 0;
border: solid transparent;
border-right-color: #eee;
border-width: 8px;
pointer-events: none;
}
#awards .description h3,
#education .description h3{
font-size: 1.2em;
margin: 0;
padding: 0;
font-weight: 700;
}
#awards .description p,
#education .description p{
margin-top: 5px;
padding: 0;
}
.job{
margin-bottom: 15px;
}
.job .details {
margin-left: 3%;
width: 95%;
padding: 10px;
margin-bottom: 10px;
background: #eee;
border-bottom: 1px solid #ccc;
border-right: 1px solid #ccc;
}
.job .where{
font-size: 1.2em;
font-weight: bold;
}
.job .year{
opacity: 0.7;
}
.job .profession{
font-size: 1.2em;
font-weight: bold;
}
.job .description{
line-height: 1.5em;
}
.job .highlights{
padding: 5px 0;
font-weight: bold;
}
.job .job-details {
padding-left: 5%;
width: 100%;
}
.publication {
margin-bottom: 0;
}
.publication .name{
font-size: 1em;
font-weight: bold;
}
.publication .year{
opacity: 0.7;
}
.publication p{
margin: 0;
padding-top: 10px;
}
.contact-item{
width: 100%;
float: left;
}
.contact-item .icon{
padding: 10px;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
color: #32475c;
background: #eee;
}
.contact-item:last-child .icon{
border-bottom: none;
}
.contact-item .title{
width: 80%;
width: calc(100% - 55px);
font-weight: 700;
opacity: 0.9;
}
.contact-item .title.only{
margin-top: 10px;
}
.contact-item .description{
width: 80%;
width: calc(100% - 55px);
color: #334960;
}
.item-interests,
.item-skills{
height: 30px;
color: #334960;
padding: 5px 10px;
margin-bottom: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 1.1em;
font-weight: 600;
}
.interest,
.skill{
color: #fff;
display: inline-block;
margin-right: 5px;
margin-bottom: 5px;
padding: 5px 10px;
background: #32475c;
position: relative;
font-size: .85em;
}
.skill-level {
background-color: #227c74;
border-radius: 4px;
color: #fff;
padding: 1px 8px;
font-size: .75em;
position: absolute;
margin: 1px 10px;
}
#language-skills .skill{
margin: 10px 0;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}

59
themes/theme.go Normal file
View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2018 Arnaud Ysmal. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package themes
import (
"fmt"
"io"
"jsonresume/model"
"path"
"text/template"
)
type Theme struct {
Name string
Functions template.FuncMap
Template string
}
var Themes map[string]*Theme
func init() {
Themes = make(map[string]*Theme)
}
func (t *Theme) Render(r *model.Resume, w io.Writer) error {
tmpl := template.Must(template.New(path.Base(t.Template)).Funcs(t.Functions).ParseFiles(t.Template))
return tmpl.Execute(w, r)
}
func (t *Theme) Register() error {
if _, exists := Themes[t.Name]; exists {
return fmt.Errorf("Package %s already registered", t.Name)
}
Themes[t.Name] = t
return nil
}