| 1 |
<!-- $Id: relatedcontent.help,v 1.1.2.1 2008/01/09 21:41:48 tbarregren Exp $ -->
|
| 2 |
<!-- Copyright (C) 2007-2008 Thomas Barregren [http://drupal.org/user/16678] -->
|
| 3 |
<p><a href="http://drupal.org/project/relatedcontent">RelatedContent</a> adds to each node a tab from which nodes to be associated with it, and optionally displayed along with it, can easily be selected by privileged users. To provide for different needs, RelatedContent uses the <a href="http://drupal.org/project/views">Views module</a> to provide the list of nodes to select from.</p>
|
| 4 |
<!--break-->
|
| 5 |
<p><strong>If you are in hurry, feel free to jump directly to the instructions on how to <a href="#installation">install</a>, <a href="#configuration">configure</a> and <a href="#usage">use</a> the module.</strong></p>
|
| 6 |
<h2 id="content">Table of contents</h2>
|
| 7 |
<ul>
|
| 8 |
<li><a href="#background">Background</a></li>
|
| 9 |
<li><a href="#rationale">Rationale</a></li>
|
| 10 |
<li><a href="#requirements">Requirements</a></li>
|
| 11 |
<li><a href="#installation">Installation</a></li>
|
| 12 |
<li><a href="#configuration">Configuration</a></li>
|
| 13 |
<li><a href="#usage">Usage</a></li>
|
| 14 |
<li><a href="#theming">Theming</a></li>
|
| 15 |
<li><a href="#api">Application Programming Interface (API)</a></li>
|
| 16 |
<li><a href="#plans">Plans for the future</a></li>
|
| 17 |
<li><a href="#author">Author</a></li>
|
| 18 |
<li><a href="#sponsors">Sponsors</a></li>
|
| 19 |
<li><a href="#license">License</a></li>
|
| 20 |
</ul>
|
| 21 |
<h2 id="background">Background</h2>
|
| 22 |
<p>It is common for websites to present collections of related content, such as teasers of promoted content, sidebars with facts and news issues. There are many great solutions for doing this in Drupal: taxonomy, views and queues, to mention a few. In spite of these possibilities, there is still need for an alternative way of compiling nodes to be shown.</p>
|
| 23 |
<p>In this section, a quick review of some possibilities are given, and the need for an alternative is explained in the context of the actual use case that motivated the development of this module in the first place.</p>
|
| 24 |
<h3>Alternatives</h3>
|
| 25 |
<p>Special pages with teasers are provided out of the box for nodes that are categorized by the core <a href="http://drupal.org/handbook/modules/taxonomy">Taxonomy module</a>. If your needs are more elaborative, you can compile a such page (or block) yourself with the wonderful <a href="http://drupal.org/project/views">Views module</a>. In both cases, you cannot directly select which teasers to show. Instead you are reduced to setup conditions for the viewed teasers to meet.</p>
|
| 26 |
<p>To directly select teasers to show, you can resort to the <a href="http://drupal.org/project/nodequeue">Node Queue module</a>. With the Node Queue module, you can create a "queue", which is a named set of references to nodes. You add references by visiting the nodes one by one or <em>en masse</em> by using the <a href="http://drupal.org/project/nodequeue_builder">Node Queue Builder module</a>. Once created, you can embed a PHP snippet into a node or a block to view teasers of the nodes referenced by a particular queue.</p>
|
| 27 |
<p>Although powerful, the Views and Node Queues are not always suitable. To understand why, consider the cause for writing the RelatedContent module in the first place:</p>
|
| 28 |
<h3>Use case</h3>
|
| 29 |
<p>The <a href="http://drupal.org/project/simplenews">Simplenews module</a> can be used to provide newsletters for visitors to subscribe to. The module provides a particular content type called "Newsletter issue". When editing a newsletter issue, the editor has a single text-area for the content. It is suitable for self-contained newsletters. However, it cannot easily be used to accomplish newsletters made up of an introductory text followed by teasers to articles already published on the website. <a href="http://drupal.org/project/relatedcontent">RelatedContent</a> was originally developed to extend Simplenews (and other content types) with this possibility.</p>
|
| 30 |
<p>So why are Views and Node Queues less appropriate for this particular purpose? There are at least two reasons: (i) it would be cumbersome, and (ii) the content would not be fixed.</p>
|
| 31 |
<p>Let us take a closer look on the Views module to see why it is so. This module can be configured to provide a single page (or rather a single URL) with teasers or full bodies of nodes that pass through its filter. A naive approach would be to create a view for each newsletter. But that is of course not a tenable solution in the long run.</p>
|
| 32 |
<p>A more sophisticated solution would be to build a single view that uses <a href="http://drupal.org/node/54455">arguments</a> to control which nodes are passed through, and <a href="http://drupal.org/node/48816">embed PHP-code</a> to output its content within the newsletters. The programming necessary can be reduced to just a function call by using the <a href="http://drupal.org/project/component">Component module</a>, or completely avoided by using the <a href="http://drupal.org/project/viewfield">Viewfield module</a> or the <a href="http://drupal.org/project/insert_view">Insert view module</a>. But nevertheless the argument handling makes this a very cumbersome solution.</p>
|
| 33 |
<p>Another problem with the Views solution is that there will be a coupling between the content embedded into the newsletter and the view itself. Depending on the filter criteria, the view might return different sets of nodes over the time. That is of course not acceptable for a newsletter. It should be possible to craft the filter and argument handling such that the returned nodes are consistent over time, but that would be a very brittle solution.</p>
|
| 34 |
<p>Let us now take a closer look at the Node Queue module. This module <a href="http://www.logrus.com/nodequeue/readme.txt">requires PHP-code to be embedded</a>. Hence, it too is cumbersome to use for non-programmers. And worse, giving all editors permission to use the necessary <em>PHP code input format</em> would pose a severe security risk.</p>
|
| 35 |
<p>The very idea of Node Queue is to provide a coupling between the output and the queue itself. Thus, any changes in a queue is propagated to wherever the queue is used. For a newsletter to not change on the web after it has been published, the queue must not be altered. The implication is that it is necessary to setup a unique queue for each issue, which of course is not feasible in the long run.</p>
|
| 36 |
<p>These problems are overcome by using the RelatedContent module.</p>
|
| 37 |
<h2 id="rationale">Rationale</h2>
|
| 38 |
<p>The very idea of the RelatedContent module is to provide means to</p>
|
| 39 |
<ul>
|
| 40 |
<li>associating an individual node with other nodes, and</li>
|
| 41 |
<li>keeping that association independent of any changes in the source from which the nodes was selected in the first place.</li>
|
| 42 |
</ul>
|
| 43 |
<p>In this section, some of the technical considerations are examined in some detail. RelatedContent is very easy to use, so you do not have to read this section to make full use of the module. It is merely included for those who are interested in why somethings work as they do.</p>
|
| 44 |
<h3>Loading and viewing nodes</h3>
|
| 45 |
<p>For each node of a content type for which the module is enabled, RelatedContent keeps an ordered list of nodes with related content. When Drupal loads a such node, the RelatedContent module hooks into the loading and add the list to the node. That list can then be used by other modules or themes to accomplish things. The RelatedContent module itself use it, during rendering of the node, to inject the listed nodes' teasers or bodies at the beginning or the end of the node's body.</p>
|
| 46 |
<h3>Selecting related content</h3>
|
| 47 |
<p>The RelatedContent module provides a user interface for selecting nodes with related content. To provide for different needs, RelatedContent use a configurable view of the <a href="http://drupal.org/project/views">Views module</a> as its source of nodes to select from.</p>
|
| 48 |
<p>A user with permission to update a node, will see a <em>RelatedContent</em> tab next to the the <em>View</em> and <em>Edit</em> tabs. By clicking on it, two secondary tabs are unveiled. The first is named <em>Overview</em> and the second is named after the view that provides the nodes to select from. By default, the overview is selected.</p>
|
| 49 |
<p>Both tabs display a table of nodes. In the overview tab, the table contains all nodes that already are on the list. That includes both nodes that are available from the view and those which are not available any more. In the view tab, the table contains all nodes that can be selected from. That includes both nodes that are selected and those which are not.</p>
|
| 50 |
<p>Each row in the table corresponds to a node. The node is presented with its title, content type, creation time and author. In front of each title is a checkbox. A node is related content if and only if it has a tick in the checkbox. To add nodes to the related content, tick them, and click on the <em>Update</em> button. To remove nodes from the related content, remove the tick, and click on the <em>Update</em> button.</p>
|
| 51 |
<h3>Sorting and grouping</h3>
|
| 52 |
<p>One of the benefits of using a view as the source of nodes to select from, is that views can be configured sort nodes to meet your needs. The RelatedContent module therefore keeps the nodes in the same order on its lists as they had in the view last time the <em>Update</em> button in the view's tab was clicked.</p>
|
| 53 |
<p>When a node with a list of related content is to be viewed, the nodes on the list are optionally partitioned into groups before themed. The nodes can be grouped by its content type or author or not at all. The nodes within a group keep their order among themselves.</p>
|
| 54 |
<p>The order of the groups are undefined. You can however sort them yourself by overriding the themeable function <code>theme_relatedcontent()</code>.</p>
|
| 55 |
<h3>Decoupling</h3>
|
| 56 |
<p>The view is only used to provide a filtered and sorted collection of nodes from which the user can pick nodes. It is not used to further filter or sort the selected nodes. The selected nodes are decoupled from the source from which they were selected. As a consequence, the nodes' order or state of being selected remain unaffected of changes in the view from which they were picked. It is important to remember this.</p>
|
| 57 |
<h3>Caveat</h3>
|
| 58 |
<p>A node in the table on the view tab, might not be available later, due to the view's filter conditions or changes in the view definition, or even because the view has been replaced or turned off. If a such node has previously been included on the list of nodes with related content, it will turn up in the table on the overview tab, but not in the table on the view tab. That might first look a little strange, but is in fact just how we want it to work. A change in the view should not have an effect on which nodes are displayed along with the node once it has been stored. But these orphans cause some trouble is some very special situations. This is best exemplified:</p>
|
| 59 |
<p>Assume your source of nodes is a view that lists nodes created the last ten days in alphabetic order. Suppose you select some nodes from this view. They will be displayed in the same order as they had in the view when they were picked, that is alphabetic. Ten days later you select some more nodes from the same view. Since the nodes selected last time has lapse from the view, they will not be included when the newly selected nodes are ordered by the view. As a consequence, the former and the latter nodes are sorted alphabetic within respective group, but not as a whole. Thus, the related content is not more alphabetic ordered.</p>
|
| 60 |
<p>This is an effect of the desirable decoupling of the nodes from the view. Since this happens only if the previously selected nodes are not any more in the view, it is easily avoided by using a view that includes all nodes for as long as it is plausible to expect more nodes to be added. In practice that should not be a problem. But if you find yourself constantly updating the related content of a node, and has trouble with orphans, you should probably consider the Node Queue module instead.</p>
|
| 61 |
<h2 id="requirements">Requirements</h2>
|
| 62 |
<p>To install RelatedContent you need:</p>
|
| 63 |
<ul>
|
| 64 |
<li><a href="http://drupal.org/project/drupal">Drupal 5.x</a></li>
|
| 65 |
<li><a href="http://drupal.org/project/views">Views module</a></li>
|
| 66 |
</ul>
|
| 67 |
<h2 id="installation">Installation</h2>
|
| 68 |
<p>Install RelatedContent as follows:</p>
|
| 69 |
<ol>
|
| 70 |
<li>Download, install and configure the <a href="http://drupal.org/project/views">Views module</a>, following the instructions for that module.</li>
|
| 71 |
<li>Download the latest stable version of RelatedContent from its <a href="http://drupal.org/project/relatedcontent">project page</a>.</li>
|
| 72 |
<li>Unpack the downloaded file into <code>sites/all/modules</code> or the modules directory of your site.</li>
|
| 73 |
<li>Go to <a href="/admin/build/modules">Administer » Site building » Modules</a> and enable the module.</li>
|
| 74 |
</ol>
|
| 75 |
<h2 id="configuration">Configuration</h2>
|
| 76 |
<p>RelatedContent is enabled and configured for each content type individually as described below:</p>
|
| 77 |
<ol>
|
| 78 |
<li>If not already existing, go to <a href="/admin/build/views">Administer » Site building » Views</a> and add at least one view that can be used to provide RelatedContent with nodes to select from.</li>
|
| 79 |
<li>Go to <a href="/admin/content/types">Administer » Content management » Content types</a>, and click on the content type for which you want to enable RelatedContent.</li>
|
| 80 |
<li>Click on the <em>RelatedContent settings</em> link to expand the settings.</li>
|
| 81 |
<li>Tick the checkbox named <em>Enable</em> to allow related content of nodes for this particular content type.</li>
|
| 82 |
<li>In the pull-down menu named <em>RelatedContent view</em>, select the view that will provide the nodes to select from. If no view is selected, the table with nodes to select from is disabled. Any previously selected nodes will remain selected.</li>
|
| 83 |
<li>In the pull-down menu named <em>Length of node table</em>, select the number of nodes to shown on each page of the table with nodes to select from.</li>
|
| 84 |
<li>Choose among the radio buttons named <em>Teasers</em>, whether related content should be shown in teasers.</li>
|
| 85 |
<li>Choose among the radio buttons named <em>Placing</em>, whether the related content should not be outputted, or be outputted at the beginning or at the end of the body.</li>
|
| 86 |
<li>Choose among the radio buttons named <em>Grouping</em>, whether the related content should not be grouped or be grouped by content type or author.</li>
|
| 87 |
<li>Choose among the radio buttons named <em>Format</em>, whether the related content should be viewed as teasers or bodies of the selected nodes.</li>
|
| 88 |
</ol>
|
| 89 |
<h2 id="usage">Usage</h2>
|
| 90 |
<p>When viewing a node of a content type with RelatedContent enabled, select nodes to be shown as follows:</p>
|
| 91 |
<ol>
|
| 92 |
<li>Next to the usual <em>View</em> and <em>Edit</em> tabs, there is a <em>RelatedContent</em> tab. Click on it. You can also go directly to <code>node/<nid>/relatedcontent</code> where <code><nid></code> is the identity number of the node.</li>
|
| 93 |
<li>Underneath the <em>RelatedContent</em> tab, there is two subtabs. The first subtab is called <em>Overview</em> and is chosen by default. When the first subtab is chosen, a table of all selected nodes is presented. The other subtab has the same name as the view that provides nodes available to select from. When the other subtab is chosen, a paged table of all nodes provided by the named view is presented. If there are more nodes than the table is configured to view, there is a <em>Next</em>-button on all pages except that last, and a <em>Previous</em>-button on all pages except the first. Use these buttons to navigate through the table's pages.</li>
|
| 94 |
<li>In the tables of nodes, each node is presented on a row with its title, type, creation time and author. Leftmost on the row, there is also a checkbox. The related content will be those nodes that are ticked when the <em>Update</em> button is pressed.</li>
|
| 95 |
</ol>
|
| 96 |
<h2 id="theming">Theming</h2>
|
| 97 |
<p>The default theming of nodes with related content is appended at the beginning or end, depending on the settings, with a sequence of following HTML-blocks:</p>
|
| 98 |
<pre>
|
| 99 |
<div class="relatedcontent-nodes $group">
|
| 100 |
<h3>$group</h3>
|
| 101 |
$contents
|
| 102 |
</div>
|
| 103 |
</pre>
|
| 104 |
<p>where <code>$group</code> is, depending on the settings, the the name of the content type or the author or <code>'all'</code>; and <code>$contents</code> is, depending on the settings, the teasers or bodies of the related content after theming with <a href="http://api.drupal.org/api/function/node_view/5">node_view()</a>. The <code><h3></code>-line is excluded if grouping is disabled.</p>
|
| 105 |
<h3>Template file</h3>
|
| 106 |
<p>The easiest way to change this theming, is to copy <code>relatedcontent.tpl.php</code> from the folder with the RelatedContent module, e.g. <code>site/all/modules/relatedcontent</code>, to the folder containing the theme's <code>page.tpl.php</code> and edit is as needed. This works for all themes based on the built-in <a href="http://drupal.org/phptemplate">PHPTemplate theme engine</a>.</p>
|
| 107 |
<p>For advanced themers, and themes not based on the PHPTemplate theme engine, it is possible to override the themeable function <code>theme_relatedcontent()</code> discussed below.</p>
|
| 108 |
<h3>Themeable functions</h3>
|
| 109 |
<p>The RelatedContent module provides following themeable functions:</p>
|
| 110 |
<dl>
|
| 111 |
<dt><code>theme_relatedcontent($output, $grouped = null, $teaser = null, $page = null)</code></dt>
|
| 112 |
<dd>
|
| 113 |
<p>where:</p>
|
| 114 |
<ul>
|
| 115 |
<li><code>$output</code> is an associative array, whose keys are the names by which the output should be grouped, i.e. the name of content types or authors, depending on the settings, or <code>'all'</code> if grouping is disabled. Each output buffer is an ordinary array with already themed nodes which should be outputted in index order as they are.</li>
|
| 116 |
<li><code>$grouped</code> is the string <code>'type'</code> if the related content should be grouped by their content types, the string <code>'name'</code> if it should be grouped by their authors, and <code>false</code> if it should not be grouped at all.</li>
|
| 117 |
<li><code>$teaser</code> is <code>true</code> or <code>false</code> depending on whether the node, whose related content is themed, will be displayed as a teaser or in full, respectively.</li>
|
| 118 |
<li><code>$page</code> is <code>true</code> or <code>false</code> depending on whether the node, whose related content is themed, will be displayed as a page itself or embedded into another page, respectively.</li>
|
| 119 |
</ul>
|
| 120 |
<p>The default implementation returns the default theming described in the previous subsection.</p>
|
| 121 |
</dd>
|
| 122 |
<dt><code>theme_relatedcontent_form($form)</code></dt>
|
| 123 |
<dd>
|
| 124 |
<p>where:</p>
|
| 125 |
<ul>
|
| 126 |
<li><code>$form['intro']</code> is a introductory text.</li>
|
| 127 |
<li><code>$form['nodes'][$n]</code> is the form element for a checkbox.</li>
|
| 128 |
<li><code>$form['title'][$n]</code> is the form element for a title.</li>
|
| 129 |
<li><code>$form['name'][$n]</code> is the form element for a content type.</li>
|
| 130 |
<li><code>$form['created'][$n]</code> is the form element for a create time.</li>
|
| 131 |
<li><code>$form['username'][$n]</code> is the form element for an author.</li>
|
| 132 |
<li><code>$n</code> is the identity number (nid) of the node in question.</li>
|
| 133 |
</ul>
|
| 134 |
<p>The default implementation returns the themed table used to select nodes whose teasers are going to be shown.</p>
|
| 135 |
</dd>
|
| 136 |
</dl>
|
| 137 |
<h2 id="api">Application Programming Interface (API)</h2>
|
| 138 |
<p>RelatedContent provides a very simple API that can be used by themes and other modules, or within nodes or blocks with the PHP input filter, to get a node's related content and do something clever with it. The API consists of the following functions:</p>
|
| 139 |
<dl>
|
| 140 |
<dt><code>relatedcontent_get_nodes(&$node)</code></dt>
|
| 141 |
<dd>
|
| 142 |
<p>where:</p>
|
| 143 |
<ul>
|
| 144 |
<li><code>$node</code> is the node object, as loaded through <a href="http://api.drupal.org/api/function/node_load/5"><code>node_load()</code></a>, for which the related content is requested.</li>
|
| 145 |
</ul>
|
| 146 |
<p>Returns an array of id numbers (nid) of nodes that are related to <code>$node</code>. The array is sorted as described in this documentation.</p>
|
| 147 |
</dd>
|
| 148 |
<dt><code>relatedcontent_set_nodes(&$node, $nodes)</code></dt>
|
| 149 |
<dd>
|
| 150 |
<p>where:</p>
|
| 151 |
<ul>
|
| 152 |
<li><code>$node</code> is the node object, as loaded through <a href="http://api.drupal.org/api/function/node_load/5"><code>node_load()</code></a>, for which the related content is to be set.</li>
|
| 153 |
<li><code>$nodes</code> is an array of id numbers (nid) of nodes that are related to <code>$node</code>. The array should be sorted as described in this documentation.</li>
|
| 154 |
</ul>
|
| 155 |
<p>Sets the id numbers (nid) of nodes that are related to <code>$node</code>.</p>
|
| 156 |
</dd>
|
| 157 |
<dt><code>relatedcontent($node, $output_grouped = false, $content_function = '', $content_function_args = array())</code></dt>
|
| 158 |
<dd>
|
| 159 |
<p>where:</p>
|
| 160 |
<ul>
|
| 161 |
<li><code>$node</code> is either a node id number (nid) or a node object, as loaded by <a href="http://api.drupal.org/api/function/node_load/5"><code>node_load()</code></a>.</li>
|
| 162 |
<li><code>$output_grouped</code> is the name of the node field by which the content should be grouped, e.g. <code>'type'</code>, <code>'name'</code>, <code>'uid'</code> and <code>'status'</code>, or <code>false</code> if the output should not be grouped. If omitted or empty, grouping s not performed.</li>
|
| 163 |
<li><code>$content_function</code> is a callback function that transforms a node object, passed in as its first argument, to the desired representation, e.g. the teaser or body. If omitted or empty, the default transform is just returning the node object itself.</li>
|
| 164 |
<li><code>$content_function_args</code> is an optional array with values that are passed in as argument 2, 3, and so forth, when calling the callback function $content_function.</li>
|
| 165 |
</ul>
|
| 166 |
<p>Returns an array whose keys are the names by which the output should be grouped, i.e. names of content types or authors, or <code>'all'</code>, depending on <code>$output_grouped</code>, and whose values are arrays with the return values of calling <code>$content_function</code> for the nodes with related content.</p>
|
| 167 |
<p>The function can for instance be used to display the related content in a bock instead of within the node. Just add a block, enable the PHP input format, and paste following code snippet in the text area:</p>
|
| 168 |
<pre>
|
| 169 |
<?php
|
| 170 |
if (arg(0) == 'node' && is_numeric($nid = arg(1))) {
|
| 171 |
$output = relatedcontent($nid, false, 'node_view', array(true));
|
| 172 |
echo theme_relatedcontent($output);
|
| 173 |
}
|
| 174 |
?>
|
| 175 |
</pre>
|
| 176 |
<p>More examples can be find in the source comments of the function.</p>
|
| 177 |
</dd>
|
| 178 |
</dl>
|
| 179 |
<h2 id="plans">Plans for the future</h2>
|
| 180 |
<p>The RelatedContent module will be further developed. Following issues are known and will be dealt with in future versions of the module:</p>
|
| 181 |
<ul>
|
| 182 |
<li>Only one view of the <a href="http://drupal.org/project/views">Views module</a> can be used to select nodes from. This will change in a near future.</li>
|
| 183 |
<li>The order of the nodes cannot be altered manual. In a future it will be possible to manually changing the sort order by drag'n'drop the nodes in the overview table and/or changing an ordinal number presented next to the nodes in the overview table.</li>
|
| 184 |
<li>An idea for the future is to make it possible to show the related content in a block instead of within the node.</li>
|
| 185 |
<li>Another idea for the future is to provide an input filter, e.g. <code>[RELATED CONTENT GOES HERE]</code>, which can be used to output the related content within the body instead of only at the beginning or end as today.</li>
|
| 186 |
</ul>
|
| 187 |
<h2 id="author">Author</h2>
|
| 188 |
<p>RelatedContent was developed by <a href="http://drupal.org/user/16678">Thomas Barregren</a>. The author can be contacted for paid customizations of this module as well as Drupal consulting, installation, development, and customizations.</p>
|
| 189 |
<h2 id="sponsors">Sponsors</h2>
|
| 190 |
<p>The development of this module has been sponsored by</p>
|
| 191 |
<ul>
|
| 192 |
<li><a href="http://www.spoon.com.au/">Spoon Media</a>,</li>
|
| 193 |
<li><a href="http://www.gmcvo.org.uk/">GMCVO</a>, and</li>
|
| 194 |
<li><a href="http://www.webbredaktoren.se/">Webbredaktören</a>.</li>
|
| 195 |
</ul>
|
| 196 |
<h2 id="license">License</h2>
|
| 197 |
<p>RelatedContent 5.x-1.5. Copyright © 2007–!year <a href="http://drupal.org/user/16678">Thomas Barregren</a>.</p>
|
| 198 |
<p>RelatedContent is free software; you can redistribute it and/or modify it under the terms of the <a href="http://www.gnu.org/licenses/gpl.html#SEC1">GNU General Public License</a> as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</p>
|
| 199 |
<p>RelatedContent is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the <a href="http://www.gnu.org/licenses/gpl.html#SEC1">GNU General Public License</a> for more details.</p>
|
| 200 |
<p>You should have received a copy of the <a href="http://www.gnu.org/licenses/gpl.html#SEC1">GNU General Public License</a> along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</p>
|