diff --git a/data/_default/WebPreferences.txt b/data/_default/WebPreferences.txt index ea92d34..aadda92 100644 --- a/data/_default/WebPreferences.txt +++ b/data/_default/WebPreferences.txt @@ -20,6 +20,10 @@ These settings override the defaults for this web only. See [[%SYSTEMWEB%.Defaul * Set WEBBGCOLOR = #DDDDDD * *Note:* This setting is automatically configured when you create a web + * Webmaster details used for separate web registration. If you want people to register for the web, uncomment the registrable field. + * Set WEBMASTER = + * #Set REGISTRABLE = on + * Image, URL and alternate tooltip text of web's logo.%BR% *Note:* Don't add your own local logos to the [[%SYSTEMWEB%.ProjectLogos][ProjectLogos]] topic; create your own logos topic instead. * #Set WEBLOGOIMG = %WEBLOGOIMG% diff --git a/lib/Foswiki.pm b/lib/Foswiki.pm index 6846350..79cef71 100644 --- a/lib/Foswiki.pm +++ b/lib/Foswiki.pm @@ -3795,6 +3795,11 @@ sub WEBLIST { else { push( @list, $aweb ) if ( $this->{store}->webExists($aweb) ); } + if ( $aweb eq 'registrable' ) { + push( @list, + $this->{store}->getListOfWebs( 'registrable', $showWeb ) + ); + } } my @items; diff --git a/lib/Foswiki/Store.pm b/lib/Foswiki/Store.pm index 002aace..e06fd95 100644 --- a/lib/Foswiki/Store.pm +++ b/lib/Foswiki/Store.pm @@ -1677,6 +1677,14 @@ sub getListOfWebs { || !$prefs->getWebPreferencesValue( 'NOSEARCHALL', $_ ) } @webList; } + if ( $filter =~ /\bregistrable\b/ ) + { + my $prefs = $this->{session}->{prefs}; + my $wn = $this->{session}->{webName}; + @webList = + grep { $prefs->getWebPreferencesValue( 'REGISTRABLE', $_ ) + } @webList; + } if ( $filter =~ /\ballowed\b/ ) { my $security = $this->{session}->security; diff --git a/lib/Foswiki/UI/Manage.pm b/lib/Foswiki/UI/Manage.pm index aa2d3a8..d62a156 100644 --- a/lib/Foswiki/UI/Manage.pm +++ b/lib/Foswiki/UI/Manage.pm @@ -95,8 +95,32 @@ sub _action_createweb { my $webName = $session->{webName}; my $query = $session->{request}; my $cUID = $session->{user}; + my $webmaster = '%WIKIWEBMASTER%'; my $newWeb = $query->param('newweb'); + if($Foswiki::cfg{SeparateWebs}){ + $webmaster = $query->param('webmaster'); + $webmaster = Foswiki::Sandbox::untaint( + $webmaster, + sub { + unless($webmaster){ + throw Foswiki::OopsException( 'attention', + def=>'webmaster_missing', + params => [$newWeb] + ); + } + unless ( $session->{users}->getCanonicalUserID($webmaster)){ + throw Foswiki::OopsException( + 'attention', + def => 'invalid_webmaster', + params => [$newWeb, $webmaster] + ); + } + return '%USERSWEB%.'.$webmaster; + } + ); + _action_createWebGroup($session, $newWeb, $webmaster, $cUID); + } # Validate and untaint $newWeb = Foswiki::Sandbox::untaint( @@ -167,12 +191,14 @@ sub _action_createweb { # Set permissions such that only the creating user can modify the # web preferences - ALLOWTOPICCHANGE => $session->{users}->getWikiName($cUID), + ALLOWTOPICCHANGE => '%USERSWEB%.'.$session->{users}->getWikiName($cUID).', '.$webmaster, ALLOWTOPICRENAME => 'nobody', }; foreach my $p ( $query->param() ) { $opts->{ uc($p) } = $query->param($p); } + #Bit of a hack, but works: + $opts->{WEBMASTER}=$webmaster; my $err = $session->{store}->createWeb( $cUID, $newWeb, $baseWeb, $opts ); if ($err) { @@ -1641,6 +1667,19 @@ sub _action_restoreRevision { Foswiki::UI::Edit::edit($session); } +sub _action_createWebGroup { + my ($session, $webName, $webmaster, $user) = @_; + if($session->{users}->supportsGroupManipulation()){ + my $webGroupName = $webName.'Group'; + $session->logger->log("creating group $webName"); + $session->{users}->createGroup($webName, $user, 'WebGroupTemplate', {WEBMASTER=>$webmaster}); + } else { + $session->logger->log("skipping creating group $webName"); + } + +} + + 1; __DATA__ # Module of Foswiki - The Free and Open Source Wiki, http://foswiki.org/ diff --git a/lib/Foswiki/UI/Register.pm b/lib/Foswiki/UI/Register.pm index d5b227d..c5884b3 100644 --- a/lib/Foswiki/UI/Register.pm +++ b/lib/Foswiki/UI/Register.pm @@ -247,7 +247,7 @@ sub _registerSingleBulkUser { # Add the user to the user management system. May throw an exception my $cUID = $users->addUser( $row->{LoginName}, $row->{WikiName}, - $row->{Password}, $row->{Email} + $row->{Password}, $row->{Email}, ); $log .= "$b1 $row->{WikiName} has been added to the password and user mapping managers\n"; @@ -392,7 +392,31 @@ sub _requireVerification { my $em = $data->{Email}; - if ( $Foswiki::cfg{EnableEmail} ) { + if ( $Foswiki::cfg{EnableEmail} && $Foswiki::cfg{SeparateWebs} ) { + $data->{Webmaster} = $session->{prefs}->getWebPreferencesValue('WEBMASTER', $data->{Web}) || + '%WIKIWEBMASTER%' ; + my $user = $session->{users}->getCanonicalUserID( $data->{Webmaster}); + if(!$user){ + $data->{Webmaster} = '%WIKIWEBMASTERNAME%'; + $data->{WebmasterEmail} = '%WIKIWEBMASTER%'; + } else { + my @userEmails = $session->{users}->getEmails($user); + $data->{WebmasterEmail} = $userEmails[-1]; + } + my $err = _sendEmail( $session, 'registerconfirmtowebadmin', $data ); + + if ($err) { + throw Foswiki::OopsException( + 'attention', + def => 'registration_mail_failed', + web => $data->{webName}, + topic => $topic, + params => [ $em, $err ] + ); + } else { + $err = _sendEmail( $session, 'registerpreconfirm', $data ); + } + } elsif ( $Foswiki::cfg{EnableEmail} ) { my $err = _sendEmail( $session, 'registerconfirm', $data ); if ($err) { @@ -418,14 +442,25 @@ sub _requireVerification { ); } - throw Foswiki::OopsException( - 'attention', - status => 200, - def => 'confirm', - web => $data->{webName}, - topic => $topic, + if($Foswiki::cfg{SeparateWebs}){ + throw Foswiki::OopsException( + 'attention', + status => 200, + def => 'confirmsenttowebadmin', + web => $data->{webName}, + topic => $topic, + params => [$data->{Web}] + ); + } else { + throw Foswiki::OopsException( + 'attention', + status => 200, + def => 'confirm', + web => $data->{webName}, + topic => $topic, params => [$em] - ); + ); + } } =begin TML @@ -895,6 +930,7 @@ sub complete { } my $users = $session->{users}; + ($data->{Group} = $data->{Web}) =~ s/(Group)?$/Group/; try { unless ( defined($data->{Password}) ) { #SMELL: should give consideration to disabling $Foswiki::cfg{Register}{HidePasswd} @@ -907,7 +943,7 @@ sub complete { my $cUID = $users->addUser( $data->{LoginName}, $data->{WikiName}, - $data->{Password}, $data->{Email} + $data->{Password}, $data->{Email}, $data->{Group} ); my $log = _createUserTopic( $session, $data ); $users->setEmails( $cUID, $data->{Email} ); @@ -1395,6 +1431,9 @@ sub _sendEmail { $p->{Name} ||= $p->{WikiName}; $text =~ s/%LOGINNAME%/$p->{LoginName}/geo; $text =~ s/%FIRSTLASTNAME%/$p->{Name}/go; + $text =~ s/%WEBMASTER%/$p->{Webmaster}/go; + $text =~ s/%WEBMASTEREMAIL%/$p->{WebmasterEmail}/go; + $text =~ s/%WEBTOJOIN%/$p->{Web}/go; $text =~ s/%WIKINAME%/$p->{WikiName}/geo; $text =~ s/%EMAILADDRESS%/$p->{Email}/go; $text =~ s/%INTRODUCTION%/$p->{Introduction}/go; diff --git a/lib/Foswiki/Users.pm b/lib/Foswiki/Users.pm index 6d500b9..159920b 100644 --- a/lib/Foswiki/Users.pm +++ b/lib/Foswiki/Users.pm @@ -231,6 +231,19 @@ sub supportsRegistration { =begin TML +---++ ObjectMethod supportGroupManipulation () -> true +return 1 if the UserMapper supports group manipulation + +=cut + +sub supportsGroupManipulation { + my ($this) = @_; + return $this->{mapping}->supportsGroupManipulation(); +} + + +=begin TML + ---++ ObjectMethod initialiseUser ($login) -> $cUID Given a login (which must have been authenticated) determine the cUID that @@ -336,7 +349,7 @@ by Foswiki to identify the user. =cut sub addUser { - my ( $this, $login, $wikiname, $password, $emails ) = @_; + my ( $this, $login, $wikiname, $password, $emails, $group ) = @_; my $removeOnFail = 0; ASSERT( $login || $wikiname ) if DEBUG; # must have at least one @@ -345,7 +358,10 @@ sub addUser { # manager. my $cUID = $this->{mapping}->addUser( $login, $wikiname, $password, $emails ); - + if( $group ){ + $this->{session}->logger->log("addUser", $group, $cUID); + $this->addUserToGroup($cUID, $group); + } # update the cached values $this->{cUID2Login}->{$cUID} = $login; $this->{cUID2WikiName}->{$cUID} = $wikiname; @@ -356,6 +372,31 @@ sub addUser { return $cUID; } +sub addUserToGroup { + my ($this, $cUID, $groupName) = @_; + #ASSERT($cUID) if DEBUG; + + #TODO: Do something with the error + + if($this->{mapping}->supportsGroupManipulation()){ + + $this->{session}->logger->log("addUserToGroup", $groupName, $cUID); + $this->{mapping}->addUserToGroup($cUID, $groupName); + } else { + throw Error::Simple('User mapper does not support Group manipulation'); + } +} + +sub createGroup { + my ($this, $cUID, $groupName, $groupTemplate, $substitutions) = @_; + + if($this->{mapping}->supportsGroupManipulation()){ + $this->{mapping}->createGroup($cUID, $groupName, $groupTemplate, $substitutions); + } else { + throw Error::Simple('User mapper does not support Group manipulation'); + } +} + =begin TML ---++ StaticMethod mapLogin2cUID( $login ) -> $cUID diff --git a/lib/Foswiki/Users/TopicUserMapping.pm b/lib/Foswiki/Users/TopicUserMapping.pm index 2a1b16f..27a90f6 100644 --- a/lib/Foswiki/Users/TopicUserMapping.pm +++ b/lib/Foswiki/Users/TopicUserMapping.pm @@ -52,6 +52,8 @@ use strict; use Assert; use Error qw( :try ); +require Foswiki::OopsException; + #use Monitor; #Monitor::MonitorMethod('Foswiki::Users::TopicUserMapping'); @@ -68,6 +70,7 @@ for any required Foswiki services. # We declare this as a global variable so we can override it during testing. our $FOSWIKI_USER_MAPPING_ID = ''; + #our $FOSWIKI_USER_MAPPING_ID = 'TestMapping_'; sub new { @@ -140,6 +143,17 @@ sub supportsRegistration { =begin TML +---++ ObjectMethod supportGroupManipulation () -> true +return 1 if the UserMapper supports group manipulation + +=cut + +sub supportsGroupManipulation { + return 1; +} + +=begin TML + ---++ ObjectMethod handlesUser ( $cUID, $login, $wikiname) -> $boolean Called by the Foswiki::Users object to determine which loaded mapping @@ -385,7 +399,7 @@ sub addUser { } elsif ( $line =~ /^\s+\*\s([A-Z]) - / ) { - # * A - - - - -^M + # * A - - - - -^M $name = $1; $insidelist = 1; } @@ -453,6 +467,133 @@ sub addUser { =begin TML +---++ ObjectMethod addUserToGroup( $cUID, $groupName ) -> $boolean + + +Adds user to Group + +=cut + +sub addUserToGroup{ + my ($this, $cUID, $groupName) = @_; + my $store = $this->{session}->{store}; + my $users = $this->{session}->{users}; + my $userName = $this->getWikiName($cUID); + my ( $meta, $text ); + my $session = $this->{session}; + $session->logger->log('addUserToGroup', $groupName, $cUID, $userName); + my $sessionUser = $session->{user}; + if ($sessionUser eq $Foswiki::cfg{DefaultUserWikiName} || $sessionUser eq $users->getCanonicalUserID( + $Foswiki::cfg{DefaultUserWikiName}) + ){ + $sessionUser = $session->{users}->getCanonicalUserID($Foswiki::cfg{Register}{RegistrationAgentWikiName}); + $session->logger->log("using $sessionUser for group registration") if DEBUG; + } + my ( $groupWeb, $groupTopic ) = $session->normalizeWebTopicName( $Foswiki::cfg{UsersWebName}, $groupName); + #untaint $groupTopic + if( $groupTopic =~/(\w+Group)$/){ + $groupTopic=$1; + } else { + $groupTopic = $1.'Group'; + } + if ( $store->topicExists( $groupWeb, $groupTopic )) + { + ( $meta, $text ) = $store->readTopic( + undef, + $groupWeb, $groupTopic + ); + } else { + $this->createGroup($groupTopic, $sessionUser, 'WebGroupTemplate', {WEBMASTER=>''}); + ( $meta, $text ) = $store->readTopic( + undef, + $groupWeb, $groupTopic + ); + } + if($groupTopic eq $Foswiki::cfg{SuperAdminGroup}){ + throw Error::Simple('Cannot add person to admin group via a script. Needs to be done manually'); + } + my $before = ''; + my $groupSetting = ''; + my $after = ''; + foreach my $line ( split( /\r?\n/, $text) ){ + if( $line =~ /$Foswiki::regex{setRegex}GROUP\s+=\s*(.+)$/ ){ + next unless ($1 eq 'Set' ); + # Note : if there are multiple GROUP assignments only + # the last will be taken. + if ($groupSetting eq ''){ + $groupSetting = $line; + } else { + $before .= $line."\n".$after; + $after = ''; + $groupSetting = $line; + } + } else { + if ($groupSetting eq ''){ + $before .= $line."\n"; + } else { + $after .= $line."\n"; + } + } + } + if ($groupSetting ne '') { + #TODO: should probably normalise for saftey. + $groupSetting .= ', '.$userName."\n"; + $text = $before.$groupSetting.$after; + $session->logger->log("$sessionUser--, $groupWeb-,- $groupTopic-,- $text-,- $meta"); + # If there is an access control violation this will throw. + try { + $store->saveTopic( $sessionUser, $groupWeb, $groupTopic, + $text, $meta, { minor => 1, comment => "adding $userName to $groupName" } ); + $session->logger->log("added $userName to $groupName"); + } catch Foswiki::AccessControlException with { + my $e = shift; + $session->logger->log("ERROR1: cannot Save $groupName topic (".$e->stringify( $session ).")"); + } catch Foswiki::OopsException with { + my $e = shift; + $session->logger->log("ERROR2: cannot Save $groupName topic (".$e->stringify( $session ).")"); + } catch Error::Simple with { + my $e = shift; + $session->logger->log("ERROR3: fallback cannot Save $groupName topic (".$e.")"); + } + } else { + #error, not an ok GROUP topic + $session->logger->log("ERROR4: cannot add $userName to $groupName - GROUP setting not valid"); + } +} + +sub createGroup{ + my ($this, $groupName, $user, $groupTemplate, $substitutions) = @_; + my $session = $this->{session}; + my $store = $session->{store}; + my ( $groupWeb, $groupTopic ) = $session->normalizeWebTopicName( $Foswiki::cfg{UsersWebName}, $groupName); + $groupTopic = Foswiki::Sandbox::untaint( + $groupTopic, + sub { + $groupTopic =~ s/(Group)?$/Group/; + return $groupTopic; + } + ); + my $groupTemplate = $groupTemplate || $Foswiki::cfg{GroupTemplateName} || 'GroupTemplate'; + my ( $meta, $text) = $store->readTopic( + undef, + $Foswiki::cfg{UsersWebName}, + $groupTemplate + ); + if($substitutions){ + foreach(keys(%$substitutions)){ + $text =~ s/\%$_\%/$substitutions->{$_}/go; + } + } + + + $store->saveTopic( + $user, $groupWeb, $groupTopic, $text, $meta, {minor =>1} ); + +} + + +=begin TML + ---++ ObjectMethod removeUser( $cUID ) -> $boolean Delete the users entry. Removes the user from the password diff --git a/templates/messages.tmpl b/templates/messages.tmpl index 1f6cf06..dc22117 100644 --- a/templates/messages.tmpl +++ b/templates/messages.tmpl @@ -118,6 +118,17 @@ %MAKETEXT{"Please go back in your browser and try again."}% %TMPL:END% + +%TMPL:DEF{"webmaster_missing"}% +---+++ %MAKETEXT{"Webmaster Missing"}% +%MAKETEXT{"Cannot create [_1] because !WebMaster wasn't defined." args="%PARAM1%"}% +%TMPL:END% + +%TMPL:DEF{"invalid_webmaster"}% +---+++ %MAKETEXT{"Invalid webmaster"}% +%MAKETEXT{"Cannot create [_1] because [_2] is not a valid wiki user." args="%PARAM1%, %PARAM2%"}% +%TMPL:END% + %TMPL:DEF{"web_exists"}% ---+++ %MAKETEXT{"Cannot create web [_1] because it already exists" args="\"%PARAM1%\""}% %MAKETEXT{"Please go back in your browser and try again."}% @@ -208,6 +219,12 @@ %TMPL:END% +%TMPL:DEF{"confirmsenttowebadmin"}% +---+++ %MAKETEXT{"Thank you for registering"}% + +%MAKETEXT{"Your activation code has been sent to the webmaster for the [_1] Web of the [_2]. Once he/she has verified you as a genuine user, you will receive an email with verification details." args="%PARAM1%, %WIKITOOLNAME%"}% + +%TMPL:END% %TMPL:DEF{"no_users_to_reset"}% ---+++ %MAKETEXT{"Password reset failed"}% %MAKETEXT{"No users to reset passwords for."}% @@ -534,4 +551,4 @@ %MAKETEXT{"Contact [_1] if you have any questions." args="%WIKIWEBMASTER%"}% -%TMPL:END% \ No newline at end of file +%TMPL:END% diff --git a/templates/registerconfirmtowebadmin.tmpl b/templates/registerconfirmtowebadmin.tmpl new file mode 100644 index 0000000..6357c54 --- /dev/null +++ b/templates/registerconfirmtowebadmin.tmpl @@ -0,0 +1,22 @@ +From: %WIKIWEBMASTERNAME% <%WIKIWEBMASTER%> +To: %WEBMASTER% <%WEBMASTEREMAIL%> +Reply-to: %FIRSTLASTNAME% <%EMAILADDRESS%> +Subject: %MAKETEXT{" [_1] needs verification on the [_2]" args="%FIRSTLASTNAME%, %WIKITOOLNAME%"}% +BCC: %WIKIWEBMASTER% +MIME-Version: 1.0 +Content-Type: text/plain; charset=%CHARSET% +Content-Transfer-Encoding: 7bit +%MAKETEXT{"[_1] (email [_2] )" args="%FIRSTLASTNAME%, %EMAILADDRESS%"}% +%MAKETEXT{"has registered for the [_1] web. His/her verification code" args="%WEBTOJOIN%"}% +%MAKETEXT{"is [_1]" args="%VERIFICATIONCODE%"}% + +%MAKETEXT{"Please reply to this email with the following information:"}% + +%MAKETEXT{"WikiName: [_1] " args="%FIRSTLASTNAME%"}% +%MAKETEXT{"Verification Code: [_1] " args="%VERIFICATIONCODE%"}% +%MAKETEXT{"Link to verify account: [_1] " args=" +%SCRIPTURL{"register"}%?action=verify;code=%VERIFICATIONCODE%"}% + +%MAKETEXT{"Note:"}% +%MAKETEXT{"If [_1] isn't someone you want on your web in the [_2], please inform him/her ([_3]) and [_4] ([_5]) ." args="%FIRSTLASTNAME%, %WIKITOOLNAME%, %EMAILADDRESS%, %WIKIWEBMASTERNAME%, %WIKIWEBMASTER%"}% + diff --git a/templates/registernotifywebadmin.tmpl b/templates/registernotifywebadmin.tmpl new file mode 100644 index 0000000..9d7b38d --- /dev/null +++ b/templates/registernotifywebadmin.tmpl @@ -0,0 +1,20 @@ +%{ This is a default template }%From: %WIKIWEBMASTERNAME% <%WIKIWEBMASTER%> +To: %WEBMASTER% <%WEBMASTEREMAIL%> +From: %WIKIWEBMASTER% +BCC: %WIKIWEBMASTERNAME% <%WIKIWEBMASTER%> +Subject: %MAKETEXT{"[_1] - Registration for [_2] ([_3])" args="%WIKITOOLNAME%, %WIKINAME%, %EMAILADDRESS%"}% +MIME-Version: 1.0 +Content-Type: text/plain; charset=%CHARSET% +Content-Transfer-Encoding: 8bit + +%MAKETEXT{"This is an automated e-mail notification of user registration in [_1]." args="%WIKITOOLNAME%"}% + +%MAKETEXT{"[_1] has been registered with e-mail [_2]" args="%WIKINAME%, %EMAILADDRESS%"}% + +%MAKETEXT{"Submitted content:"}% + +%FORMDATA% + +%MAKETEXT{"Saved to:"}% + +%SCRIPTURL{"view"}%/%USERSWEB%/%WIKINAME% diff --git a/templates/registerpreconfirm.tmpl b/templates/registerpreconfirm.tmpl new file mode 100644 index 0000000..3b62b81 --- /dev/null +++ b/templates/registerpreconfirm.tmpl @@ -0,0 +1,20 @@ +From: %WIKIWEBMASTERNAME% <%WIKIWEBMASTER%> +To: %FIRSTLASTNAME% <%EMAILADDRESS%> +BCC: %WEBMASTER% +Subject: %MAKETEXT{"You have signed up to [_1]" args="%WIKITOOLNAME%"}% +MIME-Version: 1.0 +Content-Type: text/plain; charset=%CHARSET% +Content-Transfer-Encoding: 7bit + +%MAKETEXT{"Thank you for registering in the [_1] collaboration platform. Your verification code has + been sent to the WebMaster of the web you signed up for." args="%WIKITOOLNAME%"}% + + %MAKETEXT{"Hopefully, he or she will forward you a confirmation code and address with which you can + verify that you are a genuine user of the [_1]" args="%WIKITOOLNAME%"}% + + %MAKETEXT{"Note:"}% + %MAKETEXT{"If you got this e-mail by mistake: Somebody ([_1]) registered at the [_2] site using + your mail address [_3]. Contact [_4] if this is in error." args="%FIRSTLASTNAME%, %WIKITOOLNAME%, %EMAILADDRESS%, %WIKIWEBMASTER%"}% + + +