source: trunk/lib/html_utility.cc @ 235

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

added Date class
changed date format on first page
fixed plotting only once (fixes #113)
changed interface to anchor class
more anchors to users
removed anchor to '../' in author summary in dirs
anchors are filtered through HtmlStream?
log messages are truncated and newlines are replaced with spaces

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