Wt examples
3.2.0
|
00001 /* 00002 * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium. 00003 * 00004 * See the LICENSE file for terms of use. 00005 */ 00006 00007 #include <fstream> 00008 #include <iostream> 00009 00010 #include <boost/lexical_cast.hpp> 00011 #include <boost/tokenizer.hpp> 00012 #include <boost/algorithm/string.hpp> 00013 00014 #include <Wt/WAnchor> 00015 #include <Wt/WApplication> 00016 #include <Wt/WEnvironment> 00017 #include <Wt/WLogger> 00018 #include <Wt/WMenu> 00019 #include <Wt/WPushButton> 00020 #include <Wt/WStackedWidget> 00021 #include <Wt/WTabWidget> 00022 #include <Wt/WTable> 00023 #include <Wt/WTableCell> 00024 #include <Wt/WTemplate> 00025 #include <Wt/WText> 00026 #include <Wt/WViewWidget> 00027 #include <Wt/WVBoxLayout> 00028 00029 #include "Home.h" 00030 #include "view/BlogView.h" 00031 00032 static const std::string SRC_INTERNAL_PATH = "src"; 00033 00034 Home::~Home() 00035 { 00036 } 00037 00038 Home::Home(const WEnvironment& env, const std::string& title, 00039 const std::string& resourceBundle, const std::string& cssPath) 00040 : WApplication(env), 00041 releases_(0), 00042 homePage_(0), 00043 sourceViewer_(0) 00044 { 00045 messageResourceBundle().use(appRoot() + resourceBundle, false); 00046 00047 useStyleSheet(cssPath + "/wt.css"); 00048 useStyleSheet(cssPath + "/wt_ie.css", "lt IE 7"); 00049 useStyleSheet("css/home.css"); 00050 useStyleSheet("css/sourceview.css"); 00051 setTitle(title); 00052 00053 setLocale(""); 00054 language_ = 0; 00055 } 00056 00057 void Home::init() 00058 { 00059 internalPathChanged().connect(this, &Home::setup); 00060 internalPathChanged().connect(this, &Home::setLanguageFromPath); 00061 internalPathChanged().connect(this, &Home::logInternalPath); 00062 00063 setup(); 00064 00065 setLanguageFromPath(); 00066 } 00067 00068 void Home::setup() 00069 { 00070 /* 00071 * This function switches between the two major components of the homepage, 00072 * depending on the internal path: 00073 * /src -> source viewer 00074 * /... -> homepage 00075 * 00076 * FIXME: we should take into account language /cn/src ... 00077 */ 00078 std::string base = internalPathNextPart("/"); 00079 00080 if (base == SRC_INTERNAL_PATH) { 00081 if (!sourceViewer_) { 00082 delete homePage_; 00083 homePage_ = 0; 00084 00085 root()->clear(); 00086 00087 sourceViewer_ = sourceViewer("/" + SRC_INTERNAL_PATH + "/"); 00088 WVBoxLayout *layout = new WVBoxLayout(); 00089 layout->setContentsMargins(0, 0, 0, 0); 00090 layout->addWidget(sourceViewer_); 00091 root()->setLayout(layout); 00092 } 00093 } else { 00094 if (!homePage_) { 00095 delete sourceViewer_; 00096 sourceViewer_ = 0; 00097 00098 root()->clear(); 00099 00100 createHome(); 00101 root()->addWidget(homePage_); 00102 00103 setLanguageFromPath(); 00104 } 00105 } 00106 } 00107 00108 void Home::createHome() 00109 { 00110 WTemplate *result = new WTemplate(tr("template"), root()); 00111 homePage_ = result; 00112 00113 WContainerWidget *languagesDiv = new WContainerWidget(); 00114 languagesDiv->setId("top_languages"); 00115 00116 for (unsigned i = 0; i < languages.size(); ++i) { 00117 if (i != 0) 00118 new WText("- ", languagesDiv); 00119 00120 const Lang& l = languages[i]; 00121 00122 new WAnchor(WLink(WLink::InternalPath, l.path_), 00123 WString::fromUTF8(l.longDescription_), languagesDiv); 00124 } 00125 00126 WStackedWidget *contents = new WStackedWidget(); 00127 WAnimation fade(WAnimation::Fade, WAnimation::Linear, 250); 00128 contents->setTransitionAnimation(fade); 00129 contents->setId("main_page"); 00130 00131 mainMenu_ = new WMenu(contents, Vertical); 00132 mainMenu_->setRenderAsList(true); 00133 00134 mainMenu_->addItem 00135 (tr("introduction"), introduction())->setPathComponent(""); 00136 00137 mainMenu_->addItem 00138 (tr("blog"), deferCreate(boost::bind(&Home::blog, this))); 00139 00140 mainMenu_->addItem 00141 (tr("features"), wrapView(&Home::features), WMenuItem::PreLoading); 00142 00143 mainMenu_->addItem 00144 (tr("documentation"), wrapView(&Home::documentation), 00145 WMenuItem::PreLoading); 00146 00147 mainMenu_->addItem 00148 (tr("examples"), examples(), 00149 WMenuItem::PreLoading)->setPathComponent("examples/"); 00150 00151 mainMenu_->addItem 00152 (tr("download"), deferCreate(boost::bind(&Home::download, this)), 00153 WMenuItem::PreLoading); 00154 00155 mainMenu_->addItem 00156 (tr("community"), wrapView(&Home::community), WMenuItem::PreLoading); 00157 00158 mainMenu_->addItem 00159 (tr("other-language"), wrapView(&Home::otherLanguage), 00160 WMenuItem::PreLoading); 00161 00162 mainMenu_->itemSelectRendered().connect(this, &Home::updateTitle); 00163 00164 mainMenu_->itemSelected().connect(this, &Home::googleAnalyticsLogger); 00165 00166 // Make the menu be internal-path aware. 00167 mainMenu_->setInternalPathEnabled("/"); 00168 00169 sideBarContent_ = new WContainerWidget(); 00170 00171 result->bindWidget("languages", languagesDiv); 00172 result->bindWidget("menu", mainMenu_); 00173 result->bindWidget("contents", contents); 00174 result->bindWidget("sidebar", sideBarContent_); 00175 } 00176 00177 void Home::setLanguage(int index) 00178 { 00179 if (homePage_) { 00180 const Lang& l = languages[index]; 00181 00182 setLocale(l.code_); 00183 00184 std::string langPath = l.path_; 00185 mainMenu_->setInternalBasePath(langPath); 00186 examplesMenu_->setInternalBasePath(langPath + "examples"); 00187 BlogView *blog = dynamic_cast<BlogView *>(findWidget("blog")); 00188 if (blog) 00189 blog->setInternalBasePath(langPath + "blog/"); 00190 updateTitle(); 00191 00192 language_ = index; 00193 } 00194 } 00195 00196 WWidget *Home::linkSourceBrowser(const std::string& example) 00197 { 00198 /* 00199 * Instead of using a WAnchor, which will not progress properly because 00200 * it is wrapped with wrapView() (-- should we not fix that?), we use 00201 * a WText which contains an anchor, and enable internal path encoding. 00202 */ 00203 std::string path = "#/" + SRC_INTERNAL_PATH + "/" + example; 00204 WText *a = new WText(tr("source-browser-link").arg(path)); 00205 a->setInternalPathEncoding(true); 00206 return a; 00207 } 00208 00209 void Home::setLanguageFromPath() 00210 { 00211 std::string langPath = internalPathNextPart("/"); 00212 00213 if (langPath.empty()) 00214 langPath = '/'; 00215 else 00216 langPath = '/' + langPath + '/'; 00217 00218 int newLanguage = 0; 00219 00220 for (unsigned i = 0; i < languages.size(); ++i) { 00221 if (languages[i].path_ == langPath) { 00222 newLanguage = i; 00223 break; 00224 } 00225 } 00226 00227 if (newLanguage != language_) 00228 setLanguage(newLanguage); 00229 } 00230 00231 void Home::updateTitle() 00232 { 00233 if (mainMenu_->currentItem()) { 00234 setTitle(tr("wt") + " - " + mainMenu_->currentItem()->text()); 00235 } 00236 } 00237 00238 void Home::logInternalPath(const std::string& path) 00239 { 00240 // simulate an access log for the interal paths 00241 log("path") << path; 00242 00243 // If this goes to /src, we need to invoke google analytics method too 00244 if (path.size() >= 4 && path.substr(0, 4) == "/src") { 00245 googleAnalyticsLogger(); 00246 } 00247 } 00248 00249 WWidget *Home::introduction() 00250 { 00251 return new WText(tr("home.intro")); 00252 } 00253 00254 WWidget *Home::blog() 00255 { 00256 const Lang& l = languages[language_]; 00257 std::string langPath = l.path_; 00258 BlogView *blog = new BlogView(langPath + "blog/", 00259 appRoot() + "blog.db", "/wt/blog/feed/"); 00260 blog->setObjectName("blog"); 00261 00262 if (!blog->user().empty()) 00263 chatSetUser(blog->user()); 00264 00265 blog->userChanged().connect(this, &Home::chatSetUser); 00266 00267 return blog; 00268 } 00269 00270 void Home::chatSetUser(const WString& userName) 00271 { 00272 WApplication::instance()->doJavaScript 00273 ("if (window.chat) " 00274 "try {" 00275 """window.chat.emit(window.chat, 'login', " 00276 "" "" + userName.jsStringLiteral() + "); " 00277 "} catch (e) {" 00278 """window.chatUser = " + userName.jsStringLiteral() + ";" 00279 "}" 00280 "else " 00281 """window.chatUser = " + userName.jsStringLiteral() + ";"); 00282 } 00283 00284 WWidget *Home::status() 00285 { 00286 return new WText(tr("home.status")); 00287 } 00288 00289 WWidget *Home::features() 00290 { 00291 return new WText(tr("home.features")); 00292 } 00293 00294 WWidget *Home::documentation() 00295 { 00296 WText *result = new WText(tr("home.documentation")); 00297 result->setInternalPathEncoding(true); 00298 return result; 00299 } 00300 00301 WWidget *Home::otherLanguage() 00302 { 00303 return new WText(tr("home.other-language")); 00304 } 00305 00306 WWidget *Home::wrapView(WWidget *(Home::*createWidget)()) 00307 { 00308 return makeStaticModel(boost::bind(createWidget, this)); 00309 } 00310 00311 std::string Home::href(const std::string& url, const std::string& description) 00312 { 00313 return "<a href=\"" + url + "\" target=\"_blank\">" + description + "</a>"; 00314 } 00315 00316 WWidget *Home::community() 00317 { 00318 return new WText(tr("home.community")); 00319 } 00320 00321 void Home::readReleases(WTable *releaseTable) 00322 { 00323 std::ifstream f((filePrefix() + "releases.txt").c_str()); 00324 00325 releaseTable->clear(); 00326 00327 releaseTable->elementAt(0, 0) 00328 ->addWidget(new WText(tr("home.download.version"))); 00329 releaseTable->elementAt(0, 1) 00330 ->addWidget(new WText(tr("home.download.date"))); 00331 releaseTable->elementAt(0, 2) 00332 ->addWidget(new WText(tr("home.download.description"))); 00333 00334 releaseTable->elementAt(0, 0)->resize(WLength(15, WLength::FontEx), 00335 WLength::Auto); 00336 releaseTable->elementAt(0, 1)->resize(WLength(15, WLength::FontEx), 00337 WLength::Auto); 00338 00339 int row = 1; 00340 00341 while (f) { 00342 std::string line; 00343 getline(f, line); 00344 00345 if (f) { 00346 typedef boost::tokenizer<boost::escaped_list_separator<char> > 00347 CsvTokenizer; 00348 CsvTokenizer tok(line); 00349 00350 CsvTokenizer::iterator i=tok.begin(); 00351 00352 std::string fileName = *i; 00353 std::string description = *(++i); 00354 releaseTable->elementAt(row, 0)->addWidget 00355 (new WText(href("http://prdownloads.sourceforge.net/witty/" 00356 + fileName + "?download", description))); 00357 releaseTable->elementAt(row, 1)->addWidget(new WText(*(++i))); 00358 releaseTable->elementAt(row, 2)->addWidget(new WText(*(++i))); 00359 00360 ++row; 00361 } 00362 } 00363 } 00364 00365 #ifdef WT_EMWEB_BUILD 00366 WWidget *Home::quoteForm() 00367 { 00368 WContainerWidget *result = new WContainerWidget(); 00369 result->setStyleClass("quote"); 00370 00371 WTemplate *requestTemplate = new WTemplate(tr("quote.request"), result); 00372 00373 WPushButton *quoteButton = new WPushButton(tr("quote.requestbutton")); 00374 requestTemplate->bindWidget("button", quoteButton); 00375 00376 WWidget *quoteForm = createQuoteForm(); 00377 result->addWidget(quoteForm); 00378 00379 quoteButton->clicked().connect(quoteForm, &WWidget::show); 00380 quoteButton->clicked().connect(requestTemplate, &WWidget::hide); 00381 00382 quoteForm->hide(); 00383 00384 return result; 00385 } 00386 #endif // WT_EMWEB_BUILD 00387 00388 WWidget *Home::download() 00389 { 00390 WContainerWidget *result = new WContainerWidget(); 00391 result->addWidget(new WText(tr("home.download"))); 00392 00393 result->addWidget(new WText(tr("home.download.license"))); 00394 00395 #ifdef WT_EMWEB_BUILD 00396 result->addWidget(quoteForm()); 00397 #endif // WT_EMWEB_BUILD 00398 00399 result->addWidget(new WText(tr("home.download.packages"))); 00400 00401 releases_ = new WTable(); 00402 readReleases(releases_); 00403 result->addWidget(releases_); 00404 00405 result->addWidget(new WText(tr("home.download.other"))); 00406 00407 return result; 00408 } 00409 00410 00411 WString Home::tr(const char *key) 00412 { 00413 return WString::tr(key); 00414 } 00415 00416 void Home::googleAnalyticsLogger() 00417 { 00418 std::string googleCmd = 00419 "if (window.pageTracker) {" 00420 """try {" 00421 "" "setTimeout(function() {" 00422 "" "window.pageTracker._trackPageview(\"" 00423 + environment().deploymentPath() + internalPath() + "\");" 00424 "" "}, 1000);" 00425 """} catch (e) { }" 00426 "}"; 00427 00428 doJavaScript(googleCmd); 00429 } 00430