source: trunk/lib/html_utility.cc @ 275

Last change on this file since 275 was 275, checked in by Peter Johansson, 14 years ago

Fixes #174.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.0 KB
Line 
1// $Id: html_utility.cc 275 2007-05-02 22:14:28Z peter $
2
3/*
4  Copyright (C) 2006, 2007 Peter Johansson
5
6  This file is part of svndigest, http://lev.thep.lu.se/trac/svndigest
7
8  svndigest is free software; you can redistribute it and/or modify it
9  under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12
13  svndigest is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21  02111-1307, USA.
22*/
23
24#include "html_utility.h"
25
26#include "Commitment.h"
27#include "Configuration.h"
28#include "Date.h"
29#include "HtmlStream.h"
30#include "Stats.h"
31#include "SVNlog.h"
32#include "utility.h"
33#include <config.h> // this header file is created by configure
34
35#include <algorithm>
36#include <cassert>
37#include <fstream>
38#include <iostream>
39#include <sstream>
40#include <stdexcept>
41#include <string>
42#include <sys/param.h>
43#include <unistd.h>
44#include <vector>
45
46namespace theplu{
47namespace svndigest{
48
49  std::string anchor(const std::string& href, 
50                     const std::string& name, u_int level, 
51                     const std::string& title)
52  {
53    std::stringstream ss;
54    HtmlStream hs(ss);
55    ss << "<a title=\"";
56    hs << title; 
57    ss << "\" href=\"";
58    for (size_t i=0; i<level; ++i)
59      ss << "../";
60    hs << href; 
61    ss << "\">"; 
62    hs << name; 
63    ss << "</a>";
64    return ss.str();
65  }
66
67 
68  void print_css(const std::string& str)
69  {
70    std::ofstream s(str.c_str());
71    s << "<-- svndigest.css generated by " << PACKAGE_STRING << "\n"
72
73      << "Copyright (C) 2006, 2007 Peter Johansson\n\n"
74      << "This file is part of svndigest, " 
75      << "http://lev.thep.lu.se/trac/svndigest\n\n"
76
77      << "svndigest is free software; you can redistribute it and/or " 
78      << "modify it\n"
79      << "under the terms of the GNU General Public License as published by\n"
80      << "the Free Software Foundation; either version 2 of the License, or\n"
81      << "(at your option) any later version.\n\n"
82
83      << "svndigest is distributed in the hope that it will be useful, but\n"
84      << "WITHOUT ANY WARRANTY; without even the implied warranty of\n"
85      << "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
86      << "General Public License for more details.\n\n"
87
88      << "You should have received a copy of the GNU General Public License\n"
89      << "along with this program; if not, write to the Free Software\n"
90      << "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n"
91      << "02111-1307, USA.\n\n"
92
93      << "This file was initially inspired by the cascading style sheet\n"
94      << "within the Trac open source project (http://trac.edgewall.org/)\n"
95      << "Copyright (C) 2003-2006 Edgewall Software\n"
96      << "All rights reserved.\n\n"
97
98      << "Redistribution and use in source and binary forms, with or without\n"
99      << "modification, are permitted provided that the following conditions\n"
100      << "are met:\n\n"
101     
102      << "1. Redistributions of source code must retain the above copyright\n"
103      << "   notice, this list of conditions and the following disclaimer.\n"
104      << "2. Redistributions in binary form must reproduce the above " 
105      << "copyright\n"
106      << "   notice, this list of conditions and the following disclaimer in\n"
107      << "   the documentation and/or other materials provided with the\n"
108      << "   distribution.\n"
109      << "3. The name of the author may not be used to endorse or promote\n"
110      << "   products derived from this software without specific prior\n"
111      << "   written permission.\n\n"
112     
113      << "THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS\n"
114      << "OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
115      << "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
116      << "ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n"
117      << "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
118      << "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\n"
119      << "GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
120      << "INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n"
121      << "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n"
122      << "NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n"
123      << "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.-->\n";
124
125
126    s << "body {\n";
127    s << " background: #fff; \n";
128    s << " color: #000; \n";
129    s << " margin: 0px; \n";
130    s << " padding: 0; \n";
131    s << "} \n";
132    s << "\n";
133    s << "#menu {\n";
134    s << " background: #eee;\n";
135    s << " width: 100%;\n";
136    s << " margin: 0px;\n";
137    s << " padding: 0px;\n";
138    s << "}\n\n";
139    s << "#menu ul\n";
140    s << "{ \n";
141    s << "padding: 0px;\n";
142    s << "margin: 0px;list-style-type: none; text-align: center;"
143      << "border-bottom: 1px solid black;}\n";
144    s << "#menu ul li { display: inline; border-right: 1px solid black;}\n";
145    s << "#menu ul li a {text-decoration: none; padding-right: 1em;" 
146      << "padding-left: 1em; margin: 0px;}\n";
147    s << "#menu ul li a:hover{ color: #000; background: #ddd;}\n";
148    s << "#menu ul li.highlight a {color: #fff; background: #777; }";
149    s << "\n";
150    s << "#main {\n";
151    s << " margin: 10px; \n";
152    s << "}\n";
153    s << "\n";
154    s << "body, th, td {\n";
155    s << " font: normal 13px verdana,arial,'Bitstream Vera Sans',"
156      << "helvetica,sans-serif;\n";
157    s << "}\n";
158    s << ":link, :visited {\n";
159    s << " text-decoration: none;\n";
160    s << " color: #b00;\n";
161    s << "}\n";
162    s << "\n";
163    s << "div.main {\n";
164    s << "margin-top: 50px;\n";         
165    s << "margin-left: 50px;\n";
166    s << " \n";
167    s << "}\n";
168    s << "table.main {\n"; 
169    s << " text-align: left;\n";
170    s << " padding: 0 1em .1em 0;\n";
171    s << "}\n";
172    s << "table.main th {\n";
173    s << " text-align: left;\n";
174    s << " padding: 0 1em 0.5em 0;\n";
175    s << " font-size: 150%;\n";
176    s << " font-wheight: bold;\n";
177    s << "}\n";
178    s << "table.main td {\n";
179    s << " padding: 0 1em .1em 0;\n";
180    s << "}\n";
181
182
183
184
185    s << "table.listings {\n";
186    s << " clear: both;\n";
187    s << " border-bottom: 1px solid #d7d7d7;\n";
188    s << " border-collapse: collapse;\n";
189    s << " border-spacing: 0;\n";
190    s << " margin-top: 1em;\n";
191    s << " width: 100%;\n";
192    s << "}\n";
193    s << "\n";
194    s << "table.listings th {\n";
195    s << " text-align: left;\n";
196    s << " padding: 0 1em .1em 0;\n";
197    s << " font-size: 12px\n";
198    s << "}\n";
199    s << "table.listings thead { background: #f7f7f0 }\n";
200    s << "table.listings thead th {\n";
201    s << " border: 1px solid #d7d7d7;\n";
202    s << " border-bottom-color: #999;\n";
203    s << " font-size: 11px;\n";
204    s << " font-wheight: bold;\n";
205    s << " padding: 2px .5em;\n";
206    s << " vertical-align: bottom;\n";
207    s << "}\n";
208    s << "\n";
209    s << "table.listings tbody td a:hover, table.listing tbody th a:hover {\n";
210    s << " background-color: transparent;\n";
211    s << "}\n";
212    s << "\n";
213    s << "table.listings tbody td, table.listing tbody th {\n";
214    s << " border: 1px dotted #ddd;\n";
215    s << " padding: .33em .5em;\n";
216    s << " vertical-align: top;\n";
217    s << "}\n";
218    s << "\n";
219    s << "table.listings tbody td a:hover, table.listing tbody th a:hover {\n";
220    s << " background-color: transparent;\n";
221    s << "}\n";
222    s << "table.listings tbody tr { border-top: 1px solid #ddd }\n";
223    s << "table.listings tbody tr.light { background-color: #fcfcfc }\n";
224    s << "table.listings tbody tr.dark { background-color: #f7f7f7 }\n";
225    s << "table.listings tbody tr:hover { background: #eed }\n";
226    s << "table.listings tbody td { text-align: left }\n";
227    s << "table.listings tbody td.directory a { font-weight: bold }\n";
228    s << "\n";
229    s << ".sep { color: #666}\n";
230    s << "\n";
231    s << "\n";
232    s.close();
233  }
234
235
236  void print_main_page(const std::string& dir, const SVNlog& log,
237                       const Stats& stats)
238  {
239    std::string filename="index.html";
240    std::ofstream os(filename.c_str());
241    print_header(os, dir, 0, "all", "main", "index.html");
242
243    using namespace std;
244    set<string> authors;
245    authors.insert(log.author().begin(), log.author().end());
246    // erase invalid authors
247    authors.erase("");
248    authors.erase("no author");
249
250    vector<Commitment> latest_commit;
251    latest_commit.reserve(authors.size());
252    for (set<string>::const_iterator i(authors.begin()); i!=authors.end(); ++i)
253      latest_commit.push_back(log.latest_commit(*i));
254
255    print_general_information(os, log, authors.size());
256    sort(latest_commit.begin(), latest_commit.end(), GreaterRevision());
257    print_authors(os, latest_commit, stats);
258    print_recent_logs(os, log);
259    os << "<hr width=100% />";
260    print_footer(os);
261    os.close();
262
263  }
264
265  void print_general_information(std::ostream& os, const SVNlog& log, 
266                                 size_t nof_authors)
267  { 
268    Date begin(log.date()[0]);
269    if (log.date().size()>1)
270      begin = std::min(begin, Date(log.date()[1]));
271    Date end(log.date().back());
272    Date now;
273    std::string timefmt("%a, %e %b %Y");
274
275    os << "<div class=\"main\">" 
276       << "<table class=\"main\"><thead><tr><th colspan=\"2\">" 
277       << "General Information"
278       << "</th></tr></thead>\n"
279       << "<tr><td>First Revision:</td><td>" 
280       << begin(timefmt) << "</td></tr>\n"
281       << "<tr><td>Latest Revision:</td><td>" 
282       << end(timefmt) << "</td></tr>\n"
283       << "<tr><td>Report Generated:</td><td>" 
284       << now(timefmt) << "</td></tr>\n"
285       << "<tr><td>Repository Age:</td><td>"; 
286    os << now.difftime(begin);
287    os << "</td></tr>\n"
288       << "<tr><td>Number of Authors:</td><td>" << nof_authors
289       << "</td></tr>\n"
290       << "<tr><td>Revisions:</td><td>" << log.revision().size() 
291       << "</td></tr>\n"
292       << "</table></div>\n";
293  }
294
295
296  void print_footer(std::ostream& os)
297  {
298    Date date;
299    os << "<p align=center><font size=-2>\nGenerated on "
300       << date("%a %b %d %H:%M:%S %Y") << " (UTC) by "
301       << anchor("http://lev.thep.lu.se/trac/svndigest/", 
302                 PACKAGE_STRING, 0, "")
303       << "</font>\n</p>\n</div>\n</body>\n</html>\n";
304  }
305
306
307  void print_header(std::ostream& os, std::string title, u_int level,
308                    std::string user, std::string item, std::string path)
309  {
310    os << "<!DOCTYPE html\n"
311       << "PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n"
312       << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
313       << "<html xmlns=\"http://www.w3.org/1999/xhtml\""
314       << " xml:lang=\"en\" lang=\"en\"><head>\n"
315       << "<head>\n"
316       << "<title> " << title << " - svndigest</title>\n"
317       << "</head>\n"
318       << "<link rel=\"stylesheet\" "
319       << "href=\"";
320    for (u_int i=0; i<level; ++i)
321      os << "../";
322    os << "svndigest.css\" type=\"text/css\" />\n"
323       << "<body>\n"
324       << "<div id=\"menu\">"
325       << "<ul><li></li>";
326    if (item=="main")
327      os << "<li class=\"highlight\">";
328    else
329      os << "<li>";
330    os << anchor("index.html", "Main", level, "Main page");
331    os << "</li>";
332
333    if (item=="total")
334      os << "<li class=\"highlight\">";
335    else
336      os << "<li>";
337    os << anchor(user+"/total/"+path, "Total", level, 
338           "View statistics of all lines");
339    os << "</li>";
340
341    if (item=="code")
342      os << "<li class=\"highlight\">";
343    else
344      os << "<li>";
345    os << anchor(user+"/code/"+path, "Code", level, 
346                 "View statistics of code lines");
347    os << "</li>";
348
349    if (item=="comments")
350      os << "<li class=\"highlight\">";
351    else
352      os << "<li>";
353    os << anchor(user+"/comments/"+path, "Comment", level, 
354                 "View statistics of comment lines");
355    os << "</li>";
356
357
358    if (item=="empty")
359      os << "<li class=\"highlight\">";
360    else
361      os << "<li>";
362    os << anchor(user+"/empty/"+path, "Other", level, 
363                 "View statistics of empty lines");
364    os << "</li>"
365       << "</ul></div>"
366       << "<div id=\"main\">\n";
367  }
368
369
370  void print_authors(std::ostream& os, 
371                     const std::vector<Commitment>& lc,
372                     const Stats& stats)
373  {
374    os << "<div class=\"main\">"
375       << "<table class=\"main\"><thead><tr><th colspan=\"2\">" 
376       << "Authors"
377       << "</th></tr></thead>\n";
378
379    os << "<tr><td>Author</td>" 
380       << "<td>Number of Lines</td>"
381       << "<td>Code Lines</td>"
382       << "<td>Comment Lines</td>"
383       << "<td>Latest Commitment</td>"
384       <<"</tr>";
385
386    std::string timefmt("%b %d %H:%M:%S %Y");
387    using namespace std;
388    for (vector<Commitment>::const_iterator i=lc.begin(); i!=lc.end(); ++i) {
389      os << "<tr><td>" 
390         << anchor(string(i->author()+"/total/index.html"),i->author())
391         << "</td><td>" << stats.lines(i->author()) << " (" 
392         << 100*stats.lines(i->author())/stats.lines() << "%)</td>"
393         << "<td>" << stats.code(i->author()) << " (" 
394         << 100*stats.code(i->author())/stats.code() << "%)</td>"
395         << "<td>" << stats.comments(i->author()) << " (" 
396         << 100*stats.comments(i->author())/stats.comments() << "%)</td>"
397         << "<td>" << i->date()(timefmt) << "</td>"
398         <<"</tr>";
399    }
400
401    os << "</table></div>\n";
402   
403  }
404
405
406  void print_recent_logs(std::ostream& os, const SVNlog& log)
407  {
408    os << "<div class=\"main\">\n"
409       << "<table class=\"main\"><thead><tr><th colspan=\"2\">" 
410       << "Recent Log"
411       << "</th></tr></thead>\n";
412
413    std::vector<std::string>::const_reverse_iterator a=log.author().rbegin();
414    std::vector<std::string>::const_reverse_iterator d=log.date().rbegin();
415    std::vector<std::string>::const_reverse_iterator m=log.message().rbegin();
416    std::vector<size_t>::const_reverse_iterator r=log.revision().rbegin();
417    assert(log.author().size()==log.date().size());
418    assert(log.author().size()==log.message().size());
419    assert(log.author().size()==log.revision().size());
420    os << "<tr><td>Author</td><td>Date</td><td>Rev</td><td>Message</td></tr>\n";
421    HtmlStream hs(os);
422    std::string timefmt("%b %d %H:%M:%S %Y");
423    const size_t maxlength = 80;
424    const Configuration* conf = Configuration::instance();
425    for (size_t i=0; i<10 && a!=log.author().rend(); ++i) {
426      os << "<tr><td>" << anchor(*a+"/total/index.html",*a) << "</td>";
427      Date date(*d);
428      os << "<td>" << date(timefmt) << "</td>";
429      os << "<td>"; 
430      os << trac_revision(*r);
431      os << "</td>";
432      os << "<td>";
433      std::string mess = *m;
434      // replace newlines with space
435      std::replace(mess.begin(), mess.end(), '\n', ' ');
436      mess = htrim(mess);
437      // truncate message if too long
438      if (mess.size()>maxlength)
439        mess = mess.substr(0,maxlength-3) + "...";
440
441      if (conf->trac_ticket().empty())
442        hs << mess;
443      else { // make anchors to trac
444        char c;
445        std::stringstream ss(mess);
446        for (ss.get(c); ss.good(); ss.get(c)) {
447          if (c=='#') {
448            int ticket=0;
449            ss >> ticket;
450            if (!ss.fail()){
451              std::stringstream s;
452              s << ticket;
453              os << anchor(conf->trac_ticket()+s.str(),"#"+s.str()); 
454            }
455            else {
456              ss.clear(std::ios::goodbit);
457              hs << c;
458            }
459          }
460          else
461            hs << c;
462        }
463      }
464
465      os << "</td></tr>";
466      ++a;
467      ++d;
468      ++m;
469      ++r;
470    }
471    os << "</table></div>\n";
472  }
473
474
475  std::string trac_revision(size_t r)
476  {
477    Configuration* conf = Configuration::instance();
478    std::stringstream ss;
479    if (conf->trac_revision().empty())
480      ss << r; 
481    else {
482      std::stringstream rev;
483      rev << r;
484      ss << anchor(conf->trac_revision()+rev.str(), rev.str());
485    }
486    return ss.str();
487  }
488
489}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.