module_blog.go 18 KB

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