Wt examples 3.1.10
|
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 homePage_ = initHome(); 00101 root()->addWidget(homePage_); 00102 00103 setLanguageFromPath(); 00104 } 00105 } 00106 } 00107 00108 WWidget *Home::initHome() 00109 { 00110 WTemplate *result = new WTemplate(tr("template"), root()); 00111 00112 WContainerWidget *languagesDiv = new WContainerWidget(); 00113 languagesDiv->setId("top_languages"); 00114 00115 for (unsigned i = 0; i < languages.size(); ++i) { 00116 if (i != 0) 00117 new WText("- ", languagesDiv); 00118 00119 const Lang& l = languages[i]; 00120 00121 WAnchor *a = new WAnchor("", WString::fromUTF8(l.longDescription_), 00122 languagesDiv); 00123 a->setRefInternalPath(l.path_); 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 return result; 00177 } 00178 00179 void Home::setLanguage(int index) 00180 { 00181 if (homePage_) { 00182 const Lang& l = languages[index]; 00183 00184 setLocale(l.code_); 00185 00186 std::string langPath = l.path_; 00187 mainMenu_->setInternalBasePath(langPath); 00188 examplesMenu_->setInternalBasePath(langPath + "examples"); 00189 updateTitle(); 00190 00191 language_ = index; 00192 } 00193 } 00194 00195 WWidget *Home::linkSourceBrowser(const std::string& example) 00196 { 00197 /* 00198 * Instead of using a WAnchor, which will not progress properly because 00199 * it is wrapped with wrapView() (-- should we not fix that?), we use 00200 * a WText which contains an anchor, and enable internal path encoding. 00201 */ 00202 std::string path = "#/" + SRC_INTERNAL_PATH + "/" + example; 00203 WText *a = new WText(tr("source-browser-link").arg(path)); 00204 a->setInternalPathEncoding(true); 00205 return a; 00206 } 00207 00208 void Home::setLanguageFromPath() 00209 { 00210 std::string langPath = internalPathNextPart("/"); 00211 00212 if (langPath.empty()) 00213 langPath = '/'; 00214 else 00215 langPath = '/' + langPath + '/'; 00216 00217 int newLanguage = 0; 00218 00219 for (unsigned i = 0; i < languages.size(); ++i) { 00220 if (languages[i].path_ == langPath) { 00221 newLanguage = i; 00222 break; 00223 } 00224 } 00225 00226 if (newLanguage != language_) 00227 setLanguage(newLanguage); 00228 } 00229 00230 void Home::updateTitle() 00231 { 00232 if (mainMenu_->currentItem()) { 00233 setTitle(tr("wt") + " - " + mainMenu_->currentItem()->text()); 00234 } 00235 } 00236 00237 void Home::logInternalPath(const std::string& path) 00238 { 00239 // simulate an access log for the interal paths 00240 log("path") << path; 00241 00242 // If this goes to /src, we need to invoke google analytics method too 00243 if (path.size() >= 4 && path.substr(0, 4) == "/src") { 00244 googleAnalyticsLogger(); 00245 } 00246 } 00247 00248 WWidget *Home::introduction() 00249 { 00250 return new WText(tr("home.intro")); 00251 } 00252 00253 WWidget *Home::blog() 00254 { 00255 BlogView *blog = new BlogView("/blog/", appRoot() + "blog.db", "/wt/blog/feed/"); 00256 00257 if (!blog->user().empty()) 00258 chatSetUser(blog->user()); 00259 00260 blog->userChanged().connect(this, &Home::chatSetUser); 00261 00262 return blog; 00263 } 00264 00265 void Home::chatSetUser(const WString& userName) 00266 { 00267 WApplication::instance()->doJavaScript 00268 ("if (window.chat) " 00269 "try {" 00270 """window.chat.emit(window.chat, 'login', " 00271 "" "" + userName.jsStringLiteral() + "); " 00272 "} catch (e) {" 00273 """window.chatUser = " + userName.jsStringLiteral() + ";" 00274 "}" 00275 "else " 00276 """window.chatUser = " + userName.jsStringLiteral() + ";"); 00277 } 00278 00279 WWidget *Home::status() 00280 { 00281 return new WText(tr("home.status")); 00282 } 00283 00284 WWidget *Home::features() 00285 { 00286 return new WText(tr("home.features")); 00287 } 00288 00289 WWidget *Home::documentation() 00290 { 00291 WText *result = new WText(tr("home.documentation")); 00292 result->setInternalPathEncoding(true); 00293 return result; 00294 } 00295 00296 WWidget *Home::otherLanguage() 00297 { 00298 return new WText(tr("home.other-language")); 00299 } 00300 00301 WWidget *Home::wrapView(WWidget *(Home::*createWidget)()) 00302 { 00303 return makeStaticModel(boost::bind(createWidget, this)); 00304 } 00305 00306 std::string Home::href(const std::string& url, const std::string& description) 00307 { 00308 return "<a href=\"" + url + "\" target=\"_blank\">" + description + "</a>"; 00309 } 00310 00311 WWidget *Home::community() 00312 { 00313 return new WText(tr("home.community")); 00314 } 00315 00316 void Home::readReleases(WTable *releaseTable) 00317 { 00318 std::ifstream f((filePrefix() + "releases.txt").c_str()); 00319 00320 releaseTable->clear(); 00321 00322 releaseTable->elementAt(0, 0) 00323 ->addWidget(new WText(tr("home.download.version"))); 00324 releaseTable->elementAt(0, 1) 00325 ->addWidget(new WText(tr("home.download.date"))); 00326 releaseTable->elementAt(0, 2) 00327 ->addWidget(new WText(tr("home.download.description"))); 00328 00329 releaseTable->elementAt(0, 0)->resize(WLength(15, WLength::FontEx), 00330 WLength::Auto); 00331 releaseTable->elementAt(0, 1)->resize(WLength(15, WLength::FontEx), 00332 WLength::Auto); 00333 00334 int row = 1; 00335 00336 while (f) { 00337 std::string line; 00338 getline(f, line); 00339 00340 if (f) { 00341 typedef boost::tokenizer<boost::escaped_list_separator<char> > 00342 CsvTokenizer; 00343 CsvTokenizer tok(line); 00344 00345 CsvTokenizer::iterator i=tok.begin(); 00346 00347 std::string fileName = *i; 00348 std::string description = *(++i); 00349 releaseTable->elementAt(row, 0)->addWidget 00350 (new WText(href("http://prdownloads.sourceforge.net/witty/" 00351 + fileName + "?download", description))); 00352 releaseTable->elementAt(row, 1)->addWidget(new WText(*(++i))); 00353 releaseTable->elementAt(row, 2)->addWidget(new WText(*(++i))); 00354 00355 ++row; 00356 } 00357 } 00358 } 00359 00360 #ifdef WT_EMWEB_BUILD 00361 WWidget *Home::quoteForm() 00362 { 00363 WContainerWidget *result = new WContainerWidget(); 00364 result->setStyleClass("quote"); 00365 00366 WTemplate *requestTemplate = new WTemplate(tr("quote.request"), result); 00367 00368 WPushButton *quoteButton = new WPushButton(tr("quote.requestbutton")); 00369 requestTemplate->bindWidget("button", quoteButton); 00370 00371 WWidget *quoteForm = createQuoteForm(); 00372 result->addWidget(quoteForm); 00373 00374 quoteButton->clicked().connect(quoteForm, &WWidget::show); 00375 quoteButton->clicked().connect(requestTemplate, &WWidget::hide); 00376 00377 quoteForm->hide(); 00378 00379 return result; 00380 } 00381 #endif // WT_EMWEB_BUILD 00382 00383 WWidget *Home::download() 00384 { 00385 WContainerWidget *result = new WContainerWidget(); 00386 result->addWidget(new WText(tr("home.download"))); 00387 00388 result->addWidget(new WText(tr("home.download.license"))); 00389 00390 #ifdef WT_EMWEB_BUILD 00391 result->addWidget(quoteForm()); 00392 #endif // WT_EMWEB_BUILD 00393 00394 result->addWidget(new WText(tr("home.download.packages"))); 00395 00396 releases_ = new WTable(); 00397 readReleases(releases_); 00398 result->addWidget(releases_); 00399 00400 result->addWidget(new WText(tr("home.download.other"))); 00401 00402 return result; 00403 } 00404 00405 00406 WString Home::tr(const char *key) 00407 { 00408 return WString::tr(key); 00409 } 00410 00411 void Home::googleAnalyticsLogger() 00412 { 00413 std::string googleCmd = 00414 "if (window.pageTracker) {" 00415 """try {" 00416 "" "setTimeout(function() {" 00417 "" "window.pageTracker._trackPageview(\"" 00418 + environment().deploymentPath() + internalPath() + "\");" 00419 "" "}, 1000);" 00420 """} catch (e) { }" 00421 "}"; 00422 00423 doJavaScript(googleCmd); 00424 } 00425