[#262] Convert line endings to UNIX

This commit is contained in:
Eric Davis 2011-05-29 12:00:18 -07:00
parent 05a4c4616f
commit a85b6780d2
35 changed files with 9625 additions and 9625 deletions

View File

@ -1,6 +1,6 @@
<h2><%=h @status %></h2>
<p id="errorExplanation"><%=h @message %></p>
<p><a href="javascript:history.back()">Back</a></p>
<% html_title @status %>
<h2><%=h @status %></h2>
<p id="errorExplanation"><%=h @message %></p>
<p><a href="javascript:history.back()">Back</a></p>
<% html_title @status %>

View File

@ -1,112 +1,112 @@
<% content_for :styles do %>
body { font:80% Verdana,Tahoma,Arial,sans-serif; }
h1, h2, h3, h4 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; }
pre, code { font-size:120%; }
pre code { font-size:100%; }
pre {
margin: 1em 1em 1em 1.6em;
padding: 2px;
background-color: #fafafa;
border: 1px solid #dadada;
width:95%;
overflow-x: auto;
}
a.new { color: #b73535; }
.CodeRay .c { color:#666; }
.CodeRay .cl { color:#B06; font-weight:bold }
.CodeRay .dl { color:black }
.CodeRay .fu { color:#06B; font-weight:bold }
.CodeRay .il { background: #eee }
.CodeRay .il .idl { font-weight: bold; color: #888 }
.CodeRay .iv { color:#33B }
.CodeRay .r { color:#080; font-weight:bold }
.CodeRay .s { background-color:#fff0f0 }
.CodeRay .s .dl { color:#710 }
<% end %>
<% html_title "Wiki Formatting" %>
<h1><a name="1" class="wiki-page"></a>Wiki Formatting</h1>
<h2><a name="2" class="wiki-page"></a>Links</h2>
<h3><a name="3" class="wiki-page"></a>ChiliProject links</h3>
<p>ChiliProject allows hyperlinking between issues, changesets and wiki pages from anywhere wiki formatting is used.</p>
<ul>
<li>Link to an issue: <strong>#124</strong> (displays <del><a href="#" class="issue" title="bulk edit doesn't change the category or fixed version properties (Closed)">#124</a></del>, link is striked-through if the issue is closed)</li>
<li>Link to a changeset: <strong>r758</strong> (displays <a href="#" class="changeset" title="Search engine now only searches objects the user is allowed to view.">r758</a>)</li>
<li>Link to a changeset with a non-numeric hash: <strong>commit:c6f4d0fd</strong> (displays <a href="#" class="changeset">c6f4d0fd</a>).</li>
<li>Link to a changeset of another project: <strong>sandbox:r758</strong> (displays <a href="#" class="changeset" title="Search engine now only searches objects the user is allowed to view.">sandbox:r758</a>)</li>
<li>Link to a changeset with a non-numeric hash: <strong>sandbox:c6f4d0fd</strong> (displays <a href="#" class="changeset">sandbox:c6f4d0fd</a>).</li>
</ul>
<p>Wiki links:</p>
<ul>
<li><strong>[[Guide]]</strong> displays a link to the page named 'Guide': <a href="#" class="wiki-page">Guide</a></li>
<li><strong>[[Guide#further-reading]]</strong> takes you to the anchor "further-reading". Headings get automatically assigned anchors so that you can refer to them: <a href="#" class="wiki-page">Guide</a></li>
<li><strong>[[Guide|User manual]]</strong> displays a link to the same page but with a different text: <a href="#" class="wiki-page">User manual</a></li>
</ul>
<p>You can also link to pages of an other project wiki:</p>
<ul>
<li><strong>[[sandbox:some page]]</strong> displays a link to the page named 'Some page' of the Sandbox wiki</li>
<li><strong>[[sandbox:]]</strong> displays a link to the Sandbox wiki main page</li>
</ul>
<p>Wiki links are displayed in red if the page doesn't exist yet, eg: <a href="#" class="wiki-page new">Nonexistent page</a>.</p>
<% content_for :styles do %>
body { font:80% Verdana,Tahoma,Arial,sans-serif; }
h1, h2, h3, h4 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; }
pre, code { font-size:120%; }
pre code { font-size:100%; }
pre {
margin: 1em 1em 1em 1.6em;
padding: 2px;
background-color: #fafafa;
border: 1px solid #dadada;
width:95%;
overflow-x: auto;
}
a.new { color: #b73535; }
.CodeRay .c { color:#666; }
.CodeRay .cl { color:#B06; font-weight:bold }
.CodeRay .dl { color:black }
.CodeRay .fu { color:#06B; font-weight:bold }
.CodeRay .il { background: #eee }
.CodeRay .il .idl { font-weight: bold; color: #888 }
.CodeRay .iv { color:#33B }
.CodeRay .r { color:#080; font-weight:bold }
.CodeRay .s { background-color:#fff0f0 }
.CodeRay .s .dl { color:#710 }
<% end %>
<% html_title "Wiki Formatting" %>
<h1><a name="1" class="wiki-page"></a>Wiki Formatting</h1>
<h2><a name="2" class="wiki-page"></a>Links</h2>
<h3><a name="3" class="wiki-page"></a>ChiliProject links</h3>
<p>ChiliProject allows hyperlinking between issues, changesets and wiki pages from anywhere wiki formatting is used.</p>
<ul>
<li>Link to an issue: <strong>#124</strong> (displays <del><a href="#" class="issue" title="bulk edit doesn't change the category or fixed version properties (Closed)">#124</a></del>, link is striked-through if the issue is closed)</li>
<li>Link to a changeset: <strong>r758</strong> (displays <a href="#" class="changeset" title="Search engine now only searches objects the user is allowed to view.">r758</a>)</li>
<li>Link to a changeset with a non-numeric hash: <strong>commit:c6f4d0fd</strong> (displays <a href="#" class="changeset">c6f4d0fd</a>).</li>
<li>Link to a changeset of another project: <strong>sandbox:r758</strong> (displays <a href="#" class="changeset" title="Search engine now only searches objects the user is allowed to view.">sandbox:r758</a>)</li>
<li>Link to a changeset with a non-numeric hash: <strong>sandbox:c6f4d0fd</strong> (displays <a href="#" class="changeset">sandbox:c6f4d0fd</a>).</li>
</ul>
<p>Wiki links:</p>
<ul>
<li><strong>[[Guide]]</strong> displays a link to the page named 'Guide': <a href="#" class="wiki-page">Guide</a></li>
<li><strong>[[Guide#further-reading]]</strong> takes you to the anchor "further-reading". Headings get automatically assigned anchors so that you can refer to them: <a href="#" class="wiki-page">Guide</a></li>
<li><strong>[[Guide|User manual]]</strong> displays a link to the same page but with a different text: <a href="#" class="wiki-page">User manual</a></li>
</ul>
<p>You can also link to pages of an other project wiki:</p>
<ul>
<li><strong>[[sandbox:some page]]</strong> displays a link to the page named 'Some page' of the Sandbox wiki</li>
<li><strong>[[sandbox:]]</strong> displays a link to the Sandbox wiki main page</li>
</ul>
<p>Wiki links are displayed in red if the page doesn't exist yet, eg: <a href="#" class="wiki-page new">Nonexistent page</a>.</p>
<p>Links to other resources:</p>
<ul>
<li>Documents:
<ul>
<li><strong>document#17</strong> (link to document with id 17)</li>
<li><strong>document:Greetings</strong> (link to the document with title "Greetings")</li>
<li><strong>document:"Some document"</strong> (double quotes can be used when document title contains spaces)</li>
<ul>
<li>Documents:
<ul>
<li><strong>document#17</strong> (link to document with id 17)</li>
<li><strong>document:Greetings</strong> (link to the document with title "Greetings")</li>
<li><strong>document:"Some document"</strong> (double quotes can be used when document title contains spaces)</li>
<li><strong>sandbox:document:"Some document"</strong> (link to a document with title "Some document" in other project "sandbox")</li>
</ul></li>
</ul>
<ul>
<li>Versions:
<ul>
<li><strong>version#3</strong> (link to version with id 3)</li>
<li><strong>version:1.0.0</strong> (link to version named "1.0.0")</li>
<li><strong>version:"1.0 beta 2"</strong></li>
<li><strong>sandbox:version:1.0.0</strong> (link to version "1.0.0" in the project "sandbox")</li>
</ul></li>
</ul>
<ul>
<li>Attachments:
<ul>
<li><strong>attachment:file.zip</strong> (link to the attachment of the current object named file.zip)</li>
<li>For now, attachments of the current object can be referenced only (if you're on an issue, it's possible to reference attachments of this issue only)</li>
</ul></li>
</ul>
<ul>
</ul></li>
</ul>
<ul>
<li>Versions:
<ul>
<li><strong>version#3</strong> (link to version with id 3)</li>
<li><strong>version:1.0.0</strong> (link to version named "1.0.0")</li>
<li><strong>version:"1.0 beta 2"</strong></li>
<li><strong>sandbox:version:1.0.0</strong> (link to version "1.0.0" in the project "sandbox")</li>
</ul></li>
</ul>
<ul>
<li>Attachments:
<ul>
<li><strong>attachment:file.zip</strong> (link to the attachment of the current object named file.zip)</li>
<li>For now, attachments of the current object can be referenced only (if you're on an issue, it's possible to reference attachments of this issue only)</li>
</ul></li>
</ul>
<ul>
<li>Repository files:
<ul>
<ul>
<li><strong>source:some/file</strong> (link to the file located at /some/file in the project's repository)</li>
<li><strong>source:some/file@52</strong> (link to the file's revision 52)</li>
<li><strong>source:some/file#L120</strong> (link to line 120 of the file)</li>
<li><strong>source:some/file@52#L120</strong> (link to line 120 of the file's revision 52)</li>
<li><strong>source:"some file@52#L120"</strong> (use double quotes when the URL contains spaces</li>
<li><strong>export:some/file</strong> (force the download of the file)</li>
<li><strong>sandbox:source:some/file</strong> (link to the file located at /some/file in the repository of the project "sandbox")</li>
<li><strong>export:some/file</strong> (force the download of the file)</li>
<li><strong>sandbox:source:some/file</strong> (link to the file located at /some/file in the repository of the project "sandbox")</li>
<li><strong>sandbox:export:some/file</strong> (force the download of the file)</li>
</ul></li>
</ul>
<ul>
</ul>
<ul>
<li>Forum messages:
<ul>
<li><strong>message#1218</strong> (link to message with id 1218)</li>
@ -124,139 +124,139 @@
<p>Escaping:</p>
<ul>
<li>You can prevent ChiliProject links from being parsed by preceding them with an exclamation mark: !</li>
</ul>
<h3><a name="4" class="wiki-page"></a>External links</h3>
<p>HTTP URLs and email addresses are automatically turned into clickable links:</p>
<pre>
https://www.chiliproject.org, someone@foo.bar
</pre>
<p>displays: <a class="external" href="https://www.chiliproject.org">https://www.chiliproject.org</a>, <a href="mailto:someone@foo.bar" class="email">someone@foo.bar</a></p>
<p>If you want to display a specific text instead of the URL, you can use the standard textile syntax:</p>
<pre>
"ChiliProject web site":https://www.chiliproject.org
</pre>
<p>displays: <a href="https://www.chiliproject.org" class="external">ChiliProject web site</a></p>
<h2><a name="5" class="wiki-page"></a>Text formatting</h2>
<p>For things such as headlines, bold, tables, lists, ChiliProject supports Textile syntax. See <a class="external" href="http://www.textism.com/tools/textile/">http://www.textism.com/tools/textile/</a> for information on using any of these features. A few samples are included below, but the engine is capable of much more of that.</p>
<h3><a name="6" class="wiki-page"></a>Font style</h3>
<pre>
* *bold*
* _italic_
* _*bold italic*_
* +underline+
* -strike-through-
</pre>
<p>Display:</p>
<ul>
<li><strong>bold</strong></li>
<li><em>italic</em></li>
<li><em>*bold italic*</em></li>
<li><ins>underline</ins></li>
<li><del>strike-through</del></li>
</ul>
<h3><a name="7" class="wiki-page"></a>Inline images</h3>
<ul>
<li><strong>!image_url!</strong> displays an image located at image_url (textile syntax)</li>
<li><strong>!>image_url!</strong> right floating image</li>
<li>If you have an image attached to your wiki page, it can be displayed inline using its filename: <strong>!attached_image.png!</strong></li>
</ul>
<h3><a name="8" class="wiki-page"></a>Headings</h3>
<pre>
h1. Heading
h2. Subheading
h3. Subsubheading
</pre>
<p>ChiliProject assigns an anchor to each of those headings thus you can link to them with "#Heading", "#Subheading" and so forth.</p>
<h3><a name="9" class="wiki-page"></a>Paragraphs</h3>
<pre>
p>. right aligned
p=. centered
</pre>
<p style="text-align:center;">This is a centered paragraph.</p>
<h3><a name="10" class="wiki-page"></a>Blockquotes</h3>
<p>Start the paragraph with <strong>bq.</strong></p>
<pre>
bq. Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.
To go live, all you need to add is a database and a web server.
</pre>
<p>Display:</p>
<blockquote>
<p>Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.<br />To go live, all you need to add is a database and a web server.</p>
</blockquote>
<h3><a name="11" class="wiki-page"></a>Table of content</h3>
<pre>
{{toc}} => left aligned toc
{{>toc}} => right aligned toc
</pre>
<h2><a name="12" class="wiki-page"></a>Macros</h2>
<p>ChiliProject has the following builtin macros:</p>
<p><dl><dt><code>hello_world</code></dt><dd><p>Sample macro.</p></dd><dt><code>include</code></dt><dd><p>Include a wiki page. Example:</p>
<pre><code>{{include(Foo)}}</code></pre></dd><dt><code>macro_list</code></dt><dd><p>Displays a list of all available macros, including description if available.</p></dd></dl></p>
<h2><a name="13" class="wiki-page"></a>Code highlighting</h2>
<ul>
<li>You can prevent ChiliProject links from being parsed by preceding them with an exclamation mark: !</li>
</ul>
<h3><a name="4" class="wiki-page"></a>External links</h3>
<p>HTTP URLs and email addresses are automatically turned into clickable links:</p>
<pre>
https://www.chiliproject.org, someone@foo.bar
</pre>
<p>displays: <a class="external" href="https://www.chiliproject.org">https://www.chiliproject.org</a>, <a href="mailto:someone@foo.bar" class="email">someone@foo.bar</a></p>
<p>If you want to display a specific text instead of the URL, you can use the standard textile syntax:</p>
<pre>
"ChiliProject web site":https://www.chiliproject.org
</pre>
<p>displays: <a href="https://www.chiliproject.org" class="external">ChiliProject web site</a></p>
<h2><a name="5" class="wiki-page"></a>Text formatting</h2>
<p>For things such as headlines, bold, tables, lists, ChiliProject supports Textile syntax. See <a class="external" href="http://www.textism.com/tools/textile/">http://www.textism.com/tools/textile/</a> for information on using any of these features. A few samples are included below, but the engine is capable of much more of that.</p>
<h3><a name="6" class="wiki-page"></a>Font style</h3>
<pre>
* *bold*
* _italic_
* _*bold italic*_
* +underline+
* -strike-through-
</pre>
<p>Display:</p>
<ul>
<li><strong>bold</strong></li>
<li><em>italic</em></li>
<li><em>*bold italic*</em></li>
<li><ins>underline</ins></li>
<li><del>strike-through</del></li>
</ul>
<h3><a name="7" class="wiki-page"></a>Inline images</h3>
<ul>
<li><strong>!image_url!</strong> displays an image located at image_url (textile syntax)</li>
<li><strong>!>image_url!</strong> right floating image</li>
<li>If you have an image attached to your wiki page, it can be displayed inline using its filename: <strong>!attached_image.png!</strong></li>
</ul>
<h3><a name="8" class="wiki-page"></a>Headings</h3>
<pre>
h1. Heading
h2. Subheading
h3. Subsubheading
</pre>
<p>ChiliProject assigns an anchor to each of those headings thus you can link to them with "#Heading", "#Subheading" and so forth.</p>
<h3><a name="9" class="wiki-page"></a>Paragraphs</h3>
<pre>
p>. right aligned
p=. centered
</pre>
<p style="text-align:center;">This is a centered paragraph.</p>
<h3><a name="10" class="wiki-page"></a>Blockquotes</h3>
<p>Start the paragraph with <strong>bq.</strong></p>
<pre>
bq. Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.
To go live, all you need to add is a database and a web server.
</pre>
<p>Display:</p>
<blockquote>
<p>Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.<br />To go live, all you need to add is a database and a web server.</p>
</blockquote>
<h3><a name="11" class="wiki-page"></a>Table of content</h3>
<pre>
{{toc}} => left aligned toc
{{>toc}} => right aligned toc
</pre>
<h2><a name="12" class="wiki-page"></a>Macros</h2>
<p>ChiliProject has the following builtin macros:</p>
<p><dl><dt><code>hello_world</code></dt><dd><p>Sample macro.</p></dd><dt><code>include</code></dt><dd><p>Include a wiki page. Example:</p>
<pre><code>{{include(Foo)}}</code></pre></dd><dt><code>macro_list</code></dt><dd><p>Displays a list of all available macros, including description if available.</p></dd></dl></p>
<h2><a name="13" class="wiki-page"></a>Code highlighting</h2>
<p>Code highlightment relies on <a href="http://coderay.rubychan.de/" class="external">CodeRay</a>, a fast syntax highlighting library written completely in Ruby. It currently supports c, cpp, css, delphi, groovy, html, java, javascript, json, php, python, rhtml, ruby, scheme, sql, xml and yaml languages.</p>
<p>You can highlight code in your wiki page using this syntax:</p>
<pre>
&lt;pre&gt;&lt;code class="ruby"&gt;
Place you code here.
&lt;/code&gt;&lt;/pre&gt;
</pre>
<p>Example:</p>
<pre><code class="ruby CodeRay"><span class="no"> 1</span> <span class="c"># The Greeter class</span>
<span class="no"> 2</span> <span class="r">class</span> <span class="cl">Greeter</span>
<span class="no"> 3</span> <span class="r">def</span> <span class="fu">initialize</span>(name)
<span class="no"> 4</span> <span class="iv">@name</span> = name.capitalize
<span class="no"> 5</span> <span class="r">end</span>
<span class="no"> 6</span>
<span class="no"> 7</span> <span class="r">def</span> <span class="fu">salute</span>
<span class="no"> 8</span> puts <span class="s"><span class="dl">"</span><span class="k">Hello </span><span class="il"><span class="idl">#{</span><span class="iv">@name</span><span class="idl">}</span></span><span class="k">!</span><span class="dl">"</span></span>
<span class="no"> 9</span> <span class="r">end</span>
<span class="no"><strong>10</strong></span> <span class="r">end</span>
</code>
</pre>
<p>You can highlight code in your wiki page using this syntax:</p>
<pre>
&lt;pre&gt;&lt;code class="ruby"&gt;
Place you code here.
&lt;/code&gt;&lt;/pre&gt;
</pre>
<p>Example:</p>
<pre><code class="ruby CodeRay"><span class="no"> 1</span> <span class="c"># The Greeter class</span>
<span class="no"> 2</span> <span class="r">class</span> <span class="cl">Greeter</span>
<span class="no"> 3</span> <span class="r">def</span> <span class="fu">initialize</span>(name)
<span class="no"> 4</span> <span class="iv">@name</span> = name.capitalize
<span class="no"> 5</span> <span class="r">end</span>
<span class="no"> 6</span>
<span class="no"> 7</span> <span class="r">def</span> <span class="fu">salute</span>
<span class="no"> 8</span> puts <span class="s"><span class="dl">"</span><span class="k">Hello </span><span class="il"><span class="idl">#{</span><span class="iv">@name</span><span class="idl">}</span></span><span class="k">!</span><span class="dl">"</span></span>
<span class="no"> 9</span> <span class="r">end</span>
<span class="no"><strong>10</strong></span> <span class="r">end</span>
</code>
</pre>

View File

@ -1,42 +1,42 @@
<% form_tag(project_project_enumerations_path(@project), :method => :put, :class => "tabular") do %>
<table class="list">
<thead><tr>
<th><%= l(:field_name) %></th>
<th><%= l(:enumeration_system_activity) %></th>
<% TimeEntryActivity.new.available_custom_fields.each do |value| %>
<th><%= h value.name %></th>
<% end %>
<th style="width:15%;"><%= l(:field_active) %></th>
</tr></thead>
<% @project.activities(true).each do |enumeration| %>
<% fields_for "enumerations[#{enumeration.id}]", enumeration do |ff| %>
<tr class="<%= cycle('odd', 'even') %>">
<td>
<%= ff.hidden_field :parent_id, :value => enumeration.id unless enumeration.project %>
<%= h(enumeration) %>
</td>
<td align="center" style="width:15%;"><%= checked_image !enumeration.project %></td>
<% enumeration.custom_field_values.each do |value| %>
<td align="center">
<%= custom_field_tag "enumerations[#{enumeration.id}]", value %>
</td>
<% end %>
<td align="center" style="width:15%;">
<%= ff.check_box :active %>
</td>
</tr>
<% end %>
<% end %>
</table>
<div class="contextual">
<%= link_to(l(:button_reset), project_project_enumerations_path(@project),
:method => :delete,
:confirm => l(:text_are_you_sure),
:class => 'icon icon-del') %>
</div>
<%= submit_tag l(:button_save) %>
<% end %>
<% form_tag(project_project_enumerations_path(@project), :method => :put, :class => "tabular") do %>
<table class="list">
<thead><tr>
<th><%= l(:field_name) %></th>
<th><%= l(:enumeration_system_activity) %></th>
<% TimeEntryActivity.new.available_custom_fields.each do |value| %>
<th><%= h value.name %></th>
<% end %>
<th style="width:15%;"><%= l(:field_active) %></th>
</tr></thead>
<% @project.activities(true).each do |enumeration| %>
<% fields_for "enumerations[#{enumeration.id}]", enumeration do |ff| %>
<tr class="<%= cycle('odd', 'even') %>">
<td>
<%= ff.hidden_field :parent_id, :value => enumeration.id unless enumeration.project %>
<%= h(enumeration) %>
</td>
<td align="center" style="width:15%;"><%= checked_image !enumeration.project %></td>
<% enumeration.custom_field_values.each do |value| %>
<td align="center">
<%= custom_field_tag "enumerations[#{enumeration.id}]", value %>
</td>
<% end %>
<td align="center" style="width:15%;">
<%= ff.check_box :active %>
</td>
</tr>
<% end %>
<% end %>
</table>
<div class="contextual">
<%= link_to(l(:button_reset), project_project_enumerations_path(@project),
:method => :delete,
:confirm => l(:text_are_you_sure),
:class => 'icon icon-del') %>
</div>
<%= submit_tag l(:button_save) %>
<% end %>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -21,19 +21,19 @@ class Setup < ActiveRecord::Migration
# model removed
class Permission < ActiveRecord::Base; end
def self.up
create_table "attachments", :force => true do |t|
t.column "container_id", :integer, :default => 0, :null => false
t.column "container_type", :string, :limit => 30, :default => "", :null => false
t.column "filename", :string, :default => "", :null => false
t.column "disk_filename", :string, :default => "", :null => false
t.column "filesize", :integer, :default => 0, :null => false
t.column "content_type", :string, :limit => 60, :default => ""
t.column "digest", :string, :limit => 40, :default => "", :null => false
t.column "downloads", :integer, :default => 0, :null => false
t.column "author_id", :integer, :default => 0, :null => false
t.column "created_on", :timestamp
end
def self.up
create_table "attachments", :force => true do |t|
t.column "container_id", :integer, :default => 0, :null => false
t.column "container_type", :string, :limit => 30, :default => "", :null => false
t.column "filename", :string, :default => "", :null => false
t.column "disk_filename", :string, :default => "", :null => false
t.column "filesize", :integer, :default => 0, :null => false
t.column "content_type", :string, :limit => 60, :default => ""
t.column "digest", :string, :limit => 40, :default => "", :null => false
t.column "downloads", :integer, :default => 0, :null => false
t.column "author_id", :integer, :default => 0, :null => false
t.column "created_on", :timestamp
end
create_table "auth_sources", :force => true do |t|
t.column "type", :string, :limit => 30, :default => "", :null => false
@ -49,143 +49,143 @@ class Setup < ActiveRecord::Migration
t.column "attr_mail", :string, :limit => 30
t.column "onthefly_register", :boolean, :default => false, :null => false
end
create_table "custom_fields", :force => true do |t|
t.column "type", :string, :limit => 30, :default => "", :null => false
t.column "name", :string, :limit => 30, :default => "", :null => false
t.column "field_format", :string, :limit => 30, :default => "", :null => false
t.column "possible_values", :text
t.column "regexp", :string, :default => ""
t.column "min_length", :integer, :default => 0, :null => false
t.column "max_length", :integer, :default => 0, :null => false
t.column "type", :string, :limit => 30, :default => "", :null => false
t.column "name", :string, :limit => 30, :default => "", :null => false
t.column "field_format", :string, :limit => 30, :default => "", :null => false
t.column "possible_values", :text
t.column "regexp", :string, :default => ""
t.column "min_length", :integer, :default => 0, :null => false
t.column "max_length", :integer, :default => 0, :null => false
t.column "is_required", :boolean, :default => false, :null => false
t.column "is_for_all", :boolean, :default => false, :null => false
end
create_table "custom_fields_projects", :id => false, :force => true do |t|
t.column "custom_field_id", :integer, :default => 0, :null => false
t.column "project_id", :integer, :default => 0, :null => false
end
end
create_table "custom_fields_projects", :id => false, :force => true do |t|
t.column "custom_field_id", :integer, :default => 0, :null => false
t.column "project_id", :integer, :default => 0, :null => false
end
create_table "custom_fields_trackers", :id => false, :force => true do |t|
t.column "custom_field_id", :integer, :default => 0, :null => false
t.column "tracker_id", :integer, :default => 0, :null => false
end
create_table "custom_values", :force => true do |t|
create_table "custom_values", :force => true do |t|
t.column "customized_type", :string, :limit => 30, :default => "", :null => false
t.column "customized_id", :integer, :default => 0, :null => false
t.column "custom_field_id", :integer, :default => 0, :null => false
t.column "value", :text
end
create_table "documents", :force => true do |t|
t.column "project_id", :integer, :default => 0, :null => false
t.column "category_id", :integer, :default => 0, :null => false
t.column "title", :string, :limit => 60, :default => "", :null => false
t.column "description", :text
t.column "created_on", :timestamp
t.column "custom_field_id", :integer, :default => 0, :null => false
t.column "value", :text
end
add_index "documents", ["project_id"], :name => "documents_project_id"
create_table "enumerations", :force => true do |t|
t.column "opt", :string, :limit => 4, :default => "", :null => false
t.column "name", :string, :limit => 30, :default => "", :null => false
end
create_table "issue_categories", :force => true do |t|
t.column "project_id", :integer, :default => 0, :null => false
t.column "name", :string, :limit => 30, :default => "", :null => false
end
add_index "issue_categories", ["project_id"], :name => "issue_categories_project_id"
create_table "issue_histories", :force => true do |t|
t.column "issue_id", :integer, :default => 0, :null => false
t.column "status_id", :integer, :default => 0, :null => false
t.column "author_id", :integer, :default => 0, :null => false
t.column "notes", :text
t.column "created_on", :timestamp
end
add_index "issue_histories", ["issue_id"], :name => "issue_histories_issue_id"
create_table "issue_statuses", :force => true do |t|
t.column "name", :string, :limit => 30, :default => "", :null => false
t.column "is_closed", :boolean, :default => false, :null => false
t.column "is_default", :boolean, :default => false, :null => false
t.column "html_color", :string, :limit => 6, :default => "FFFFFF", :null => false
end
create_table "issues", :force => true do |t|
t.column "tracker_id", :integer, :default => 0, :null => false
t.column "project_id", :integer, :default => 0, :null => false
t.column "subject", :string, :default => "", :null => false
create_table "documents", :force => true do |t|
t.column "project_id", :integer, :default => 0, :null => false
t.column "category_id", :integer, :default => 0, :null => false
t.column "title", :string, :limit => 60, :default => "", :null => false
t.column "description", :text
t.column "due_date", :date
t.column "category_id", :integer
t.column "status_id", :integer, :default => 0, :null => false
t.column "assigned_to_id", :integer
t.column "priority_id", :integer, :default => 0, :null => false
t.column "fixed_version_id", :integer
t.column "author_id", :integer, :default => 0, :null => false
t.column "lock_version", :integer, :default => 0, :null => false
t.column "created_on", :timestamp
t.column "updated_on", :timestamp
end
add_index "issues", ["project_id"], :name => "issues_project_id"
create_table "members", :force => true do |t|
t.column "user_id", :integer, :default => 0, :null => false
t.column "project_id", :integer, :default => 0, :null => false
t.column "role_id", :integer, :default => 0, :null => false
t.column "created_on", :timestamp
end
create_table "news", :force => true do |t|
t.column "project_id", :integer
t.column "title", :string, :limit => 60, :default => "", :null => false
t.column "summary", :string, :limit => 255, :default => ""
t.column "description", :text
t.column "author_id", :integer, :default => 0, :null => false
t.column "created_on", :timestamp
t.column "created_on", :timestamp
end
add_index "news", ["project_id"], :name => "news_project_id"
create_table "permissions", :force => true do |t|
t.column "controller", :string, :limit => 30, :default => "", :null => false
t.column "action", :string, :limit => 30, :default => "", :null => false
t.column "description", :string, :limit => 60, :default => "", :null => false
t.column "is_public", :boolean, :default => false, :null => false
t.column "sort", :integer, :default => 0, :null => false
t.column "mail_option", :boolean, :default => false, :null => false
t.column "mail_enabled", :boolean, :default => false, :null => false
end
create_table "permissions_roles", :id => false, :force => true do |t|
t.column "permission_id", :integer, :default => 0, :null => false
t.column "role_id", :integer, :default => 0, :null => false
end
add_index "permissions_roles", ["role_id"], :name => "permissions_roles_role_id"
create_table "projects", :force => true do |t|
t.column "name", :string, :limit => 30, :default => "", :null => false
t.column "description", :string, :default => "", :null => false
t.column "homepage", :string, :limit => 60, :default => ""
add_index "documents", ["project_id"], :name => "documents_project_id"
create_table "enumerations", :force => true do |t|
t.column "opt", :string, :limit => 4, :default => "", :null => false
t.column "name", :string, :limit => 30, :default => "", :null => false
end
create_table "issue_categories", :force => true do |t|
t.column "project_id", :integer, :default => 0, :null => false
t.column "name", :string, :limit => 30, :default => "", :null => false
end
add_index "issue_categories", ["project_id"], :name => "issue_categories_project_id"
create_table "issue_histories", :force => true do |t|
t.column "issue_id", :integer, :default => 0, :null => false
t.column "status_id", :integer, :default => 0, :null => false
t.column "author_id", :integer, :default => 0, :null => false
t.column "notes", :text
t.column "created_on", :timestamp
end
add_index "issue_histories", ["issue_id"], :name => "issue_histories_issue_id"
create_table "issue_statuses", :force => true do |t|
t.column "name", :string, :limit => 30, :default => "", :null => false
t.column "is_closed", :boolean, :default => false, :null => false
t.column "is_default", :boolean, :default => false, :null => false
t.column "html_color", :string, :limit => 6, :default => "FFFFFF", :null => false
end
create_table "issues", :force => true do |t|
t.column "tracker_id", :integer, :default => 0, :null => false
t.column "project_id", :integer, :default => 0, :null => false
t.column "subject", :string, :default => "", :null => false
t.column "description", :text
t.column "due_date", :date
t.column "category_id", :integer
t.column "status_id", :integer, :default => 0, :null => false
t.column "assigned_to_id", :integer
t.column "priority_id", :integer, :default => 0, :null => false
t.column "fixed_version_id", :integer
t.column "author_id", :integer, :default => 0, :null => false
t.column "lock_version", :integer, :default => 0, :null => false
t.column "created_on", :timestamp
t.column "updated_on", :timestamp
end
add_index "issues", ["project_id"], :name => "issues_project_id"
create_table "members", :force => true do |t|
t.column "user_id", :integer, :default => 0, :null => false
t.column "project_id", :integer, :default => 0, :null => false
t.column "role_id", :integer, :default => 0, :null => false
t.column "created_on", :timestamp
end
create_table "news", :force => true do |t|
t.column "project_id", :integer
t.column "title", :string, :limit => 60, :default => "", :null => false
t.column "summary", :string, :limit => 255, :default => ""
t.column "description", :text
t.column "author_id", :integer, :default => 0, :null => false
t.column "created_on", :timestamp
end
add_index "news", ["project_id"], :name => "news_project_id"
create_table "permissions", :force => true do |t|
t.column "controller", :string, :limit => 30, :default => "", :null => false
t.column "action", :string, :limit => 30, :default => "", :null => false
t.column "description", :string, :limit => 60, :default => "", :null => false
t.column "is_public", :boolean, :default => false, :null => false
t.column "sort", :integer, :default => 0, :null => false
t.column "mail_option", :boolean, :default => false, :null => false
t.column "mail_enabled", :boolean, :default => false, :null => false
end
create_table "permissions_roles", :id => false, :force => true do |t|
t.column "permission_id", :integer, :default => 0, :null => false
t.column "role_id", :integer, :default => 0, :null => false
end
add_index "permissions_roles", ["role_id"], :name => "permissions_roles_role_id"
create_table "projects", :force => true do |t|
t.column "name", :string, :limit => 30, :default => "", :null => false
t.column "description", :string, :default => "", :null => false
t.column "homepage", :string, :limit => 60, :default => ""
t.column "is_public", :boolean, :default => true, :null => false
t.column "parent_id", :integer
t.column "projects_count", :integer, :default => 0
t.column "created_on", :timestamp
t.column "updated_on", :timestamp
end
create_table "roles", :force => true do |t|
t.column "name", :string, :limit => 30, :default => "", :null => false
end
t.column "projects_count", :integer, :default => 0
t.column "created_on", :timestamp
t.column "updated_on", :timestamp
end
create_table "roles", :force => true do |t|
t.column "name", :string, :limit => 30, :default => "", :null => false
end
create_table "tokens", :force => true do |t|
t.column "user_id", :integer, :default => 0, :null => false
@ -193,98 +193,98 @@ class Setup < ActiveRecord::Migration
t.column "value", :string, :limit => 40, :default => "", :null => false
t.column "created_on", :datetime, :null => false
end
create_table "trackers", :force => true do |t|
t.column "name", :string, :limit => 30, :default => "", :null => false
t.column "is_in_chlog", :boolean, :default => false, :null => false
end
create_table "users", :force => true do |t|
t.column "login", :string, :limit => 30, :default => "", :null => false
t.column "hashed_password", :string, :limit => 40, :default => "", :null => false
t.column "firstname", :string, :limit => 30, :default => "", :null => false
t.column "lastname", :string, :limit => 30, :default => "", :null => false
t.column "mail", :string, :limit => 60, :default => "", :null => false
t.column "mail_notification", :boolean, :default => true, :null => false
t.column "admin", :boolean, :default => false, :null => false
t.column "status", :integer, :default => 1, :null => false
t.column "last_login_on", :datetime
create_table "trackers", :force => true do |t|
t.column "name", :string, :limit => 30, :default => "", :null => false
t.column "is_in_chlog", :boolean, :default => false, :null => false
end
create_table "users", :force => true do |t|
t.column "login", :string, :limit => 30, :default => "", :null => false
t.column "hashed_password", :string, :limit => 40, :default => "", :null => false
t.column "firstname", :string, :limit => 30, :default => "", :null => false
t.column "lastname", :string, :limit => 30, :default => "", :null => false
t.column "mail", :string, :limit => 60, :default => "", :null => false
t.column "mail_notification", :boolean, :default => true, :null => false
t.column "admin", :boolean, :default => false, :null => false
t.column "status", :integer, :default => 1, :null => false
t.column "last_login_on", :datetime
t.column "language", :string, :limit => 2, :default => ""
t.column "auth_source_id", :integer
t.column "created_on", :timestamp
t.column "updated_on", :timestamp
end
create_table "versions", :force => true do |t|
t.column "project_id", :integer, :default => 0, :null => false
t.column "name", :string, :limit => 30, :default => "", :null => false
t.column "description", :string, :default => ""
t.column "effective_date", :date
t.column "created_on", :timestamp
t.column "updated_on", :timestamp
t.column "auth_source_id", :integer
t.column "created_on", :timestamp
t.column "updated_on", :timestamp
end
create_table "versions", :force => true do |t|
t.column "project_id", :integer, :default => 0, :null => false
t.column "name", :string, :limit => 30, :default => "", :null => false
t.column "description", :string, :default => ""
t.column "effective_date", :date
t.column "created_on", :timestamp
t.column "updated_on", :timestamp
end
add_index "versions", ["project_id"], :name => "versions_project_id"
create_table "workflows", :force => true do |t|
t.column "tracker_id", :integer, :default => 0, :null => false
t.column "old_status_id", :integer, :default => 0, :null => false
t.column "new_status_id", :integer, :default => 0, :null => false
t.column "role_id", :integer, :default => 0, :null => false
end
# project
Permission.create :controller => "projects", :action => "show", :description => "label_overview", :sort => 100, :is_public => true
Permission.create :controller => "projects", :action => "changelog", :description => "label_change_log", :sort => 105, :is_public => true
Permission.create :controller => "reports", :action => "issue_report", :description => "label_report_plural", :sort => 110, :is_public => true
Permission.create :controller => "projects", :action => "settings", :description => "label_settings", :sort => 150
Permission.create :controller => "projects", :action => "edit", :description => "button_edit", :sort => 151
# members
Permission.create :controller => "projects", :action => "list_members", :description => "button_list", :sort => 200, :is_public => true
Permission.create :controller => "projects", :action => "add_member", :description => "button_add", :sort => 220
Permission.create :controller => "members", :action => "edit", :description => "button_edit", :sort => 221
Permission.create :controller => "members", :action => "destroy", :description => "button_delete", :sort => 222
# versions
Permission.create :controller => "projects", :action => "add_version", :description => "button_add", :sort => 320
Permission.create :controller => "versions", :action => "edit", :description => "button_edit", :sort => 321
Permission.create :controller => "versions", :action => "destroy", :description => "button_delete", :sort => 322
# issue categories
Permission.create :controller => "projects", :action => "add_issue_category", :description => "button_add", :sort => 420
Permission.create :controller => "issue_categories", :action => "edit", :description => "button_edit", :sort => 421
Permission.create :controller => "issue_categories", :action => "destroy", :description => "button_delete", :sort => 422
# issues
Permission.create :controller => "projects", :action => "list_issues", :description => "button_list", :sort => 1000, :is_public => true
add_index "versions", ["project_id"], :name => "versions_project_id"
create_table "workflows", :force => true do |t|
t.column "tracker_id", :integer, :default => 0, :null => false
t.column "old_status_id", :integer, :default => 0, :null => false
t.column "new_status_id", :integer, :default => 0, :null => false
t.column "role_id", :integer, :default => 0, :null => false
end
# project
Permission.create :controller => "projects", :action => "show", :description => "label_overview", :sort => 100, :is_public => true
Permission.create :controller => "projects", :action => "changelog", :description => "label_change_log", :sort => 105, :is_public => true
Permission.create :controller => "reports", :action => "issue_report", :description => "label_report_plural", :sort => 110, :is_public => true
Permission.create :controller => "projects", :action => "settings", :description => "label_settings", :sort => 150
Permission.create :controller => "projects", :action => "edit", :description => "button_edit", :sort => 151
# members
Permission.create :controller => "projects", :action => "list_members", :description => "button_list", :sort => 200, :is_public => true
Permission.create :controller => "projects", :action => "add_member", :description => "button_add", :sort => 220
Permission.create :controller => "members", :action => "edit", :description => "button_edit", :sort => 221
Permission.create :controller => "members", :action => "destroy", :description => "button_delete", :sort => 222
# versions
Permission.create :controller => "projects", :action => "add_version", :description => "button_add", :sort => 320
Permission.create :controller => "versions", :action => "edit", :description => "button_edit", :sort => 321
Permission.create :controller => "versions", :action => "destroy", :description => "button_delete", :sort => 322
# issue categories
Permission.create :controller => "projects", :action => "add_issue_category", :description => "button_add", :sort => 420
Permission.create :controller => "issue_categories", :action => "edit", :description => "button_edit", :sort => 421
Permission.create :controller => "issue_categories", :action => "destroy", :description => "button_delete", :sort => 422
# issues
Permission.create :controller => "projects", :action => "list_issues", :description => "button_list", :sort => 1000, :is_public => true
Permission.create :controller => "projects", :action => "export_issues_csv", :description => "label_export_csv", :sort => 1001, :is_public => true
Permission.create :controller => "issues", :action => "show", :description => "button_view", :sort => 1005, :is_public => true
Permission.create :controller => "issues", :action => "download", :description => "button_download", :sort => 1010, :is_public => true
Permission.create :controller => "projects", :action => "add_issue", :description => "button_add", :sort => 1050, :mail_option => 1, :mail_enabled => 1
Permission.create :controller => "issues", :action => "edit", :description => "button_edit", :sort => 1055
Permission.create :controller => "issues", :action => "change_status", :description => "label_change_status", :sort => 1060, :mail_option => 1, :mail_enabled => 1
Permission.create :controller => "issues", :action => "destroy", :description => "button_delete", :sort => 1065
Permission.create :controller => "issues", :action => "add_attachment", :description => "label_attachment_new", :sort => 1070
Permission.create :controller => "issues", :action => "destroy_attachment", :description => "label_attachment_delete", :sort => 1075
# news
Permission.create :controller => "projects", :action => "list_news", :description => "button_list", :sort => 1100, :is_public => true
Permission.create :controller => "news", :action => "show", :description => "button_view", :sort => 1101, :is_public => true
Permission.create :controller => "projects", :action => "add_news", :description => "button_add", :sort => 1120
Permission.create :controller => "news", :action => "edit", :description => "button_edit", :sort => 1121
Permission.create :controller => "news", :action => "destroy", :description => "button_delete", :sort => 1122
Permission.create :controller => "issues", :action => "show", :description => "button_view", :sort => 1005, :is_public => true
Permission.create :controller => "issues", :action => "download", :description => "button_download", :sort => 1010, :is_public => true
Permission.create :controller => "projects", :action => "add_issue", :description => "button_add", :sort => 1050, :mail_option => 1, :mail_enabled => 1
Permission.create :controller => "issues", :action => "edit", :description => "button_edit", :sort => 1055
Permission.create :controller => "issues", :action => "change_status", :description => "label_change_status", :sort => 1060, :mail_option => 1, :mail_enabled => 1
Permission.create :controller => "issues", :action => "destroy", :description => "button_delete", :sort => 1065
Permission.create :controller => "issues", :action => "add_attachment", :description => "label_attachment_new", :sort => 1070
Permission.create :controller => "issues", :action => "destroy_attachment", :description => "label_attachment_delete", :sort => 1075
# news
Permission.create :controller => "projects", :action => "list_news", :description => "button_list", :sort => 1100, :is_public => true
Permission.create :controller => "news", :action => "show", :description => "button_view", :sort => 1101, :is_public => true
Permission.create :controller => "projects", :action => "add_news", :description => "button_add", :sort => 1120
Permission.create :controller => "news", :action => "edit", :description => "button_edit", :sort => 1121
Permission.create :controller => "news", :action => "destroy", :description => "button_delete", :sort => 1122
# documents
Permission.create :controller => "projects", :action => "list_documents", :description => "button_list", :sort => 1200, :is_public => true
Permission.create :controller => "documents", :action => "show", :description => "button_view", :sort => 1201, :is_public => true
Permission.create :controller => "documents", :action => "download", :description => "button_download", :sort => 1202, :is_public => true
Permission.create :controller => "projects", :action => "add_document", :description => "button_add", :sort => 1220
Permission.create :controller => "documents", :action => "edit", :description => "button_edit", :sort => 1221
Permission.create :controller => "documents", :action => "destroy", :description => "button_delete", :sort => 1222
Permission.create :controller => "documents", :action => "add_attachment", :description => "label_attachment_new", :sort => 1223
Permission.create :controller => "documents", :action => "destroy_attachment", :description => "label_attachment_delete", :sort => 1224
# files
Permission.create :controller => "projects", :action => "list_files", :description => "button_list", :sort => 1300, :is_public => true
Permission.create :controller => "versions", :action => "download", :description => "button_download", :sort => 1301, :is_public => true
Permission.create :controller => "projects", :action => "add_file", :description => "button_add", :sort => 1320
Permission.create :controller => "versions", :action => "destroy_file", :description => "button_delete", :sort => 1322
# create default administrator account
Permission.create :controller => "projects", :action => "list_documents", :description => "button_list", :sort => 1200, :is_public => true
Permission.create :controller => "documents", :action => "show", :description => "button_view", :sort => 1201, :is_public => true
Permission.create :controller => "documents", :action => "download", :description => "button_download", :sort => 1202, :is_public => true
Permission.create :controller => "projects", :action => "add_document", :description => "button_add", :sort => 1220
Permission.create :controller => "documents", :action => "edit", :description => "button_edit", :sort => 1221
Permission.create :controller => "documents", :action => "destroy", :description => "button_delete", :sort => 1222
Permission.create :controller => "documents", :action => "add_attachment", :description => "label_attachment_new", :sort => 1223
Permission.create :controller => "documents", :action => "destroy_attachment", :description => "label_attachment_delete", :sort => 1224
# files
Permission.create :controller => "projects", :action => "list_files", :description => "button_list", :sort => 1300, :is_public => true
Permission.create :controller => "versions", :action => "download", :description => "button_download", :sort => 1301, :is_public => true
Permission.create :controller => "projects", :action => "add_file", :description => "button_add", :sort => 1320
Permission.create :controller => "versions", :action => "destroy_file", :description => "button_delete", :sort => 1322
# create default administrator account
user = User.create :login => "admin",
:hashed_password => "d033e22ae348aeb5660fc2140aec35850c4da997",
:admin => true,
@ -296,29 +296,29 @@ class Setup < ActiveRecord::Migration
:status => 1
end
def self.down
def self.down
drop_table :attachments
drop_table :auth_sources
drop_table :custom_fields
drop_table :auth_sources
drop_table :custom_fields
drop_table :custom_fields_projects
drop_table :custom_fields_trackers
drop_table :custom_values
drop_table :documents
drop_table :enumerations
drop_table :issue_categories
drop_table :issue_histories
drop_table :issue_statuses
drop_table :issues
drop_table :members
drop_table :news
drop_table :permissions
drop_table :permissions_roles
drop_table :projects
drop_table :roles
drop_table :trackers
drop_table :custom_fields_trackers
drop_table :custom_values
drop_table :documents
drop_table :enumerations
drop_table :issue_categories
drop_table :issue_histories
drop_table :issue_statuses
drop_table :issues
drop_table :members
drop_table :news
drop_table :permissions
drop_table :permissions_roles
drop_table :projects
drop_table :roles
drop_table :trackers
drop_table :tokens
drop_table :users
drop_table :versions
drop_table :users
drop_table :versions
drop_table :workflows
end
end

View File

@ -1,148 +1,148 @@
require 'rexml/document'
require 'SVG/Graph/Graph'
require 'SVG/Graph/BarBase'
module SVG
module Graph
# === Create presentation quality SVG bar graphs easily
#
# = Synopsis
#
# require 'SVG/Graph/Bar'
#
# fields = %w(Jan Feb Mar);
# data_sales_02 = [12, 45, 21]
#
# graph = SVG::Graph::Bar.new(
# :height => 500,
# :width => 300,
# :fields => fields
# )
#
# graph.add_data(
# :data => data_sales_02,
# :title => 'Sales 2002'
# )
#
# print "Content-type: image/svg+xml\r\n\r\n"
# print graph.burn
#
# = Description
#
# This object aims to allow you to easily create high quality
# SVG[http://www.w3c.org/tr/svg bar graphs. You can either use the default
# style sheet or supply your own. Either way there are many options which
# can be configured to give you control over how the graph is generated -
# with or without a key, data elements at each point, title, subtitle etc.
#
# = Notes
#
# The default stylesheet handles upto 12 data sets, if you
# use more you must create your own stylesheet and add the
# additional settings for the extra data sets. You will know
# if you go over 12 data sets as they will have no style and
# be in black.
#
# = Examples
#
# * http://germane-software.com/repositories/public/SVG/test/test.rb
#
# = See also
#
# * SVG::Graph::Graph
# * SVG::Graph::BarHorizontal
# * SVG::Graph::Line
# * SVG::Graph::Pie
# * SVG::Graph::Plot
# * SVG::Graph::TimeSeries
class Bar < BarBase
include REXML
# See Graph::initialize and BarBase::set_defaults
def set_defaults
super
self.top_align = self.top_font = 1
end
protected
def get_x_labels
@config[:fields]
end
def get_y_labels
maxvalue = max_value
minvalue = min_value
range = maxvalue - minvalue
top_pad = range == 0 ? 10 : range / 20.0
scale_range = (maxvalue + top_pad) - minvalue
scale_division = scale_divisions || (scale_range / 10.0)
if scale_integers
scale_division = scale_division < 1 ? 1 : scale_division.round
end
rv = []
maxvalue = maxvalue%scale_division == 0 ?
maxvalue : maxvalue + scale_division
minvalue.step( maxvalue, scale_division ) {|v| rv << v}
return rv
end
def x_label_offset( width )
width / 2.0
end
def draw_data
minvalue = min_value
fieldwidth = field_width
unit_size = (@graph_height.to_f - font_size*2*top_font) /
(get_y_labels.max - get_y_labels.min)
bargap = bar_gap ? (fieldwidth < 10 ? fieldwidth / 2 : 10) : 0
bar_width = fieldwidth - bargap
bar_width /= @data.length if stack == :side
x_mod = (@graph_width-bargap)/2 - (stack==:side ? bar_width/2 : 0)
bottom = @graph_height
field_count = 0
@config[:fields].each_index { |i|
dataset_count = 0
for dataset in @data
# cases (assume 0 = +ve):
# value min length
# +ve +ve value - min
# +ve -ve value - 0
# -ve -ve value.abs - 0
value = dataset[:data][i]
left = (fieldwidth * field_count)
length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
# top is 0 if value is negative
top = bottom - (((value < 0 ? 0 : value) - minvalue) * unit_size)
left += bar_width * dataset_count if stack == :side
@graph.add_element( "rect", {
"x" => left.to_s,
"y" => top.to_s,
"width" => bar_width.to_s,
"height" => length.to_s,
"class" => "fill#{dataset_count+1}"
})
make_datapoint_text(left + bar_width/2.0, top - 6, value.to_s)
dataset_count += 1
end
field_count += 1
}
end
end
end
end
require 'rexml/document'
require 'SVG/Graph/Graph'
require 'SVG/Graph/BarBase'
module SVG
module Graph
# === Create presentation quality SVG bar graphs easily
#
# = Synopsis
#
# require 'SVG/Graph/Bar'
#
# fields = %w(Jan Feb Mar);
# data_sales_02 = [12, 45, 21]
#
# graph = SVG::Graph::Bar.new(
# :height => 500,
# :width => 300,
# :fields => fields
# )
#
# graph.add_data(
# :data => data_sales_02,
# :title => 'Sales 2002'
# )
#
# print "Content-type: image/svg+xml\r\n\r\n"
# print graph.burn
#
# = Description
#
# This object aims to allow you to easily create high quality
# SVG[http://www.w3c.org/tr/svg bar graphs. You can either use the default
# style sheet or supply your own. Either way there are many options which
# can be configured to give you control over how the graph is generated -
# with or without a key, data elements at each point, title, subtitle etc.
#
# = Notes
#
# The default stylesheet handles upto 12 data sets, if you
# use more you must create your own stylesheet and add the
# additional settings for the extra data sets. You will know
# if you go over 12 data sets as they will have no style and
# be in black.
#
# = Examples
#
# * http://germane-software.com/repositories/public/SVG/test/test.rb
#
# = See also
#
# * SVG::Graph::Graph
# * SVG::Graph::BarHorizontal
# * SVG::Graph::Line
# * SVG::Graph::Pie
# * SVG::Graph::Plot
# * SVG::Graph::TimeSeries
class Bar < BarBase
include REXML
# See Graph::initialize and BarBase::set_defaults
def set_defaults
super
self.top_align = self.top_font = 1
end
protected
def get_x_labels
@config[:fields]
end
def get_y_labels
maxvalue = max_value
minvalue = min_value
range = maxvalue - minvalue
top_pad = range == 0 ? 10 : range / 20.0
scale_range = (maxvalue + top_pad) - minvalue
scale_division = scale_divisions || (scale_range / 10.0)
if scale_integers
scale_division = scale_division < 1 ? 1 : scale_division.round
end
rv = []
maxvalue = maxvalue%scale_division == 0 ?
maxvalue : maxvalue + scale_division
minvalue.step( maxvalue, scale_division ) {|v| rv << v}
return rv
end
def x_label_offset( width )
width / 2.0
end
def draw_data
minvalue = min_value
fieldwidth = field_width
unit_size = (@graph_height.to_f - font_size*2*top_font) /
(get_y_labels.max - get_y_labels.min)
bargap = bar_gap ? (fieldwidth < 10 ? fieldwidth / 2 : 10) : 0
bar_width = fieldwidth - bargap
bar_width /= @data.length if stack == :side
x_mod = (@graph_width-bargap)/2 - (stack==:side ? bar_width/2 : 0)
bottom = @graph_height
field_count = 0
@config[:fields].each_index { |i|
dataset_count = 0
for dataset in @data
# cases (assume 0 = +ve):
# value min length
# +ve +ve value - min
# +ve -ve value - 0
# -ve -ve value.abs - 0
value = dataset[:data][i]
left = (fieldwidth * field_count)
length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
# top is 0 if value is negative
top = bottom - (((value < 0 ? 0 : value) - minvalue) * unit_size)
left += bar_width * dataset_count if stack == :side
@graph.add_element( "rect", {
"x" => left.to_s,
"y" => top.to_s,
"width" => bar_width.to_s,
"height" => length.to_s,
"class" => "fill#{dataset_count+1}"
})
make_datapoint_text(left + bar_width/2.0, top - 6, value.to_s)
dataset_count += 1
end
field_count += 1
}
end
end
end
end

View File

@ -1,139 +1,139 @@
require 'rexml/document'
require 'SVG/Graph/Graph'
module SVG
module Graph
# = Synopsis
#
# A superclass for bar-style graphs. Do not attempt to instantiate
# directly; use one of the subclasses instead.
#
# = Author
#
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
#
# Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt]
#
class BarBase < SVG::Graph::Graph
# Ensures that :fields are provided in the configuration.
def initialize config
raise "fields was not supplied or is empty" unless config[:fields] &&
config[:fields].kind_of?(Array) &&
config[:fields].length > 0
super
end
# In addition to the defaults set in Graph::initialize, sets
# [bar_gap] true
# [stack] :overlap
def set_defaults
init_with( :bar_gap => true, :stack => :overlap )
end
# Whether to have a gap between the bars or not, default
# is true, set to false if you don't want gaps.
attr_accessor :bar_gap
# How to stack data sets. :overlap overlaps bars with
# transparent colors, :top stacks bars on top of one another,
# :side stacks the bars side-by-side. Defaults to :overlap.
attr_accessor :stack
protected
def max_value
@data.collect{|x| x[:data].max}.max
end
def min_value
min = 0
if min_scale_value.nil?
min = @data.collect{|x| x[:data].min}.min
min = min > 0 ? 0 : min
else
min = min_scale_value
end
return min
end
def get_css
return <<EOL
/* default fill styles for multiple datasets (probably only use a single dataset on this graph though) */
.key1,.fill1{
fill: #ff0000;
fill-opacity: 0.5;
stroke: none;
stroke-width: 0.5px;
}
.key2,.fill2{
fill: #0000ff;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key3,.fill3{
fill: #00ff00;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key4,.fill4{
fill: #ffcc00;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key5,.fill5{
fill: #00ccff;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key6,.fill6{
fill: #ff00ff;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key7,.fill7{
fill: #00ffff;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key8,.fill8{
fill: #ffff00;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key9,.fill9{
fill: #cc6666;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key10,.fill10{
fill: #663399;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key11,.fill11{
fill: #339900;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key12,.fill12{
fill: #9966FF;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
EOL
end
end
end
end
require 'rexml/document'
require 'SVG/Graph/Graph'
module SVG
module Graph
# = Synopsis
#
# A superclass for bar-style graphs. Do not attempt to instantiate
# directly; use one of the subclasses instead.
#
# = Author
#
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
#
# Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt]
#
class BarBase < SVG::Graph::Graph
# Ensures that :fields are provided in the configuration.
def initialize config
raise "fields was not supplied or is empty" unless config[:fields] &&
config[:fields].kind_of?(Array) &&
config[:fields].length > 0
super
end
# In addition to the defaults set in Graph::initialize, sets
# [bar_gap] true
# [stack] :overlap
def set_defaults
init_with( :bar_gap => true, :stack => :overlap )
end
# Whether to have a gap between the bars or not, default
# is true, set to false if you don't want gaps.
attr_accessor :bar_gap
# How to stack data sets. :overlap overlaps bars with
# transparent colors, :top stacks bars on top of one another,
# :side stacks the bars side-by-side. Defaults to :overlap.
attr_accessor :stack
protected
def max_value
@data.collect{|x| x[:data].max}.max
end
def min_value
min = 0
if min_scale_value.nil?
min = @data.collect{|x| x[:data].min}.min
min = min > 0 ? 0 : min
else
min = min_scale_value
end
return min
end
def get_css
return <<EOL
/* default fill styles for multiple datasets (probably only use a single dataset on this graph though) */
.key1,.fill1{
fill: #ff0000;
fill-opacity: 0.5;
stroke: none;
stroke-width: 0.5px;
}
.key2,.fill2{
fill: #0000ff;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key3,.fill3{
fill: #00ff00;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key4,.fill4{
fill: #ffcc00;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key5,.fill5{
fill: #00ccff;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key6,.fill6{
fill: #ff00ff;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key7,.fill7{
fill: #00ffff;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key8,.fill8{
fill: #ffff00;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key9,.fill9{
fill: #cc6666;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key10,.fill10{
fill: #663399;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key11,.fill11{
fill: #339900;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
.key12,.fill12{
fill: #9966FF;
fill-opacity: 0.5;
stroke: none;
stroke-width: 1px;
}
EOL
end
end
end
end

View File

@ -1,149 +1,149 @@
require 'rexml/document'
require 'SVG/Graph/BarBase'
module SVG
module Graph
# === Create presentation quality SVG horitonzal bar graphs easily
#
# = Synopsis
#
# require 'SVG/Graph/BarHorizontal'
#
# fields = %w(Jan Feb Mar)
# data_sales_02 = [12, 45, 21]
#
# graph = SVG::Graph::BarHorizontal.new({
# :height => 500,
# :width => 300,
# :fields => fields,
# })
#
# graph.add_data({
# :data => data_sales_02,
# :title => 'Sales 2002',
# })
#
# print "Content-type: image/svg+xml\r\n\r\n"
# print graph.burn
#
# = Description
#
# This object aims to allow you to easily create high quality
# SVG horitonzal bar graphs. You can either use the default style sheet
# or supply your own. Either way there are many options which can
# be configured to give you control over how the graph is
# generated - with or without a key, data elements at each point,
# title, subtitle etc.
#
# = Examples
#
# * http://germane-software.com/repositories/public/SVG/test/test.rb
#
# = See also
#
# * SVG::Graph::Graph
# * SVG::Graph::Bar
# * SVG::Graph::Line
# * SVG::Graph::Pie
# * SVG::Graph::Plot
# * SVG::Graph::TimeSeries
#
# == Author
#
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
#
# Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt]
#
class BarHorizontal < BarBase
# In addition to the defaults set in BarBase::set_defaults, sets
# [rotate_y_labels] true
# [show_x_guidelines] true
# [show_y_guidelines] false
def set_defaults
super
init_with(
:rotate_y_labels => true,
:show_x_guidelines => true,
:show_y_guidelines => false
)
self.right_align = self.right_font = 1
end
protected
def get_x_labels
maxvalue = max_value
minvalue = min_value
range = maxvalue - minvalue
top_pad = range == 0 ? 10 : range / 20.0
scale_range = (maxvalue + top_pad) - minvalue
scale_division = scale_divisions || (scale_range / 10.0)
if scale_integers
scale_division = scale_division < 1 ? 1 : scale_division.round
end
rv = []
maxvalue = maxvalue%scale_division == 0 ?
maxvalue : maxvalue + scale_division
minvalue.step( maxvalue, scale_division ) {|v| rv << v}
return rv
end
def get_y_labels
@config[:fields]
end
def y_label_offset( height )
height / -2.0
end
def draw_data
minvalue = min_value
fieldheight = field_height
unit_size = (@graph_width.to_f - font_size*2*right_font ) /
(get_x_labels.max - get_x_labels.min )
bargap = bar_gap ? (fieldheight < 10 ? fieldheight / 2 : 10) : 0
bar_height = fieldheight - bargap
bar_height /= @data.length if stack == :side
y_mod = (bar_height / 2) + (font_size / 2)
field_count = 1
@config[:fields].each_index { |i|
dataset_count = 0
for dataset in @data
value = dataset[:data][i]
top = @graph_height - (fieldheight * field_count)
top += (bar_height * dataset_count) if stack == :side
# cases (assume 0 = +ve):
# value min length left
# +ve +ve value.abs - min minvalue.abs
# +ve -ve value.abs - 0 minvalue.abs
# -ve -ve value.abs - 0 minvalue.abs + value
length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
left = (minvalue.abs + (value < 0 ? value : 0)) * unit_size
@graph.add_element( "rect", {
"x" => left.to_s,
"y" => top.to_s,
"width" => length.to_s,
"height" => bar_height.to_s,
"class" => "fill#{dataset_count+1}"
})
make_datapoint_text(
left+length+5, top+y_mod, value, "text-anchor: start; "
)
dataset_count += 1
end
field_count += 1
}
end
end
end
end
require 'rexml/document'
require 'SVG/Graph/BarBase'
module SVG
module Graph
# === Create presentation quality SVG horitonzal bar graphs easily
#
# = Synopsis
#
# require 'SVG/Graph/BarHorizontal'
#
# fields = %w(Jan Feb Mar)
# data_sales_02 = [12, 45, 21]
#
# graph = SVG::Graph::BarHorizontal.new({
# :height => 500,
# :width => 300,
# :fields => fields,
# })
#
# graph.add_data({
# :data => data_sales_02,
# :title => 'Sales 2002',
# })
#
# print "Content-type: image/svg+xml\r\n\r\n"
# print graph.burn
#
# = Description
#
# This object aims to allow you to easily create high quality
# SVG horitonzal bar graphs. You can either use the default style sheet
# or supply your own. Either way there are many options which can
# be configured to give you control over how the graph is
# generated - with or without a key, data elements at each point,
# title, subtitle etc.
#
# = Examples
#
# * http://germane-software.com/repositories/public/SVG/test/test.rb
#
# = See also
#
# * SVG::Graph::Graph
# * SVG::Graph::Bar
# * SVG::Graph::Line
# * SVG::Graph::Pie
# * SVG::Graph::Plot
# * SVG::Graph::TimeSeries
#
# == Author
#
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
#
# Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt]
#
class BarHorizontal < BarBase
# In addition to the defaults set in BarBase::set_defaults, sets
# [rotate_y_labels] true
# [show_x_guidelines] true
# [show_y_guidelines] false
def set_defaults
super
init_with(
:rotate_y_labels => true,
:show_x_guidelines => true,
:show_y_guidelines => false
)
self.right_align = self.right_font = 1
end
protected
def get_x_labels
maxvalue = max_value
minvalue = min_value
range = maxvalue - minvalue
top_pad = range == 0 ? 10 : range / 20.0
scale_range = (maxvalue + top_pad) - minvalue
scale_division = scale_divisions || (scale_range / 10.0)
if scale_integers
scale_division = scale_division < 1 ? 1 : scale_division.round
end
rv = []
maxvalue = maxvalue%scale_division == 0 ?
maxvalue : maxvalue + scale_division
minvalue.step( maxvalue, scale_division ) {|v| rv << v}
return rv
end
def get_y_labels
@config[:fields]
end
def y_label_offset( height )
height / -2.0
end
def draw_data
minvalue = min_value
fieldheight = field_height
unit_size = (@graph_width.to_f - font_size*2*right_font ) /
(get_x_labels.max - get_x_labels.min )
bargap = bar_gap ? (fieldheight < 10 ? fieldheight / 2 : 10) : 0
bar_height = fieldheight - bargap
bar_height /= @data.length if stack == :side
y_mod = (bar_height / 2) + (font_size / 2)
field_count = 1
@config[:fields].each_index { |i|
dataset_count = 0
for dataset in @data
value = dataset[:data][i]
top = @graph_height - (fieldheight * field_count)
top += (bar_height * dataset_count) if stack == :side
# cases (assume 0 = +ve):
# value min length left
# +ve +ve value.abs - min minvalue.abs
# +ve -ve value.abs - 0 minvalue.abs
# -ve -ve value.abs - 0 minvalue.abs + value
length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
left = (minvalue.abs + (value < 0 ? value : 0)) * unit_size
@graph.add_element( "rect", {
"x" => left.to_s,
"y" => top.to_s,
"width" => length.to_s,
"height" => bar_height.to_s,
"class" => "fill#{dataset_count+1}"
})
make_datapoint_text(
left+length+5, top+y_mod, value, "text-anchor: start; "
)
dataset_count += 1
end
field_count += 1
}
end
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -1,395 +1,395 @@
require 'SVG/Graph/Graph'
module SVG
module Graph
# === Create presentation quality SVG pie graphs easily
#
# == Synopsis
#
# require 'SVG/Graph/Pie'
#
# fields = %w(Jan Feb Mar)
# data_sales_02 = [12, 45, 21]
#
# graph = SVG::Graph::Pie.new({
# :height => 500,
# :width => 300,
# :fields => fields,
# })
#
# graph.add_data({
# :data => data_sales_02,
# :title => 'Sales 2002',
# })
#
# print "Content-type: image/svg+xml\r\n\r\n"
# print graph.burn();
#
# == Description
#
# This object aims to allow you to easily create high quality
# SVG pie graphs. You can either use the default style sheet
# or supply your own. Either way there are many options which can
# be configured to give you control over how the graph is
# generated - with or without a key, display percent on pie chart,
# title, subtitle etc.
#
# = Examples
#
# http://www.germane-software/repositories/public/SVG/test/single.rb
#
# == See also
#
# * SVG::Graph::Graph
# * SVG::Graph::BarHorizontal
# * SVG::Graph::Bar
# * SVG::Graph::Line
# * SVG::Graph::Plot
# * SVG::Graph::TimeSeries
#
# == Author
#
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
#
# Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt]
#
class Pie < Graph
# Defaults are those set by Graph::initialize, and
# [show_shadow] true
# [shadow_offset] 10
# [show_data_labels] false
# [show_actual_values] false
# [show_percent] true
# [show_key_data_labels] true
# [show_key_actual_values] true
# [show_key_percent] false
# [expanded] false
# [expand_greatest] false
# [expand_gap] 10
# [show_x_labels] false
# [show_y_labels] false
# [datapoint_font_size] 12
def set_defaults
init_with(
:show_shadow => true,
:shadow_offset => 10,
:show_data_labels => false,
:show_actual_values => false,
:show_percent => true,
:show_key_data_labels => true,
:show_key_actual_values => true,
:show_key_percent => false,
:expanded => false,
:expand_greatest => false,
:expand_gap => 10,
:show_x_labels => false,
:show_y_labels => false,
:datapoint_font_size => 12
)
@data = []
end
# Adds a data set to the graph.
#
# graph.add_data( { :data => [1,2,3,4] } )
#
# Note that the :title is not necessary. If multiple
# data sets are added to the graph, the pie chart will
# display the +sums+ of the data. EG:
#
# graph.add_data( { :data => [1,2,3,4] } )
# graph.add_data( { :data => [2,3,5,9] } )
#
# is the same as:
#
# graph.add_data( { :data => [3,5,8,13] } )
def add_data arg
arg[:data].each_index {|idx|
@data[idx] = 0 unless @data[idx]
@data[idx] += arg[:data][idx]
}
end
# If true, displays a drop shadow for the chart
attr_accessor :show_shadow
# Sets the offset of the shadow from the pie chart
attr_accessor :shadow_offset
# If true, display the data labels on the chart
attr_accessor :show_data_labels
# If true, display the actual field values in the data labels
attr_accessor :show_actual_values
# If true, display the percentage value of each pie wedge in the data
# labels
attr_accessor :show_percent
# If true, display the labels in the key
attr_accessor :show_key_data_labels
# If true, display the actual value of the field in the key
attr_accessor :show_key_actual_values
# If true, display the percentage value of the wedges in the key
attr_accessor :show_key_percent
# If true, "explode" the pie (put space between the wedges)
attr_accessor :expanded
# If true, expand the largest pie wedge
attr_accessor :expand_greatest
# The amount of space between expanded wedges
attr_accessor :expand_gap
# The font size of the data point labels
attr_accessor :datapoint_font_size
protected
def add_defs defs
gradient = defs.add_element( "filter", {
"id"=>"dropshadow",
"width" => "1.2",
"height" => "1.2",
} )
gradient.add_element( "feGaussianBlur", {
"stdDeviation" => "4",
"result" => "blur"
})
end
# We don't need the graph
def draw_graph
end
def get_y_labels
[""]
end
def get_x_labels
[""]
end
def keys
total = 0
max_value = 0
@data.each {|x| total += x }
percent_scale = 100.0 / total
count = -1
a = @config[:fields].collect{ |x|
count += 1
v = @data[count]
perc = show_key_percent ? " "+(v * percent_scale).round.to_s+"%" : ""
x + " [" + v.to_s + "]" + perc
}
end
RADIANS = Math::PI/180
def draw_data
@graph = @root.add_element( "g" )
background = @graph.add_element("g")
midground = @graph.add_element("g")
diameter = @graph_height > @graph_width ? @graph_width : @graph_height
diameter -= expand_gap if expanded or expand_greatest
diameter -= datapoint_font_size if show_data_labels
diameter -= 10 if show_shadow
radius = diameter / 2.0
xoff = (width - diameter) / 2
yoff = (height - @border_bottom - diameter)
yoff -= 10 if show_shadow
@graph.attributes['transform'] = "translate( #{xoff} #{yoff} )"
wedge_text_pad = 5
wedge_text_pad = 20 if show_percent and show_data_labels
total = 0
max_value = 0
@data.each {|x|
max_value = max_value < x ? x : max_value
total += x
}
percent_scale = 100.0 / total
prev_percent = 0
rad_mult = 3.6 * RADIANS
@config[:fields].each_index { |count|
value = @data[count]
percent = percent_scale * value
radians = prev_percent * rad_mult
x_start = radius+(Math.sin(radians) * radius)
y_start = radius-(Math.cos(radians) * radius)
radians = (prev_percent+percent) * rad_mult
x_end = radius+(Math.sin(radians) * radius)
x_end -= 0.00001 if @data.length == 1
y_end = radius-(Math.cos(radians) * radius)
path = "M#{radius},#{radius} L#{x_start},#{y_start} "+
"A#{radius},#{radius} "+
"0, #{percent >= 50 ? '1' : '0'},1, "+
"#{x_end} #{y_end} Z"
wedge = @foreground.add_element( "path", {
"d" => path,
"class" => "fill#{count+1}"
})
translate = nil
tx = 0
ty = 0
half_percent = prev_percent + percent / 2
radians = half_percent * rad_mult
if show_shadow
shadow = background.add_element( "path", {
"d" => path,
"filter" => "url(#dropshadow)",
"style" => "fill: #ccc; stroke: none;"
})
clear = midground.add_element( "path", {
"d" => path,
"style" => "fill: #fff; stroke: none;"
})
end
if expanded or (expand_greatest && value == max_value)
tx = (Math.sin(radians) * expand_gap)
ty = -(Math.cos(radians) * expand_gap)
translate = "translate( #{tx} #{ty} )"
wedge.attributes["transform"] = translate
clear.attributes["transform"] = translate if clear
end
if show_shadow
shadow.attributes["transform"] =
"translate( #{tx+shadow_offset} #{ty+shadow_offset} )"
end
if show_data_labels and value != 0
label = ""
label += @config[:fields][count] if show_key_data_labels
label += " ["+value.to_s+"]" if show_actual_values
label += " "+percent.round.to_s+"%" if show_percent
msr = Math.sin(radians)
mcr = Math.cos(radians)
tx = radius + (msr * radius)
ty = radius -(mcr * radius)
if expanded or (expand_greatest && value == max_value)
tx += (msr * expand_gap)
ty -= (mcr * expand_gap)
end
@foreground.add_element( "text", {
"x" => tx.to_s,
"y" => ty.to_s,
"class" => "dataPointLabel",
"style" => "stroke: #fff; stroke-width: 2;"
}).text = label.to_s
@foreground.add_element( "text", {
"x" => tx.to_s,
"y" => ty.to_s,
"class" => "dataPointLabel",
}).text = label.to_s
end
prev_percent += percent
}
end
def round val, to
up = 10**to.to_f
(val * up).to_i / up
end
def get_css
return <<EOL
.dataPointLabel{
fill: #000000;
text-anchor:middle;
font-size: #{datapoint_font_size}px;
font-family: "Arial", sans-serif;
font-weight: normal;
}
/* key - MUST match fill styles */
.key1,.fill1{
fill: #ff0000;
fill-opacity: 0.7;
stroke: none;
stroke-width: 1px;
}
.key2,.fill2{
fill: #0000ff;
fill-opacity: 0.7;
stroke: none;
stroke-width: 1px;
}
.key3,.fill3{
fill-opacity: 0.7;
fill: #00ff00;
stroke: none;
stroke-width: 1px;
}
.key4,.fill4{
fill-opacity: 0.7;
fill: #ffcc00;
stroke: none;
stroke-width: 1px;
}
.key5,.fill5{
fill-opacity: 0.7;
fill: #00ccff;
stroke: none;
stroke-width: 1px;
}
.key6,.fill6{
fill-opacity: 0.7;
fill: #ff00ff;
stroke: none;
stroke-width: 1px;
}
.key7,.fill7{
fill-opacity: 0.7;
fill: #00ff99;
stroke: none;
stroke-width: 1px;
}
.key8,.fill8{
fill-opacity: 0.7;
fill: #ffff00;
stroke: none;
stroke-width: 1px;
}
.key9,.fill9{
fill-opacity: 0.7;
fill: #cc6666;
stroke: none;
stroke-width: 1px;
}
.key10,.fill10{
fill-opacity: 0.7;
fill: #663399;
stroke: none;
stroke-width: 1px;
}
.key11,.fill11{
fill-opacity: 0.7;
fill: #339900;
stroke: none;
stroke-width: 1px;
}
.key12,.fill12{
fill-opacity: 0.7;
fill: #9966FF;
stroke: none;
stroke-width: 1px;
}
EOL
end
end
end
end
require 'SVG/Graph/Graph'
module SVG
module Graph
# === Create presentation quality SVG pie graphs easily
#
# == Synopsis
#
# require 'SVG/Graph/Pie'
#
# fields = %w(Jan Feb Mar)
# data_sales_02 = [12, 45, 21]
#
# graph = SVG::Graph::Pie.new({
# :height => 500,
# :width => 300,
# :fields => fields,
# })
#
# graph.add_data({
# :data => data_sales_02,
# :title => 'Sales 2002',
# })
#
# print "Content-type: image/svg+xml\r\n\r\n"
# print graph.burn();
#
# == Description
#
# This object aims to allow you to easily create high quality
# SVG pie graphs. You can either use the default style sheet
# or supply your own. Either way there are many options which can
# be configured to give you control over how the graph is
# generated - with or without a key, display percent on pie chart,
# title, subtitle etc.
#
# = Examples
#
# http://www.germane-software/repositories/public/SVG/test/single.rb
#
# == See also
#
# * SVG::Graph::Graph
# * SVG::Graph::BarHorizontal
# * SVG::Graph::Bar
# * SVG::Graph::Line
# * SVG::Graph::Plot
# * SVG::Graph::TimeSeries
#
# == Author
#
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
#
# Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt]
#
class Pie < Graph
# Defaults are those set by Graph::initialize, and
# [show_shadow] true
# [shadow_offset] 10
# [show_data_labels] false
# [show_actual_values] false
# [show_percent] true
# [show_key_data_labels] true
# [show_key_actual_values] true
# [show_key_percent] false
# [expanded] false
# [expand_greatest] false
# [expand_gap] 10
# [show_x_labels] false
# [show_y_labels] false
# [datapoint_font_size] 12
def set_defaults
init_with(
:show_shadow => true,
:shadow_offset => 10,
:show_data_labels => false,
:show_actual_values => false,
:show_percent => true,
:show_key_data_labels => true,
:show_key_actual_values => true,
:show_key_percent => false,
:expanded => false,
:expand_greatest => false,
:expand_gap => 10,
:show_x_labels => false,
:show_y_labels => false,
:datapoint_font_size => 12
)
@data = []
end
# Adds a data set to the graph.
#
# graph.add_data( { :data => [1,2,3,4] } )
#
# Note that the :title is not necessary. If multiple
# data sets are added to the graph, the pie chart will
# display the +sums+ of the data. EG:
#
# graph.add_data( { :data => [1,2,3,4] } )
# graph.add_data( { :data => [2,3,5,9] } )
#
# is the same as:
#
# graph.add_data( { :data => [3,5,8,13] } )
def add_data arg
arg[:data].each_index {|idx|
@data[idx] = 0 unless @data[idx]
@data[idx] += arg[:data][idx]
}
end
# If true, displays a drop shadow for the chart
attr_accessor :show_shadow
# Sets the offset of the shadow from the pie chart
attr_accessor :shadow_offset
# If true, display the data labels on the chart
attr_accessor :show_data_labels
# If true, display the actual field values in the data labels
attr_accessor :show_actual_values
# If true, display the percentage value of each pie wedge in the data
# labels
attr_accessor :show_percent
# If true, display the labels in the key
attr_accessor :show_key_data_labels
# If true, display the actual value of the field in the key
attr_accessor :show_key_actual_values
# If true, display the percentage value of the wedges in the key
attr_accessor :show_key_percent
# If true, "explode" the pie (put space between the wedges)
attr_accessor :expanded
# If true, expand the largest pie wedge
attr_accessor :expand_greatest
# The amount of space between expanded wedges
attr_accessor :expand_gap
# The font size of the data point labels
attr_accessor :datapoint_font_size
protected
def add_defs defs
gradient = defs.add_element( "filter", {
"id"=>"dropshadow",
"width" => "1.2",
"height" => "1.2",
} )
gradient.add_element( "feGaussianBlur", {
"stdDeviation" => "4",
"result" => "blur"
})
end
# We don't need the graph
def draw_graph
end
def get_y_labels
[""]
end
def get_x_labels
[""]
end
def keys
total = 0
max_value = 0
@data.each {|x| total += x }
percent_scale = 100.0 / total
count = -1
a = @config[:fields].collect{ |x|
count += 1
v = @data[count]
perc = show_key_percent ? " "+(v * percent_scale).round.to_s+"%" : ""
x + " [" + v.to_s + "]" + perc
}
end
RADIANS = Math::PI/180
def draw_data
@graph = @root.add_element( "g" )
background = @graph.add_element("g")
midground = @graph.add_element("g")
diameter = @graph_height > @graph_width ? @graph_width : @graph_height
diameter -= expand_gap if expanded or expand_greatest
diameter -= datapoint_font_size if show_data_labels
diameter -= 10 if show_shadow
radius = diameter / 2.0
xoff = (width - diameter) / 2
yoff = (height - @border_bottom - diameter)
yoff -= 10 if show_shadow
@graph.attributes['transform'] = "translate( #{xoff} #{yoff} )"
wedge_text_pad = 5
wedge_text_pad = 20 if show_percent and show_data_labels
total = 0
max_value = 0
@data.each {|x|
max_value = max_value < x ? x : max_value
total += x
}
percent_scale = 100.0 / total
prev_percent = 0
rad_mult = 3.6 * RADIANS
@config[:fields].each_index { |count|
value = @data[count]
percent = percent_scale * value
radians = prev_percent * rad_mult
x_start = radius+(Math.sin(radians) * radius)
y_start = radius-(Math.cos(radians) * radius)
radians = (prev_percent+percent) * rad_mult
x_end = radius+(Math.sin(radians) * radius)
x_end -= 0.00001 if @data.length == 1
y_end = radius-(Math.cos(radians) * radius)
path = "M#{radius},#{radius} L#{x_start},#{y_start} "+
"A#{radius},#{radius} "+
"0, #{percent >= 50 ? '1' : '0'},1, "+
"#{x_end} #{y_end} Z"
wedge = @foreground.add_element( "path", {
"d" => path,
"class" => "fill#{count+1}"
})
translate = nil
tx = 0
ty = 0
half_percent = prev_percent + percent / 2
radians = half_percent * rad_mult
if show_shadow
shadow = background.add_element( "path", {
"d" => path,
"filter" => "url(#dropshadow)",
"style" => "fill: #ccc; stroke: none;"
})
clear = midground.add_element( "path", {
"d" => path,
"style" => "fill: #fff; stroke: none;"
})
end
if expanded or (expand_greatest && value == max_value)
tx = (Math.sin(radians) * expand_gap)
ty = -(Math.cos(radians) * expand_gap)
translate = "translate( #{tx} #{ty} )"
wedge.attributes["transform"] = translate
clear.attributes["transform"] = translate if clear
end
if show_shadow
shadow.attributes["transform"] =
"translate( #{tx+shadow_offset} #{ty+shadow_offset} )"
end
if show_data_labels and value != 0
label = ""
label += @config[:fields][count] if show_key_data_labels
label += " ["+value.to_s+"]" if show_actual_values
label += " "+percent.round.to_s+"%" if show_percent
msr = Math.sin(radians)
mcr = Math.cos(radians)
tx = radius + (msr * radius)
ty = radius -(mcr * radius)
if expanded or (expand_greatest && value == max_value)
tx += (msr * expand_gap)
ty -= (mcr * expand_gap)
end
@foreground.add_element( "text", {
"x" => tx.to_s,
"y" => ty.to_s,
"class" => "dataPointLabel",
"style" => "stroke: #fff; stroke-width: 2;"
}).text = label.to_s
@foreground.add_element( "text", {
"x" => tx.to_s,
"y" => ty.to_s,
"class" => "dataPointLabel",
}).text = label.to_s
end
prev_percent += percent
}
end
def round val, to
up = 10**to.to_f
(val * up).to_i / up
end
def get_css
return <<EOL
.dataPointLabel{
fill: #000000;
text-anchor:middle;
font-size: #{datapoint_font_size}px;
font-family: "Arial", sans-serif;
font-weight: normal;
}
/* key - MUST match fill styles */
.key1,.fill1{
fill: #ff0000;
fill-opacity: 0.7;
stroke: none;
stroke-width: 1px;
}
.key2,.fill2{
fill: #0000ff;
fill-opacity: 0.7;
stroke: none;
stroke-width: 1px;
}
.key3,.fill3{
fill-opacity: 0.7;
fill: #00ff00;
stroke: none;
stroke-width: 1px;
}
.key4,.fill4{
fill-opacity: 0.7;
fill: #ffcc00;
stroke: none;
stroke-width: 1px;
}
.key5,.fill5{
fill-opacity: 0.7;
fill: #00ccff;
stroke: none;
stroke-width: 1px;
}
.key6,.fill6{
fill-opacity: 0.7;
fill: #ff00ff;
stroke: none;
stroke-width: 1px;
}
.key7,.fill7{
fill-opacity: 0.7;
fill: #00ff99;
stroke: none;
stroke-width: 1px;
}
.key8,.fill8{
fill-opacity: 0.7;
fill: #ffff00;
stroke: none;
stroke-width: 1px;
}
.key9,.fill9{
fill-opacity: 0.7;
fill: #cc6666;
stroke: none;
stroke-width: 1px;
}
.key10,.fill10{
fill-opacity: 0.7;
fill: #663399;
stroke: none;
stroke-width: 1px;
}
.key11,.fill11{
fill-opacity: 0.7;
fill: #339900;
stroke: none;
stroke-width: 1px;
}
.key12,.fill12{
fill-opacity: 0.7;
fill: #9966FF;
stroke: none;
stroke-width: 1px;
}
EOL
end
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -1,241 +1,241 @@
require 'SVG/Graph/Plot'
require 'parsedate'
module SVG
module Graph
# === For creating SVG plots of scalar temporal data
#
# = Synopsis
#
# require 'SVG/Graph/TimeSeriess'
#
# # Data sets are x,y pairs
# data1 = ["6/17/72", 11, "1/11/72", 7, "4/13/04 17:31", 11,
# "9/11/01", 9, "9/1/85", 2, "9/1/88", 1, "1/15/95", 13]
# data2 = ["8/1/73", 18, "3/1/77", 15, "10/1/98", 4,
# "5/1/02", 14, "3/1/95", 6, "8/1/91", 12, "12/1/87", 6,
# "5/1/84", 17, "10/1/80", 12]
#
# graph = SVG::Graph::TimeSeries.new( {
# :width => 640,
# :height => 480,
# :graph_title => title,
# :show_graph_title => true,
# :no_css => true,
# :key => true,
# :scale_x_integers => true,
# :scale_y_integers => true,
# :min_x_value => 0,
# :min_y_value => 0,
# :show_data_labels => true,
# :show_x_guidelines => true,
# :show_x_title => true,
# :x_title => "Time",
# :show_y_title => true,
# :y_title => "Ice Cream Cones",
# :y_title_text_direction => :bt,
# :stagger_x_labels => true,
# :x_label_format => "%m/%d/%y",
# })
#
# graph.add_data({
# :data => projection
# :title => 'Projected',
# })
#
# graph.add_data({
# :data => actual,
# :title => 'Actual',
# })
#
# print graph.burn()
#
# = Description
#
# Produces a graph of temporal scalar data.
#
# = Examples
#
# http://www.germane-software/repositories/public/SVG/test/timeseries.rb
#
# = Notes
#
# The default stylesheet handles upto 10 data sets, if you
# use more you must create your own stylesheet and add the
# additional settings for the extra data sets. You will know
# if you go over 10 data sets as they will have no style and
# be in black.
#
# Unlike the other types of charts, data sets must contain x,y pairs:
#
# [ "12:30", 2 ] # A data set with 1 point: ("12:30",2)
# [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and
# # ("14:20",6)
#
# Note that multiple data sets within the same chart can differ in length,
# and that the data in the datasets needn't be in order; they will be ordered
# by the plot along the X-axis.
#
# The dates must be parseable by ParseDate, but otherwise can be
# any order of magnitude (seconds within the hour, or years)
#
# = See also
#
# * SVG::Graph::Graph
# * SVG::Graph::BarHorizontal
# * SVG::Graph::Bar
# * SVG::Graph::Line
# * SVG::Graph::Pie
# * SVG::Graph::Plot
#
# == Author
#
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
#
# Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt]
#
class TimeSeries < Plot
# In addition to the defaults set by Graph::initialize and
# Plot::set_defaults, sets:
# [x_label_format] '%Y-%m-%d %H:%M:%S'
# [popup_format] '%Y-%m-%d %H:%M:%S'
def set_defaults
super
init_with(
#:max_time_span => '',
:x_label_format => '%Y-%m-%d %H:%M:%S',
:popup_format => '%Y-%m-%d %H:%M:%S'
)
end
# The format string use do format the X axis labels.
# See Time::strformat
attr_accessor :x_label_format
# Use this to set the spacing between dates on the axis. The value
# must be of the form
# "\d+ ?(days|weeks|months|years|hours|minutes|seconds)?"
#
# EG:
#
# graph.timescale_divisions = "2 weeks"
#
# will cause the chart to try to divide the X axis up into segments of
# two week periods.
attr_accessor :timescale_divisions
# The formatting used for the popups. See x_label_format
attr_accessor :popup_format
# Add data to the plot.
#
# d1 = [ "12:30", 2 ] # A data set with 1 point: ("12:30",2)
# d2 = [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and
# # ("14:20",6)
# graph.add_data(
# :data => d1,
# :title => 'One'
# )
# graph.add_data(
# :data => d2,
# :title => 'Two'
# )
#
# Note that the data must be in time,value pairs, and that the date format
# may be any date that is parseable by ParseDate.
def add_data data
@data = [] unless @data
raise "No data provided by #{@data.inspect}" unless data[:data] and
data[:data].kind_of? Array
raise "Data supplied must be x,y pairs! "+
"The data provided contained an odd set of "+
"data points" unless data[:data].length % 2 == 0
return if data[:data].length == 0
x = []
y = []
data[:data].each_index {|i|
if i%2 == 0
arr = ParseDate.parsedate( data[:data][i] )
t = Time.local( *arr[0,6].compact )
x << t.to_i
else
y << data[:data][i]
end
}
sort( x, y )
data[:data] = [x,y]
@data << data
end
protected
def min_x_value=(value)
arr = ParseDate.parsedate( value )
@min_x_value = Time.local( *arr[0,6].compact ).to_i
end
def format x, y
Time.at( x ).strftime( popup_format )
end
def get_x_labels
get_x_values.collect { |v| Time.at(v).strftime( x_label_format ) }
end
private
def get_x_values
rv = []
min, max, scale_division = x_range
if timescale_divisions
timescale_divisions =~ /(\d+) ?(day|week|month|year|hour|minute|second)?/
division_units = $2 ? $2 : "day"
amount = $1.to_i
if amount
step = nil
case division_units
when "month"
cur = min
while cur < max
rv << cur
arr = Time.at( cur ).to_a
arr[4] += amount
if arr[4] > 12
arr[5] += (arr[4] / 12).to_i
arr[4] = (arr[4] % 12)
end
cur = Time.local(*arr).to_i
end
when "year"
cur = min
while cur < max
rv << cur
arr = Time.at( cur ).to_a
arr[5] += amount
cur = Time.local(*arr).to_i
end
when "week"
step = 7 * 24 * 60 * 60 * amount
when "day"
step = 24 * 60 * 60 * amount
when "hour"
step = 60 * 60 * amount
when "minute"
step = 60 * amount
when "second"
step = amount
end
min.step( max, step ) {|v| rv << v} if step
return rv
end
end
min.step( max, scale_division ) {|v| rv << v}
return rv
end
end
end
end
require 'SVG/Graph/Plot'
require 'parsedate'
module SVG
module Graph
# === For creating SVG plots of scalar temporal data
#
# = Synopsis
#
# require 'SVG/Graph/TimeSeriess'
#
# # Data sets are x,y pairs
# data1 = ["6/17/72", 11, "1/11/72", 7, "4/13/04 17:31", 11,
# "9/11/01", 9, "9/1/85", 2, "9/1/88", 1, "1/15/95", 13]
# data2 = ["8/1/73", 18, "3/1/77", 15, "10/1/98", 4,
# "5/1/02", 14, "3/1/95", 6, "8/1/91", 12, "12/1/87", 6,
# "5/1/84", 17, "10/1/80", 12]
#
# graph = SVG::Graph::TimeSeries.new( {
# :width => 640,
# :height => 480,
# :graph_title => title,
# :show_graph_title => true,
# :no_css => true,
# :key => true,
# :scale_x_integers => true,
# :scale_y_integers => true,
# :min_x_value => 0,
# :min_y_value => 0,
# :show_data_labels => true,
# :show_x_guidelines => true,
# :show_x_title => true,
# :x_title => "Time",
# :show_y_title => true,
# :y_title => "Ice Cream Cones",
# :y_title_text_direction => :bt,
# :stagger_x_labels => true,
# :x_label_format => "%m/%d/%y",
# })
#
# graph.add_data({
# :data => projection
# :title => 'Projected',
# })
#
# graph.add_data({
# :data => actual,
# :title => 'Actual',
# })
#
# print graph.burn()
#
# = Description
#
# Produces a graph of temporal scalar data.
#
# = Examples
#
# http://www.germane-software/repositories/public/SVG/test/timeseries.rb
#
# = Notes
#
# The default stylesheet handles upto 10 data sets, if you
# use more you must create your own stylesheet and add the
# additional settings for the extra data sets. You will know
# if you go over 10 data sets as they will have no style and
# be in black.
#
# Unlike the other types of charts, data sets must contain x,y pairs:
#
# [ "12:30", 2 ] # A data set with 1 point: ("12:30",2)
# [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and
# # ("14:20",6)
#
# Note that multiple data sets within the same chart can differ in length,
# and that the data in the datasets needn't be in order; they will be ordered
# by the plot along the X-axis.
#
# The dates must be parseable by ParseDate, but otherwise can be
# any order of magnitude (seconds within the hour, or years)
#
# = See also
#
# * SVG::Graph::Graph
# * SVG::Graph::BarHorizontal
# * SVG::Graph::Bar
# * SVG::Graph::Line
# * SVG::Graph::Pie
# * SVG::Graph::Plot
#
# == Author
#
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
#
# Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt]
#
class TimeSeries < Plot
# In addition to the defaults set by Graph::initialize and
# Plot::set_defaults, sets:
# [x_label_format] '%Y-%m-%d %H:%M:%S'
# [popup_format] '%Y-%m-%d %H:%M:%S'
def set_defaults
super
init_with(
#:max_time_span => '',
:x_label_format => '%Y-%m-%d %H:%M:%S',
:popup_format => '%Y-%m-%d %H:%M:%S'
)
end
# The format string use do format the X axis labels.
# See Time::strformat
attr_accessor :x_label_format
# Use this to set the spacing between dates on the axis. The value
# must be of the form
# "\d+ ?(days|weeks|months|years|hours|minutes|seconds)?"
#
# EG:
#
# graph.timescale_divisions = "2 weeks"
#
# will cause the chart to try to divide the X axis up into segments of
# two week periods.
attr_accessor :timescale_divisions
# The formatting used for the popups. See x_label_format
attr_accessor :popup_format
# Add data to the plot.
#
# d1 = [ "12:30", 2 ] # A data set with 1 point: ("12:30",2)
# d2 = [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and
# # ("14:20",6)
# graph.add_data(
# :data => d1,
# :title => 'One'
# )
# graph.add_data(
# :data => d2,
# :title => 'Two'
# )
#
# Note that the data must be in time,value pairs, and that the date format
# may be any date that is parseable by ParseDate.
def add_data data
@data = [] unless @data
raise "No data provided by #{@data.inspect}" unless data[:data] and
data[:data].kind_of? Array
raise "Data supplied must be x,y pairs! "+
"The data provided contained an odd set of "+
"data points" unless data[:data].length % 2 == 0
return if data[:data].length == 0
x = []
y = []
data[:data].each_index {|i|
if i%2 == 0
arr = ParseDate.parsedate( data[:data][i] )
t = Time.local( *arr[0,6].compact )
x << t.to_i
else
y << data[:data][i]
end
}
sort( x, y )
data[:data] = [x,y]
@data << data
end
protected
def min_x_value=(value)
arr = ParseDate.parsedate( value )
@min_x_value = Time.local( *arr[0,6].compact ).to_i
end
def format x, y
Time.at( x ).strftime( popup_format )
end
def get_x_labels
get_x_values.collect { |v| Time.at(v).strftime( x_label_format ) }
end
private
def get_x_values
rv = []
min, max, scale_division = x_range
if timescale_divisions
timescale_divisions =~ /(\d+) ?(day|week|month|year|hour|minute|second)?/
division_units = $2 ? $2 : "day"
amount = $1.to_i
if amount
step = nil
case division_units
when "month"
cur = min
while cur < max
rv << cur
arr = Time.at( cur ).to_a
arr[4] += amount
if arr[4] > 12
arr[5] += (arr[4] / 12).to_i
arr[4] = (arr[4] % 12)
end
cur = Time.local(*arr).to_i
end
when "year"
cur = min
while cur < max
rv << cur
arr = Time.at( cur ).to_a
arr[5] += amount
cur = Time.local(*arr).to_i
end
when "week"
step = 7 * 24 * 60 * 60 * amount
when "day"
step = 24 * 60 * 60 * amount
when "hour"
step = 60 * 60 * amount
when "minute"
step = 60 * amount
when "second"
step = amount
end
min.step( max, step ) {|v| rv << v} if step
return rv
end
end
min.step( max, scale_division ) {|v| rv << v}
return rv
end
end
end
end

View File

@ -1,282 +1,282 @@
# Redmine - project management software
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'redmine/scm/adapters/abstract_adapter'
require 'uri'
module Redmine
module Scm
module Adapters
class SubversionAdapter < AbstractAdapter
# SVN executable name
SVN_BIN = Redmine::Configuration['scm_subversion_command'] || "svn"
class << self
def client_command
@@bin ||= SVN_BIN
end
def sq_bin
@@sq_bin ||= shell_quote(SVN_BIN)
end
def client_version
@@client_version ||= (svn_binary_version || [])
end
def client_available
!client_version.empty?
end
def svn_binary_version
scm_version = scm_version_from_command_line.dup
if scm_version.respond_to?(:force_encoding)
scm_version.force_encoding('ASCII-8BIT')
end
if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
m[2].scan(%r{\d+}).collect(&:to_i)
end
end
def scm_version_from_command_line
shellout("#{sq_bin} --version") { |io| io.read }.to_s
end
end
# Get info about the svn repository
def info
cmd = "#{self.class.sq_bin} info --xml #{target}"
cmd << credentials_string
info = nil
shellout(cmd) do |io|
output = io.read
if output.respond_to?(:force_encoding)
output.force_encoding('UTF-8')
end
begin
doc = ActiveSupport::XmlMini.parse(output)
#root_url = doc.elements["info/entry/repository/root"].text
info = Info.new({:root_url => doc['info']['entry']['repository']['root']['__content__'],
:lastrev => Revision.new({
:identifier => doc['info']['entry']['commit']['revision'],
:time => Time.parse(doc['info']['entry']['commit']['date']['__content__']).localtime,
:author => (doc['info']['entry']['commit']['author'] ? doc['info']['entry']['commit']['author']['__content__'] : "")
})
})
rescue
end
end
return nil if $? && $?.exitstatus != 0
info
rescue CommandFailed
return nil
end
# Returns an Entries collection
# or nil if the given path doesn't exist in the repository
def entries(path=nil, identifier=nil)
path ||= ''
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
entries = Entries.new
cmd = "#{self.class.sq_bin} list --xml #{target(path)}@#{identifier}"
cmd << credentials_string
shellout(cmd) do |io|
output = io.read
if output.respond_to?(:force_encoding)
output.force_encoding('UTF-8')
end
begin
doc = ActiveSupport::XmlMini.parse(output)
each_xml_element(doc['lists']['list'], 'entry') do |entry|
commit = entry['commit']
commit_date = commit['date']
# Skip directory if there is no commit date (usually that
# means that we don't have read access to it)
next if entry['kind'] == 'dir' && commit_date.nil?
name = entry['name']['__content__']
entries << Entry.new({:name => URI.unescape(name),
:path => ((path.empty? ? "" : "#{path}/") + name),
:kind => entry['kind'],
:size => ((s = entry['size']) ? s['__content__'].to_i : nil),
:lastrev => Revision.new({
:identifier => commit['revision'],
:time => Time.parse(commit_date['__content__'].to_s).localtime,
:author => ((a = commit['author']) ? a['__content__'] : nil)
})
})
end
rescue Exception => e
logger.error("Error parsing svn output: #{e.message}")
logger.error("Output was:\n #{output}")
end
end
return nil if $? && $?.exitstatus != 0
logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
entries.sort_by_name
end
def properties(path, identifier=nil)
# proplist xml output supported in svn 1.5.0 and higher
return nil unless self.class.client_version_above?([1, 5, 0])
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
cmd = "#{self.class.sq_bin} proplist --verbose --xml #{target(path)}@#{identifier}"
cmd << credentials_string
properties = {}
shellout(cmd) do |io|
output = io.read
if output.respond_to?(:force_encoding)
output.force_encoding('UTF-8')
end
begin
doc = ActiveSupport::XmlMini.parse(output)
each_xml_element(doc['properties']['target'], 'property') do |property|
properties[ property['name'] ] = property['__content__'].to_s
end
rescue
end
end
return nil if $? && $?.exitstatus != 0
properties
end
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
path ||= ''
identifier_from = (identifier_from && identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD"
identifier_to = (identifier_to && identifier_to.to_i > 0) ? identifier_to.to_i : 1
revisions = Revisions.new
cmd = "#{self.class.sq_bin} log --xml -r #{identifier_from}:#{identifier_to}"
cmd << credentials_string
cmd << " --verbose " if options[:with_paths]
cmd << " --limit #{options[:limit].to_i}" if options[:limit]
cmd << ' ' + target(path)
shellout(cmd) do |io|
output = io.read
if output.respond_to?(:force_encoding)
output.force_encoding('UTF-8')
end
begin
doc = ActiveSupport::XmlMini.parse(output)
each_xml_element(doc['log'], 'logentry') do |logentry|
paths = []
each_xml_element(logentry['paths'], 'path') do |path|
paths << {:action => path['action'],
:path => path['__content__'],
:from_path => path['copyfrom-path'],
:from_revision => path['copyfrom-rev']
}
end if logentry['paths'] && logentry['paths']['path']
paths.sort! { |x,y| x[:path] <=> y[:path] }
revisions << Revision.new({:identifier => logentry['revision'],
:author => (logentry['author'] ? logentry['author']['__content__'] : ""),
:time => Time.parse(logentry['date']['__content__'].to_s).localtime,
:message => logentry['msg']['__content__'],
:paths => paths
})
end
rescue
end
end
return nil if $? && $?.exitstatus != 0
revisions
end
def diff(path, identifier_from, identifier_to=nil, type="inline")
path ||= ''
identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : ''
identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : (identifier_from.to_i - 1)
cmd = "#{self.class.sq_bin} diff -r "
cmd << "#{identifier_to}:"
cmd << "#{identifier_from}"
cmd << " #{target(path)}@#{identifier_from}"
cmd << credentials_string
diff = []
shellout(cmd) do |io|
io.each_line do |line|
diff << line
end
end
return nil if $? && $?.exitstatus != 0
diff
end
def cat(path, identifier=nil)
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
cmd = "#{self.class.sq_bin} cat #{target(path)}@#{identifier}"
cmd << credentials_string
cat = nil
shellout(cmd) do |io|
io.binmode
cat = io.read
end
return nil if $? && $?.exitstatus != 0
cat
end
def annotate(path, identifier=nil)
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
cmd = "#{self.class.sq_bin} blame #{target(path)}@#{identifier}"
cmd << credentials_string
blame = Annotate.new
shellout(cmd) do |io|
io.each_line do |line|
next unless line =~ %r{^\s*(\d+)\s*(\S+)\s(.*)$}
blame.add_line($3.rstrip, Revision.new(:identifier => $1.to_i, :author => $2.strip))
end
end
return nil if $? && $?.exitstatus != 0
blame
end
private
def credentials_string
str = ''
str << " --username #{shell_quote(@login)}" unless @login.blank?
str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
str << " --no-auth-cache --non-interactive"
str
end
# Helper that iterates over the child elements of a xml node
# MiniXml returns a hash when a single child is found or an array of hashes for multiple children
def each_xml_element(node, name)
if node && node[name]
if node[name].is_a?(Hash)
yield node[name]
else
node[name].each do |element|
yield element
end
end
end
end
def target(path = '')
base = path.match(/^\//) ? root_url : url
uri = "#{base}/#{path}"
uri = URI.escape(URI.escape(uri), '[]')
shell_quote(uri.gsub(/[?<>\*]/, ''))
end
end
end
end
end
# Redmine - project management software
# Copyright (C) 2006-2010 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'redmine/scm/adapters/abstract_adapter'
require 'uri'
module Redmine
module Scm
module Adapters
class SubversionAdapter < AbstractAdapter
# SVN executable name
SVN_BIN = Redmine::Configuration['scm_subversion_command'] || "svn"
class << self
def client_command
@@bin ||= SVN_BIN
end
def sq_bin
@@sq_bin ||= shell_quote(SVN_BIN)
end
def client_version
@@client_version ||= (svn_binary_version || [])
end
def client_available
!client_version.empty?
end
def svn_binary_version
scm_version = scm_version_from_command_line.dup
if scm_version.respond_to?(:force_encoding)
scm_version.force_encoding('ASCII-8BIT')
end
if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
m[2].scan(%r{\d+}).collect(&:to_i)
end
end
def scm_version_from_command_line
shellout("#{sq_bin} --version") { |io| io.read }.to_s
end
end
# Get info about the svn repository
def info
cmd = "#{self.class.sq_bin} info --xml #{target}"
cmd << credentials_string
info = nil
shellout(cmd) do |io|
output = io.read
if output.respond_to?(:force_encoding)
output.force_encoding('UTF-8')
end
begin
doc = ActiveSupport::XmlMini.parse(output)
#root_url = doc.elements["info/entry/repository/root"].text
info = Info.new({:root_url => doc['info']['entry']['repository']['root']['__content__'],
:lastrev => Revision.new({
:identifier => doc['info']['entry']['commit']['revision'],
:time => Time.parse(doc['info']['entry']['commit']['date']['__content__']).localtime,
:author => (doc['info']['entry']['commit']['author'] ? doc['info']['entry']['commit']['author']['__content__'] : "")
})
})
rescue
end
end
return nil if $? && $?.exitstatus != 0
info
rescue CommandFailed
return nil
end
# Returns an Entries collection
# or nil if the given path doesn't exist in the repository
def entries(path=nil, identifier=nil)
path ||= ''
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
entries = Entries.new
cmd = "#{self.class.sq_bin} list --xml #{target(path)}@#{identifier}"
cmd << credentials_string
shellout(cmd) do |io|
output = io.read
if output.respond_to?(:force_encoding)
output.force_encoding('UTF-8')
end
begin
doc = ActiveSupport::XmlMini.parse(output)
each_xml_element(doc['lists']['list'], 'entry') do |entry|
commit = entry['commit']
commit_date = commit['date']
# Skip directory if there is no commit date (usually that
# means that we don't have read access to it)
next if entry['kind'] == 'dir' && commit_date.nil?
name = entry['name']['__content__']
entries << Entry.new({:name => URI.unescape(name),
:path => ((path.empty? ? "" : "#{path}/") + name),
:kind => entry['kind'],
:size => ((s = entry['size']) ? s['__content__'].to_i : nil),
:lastrev => Revision.new({
:identifier => commit['revision'],
:time => Time.parse(commit_date['__content__'].to_s).localtime,
:author => ((a = commit['author']) ? a['__content__'] : nil)
})
})
end
rescue Exception => e
logger.error("Error parsing svn output: #{e.message}")
logger.error("Output was:\n #{output}")
end
end
return nil if $? && $?.exitstatus != 0
logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
entries.sort_by_name
end
def properties(path, identifier=nil)
# proplist xml output supported in svn 1.5.0 and higher
return nil unless self.class.client_version_above?([1, 5, 0])
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
cmd = "#{self.class.sq_bin} proplist --verbose --xml #{target(path)}@#{identifier}"
cmd << credentials_string
properties = {}
shellout(cmd) do |io|
output = io.read
if output.respond_to?(:force_encoding)
output.force_encoding('UTF-8')
end
begin
doc = ActiveSupport::XmlMini.parse(output)
each_xml_element(doc['properties']['target'], 'property') do |property|
properties[ property['name'] ] = property['__content__'].to_s
end
rescue
end
end
return nil if $? && $?.exitstatus != 0
properties
end
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
path ||= ''
identifier_from = (identifier_from && identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD"
identifier_to = (identifier_to && identifier_to.to_i > 0) ? identifier_to.to_i : 1
revisions = Revisions.new
cmd = "#{self.class.sq_bin} log --xml -r #{identifier_from}:#{identifier_to}"
cmd << credentials_string
cmd << " --verbose " if options[:with_paths]
cmd << " --limit #{options[:limit].to_i}" if options[:limit]
cmd << ' ' + target(path)
shellout(cmd) do |io|
output = io.read
if output.respond_to?(:force_encoding)
output.force_encoding('UTF-8')
end
begin
doc = ActiveSupport::XmlMini.parse(output)
each_xml_element(doc['log'], 'logentry') do |logentry|
paths = []
each_xml_element(logentry['paths'], 'path') do |path|
paths << {:action => path['action'],
:path => path['__content__'],
:from_path => path['copyfrom-path'],
:from_revision => path['copyfrom-rev']
}
end if logentry['paths'] && logentry['paths']['path']
paths.sort! { |x,y| x[:path] <=> y[:path] }
revisions << Revision.new({:identifier => logentry['revision'],
:author => (logentry['author'] ? logentry['author']['__content__'] : ""),
:time => Time.parse(logentry['date']['__content__'].to_s).localtime,
:message => logentry['msg']['__content__'],
:paths => paths
})
end
rescue
end
end
return nil if $? && $?.exitstatus != 0
revisions
end
def diff(path, identifier_from, identifier_to=nil, type="inline")
path ||= ''
identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : ''
identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : (identifier_from.to_i - 1)
cmd = "#{self.class.sq_bin} diff -r "
cmd << "#{identifier_to}:"
cmd << "#{identifier_from}"
cmd << " #{target(path)}@#{identifier_from}"
cmd << credentials_string
diff = []
shellout(cmd) do |io|
io.each_line do |line|
diff << line
end
end
return nil if $? && $?.exitstatus != 0
diff
end
def cat(path, identifier=nil)
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
cmd = "#{self.class.sq_bin} cat #{target(path)}@#{identifier}"
cmd << credentials_string
cat = nil
shellout(cmd) do |io|
io.binmode
cat = io.read
end
return nil if $? && $?.exitstatus != 0
cat
end
def annotate(path, identifier=nil)
identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
cmd = "#{self.class.sq_bin} blame #{target(path)}@#{identifier}"
cmd << credentials_string
blame = Annotate.new
shellout(cmd) do |io|
io.each_line do |line|
next unless line =~ %r{^\s*(\d+)\s*(\S+)\s(.*)$}
blame.add_line($3.rstrip, Revision.new(:identifier => $1.to_i, :author => $2.strip))
end
end
return nil if $? && $?.exitstatus != 0
blame
end
private
def credentials_string
str = ''
str << " --username #{shell_quote(@login)}" unless @login.blank?
str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
str << " --no-auth-cache --non-interactive"
str
end
# Helper that iterates over the child elements of a xml node
# MiniXml returns a hash when a single child is found or an array of hashes for multiple children
def each_xml_element(node, name)
if node && node[name]
if node[name].is_a?(Hash)
yield node[name]
else
node[name].each do |element|
yield element
end
end
end
end
def target(path = '')
base = path.match(/^\//) ? root_url : url
uri = "#{base}/#{path}"
uri = URI.escape(URI.escape(uri), '[]')
shell_quote(uri.gsub(/[?<>\*]/, ''))
end
end
end
end
end

View File

@ -1,186 +1,186 @@
# Redmine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
namespace :redmine do
namespace :email do
desc <<-END_DESC
Read an email from standard input.
General options:
unknown_user=ACTION how to handle emails from an unknown user
ACTION can be one of the following values:
ignore: email is ignored (default)
accept: accept as anonymous user
create: create a user account
no_permission_check=1 disable permission checking when receiving
the email
Issue attributes control options:
project=PROJECT identifier of the target project
status=STATUS name of the target status
tracker=TRACKER name of the target tracker
category=CATEGORY name of the target category
priority=PRIORITY name of the target priority
allow_override=ATTRS allow email content to override attributes
specified by previous options
ATTRS is a comma separated list of attributes
Examples:
# No project specified. Emails MUST contain the 'Project' keyword:
rake redmine:email:read RAILS_ENV="production" < raw_email
# Fixed project and default tracker specified, but emails can override
# both tracker and priority attributes:
rake redmine:email:read RAILS_ENV="production" \\
project=foo \\
tracker=bug \\
allow_override=tracker,priority < raw_email
END_DESC
task :read => :environment do
options = { :issue => {} }
%w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user']
options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check']
MailHandler.receive(STDIN.read, options)
end
desc <<-END_DESC
Read emails from an IMAP server.
General options:
unknown_user=ACTION how to handle emails from an unknown user
ACTION can be one of the following values:
ignore: email is ignored (default)
accept: accept as anonymous user
create: create a user account
no_permission_check=1 disable permission checking when receiving
the email
Available IMAP options:
host=HOST IMAP server host (default: 127.0.0.1)
port=PORT IMAP server port (default: 143)
ssl=SSL Use SSL? (default: false)
username=USERNAME IMAP account
password=PASSWORD IMAP password
folder=FOLDER IMAP folder to read (default: INBOX)
Issue attributes control options:
project=PROJECT identifier of the target project
status=STATUS name of the target status
tracker=TRACKER name of the target tracker
category=CATEGORY name of the target category
priority=PRIORITY name of the target priority
allow_override=ATTRS allow email content to override attributes
specified by previous options
ATTRS is a comma separated list of attributes
Processed emails control options:
move_on_success=MAILBOX move emails that were successfully received
to MAILBOX instead of deleting them
move_on_failure=MAILBOX move emails that were ignored to MAILBOX
Examples:
# No project specified. Emails MUST contain the 'Project' keyword:
rake redmine:email:receive_iamp RAILS_ENV="production" \\
host=imap.foo.bar username=redmine@example.net password=xxx
# Fixed project and default tracker specified, but emails can override
# both tracker and priority attributes:
rake redmine:email:receive_iamp RAILS_ENV="production" \\
host=imap.foo.bar username=redmine@example.net password=xxx ssl=1 \\
project=foo \\
tracker=bug \\
allow_override=tracker,priority
END_DESC
task :receive_imap => :environment do
imap_options = {:host => ENV['host'],
:port => ENV['port'],
:ssl => ENV['ssl'],
:username => ENV['username'],
:password => ENV['password'],
:folder => ENV['folder'],
:move_on_success => ENV['move_on_success'],
:move_on_failure => ENV['move_on_failure']}
options = { :issue => {} }
%w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user']
options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check']
Redmine::IMAP.check(imap_options, options)
end
desc <<-END_DESC
Read emails from an POP3 server.
Available POP3 options:
host=HOST POP3 server host (default: 127.0.0.1)
port=PORT POP3 server port (default: 110)
username=USERNAME POP3 account
password=PASSWORD POP3 password
apop=1 use APOP authentication (default: false)
delete_unprocessed=1 delete messages that could not be processed
successfully from the server (default
behaviour is to leave them on the server)
See redmine:email:receive_imap for more options and examples.
END_DESC
task :receive_pop3 => :environment do
pop_options = {:host => ENV['host'],
:port => ENV['port'],
:apop => ENV['apop'],
:username => ENV['username'],
:password => ENV['password'],
:delete_unprocessed => ENV['delete_unprocessed']}
options = { :issue => {} }
%w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user']
options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check']
Redmine::POP3.check(pop_options, options)
end
desc "Send a test email to the user with the provided login name"
task :test, [:login] => :environment do |task, args|
include Redmine::I18n
abort l(:notice_email_error, "Please include the user login to test with. Example: login=example-login") if args[:login].blank?
user = User.find_by_login(args[:login])
abort l(:notice_email_error, "User #{args[:login]} not found") unless user && user.logged?
ActionMailer::Base.raise_delivery_errors = true
begin
Mailer.deliver_test(User.current)
puts l(:notice_email_sent, user.mail)
rescue Exception => e
abort l(:notice_email_error, e.message)
end
end
end
end
# Redmine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
namespace :redmine do
namespace :email do
desc <<-END_DESC
Read an email from standard input.
General options:
unknown_user=ACTION how to handle emails from an unknown user
ACTION can be one of the following values:
ignore: email is ignored (default)
accept: accept as anonymous user
create: create a user account
no_permission_check=1 disable permission checking when receiving
the email
Issue attributes control options:
project=PROJECT identifier of the target project
status=STATUS name of the target status
tracker=TRACKER name of the target tracker
category=CATEGORY name of the target category
priority=PRIORITY name of the target priority
allow_override=ATTRS allow email content to override attributes
specified by previous options
ATTRS is a comma separated list of attributes
Examples:
# No project specified. Emails MUST contain the 'Project' keyword:
rake redmine:email:read RAILS_ENV="production" < raw_email
# Fixed project and default tracker specified, but emails can override
# both tracker and priority attributes:
rake redmine:email:read RAILS_ENV="production" \\
project=foo \\
tracker=bug \\
allow_override=tracker,priority < raw_email
END_DESC
task :read => :environment do
options = { :issue => {} }
%w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user']
options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check']
MailHandler.receive(STDIN.read, options)
end
desc <<-END_DESC
Read emails from an IMAP server.
General options:
unknown_user=ACTION how to handle emails from an unknown user
ACTION can be one of the following values:
ignore: email is ignored (default)
accept: accept as anonymous user
create: create a user account
no_permission_check=1 disable permission checking when receiving
the email
Available IMAP options:
host=HOST IMAP server host (default: 127.0.0.1)
port=PORT IMAP server port (default: 143)
ssl=SSL Use SSL? (default: false)
username=USERNAME IMAP account
password=PASSWORD IMAP password
folder=FOLDER IMAP folder to read (default: INBOX)
Issue attributes control options:
project=PROJECT identifier of the target project
status=STATUS name of the target status
tracker=TRACKER name of the target tracker
category=CATEGORY name of the target category
priority=PRIORITY name of the target priority
allow_override=ATTRS allow email content to override attributes
specified by previous options
ATTRS is a comma separated list of attributes
Processed emails control options:
move_on_success=MAILBOX move emails that were successfully received
to MAILBOX instead of deleting them
move_on_failure=MAILBOX move emails that were ignored to MAILBOX
Examples:
# No project specified. Emails MUST contain the 'Project' keyword:
rake redmine:email:receive_iamp RAILS_ENV="production" \\
host=imap.foo.bar username=redmine@example.net password=xxx
# Fixed project and default tracker specified, but emails can override
# both tracker and priority attributes:
rake redmine:email:receive_iamp RAILS_ENV="production" \\
host=imap.foo.bar username=redmine@example.net password=xxx ssl=1 \\
project=foo \\
tracker=bug \\
allow_override=tracker,priority
END_DESC
task :receive_imap => :environment do
imap_options = {:host => ENV['host'],
:port => ENV['port'],
:ssl => ENV['ssl'],
:username => ENV['username'],
:password => ENV['password'],
:folder => ENV['folder'],
:move_on_success => ENV['move_on_success'],
:move_on_failure => ENV['move_on_failure']}
options = { :issue => {} }
%w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user']
options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check']
Redmine::IMAP.check(imap_options, options)
end
desc <<-END_DESC
Read emails from an POP3 server.
Available POP3 options:
host=HOST POP3 server host (default: 127.0.0.1)
port=PORT POP3 server port (default: 110)
username=USERNAME POP3 account
password=PASSWORD POP3 password
apop=1 use APOP authentication (default: false)
delete_unprocessed=1 delete messages that could not be processed
successfully from the server (default
behaviour is to leave them on the server)
See redmine:email:receive_imap for more options and examples.
END_DESC
task :receive_pop3 => :environment do
pop_options = {:host => ENV['host'],
:port => ENV['port'],
:apop => ENV['apop'],
:username => ENV['username'],
:password => ENV['password'],
:delete_unprocessed => ENV['delete_unprocessed']}
options = { :issue => {} }
%w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user']
options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check']
Redmine::POP3.check(pop_options, options)
end
desc "Send a test email to the user with the provided login name"
task :test, [:login] => :environment do |task, args|
include Redmine::I18n
abort l(:notice_email_error, "Please include the user login to test with. Example: login=example-login") if args[:login].blank?
user = User.find_by_login(args[:login])
abort l(:notice_email_error, "User #{args[:login]} not found") unless user && user.logged?
ActionMailer::Base.raise_delivery_errors = true
begin
Mailer.deliver_test(User.current)
puts l(:notice_email_sent, user.mail)
rescue Exception => e
abort l(:notice_email_error, e.message)
end
end
end
end

View File

@ -1,24 +1,24 @@
desc 'Create YAML test fixtures from data in an existing database.
Defaults to development database. Set RAILS_ENV to override.'
task :extract_fixtures => :environment do
sql = "SELECT * FROM %s"
skip_tables = ["schema_info"]
ActiveRecord::Base.establish_connection
(ActiveRecord::Base.connection.tables - skip_tables).each do |table_name|
i = "000"
File.open("#{RAILS_ROOT}/#{table_name}.yml", 'w' ) do |file|
data = ActiveRecord::Base.connection.select_all(sql % table_name)
file.write data.inject({}) { |hash, record|
# cast extracted values
ActiveRecord::Base.connection.columns(table_name).each { |col|
record[col.name] = col.type_cast(record[col.name]) if record[col.name]
}
hash["#{table_name}_#{i.succ!}"] = record
hash
}.to_yaml
end
end
desc 'Create YAML test fixtures from data in an existing database.
Defaults to development database. Set RAILS_ENV to override.'
task :extract_fixtures => :environment do
sql = "SELECT * FROM %s"
skip_tables = ["schema_info"]
ActiveRecord::Base.establish_connection
(ActiveRecord::Base.connection.tables - skip_tables).each do |table_name|
i = "000"
File.open("#{RAILS_ROOT}/#{table_name}.yml", 'w' ) do |file|
data = ActiveRecord::Base.connection.select_all(sql % table_name)
file.write data.inject({}) { |hash, record|
# cast extracted values
ActiveRecord::Base.connection.columns(table_name).each { |col|
record[col.name] = col.type_cast(record[col.name]) if record[col.name]
}
hash["#{table_name}_#{i.succ!}"] = record
hash
}.to_yaml
end
end
end

View File

@ -1,24 +1,24 @@
# redMine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
desc 'Fetch changesets from the repositories'
namespace :redmine do
task :fetch_changesets => :environment do
Repository.fetch_changesets
end
end
# redMine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
desc 'Fetch changesets from the repositories'
namespace :redmine do
task :fetch_changesets => :environment do
Repository.fetch_changesets
end
end

View File

@ -1,35 +1,35 @@
desc 'Load Redmine default configuration data. Language is chosen interactively or by setting REDMINE_LANG environment variable.'
namespace :redmine do
task :load_default_data => :environment do
include Redmine::I18n
set_language_if_valid('en')
envlang = ENV['REDMINE_LANG']
if !envlang || !set_language_if_valid(envlang)
puts
while true
print "Select language: "
print valid_languages.collect(&:to_s).sort.join(", ")
print " [#{current_language}] "
STDOUT.flush
lang = STDIN.gets.chomp!
break if lang.empty?
break if set_language_if_valid(lang)
puts "Unknown language!"
end
STDOUT.flush
puts "===================================="
end
begin
Redmine::DefaultData::Loader.load(current_language)
puts "Default configuration data loaded."
rescue Redmine::DefaultData::DataAlreadyLoaded => error
puts error
rescue => error
puts "Error: " + error
puts "Default configuration data was not loaded."
end
end
end
desc 'Load Redmine default configuration data. Language is chosen interactively or by setting REDMINE_LANG environment variable.'
namespace :redmine do
task :load_default_data => :environment do
include Redmine::I18n
set_language_if_valid('en')
envlang = ENV['REDMINE_LANG']
if !envlang || !set_language_if_valid(envlang)
puts
while true
print "Select language: "
print valid_languages.collect(&:to_s).sort.join(", ")
print " [#{current_language}] "
STDOUT.flush
lang = STDIN.gets.chomp!
break if lang.empty?
break if set_language_if_valid(lang)
puts "Unknown language!"
end
STDOUT.flush
puts "===================================="
end
begin
Redmine::DefaultData::Loader.load(current_language)
puts "Default configuration data loaded."
rescue Redmine::DefaultData::DataAlreadyLoaded => error
puts error
rescue => error
puts "Error: " + error
puts "Default configuration data was not loaded."
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,15 @@
namespace :db do
desc 'Migrates installed plugins.'
task :migrate_plugins => :environment do
if Rails.respond_to?('plugins')
Rails.plugins.each do |plugin|
next unless plugin.respond_to?('migrate')
puts "Migrating #{plugin.name}..."
plugin.migrate
end
else
puts "Undefined method plugins for Rails!"
puts "Make sure engines plugin is installed."
end
end
end
namespace :db do
desc 'Migrates installed plugins.'
task :migrate_plugins => :environment do
if Rails.respond_to?('plugins')
Rails.plugins.each do |plugin|
next unless plugin.respond_to?('migrate')
puts "Migrating #{plugin.name}..."
plugin.migrate
end
else
puts "Undefined method plugins for Rails!"
puts "Make sure engines plugin is installed."
end
end
end

View File

@ -1,91 +1,91 @@
### From http://svn.geekdaily.org/public/rails/plugins/generally_useful/tasks/coverage_via_rcov.rake
namespace :test do
desc 'Measures test coverage'
task :coverage do
rm_f "coverage"
rm_f "coverage.data"
rcov = "rcov --rails --aggregate coverage.data --text-summary -Ilib --html"
files = Dir.glob("test/**/*_test.rb").join(" ")
system("#{rcov} #{files}")
system("open coverage/index.html") if PLATFORM['darwin']
end
desc 'Run unit and functional scm tests'
task :scm do
errors = %w(test:scm:units test:scm:functionals).collect do |task|
begin
Rake::Task[task].invoke
nil
rescue => e
task
end
end.compact
abort "Errors running #{errors.to_sentence(:locale => :en)}!" if errors.any?
end
namespace :scm do
namespace :setup do
desc "Creates directory for test repositories"
task :create_dir do
FileUtils.mkdir_p Rails.root + '/tmp/test'
end
supported_scms = [:subversion, :cvs, :bazaar, :mercurial, :git, :darcs, :filesystem]
desc "Creates a test subversion repository"
task :subversion => :create_dir do
repo_path = "tmp/test/subversion_repository"
system "svnadmin create #{repo_path}"
system "gunzip < test/fixtures/repositories/subversion_repository.dump.gz | svnadmin load #{repo_path}"
end
desc "Creates a test mercurial repository"
task :mercurial => :create_dir do
repo_path = "tmp/test/mercurial_repository"
bundle_path = "test/fixtures/repositories/mercurial_repository.hg"
system "hg init #{repo_path}"
system "hg -R #{repo_path} pull #{bundle_path}"
end
(supported_scms - [:subversion, :mercurial]).each do |scm|
desc "Creates a test #{scm} repository"
task scm => :create_dir do
### From http://svn.geekdaily.org/public/rails/plugins/generally_useful/tasks/coverage_via_rcov.rake
namespace :test do
desc 'Measures test coverage'
task :coverage do
rm_f "coverage"
rm_f "coverage.data"
rcov = "rcov --rails --aggregate coverage.data --text-summary -Ilib --html"
files = Dir.glob("test/**/*_test.rb").join(" ")
system("#{rcov} #{files}")
system("open coverage/index.html") if PLATFORM['darwin']
end
desc 'Run unit and functional scm tests'
task :scm do
errors = %w(test:scm:units test:scm:functionals).collect do |task|
begin
Rake::Task[task].invoke
nil
rescue => e
task
end
end.compact
abort "Errors running #{errors.to_sentence(:locale => :en)}!" if errors.any?
end
namespace :scm do
namespace :setup do
desc "Creates directory for test repositories"
task :create_dir do
FileUtils.mkdir_p Rails.root + '/tmp/test'
end
supported_scms = [:subversion, :cvs, :bazaar, :mercurial, :git, :darcs, :filesystem]
desc "Creates a test subversion repository"
task :subversion => :create_dir do
repo_path = "tmp/test/subversion_repository"
system "svnadmin create #{repo_path}"
system "gunzip < test/fixtures/repositories/subversion_repository.dump.gz | svnadmin load #{repo_path}"
end
desc "Creates a test mercurial repository"
task :mercurial => :create_dir do
repo_path = "tmp/test/mercurial_repository"
bundle_path = "test/fixtures/repositories/mercurial_repository.hg"
system "hg init #{repo_path}"
system "hg -R #{repo_path} pull #{bundle_path}"
end
(supported_scms - [:subversion, :mercurial]).each do |scm|
desc "Creates a test #{scm} repository"
task scm => :create_dir do
# system "gunzip < test/fixtures/repositories/#{scm}_repository.tar.gz | tar -xv -C tmp/test"
system "tar -xvz -C tmp/test -f test/fixtures/repositories/#{scm}_repository.tar.gz"
end
end
desc "Creates all test repositories"
task :all => supported_scms
end
desc "Updates installed test repositories"
task :update do
require 'fileutils'
Dir.glob("tmp/test/*_repository").each do |dir|
next unless File.basename(dir) =~ %r{^(.+)_repository$} && File.directory?(dir)
scm = $1
next unless fixture = Dir.glob("test/fixtures/repositories/#{scm}_repository.*").first
next if File.stat(dir).ctime > File.stat(fixture).mtime
FileUtils.rm_rf dir
Rake::Task["test:scm:setup:#{scm}"].execute
end
end
Rake::TestTask.new(:units => "db:test:prepare") do |t|
t.libs << "test"
t.verbose = true
t.test_files = FileList['test/unit/repository*_test.rb'] + FileList['test/unit/lib/redmine/scm/**/*_test.rb']
end
Rake::Task['test:scm:units'].comment = "Run the scm unit tests"
Rake::TestTask.new(:functionals => "db:test:prepare") do |t|
t.libs << "test"
t.verbose = true
t.test_files = FileList['test/functional/repositories*_test.rb']
end
Rake::Task['test:scm:functionals'].comment = "Run the scm functional tests"
end
end
end
end
desc "Creates all test repositories"
task :all => supported_scms
end
desc "Updates installed test repositories"
task :update do
require 'fileutils'
Dir.glob("tmp/test/*_repository").each do |dir|
next unless File.basename(dir) =~ %r{^(.+)_repository$} && File.directory?(dir)
scm = $1
next unless fixture = Dir.glob("test/fixtures/repositories/#{scm}_repository.*").first
next if File.stat(dir).ctime > File.stat(fixture).mtime
FileUtils.rm_rf dir
Rake::Task["test:scm:setup:#{scm}"].execute
end
end
Rake::TestTask.new(:units => "db:test:prepare") do |t|
t.libs << "test"
t.verbose = true
t.test_files = FileList['test/unit/repository*_test.rb'] + FileList['test/unit/lib/redmine/scm/**/*_test.rb']
end
Rake::Task['test:scm:units'].comment = "Run the scm unit tests"
Rake::TestTask.new(:functionals => "db:test:prepare") do |t|
t.libs << "test"
t.verbose = true
t.test_files = FileList['test/functional/repositories*_test.rb']
end
Rake::Task['test:scm:functionals'].comment = "Run the scm functional tests"
end
end

View File

@ -1,11 +1,11 @@
class Version < ActiveRecord::Base
generator_for :name, :method => :next_name
generator_for :status => 'open'
def self.next_name
@last_name ||= 'Version 1.0.0'
@last_name.succ!
@last_name
end
end
class Version < ActiveRecord::Base
generator_for :name, :method => :next_name
generator_for :status => 'open'
def self.next_name
@last_name ||= 'Version 1.0.0'
@last_name.succ!
@last_name
end
end

View File

@ -1,48 +1,48 @@
Return-Path: <JSmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
In-Reply-To: <chiliproject.issue-2.20060719210421@osiris>
From: "John Smith" <JSmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: Re: update to issue 2
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
An update to the issue by the sender.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris --- Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
>> > --- Reply above. Do not remove this line. ---
>> >
>> > Issue #6779 has been updated by Eric Davis.
>> >
>> > Subject changed from Projects with JSON to Project JSON API
>> > Status changed from New to Assigned
>> > Assignee set to Eric Davis
>> > Priority changed from Low to Normal
>> > Estimated time deleted (1.00)
>> >
>> > Looks like the JSON api for projects was missed. I'm going to be
>> > reviewing the existing APIs and trying to clean them up over the next
>> > few weeks.
Return-Path: <JSmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
In-Reply-To: <chiliproject.issue-2.20060719210421@osiris>
From: "John Smith" <JSmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: Re: update to issue 2
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
An update to the issue by the sender.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris --- Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
>> > --- Reply above. Do not remove this line. ---
>> >
>> > Issue #6779 has been updated by Eric Davis.
>> >
>> > Subject changed from Projects with JSON to Project JSON API
>> > Status changed from New to Assigned
>> > Assignee set to Eric Davis
>> > Priority changed from Low to Normal
>> > Estimated time deleted (1.00)
>> >
>> > Looks like the JSON api for projects was missed. I'm going to be
>> > reviewing the existing APIs and trying to clean them up over the next
>> > few weeks.

View File

@ -1,48 +1,48 @@
Return-Path: <JSmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
In-Reply-To: <chiliproject.issue-2.20060719210421@osiris>
From: "John Smith" <JSmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: Re: update to issue 2
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
An update to the issue by the sender.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris --- Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
> --- Reply above. Do not remove this line. ---
>
> Issue #6779 has been updated by Eric Davis.
>
> Subject changed from Projects with JSON to Project JSON API
> Status changed from New to Assigned
> Assignee set to Eric Davis
> Priority changed from Low to Normal
> Estimated time deleted (1.00)
>
> Looks like the JSON api for projects was missed. I'm going to be
> reviewing the existing APIs and trying to clean them up over the next
> few weeks.
Return-Path: <JSmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
In-Reply-To: <chiliproject.issue-2.20060719210421@osiris>
From: "John Smith" <JSmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: Re: update to issue 2
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
An update to the issue by the sender.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris --- Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
> --- Reply above. Do not remove this line. ---
>
> Issue #6779 has been updated by Eric Davis.
>
> Subject changed from Projects with JSON to Project JSON API
> Status changed from New to Assigned
> Assignee set to Eric Davis
> Priority changed from Low to Normal
> Estimated time deleted (1.00)
>
> Looks like the JSON api for projects was missed. I'm going to be
> reviewing the existing APIs and trying to clean them up over the next
> few weeks.

View File

@ -1,17 +1,17 @@
Return-Path: <john.doe@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
To: <redmine@somenet.foo>
Subject: Ticket by unknown user
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
This is a ticket submitted by an unknown user.
Return-Path: <john.doe@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
To: <redmine@somenet.foo>
Subject: Ticket by unknown user
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
This is a ticket submitted by an unknown user.

View File

@ -1,18 +1,18 @@
Return-Path: <john.doe@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
From: "John Doe" <john.doe@somenet.foo>
To: <redmine@somenet.foo>
Subject: Ticket by unknown user
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
This is a ticket submitted by an unknown user.
Return-Path: <john.doe@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
From: "John Doe" <john.doe@somenet.foo>
To: <redmine@somenet.foo>
Subject: Ticket by unknown user
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
This is a ticket submitted by an unknown user.

View File

@ -1,60 +1,60 @@
Return-Path: <JSmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
From: "John Smith" <JSmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: New ticket on a given project
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris --- Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
--- This line starts with a delimiter and should not be stripped
This paragraph is before delimiters.
BREAK
This paragraph is between delimiters.
---
This paragraph is after the delimiter so it shouldn't appear.
Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque
sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem.
Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et,
dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed,
massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo
pulvinar dui, a gravida orci mi eget odio. Nunc a lacus.
Project: onlinestore
Status: Resolved
due date: 2010-12-31
Start Date:2010-01-01
Assigned to: John Smith
fixed version: alpha
estimated hours: 2.5
done ratio: 30
Return-Path: <JSmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
From: "John Smith" <JSmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: New ticket on a given project
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris --- Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
--- This line starts with a delimiter and should not be stripped
This paragraph is before delimiters.
BREAK
This paragraph is between delimiters.
---
This paragraph is after the delimiter so it shouldn't appear.
Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque
sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem.
Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et,
dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed,
massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo
pulvinar dui, a gravida orci mi eget odio. Nunc a lacus.
Project: onlinestore
Status: Resolved
due date: 2010-12-31
Start Date:2010-01-01
Assigned to: John Smith
fixed version: alpha
estimated hours: 2.5
done ratio: 30

View File

@ -1,74 +1,74 @@
Return-Path: <jsmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sat, 21 Jun 2008 18:41:39 +0200
Message-ID: <006a01c8d3bd$ad9baec0$0a00a8c0@osiris>
In-Reply-To: <chiliproject.issue-2.20060719210421@osiris>
From: "John Smith" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
References: <485d0ad366c88_d7014663a025f@osiris.tmail>
Subject: Re: Add ingredients categories
Date: Sat, 21 Jun 2008 18:41:39 +0200
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_NextPart_000_0067_01C8D3CE.711F9CC0"
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
This is a multi-part message in MIME format.
------=_NextPart_000_0067_01C8D3CE.711F9CC0
Content-Type: text/plain;
charset="utf-8"
Content-Transfer-Encoding: quoted-printable
This is reply
------=_NextPart_000_0067_01C8D3CE.711F9CC0
Content-Type: text/html;
charset="utf-8"
Content-Transfer-Encoding: quoted-printable
=EF=BB=BF<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; charset=3Dutf-8">
<STYLE>BODY {
FONT-SIZE: 0.8em; COLOR: #484848; FONT-FAMILY: Verdana, sans-serif
}
BODY H1 {
FONT-SIZE: 1.2em; MARGIN: 0px; FONT-FAMILY: "Trebuchet MS", Verdana, =
sans-serif
}
A {
COLOR: #2a5685
}
A:link {
COLOR: #2a5685
}
A:visited {
COLOR: #2a5685
}
A:hover {
COLOR: #c61a1a
}
A:active {
COLOR: #c61a1a
}
HR {
BORDER-RIGHT: 0px; BORDER-TOP: 0px; BACKGROUND: #ccc; BORDER-LEFT: 0px; =
WIDTH: 100%; BORDER-BOTTOM: 0px; HEIGHT: 1px
}
.footer {
FONT-SIZE: 0.8em; FONT-STYLE: italic
}
</STYLE>
<META content=3D"MSHTML 6.00.2900.2883" name=3DGENERATOR></HEAD>
<BODY bgColor=3D#ffffff>
<DIV><SPAN class=3Dfooter><FONT face=3DArial color=3D#000000 =
size=3D2>This is=20
reply</FONT></DIV></SPAN></BODY></HTML>
------=_NextPart_000_0067_01C8D3CE.711F9CC0--
Return-Path: <jsmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sat, 21 Jun 2008 18:41:39 +0200
Message-ID: <006a01c8d3bd$ad9baec0$0a00a8c0@osiris>
In-Reply-To: <chiliproject.issue-2.20060719210421@osiris>
From: "John Smith" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
References: <485d0ad366c88_d7014663a025f@osiris.tmail>
Subject: Re: Add ingredients categories
Date: Sat, 21 Jun 2008 18:41:39 +0200
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_NextPart_000_0067_01C8D3CE.711F9CC0"
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
This is a multi-part message in MIME format.
------=_NextPart_000_0067_01C8D3CE.711F9CC0
Content-Type: text/plain;
charset="utf-8"
Content-Transfer-Encoding: quoted-printable
This is reply
------=_NextPart_000_0067_01C8D3CE.711F9CC0
Content-Type: text/html;
charset="utf-8"
Content-Transfer-Encoding: quoted-printable
=EF=BB=BF<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; charset=3Dutf-8">
<STYLE>BODY {
FONT-SIZE: 0.8em; COLOR: #484848; FONT-FAMILY: Verdana, sans-serif
}
BODY H1 {
FONT-SIZE: 1.2em; MARGIN: 0px; FONT-FAMILY: "Trebuchet MS", Verdana, =
sans-serif
}
A {
COLOR: #2a5685
}
A:link {
COLOR: #2a5685
}
A:visited {
COLOR: #2a5685
}
A:hover {
COLOR: #c61a1a
}
A:active {
COLOR: #c61a1a
}
HR {
BORDER-RIGHT: 0px; BORDER-TOP: 0px; BACKGROUND: #ccc; BORDER-LEFT: 0px; =
WIDTH: 100%; BORDER-BOTTOM: 0px; HEIGHT: 1px
}
.footer {
FONT-SIZE: 0.8em; FONT-STYLE: italic
}
</STYLE>
<META content=3D"MSHTML 6.00.2900.2883" name=3DGENERATOR></HEAD>
<BODY bgColor=3D#ffffff>
<DIV><SPAN class=3Dfooter><FONT face=3DArial color=3D#000000 =
size=3D2>This is=20
reply</FONT></DIV></SPAN></BODY></HTML>
------=_NextPart_000_0067_01C8D3CE.711F9CC0--

View File

@ -1,248 +1,248 @@
Return-Path: <jsmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sat, 21 Jun 2008 15:53:25 +0200
Message-ID: <002301c8d3a6$2cdf6950$0a00a8c0@osiris>
From: "John Smith" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: Ticket created by email with attachment
Date: Sat, 21 Jun 2008 15:53:25 +0200
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="----=_NextPart_000_001F_01C8D3B6.F05C5270"
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
This is a multi-part message in MIME format.
------=_NextPart_000_001F_01C8D3B6.F05C5270
Content-Type: multipart/alternative;
boundary="----=_NextPart_001_0020_01C8D3B6.F05C5270"
------=_NextPart_001_0020_01C8D3B6.F05C5270
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
This is a new ticket with attachments
------=_NextPart_001_0020_01C8D3B6.F05C5270
Content-Type: text/html;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 6.00.2900.2883" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<DIV><FONT face=3DArial size=3D2>This is&nbsp; a new ticket with=20
attachments</FONT></DIV></BODY></HTML>
------=_NextPart_001_0020_01C8D3B6.F05C5270--
------=_NextPart_000_001F_01C8D3B6.F05C5270
Content-Type: image/jpeg;
name="Paella.jpg"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="Paella.jpg"
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
cbis+/WpUqUcMZKdF44n/9k=
------=_NextPart_000_001F_01C8D3B6.F05C5270--
Return-Path: <jsmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sat, 21 Jun 2008 15:53:25 +0200
Message-ID: <002301c8d3a6$2cdf6950$0a00a8c0@osiris>
From: "John Smith" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: Ticket created by email with attachment
Date: Sat, 21 Jun 2008 15:53:25 +0200
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="----=_NextPart_000_001F_01C8D3B6.F05C5270"
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
This is a multi-part message in MIME format.
------=_NextPart_000_001F_01C8D3B6.F05C5270
Content-Type: multipart/alternative;
boundary="----=_NextPart_001_0020_01C8D3B6.F05C5270"
------=_NextPart_001_0020_01C8D3B6.F05C5270
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
This is a new ticket with attachments
------=_NextPart_001_0020_01C8D3B6.F05C5270
Content-Type: text/html;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 6.00.2900.2883" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<DIV><FONT face=3DArial size=3D2>This is&nbsp; a new ticket with=20
attachments</FONT></DIV></BODY></HTML>
------=_NextPart_001_0020_01C8D3B6.F05C5270--
------=_NextPart_000_001F_01C8D3B6.F05C5270
Content-Type: image/jpeg;
name="Paella.jpg"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="Paella.jpg"
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
cbis+/WpUqUcMZKdF44n/9k=
------=_NextPart_000_001F_01C8D3B6.F05C5270--

View File

@ -1,47 +1,47 @@
Return-Path: <jsmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
From: "John Smith" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: New ticket on a given project
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque
sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem.
Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et,
dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed,
massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo
pulvinar dui, a gravida orci mi eget odio. Nunc a lacus.
Project: onlinestore
Tracker: Feature request
category: Stock management
assigned to: miscuser9@foo.bar
priority: foo
done ratio: x
start date: some day
due date: never
Return-Path: <jsmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
From: "John Smith" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: New ticket on a given project
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque
sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem.
Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et,
dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed,
massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo
pulvinar dui, a gravida orci mi eget odio. Nunc a lacus.
Project: onlinestore
Tracker: Feature request
category: Stock management
assigned to: miscuser9@foo.bar
priority: foo
done ratio: x
start date: some day
due date: never

View File

@ -1,43 +1,43 @@
Return-Path: <jsmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
From: "John Smith" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: New ticket on a given project
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque
sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem.
Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et,
dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed,
massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo
pulvinar dui, a gravida orci mi eget odio. Nunc a lacus.
Projet: onlinestore
Tracker: Feature request
catégorie: Stock management
priorité: Urgent
Return-Path: <jsmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
From: "John Smith" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: New ticket on a given project
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque
sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem.
Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et,
dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed,
massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo
pulvinar dui, a gravida orci mi eget odio. Nunc a lacus.
Projet: onlinestore
Tracker: Feature request
catégorie: Stock management
priorité: Urgent

View File

@ -1,57 +1,57 @@
Return-Path: <JSmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
From: "John Smith" <JSmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: New ticket on a given project with a very long subject line which exceeds 255 chars and should not be ignored but chopped off. And if the subject line is still not long enough, we just add more text. And more text. Wow, this is really annoying. Especially, if you have nothing to say...
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris --- Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
--- This line starts with a delimiter and should not be stripped
This paragraph is before delimiters.
BREAK
This paragraph is between delimiters.
---
This paragraph is after the delimiter so it shouldn't appear.
Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque
sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem.
Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et,
dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed,
massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo
pulvinar dui, a gravida orci mi eget odio. Nunc a lacus.
Project: onlinestore
Status: Resolved
due date: 2010-12-31
Start Date:2010-01-01
Assigned to: John Smith
Return-Path: <JSmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
From: "John Smith" <JSmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: New ticket on a given project with a very long subject line which exceeds 255 chars and should not be ignored but chopped off. And if the subject line is still not long enough, we just add more text. And more text. Wow, this is really annoying. Especially, if you have nothing to say...
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris --- Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
--- This line starts with a delimiter and should not be stripped
This paragraph is before delimiters.
BREAK
This paragraph is between delimiters.
---
This paragraph is after the delimiter so it shouldn't appear.
Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque
sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem.
Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et,
dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed,
massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo
pulvinar dui, a gravida orci mi eget odio. Nunc a lacus.
Project: onlinestore
Status: Resolved
due date: 2010-12-31
Start Date:2010-01-01
Assigned to: John Smith