module_blog.go 18 KB

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