use collective.recipe.sphinxbuilder;

svn path=/plone.app.discussion/trunk/; revision=35274
This commit is contained in:
Timo Stollenwerk
2010-03-18 14:06:04 +00:00
parent 8150617081
commit 6135fcf6d4
35 changed files with 1431 additions and 56 deletions
+4
View File
@@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: ad04f153429ceaff21b3a3a4a57a98f6
tags: fbb0d17656682115ca4d033fb2f83ba1
+59
View File
@@ -0,0 +1,59 @@
========================
Architectural Principles
========================
This document outlines architectural principles used in the design of
plone.app.discussion.
**Discussion items have a portal_type**
This makes it easier to search for them and manage them using existing
CMF and Plone UI constructs.
**Discussion items are cataloged**
It is be possible to search for discussion items like any other type of
content.
**Discussion items are subject to workflow and permission**
Moderation, anonymous commenting, and auto approve/reject should be
handled using workflow states, automatic and manual transitions, and
permissions.
**Discussion items are light weight objects**
Discussion item objects are as light weight as possible.
Ideally, a discussion item should be as lightweight as a catalog brain.
This may mean that we forego convenience base classes and re-implement
certain interfaces. Comments should not provide the full set of dublin
core metadata, though custom indexers can be used to provide values for
standard catalog indexes.
**Optimise for retrival speed**
HTML filtering and other processing should happen on save, not on render,
to make rendering quick.
**Settings are stored using plone.registry**
Any global setting should be stored in plone.registry records
**Forms are constructed using extensible z3c.form forms**
This allows plugins (such as spam protection algorithms) to provide
additional validation
**Discussion items are stored in a BTree container**
This allows faster lookup and manipulation
**Discussion items are accessed using a dict-like interface**
This makes iteration and manipulation more natural. Even if comments are
not stored threaded, the dict interface should act as if they are, i.e.
calling items() on a comment should return the replies to that comment
(in order).
**Discussion items are retrieved in reverse creation date order**
Discussion items do not need to support explicit ordering. They should
always be retrieved in reverse creation date order (most recent for).
They can be stored with keys so that this is always true.
**Discussion items do not need readable ids**
Ids can be based on the creation date.
**Discussion items send events**
The usual zope.lifecycleevent and zope.container events are fired when
discussion items are added, removed, or modified.
+169
View File
@@ -0,0 +1,169 @@
============
Design Notes
============
This document contains design notes for plone.app.discussion.
Storage and traversal
---------------------
For each content item, there is a Conversation object stored in annotations.
This can be traversed to via the ++conversation++ namespace, but also fetched
via an adapter lookup to IConversation.
The conversation stores all comments related to a content object. Each
comment has an integer id (also representable as a string, to act as an OFS
id and allow traversal). Hence, traversing to obj/++conversation++/123 retrieves
the comment with id 123.
Comments ids are assigned in order, so a comment with id N was posted before
a comment with id N + 1. However, it is not guaranteed that ids will be
incremental. Ids must be positive integers - 0 or negative numbers are not
allowed.
Threading information is stored in the conversation: we keep track of the
set of children and the parent if any comment. Top-level comments have a
parent id of 0. This information is managed by the conversation class when
comments are manipulated using a dict-like API.
Note that the __parent__/acquisition parent of an IComment is the
IConversation, and the __parent__/acquisition parent of an IConversation is
the content object.
Events
------
Manipulating the IConversation object should fire the usual IObjectAddedEvent
and IObjectRemovedEvent events. The UI may further fire IObjectCreatedEvent
and IObjectModifiedEvent for comments.
Factories
---------
Comments should always be created via the 'Discussion Item' IFactory utility.
Conversations should always be obtained via the IConversation adapter (even
the ++conversation++ namespace should use this). This makes it possible to
replace conversations and comments transparently.
The Comment class
-----------------
The inheritance tree for DiscussionItem is shown below. Classes we want to
mix in and interface we want to implement in the Comment class are marked
with [x].
[ ] DiscussionItem
[ ] Document
[ ] PortalContent = [ ] IContentish
[ ] DynamicType = [ ] IDynamicType
[ ] CMFCatalogAware = [ ] <no interface>
[ ] SimpleItem = [ ] ISimpleItem
[ ] Item [ ]
[?] Base = [ ] <no interface>
[ ] Resource = [ ] <no interface>
[ ] CopySource = [ ] ICopySource
[ ] Tabs = [ ] <no interface>
[x] Traversable = [ ] ITraversable
[ ] Element = [ ] <no interface>
[x] Owned = [ ] IOwned
[ ] UndoSupport = [ ] IUndoSupport
[ ] Persistent [ ]
[ ] Implicit [ ]
[x] RoleManager = [ ] IRoleManager
[ ] RoleManager = [ ] IPermissionMappingSupport
[ ] DefaultDublinCoreImpl = [ ] IDublinCore
[ ] ICatalogableDublinCore
[ ] IMutableDublinCore
[ ] PropertyManager = [ ] IPropertyManager
Thus, we want:
* Traversable, to get absolute_url() and friends
- this requires a good acquisition chain at all times
* Acquisition.Explicit, to support acquisition
- we do not want implicit acquisition
* Owned, to be able to track ownership
* RoleManager, to support permissions and local roles
We also want to use a number of custom indexers for most of the standard
metadata such as creator, effective date etc.
Finally, we'll need event handlers to perform the actual indexing.
Discussion settings
-------------------
Discussion can be enabled per-type and per-instance, via values in the FTI
(allow_discussion) and on the object. These will remain unchanged. The
IConversation object's 'enabled' property should consult these.
Global settings should be managed using plone.registry. A control panel
can be generated from this as well, using the helper class in
plone.app.registry.
Note that some settings, notably those to do with permissions and workflow,
will need to be wired up as custom form fields with custom data mangers
or similar.
Workflow and permissions
------------------------
Where possible, we should use existing permissions:
* View
* Reply to Item
* Modify Portal Content
* Request Review
In addition, we'll need a 'Moderator' role and a moderation permission,
* Moderate comment
* Bypass moderation
To control whether Anonymous can post comments, we manage the 'Reply to Item'
permission. To control whether moderation is required for various roles, we
could manage the 'Bypass moderation' permission.
These could work in a workflow like this:
* --> [posted] -- {publish} --> [published]--> *
| ^
| |
+----- {auto-publish} -----+
| |
+----- {auto-moderate} ----+
The 'posted' state is the initial state. 'published' is the state where the
comment is visible to non-reviewers.
The 'publish' transition would be protected by the 'Moderate comment'
permission. We could have states and transition for 'rejected', etc, but it
is probably just as good to delete comments that are rejected.
The 'auto-publish' transition would be an automatic transition protected by
the 'Bypass moderation' permission.
The 'auto-moderate' transition would be another automatic transition protected
by an expression (e.g. calling a view) that returns True if the user is on
an auto-moderation 'white-list', e.g. by email address or username.
Forms and UI
------------
The basic commenting display/reply form should be placed in a viewlet.
Ideally, the reply form should be inline, perhaps revealed with JavaScript
if enabled. This allows full contextualisation of replies. The current
solution, with a separate form that shows some context, is brittle and
over-complicated.
If we support quoting of comments in replies, we can load the text to quote
using JavaScript as well.
As a fall-back for non-JavaScript enabled browsers, it is probably OK not to
support quoting and/or viewing of context, e.g. the user is taken to a standalone
'comment reply' form.
All actual forms should be handled using z3c.form and plone.z3cform's
ExtensibleForm support. This makes it possible to plug in additional fields
declaratively, e.g. to include spam protection.
+38
View File
@@ -0,0 +1,38 @@
.. plone.app.discussion documentation master file, created by
sphinx-quickstart on Thu Mar 18 10:17:15 2010.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to plone.app.discussion's documentation!
================================================
.. module:`plone.app.discussion`
Contents:
.. toctree::
:maxdepth: 2
architecture.txt
design.txt
API
---
.. automodule:: plone.app.discussion
.. autoclass:: plone.app.discussion.conversation.Conversation
.. automethod:: plone.app.discussion.conversation.Conversation.enabled
.. include:: ../../CHANGES.txt
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
+417
View File
@@ -0,0 +1,417 @@
/**
* Sphinx stylesheet -- basic theme
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
img {
border: 0;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li div.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable dl, table.indextable dd {
margin-top: 0;
margin-bottom: 0;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
/* -- general body styles --------------------------------------------------- */
a.headerlink {
visibility: hidden;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.field-list ul {
padding-left: 1em;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px 7px 0 7px;
background-color: #ffe;
width: 40%;
float: right;
}
p.sidebar-title {
font-weight: bold;
}
/* -- topics ---------------------------------------------------------------- */
div.topic {
border: 1px solid #ccc;
padding: 7px 7px 0 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
div.admonition dl {
margin-bottom: 0;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
border: 0;
border-collapse: collapse;
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 0;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
table.field-list td, table.field-list th {
border: 0 !important;
}
table.footnote td, table.footnote th {
border: 0 !important;
}
th {
text-align: left;
padding-right: 5px;
}
/* -- other body styles ----------------------------------------------------- */
dl {
margin-bottom: 15px;
}
dd p {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
dt:target, .highlight {
background-color: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.refcount {
color: #060;
}
.optional {
font-size: 1.3em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
}
td.linenos pre {
padding: 5px 0px;
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
margin-left: 0.5em;
}
table.highlighttable td {
padding: 0 0.5em 0 0.5em;
}
tt.descname {
background-color: transparent;
font-weight: bold;
font-size: 1.2em;
}
tt.descclassname {
background-color: transparent;
}
tt.xref, a tt {
background-color: transparent;
font-weight: bold;
}
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
background-color: transparent;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}
+218
View File
@@ -0,0 +1,218 @@
/**
* Sphinx stylesheet -- default theme
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: sans-serif;
font-size: 100%;
background-color: #11303d;
color: #000;
margin: 0;
padding: 0;
}
div.document {
background-color: #1c4e63;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 230px;
}
div.body {
background-color: #ffffff;
color: #000000;
padding: 0 20px 30px 20px;
}
div.footer {
color: #ffffff;
width: 100%;
padding: 9px 0 9px 0;
text-align: center;
font-size: 75%;
}
div.footer a {
color: #ffffff;
text-decoration: underline;
}
div.related {
background-color: #133f52;
line-height: 30px;
color: #ffffff;
}
div.related a {
color: #ffffff;
}
div.sphinxsidebar {
}
div.sphinxsidebar h3 {
font-family: 'Trebuchet MS', sans-serif;
color: #ffffff;
font-size: 1.4em;
font-weight: normal;
margin: 0;
padding: 0;
}
div.sphinxsidebar h3 a {
color: #ffffff;
}
div.sphinxsidebar h4 {
font-family: 'Trebuchet MS', sans-serif;
color: #ffffff;
font-size: 1.3em;
font-weight: normal;
margin: 5px 0 0 0;
padding: 0;
}
div.sphinxsidebar p {
color: #ffffff;
}
div.sphinxsidebar p.topless {
margin: 5px 10px 10px 10px;
}
div.sphinxsidebar ul {
margin: 10px;
padding: 0;
color: #ffffff;
}
div.sphinxsidebar a {
color: #98dbcc;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #355f7c;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
div.body p, div.body dd, div.body li {
text-align: justify;
line-height: 130%;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Trebuchet MS', sans-serif;
background-color: #f2f2f2;
font-weight: normal;
color: #20435c;
border-bottom: 1px solid #ccc;
margin: 20px -20px 10px -20px;
padding: 3px 0 3px 10px;
}
div.body h1 { margin-top: 0; font-size: 200%; }
div.body h2 { font-size: 160%; }
div.body h3 { font-size: 140%; }
div.body h4 { font-size: 120%; }
div.body h5 { font-size: 110%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #c60f0f;
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
}
a.headerlink:hover {
background-color: #c60f0f;
color: white;
}
div.body p, div.body dd, div.body li {
text-align: justify;
line-height: 130%;
}
div.admonition p.admonition-title + p {
display: inline;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre {
padding: 5px;
background-color: #eeffcc;
color: #333333;
line-height: 120%;
border: 1px solid #ac9;
border-left: none;
border-right: none;
}
tt {
background-color: #ecf0f3;
padding: 0 1px 0 1px;
font-size: 0.95em;
}
.warning tt {
background: #efc2c2;
}
.note tt {
background: #d6d6d6;
}
+232
View File
@@ -0,0 +1,232 @@
/// XXX: make it cross browser
/**
* make the code below compatible with browsers without
* an installed firebug like debugger
*/
if (!window.console || !console.firebug) {
var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
"group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
window.console = {};
for (var i = 0; i < names.length; ++i)
window.console[names[i]] = function() {}
}
/**
* small helper function to urldecode strings
*/
jQuery.urldecode = function(x) {
return decodeURIComponent(x).replace(/\+/g, ' ');
}
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s == 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
}
/**
* small function to check if an array contains
* a given item.
*/
jQuery.contains = function(arr, item) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] == item)
return true;
}
return false;
}
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node) {
if (node.nodeType == 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 && !jQuery.className.has(node.parentNode, className)) {
var span = document.createElement("span");
span.className = className;
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this)
});
}
}
return this.each(function() {
highlight(this);
});
}
/**
* Small JavaScript module for the documentation.
*/
var Documentation = {
init : function() {
this.fixFirefoxAnchorBug();
this.highlightSearchWords();
this.initModIndex();
},
/**
* i18n support
*/
TRANSLATIONS : {},
PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; },
LOCALE : 'unknown',
// gettext and ngettext don't access this so that the functions
// can savely bound to a different name (_ = Documentation.gettext)
gettext : function(string) {
var translated = Documentation.TRANSLATIONS[string];
if (typeof translated == 'undefined')
return string;
return (typeof translated == 'string') ? translated : translated[0];
},
ngettext : function(singular, plural, n) {
var translated = Documentation.TRANSLATIONS[singular];
if (typeof translated == 'undefined')
return (n == 1) ? singular : plural;
return translated[Documentation.PLURALEXPR(n)];
},
addTranslations : function(catalog) {
for (var key in catalog.messages)
this.TRANSLATIONS[key] = catalog.messages[key];
this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
this.LOCALE = catalog.locale;
},
/**
* add context elements like header anchor links
*/
addContextElements : function() {
$('div[id] > :header:first').each(function() {
$('<a class="headerlink">\u00B6</a>').
attr('href', '#' + this.id).
attr('title', _('Permalink to this headline')).
appendTo(this);
});
$('dt[id]').each(function() {
$('<a class="headerlink">\u00B6</a>').
attr('href', '#' + this.id).
attr('title', _('Permalink to this definition')).
appendTo(this);
});
},
/**
* workaround a firefox stupidity
*/
fixFirefoxAnchorBug : function() {
if (document.location.hash && $.browser.mozilla)
window.setTimeout(function() {
document.location.href += '';
}, 10);
},
/**
* highlight the search words provided in the url in the text
*/
highlightSearchWords : function() {
var params = $.getQueryParameters();
var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
if (terms.length) {
var body = $('div.body');
window.setTimeout(function() {
$.each(terms, function() {
body.highlightText(this.toLowerCase(), 'highlight');
});
}, 10);
$('<li class="highlight-link"><a href="javascript:Documentation.' +
'hideSearchWords()">' + _('Hide Search Matches') + '</a></li>')
.appendTo($('.sidebar .this-page-menu'));
}
},
/**
* init the modindex toggle buttons
*/
initModIndex : function() {
var togglers = $('img.toggler').click(function() {
var src = $(this).attr('src');
var idnum = $(this).attr('id').substr(7);
console.log($('tr.cg-' + idnum).toggle());
if (src.substr(-9) == 'minus.png')
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
else
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
}).css('display', '');
if (DOCUMENTATION_OPTIONS.COLLAPSE_MODINDEX) {
togglers.click();
}
},
/**
* helper function to hide the search marks again
*/
hideSearchWords : function() {
$('.sidebar .this-page-menu li.highlight-link').fadeOut(300);
$('span.highlight').removeClass('highlight');
},
/**
* make the url absolute
*/
makeURL : function(relativeURL) {
return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
},
/**
* get the current relative url
*/
getCurrentURL : function() {
var path = document.location.pathname;
var parts = path.split(/\//);
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
if (this == '..')
parts.pop();
});
var url = parts.join('/');
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
}
};
// quick alias for translations
_ = Documentation.gettext;
$(document).ready(function() {
Documentation.init();
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

+32
View File
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

+61
View File
@@ -0,0 +1,61 @@
.hll { background-color: #ffffcc }
.c { color: #408090; font-style: italic } /* Comment */
.err { border: 1px solid #FF0000 } /* Error */
.k { color: #007020; font-weight: bold } /* Keyword */
.o { color: #666666 } /* Operator */
.cm { color: #408090; font-style: italic } /* Comment.Multiline */
.cp { color: #007020 } /* Comment.Preproc */
.c1 { color: #408090; font-style: italic } /* Comment.Single */
.cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
.gd { color: #A00000 } /* Generic.Deleted */
.ge { font-style: italic } /* Generic.Emph */
.gr { color: #FF0000 } /* Generic.Error */
.gh { color: #000080; font-weight: bold } /* Generic.Heading */
.gi { color: #00A000 } /* Generic.Inserted */
.go { color: #303030 } /* Generic.Output */
.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
.gs { font-weight: bold } /* Generic.Strong */
.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.gt { color: #0040D0 } /* Generic.Traceback */
.kc { color: #007020; font-weight: bold } /* Keyword.Constant */
.kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
.kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
.kp { color: #007020 } /* Keyword.Pseudo */
.kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
.kt { color: #902000 } /* Keyword.Type */
.m { color: #208050 } /* Literal.Number */
.s { color: #4070a0 } /* Literal.String */
.na { color: #4070a0 } /* Name.Attribute */
.nb { color: #007020 } /* Name.Builtin */
.nc { color: #0e84b5; font-weight: bold } /* Name.Class */
.no { color: #60add5 } /* Name.Constant */
.nd { color: #555555; font-weight: bold } /* Name.Decorator */
.ni { color: #d55537; font-weight: bold } /* Name.Entity */
.ne { color: #007020 } /* Name.Exception */
.nf { color: #06287e } /* Name.Function */
.nl { color: #002070; font-weight: bold } /* Name.Label */
.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
.nt { color: #062873; font-weight: bold } /* Name.Tag */
.nv { color: #bb60d5 } /* Name.Variable */
.ow { color: #007020; font-weight: bold } /* Operator.Word */
.w { color: #bbbbbb } /* Text.Whitespace */
.mf { color: #208050 } /* Literal.Number.Float */
.mh { color: #208050 } /* Literal.Number.Hex */
.mi { color: #208050 } /* Literal.Number.Integer */
.mo { color: #208050 } /* Literal.Number.Oct */
.sb { color: #4070a0 } /* Literal.String.Backtick */
.sc { color: #4070a0 } /* Literal.String.Char */
.sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
.s2 { color: #4070a0 } /* Literal.String.Double */
.se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
.sh { color: #4070a0 } /* Literal.String.Heredoc */
.si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
.sx { color: #c65d09 } /* Literal.String.Other */
.sr { color: #235388 } /* Literal.String.Regex */
.s1 { color: #4070a0 } /* Literal.String.Single */
.ss { color: #517918 } /* Literal.String.Symbol */
.bp { color: #007020 } /* Name.Builtin.Pseudo */
.vc { color: #bb60d5 } /* Name.Variable.Class */
.vg { color: #bb60d5 } /* Name.Variable.Global */
.vi { color: #bb60d5 } /* Name.Variable.Instance */
.il { color: #208050 } /* Literal.Number.Integer.Long */
+467
View File
@@ -0,0 +1,467 @@
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words, hlwords is the list of normal, unstemmed
* words. the first one is used to find the occurance, the
* latter for highlighting it.
*/
jQuery.makeSearchSummary = function(text, keywords, hlwords) {
var textLower = text.toLowerCase();
var start = 0;
$.each(keywords, function() {
var i = textLower.indexOf(this.toLowerCase());
if (i > -1)
start = i;
});
start = Math.max(start - 120, 0);
var excerpt = ((start > 0) ? '...' : '') +
$.trim(text.substr(start, 240)) +
((start + 240 - text.length) ? '...' : '');
var rv = $('<div class="context"></div>').text(excerpt);
$.each(hlwords, function() {
rv = rv.highlightText(this, 'highlight');
});
return rv;
}
/**
* Porter Stemmer
*/
var PorterStemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}
/**
* Search Module
*/
var Search = {
_index : null,
_queued_query : null,
_pulse_status : -1,
init : function() {
var params = $.getQueryParameters();
if (params.q) {
var query = params.q[0];
$('input[name="q"]')[0].value = query;
this.performSearch(query);
}
},
/**
* Sets the index
*/
setIndex : function(index) {
var q;
this._index = index;
if ((q = this._queued_query) !== null) {
this._queued_query = null;
Search.query(q);
}
},
hasIndex : function() {
return this._index !== null;
},
deferQuery : function(query) {
this._queued_query = query;
},
stopPulse : function() {
this._pulse_status = 0;
},
startPulse : function() {
if (this._pulse_status >= 0)
return;
function pulse() {
Search._pulse_status = (Search._pulse_status + 1) % 4;
var dotString = '';
for (var i = 0; i < Search._pulse_status; i++)
dotString += '.';
Search.dots.text(dotString);
if (Search._pulse_status > -1)
window.setTimeout(pulse, 500);
};
pulse();
},
/**
* perform a search for something
*/
performSearch : function(query) {
// create the required interface elements
this.out = $('#search-results');
this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
this.dots = $('<span></span>').appendTo(this.title);
this.status = $('<p style="display: none"></p>').appendTo(this.out);
this.output = $('<ul class="search"/>').appendTo(this.out);
$('#search-progress').text(_('Preparing search...'));
this.startPulse();
// index already loaded, the browser was quick!
if (this.hasIndex())
this.query(query);
else
this.deferQuery(query);
},
query : function(query) {
// stem the searchterms and add them to the
// correct list
var stemmer = new PorterStemmer();
var searchterms = [];
var excluded = [];
var hlterms = [];
var tmp = query.split(/\s+/);
var object = (tmp.length == 1) ? tmp[0].toLowerCase() : null;
for (var i = 0; i < tmp.length; i++) {
// stem the word
var word = stemmer.stemWord(tmp[i]).toLowerCase();
// select the correct list
if (word[0] == '-') {
var toAppend = excluded;
word = word.substr(1);
}
else {
var toAppend = searchterms;
hlterms.push(tmp[i].toLowerCase());
}
// only add if not already in the list
if (!$.contains(toAppend, word))
toAppend.push(word);
};
var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
console.debug('SEARCH: searching for:');
console.info('required: ', searchterms);
console.info('excluded: ', excluded);
// prepare search
var filenames = this._index.filenames;
var titles = this._index.titles;
var terms = this._index.terms;
var descrefs = this._index.descrefs;
var modules = this._index.modules;
var desctypes = this._index.desctypes;
var fileMap = {};
var files = null;
var objectResults = [];
var regularResults = [];
$('#search-progress').empty();
// lookup as object
if (object != null) {
for (var module in modules) {
if (module.indexOf(object) > -1) {
fn = modules[module];
descr = _('module, in ') + titles[fn];
objectResults.push([filenames[fn], module, '#module-'+module, descr]);
}
}
for (var prefix in descrefs) {
for (var name in descrefs[prefix]) {
var fullname = (prefix ? prefix + '.' : '') + name;
if (fullname.toLowerCase().indexOf(object) > -1) {
match = descrefs[prefix][name];
descr = desctypes[match[1]] + _(', in ') + titles[match[0]];
objectResults.push([filenames[match[0]], fullname, '#'+fullname, descr]);
}
}
}
}
// sort results descending
objectResults.sort(function(a, b) {
return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
});
// perform the search on the required terms
for (var i = 0; i < searchterms.length; i++) {
var word = searchterms[i];
// no match but word was a required one
if ((files = terms[word]) == null)
break;
if (files.length == undefined) {
files = [files];
}
// create the mapping
for (var j = 0; j < files.length; j++) {
var file = files[j];
if (file in fileMap)
fileMap[file].push(word);
else
fileMap[file] = [word];
}
}
// now check if the files don't contain excluded terms
for (var file in fileMap) {
var valid = true;
// check if all requirements are matched
if (fileMap[file].length != searchterms.length)
continue;
// ensure that none of the excluded terms is in the
// search result.
for (var i = 0; i < excluded.length; i++) {
if (terms[excluded[i]] == file ||
$.contains(terms[excluded[i]] || [], file)) {
valid = false;
break;
}
}
// if we have still a valid result we can add it
// to the result list
if (valid)
regularResults.push([filenames[file], titles[file], '', null]);
}
// delete unused variables in order to not waste
// memory until list is retrieved completely
delete filenames, titles, terms;
// now sort the regular results descending by title
regularResults.sort(function(a, b) {
var left = a[1].toLowerCase();
var right = b[1].toLowerCase();
return (left > right) ? -1 : ((left < right) ? 1 : 0);
});
// combine both
var results = regularResults.concat(objectResults);
// print the results
var resultCount = results.length;
function displayNextItem() {
// results left, load the summary and display it
if (results.length) {
var item = results.pop();
var listItem = $('<li style="display:none"></li>');
listItem.append($('<a/>').attr(
'href',
item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
highlightstring + item[2]).html(item[1]));
if (item[3]) {
listItem.append($('<span> (' + item[3] + ')</span>'));
Search.output.append(listItem);
listItem.slideDown(5, function() {
displayNextItem();
});
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
$.get('_sources/' + item[0] + '.txt', function(data) {
listItem.append($.makeSearchSummary(data, searchterms, hlterms));
Search.output.append(listItem);
listItem.slideDown(5, function() {
displayNextItem();
});
});
} else {
// no source available, just display title
Search.output.append(listItem);
listItem.slideDown(5, function() {
displayNextItem();
});
}
}
// search finished, update title and status message
else {
Search.stopPulse();
Search.title.text(_('Search Results'));
if (!resultCount)
Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
else
Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
Search.status.fadeIn(500);
}
}
displayNextItem();
}
}
$(document).ready(function() {
Search.init();
});
+159
View File
@@ -0,0 +1,159 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Architectural Principles &mdash; plone.app.discussion v1.0b4 documentation</title>
<link rel="stylesheet" href="_static/default.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '#',
VERSION: '1.0b4',
COLLAPSE_MODINDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="plone.app.discussion v1.0b4 documentation" href="index.html" />
<link rel="next" title="Design Notes" href="design.html" />
<link rel="prev" title="Welcome to plone.app.discussions documentation!" href="index.html" />
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="modindex.html" title="Global Module Index"
accesskey="M">modules</a> |</li>
<li class="right" >
<a href="design.html" title="Design Notes"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="index.html" title="Welcome to plone.app.discussions documentation!"
accesskey="P">previous</a> |</li>
<li><a href="index.html">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
<div class="section" id="architectural-principles">
<h1>Architectural Principles<a class="headerlink" href="#architectural-principles" title="Permalink to this headline"></a></h1>
<p>This document outlines architectural principles used in the design of
plone.app.discussion.</p>
<blockquote>
<dl class="docutils">
<dt><strong>Discussion items have a portal_type</strong></dt>
<dd>This makes it easier to search for them and manage them using existing
CMF and Plone UI constructs.</dd>
<dt><strong>Discussion items are cataloged</strong></dt>
<dd>It is be possible to search for discussion items like any other type of
content.</dd>
<dt><strong>Discussion items are subject to workflow and permission</strong></dt>
<dd>Moderation, anonymous commenting, and auto approve/reject should be
handled using workflow states, automatic and manual transitions, and
permissions.</dd>
<dt><strong>Discussion items are light weight objects</strong></dt>
<dd>Discussion item objects are as light weight as possible.
Ideally, a discussion item should be as lightweight as a catalog brain.
This may mean that we forego convenience base classes and re-implement
certain interfaces. Comments should not provide the full set of dublin
core metadata, though custom indexers can be used to provide values for
standard catalog indexes.</dd>
<dt><strong>Optimise for retrival speed</strong></dt>
<dd>HTML filtering and other processing should happen on save, not on render,
to make rendering quick.</dd>
<dt><strong>Settings are stored using plone.registry</strong></dt>
<dd>Any global setting should be stored in plone.registry records</dd>
<dt><strong>Forms are constructed using extensible z3c.form forms</strong></dt>
<dd>This allows plugins (such as spam protection algorithms) to provide
additional validation</dd>
<dt><strong>Discussion items are stored in a BTree container</strong></dt>
<dd>This allows faster lookup and manipulation</dd>
<dt><strong>Discussion items are accessed using a dict-like interface</strong></dt>
<dd>This makes iteration and manipulation more natural. Even if comments are
not stored threaded, the dict interface should act as if they are, i.e.
calling items() on a comment should return the replies to that comment
(in order).</dd>
<dt><strong>Discussion items are retrieved in reverse creation date order</strong></dt>
<dd>Discussion items do not need to support explicit ordering. They should
always be retrieved in reverse creation date order (most recent for).
They can be stored with keys so that this is always true.</dd>
<dt><strong>Discussion items do not need readable ids</strong></dt>
<dd>Ids can be based on the creation date.</dd>
<dt><strong>Discussion items send events</strong></dt>
<dd>The usual zope.lifecycleevent and zope.container events are fired when
discussion items are added, removed, or modified.</dd>
</dl>
</blockquote>
</div>
</div>
</div>
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
<h4>Previous topic</h4>
<p class="topless"><a href="index.html"
title="previous chapter">Welcome to plone.app.discussion&#8217;s documentation!</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="design.html"
title="next chapter">Design Notes</a></p>
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/architecture.txt"
rel="nofollow">Show Source</a></li>
</ul>
<div id="searchbox" style="display: none">
<h3>Quick search</h3>
<form class="search" action="search.html" method="get">
<input type="text" name="q" size="18" />
<input type="submit" value="Go" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<p class="searchtip" style="font-size: 90%">
Enter search terms or a module, class or function name.
</p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="modindex.html" title="Global Module Index"
>modules</a> |</li>
<li class="right" >
<a href="design.html" title="Design Notes"
>next</a> |</li>
<li class="right" >
<a href="index.html" title="Welcome to plone.app.discussions documentation!"
>previous</a> |</li>
<li><a href="index.html">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2010, Timo Stollenwerk.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 0.6.4.
</div>
</body>
</html>
+303
View File
@@ -0,0 +1,303 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Design Notes &mdash; plone.app.discussion v1.0b4 documentation</title>
<link rel="stylesheet" href="_static/default.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '#',
VERSION: '1.0b4',
COLLAPSE_MODINDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="plone.app.discussion v1.0b4 documentation" href="index.html" />
<link rel="prev" title="Architectural Principles" href="architecture.html" />
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="modindex.html" title="Global Module Index"
accesskey="M">modules</a> |</li>
<li class="right" >
<a href="architecture.html" title="Architectural Principles"
accesskey="P">previous</a> |</li>
<li><a href="index.html">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
<div class="section" id="design-notes">
<h1>Design Notes<a class="headerlink" href="#design-notes" title="Permalink to this headline"></a></h1>
<p>This document contains design notes for plone.app.discussion.</p>
<div class="section" id="storage-and-traversal">
<h2>Storage and traversal<a class="headerlink" href="#storage-and-traversal" title="Permalink to this headline"></a></h2>
<p>For each content item, there is a Conversation object stored in annotations.
This can be traversed to via the ++conversation++ namespace, but also fetched
via an adapter lookup to IConversation.</p>
<p>The conversation stores all comments related to a content object. Each
comment has an integer id (also representable as a string, to act as an OFS
id and allow traversal). Hence, traversing to obj/++conversation++/123 retrieves
the comment with id 123.</p>
<p>Comments ids are assigned in order, so a comment with id N was posted before
a comment with id N + 1. However, it is not guaranteed that ids will be
incremental. Ids must be positive integers - 0 or negative numbers are not
allowed.</p>
<p>Threading information is stored in the conversation: we keep track of the
set of children and the parent if any comment. Top-level comments have a
parent id of 0. This information is managed by the conversation class when
comments are manipulated using a dict-like API.</p>
<p>Note that the __parent__/acquisition parent of an IComment is the
IConversation, and the __parent__/acquisition parent of an IConversation is
the content object.</p>
</div>
<div class="section" id="events">
<h2>Events<a class="headerlink" href="#events" title="Permalink to this headline"></a></h2>
<p>Manipulating the IConversation object should fire the usual IObjectAddedEvent
and IObjectRemovedEvent events. The UI may further fire IObjectCreatedEvent
and IObjectModifiedEvent for comments.</p>
</div>
<div class="section" id="factories">
<h2>Factories<a class="headerlink" href="#factories" title="Permalink to this headline"></a></h2>
<p>Comments should always be created via the &#8216;Discussion Item&#8217; IFactory utility.
Conversations should always be obtained via the IConversation adapter (even
the ++conversation++ namespace should use this). This makes it possible to
replace conversations and comments transparently.</p>
</div>
<div class="section" id="the-comment-class">
<h2>The Comment class<a class="headerlink" href="#the-comment-class" title="Permalink to this headline"></a></h2>
<p>The inheritance tree for DiscussionItem is shown below. Classes we want to
mix in and interface we want to implement in the Comment class are marked
with [x].</p>
<blockquote>
<dl class="docutils">
<dt>[ ] DiscussionItem</dt>
<dd><dl class="first last docutils">
<dt>[ ] Document</dt>
<dd><dl class="first last docutils">
<dt>[ ] PortalContent = [ ] IContentish</dt>
<dd><p class="first">[ ] DynamicType = [ ] IDynamicType
[ ] CMFCatalogAware = [ ] &lt;no interface&gt;
[ ] SimpleItem = [ ] ISimpleItem</p>
<blockquote class="last">
<dl class="docutils">
<dt>[ ] Item [ ]</dt>
<dd>[?] Base = [ ] &lt;no interface&gt;
[ ] Resource = [ ] &lt;no interface&gt;
[ ] CopySource = [ ] ICopySource
[ ] Tabs = [ ] &lt;no interface&gt;
[x] Traversable = [ ] ITraversable
[ ] Element = [ ] &lt;no interface&gt;
[x] Owned = [ ] IOwned
[ ] UndoSupport = [ ] IUndoSupport</dd>
</dl>
<p>[ ] Persistent [ ]
[ ] Implicit [ ]
[x] RoleManager = [ ] IRoleManager</p>
<blockquote>
[ ] RoleManager = [ ] IPermissionMappingSupport</blockquote>
</blockquote>
</dd>
<dt>[ ] DefaultDublinCoreImpl = [ ] IDublinCore</dt>
<dd><blockquote class="first">
[ ] ICatalogableDublinCore
[ ] IMutableDublinCore</blockquote>
<p class="last">[ ] PropertyManager = [ ] IPropertyManager</p>
</dd>
</dl>
</dd>
</dl>
</dd>
</dl>
</blockquote>
<p>Thus, we want:</p>
<blockquote>
<ul>
<li><dl class="first docutils">
<dt>Traversable, to get absolute_url() and friends</dt>
<dd><ul class="first last simple">
<li>this requires a good acquisition chain at all times</li>
</ul>
</dd>
</dl>
</li>
<li><dl class="first docutils">
<dt>Acquisition.Explicit, to support acquisition</dt>
<dd><ul class="first last simple">
<li>we do not want implicit acquisition</li>
</ul>
</dd>
</dl>
</li>
<li><p class="first">Owned, to be able to track ownership</p>
</li>
<li><p class="first">RoleManager, to support permissions and local roles</p>
</li>
</ul>
</blockquote>
<p>We also want to use a number of custom indexers for most of the standard
metadata such as creator, effective date etc.</p>
<p>Finally, we&#8217;ll need event handlers to perform the actual indexing.</p>
</div>
<div class="section" id="discussion-settings">
<h2>Discussion settings<a class="headerlink" href="#discussion-settings" title="Permalink to this headline"></a></h2>
<p>Discussion can be enabled per-type and per-instance, via values in the FTI
(allow_discussion) and on the object. These will remain unchanged. The
IConversation object&#8217;s &#8216;enabled&#8217; property should consult these.</p>
<p>Global settings should be managed using plone.registry. A control panel
can be generated from this as well, using the helper class in
plone.app.registry.</p>
<p>Note that some settings, notably those to do with permissions and workflow,
will need to be wired up as custom form fields with custom data mangers
or similar.</p>
</div>
<div class="section" id="workflow-and-permissions">
<h2>Workflow and permissions<a class="headerlink" href="#workflow-and-permissions" title="Permalink to this headline"></a></h2>
<p>Where possible, we should use existing permissions:</p>
<blockquote>
<ul class="simple">
<li>View</li>
<li>Reply to Item</li>
<li>Modify Portal Content</li>
<li>Request Review</li>
</ul>
</blockquote>
<p>In addition, we&#8217;ll need a &#8216;Moderator&#8217; role and a moderation permission,</p>
<blockquote>
<ul class="simple">
<li>Moderate comment</li>
<li>Bypass moderation</li>
</ul>
</blockquote>
<p>To control whether Anonymous can post comments, we manage the &#8216;Reply to Item&#8217;
permission. To control whether moderation is required for various roles, we
could manage the &#8216;Bypass moderation&#8217; permission.</p>
<p>These could work in a workflow like this:</p>
<blockquote>
<ul>
<li><dl class="first docutils">
<dt>&#8211;&gt; [posted] &#8211; {publish} &#8211;&gt; [published]&#8211;&gt; *</dt>
<dd><div class="first line-block">
<div class="line">^</div>
<div class="line"><a href="#id1"><span class="problematic" id="id2">|</span></a></div>
</div>
<p class="last">+&#8212;&#8211; {auto-publish} &#8212;&#8211;+
| |
+&#8212;&#8211; {auto-moderate} &#8212;-+</p>
</dd>
</dl>
</li>
</ul>
</blockquote>
<p>The &#8216;posted&#8217; state is the initial state. &#8216;published&#8217; is the state where the
comment is visible to non-reviewers.</p>
<p>The &#8216;publish&#8217; transition would be protected by the &#8216;Moderate comment&#8217;
permission. We could have states and transition for &#8216;rejected&#8217;, etc, but it
is probably just as good to delete comments that are rejected.</p>
<p>The &#8216;auto-publish&#8217; transition would be an automatic transition protected by
the &#8216;Bypass moderation&#8217; permission.</p>
<p>The &#8216;auto-moderate&#8217; transition would be another automatic transition protected
by an expression (e.g. calling a view) that returns True if the user is on
an auto-moderation &#8216;white-list&#8217;, e.g. by email address or username.</p>
</div>
<div class="section" id="forms-and-ui">
<h2>Forms and UI<a class="headerlink" href="#forms-and-ui" title="Permalink to this headline"></a></h2>
<p>The basic commenting display/reply form should be placed in a viewlet.</p>
<p>Ideally, the reply form should be inline, perhaps revealed with JavaScript
if enabled. This allows full contextualisation of replies. The current
solution, with a separate form that shows some context, is brittle and
over-complicated.</p>
<p>If we support quoting of comments in replies, we can load the text to quote
using JavaScript as well.</p>
<p>As a fall-back for non-JavaScript enabled browsers, it is probably OK not to
support quoting and/or viewing of context, e.g. the user is taken to a standalone
&#8216;comment reply&#8217; form.</p>
<p>All actual forms should be handled using z3c.form and plone.z3cform&#8217;s
ExtensibleForm support. This makes it possible to plug in additional fields
declaratively, e.g. to include spam protection.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
<h3><a href="index.html">Table Of Contents</a></h3>
<ul>
<li><a class="reference external" href="#">Design Notes</a><ul>
<li><a class="reference external" href="#storage-and-traversal">Storage and traversal</a></li>
<li><a class="reference external" href="#events">Events</a></li>
<li><a class="reference external" href="#factories">Factories</a></li>
<li><a class="reference external" href="#the-comment-class">The Comment class</a></li>
<li><a class="reference external" href="#discussion-settings">Discussion settings</a></li>
<li><a class="reference external" href="#workflow-and-permissions">Workflow and permissions</a></li>
<li><a class="reference external" href="#forms-and-ui">Forms and UI</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="architecture.html"
title="previous chapter">Architectural Principles</a></p>
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/design.txt"
rel="nofollow">Show Source</a></li>
</ul>
<div id="searchbox" style="display: none">
<h3>Quick search</h3>
<form class="search" action="search.html" method="get">
<input type="text" name="q" size="18" />
<input type="submit" value="Go" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<p class="searchtip" style="font-size: 90%">
Enter search terms or a module, class or function name.
</p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="modindex.html" title="Global Module Index"
>modules</a> |</li>
<li class="right" >
<a href="architecture.html" title="Architectural Principles"
>previous</a> |</li>
<li><a href="index.html">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2010, Timo Stollenwerk.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 0.6.4.
</div>
</body>
</html>
+116
View File
@@ -0,0 +1,116 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Index &mdash; plone.app.discussion v1.0b4 documentation</title>
<link rel="stylesheet" href="_static/default.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '#',
VERSION: '1.0b4',
COLLAPSE_MODINDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="plone.app.discussion v1.0b4 documentation" href="index.html" />
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="modindex.html" title="Global Module Index"
accesskey="M">modules</a> |</li>
<li><a href="index.html">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
<h1 id="index">Index</h1>
<a href="#C"><strong>C</strong></a> | <a href="#E"><strong>E</strong></a> | <a href="#P"><strong>P</strong></a>
<hr />
<h2 id="C">C</h2>
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
<dl>
<dt><a href="index.html#plone.app.discussion.conversation.Conversation">Conversation (class in plone.app.discussion.conversation)</a></dt></dl></td><td width="33%" valign="top"><dl>
</dl></td></tr></table>
<h2 id="E">E</h2>
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
<dl>
<dt><a href="index.html#plone.app.discussion.conversation.Conversation.enabled">enabled() (plone.app.discussion.conversation.Conversation method)</a></dt></dl></td><td width="33%" valign="top"><dl>
</dl></td></tr></table>
<h2 id="P">P</h2>
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
<dl>
<dt><a href="index.html#module-plone.app.discussion">plone.app.discussion (module)</a></dt></dl></td><td width="33%" valign="top"><dl>
</dl></td></tr></table>
</div>
</div>
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
<div id="searchbox" style="display: none">
<h3>Quick search</h3>
<form class="search" action="search.html" method="get">
<input type="text" name="q" size="18" />
<input type="submit" value="Go" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<p class="searchtip" style="font-size: 90%">
Enter search terms or a module, class or function name.
</p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
>index</a></li>
<li class="right" >
<a href="modindex.html" title="Global Module Index"
>modules</a> |</li>
<li><a href="index.html">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2010, Timo Stollenwerk.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 0.6.4.
</div>
</body>
</html>
+278
View File
@@ -0,0 +1,278 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Welcome to plone.app.discussions documentation! &mdash; plone.app.discussion v1.0b4 documentation</title>
<link rel="stylesheet" href="_static/default.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '#',
VERSION: '1.0b4',
COLLAPSE_MODINDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="plone.app.discussion v1.0b4 documentation" href="#" />
<link rel="next" title="Architectural Principles" href="architecture.html" />
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="modindex.html" title="Global Module Index"
accesskey="M">modules</a> |</li>
<li class="right" >
<a href="architecture.html" title="Architectural Principles"
accesskey="N">next</a> |</li>
<li><a href="#">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
<div class="section" id="welcome-to-plone-app-discussion-s-documentation">
<h1>Welcome to plone.app.discussion&#8217;s documentation!<a class="headerlink" href="#welcome-to-plone-app-discussion-s-documentation" title="Permalink to this headline"></a></h1>
<p>Contents:</p>
<ul>
<li class="toctree-l1"><a class="reference external" href="architecture.html">Architectural Principles</a></li>
<li class="toctree-l1"><a class="reference external" href="design.html">Design Notes</a><ul>
<li class="toctree-l2"><a class="reference external" href="design.html#storage-and-traversal">Storage and traversal</a></li>
<li class="toctree-l2"><a class="reference external" href="design.html#events">Events</a></li>
<li class="toctree-l2"><a class="reference external" href="design.html#factories">Factories</a></li>
<li class="toctree-l2"><a class="reference external" href="design.html#the-comment-class">The Comment class</a></li>
<li class="toctree-l2"><a class="reference external" href="design.html#discussion-settings">Discussion settings</a></li>
<li class="toctree-l2"><a class="reference external" href="design.html#workflow-and-permissions">Workflow and permissions</a></li>
<li class="toctree-l2"><a class="reference external" href="design.html#forms-and-ui">Forms and UI</a></li>
</ul>
</li>
</ul>
<div class="section" id="module-plone.app.discussion">
<h2>API<a class="headerlink" href="#module-plone.app.discussion" title="Permalink to this headline"></a></h2>
<dl class="class">
<dt id="plone.app.discussion.conversation.Conversation">
<em class="property">class </em><tt class="descclassname">plone.app.discussion.conversation.</tt><tt class="descname">Conversation</tt><big>(</big><em>id='++conversation++default'</em><big>)</big><a class="headerlink" href="#plone.app.discussion.conversation.Conversation" title="Permalink to this definition"></a></dt>
<dd><p>A conversation is a container for all comments on a content object.</p>
<p>It manages internal data structures for comment threading and efficient
comment lookup.</p>
<dl class="method">
<dt id="plone.app.discussion.conversation.Conversation.enabled">
<tt class="descname">enabled</tt><big>(</big><big>)</big><a class="headerlink" href="#plone.app.discussion.conversation.Conversation.enabled" title="Permalink to this definition"></a></dt>
<dd>hi world</dd></dl>
</dd></dl>
</div>
</div>
<div class="section" id="changelog">
<h1>Changelog<a class="headerlink" href="#changelog" title="Permalink to this headline"></a></h1>
<div class="section" id="b4-unreleased">
<h2>1.0b4 (unreleased)<a class="headerlink" href="#b4-unreleased" title="Permalink to this headline"></a></h2>
<ul class="simple">
<li>Fix #662654: As an administrator, I can configure a Collection to show recent comments.
Comment.Type() now correctly returns the FTI title (&#8216;Comment&#8217;)
[chaoflow]</li>
<li>German translation updated.
[juh]</li>
<li>Fix #2419342: Fix untranslated published/deleted status messages.
[timo]</li>
<li>Remove fixed width of the actions column of the moderation view. The
translated button titles can differ in size from the English titles.
[timo]</li>
<li>Fix #2494228: Remove comments as well when a content object is deleted.
[timo]</li>
<li>Fix unicode error when non-ASCII characters are typed into the name field of a
comment by anonymous users.
[regebro]</li>
<li>New feature: As a moderator, I am notified when new comments require my
attention.
[timo]</li>
<li>Make p.a.d. work with the recent version of plone.z3cform (0.5.10)
[timo]</li>
<li>Make p.a.d. styles less generic. This fixes #10253.
[timo]</li>
<li>Added greek translation.
[ggozad]</li>
</ul>
</div>
<div class="section" id="b3-2010-01-28">
<h2>1.0b3 (2010-01-28)<a class="headerlink" href="#b3-2010-01-28" title="Permalink to this headline"></a></h2>
<ul class="simple">
<li>Added an i18n directory for messages in the plone domain and updated scripts
to rebuild and sync it.
[hannosch]</li>
<li>Added an optional conversationCanonicalAdapterFactory showing how to share
comments across all translations with LinguaPlone, by storing and retrieving
the conversation from the canonical object.
[hannosch]</li>
<li>Play by the Plone 3.3+ rules and use the INavigationRoot as a base for the
moderation view.
[hannosch]</li>
<li>Added a commentTitle CSS class to the comment titles.
[hannosch]</li>
<li>Update message ids to match their real text.
[hannosch]</li>
<li>Set CSS classes for the comment form in the updateActions method.
[timo]</li>
<li>Respect the allow_comments field on an object and avoid calculations if no
comments should be shown.
[hannosch]</li>
<li>Automatically load the ZCML files of the captcha widgets if they are
installed.
[hannosch]</li>
<li>Fixed i18n domain in GenericSetup profiles to be <tt class="docutils literal"><span class="pre">plone</span></tt>. Other values
aren&#8217;t supported for GS profiles.
[hannosch]</li>
<li>Provide our own copy of the default one state workflow. Not all Plone sites
have this workflow installed.
[hannosch]</li>
<li>Register the event subscribers for the correct interfaces in Plone 3.
[hannosch]</li>
<li>Factored out subscriber declarations into its own ZCML file.
[hannosch]</li>
<li>Bugfix for #2281226: Moderation View: Comments disappear when hitting the
&#8216;Apply&#8217; button without choosing a bulk action.
[timo]</li>
<li>Allow to show the full text of a comment in the moderation view.
[timo]</li>
<li>German translation added.
[timo]</li>
<li>Italian translation added.
[keul]</li>
</ul>
</div>
<div class="section" id="b2-2010-01-22">
<h2>1.0b2 (2010-01-22)<a class="headerlink" href="#b2-2010-01-22" title="Permalink to this headline"></a></h2>
<ul class="simple">
<li>Bugfix for #2010181: The name of a commenter who commented while not logged in
should not appear as a link.</li>
<li>Bugfix for #2010078: Comments that await moderation are visually distinguished
from published comments.</li>
<li>Bugfix for #2010085: Use object_provides instead of portal_type to query the
catalog for comment.</li>
<li>Bugfix for #2010071: p.a.d. works with plone.z3cform 0.5.7 and
plone.app.z3cform 0.4.9 now.</li>
<li>Bugfix for #1513398: Show &#8220;anonymous&#8221; when name field is empty in comment
form.</li>
<li>Migration view: Dry run option added, abort transaction when something goes
wrong during migration, be more verbose about errors.</li>
</ul>
</div>
<div class="section" id="b1-2009-12-08">
<h2>1.0b1 (2009-12-08)<a class="headerlink" href="#b1-2009-12-08" title="Permalink to this headline"></a></h2>
<ul class="simple">
<li>Fix redirect after a adding a comment</li>
<li>Replace yes/no widgets with check boxes in the discussion control panel</li>
<li>Make comments viewlet show up in Plone 4</li>
<li>Apply Plone 4 styles to comment form</li>
<li>Simplify moderation view by removing the filters</li>
</ul>
</div>
<div class="section" id="a2-2009-10-18">
<h2>1.0a2 (2009-10-18)<a class="headerlink" href="#a2-2009-10-18" title="Permalink to this headline"></a></h2>
<ul class="simple">
<li>Plone 4 / Zope 2.12 support</li>
<li>Comment migration script added</li>
<li>Pluggable plone.z3cform comment forms</li>
<li>Captcha and ReCaptcha support added</li>
</ul>
</div>
<div class="section" id="a1-2009-06-07">
<h2>1.0a1 (2009-06-07)<a class="headerlink" href="#a1-2009-06-07" title="Permalink to this headline"></a></h2>
<ul class="simple">
<li>Basic commenting functionality and batch moderation.</li>
</ul>
</div>
</div>
<div class="section" id="indices-and-tables">
<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this headline"></a></h1>
<ul class="simple">
<li><a class="reference external" href="genindex.html"><em>Index</em></a></li>
<li><a class="reference external" href="modindex.html"><em>Module Index</em></a></li>
<li><a class="reference external" href="search.html"><em>Search Page</em></a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
<h3><a href="#">Table Of Contents</a></h3>
<ul>
<li><a class="reference external" href="#">Welcome to plone.app.discussion&#8217;s documentation!</a><ul>
<li><a class="reference external" href="#module-plone.app.discussion">API</a></li>
</ul>
</li>
<li><a class="reference external" href="#changelog">Changelog</a><ul>
<li><a class="reference external" href="#b4-unreleased">1.0b4 (unreleased)</a></li>
<li><a class="reference external" href="#b3-2010-01-28">1.0b3 (2010-01-28)</a></li>
<li><a class="reference external" href="#b2-2010-01-22">1.0b2 (2010-01-22)</a></li>
<li><a class="reference external" href="#b1-2009-12-08">1.0b1 (2009-12-08)</a></li>
<li><a class="reference external" href="#a2-2009-10-18">1.0a2 (2009-10-18)</a></li>
<li><a class="reference external" href="#a1-2009-06-07">1.0a1 (2009-06-07)</a></li>
</ul>
</li>
<li><a class="reference external" href="#indices-and-tables">Indices and tables</a></li>
</ul>
<h4>Next topic</h4>
<p class="topless"><a href="architecture.html"
title="next chapter">Architectural Principles</a></p>
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/index.txt"
rel="nofollow">Show Source</a></li>
</ul>
<div id="searchbox" style="display: none">
<h3>Quick search</h3>
<form class="search" action="search.html" method="get">
<input type="text" name="q" size="18" />
<input type="submit" value="Go" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<p class="searchtip" style="font-size: 90%">
Enter search terms or a module, class or function name.
</p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="modindex.html" title="Global Module Index"
>modules</a> |</li>
<li class="right" >
<a href="architecture.html" title="Architectural Principles"
>next</a> |</li>
<li><a href="#">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2010, Timo Stollenwerk.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 0.6.4.
</div>
</body>
</html>
+104
View File
@@ -0,0 +1,104 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Global Module Index &mdash; plone.app.discussion v1.0b4 documentation</title>
<link rel="stylesheet" href="_static/default.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '#',
VERSION: '1.0b4',
COLLAPSE_MODINDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="plone.app.discussion v1.0b4 documentation" href="index.html" />
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="#" title="Global Module Index"
accesskey="M">modules</a> |</li>
<li><a href="index.html">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
<h1 id="global-module-index">Global Module Index</h1>
<a href="#cap-P"><strong>P</strong></a>
<hr/>
<table width="100%" class="indextable" cellspacing="0" cellpadding="2"><tr class="pcap"><td></td><td>&nbsp;</td><td></td></tr>
<tr class="cap"><td></td><td><a name="cap-P"><strong>P</strong></a></td><td></td></tr><tr>
<td><img src="_static/minus.png" id="toggle-1"
class="toggler" style="display: none" alt="-" /></td>
<td>
<tt class="xref">plone</tt></td><td>
<em></em></td></tr><tr class="cg-1">
<td></td>
<td>&nbsp;&nbsp;&nbsp;
<a href="index.html#module-plone.app.discussion"><tt class="xref">plone.app.discussion</tt></a></td><td>
<em></em></td></tr>
</table>
</div>
</div>
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
<div id="searchbox" style="display: none">
<h3>Quick search</h3>
<form class="search" action="search.html" method="get">
<input type="text" name="q" size="18" />
<input type="submit" value="Go" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<p class="searchtip" style="font-size: 90%">
Enter search terms or a module, class or function name.
</p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="#" title="Global Module Index"
>modules</a> |</li>
<li><a href="index.html">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2010, Timo Stollenwerk.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 0.6.4.
</div>
</body>
</html>
+6
View File
@@ -0,0 +1,6 @@
# Sphinx inventory version 1
# Project: plone.app.discussion
# Version: 1.0b4
plone.app.discussion mod index.html
plone.app.discussion.conversation.Conversation class index.html
plone.app.discussion.conversation.Conversation.enabled method index.html
+97
View File
@@ -0,0 +1,97 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Search &mdash; plone.app.discussion v1.0b4 documentation</title>
<link rel="stylesheet" href="_static/default.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '#',
VERSION: '1.0b4',
COLLAPSE_MODINDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<script type="text/javascript" src="_static/searchtools.js"></script>
<link rel="top" title="plone.app.discussion v1.0b4 documentation" href="index.html" />
</head>
<body>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="modindex.html" title="Global Module Index"
accesskey="M">modules</a> |</li>
<li><a href="index.html">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
<h1 id="search-documentation">Search</h1>
<div id="fallback" class="admonition warning">
<script type="text/javascript">$('#fallback').hide();</script>
<p>
Please activate JavaScript to enable the search
functionality.
</p>
</div>
<p>
From here you can search these documents. Enter your search
words into the box below and click "search". Note that the search
function will automatically search for all of the words. Pages
containing fewer words won't appear in the result list.
</p>
<form action="" method="get">
<input type="text" name="q" value="" />
<input type="submit" value="search" />
<span id="search-progress" style="padding-left: 10px"></span>
</form>
<div id="search-results">
</div>
</div>
</div>
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="modindex.html" title="Global Module Index"
>modules</a> |</li>
<li><a href="index.html">plone.app.discussion v1.0b4 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2010, Timo Stollenwerk.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 0.6.4.
</div>
<script type="text/javascript" src="searchindex.js"></script>
</body>
</html>
File diff suppressed because one or more lines are too long