Created
July 25, 2013 21:13
-
-
Save LouisStAmour/6083826 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{{define "carousel"}} | |
{{$page := .}} | |
<div class="tabset tabbed_carousel"> | |
<ul class="tabs"> | |
{{range .NewTitleLists}} | |
<li class="tab tab_{{.Index}}"> | |
<a href="{{.Link}}">{{.Title}}</a> | |
</li> | |
{{end}} | |
</ul> | |
<div class="bodies"> | |
{{range $list := .NewTitleLists}} | |
<div class="body_for_tab body_for_tab_{{$list.Index}}"> | |
<div class="carousel bc_catalogue_reference_carousel"> | |
<div class="left"> </div> | |
<div class="body"><div class="item-list"><ul> | |
{{range $bib := $list.SearchResult.Bibs}} | |
{{if $bib.CoverResized}} | |
<li> | |
<div class="thumbnail"><div class="cover"> | |
<a style="padding-bottom: 10px; width: {{$bib.CoverResized.Width}}px; height:{{$bib.CoverResized.Height}}px; display: block; background: #fff url('/sites/default/files/go/{{$page.FilenamePrefix}}new_titles_covers_{{$list.Id}}.jpg') -{{$bib.CoverOffset}}px 0px no-repeat;" href="{{$bib.Link}}"><img style="display: none;" alt="{{$bib.Title}}" title="{{$bib.Title}}" width="{{$bib.CoverResized.Width}}" height="{{$bib.CoverResized.Height}}" /></a> | |
<div class="info"> | |
<h5><a href="{{$bib.Link}}">{{$bib.Title}}</a></h5> | |
{{if $bib.SubTitle}}<h6>{{$bib.SubTitle}}</h6>{{end}} | |
<p>{{if $bib.AuthorLink}}By {{$bib.AuthorLink}}{{else}} {{end}}</p> | |
</div></div></div> | |
</li> | |
{{end}} | |
{{end}} | |
</ul></div></div> | |
<div class="right"> </div> | |
<div class="clear"></div> | |
<div class="caption"></div> | |
<div class="source"> | |
<a href="{{$list.Link}}">View all new titles ≫</a> | |
</div> | |
</div> | |
</div> | |
{{end}} | |
</div> | |
</div> | |
{{end}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "database/sql" | |
import "encoding/xml" | |
import "flag" | |
import "html" | |
import "html/template" | |
import "net/http" | |
import "net/url" | |
import "bytes" | |
import "fmt" | |
import "strconv" | |
import "os" | |
import "regexp" | |
import "strings" | |
import "io/ioutil" | |
import _ "github.com/Go-SQL-Driver/MySQL" | |
import "image" | |
import "image/color" | |
import "image/draw" | |
import _ "image/png" | |
import "image/jpeg" | |
import _ "image/gif" | |
import "github.com/nfnt/resize" | |
var ( | |
username = flag.String("username", "root", "MySQL username") | |
password = flag.String("password", "p4ssw0rd", "MySQL password") | |
location = flag.String("location", "unix(/var/run/mysqld/mysqld.sock)", "MySQL host and/or port") | |
database = flag.String("database", "my_app", "MySQL database") | |
) | |
const ( | |
HEIGHT = 120 | |
) | |
type Page struct { | |
TemplateName string | |
DatabasePostfix string | |
FilenamePrefix string | |
NewTitleLists []*NewTitleList | |
StaffPicksTopics []*StaffPicksTopic | |
} | |
type NewTitleList struct { | |
Id int | |
Title string | |
Link string | |
SearchResult SearchResult | |
WidthOfCovers int | |
Index int | |
} | |
type Bib struct { | |
Format string | |
Title string | |
SubTitle string | |
Link string | |
AuthorLink template.HTML | |
Authors []string `xml:"Authors>String"` | |
ISBNs []string `xml:"ISBNs>String"` | |
UPCs []string `xml:"UPCs>String"` | |
EANs []string `xml:"EANs>String"` | |
BcId string | |
Cover interface{} | |
CoverResized interface{} | |
CoverOffset int | |
} | |
type Cover struct { | |
URL string | |
Width int | |
Height int | |
Format string | |
Image image.Image | |
} | |
type StaffPicksTopic struct { | |
Name string | |
Index int | |
HideButtons bool | |
Lists []*StaffPicksList | |
} | |
type StaffPicksList struct { | |
Id int | |
BcId string | |
Title string | |
TitleNil bool | |
Topic string | |
Link string | |
Description string | |
DescNil bool | |
AuthorName string | |
AuthorNil bool | |
AuthorLink template.HTML | |
Index int | |
UserList UserList | |
} | |
type UserList struct { | |
Name string | |
Description string | |
User struct { | |
BcId string | |
DisplayName string | |
} | |
ListItems struct { | |
PageSize int | |
Page int | |
TotalCount int | |
TotalPages int | |
Bibs []Bib `xml:"BibListItem>Bib"` | |
} | |
} | |
type SearchResult struct { | |
PageSize int | |
Page int | |
TotalCount int | |
TotalPages int | |
Bibs []Bib `xml:"Bib"` | |
} | |
func parseLinkFromDatabase(link string) (string, error) { | |
u, err := url.ParseRequestURI(link) | |
if err != nil { | |
return "", err | |
} | |
v := u.Query() | |
v.Set("display_quantity", strconv.Itoa(25)) | |
v.Set("page", strconv.Itoa(1)) | |
u.RawQuery = v.Encode() | |
u.Path += ".xml" | |
return u.String(), nil | |
} | |
func handleError(err error) { | |
if err != nil { | |
panic(err.Error()) | |
} | |
} | |
func imageHasDifferentColors(img image.Image) bool { | |
r, g, b, a := img.At(0, 0).RGBA() | |
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { | |
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { | |
cR, cG, cB, cA := img.At(x, y).RGBA() | |
if cR != r || cG != g || cB != b || cA != a { | |
return true | |
} | |
} | |
} | |
return false | |
} | |
func lookupCover(number string, isbn bool, size string) (*Cover, error) { | |
cover := new(Cover) | |
if isbn { | |
cover.URL = "http://www.syndetics.com/index.aspx?isbn=" + template.URLQueryEscaper(number) + "/" + template.URLQueryEscaper(size) + "C.JPG&client=XXX&type=xw12&oclc=&upc=" | |
} else { | |
cover.URL = "http://www.syndetics.com/index.aspx?isbn=/" + template.URLQueryEscaper(size) + "C.JPG&client=XXX&type=xw12&oclc=&upc=" + template.URLQueryEscaper(number) | |
} | |
resp, err := http.Get(cover.URL) | |
if err != nil { | |
if strings.Contains(err.Error(), "unexpected EOF") { | |
resp, err = http.Get(cover.URL) | |
if err != nil { | |
return cover, err | |
} | |
} else { | |
return cover, err | |
} | |
} | |
defer resp.Body.Close() | |
cover.Image, cover.Format, err = image.Decode(resp.Body) | |
if err != nil { | |
return cover, err | |
} | |
if imageHasDifferentColors(cover.Image) { | |
cover.Width = cover.Image.Bounds().Size().X | |
cover.Height = cover.Image.Bounds().Size().Y | |
} else { | |
cover.Width = 0 | |
cover.Height = 0 | |
} | |
return cover, nil | |
} | |
func excerptFromString(s string, count int) string { | |
r := regexp.MustCompile("\\s*\\S+") | |
m := r.FindAllString(s, count) | |
if m != nil && len(m) == count { | |
return strings.Join(m, " ") + " . . ." | |
} | |
return s | |
} | |
func (p *Page) getStaffPicks(db *sql.DB) { | |
page := *p | |
rows, err := db.Query("select f.id, t.title AS topic, f.title AS feed, f.link, f.description, f.author_name, f.author_link from bc_staff_picks" + page.DatabasePostfix + "_feeds f, bc_staff_picks" + page.DatabasePostfix + "_topics t WHERE f.topic_id = t.id ORDER BY t.weight, f.weight") | |
handleError(err) | |
defer rows.Close() | |
var ( | |
authorLink string | |
authorName interface{} | |
title interface{} | |
description interface{} | |
topic *StaffPicksTopic | |
i int | |
j int | |
) | |
for rows.Next() { | |
list := new(StaffPicksList) | |
var topicName string | |
err = rows.Scan(&list.Id, &topicName, &title, &list.Link, &description, &authorName, &authorLink) | |
handleError(err) | |
if topic == nil || topicName != topic.Name { | |
j = 0 | |
topic = new(StaffPicksTopic) | |
topic.Name = topicName | |
topic.Index = i + 1 | |
i++ | |
page.StaffPicksTopics = append(page.StaffPicksTopics, topic) | |
} | |
if title == nil { | |
list.TitleNil = true | |
} else { | |
list.Title = string(title.([]byte)) | |
} | |
if description == nil { | |
list.DescNil = true | |
} else { | |
list.Description = string(description.([]byte)) | |
} | |
list.Description = excerptFromString(list.Description, 25) | |
if authorName == nil { | |
list.AuthorNil = true | |
} else { | |
list.AuthorName = string(authorName.([]byte)) | |
} | |
list.AuthorLink = template.HTML(authorLink) | |
list.Index = j + 1 | |
topic.HideButtons = list.Index < 2 | |
j++ | |
topic.Lists = append(topic.Lists, list) | |
} | |
err = rows.Err() | |
handleError(err) | |
*p = page | |
} | |
func (p *Page) getNewTitles(db *sql.DB) { | |
page := *p | |
rows, err := db.Query("SELECT `id`, `title`, `link` FROM bc_new_titles" + page.DatabasePostfix + " ORDER BY weight ASC") | |
handleError(err) | |
defer rows.Close() | |
var ( | |
i int | |
) | |
for rows.Next() { | |
list := new(NewTitleList) | |
err = rows.Scan(&list.Id, &list.Title, &list.Link) | |
handleError(err) | |
list.Index = i + 1 | |
i++ | |
page.NewTitleLists = append(page.NewTitleLists, list) | |
} | |
err = rows.Err() | |
handleError(err) | |
*p = page | |
} | |
func (bib *Bib) LookupCover(width uint, height uint) { | |
sizes := [3]string{"M", "S", "L"} // Large as last resort | |
for _, size := range sizes { | |
for _, ean := range bib.EANs { | |
if bib.Cover != nil { | |
break | |
} | |
cover, err := lookupCover(ean, true, size) | |
if err != nil { | |
if err.Error() != "image: unknown format" { | |
handleError(err) | |
} | |
} else { | |
if cover.Width > 1 && cover.Height > 1 { | |
bib.Cover = *cover | |
} | |
} | |
} | |
for _, isbn := range bib.ISBNs { | |
if bib.Cover != nil { | |
break | |
} | |
cover, err := lookupCover(isbn, true, size) | |
if err != nil { | |
if err.Error() != "image: unknown format" { | |
handleError(err) | |
} | |
} else { | |
if cover.Width > 1 && cover.Height > 1 { | |
bib.Cover = *cover | |
} | |
} | |
} | |
for _, upc := range bib.UPCs { | |
if bib.Cover != nil { | |
break | |
} | |
cover, err := lookupCover(upc, false, size) | |
if err != nil { | |
if err.Error() != "image: unknown format" { | |
handleError(err) | |
} | |
} else { | |
if cover.Width > 1 && cover.Height > 1 { | |
bib.Cover = *cover | |
} | |
} | |
} | |
} | |
if bib.Cover == nil { | |
} else { | |
c := new(Cover) | |
c.Image = resize.Resize(width, height, bib.Cover.(Cover).Image, resize.Bicubic) | |
c.Width = c.Image.Bounds().Size().X | |
c.Height = c.Image.Bounds().Size().Y | |
bib.CoverResized = *c | |
} | |
} | |
var lastDigitsRegexp = regexp.MustCompile("/(\\d+)[^/]*$") | |
const ( | |
SP_WIDTH = 35 | |
SP_HEIGHT = 52 | |
SP_PADDING = 5 | |
SP_GRID_SIZE = 3 | |
) | |
var doubleQuotedReplacer = strings.NewReplacer("&amp;", "&", "&lt;", "<", "&gt;", ">", "&apos;", "'", "&quot;", """) | |
func (page *Page) processStaffPicks() { | |
for t, topics := range page.StaffPicksTopics { | |
for _, list := range topics.Lists { | |
submatches := lastDigitsRegexp.FindStringSubmatch(list.Link) | |
if submatches == nil || len(submatches) < 1 || submatches[1] == "" { | |
fmt.Println("ListID", list.Id, "has unparsable link", list.Link) | |
} else { | |
list.BcId = submatches[1] | |
resp, err := http.Get("http://example.org/api/UserList/" + list.BcId) | |
handleError(err) | |
defer resp.Body.Close() | |
body, err := ioutil.ReadAll(resp.Body) | |
handleError(err) | |
decoder := xml.NewDecoder(strings.NewReader(doubleQuotedReplacer.Replace(string(body)))) | |
decoder.Strict = false | |
var ul UserList | |
decoder.Decode(&ul) | |
if list.TitleNil { | |
list.Title = ul.Name | |
} else { | |
list.Title = html.UnescapeString(list.Title) | |
} | |
if list.DescNil { | |
list.Description = ul.Description | |
} else { | |
list.Description = html.UnescapeString(list.Description) | |
} | |
if list.AuthorNil { | |
list.AuthorName = ul.User.DisplayName | |
} else { | |
list.AuthorName = html.UnescapeString(list.AuthorName) | |
} | |
list.AuthorLink = template.HTML("<a href=\"http://example.org/collection/show/" + ul.User.BcId + "/library\">" + list.AuthorName + "</a>") | |
fmt.Println(list.Title) | |
list.UserList = ul | |
} | |
} | |
for i, list := range topics.Lists { | |
var coversFound int | |
for j, bib := range list.UserList.ListItems.Bibs { | |
if coversFound < SP_GRID_SIZE*SP_GRID_SIZE { | |
bib.LookupCover(SP_WIDTH, SP_HEIGHT) | |
if bib.Cover != nil { | |
coversFound++ | |
} else { | |
fmt.Println("No cover found for", bib.BcId) | |
} | |
} | |
// Save our progress | |
page.StaffPicksTopics[t].Lists[i].UserList.ListItems.Bibs[j] = bib | |
} | |
} | |
for _, list := range topics.Lists { | |
m := image.NewRGBA(image.Rect(0, 0, 115, 166)) | |
white := color.RGBA{255, 255, 255, 255} | |
draw.Draw(m, m.Bounds(), &image.Uniform{white}, image.ZP, draw.Src) | |
var c int | |
for _, bib := range list.UserList.ListItems.Bibs { | |
if c < SP_GRID_SIZE*SP_GRID_SIZE && bib.CoverResized != nil { | |
cover := bib.CoverResized.(Cover) | |
dp := image.Point{c%SP_GRID_SIZE*SP_WIDTH + c%SP_GRID_SIZE*SP_PADDING, | |
c/SP_GRID_SIZE*SP_HEIGHT + c/SP_GRID_SIZE*SP_PADDING} | |
r := image.Rectangle{dp, dp.Add(cover.Image.Bounds().Size())} | |
draw.Draw(m, r, cover.Image, cover.Image.Bounds().Min, draw.Over) | |
c++ | |
} | |
} | |
saveImage(m, page.FilenamePrefix+"staff_picks_covers_"+strconv.Itoa(list.Id)) | |
} | |
} | |
} | |
func saveImage(m *image.RGBA, filename string) { | |
img, err := os.Create(filename + ".jpg") | |
defer img.Close() | |
handleError(err) | |
jpeg.Encode(img, m, &jpeg.Options{95}) | |
} | |
func (page *Page) processNewTitles() { | |
for _, list := range page.NewTitleLists { | |
url, err := parseLinkFromDatabase(list.Link) | |
handleError(err) | |
resp, err := http.Get(url) | |
handleError(err) | |
defer resp.Body.Close() | |
body, err := ioutil.ReadAll(resp.Body) | |
handleError(err) | |
decoder := xml.NewDecoder(strings.NewReader(doubleQuotedReplacer.Replace(string(body)))) | |
decoder.Strict = false | |
var sr SearchResult | |
decoder.Decode(&sr) | |
list.SearchResult = sr | |
} | |
for i, list := range page.NewTitleLists { | |
for j, bib := range list.SearchResult.Bibs { | |
bib.Link = "http://example.org/item/show/" + bib.BcId | |
if len(bib.Authors) > 0 { | |
bib.AuthorLink = template.HTML("<a href=\"http://example.org/search?q=" + html.EscapeString(template.URLQueryEscaper(bib.Authors[0])) + "&t=author\">" + template.HTMLEscaper(bib.Authors[0]) + "</a>") | |
} | |
bib.LookupCover(0, HEIGHT) | |
if bib.CoverResized != nil { | |
bib.CoverOffset = list.WidthOfCovers | |
list.WidthOfCovers += bib.CoverResized.(Cover).Width | |
} | |
// Save our progress | |
page.NewTitleLists[i].SearchResult.Bibs[j] = bib | |
} | |
} | |
for _, list := range page.NewTitleLists { | |
m := image.NewRGBA(image.Rect(0, 0, list.WidthOfCovers, HEIGHT)) | |
white := color.RGBA{255, 255, 255, 255} | |
draw.Draw(m, m.Bounds(), &image.Uniform{white}, image.ZP, draw.Src) | |
var c int | |
for _, bib := range list.SearchResult.Bibs { | |
if bib.CoverResized != nil { | |
cover := bib.CoverResized.(Cover) | |
dp := image.Point{c, 0} | |
r := image.Rectangle{dp, dp.Add(cover.Image.Bounds().Size())} | |
draw.Draw(m, r, cover.Image, cover.Image.Bounds().Min, draw.Over) | |
c += cover.Width | |
} | |
} | |
saveImage(m, page.FilenamePrefix+"new_titles_covers_"+strconv.Itoa(list.Id)) | |
} | |
} | |
func main() { | |
flag.Parse() | |
db, err := sql.Open("mysql", *username+":"+*password+"@"+*location+"/"+*database+"?charset=utf8") | |
handleError(err) | |
defer db.Close() | |
pages := [3]*Page{ | |
&Page{TemplateName: "teens", DatabasePostfix: "_teens", FilenamePrefix: "teens_"}, | |
&Page{TemplateName: "kids", DatabasePostfix: "_kids", FilenamePrefix: "kids_"}, | |
&Page{TemplateName: "index", DatabasePostfix: "", FilenamePrefix: ""}, | |
} | |
t := template.Must(template.ParseFiles( | |
"summary_carousel.template.html", | |
"carousel.template.html")) | |
q, err := db.Prepare("INSERT INTO go_blocks (id, data) values(?, ?) on duplicate key update data=values(data)") | |
handleError(err) | |
for _, page := range pages { | |
page.getStaffPicks(db) | |
page.getNewTitles(db) | |
page.processStaffPicks() | |
page.processNewTitles() | |
var ( | |
staffPicksHTML bytes.Buffer | |
newTitlesHTML bytes.Buffer | |
) | |
// out, err := os.Create(page.TemplateName + ".html") | |
// defer out.Close() | |
// handleError(err) | |
err = t.ExecuteTemplate(&staffPicksHTML, "summary_carousel", page) | |
handleError(err) | |
tmpString := staffPicksHTML.String() | |
_, err = q.Exec("staff_picks"+page.DatabasePostfix, tmpString) | |
handleError(err) | |
err = t.ExecuteTemplate(&newTitlesHTML, "carousel", page) | |
handleError(err) | |
tmpString = newTitlesHTML.String() | |
_, err = q.Exec("new_titles"+page.DatabasePostfix, tmpString) | |
handleError(err) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{{define "index"}} | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr" class="js"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
<title>Homepage</title> | |
</head> | |
<body class="not-front logged-in page-kids-1 node-type-page-1 page-kids-home section-kids two-sidebars"> | |
<h2 class="title">Booklists</h2> | |
<div class="content"> | |
{{template "summary_carousel" .}} | |
</div> | |
</div> <!-- /.block --><div id="block-bc_new_titles_teens-0" class="block block-bc_new_titles_teens last region-odd odd region-count-3 count-7"> | |
<h2 class="title">New Titles</h2> | |
<div class="content"> | |
{{template "carousel" .}} | |
</div> | |
</div> <!-- /.block --></div> <!-- /.region --> | |
</body></html> | |
{{end}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{{define "kids"}} | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr" class="js"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
<title>Kids Homepage</title> | |
</head> | |
<body class="not-front logged-in page-kids-1 node-type-page-1 page-kids-home section-kids two-sidebars"> | |
<h2 class="title">Booklists</h2> | |
<div class="content"> | |
{{template "summary_carousel" .}} | |
</div> | |
</div> <!-- /.block --><div id="block-bc_new_titles_teens-0" class="block block-bc_new_titles_teens last region-odd odd region-count-3 count-7"> | |
<h2 class="title">New Titles</h2> | |
<div class="content"> | |
{{template "carousel" .}} | |
</div> | |
</div> <!-- /.block --></div> <!-- /.region --> | |
</body></html> | |
{{end}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{{define "summary_carousel"}} | |
{{$page := .}} | |
<div class="tabset tabbed_carousel"> | |
<ul class="tabs"> | |
{{range .StaffPicksTopics}} | |
<li class="tab tab_{{.Index}}"> | |
<a href="#">{{.Name}}</a> | |
</li> | |
{{end}} | |
</ul> | |
<div class="bodies"> | |
{{range $topic := .StaffPicksTopics}} | |
<div class="body_for_tab body_for_tab_{{.Index}}"> | |
<div class="carousel summary_carousel"> | |
<div class="left"{{if .HideButtons}} style="background: none"{{end}}> </div> | |
<div class="body"> | |
<ul class="elements"> | |
{{range $list := .Lists}} | |
<li class="summary summary_{{$list.Index}} first_summary"> | |
<div class="index_card bc_list_index_card"> | |
<div class="covers"> | |
<a href="{{$list.Link}}" style="display: block; width: 115px; height: 166px; background: #fff url('/sites/default/files/go/{{$page.FilenamePrefix}}staff_picks_covers_{{$list.Id}}.jpg') no-repeat top left; padding-bottom: 5px;"> | |
</a> | |
</div> | |
<h5><a href="{{$list.Link}}">{{$list.Title}}</a></h5> | |
<h6>A list by {{$list.AuthorLink}}</a></h6> | |
<p>{{$list.Description}}</p> | |
<p><em><a href="{{$list.Link}}">View list</a></em></p> | |
</div> | |
</li> | |
{{end}} | |
</ul> | |
</div> | |
<div class="right"{{if .HideButtons}} style="background: none"{{end}}> </div> | |
</div> | |
</div> | |
{{end}} | |
</div> | |
</div> | |
{{end}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{{define "teens"}} | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr" class="js"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
<title>Teens Homepage</title> | |
</head> | |
<body class="not-front logged-in page-kids-1 node-type-page-1 page-kids-home section-kids two-sidebars"> | |
<h2 class="title">Booklists</h2> | |
<div class="content"> | |
{{template "summary_carousel" .}} | |
</div> | |
</div> <!-- /.block --><div id="block-bc_new_titles_teens-0" class="block block-bc_new_titles_teens last region-odd odd region-count-3 count-7"> | |
<h2 class="title">New Titles</h2> | |
<div class="content"> | |
{{template "carousel" .}} | |
</div> | |
</div> <!-- /.block --></div> <!-- /.region --> | |
</body></html> | |
{{end}} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment