Fork de wikipp, le moteur de wiki en c++, basé sur cppcms. Le fork ajoute la langue française
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.

page.cpp 10.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. #include "page.h"
  2. #include "page_content.h"
  3. #include "wiki.h"
  4. #include "diff.h"
  5. #include <booster/posix_time.h>
  6. #include <cppcms/url_dispatcher.h>
  7. #include <cppcms/cache_interface.h>
  8. #define _(X) ::cppcms::locale::translate(X)
  9. namespace content {
  10. // Page content
  11. page_form::page_form(apps::wiki *_w):
  12. w(_w)
  13. {
  14. title.message(_("Title"));
  15. content.message(_("Content"));
  16. sidebar.message(_("Sidebar"));
  17. save.value(_("Save"));
  18. save_cont.value(_("Save and Continue"));
  19. preview.value(_("Preview"));
  20. fields.add(title);
  21. fields.add(content);
  22. fields.add(sidebar);
  23. buttons.add(save);
  24. buttons.add(save_cont);
  25. buttons.add(preview);
  26. buttons.add(users_only);
  27. add(fields);
  28. add(buttons);
  29. users_only.help(_("Disable editing by visitors"));
  30. users_only.error_message(_("Please Login"));
  31. title.non_empty();
  32. content.non_empty();
  33. content.rows(25);
  34. content.cols(60);
  35. sidebar.rows(10);
  36. sidebar.cols(60);
  37. }
  38. bool page_form::validate()
  39. {
  40. bool res=form::validate();
  41. if(users_only.value() && !w->users.auth()) {
  42. users_only.valid(false);
  43. users_only.value(false);
  44. return false;
  45. }
  46. return res;
  47. }
  48. } // namespace content
  49. namespace apps {
  50. page::page(wiki &w):
  51. master(w)
  52. {
  53. wi.dispatcher().assign("^/page/(\\w+)/version/(\\d+)$",&page::display_ver,this,1,2);
  54. wi.dispatcher().assign("^/page/(\\w+)/?$",&page::display,this,1);
  55. wi.dispatcher().assign("^/page/(\\w+)/edit(/version/(\\d+))?$",&page::edit,this,1,3);
  56. wi.dispatcher().assign("^/page/(\\w+)/history(/|/(\\d+))?$",&page::history,this,1,3);
  57. wi.dispatcher().assign("^/page/(\\w+)/diff/(\\d+)vs(\\d+)/?$",&page::diff,this,1,2,3);
  58. }
  59. std::string page::diff_url(int v1,int v2,std::string l,std::string s)
  60. {
  61. if(l.empty()) l=locale_name;
  62. if(s.empty()) s=slug;
  63. return wi.root(l) +
  64. (booster::locale::format("/page/{1}/diff/{2}vs{3}") % s % v1 % v2).str();
  65. }
  66. std::string page::page_url(std::string l,std::string s)
  67. {
  68. if(l.empty()) l=locale_name;
  69. if(s.empty()) s=slug;
  70. return wi.root(l)+"/page/"+s;
  71. }
  72. std::string page::page_version_url(int ver,std::string l,std::string s)
  73. {
  74. if(l.empty()) l=locale_name;
  75. if(s.empty()) s=slug;
  76. return wi.root(l)+
  77. (booster::locale::format("/page/{1}/version/{2}") % s % ver).str();
  78. }
  79. std::string page::edit_url()
  80. {
  81. return wi.root()+"/page/"+slug+"/edit";
  82. }
  83. std::string page::edit_version_url(int ver)
  84. {
  85. return (booster::locale::format(edit_url()+"/version/{1}") % ver).str();
  86. }
  87. std::string page::history_url(int n)
  88. {
  89. std::string u=wi.root()+"/page/"+slug+"/history/";
  90. if(n)
  91. u+=(booster::locale::format("{1}") % n).str();
  92. return u;
  93. }
  94. void page::diff(std::string slug,std::string sv1,std::string sv2)
  95. {
  96. int v1=atoi(sv1.c_str()), v2=atoi(sv2.c_str());
  97. this->slug=slug;
  98. cppdb::session sql(conn);
  99. cppdb::result r;
  100. content::diff c;
  101. c.v1=v1;
  102. c.v2=v2;
  103. c.edit_v1=edit_version_url(v1);
  104. c.edit_v2=edit_version_url(v2);
  105. r=sql<< "SELECT version,history.title,history.content,history.sidebar,pages.title FROM pages "
  106. "JOIN history ON pages.id=history.id "
  107. "WHERE lang=? AND slug=? AND version IN (?,?) " << locale_name << slug << v1 << v2;
  108. std::string t1,c1,s1,t2,c2,s2;
  109. int count=0;
  110. while(r.next()) {
  111. int ver;
  112. r>>ver;
  113. if(ver==v1) {
  114. r>>t1>>c1>>s1>>c.title;
  115. }
  116. else {
  117. r>>t2>>c2>>s2;
  118. }
  119. count++;
  120. }
  121. if(count != 2) {
  122. c.no_versions=true;
  123. master::ini(c);
  124. render("diff",c);
  125. return;
  126. }
  127. if(t1!=t2) {
  128. c.title_diff=true;
  129. c.title_1=t1;
  130. c.title_2=t2;
  131. }
  132. else {
  133. c.title=t1;
  134. }
  135. if(c1!=c2) {
  136. c.content_diff=true;
  137. std::vector<std::string> X=split(c1);
  138. std::vector<std::string> Y=split(c2);
  139. diff::diff(X,Y,c.content_diff_content);
  140. }
  141. if(s1!=s2) {
  142. c.sidebar_diff=true;
  143. std::vector<std::string> X=split(s1);
  144. std::vector<std::string> Y=split(s2);
  145. diff::diff(X,Y,c.sidebar_diff_content);
  146. }
  147. if(t1==t2 && c1==c2 && s1==s2)
  148. c.no_diff=true;
  149. master::ini(c);
  150. render("diff",c);
  151. }
  152. void page::history(std::string slug,std::string page)
  153. {
  154. this->slug=slug;
  155. unsigned const vers=10;
  156. int offset;
  157. content::history c;
  158. master::ini(c);
  159. if(page.empty())
  160. offset=0;
  161. else
  162. offset=atoi(page.c_str());
  163. cppdb::session sql(conn);
  164. cppdb::result r;
  165. r=sql<< "SELECT title,id FROM pages "
  166. "WHERE pages.lang=? AND pages.slug=? " << locale_name << slug << cppdb::row;
  167. if(r.empty()) {
  168. redirect(locale_name);
  169. return;
  170. }
  171. int id;
  172. r>>c.title>>id;
  173. r=sql<< "SELECT created,version,author FROM history "
  174. "WHERE id=? "
  175. "ORDER BY version DESC "
  176. "LIMIT ? "
  177. "OFFSET ?"
  178. <<id << vers+1 << offset*vers;
  179. c.hist.reserve(vers);
  180. for(unsigned i=0;r.next() && i<vers;i++) {
  181. int ver;
  182. c.hist.resize(i+1);
  183. std::tm update = std::tm();
  184. r>> update >> ver >> c.hist[i].author ;
  185. c.hist[i].update = mktime(&update);
  186. c.hist[i].version=ver;
  187. c.hist[i].show_url=page_version_url(ver);
  188. c.hist[i].edit_url=edit_version_url(ver);
  189. if(ver>1)
  190. c.hist[i].diff_url=diff_url(ver-1,ver);
  191. }
  192. if(r.next()) {
  193. c.page=history_url(offset+1);
  194. }
  195. c.page_link=page_url();
  196. render("history",c);
  197. }
  198. void page::display(std::string slug)
  199. {
  200. this->slug=slug;
  201. std::string key="article_"+locale_name+":"+slug;
  202. if(cache().fetch_page(key))
  203. return;
  204. content::page c;
  205. cppdb::session sql(conn);
  206. cppdb::result r;
  207. r=sql<< "SELECT title,content,sidebar FROM pages WHERE lang=? AND slug=?"
  208. <<locale_name << slug << cppdb::row;
  209. if(r.empty()) {
  210. std::string redirect=edit_url();
  211. std::cerr << " Page " << redirect << std::endl;
  212. response().set_redirect_header(redirect);
  213. return;
  214. }
  215. ini(c);
  216. r >> c.title >> c.content >> c.sidebar;
  217. render("page",c);
  218. cache().store_page(key);
  219. }
  220. void page::ini(content::page &c)
  221. {
  222. master::ini(c);
  223. c.edit_link=edit_url();
  224. c.history_link=history_url();
  225. }
  226. void page::edit(std::string slug,std::string version)
  227. {
  228. this->slug=slug;
  229. content::edit_page c(&wi);
  230. if(request().request_method()=="POST") {
  231. if(!edit_on_post(c))
  232. return;
  233. }
  234. else {
  235. if(version.empty()) {
  236. c.new_page=!load(c.form);
  237. if(c.new_page) {
  238. wi.options.load();
  239. c.form.users_only.value(wi.options.global.users_only_edit);
  240. }
  241. }
  242. else {
  243. int ver=atoi(version.c_str());
  244. if(!load_history(ver,c.form)) {
  245. redirect(locale_name,slug);
  246. return;
  247. }
  248. }
  249. if(c.form.users_only.value() && !wi.users.auth()) {
  250. wi.users.error_forbidden();
  251. }
  252. }
  253. ini(c);
  254. c.back=page_url();
  255. c.submit=edit_url();
  256. render("edit_page",c);
  257. }
  258. bool page::load(content::page_form &form)
  259. {
  260. cppdb::session sql(conn);
  261. cppdb::result r;
  262. r=sql<< "SELECT title,content,sidebar,users_only "
  263. "FROM pages WHERE lang=? AND slug=?" << locale_name << slug << cppdb::row;
  264. if(!r.empty()) {
  265. std::string title,content,sidebar;
  266. int users_only;
  267. r >> title >> content >> sidebar >> users_only;
  268. form.title.value(title);
  269. form.content.value(content);
  270. form.sidebar.value(sidebar);
  271. form.users_only.value(users_only);
  272. return true;
  273. }
  274. wi.options.load();
  275. form.users_only.set(wi.options.global.users_only_edit);
  276. return false;
  277. }
  278. void page::redirect(std::string loc,std::string slug)
  279. {
  280. std::string redirect=page_url(loc,slug);
  281. response().set_redirect_header(redirect);
  282. }
  283. void page::save(int id,content::page_form &form,cppdb::session &sql)
  284. {
  285. std::tm t = booster::ptime::local_time(booster::ptime::now());
  286. wi.users.auth();
  287. if(id!=-1) {
  288. sql<< "UPDATE pages SET content=?,title=?,sidebar=?,users_only=? "
  289. "WHERE lang=? AND slug=?"
  290. << form.content.value() << form.title.value()
  291. << form.sidebar.value() << form.users_only.value()
  292. << locale_name << slug << cppdb::exec;
  293. }
  294. else {
  295. cppdb::statement s;
  296. s=sql<< "INSERT INTO pages(lang,slug,title,content,sidebar,users_only) "
  297. "VALUES(?,?,?,?,?,?)"
  298. << locale_name << slug
  299. << form.title.value()
  300. << form.content.value()
  301. << form.sidebar.value()
  302. << form.users_only.value();
  303. s.exec();
  304. id=s.sequence_last("pages_id_seq");
  305. }
  306. sql<< "INSERT INTO history(id,version,created,title,content,sidebar,author) "
  307. "SELECT ?,"
  308. " (SELECT COALESCE(MAX(version),0)+1 FROM history WHERE id=?),"
  309. " ?,?,?,?,?"
  310. << id << id << t
  311. << form.title.value()
  312. << form.content.value()
  313. << form.sidebar.value()
  314. << wi.users.username
  315. << cppdb::exec;
  316. }
  317. bool page::edit_on_post(content::edit_page &c)
  318. {
  319. wi.options.load();
  320. cppdb::session sql(conn);
  321. cppdb::transaction tr(sql);
  322. cppdb::result r;
  323. r=sql<< "SELECT id,users_only FROM pages WHERE lang=? and slug=?" << locale_name << slug << cppdb::row;
  324. int id=-1,users_only=wi.options.global.users_only_edit;
  325. if(!r.empty()) {
  326. r>>id>>users_only;
  327. }
  328. r.clear();
  329. if(users_only && !wi.users.auth()) {
  330. wi.users.error_forbidden();
  331. return false;
  332. }
  333. c.form.load(context());
  334. if(c.form.validate()) {
  335. if(c.form.save.value() || c.form.save_cont.value()) {
  336. save(id,c.form,sql);
  337. cache().rise("article_"+locale_name+":"+slug);
  338. }
  339. if(c.form.save.value()) {
  340. redirect(locale_name,slug);
  341. tr.commit();
  342. return false;
  343. }
  344. if(c.form.preview.value()) {
  345. c.title=c.form.title.value();
  346. c.content=c.form.content.value();
  347. c.sidebar=c.form.sidebar.value();
  348. }
  349. }
  350. tr.commit();
  351. return true;
  352. }
  353. bool page::load_history(int ver,content::page_form &form)
  354. {
  355. cppdb::session sql(conn);
  356. cppdb::result r;
  357. r=sql<< "SELECT history.title,history.content,history.sidebar,pages.users_only "
  358. "FROM pages "
  359. "JOIN history ON pages.id=history.id "
  360. "WHERE pages.lang=? AND pages.slug=? AND history.version=?"
  361. <<locale_name<<slug<<ver<<cppdb::row;
  362. if(!r.empty()) {
  363. std::string title,content,sidebar;
  364. int uonly;
  365. r >> title >> content >> sidebar >> uonly;
  366. form.title.value(title);
  367. form.content.value(content);
  368. form.sidebar.value(sidebar);
  369. form.users_only.value(uonly);
  370. return true;
  371. }
  372. return false;
  373. }
  374. void page::display_ver(std::string slug,std::string sid)
  375. {
  376. this->slug=slug;
  377. content::page_hist c;
  378. int id=atoi(sid.c_str());
  379. cppdb::result r;
  380. cppdb::session sql(conn);
  381. r=sql<< "SELECT history.title,history.content,history.sidebar,history.created "
  382. "FROM pages "
  383. "JOIN history ON pages.id=history.id "
  384. "WHERE pages.lang=? AND pages.slug=? AND history.version=?"
  385. <<locale_name << slug << id << cppdb::row;
  386. if(!r.empty()) {
  387. redirect(locale_name,slug);
  388. return;
  389. }
  390. std::tm date;
  391. r>>c.title>>c.content>>c.sidebar>>date;
  392. c.date = mktime(&date);
  393. c.version=id;
  394. c.rollback=edit_version_url(id);
  395. ini(c);
  396. render("page_hist",c);
  397. }
  398. }