diff --git a/docs/upgrades/templates-7.3.0/default-wiki-front-page.tmpl b/docs/upgrades/templates-7.3.0/default-wiki-front-page.tmpl new file mode 100644 index 000000000..a326294e2 --- /dev/null +++ b/docs/upgrades/templates-7.3.0/default-wiki-front-page.tmpl @@ -0,0 +1,15 @@ +#WikiFrontTmpl000000001 +#create +#namespace:WikiMaster_front +#url:default-wiki-front-page +#title:Default Wiki Front Page +#menuTitle:Default Wiki Front Page + + +

Search

+Search: +

Recent changes (more)

+
    +
  • : + by
  • +
diff --git a/docs/upgrades/templates-7.3.0/default-wiki-master.tmpl b/docs/upgrades/templates-7.3.0/default-wiki-master.tmpl new file mode 100644 index 000000000..3ac655f86 --- /dev/null +++ b/docs/upgrades/templates-7.3.0/default-wiki-master.tmpl @@ -0,0 +1,18 @@ +#WikiMasterTmpl00000001 +#create +#namespace:WikiMaster +#url:default-wiki-master +#title:Default Wiki Master +#menuTitle:Default Wiki Master + +

+

+ +

Other actions

+
    +
  • +
  • +
  • +
  • +
  • +
diff --git a/docs/upgrades/templates-7.3.0/default-wiki-page-edit.tmpl b/docs/upgrades/templates-7.3.0/default-wiki-page-edit.tmpl new file mode 100644 index 000000000..77c18fbca --- /dev/null +++ b/docs/upgrades/templates-7.3.0/default-wiki-page-edit.tmpl @@ -0,0 +1,12 @@ +#WikiPageEditTmpl000001 +#create +#namespace:WikiPage_edit +#url:default-wiki-page-edit +#title:Default Wiki Page Edit +#menuTitle:Default Wiki Page Edit + + +

Title:
+Content:

+ + diff --git a/docs/upgrades/templates-7.3.0/default-wiki-page-history.tmpl b/docs/upgrades/templates-7.3.0/default-wiki-page-history.tmpl new file mode 100644 index 000000000..6aabc148c --- /dev/null +++ b/docs/upgrades/templates-7.3.0/default-wiki-page-history.tmpl @@ -0,0 +1,13 @@ +#WikiPHTmpl000000000001 +#create +#namespace:WikiPage_pageHistory +#url:default-wiki-page-history +#title:Default Page History +#menuTitle:Default Page History + + diff --git a/docs/upgrades/templates-7.3.0/default-wiki-page-list.tmpl b/docs/upgrades/templates-7.3.0/default-wiki-page-list.tmpl new file mode 100644 index 000000000..1421a9dd9 --- /dev/null +++ b/docs/upgrades/templates-7.3.0/default-wiki-page-list.tmpl @@ -0,0 +1,13 @@ +#WikiPLTmpl000000000001 +#create +#namespace:WikiMaster_pageList +#url:default-wiki-page-list +#title:Default Page List +#menuTitle:Default Page List + +

+
    + +
  • +
    +
diff --git a/docs/upgrades/templates-7.3.0/default-wiki-page.tmpl b/docs/upgrades/templates-7.3.0/default-wiki-page.tmpl new file mode 100644 index 000000000..8075106d3 --- /dev/null +++ b/docs/upgrades/templates-7.3.0/default-wiki-page.tmpl @@ -0,0 +1,29 @@ +#WikiPageTmpl0000000001 +#create +#namespace:WikiPage +#url:default-wiki-page +#title:Default Wiki Page +#menuTitle:Default Wiki Page + + +

This page

    +
  • + +
  • +
  • (Protected page)
  • +
    + +
  • +
    + + +
  • + +
  • +
    +
    + +
  • +
  • +
    +
diff --git a/docs/upgrades/templates-7.3.0/default-wiki-recent-changes.tmpl b/docs/upgrades/templates-7.3.0/default-wiki-recent-changes.tmpl new file mode 100644 index 000000000..214ab370e --- /dev/null +++ b/docs/upgrades/templates-7.3.0/default-wiki-recent-changes.tmpl @@ -0,0 +1,14 @@ +#WikiRCTmpl000000000001 +#create +#namespace:WikiMaster_recentChanges +#url:default-wiki-recent-changes +#title:Default Recent Changes +#menuTitle:Default Recent Changes + + +

+
    +
  • : + by
  • +
+
diff --git a/docs/upgrades/templates-7.3.0/default-wiki-search.tmpl b/docs/upgrades/templates-7.3.0/default-wiki-search.tmpl new file mode 100644 index 000000000..44f9c8fad --- /dev/null +++ b/docs/upgrades/templates-7.3.0/default-wiki-search.tmpl @@ -0,0 +1,11 @@ +#WikiSearchTmpl00000001 +#create +#namespace:WikiMaster_search +#url:default-wiki-search +#title:Default Wiki Search +#menuTitle:Default Wiki Search + +Search: +

Results

