calendar.pl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. # Copyright (C) 2004–2023 Alex Schroeder <alex@gnu.org>
  2. # Copyright (C) 2006 Ingo Belka
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. use strict;
  17. use v5.10;
  18. AddModuleDescription('calendar.pl', 'Calendar Extension');
  19. our ($q, %Page, %Action, $Now, $OpenPageName, $CollectingJournal, $FreeLinkPattern, @MyRules);
  20. our ($CalendarOnEveryPage, $CalAsTable, $CalStartMonday);
  21. $CalendarOnEveryPage = 0; # 1=on every page is a month-div situated in the header, use css to control
  22. # 2=this month and the previous month; 3=this, previous and next month
  23. $CalAsTable = 0; # 0=every month-div is "free", 1=every month-div is caught in a table, use css to control
  24. $CalStartMonday = 0; # 0=week starts with Su, 1=week starts with Mo
  25. *OldCalendarGetHeader = \&GetHeader;
  26. *GetHeader = \&NewCalendarGetHeader;
  27. sub NewCalendarGetHeader {
  28. my $header = OldCalendarGetHeader(@_);
  29. return $header unless $CalendarOnEveryPage;
  30. my $action = GetParam('action', 'browse');
  31. return $header if grep(/^$action$/, ('calendar', 'edit'));
  32. my $cal;
  33. my ($sec, $min, $hour, $mday, $mon, $year) = localtime($Now);
  34. $year += 1900;
  35. # $mon is 0 based and thus good for previous month
  36. if ($mon < 1) { $year -= 1; $mon += 12; };
  37. $cal .= Cal($year, $mon) if $CalendarOnEveryPage > 1;
  38. # the current month
  39. $mon += 1;
  40. if ($mon > 12) { $year += 1; $mon -= 12; };
  41. $cal .= Cal($year, $mon) if $CalendarOnEveryPage;
  42. # the next month
  43. $mon += 1;
  44. if ($mon > 12) { $year += 1; $mon -= 12; };
  45. $cal .= Cal($year, $mon) if $CalendarOnEveryPage > 2;
  46. # insert calendars before header div
  47. $header =~ s!<div class="header">!<div class="cal">$cal</div><div class="header">!;
  48. return $header;
  49. }
  50. sub Cal {
  51. my ($year, $mon, $unlink_year, $id) = @_; # example: 2004, 12
  52. $id = FreeToNormal($id);
  53. my ($sec_now, $min_now, $hour_now, $mday_now, $mon_now, $year_now) = localtime($Now);
  54. $mon_now += 1;
  55. $mon = $mon_now unless $mon;
  56. $year_now += 1900;
  57. $year = $year_now unless $year;
  58. if ($year < 1) {
  59. return $q->p(T('Illegal year value: Use 0001-9999'));
  60. }
  61. my @pages = AllPagesList();
  62. my $cal = draw_month($mon, $year);
  63. $cal =~ s{ ( ?\d{1,2})\b}{{
  64. my $day = $1;
  65. my $date = sprintf("%d-%02d-%02d", $year, $mon, $day);
  66. my $re = "^$date";
  67. $re .= ".*$id" if $id;
  68. my $page = $date;
  69. $page .= "_$id" if $id;
  70. my $class = '';
  71. $class .= ' today' if $day == $mday_now and $mon == $mon_now and $year == $year_now;
  72. my @matches = grep(/$re/, @pages);
  73. my $link = ' ';
  74. if (@matches == 0) { # not using GetEditLink because of $class
  75. $link .= ScriptLink('action=edit;id=' . UrlEncode($page), $day, 'edit' . $class);
  76. } elsif (@matches == 1) { # not using GetPageLink because of $class
  77. $link .= ScriptLink($matches[0], $day, 'local exact' . $class);
  78. } else {
  79. $link .= ScriptLink('action=collect;match=' . UrlEncode($re), $day, 'local collection' . $class);
  80. }
  81. $link;
  82. }}eg;
  83. $cal =~ s{(\S+) (\d\d\d\d)}{{
  84. my ($month_text, $year_text) = ($1, $2);
  85. my $date = sprintf("%d-%02d", $year, $mon);
  86. if ($unlink_year) {
  87. $q->span({-class=>'title'}, ScriptLink('action=collect;match=%5e' . $date,
  88. "$month_text $year_text", 'local collection month'));
  89. } else {
  90. $q->span({-class=>'title'}, ScriptLink('action=collect;match=%5e' . $date,
  91. $month_text, 'local collection month') . ' '
  92. . ScriptLink('action=calendar;year=' . $year,
  93. $year_text, 'local collection year'));
  94. }
  95. }}e;
  96. return "<div class=\"month\"><pre>$cal</pre></div>";
  97. }
  98. $Action{collect} = \&DoCollect;
  99. # inspired by journal
  100. sub DoCollect {
  101. my $id = shift;
  102. my $match = GetParam('match', '');
  103. my $search = GetParam('search', '');
  104. ReportError(T('The match parameter is missing.')) unless $match or $search;
  105. print GetHeader('', Ts('Page Collection for %s', $match||$search), '');
  106. my @pages = Matched($match, $search ? SearchTitleAndBody($search) : AllPagesList());
  107. if (!$CollectingJournal) {
  108. $CollectingJournal = 1;
  109. # Now save information required for saving the cache of the current page.
  110. local (%Page, $OpenPageName);
  111. print $q->start_div({-class=>'content journal collection'});
  112. PrintAllPages(1, 1, undef, undef, @pages);
  113. print $q->end_div();
  114. }
  115. $CollectingJournal = 0;
  116. PrintFooter();
  117. }
  118. push(@MyRules, \&CalendarRule);
  119. sub CalendarRule {
  120. if (/\G(calendar:(\d\d\d\d))/cg) {
  121. my $oldpos = pos;
  122. Clean(CloseHtmlEnvironments());
  123. Dirty($1);
  124. PrintYearCalendar($2);
  125. pos = $oldpos;
  126. return AddHtmlEnvironment('p');
  127. } elsif (/\G(month:(\d\d\d\d)-(\d\d))/cg) {
  128. my $oldpos = pos;
  129. Clean(CloseHtmlEnvironments());
  130. Dirty($1);
  131. print $q->div({-class => 'cal'}, Cal($2, $3));
  132. pos = $oldpos;
  133. return AddHtmlEnvironment('p');
  134. } elsif (/\G(month:([+-]\d\d?))/cg
  135. or /\G(\[\[month:([+-]\d\d?) $FreeLinkPattern\]\])/cg) {
  136. my $oldpos = pos;
  137. Clean(CloseHtmlEnvironments());
  138. Dirty($1);
  139. my $delta = $2;
  140. my $id = $3;
  141. my ($sec, $min, $hour, $mday, $mon, $year) = localtime($Now);
  142. $year += 1900;
  143. $mon += 1 + $delta;
  144. while ($mon < 1) { $year -= 1; $mon += 12; };
  145. while ($mon > 12) { $year += 1; $mon -= 12; };
  146. print $q->div({-class => 'cal'}, Cal($year, $mon, undef, $id));
  147. pos = $oldpos;
  148. return AddHtmlEnvironment('p');
  149. }
  150. return;
  151. }
  152. sub PrintYearCalendar {
  153. my $year = shift;
  154. print $q->p({-class=>'nav'},
  155. ScriptLink('action=calendar;year=' . ($year-1), T('Previous')),
  156. '|',
  157. ScriptLink('action=calendar;year=' . ($year+1), T('Next')));
  158. if ($CalAsTable) {
  159. print '<table><tr>';
  160. for my $mon (1..12) {
  161. print '<td>'.Cal($year, $mon, 1).'</td>';
  162. if ($mon == 3 or $mon == 6 or $mon == 9) {
  163. print '</tr><tr>';
  164. }
  165. }
  166. print '</tr></table>';
  167. } else {
  168. for my $mon (1..12) {
  169. print Cal($year, $mon, 1);
  170. }
  171. }
  172. }
  173. $Action{calendar} = \&DoYearCalendar;
  174. sub DoYearCalendar {
  175. my ($sec, $min, $hour, $mday, $mon, $year) = localtime($Now);
  176. $year += 1900;
  177. $year = GetParam('year', $year);
  178. print GetHeader('', Ts('Calendar %s', $year), '');
  179. print $q->start_div({-class=>'content cal year'});
  180. PrintYearCalendar($year);
  181. print $q->end_div();
  182. PrintFooter();
  183. }
  184. sub draw_month {
  185. my $month = shift;
  186. my $year = shift;
  187. my @weekday = (T('Su'), T('Mo'), T('Tu'), T('We'),
  188. T('Th'), T('Fr'), T('Sa'));
  189. my ($day, $col, $monthdays, $monthplus, $mod);
  190. my $weekday = zeller(1,$month,$year);
  191. # select the starting day for the week
  192. if ($CalStartMonday){
  193. push @weekday, shift @weekday;
  194. if ($weekday) {
  195. $weekday = $weekday -1;
  196. } else {
  197. $weekday = 6;
  198. }
  199. }
  200. my $start = 1 - $weekday;
  201. my $space_count = int((21 - length(month_name($month).' '.sprintf("%04u",$year)))/2 + 0.5);
  202. # the Cal()-sub needs a 4 digit year working right
  203. my $output = (' ' x $space_count).month_name($month).' '.sprintf("%04u",$year)."\n";
  204. $col = 0;
  205. $monthdays = &month_days($month,&leap_year($year));
  206. if ((($monthdays-$start) < 42) and (($monthdays-$start) > 35)) {
  207. $monthplus=41 - ($monthdays-$start);
  208. } elsif ((($monthdays-$start)<35) and (($monthdays-$start)>28)) {
  209. $monthplus=34 - ($monthdays-$start);
  210. } else {
  211. $monthplus=0;
  212. }
  213. $output .= join('', map {" ".$_} @weekday);
  214. $output .= "\n";
  215. for ($day=$start;$day<=$monthdays+$monthplus;$day++) {
  216. $col++;
  217. if (($day < 1) or ($day>$monthdays)) {
  218. $output .= ' ';
  219. } else {
  220. $output .= sprintf("%3d", $day);
  221. }
  222. $mod=($col/7)-int($col/7);
  223. if ($mod == 0) {
  224. $output .= "\n";
  225. }
  226. if ($year==1582 and $month==10 and $day==4) {
  227. $day=14;
  228. }
  229. }
  230. $output .= "\n" x (8 - ($output =~ tr/\n//)); # every month has to have 8 lines as output
  231. return $output;
  232. }
  233. # formula of Zeller (Julius Christian Johannes Zeller * 1822, + 1899) for countig the day of week
  234. # only works for all years greater then 0 and can handle 1582 the year Pope Gregor has changed the
  235. # calculation of times from the Julian calendar to the Gregorian calendar
  236. sub zeller {
  237. my $t = shift;
  238. my $m = shift;
  239. my $year = shift;
  240. my ($h,$j,$w);
  241. $h=int($year/100);
  242. $j=$year%100;
  243. if ($m<3) {
  244. $m = $m+10;
  245. if ($j==0) {
  246. $j=99;
  247. $h=$h-1;
  248. } else {
  249. $j=$j-1;
  250. }
  251. } else {
  252. $m=$m-2;
  253. }
  254. if (($year > 0) and ($year < 1582)) {
  255. $w = $t + int((2.61 * $m) - 0.2) + $j + int($j/4) + 5 - $h;
  256. } elsif ($year==1582) {
  257. if ($m > 10) {
  258. $w = $t + int((2.61 * $m) - 0.2) + $j + int($j/4) + 5 - $h;
  259. } elsif ($m==8) {
  260. if ($t>=1 and $t<=4) {
  261. $w = $t + int((2.61 * $m) - 0.2) + $j + int($j/4) + 5 - $h;
  262. } elsif ($t>=15) {
  263. $w = $t + int((2.61 * $m) - 0.2) + $j + int($j/4) + int($h/4) - (2*$h);
  264. }
  265. } elsif ($m <= 10) {
  266. $w = $t + int((2.61 * $m) - 0.2) + $j + int($j/4) + int($h/4) - (2*$h);
  267. }
  268. } elsif ($year > 1582) {
  269. $w = $t + int((2.61 * $m) - 0.2) + $j + int($j/4) + int($h/4) - (2*$h);
  270. }
  271. if (($w % 7) >= 0) {
  272. $w = $w % 7;
  273. } else {
  274. $w = 7 - (-1 * ($w % 7));
  275. }
  276. return $w;
  277. }
  278. sub leap_year {
  279. my $year = shift;
  280. if ((($year % 4)==0) and !((($year % 100)==0) and (($year % 400) != 0))) {
  281. return 1;
  282. } else {
  283. return 0;
  284. }
  285. }
  286. sub month_days {
  287. my $month = shift;
  288. my $leap_year = shift;
  289. my @month_days = (31,28,31,30,31,30,31,31,30,31,30,31);
  290. if (($month == 2) and $leap_year) {
  291. return $month_days[$month - 1] + 1;
  292. } else {
  293. return $month_days[$month - 1];
  294. }
  295. }
  296. sub month_name {
  297. my $month = shift;
  298. my @month_name = (T('January'), T('February'), T('March'), T('April'),
  299. T('May'), T('June'), T('July'), T('August'),
  300. T('September'), T('October'), T('November'),
  301. T('December'));
  302. return $month_name[$month-1];
  303. }