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.

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