1 /*
2  * Copyright (C) 2009 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 
7 #include "CommentView.h"
8 
9 #include "../model/BlogSession.h"
10 #include "../model/Comment.h"
11 #include "../model/User.h"
12 
13 #include <Wt/WContainerWidget.h>
14 #include <Wt/WPushButton.h>
15 #include <Wt/WTemplate.h>
16 #include <Wt/WText.h>
17 #include <Wt/WTextArea.h>
18 
19 namespace dbo = Wt::Dbo;
20 
CommentView(BlogSession & session,dbo::ptr<Comment> comment)21 CommentView::CommentView(BlogSession& session, dbo::ptr<Comment> comment)
22   : session_(session),
23     comment_(comment)
24 {
25   comment_ = comment;
26 
27   renderView();
28 }
29 
CommentView(BlogSession & session,long long parentId)30 CommentView::CommentView(BlogSession& session, long long parentId)
31   : session_(session)
32 {
33   dbo::ptr<Comment> parent = session_.load<Comment>(parentId);
34 
35   comment_ = std::make_unique<Comment>();
36   comment_.modify()->parent = parent;
37   comment_.modify()->post = parent->post;
38 
39   edit();
40 }
41 
isNew()42 bool CommentView::isNew() const
43 {
44   return comment_.id() == -1;
45 }
46 
edit()47 void CommentView::edit()
48 {
49   clear();
50 
51   dbo::Transaction t(session_);
52 
53   setTemplateText(tr("blog-edit-comment"));
54 
55   auto editArea = std::make_unique<Wt::WTextArea>();
56   editArea_ = editArea.get();
57   editArea_->setText(comment_->textSrc());
58   editArea_->setFocus();
59 
60   auto save = std::make_unique<Wt::WPushButton>(tr("save"));
61   save->clicked().connect(this, &CommentView::save);
62 
63   auto cancel = std::make_unique<Wt::WPushButton>(tr("cancel"));
64   cancel->clicked().connect(this, &CommentView::cancel);
65 
66   bindWidget("area", std::move(editArea));
67   bindWidget("save", std::move(save));
68   bindWidget("cancel", std::move(cancel));
69 
70   t.commit();
71 }
72 
cancel()73 void CommentView::cancel()
74 {
75   if (isNew())
76     removeFromParent();
77   else {
78     dbo::Transaction t(session_);
79     renderView();
80     t.commit();
81   }
82 }
83 
renderTemplate(std::ostream & result)84 void CommentView::renderTemplate(std::ostream& result)
85 {
86   dbo::Transaction t(session_);
87 
88   WTemplate::renderTemplate(result);
89 
90   comment_.purge();
91 
92   t.commit();
93 }
94 
resolveString(const std::string & varName,const std::vector<Wt::WString> & args,std::ostream & result)95 void CommentView::resolveString(const std::string& varName,
96                                 const std::vector<Wt::WString>& args,
97 				std::ostream& result)
98 {
99   if (varName == "author")
100     format(result, comment_->author ? comment_->author->name : "anonymous");
101   else if (varName == "date")
102     format(result, comment_->date.timeTo(Wt::WDateTime::currentDateTime())
103 	   + " ago");
104   else if (varName == "contents")
105     format(result, comment_->textHtml(), Wt::TextFormat::XHTML);
106   else
107     WTemplate::resolveString(varName, args, result);
108 }
109 
renderView()110 void CommentView::renderView()
111 {
112   clear();
113 
114   bool isRootComment = !comment_->parent;
115   setTemplateText(isRootComment ? tr("blog-root-comment")
116 		  : tr("blog-comment"));
117 
118   bindString("collapse-expand", Wt::WString::Empty); // NYI
119 
120   auto replyText
121       = std::make_unique<Wt::WText>(isRootComment ? tr("comment-add")
122 			       : tr("comment-reply"));
123   replyText->setStyleClass("link");
124   replyText->clicked().connect(this, &CommentView::reply);
125   bindWidget("reply", std::move(replyText));
126 
127   bool mayEdit = session_.user()
128     && (comment_->author == session_.user()
129 	|| session_.user()->role == User::Admin);
130 
131   if (mayEdit) {
132     auto editText
133         = std::make_unique<Wt::WText>(tr("comment-edit"));
134     editText->setStyleClass("link");
135     editText->clicked().connect(this, &CommentView::edit);
136     bindWidget("edit", std::move(editText));
137   } else
138     bindString("edit", Wt::WString::Empty);
139 
140   bool mayDelete
141     = (session_.user() && session_.user() == comment_->author)
142     || session_.user() == comment_->post->author;
143 
144   if (mayDelete) {
145     auto deleteText
146         = std::make_unique<Wt::WText>(tr("comment-delete"));
147     deleteText->setStyleClass("link");
148     deleteText->clicked().connect(this, &CommentView::rm);
149     bindWidget("delete", std::move(deleteText));
150   } else
151     bindString("delete", Wt::WString::Empty);
152 
153   typedef std::vector< dbo::ptr<Comment> > CommentVector;
154   CommentVector comments;
155   {
156     dbo::collection<dbo::ptr<Comment> > cmts
157       = comment_->children.find().orderBy("date");
158     comments.insert(comments.end(), cmts.begin(), cmts.end());
159   }
160 
161   auto children
162       = std::make_unique<Wt::WContainerWidget>();
163   for (int i = (int)comments.size() - 1; i >= 0; --i)
164     children->addWidget(std::make_unique<CommentView>(session_, comments[i]));
165 
166   bindWidget("children", std::move(children));
167 }
168 
save()169 void CommentView::save()
170 {
171   dbo::Transaction t(session_);
172 
173   bool isNew = comment_.id() == -1;
174 
175   Comment *comment = comment_.modify();
176 
177   comment->setText(editArea_->text());
178 
179   if (isNew) {
180     session_.add(comment_);
181     comment->date = Wt::WDateTime::currentDateTime();
182     comment->author = session_.user();
183     session_.commentsChanged().emit(comment_);
184   }
185 
186   renderView();
187 
188   t.commit();
189 }
190 
reply()191 void CommentView::reply()
192 {
193   dbo::Transaction t(session_);
194 
195   Wt::WContainerWidget *c = resolve<Wt::WContainerWidget *>("children");
196   c->insertWidget(0, std::make_unique<CommentView>(session_, comment_.id()));
197 
198   t.commit();
199 }
200 
rm()201 void CommentView::rm()
202 {
203   dbo::Transaction t(session_);
204 
205   comment_.modify()->setDeleted();
206   renderView();
207 
208   t.commit();
209 }
210