module_index.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. package modules
  2. import (
  3. "database/sql"
  4. _ "github.com/go-sql-driver/mysql"
  5. "fmt"
  6. "html"
  7. "os"
  8. "strconv"
  9. "golang-fave/assets"
  10. "golang-fave/consts"
  11. "golang-fave/engine/builder"
  12. "golang-fave/engine/wrapper"
  13. "golang-fave/utils"
  14. )
  15. func (this *Modules) RegisterModule_Index() *Module {
  16. return this.newModule(MInfo{
  17. WantDB: true,
  18. Mount: "index",
  19. Name: "Pages",
  20. Order: 0,
  21. Icon: assets.SysSvgIconPage,
  22. Sub: &[]MISub{
  23. {Mount: "default", Name: "List of Pages", Show: true, Icon: assets.SysSvgIconList},
  24. {Mount: "add", Name: "Add New Page", Show: true, Icon: assets.SysSvgIconPlus},
  25. {Mount: "modify", Name: "Modify Page", Show: false},
  26. },
  27. }, func(wrap *wrapper.Wrapper) {
  28. // Front-end
  29. wrap.RenderFrontEnd("index", consts.TmplDataModIndex{
  30. MetaTitle: "Meta Title",
  31. MetaKeywords: "Meta Keywords",
  32. MetaDescription: "Meta Description",
  33. MainMenuItems: []consts.TmplDataMainMenuItem{
  34. {Name: "Home", Link: "/", Active: true},
  35. {Name: "Item 1", Link: "/#1", Active: false},
  36. {Name: "Item 2", Link: "/#2", Active: false},
  37. {Name: "Item 3", Link: "/#3", Active: false},
  38. },
  39. })
  40. }, func(wrap *wrapper.Wrapper) (string, string, string) {
  41. content := ""
  42. sidebar := ""
  43. if wrap.CurrSubModule == "" || wrap.CurrSubModule == "default" {
  44. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  45. {Name: "List of Pages"},
  46. })
  47. content += builder.DataTable(wrap, "pages", "id", "DESC", &[]builder.DataTableRow{
  48. {
  49. DBField: "id",
  50. },
  51. {
  52. DBField: "name",
  53. NameInTable: "Page / Alias",
  54. CallBack: func(values *[]string) string {
  55. name := `<a href="/cp/` + wrap.CurrModule + `/modify/` + (*values)[0] + `/">` + html.EscapeString((*values)[1]) + `</a>`
  56. alias := html.EscapeString((*values)[2])
  57. return `<div>` + name + `</div><div><small>` + alias + `</small></div>`
  58. },
  59. },
  60. {
  61. DBField: "alias",
  62. },
  63. {
  64. DBField: "datetime",
  65. DBExp: "UNIX_TIMESTAMP(`datetime`)",
  66. NameInTable: "Date / Time",
  67. CallBack: func(values *[]string) string {
  68. t := int64(utils.StrToInt((*values)[3]))
  69. return `<div>` + utils.UnixTimestampToFormat(t, "02.01.2006") + `</div>` +
  70. `<div><small>` + utils.UnixTimestampToFormat(t, "15:04:05") + `</small></div>`
  71. },
  72. },
  73. {
  74. DBField: "active",
  75. NameInTable: "Active",
  76. CallBack: func(values *[]string) string {
  77. return builder.CheckBox(utils.StrToInt((*values)[4]))
  78. },
  79. },
  80. }, func(values *[]string) string {
  81. return builder.DataTableAction(&[]builder.DataTableActionRow{
  82. {
  83. Icon: assets.SysSvgIconView,
  84. Href: (*values)[2],
  85. Hint: "View",
  86. Target: "_blank",
  87. },
  88. {
  89. Icon: assets.SysSvgIconEdit,
  90. Href: "/cp/" + wrap.CurrModule + "/modify/" + (*values)[0] + "/",
  91. Hint: "Edit",
  92. },
  93. {
  94. Icon: assets.SysSvgIconRemove,
  95. Href: "javascript:fave.ActionDataTableDelete(this,'index-delete','" +
  96. (*values)[0] + "','Are you sure want to delete page?');",
  97. Hint: "Delete",
  98. },
  99. })
  100. }, "/cp/"+wrap.CurrModule+"/")
  101. } else if wrap.CurrSubModule == "add" || wrap.CurrSubModule == "modify" {
  102. if wrap.CurrSubModule == "add" {
  103. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  104. {Name: "Add New Page"},
  105. })
  106. } else {
  107. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  108. {Name: "Modify Page"},
  109. })
  110. }
  111. data := utils.MySql_page{
  112. A_id: 0,
  113. A_user: 0,
  114. A_name: "",
  115. A_alias: "",
  116. A_content: "",
  117. A_meta_title: "",
  118. A_meta_keywords: "",
  119. A_meta_description: "",
  120. A_datetime: 0,
  121. A_active: 0,
  122. }
  123. if wrap.CurrSubModule == "modify" {
  124. if len(wrap.UrlArgs) != 3 {
  125. return "", "", ""
  126. }
  127. if !utils.IsNumeric(wrap.UrlArgs[2]) {
  128. return "", "", ""
  129. }
  130. err := wrap.DB.QueryRow(`
  131. SELECT
  132. id,
  133. user,
  134. name,
  135. alias,
  136. content,
  137. meta_title,
  138. meta_keywords,
  139. meta_description,
  140. active
  141. FROM
  142. pages
  143. WHERE
  144. id = ?
  145. LIMIT 1;`,
  146. utils.StrToInt(wrap.UrlArgs[2]),
  147. ).Scan(
  148. &data.A_id,
  149. &data.A_user,
  150. &data.A_name,
  151. &data.A_alias,
  152. &data.A_content,
  153. &data.A_meta_title,
  154. &data.A_meta_keywords,
  155. &data.A_meta_description,
  156. &data.A_active,
  157. )
  158. if err != nil {
  159. return "", "", ""
  160. }
  161. }
  162. content += builder.DataForm(wrap, []builder.DataFormField{
  163. {
  164. Kind: builder.DFKHidden,
  165. Name: "action",
  166. Value: "index-modify",
  167. },
  168. {
  169. Kind: builder.DFKHidden,
  170. Name: "id",
  171. Value: utils.IntToStr(data.A_id),
  172. },
  173. {
  174. Kind: builder.DFKText,
  175. Caption: "Page Name",
  176. Name: "name",
  177. Value: data.A_name,
  178. },
  179. {
  180. Kind: builder.DFKText,
  181. Caption: "Page Alias",
  182. Name: "alias",
  183. Value: data.A_alias,
  184. Hint: "Example: /about-us/ or /about-us.html or /about/team.html",
  185. },
  186. {
  187. Kind: builder.DFKTextArea,
  188. Caption: "Page Content",
  189. Name: "content",
  190. Value: data.A_content,
  191. },
  192. {
  193. Kind: builder.DFKText,
  194. Caption: "Meta Title",
  195. Name: "meta_title",
  196. Value: data.A_meta_title,
  197. },
  198. {
  199. Kind: builder.DFKText,
  200. Caption: "Meta Keywords",
  201. Name: "meta_keywords",
  202. Value: data.A_meta_keywords,
  203. },
  204. {
  205. Kind: builder.DFKTextArea,
  206. Caption: "Meta Description",
  207. Name: "meta_description",
  208. Value: data.A_meta_description,
  209. },
  210. {
  211. Kind: builder.DFKCheckBox,
  212. Caption: "Active",
  213. Name: "active",
  214. Value: utils.IntToStr(data.A_active),
  215. },
  216. {
  217. Kind: builder.DFKMessage,
  218. },
  219. {
  220. Kind: builder.DFKSubmit,
  221. Value: "Add",
  222. Target: "add-edit-button",
  223. },
  224. })
  225. if wrap.CurrSubModule == "add" {
  226. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Add</button>`
  227. } else {
  228. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Save</button>`
  229. }
  230. }
  231. return this.getSidebarModules(wrap), content, sidebar
  232. })
  233. }
  234. func (this *Modules) RegisterAction_IndexModify() *Action {
  235. return this.newAction(AInfo{
  236. WantDB: true,
  237. Mount: "index-modify",
  238. WantAdmin: true,
  239. }, func(wrap *wrapper.Wrapper) {
  240. pf_id := wrap.R.FormValue("id")
  241. pf_name := wrap.R.FormValue("name")
  242. pf_alias := wrap.R.FormValue("alias")
  243. pf_content := wrap.R.FormValue("content")
  244. pf_meta_title := wrap.R.FormValue("meta_title")
  245. pf_meta_keywords := wrap.R.FormValue("meta_keywords")
  246. pf_meta_description := wrap.R.FormValue("meta_description")
  247. pf_active := wrap.R.FormValue("active")
  248. if pf_active == "" {
  249. pf_active = "0"
  250. }
  251. if !utils.IsNumeric(pf_id) {
  252. wrap.MsgError(`Inner system error`)
  253. return
  254. }
  255. if pf_name == "" {
  256. wrap.MsgError(`Please specify page name`)
  257. return
  258. }
  259. if pf_alias == "" {
  260. pf_alias = utils.GenerateAlias(pf_name)
  261. }
  262. if !utils.IsValidAlias(pf_alias) {
  263. wrap.MsgError(`Please specify correct page alias`)
  264. return
  265. }
  266. if pf_id == "0" {
  267. // Add new page
  268. _, err := wrap.DB.Query(
  269. `INSERT INTO pages SET
  270. user = ?,
  271. name = ?,
  272. alias = ?,
  273. content = ?,
  274. meta_title = ?,
  275. meta_keywords = ?,
  276. meta_description = ?,
  277. datetime = ?,
  278. active = ?
  279. ;`,
  280. wrap.User.A_id,
  281. pf_name,
  282. pf_alias,
  283. pf_content,
  284. pf_meta_title,
  285. pf_meta_keywords,
  286. pf_meta_description,
  287. utils.UnixTimestampToMySqlDateTime(utils.GetCurrentUnixTimestamp()),
  288. pf_active,
  289. )
  290. if err != nil {
  291. wrap.MsgError(err.Error())
  292. return
  293. }
  294. wrap.Write(`window.location='/cp/';`)
  295. } else {
  296. // Update page
  297. _, err := wrap.DB.Query(
  298. `UPDATE pages SET
  299. name = ?,
  300. alias = ?,
  301. content = ?,
  302. meta_title = ?,
  303. meta_keywords = ?,
  304. meta_description = ?,
  305. active = ?
  306. WHERE
  307. id = ?
  308. ;`,
  309. pf_name,
  310. pf_alias,
  311. pf_content,
  312. pf_meta_title,
  313. pf_meta_keywords,
  314. pf_meta_description,
  315. pf_active,
  316. utils.StrToInt(pf_id),
  317. )
  318. if err != nil {
  319. wrap.MsgError(err.Error())
  320. return
  321. }
  322. wrap.Write(`window.location='/cp/index/modify/` + pf_id + `/';`)
  323. }
  324. })
  325. }
  326. func (this *Modules) RegisterAction_IndexDelete() *Action {
  327. return this.newAction(AInfo{
  328. WantDB: true,
  329. Mount: "index-delete",
  330. WantAdmin: true,
  331. }, func(wrap *wrapper.Wrapper) {
  332. pf_id := wrap.R.FormValue("id")
  333. if !utils.IsNumeric(pf_id) {
  334. wrap.MsgError(`Inner system error`)
  335. return
  336. }
  337. // Delete page
  338. _, err := wrap.DB.Query(
  339. `DELETE FROM pages WHERE id = ?;`,
  340. utils.StrToInt(pf_id),
  341. )
  342. if err != nil {
  343. wrap.MsgError(err.Error())
  344. return
  345. }
  346. // Reload current page
  347. wrap.Write(`window.location.reload(false);`)
  348. })
  349. }
  350. func (this *Modules) RegisterAction_IndexMysqlSetup() *Action {
  351. return this.newAction(AInfo{
  352. WantDB: false,
  353. Mount: "index-mysql-setup",
  354. }, func(wrap *wrapper.Wrapper) {
  355. pf_host := wrap.R.FormValue("host")
  356. pf_port := wrap.R.FormValue("port")
  357. pf_name := wrap.R.FormValue("name")
  358. pf_user := wrap.R.FormValue("user")
  359. pf_password := wrap.R.FormValue("password")
  360. if pf_host == "" {
  361. wrap.MsgError(`Please specify host for MySQL connection`)
  362. return
  363. }
  364. if pf_port == "" {
  365. wrap.MsgError(`Please specify host port for MySQL connection`)
  366. return
  367. }
  368. if _, err := strconv.Atoi(pf_port); err != nil {
  369. wrap.MsgError(`MySQL host port must be integer number`)
  370. return
  371. }
  372. if pf_name == "" {
  373. wrap.MsgError(`Please specify MySQL database name`)
  374. return
  375. }
  376. if pf_user == "" {
  377. wrap.MsgError(`Please specify MySQL user`)
  378. return
  379. }
  380. // Try connect to mysql
  381. db, err := sql.Open("mysql", pf_user+":"+pf_password+"@tcp("+pf_host+":"+pf_port+")/"+pf_name)
  382. if err != nil {
  383. wrap.MsgError(err.Error())
  384. return
  385. }
  386. defer db.Close()
  387. err = db.Ping()
  388. if err != nil {
  389. wrap.MsgError(err.Error())
  390. return
  391. }
  392. // Try to install all tables
  393. _, err = db.Query(fmt.Sprintf(
  394. `CREATE TABLE %s.users (
  395. id int(11) NOT NULL AUTO_INCREMENT COMMENT 'AI',
  396. first_name VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'User first name',
  397. last_name VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'User last name',
  398. email VARCHAR(64) NOT NULL COMMENT 'User email',
  399. password VARCHAR(32) NOT NULL COMMENT 'User password (MD5)',
  400. admin int(1) NOT NULL COMMENT 'Is admin user or not',
  401. active int(1) NOT NULL COMMENT 'Is active user or not',
  402. PRIMARY KEY (id)
  403. ) ENGINE = InnoDB;`,
  404. pf_name))
  405. if err != nil {
  406. wrap.MsgError(err.Error())
  407. return
  408. }
  409. _, err = db.Query(fmt.Sprintf(
  410. `CREATE TABLE %s.pages (
  411. id int(11) NOT NULL AUTO_INCREMENT COMMENT 'AI',
  412. user int(11) NOT NULL COMMENT 'User id',
  413. name varchar(255) NOT NULL COMMENT 'Page name',
  414. alias varchar(255) NOT NULL COMMENT 'Page url part',
  415. content text NOT NULL COMMENT 'Page content',
  416. meta_title varchar(255) NOT NULL DEFAULT '' COMMENT 'Page meta title',
  417. meta_keywords varchar(255) NOT NULL DEFAULT '' COMMENT 'Page meta keywords',
  418. meta_description varchar(510) NOT NULL DEFAULT '' COMMENT 'Page meta description',
  419. datetime datetime NOT NULL COMMENT 'Creation date/time',
  420. active int(1) NOT NULL COMMENT 'Is active page or not',
  421. PRIMARY KEY (id)
  422. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`,
  423. pf_name))
  424. if err != nil {
  425. wrap.MsgError(err.Error())
  426. return
  427. }
  428. _, err = db.Query(fmt.Sprintf(
  429. `ALTER TABLE %s.pages ADD UNIQUE KEY alias (alias);`,
  430. pf_name))
  431. if err != nil {
  432. wrap.MsgError(err.Error())
  433. return
  434. }
  435. // Save mysql config file
  436. err = utils.MySqlConfigWrite(wrap.DConfig+string(os.PathSeparator)+"mysql.json", pf_host, pf_port, pf_name, pf_user, pf_password)
  437. if err != nil {
  438. wrap.MsgError(err.Error())
  439. return
  440. }
  441. // Reload current page
  442. wrap.Write(`window.location.reload(false);`)
  443. })
  444. }
  445. func (this *Modules) RegisterAction_IndexFirstUser() *Action {
  446. return this.newAction(AInfo{
  447. WantDB: true,
  448. Mount: "index-first-user",
  449. }, func(wrap *wrapper.Wrapper) {
  450. pf_first_name := wrap.R.FormValue("first_name")
  451. pf_last_name := wrap.R.FormValue("last_name")
  452. pf_email := wrap.R.FormValue("email")
  453. pf_password := wrap.R.FormValue("password")
  454. if pf_email == "" {
  455. wrap.MsgError(`Please specify user email`)
  456. return
  457. }
  458. if !utils.IsValidEmail(pf_email) {
  459. wrap.MsgError(`Please specify correct user email`)
  460. return
  461. }
  462. if pf_password == "" {
  463. wrap.MsgError(`Please specify user password`)
  464. return
  465. }
  466. _, err := wrap.DB.Query(
  467. `INSERT INTO users SET
  468. first_name = ?,
  469. last_name = ?,
  470. email = ?,
  471. password = MD5(?),
  472. admin = 1,
  473. active = 1
  474. ;`,
  475. pf_first_name,
  476. pf_last_name,
  477. pf_email,
  478. pf_password,
  479. )
  480. if err != nil {
  481. wrap.MsgError(err.Error())
  482. return
  483. }
  484. // Reload current page
  485. wrap.Write(`window.location.reload(false);`)
  486. })
  487. }
  488. func (this *Modules) RegisterAction_IndexUserSignIn() *Action {
  489. return this.newAction(AInfo{
  490. WantDB: true,
  491. Mount: "index-user-sign-in",
  492. }, func(wrap *wrapper.Wrapper) {
  493. pf_email := wrap.R.FormValue("email")
  494. pf_password := wrap.R.FormValue("password")
  495. if pf_email == "" {
  496. wrap.MsgError(`Please specify user email`)
  497. return
  498. }
  499. if !utils.IsValidEmail(pf_email) {
  500. wrap.MsgError(`Please specify correct user email`)
  501. return
  502. }
  503. if pf_password == "" {
  504. wrap.MsgError(`Please specify user password`)
  505. return
  506. }
  507. if wrap.S.GetInt("UserId", 0) > 0 {
  508. wrap.MsgError(`You already logined`)
  509. return
  510. }
  511. var user_id int
  512. err := wrap.DB.QueryRow(
  513. `SELECT
  514. id
  515. FROM
  516. users
  517. WHERE
  518. email = ? and
  519. password = MD5(?) and
  520. admin = 1 and
  521. active = 1
  522. LIMIT 1;`,
  523. pf_email,
  524. pf_password,
  525. ).Scan(
  526. &user_id,
  527. )
  528. if err != nil && err != sql.ErrNoRows {
  529. wrap.MsgError(err.Error())
  530. return
  531. }
  532. if err == sql.ErrNoRows {
  533. wrap.MsgError(`Incorrect email or password`)
  534. return
  535. }
  536. // Save to current session
  537. wrap.S.SetInt("UserId", user_id)
  538. // Reload current page
  539. wrap.Write(`window.location.reload(false);`)
  540. })
  541. }
  542. func (this *Modules) RegisterAction_IndexUserLogout() *Action {
  543. return this.newAction(AInfo{
  544. WantDB: true,
  545. Mount: "index-user-logout",
  546. WantUser: true,
  547. }, func(wrap *wrapper.Wrapper) {
  548. // Reset session var
  549. wrap.S.SetInt("UserId", 0)
  550. // Reload current page
  551. wrap.Write(`window.location.reload(false);`)
  552. })
  553. }
  554. func (this *Modules) RegisterAction_IndexUserUpdateProfile() *Action {
  555. return this.newAction(AInfo{
  556. WantDB: true,
  557. Mount: "index-user-update-profile",
  558. WantUser: true,
  559. }, func(wrap *wrapper.Wrapper) {
  560. pf_first_name := wrap.R.FormValue("first_name")
  561. pf_last_name := wrap.R.FormValue("last_name")
  562. pf_email := wrap.R.FormValue("email")
  563. pf_password := wrap.R.FormValue("password")
  564. if pf_email == "" {
  565. wrap.MsgError(`Please specify user email`)
  566. return
  567. }
  568. if !utils.IsValidEmail(pf_email) {
  569. wrap.MsgError(`Please specify correct user email`)
  570. return
  571. }
  572. if pf_password != "" {
  573. // Update with password if set
  574. _, err := wrap.DB.Query(
  575. `UPDATE users SET
  576. first_name = ?,
  577. last_name = ?,
  578. email = ?,
  579. password = MD5(?)
  580. WHERE
  581. id = ?
  582. ;`,
  583. pf_first_name,
  584. pf_last_name,
  585. pf_email,
  586. pf_password,
  587. wrap.User.A_id,
  588. )
  589. if err != nil {
  590. wrap.MsgError(err.Error())
  591. return
  592. }
  593. } else {
  594. // Update without password if not set
  595. _, err := wrap.DB.Query(
  596. `UPDATE users SET
  597. first_name = ?,
  598. last_name = ?,
  599. email = ?
  600. WHERE
  601. id = ?
  602. ;`,
  603. pf_first_name,
  604. pf_last_name,
  605. pf_email,
  606. wrap.User.A_id,
  607. )
  608. if err != nil {
  609. wrap.MsgError(err.Error())
  610. return
  611. }
  612. }
  613. // Reload current page
  614. wrap.Write(`window.location.reload(false);`)
  615. })
  616. }