- Timestamp:
- 10/15/09 19:28:59 (3 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/Release01x00/MailerContrib/lib/Foswiki/Contrib/MailerContrib.pm
r5033 r5269 15 15 use strict; 16 16 17 use URI ;17 use URI (); 18 18 use CGI qw(-any); 19 19 20 require Foswiki; 21 require Foswiki::Plugins; 22 require Foswiki::Time; 23 require Foswiki::Func; 24 require Foswiki::Contrib::MailerContrib::WebNotify; 25 require Foswiki::Contrib::MailerContrib::Change; 26 require Foswiki::Contrib::MailerContrib::UpData; 27 28 use vars qw ( $VERSION $RELEASE $verbose ); 29 30 $VERSION = '$Rev$'; 31 $RELEASE = '20 Sep 2009'; 20 use Foswiki (); 21 use Foswiki::Plugins (); 22 use Foswiki::Time (); 23 use Foswiki::Func (); 24 use Foswiki::Contrib::MailerContrib::WebNotify (); 25 use Foswiki::Contrib::MailerContrib::Change (); 26 use Foswiki::Contrib::MailerContrib::UpData (); 27 28 our $VERSION = '$Rev$'; 29 our $RELEASE = '15 Oct 2009'; 30 our $SHORTDESCRIPTION = 'Supports e-mail notification of changes'; 31 32 our $verbose = 0; 32 33 33 34 # PROTECTED STATIC ensure the contrib is initernally initialised … … 54 55 55 56 sub mailNotify { 56 my ( $webs, $twiki, $noisy, $exwebs ) = @_;57 my ( $webs, $twiki, $noisy, $exwebs ) = @_; 57 58 58 59 $verbose = $noisy; 59 60 60 61 my $webstr; 61 if ( defined( $webs )) {62 if ( defined($webs) ) { 62 63 $webstr = join( '|', @$webs ); 63 64 } 64 $webstr = '*' unless ( $webstr);65 $webstr = '*' unless ($webstr); 65 66 $webstr =~ s/\*/\.\*/g; 66 67 67 68 my $exwebstr = ''; 68 if ( defined( $exwebs )) {69 if ( defined($exwebs) ) { 69 70 $exwebstr = join( '|', @$exwebs ); 70 71 } 71 72 $exwebstr =~ s/\*/\.\*/g; 72 73 73 if ( !defined $twiki) {74 if ( !defined $twiki ) { 74 75 $twiki = new Foswiki(); 75 76 } … … 87 88 88 89 my $report = ''; 89 foreach my $web ( Foswiki::Func::getListOfWebs( 'user ') ) {90 if ( $web =~ /^($webstr)$/ && $web !~ /^($exwebstr)$/ ) {91 $report .= _processWeb( $twiki, $web );92 }90 foreach my $web ( Foswiki::Func::getListOfWebs('user ') ) { 91 if ( $web =~ /^($webstr)$/ && $web !~ /^($exwebstr)$/ ) { 92 $report .= _processWeb( $twiki, $web ); 93 } 93 94 } 94 95 … … 103 104 104 105 sub changeSubscription { 105 my ($defaultWeb, $who, $topicList, $unsubscribe) = @_; 106 107 #we can get away with a normalise on a list of topics, so long as the list starts with a topic 108 my ($web, $t) = Foswiki::Func::normalizeWebTopicName($defaultWeb, $topicList); 106 my ( $defaultWeb, $who, $topicList, $unsubscribe ) = @_; 107 108 #we can get away with a normalise on a list of topics, so long as the list starts with a topic 109 my ( $web, $t ) = 110 Foswiki::Func::normalizeWebTopicName( $defaultWeb, $topicList ); 111 109 112 #TODO: this limits us to subscribing to one web. 110 113 my $wn = new Foswiki::Contrib::MailerContrib::WebNotify( … … 115 118 } 116 119 117 118 120 =pod 119 121 ---+++ isSubscribedTo ($web, $who, $topicList) -> boolean … … 126 128 127 129 sub isSubscribedTo { 128 my ( $defaultWeb, $who, $topicList) = @_;129 130 my ( $defaultWeb, $who, $topicList ) = @_; 131 130 132 my $subscribed = { 131 currentWeb=>$defaultWeb, 132 topicSub=>\&_isSubscribedToTopic 133 }; 134 135 my $ret = Foswiki::Contrib::MailerContrib::parsePageList($subscribed, $who, $topicList); 136 137 return (!defined($subscribed->{not_subscribed}) || 138 (0 == scalar($subscribed->{not_subscribed})) ); 139 } 133 currentWeb => $defaultWeb, 134 topicSub => \&_isSubscribedToTopic 135 }; 136 137 my $ret = parsePageList( $subscribed, $who, $topicList ); 138 139 return ( !defined( $subscribed->{not_subscribed} ) 140 || ( 0 == scalar( $subscribed->{not_subscribed} ) ) ); 141 } 142 140 143 sub _isSubscribedToTopic { 141 144 my ( $subscribed, $who, $unsubscribe, $topic, $options, $childDepth ) = @_; 142 145 143 146 require Foswiki::Contrib::MailerContrib::WebNotify; 144 my ($sweb, $stopic) = Foswiki::Func::normalizeWebTopicName($subscribed->{currentWeb}, $topic); 145 146 #TODO: extract this code so we only create $wn objects for each web once.. 147 my $wn = new Foswiki::Contrib::MailerContrib::WebNotify( $Foswiki::Plugins::SESSION, $sweb, $Foswiki::cfg{NotifyTopicName} ); 147 my ( $sweb, $stopic ) = 148 Foswiki::Func::normalizeWebTopicName( $subscribed->{currentWeb}, $topic ); 149 150 #TODO: extract this code so we only create $wn objects for each web once.. 151 my $wn = new Foswiki::Contrib::MailerContrib::WebNotify( 152 $Foswiki::Plugins::SESSION, $sweb, $Foswiki::cfg{NotifyTopicName} ); 148 153 my $subscriber = $wn->getSubscriber($who); 149 150 my $db = new Foswiki::Contrib::MailerContrib::UpData( $Foswiki::Plugins::SESSION, $sweb ); 154 155 my $db = 156 new Foswiki::Contrib::MailerContrib::UpData( $Foswiki::Plugins::SESSION, 157 $sweb ); 158 151 159 #TODO: need to check $childDepth topics too (somehow) 152 if ( $subscriber->isSubscribedTo($stopic, $db) && 153 (!$subscriber->isUnsubscribedFrom($stopic, $db))) { 154 push(@{$subscribed->{subscribed}}, $stopic); 155 } else { 156 push(@{$subscribed->{not_subscribed}}, $stopic); 157 } 158 return ''; 160 if ( $subscriber->isSubscribedTo( $stopic, $db ) 161 && ( !$subscriber->isUnsubscribedFrom( $stopic, $db ) ) ) 162 { 163 push( @{ $subscribed->{subscribed} }, $stopic ); 164 } 165 else { 166 push( @{ $subscribed->{not_subscribed} }, $stopic ); 167 } 159 168 } 160 169 161 170 =pod 171 162 172 ---+++ sub parsePageList ( $object, $who, $spec, $unsubscribe ) => unprocessable remainder of $spec line 163 calls the $topicSub (ref to sub)once per identified topic entry.164 * $object ( is a hashref) can be used to set status' and its definition is dependent on $topicSub165 * $object->{topicSub} _must_ be a sub ref and _must_ return an empty string166 * $unsubscribe can be set to '-' to force an unsubscription (used by SubscribePlugin)167 168 $object is a functor.173 Calls the $object->{topicSub} once per identified topic entry. 174 * $object (a hashref) may be a hashref that has the field, =topicSub=, 175 which _may_ be a sub ref as follows: 176 =&topicSub($object, $who, $unsubscribe, $webTopic, $options, $childDepth)= 177 * =$unsubscribe= can be set to '-' to force an unsubscription 178 (used by SubscribePlugin) 169 179 170 180 =cut … … 172 182 sub parsePageList { 173 183 my ( $object, $who, $spec, $unsubscribe ) = @_; 184 174 185 #ASSERT(defined($object->{topicSub})); 175 176 return $spec if ( !defined($object->{topicSub}));177 186 187 return $spec if ( !$object || !defined( $object->{topicSub} ) ); 188 178 189 $spec =~ s/,/ /g; 179 #TODO: refine the $2 regex to be proper web.topic/topic/* style.. 180 while ($spec =~ s/^\s*([+-])?\s*([\w.\*]+)([!?]?)\s*(?:\((\d+)\))?/&{$object->{topicSub}}($object, $who, $unsubscribe||$1, $2, $3, $4)/e) { 181 #go 190 191 # $1: + or -, optional 192 # $2: the wildcarded topic specifier (may be quoted) 193 # TODO: refine the $2 regex to be proper web.topic/topic/* style.. 194 # $3: options 195 # $4: child depth 196 while ( $spec =~ 197 s/^\s*([+-])?\s*([*\w.]+|'.*?'|".*?")([!?]?)\s*(?:\((\d+)\))?//) { 198 my ( $us, $webTopic, $options, $childDepth ) = ( 199 $unsubscribe||$1||'+', $2, $3, $4||0 ); 200 $webTopic =~ s/^(['"])(.*)\1$/$2/; # remove quotes 201 &{$object->{topicSub}}( 202 $object, $who, $us, $webTopic, $options, $childDepth); 203 #go 182 204 } 183 205 return $spec; 184 206 } 185 186 207 187 208 # PRIVATE: Read the webnotify, and notify changes 188 209 sub _processWeb { 189 my( $twiki, $web) = @_; 190 191 if( ! Foswiki::Func::webExists( $web ) ) { 192 # print STDERR "**** ERROR mailnotifier cannot find web $web\n"; 210 my ( $twiki, $web ) = @_; 211 212 if ( !Foswiki::Func::webExists($web) ) { 213 214 # print STDERR "**** ERROR mailnotifier cannot find web $web\n"; 193 215 return ''; 194 216 } … … 199 221 200 222 # Read the webnotify and load subscriptions 201 my $wn = new Foswiki::Contrib::MailerContrib::WebNotify( 202 $twiki, $web, $Foswiki::cfg{NotifyTopicName} ); 223 my $wn = 224 new Foswiki::Contrib::MailerContrib::WebNotify( $twiki, $web, 225 $Foswiki::cfg{NotifyTopicName} ); 203 226 if ( $wn->isEmpty() ) { 204 227 print "\t$web has no subscribers\n" if $verbose; 205 } else { 228 } 229 else { 230 206 231 # create a DB object for parent pointers 207 232 print $wn->stringify(1) if $verbose; … … 223 248 224 249 my $timeOfLastNotify = 0; 225 if ( open(F, "<$notmeta")) {250 if ( open( F, '<', $notmeta ) ) { 226 251 local $/ = undef; 227 252 $timeOfLastNotify = <F>; … … 229 254 } 230 255 231 if ( $verbose) {232 print "\tLast notification was at " .233 Foswiki::Time::formatTime( $timeOfLastNotify, 'iso' ). "\n";256 if ($verbose) { 257 print "\tLast notification was at " 258 . Foswiki::Time::formatTime( $timeOfLastNotify, 'iso' ) . "\n"; 234 259 } 235 260 … … 252 277 my %allSet; 253 278 254 if ( !defined( &Foswiki::Func::eachChangeSince )) {279 if ( !defined(&Foswiki::Func::eachChangeSince) ) { 255 280 require Foswiki::Contrib::MailerContrib::CompatibilityHacks; 256 281 } … … 258 283 # + 1 because the 'since' check is >= 259 284 my $it = Foswiki::Func::eachChangeSince( $web, $timeOfLastNotify + 1 ); 260 while ( $it->hasNext() ) {285 while ( $it->hasNext() ) { 261 286 my $change = $it->next(); 262 287 next if $change->{more} && $change->{more} =~ /minor$/; … … 264 289 next unless Foswiki::Func::topicExists( $web, $change->{topic} ); 265 290 266 $timeOfLastChange = $change->{time} unless( $timeOfLastChange ); 267 268 print "\tChange to $change->{topic} at ". 269 Foswiki::Time::formatTime( $change->{time}, 'iso' ). 270 ". New revision is $change->{revision}\n" if ( $verbose ); 291 $timeOfLastChange = $change->{time} unless ($timeOfLastChange); 292 293 print "\tChange to $change->{topic} at " 294 . Foswiki::Time::formatTime( $change->{time}, 'iso' ) 295 . ". New revision is $change->{revision}\n" 296 if ($verbose); 271 297 272 298 # Formulate a change record, irrespective of 273 299 # whether any subscriber is interested 274 $change = new Foswiki::Contrib::MailerContrib::Change( 275 $twiki, $web, $change->{topic}, $change->{user}, 276 $change->{time}, $change->{revision} ); 300 $change = 301 new Foswiki::Contrib::MailerContrib::Change( $twiki, $web, 302 $change->{topic}, $change->{user}, $change->{time}, 303 $change->{revision} ); 277 304 278 305 # Now, find subscribers to this change and extend the change set 279 $notify->processChange( 280 $change, $db, \%changeset, \%seenset, \%allSet ); 281 } 306 $notify->processChange( $change, $db, \%changeset, \%seenset, 307 \%allSet ); 308 } 309 282 310 # For each topic, see if there's a compulsory subscription independent 283 311 # of the time since last notify 284 foreach my $topic ( Foswiki::Func::getTopicList($web)) {312 foreach my $topic ( Foswiki::Func::getTopicList($web) ) { 285 313 $notify->processCompulsory( $topic, $db, \%allSet ); 286 314 } 287 315 288 316 # Now generate emails for each recipient 289 my $report = _sendChangesMails(290 $twiki, $web, \%changeset,317 my $report = 318 _sendChangesMails( $twiki, $web, \%changeset, 291 319 Foswiki::Time::formatTime($timeOfLastNotify) ); 292 320 293 $report .= _sendNewsletterMails( $twiki, $web, \%allSet );294 295 if ( $timeOfLastChange != 0) {296 if ( open(F, ">$notmeta" )) {321 $report .= _sendNewsletterMails( $twiki, $web, \%allSet ); 322 323 if ( $timeOfLastChange != 0 ) { 324 if ( open( F, '>', $notmeta ) ) { 297 325 print F $timeOfLastChange; 298 326 close(F); … … 308 336 my $report = ''; 309 337 338 # We read the mailnotify template in the context (skin and web) or the 339 # WebNotify topic we are currently processing 340 Foswiki::Func::pushTopicContext( $web, $Foswiki::cfg{NotifyTopicName} ); 310 341 my $skin = Foswiki::Func::getSkin(); 311 342 my $template = Foswiki::Func::readTemplate( 'mailnotify', $skin ); 343 Foswiki::Func::popTopicContext(); 312 344 313 345 my $homeTopic = $Foswiki::cfg{HomeTopicName}; 314 346 315 my $before_html = Foswiki::Func::expandTemplate( 'HTML:before' ); 316 my $middle_html = Foswiki::Func::expandTemplate( 'HTML:middle' ); 317 my $after_html = Foswiki::Func::expandTemplate( 'HTML:after' ); 318 319 my $before_plain = Foswiki::Func::expandTemplate( 'PLAIN:before' ); 320 my $middle_plain = Foswiki::Func::expandTemplate( 'PLAIN:middle' ); 321 my $after_plain = Foswiki::Func::expandTemplate( 'PLAIN:after' ); 322 323 my $mailtmpl = Foswiki::Func::expandTemplate( 'MailNotifyBody' ); 324 $mailtmpl = Foswiki::Func::expandCommonVariables( 325 $mailtmpl, $homeTopic, $web ); 326 if( $Foswiki::cfg{RemoveImgInMailnotify} ) { 347 my $before_html = Foswiki::Func::expandTemplate('HTML:before'); 348 my $middle_html = Foswiki::Func::expandTemplate('HTML:middle'); 349 my $after_html = Foswiki::Func::expandTemplate('HTML:after'); 350 351 my $before_plain = Foswiki::Func::expandTemplate('PLAIN:before'); 352 my $middle_plain = Foswiki::Func::expandTemplate('PLAIN:middle'); 353 my $after_plain = Foswiki::Func::expandTemplate('PLAIN:after'); 354 355 my $mailtmpl = Foswiki::Func::expandTemplate('MailNotifyBody'); 356 $mailtmpl = 357 Foswiki::Func::expandCommonVariables( $mailtmpl, $homeTopic, $web ); 358 if ( $Foswiki::cfg{RemoveImgInMailnotify} ) { 359 327 360 # change images to [alt] text if there, else remove image 328 361 $mailtmpl =~ s/<img\s[^>]*\balt=\"([^\"]+)[^>]*>/[$1]/goi; … … 333 366 334 367 foreach my $email ( keys %{$changeset} ) { 335 my $html = '';368 my $html = ''; 336 369 my $plain = ''; 337 foreach my $change (sort { $a->{TIME} cmp $b->{TIME} } 338 @{$changeset->{$email}} ) { 339 340 $html .= $change->expandHTML( $middle_html ); 341 $plain .= $change->expandPlain( $middle_plain ); 370 foreach my $change ( sort { $a->{TIME} cmp $b->{TIME} } 371 @{ $changeset->{$email} } ) 372 { 373 374 $html .= $change->expandHTML($middle_html); 375 $plain .= $change->expandPlain($middle_plain); 342 376 } 343 377 … … 363 397 if ($error) { 364 398 print STDERR "Error sending mail forf $web: $error\n"; 365 $report .= $error."\n"; 366 } else { 399 $report .= $error . "\n"; 400 } 401 else { 367 402 print "Notified $email of changes in $web\n" if $verbose; 368 403 $sentMails++; … … 375 410 376 411 sub relativeURL { 377 my ( $base, $link ) = @_;412 my ( $base, $link ) = @_; 378 413 return URI->new_abs( $link, URI->new($base) )->as_string; 379 414 } 380 415 381 416 sub _sendNewsletterMails { 382 my ( $twiki, $web, $allSet) = @_;417 my ( $twiki, $web, $allSet ) = @_; 383 418 384 419 my $report = ''; 385 foreach my $topic ( keys %$allSet) {386 $report .= _sendNewsletterMail(387 $twiki, $web, $topic, $allSet->{$topic});420 foreach my $topic ( keys %$allSet ) { 421 $report .= 422 _sendNewsletterMail( $twiki, $web, $topic, $allSet->{$topic} ); 388 423 } 389 424 return $report; … … 391 426 392 427 sub _sendNewsletterMail { 393 my ( $twiki, $web, $topic, $emails) = @_;428 my ( $twiki, $web, $topic, $emails ) = @_; 394 429 my $wikiName = Foswiki::Func::getWikiName(); 395 430 … … 397 432 398 433 # Read topic data. 399 my ( $meta, $text) = Foswiki::Func::readTopic( $web, $topic );400 401 if ( !defined( &Foswiki::Func::pushTopicContext )) {434 my ( $meta, $text ) = Foswiki::Func::readTopic( $web, $topic ); 435 436 if ( !defined(&Foswiki::Func::pushTopicContext) ) { 402 437 require Foswiki::Contrib::MailerContrib::TopicContext; 403 438 } 439 440 # SMELL: Have to hack into the core to set internal preferences :-( 441 my %old = map { $_ => undef } qw(BASEWEB BASETOPIC INCLUDINGWEB INCLUDINGTOPIC); 442 if (defined $Foswiki::Plugins::SESSION->{SESSION_TAGS}) { 443 444 # In 1.0.6 and earlier, have to handle some session tags ourselves 445 # because pushTopicContext doesn't do it. ** 446 foreach my $macro (keys %old) { 447 $old{$macro} = Foswiki::Func::getPreferencesValue($macro); 448 } 449 } 404 450 Foswiki::Func::pushTopicContext( $web, $topic ); 451 452 # See ** above 453 if (defined $Foswiki::Plugins::SESSION->{SESSION_TAGS}) { 454 my $stags = $Foswiki::Plugins::SESSION->{SESSION_TAGS}; 455 $stags->{BASEWEB} = $web; 456 $stags->{BASETOPIC} = $topic; 457 $stags->{INCLUDINGWEB} = $web; 458 $stags->{INCLUDINGTOPIC} = $topic; 459 } 405 460 406 461 $twiki->enterContext( 'can_render_meta', $meta ); … … 409 464 my $skin = Foswiki::Func::getSkin(); 410 465 Foswiki::Func::readTemplate( 'newsletter', $skin ); 411 my $header = Foswiki::Func::expandTemplate( 'NEWS:header');412 my $body = Foswiki::Func::expandTemplate( 'NEWS:body');413 my $footer = Foswiki::Func::expandTemplate( 'NEWS:footer');414 415 my ( $revdate, $revuser, $maxrev);416 ( $revdate, $revuser, $maxrev) = $meta->getRevisionInfo();466 my $header = Foswiki::Func::expandTemplate('NEWS:header'); 467 my $body = Foswiki::Func::expandTemplate('NEWS:body'); 468 my $footer = Foswiki::Func::expandTemplate('NEWS:footer'); 469 470 my ( $revdate, $revuser, $maxrev ); 471 ( $revdate, $revuser, $maxrev ) = $meta->getRevisionInfo(); 417 472 418 473 # Handle standard formatting. 419 474 $body =~ s/%TEXT%/$text/g; 475 420 476 # Don't render the header, it is preformatted 421 $header = Foswiki::Func::expandCommonVariables( $header, $topic, $web);477 $header = Foswiki::Func::expandCommonVariables( $header, $topic, $web ); 422 478 my $tmpl = "$body\n$footer"; 423 $tmpl = Foswiki::Func::expandCommonVariables( $tmpl, $topic, $web);424 $tmpl = Foswiki::Func::renderText( $tmpl, "", $meta);479 $tmpl = Foswiki::Func::expandCommonVariables( $tmpl, $topic, $web ); 480 $tmpl = Foswiki::Func::renderText( $tmpl, "", $meta ); 425 481 $tmpl = "$header$tmpl"; 426 482 … … 432 488 my $tagSeen = 0; 433 489 my $publish = 1; 434 foreach my $s ( split( /(%STARTPUBLISH%|%STOPPUBLISH%)/, $tmpl ) ) {435 if ( $s eq '%STARTPUBLISH%' ) {490 foreach my $s ( split( /(%STARTPUBLISH%|%STOPPUBLISH%)/, $tmpl ) ) { 491 if ( $s eq '%STARTPUBLISH%' ) { 436 492 $publish = 1; 437 $newTmpl = '' unless ( $tagSeen);493 $newTmpl = '' unless ($tagSeen); 438 494 $tagSeen = 1; 439 } elsif( $s eq '%STOPPUBLISH%' ) { 495 } 496 elsif ( $s eq '%STOPPUBLISH%' ) { 440 497 $publish = 0; 441 498 $tagSeen = 1; 442 } elsif( $publish ) { 499 } 500 elsif ($publish) { 443 501 $newTmpl .= $s; 444 502 } … … 453 511 # Remove <base.../> tag 454 512 $tmpl =~ s/<base[^>]+\/>//; 513 455 514 # Remove <base...>...</base> tag 456 515 $tmpl =~ s/<base[^>]+>.*?<\/base>//; … … 461 520 $tmpl =~ s/(action=\")([^"]+)/$1.relativeURL($base,$2)/goei; 462 521 463 my $report = '';522 my $report = ''; 464 523 my $sentMails = 0; 465 524 … … 482 541 if ($error) { 483 542 print STDERR "Error sending mail for $web: $error\n"; 484 $report .= $error."\n"; 485 } else { 543 $report .= $error . "\n"; 544 } 545 else { 486 546 print "Sent newletter for $web to $email\n" if $verbose; 487 547 $sentMails++; … … 491 551 492 552 Foswiki::Func::popTopicContext(); 553 554 # SMELL: See ** above 555 if (defined $Foswiki::Plugins::SESSION->{SESSION_TAGS}) { 556 557 # In 1.0.6 and earlier, have to handle some session tags ourselves 558 # because pushTopicContext doesn't do it. ** 559 foreach my $macro (keys %old) { 560 $Foswiki::Plugins::SESSION->{SESSION_TAGS}{$macro} = 561 $old{$macro}; 562 } 563 } 493 564 494 565 return $report;
Note: See TracChangeset
for help on using the changeset viewer.