+
+
diff --git a/docs/upgrades/upgrade_7.2.3-7.3.0.pl b/docs/upgrades/upgrade_7.2.3-7.3.0.pl index 4be382604..e1da24188 100644 --- a/docs/upgrades/upgrade_7.2.3-7.3.0.pl +++ b/docs/upgrades/upgrade_7.2.3-7.3.0.pl @@ -19,20 +19,96 @@ my $quiet; # this line required my $session = start(); # this line required - -# upgrade functions go here - +addWikiAssets($session); +deleteOldFiles($session); finish($session); # this line required -##------------------------------------------------- -#sub exampleFunction { -# my $session = shift; -# print "\tWe're doing some stuff here that you should know about.\n" unless ($quiet); -# # and here's our code -#} +#------------------------------------------------- +sub deleteOldFiles { + my $session = shift; + print "\tDeleting old unneeded files.\n" unless $quiet; + unlink "../../www/extras/assets/wiki.gif"; + unlink "../../www/extras/assets/wikiPost.gif"; + unlink "../../www/extras/assets/small/wiki.gif"; + unlink "../../www/extras/assets/small/wikiPost.gif"; +} +#------------------------------------------------- +sub addWikiAssets { + my $session = shift; + print "\tAdding wiki assets.\n" unless $quiet; + $session->db->write($_) for(<<'EOT', + CREATE TABLE `WikiMaster` ( + `assetId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `revisionDate` bigint(20) NOT NULL, + `groupToEditPages` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `groupToAdminister` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `richEditor` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'PBrichedit000000000002', + `masterTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiMasterTmpl00000001', + `frontPageTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiFrontTmpl000000001', + `pageTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiPageTmpl0000000001', + `pageEditTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiPageEditTmpl000001', + `recentChangesTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiRCTmpl000000000001', + `pageHistoryTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiPHTmpl000000000001', + `pageListTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiPLTmpl000000000001', + `searchTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiSearchTmpl00000001', + `recentChangesCount` int(11) NOT NULL default 50, + `recentChangesCountFront` int(11) NOT NULL default 10, + PRIMARY KEY (`assetId`, `revisionDate`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +EOT + <<'EOT', + CREATE TABLE `WikiPage` ( + `assetId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `revisionDate` bigint(20) NOT NULL, + `content` mediumtext, + `storageId` varchar(22) character set utf8 collate utf8_bin NULL, + `views` bigint(20) NOT NULL default 0, + PRIMARY KEY (`assetId`, `revisionDate`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +EOT + <<'EOT', + CREATE TABLE `WikiMaster_titleIndex` ( + `assetId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `pageId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `title` varchar(255) NOT NULL, + PRIMARY KEY (`assetId`, `pageId`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +EOT + # Don't want protection to be versioned, so put it in a + # separate table. + <<'EOT', + CREATE TABLE `WikiPage_protected` ( + `assetId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + PRIMARY KEY (`assetId`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +EOT + <<'EOT', + CREATE TABLE `WikiPage_extraHistory` ( + `assetId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `userId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `dateStamp` bigint(20) NOT NULL, + `actionTaken` varchar(255) NOT NULL, + `url` varchar(255) NOT NULL, + `title` varchar(255) NOT NULL + ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +EOT + ); + + my $config = $session->config; + $config->addToArray('assetContainers', 'WebGUI::Asset::Wobject::WikiMaster'); +} # ---- DO NOT EDIT BELOW THIS LINE ---- diff --git a/lib/WebGUI/Asset/WikiPage.pm b/lib/WebGUI/Asset/WikiPage.pm new file mode 100644 index 000000000..470f0e935 --- /dev/null +++ b/lib/WebGUI/Asset/WikiPage.pm @@ -0,0 +1,364 @@ +package WebGUI::Asset::WikiPage; + +# ------------------------------------------------------------------- +# WebGUI is Copyright 2001-2006 Plain Black Corporation. +# ------------------------------------------------------------------- +# Please read the legal notices (docs/legal.txt) and the license +# (docs/license.txt) that came with this distribution before using +# this software. +# ------------------------------------------------------------------- +# http://www.plainblack.com info@plainblack.com +# ------------------------------------------------------------------- + +use base 'WebGUI::Asset'; +use strict; +use Tie::IxHash; +use WebGUI::International; +use WebGUI::Utility; + +#------------------------------------------------------------------- +# Buggo, semi-duplication with WikiMaster; move this into a common utility routine somewhere +sub _appendFuncTemplateVars { + my $self = shift; + my $var = shift; + my @funcs = @_; + my $i18n = WebGUI::International->new($self->session, 'Asset_WikiPage'); + my %specialFuncs = (); + my $revision = $self->session->form->process('revision'); + my $revisionSuffix = defined($revision)? ";revision=$revision" : ''; + @funcs = (qw/view edit pageHistory protect unprotect delete wikiPurgeRevision/) unless @funcs; + + foreach my $func (@funcs) { + $var->{$func.'.url'} = $self->getUrl($specialFuncs{$func} + || "func=$func$revisionSuffix"); + $var->{$func.'.text'} = $i18n->get("func $func link text"); + my $confirmation = $i18n->get("func $func link confirm"); + if (length $confirmation) { + $confirmation =~ s/\'/\\\'/g; + $var->{$func.'.confirm'} = "return confirm('$confirmation')"; + } + } +} + +#------------------------------------------------------------------- +sub canDelete { + my $self = shift; + my $userId = shift || $self->session->user->userId; + return $self->getWiki->canAdminister($userId); +} + +#------------------------------------------------------------------- +sub canEdit { + my $self = shift; + my $userId = shift || $self->session->user->userId; + return 0 if $self->isProtected and not $self->getWiki->canAdminister($userId); + return $self->couldEdit($userId); +} + +#------------------------------------------------------------------- +sub canProtect { + my $self = shift; + my $userId = shift || $self->session->user->userId; + return $self->getWiki->canAdminister($userId); +} + +#------------------------------------------------------------------- +sub couldEdit { + my $self = shift; + my $userId = shift || $self->session->user->userId; + return 0 unless $self->getWiki->canEditPages($userId); + return 1; +} + +#------------------------------------------------------------------- +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = WebGUI::International->new($session, "Asset_WikiPage"); + + my %properties; + tie %properties, 'Tie::IxHash'; + %properties = + ( + storageId => { fieldType => 'image', + defaultValue => undef }, + content => { fieldType => "HTMLArea", + defaultValue => undef }, + ); + + push @$definition, + { + assetName => $i18n->get('assetName'), + icon => 'wikiPage.gif', + autoGenerateForms => 1, + tableName => 'WikiPage', + className => 'WebGUI::Asset::WikiPage', + properties => \%properties, + }; + + return $class->SUPER::definition($session, $definition); +} + + +#------------------------------------------------------------------- +# BUGGO: how to handle this? +sub duplicate { + my $self = shift; + my $newAsset = $self->SUPER::duplicate(@_); + return $newAsset; +} + + +#------------------------------------------------------------------- +sub getWiki { + my $self = shift; + my $parent = $self->getParent; + return undef unless defined $parent and $parent->isa('WebGUI::Asset::Wobject::WikiMaster'); + return $parent; +} + +#------------------------------------------------------------------- +sub indexContent { + my $self = shift; + my $indexer = $self->SUPER::indexContent; + $indexer->addKeywords($self->get('content')); + return $indexer; +} + +#------------------------------------------------------------------- +sub purge { + my $self = shift; + $self->getWiki->updateTitleIndex([$self], from => 'purge'); + $self->session->db->write("DELETE FROM WikiPage_protected WHERE assetId = ?", [$self->getId]); + $self->session->db->write("DELETE FROM WikiPage_extraHistory WHERE assetId = ?", [$self->getId]); + return $self->SUPER::purge; +} + +#------------------------------------------------------------------- +sub purgeRevision { + my $self = shift; + $self->getWiki->updateTitleIndex([$self], from => 'purgeRevision'); + return $self->SUPER::purgeRevision; +} + +#------------------------------------------------------------------- +sub preparePageTemplate { + my $self = shift; + return $self->{_pageTemplate} if $self->{_pageTemplate}; + $self->{_pageTemplate} = + WebGUI::Asset::Template->new($self->session, $self->getWiki->get('pageTemplateId')); + $self->{_pageTemplate}->prepare; + return $self->{_pageTemplate}; +} + +#------------------------------------------------------------------- +sub processPageTemplate { + my $self = shift; + my $content = shift; + my $func = shift || $self->session->form->process('func'); + my $var = {}; + my $template = $self->preparePageTemplate; + + $var->{content} = $content; + $var->{canEdit} = $self->canEdit; + $var->{couldEdit} = $self->couldEdit; + $var->{canProtect} = $self->canProtect; + $var->{canDelete} = $self->canDelete; + $var->{isProtected} = $self->isProtected; + $var->{inEdit} = isIn($func, qw/edit add/); + $var->{inView} = isIn($func, qw/view/) || !defined $func; + $var->{inHistory} = isIn($func, qw/pageHistory/); + $self->_appendFuncTemplateVars($var); + + return $self->processTemplate($var, undef, $template); +} + +#------------------------------------------------------------------- +sub prepareView { + my $self = shift; + $self->SUPER::prepareView; + $self->preparePageTemplate; +} + +#------------------------------------------------------------------- +sub view { + my $self = shift; + my $var = {}; + my $title = $self->get('title'); + my $content = $self->getWiki->autolinkHtml($self->get('content')); + return $self->getWiki->processMasterTemplate($title, $self->processPageTemplate($content, 'view')); +} + +#------------------------------------------------------------------- +sub isProtected { + my $self = shift; + return $self->{_isProtected} if exists $self->{_isProtected}; + ($self->{_isProtected}) = $self->session->db->quickArray("SELECT COUNT(assetId) FROM WikiPage_protected WHERE assetId = ?", [$self->getId]); + return $self->{_isProtected}; +} + +#------------------------------------------------------------------- +sub processPropertiesFromFormPost { + my $self = shift; + my $ret = $self->SUPER::processPropertiesFromFormPost(@_); + $self->update({ groupIdView => $self->getWiki->get('groupIdView'), + groupIdEdit => $self->getWiki->get('groupIdEdit') }); + $self->getWiki->updateTitleIndex([$self], from => 'edit'); + return $ret; +} + +#------------------------------------------------------------------- +sub updateWikiHistory { + my $self = shift; + my $action = shift; + my $userId = shift || $self->session->user->userId; + $self->session->db->write("INSERT INTO WikiPage_extraHistory (assetId, userId, dateStamp, actionTaken, url, title) VALUES (?, ?, ?, ?, ?, ?)", [$self->getId, $userId, $self->session->datetime->time, $action, $self->getUrl, $self->get('title')]); +} + + +#------------------------------------------------------------------- +sub www_delete { + my $self = shift; + return $self->session->privilege->insufficient unless $self->canDelete; + $self->trash; + $self->session->asset($self->getParent); + return $self->getParent->www_view; +} + +#------------------------------------------------------------------- +sub www_edit { + my $self = shift; + return $self->session->privilege->insufficient unless $self->canEdit; + + my $template = WebGUI::Asset::Template->new($self->session, $self->getWiki->get('pageEditTemplateId')); + my $var = {}; + my $newPage = 0; + $template->prepare; + + if ($self->session->form->process('func') eq 'add') { + # New page. + $newPage = 1; + $var->{'form.header'} = join '', + (WebGUI::Form::formHeader($self->session, + { action => $self->getWiki->getUrl('func=addPageSave') }), + WebGUI::Form::hidden($self->session, { name => 'class', value => ref $self })); + } else { + # Editing a page. + $newPage = 0; + $var->{'form.header'} = join '', + (WebGUI::Form::formHeader($self->session, + { action => $self->getUrl('func=editSave') })); + } + + $var->{'form.title'} = WebGUI::Form::text + ($self->session, { name => 'title', maxlength => 255, + size => 40, value => $self->get('title') }); + $var->{'form.content'} = WebGUI::Form::HTMLArea + ($self->session, { name => 'content', richEditId => $self->getWiki->get('richEditor'), + value => $self->get('content') }); + $var->{'form.submit'} = WebGUI::Form::submit + ($self->session, { value => 'Save' }); + $var->{'form.footer'} = WebGUI::Form::formFooter($self->session); + $self->_appendFuncTemplateVars($var); + + my $title = "Editing ".(defined($self->get('title'))? $self->get('title') : 'new page'); + + return $self->getWiki->processStyle($self->getWiki->processMasterTemplate($title, $self->processPageTemplate($self->processTemplate($var, undef, $template), 'edit'))); +} + +#------------------------------------------------------------------- +sub www_editSave { + my $self = shift; + return $self->session->privilege->insufficient unless $self->canEdit; + + # TODO: refactor: duplication with A::W::Matrix::www_editListingSave + my $oldTag = WebGUI::VersionTag->getWorking($self->session, 1); + my $newTag = WebGUI::VersionTag->create + ($self->session, { name => (sprintf "%s edit of %s - %s", + $self->getWiki->get('title'), $self->get('title'), + $self->session->user->username), + workflowId => 'pbworkflow000000000003' }); + $newTag->setWorking; + + my $newSelf = $self->addRevision; + my $error = $newSelf->processPropertiesFromFormPost; + if (ref $error eq 'ARRAY') { + $self->session->stow->set('editFormErrors', $error); + $newTag->rollback; + $oldTag->setWorking if defined $oldTag; + return $self->www_edit; + } + + $newSelf->updateHistory('edited'); + $newTag->requestCommit; + $newTag->clearWorking; + $oldTag->setWorking if defined $oldTag; + + return $newSelf->www_view; +} + +#------------------------------------------------------------------- +sub www_pageHistory { + my $self = shift; + my $title = sprintf(WebGUI::International->new($self->session, 'Asset_WikiPage')->get('pageHistory title'), + $self->get('title')); + my $template = WebGUI::Asset::Template->new($self->session, $self->getWiki->get('pageHistoryTemplateId')); + $template->prepare; + + # Buggo: hardcoded count + my $var = {}; + $self->getWiki->_appendPageHistoryVars($var, [0, 50], $self); + + return $self->getWiki->processStyle($self->getWiki->processMasterTemplate($title, $self->processPageTemplate($self->processTemplate($var, undef, $template), 'pageHistory'))); +} + +#------------------------------------------------------------------- +sub www_protect { + my $self = shift; + return $self->session->privilege->insufficient unless $self->canProtect; + return $self->www_view if $self->isProtected; + + $self->session->db->write("DELETE FROM WikiPage_protected WHERE assetId = ?", [$self->getId]); + $self->session->db->write("INSERT INTO WikiPage_protected (assetId) VALUES (?)", [$self->getId]); + $self->{_isProtected} = 1; + $self->updateWikiHistory('protected'); + return $self->www_view; +} + +#------------------------------------------------------------------- +sub www_unprotect { + my $self = shift; + return $self->session->privilege->insufficient unless $self->canProtect; + return $self->www_view if !$self->isProtected; + + $self->session->db->write("DELETE FROM WikiPage_protected WHERE assetId = ?", [$self->getId]); + $self->{_isProtected} = 0; + $self->updateWikiHistory('unprotected'); + return $self->www_view; +} + +#------------------------------------------------------------------- +sub www_view { + my $self = shift; + return $self->session->privilege->noAccess unless $self->canView; + $self->update({ views => $self->get('views')+1 }); + # TODO: This should probably exist, as the CS has one. +# $self->session->http->setCacheControl($self->getWiki->get('visitorCacheTimeout')) +# if ($self->session->user->userId eq '1'); + $self->session->http->sendHeader; + $self->prepareView; + return $self->getWiki->processStyle($self->view); +} + + +#------------------------------------------------------------------- +sub www_wikiPurgeRevision { + my $self = shift; + return $self->session->privilege->insufficient unless $self->canDelete; + $self->purgeRevision; + $self->session->asset($self->getParent); + return $self->getParent->www_view; +} + +1; diff --git a/lib/WebGUI/Asset/Wobject/WikiMaster.pm b/lib/WebGUI/Asset/Wobject/WikiMaster.pm new file mode 100644 index 000000000..a4d6cdf87 --- /dev/null +++ b/lib/WebGUI/Asset/Wobject/WikiMaster.pm @@ -0,0 +1,604 @@ +package WebGUI::Asset::Wobject::WikiMaster; + +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2006 Plain Black Corporation. +#------------------------------------------------------------------- +# Please read the legal notices (docs/legal.txt) and the license +# (docs/license.txt) that came with this distribution before using +# this software. +#------------------------------------------------------------------- +# http://www.plainblack.com info@plainblack.com +#------------------------------------------------------------------- + +use base 'WebGUI::Asset::Wobject'; +use strict; +use Tie::IxHash; +use WebGUI::International; +use WebGUI::Utility; +use HTML::Parser; +use URI::Escape; + +#------------------------------------------------------------------- +sub _appendFuncTemplateVars { + my $self = shift; + my $var = shift; + my @funcs = @_; + my $i18n = WebGUI::International->new($self->session, 'Asset_WikiMaster'); + my %specialFuncs = + (addPage => 'func=add;class=WebGUI::Asset::WikiPage'); + + foreach my $func (@funcs) { + $var->{$func.'.url'} = $self->getUrl($specialFuncs{$func} || "func=$func"); + $var->{$func.'.text'} = $i18n->get("func $func link text"); + } +} + +#------------------------------------------------------------------- +sub _appendPageHistoryVars { + my $self = shift; + my $var = shift; + my $limit = shift; + my $page = shift; + my $time = $self->session->datetime->time; + my $entries = $self->_templateSubvarsRefOfEdits($self->_editsRefOfPageHistory($page, $limit), $time); + my $days = $self->_daysRefOfTemplateSubvars($entries); + + $var->{'ph.entries'} = $entries; + $var->{'ph.days'} = $days; + return $self; +} + +#------------------------------------------------------------------- +sub _appendRecentChangesVars { + my $self = shift; + my $var = shift; + my $limit = shift; + my $time = $self->session->datetime->time; + my $entries = $self->_templateSubvarsRefOfEdits($self->_editsRefOfRecentChanges($limit), $time); + my $days = $self->_daysRefOfTemplateSubvars($entries); + + $var->{'rc.entries'} = $entries; + $var->{'rc.days'} = $days; + return $self; +} + +#------------------------------------------------------------------- +sub _appendSearchBoxVars { + my $self = shift; + my $var = shift; + my $queryText = shift; + my $submitText = WebGUI::International->new($self->session, 'Asset_WikiMaster')->get('search submit'); + + $var->{'search.formHeader'} = join '', + (WebGUI::Form::formHeader($self->session, { action => $self->getUrl, method => 'GET' }), + WebGUI::Form::hidden($self->session, { name => 'func', value => 'search' })); + $var->{'search.query'} = WebGUI::Form::text($self->session, { name => 'query', value => $queryText }); + $var->{'search.submit'} = WebGUI::Form::submit($self->session, { value => $submitText }); + $var->{'search.formFooter'} = WebGUI::Form::formFooter($self->session); + return $self; +} + +#------------------------------------------------------------------- +sub _appendSearchResultVars { + my $self = shift; + my $var = shift; + my $rs = shift; + my @results = (); + my $dt = $self->session->datetime; + + while (defined(my $row = $rs->hashRef)) { + push @results, $self->_templateSubvarOfPage($row->{assetId}); + } + + $var->{'search.results'} = \@results; + + return $self; +} + +#------------------------------------------------------------------- +sub _appendVarsOfDate { + my $self = shift; + my $var = shift; + my $date = shift; + my $prefix = shift; + my $relativeTo = shift; + my $dt = $self->session->datetime; + + $var->{$prefix . (length($prefix)? 'Date':'date')} = $dt->epochToHuman($date, '%z'); + $var->{$prefix . (length($prefix)? 'Time':'time')} = $dt->epochToHuman($date, '%Z'); + if (defined $relativeTo) { + my $ago = WebGUI::International->new($self->session, 'Asset')->get('ago'); + $var->{$prefix . (length($prefix)? 'Interval':'interval')} = sprintf '%s %s %s', + ($dt->secondsToInterval($relativeTo - $date), $ago); + } + + return $self; +} + +#------------------------------------------------------------------- +sub _daysRefOfTemplateSubvars { + my $self = shift; + my $subvars = shift; + my @days = (); + + foreach my $subvar (@$subvars) { + if (!@days or $subvar->{date} ne $days[-1][0]{date}) { + push @days, [$subvar]; + } else { + push @{$days[-1]}, $subvar; + } + } + + return [map { {'day.date' => $$_[0]{date}, 'day.entries' => $_} } @days]; +} + +#------------------------------------------------------------------- +sub _editsRefOfPageHistory { + my $self = shift; + my $page = shift; + my $limit = shift; + $self->_editsRefOfQuery("a.assetId = ?", [$page->getId], undef, $limit); +} + +#------------------------------------------------------------------- +sub _editsRefOfQuery { + my $self = shift; + my $queryPiece = shift || 'true'; + my $placeholders = shift || []; + my $allowedActions = shift || [qw/edited trashed protected unprotected/]; + my $allowedActionsPredicate = "IN (".join(', ', ('?') x @$allowedActions).")"; + my $limit = shift; + my $limitClause = $limit? sprintf("LIMIT %d,%d", @$limit[0..1]) : ""; + + # Ick. Apparently assetHistory.dateStamp isn't always equivalent to assetData.revisionDate. + # It looks like the relationship between them is in fact semi-arbitrary. Then there's also that + # it doesn't seem to be safe to add extra possible values to assetHistory. So we have to do this. + # Bleagh. + $self->session->db->buildArrayRefOfHashRefs(<<"EOT", [(@$placeholders, @$allowedActions) x 3]); +SELECT h.userId AS userId, u.username AS username, h.dateStamp AS dateStamp, + h.actionTaken AS action, CONCAT('/', d.url) AS url, d.title AS title, a.assetId AS assetId + FROM assetHistory AS h LEFT JOIN users AS u ON h.userId = u.userId + INNER JOIN asset AS a ON h.assetId = a.assetId + INNER JOIN assetData AS d ON a.assetId = d.assetId AND h.dateStamp = d.revisionDate + WHERE $queryPiece AND h.actionTaken $allowedActionsPredicate + UNION +SELECT eh.userId AS userId, u.username AS username, eh.dateStamp AS dateStamp, + eh.actionTaken AS action, CONCAT('/', eh.url) AS url, eh.title AS title, a.assetId AS assetId + FROM WikiPage_extraHistory AS eh LEFT JOIN users AS u ON eh.userId = u.userId + INNER JOIN asset AS a ON eh.assetId = a.assetId + WHERE $queryPiece AND eh.actionTaken $allowedActionsPredicate + UNION +SELECT d.revisedBy AS userId, u.username AS username, d.revisionDate AS dateStamp, + 'edited' AS action, CONCAT('/', d.url) AS url, d.title AS title, a.assetId AS assetId + FROM assetData AS d LEFT JOIN users AS u on d.revisedBy = u.userId + INNER JOIN asset AS a ON d.assetId = a.assetId + WHERE $queryPiece AND 'edited' $allowedActionsPredicate + ORDER BY dateStamp DESC + $limitClause +EOT +} + +#------------------------------------------------------------------- +sub _editsRefOfRecentChanges { + my $self = shift; + my $limit = shift; + $self->_editsRefOfQuery("a.lineage LIKE CONCAT(?, '%') AND a.assetId <> ?", [$self->get('lineage'), $self->getId], [qw/edited trashed/], $limit); +} + +#------------------------------------------------------------------- +sub _templateSubvarOfEdit { + my $self = shift; + my $edit = shift; + my $time = shift; + my $i18n = WebGUI::International->new($self->session, 'Asset_WikiMaster'); + my $subvar = +{%$edit}; + + $self->_appendVarsOfDate($subvar, $subvar->{dateStamp}, '', $time); + + # If only HTML::Template::Expr were standard. + $subvar->{isDelete} = ($subvar->{action} eq 'trashed'); + $subvar->{isEdit} = ($subvar->{action} eq 'edited'); + $subvar->{isProtect} = ($subvar->{action} eq 'protected'); + $subvar->{isUnprotect} = ($subvar->{action} eq 'unprotected'); + $subvar->{isCreateOrEdit} = $subvar->{isEdit}; + + $subvar->{viewLatest} = $subvar->{url}; + $subvar->{editLatest} = $subvar->{url}.'?func=edit'; + + if ($subvar->{isEdit}) { + $subvar->{viewRevision} = $subvar->{url}.'?func=view;revision='.$subvar->{dateStamp}; + $subvar->{editRevision} = $subvar->{url}.'?func=edit;revision='.$subvar->{dateStamp}; + } + + if ($subvar->{isEdit} and ($self->session->db->quickArray("SELECT MIN(revisionDate) FROM assetData WHERE assetId = ?", [$subvar->{assetId}]))[0] == $subvar->{dateStamp}) { + $subvar->{action} = 'created'; + $subvar->{isEdit} = 0; + $subvar->{isCreate} = 1; + } + + $subvar->{actionN} = $i18n->get('actionN '.$subvar->{action}); + $subvar->{actionNL} = lc $subvar->{actionN}; + + return $subvar; +} + +#------------------------------------------------------------------- +sub _templateSubvarOfPage { + my $self = shift; + my $page = shift; + my $subvar = {}; + $page = WebGUI::Asset->newByDynamicClass($self->session, $page) unless ref $page; + + $subvar->{title} = $page->get('title'); + $subvar->{assetId} = $page->getId; + $subvar->{viewLatest} = $page->getUrl; + $subvar->{editLatest} = $page->getUrl('func=edit'); + return $subvar; +} + +#------------------------------------------------------------------- +sub _templateSubvarsRefOfEdits { + my $self = shift; + my $edits = shift; + my $time = shift; + return [map { $self->_templateSubvarOfEdit($_, $time) } @$edits]; +} + +#------------------------------------------------------------------- +sub _templateSubvarsRefOfPages { + my $self = shift; + my $pages = shift; + return [map { $self->_templateSubvarOfPage($_) } @$pages]; +} + +#------------------------------------------------------------------- +sub autolinkHtml { + my $self = shift; + my $html = shift; + + # TODO: ignore caching for now, but maybe do it later. + my %mapping = $self->session->db->buildHash("SELECT LOWER(i.title), d.url FROM WikiMaster_titleIndex AS i INNER JOIN assetData AS d ON i.pageId = d.assetId WHERE i.assetId = ?", [$self->getId]); + return $html unless %mapping; + + foreach my $key (keys %mapping) { + $mapping{$key} = WebGUI::HTML::format('/'.$mapping{$key}, 'text'); + } + + my $matchString = join('|', map{quotemeta} keys %mapping); + my $regexp = qr/\b($matchString)\b/i; + + my @acc = (); + my $in_a = 0; + my $p = HTML::Parser->new; + $p->case_sensitive(1); + $p->marked_sections(1); + $p->unbroken_text(1); + $p->handler(start => sub { push @acc, $_[2]; if ($_[0] eq 'a' and exists $_[1]{href}) { $in_a++ } }, + 'tagname, attr, text'); + $p->handler(end => sub { push @acc, $_[2]; if ($_[0] eq 'a' and exists $_[1]{href}) { $in_a-- } }, + 'tagname, attr, text'); + $p->handler(text => sub { + my $text = $_[0]; + unless ($in_a) { + while ($text =~ s#^(.*?)$regexp##i) { + push @acc, sprintf '%s%s', + ($1, $mapping{lc $2}, $2); + } + } + push @acc, $text; + }, 'text'); + $p->handler(default => sub { push @acc, $_[0] }, 'text'); + $p->parse($html); + $p->eof; + undef $p; # Just in case there might be reference loops. + + return join '', @acc; +} + +#------------------------------------------------------------------- +sub canAdminister { + my $self = shift; + my $userId = shift || $self->session->user->userId; + my $user = WebGUI::User->new($self->session, $userId); + return $self->canView($userId) && $user->isInGroup($self->get('groupToAdminister')); +} + +#------------------------------------------------------------------- +sub canEditPages { + my $self = shift; + return $self->session->user->isInGroup($self->get("groupToEditPages")) || $self->canAdminister; +} + +#------------------------------------------------------------------- +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = WebGUI::International->new($session, 'Asset_WikiMaster'); + + # BUGGO: duplication with Collaboration; move this into WebGUI::Asset::RichEdit + my $richEditorOptions = $session->db->buildHashRef("select distinct(assetData.assetId), assetData.title from asset, assetData where asset.className='WebGUI::Asset::RichEdit' and asset.assetId=assetData.assetId order by assetData.title"); + + my %properties; + tie %properties, 'Tie::IxHash'; + %properties = + ( + groupToEditPages => { fieldType => 'group', + defaultValue => ['7'], + tab => 'security', + hoverHelp => $i18n->get('groupToEditPages hoverHelp'), + label => $i18n->get('groupToEditPages label') }, + + groupToAdminister => { fieldType => 'group', + defaultValue => ['3'], + tab => 'security', + hoverHelp => $i18n->get('groupToAdminister hoverHelp'), + label => $i18n->get('groupToAdminister label') }, + + richEditor => { fieldType => 'selectBox', + options => $richEditorOptions, + defaultValue => 'PBrichedit000000000001', + tab => 'display', + hoverHelp => $i18n->get('richEditor hoverHelp'), + label => $i18n->get('richEditor label') }, + + masterTemplateId => { fieldType => 'template', + namespace => 'WikiMaster', + defaultValue => 'WikiMasterTmpl00000001', + tab => 'display', + hoverHelp => $i18n->get('masterTemplateId hoverHelp'), + label => $i18n->get('masterTemplateId label') }, + + frontPageTemplateId => { fieldType => 'template', + namespace => 'WikiMaster_front', + defaultValue => 'WikiFrontTmpl000000001', + tab => 'display', + hoverHelp => $i18n->get('frontPageTemplateId hoverHelp'), + label => $i18n->get('frontPageTemplateId label') }, + + pageTemplateId => { fieldType => 'template', + namespace => 'WikiPage', + defaultValue => 'WikiPageTmpl0000000001', + tab => 'display', + hoverHelp => $i18n->get('pageTemplateId hoverHelp'), + label => $i18n->get('pageTemplateId label') }, + + pageHistoryTemplateId => { fieldType => 'template', + namespace => 'WikiPage_pageHistory', + defaultValue => 'WikiPHTmpl000000000001', + tab => 'display', + hoverHelp => $i18n->get('pageHistoryTemplateId hoverHelp'), + label => $i18n->get('pageHistoryTemplateId label') }, + + recentChangesTemplateId => { fieldType => 'template', + namespace => 'WikiMaster_recentChanges', + defaultValue => 'WikiRCTmpl000000000001', + tab => 'display', + hoverHelp => $i18n->get('recentChangesTemplateId hoverHelp'), + label => $i18n->get('recentChangesTemplateId label') }, + + pageListTemplateId => { fieldType => 'template', + namespace => 'WikiMaster_pageList', + defaultValue => 'WikiPLTmpl000000000001', + tab => 'display', + hoverHelp => $i18n->get('pageListTemplateId hoverHelp'), + label => $i18n->get('pageListTemplateId label') }, + + searchTemplateId => { fieldType => 'template', + namespace => 'WikiMaster_search', + defaultValue => 'WikiSearchTmpl00000001', + tab => 'display', + hoverHelp => $i18n->get('searchTemplateId hoverHelp'), + label => $i18n->get('searchTemplateId label') }, + + recentChangesCount => { fieldType => 'integer', + defaultValue => 50, + tab => 'display', + hoverHelp => $i18n->get('recentChangesCount hoverHelp'), + label => $i18n->get('recentChangesCount label') }, + + recentChangesCountFront => { fieldType => 'integer', + defaultValue => 10, + tab => 'display', + hoverHelp => $i18n->get('recentChangesCountFront hoverHelp'), + label => $i18n->get('recentChangesCountFront label') }, + ); + + push @$definition, + { + assetName => $i18n->get('assetName'), + icon => 'wikiMaster.gif', + autoGenerateForms => 1, + tableName => 'WikiMaster', + className => 'WebGUI::Asset::Wobject::WikiMaster', + properties => \%properties, + }; + + return $class->SUPER::definition($session, $definition); +} + +#------------------------------------------------------------------- +sub getContentLastModified { + my $self = shift; + my ($lastModified) = $self->session->db->quickArray("SELECT MAX(d.revisionDate) FROM assetData AS d INNER JOIN asset AS a ON a.assetId = d.assetId WHERE a.lineage LIKE CONCAT(?, '%')", [$self->get('lineage')]); + return $lastModified; +} + +#------------------------------------------------------------------- +sub prepareMasterTemplate { + my $self = shift; + return $self->{_masterTemplate} if $self->{_masterTemplate}; + $self->{_masterTemplate} = + WebGUI::Asset::Template->new($self->session, $self->get('masterTemplateId')); + $self->{_masterTemplate}->prepare; + return $self->{_masterTemplate}; +} + +#------------------------------------------------------------------- +sub processMasterTemplate { + my $self = shift; + my $title = shift; + my $content = shift; + my $var = {}; + my $template = $self->prepareMasterTemplate; + + $var->{title} = $title; + $var->{content} = $content; + $var->{displayTitle} = $self->get('displayTitle'); + $var->{canEdit} = $self->canEditPages; + $self->_appendFuncTemplateVars($var, qw/addPage listPages recentChanges view search/); + + return $self->processTemplate($var, undef, $template); +} + +#------------------------------------------------------------------- +sub prepareView { + my $self = shift; + $self->SUPER::prepareView; + $self->prepareMasterTemplate; + $self->{_frontPageTemplate} = + WebGUI::Asset::Template->new($self->session, $self->get('frontPageTemplateId')); + $self->{_frontPageTemplate}->prepare; +} + +#------------------------------------------------------------------- +sub processPropertiesFromFormPost { + my $self = shift; + + my $groupsChanged = + (($self->session->form->process('groupIdView') ne $self->get('groupIdView')) + or ($self->session->form->process('groupIdEdit') ne $self->get('groupIdEdit'))); + my $ret = $self->SUPER::processPropertiesFromFormPost(@_); + + if ($groupsChanged) { + foreach my $child (@{$self->getLineage(['children'], {returnObjects => 1})}) { + $child->update({ groupIdView => $self->get('groupIdView'), + groupIdEdit => $self->get('groupIdEdit') }); + } + } + + return $ret; +} + +#------------------------------------------------------------------- +sub purge { + my $self = shift; + $self->session->db->write('DELETE FROM WikiMaster_titleIndex WHERE assetId = ?', [$self->getId]); + return $self->SUPER::purge; +} + +#------------------------------------------------------------------- +sub updateTitleIndex { + my $self = shift; + my @pages = @{+shift}; + my %opts = @_; + return unless @pages; + $self->session->db->write("DELETE FROM WikiMaster_titleIndex WHERE assetId = ? AND pageId IN (".join(', ', ('?') x @pages).")", [$self->getId, map{$_->getId} @pages]); + + foreach my $page (@pages) { + my ($pageId, $title) = ($page->getId, $page->get('title')); + $self->session->db->write("INSERT INTO WikiMaster_titleIndex (assetId, pageId, title) VALUES (?, ?, ?)", [$self->getId, $pageId, $title]); + } +} + +#------------------------------------------------------------------- +sub view { + my $self = shift; + my $var = {}; + my $template = $self->{_frontPageTemplate}; + + $var->{'description'} = $self->autolinkHtml($self->get('description')); + $self->_appendSearchBoxVars($var); + $self->_appendRecentChangesVars($var, [0, $self->get('recentChangesCountFront')]); + $self->_appendFuncTemplateVars($var, qw/recentChanges/); + + return $self->processMasterTemplate($self->get('title'), $self->processTemplate($var, undef, $template)); +} + +#------------------------------------------------------------------- +sub www_addPageSave { + my $self = shift; + my $pageClass = $self->session->form->process('class'); + return $self->session->privilege->insufficient unless + $self->canEditPages and UNIVERSAL::isa($pageClass, 'WebGUI::Asset::WikiPage'); + + # Refactor: duplication with A::W::Matrix::www_editListingSave + my $oldTag = WebGUI::VersionTag->getWorking($self->session, 1); + my $newTag = WebGUI::VersionTag->create + ($self->session, { name => (sprintf "%s create of %s - %s", + $self->get('title'), $self->session->form->process('title'), + $self->session->user->username), + workflowId => 'pbworkflow000000000003' }); + $newTag->setWorking; + + # Hrm. Duplication with Asset::www_editSave. How to fix that? + my $page = $self->addChild({ className => $pageClass }); + $page->{_parent} = $self; + + my $error = $page->processPropertiesFromFormPost; + if (ref $error eq 'ARRAY') { + $self->session->stow->set('editFormErrors', $error); + $page->purge; + return $self->www_add; + } + + $page->updateHistory('edited'); + $newTag->requestCommit; + $newTag->clearWorking; + $oldTag->setWorking if defined $oldTag; + return $page->www_view; +} + +#------------------------------------------------------------------- +sub www_listPages { + my $self = shift; + my $i18n = WebGUI::International->new($self->session, 'Asset_WikiMaster'); + my $title = $i18n->get('listPages title'); + my $template = WebGUI::Asset::Template->new($self->session, $self->get('pageListTemplateId')); + $template->prepare; + + my @pages = sort { lc($a->get('title')) <=> lc($b->get('title')) } + @{$self->getLineage(['children'], {returnObjects => 1})}; + my $var = {}; + $var->{'pl.entries'} = $self->_templateSubvarsRefOfPages(\@pages); + + return $self->processStyle($self->processMasterTemplate($title, $self->processTemplate($var, undef, $template))); +} + +#------------------------------------------------------------------- +sub www_recentChanges { + my $self = shift; + my $title = WebGUI::International->new($self->session, 'Asset_WikiMaster')->get('recentChanges title'); + my $template = WebGUI::Asset::Template->new($self->session, $self->get('recentChangesTemplateId')); + $template->prepare; + + my $var = {}; + $self->_appendRecentChangesVars($var, [0, $self->get('recentChangesCount')]); + + return $self->processStyle($self->processMasterTemplate($title, $self->processTemplate($var, undef, $template))); +} + +#------------------------------------------------------------------- +sub www_search { + my $self = shift; + my $var = {}; + my $queryString = $self->session->form->process('query', 'text'); + $self->_appendSearchBoxVars($var, $queryString); + + if (length $queryString) { + my $search = WebGUI::Search->new($self->session); + $search->search({ keywords => $queryString, + lineage => [$self->get('lineage')], + classes => ['WebGUI::Asset::WikiPage'] }); + my $rs = $search->getResultSet; + $self->_appendSearchResultVars($var, $rs); + } + + my $title = WebGUI::International->new($self->session, 'Asset_WikiMaster')->get('search title'); + my $template = WebGUI::Asset::Template->new($self->session, $self->get('searchTemplateId')); + $template->prepare; + + return $self->processStyle($self->processMasterTemplate($title, $self->processTemplate($var, undef, $template))); +} + +1; diff --git a/www/extras/yui-webgui/accordion.js b/www/extras/yui-webgui/accordion.js new file mode 100644 index 000000000..488bfd59b --- /dev/null +++ b/www/extras/yui-webgui/accordion.js @@ -0,0 +1,79 @@ +// This is a modified version of the accordion tutorial from www.webthreads.de + + +/* + +var myAccordion = new Accordion(id [, height]); + + id: The id of the div that contains the accordion structure. + + height: An integer representing the height that the accordion should be drawn in pixels. Defaults to the viewport height minus one header width. + +*/ + +function Accordion(id, accordionHeight) { + // pointer to the accordion container + this.accContainer = document.getElementById(id); + + // all items with class = accordionItem + this.accItems = YAHOO.util.Dom.getElementsByClassName("accordionItem", "div", this.accContainer); + + // scale the acccordion to the appropriate height + this.accordionHeight = 0; + var headerHeight = YAHOO.util.Dom.getElementsByClassName("accordionHeader", "div", this.accItems[0])[0].offsetHeight; + if (accordionHeight > 0) { + this.accordionHeight = accordionHeight; + } else { + this.accordionHeight = YAHOO.util.Dom.getViewportHeight() - headerHeight; + } + this.accContainer.style.height = this.accordionHeight + "px"; + var bodyHeight = this.accordionHeight - (headerHeight * this.accItems.length); + YAHOO.util.Dom.getElementsByClassName("accordionBody", "div", this.accItems[0])[0].style.height = bodyHeight + "px"; + + // set the default accordion body height + this.accItemBodyHeight = 0; + + // iterate over all the accordian elements and store them in an array + for (var i=0; i this.accItemBodyHeight) { + this.accItemBodyHeight = this.accItems[i].body.offsetHeight; + this.activeItem = this.accItems[i]; + this.activeItem.body.style.height = this.accItemBodyHeight + "px"; + } + + // register the click event on the header for changing the active element in the accordion + YAHOO.util.Event.addListener(this.accItems[i].header, "click", function(){ + // do nothing if they click on the active header + if(this.parent.activeItem == this){ + return; + } + + // shrink animation + var shrinkLastAccAnim = new YAHOO.util.Anim(this.parent.activeItem.body, {height:{from:this.parent.accItemBodyHeight, to:0}}, 0.1); + + // expand animation + var expandNewActiveAccAnim = new YAHOO.util.Anim(this.body, {height:{from:0, to:this.parent.accItemBodyHeight}}, 0.1); + + // set the selected element as active + expandNewActiveAccAnim.onStart.subscribe(function() { this.parent.activeItem = this; }, this, true); + + // execute the animation + shrinkLastAccAnim.animate(); + expandNewActiveAccAnim.animate(); + }, this.accItems[i], true); + } + + // only the active element remains expanded + for(var i=0; i + + + + Accordion Example + + + + + + + + + + + + + + +
+
+ +
accordion header 1
+
+ accordion 1 accordion 1 accordion 1 accordion 1 + accordion 1 accordion 1 accordion 1 accordion 1 + accordion 1 accordion 1 accordion 1 accordion 1 + accordion 1 accordion 1 accordion 1 accordion 1 + accordion 1 accordion 1 accordion 1 accordion 1 + accordion 1 accordion 1 accordion 1 accordion 1 +
+
+
+
accordion header 2
+
+ + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 + accordion 2 accordion 2 accordion 2 accordion 2 +
+
+
+
accordion header 3
+
accordion body 3
+
+
+ + + + diff --git a/www/extras/yui-webgui/resources/css/accordion.css b/www/extras/yui-webgui/resources/css/accordion.css new file mode 100644 index 000000000..1457a3aa0 --- /dev/null +++ b/www/extras/yui-webgui/resources/css/accordion.css @@ -0,0 +1,11 @@ +.accordion { } + +.accordionHeader { + cursor: pointer; +} + +.accordionBody { + height: 0px; + overflow: auto; +} +