package model import ( "database/sql" "errors" "time" "git.aiterp.net/AiteRP/aitestory/server" ) // Header contains a subset of the page database // table needed for the front page. It's a read-only // model. type Header struct { ID string `json:"id"` Name string `json:"name"` Author string `json:"author"` Category string `json:"category"` FictionalDate time.Time `json:"fictionalDate"` PublishDate time.Time `json:"publishDate"` EditDate time.Time `json:"editDate"` PrimaryTag *Tag `json:"primaryTag"` } func (header *Header) Dated() bool { return !header.FictionalDate.IsZero() } // CategoryInfo gets information about the category func (header *Header) CategoryInfo() PageCategory { for _, category := range PageCategories { if category.Key == header.Category { return category } } return PageCategory{"Unknown", "Unknown", "?", ""} } // ListHeaders grabs all the general pages from // the database to list them func ListHeaders() ([]Header, error) { const query = ` SELECT page.id,page.name,author,category,fictional_date,publish_date,edit_date,tag.id,tag.type,tag.name FROM page LEFT JOIN page_tag ON (page.id = page_tag.page_id AND page_tag.primary = true) LEFT JOIN tag ON (tag.id = page_tag.tag_id) WHERE page.specific=false AND page.published=true AND page.unlisted=false ORDER BY page.publish_date DESC ` db := server.Main.DB rows, err := db.Query(query) if err != nil { return nil, err } defer rows.Close() results := make([]Header, 0, 64) header := Header{} for rows.Next() { err := parseHeader(&header, rows) if err != nil { return nil, err } results = append(results, header) } return results, nil } // ListHeadersByCategory grabs all the pages in the given category func ListHeadersByCategory(category string) ([]Header, error) { const query = ` SELECT page.id,page.name,author,category,fictional_date,publish_date,edit_date,tag.id,tag.type,tag.name FROM page LEFT JOIN page_tag ON (page.id = page_tag.page_id AND page_tag.primary = true) LEFT JOIN tag ON (tag.id = page_tag.tag_id) WHERE page.specific=false AND page.published=true AND page.unlisted=false AND page.category = ? ORDER BY page.publish_date DESC ` db := server.Main.DB rows, err := db.Query(query, category) if err != nil { return nil, err } defer rows.Close() results := make([]Header, 0, 64) header := Header{} for rows.Next() { err := parseHeader(&header, rows) if err != nil { return nil, err } results = append(results, header) } return results, nil } // ListHeadersByTag lists all headers that has the tag. Leave the category empty // to not filter by it func ListHeadersByTag(category string, tag *Tag) ([]Header, error) { const query1 = ` SELECT page.id,page.name,page.author,page.category,page.fictional_date,page.publish_date,page.edit_date,tag.id,tag.type,tag.name FROM page_tag RIGHT JOIN page ON page.id = page_tag.page_id LEFT JOIN (page_tag AS pt2) ON (page.id = pt2.page_id AND pt2.primary = true) LEFT JOIN (tag AS tag) ON (tag.id = pt2.tag_id) WHERE page_tag.tag_id=? AND page.unlisted=false AND page.published=true ORDER BY page.publish_date DESC ` const query2 = ` SELECT page.id,page.name,page.author,page.category,page.fictional_date,page.publish_date,page.edit_date,tag.id,tag.type,tag.name FROM page_tag RIGHT JOIN page ON page.id = page_tag.page_id LEFT JOIN (page_tag AS pt2) ON (page.id = pt2.page_id AND pt2.primary = true) LEFT JOIN (tag AS tag) ON (tag.id = pt2.tag_id) WHERE page_tag.tag_id=? AND page.category=? AND page.unlisted=false AND page.published=true ORDER BY page.publish_date DESC ` if tag == nil { return nil, errors.New("no tag") } db := server.Main.DB query := query1 if category != "" { query = query2 } rows, err := db.Query(query, tag.ID) if err != nil { return nil, err } defer rows.Close() results := make([]Header, 0, 64) header := Header{} for rows.Next() { err := parseHeader(&header, rows) if err != nil { return nil, err } results = append(results, header) } return results, nil } // ListHeadersByTags searches for the first tag, then filters the result based on the // others. There is room for improvement in this function, but I'll have to judge whether // there will be a need for that. func ListHeadersByTags(category string, tags []Tag) ([]Header, error) { if len(tags) == 0 { return nil, errors.New("no tags") } headers, err := ListHeadersByTag(category, &tags[0]) if err != nil { return nil, err } if len(headers) == 0 { return headers, nil } for _, tag := range tags[1:] { headers2, err := ListHeadersByTag(category, &tag) if err != nil { return nil, err } results := make([]Header, 0, len(headers)) for _, header := range headers { found := false for _, header2 := range headers2 { if header.ID == header2.ID { found = true break } } if found { results = append(results, header) } } headers = results } return headers, nil } func parseHeader(header *Header, rows *sql.Rows) error { var tagID, tagName, tagType string var fictionalDate, publishDate, editDate string var err error rows.Scan(&header.ID, &header.Name, &header.Author, &header.Category, &fictionalDate, &publishDate, &editDate, &tagID, &tagType, &tagName) if tagID != "" { header.PrimaryTag = &Tag{tagID, tagType, tagName} } else { header.PrimaryTag = nil } header.FictionalDate, err = time.Parse("2006-01-02 15:04:05", fictionalDate) if err != nil { header.FictionalDate = time.Time{} } header.PublishDate, err = time.Parse("2006-01-02 15:04:05", publishDate) if err != nil { return err } header.EditDate, err = time.Parse("2006-01-02 15:04:05", editDate) if err != nil { return err } return nil }