The backend for the AiteStory website
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

238 lines
4.9 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. package model
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. "git.aiterp.net/AiteRP/aitestory/server"
  7. "git.aiterp.net/gisle/wrouter/generate"
  8. )
  9. // TagTypes are the allowed values for Tag.Type
  10. var TagTypes = []string{
  11. "Location",
  12. "Character",
  13. "Event",
  14. "Organization",
  15. "Series",
  16. }
  17. // TagTitles are the titles to use when listing tags
  18. var TagTitles = map[string]string{
  19. "Location": "Locations",
  20. "Character": "Characters",
  21. "Event": "Plots & Events",
  22. "Organization": "Organizations",
  23. "Series": "Series",
  24. }
  25. // Tag describes a tag
  26. type Tag struct {
  27. ID string
  28. Type string
  29. Name string
  30. }
  31. // Icon is the API used to get the icon for the tag.
  32. func (tag Tag) Icon() string {
  33. return tag.Type[0:1]
  34. }
  35. // CSSCLass gets the CSS class that colors the tag on the
  36. // page list
  37. func (tag Tag) CSSCLass() string {
  38. return fmt.Sprintf("ttype-%s", strings.ToLower(tag.Type))
  39. }
  40. // Insert adds the tag to the database, giving it a new unique ID
  41. func (tag *Tag) Insert() error {
  42. db := server.Main.DB
  43. // Validate tag type
  44. if err := tag.Validate(); err != nil {
  45. return err
  46. }
  47. // Generate an ID if none exists
  48. if tag.ID == "" {
  49. tag.ID = generate.ID()
  50. }
  51. // Do the thing
  52. _, err := db.Exec("INSERT INTO `tag` (id,type,name,disabled) VALUES (?,?,?,false)", tag.ID, tag.Type, tag.Name)
  53. if err != nil {
  54. return err
  55. }
  56. return nil
  57. }
  58. // Update saves the entry for the tag in the database
  59. func (tag *Tag) Update() error {
  60. db := server.Main.DB
  61. // Validate tag type
  62. if err := tag.Validate(); err != nil {
  63. return err
  64. }
  65. // Do the thing
  66. _, err := db.Exec("UPDATE `tag` SET type=?,name=? WHERE id=?", tag.Type, tag.Name, tag.ID)
  67. if err != nil {
  68. return err
  69. }
  70. return nil
  71. }
  72. // Delete removes a tag from the database
  73. func (tag *Tag) Delete() error {
  74. db := server.Main.DB
  75. // Do the thing
  76. results, err := db.Exec("DELETE FROM `tag` WHERE id=? LIMIT 1", tag.ID)
  77. if err != nil {
  78. return err
  79. }
  80. // Count the stuffs that were done things to
  81. affected, err := results.RowsAffected()
  82. if err != nil {
  83. return err
  84. }
  85. if affected == 0 {
  86. return errors.New("tag not found")
  87. }
  88. return nil
  89. }
  90. // Validate checks the name and type, and returns an error if they're not valid. It's
  91. // ran by Update and Insert before doing anything
  92. func (tag *Tag) Validate() error {
  93. validType := false
  94. for _, tagType := range TagTypes {
  95. if tagType == tag.Type {
  96. validType = true
  97. break
  98. }
  99. }
  100. if !validType {
  101. return errors.New("invalid tag type")
  102. }
  103. // Validate tag name
  104. if len(tag.Name) == 0 || len(tag.Name) > 64 {
  105. return errors.New("invalid length")
  106. }
  107. return nil
  108. }
  109. // Path returns a path used for the tag's list page
  110. func (tag Tag) Path() string {
  111. return fmt.Sprintf("/%s/%s", strings.ToLower(tag.Type), strings.Replace(tag.Name, " ", "_", -1))
  112. }
  113. // CSSClass gets the tag's css class, which determines its color.
  114. func (tag Tag) CSSClass() string {
  115. return fmt.Sprintf("ttype-%s", strings.ToLower(tag.Type))
  116. }
  117. // FindTag finds a tag by ID. Leave tagtype blank to ignore it. Both key and
  118. // id are checked against their respective lists to prevent SQL injection attacks.
  119. func FindTag(key string, id string, tagType string) (*Tag, error) {
  120. db := server.Main.DB
  121. // Make damn sure that – should this take user data as key – it
  122. // does not open up for a SQL injection attack
  123. if key != "name" && key != "id" {
  124. return nil, errors.New("invalid key")
  125. }
  126. query := "SELECT id,type,name FROM `tag` WHERE " + key + "=? AND disabled=false"
  127. if tagType != "" {
  128. // Prevent more shenanigans
  129. found := false
  130. for _, existing := range TagTypes {
  131. if existing == tagType {
  132. found = true
  133. break
  134. }
  135. }
  136. if !found {
  137. return nil, errors.New("invalid tag type")
  138. }
  139. query += fmt.Sprintf(` AND type="%s"`, tagType)
  140. }
  141. rows, err := db.Query(query, id)
  142. if err != nil {
  143. return nil, err
  144. }
  145. defer rows.Close()
  146. if !rows.Next() {
  147. return nil, errors.New("not found")
  148. }
  149. tag := new(Tag)
  150. rows.Scan(&tag.ID, &tag.Type, &tag.Name)
  151. return tag, nil
  152. }
  153. // EnsureTag finds or creates a tag.
  154. func EnsureTag(tagType, tagName string) (*Tag, error) {
  155. // Try to find it first
  156. tag, err := FindTag("name", tagName, tagType)
  157. if err != nil && err.Error() != "not found" { // You saw nothing
  158. return nil, err
  159. }
  160. // Failing that, make it
  161. if tag == nil {
  162. tag = new(Tag)
  163. tag.Type = tagType
  164. tag.Name = tagName
  165. err = tag.Insert()
  166. if err != nil {
  167. return nil, err
  168. }
  169. }
  170. return tag, nil
  171. }
  172. // ListTags finds all the tags, without filter.
  173. func ListTags() ([]Tag, error) {
  174. db := server.Main.DB
  175. query := `
  176. SELECT id,type,name,count(pt.page_id) FROM tag
  177. LEFT JOIN page_tag AS pt ON (pt.tag_id = tag.id)
  178. WHERE disabled=false
  179. GROUP BY pt.tag_id
  180. ORDER BY type,name
  181. `
  182. rows, err := db.Query(query)
  183. if err != nil {
  184. return nil, err
  185. }
  186. defer rows.Close()
  187. results := make([]Tag, 0, 64)
  188. for rows.Next() {
  189. count := 0
  190. tag := Tag{}
  191. rows.Scan(&tag.ID, &tag.Type, &tag.Name, &count)
  192. // Let's pretend unused tags don't exist. >_>
  193. if count > 0 {
  194. results = append(results, tag)
  195. }
  196. }
  197. return results, nil
  198. }