source: trunk/lib/html_utility.cc @ 234

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

fixes #87 and #95 and #103 and #104 and #80 and #108, and refs #69

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