From: <de...@de...> - 2012-08-23 07:50:04
|
Author: HideyoImazu Date: 2012-08-23 02:49:52 -0500 (Thu, 23 Aug 2012) New Revision: 23194 Trac url: http://develop.twiki.org/trac/changeset/23194 Added: twiki/trunk/core/data/TWiki/ReadOnlyAndMirrorWebs.txt twiki/trunk/core/pub/TWiki/ReadOnlyAndMirrorWebs/ twiki/trunk/core/pub/TWiki/ReadOnlyAndMirrorWebs/bottom-toolbar-w-mirror-to-all.png twiki/trunk/core/pub/TWiki/ReadOnlyAndMirrorWebs/bottom-toolbar-w-view-master.png Modified: twiki/trunk/core/data/TWiki/VarWEBLIST.txt twiki/trunk/core/lib/TWiki.pm twiki/trunk/core/lib/TWiki/Func.pm twiki/trunk/core/lib/TWiki/Store.pm twiki/trunk/core/lib/TWiki/UI.pm twiki/trunk/core/lib/TWiki/UI/Edit.pm twiki/trunk/core/lib/TWiki/UI/Save.pm twiki/trunk/core/lib/TWiki/UI/Upload.pm twiki/trunk/core/lib/TWiki/UI/View.pm twiki/trunk/core/templates/messages.tmpl twiki/trunk/core/templates/moveattachment.tmpl twiki/trunk/core/templates/rename.tmpl twiki/trunk/core/templates/renameweb.tmpl twiki/trunk/core/templates/viewtopicactionbuttons.tmpl Log: Item6916: Read-only and mirror web support Added: twiki/trunk/core/data/TWiki/ReadOnlyAndMirrorWebs.txt =================================================================== --- twiki/trunk/core/data/TWiki/ReadOnlyAndMirrorWebs.txt (rev 0) +++ twiki/trunk/core/data/TWiki/ReadOnlyAndMirrorWebs.txt 2012-08-23 07:49:52 UTC (rev 23194) @@ -0,0 +1,107 @@ +%META:TOPICINFO{author="TWikiContributor" date="1345617845" format="1.1" version="$Rev$"}% +---+!! Read only and mirror web support + +---++ Scope + +This topic describes how to set up read-only webs. +A read-only web can be mirrored from another site. +But how to mirror a web from another site is out of scope. + +---++ Motivation + +There are cases where read-only webs are useful. + * %<nop>SYSTEMWEB% web being a part of installation rather than a content, it may reside in read-only storage. In that case, It's better for TWiki to recognize as such and not to allow update operations rather than update operations causing errors. Even if you don't see an edit or attach link, users may enter an edit or attach URL manually. In that case, the edit and attach scripts should display "operation not allowed" kind of message. + * You may want to run a federation of TWiki sites, in which member sites have read-only mirror of other sites' webs. Read-only mirror webs are not so useful for tight collaboration because of the time lag of mirroring, which typically happens once a day. But it's useful for a web viewed by many users geographically distributed. + +---++ Master and slave + +Let's assume a federated TWiki sites depicted on MetadataRepository#Federation_of_sites. +Each web has one master site and the other sites are slave. +On such a web, updates happen only on the master site and the slaves get those changes when they mirror the web. + +---++ Local webs + +Each site in a federation needs to have some webs locally without sharing them with other federation members - definitely with the Trash web. +It's not critical but the Sandbox web should not be mirrored hence should be local to each site. + +If this feature is not turned on, all webs are regarded local. + +---++ Content modes + +Given the explanation so far, a web is either of the following content modes. +| *Mode name* | *Description* | *Context* | +| local | editable locally and not shared with other sites | | +| master | editable locally and mirrored to other sites | content_master | +| slave | not editable locally and mirrored from the master site | content_slave | +| read-only | cannot be edited | inactive | + +In case you need to know which mode a web is in, the contentMode attribute of the session object has a mode name. +In a skin, you can use the context each mode provides. + +---++ Setting up + +Setting the site's identifier (a few character long alpha-numeric string) to $TWiki::cfg{SiteName} enables you to use this feature. + +There are two ways to set read-only and mirroring related information. +One is to use MetadataRepository and the other is setting preferences on !WebPreferences. + +For a site federation having hundreds or thousands of webs, MetadataRepository should be the only practical option. +But just to make %<nop>SYSTEMWEB% read-only, that's too much. +So you can accomplish that without introducing MetadataReporisory. +That should be handy if you want to try it out before diving into it. + +---+++ !WebPreferences +If you are not using MetadataRepository, you can make a web read-only by setting MASTERSITENAME on !WebPreferences to 'ro' (or 'readonly' or anhything different from $TWiki::cfg{SiteName} value. + +If it's a mirror site copied from another site which is accessible, you need to set MASTERWEBSCRIPTURLTMPL to e.g. http:<nop>//twiki.example.com/cgi-bin//WebName. +If the master site needs a file name extension such as .cgi for CGI programs, you need to set MASTERSCRIPTSUFFIX. + +There are cases where the view URL format is different from URLs for other operations. For example, view URLs may be http:<nop>/twiki.example.com/Web/Topic. +In such cases, you should set MASTERWEBVIEWURL to e.g. http:<nop>/twiki.example.com/Web. +Because this is not the norm, MASTERWEBVIEWURL is optional. + +---+++ Metadata Repository +If you use MetadataRepository, read-only and mirroring related information comes only from MetadataRepository (except with $TWiki::cfg{SiteName}) and preferences on !WebPreferences don't matter. + +The web's 'master' field specifies the master site. +If it's the same as $TWiki::cfg{SiteName}, then the web's content mode is 'master'. +Otherwise, the sites table is referred to get information of the master site. + +If the sites table doesn't have the record for the master site, the web is regarded as 'read-only'. Even if there is a record, if the record doesn't have the 'scripturl' field, the web is still regarded as 'read-only' + +If the 'scripturl' field is present, the web is regarded as 'slave' and the 'scripturl' value is used to make the 'webScriptUrlTmpl' value of the web. +In addition, the 'scriptsuffix' and 'viewurl' fields of the site record are examined. + +---+++ Site-wide preference +A master web is to have a link to mirror the web to all slave sites. +The link destination needs to be set to MIRRORTOALLURL. + +---++ How read-only and mirror webs affect TWiki's behavior + +Besides contexts mentioned [[#Content_modes][above]], read-only and mirror webs affect TWiki's behavior as shown below. + +---+++ Skin +A topic on a read-only web is displayed in the same manner as a topic of a prior revision - Edit, Attach, and More actions links are disabled. + +A topic on a master web is almost as usual. +The only difference from a local web is to have a "Mirror to all" link on the bottom tool bar. + +%ATTACHURL%/bottom-toolbar-w-mirror-to-all.png + +A topic on a slave web looks similar but Edit and Attach links are pointing to the master site. More actions link is disabled. +And "View master" link is added on the bottom tool bar. + +%ATTACHURL%/bottom-toolbar-w-view-master.png + +These changes are made on template/viewtopicactionbuttons.tmpl, which is supposed to be included by all skins. +If you make your skin in a proper manner, those behaviors are automatically implemented. + +---+++ Variables + $ %<nop>READONLYWEB% : Becomes 'on' in 'read-only' mode. Otherwise '' (zero width string). + $ %<nop>MASTERWEBSCRIPTURL{script}% : On a 'slave' web, it returns the URL of the specified script of the web on the master site. If the view URL format is different from other scripts, %<nop>MASTERWEBSCRIPTURL{"view"}% returns the proper URL. %BR% + On a non-slave web, it returns a zero width string. + $ $<nop>WEBLIST{...}% : You can specify webs="canmoveto" parameter, in which case webs to which a topic of the current web cannot be moved to are excluded. + When the read-only and mirror web feature is not in effect, web="canmoveto" is identical to webs="public" because all webs are local in that case. + +%META:FILEATTACHMENT{name="bottom-toolbar-w-view-master.png" attachment="bottom-toolbar-w-view-master.png" attr="" comment="" date="1345617845" path="bottom-toolbar-w-view-master.png" size="9790" user="TWikiContributor" version=""}% +%META:FILEATTACHMENT{name="bottom-toolbar-w-mirror-to-all.png" attachment="bottom-toolbar-w-mirror-to-all.png" attr="" comment="" date="1345617845" path="bottom-toolbar-w-mirror-to-all.png" size="9067" user="TWikiContributor" version=""}% Modified: twiki/trunk/core/data/TWiki/VarWEBLIST.txt =================================================================== --- twiki/trunk/core/data/TWiki/VarWEBLIST.txt 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/data/TWiki/VarWEBLIST.txt 2012-08-23 07:49:52 UTC (rev 23194) @@ -10,7 +10,7 @@ | =format="format"= | (Alternative to above) | ="$name"= | | =separator=", "= | Line separator | ="$n"= (new line) | | =web=""= | if you specify =$web= in format, it will be replaced with this | =""= | - | =webs="public"= | Comma separated list of webs, =public= expands to all non-hidden.%BR% __NOTE:__ Administrators will see all webs, not just the public ones | ="public"= | + | =webs="public"= | Comma separated list of webs, =public= expands to all non-hidden. =canmoveto= is similar to =public=, but webs to which a topic of the current web cannot be moved to are excluded. %BR% __NOTE:__ Administrators will see all webs, not just the public ones. If MetadataRepository is used and webs are required to be registered, then top level webs and subwebs of the current webs are listed regardless of accessibility - subwebs of non-current webs are excluded. | ="public"= | | =marker="selected"= | Text for =$marker= if the item matches =selection= | ="selected"= | | =selection="%<nop>WEB%"= | Current value to be selected in list | =selection="%<nop>WEB%"= | | =subwebs="Sandbox"= | Show webs that are a sub-web of this one (recursivly) | =""= | @@ -18,4 +18,4 @@ | =overlimit="..."= | Message shown if over limit, such as: =overlimit=" * [<nop>[%<nop>SYSTEMWEB%.SiteMap][More...]]"= | =""= | * Example: =%<nop>WEBLIST{" * [<nop>[$name.%HOMETOPIC%]]"}%= - creates a bullet list of all webs. * Example: =<form><select name="web"> %<nop>WEBLIST{"<option $marker value=$qname>$name</option>" webs="Trash, public" selection="%<nop>WEB%" separator=" "}% </select></form>= - creates a dropdown of all public webs + Trash web, with the current web highlighted. - * Related: [[%IF{"'%INCLUDINGTOPIC%'='TWikiVariables'" then="#"}%VarTOPICLIST][TOPICLIST]], [[%IF{"'%INCLUDINGTOPIC%'='TWikiVariables'" then="#"}%VarSEARCH][SEARCH]] + * Related: [[%IF{"'%INCLUDINGTOPIC%'='TWikiVariables'" then="#"}%VarTOPICLIST][TOPICLIST]], [[%IF{"'%INCLUDINGTOPIC%'='TWikiVariables'" then="#"}%VarSEARCH][SEARCH]], MetadataRepository, ReadOnlyAndMirrorWebs Modified: twiki/trunk/core/lib/TWiki/Func.pm =================================================================== --- twiki/trunk/core/lib/TWiki/Func.pm 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/lib/TWiki/Func.pm 2012-08-23 07:49:52 UTC (rev 23194) @@ -1598,8 +1598,7 @@ ASSERT($TWiki::Plugins::SESSION) if DEBUG; my $session = $TWiki::Plugins::SESSION; - my( $mirrorSite, $mirrorViewURL ) = $session->readOnlyMirrorWeb( $web ); - throw Error::Simple('Cannot save on a mirror site') if( $mirrorSite ); + TWiki::UI::checkWritable($session, $web); # check access permission unless( $ignorePermissions || Modified: twiki/trunk/core/lib/TWiki/Store.pm =================================================================== --- twiki/trunk/core/lib/TWiki/Store.pm 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/lib/TWiki/Store.pm 2012-08-23 07:49:52 UTC (rev 23194) @@ -1562,6 +1562,18 @@ return $handler->getTopicNames(); } +# filter out webs to which you cannot move the current web to +sub _filterCanMoveTo { + my ($session, $webListRef) = @_; + my @result; + for my $i ( @$webListRef ) { + my $mode = ($session->modeAndMaster($i))[0]; + push(@result, $i) + if ( $mode eq 'local' || $mode eq 'master' ); + } + return @result; +} + =pod ---++ ObjectMethod getListOfWebs( $filter ) -> @webNames @@ -1593,13 +1605,17 @@ @webList = grep { /(?:^_|\/_)/, } @webList; } + my $session = $this->{session}; if ( $TWiki::cfg{Mdrepo}{WebRecordRequired} && $this->{session}{mdrepo} ) { # skipping public and allowed check because it may take too much # time with thousands of webs + if ( $filter =~ /\bcanmoveto\b/ ) { + @webList = _filterCanMoveTo($session, \@webList); + } return sort @webList; } - my $user = $this->{session}->{user}; + my $user = $session->{user}; if( $filter =~ /\bpublic\b/ && !$this->{session}->{users}->isAdmin( $user )) { my $prefs = $this->{session}->{prefs}; @@ -1620,6 +1636,10 @@ } @webList; } + if( $filter =~ /\bcanmoveto\b/ ) { + @webList = _filterCanMoveTo($session, \@webList); + } + # Only return webs that really exist return sort grep { $this->webExists( $_ ) } @webList; } Modified: twiki/trunk/core/lib/TWiki/UI/Edit.pm =================================================================== --- twiki/trunk/core/lib/TWiki/UI/Edit.pm 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/lib/TWiki/UI/Edit.pm 2012-08-23 07:49:52 UTC (rev 23194) @@ -98,7 +98,7 @@ my $store = $session->{store}; TWiki::UI::checkWebExists( $session, $webName, $topic, 'edit' ); - TWiki::UI::checkMirror( $session, $webName, $topic ); + TWiki::UI::checkWritable( $session ); my $tmpl = ''; my $text = ''; Modified: twiki/trunk/core/lib/TWiki/UI/Save.pm =================================================================== --- twiki/trunk/core/lib/TWiki/UI/Save.pm 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/lib/TWiki/UI/Save.pm 2012-08-23 07:49:52 UTC (rev 23194) @@ -92,7 +92,7 @@ params => [ $script ]); } - TWiki::UI::checkMirror( $session, $webName, $topic ); + TWiki::UI::checkWritable( $session ); TWiki::UI::checkWebExists( $session, $webName, $topic, 'save' ); my $topicExists = $store->topicExists( $webName, $topic ); Modified: twiki/trunk/core/lib/TWiki/UI/Upload.pm =================================================================== --- twiki/trunk/core/lib/TWiki/UI/Upload.pm 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/lib/TWiki/UI/Upload.pm 2012-08-23 07:49:52 UTC (rev 23194) @@ -74,7 +74,7 @@ my $isHideChecked = ''; my $users = $session->{users}; - TWiki::UI::checkMirror( $session, $webName, $topic ); + TWiki::UI::checkWritable( $session ); TWiki::UI::checkAccess( $session, $webName, $topic, 'CHANGE', $session->{user} ); @@ -210,7 +210,7 @@ TWiki::UI::checkWebExists( $session, $webName, $topic, 'attach files to' ); TWiki::UI::checkTopicExists( $session, $webName, $topic, 'attach files to' ); - TWiki::UI::checkMirror( $session, $webName, $topic ); + TWiki::UI::checkWritable( $session ); TWiki::UI::checkAccess( $session, $webName, $topic, 'CHANGE', $user ); my $hideFile = $query->param('hidefile') || ''; Modified: twiki/trunk/core/lib/TWiki/UI/View.pm =================================================================== --- twiki/trunk/core/lib/TWiki/UI/View.pm 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/lib/TWiki/UI/View.pm 2012-08-23 07:49:52 UTC (rev 23194) @@ -178,13 +178,11 @@ $session->writeLog( 'view', $webName.'.'.$topicName, $logEntry ); } - my( $mirrorSiteName, $mirrorViewURL, $mirrorLink, $mirrorNote ) = - $session->readOnlyMirrorWeb( $webName ); - # Note; must enter all contexts before the template is read, as # TMPL:P is expanded on the fly in the template reader. :-( my( $revTitle, $revArg ) = ( '', '' ); - if( $mirrorSiteName ) { + my $mode = $session->{contentMode}; + if( $mode eq 'read-only' ) { $session->enterContext( 'inactive' ); unless( $topicExists ) { $text = ''; @@ -195,6 +193,11 @@ $revTitle = '(r'.$rev.')'; $revArg = '&rev='.$rev; } + if( $mode eq 'slave' ) { + $session->enterContext( 'content_slave' ); + } elsif( $mode eq 'master' ) { + $session->enterContext( 'content_master' ); + } my $template = $query->param( 'template' ) || $session->{prefs}->getPreferencesValue( 'VIEW_TEMPLATE' ) || @@ -218,7 +221,6 @@ params => [ $template, 'VIEW_TEMPLATE' ] ); } - $tmpl =~ s/%REVINFO%/%REVINFO%$mirrorNote/go; $tmpl =~ s/%REVTITLE%/$revTitle/g; $tmpl =~ s/%REVARG%/$revArg/g; Modified: twiki/trunk/core/lib/TWiki/UI.pm =================================================================== --- twiki/trunk/core/lib/TWiki/UI.pm 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/lib/TWiki/UI.pm 2012-08-23 07:49:52 UTC (rev 23194) @@ -334,24 +334,33 @@ =pod twiki ----++ StaticMethod checkMirror( $session, $web, $topic ) +---++ StaticMethod checkWritable( $session ) -Checks if this web is a mirror web, throwing an OopsException -if it is. +Checks if this web is writable on this site, Throwing an exception +if it is not. =cut -sub checkMirror { - my ( $session, $webName, $topic ) = @_; +sub checkWritable { + my ( $session, $web ) = @_; ASSERT($session->isa( 'TWiki')) if DEBUG; - my( $mirrorSiteName, $mirrorViewURL ) = - $session->readOnlyMirrorWeb( $webName ); - - return unless ( $mirrorSiteName ); - - throw Error::Simple( - "This is a mirror site $mirrorSiteName, $mirrorViewURL" ); + my $mode; + if ( $web && $web ne $session->{webName} ) { + my $dummy; + ($mode, $dummy) = $session->modeAndMaster($web); + } + else { + $web = $session->{webName}; + $mode = $session->{contentMode}; + } + if ( $mode eq 'slave' || $mode eq 'read-only' ) { + throw TWiki::OopsException( 'accessdenied', + def => 'not_writable', + web => $web, + ); + } + return; } =pod twiki Modified: twiki/trunk/core/lib/TWiki.pm =================================================================== --- twiki/trunk/core/lib/TWiki.pm 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/lib/TWiki.pm 2012-08-23 07:49:52 UTC (rev 23194) @@ -230,6 +230,7 @@ INTURLENCODE => \&INTURLENCODE_deprecated, LANGUAGES => \&LANGUAGES, MAKETEXT => \&MAKETEXT, + MASTERWEBSCRIPTURL=> \&MASTERWEBSCRIPTURL, MDREPO => \&MDREPO, META => \&META, METASEARCH => \&METASEARCH, @@ -1017,39 +1018,105 @@ =pod ----++ ObjectMethod readOnlyMirrorWeb( $theWeb ) -> ( $mirrorSiteName, $mirrorViewURL, $mirrorLink, $mirrorNote ) +---++ ObjectMethod modeAndMaster( $web ) +it returns the following hash reference such as this: +('master', undef) -If this is a mirrored web, return information about the mirror. The info -is returned in a quadruple: +and this: +('slave', { # master site data + siteName => 'na', + webScriptUrlTmpl => 'http://twiki.example.com/cgi-bin//Web', + scriptSuffix => '', + webViewUrl => 'http://twiki.example.com/Web', +}) -| site name | URL | link | note | - +The first value is the mode of the web: either 'local', 'master', 'slave', +or 'read-only'. The second value is defined Only when the mode is 'slave'. =cut -sub readOnlyMirrorWeb { - my( $this, $theWeb ) = @_; - - my @mirrorInfo = ( '', '', '', '' ); - if( $TWiki::cfg{SiteWebTopicName} ) { - my $mirrorSiteName = - $this->{prefs}->getWebPreferencesValue( 'MIRRORSITENAME', $theWeb ); - if( $mirrorSiteName && $mirrorSiteName ne $TWiki::cfg{SiteWebTopicName} ) { - my $mirrorViewURL = - $this->{prefs}->getWebPreferencesValue( 'MIRRORVIEWURL', $theWeb ); - my $mirrorLink = $this->templates->readTemplate( 'mirrorlink' ); - $mirrorLink =~ s/%MIRRORSITENAME%/$mirrorSiteName/g; - $mirrorLink =~ s/%MIRRORVIEWURL%/$mirrorViewURL/g; - $mirrorLink =~ s/\s*$//g; - my $mirrorNote = $this->templates->readTemplate( 'mirrornote' ); - $mirrorNote =~ s/%MIRRORSITENAME%/$mirrorSiteName/g; - $mirrorNote =~ s/%MIRRORVIEWURL%/$mirrorViewURL/g; - $mirrorNote = $this->renderer->getRenderedVersion - ( $mirrorNote, $theWeb, $TWiki::cfg{HomeTopic} ); - $mirrorNote =~ s/\s*$//g; - @mirrorInfo = ( $mirrorSiteName, $mirrorViewURL, $mirrorLink, $mirrorNote ); +sub modeAndMaster { + my ($this, $web) = @_; + my $mode = 'local'; # by default a web is local + if ( !$TWiki::cfg{SiteName} ) { + return ($mode, undef); + } + my $cache = $this->{modeAndMaster} ||= {}; + if ( my $cached = $cache->{$web} ) { + return @$cached + } + my $cacheHereToo; + my %master; + if ( my $mdrepo = $this->{mdrepo} ) { + my $tlweb = topLevelWeb($web); + if ( my $cached = $cache->{$tlweb} ) { + $cache->{$web} = $cached if ( $tlweb ne $web ); + return @$cached; } + $cacheHereToo = $tlweb ne $web ? $tlweb : ''; + my $webRec = $mdrepo->getRec('webs', topLevelWeb($web)); + if ( $webRec ) { + my $masterSite = $webRec->{master}; + if ( $masterSite ) { + $master{siteName} = $masterSite; + if ( $masterSite eq $TWiki::cfg{SiteName} ) { + $mode = 'master'; + } + else { + my $siteRec = $mdrepo->getRec('sites', $masterSite); + if ( $siteRec && $siteRec->{scripturl} ) { + $mode = 'slave'; + $master{webScriptUrlTmpl} = + $siteRec->{scripturl} . '//' . $web; + $master{scriptSuffix} = $siteRec->{scriptsuffix}; + $master{webViewUrl} = $siteRec->{viewurl} . '/' . $web + if ( $siteRec->{viewurl} ); + } + else { + $mode = 'read-only'; + } + } + } + } + # If the metadata repository is in use and the web's record + # doesn't exist or doesn't have the master field, then the web + # is regarded as 'local'. + # No fall back to the none metadata repository situation processed + # below. } - return @mirrorInfo; + else { + my $prefs = $this->{prefs}; + my $masterSite = $prefs->getWebPreferencesValue('MASTERSITENAME', $web); + if ( $masterSite ) { + $master{siteName} = $masterSite; + if ( $masterSite eq $TWiki::cfg{SiteName} ) { + $mode = 'master'; + } + else { + my $webScriptUrlTmpl = $master{webScriptUrlTmpl} = + $prefs->getWebPreferencesValue( + 'MASTERWEBSCRIPTURLTMPL', $web + ); + if ( $webScriptUrlTmpl ) { + $mode = 'slave'; + $master{scriptSuffix} = + $prefs->getWebPreferencesValue( + 'MASTERSCRIPTSUFFIX', $web + ); + $master{webViewUrl} = + $prefs->getWebPreferencesValue( + 'MASTERWEBVIEWURL', $web + ); + } + else { + $mode = 'read-only' + } + } + } + } + my $result = $cache->{$web} = + [$mode, ($mode eq 'slave' && %master) ? \%master : undef]; + $cache->{$cacheHereToo} = $result if ( $cacheHereToo ); + return @$result; } =pod @@ -1740,6 +1807,16 @@ # SMELL: Every place should localize it before use, so it's not necessary here. $TWiki::Plugins::SESSION = $this; + my ($mode, $master) = $this->modeAndMaster($this->{webName}); + $this->{contentMode} = $mode; + $this->{SESSION_TAGS}{READONLYWEB} = $mode eq 'read-only' ? 'on' : ''; + if ( $master ) { + $this->{master} = $master; + if ( $this->{mdrepo} ) { + $this->{SESSION_TAGS}{MASTERSITENAME} = $master->{siteName}; + } + } + Monitor::MARK("TWiki session created"); return $this; @@ -1918,6 +1995,9 @@ undef $this->{_INCLUDES}; undef $this->{response}; undef $this->{evaluating_if}; + undef $this->{contentMode}; + undef $this->{master}; + undef $this->{modeAndMaster}; } =pod @@ -4098,6 +4178,8 @@ foreach my $aweb ( @webslist ) { if( $aweb eq 'public' ) { push( @list, $this->{store}->getListOfWebs( 'user,public,allowed', $showWeb ) ); + } elsif ( $aweb eq 'canmoveto' ) { + push( @list, $this->{store}->getListOfWebs( 'user,public,allowed,canmoveto', $showWeb ) ); } elsif( $aweb eq 'webtemplate' ) { push( @list, $this->{store}->getListOfWebs( 'template,allowed', $showWeb )); } else { @@ -4439,6 +4521,20 @@ return $this->getPubUrl(0); } +sub MASTERWEBSCRIPTURL { + my ( $this, $params, $topic, $web ) = @_; + my $master = $this->{master}; + return '' unless ( $master ); + my $url = $master->{webScriptUrlTmpl}; + return '' unless ( $url ); + my $script = $params->{_DEFAULT} || 'view'; + return $master->{webViewUrl} + if ( $script eq 'view' && $master->{webViewUrl} ); + my $suffix = $master->{scriptSuffix} || ''; + $url =~ s:^(.*)//:$1/$script$suffix/:; + return $url; +} + sub ALLVARIABLES { return shift->{prefs}->stringify(); } Added: twiki/trunk/core/pub/TWiki/ReadOnlyAndMirrorWebs/bottom-toolbar-w-mirror-to-all.png =================================================================== (Binary files differ) Property changes on: twiki/trunk/core/pub/TWiki/ReadOnlyAndMirrorWebs/bottom-toolbar-w-mirror-to-all.png ___________________________________________________________________ Name: svn:executable + * Name: svn:mime-type + application/octet-stream Added: twiki/trunk/core/pub/TWiki/ReadOnlyAndMirrorWebs/bottom-toolbar-w-view-master.png =================================================================== (Binary files differ) Property changes on: twiki/trunk/core/pub/TWiki/ReadOnlyAndMirrorWebs/bottom-toolbar-w-view-master.png ___________________________________________________________________ Name: svn:executable + * Name: svn:mime-type + application/octet-stream Modified: twiki/trunk/core/templates/messages.tmpl =================================================================== --- twiki/trunk/core/templates/messages.tmpl 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/templates/messages.tmpl 2012-08-23 07:49:52 UTC (rev 23194) @@ -586,3 +586,8 @@ %MAKETEXT{"To save your changes, press the browser back button, copy all content into the clipboard, cancel edit, edit again, and replace content with clipboard content. Whew ... nothing lost!"}% %TMPL:END% +%TMPL:DEF{"not_writable"}% +%MAKETEXT{"The [_1] web is not writable on this site" args="<nop>%WEB%"}%. + +%MAKETEXT{"Contact [_1] if you have any questions." args="<a href=\"mailto:%WIKIWEBMASTER%?subject=%ENCODE{"%WEB% is not writable'"}%\">%WIKIWEBMASTER%</a>"}% +%TMPL:END% Modified: twiki/trunk/core/templates/moveattachment.tmpl =================================================================== --- twiki/trunk/core/templates/moveattachment.tmpl 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/templates/moveattachment.tmpl 2012-08-23 07:49:52 UTC (rev 23194) @@ -16,7 +16,7 @@ %TMPL:DEF{"namehelp"}%<p>%MAKETEXT{"The new topic chosen must already exist."}%</p>%TMPL:END% -%TMPL:DEF{"newweb"}%<p><select class="twikiSelect" name="newweb" onchange="if (document.rename.newweb.value == 'Trash') { document.rename.newtopic.value = 'TrashAttachment'; }"> %WEBLIST{"<option $marker value=$qname>$name</option>" webs="%TRASHWEB%,public" selection="%WEB%" separator=" "}% </select><input type="hidden" name="attachment" value="%FILENAME%" /></p>%TMPL:END% +%TMPL:DEF{"newweb"}%<p><select class="twikiSelect" name="newweb" onchange="if (document.rename.newweb.value == 'Trash') { document.rename.newtopic.value = 'TrashAttachment'; }"> %WEBLIST{"<option $marker value=$qname>$name</option>" webs="%TRASHWEB%,canmoveto" selection="%WEB%" separator=" "}% </select><input type="hidden" name="attachment" value="%FILENAME%" /></p>%TMPL:END% %TMPL:DEF{"towebtitle"}%---+++ %MAKETEXT{"Move attachment to web:"}%%TMPL:END% Modified: twiki/trunk/core/templates/rename.tmpl =================================================================== --- twiki/trunk/core/templates/rename.tmpl 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/templates/rename.tmpl 2012-08-23 07:49:52 UTC (rev 23194) @@ -14,7 +14,7 @@ %TMPL:P{"nonwikiword"}%%TMPL:END% -%TMPL:DEF{"newweb"}%<select name="newweb" class="twikiSelect">%WEBLIST{"<option $marker value=$qname>$name</option>" webs="%NEW_WEB%,public" selection="%NEW_WEB%" separator=" "}%</select>%TMPL:END% +%TMPL:DEF{"newweb"}%<select name="newweb" class="twikiSelect">%WEBLIST{"<option $marker value=$qname>$name</option>" webs="%NEW_WEB%,canmoveto" selection="%NEW_WEB%" separator=" "}%</select>%TMPL:END% %TMPL:DEF{"topicactionbuttons"}%<input type="submit" class="twikiSubmit" value='%MAKETEXT{"Rename/Move"}%' /> %MAKETEXT{"or"}% %TMPL:P{"canceltopicaction"}%%TMPL:END% \ No newline at end of file Modified: twiki/trunk/core/templates/renameweb.tmpl =================================================================== --- twiki/trunk/core/templates/renameweb.tmpl 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/templates/renameweb.tmpl 2012-08-23 07:49:52 UTC (rev 23194) @@ -10,7 +10,7 @@ %TMPL:DEF{"newweb"}%<p><input class="twikiInputField" value="%NEW_SUBWEB%" name="newsubweb" size="20" /><input type="hidden" name="action" value="renameweb" /><input type="hidden" name="confirm" value="getlock" /></p>%TMPL:END% -%TMPL:DEF{"newparentweb"}%<p><select class="twikiSelect" name="newparentweb"><option value=""></option>%WEBLIST{"<option $marker value=$qname>$name</option>" webs="%TRASHWEB%,public" selection="%NEW_PARENTWEB%" separator=" "}%</select></p>%TMPL:END% +%TMPL:DEF{"newparentweb"}%<p><select class="twikiSelect" name="newparentweb"><option value=""></option>%WEBLIST{"<option $marker value=$qname>$name</option>" webs="%TRASHWEB%,canmoveto" selection="%NEW_PARENTWEB%" separator=" "}%</select></p>%TMPL:END% Modified: twiki/trunk/core/templates/viewtopicactionbuttons.tmpl =================================================================== --- twiki/trunk/core/templates/viewtopicactionbuttons.tmpl 2012-08-17 08:02:41 UTC (rev 23193) +++ twiki/trunk/core/templates/viewtopicactionbuttons.tmpl 2012-08-23 07:49:52 UTC (rev 23194) @@ -1,19 +1,19 @@ -%TMPL:DEF{"topicactionbuttons"}%%TMPL:P{"action_activatable_edit_or_create"}%%TMPL:P{"action_activatable_attach"}%%TMPL:P{"action_printable"}%%TMPL:P{"action_revisions"}%%TMPL:P{"action_backlinks_simple"}%%TMPL:P{"action_raw_or_view"}%%TMPL:P{"action_activatable_raw_edit"}%%TMPL:P{"activatable_more"}%%TMPL:END% +%TMPL:DEF{"topicactionbuttons"}%%TMPL:P{"action_activatable_edit_or_create"}%%TMPL:P{"action_activatable_attach"}%%TMPL:P{"action_printable"}%%TMPL:P{"action_revisions"}%%TMPL:P{"action_backlinks_simple"}%%TMPL:P{"action_raw_or_view"}%%TMPL:P{"action_mirror"}%%TMPL:P{"action_activatable_raw_edit"}%%TMPL:P{"activatable_more"}%%TMPL:END% -%TMPL:DEF{"create_topic_link"}%<span><a href='%SCRIPTURL{"edit"}%/%BASEWEB%/%BASETOPIC%?t=%GMTIME{"$epoch"}%%TMPL:P{"url_param_editaction"}%%IF{"context TinyMCEPluginEnabled" then=";nowysiwyg=%IF{ "$ EDITMETHOD='wysiwyg'" then="0" else="1" }%"}%' rel='nofollow' %MAKETEXT{"title='Create new topic' accesskey='c'>&Create"}%</a></span>%TMPL:END% +%TMPL:DEF{"create_topic_link"}%<span><a href='%IF{"context content_slave" then="%MASTERWEBSCRIPTURL{"edit"}%" else="%SCRIPTURL{"edit"}%/%BASEWEB%"}%/%BASETOPIC%?t=%GMTIME{"$epoch"}%%TMPL:P{"url_param_editaction"}%%IF{"context TinyMCEPluginEnabled" then=";nowysiwyg=%IF{ "$ EDITMETHOD='wysiwyg'" then="0" else="1" }%"}%' rel='nofollow' %MAKETEXT{"title='Create new topic' accesskey='c'>&Create"}%</a></span>%TMPL:END% -%TMPL:DEF{"edit_topic_link"}%<span><a href='%SCRIPTURL{"edit"}%/%BASEWEB%/%BASETOPIC%?t=%GMTIME{"$epoch"}%%TMPL:P{"url_param_editaction"}%%IF{"context TinyMCEPluginEnabled" then=";nowysiwyg=%IF{ "$ EDITMETHOD='wysiwyg'" then="0" else="1" }%"}%' rel='nofollow' %MAKETEXT{"title='Edit this topic text' accesskey='e'>[_1] &Edit" args="<img src='%ICONURLPATH{uweb-o14}%' width='14' height='14' border='0' alt='' />"}%</a></span>%TMPL:END% +%TMPL:DEF{"edit_topic_link"}%<span><a href='%IF{"context content_slave" then="%MASTERWEBSCRIPTURL{"edit"}%" else="%SCRIPTURL{"edit"}%/%BASEWEB%"}%/%BASETOPIC%?t=%GMTIME{"$epoch"}%%TMPL:P{"url_param_editaction"}%%IF{"context TinyMCEPluginEnabled" then=";nowysiwyg=%IF{ "$ EDITMETHOD='wysiwyg'" then="0" else="1" }%"}%' rel='nofollow' %MAKETEXT{"title='Edit this topic text' accesskey='e'>[_1] &Edit" args="<img src='%ICONURLPATH{uweb-o14}%' width='14' height='14' border='0' alt='' />"}%</a></span>%TMPL:END% -%TMPL:DEF{"raw_edit_link"}%<span><a href='%SCRIPTURL{"edit"}%/%WEB%/%TOPIC%?t=%GMTIME{"$epoch"}%%TMPL:P{"url_param_editaction"}%;nowysiwyg=%IF{ "$ EDITMETHOD!='wysiwyg'" then="0" else="1" }%' rel='nofollow' %MAKETEXT{"title='%IF{ "$ EDITMETHOD!='wysiwyg'" then="WYSIWYG editor" else="Raw Edit this topic text" }%' accesskey='w'>%IF{ "$ EDITMETHOD!='wysiwyg'" then="WYSIWYG" else="Ra&w edit" }%"}%</a></span>%TMPL:END% +%TMPL:DEF{"raw_edit_link"}%<span><a href='%IF{"context content_slave" then="%MASTERWEBSCRIPTURL{"edit"}%" else="%SCRIPTURL{"edit"}%/%WEB%"}%/%TOPIC%?t=%GMTIME{"$epoch"}%%TMPL:P{"url_param_editaction"}%;nowysiwyg=%IF{ "$ EDITMETHOD!='wysiwyg'" then="0" else="1" }%' rel='nofollow' %MAKETEXT{"title='%IF{ "$ EDITMETHOD!='wysiwyg'" then="WYSIWYG editor" else="Raw Edit this topic text" }%' accesskey='w'>%IF{ "$ EDITMETHOD!='wysiwyg'" then="WYSIWYG" else="Ra&w edit" }%"}%</a></span>%TMPL:END% %TMPL:DEF{"view_topic_link"}%<span><a href='%SCRIPTURL{"view"}%/%BASEWEB%/%BASETOPIC%' rel='nofollow' %MAKETEXT{"title='View topic' accesskey='v'>&View topic"}%</a></span>%TMPL:END% -%TMPL:DEF{"attach_link"}%<span><a href='%SCRIPTURLPATH{"attach"}%/%BASEWEB%/%BASETOPIC%' rel='nofollow' %MAKETEXT{"title='Attach an image or document to this topic' accesskey='a'>&Attach"}%</a></span>%TMPL:END% +%TMPL:DEF{"attach_link"}%<span><a href='%IF{"context content_slave" then="%MASTERWEBSCRIPTURL{"attach"}%" else="%SCRIPTURLPATH{"attach"}%/%BASEWEB%"}%/%BASETOPIC%' rel='nofollow' %MAKETEXT{"title='Attach an image or document to this topic' accesskey='a'>&Attach"}%</a></span>%TMPL:END% %TMPL:DEF{"more_link"}%<span><a href='%SCRIPTURLPATH{"oops"}%/%BASEWEB%/%BASETOPIC%?template=oopsmore¶m1=%MAXREV%¶m2=%CURRREV%' rel='nofollow' %MAKETEXT{"title='Delete or rename this topic; set parent topic; view and compare revisions' accesskey='m'>&More topic actions"}%</a></span>%TMPL:END% @@ -79,13 +79,27 @@ %TMPL:DEF{"action_activatable_more"}%%TMPL:P{"activatable_more"}%%TMPL:END% -%TMPL:DEF{"activatable_more"}%%TMPL:P{context="inactive" then="inactive_more" else="active_more"}%%TMPL:END% +%TMPL:DEF{"activatable_more"}%%TMPL:P{context="inactive" then="inactive_more" else="maybe_active_more"}%%TMPL:END% +%TMPL:DEF{"maybe_active_more"}%%TMPL:P{context="content_slave" then="inactive_more" else="active_more"}%%TMPL:END% + %TMPL:DEF{"action_raw_or_view"}%%TMPL:P{"raw_or_view"}%%TMPL:P{"sep"}%%TMPL:END% %TMPL:DEF{"raw_or_view"}%%IF{"defined raw" then="%TMPL:P{"view_topic_link"}%" else="%TMPL:P{"raw_link"}%"}%%TMPL:END% + +%TMPL:DEF{"action_mirror"}%%TMPL:P{context="content_master" then="action_master"}%%TMPL:P{context="content_slave" then="action_slave"}%%TMPL:END% + +%TMPL:DEF{"action_master"}%%TMPL:P{"mirror_to_all_link"}%%TMPL:P{"sep"}%%TMPL:END% + +%TMPL:DEF{"action_slave"}%%TMPL:P{"view_master_link"}%%TMPL:P{"sep"}%%TMPL:END% + +%TMPL:DEF{"view_master_link"}%<span><a href='%MASTERWEBSCRIPTURL{"view"}%/%BASETOPIC%?%ENCODE{%QUERYPARAMSTRING%}%%REVARG%' rel='nofollow' %MAKETEXT{"title='View topic on the master site'>View master"}%</a></span>%TMPL:END% + +%TMPL:DEF{"mirror_to_all_link"}%<span><a href='%MIRRORTOALLURL%' rel='nofollow' %MAKETEXT{"title='Mirror this web to all slave sites'>Mirror to all"}%</a></span>%TMPL:END% + + %TMPL:DEF{"action_backlinks"}%%TMPL:P{"backlinks"}%%TMPL:P{"sep"}%%TMPL:END% %TMPL:DEF{"backlinks"}%%MAKETEXT{"Backlinks"}%: %TMPL:P{"backlinks_web_link"}%, %TMPL:P{"backlinks_all_link"}%%TMPL:END% |