module_blog.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. package modules
  2. import (
  3. "html"
  4. "net/http"
  5. "strings"
  6. "golang-fave/assets"
  7. "golang-fave/consts"
  8. "golang-fave/engine/builder"
  9. "golang-fave/engine/fetdata"
  10. "golang-fave/engine/sqlw"
  11. "golang-fave/engine/wrapper"
  12. "golang-fave/utils"
  13. )
  14. func (this *Modules) RegisterModule_Blog() *Module {
  15. return this.newModule(MInfo{
  16. WantDB: true,
  17. Mount: "blog",
  18. Name: "Blog",
  19. Order: 1,
  20. System: false,
  21. Icon: assets.SysSvgIconList,
  22. Sub: &[]MISub{
  23. {Mount: "default", Name: "List of posts", Show: true, Icon: assets.SysSvgIconList},
  24. {Mount: "add", Name: "Add new post", Show: true, Icon: assets.SysSvgIconPlus},
  25. {Mount: "modify", Name: "Modify post", Show: false},
  26. {Sep: true, Show: true},
  27. {Mount: "categories", Name: "List of categories", Show: true, Icon: assets.SysSvgIconList},
  28. {Mount: "categories-add", Name: "Add new category", Show: true, Icon: assets.SysSvgIconPlus},
  29. {Mount: "categories-modify", Name: "Modify category", Show: false},
  30. },
  31. }, func(wrap *wrapper.Wrapper) {
  32. if len(wrap.UrlArgs) == 3 && wrap.UrlArgs[0] == "blog" && wrap.UrlArgs[1] == "category" && wrap.UrlArgs[2] != "" {
  33. // Blog category
  34. row := &utils.MySql_blog_category{}
  35. rou := &utils.MySql_user{}
  36. err := wrap.DB.QueryRow(`
  37. SELECT
  38. main.id,
  39. main.user,
  40. main.name,
  41. main.alias,
  42. main.lft,
  43. main.rgt,
  44. main.depth,
  45. parent.id AS parent_id,
  46. users.id,
  47. users.first_name,
  48. users.last_name,
  49. users.email,
  50. users.admin,
  51. users.active
  52. FROM
  53. (
  54. SELECT
  55. node.id,
  56. node.user,
  57. node.name,
  58. node.alias,
  59. node.lft,
  60. node.rgt,
  61. (COUNT(parent.id) - 1) AS depth
  62. FROM
  63. blog_cats AS node,
  64. blog_cats AS parent
  65. WHERE
  66. node.lft BETWEEN parent.lft AND parent.rgt
  67. GROUP BY
  68. node.id
  69. ORDER BY
  70. node.lft ASC
  71. ) AS main
  72. LEFT JOIN (
  73. SELECT
  74. node.id,
  75. node.user,
  76. node.name,
  77. node.alias,
  78. node.lft,
  79. node.rgt,
  80. (COUNT(parent.id) - 0) AS depth
  81. FROM
  82. blog_cats AS node,
  83. blog_cats AS parent
  84. WHERE
  85. node.lft BETWEEN parent.lft AND parent.rgt
  86. GROUP BY
  87. node.id
  88. ORDER BY
  89. node.lft ASC
  90. ) AS parent ON
  91. parent.depth = main.depth AND
  92. main.lft > parent.lft AND
  93. main.rgt < parent.rgt
  94. LEFT JOIN users ON users.id = main.user
  95. WHERE
  96. main.id > 1 AND
  97. main.alias = ?
  98. ORDER BY
  99. main.lft ASC
  100. ;`,
  101. wrap.UrlArgs[2],
  102. ).Scan(
  103. &row.A_id,
  104. &row.A_user,
  105. &row.A_name,
  106. &row.A_alias,
  107. &row.A_lft,
  108. &row.A_rgt,
  109. &row.A_depth,
  110. &row.A_parent,
  111. &rou.A_id,
  112. &rou.A_first_name,
  113. &rou.A_last_name,
  114. &rou.A_email,
  115. &rou.A_admin,
  116. &rou.A_active,
  117. )
  118. if err != nil && err != wrapper.ErrNoRows {
  119. // System error 500
  120. wrap.LogCpError(&err)
  121. utils.SystemErrorPageEngine(wrap.W, err)
  122. return
  123. } else if err == wrapper.ErrNoRows {
  124. // User error 404 page
  125. wrap.RenderFrontEnd("404", fetdata.New(wrap, true, nil, nil), http.StatusNotFound)
  126. return
  127. }
  128. // Fix url
  129. if wrap.R.URL.Path[len(wrap.R.URL.Path)-1] != '/' {
  130. http.Redirect(wrap.W, wrap.R, wrap.R.URL.Path+"/"+utils.ExtractGetParams(wrap.R.RequestURI), 301)
  131. return
  132. }
  133. // Render template
  134. wrap.RenderFrontEnd("blog-category", fetdata.New(wrap, false, row, rou), http.StatusOK)
  135. return
  136. } else if len(wrap.UrlArgs) == 2 && wrap.UrlArgs[0] == "blog" && wrap.UrlArgs[1] != "" {
  137. // Blog post
  138. row := &utils.MySql_blog_post{}
  139. rou := &utils.MySql_user{}
  140. err := wrap.DB.QueryRow(`
  141. SELECT
  142. blog_posts.id,
  143. blog_posts.user,
  144. blog_posts.name,
  145. blog_posts.alias,
  146. blog_posts.category,
  147. blog_posts.briefly,
  148. blog_posts.content,
  149. UNIX_TIMESTAMP(blog_posts.datetime) as datetime,
  150. blog_posts.active,
  151. users.id,
  152. users.first_name,
  153. users.last_name,
  154. users.email,
  155. users.admin,
  156. users.active
  157. FROM
  158. blog_posts
  159. LEFT JOIN users ON users.id = blog_posts.user
  160. WHERE
  161. blog_posts.active = 1 and
  162. blog_posts.alias = ?
  163. LIMIT 1;`,
  164. wrap.UrlArgs[1],
  165. ).Scan(
  166. &row.A_id,
  167. &row.A_user,
  168. &row.A_name,
  169. &row.A_alias,
  170. &row.A_category,
  171. &row.A_briefly,
  172. &row.A_content,
  173. &row.A_datetime,
  174. &row.A_active,
  175. &rou.A_id,
  176. &rou.A_first_name,
  177. &rou.A_last_name,
  178. &rou.A_email,
  179. &rou.A_admin,
  180. &rou.A_active,
  181. )
  182. if err != nil && err != wrapper.ErrNoRows {
  183. // System error 500
  184. wrap.LogCpError(&err)
  185. utils.SystemErrorPageEngine(wrap.W, err)
  186. return
  187. } else if err == wrapper.ErrNoRows {
  188. // User error 404 page
  189. wrap.RenderFrontEnd("404", fetdata.New(wrap, true, nil, nil), http.StatusNotFound)
  190. return
  191. }
  192. // Fix url
  193. if wrap.R.URL.Path[len(wrap.R.URL.Path)-1] != '/' {
  194. http.Redirect(wrap.W, wrap.R, wrap.R.URL.Path+"/"+utils.ExtractGetParams(wrap.R.RequestURI), 301)
  195. return
  196. }
  197. // Render template
  198. wrap.RenderFrontEnd("blog-post", fetdata.New(wrap, false, row, rou), http.StatusOK)
  199. return
  200. } else if len(wrap.UrlArgs) == 1 && wrap.UrlArgs[0] == "blog" {
  201. // Blog
  202. // Fix url
  203. if wrap.R.URL.Path[len(wrap.R.URL.Path)-1] != '/' {
  204. http.Redirect(wrap.W, wrap.R, wrap.R.URL.Path+"/"+utils.ExtractGetParams(wrap.R.RequestURI), 301)
  205. return
  206. }
  207. // Render template
  208. wrap.RenderFrontEnd("blog", fetdata.New(wrap, false, nil, nil), http.StatusOK)
  209. return
  210. } else if (*wrap.Config).Engine.MainModule == 1 {
  211. // Render template
  212. wrap.RenderFrontEnd("blog", fetdata.New(wrap, false, nil, nil), http.StatusOK)
  213. return
  214. }
  215. // User error 404 page
  216. wrap.RenderFrontEnd("404", fetdata.New(wrap, true, nil, nil), http.StatusNotFound)
  217. }, func(wrap *wrapper.Wrapper) (string, string, string) {
  218. content := ""
  219. sidebar := ""
  220. if wrap.CurrSubModule == "" || wrap.CurrSubModule == "default" {
  221. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  222. {Name: "List of posts"},
  223. })
  224. content += builder.DataTable(
  225. wrap,
  226. "blog_posts",
  227. "id",
  228. "DESC",
  229. &[]builder.DataTableRow{
  230. {
  231. DBField: "id",
  232. },
  233. {
  234. DBField: "name",
  235. NameInTable: "Post / URL",
  236. CallBack: func(values *[]string) string {
  237. name := `<a href="/cp/` + wrap.CurrModule + `/modify/` + (*values)[0] + `/">` + html.EscapeString((*values)[1]) + `</a>`
  238. alias := html.EscapeString((*values)[2])
  239. return `<div>` + name + `</div><div><small>/blog/` + alias + `/</small></div>`
  240. },
  241. },
  242. {
  243. DBField: "alias",
  244. },
  245. {
  246. DBField: "datetime",
  247. DBExp: "UNIX_TIMESTAMP(`datetime`)",
  248. NameInTable: "Date / Time",
  249. Classes: "d-none d-md-table-cell",
  250. CallBack: func(values *[]string) string {
  251. t := int64(utils.StrToInt((*values)[3]))
  252. return `<div>` + utils.UnixTimestampToFormat(t, "02.01.2006") + `</div>` +
  253. `<div><small>` + utils.UnixTimestampToFormat(t, "15:04:05") + `</small></div>`
  254. },
  255. },
  256. {
  257. DBField: "active",
  258. NameInTable: "Active",
  259. Classes: "d-none d-sm-table-cell",
  260. CallBack: func(values *[]string) string {
  261. return builder.CheckBox(utils.StrToInt((*values)[4]))
  262. },
  263. },
  264. },
  265. func(values *[]string) string {
  266. return builder.DataTableAction(&[]builder.DataTableActionRow{
  267. {
  268. Icon: assets.SysSvgIconView,
  269. Href: `/blog/` + (*values)[2] + `/`,
  270. Hint: "View",
  271. Target: "_blank",
  272. },
  273. {
  274. Icon: assets.SysSvgIconEdit,
  275. Href: "/cp/" + wrap.CurrModule + "/modify/" + (*values)[0] + "/",
  276. Hint: "Edit",
  277. },
  278. {
  279. Icon: assets.SysSvgIconRemove,
  280. Href: "javascript:fave.ActionDataTableDelete(this,'blog-delete','" +
  281. (*values)[0] + "','Are you sure want to delete post?');",
  282. Hint: "Delete",
  283. Classes: "delete",
  284. },
  285. })
  286. },
  287. "/cp/"+wrap.CurrModule+"/",
  288. nil,
  289. nil,
  290. true,
  291. )
  292. } else if wrap.CurrSubModule == "categories" {
  293. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  294. {Name: "Categories", Link: "/cp/" + wrap.CurrModule + "/" + wrap.CurrSubModule + "/"},
  295. {Name: "List of categories"},
  296. })
  297. content += builder.DataTable(
  298. wrap,
  299. "blog_cats",
  300. "id",
  301. "ASC",
  302. &[]builder.DataTableRow{
  303. {
  304. DBField: "id",
  305. },
  306. {
  307. DBField: "user",
  308. },
  309. {
  310. DBField: "name",
  311. NameInTable: "Category",
  312. CallBack: func(values *[]string) string {
  313. depth := utils.StrToInt((*values)[4]) - 1
  314. if depth < 0 {
  315. depth = 0
  316. }
  317. sub := strings.Repeat("&mdash; ", depth)
  318. name := `<a href="/cp/` + wrap.CurrModule + `/categories-modify/` + (*values)[0] + `/">` + sub + html.EscapeString((*values)[2]) + `</a>`
  319. return `<div>` + name + `</div>`
  320. },
  321. },
  322. {
  323. DBField: "alias",
  324. },
  325. {
  326. DBField: "depth",
  327. },
  328. },
  329. func(values *[]string) string {
  330. return builder.DataTableAction(&[]builder.DataTableActionRow{
  331. {
  332. Icon: assets.SysSvgIconView,
  333. Href: `/blog/category/` + (*values)[3] + `/`,
  334. Hint: "View",
  335. Target: "_blank",
  336. },
  337. {
  338. Icon: assets.SysSvgIconEdit,
  339. Href: "/cp/" + wrap.CurrModule + "/categories-modify/" + (*values)[0] + "/",
  340. Hint: "Edit",
  341. },
  342. {
  343. Icon: assets.SysSvgIconRemove,
  344. Href: "javascript:fave.ActionDataTableDelete(this,'blog-categories-delete','" +
  345. (*values)[0] + "','Are you sure want to delete category?');",
  346. Hint: "Delete",
  347. Classes: "delete",
  348. },
  349. })
  350. },
  351. "/cp/"+wrap.CurrModule+"/"+wrap.CurrSubModule+"/",
  352. nil,
  353. func(limit_offset int, pear_page int) (*sqlw.Rows, error) {
  354. return wrap.DB.Query(
  355. `SELECT
  356. node.id,
  357. node.user,
  358. node.name,
  359. node.alias,
  360. (COUNT(parent.id) - 1) AS depth
  361. FROM
  362. blog_cats AS node,
  363. blog_cats AS parent
  364. WHERE
  365. node.lft BETWEEN parent.lft AND parent.rgt AND
  366. node.id > 1
  367. GROUP BY
  368. node.id
  369. ORDER BY
  370. node.lft ASC
  371. ;`,
  372. )
  373. },
  374. false,
  375. )
  376. } else if wrap.CurrSubModule == "add" || wrap.CurrSubModule == "modify" {
  377. if wrap.CurrSubModule == "add" {
  378. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  379. {Name: "Add new post"},
  380. })
  381. } else {
  382. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  383. {Name: "Modify post"},
  384. })
  385. }
  386. data := utils.MySql_blog_post{
  387. A_id: 0,
  388. A_user: 0,
  389. A_name: "",
  390. A_alias: "",
  391. A_category: 0,
  392. A_content: "",
  393. A_datetime: 0,
  394. A_active: 0,
  395. }
  396. if wrap.CurrSubModule == "modify" {
  397. if len(wrap.UrlArgs) != 3 {
  398. return "", "", ""
  399. }
  400. if !utils.IsNumeric(wrap.UrlArgs[2]) {
  401. return "", "", ""
  402. }
  403. err := wrap.DB.QueryRow(`
  404. SELECT
  405. id,
  406. user,
  407. name,
  408. alias,
  409. category,
  410. briefly,
  411. content,
  412. active
  413. FROM
  414. blog_posts
  415. WHERE
  416. id = ?
  417. LIMIT 1;`,
  418. utils.StrToInt(wrap.UrlArgs[2]),
  419. ).Scan(
  420. &data.A_id,
  421. &data.A_user,
  422. &data.A_name,
  423. &data.A_alias,
  424. &data.A_category,
  425. &data.A_briefly,
  426. &data.A_content,
  427. &data.A_active,
  428. )
  429. if *wrap.LogCpError(&err) != nil {
  430. return "", "", ""
  431. }
  432. }
  433. // All post current categories
  434. var selids []int
  435. if data.A_id > 0 {
  436. rows, err := wrap.DB.Query("SELECT category_id FROM blog_cat_post_rel WHERE post_id = ?;", data.A_id)
  437. if err == nil {
  438. defer rows.Close()
  439. values := make([]int, 1)
  440. scan := make([]interface{}, len(values))
  441. for i := range values {
  442. scan[i] = &values[i]
  443. }
  444. for rows.Next() {
  445. err = rows.Scan(scan...)
  446. if *wrap.LogCpError(&err) == nil {
  447. selids = append(selids, int(values[0]))
  448. }
  449. }
  450. }
  451. }
  452. btn_caption := "Add"
  453. if wrap.CurrSubModule == "modify" {
  454. btn_caption = "Save"
  455. }
  456. content += builder.DataForm(wrap, []builder.DataFormField{
  457. {
  458. Kind: builder.DFKHidden,
  459. Name: "action",
  460. Value: "blog-modify",
  461. },
  462. {
  463. Kind: builder.DFKHidden,
  464. Name: "id",
  465. Value: utils.IntToStr(data.A_id),
  466. },
  467. {
  468. Kind: builder.DFKText,
  469. Caption: "Post name",
  470. Name: "name",
  471. Value: data.A_name,
  472. Required: true,
  473. Min: "1",
  474. Max: "255",
  475. },
  476. {
  477. Kind: builder.DFKText,
  478. Caption: "Post alias",
  479. Name: "alias",
  480. Value: data.A_alias,
  481. Hint: "Example: our-news",
  482. Max: "255",
  483. },
  484. {
  485. Kind: builder.DFKText,
  486. Caption: "Category",
  487. Name: "category",
  488. Value: "0",
  489. CallBack: func(field *builder.DataFormField) string {
  490. return `<div class="form-group n4">` +
  491. `<div class="row">` +
  492. `<div class="col-md-3">` +
  493. `<label for="lbl_category">Category</label>` +
  494. `</div>` +
  495. `<div class="col-md-9">` +
  496. `<div>` +
  497. `<select class="selectpicker form-control" id="lbl_category" name="category" data-live-search="true">` +
  498. `<option title="Nothing selected" value="0">&mdash;</option>` +
  499. this.blog_GetCategorySelectOptions(wrap, 0, data.A_category, []int{}) +
  500. `</select>` +
  501. `</div>` +
  502. `</div>` +
  503. `</div>` +
  504. `</div>`
  505. },
  506. },
  507. {
  508. Kind: builder.DFKText,
  509. Caption: "Categories",
  510. Name: "cats",
  511. Value: "0",
  512. CallBack: func(field *builder.DataFormField) string {
  513. return `<div class="form-group n5">` +
  514. `<div class="row">` +
  515. `<div class="col-md-3">` +
  516. `<label for="lbl_parent">Categories</label>` +
  517. `</div>` +
  518. `<div class="col-md-9">` +
  519. `<div>` +
  520. `<select class="selectpicker form-control" id="lbl_cats" name="cats[]" data-live-search="true" multiple>` +
  521. this.blog_GetCategorySelectOptions(wrap, 0, 0, selids) +
  522. `</select>` +
  523. `</div>` +
  524. `</div>` +
  525. `</div>` +
  526. `</div>`
  527. },
  528. },
  529. {
  530. Kind: builder.DFKTextArea,
  531. Caption: "Briefly",
  532. Name: "briefly",
  533. Value: data.A_briefly,
  534. Classes: "briefly wysiwyg",
  535. },
  536. {
  537. Kind: builder.DFKTextArea,
  538. Caption: "Post content",
  539. Name: "content",
  540. Value: data.A_content,
  541. Classes: "wysiwyg",
  542. },
  543. {
  544. Kind: builder.DFKCheckBox,
  545. Caption: "Active",
  546. Name: "active",
  547. Value: utils.IntToStr(data.A_active),
  548. },
  549. {
  550. Kind: builder.DFKSubmit,
  551. Value: btn_caption,
  552. Target: "add-edit-button",
  553. },
  554. })
  555. if wrap.CurrSubModule == "add" {
  556. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Add</button>`
  557. } else {
  558. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Save</button>`
  559. }
  560. } else if wrap.CurrSubModule == "categories-add" || wrap.CurrSubModule == "categories-modify" {
  561. if wrap.CurrSubModule == "categories-add" {
  562. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  563. {Name: "Categories", Link: "/cp/" + wrap.CurrModule + "/categories/"},
  564. {Name: "Add new category"},
  565. })
  566. } else {
  567. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  568. {Name: "Categories", Link: "/cp/" + wrap.CurrModule + "/categories/"},
  569. {Name: "Modify category"},
  570. })
  571. }
  572. data := utils.MySql_blog_category{
  573. A_id: 0,
  574. A_user: 0,
  575. A_name: "",
  576. A_alias: "",
  577. A_lft: 0,
  578. A_rgt: 0,
  579. }
  580. if wrap.CurrSubModule == "categories-modify" {
  581. if len(wrap.UrlArgs) != 3 {
  582. return "", "", ""
  583. }
  584. if !utils.IsNumeric(wrap.UrlArgs[2]) {
  585. return "", "", ""
  586. }
  587. err := wrap.DB.QueryRow(`
  588. SELECT
  589. id,
  590. user,
  591. name,
  592. alias,
  593. lft,
  594. rgt
  595. FROM
  596. blog_cats
  597. WHERE
  598. id = ?
  599. LIMIT 1;`,
  600. utils.StrToInt(wrap.UrlArgs[2]),
  601. ).Scan(
  602. &data.A_id,
  603. &data.A_user,
  604. &data.A_name,
  605. &data.A_alias,
  606. &data.A_lft,
  607. &data.A_rgt,
  608. )
  609. if *wrap.LogCpError(&err) != nil {
  610. return "", "", ""
  611. }
  612. }
  613. btn_caption := "Add"
  614. if wrap.CurrSubModule == "categories-modify" {
  615. btn_caption = "Save"
  616. }
  617. parentId := 0
  618. if wrap.CurrSubModule == "categories-modify" {
  619. parentId = this.blog_GetCategoryParentId(wrap, data.A_id)
  620. }
  621. content += builder.DataForm(wrap, []builder.DataFormField{
  622. {
  623. Kind: builder.DFKHidden,
  624. Name: "action",
  625. Value: "blog-categories-modify",
  626. },
  627. {
  628. Kind: builder.DFKHidden,
  629. Name: "id",
  630. Value: utils.IntToStr(data.A_id),
  631. },
  632. {
  633. Kind: builder.DFKText,
  634. Caption: "Parent",
  635. Name: "parent",
  636. Value: "0",
  637. CallBack: func(field *builder.DataFormField) string {
  638. return `<div class="form-group n2">` +
  639. `<div class="row">` +
  640. `<div class="col-md-3">` +
  641. `<label for="lbl_parent">Parent</label>` +
  642. `</div>` +
  643. `<div class="col-md-9">` +
  644. `<div>` +
  645. `<select class="selectpicker form-control" id="lbl_parent" name="parent" data-live-search="true">` +
  646. `<option title="Nothing selected" value="0">&mdash;</option>` +
  647. this.blog_GetCategorySelectOptions(wrap, data.A_id, parentId, []int{}) +
  648. `</select>` +
  649. `</div>` +
  650. `</div>` +
  651. `</div>` +
  652. `</div>`
  653. },
  654. },
  655. {
  656. Kind: builder.DFKText,
  657. Caption: "Name",
  658. Name: "name",
  659. Value: data.A_name,
  660. Required: true,
  661. Min: "1",
  662. Max: "255",
  663. },
  664. {
  665. Kind: builder.DFKText,
  666. Caption: "Alias",
  667. Name: "alias",
  668. Value: data.A_alias,
  669. Hint: "Example: popular-posts",
  670. Max: "255",
  671. },
  672. {
  673. Kind: builder.DFKSubmit,
  674. Value: btn_caption,
  675. Target: "add-edit-button",
  676. },
  677. })
  678. if wrap.CurrSubModule == "categories-add" {
  679. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Add</button>`
  680. } else {
  681. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Save</button>`
  682. }
  683. }
  684. return this.getSidebarModules(wrap), content, sidebar
  685. })
  686. }