129 Commits

Author SHA1 Message Date
Thomas Keller
464c1a8ef5 Fix / adapt the side-by-side review diff rendering. 2011-10-09 03:14:43 +02:00
Thomas Keller
e5b10a8494 Merge branch 'develop' into feature.diff-whitespace 2011-10-09 02:25:49 +02:00
Thomas Keller
ffc49b9ea6 Merge branch 'develop' of projects.ceondo.com:indefero into develop 2011-10-09 02:25:03 +02:00
Thomas Keller
0efc14dd6f The autocompleter for the issue relations feature broke the review view, do'h 2011-10-09 02:22:43 +02:00
Thomas Keller
4fb15ccb7d 6abd0b6f made problems in reviews, need to be reworked anyways in a few. 2011-10-09 02:13:49 +02:00
Thomas Keller
234b70845c Render the TAB char wider, so it is easier recognized as tab 2011-10-09 02:04:13 +02:00
Thomas Keller
6abd0b6faa - Move common static methods out of IDF_Diff and into IDF_FileUtil.
- Make stuff that should be private in IDF_Diff really private
  and comment out a test that was the only call path for a previously
  public method.
- Apply the whitespace emphasizing on the normal file view as well
  and get finally rid of padLine()
2011-10-09 01:54:51 +02:00
Thomas Keller
fef2bd15bf Merge branch 'develop' into feature.diff-whitespace 2011-10-09 01:54:05 +02:00
Thomas Keller
74d07d8fb8 Ignore a couple of file patterns more and sort the list for easier reading. 2011-10-09 01:53:37 +02:00
Thomas Keller
473e9153ed Merge branch 'develop' into feature.diff-whitespace 2011-10-09 01:40:59 +02:00
Thomas Keller
efa10c9afd Properly quote file names for the system call. 2011-10-09 01:39:05 +02:00
Thomas Keller
7438a2bf19 Improve the rendering of the control characters greatly by using
the Unicode counterparts in th 0x24## plane and add a little bit
of explanation what we are doing there and why we are doing this.
2011-10-09 00:55:42 +02:00
Thomas Keller
2e0995abac Fixed rendering in Firefox which did, unlike Chrome, not expand the last
row's height to fit the up-popping horizontal scrollbar, but all rows
just a little, so the heights did not match. I've reworked this to not
used the ill-advised rowspan any longer, but two separate tables whose
heights match each other now in both browsers.

Also I fixed a bug in the whitespace detection code - utf8 characters
where broken into single bytes, so apparently the [:print:] character
class does not accout for them, even in //u mode, so we're selecting
the characters that we want to make visible on our own (basically
control characters lower than space, I might add more).
2011-10-09 00:13:34 +02:00
Thomas Keller
c84afd0f78 Some git diffs (most likely octopus merges) start with diff --cc,
so we have to detect and stop log parsing when this occurs as well.
2011-10-09 00:08:16 +02:00
Thomas Keller
b1276dff6c Merge branch 'develop' of projects.ceondo.com:indefero into feature.diff-whitespace 2011-10-08 19:25:08 +02:00
Thomas Keller
b413b7ee89 Improve the calculation part and reuse the values we have from the diff hunks. 2011-10-06 02:31:36 +02:00
Thomas Keller
f19f07ec59 Change the unidiff rendering by letting the actual content be rendered
into a separate container that can overflow and side-scroll for long lines.

This effectively removes the need for all kinds of line-breaking hacks
that have been applied before and only worked when the browser was
actually able to break a word group apart somewhere.

Lines are now always rendered as-is; as a nice side effect the line numbers
are always visible, independently how far one scrolled into one direction,
so the context is always clear. If the rendering area is made smaller, the
table rendering also degrades gracefully and provides horizontal scrolling
for views that did not need them before.

The size that is occupied by the number display is now also automatically
determined by the size that is needed to render the biggest line number
in a column. Empty columns are rendered with a zero size.

Currently all this works nicely with a recent version of Chrome, Firefox
still needs some fine tuning for the vertical positioning. Other browsers
are untested as of now.
2011-10-06 02:06:51 +02:00
Thomas Keller
83761c66c5 The left floating breaks the vertical alignment of the unprintable characters. Remove that workaround, which was introduced to wrap very long lines; it should be replaced by something smarter. 2011-10-06 00:00:07 +02:00
Thomas Keller
d0e2977746 Add support for line ending detection in our diff parser.
Line endings are now preserved during the diff parsing. When the diff
is then rendered later on, we replace non-printable characters by their
ordinal counterparts, so a user can easily grasp changes when a hunk
comes with edits that might be invisible at first.

The expected format for the diff test suite has changed from serialized
PHP to var_export, which is easier readable, editable and understandable,
while still keeping parsable as well.

Support for old Macintosh line endings could not be added, mainly because
modern SCMs do not support single \r in their unified diff output either
and working around and parsing these "lines" would have been a major
headache with not much outcome (given the fact that all Macs that have been
sold since 2001 or 2002 have been BSD-based and as such used Unix line
endings by default).

This commit fixes issue 636.
2011-10-05 02:44:04 +02:00
Thomas Keller
1be91e5a2a Do not split Hg's and git's log output into lines - this will make it
impossible for us to detect proper line endings later on.
2011-10-05 02:42:51 +02:00
William MARTIN
15d4d1aa7d Fix issue 713 : Wiki page have now a css for printer output 2011-10-04 22:45:51 +02:00
William MARTIN
708b90fccd Fix url /p/aaa/issues/status/open/ 2011-10-04 21:45:23 +02:00
William MARTIN
ac7a4c4aa5 Update NEWS 2011-10-04 21:29:11 +02:00
William MARTIN
b90246a239 Merge branch 'feature.issue-of-others' into develop 2011-10-04 20:57:18 +02:00
Thomas Keller
a9d327d54f Note that issues can now be filtered in the search view. 2011-10-04 12:50:51 +02:00
Thomas Keller
eb8fd0aa55 Merge branch 'feature.search-filter' of projects.ceondo.com:indefero into develop 2011-10-04 12:46:28 +02:00
William MARTIN
813184f06c Fix an issue with "unasigned issues"
Rename the view to userIssues
2011-10-04 10:05:42 +02:00
Thomas Keller
ef2d3a9af9 Merge branch 'feature.issue-of-others' of projects.ceondo.com:indefero into feature.issue-of-others 2011-10-04 01:01:31 +02:00
Thomas Keller
9a8bd464a3 Remove a couple of unused calls to get a list of project tags; these
calls are currently done from within the tags-cloud template most of
the time, which is ugly, but the way it works as of now.
2011-10-04 00:47:31 +02:00
Thomas Keller
9e2ea7404b Tags are now returned grouped by class, I seem to have missed this call
when I changed the API...
2011-10-04 00:46:48 +02:00
Thomas Keller
160d11b89b Properly initialize arrays with 0 for IN conditions 2011-10-04 00:37:28 +02:00
William MARTIN
d860f299fd The last part of the previous patch. 2011-10-03 10:17:10 +02:00
William MARTIN
33882d4fa7 Update how the myIssue view works.
It's allow to display this view for other members.
In the issue summary, we can now follow make links for each user display in the part "Unresolved: By Assignee".
2011-10-03 10:00:35 +02:00
Thomas Keller
85978a4d18 Implement basic filtering capabilities in the issue search view.
Since IDF's text search component does not allow further restrictions
on the result set, we make a second, filtered query to restrict to
the item state ('open' or 'closed') and optionally a label. All in
all this is all harder than it could be, especially the tag cloud
is very monolithic and should be replaced by a data-driven component
that is less dependent on a single data / query and link usage, but
this would for now require too many changes.

Similar questionable is the code duplication for the index, listStatus
and listLabel view implementations that all do more or less the same.
The search implementation now only uses one implementation for a very
similar use case. It also removes the artificial restriction to 100
results we had previously there and does not query a record for each
single result (as was done with Pluf_Search_ResultSet previously).

On my way through this I tried to generalize a couple of i18n texts
and removed smaller issues like the "trailing comma" in label lists.

This partially fixes issue 548.
2011-10-03 01:54:01 +02:00
Thomas Keller
e1e7696d53 Add Stéphane to AUTHORS. 2011-10-02 00:10:14 +02:00
Thomas Keller
695428075b Merge branch 'develop' of projects.ceondo.com:indefero into develop 2011-10-02 00:06:17 +02:00
Thomas Keller
e7c2e721b4 The tag cloud was fixed to "issues" for both, the open and closed issue list
which is clearly wrong.
2011-10-02 00:05:08 +02:00
William MARTIN
13fad756ab Fix issue 732
Commit based on Stéphane Baron patch
2011-10-01 22:43:00 +02:00
Thomas Keller
1f0791df0e Make the '@rev' part in the regex optional (fixes issue 730). 2011-09-12 17:54:40 +02:00
Thomas Keller
a2c832a130 Improve the 'parents' parsing for git and ignore any empty parts; also
react gracefully if we could not parse the parents for some weird reason.
2011-08-17 20:17:14 +02:00
Thomas Keller
b17de014ec Reworked the option / argument handling in the SVN interface to
have less code duplication.
2011-08-13 02:28:15 +02:00
Thomas Keller
b1b72190e1 Dropped a few more not needed files. 2011-08-13 01:19:58 +02:00
Thomas Keller
2ff2f888bc - Make the SVN test case work without specific test configuration.
- Rename the test repo to match the test function so we can create
  more test repositories for other tests at a later stage.
- Remove useless hooks and configs from the repo (they are not used
  for our specific test and just need memory).
- Note the fix for issue 721 in NEWS.mdtext.
2011-08-13 01:11:00 +02:00
Patrick Georgi
57c2389aae Make SVN backend more robust
The SVN backend failed when trying to access historical information on deleted files.

There's also an initial test case for the SVN backend, testing this issue
and issue 364, which is about a similar problem for renamed files.
Reverting any of these fixes breaks the test.
2011-08-12 20:53:26 +02:00
Thomas Keller
d54c86f813 Note the change from issue 716. 2011-07-27 20:06:56 +02:00
Thomas Keller
05a9697007 Merge branch 'feature.content-md5' into develop 2011-07-27 19:59:18 +02:00
Patrick Georgi
945429abf0 Provide MD5 value of downloads to HTTP client
Content-MD5 is a HTTP header to provide end-to-end integrity checks
(see RFC2616, 14.15). This doesn't protect against malicious
modifications, but against transmissions errors and storage errors
on the server.

The change also removes one redirect when downloading files.
2011-07-24 22:12:36 +02:00
William MARTIN
a016bcb51b Merge branch 'develop' of projects.ceondo.com:indefero into develop 2011-07-05 11:31:05 +02:00
William MARTIN
f2b1ce795c Fix issue 247 : cron overwrites authorized_keys during cron run 2011-07-05 11:30:23 +02:00
Thomas Keller
3a8c56acc4 Postgres needs a VARCHAR cast, which MySQL doesn't understand, of
course. *sigh*
2011-07-01 13:35:43 +02:00
Thomas Keller
7b2552f940 Postgres (and probably others as well) needs an explicit char cast. 2011-06-30 00:25:29 +02:00
Thomas Keller
324b202215 Fix the rendering of issue changes in a mail template and the issue feed fragment. 2011-06-29 17:41:18 +02:00
Loïc d'Anterroches
2c2da6082a Fixed stupid missing semicolon. 2011-06-29 14:41:57 +02:00
Loïc d'Anterroches
dd3fbbd7e4 Fixes to support older PHP versions. 2011-06-29 14:30:17 +02:00
William MARTIN
9bbcd571ec Merge branch 'feature.issue-summary' into develop 2011-06-20 11:37:26 +02:00
William MARTIN
bbc185bf3b Add unit test for IDF_Project::getIssueCountByOwner 2011-06-20 11:34:42 +02:00
Thomas Keller
d1bcdcda20 Fix the mtn getChanges() test. 2011-06-17 23:50:35 +02:00
William MARTIN
6d55602ef3 Add IDF_Project::getIssueCountByOwner and use it into IDF_Views_Issue::summary 2011-06-15 17:30:23 +02:00
William MARTIN
6e7c9f7c4b Use css instead of   2011-06-15 17:29:29 +02:00
William MARTIN
5427aab456 Change "ifnull" sql function to "coalesce" which is supported by postgresql 2011-06-15 13:52:58 +02:00
Thomas Keller
4879d64989 If git's author name does not contain valid utf-8 bytes, skip the author
lookup in the database, which would otherwise only bring up errors.
2011-06-15 13:50:02 +02:00
William MARTIN
dab8ea63fc Mark string for translation 2011-06-15 11:35:30 +02:00
William MARTIN
b03d7a04a0 improve rendering of 0% bar 2011-06-15 11:30:11 +02:00
William MARTIN
ef5b93e3f7 Fix the special case of a empty issue tracker, for the issue summary. 2011-06-15 11:18:41 +02:00
William MARTIN
69ae1c08ef Add an sub-tab under Issue to add a summury view 2011-06-15 09:11:47 +02:00
Thomas Keller
8e4f828cc6 Spelling. 2011-06-13 12:43:39 +02:00
Thomas Keller
c4f92f4569 Access all private methods in a non-static way, now that we have a instance-dependent _diagnoseProblem() method 2011-06-13 12:23:22 +02:00
Thomas Keller
c4d2b99656 Wrong object to call commit() on... 2011-06-13 01:59:21 +02:00
Thomas Keller
d4fe88adab Disable the fixed context bar if the content is already to big 2011-06-12 17:59:43 +02:00
Thomas Keller
69d0e8313a Merge branch 'develop' of projects.ceondo.com:indefero into develop 2011-06-11 00:48:38 +02:00
Thomas Keller
11a234e135 Fix the branch links users of the Subversion frontend get when they enter
a wrong revision and only display this list if there are any branches
available for all SCMs.
2011-06-11 00:47:54 +02:00
Thomas Keller
2f30e4e2f6 Fix a regression introduced with commit 20c3f14cc: If we can read from a process'
pipe doesn't mean the exit code of the process in question is zero, this is
actually what pclose() returns.
2011-06-11 00:44:32 +02:00
Thomas Keller
118ca9f11f Now that IDF_Scm::getChanges() is implemented for all SCMs, there is no need for the try-catch anymore. 2011-06-10 23:40:58 +02:00
Thomas Keller
24fc41ee0d XML node access didn't work and as such renames and copies weren't properly detected. 2011-06-10 23:40:22 +02:00
William MARTIN
c00dbac5e0 Merge branch 'feature.download-md5' into develop 2011-06-10 09:26:20 +02:00
William MARTIN
d7857c5126 Rename the migration script 2011-06-10 09:25:17 +02:00
Thomas Keller
ac6be0d3c0 Implement IDF_Scm::getChanges() for Subversion (closes issue 622) 2011-06-10 02:23:54 +02:00
Thomas Keller
7ff6f09f67 We can actually differentiate between copies and renames in Hg, so lets
add support for another change type, 'copies'. The previous implementation
for Hg was also slightly flawed in the way that it mixed sources with
targets.
2011-06-10 01:34:33 +02:00
Thomas Keller
00b576c5a3 Merge branch 'feature.issue-links' into develop 2011-06-10 00:39:39 +02:00
Thomas Keller
2a33510c96 \w doesn't match -, so better use [^)] to match server names 2011-06-06 23:24:00 +02:00
Thomas Keller
d1f79d906d Do not try to symlink the same file twice (fixes issue 695) 2011-06-06 23:18:06 +02:00
Thomas Keller
aa043f14ac Two occurrences forgotten 2011-06-06 23:01:25 +02:00
Thomas Keller
638b28399e Fix issues 695 and 697. Still, public keys aren't added to monotone
databases for some weird reason, so issue 696 is still open.
2011-06-05 02:37:47 +02:00
Thomas Keller
1d86f036a3 Add a warning to not configure a global key for all monotone
instances in usher.conf.
2011-06-05 01:31:21 +02:00
Thomas Keller
2f6e0f0a22 Include examples how to setup ACLs for different platforms. 2011-06-05 01:13:00 +02:00
Thomas Keller
1b1b00a10c The server list no longer renders a bogus server entry in case no
monotone server has been configured yet in the connected usher instance.
Also display a short info text if the server or connection list for
a running monotone server is empty.
2011-06-04 23:15:58 +02:00
Thomas Keller
8693418d39 Revert "Improve error handling and reporting (partially resolves issue 695)"
This reverts commit 8ff15368ce.
2011-06-04 01:14:22 +02:00
Thomas Keller
8ff15368ce Improve error handling and reporting (partially resolves issue 695) 2011-06-04 01:03:30 +02:00
Thomas Keller
32cde534bd Fix some errors in the German translation and unify some of the strings.
Thanks to Daniel Steudler <steudlerdaniel@gmail.com> for some of the
pointers. Fixes issue 694.
2011-06-04 00:31:12 +02:00
Thomas Keller
c0e26133bd Keep the context area in the view if a view scrolls down several pages. 2011-06-02 02:04:52 +02:00
Thomas Keller
592c2ff9ff Several f'ups in hg's parseLog() method 2011-06-02 01:21:32 +02:00
Thomas Keller
30efd0a2db Merge branch 'develop' of projects.ceondo.com:indefero into develop 2011-06-02 00:55:17 +02:00
Thomas Keller
20c3f14cc8 git and hg diff views did not show empty context lines, a regression
from the commit(s) from issue 633. The diff parser assumed a properly
formatted diff that denotes empty context lines with a single space in
the first column. This single space however was missing, because the
hg and git backends got the diff through PHP's exec() function and
this returns already line-splitted output, but - and this is the actual
problem - removes trailing whitespace at the end of each line, essentially
making " \n" only "\n". When splitting this string now again with
PREG_SPLIT_NO_EMPTY the empty line was completely lost in the diff output.

To make it clear that an empty line does not mark a context line now, but
should stop the diff parsing, the Diff parser now also defaults to 'false'
as line type.

This commit fixes issue 688.
2011-06-02 00:48:38 +02:00
Thomas Keller
80313c88c8 Properly reverse-sort git's tag list again; the rsort on the original output
of the command looked bogus: since each tag ref is prefixed by an iso date and
since commit 94da55d1 by hash, the whole string was of course sorted by this
date and later hash, but not the tag name. This should now make more sense.
2011-06-02 00:38:14 +02:00
Thomas Keller
cab1c09ffc Note that issue 559 has been fixed with the fix for issue 686. 2011-06-02 00:08:13 +02:00
William MARTIN
c421919092 Fix php syntax 2011-06-01 11:20:03 +02:00
William MARTIN
d350876bc1 Update allowed attributes in the Markdown Prefilter, to be less strict.
Fix issue 578
2011-06-01 10:10:01 +02:00
William MARTIN
de09c8af56 Merge branch 'develop' of projects.ceondo.com:indefero into develop 2011-05-31 15:40:14 +02:00
William MARTIN
998f4576fe Fix div collision 2011-05-31 15:39:46 +02:00
William MARTIN
06c57f7da6 Set the SQL type to VARCHAR(32)
Process the md5 of each file during the migrationUpdate all IDF_Upload object to process their md5 during the migration
2011-05-31 14:53:44 +02:00
William MARTIN
4d5418a601 Fix migration methods name 2011-05-31 14:01:39 +02:00
William MARTIN
95cc7f627f Process a md5 on the uploaded file, and display it in the per file view.
- The uploader can check that the uploaded file is correct
- The downloader can check his download too
2011-05-31 13:58:12 +02:00
Thomas Keller
2aab4eea3b Apply (and adapt) the patch from issue 364, which should fix the issue. 2011-05-31 13:41:37 +02:00
Thomas Keller
5bbff9f5a6 Rename the "Go to revision" button in the SVN views to "Switch", so it
doesn't break into the next line.
2011-05-31 13:39:55 +02:00
Thomas Keller
b5fcf1636a String improvements. 2011-05-31 13:34:28 +02:00
Thomas Keller
13012be5d7 Merge branch 'develop' of projects.ceondo.com:indefero into develop 2011-05-31 13:14:21 +02:00
Thomas Keller
ca2ef814fb Add support for the display of monotone node attributes.
(also fix the way the attributes view is displayed, i.e. do not
 use single strings, but one string in context which is much easier
 to translate)
2011-05-31 13:11:55 +02:00
William MARTIN
b9407f6aee Add some nobr, to keep the text formated when the message is long 2011-05-31 11:06:39 +02:00
Thomas Keller
9bcb5f9456 Fix a PHP notice / Pluf exception in case any issue type field is cleared. 2011-05-31 10:51:24 +02:00
Thomas Keller
f412099f69 Fix whitespace issues in the timeline. 2011-05-30 14:48:36 +02:00
Thomas Keller
0aa5999bb3 Finalize ticket relations (closes issue 638)
- IssueUpdate.php: use dynamically set field validators for dynamically
  created fields; let relation_type0 and relation_issue0 exist at any time;
  check the validity of a user selection and combine the various input fields
  if possible; do the database updates for links; change the "change" format
  for labels to a more precise structure and no longer trust on a leading
  dash for removed labels
- IssueCreate.php: change the validator calls and field names
- Issue.php (getGroupedRelatedIssues): make it possible to return only a
  flat list of integers for easier processing
- 17AddIssueRelations.php: migrate the previous serialized "changes" format
  for issue comments to the new, more structured format (up and down)
- js-autocomplete.html: add support for multiple input fields
- view.html: output relation changes and wrap the related issues stanzas into
  paragraphs
- NEWS.mdtext: note the addition and the need for a specific version of Pluf
2011-05-30 14:02:10 +02:00
Thomas Keller
16dda0743c Basic storage of relations for new issues has been done; the relations
are also properly displayed at the left side in the issue's detail view.
2011-05-28 23:48:00 +02:00
Thomas Keller
d079838818 Improve a translation 2011-05-28 01:36:25 +02:00
Thomas Keller
81ce4688df Let the SVN command line client not store the login credentials we give him. 2011-05-28 01:05:10 +02:00
Thomas Keller
ee33cc1832 Address files by name rather than ID in the downloads section (issue 686) 2011-05-28 00:48:24 +02:00
Thomas Keller
82bb18fe10 Merge branch 'release-1.1' into develop 2011-05-26 10:51:16 +02:00
Thomas Keller
8987ca7db6 Timezone always comes last 2011-05-26 10:46:12 +02:00
Thomas Keller
8066fd8982 Start 1.1.3 development 2011-05-26 10:46:00 +02:00
William MARTIN
a96d8c05a7 Update the NEWS file and set the version number 2011-05-26 09:57:05 +02:00
William MARTIN
4238a5dd50 Update PO file from transifex 2011-05-26 09:56:25 +02:00
William MARTIN
94da55d15e Fix tags extraction from git repository (issue 675) 2011-05-26 09:56:04 +02:00
Thomas Keller
09979b8551 Rewrite log parsing in Mercurial and fix whitespace bugs on the way:
- tags and branches with spaces are now properly parsed (issue 663)
- use --style instead of --template option of hg to include much more
  output for the log command; use that to query the parent revisions of
  a revision (if any) and also to query the actual changes of a revision
- make the log parser much more robust towards changes in the "header"
  of the log output
2011-05-25 23:26:14 +02:00
Thomas Keller
5b82efa0be Fix a couple of issues with our key parsing / validating code.
- be explicit and expect only ssh-dss or ssh-rsa keys
- allow any character (even line breaks and whitespace) in the optional comment,
  but shrink all of them to simple spaces (fixes issue 679)
- test the newly uploaded key against existing keys only by the base key data,
  not the fully uploaded string (that might contain a changed comment line or
  the like) to avoid duplicates; also only check the keys of the user for
  duplicates, not all existing keys in the forge (if for whatever reason two
  user accounts share a key)
2011-05-25 02:13:50 +02:00
Thomas Keller
8502a36481 Mark two new strings in the latest updates view for translation;
prettify the RSS icon display somewhat.
2011-05-16 23:49:08 +02:00
Thomas Keller
bcba64b2a1 Basic model files added. 2011-05-10 17:22:32 +02:00
Thomas Keller
e40d922eef Add two targets to easily install and update the IDF database 2011-05-10 17:21:56 +02:00
Thomas Keller
7e226b43d3 More work on the issue relation infrastructure
- actually query data for the incoming query
- exclude the current issue from being linked with itself
- allow multiple issues to be given in the second input field
- add the form fields to the ticket update view as well
2011-05-10 16:21:29 +02:00
William MARTIN
bbf1a1882a Fix issue 671 : Update the SSH Key preg pattern 2011-05-04 11:24:05 +02:00
Thomas Keller
5322cdf609 Fix an URL in the RSS feed 2011-04-27 15:25:18 +02:00
127 changed files with 11451 additions and 1635 deletions

19
.gitignore vendored
View File

@@ -1,14 +1,19 @@
*~ *~
tmp .buildpath
.externalToolBuilders
.project
.settings
.tx/config
attachments
indefero-*.zip
src/IDF/conf/idf.php src/IDF/conf/idf.php
src/IDF/conf/idf.test.php src/IDF/conf/idf.test.php
www/test.php
www/media/upload
src/IDF/gettexttemplates
indefero-*.zip
src/IDF/conf/path.php src/IDF/conf/path.php
.tx/config src/IDF/gettexttemplates
src/IDF/locale/idf.pot.bak src/IDF/locale/idf.pot.bak
test/config.php
test/test.db test/test.db
test/tmp test/tmp
test/config.php tmp
www/media/upload
www/test.php

View File

@@ -12,6 +12,7 @@ Much appreciated contributors (in alphabetical order):
Brian Armstrong <brianar> Brian Armstrong <brianar>
Charles Melbye <charlie@yourwiki.net> Charles Melbye <charlie@yourwiki.net>
Ciaran Gultnieks <ciaran@ciarang.com> Ciaran Gultnieks <ciaran@ciarang.com>
Daniel Steudler <steudlerdaniel@gmail.com>
David Feeney <davidf> David Feeney <davidf>
Denis Kot <denis.kot@gmail.com> - Russian translation Denis Kot <denis.kot@gmail.com> - Russian translation
Dmitry Dulepov <dmitryd> Dmitry Dulepov <dmitryd>
@@ -32,6 +33,7 @@ Much appreciated contributors (in alphabetical order):
Samuel Suther <info@suther.de> - German translation Samuel Suther <info@suther.de> - German translation
Sindre R. Myren <sindrero@stud.ntnu.no> Sindre R. Myren <sindrero@stud.ntnu.no>
Stewart Platt <stew@futurete.ch> Stewart Platt <stew@futurete.ch>
Stéphane Baron <sbaron>
Thomas Keller <me@thomaskeller.biz> - Monotone support Thomas Keller <me@thomaskeller.biz> - Monotone support
Vladimir Solomatin <slash> Vladimir Solomatin <slash>
William Martin <william.martin@lcpc.fr> William Martin <william.martin@lcpc.fr>

View File

@@ -37,6 +37,10 @@ help:
@printf "\tpo-push - Send the all PO files to the transifex server.\n" @printf "\tpo-push - Send the all PO files to the transifex server.\n"
@printf "\tpo-pull - Get all PO files from the transifex server.\n" @printf "\tpo-pull - Get all PO files from the transifex server.\n"
@printf "\tpo-stats - Show translation statistics of all PO files.\n" @printf "\tpo-stats - Show translation statistics of all PO files.\n"
@printf "\nMisc Rules :\n";
@printf "\tdb-install - Install the database schema.\n"
@printf "\tdb-update - Update the database schema.\n"
# #
# Internationalization rule, POT & PO file manipulation # Internationalization rule, POT & PO file manipulation
@@ -139,3 +143,8 @@ po-stats:
> indefero-$(@:-zipfile=)-`git log $(@:-zipfile=) -n 1 \ > indefero-$(@:-zipfile=)-`git log $(@:-zipfile=) -n 1 \
--pretty=format:%h`.zip --pretty=format:%h`.zip
db-install:
@cd src && php $(PLUF_PATH)/migrate.php --conf=IDF/conf/idf.php -a -d -i
db-update:
@cd src && php $(PLUF_PATH)/migrate.php --conf=IDF/conf/idf.php -a -d

View File

@@ -1,17 +1,76 @@
# InDefero 1.2 - xxx xxx xx xx:xx 2011 UTC # InDefero 1.2 - xxx xxx xx xx:xx 2011 UTC
ATTENTION: You need Pluf [324ae60b](http://projects.ceondo.com/p/pluf/source/commit/324ae60b)
or newer to properly run this version of Indefero!
## New Features ## New Features
## Bugfixes - Indefero's issue tracker can now bi-directionally link issues with variable, configurable
terms, such as "is related to", "is blocked by" or "is duplicated by" (issue 638)
- When you search for issues, the results can further be refined by issue state (open or closed)
and label (partially implements issue 548)
- Mercurial source views now show parent revisions (if any) and detailed change information
- Subversion source views now show detailed change information (issue 622)
- File download URLs now contain the file name rather than the upload id; old links still work though (issues 559 and 686)
- Display monotone file and directory attributes in the tree and file view
(needs a monotone with an interface version of 13.1 or newer)
- The context area is now kept in view when a page scrolls down several pages
- Add a summary section to the issue tracker with statistics about open/close issues,
tags of open issue, and count of open tickets for each owner.
- Improved home page with an customizable icon for each project.
- The download section provide MD5 for each files.
- Wiki page have now a css for printer output (issue 713)
## Bugfixes
- The SVN interface acts more robust if an underlying repository has been restructured (issues 364 and 721)
- monotone zip archive entries now all carry the revision date as mtime (issue 645) - monotone zip archive entries now all carry the revision date as mtime (issue 645)
- Timeline only displays filter options for items a user has actually access to (issue 655) - Timeline only displays filter options for items a user has actually access to (issue 655)
- Fix the self-link of the RSS feed (issue 666) - The log, tags and branches parsers for Mercurial are more robust now (issue 663)
- Fix SSH public key parsing issues and improve the check for existing, uploaded keys (issue 679)
- Diff views now show empty context lines for git and hg again (issue 688)
- Let the SVN command line client not store the login credentials we give him as arguments
- The usher section in the forge administration no longer displays a bogus
server enty in case no monotone server is configured in the connected
usher instance
- Prevent a timeout from popping up when Usher is restarted (issue 695)
- The SyncMonotone plugin now cleans up partial artifacts it created during the addition of
a new project or monotone key, in case an error popped up in the middle (issue 697)
- Indefero now sends the MD5 checksum as HTTP header when downloading a file from the
download area. Additionally, a unneeded redirect has been removed. (issue 716)
- Source links without a specific revision did not work due to a wrong regex (issue 730)
- Better error detection and reporting in the SyncMonotone plugin
ATTENTION: This needs Pluf 46b7f251 or newer!
- Fix the branch links users of the Subversion frontend get when they enter a wrong revision
and only display this list if there are any branches available for all SCMs
- If git's author name is not encoded in an UTF-8 compatible encoding, skip the author lookup,
as we have no information what the author string is actually encoded in
- Indefero no longer displays an empty parents paragraph in the commit view for root revisions of
a git repository
- Indefero now only shows the tags of the closed and not the open issues in the closed issues list
- Avatar URL generation use correctly the configuration (issue 732)
- Git cron job doesn't erase anymore manually added keys (issue 247)
## Documentation ## Documentation
- The documentation on the setup of the monotone plugin has been improved.
## Translations ## Translations
# InDefero 1.1.2 - Thu May 26 07:42:25 2011 UTC
## Bugfixes
- Fix tags extraction from git repository (issue 675)
- Fix SSH validation method (issue 671)
- Fix malformed URL in the RSS (issue 666)
- Fix validateRevision call for Mercurial Scm (issue 657)
## Translations
- Missing word in French translation (issue 672)
- Update Spanish translation
# InDefero 1.1.1 - Mon Mar 28 15:52 2011 UTC # InDefero 1.1.1 - Mon Mar 28 15:52 2011 UTC
## Bugfixes ## Bugfixes

View File

@@ -25,7 +25,7 @@ If you install monotone from source (<http://monotone.ca/downloads.php>),
please follow the `INSTALL` document which comes with the software. please follow the `INSTALL` document which comes with the software.
It contains detailed instructions, including all needed dependencies. It contains detailed instructions, including all needed dependencies.
## Choose your indefero setup ## Choose your indefero (IDF) setup
The monotone plugin can be used in several different ways: The monotone plugin can be used in several different ways:
@@ -112,14 +112,40 @@ The monotone plugin can be used in several different ways:
^D ^D
$ chmod 600 usher.conf $ chmod 600 usher.conf
**ATTENTION:** Do _not_ configure a default monotone server key in `usher.conf`,
otherwise the individual server key that IDF creates for each project is
not used and this could cause problems.
Your indefero www user needs later write access to `usher.conf` and Your indefero www user needs later write access to `usher.conf` and
`projects/`. There are two ways of setting this up: `projects/`. There are two ways of setting this up:
* Make the usher user the web user, for example via Apache's `suexec` * Make the usher user the web user, for example via Apache's `suexec`.
* Use acls, like this: This is however a bit clumsy.
* Preferred: Use Access Control Lists (ACLs), like this:
#
# Linux
#
$ setfacl -m u:www:rw usher.conf $ setfacl -m u:www:rw usher.conf
$ setfacl -m d:u:www:rwx projects/ $ setfacl -m d:u:www:rwx projects/
$ setfacl -m d:u:usher:rwx projects/
#
# FreeBSD
#
$ setfacl -m user:www:rw::allow usher.conf
$ setfacl -m user:www:rwxp:fd:allow projects/
$ setfacl -m user:usher:rwxp:fd:allow projects/
#
# Mac OS X
#
chmod +a '_www allow read,write' usher.conf
chmod +a '_www allow read,write,delete,add_file,add_subdirectory,file_inherit,directory_inherit' projects/
chmod +a 'usher allow read,write,delete,add_file,add_subdirectory,file_inherit,directory_inherit' projects/
In each example's last line, `usher` is the user which is executing
the usher instance. **It is very important to add this line, otherwise
usher won't be able to read and write into the initial file system
setup IDF creates!**
5. Wrap a daemonizer around usher, for example supervise from daemontools 5. Wrap a daemonizer around usher, for example supervise from daemontools
(<http://cr.yp.to/damontools.html>): (<http://cr.yp.to/damontools.html>):

View File

@@ -35,9 +35,7 @@ class IDF_Diff
public function __construct($diff, $path_strip_level = 0) public function __construct($diff, $path_strip_level = 0)
{ {
$this->path_strip_level = $path_strip_level; $this->path_strip_level = $path_strip_level;
// this works because in unified diff format even empty lines are $this->lines = IDF_FileUtil::splitIntoLines($diff, true);
// either prefixed with a '+', '-' or ' '
$this->lines = preg_split("/\015\012|\015|\012/", $diff, -1, PREG_SPLIT_NO_EMPTY);
} }
public function parse() public function parse()
@@ -66,12 +64,12 @@ class IDF_Diff
} }
// use new file name by default // use new file name by default
preg_match("/^\+\+\+ ([^\t]+)/", $newfileline, $m); preg_match("/^\+\+\+ ([^\t\n\r]+)/", $newfileline, $m);
$current_file = $m[1]; $current_file = $m[1];
if ($current_file === '/dev/null') { if ($current_file === '/dev/null') {
// except if it's /dev/null, use the old one instead // except if it's /dev/null, use the old one instead
// eg. mtn 0.48 and newer // eg. mtn 0.48 and newer
preg_match("/^--- ([^\t]+)/", $oldfileline, $m); preg_match("/^--- ([^\t\r\n]+)/", $oldfileline, $m);
$current_file = $m[1]; $current_file = $m[1];
} }
if ($this->path_strip_level > 0) { if ($this->path_strip_level > 0) {
@@ -101,11 +99,12 @@ class IDF_Diff
$files[$current_file]['chunks'][] = array(); $files[$current_file]['chunks'][] = array();
while ($i < $diffsize && ($addlines >= 0 || $dellines >= 0)) { while ($i < $diffsize && ($addlines >= 0 || $dellines >= 0)) {
$linetype = $this->lines[$i] != '' ? $this->lines[$i][0] : ' '; $linetype = $this->lines[$i] != '' ? $this->lines[$i][0] : false;
$content = substr($this->lines[$i], 1);
switch ($linetype) { switch ($linetype) {
case ' ': case ' ':
$files[$current_file]['chunks'][$current_chunk][] = $files[$current_file]['chunks'][$current_chunk][] =
array($delstart, $addstart, substr($this->lines[$i++], 1)); array($delstart, $addstart, $content);
$dellines--; $dellines--;
$addlines--; $addlines--;
$delstart++; $delstart++;
@@ -113,23 +112,26 @@ class IDF_Diff
break; break;
case '+': case '+':
$files[$current_file]['chunks'][$current_chunk][] = $files[$current_file]['chunks'][$current_chunk][] =
array('', $addstart, substr($this->lines[$i++], 1)); array('', $addstart, $content);
$addlines--; $addlines--;
$addstart++; $addstart++;
break; break;
case '-': case '-':
$files[$current_file]['chunks'][$current_chunk][] = $files[$current_file]['chunks'][$current_chunk][] =
array($delstart, '', substr($this->lines[$i++], 1)); array($delstart, '', $content);
$dellines--; $dellines--;
$delstart++; $delstart++;
break; break;
case '\\': case '\\':
// ignore newline handling for now, see issue 636 // no new line at the end of this file; remove pseudo new line from last line
$i++; $cur = count($files[$current_file]['chunks'][$current_chunk]) - 1;
$files[$current_file]['chunks'][$current_chunk][$cur][2] =
rtrim($files[$current_file]['chunks'][$current_chunk][$cur][2], "\r\n");
continue; continue;
default: default:
break 2; break 2;
} }
$i++;
} }
$current_chunk++; $current_chunk++;
} }
@@ -144,46 +146,80 @@ class IDF_Diff
public function as_html() public function as_html()
{ {
$out = ''; $out = '';
foreach ($this->files as $filename=>$file) { foreach ($this->files as $filename => $file) {
$pretty = ''; $pretty = '';
$fileinfo = IDF_FileUtil::getMimeType($filename); $fileinfo = IDF_FileUtil::getMimeType($filename);
if (IDF_FileUtil::isSupportedExtension($fileinfo[2])) { if (IDF_FileUtil::isSupportedExtension($fileinfo[2])) {
$pretty = ' prettyprint'; $pretty = ' prettyprint';
} }
$out .= "\n".'<table class="diff" summary="">'."\n";
$out .= '<tr id="diff-'.md5($filename).'"><th colspan="3">'.Pluf_esc($filename).'</th></tr>'."\n";
$cc = 1; $cc = 1;
$offsets = array();
$contents = array();
foreach ($file['chunks'] as $chunk) { foreach ($file['chunks'] as $chunk) {
foreach ($chunk as $line) { foreach ($chunk as $line) {
if ($line[0] and $line[1]) { list($left, $right, $content) = $line;
$class = 'diff-c'; if ($left and $right) {
} elseif ($line[0]) { $class = 'context';
$class = 'diff-r'; } elseif ($left) {
$class = 'removed';
} else { } else {
$class = 'diff-a'; $class = 'added';
} }
$line_content = self::padLine(Pluf_esc($line[2]));
$out .= sprintf('<tr class="diff-line"><td class="diff-lc">%s</td><td class="diff-lc">%s</td><td class="%s%s mono">%s</td></tr>'."\n", $line[0], $line[1], $class, $pretty, $line_content); $offsets[] = sprintf('<td>%s</td><td>%s</td>', $left, $right);
$content = IDF_FileUtil::emphasizeControlCharacters(Pluf_esc($content));
$contents[] = sprintf('<td class="%s%s mono">%s</td>', $class, $pretty, $content);
}
if (count($file['chunks']) > $cc) {
$offsets[] = '<td class="next">...</td><td class="next">...</td>';
$contents[] = '<td class="next"></td>';
} }
if (count($file['chunks']) > $cc)
$out .= '<tr class="diff-next"><td>...</td><td>...</td><td>&nbsp;</td></tr>'."\n";
$cc++; $cc++;
} }
$out .= '</table>';
}
return Pluf_Template::markSafe($out);
}
public static function padLine($line) list($added, $removed) = end($file['chunks_def']);
{
$line = str_replace("\t", ' ', $line); $added = $added[0] + $added[1];
$n = strlen($line); $leftwidth = 1;
for ($i=0;$i<$n;$i++) { if ($added > 0)
if (substr($line, $i, 1) != ' ') { $leftwidth = ((ceil(log10($added)) + 1) * 8) + 12;
break;
} $removed = $removed[0] + $removed[1];
$rightwidth = 1;
if ($removed > 0)
$rightwidth = ((ceil(log10($removed)) + 1) * 8) + 12;
$inner_linecounts =
'<table class="diff-linecounts">' ."\n".
'<colgroup><col width="'.$leftwidth.'" /><col width="'. $rightwidth.'" /></colgroup>' ."\n".
'<tr class="line">' .
implode('</tr>'."\n".'<tr class="line">', $offsets).
'</tr>' ."\n".
'</table>' ."\n";
$inner_contents =
'<table class="diff-contents">' ."\n".
'<tr class="line">' .
implode('</tr>'."\n".'<tr class="line">', $contents) .
'</tr>' ."\n".
'</table>' ."\n";
$out .= '<table class="diff unified">' ."\n".
'<colgroup><col width="'.($leftwidth + $rightwidth + 1).'" /><col width="*" /></colgroup>' ."\n".
'<tr id="diff-'.md5($filename).'">'.
'<th colspan="2">'.Pluf_esc($filename).'</th>'.
'</tr>' ."\n".
'<tr>' .
'<td>'. $inner_linecounts .'</td>'. "\n".
'<td><div class="scroll">'. $inner_contents .'</div></td>'.
'</tr>' ."\n".
'</table>' ."\n";
} }
return str_repeat('&nbsp;', $i).substr($line, $i);
return Pluf_Template::markSafe($out);
} }
/** /**
@@ -208,12 +244,12 @@ class IDF_Diff
*/ */
public function fileCompare($orig, $chunks, $filename, $context=10) public function fileCompare($orig, $chunks, $filename, $context=10)
{ {
$orig_lines = preg_split("/\015\012|\015|\012/", $orig); $orig_lines = IDF_FileUtil::splitIntoLines($orig);
$new_chunks = $this->mergeChunks($orig_lines, $chunks, $context); $new_chunks = $this->mergeChunks($orig_lines, $chunks, $context);
return $this->renderCompared($new_chunks, $filename); return $this->renderCompared($new_chunks, $filename);
} }
public function mergeChunks($orig_lines, $chunks, $context=10) private function mergeChunks($orig_lines, $chunks, $context=10)
{ {
$spans = array(); $spans = array();
$new_chunks = array(); $new_chunks = array();
@@ -310,38 +346,115 @@ class IDF_Diff
return $nnew_chunks; return $nnew_chunks;
} }
public function renderCompared($chunks, $filename) private function renderCompared($chunks, $filename)
{ {
$fileinfo = IDF_FileUtil::getMimeType($filename); $fileinfo = IDF_FileUtil::getMimeType($filename);
$pretty = ''; $pretty = '';
if (IDF_FileUtil::isSupportedExtension($fileinfo[2])) { if (IDF_FileUtil::isSupportedExtension($fileinfo[2])) {
$pretty = ' prettyprint'; $pretty = ' prettyprint';
} }
$out = '';
$cc = 1; $cc = 1;
$i = 0; $left_offsets = array();
$left_contents = array();
$right_offsets = array();
$right_contents = array();
$max_lineno_left = $max_lineno_right = 0;
foreach ($chunks as $chunk) { foreach ($chunks as $chunk) {
foreach ($chunk as $line) { foreach ($chunk as $line) {
$line1 = '&nbsp;'; $left = '';
$line2 = '&nbsp;'; $right = '';
$line[2] = (strlen($line[2])) ? self::padLine(Pluf_esc($line[2])) : '&nbsp;'; $content = IDF_FileUtil::emphasizeControlCharacters(Pluf_esc($line[2]));
if ($line[0] and $line[1]) { if ($line[0] and $line[1]) {
$class = 'diff-c'; $class = 'context';
$line1 = $line2 = $line[2]; $left = $right = $content;
} elseif ($line[0]) { } elseif ($line[0]) {
$class = 'diff-r'; $class = 'removed';
$line1 = $line[2]; $left = $content;
} else { } else {
$class = 'diff-a'; $class = 'added';
$line2 = $line[2]; $right = $content;
} }
$out .= sprintf('<tr class="diff-line"><td class="diff-lc">%s</td><td class="%s mono%s"><code>%s</code></td><td class="diff-lc">%s</td><td class="%s mono%s"><code>%s</code></td></tr>'."\n", $line[0], $class, $pretty, $line1, $line[1], $class, $pretty, $line2);
$left_offsets[] = sprintf('<td>%s</td>', $line[0]);
$right_offsets[] = sprintf('<td>%s</td>', $line[1]);
$left_contents[] = sprintf('<td class="%s%s mono">%s</td>', $class, $pretty, $left);
$right_contents[] = sprintf('<td class="%s%s mono">%s</td>', $class, $pretty, $right);
$max_lineno_left = max($max_lineno_left, $line[0]);
$max_lineno_right = max($max_lineno_right, $line[1]);
}
if (count($chunks) > $cc) {
$left_offsets[] = '<td class="next">...</td>';
$right_offsets[] = '<td class="next">...</td>';
$left_contents[] = '<td></td>';
$right_contents[] = '<td></td>';
} }
if (count($chunks) > $cc)
$out .= '<tr class="diff-next"><td>...</td><td>&nbsp;</td><td>...</td><td>&nbsp;</td></tr>'."\n";
$cc++; $cc++;
$i++;
} }
$leftwidth = 1;
if ($max_lineno_left > 0)
$leftwidth = ((ceil(log10($max_lineno_left)) + 1) * 8) + 12;
$rightwidth = 1;
if ($max_lineno_right > 0)
$rightwidth = ((ceil(log10($max_lineno_right)) + 1) * 8) + 12;
$inner_linecounts_left =
'<table class="diff-linecounts">' ."\n".
'<colgroup><col width="'.$leftwidth.'" /></colgroup>' ."\n".
'<tr class="line">' .
implode('</tr>'."\n".'<tr class="line">', $left_offsets).
'</tr>' ."\n".
'</table>' ."\n";
$inner_linecounts_right =
'<table class="diff-linecounts">' ."\n".
'<colgroup><col width="'.$rightwidth.'" /></colgroup>' ."\n".
'<tr class="line">' .
implode('</tr>'."\n".'<tr class="line">', $right_offsets).
'</tr>' ."\n".
'</table>' ."\n";
$inner_contents_left =
'<table class="diff-contents">' ."\n".
'<tr class="line">' .
implode('</tr>'."\n".'<tr class="line">', $left_contents) .
'</tr>' ."\n".
'</table>' ."\n";
$inner_contents_right =
'<table class="diff-contents">' ."\n".
'<tr class="line">' .
implode('</tr>'."\n".'<tr class="line">', $right_contents) .
'</tr>' ."\n".
'</table>' ."\n";
$out =
'<table class="diff context">' ."\n".
'<colgroup>' .
'<col width="'.($leftwidth + 1).'" /><col width="*" />' .
'<col width="'.($rightwidth + 1).'" /><col width="*" />' .
'</colgroup>' ."\n".
'<tr id="diff-'.md5($filename).'">'.
'<th colspan="4">'.Pluf_esc($filename).'</th>'.
'</tr>' ."\n".
'<tr>' .
'<th colspan="2">'.__('Old').'</th><th colspan="2">'.__('New').'</th>' .
'</tr>'.
'<tr>' .
'<td>'. $inner_linecounts_left .'</td>'. "\n".
'<td><div class="scroll">'. $inner_contents_left .'</div></td>'. "\n".
'<td>'. $inner_linecounts_right .'</td>'. "\n".
'<td><div class="scroll">'. $inner_contents_right .'</div></td>'. "\n".
'</tr>' ."\n".
'</table>' ."\n";
return Pluf_Template::markSafe($out); return Pluf_Template::markSafe($out);
} }
} }

View File

@@ -65,9 +65,9 @@ class IDF_FileUtil
} }
$table = array(); $table = array();
$i = 1; $i = 1;
foreach (preg_split("/\015\012|\015|\012/", $content) as $line) { foreach (self::splitIntoLines($content) as $line) {
$table[] = '<tr class="c-line"><td class="code-lc" id="L'.$i.'"><a href="#L'.$i.'">'.$i.'</a></td>' $table[] = '<tr class="c-line"><td class="code-lc" id="L'.$i.'"><a href="#L'.$i.'">'.$i.'</a></td>'
.'<td class="code mono'.$pretty.'">'.IDF_Diff::padLine(Pluf_esc($line)).'</td></tr>'; .'<td class="code mono'.$pretty.'">'.self::emphasizeControlCharacters(Pluf_esc($line)).'</td></tr>';
$i++; $i++;
} }
return Pluf_Template::markSafe(implode("\n", $table)); return Pluf_Template::markSafe(implode("\n", $table));
@@ -143,6 +143,56 @@ class IDF_FileUtil
return $res; return $res;
} }
/**
* Splits a string into separate lines while retaining the individual
* line ending character for every line.
*
* OS9 line endings are not supported.
*
* @param string content
* @param boolean if true, skip completely empty lines
* @return string
*/
public static function splitIntoLines($content, $skipEmpty = false)
{
$flags = PREG_SPLIT_OFFSET_CAPTURE;
if ($skipEmpty) $flags |= PREG_SPLIT_NO_EMPTY;
$splitted = preg_split("/\r\n|\n/", $content, -1, $flags);
$last_off = -1;
$lines = array();
while (($split = array_shift($splitted)) !== null) {
if ($last_off != -1) {
$lines[] .= substr($content, $last_off, $split[1] - $last_off);
}
$last_off = $split[1];
}
$lines[] = substr($content, $last_off);
return $lines;
}
/**
* This translates most of the C0 ASCII control characters into
* their visual counterparts in the 0x24## unicode plane
* (http://en.wikipedia.org/wiki/C0_and_C1_control_codes).
*
* We could add DEL (0x7F) to this set, but unfortunately this
* is not nicely mapped to 0x247F in the control plane, but 0x2421
* and adding an if expression below just for this is a little bit
* of a hassle. And of course, the more esoteric ones from C1 are
* missing as well...
*
* @param string $content
* @return string
*/
public static function emphasizeControlCharacters($content)
{
return preg_replace(
'/([\x00-\x1F])/ue',
'"<span class=\"ctrl-char\" title=\"0x".bin2hex("\\1")."\">&#x24".bin2hex("\\1")."</span>"',
$content);
}
/** /**
* Find if a given mime type is a text file. * Find if a given mime type is a text file.
* This uses the output of the self::getMimeType function. * This uses the output of the self::getMimeType function.

View File

@@ -36,6 +36,7 @@ class IDF_Form_IssueCreate extends Pluf_Form
public $user = null; public $user = null;
public $project = null; public $project = null;
public $show_full = false; public $show_full = false;
public $relation_types = null;
public function initFields($extra=array()) public function initFields($extra=array())
{ {
@@ -45,9 +46,12 @@ class IDF_Form_IssueCreate extends Pluf_Form
or $this->user->hasPerm('IDF.project-member', $this->project)) { or $this->user->hasPerm('IDF.project-member', $this->project)) {
$this->show_full = true; $this->show_full = true;
} }
$this->relation_types = $this->project->getRelationsFromConfig();
$contentTemplate = $this->project->getConf()->getVal( $contentTemplate = $this->project->getConf()->getVal(
'labels_issue_template', IDF_Form_IssueTrackingConf::init_template 'labels_issue_template', IDF_Form_IssueTrackingConf::init_template
); );
$this->fields['summary'] = new Pluf_Form_Field_Varchar( $this->fields['summary'] = new Pluf_Form_Field_Varchar(
array('required' => true, array('required' => true,
'label' => __('Summary'), 'label' => __('Summary'),
@@ -109,15 +113,14 @@ class IDF_Form_IssueCreate extends Pluf_Form
), ),
)); ));
$relation_types = $extra['project']->getRelationsFromConfig(); $this->fields['relation_type0'] = new Pluf_Form_Field_Varchar(
$this->fields['relation_type'] = new Pluf_Form_Field_Varchar(
array('required' => false, array('required' => false,
'label' => __('This issue'), 'label' => __('This issue'),
'initial' => $relation_types[0], 'initial' => current($this->relation_types),
'widget_attrs' => array('size' => 15), 'widget_attrs' => array('size' => 15),
)); ));
$this->fields['relation_issue'] = new Pluf_Form_Field_Varchar( $this->fields['relation_issue0'] = new Pluf_Form_Field_Varchar(
array('required' => false, array('required' => false,
'label' => null, 'label' => null,
'initial' => '', 'initial' => '',
@@ -250,6 +253,63 @@ class IDF_Form_IssueCreate extends Pluf_Form
return $this->cleaned_data['status']; return $this->cleaned_data['status'];
} }
// this method is not called from Pluf_Form directly, but shared for
// among all similar fields
function clean_relation_type($value)
{
$relation_type = trim($value);
if (empty($relation_type))
return '';
$found = false;
foreach ($this->relation_types as $type) {
if ($type == $relation_type) {
$found = true;
break;
}
}
if (!$found) {
throw new Pluf_Form_Invalid(__('You provided an invalid relation type.'));
}
return $relation_type;
}
function clean_relation_type0()
{
return $this->clean_relation_type($this->cleaned_data['relation_type0']);
}
// this method is not called from Pluf_Form directly, but shared for
// among all similar fields
function clean_relation_issue($value)
{
$issues = trim($value);
if (empty($issues))
return '';
$issue_ids = preg_split('/\s*,\s*/', $issues, -1, PREG_SPLIT_NO_EMPTY);
foreach ($issue_ids as $issue_id) {
if (!ctype_digit($issue_id) || (int)$issue_id < 1) {
throw new Pluf_Form_Invalid(sprintf(
__('The value "%s" is not a valid issue id.'), $issue_id
));
}
$issue = new IDF_Issue($issue_id);
if ($issue->id != $issue_id || $issue->project != $this->project->id) {
throw new Pluf_Form_Invalid(sprintf(
__('The issue "%s" does not exist.'), $issue_id
));
}
}
return implode(', ', $issue_ids);
}
function clean_relation_issue0()
{
return $this->clean_relation_issue($this->cleaned_data['relation_issue0']);
}
/** /**
* Clean the attachments post failure. * Clean the attachments post failure.
*/ */
@@ -313,6 +373,30 @@ class IDF_Form_IssueCreate extends Pluf_Form
foreach ($tags as $tag) { foreach ($tags as $tag) {
$issue->setAssoc($tag); $issue->setAssoc($tag);
} }
// add relations (if any)
if (!empty($this->cleaned_data['relation_type0'])) {
$verb = $this->cleaned_data['relation_type0'];
$other_verb = $this->relation_types[$verb];
$related_issues = preg_split('/\s*,\s*/', $this->cleaned_data['relation_issue0'], -1, PREG_SPLIT_NO_EMPTY);
foreach ($related_issues as $related_issue_id) {
$related_issue = new IDF_Issue($related_issue_id);
$rel = new IDF_IssueRelation();
$rel->issue = $issue;
$rel->verb = $verb;
$rel->other_issue = $related_issue;
$rel->submitter = $this->user;
$rel->create();
$other_rel = new IDF_IssueRelation();
$other_rel->issue = $related_issue;
$other_rel->verb = $other_verb;
$other_rel->other_issue = $issue;
$other_rel->submitter = $this->user;
$other_rel->create();
}
}
// add the first comment // add the first comment
$comment = new IDF_IssueComment(); $comment = new IDF_IssueComment();
$comment->issue = $issue; $comment->issue = $issue;

View File

@@ -72,15 +72,29 @@ Performance = Performance issue
Usability = Affects program usability Usability = Affects program usability
Maintainability = Hinders future changes'; Maintainability = Hinders future changes';
const init_one_max = 'Type, Priority, Milestone'; const init_one_max = 'Type, Priority, Milestone';
// ATTENTION: if you change something here, change the values below as well!
const init_relations = 'is related to const init_relations = 'is related to
blocks, is blocked by blocks, is blocked by
duplicates, is duplicated by'; duplicates, is duplicated by';
// These are actually all noop's, but we have no other chance to
// tell IDF's translation mechanism to mark the strings as translatable
// FIXME: IDF should get a internal translation system for strings like
// that, that can also be easily expanded by users
private function noop()
{
__('is related to');
__('blocks');
__('is blocked by');
__('duplicates');
__('is duplicated by');
}
public function initFields($extra=array()) public function initFields($extra=array())
{ {
$this->fields['labels_issue_template'] = new Pluf_Form_Field_Varchar( $this->fields['labels_issue_template'] = new Pluf_Form_Field_Varchar(
array('required' => false, array('required' => false,
'label' => __('Define an issue template to hint to the reporter to provide certain information'), 'label' => __('Define an issue template to hint the reporter to provide certain information'),
'initial' => self::init_template, 'initial' => self::init_template,
'widget_attrs' => array('rows' => 7, 'widget_attrs' => array('rows' => 7,
'cols' => 75), 'cols' => 75),

View File

@@ -39,6 +39,7 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
or $this->user->hasPerm('IDF.project-member', $this->project)) { or $this->user->hasPerm('IDF.project-member', $this->project)) {
$this->show_full = true; $this->show_full = true;
} }
$this->relation_types = $this->project->getRelationsFromConfig();
if ($this->show_full) { if ($this->show_full) {
$this->fields['summary'] = new Pluf_Form_Field_Varchar( $this->fields['summary'] = new Pluf_Form_Field_Varchar(
array('required' => true, array('required' => true,
@@ -69,11 +70,11 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
// case of someone allowing the upload path to be accessible // case of someone allowing the upload path to be accessible
// to everybody. // to everybody.
for ($i=1;$i<4;$i++) { for ($i=1;$i<4;$i++) {
$filename = substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/'.substr($md5, 4).'/%s.dummy'; $filename = substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/'.substr($md5, 4).'/%s.dummy';
$this->fields['attachment'.$i] = new Pluf_Form_Field_File( $this->fields['attachment'.$i] = new Pluf_Form_Field_File(
array('required' => false, array('required' => false,
'label' => __('Attach a file'), 'label' => __('Attach a file'),
'move_function_params' => 'move_function_params' =>
array('upload_path' => $upload_path, array('upload_path' => $upload_path,
'upload_path_create' => true, 'upload_path_create' => true,
'file_name' => $filename, 'file_name' => $filename,
@@ -102,6 +103,52 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
'size' => 15, 'size' => 15,
), ),
)); ));
$idx = 0;
// note: clean_relation_type0 and clean_relation_issue0 already
// exist in the base class
$this->fields['relation_type'.$idx] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('This issue'),
'initial' => current($this->relation_types),
'widget_attrs' => array('size' => 15),
));
$this->fields['relation_issue'.$idx] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => null,
'initial' => '',
'widget_attrs' => array('size' => 10),
));
++$idx;
$relatedIssues = $this->issue->getGroupedRelatedIssues(array(), true);
foreach ($relatedIssues as $verb => $ids) {
$this->fields['relation_type'.$idx] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('This issue'),
'initial' => $verb,
'widget_attrs' => array('size' => 15),
));
$m = 'clean_relation_type'.$idx;
$this->$m = create_function('$form', '
return $form->clean_relation_type($form->cleaned_data["relation_type'.$idx.'"]);
');
$this->fields['relation_issue'.$idx] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => null,
'initial' => implode(', ', $ids),
'widget_attrs' => array('size' => 10),
));
$m = 'clean_relation_issue'.$idx;
$this->$m = create_function('$form', '
return $form->clean_relation_issue($form->cleaned_data["relation_issue'.$idx.'"]);
');
++$idx;
}
$tags = $this->issue->get_tags_list(); $tags = $this->issue->get_tags_list();
for ($i=1;$i<7;$i++) { for ($i=1;$i<7;$i++) {
$initial = ''; $initial = '';
@@ -155,6 +202,51 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
public function clean() public function clean()
{ {
$this->cleaned_data = parent::clean(); $this->cleaned_data = parent::clean();
// normalize the user's input by removing dublettes and by combining
// ids from identical verbs in different input fields into one array
$normRelatedIssues = array();
for ($idx = 0; isset($this->cleaned_data['relation_type'.$idx]); ++$idx) {
$verb = $this->cleaned_data['relation_type'.$idx];
if (empty($verb))
continue;
$ids = preg_split('/\s*,\s*/', $this->cleaned_data['relation_issue'.$idx],
-1, PREG_SPLIT_NO_EMPTY);
if (count($ids) == 0)
continue;
if (!array_key_exists($verb, $normRelatedIssues))
$normRelatedIssues[$verb] = array();
foreach ($ids as $id) {
if (!in_array($id, $normRelatedIssues[$verb]))
$normRelatedIssues[$verb][] = $id;
}
}
// now look at any added / removed ids
$added = $removed = array();
$relatedIssues = $this->issue->getGroupedRelatedIssues(array(), true);
$added = array_diff_key($normRelatedIssues, $relatedIssues);
$removed = array_diff_key($relatedIssues, $normRelatedIssues);
$keysToLookAt = array_keys(
array_intersect_key($relatedIssues, $normRelatedIssues)
);
foreach ($keysToLookAt as $key) {
$a = array_diff($normRelatedIssues[$key], $relatedIssues[$key]);
if (count($a) > 0)
$added[$key] = $a;
$r = array_diff($relatedIssues[$key], $normRelatedIssues[$key]);
if (count($r) > 0)
$removed[$key] = $r;
}
// cache the added / removed data, so we do not have to
// calculate that again
$this->cleaned_data['_added_issue_relations'] = $added;
$this->cleaned_data['_removed_issue_relations'] = $removed;
// As soon as we know that at least one change was done, we // As soon as we know that at least one change was done, we
// return the cleaned data and do not go further. // return the cleaned data and do not go further.
if (strlen(trim($this->cleaned_data['content']))) { if (strlen(trim($this->cleaned_data['content']))) {
@@ -214,6 +306,11 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
return $this->cleaned_data; return $this->cleaned_data;
} }
} }
if (count($this->cleaned_data['_added_issue_relations']) != 0 ||
count($this->cleaned_data['_removed_issue_relations']) != 0) {
return $this->cleaned_data;
}
} }
// no changes! // no changes!
throw new Pluf_Form_Invalid(__('No changes were entered.')); throw new Pluf_Form_Invalid(__('No changes were entered.'));
@@ -255,20 +352,22 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
foreach ($tags as $tag) { foreach ($tags as $tag) {
if (!Pluf_Model_InArray($tag, $oldtags)) { if (!Pluf_Model_InArray($tag, $oldtags)) {
if (!isset($changes['lb'])) $changes['lb'] = array(); if (!isset($changes['lb'])) $changes['lb'] = array();
if (!isset($changes['lb']['add'])) $changes['lb']['add'] = array();
if ($tag->class != 'Other') { if ($tag->class != 'Other') {
$changes['lb'][] = (string) $tag; //new tag $changes['lb']['add'][] = (string) $tag; //new tag
} else { } else {
$changes['lb'][] = (string) $tag->name; $changes['lb']['add'][] = (string) $tag->name;
} }
} }
} }
foreach ($oldtags as $tag) { foreach ($oldtags as $tag) {
if (!Pluf_Model_InArray($tag, $tags)) { if (!Pluf_Model_InArray($tag, $tags)) {
if (!isset($changes['lb'])) $changes['lb'] = array(); if (!isset($changes['lb'])) $changes['lb'] = array();
if (!isset($changes['lb']['rem'])) $changes['lb']['rem'] = array();
if ($tag->class != 'Other') { if ($tag->class != 'Other') {
$changes['lb'][] = '-'.(string) $tag; //new tag $changes['lb']['rem'][] = (string) $tag; //new tag
} else { } else {
$changes['lb'][] = '-'.(string) $tag->name; $changes['lb']['rem'][] = (string) $tag->name;
} }
} }
} }
@@ -286,6 +385,47 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
or ((!is_null($owner) and !is_null($this->issue->get_owner())) and $owner->id != $this->issue->get_owner()->id)) { or ((!is_null($owner) and !is_null($this->issue->get_owner())) and $owner->id != $this->issue->get_owner()->id)) {
$changes['ow'] = (is_null($owner)) ? '---' : $owner->login; $changes['ow'] = (is_null($owner)) ? '---' : $owner->login;
} }
// Issue relations - additions
foreach ($this->cleaned_data['_added_issue_relations'] as $verb => $ids) {
$other_verb = $this->relation_types[$verb];
foreach ($ids as $id) {
$related_issue = new IDF_Issue($id);
$rel = new IDF_IssueRelation();
$rel->issue = $this->issue;
$rel->verb = $verb;
$rel->other_issue = $related_issue;
$rel->submitter = $this->user;
$rel->create();
$other_rel = new IDF_IssueRelation();
$other_rel->issue = $related_issue;
$other_rel->verb = $other_verb;
$other_rel->other_issue = $this->issue;
$other_rel->submitter = $this->user;
$other_rel->create();
}
if (!isset($changes['rel'])) $changes['rel'] = array();
if (!isset($changes['rel']['add'])) $changes['rel']['add'] = array();
$changes['rel']['add'][] = $verb.' '.implode(', ', $ids);
}
// Issue relations - removals
foreach ($this->cleaned_data['_removed_issue_relations'] as $verb => $ids) {
foreach ($ids as $id) {
$db = &Pluf::db();
$table = Pluf::factory('IDF_IssueRelation')->getSqlTable();
$sql = new Pluf_SQL('verb=%s AND (
(issue=%s AND other_issue=%s) OR
(other_issue=%s AND issue=%s))',
array($verb,
$this->issue->id, $id,
$this->issue->id, $id));
$db->execute('DELETE FROM '.$table.' WHERE '.$sql->gen());
}
if (!isset($changes['rel'])) $changes['rel'] = array();
if (!isset($changes['rel']['rem'])) $changes['rel']['rem'] = array();
$changes['rel']['rem'][] = $verb.' '.implode(', ', $ids);
}
// Update the issue // Update the issue
$this->issue->batchAssoc('IDF_Tag', $tagids); $this->issue->batchAssoc('IDF_Tag', $tagids);
$this->issue->summary = trim($this->cleaned_data['summary']); $this->issue->summary = trim($this->cleaned_data['summary']);

View File

@@ -317,8 +317,15 @@ class IDF_Form_UserAccount extends Pluf_Form
return ''; return '';
} }
if (preg_match('#^ssh\-[a-z]{3}\s\S+(\s\S+)?$#', $key)) { $keysearch = '';
$key = str_replace(array("\n", "\r"), '', $key); if (preg_match('#^(ssh\-(?:dss|rsa)\s+\S+)(.*)#', $key, $m)) {
$basekey = preg_replace('/\s+/', ' ', $m[1]);
$comment = trim(preg_replace('/[\r\n]/', ' ', $m[2]));
$keysearch = $basekey.'%';
$key = $basekey;
if (!empty($comment))
$key .= ' '.$comment;
if (Pluf::f('idf_strong_key_check', false)) { if (Pluf::f('idf_strong_key_check', false)) {
@@ -337,7 +344,9 @@ class IDF_Form_UserAccount extends Pluf_Form
} }
} }
} }
else if (preg_match('#^\[pubkey [^\]]+\]\s*\S+\s*\[end\]$#', $key)) { else if (preg_match('#^\[pubkey [^\]]+\]\s*(\S+)\s*\[end\]$#', $key, $m)) {
$keysearch = '%'.$m[1].'%';
if (Pluf::f('idf_strong_key_check', false)) { if (Pluf::f('idf_strong_key_check', false)) {
// if monotone can read it, it should be valid // if monotone can read it, it should be valid
@@ -367,7 +376,7 @@ class IDF_Form_UserAccount extends Pluf_Form
if ($user) { if ($user) {
$ruser = Pluf::factory('Pluf_User', $user); $ruser = Pluf::factory('Pluf_User', $user);
if ($ruser->id > 0) { if ($ruser->id > 0) {
$sql = new Pluf_SQL('content=%s', array($key)); $sql = new Pluf_SQL('content LIKE %s AND user=%s', array($keysearch, $ruser->id));
$keys = Pluf::factory('IDF_Key')->getList(array('filter' => $sql->gen())); $keys = Pluf::factory('IDF_Key')->getList(array('filter' => $sql->gen()));
if (count($keys) > 0) { if (count($keys) > 0) {
throw new Pluf_Form_Invalid( throw new Pluf_Form_Invalid(

View File

@@ -169,6 +169,24 @@ class IDF_Issue extends Pluf_Model
} }
} }
function getGroupedRelatedIssues($opts = array(), $idsOnly = false)
{
$rels = $this->get_related_issues_list(array_merge($opts, array(
'view' => 'with_other_issue',
)));
$res = array();
foreach ($rels as $rel) {
$verb = $rel->verb;
if (!array_key_exists($verb, $res)) {
$res[$verb] = array();
}
$res[$verb][] = $idsOnly ? $rel->other_issue : $rel;
}
return $res;
}
/** /**
* Returns an HTML fragment used to display this issue in the * Returns an HTML fragment used to display this issue in the
* timeline. * timeline.

View File

@@ -155,10 +155,19 @@ class IDF_IssueComment extends Pluf_Model
$out .= __('Owner:'); break; $out .= __('Owner:'); break;
case 'lb': case 'lb':
$out .= __('Labels:'); break; $out .= __('Labels:'); break;
case 'rel':
$out .= __('Relations:'); break;
} }
$out .= '</strong>&nbsp;'; $out .= '</strong>&nbsp;';
if ($w == 'lb') { if ($w == 'lb' || $w == 'rel') {
$out .= Pluf_esc(implode(', ', $v)); foreach ($v as $t => $ls) {
foreach ($ls as $l) {
if ($t == 'rem') $out .= '<s>';
$out .= Pluf_esc($l);
if ($t == 'rem') $out .= '</s>';
$out .= ' ';
}
}
} else { } else {
$out .= Pluf_esc($v); $out .= Pluf_esc($v);
} }

100
src/IDF/IssueRelation.php Normal file
View File

@@ -0,0 +1,100 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
#
# InDefero 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.
#
# InDefero 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 St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* A relation of one issue to another
*/
class IDF_IssueRelation extends Pluf_Model
{
public $_model = __CLASS__;
function init()
{
$this->_a['table'] = 'idf_issuerelations';
$this->_a['model'] = __CLASS__;
$this->_a['cols'] = array(
// It is mandatory to have an "id" column.
'id' =>
array(
'type' => 'Pluf_DB_Field_Sequence',
'blank' => true,
),
'issue' =>
array(
'type' => 'Pluf_DB_Field_Foreignkey',
'model' => 'IDF_Issue',
'blank' => false,
'verbose' => __('issue'),
'relate_name' => 'related_issues',
),
'verb' =>
array(
'type' => 'Pluf_DB_Field_Text',
'blank' => false,
'verbose' => __('verb'),
),
'other_issue' =>
array(
'type' => 'Pluf_DB_Field_Foreignkey',
'model' => 'IDF_Issue',
'blank' => false,
'verbose' => __('other issue'),
'relate_name' => 'related_other_issues',
),
'submitter' =>
array(
'type' => 'Pluf_DB_Field_Foreignkey',
'model' => 'Pluf_User',
'blank' => false,
'verbose' => __('submitter'),
),
'creation_dtime' =>
array(
'type' => 'Pluf_DB_Field_Datetime',
'blank' => true,
'verbose' => __('creation date'),
),
);
$this->_a['idx'] = array(
'creation_dtime_idx' =>
array(
'col' => 'creation_dtime',
'type' => 'normal',
),
);
$issuetbl = $this->_con->pfx.'idf_issues';
$this->_a['views'] = array(
'with_other_issue' => array(
'join' => 'INNER JOIN '.$issuetbl.' ON other_issue='.$issuetbl.'.id',
'select' => $this->getSelect().', summary',
'props' => array('summary' => 'other_summary'),
));
}
function preSave($create=false)
{
if ($this->id == '') {
$this->creation_dtime = gmdate('Y-m-d H:i:s');
}
}
}

View File

@@ -80,7 +80,7 @@ class IDF_Key extends Pluf_Model
if (preg_match('#^\[pubkey ([^\]]+)\]\s*(\S+)\s*\[end\]$#', $this->content, $m)) { if (preg_match('#^\[pubkey ([^\]]+)\]\s*(\S+)\s*\[end\]$#', $this->content, $m)) {
return array('mtn', $m[1], $m[2]); return array('mtn', $m[1], $m[2]);
} }
else if (preg_match('#^ssh\-[a-z]{3}\s(\S+)(?:\s(\S*))?$#', $this->content, $m)) { else if (preg_match('#^ssh\-(?:dss|rsa)\s(\S+)(?:\s(.*))?$#', $this->content, $m)) {
if (!isset($m[2])) { if (!isset($m[2])) {
$m[2] = ""; $m[2] = "";
} }

View File

@@ -88,6 +88,7 @@ class IDF_Middleware
'showuser' => 'IDF_Template_ShowUser', 'showuser' => 'IDF_Template_ShowUser',
'ashowuser' => 'IDF_Template_AssignShowUser', 'ashowuser' => 'IDF_Template_AssignShowUser',
'appversion' => 'IDF_Template_AppVersion', 'appversion' => 'IDF_Template_AppVersion',
'upload' => 'IDF_Template_Tag_UploadUrl',
)); ));
$params['modifiers'] = array_merge($params['modifiers'], $params['modifiers'] = array_merge($params['modifiers'],
array( array(

View File

@@ -0,0 +1,90 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
#
# InDefero 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.
#
# InDefero 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 St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Add the new IDF_IssueRelation model.
*
*/
function IDF_Migrations_17AddIssueRelations_up($params=null)
{
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
$schema->model = new IDF_IssueRelation();
$schema->createTables();
// change the serialization format for added / removed labels in IDF_IssueComment
$comments = Pluf::factory('IDF_IssueComment')->getList();
foreach ($comments as $comment) {
if (!isset($comment->changes['lb'])) continue;
$changes = $comment->changes;
$adds = $removals = array();
foreach ($comment->changes['lb'] as $lb) {
if (substr($lb, 0, 1) == '-')
$removals[] = substr($lb, 1);
else
$adds[] = $lb;
}
$changes['lb'] = array();
if (count($adds) > 0)
$changes['lb']['add'] = $adds;
if (count($removals) > 0)
$changes['lb']['rem'] = $removals;
$comment->changes = $changes;
$comment->update();
}
}
function IDF_Migrations_17AddIssueRelations_down($params=null)
{
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
$schema->model = new IDF_IssueRelation();
$schema->dropTables();
// change the serialization format for added / removed labels in IDF_IssueComment
$comments = Pluf::factory('IDF_IssueComment')->getList();
foreach ($comments as $comment) {
$changes = $comment->changes;
if (empty($changes))
continue;
if (isset($changes['lb'])) {
$labels = array();
foreach ($changes['lb'] as $type => $lbs) {
if (!is_array($lbs)) {
$labels[] = $lbs;
continue;
}
foreach ($lbs as $lb) {
$labels[] = ($type == 'rem' ? '-' : '') . $lb;
}
}
$changes['lb'] = $labels;
}
// while we're at it, remove any 'rel' changes
unset($changes['rel']);
$comment->changes = $changes;
$comment->update();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
#
# InDefero 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.
#
# InDefero 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 St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Add the md5 column for the download model.
*/
function IDF_Migrations_18DownloadMD5_up($params=null)
{
// Add the row
$table = Pluf::factory('IDF_Upload')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' ADD COLUMN "md5" VARCHAR(32) DEFAULT \'\'';
$sql['MySQL'] = 'ALTER TABLE '.$table.' ADD COLUMN `md5` VARCHAR(32) DEFAULT \'\'';
$db = Pluf::db();
$engine = Pluf::f('db_engine');
if (!isset($sql[$engine])) {
throw new Exception('SQLite complex migration not supported.');
}
$db->execute($sql[$engine]);
// Process md5 of already uploaded file
$files = Pluf::factory('IDF_Upload')->getList();
foreach ($files as $f) {
$f->md5 = md5_file (Pluf::f('upload_path') . '/' . $f->get_project()->shortname . '/files/' . $f->file);
$f->update();
}
}
function IDF_Migrations_18DownloadMD5_down($params=null)
{
// Remove the row
$table = Pluf::factory('IDF_Upload')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' DROP COLUMN "md5"';
$sql['MySQL'] = 'ALTER TABLE '.$table.' DROP COLUMN `md5`';
$db = Pluf::db();
$engine = Pluf::f('db_engine');
if (!isset($sql[$engine])) {
throw new Exception('SQLite complex migration not supported.');
}
$db->execute($sql[$engine]);
}

View File

@@ -54,6 +54,7 @@ function IDF_Migrations_Backup_run($folder, $name=null)
'IDF_Queue', 'IDF_Queue',
'IDF_Gconf', 'IDF_Gconf',
'IDF_EmailAddress', 'IDF_EmailAddress',
'IDF_IssueRelation',
); );
$db = Pluf::db(); $db = Pluf::db();
// Now, for each table, we dump the content in json, this is a // Now, for each table, we dump the content in json, this is a
@@ -100,6 +101,7 @@ function IDF_Migrations_Backup_restore($folder, $name)
'IDF_Queue', 'IDF_Queue',
'IDF_Gconf', 'IDF_Gconf',
'IDF_EmailAddress', 'IDF_EmailAddress',
'IDF_IssueRelation',
); );
$db = Pluf::db(); $db = Pluf::db();
$schema = new Pluf_DB_Schema($db); $schema = new Pluf_DB_Schema($db);

View File

@@ -51,6 +51,7 @@ function IDF_Migrations_Install_setup($params=null)
'IDF_Queue', 'IDF_Queue',
'IDF_Gconf', 'IDF_Gconf',
'IDF_EmailAddress', 'IDF_EmailAddress',
'IDF_IssueRelation',
); );
$db = Pluf::db(); $db = Pluf::db();
$schema = new Pluf_DB_Schema($db); $schema = new Pluf_DB_Schema($db);
@@ -109,6 +110,7 @@ function IDF_Migrations_Install_teardown($params=null)
'IDF_Commit', 'IDF_Commit',
'IDF_Project', 'IDF_Project',
'IDF_EmailAddress', 'IDF_EmailAddress',
'IDF_IssueRelation',
); );
$db = Pluf::db(); $db = Pluf::db();
$schema = new Pluf_DB_Schema($db); $schema = new Pluf_DB_Schema($db);

View File

@@ -59,6 +59,16 @@ class IDF_Plugin_SyncGit_Cron
$out .= sprintf($template, $cmd, $key->login, $content)."\n"; $out .= sprintf($template, $cmd, $key->login, $content)."\n";
} }
} }
$out = "# indefero start" . PHP_EOL . $out . "# indefero end" . PHP_EOL;
// We update only the part of the file between IDF_START / IDF_END comment
$original_keys = file_get_contents($authorized_keys);
if (strstr($original_keys, "# indefero start") && strstr($original_keys, "# indefero end")) {
$out = preg_replace('/(#\sindefero\sstart).+(#\sindefero\send\s\s?)/isU',
$out, $original_keys);
} else {
$out .= $original_keys;
}
file_put_contents($authorized_keys, $out, LOCK_EX); file_put_contents($authorized_keys, $out, LOCK_EX);
} }

View File

@@ -27,6 +27,18 @@
*/ */
class IDF_Plugin_SyncMonotone class IDF_Plugin_SyncMonotone
{ {
private $old_err_rep = 0;
public function __construct()
{
$this->old_err_rep = error_reporting(0);
}
public function __destruct()
{
error_reporting($this->old_err_rep);
}
/** /**
* Entry point of the plugin. * Entry point of the plugin.
*/ */
@@ -80,24 +92,33 @@ class IDF_Plugin_SyncMonotone
return; return;
} }
// This guard cleans up on any kind of error, and here is how it works:
// As long as the guard is not committed, it keeps a reference to
// the given project. When the guard is destroyed and the reference
// is still present, it deletes the object. The deletion indirectly
// also calls into this plugin again, as the project delete hook
// will be called, that removes any changes we've made during the
// process.
$projectGuard = new IDF_Plugin_SyncMonotone_ModelGuard($project);
$projecttempl = Pluf::f('mtn_repositories', false); $projecttempl = Pluf::f('mtn_repositories', false);
if ($projecttempl === false) { if ($projecttempl === false) {
throw new IDF_Scm_Exception( $this->_diagnoseProblem(
__('"mtn_repositories" must be defined in your configuration file.') __('"mtn_repositories" must be defined in your configuration file')
); );
} }
$usher_config = Pluf::f('mtn_usher_conf', false); $usher_config = Pluf::f('mtn_usher_conf', false);
if (!$usher_config || !is_writable($usher_config)) { if (!$usher_config || !is_writable($usher_config)) {
throw new IDF_Scm_Exception( $this->_diagnoseProblem(
__('"mtn_usher_conf" does not exist or is not writable.') __('"mtn_usher_conf" does not exist or is not writable')
); );
} }
$mtnpostpush = realpath(dirname(__FILE__) . '/../../../scripts/mtn-post-push'); $mtnpostpush = realpath(dirname(__FILE__) . '/../../../scripts/mtn-post-push');
if (!file_exists($mtnpostpush)) { if (!file_exists($mtnpostpush)) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not find mtn-post-push script "%s".'), $mtnpostpush __('Could not find mtn-post-push script "%s"'), $mtnpostpush
)); ));
} }
@@ -110,13 +131,12 @@ class IDF_Plugin_SyncMonotone
'monotonerc.in', 'monotonerc.in',
'remote-automate-permissions.in', 'remote-automate-permissions.in',
'hooks.d/', 'hooks.d/',
// this is linked and not copied to be able to update
// the list of read-only commands on upgrades
'hooks.d/indefero_authorize_remote_automate.conf',
'hooks.d/indefero_authorize_remote_automate.lua', 'hooks.d/indefero_authorize_remote_automate.lua',
'hooks.d/indefero_post_push.conf.in', 'hooks.d/indefero_post_push.conf.in',
'hooks.d/indefero_post_push.lua', 'hooks.d/indefero_post_push.lua',
); );
// enable remote command execution of read-only commands
// only for public projects
if (!$project->private) { if (!$project->private) {
// this is linked and not copied to be able to update // this is linked and not copied to be able to update
// the list of read-only commands on upgrades // the list of read-only commands on upgrades
@@ -131,8 +151,8 @@ class IDF_Plugin_SyncMonotone
} }
foreach ($confdir_contents as $content) { foreach ($confdir_contents as $content) {
if (!file_exists($confdir.$content)) { if (!file_exists($confdir.$content)) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('The configuration file %s is missing.'), $content __('The configuration file "%s" is missing'), $content
)); ));
} }
} }
@@ -140,14 +160,15 @@ class IDF_Plugin_SyncMonotone
$shortname = $project->shortname; $shortname = $project->shortname;
$projectpath = sprintf($projecttempl, $shortname); $projectpath = sprintf($projecttempl, $shortname);
if (file_exists($projectpath)) { if (file_exists($projectpath)) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('The project path %s already exists.'), $projectpath __('The project path "%s" already exists'), $projectpath
)); ));
} }
if (!mkdir($projectpath)) { if (!@mkdir($projectpath)) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('The project path %s could not be created.'), $projectpath __('The project path "%s" could not be created'),
$projectpath
)); ));
} }
@@ -156,7 +177,7 @@ class IDF_Plugin_SyncMonotone
// //
$dbfile = $projectpath.'/database.mtn'; $dbfile = $projectpath.'/database.mtn';
$cmd = sprintf('db init -d %s', escapeshellarg($dbfile)); $cmd = sprintf('db init -d %s', escapeshellarg($dbfile));
self::_mtn_exec($cmd); $this->_mtn_exec($cmd);
// //
// step 2) create a server key // step 2) create a server key
@@ -175,16 +196,17 @@ class IDF_Plugin_SyncMonotone
escapeshellarg($projectpath), escapeshellarg($projectpath),
escapeshellarg($serverkey) escapeshellarg($serverkey)
); );
self::_mtn_exec($cmd); $this->_mtn_exec($cmd);
// //
// step 3) create a client key, and save it in IDF // step 3) create a client key, and save it in IDF
// //
$keydir = Pluf::f('tmp_folder').'/mtn-client-keys'; $keydir = Pluf::f('tmp_folder').'/mtn-client-keys';
if (!file_exists($keydir)) { if (!file_exists($keydir)) {
if (!mkdir($keydir)) { if (!@mkdir($keydir)) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('The key directory %s could not be created.'), $keydir __('The key directory "%s" could not be created'),
$keydir
)); ));
} }
} }
@@ -194,14 +216,14 @@ class IDF_Plugin_SyncMonotone
escapeshellarg($keydir), escapeshellarg($keydir),
escapeshellarg($clientkey_name) escapeshellarg($clientkey_name)
); );
$keyinfo = self::_mtn_exec($cmd); $keyinfo = $this->_mtn_exec($cmd);
$parsed_keyinfo = array(); $parsed_keyinfo = array();
try { try {
$parsed_keyinfo = IDF_Scm_Monotone_BasicIO::parse($keyinfo); $parsed_keyinfo = IDF_Scm_Monotone_BasicIO::parse($keyinfo);
} }
catch (Exception $e) { catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not parse key information: %s'), $e->getMessage() __('Could not parse key information: %s'), $e->getMessage()
)); ));
} }
@@ -219,13 +241,13 @@ class IDF_Plugin_SyncMonotone
escapeshellarg($keydir), escapeshellarg($keydir),
escapeshellarg($clientkey_hash) escapeshellarg($clientkey_hash)
); );
$clientkey_pubdata = self::_mtn_exec($cmd); $clientkey_pubdata = $this->_mtn_exec($cmd);
$cmd = sprintf('au put_public_key --db=%s %s', $cmd = sprintf('au put_public_key --db=%s %s',
escapeshellarg($dbfile), escapeshellarg($dbfile),
escapeshellarg($clientkey_pubdata) escapeshellarg($clientkey_pubdata)
); );
self::_mtn_exec($cmd); $this->_mtn_exec($cmd);
// //
// step 4) setup the configuration // step 4) setup the configuration
@@ -238,18 +260,20 @@ class IDF_Plugin_SyncMonotone
foreach ($confdir_contents as $content) { foreach ($confdir_contents as $content) {
$filepath = $projectpath.'/'.$content; $filepath = $projectpath.'/'.$content;
if (substr($content, -1) == '/') { if (substr($content, -1) == '/') {
if (!mkdir($filepath)) { if (!@mkdir($filepath)) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not create configuration directory "%s"'), $filepath __('Could not create configuration directory "%s"'),
$filepath
)); ));
} }
continue; continue;
} }
if (substr($content, -3) != '.in') { if (substr($content, -3) != '.in') {
if (!symlink($confdir.$content, $filepath)) { if (!@symlink($confdir.$content, $filepath)) {
IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not create symlink "%s"'), $filepath __('Could not create symlink for configuration file "%s"'),
$filepath
)); ));
} }
continue; continue;
@@ -264,9 +288,10 @@ class IDF_Plugin_SyncMonotone
// remove the .in // remove the .in
$filepath = substr($filepath, 0, -3); $filepath = substr($filepath, 0, -3);
if (file_put_contents($filepath, $filecontents, LOCK_EX) === false) { if (@file_put_contents($filepath, $filecontents, LOCK_EX) === false) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not write configuration file "%s"'), $filepath __('Could not write configuration file "%s"'),
$filepath
)); ));
} }
} }
@@ -280,7 +305,7 @@ class IDF_Plugin_SyncMonotone
$parsed_config = IDF_Scm_Monotone_BasicIO::parse($usher_rc); $parsed_config = IDF_Scm_Monotone_BasicIO::parse($usher_rc);
} }
catch (Exception $e) { catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not parse usher configuration in "%s": %s'), __('Could not parse usher configuration in "%s": %s'),
$usher_config, $e->getMessage() $usher_config, $e->getMessage()
)); ));
@@ -291,7 +316,7 @@ class IDF_Plugin_SyncMonotone
foreach ($stanzas as $stanza_line) { foreach ($stanzas as $stanza_line) {
if ($stanza_line['key'] == 'server' && if ($stanza_line['key'] == 'server' &&
$stanza_line['values'][0] == $shortname) { $stanza_line['values'][0] == $shortname) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('usher configuration already contains a server '. __('usher configuration already contains a server '.
'entry named "%s"'), 'entry named "%s"'),
$shortname $shortname
@@ -315,9 +340,10 @@ class IDF_Plugin_SyncMonotone
// FIXME: more sanity - what happens on failing writes? we do not // FIXME: more sanity - what happens on failing writes? we do not
// have a backup copy of usher.conf around... // have a backup copy of usher.conf around...
if (file_put_contents($usher_config, $usher_rc, LOCK_EX) === false) { if (@file_put_contents($usher_config, $usher_rc, LOCK_EX) === false) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not write usher configuration file "%s"'), $usher_config __('Could not write usher configuration file "%s"'),
$usher_config
)); ));
} }
@@ -325,6 +351,9 @@ class IDF_Plugin_SyncMonotone
// step 6) reload usher to pick up the new configuration // step 6) reload usher to pick up the new configuration
// //
IDF_Scm_Monotone_Usher::reload(); IDF_Scm_Monotone_Usher::reload();
// commit the guard, so the newly created project is not deleted
$projectGuard->commit();
} }
/** /**
@@ -345,8 +374,8 @@ class IDF_Plugin_SyncMonotone
$mtn = IDF_Scm_Monotone::factory($project); $mtn = IDF_Scm_Monotone::factory($project);
$stdio = $mtn->getStdio(); $stdio = $mtn->getStdio();
$projectpath = self::_get_project_path($project); $projectpath = $this->_get_project_path($project);
$auth_ids = self::_get_authorized_user_ids($project); $auth_ids = $this->_get_authorized_user_ids($project);
$key_ids = array(); $key_ids = array();
foreach ($auth_ids as $auth_id) { foreach ($auth_ids as $auth_id) {
$sql = new Pluf_SQL('user=%s', array($auth_id)); $sql = new Pluf_SQL('user=%s', array($auth_id));
@@ -361,9 +390,10 @@ class IDF_Plugin_SyncMonotone
$write_permissions = implode("\n", $key_ids); $write_permissions = implode("\n", $key_ids);
$rcfile = $projectpath.'/write-permissions'; $rcfile = $projectpath.'/write-permissions';
if (file_put_contents($rcfile, $write_permissions, LOCK_EX) === false) { if (@file_put_contents($rcfile, $write_permissions, LOCK_EX) === false) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not write write-permissions file "%s"'), $rcfile __('Could not write write-permissions file "%s"'),
$rcfile
)); ));
} }
@@ -382,11 +412,13 @@ class IDF_Plugin_SyncMonotone
array('key' => 'allow', 'values' => array('*')), array('key' => 'allow', 'values' => array('*')),
); );
} }
$read_permissions = IDF_Scm_Monotone_BasicIO::compile(array($stanza)); $read_permissions = IDF_Scm_Monotone_BasicIO::compile(array($stanza));
$rcfile = $projectpath.'/read-permissions'; $rcfile = $projectpath.'/read-permissions';
if (file_put_contents($rcfile, $read_permissions, LOCK_EX) === false) { if (@file_put_contents($rcfile, $read_permissions, LOCK_EX) === false) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not write read-permissions file "%s"'), $rcfile __('Could not write read-permissions file "%s"'),
$rcfile
)); ));
} }
@@ -401,16 +433,16 @@ class IDF_Plugin_SyncMonotone
$serverRestartRequired = false; $serverRestartRequired = false;
if ($project->private && file_exists($projectfile) && is_link($projectfile)) { if ($project->private && file_exists($projectfile) && is_link($projectfile)) {
if (!unlink($projectfile)) { if (!@unlink($projectfile)) {
IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not remove symlink "%s"'), $projectfile __('Could not remove symlink "%s"'), $projectfile
)); ));
} }
$serverRestartRequired = true; $serverRestartRequired = true;
} else } else
if (!$project->private && !file_exists($projectfile)) { if (!$project->private && !file_exists($projectfile)) {
if (!symlink($templatefile, $projectfile)) { if (!@symlink($templatefile, $projectfile)) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not create symlink "%s"'), $projectfile __('Could not create symlink "%s"'), $projectfile
)); ));
} }
@@ -422,6 +454,9 @@ class IDF_Plugin_SyncMonotone
// seems to be ignored when the server should be started // seems to be ignored when the server should be started
// again immediately afterwards // again immediately afterwards
IDF_Scm_Monotone_Usher::killServer($project->shortname); IDF_Scm_Monotone_Usher::killServer($project->shortname);
// give usher some time to cool down, otherwise it might hang
// (see https://code.monotone.ca/p/contrib/issues/175/)
sleep(2);
IDF_Scm_Monotone_Usher::startServer($project->shortname); IDF_Scm_Monotone_Usher::startServer($project->shortname);
} }
} }
@@ -443,8 +478,8 @@ class IDF_Plugin_SyncMonotone
$usher_config = Pluf::f('mtn_usher_conf', false); $usher_config = Pluf::f('mtn_usher_conf', false);
if (!$usher_config || !is_writable($usher_config)) { if (!$usher_config || !is_writable($usher_config)) {
throw new IDF_Scm_Exception( $this->_diagnoseProblem(
__('"mtn_usher_conf" does not exist or is not writable.') __('"mtn_usher_conf" does not exist or is not writable')
); );
} }
@@ -453,16 +488,16 @@ class IDF_Plugin_SyncMonotone
$projecttempl = Pluf::f('mtn_repositories', false); $projecttempl = Pluf::f('mtn_repositories', false);
if ($projecttempl === false) { if ($projecttempl === false) {
throw new IDF_Scm_Exception( $this->_diagnoseProblem(
__('"mtn_repositories" must be defined in your configuration file.') __('"mtn_repositories" must be defined in your configuration file')
); );
} }
$projectpath = sprintf($projecttempl, $shortname); $projectpath = sprintf($projecttempl, $shortname);
if (file_exists($projectpath)) { if (file_exists($projectpath)) {
if (!self::_delete_recursive($projectpath)) { if (!$this->_delete_recursive($projectpath)) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('One or more paths underknees %s could not be deleted.'), $projectpath __('One or more paths underneath %s could not be deleted'), $projectpath
)); ));
} }
} }
@@ -473,8 +508,9 @@ class IDF_Plugin_SyncMonotone
if ($keyname && $keyhash && if ($keyname && $keyhash &&
file_exists($keydir .'/'. $keyname . '.' . $keyhash)) { file_exists($keydir .'/'. $keyname . '.' . $keyhash)) {
if (!@unlink($keydir .'/'. $keyname . '.' . $keyhash)) { if (!@unlink($keydir .'/'. $keyname . '.' . $keyhash)) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not delete client private key %s'), $keyname __('Could not delete client private key "%s"'),
$keyname
)); ));
} }
} }
@@ -485,7 +521,7 @@ class IDF_Plugin_SyncMonotone
$parsed_config = IDF_Scm_Monotone_BasicIO::parse($usher_rc); $parsed_config = IDF_Scm_Monotone_BasicIO::parse($usher_rc);
} }
catch (Exception $e) { catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not parse usher configuration in "%s": %s'), __('Could not parse usher configuration in "%s": %s'),
$usher_config, $e->getMessage() $usher_config, $e->getMessage()
)); ));
@@ -505,9 +541,10 @@ class IDF_Plugin_SyncMonotone
// FIXME: more sanity - what happens on failing writes? we do not // FIXME: more sanity - what happens on failing writes? we do not
// have a backup copy of usher.conf around... // have a backup copy of usher.conf around...
if (file_put_contents($usher_config, $usher_rc, LOCK_EX) === false) { if (@file_put_contents($usher_config, $usher_rc, LOCK_EX) === false) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not write usher configuration file "%s"'), $usher_config __('Could not write usher configuration file "%s"'),
$usher_config
)); ));
} }
@@ -528,6 +565,8 @@ class IDF_Plugin_SyncMonotone
return; return;
} }
$keyGuard = new IDF_Plugin_SyncMonotone_ModelGuard($key);
foreach (Pluf::factory('IDF_Project')->getList() as $project) { foreach (Pluf::factory('IDF_Project')->getList() as $project) {
$conf = new IDF_Conf(); $conf = new IDF_Conf();
$conf->setProject($project); $conf->setProject($project);
@@ -535,8 +574,8 @@ class IDF_Plugin_SyncMonotone
if ($scm != 'mtn') if ($scm != 'mtn')
continue; continue;
$projectpath = self::_get_project_path($project); $projectpath = $this->_get_project_path($project);
$auth_ids = self::_get_authorized_user_ids($project); $auth_ids = $this->_get_authorized_user_ids($project);
if (!in_array($key->user, $auth_ids)) if (!in_array($key->user, $auth_ids))
continue; continue;
@@ -556,7 +595,7 @@ class IDF_Plugin_SyncMonotone
$parsed_read_perms = IDF_Scm_Monotone_BasicIO::parse($read_perms); $parsed_read_perms = IDF_Scm_Monotone_BasicIO::parse($read_perms);
} }
catch (Exception $e) { catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not parse read-permissions for project "%s": %s'), __('Could not parse read-permissions for project "%s": %s'),
$shortname, $e->getMessage() $shortname, $e->getMessage()
)); ));
@@ -598,10 +637,11 @@ class IDF_Plugin_SyncMonotone
$read_perms = IDF_Scm_Monotone_BasicIO::compile($parsed_read_perms); $read_perms = IDF_Scm_Monotone_BasicIO::compile($parsed_read_perms);
if (file_put_contents($projectpath.'/read-permissions', if (@file_put_contents($projectpath.'/read-permissions',
$read_perms, LOCK_EX) === false) { $read_perms, LOCK_EX) === false) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not write read-permissions for project "%s"'), $shortname __('Could not write read-permissions for project "%s"'),
$shortname
)); ));
} }
} }
@@ -611,9 +651,9 @@ class IDF_Plugin_SyncMonotone
if (!in_array('*', $lines) && !in_array($mtn_key_id, $lines)) { if (!in_array('*', $lines) && !in_array($mtn_key_id, $lines)) {
$lines[] = $mtn_key_id; $lines[] = $mtn_key_id;
} }
if (file_put_contents($projectpath.'/write-permissions', if (@file_put_contents($projectpath.'/write-permissions',
implode("\n", $lines) . "\n", LOCK_EX) === false) { implode("\n", $lines) . "\n", LOCK_EX) === false) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not write write-permissions file for project "%s"'), __('Could not write write-permissions file for project "%s"'),
$shortname $shortname
)); ));
@@ -623,6 +663,8 @@ class IDF_Plugin_SyncMonotone
$stdio = $mtn->getStdio(); $stdio = $mtn->getStdio();
$stdio->exec(array('put_public_key', $key->content)); $stdio->exec(array('put_public_key', $key->content));
} }
$keyGuard->commit();
} }
/** /**
@@ -651,8 +693,8 @@ class IDF_Plugin_SyncMonotone
if ($scm != 'mtn') if ($scm != 'mtn')
continue; continue;
$projectpath = self::_get_project_path($project); $projectpath = $this->_get_project_path($project);
$auth_ids = self::_get_authorized_user_ids($project); $auth_ids = $this->_get_authorized_user_ids($project);
if (!in_array($key->user, $auth_ids)) if (!in_array($key->user, $auth_ids))
continue; continue;
@@ -672,7 +714,7 @@ class IDF_Plugin_SyncMonotone
$parsed_read_perms = IDF_Scm_Monotone_BasicIO::parse($read_perms); $parsed_read_perms = IDF_Scm_Monotone_BasicIO::parse($read_perms);
} }
catch (Exception $e) { catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not parse read-permissions for project "%s": %s'), __('Could not parse read-permissions for project "%s": %s'),
$shortname, $e->getMessage() $shortname, $e->getMessage()
)); ));
@@ -693,10 +735,11 @@ class IDF_Plugin_SyncMonotone
$read_perms = IDF_Scm_Monotone_BasicIO::compile($parsed_read_perms); $read_perms = IDF_Scm_Monotone_BasicIO::compile($parsed_read_perms);
if (file_put_contents($projectpath.'/read-permissions', if (@file_put_contents($projectpath.'/read-permissions',
$read_perms, LOCK_EX) === false) { $read_perms, LOCK_EX) === false) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not write read-permissions for project "%s"'), $shortname __('Could not write read-permissions for project "%s"'),
$shortname
)); ));
} }
} }
@@ -711,9 +754,9 @@ class IDF_Plugin_SyncMonotone
continue; continue;
} }
} }
if (file_put_contents($projectpath.'/write-permissions', if (@file_put_contents($projectpath.'/write-permissions',
implode("\n", $lines) . "\n", LOCK_EX) === false) { implode("\n", $lines) . "\n", LOCK_EX) === false) {
throw new IDF_Scm_Exception(sprintf( $this->_diagnoseProblem(sprintf(
__('Could not write write-permissions file for project "%s"'), __('Could not write write-permissions file for project "%s"'),
$shortname $shortname
)); ));
@@ -762,7 +805,43 @@ class IDF_Plugin_SyncMonotone
)); ));
} }
private static function _get_authorized_user_ids($project) private function _get_project_path($project)
{
$projecttempl = Pluf::f('mtn_repositories', false);
if ($projecttempl === false) {
$this->_diagnoseProblem(
__('"mtn_repositories" must be defined in your configuration file.')
);
}
$projectpath = sprintf($projecttempl, $project->shortname);
if (!file_exists($projectpath)) {
$this->_diagnoseProblem(sprintf(
__('The project path %s does not exists.'), $projectpath
));
}
return $projectpath;
}
private function _mtn_exec($cmd)
{
$fullcmd = sprintf('%s %s %s',
Pluf::f('idf_exec_cmd_prefix', ''),
Pluf::f('mtn_path', 'mtn'),
$cmd
);
$output = $return = null;
exec($fullcmd, $output, $return);
if ($return != 0) {
$this->_diagnoseProblem(sprintf(
__('The command "%s" could not be executed.'), $cmd
));
}
return implode("\n", $output);
}
private function _get_authorized_user_ids($project)
{ {
$mem = $project->getMembershipData(); $mem = $project->getMembershipData();
$members = array_merge((array)$mem['members'], $members = array_merge((array)$mem['members'],
@@ -775,43 +854,7 @@ class IDF_Plugin_SyncMonotone
return $userids; return $userids;
} }
private static function _get_project_path($project) private function _delete_recursive($path)
{
$projecttempl = Pluf::f('mtn_repositories', false);
if ($projecttempl === false) {
throw new IDF_Scm_Exception(
__('"mtn_repositories" must be defined in your configuration file.')
);
}
$projectpath = sprintf($projecttempl, $project->shortname);
if (!file_exists($projectpath)) {
throw new IDF_Scm_Exception(sprintf(
__('The project path %s does not exists.'), $projectpath
));
}
return $projectpath;
}
private static function _mtn_exec($cmd)
{
$fullcmd = sprintf('%s %s %s',
Pluf::f('idf_exec_cmd_prefix', ''),
Pluf::f('mtn_path', 'mtn'),
$cmd
);
$output = $return = null;
exec($fullcmd, $output, $return);
if ($return != 0) {
throw new IDF_Scm_Exception(sprintf(
__('The command "%s" could not be executed.'), $cmd
));
}
return implode("\n", $output);
}
private static function _delete_recursive($path)
{ {
if (is_file($path) || is_link($path)) { if (is_file($path) || is_link($path)) {
return @unlink($path); return @unlink($path);
@@ -821,10 +864,48 @@ class IDF_Plugin_SyncMonotone
$scan = glob(rtrim($path, '/') . '/*'); $scan = glob(rtrim($path, '/') . '/*');
$status = 0; $status = 0;
foreach ($scan as $subpath) { foreach ($scan as $subpath) {
$status |= self::_delete_recursive($subpath); $status |= $this->_delete_recursive($subpath);
} }
$status |= rmdir($path); $status |= @rmdir($path);
return $status; return $status;
} }
} }
private function _diagnoseProblem($msg)
{
$system_err = error_get_last();
if (!empty($system_err)) {
$msg .= ': '.$system_err['message'];
}
error_reporting($this->old_err_rep);
throw new IDF_Scm_Exception($msg);
}
} }
/**
* A simple helper class that deletes the model instance if
* it is not committed
*/
class IDF_Plugin_SyncMonotone_ModelGuard
{
private $model;
public function __construct(Pluf_Model $m)
{
$this->model = $m;
}
public function __destruct()
{
if ($this->model == null)
return;
$this->model->delete();
}
public function commit()
{
$this->model = null;
}
}

View File

@@ -140,7 +140,48 @@ class IDF_Project extends Pluf_Model
* @param IDF_Tag Subfilter with a label (null) * @param IDF_Tag Subfilter with a label (null)
* @return int Count * @return int Count
*/ */
public function getIssueCountByStatus($status='open', $label=null) public function getIssueCountByOwner($status='open')
{
switch ($status) {
case 'open':
$tags = implode(',', $this->getTagIdsByStatus('open'));
break;
case 'closed':
default:
$tags = implode(',', $this->getTagIdsByStatus('closed'));
break;
}
$sqlIssueTable = Pluf::factory('IDF_Issue')->getSqlTable();
$query = "SELECT uid AS id,COUNT(uid) AS nb
FROM (
SELECT COALESCE(owner, -1) AS uid
FROM $sqlIssueTable
WHERE status IN ($tags)
) AS ff
GROUP BY uid";
$db = Pluf::db();
$dbData = $db->select($query);
$ownerStatistics = array();
foreach ($dbData as $k => $v) {
$key = ($v['id'] === '-1') ? null : $v['id'];
$ownerStatistics[$key] = (int)$v['nb'];
}
arsort($ownerStatistics);
return $ownerStatistics;
}
/**
* Returns the number of open/closed issues.
*
* @param string Status ('open'), 'closed'
* @param IDF_Tag Subfilter with a label (null)
* @param array Restrict further to a list of ids
* @return int Count
*/
public function getIssueCountByStatus($status='open', $label=null, $ids=array())
{ {
switch ($status) { switch ($status) {
case 'open': case 'open':
@@ -163,12 +204,48 @@ class IDF_Project extends Pluf_Model
$sql2 = new Pluf_SQL('idf_tag_id=%s', array($label->id)); $sql2 = new Pluf_SQL('idf_tag_id=%s', array($label->id));
$sql->SAnd($sql2); $sql->SAnd($sql2);
} }
if (count($ids) > 0) {
$sql2 = new Pluf_SQL(sprintf('id IN (%s)', implode(', ', $ids)));
$sql->SAnd($sql2);
}
$params = array('filter' => $sql->gen()); $params = array('filter' => $sql->gen());
if (!is_null($label)) { $params['view'] = 'join_tags'; } if (!is_null($label)) { $params['view'] = 'join_tags'; }
$gissue = new IDF_Issue(); $gissue = new IDF_Issue();
return $gissue->getCount($params); return $gissue->getCount($params);
} }
/**
* Get the tags for a specific list of issues.
*
* @param string Status ('open') or 'closed'
* @param array A list of issue ids
* @return array An array of tag objects
*/
public function getTagsByIssues($issue_ids=array())
{
// make the below query always a valid one
if (count($issue_ids) == 0) $issue_ids[] = 0;
$assocTable = $this->_con->pfx.'idf_issue_idf_tag_assoc';
$query = sprintf(
'SELECT DISTINCT idf_tag_id FROM %s '.
'WHERE idf_issue_id IN (%s) '.
'GROUP BY idf_tag_id',
$assocTable, implode(',', $issue_ids)
);
$db = Pluf::db();
$dbData = $db->select($query);
$ids = array(0);
foreach ($dbData as $data) {
$ids[] = $data['idf_tag_id'];
}
$sql = new Pluf_SQL(sprintf('id IN (%s)', implode(', ', $ids)));
$model = new IDF_Tag();
return $model->getList(array('filter' => $sql->gen()));
}
/** /**
* Get the open/closed tag ids as they are often used when doing * Get the open/closed tag ids as they are often used when doing
* listings. * listings.
@@ -234,7 +311,10 @@ class IDF_Project extends Pluf_Model
} }
/** /**
* Returns a list of relations which are available in this project * Returns a list of relations which are available in this project as
* associative array. Each key-value pair marks a set of orthogonal
* relations. To ease processing, each of these pairs is included twice
* in the array, once as key1 => key2 and once as key2 => key1.
* *
* @return array List of relation names * @return array List of relation names
*/ */
@@ -244,7 +324,11 @@ class IDF_Project extends Pluf_Model
$rel = $conf->getVal('issue_relations', IDF_Form_IssueTrackingConf::init_relations); $rel = $conf->getVal('issue_relations', IDF_Form_IssueTrackingConf::init_relations);
$relations = array(); $relations = array();
foreach (preg_split("/\015\012|\015|\012/", $rel, -1, PREG_SPLIT_NO_EMPTY) as $s) { foreach (preg_split("/\015\012|\015|\012/", $rel, -1, PREG_SPLIT_NO_EMPTY) as $s) {
$relations = array_merge($relations, preg_split("/\s*,\s*/", $s, 2)); $verbs = preg_split("/\s*,\s*/", $s, 2);
if (count($verbs) == 1)
$relations += array($verbs[0] => $verbs[0]);
else
$relations += array($verbs[0] => $verbs[1], $verbs[1] => $verbs[0]);
} }
return $relations; return $relations;
} }
@@ -368,7 +452,11 @@ class IDF_Project extends Pluf_Model
foreach ($this->_con->select($sql) as $idc) { foreach ($this->_con->select($sql) as $idc) {
$tag = new IDF_Tag($idc['id']); $tag = new IDF_Tag($idc['id']);
$tag->nb_use = $idc['nb_use']; $tag->nb_use = $idc['nb_use'];
$tags[] = $tag; // group by class
if (!array_key_exists($tag->class, $tags)) {
$tags[$tag->class] = array();
}
$tags[$tag->class][] = $tag;
} }
return new Pluf_Template_ContextVars($tags); return new Pluf_Template_ContextVars($tags);
} }

View File

@@ -88,22 +88,36 @@ class IDF_Scm
} }
/** /**
* Run exec and log some information. * Runs the given command and log some information.
*
* A previous version used plain exec(), but this should not be used
* for various reasons, one being that this command does not preserve
* trailing whitespace, which is essential for proper diff parsing.
* *
* @param $caller Calling method * @param $caller Calling method
* @param $cmd Command to run * @param $cmd Command to run
* @param &$out Array of output * @param &$out Array of output
* @param &$return Return value * @param &$return Return value
* @return string Last line of the command
*/ */
public static function exec($caller, $cmd, &$out=null, &$return=null) public static function exec($caller, $cmd, &$out=null, &$return=null)
{ {
$return = -1;
Pluf_Log::stime('timer'); Pluf_Log::stime('timer');
$ret = exec($cmd, $out, $return); $fp = popen($cmd, 'r');
$buf = '';
if ($fp !== false) {
while (!feof($fp)) {
$buf .= fread($fp, 1024);
}
$return = pclose($fp);
}
$out = preg_split('/\r\n|\r|\n/', $buf);
$elem = count($out);
if ($elem > 0 && $out[$elem-1] === '')
unset($out[$elem-1]);
Pluf_Log::perf(array($caller, $cmd, Pluf_Log::etime('timer', 'total_exec'))); Pluf_Log::perf(array($caller, $cmd, Pluf_Log::etime('timer', 'total_exec')));
Pluf_Log::debug(array($caller, $cmd, $out)); Pluf_Log::debug(array($caller, $cmd, $out));
Pluf_Log::inc('exec_calls'); Pluf_Log::inc('exec_calls');
return $ret;
} }
/** /**
@@ -325,7 +339,8 @@ class IDF_Scm
* stdClass object { * stdClass object {
* 'additions' => array('path/to/file', 'path/to/directory', ...), * 'additions' => array('path/to/file', 'path/to/directory', ...),
* 'deletions' => array('path/to/file', 'path/to/directory', ...), * 'deletions' => array('path/to/file', 'path/to/directory', ...),
* 'renames' => array('old/path/to/file' => 'new/path/to/file', ...) * 'renames' => array('old/path/to/file' => 'new/path/to/file', ...),
* 'copies' => array('path/to/source' => 'path/to/target', ...),
* 'patches' => array('path/to/file', ...), * 'patches' => array('path/to/file', ...),
* 'properties' => array('path/to/file' => array( * 'properties' => array('path/to/file' => array(
* 'propname' => 'propvalue', 'deletedprop' => null, ...) * 'propname' => 'propvalue', 'deletedprop' => null, ...)

View File

@@ -67,6 +67,7 @@ class IDF_Scm_Git extends IDF_Scm
'additions' => array(), 'additions' => array(),
'deletions' => array(), 'deletions' => array(),
'renames' => array(), 'renames' => array(),
'copies' => array(),
'patches' => array(), 'patches' => array(),
'properties' => array(), 'properties' => array(),
); );
@@ -209,7 +210,7 @@ class IDF_Scm_Git extends IDF_Scm
return $this->cache['tags']; return $this->cache['tags'];
} }
$cmd = Pluf::f('idf_exec_cmd_prefix', '') $cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf('GIT_DIR=%s %s for-each-ref --format="%%(taggerdate:iso)%%(committerdate:iso) %%(objectname) %%(refname)" refs/tags', .sprintf('GIT_DIR=%s %s for-each-ref --format="%%(objectname) %%(refname)" refs/tags',
escapeshellarg($this->repo), escapeshellarg($this->repo),
Pluf::f('git_path', 'git')); Pluf::f('git_path', 'git'));
self::exec('IDF_Scm_Git::getTags', $cmd, $out, $return); self::exec('IDF_Scm_Git::getTags', $cmd, $out, $return);
@@ -218,18 +219,15 @@ class IDF_Scm_Git extends IDF_Scm
$cmd, $return, $cmd, $return,
implode("\n", $out))); implode("\n", $out)));
} }
rsort($out);
$res = array(); $res = array();
foreach ($out as $b) { foreach ($out as $b) {
$elts = explode(' ', $b, 5); $elts = explode(' ', $b, 2);
$tag = substr(trim($elts[4]), 10); $tag = substr(trim($elts[1]), 10); // Remove refs/tags/ prefix
if (false !== strpos($tag, '/')) { $res[$tag] = '';
$res[$elts[3]] = $b;
} else {
$res[$tag] = '';
}
} }
krsort($res);
$this->cache['tags'] = $res; $this->cache['tags'] = $res;
return $res; return $res;
} }
@@ -348,6 +346,14 @@ class IDF_Scm_Git extends IDF_Scm
if (!preg_match('/<(.*)>/', $author, $match)) { if (!preg_match('/<(.*)>/', $author, $match)) {
return null; return null;
} }
// FIXME: newer git versions know a i18n.commitencoding setting which
// leads to another header, "encoding", with which we _could_ try to
// decode the string into utf8. Unfortunately this does not always
// work, especially not in older repos, so we would then still have
// to supply some fallback.
if (!mb_check_encoding($match[1], 'UTF-8')) {
return null;
}
$sql = new Pluf_SQL('login=%s', array($match[1])); $sql = new Pluf_SQL('login=%s', array($match[1]));
$users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen())); $users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
if ($users->count() > 0) { if ($users->count() > 0) {
@@ -501,33 +507,27 @@ class IDF_Scm_Git extends IDF_Scm
"'".$this->mediumtree_fmt."'", "'".$this->mediumtree_fmt."'",
escapeshellarg($commit)); escapeshellarg($commit));
} }
$out = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Git::getCommit', $cmd, $out, $ret); $out = self::shell_exec('IDF_Scm_Git::getCommit', $cmd);
if ($ret != 0 or count($out) == 0) { if (strlen($out) == 0) {
return false; return false;
} }
if ($getdiff) {
$log = array(); $diffStart = false;
$change = array(); if (preg_match('/^diff (?:--git a|--cc)/m', $out, $m, PREG_OFFSET_CAPTURE)) {
$inchange = false; $diffStart = $m[0][1];
foreach ($out as $line) {
if (!$inchange and 0 === strpos($line, 'diff --git a')) {
$inchange = true;
}
if ($inchange) {
$change[] = $line;
} else {
$log[] = $line;
}
}
$out = self::parseLog($log);
$out[0]->diff = implode("\n", $change);
} else {
$out = self::parseLog($out);
$out[0]->diff = '';
} }
$diff = '';
if ($diffStart !== false) {
$log = substr($out, 0, $diffStart);
$diff = substr($out, $diffStart);
} else {
$log = $out;
}
$out = self::parseLog(preg_split('/\r\n|\n/', $log));
$out[0]->diff = $diff;
$out[0]->branch = implode(', ', $this->inBranches($out[0]->commit, null)); $out[0]->branch = implode(', ', $this->inBranches($out[0]->commit, null));
return $out[0]; return $out[0];
} }
@@ -640,7 +640,11 @@ class IDF_Scm_Git extends IDF_Scm
$c['full_message'] = IDF_Commit::toUTF8($c['full_message']); $c['full_message'] = IDF_Commit::toUTF8($c['full_message']);
$c['title'] = IDF_Commit::toUTF8($c['title']); $c['title'] = IDF_Commit::toUTF8($c['title']);
if (isset($c['parents'])) { if (isset($c['parents'])) {
$c['parents'] = explode(' ', trim($c['parents'])); $c['parents'] = preg_split('/ /', trim($c['parents']), -1, PREG_SPLIT_NO_EMPTY);
} else {
// this is actually an error state because we should _always_
// be able to parse the parents line with every git version
$c['parents'] = null;
} }
$res[] = (object) $c; $res[] = (object) $c;
return $res; return $res;

View File

@@ -22,19 +22,77 @@
# ***** END LICENSE BLOCK ***** */ # ***** END LICENSE BLOCK ***** */
/** /**
* Mercurial utils. * A simple RAII helper that manages style files to format hg's log output
*/
class IDF_Scm_Mercurial_LogStyle
{
const FULL_LOG = 1;
const CHANGES = 2;
public function __construct($type)
{
$this->file = tempnam(Pluf::f('tmp_folder'), 'hg-log-style-');
if ($type == self::FULL_LOG) {
$style = 'changeset = "'
. 'changeset: {node|short}\n'
. 'branch: {branch}\n'
. 'author: {author}\n'
. 'date: {date|isodate}\n'
. 'parents: {parents}\n\n'
. '{desc}\n'
. '\0\n"'
. "\n"
. 'parent = "{node|short} "'
. "\n";
} elseif ($type == self::CHANGES) {
$style = 'changeset = "'
. 'file_mods: {file_mods}\n'
. 'file_adds: {file_adds}\n'
. 'file_dels: {file_dels}\n'
. 'file_copies: {file_copies}\n\n'
. '\0\n"'
. "\n"
. 'file_mod = "{file_mod}\0"'
. "\n"
. 'file_add = "{file_add}\0"'
. "\n"
. 'file_del = "{file_del}\0"'
. "\n"
. 'file_copy = "{source}\0{name}\0"'
. "\n";
} else {
throw new IDF_Scm_Exception('invalid type ' . $type);
}
file_put_contents($this->file, $style);
}
public function __destruct()
{
@unlink($this->file);
}
public function get()
{
return $this->file;
}
}
/**
* Main SCM class for Mercurial
* *
* Note: Some commands take a --debug option, this is not lousy coding, but
* totally wanted, as hg returns additional / different data in this
* mode on which this largely depends.
*/ */
class IDF_Scm_Mercurial extends IDF_Scm class IDF_Scm_Mercurial extends IDF_Scm
{ {
protected $hg_log_template;
public function __construct($repo, $project=null) public function __construct($repo, $project=null)
{ {
$this->repo = $repo; $this->repo = $repo;
$this->project = $project; $this->project = $project;
$this->hg_log_template = "'".'changeset: {rev}:{node|short}\nauthor: {author}\ndate: {date|isodate}\nfiles: {files}\n{desc}\n'."'"; }
}
public function getRepositorySize() public function getRepositorySize()
{ {
@@ -158,7 +216,8 @@ class IDF_Scm_Mercurial extends IDF_Scm
throw new Exception(sprintf(__('Not a valid tree: %s.'), $tree)); throw new Exception(sprintf(__('Not a valid tree: %s.'), $tree));
} }
$cmd_tmpl = Pluf::f('hg_path', 'hg').' manifest -R %s --debug -r %s'; $cmd_tmpl = Pluf::f('hg_path', 'hg').' manifest -R %s --debug -r %s';
$cmd = sprintf($cmd_tmpl, escapeshellarg($this->repo), $tree, ($recurse) ? '' : ''); $cmd = sprintf($cmd_tmpl, escapeshellarg($this->repo),
escapeshellarg($tree));
$out = array(); $out = array();
$res = array(); $res = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
@@ -208,7 +267,8 @@ class IDF_Scm_Mercurial extends IDF_Scm
public function getPathInfo($totest, $commit='tip') public function getPathInfo($totest, $commit='tip')
{ {
$cmd_tmpl = Pluf::f('hg_path', 'hg').' manifest -R %s --debug -r %s'; $cmd_tmpl = Pluf::f('hg_path', 'hg').' manifest -R %s --debug -r %s';
$cmd = sprintf($cmd_tmpl, escapeshellarg($this->repo), $commit); $cmd = sprintf($cmd_tmpl, escapeshellarg($this->repo),
escapeshellarg($commit));
$out = array(); $out = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Mercurial::getPathInfo', $cmd, $out); self::exec('IDF_Scm_Mercurial::getPathInfo', $cmd, $out);
@@ -284,7 +344,7 @@ class IDF_Scm_Mercurial extends IDF_Scm
self::exec('IDF_Scm_Mercurial::getBranches', $cmd, $out); self::exec('IDF_Scm_Mercurial::getBranches', $cmd, $out);
$res = array(); $res = array();
foreach ($out as $b) { foreach ($out as $b) {
preg_match('/(\S+).*\S+:(\S+)/', $b, $match); preg_match('/(.+?)\s+\S+:(\S+)/', $b, $match);
$res[$match[1]] = ''; $res[$match[1]] = '';
} }
$this->cache['branches'] = $res; $this->cache['branches'] = $res;
@@ -308,7 +368,7 @@ class IDF_Scm_Mercurial extends IDF_Scm
self::exec('IDF_Scm_Mercurial::getTags', $cmd, $out); self::exec('IDF_Scm_Mercurial::getTags', $cmd, $out);
$res = array(); $res = array();
foreach ($out as $b) { foreach ($out as $b) {
preg_match('/(\S+).*\S+:(\S+)/', $b, $match); preg_match('/(.+?)\s+\S+:(\S+)/', $b, $match);
$res[$match[1]] = ''; $res[$match[1]] = '';
} }
$this->cache['tags'] = $res; $this->cache['tags'] = $res;
@@ -339,33 +399,92 @@ class IDF_Scm_Mercurial extends IDF_Scm
if ($this->validateRevision($commit) != IDF_Scm::REVISION_VALID) { if ($this->validateRevision($commit) != IDF_Scm::REVISION_VALID) {
return false; return false;
} }
$logStyle = new IDF_Scm_Mercurial_LogStyle(IDF_Scm_Mercurial_LogStyle::FULL_LOG);
$tmpl = ($getdiff) $tmpl = ($getdiff)
? Pluf::f('hg_path', 'hg').' log -p -r %s -R %s --template %s' ? Pluf::f('hg_path', 'hg').' log --debug -p -r %s -R %s --style %s'
: Pluf::f('hg_path', 'hg').' log -r %s -R %s --template %s'; : Pluf::f('hg_path', 'hg').' log --debug -r %s -R %s --style %s';
$cmd = sprintf($tmpl, $cmd = sprintf($tmpl,
escapeshellarg($commit), escapeshellarg($commit),
escapeshellarg($this->repo), escapeshellarg($this->repo),
$this->hg_log_template); escapeshellarg($logStyle->get()));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$out = self::shell_exec('IDF_Scm_Mercurial::getCommit', $cmd);
if (strlen($out) == 0) {
return false;
}
$diffStart = strpos($out, 'diff -r');
$diff = '';
if ($diffStart !== false) {
$log = substr($out, 0, $diffStart);
$diff = substr($out, $diffStart);
} else {
$log = $out;
}
$out = self::parseLog(preg_split('/\r\n|\n/', $log));
$out[0]->diff = $diff;
return $out[0];
}
/**
* @see IDF_Scm::getChanges()
*/
public function getChanges($commit)
{
if ($this->validateRevision($commit) != IDF_Scm::REVISION_VALID) {
return null;
}
$logStyle = new IDF_Scm_Mercurial_LogStyle(IDF_Scm_Mercurial_LogStyle::CHANGES);
$tmpl = Pluf::f('hg_path', 'hg').' log --debug -r %s -R %s --style %s';
$cmd = sprintf($tmpl,
escapeshellarg($commit),
escapeshellarg($this->repo),
escapeshellarg($logStyle->get()));
$out = array(); $out = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Mercurial::getCommit', $cmd, $out); self::exec('IDF_Scm_Mercurial::getChanges', $cmd, $out);
$log = array(); $log = self::parseLog($out);
$change = array(); // we expect only one log entry that contains all the needed information
$inchange = false; $log = $log[0];
foreach ($out as $line) {
if (!$inchange and 0 === strpos($line, 'diff -r')) { $return = (object) array(
$inchange = true; 'additions' => preg_split('/\0/', $log->file_adds, -1, PREG_SPLIT_NO_EMPTY),
'deletions' => preg_split('/\0/', $log->file_dels, -1, PREG_SPLIT_NO_EMPTY),
'patches' => preg_split('/\0/', $log->file_mods, -1, PREG_SPLIT_NO_EMPTY),
// hg has no support for built-in attributes, so this keeps empty
'properties' => array(),
// these two are filled below
'copies' => array(),
'renames' => array(),
);
$file_copies = preg_split('/\0/', $log->file_copies, -1, PREG_SPLIT_NO_EMPTY);
// copies are treated as renames if they have an add _and_ a drop;
// only if they only have an add, but no drop, they're treated as copies
for ($i=0; $i<count($file_copies); $i+=2) {
$src = $file_copies[$i];
$trg = $file_copies[$i+1];
$srcidx = array_search($src, $return->deletions);
$trgidx = array_search($trg, $return->additions);
if ($srcidx !== false && $trgidx !== false) {
$return->renames[$src] = $trg;
unset($return->deletions[$srcidx]);
unset($return->additions[$trgidx]);
continue;
} }
if ($inchange) { if ($srcidx === false && $trgidx !== false) {
$change[] = $line; $return->copies[$src] = $trg;
} else { unset($return->additions[$trgidx]);
$log[] = $line; continue;
} }
// file sutures (counter-operation to copy) not supported
} }
$out = self::parseLog($log, 4);
$out[0]->diff = implode("\n", $change); return $return;
return $out[0];
} }
/** /**
@@ -388,54 +507,65 @@ class IDF_Scm_Mercurial extends IDF_Scm
*/ */
public function getChangeLog($commit='tip', $n=10) public function getChangeLog($commit='tip', $n=10)
{ {
$cmd = sprintf(Pluf::f('hg_path', 'hg').' log -R %s -l%s --template %s', escapeshellarg($this->repo), $n, $this->hg_log_template, $commit); $logStyle = new IDF_Scm_Mercurial_LogStyle(IDF_Scm_Mercurial_LogStyle::FULL_LOG);
// hg accepts revision IDs as arguments to --branch / -b as well and
// uses the branch of the revision in question to filter the other
// revisions
$cmd = sprintf(Pluf::f('hg_path', 'hg').' log --debug -R %s -l%s --style %s -b %s',
escapeshellarg($this->repo),
$n,
escapeshellarg($logStyle->get()),
escapeshellarg($commit));
$out = array(); $out = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Mercurial::getChangeLog', $cmd, $out); self::exec('IDF_Scm_Mercurial::getChangeLog', $cmd, $out);
return self::parseLog($out, 4); return self::parseLog($out);
} }
/** /**
* Parse the log lines of a --pretty=medium log output. * Parse the log lines of our custom style format.
* *
* @param array Lines. * @param array Lines.
* @param int Number of lines in the headers (3)
* @return array Change log. * @return array Change log.
*/ */
public static function parseLog($lines)
public static function parseLog($lines, $hdrs=3)
{ {
$res = array(); $res = array();
$c = array(); $c = array();
$i = 0; $headers_processed = false;
$hdrs += 1;
foreach ($lines as $line) { foreach ($lines as $line) {
$i++; if ($line == "\0") {
if (0 === strpos($line, 'changeset:')) { $headers_processed = false;
if (count($c) > 0) { if (count($c) > 0) {
$c['full_message'] = trim($c['full_message']); if (array_key_exists('full_message', $c))
$c['full_message'] = trim($c['full_message']);
$res[] = (object) $c; $res[] = (object) $c;
} }
$c = array();
$c['commit'] = substr(strrchr($line, ':'), 1);
$c['full_message'] = '';
$i=1;
continue;
}
if ($i == $hdrs) {
$c['title'] = trim($line);
continue; continue;
} }
$match = array(); if (!$headers_processed && empty($line)) {
if (preg_match('/^(\S+):\s*(.*)/', $line, $match)) { $headers_processed = true;
continue;
}
if (!$headers_processed && preg_match('/^(\S+):\s*(.*)/', $line, $match)) {
$match[1] = strtolower($match[1]); $match[1] = strtolower($match[1]);
if ($match[1] == 'user') { if ($match[1] == 'changeset') {
$c = array();
$c['commit'] = $match[2];
$c['tree'] = $c['commit'];
$c['full_message'] = '';
} elseif ($match[1] == 'author') {
$c['author'] = $match[2]; $c['author'] = $match[2];
} elseif ($match[1] == 'summary') {
$c['title'] = $match[2];
} elseif ($match[1] == 'branch') { } elseif ($match[1] == 'branch') {
$c['branch'] = $match[2]; $c['branch'] = empty($match[2]) ? 'default' : $match[2];
} elseif ($match[1] == 'parents') {
$parents = preg_split('/\s+/', $match[2], -1, PREG_SPLIT_NO_EMPTY);
for ($i=0, $j=count($parents); $i<$j; ++$i) {
if ($parents[$i] == '000000000000')
unset($parents[$i]);
}
$c['parents'] = $parents;
} else { } else {
$c[$match[1]] = trim($match[2]); $c[$match[1]] = trim($match[2]);
} }
@@ -444,15 +574,14 @@ class IDF_Scm_Mercurial extends IDF_Scm
} }
continue; continue;
} }
if ($i > ($hdrs+1)) { if ($headers_processed) {
$c['full_message'] .= trim($line)."\n"; if (empty($c['title']))
$c['title'] = trim($line);
else
$c['full_message'] .= trim($line)."\n";
continue; continue;
} }
} }
$c['tree'] = !empty($c['commit']) ? trim($c['commit']) : '';
$c['branch'] = empty($c['branch']) ? 'default' : $c['branch'];
$c['full_message'] = !empty($c['full_message']) ? trim($c['full_message']) : '';
$res[] = (object) $c;
return $res; return $res;
} }

View File

@@ -31,10 +31,10 @@ class IDF_Scm_Monotone extends IDF_Scm
/** the minimum supported interface version */ /** the minimum supported interface version */
public static $MIN_INTERFACE_VERSION = 13.0; public static $MIN_INTERFACE_VERSION = 13.0;
private $stdio;
private static $instances = array(); private static $instances = array();
private $stdio;
/** /**
* Constructor * Constructor
*/ */
@@ -609,6 +609,7 @@ class IDF_Scm_Monotone extends IDF_Scm
'additions' => array(), 'additions' => array(),
'deletions' => array(), 'deletions' => array(),
'renames' => array(), 'renames' => array(),
'copies' => array(),
'patches' => array(), 'patches' => array(),
'properties' => array(), 'properties' => array(),
); );
@@ -698,6 +699,29 @@ class IDF_Scm_Monotone extends IDF_Scm
return (object) $res; return (object) $res;
} }
/**
* @see IDF_Scm::getProperties()
*/
public function getProperties($rev, $path='')
{
$out = $this->stdio->exec(array('interface_version'));
// support for querying file attributes of committed revisions
// was added for mtn 1.1 (interface version 13.1)
if (floatval($out) < 13.1)
return array();
$out = $this->stdio->exec(array('get_attributes', $path), array('r' => $rev));
$stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
$res = array();
foreach ($stanzas as $stanza) {
$line = $stanza[0];
$res[$line['values'][0]] = $line['values'][1];
}
return $res;
}
/** /**
* @see IDF_Scm::getExtraProperties * @see IDF_Scm::getExtraProperties
*/ */

View File

@@ -48,7 +48,7 @@ class IDF_Scm_Monotone_Usher
if ($conn == 'none') if ($conn == 'none')
return array(); return array();
return preg_split('/[ ]/', $conn); return preg_split('/[ ]/', $conn, -1, PREG_SPLIT_NO_EMPTY);
} }
/** /**
@@ -73,10 +73,10 @@ class IDF_Scm_Monotone_Usher
if ($conn == 'none') if ($conn == 'none')
return array(); return array();
$single_conns = preg_split('/[ ]/', $conn); $single_conns = preg_split('/[ ]/', $conn, -1, PREG_SPLIT_NO_EMPTY);
$ret = array(); $ret = array();
foreach ($single_conns as $conn) { foreach ($single_conns as $conn) {
preg_match('/\((\w+)\)([^:]+):(\d+)/', $conn, $matches); preg_match('/\(([^)]+)\)([^:]+):(\d+)/', $conn, $matches);
$ret[$matches[1]][] = (object)array( $ret[$matches[1]][] = (object)array(
'server' => $matches[1], 'server' => $matches[1],
'address' => $matches[2], 'address' => $matches[2],

View File

@@ -33,7 +33,6 @@
*/ */
class IDF_Scm_Svn extends IDF_Scm class IDF_Scm_Svn extends IDF_Scm
{ {
public $username = ''; public $username = '';
public $password = ''; public $password = '';
private $assoc = array('dir' => 'tree', private $assoc = array('dir' => 'tree',
@@ -48,11 +47,7 @@ class IDF_Scm_Svn extends IDF_Scm
public function isAvailable() public function isAvailable()
{ {
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --xml --username=%s --password=%s %s', $cmd = $this->svnCmd(array('info', '--xml'), $this->repo);
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlInfo = self::shell_exec('IDF_Scm_Svn::isAvailable', $cmd); $xmlInfo = self::shell_exec('IDF_Scm_Svn::isAvailable', $cmd);
try { try {
@@ -163,12 +158,7 @@ class IDF_Scm_Svn extends IDF_Scm
return IDF_Scm::REVISION_VALID; return IDF_Scm::REVISION_VALID;
} }
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --username=%s --password=%s %s@%s', $cmd = $this->svnCmd(array('info'), $this->repo, $rev);
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($rev));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Svn::validateRevision', $cmd, $out, $ret); self::exec('IDF_Scm_Svn::validateRevision', $cmd, $out, $ret);
if ($ret == 0) if ($ret == 0)
@@ -176,7 +166,6 @@ class IDF_Scm_Svn extends IDF_Scm
return IDF_Scm::REVISION_INVALID; return IDF_Scm::REVISION_INVALID;
} }
/** /**
* Test a given object hash. * Test a given object hash.
* *
@@ -191,12 +180,9 @@ class IDF_Scm_Svn extends IDF_Scm
} }
// Else, test the path on revision // Else, test the path on revision
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --xml --username=%s --password=%s %s@%s', $cmd = $this->svnCmd(array('info', '--xml'),
escapeshellarg($this->username), $this->repo.'/'.self::smartEncode($path),
escapeshellarg($this->password), $rev);
escapeshellarg($this->repo.'/'.self::smartEncode($path)),
escapeshellarg($rev));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlInfo = self::shell_exec('IDF_Scm_Svn::testHash', $cmd); $xmlInfo = self::shell_exec('IDF_Scm_Svn::testHash', $cmd);
// If exception is thrown, return false // If exception is thrown, return false
@@ -218,12 +204,9 @@ class IDF_Scm_Svn extends IDF_Scm
public function getTree($commit, $folder='/', $branch=null) public function getTree($commit, $folder='/', $branch=null)
{ {
$cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --xml --username=%s --password=%s %s@%s', $cmd = $this->svnCmd(array('ls', '--xml'),
escapeshellarg($this->username), $this->repo.'/'.self::smartEncode($folder),
escapeshellarg($this->password), $commit);
escapeshellarg($this->repo.'/'.self::smartEncode($folder)),
escapeshellarg($commit));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getTree', $cmd)); $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getTree', $cmd));
$res = array(); $res = array();
$folder = (strlen($folder) and ($folder != '/')) ? $folder.'/' : ''; $folder = (strlen($folder) and ($folder != '/')) ? $folder.'/' : '';
@@ -248,7 +231,6 @@ class IDF_Scm_Svn extends IDF_Scm
return $res; return $res;
} }
/** /**
* Get the commit message of a revision revision. * Get the commit message of a revision revision.
* *
@@ -260,12 +242,7 @@ class IDF_Scm_Svn extends IDF_Scm
if (isset($this->cache['commitmess'][$rev])) { if (isset($this->cache['commitmess'][$rev])) {
return $this->cache['commitmess'][$rev]; return $this->cache['commitmess'][$rev];
} }
$cmd = sprintf(Pluf::f('svn_path', 'svn').' log --xml --limit 1 --username=%s --password=%s %s@%s', $cmd = $this->svnCmd(array('log', '--xml', '--limit', '1'), $this->repo, $rev);
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($rev));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
try { try {
$xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getCommitMessage', $cmd)); $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getCommitMessage', $cmd));
$this->cache['commitmess'][$rev] = (string) $xml->logentry->msg; $this->cache['commitmess'][$rev] = (string) $xml->logentry->msg;
@@ -281,12 +258,8 @@ class IDF_Scm_Svn extends IDF_Scm
if ($rev == null) { if ($rev == null) {
$rev = 'HEAD'; $rev = 'HEAD';
} }
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --xml --username=%s --password=%s %s@%s', $cmd = $this->svnCmd(array('info', '--xml'),
escapeshellarg($this->username), $this->repo.'/'.self::smartEncode($filename), $rev);
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.self::smartEncode($filename)),
escapeshellarg($rev));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getPathInfo', $cmd)); $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getPathInfo', $cmd));
if (!isset($xml->entry)) { if (!isset($xml->entry)) {
return false; return false;
@@ -308,12 +281,9 @@ class IDF_Scm_Svn extends IDF_Scm
public function getFile($def, $cmd_only=false) public function getFile($def, $cmd_only=false)
{ {
$cmd = sprintf(Pluf::f('svn_path', 'svn').' cat --username=%s --password=%s %s@%s', $cmd = $this->svnCmd(array('cat'),
escapeshellarg($this->username), $this->repo.'/'.self::smartEncode($def->fullpath),
escapeshellarg($this->password), $def->rev);
escapeshellarg($this->repo.'/'.self::smartEncode($def->fullpath)),
escapeshellarg($def->rev));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
return ($cmd_only) ? return ($cmd_only) ?
$cmd : self::shell_exec('IDF_Scm_Svn::getFile', $cmd); $cmd : self::shell_exec('IDF_Scm_Svn::getFile', $cmd);
} }
@@ -329,11 +299,7 @@ class IDF_Scm_Svn extends IDF_Scm
return $this->cache['branches']; return $this->cache['branches'];
} }
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --username=%s --password=%s %s@HEAD', $cmd = $this->svnCmd(array('ls'), $this->repo.'/branches', 'HEAD');
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/branches'));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Svn::getBranches', $cmd, $out, $ret); self::exec('IDF_Scm_Svn::getBranches', $cmd, $out, $ret);
if ($ret == 0) { if ($ret == 0) {
foreach ($out as $entry) { foreach ($out as $entry) {
@@ -344,11 +310,8 @@ class IDF_Scm_Svn extends IDF_Scm
} }
} }
ksort($res); ksort($res);
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --username=%s --password=%s %s@HEAD',
escapeshellarg($this->username), $cmd = $this->svnCmd(array('info'), $this->repo.'/trunk', 'HEAD');
escapeshellarg($this->password),
escapeshellarg($this->repo.'/trunk'));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Svn::getBranches', $cmd, $out, $ret); self::exec('IDF_Scm_Svn::getBranches', $cmd, $out, $ret);
if ($ret == 0) { if ($ret == 0) {
$res = array('trunk' => 'trunk') + $res; $res = array('trunk' => 'trunk') + $res;
@@ -368,11 +331,7 @@ class IDF_Scm_Svn extends IDF_Scm
return $this->cache['tags']; return $this->cache['tags'];
} }
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --username=%s --password=%s %s@HEAD', $cmd = $this->svnCmd(array('ls'), $this->repo.'/tags', 'HEAD');
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/tags'));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Svn::getTags', $cmd, $out, $ret); self::exec('IDF_Scm_Svn::getTags', $cmd, $out, $ret);
if ($ret == 0) { if ($ret == 0) {
foreach ($out as $entry) { foreach ($out as $entry) {
@@ -412,7 +371,6 @@ class IDF_Scm_Svn extends IDF_Scm
return array(); return array();
} }
/** /**
* Get commit details. * Get commit details.
* *
@@ -426,12 +384,8 @@ class IDF_Scm_Svn extends IDF_Scm
return false; return false;
} }
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' log --xml --limit 1 -v --username=%s --password=%s %s@%s', $cmd = $this->svnCmd(array('log', '--xml', '--limit', '1', '-v'),
escapeshellarg($this->username), $this->repo, $commit);
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($commit));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlRes = self::shell_exec('IDF_Scm_Svn::getCommit', $cmd); $xmlRes = self::shell_exec('IDF_Scm_Svn::getCommit', $cmd);
$xml = simplexml_load_string($xmlRes); $xml = simplexml_load_string($xmlRes);
$res['author'] = (string) $xml->logentry->author; $res['author'] = (string) $xml->logentry->author;
@@ -473,15 +427,87 @@ class IDF_Scm_Svn extends IDF_Scm
private function getDiff($rev='HEAD') private function getDiff($rev='HEAD')
{ {
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' diff -c %s --username=%s --password=%s %s', $cmd = $this->svnCmd(array('diff', '-c', $rev), $this->repo);
escapeshellarg($rev),
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
return self::shell_exec('IDF_Scm_Svn::getDiff', $cmd); return self::shell_exec('IDF_Scm_Svn::getDiff', $cmd);
} }
/**
* @see IDF_Scm::getChanges()
*/
public function getChanges($commit)
{
if ($this->validateRevision($commit) != IDF_Scm::REVISION_VALID) {
return null;
}
$cmd = $this->svnCmd(array('log', '--xml', '-v'), $this->repo, $commit);
$out = array();
$out = self::shell_exec('IDF_Scm_Svn::getChanges', $cmd);
$xml = simplexml_load_string($out);
if (count($xml) == 0) {
return null;
}
$entry = current($xml);
$return = (object) array(
'additions' => array(),
'deletions' => array(),
'patches' => array(),
// while SVN has support for attributes, we cannot see their changes
// in the log's XML unfortunately
'properties' => array(),
'copies' => array(),
'renames' => array(),
);
foreach ($entry->paths->path as $p) {
$path = (string) $p;
foreach ($p->attributes() as $k => $v) {
$key = (string) $k;
$val = (string) $v;
if ($key != 'action')
continue;
if ($val == 'M')
$return->patches[] = $path;
else if ($val == 'A')
$return->additions[] = $path;
else if ($val == 'D')
$return->deletions[] = $path;
}
}
// copies are treated as renames if they have an add _and_ a drop;
// only if they only have an add, but no drop, they're treated as copies
foreach ($entry->paths->path as $p) {
$trg = (string) $p;
$src = null;
foreach ($p->attributes() as $k => $v) {
if ((string) $k == 'copyfrom-path') {
$src = (string) $v;
break;
}
}
if ($src == null)
continue;
$srcidx = array_search($src, $return->deletions);
$trgidx = array_search($trg, $return->additions);
if ($srcidx !== false && $trgidx !== false) {
$return->renames[$src] = $trg;
unset($return->deletions[$srcidx]);
unset($return->additions[$trgidx]);
continue;
}
if ($srcidx === false && $trgidx !== false) {
$return->copies[$src] = $trg;
unset($return->additions[$trgidx]);
continue;
}
// file sutures (counter-operation to copy) not supported
}
return $return;
}
/** /**
* Get latest changes. * Get latest changes.
@@ -491,20 +517,15 @@ class IDF_Scm_Svn extends IDF_Scm
* *
* @return array Changes. * @return array Changes.
*/ */
public function getChangeLog($branch=null, $n=10) public function getChangeLog($rev=null, $n=10)
{ {
if ($branch != 'HEAD' and !preg_match('/^\d+$/', $branch)) { if ($rev != 'HEAD' and !preg_match('/^\d+$/', $rev)) {
// we accept only revisions or HEAD // we accept only revisions or HEAD
$branch = 'HEAD'; $rev = 'HEAD';
} }
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' log --xml -v --limit %s --username=%s --password=%s %s@%s', $cmd = $this->svnCmd(array('log', '--xml', '-v', '--limit', $n),
escapeshellarg($n), $this->repo.'@'.$rev);
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($branch));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlRes = self::shell_exec('IDF_Scm_Svn::getChangeLog', $cmd); $xmlRes = self::shell_exec('IDF_Scm_Svn::getChangeLog', $cmd);
$xml = simplexml_load_string($xmlRes); $xml = simplexml_load_string($xmlRes);
foreach ($xml->logentry as $entry) { foreach ($xml->logentry as $entry) {
@@ -520,7 +541,6 @@ class IDF_Scm_Svn extends IDF_Scm
return $res; return $res;
} }
/** /**
* Get additionnals properties on path and revision * Get additionnals properties on path and revision
* *
@@ -531,12 +551,8 @@ class IDF_Scm_Svn extends IDF_Scm
public function getProperties($rev, $path='') public function getProperties($rev, $path='')
{ {
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' proplist --xml --username=%s --password=%s %s@%s', $cmd = $this->svnCmd(array('proplist', '--xml'),
escapeshellarg($this->username), $this->repo.'/'.self::smartEncode($path), $rev);
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.self::smartEncode($path)),
escapeshellarg($rev));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlProps = self::shell_exec('IDF_Scm_Svn::getProperties', $cmd); $xmlProps = self::shell_exec('IDF_Scm_Svn::getProperties', $cmd);
$props = simplexml_load_string($xmlProps); $props = simplexml_load_string($xmlProps);
@@ -554,7 +570,6 @@ class IDF_Scm_Svn extends IDF_Scm
return $res; return $res;
} }
/** /**
* Get a specific additionnal property on path and revision * Get a specific additionnal property on path and revision
* *
@@ -566,20 +581,14 @@ class IDF_Scm_Svn extends IDF_Scm
private function getProperty($property, $rev, $path='') private function getProperty($property, $rev, $path='')
{ {
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' propget --xml %s --username=%s --password=%s %s@%s', $cmd = $this->svnCmd(array('propget', $property, '--xml'),
escapeshellarg($property), $this->repo.'/'.self::smartEncode($path), $rev);
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.self::smartEncode($path)),
escapeshellarg($rev));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlProp = self::shell_exec('IDF_Scm_Svn::getProperty', $cmd); $xmlProp = self::shell_exec('IDF_Scm_Svn::getProperty', $cmd);
$prop = simplexml_load_string($xmlProp); $prop = simplexml_load_string($xmlProp);
return (string) $prop->target->property; return (string) $prop->target->property;
} }
/** /**
* Get the number of the last commit in the repository. * Get the number of the last commit in the repository.
* *
@@ -590,16 +599,38 @@ class IDF_Scm_Svn extends IDF_Scm
public function getLastCommit($rev='HEAD') public function getLastCommit($rev='HEAD')
{ {
$xmlInfo = ''; $xmlInfo = '';
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --xml --username=%s --password=%s %s@%s', $cmd = $this->svnCmd(array('info', '--xml'), $this->repo, $rev);
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($rev));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlInfo = self::shell_exec('IDF_Scm_Svn::getLastCommit', $cmd); $xmlInfo = self::shell_exec('IDF_Scm_Svn::getLastCommit', $cmd);
$xml = simplexml_load_string($xmlInfo); $xml = simplexml_load_string($xmlInfo);
return (string) $xml->entry->commit['revision']; return (string) $xml->entry->commit['revision'];
} }
private function svnCmd($args = array(), $repoarg = null, $revarg = null)
{
$cmdline = array();
$cmdline[] = Pluf::f('idf_exec_cmd_prefix', '');
$cmdline[] = Pluf::f('svn_path', 'svn');
$cmdline[] = '--no-auth-cache';
$cmdline[] = '--username='.escapeshellarg($this->username);
$cmdline[] = '--password='.escapeshellarg($this->password);
foreach ($args as $arg) {
$cmdline[] = escapeshellarg($arg);
}
if ($repoarg != null) {
if ($revarg != null) {
$repoarg .= '@'.$revarg;
}
$cmdline[] = escapeshellarg($repoarg);
}
if ($revarg != null) {
$cmdline[] = '--revision='.escapeshellarg($revarg);
}
return implode(' ', $cmdline);
}
} }

View File

@@ -58,7 +58,7 @@ class IDF_Template_IssueComment extends Pluf_Template_Tag
implode('|', $nouns); implode('|', $nouns);
$text = IDF_Template_safePregReplace('#((?:'.$prefix.')(?:\s+r?))([0-9a-f]{1,40}((?:\s+and|\s+or|,)\s+r?[0-9a-f]{1,40})*)\b#i', $text = IDF_Template_safePregReplace('#((?:'.$prefix.')(?:\s+r?))([0-9a-f]{1,40}((?:\s+and|\s+or|,)\s+r?[0-9a-f]{1,40})*)\b#i',
array($this, 'callbackCommits'), $text); array($this, 'callbackCommits'), $text);
$text = IDF_Template_safePregReplace('=(src:)([^\s@#,\(\)\\\\]+(?:(\\\\)[\s@#][^\s@#,\(\)\\\\]+){0,})+(?:\@([^\s#,]+))(?:#(\d+))?=im', $text = IDF_Template_safePregReplace('=(src:)([^\s@#,\(\)\\\\]+(?:(\\\\)[\s@#][^\s@#,\(\)\\\\]+){0,})+(?:\@([^\s#,]+))?(?:#(\d+))?=im',
array($this, 'callbackSource'), $text); array($this, 'callbackSource'), $text);
} }
if ($wordwrap) $text = Pluf_Text::wrapHtml($text, 69, "\n"); if ($wordwrap) $text = Pluf_Text::wrapHtml($text, 69, "\n");

View File

@@ -90,40 +90,69 @@ class IDF_Template_MarkdownPrefilter extends Pluf_Text_HTML_Filter
); );
public $allowed = array( public $allowed = array(
'a' => array('href', 'title', 'rel'), 'a' => array('class', 'dir', 'id', 'style', 'title',
'abbr' => array('title'), 'href', 'hreflang', 'rel'),
'address' => array(), 'abbr' => array('class', 'dir', 'id', 'style', 'title'),
'b' => array(), 'address' => array('class', 'dir', 'id', 'style', 'title'),
'blockquote' => array(), 'b' => array('class', 'dir', 'id', 'style', 'title'),
'br' => array(), 'blockquote' => array('class', 'dir', 'id', 'style', 'title',
'caption' => array(), 'cite'),
'code' => array(), 'br' => array('class', 'id', 'style', 'title'),
'dd' => array(), 'caption' => array('class', 'dir', 'id', 'style', 'title',
'del' => array('cite', 'class', 'datetime', 'dir', 'id', 'title'), 'align'), // deprecated attribute),
'div' => array('align', 'class'), 'code' => array('class', 'dir', 'id', 'style', 'title'),
'dl' => array(), 'dd' => array('class', 'dir', 'id', 'style', 'title'),
'dt' => array(), 'del' => array('class', 'dir', 'id', 'style', 'title',
'em' => array(), 'cite', 'datetime'),
'h1' => array('id'), 'div' => array('class', 'dir', 'id', 'style', 'title',
'h2' => array('id'), 'align'), // deprecated attribute
'h3' => array('id'), 'dl' => array('class', 'dir', 'id', 'style', 'title'),
'h4' => array('id'), 'dt' => array('class', 'dir', 'id', 'style', 'title'),
'h5' => array('id'), 'em' => array('class', 'dir', 'id', 'style', 'title'),
'h6' => array('id'), 'font' => array('class', 'dir', 'id', 'style', 'title', // deprecated element
'hr' => array(), 'color', 'face', 'size'), // deprecated attribute
'i' => array(), 'h1' => array('class', 'dir', 'id', 'style', 'title',
'img' => array('src', 'class', 'alt', 'height', 'width', 'style'), 'align'), // deprecated attribute
'ins' => array('cite', 'class', 'datetime', 'dir', 'id', 'title'), 'h2' => array('class', 'dir', 'id', 'style', 'title',
'li' => array(), 'align'), // deprecated attribute
'ol' => array(), 'h3' => array('class', 'dir', 'id', 'style', 'title',
'p' => array('align', 'class'), 'align'), // deprecated attribute
'pre' => array(), 'h4' => array('class', 'dir', 'id', 'style', 'title',
'strong' => array(), 'align'), // deprecated attribute
'table' => array('summary'), 'h5' => array('class', 'dir', 'id', 'style', 'title',
'td' => array('style'), 'align'), // deprecated attribute
'th' => array(), 'h6' => array('class', 'dir', 'id', 'style', 'title',
'tr' => array(), 'align'), // deprecated attribute
'ul' => array(), 'hr' => array('class', 'dir', 'id', 'style', 'title',
'align', 'noshade', 'size', 'width'), // deprecated attribute
'i' => array('class', 'dir', 'id', 'style', 'title'),
'img' => array('class', 'dir', 'id', 'style', 'title',
'src', 'alt', 'height', 'width'),
'ins' => array('class', 'dir', 'id', 'style', 'title',
'cite', 'datetime'),
'li' => array('class', 'dir', 'id', 'style', 'title',
'type'), // deprecated attribute
'ol' => array('class', 'dir', 'id', 'style', 'title',
'type'), // deprecated attribute
'p' => array('class', 'dir', 'id', 'style', 'title',
'align'), // deprecated attribute
'pre' => array('class', 'dir', 'id', 'style', 'title',
'width'), // deprecated attribute
'strong' => array('class', 'dir', 'id', 'style', 'title'),
'table' => array('class', 'dir', 'id', 'style', 'title',
'border', 'cellpadding', 'cellspacing', 'frame', 'rules', 'summary', 'width',
'align', 'bgcolor'), // deprecated attribute
'td' => array('class', 'dir', 'id', 'style', 'title',
'align', 'colspan', 'headers', 'rowspan', 'scope', 'valign',
'bgcolor', 'height', 'nowrap', 'width'), // deprecated attribute
'th' => array('class', 'dir', 'id', 'style', 'title',
'align', 'colspan', 'rowspan', 'scope', 'valign',
'bgcolor', 'height', 'nowrap', 'width'), // deprecated attribute
'tr' => array('class', 'dir', 'id', 'style', 'title',
'align', 'valign',
'bgcolor'), // deprecated attribute
'ul' => array('class', 'dir', 'id', 'style', 'title',
'type'), // deprecated attribute
); );
// tags which should always be self-closing (e.g. "<img />") // tags which should always be self-closing (e.g. "<img />")
public $no_close = array( public $no_close = array(

View File

@@ -0,0 +1,35 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2011 Céondo Ltd and contributors.
#
# InDefero 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.
#
# InDefero 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 St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
class IDF_Template_Tag_UploadUrl extends Pluf_Template_Tag
{
function start($file='')
{
echo IDF_Template_Tag_UploadUrl::url($file);
}
public static function url($file='')
{
return Pluf::f('url_upload', Pluf_Template_Tag_MediaUrl::url() . '/upload') . $file;
}
}

View File

@@ -32,21 +32,24 @@ class IDF_Tests_TestDiff extends UnitTestCase
parent::__construct('Test the diff parser.'); parent::__construct('Test the diff parser.');
} }
public function testBinaryDiff() //
{ // IDF_Diff::mergeChunks() is now private, so this test needs to be rewritten
$diff_content = file_get_contents(dirname(__FILE__).'/test-diff.diff'); //
$orig = file_get_contents(dirname(__FILE__).'/test-diff-view.html'); //public function testBinaryDiff()
$diff = new IDF_Diff($diff_content); //{
$diff->parse(); // $diff_content = file_get_contents(dirname(__FILE__).'/test-diff.diff');
$def = $diff->files['src/IDF/templates/idf/issues/view.html']; // $orig = file_get_contents(dirname(__FILE__).'/test-diff-view.html');
// $diff = new IDF_Diff($diff_content);
$orig_lines = preg_split("/\015\012|\015|\012/", $orig); // $diff->parse();
$merged = $diff->mergeChunks($orig_lines, $def, 10); // $def = $diff->files['src/IDF/templates/idf/issues/view.html'];
$lchunk = end($merged); //
$lline = end($lchunk); // $orig_lines = preg_split("/\015\012|\015|\012/", $orig);
$this->assertEqual(array('', '166', '{/if}{/block}'), // $merged = $diff->mergeChunks($orig_lines, $def, 10);
$lline); // $lchunk = end($merged);
} // $lline = end($lchunk);
// $this->assertEqual(array('', '166', '{/if}{/block}'),
// $lline);
//}
public function testDiffWithHeaders() public function testDiffWithHeaders()
{ {

View File

@@ -77,6 +77,12 @@ class IDF_Upload extends Pluf_Model
'default' => 0, 'default' => 0,
'verbose' => __('file size in bytes'), 'verbose' => __('file size in bytes'),
), ),
'md5' =>
array(
'type' => 'Pluf_DB_Field_Text',
'blank' => true,
'verbose' => __('MD5'),
),
'submitter' => 'submitter' =>
array( array(
'type' => 'Pluf_DB_Field_Foreignkey', 'type' => 'Pluf_DB_Field_Foreignkey',
@@ -144,6 +150,7 @@ class IDF_Upload extends Pluf_Model
if ($this->id == '') { if ($this->id == '') {
$this->creation_dtime = gmdate('Y-m-d H:i:s'); $this->creation_dtime = gmdate('Y-m-d H:i:s');
$this->modif_dtime = gmdate('Y-m-d H:i:s'); $this->modif_dtime = gmdate('Y-m-d H:i:s');
$this->md5 = md5_file ($this->getFullPath());
} }
} }
@@ -160,6 +167,11 @@ class IDF_Upload extends Pluf_Model
return Pluf::f('url_upload').'/'.$project->shortname.'/files/'.$this->file; return Pluf::f('url_upload').'/'.$project->shortname.'/files/'.$this->file;
} }
function getFullPath()
{
return(Pluf::f('upload_path').'/'.$this->get_project()->shortname.'/files/'.$this->file);
}
/** /**
* We drop the information from the timeline. * We drop the information from the timeline.
*/ */
@@ -249,4 +261,4 @@ class IDF_Upload extends Pluf_Model
} }
Pluf_Translation::loadSetLocale($current_locale); Pluf_Translation::loadSetLocale($current_locale);
} }
} }

View File

@@ -59,7 +59,7 @@ class IDF_Views_Download
$sql .= ' AND id NOT IN ('.implode(',', $ids).')'; $sql .= ' AND id NOT IN ('.implode(',', $ids).')';
} }
$pag->forced_where = new Pluf_SQL($sql, array($prj->id)); $pag->forced_where = new Pluf_SQL($sql, array($prj->id));
$list_display = array( $list_display = array(
'file' => __('File'), 'file' => __('File'),
array('summary', 'IDF_Views_Download_SummaryAndLabels', __('Summary')), array('summary', 'IDF_Views_Download_SummaryAndLabels', __('Summary')),
@@ -79,7 +79,7 @@ class IDF_Views_Download
'dlabel' => $dtag, 'dlabel' => $dtag,
), ),
$request); $request);
} }
/** /**
@@ -99,17 +99,17 @@ class IDF_Views_Download
$deprecated = Pluf_Model_InArray($dtag, $tags); $deprecated = Pluf_Model_InArray($dtag, $tags);
if ($request->method == 'POST' and if ($request->method == 'POST' and
true === IDF_Precondition::projectMemberOrOwner($request)) { true === IDF_Precondition::projectMemberOrOwner($request)) {
$form = new IDF_Form_UpdateUpload($request->POST, $form = new IDF_Form_UpdateUpload($request->POST,
array('project' => $prj, array('project' => $prj,
'upload' => $upload, 'upload' => $upload,
'user' => $request->user)); 'user' => $request->user));
if ($form->isValid()) { if ($form->isValid()) {
$upload = $form->save(); $upload = $form->save();
$urlfile = Pluf_HTTP_URL_urlForView('IDF_Views_Download::view', $urlfile = Pluf_HTTP_URL_urlForView('IDF_Views_Download::view',
array($prj->shortname, $upload->id)); array($prj->shortname, $upload->id));
$request->user->setMessage(sprintf(__('The file <a href="%1$s">%2$s</a> has been updated.'), $urlfile, Pluf_esc($upload->file))); $request->user->setMessage(sprintf(__('The file <a href="%1$s">%2$s</a> has been updated.'), $urlfile, Pluf_esc($upload->file)));
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::index', $url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::index',
array($prj->shortname)); array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url); return new Pluf_HTTP_Response_Redirect($url);
} }
@@ -162,7 +162,7 @@ class IDF_Views_Download
* [description] * [description]
* *
* This signal allows an application to perform a set of tasks * This signal allows an application to perform a set of tasks
* just before the deletion of the corresponding object in the * just before the deletion of the corresponding object in the
* database but just after the deletion from the storage. * database but just after the deletion from the storage.
* *
* [parameters] * [parameters]
@@ -171,11 +171,11 @@ class IDF_Views_Download
* *
*/ */
$params = array('upload' => $upload); $params = array('upload' => $upload);
Pluf_Signal::send('IDF_Upload::delete', Pluf_Signal::send('IDF_Upload::delete',
'IDF_Views_Download', $params); 'IDF_Views_Download', $params);
$upload->delete(); $upload->delete();
$request->user->setMessage(__('The file has been deleted.')); $request->user->setMessage(__('The file has been deleted.'));
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::index', $url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::index',
array($prj->shortname)); array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url); return new Pluf_HTTP_Response_Redirect($url);
} }
@@ -190,17 +190,37 @@ class IDF_Views_Download
} }
/** /**
* Download a file. * Download the file with the given name.
*/ */
public $download_precond = array('IDF_Precondition::accessDownloads'); public $download_precond = array('IDF_Precondition::accessDownloads');
public function download($request, $match) public function download($request, $match)
{ {
$prj = $request->project; $prj = $request->project;
$upload = Pluf_Shortcuts_GetObjectOr404('IDF_Upload', $match[2]); $sql = new Pluf_SQL('file=%s', array($match[2]));
$upload = Pluf::factory('IDF_Upload')->getOne(array('filter' => $sql->gen()));
if (!$upload) throw new Pluf_HTTP_Error404();
$prj->inOr404($upload); $prj->inOr404($upload);
$upload->downloads += 1; $upload->downloads += 1;
$upload->update(); $upload->update();
return new Pluf_HTTP_Response_Redirect($upload->getAbsoluteUrl($prj)); $path = $upload->getFullPath();
$mime = IDF_FileUtil::getMimeType($path);
$render = new Pluf_HTTP_Response_File($path, $mime[0]);
$render->headers["Content-MD5"] = $upload->md5;
return $render;
}
/**
* Download the file with the given ID (for legacy links).
*/
public $downloadById_precond = array('IDF_Precondition::accessDownloads');
public function downloadById($request, $match)
{
$upload = Pluf_Shortcuts_GetObjectOr404('IDF_Upload', $match[2]);
return new Pluf_HTTP_Response_Redirect(
Pluf_HTTP_URL_urlForView('IDF_Views_Download::download', array(
$match[1], $upload->file
)), 301
);
} }
/** /**
@@ -218,10 +238,10 @@ class IDF_Views_Download
'user' => $request->user)); 'user' => $request->user));
if ($form->isValid()) { if ($form->isValid()) {
$upload = $form->save(); $upload = $form->save();
$urlfile = Pluf_HTTP_URL_urlForView('IDF_Views_Download::view', $urlfile = Pluf_HTTP_URL_urlForView('IDF_Views_Download::view',
array($prj->shortname, $upload->id)); array($prj->shortname, $upload->id));
$request->user->setMessage(sprintf(__('The <a href="%s">file</a> has been uploaded.'), $urlfile)); $request->user->setMessage(sprintf(__('The <a href="%s">file</a> has been uploaded.'), $urlfile));
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::index', $url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::index',
array($prj->shortname)); array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url); return new Pluf_HTTP_Response_Redirect($url);
} }
@@ -246,7 +266,7 @@ class IDF_Views_Download
{ {
$conf = new IDF_Conf(); $conf = new IDF_Conf();
$conf->setProject($project); $conf->setProject($project);
$st = preg_split("/\015\012|\015|\012/", $st = preg_split("/\015\012|\015|\012/",
$conf->getVal('labels_download_predefined', IDF_Form_UploadConf::init_predefined), -1, PREG_SPLIT_NO_EMPTY); $conf->getVal('labels_download_predefined', IDF_Form_UploadConf::init_predefined), -1, PREG_SPLIT_NO_EMPTY);
$auto = ''; $auto = '';
foreach ($st as $s) { foreach ($st as $s) {
@@ -299,13 +319,11 @@ class IDF_Views_Download
$pag->no_results_text = __('No downloads were found.'); $pag->no_results_text = __('No downloads were found.');
$pag->sort_order = array('creation_dtime', 'DESC'); $pag->sort_order = array('creation_dtime', 'DESC');
$pag->setFromRequest($request); $pag->setFromRequest($request);
$tags = $prj->getTagCloud('downloads');
return Pluf_Shortcuts_RenderToResponse('idf/downloads/index.html', return Pluf_Shortcuts_RenderToResponse('idf/downloads/index.html',
array( array(
'page_title' => $title, 'page_title' => $title,
'label' => $tag, 'label' => $tag,
'downloads' => $pag, 'downloads' => $pag,
'tags' => $tags,
'dlabel' => $dtag, 'dlabel' => $dtag,
), ),
$request); $request);
@@ -354,7 +372,7 @@ function IDF_Views_Download_SummaryAndLabels($field, $down, $extra='')
{ {
$tags = array(); $tags = array();
foreach ($down->get_tags_list() as $tag) { foreach ($down->get_tags_list() as $tag) {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::listLabel', $url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::listLabel',
array($down->shortname, $tag->id)); array($down->shortname, $tag->id));
$tags[] = sprintf('<a href="%s" class="label">%s</a>', $url, Pluf_esc((string) $tag)); $tags[] = sprintf('<a href="%s" class="label">%s</a>', $url, Pluf_esc((string) $tag));
} }

View File

@@ -71,12 +71,91 @@ class IDF_Views_Issue
'page_title' => $title, 'page_title' => $title,
'open' => $open, 'open' => $open,
'closed' => $closed, 'closed' => $closed,
'issues' => $pag); 'issues' => $pag,
'cloud' => 'issues',
);
if ($api) return $params; if ($api) return $params;
return Pluf_Shortcuts_RenderToResponse('idf/issues/index.html', return Pluf_Shortcuts_RenderToResponse('idf/issues/index.html',
$params, $request); $params, $request);
} }
/**
* View the issue summary.
* TODO Add thoses data in cache, and process it only after an issue update
*/
public $summary_precond = array('IDF_Precondition::accessIssues');
public function summary($request, $match)
{
$tagStatistics = array();
$ownerStatistics = array();
$status = array();
$isTrackerEmpty = false;
$prj = $request->project;
$opened = $prj->getIssueCountByStatus('open');
$closed = $prj->getIssueCountByStatus('closed');
// Check if the tracker is empty
if ($opened === 0 && $closed === 0) {
$isTrackerEmpty = true;
} else {
if ($opened > 0 || $closed > 0) {
// Issue status statistics
$status['Open'] = array($opened, (int)(100 * $opened / ($opened + $closed)));
$status['Closed'] = array($closed, (int)(100 * $closed / ($opened + $closed)));
}
if ($opened > 0) {
// Issue owner statistics
$owners = $prj->getIssueCountByOwner('open');
foreach ($owners as $user => $nb) {
if ($user === '') {
$key = __('Not assigned');
$login = null;
} else {
$obj = Pluf::factory('Pluf_User')->getOne(array('filter'=>'id='.$user));
$key = $obj->first_name . ' ' . $obj->last_name;
$login = $obj->login;
}
$ownerStatistics[$key] = array($nb, (int)(100 * $nb / $opened), $login);
}
// Issue class tag statistics
$grouped_tags = $prj->getTagCloud();
foreach ($grouped_tags as $class => $tags) {
foreach ($tags as $tag) {
$tagStatistics[$class][$tag->name] = array($tag->nb_use, $tag->id);
}
}
foreach($tagStatistics as $k => $v) {
$nbIssueInClass = 0;
foreach ($v as $val) {
$nbIssueInClass += $val[0];
}
foreach ($v as $kk => $vv) {
$tagStatistics[$k][$kk] = array($vv[0], (int)(100 * $vv[0] / $nbIssueInClass), $vv[1]);
}
}
// Sort
krsort($tagStatistics);
arsort($ownerStatistics);
}
}
$title = sprintf(__('Summary of tracked issues in %s.'), (string) $prj);
return Pluf_Shortcuts_RenderToResponse('idf/issues/summary.html',
array('page_title' => $title,
'trackerEmpty' => $isTrackerEmpty,
'project' => $prj,
'tagStatistics' => $tagStatistics,
'ownerStatistics' => $ownerStatistics,
'status' => $status,
),
$request);
}
/** /**
* View the issues watch list of a given user. * View the issues watch list of a given user.
* Limited to a specified project * Limited to a specified project
@@ -240,42 +319,55 @@ class IDF_Views_Issue
* *
* Only open issues are shown. * Only open issues are shown.
*/ */
public $myIssues_precond = array('IDF_Precondition::accessIssues', public $userIssues_precond = array('IDF_Precondition::accessIssues');
'Pluf_Precondition::loginRequired'); public function userIssues($request, $match)
public function myIssues($request, $match)
{ {
$prj = $request->project; $prj = $request->project;
$sql = new Pluf_SQL('login=%s', array($match[2]));
$user = Pluf::factory('Pluf_User')->getOne(array('filter' => $sql->gen()));
if ($user === null) {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::index',
array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url);
}
$otags = $prj->getTagIdsByStatus('open'); $otags = $prj->getTagIdsByStatus('open');
$ctags = $prj->getTagIdsByStatus('closed'); $ctags = $prj->getTagIdsByStatus('closed');
if (count($otags) == 0) $otags[] = 0; if (count($otags) == 0) $otags[] = 0;
if (count($ctags) == 0) $ctags[] = 0; if (count($ctags) == 0) $ctags[] = 0;
switch ($match[2]) { switch ($match[3]) {
case 'submit': case 'submit':
$title = sprintf(__('My Submitted %s Issues'), (string) $prj); $titleFormat = __('%s %s Submitted %s Issues');
$f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
break; break;
case 'submitclosed': case 'submitclosed':
$title = sprintf(__('My Closed Submitted %s Issues'), (string) $prj); $titleFormat = __('%s %s Closed Submitted %s Issues');
$f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
break; break;
case 'ownerclosed': case 'ownerclosed':
$title = sprintf(__('My Closed Working %s Issues'), (string) $prj); $titleFormat = __('%s %s Closed Working %s Issues');
$f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
break; break;
default: default:
$title = sprintf(__('My Working %s Issues'), (string) $prj); $titleFormat = __('%s %s Working %s Issues');
$f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
break; break;
} }
$title = sprintf($titleFormat,
$user->first_name,
$user->last_name,
(string) $prj);
// Get stats about the issues // Get stats about the issues
$sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
$nb_submit = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_submit = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
$sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
$nb_owner = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_owner = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
// Closed issues // Closed issues
$sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
$nb_submit_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_submit_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
$sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
$nb_owner_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_owner_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
// Paginator to paginate the issues // Paginator to paginate the issues
@@ -286,7 +378,7 @@ class IDF_Views_Issue
'current_user' => $request->user); 'current_user' => $request->user);
$pag->summary = __('This table shows the open issues.'); $pag->summary = __('This table shows the open issues.');
$pag->forced_where = $f_sql; $pag->forced_where = $f_sql;
$pag->action = array('IDF_Views_Issue::myIssues', array($prj->shortname, $match[2])); $pag->action = array('IDF_Views_Issue::userIssues', array($prj->shortname, $match[2]));
$pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted $pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted
$pag->sort_reverse_order = array('modif_dtime'); $pag->sort_reverse_order = array('modif_dtime');
$pag->sort_link_title = true; $pag->sort_link_title = true;
@@ -301,9 +393,10 @@ class IDF_Views_Issue
$pag->items_per_page = 10; $pag->items_per_page = 10;
$pag->no_results_text = __('No issues were found.'); $pag->no_results_text = __('No issues were found.');
$pag->setFromRequest($request); $pag->setFromRequest($request);
return Pluf_Shortcuts_RenderToResponse('idf/issues/my-issues.html', return Pluf_Shortcuts_RenderToResponse('idf/issues/userIssues.html',
array('project' => $prj, array('project' => $prj,
'page_title' => $title, 'page_title' => $title,
'login' => $user->login,
'nb_submit' => $nb_submit, 'nb_submit' => $nb_submit,
'nb_owner' => $nb_owner, 'nb_owner' => $nb_owner,
'nb_submit_closed' => $nb_submit_closed, 'nb_submit_closed' => $nb_submit_closed,
@@ -345,6 +438,7 @@ class IDF_Views_Issue
'form' => $form, 'form' => $form,
'page_title' => $title, 'page_title' => $title,
'preview' => $preview, 'preview' => $preview,
'issue' => new IDF_Issue(),
), ),
self::autoCompleteArrays($prj) self::autoCompleteArrays($prj)
); );
@@ -355,45 +449,142 @@ class IDF_Views_Issue
public $search_precond = array('IDF_Precondition::accessIssues'); public $search_precond = array('IDF_Precondition::accessIssues');
public function search($request, $match) public function search($request, $match)
{
$query = !isset($request->REQUEST['q']) ? '' : $request->REQUEST['q'];
return $this->doSearch($request, $query, 'open');
}
public $searchStatus_precond = array('IDF_Precondition::accessIssues');
public function searchStatus($request, $match)
{
$query = !isset($request->REQUEST['q']) ? '' : $request->REQUEST['q'];
$status = in_array($match[2], array('open', 'closed')) ? $match[2] : 'open';
return $this->doSearch($request, $query, $status);
}
public $searchLabel_precond = array('IDF_Precondition::accessIssues');
public function searchLabel($request, $match)
{
$query = !isset($request->REQUEST['q']) ? '' : $request->REQUEST['q'];
$tag_id = intval($match[2]);
$status = in_array($match[3], array('open', 'closed')) ? $match[3] : 'open';
return $this->doSearch($request, $query, $status, $tag_id);
}
private function doSearch($request, $query, $status, $tag_id=null)
{ {
$prj = $request->project; $prj = $request->project;
if (!isset($request->REQUEST['q']) or trim($request->REQUEST['q']) == '') { if (trim($query) == '') {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::index', $url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::index', array($prj->shortname));
array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url); return new Pluf_HTTP_Response_Redirect($url);
} }
$q = $request->REQUEST['q'];
$title = sprintf(__('Search Issues - %s'), $q); $tag = null;
$issues = new Pluf_Search_ResultSet(IDF_Search::mySearch($q, $prj, 'IDF_Issue')); if ($tag_id !== null) {
if (count($issues) > 100) { $tag = Pluf_Shortcuts_GetObjectOr404('IDF_Tag', $tag_id);
// no more than 100 results as we do not care
$issues->results = array_slice($issues->results, 0, 100);
} }
$title = sprintf(__('Search issues - %s'), $query);
if ($status === 'closed') {
$title = sprintf(__('Search closed issues - %s'), $query);
}
// using Plufs ResultSet implementation here is inefficient, because
// it makes a SELECT for each item and does not allow for further
// filtering neither, so we just return the ids and filter by them
// and other things in the next round
$results = IDF_Search::mySearch($query, $prj, 'IDF_Issue');
$issue_ids = array(0);
foreach ($results as $result) {
$issue_ids[] = $result['model_id'];
}
$otags = $prj->getTagIdsByStatus($status);
if (count($otags) == 0) $otags[] = 0;
$sql = new Pluf_SQL(
'id IN ('.implode(',', $issue_ids).') '.
'AND status IN ('.implode(', ', $otags).') '.
($tag_id !== null ? 'AND idf_tag_id='.$tag_id.' ' : '')
);
$model = new IDF_Issue();
$issues = $model->getList(array('filter' => $sql->gen(), 'view' => 'join_tags'));
// we unfortunately loose the original sort order,
// so we manually have to apply it here again
$sorted_issues = new ArrayObject();
$filtered_issue_ids = array(0);
foreach ($issue_ids as $issue_id) {
foreach ($issues as $issue) {
if ($issue->id != $issue_id)
continue;
if (array_key_exists($issue_id, $sorted_issues))
continue;
$sorted_issues[$issue_id] = $issue;
$filtered_issue_ids[] = $issue_id;
}
}
$pag = new Pluf_Paginator(); $pag = new Pluf_Paginator();
$pag->items = $issues;
$pag->class = 'recent-issues'; $pag->class = 'recent-issues';
$pag->item_extra_props = array('project_m' => $prj, $pag->items = $sorted_issues;
'shortname' => $prj->shortname, $pag->item_extra_props = array(
'current_user' => $request->user); 'project_m' => $prj,
'shortname' => $prj->shortname,
'current_user' => $request->user
);
$pag->summary = __('This table shows the found issues.'); $pag->summary = __('This table shows the found issues.');
$pag->action = array('IDF_Views_Issue::search', array($prj->shortname), array('q'=> $q));
$pag->extra_classes = array('a-c', '', 'a-c', ''); $pag->extra_classes = array('a-c', '', 'a-c', '');
$list_display = array( $pag->configure(array(
'id' => __('Id'), 'id' => __('Id'),
array('summary', 'IDF_Views_Issue_SummaryAndLabels', __('Summary')), array('summary', 'IDF_Views_Issue_SummaryAndLabels', __('Summary')),
array('status', 'IDF_Views_Issue_ShowStatus', __('Status')), array('status', 'IDF_Views_Issue_ShowStatus', __('Status')),
array('modif_dtime', 'Pluf_Paginator_DateAgo', __('Last Updated')), array('modif_dtime', 'Pluf_Paginator_DateAgo', __('Last Updated')),
); ));
$pag->configure($list_display); // disable paginating
$pag->items_per_page = 100; $pag->items_per_page = PHP_INT_MAX;
$pag->no_results_text = __('No issues were found.'); $pag->no_results_text = __('No issues were found.');
$pag->setFromRequest($request); $pag->setFromRequest($request);
$params = array('page_title' => $title,
'issues' => $pag,
'q' => $q,
);
return Pluf_Shortcuts_RenderToResponse('idf/issues/search.html', $params, $request);
if ($tag_id === null) {
$pag->action = array('IDF_Views_Issue::searchStatus',
array($prj->shortname, $status),
array('q'=> $query),
);
} else {
$pag->action = array('IDF_Views_Issue::searchLabel',
array($prj->shortname, $tag_id, $status),
array('q'=> $query),
);
}
// get stats about the issues
$open = $prj->getIssueCountByStatus('open', $tag, $issue_ids);
$closed = $prj->getIssueCountByStatus('closed', $tag, $issue_ids);
// query the available tags for this search result
$all_tags = $prj->getTagsByIssues($filtered_issue_ids);
$grouped_tags = array();
foreach ($all_tags as $atag) {
// group by class
if (!array_key_exists($atag->class, $grouped_tags)) {
$grouped_tags[$atag->class] = array();
}
$grouped_tags[$atag->class][] = $atag;
}
$params = array(
'page_title' => $title,
'issues' => $pag,
'query' => $query,
'status' => $status,
'open' => $open,
'closed' => $closed,
'tag' => $tag,
'all_tags' => $grouped_tags,
);
return Pluf_Shortcuts_RenderToResponse('idf/issues/search.html', $params, $request);
} }
public $view_precond = array('IDF_Precondition::accessIssues'); public $view_precond = array('IDF_Precondition::accessIssues');
@@ -403,6 +594,8 @@ class IDF_Views_Issue
$issue = Pluf_Shortcuts_GetObjectOr404('IDF_Issue', $match[2]); $issue = Pluf_Shortcuts_GetObjectOr404('IDF_Issue', $match[2]);
$prj->inOr404($issue); $prj->inOr404($issue);
$comments = $issue->get_comments_list(array('order' => 'id ASC')); $comments = $issue->get_comments_list(array('order' => 'id ASC'));
$related_issues = $issue->getGroupedRelatedIssues(array('order' => 'other_issue ASC'));
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view', $url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
array($prj->shortname, $issue->id)); array($prj->shortname, $issue->id));
$title = Pluf_Template::markSafe(sprintf(__('Issue <a href="%s">%d</a>: %s'), $url, $issue->id, $issue->summary)); $title = Pluf_Template::markSafe(sprintf(__('Issue <a href="%s">%d</a>: %s'), $url, $issue->id, $issue->summary));
@@ -470,7 +663,8 @@ class IDF_Views_Issue
'preview' => $preview, 'preview' => $preview,
'interested' => $interested->count(), 'interested' => $interested->count(),
'previous_issue_id' => $previous_issue_id, 'previous_issue_id' => $previous_issue_id,
'next_issue_id' => $next_issue_id 'next_issue_id' => $next_issue_id,
'related_issues' => $related_issues,
), ),
$arrays), $arrays),
$request); $request);
@@ -538,6 +732,13 @@ class IDF_Views_Issue
{ {
$prj = $request->project; $prj = $request->project;
$status = $match[2]; $status = $match[2];
if (mb_strtolower($status) == 'open') {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::index',
array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url);
}
$title = sprintf(__('%s Closed Issues'), (string) $prj); $title = sprintf(__('%s Closed Issues'), (string) $prj);
// Get stats about the issues // Get stats about the issues
$open = $prj->getIssueCountByStatus('open'); $open = $prj->getIssueCountByStatus('open');
@@ -651,22 +852,66 @@ class IDF_Views_Issue
public function autoCompleteIssueList($request, $match) public function autoCompleteIssueList($request, $match)
{ {
$prj = $request->project; $prj = $request->project;
$issue_id = !empty($match[2]) ? intval($match[2]) : 0;
$query = trim($request->REQUEST['q']);
$limit = !empty($request->REQUEST['limit']) ? intval($request->REQUEST['limit']) : 0;
$limit = max(10, $limit);
$issues = array();
// empty search, return the most recently updated issues
if (empty($query)) {
$sql = new Pluf_SQL('project=%s', array($prj->id));
$tmp = Pluf::factory('IDF_Issue')->getList(array(
'filter' => $sql->gen(),
'order' => 'modif_dtime DESC'
));
$issues += $tmp->getArrayCopy();
}
else {
// ID-based search
if (is_numeric($query)) {
$sql = 'project=%s AND CAST(id AS VARCHAR) LIKE %s';
// MySQL can't cast to VARCHAR and a CAST to CHAR converts
// the whole number, not just the first digit
if (strtolower(Pluf::f('db_engine')) == 'mysql') {
$sql = 'project=%s AND CAST(id AS CHAR) LIKE %s';
}
$sql = new Pluf_SQL($sql, array($prj->id, $query.'%'));
$tmp = Pluf::factory('IDF_Issue')->getList(array(
'filter' => $sql->gen(),
'order' => 'id ASC'
));
$issues += $tmp->getArrayCopy();
}
// text-based search
$res = new Pluf_Search_ResultSet(
IDF_Search::mySearch($query, $prj, 'IDF_Issue')
);
foreach ($res as $issue)
$issues[] = $issue;
}
// Autocomplete from jQuery UI works with JSON, this old one still // Autocomplete from jQuery UI works with JSON, this old one still
// expects a parsable string; since we'd need to bump jQuery beyond // expects a parsable string; since we'd need to bump jQuery beyond
// 1.2.6 for this to use as well, we're trying to cope with the old format. // 1.2.6 for this to use as well, we're trying to cope with the old format.
// see http://www.learningjquery.com/2010/06/autocomplete-migration-guide // see http://www.learningjquery.com/2010/06/autocomplete-migration-guide
$arr = array(
'Fo|o' => 110,
'Bar' => 111,
'Baz' => 112,
);
$out = ''; $out = '';
foreach ($arr as $key => $val) $ids = array();
foreach ($issues as $issue)
{ {
$out .= str_replace('|', '&#124;', $key).'|'.$val."\n"; if ($issue->id == $issue_id)
continue;
if (in_array($issue->id, $ids))
continue;
if (--$limit < 0)
break;
$out .= str_replace('|', '&#124;', $issue->summary) .'|'.$issue->id."\n";
$ids[] = $issue->id;
} }
return new Pluf_HTTP_Response($out); return new Pluf_HTTP_Response($out);

View File

@@ -58,7 +58,7 @@ class IDF_Views_Source
$title = sprintf(__('%s Invalid Revision'), (string) $request->project); $title = sprintf(__('%s Invalid Revision'), (string) $request->project);
$scm = IDF_Scm::get($request->project); $scm = IDF_Scm::get($request->project);
$branches = $scm->getBranches(); $branches = $scm->getBranches();
$commit = $match[2]; $commit = $match[2];
$params = array( $params = array(
'page_title' => $title, 'page_title' => $title,
@@ -66,7 +66,8 @@ class IDF_Views_Source
'commit' => $commit, 'commit' => $commit,
'branches' => $branches, 'branches' => $branches,
); );
return Pluf_Shortcuts_RenderToResponse('idf/source/invalid_revision.html', $scmConf = $request->conf->getVal('scm', 'git');
return Pluf_Shortcuts_RenderToResponse('idf/source/'.$scmConf.'/invalid_revision.html',
$params, $request); $params, $request);
} }
@@ -307,12 +308,7 @@ class IDF_Views_Source
$cobject->diff = null; $cobject->diff = null;
$diff->parse(); $diff->parse();
$scmConf = $request->conf->getVal('scm', 'git'); $scmConf = $request->conf->getVal('scm', 'git');
try { $changes = $scm->getChanges($commit);
$changes = $scm->getChanges($commit);
} catch (Exception $e) {
// getChanges is not yes supported by this backend.
$changes = array();
}
$branches = $scm->getBranches(); $branches = $scm->getBranches();
$in_branches = $scm->inBranches($cobject->commit, ''); $in_branches = $scm->inBranches($cobject->commit, '');
$tags = $scm->getTags(); $tags = $scm->getTags();
@@ -477,7 +473,7 @@ class IDF_Views_Source
} }
// compare two nodes of different types, directories ("tree") // compare two nodes of different types, directories ("tree")
// should come before files ("blob") // should come before files ("blob")
if ($a->type > $b->type) { if ($a->type > $b->type) {
return -1; return -1;
} }

View File

@@ -152,13 +152,11 @@ class IDF_Views_Wiki
$pag->items_per_page = 25; $pag->items_per_page = 25;
$pag->no_results_text = __('No documentation pages were found.'); $pag->no_results_text = __('No documentation pages were found.');
$pag->setFromRequest($request); $pag->setFromRequest($request);
$tags = $prj->getTagCloud('wiki');
return Pluf_Shortcuts_RenderToResponse('idf/wiki/index.html', return Pluf_Shortcuts_RenderToResponse('idf/wiki/index.html',
array( array(
'page_title' => $title, 'page_title' => $title,
'label' => $tag, 'label' => $tag,
'pages' => $pag, 'pages' => $pag,
'tags' => $tags,
'dlabel' => $dtag, 'dlabel' => $dtag,
), ),
$request); $request);

View File

@@ -118,11 +118,26 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/$#',
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'index'); 'method' => 'index');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/summary/$#',
'base' => $base,
'model' => 'IDF_Views_Issue',
'method' => 'summary');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/search/$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/search/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'search'); 'method' => 'search');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/search/status/(\w+)/$#',
'base' => $base,
'model' => 'IDF_Views_Issue',
'method' => 'searchStatus');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/search/label/(\d+)/(\w+)/$#',
'base' => $base,
'model' => 'IDF_Views_Issue',
'method' => 'searchLabel');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/(\d+)/$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/(\d+)/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
@@ -148,10 +163,10 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/create/$#',
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'create'); 'method' => 'create');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/my/(\w+)/$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/(.*)/(\w+)/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'myIssues'); 'method' => 'userIssues');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/attachment/(\d+)/(.*)$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/attachment/(\d+)/(.*)$#',
'base' => $base, 'base' => $base,
@@ -173,7 +188,7 @@ $ctl[] = array('regex' => '#^/watchlist/(\w+)$#',
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'forgeWatchList'); 'method' => 'forgeWatchList');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/autocomplete/$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/autocomplete/(\d*)$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'autoCompleteIssueList'); 'method' => 'autoCompleteIssueList');
@@ -299,11 +314,16 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/(\d+)/$#',
'model' => 'IDF_Views_Download', 'model' => 'IDF_Views_Download',
'method' => 'view'); 'method' => 'view');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/(\d+)/get/$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/get/(.+)$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Download', 'model' => 'IDF_Views_Download',
'method' => 'download'); 'method' => 'download');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/(\d+)/get/$#',
'base' => $base,
'model' => 'IDF_Views_Download',
'method' => 'downloadById');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/create/$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/create/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Download', 'model' => 'IDF_Views_Download',

View File

@@ -130,7 +130,7 @@ msgstr "Entferntes Subversion-Depot"
#: IDF/Form/Admin/ProjectCreate.php:92 IDF/Form/SourceConf.php:40 #: IDF/Form/Admin/ProjectCreate.php:92 IDF/Form/SourceConf.php:40
msgid "Repository username" msgid "Repository username"
msgstr "Depot-Nutzername" msgstr "Depot-Benutzername"
#: IDF/Form/Admin/ProjectCreate.php:99 IDF/Form/SourceConf.php:47 #: IDF/Form/Admin/ProjectCreate.php:99 IDF/Form/SourceConf.php:47
msgid "Repository password" msgid "Repository password"
@@ -156,11 +156,11 @@ msgstr "Projekt-Eigentümer"
#: IDF/Form/Admin/ProjectCreate.php:123 IDF/Form/Admin/ProjectUpdate.php:76 #: IDF/Form/Admin/ProjectCreate.php:123 IDF/Form/Admin/ProjectUpdate.php:76
#: IDF/Form/MembersConf.php:54 IDF/Form/TabsConf.php:52 #: IDF/Form/MembersConf.php:54 IDF/Form/TabsConf.php:52
msgid "Project members" msgid "Project members"
msgstr "Projekt-Mitglieder" msgstr "Projektmitglieder"
#: IDF/Form/Admin/ProjectCreate.php:136 #: IDF/Form/Admin/ProjectCreate.php:136
msgid "Project template" msgid "Project template"
msgstr "Projekt-Vorlage" msgstr "Projektvorlage"
#: IDF/Form/Admin/ProjectCreate.php:138 #: IDF/Form/Admin/ProjectCreate.php:138
msgid "" msgid ""
@@ -168,7 +168,7 @@ msgid ""
"general configuration will be taken from the template project." "general configuration will be taken from the template project."
msgstr "" msgstr ""
"Benutze das vorgegebene Projekt, um ein neues zu initialisieren. " "Benutze das vorgegebene Projekt, um ein neues zu initialisieren. "
"Zugriffsrechte und allgemeine Konfiguration werden von der Projekt-Vorlage " "Zugriffsrechte und allgemeine Konfiguration werden von der Projektvorlage "
"übernommen." "übernommen."
#: IDF/Form/Admin/ProjectCreate.php:185 #: IDF/Form/Admin/ProjectCreate.php:185
@@ -350,7 +350,7 @@ msgid ""
"The password must be hard for other people to guess, but easy for the user " "The password must be hard for other people to guess, but easy for the user "
"to remember." "to remember."
msgstr "" msgstr ""
"Das Passwort sollte für andere Leute schwer zu erraten, aber für den Nutzer " "Das Passwort sollte für andere Leute schwer zu erraten, aber für den Benutzer "
"einfach zu erinnern sein." "einfach zu erinnern sein."
#: IDF/Form/Admin/UserUpdate.php:89 #: IDF/Form/Admin/UserUpdate.php:89
@@ -366,7 +366,7 @@ msgstr "Beschreibung"
#: IDF/Form/Admin/UserUpdate.php:109 IDF/Form/UserAccount.php:110 #: IDF/Form/Admin/UserUpdate.php:109 IDF/Form/UserAccount.php:110
msgid "Twitter username" msgid "Twitter username"
msgstr "Twitter-Nutzername" msgstr "Twitter-Benutzername"
#: IDF/Form/Admin/UserUpdate.php:119 IDF/Form/UserAccount.php:120 #: IDF/Form/Admin/UserUpdate.php:119 IDF/Form/UserAccount.php:120
msgid "Public email address" msgid "Public email address"
@@ -403,7 +403,7 @@ msgstr "Stab"
#: IDF/Form/Admin/UserUpdate.php:164 #: IDF/Form/Admin/UserUpdate.php:164
msgid "If you give staff rights to a user, you really need to trust them." msgid "If you give staff rights to a user, you really need to trust them."
msgstr "" msgstr ""
"Wenn Du einen Nutzer zum Stab hinzufügst, solltest Du ihm wirklich trauen." "Wenn Du einen Benutzer zum Stab hinzufügst, solltest Du ihm wirklich trauen."
#: IDF/Form/Admin/UserUpdate.php:172 IDF/Views/Admin.php:213 #: IDF/Form/Admin/UserUpdate.php:172 IDF/Views/Admin.php:213
msgid "Active" msgid "Active"
@@ -414,8 +414,8 @@ msgid ""
"If the user is not getting the confirmation email or is abusing the system, " "If the user is not getting the confirmation email or is abusing the system, "
"you can directly enable or disable their account here." "you can directly enable or disable their account here."
msgstr "" msgstr ""
"Wenn der Nutzer keine Bestätigungs-E-Mail erhält oder das System " "Wenn der Benutzer keine Bestätigungs-E-Mail erhält oder das System "
"missbraucht, kannst Du sein Konto hier direkt aktivieren oder deaktivieren." "missbraucht, kannst Du sein Benutzerkonto hier direkt aktivieren oder deaktivieren."
#: IDF/Form/Admin/UserUpdate.php:274 #: IDF/Form/Admin/UserUpdate.php:274
msgid "--- is not a valid first name." msgid "--- is not a valid first name."
@@ -425,7 +425,7 @@ msgstr "--- ist kein gültiger Vorname"
msgid "" msgid ""
"A user with this email already exists, please provide another email address." "A user with this email already exists, please provide another email address."
msgstr "" msgstr ""
"Ein Nutzer mit dieser E-Mail-Adresse existiert bereits, bitte gib eine " "Ein Benutzer mit dieser E-Mail-Adresse existiert bereits, bitte gib eine "
"andere E-Mail-Adresse an." "andere E-Mail-Adresse an."
#: IDF/Form/Admin/UserUpdate.php:301 IDF/Form/UserAccount.php:389 #: IDF/Form/Admin/UserUpdate.php:301 IDF/Form/UserAccount.php:389
@@ -678,7 +678,7 @@ msgstr ""
#: IDF/Form/Register.php:148 #: IDF/Form/Register.php:148
msgid "Confirm the creation of your account." msgid "Confirm the creation of your account."
msgstr "Bestätige die Erstellung deines Accounts." msgstr "Bestätige die Erstellung Deines Benutzerkontos."
#: IDF/Form/RegisterConfirmation.php:40 IDF/Form/RegisterInputKey.php:36 #: IDF/Form/RegisterConfirmation.php:40 IDF/Form/RegisterInputKey.php:36
msgid "Your confirmation key" msgid "Your confirmation key"
@@ -705,7 +705,7 @@ msgid ""
"This account has already been confirmed. Maybe should you try to recover " "This account has already been confirmed. Maybe should you try to recover "
"your password using the help link." "your password using the help link."
msgstr "" msgstr ""
"Dieses Konto wurde bereits bestätigt. Vielleicht möchtest Du versuchen, Dein " "Dieses Benutzerkonto wurde bereits bestätigt. Vielleicht möchtest Du versuchen, Dein "
"Passwort über den Hilfe-Link wiederherzustellen?" "Passwort über den Hilfe-Link wiederherzustellen?"
#: IDF/Form/ReviewCreate.php:74 #: IDF/Form/ReviewCreate.php:74
@@ -1204,7 +1204,7 @@ msgstr ""
"Quellcode.<br />\n" "Quellcode.<br />\n"
"Wenn Du den Zugriff auf den Quellcode beschränkst, wird kein anonymer " "Wenn Du den Zugriff auf den Quellcode beschränkst, wird kein anonymer "
"Zugriff<br />\n" "Zugriff<br />\n"
"angeboten und die Nutzer müssen sich mit ihrem Passwort oder " "angeboten und die Benutzer müssen sich mit ihrem Passwort oder "
"öffentlichen<br />\n" "öffentlichen<br />\n"
"Schlüssel authentifizieren." "Schlüssel authentifizieren."
@@ -1241,7 +1241,7 @@ msgid ""
"\" will default to authorized users only." "\" will default to authorized users only."
msgstr "" msgstr ""
"Wenn Du ein Projekt als privat markierst, haben nur Projektmitglieder und " "Wenn Du ein Projekt als privat markierst, haben nur Projektmitglieder und "
"Administratoren zusammen mit den von Dir extra authorisierten Nutzern " "Administratoren zusammen mit den von Dir extra authorisierten Benutzern "
"Zugriff darauf. Du kannst weiterhin zusätzliche Zugriffsrechte für bestimmte " "Zugriff darauf. Du kannst weiterhin zusätzliche Zugriffsrechte für bestimmte "
"Projektfunktionen vergeben, aber die Einstellungen \"Für alle offen\" und " "Projektfunktionen vergeben, aber die Einstellungen \"Für alle offen\" und "
"\"Angemeldete Benutzer\" werden standardmäßig nur authorisierte Benutzer " "\"Angemeldete Benutzer\" werden standardmäßig nur authorisierte Benutzer "
@@ -1287,7 +1287,7 @@ msgid ""
"<a href=\"%%url%%\">Sign in or create your account</a> to create issues or " "<a href=\"%%url%%\">Sign in or create your account</a> to create issues or "
"add comments" "add comments"
msgstr "" msgstr ""
"<a href=\"%%url%%\">Melde Dich an oder lege ein Konto an</a>, um Tickets " "<a href=\"%%url%%\">Melde Dich an oder lege ein Benutzerkonto an</a>, um Tickets "
"oder Kommentare hinzuzufügen" "oder Kommentare hinzuzufügen"
#: IDF/gettexttemplates/idf/base-full.html.php:4 #: IDF/gettexttemplates/idf/base-full.html.php:4
@@ -1587,7 +1587,7 @@ msgid ""
"You need to create an account on <a href=\"http://en.gravatar.com/" "You need to create an account on <a href=\"http://en.gravatar.com/"
"\">Gravatar</a>, this takes about 5 minutes and is free." "\">Gravatar</a>, this takes about 5 minutes and is free."
msgstr "" msgstr ""
"Du musst Dir einen Account auf <a href=\"http://de.gravatar.com/\">Gravatar</" "Du musst Dir ein Benutzerkonto auf <a href=\"http://de.gravatar.com/\">Gravatar.com</"
"a> erstellen, es dauert nur 5 Minuten und ist kostenfrei." "a> erstellen, es dauert nur 5 Minuten und ist kostenfrei."
#: IDF/gettexttemplates/idf/faq.html.php:10 #: IDF/gettexttemplates/idf/faq.html.php:10
@@ -1632,7 +1632,7 @@ msgstr "<kbd>Umschalt+h</kbd>: Diese Hilfeseite."
#: IDF/gettexttemplates/idf/faq.html.php:18 #: IDF/gettexttemplates/idf/faq.html.php:18
msgid "If you are in a project, you have the following shortcuts:" msgid "If you are in a project, you have the following shortcuts:"
msgstr "" msgstr ""
"Befindest Du Dich in einem Projekt, kannst du die folgenden Tastaturkürzel " "Befindest Du Dich in einem Projekt, kannst Du die folgenden Tastaturkürzel "
"benutzen:" "benutzen:"
#: IDF/gettexttemplates/idf/faq.html.php:19 #: IDF/gettexttemplates/idf/faq.html.php:19
@@ -1874,28 +1874,28 @@ msgstr "Es wird eine Bestätigung verlangt."
#: IDF/gettexttemplates/idf/gadmin/users/base.html.php:3 #: IDF/gettexttemplates/idf/gadmin/users/base.html.php:3
#: IDF/Views/Admin.php:201 #: IDF/Views/Admin.php:201
msgid "User List" msgid "User List"
msgstr "Nutzerliste" msgstr "Benutzerliste"
#: IDF/gettexttemplates/idf/gadmin/users/base.html.php:4 #: IDF/gettexttemplates/idf/gadmin/users/base.html.php:4
#: IDF/gettexttemplates/idf/gadmin/users/update.html.php:13 #: IDF/gettexttemplates/idf/gadmin/users/update.html.php:13
msgid "Update User" msgid "Update User"
msgstr "Nutzer aktualisieren" msgstr "Benutzer aktualisieren"
#: IDF/gettexttemplates/idf/gadmin/users/base.html.php:5 #: IDF/gettexttemplates/idf/gadmin/users/base.html.php:5
#: IDF/gettexttemplates/idf/gadmin/users/create.html.php:4 #: IDF/gettexttemplates/idf/gadmin/users/create.html.php:4
msgid "Create User" msgid "Create User"
msgstr "Nutzer anlegen" msgstr "Benutzer anlegen"
#: IDF/gettexttemplates/idf/gadmin/users/create.html.php:3 #: IDF/gettexttemplates/idf/gadmin/users/create.html.php:3
msgid "The form contains some errors. Please correct them to create the user." msgid "The form contains some errors. Please correct them to create the user."
msgstr "" msgstr ""
"Die Eingabemaske enthält einige Fehler. Bitte korrigiere diese, um den " "Die Eingabemaske enthält einige Fehler. Bitte korrigiere diese, um den "
"Nutzer zu erstellen." "Benutzer zu erstellen."
#: IDF/gettexttemplates/idf/gadmin/users/create.html.php:6 #: IDF/gettexttemplates/idf/gadmin/users/create.html.php:6
msgid "The user password will be sent by email to the user." msgid "The user password will be sent by email to the user."
msgstr "" msgstr ""
"Das Passwort des Nutzers wird an die E-Mail-Adresse des Nutzers versandt." "Das Passwort des Benutzers wird an die E-Mail-Adresse des Benutzers versandt."
#: IDF/gettexttemplates/idf/gadmin/users/createuser-email.txt.php:3 #: IDF/gettexttemplates/idf/gadmin/users/createuser-email.txt.php:3
#, php-format #, php-format
@@ -1916,10 +1916,11 @@ msgid ""
msgstr "" msgstr ""
"Hallo %%user%%,\n" "Hallo %%user%%,\n"
"\n" "\n"
"Der Administrator %%admin%% hat ein neues Konto für Dich\n" "Der Administrator %%admin%% hat ein neues Benutzerkonto\n"
"auf der Forge angelegt.\n" "für Dich auf der Forge angelegt.\n"
"\n" "\n"
"Hier sind die Zugangsdaten, mit denen Du auf Dein Konto zugreifen kannst:\n" "Hier sind die Zugangsdaten, mit denen Du auf Dein Benutzerkonto\n"
"zugreifen kannst:\n"
"\n" "\n"
" Adresse: %%url%%\n" " Adresse: %%url%%\n"
" Anmeldename: %%user.login%%\n" " Anmeldename: %%user.login%%\n"
@@ -1931,17 +1932,17 @@ msgstr ""
#: IDF/gettexttemplates/idf/gadmin/users/index.html.php:3 #: IDF/gettexttemplates/idf/gadmin/users/index.html.php:3
#, php-format #, php-format
msgid "See <a href=\"%%url%%\">not validated users</a>." msgid "See <a href=\"%%url%%\">not validated users</a>."
msgstr "Zeige <a href=\"%%url%%\">nicht validierte Nutzer</a>." msgstr "Zeige <a href=\"%%url%%\">nicht validierte Benutzer</a>."
#: IDF/gettexttemplates/idf/gadmin/users/index.html.php:4 #: IDF/gettexttemplates/idf/gadmin/users/index.html.php:4
msgid "<p>You have here an overview of the users registered in the forge.</p>" msgid "<p>You have here an overview of the users registered in the forge.</p>"
msgstr "" msgstr ""
"<p>Hier siehst Du eine Übersicht der Nutzer, die sich in der Forge " "<p>Hier siehst Du eine Übersicht der Benutzer, die sich in der Forge "
"registriert haben." "registriert haben."
#: IDF/gettexttemplates/idf/gadmin/users/index.html.php:5 #: IDF/gettexttemplates/idf/gadmin/users/index.html.php:5
msgid "Number of users:" msgid "Number of users:"
msgstr "Anzahl der Nutzer:" msgstr "Anzahl der Benutzer:"
#: IDF/gettexttemplates/idf/gadmin/users/update.html.php:3 #: IDF/gettexttemplates/idf/gadmin/users/update.html.php:3
msgid "" msgid ""
@@ -1949,7 +1950,7 @@ msgid ""
"need to ensure that you are providing a valid email\n" "need to ensure that you are providing a valid email\n"
"address" "address"
msgstr "" msgstr ""
"Wenn Du die E-Mail-Adresse eines Nutzers änderst,\n" "Wenn Du die E-Mail-Adresse eines Benutzers änderst,\n"
"musst Du sicherstellen, eine gültige, neue E-Mail-Adresse\n" "musst Du sicherstellen, eine gültige, neue E-Mail-Adresse\n"
"als Ersatz bereitzustellen." "als Ersatz bereitzustellen."
@@ -1959,14 +1960,14 @@ msgid ""
"able to create new projects and update other non staff users.\n" "able to create new projects and update other non staff users.\n"
msgstr "" msgstr ""
"Wenn Du dem Benutzer Stab-Rechte erteilst, kann er\n" "Wenn Du dem Benutzer Stab-Rechte erteilst, kann er\n"
"neue Projekte anlegen und andere Nutzer, die nicht zum Stab\n" "neue Projekte anlegen und andere Benutzer, die nicht zum Stab\n"
"gehören, aktualisieren.\n" "gehören, aktualisieren.\n"
#: IDF/gettexttemplates/idf/gadmin/users/update.html.php:9 #: IDF/gettexttemplates/idf/gadmin/users/update.html.php:9
msgid "The form contains some errors. Please correct them to update the user." msgid "The form contains some errors. Please correct them to update the user."
msgstr "" msgstr ""
"Die Eingabemaske enthält einige Fehler. Bitte korrigiere diese, um den " "Die Eingabemaske enthält einige Fehler. Bitte korrigiere diese, um den "
"Nutzer zu aktualisieren." "Benutzer zu aktualisieren."
#: IDF/gettexttemplates/idf/gadmin/users/update.html.php:10 #: IDF/gettexttemplates/idf/gadmin/users/update.html.php:10
#: IDF/gettexttemplates/idf/register/confirmation.html.php:4 #: IDF/gettexttemplates/idf/register/confirmation.html.php:4
@@ -2449,7 +2450,7 @@ msgid ""
"is still valid and more work is needed to fully fix it." "is still valid and more work is needed to fully fix it."
msgstr "" msgstr ""
"Dieses Ticket wurde als geschlossen markiert. Füge nur dann einen Kommentar " "Dieses Ticket wurde als geschlossen markiert. Füge nur dann einen Kommentar "
"hinzu, wenn du denkst, dass das geschilderte Problem noch nicht ganz " "hinzu, wenn Du denkst, dass das geschilderte Problem noch nicht ganz "
"beseitigt wurde." "beseitigt wurde."
#: IDF/gettexttemplates/idf/issues/view.html.orig.php:8 #: IDF/gettexttemplates/idf/issues/view.html.orig.php:8
@@ -2531,7 +2532,7 @@ msgid ""
"If you don't have an account yet, you can create one <a href=\"%%url%%" "If you don't have an account yet, you can create one <a href=\"%%url%%"
"\">here</a>." "\">here</a>."
msgstr "" msgstr ""
"Wenn Du noch kein Konto hast, kannst Du <a href=\"%%url%%\">hier</a> ein " "Wenn Du noch kein Benutzerkonto hast, kannst Du <a href=\"%%url%%\">hier</a> ein "
"neues erstellen." "neues erstellen."
#: IDF/gettexttemplates/idf/login_form.html.php:4 #: IDF/gettexttemplates/idf/login_form.html.php:4
@@ -2560,7 +2561,7 @@ msgstr "Willkommen."
#: IDF/gettexttemplates/idf/login_form.html.php:10 #: IDF/gettexttemplates/idf/login_form.html.php:10
msgid "It takes less than a minute to create your account." msgid "It takes less than a minute to create your account."
msgstr "Es dauert weniger als eine Minute, um Dein Konto zu erstellen." msgstr "Es dauert weniger als eine Minute, um Dein Benutzerkonto zu erstellen."
#: IDF/gettexttemplates/idf/main-menu.html.php:3 #: IDF/gettexttemplates/idf/main-menu.html.php:3
#, php-format #, php-format
@@ -2576,7 +2577,7 @@ msgstr "Abmelden"
#: IDF/gettexttemplates/idf/main-menu.html.php:5 #: IDF/gettexttemplates/idf/main-menu.html.php:5
msgid "Sign in or create your account" msgid "Sign in or create your account"
msgstr "Anmelden oder Konto erstellen" msgstr "Anmelden oder Benutzerkonto erstellen"
#: IDF/gettexttemplates/idf/main-menu.html.php:8 IDF/Views/Admin.php:42 #: IDF/gettexttemplates/idf/main-menu.html.php:8 IDF/Views/Admin.php:42
msgid "Forge Management" msgid "Forge Management"
@@ -2671,7 +2672,7 @@ msgid ""
msgstr "" msgstr ""
"Hallo,\n" "Hallo,\n"
"\n" "\n"
"Du hast die Anlage eines Kontos angeforder, um an dem Leben\n" "Du hast die Anlage eines Benutzerkontos angefordert, um an dem Leben\n"
"eines Softwareprojektes teilhaben zu können.\n" "eines Softwareprojektes teilhaben zu können.\n"
"\n" "\n"
"Um Dein Konto zu bestätigen, folge bitte dem folgenden Link:\n" "Um Dein Konto zu bestätigen, folge bitte dem folgenden Link:\n"
@@ -2688,7 +2689,7 @@ msgstr ""
"\n" "\n"
"Wenn Du nicht mehr länger an diesem Software-Projekt\n" "Wenn Du nicht mehr länger an diesem Software-Projekt\n"
"interessiert bist oder wenn Du Dich nicht daran erinnern\n" "interessiert bist oder wenn Du Dich nicht daran erinnern\n"
"kannst, dieses Konto erstellen zu wollen, entschuldige\n" "kannst, dieses Benutzerkonto erstellen zu wollen, entschuldige\n"
"und ignoriere einfach diese E-Mail.\n" "und ignoriere einfach diese E-Mail.\n"
"\n" "\n"
"Mit freundlichen Grüßen,\n" "Mit freundlichen Grüßen,\n"
@@ -2716,7 +2717,7 @@ msgid ""
"strong> to log in afterwards." "strong> to log in afterwards."
msgstr "" msgstr ""
"Dies ist der letzte Schritt, aber bitte <strong>stelle sicher, dass Du " "Dies ist der letzte Schritt, aber bitte <strong>stelle sicher, dass Du "
"Cookies aktiviert hast</strong>, um Dich im folgenden anzumelden." "Cookies aktiviert hast</strong>, um Dich im Folgenden anzumelden."
#: IDF/gettexttemplates/idf/register/index.html.php:3 #: IDF/gettexttemplates/idf/register/index.html.php:3
#: IDF/gettexttemplates/idf/register/index.html~.php:3 #: IDF/gettexttemplates/idf/register/index.html~.php:3
@@ -2736,7 +2737,7 @@ msgid ""
"login name and password." "login name and password."
msgstr "" msgstr ""
"Wenn Du einfach nur Deine Anmeldedaten vergessen hast, gibt es keinen Grund, " "Wenn Du einfach nur Deine Anmeldedaten vergessen hast, gibt es keinen Grund, "
"ein neues Konto zu erstellen. Gehe einfach <a href=\"%%url%%\">hier her</a>, " "ein neues Benutzerkonto zu erstellen. Gehe einfach <a href=\"%%url%%\">hier her</a>, "
"um Deinen Anmeldenamen und Dein Passwort wiederherzustellen." "um Deinen Anmeldenamen und Dein Passwort wiederherzustellen."
#: IDF/gettexttemplates/idf/register/index.html.php:5 #: IDF/gettexttemplates/idf/register/index.html.php:5
@@ -2748,7 +2749,7 @@ msgid ""
"you have troubles, you can <a href=\"%%url%%\">let us know about your issues " "you have troubles, you can <a href=\"%%url%%\">let us know about your issues "
"at anytime</a>!" "at anytime</a>!"
msgstr "" msgstr ""
"Durch Dein Konto bist Du in der Lage, an dem Leben aller Softwareprojekte, " "Durch Dein Benutzerkonto bist Du in der Lage, an dem Leben aller Softwareprojekte, "
"die hierüber verwaltet werden, teilzuhaben. Das Teilnehmen an einem " "die hierüber verwaltet werden, teilzuhaben. Das Teilnehmen an einem "
"Softwareprojekt muss Spass machen, deshalb <a href=\"%%url%%\">lass uns " "Softwareprojekt muss Spass machen, deshalb <a href=\"%%url%%\">lass uns "
"jederzeit von Deinen Problemen wissen</a>, solltest Du welche haben." "jederzeit von Deinen Problemen wissen</a>, solltest Du welche haben."
@@ -3209,13 +3210,13 @@ msgstr "Zweige filtern"
#: IDF/gettexttemplates/idf/source/mercurial/branch_tag_list.html.php:5 #: IDF/gettexttemplates/idf/source/mercurial/branch_tag_list.html.php:5
#: IDF/gettexttemplates/idf/source/mtn/branch_tag_list.html.php:5 #: IDF/gettexttemplates/idf/source/mtn/branch_tag_list.html.php:5
msgid "Tags" msgid "Tags"
msgstr "Marken" msgstr "Tags"
#: IDF/gettexttemplates/idf/source/git/branch_tag_list.html.php:6 #: IDF/gettexttemplates/idf/source/git/branch_tag_list.html.php:6
#: IDF/gettexttemplates/idf/source/mercurial/branch_tag_list.html.php:6 #: IDF/gettexttemplates/idf/source/mercurial/branch_tag_list.html.php:6
#: IDF/gettexttemplates/idf/source/mtn/branch_tag_list.html.php:6 #: IDF/gettexttemplates/idf/source/mtn/branch_tag_list.html.php:6
msgid "filter tags" msgid "filter tags"
msgstr "Marken filtern" msgstr "Tags filtern"
#: IDF/gettexttemplates/idf/source/git/file.html.php:3 #: IDF/gettexttemplates/idf/source/git/file.html.php:3
#: IDF/gettexttemplates/idf/source/git/tree.html.php:3 #: IDF/gettexttemplates/idf/source/git/tree.html.php:3
@@ -3231,7 +3232,7 @@ msgid ""
"%%cobject.date%%." "%%cobject.date%%."
msgstr "" msgstr ""
"Quellcode bei Revision <a class=\"mono\" href=\"%%url%%\">%%commit%%</a> " "Quellcode bei Revision <a class=\"mono\" href=\"%%url%%\">%%commit%%</a> "
"erzeugt am %%cobject.date%%." "erzeugt %%cobject.date%%."
#: IDF/gettexttemplates/idf/source/git/file.html.php:4 #: IDF/gettexttemplates/idf/source/git/file.html.php:4
#: IDF/gettexttemplates/idf/source/git/tree.html.php:4 #: IDF/gettexttemplates/idf/source/git/tree.html.php:4
@@ -3409,8 +3410,8 @@ msgstr "Revision:"
#: IDF/gettexttemplates/idf/source/svn/commit.html.php:4 #: IDF/gettexttemplates/idf/source/svn/commit.html.php:4
#: IDF/gettexttemplates/idf/source/svn/file.html.php:11 #: IDF/gettexttemplates/idf/source/svn/file.html.php:11
#: IDF/gettexttemplates/idf/source/svn/tree.html.php:16 #: IDF/gettexttemplates/idf/source/svn/tree.html.php:16
msgid "Go to revision" msgid "Switch"
msgstr "Zu Revision gehen:" msgstr "Wechseln"
#: IDF/gettexttemplates/idf/source/svn/file.html.php:6 #: IDF/gettexttemplates/idf/source/svn/file.html.php:6
#: IDF/gettexttemplates/idf/source/svn/tree.html.php:11 #: IDF/gettexttemplates/idf/source/svn/tree.html.php:11
@@ -3420,7 +3421,7 @@ msgstr "Eigenschaft"
#: IDF/gettexttemplates/idf/source/svn/file.html.php:7 #: IDF/gettexttemplates/idf/source/svn/file.html.php:7
#: IDF/gettexttemplates/idf/source/svn/tree.html.php:12 #: IDF/gettexttemplates/idf/source/svn/tree.html.php:12
msgid "set to:" msgid "set to:"
msgstr "setzen auf:" msgstr "gesetzt auf:"
#: IDF/gettexttemplates/idf/source/svn/help.html.php:3 #: IDF/gettexttemplates/idf/source/svn/help.html.php:3
#, php-format #, php-format
@@ -3442,7 +3443,7 @@ msgstr "Zweige:"
#: IDF/gettexttemplates/idf/source/svn/tree.html.php:18 #: IDF/gettexttemplates/idf/source/svn/tree.html.php:18
msgid "Tags:" msgid "Tags:"
msgstr "Marken:" msgstr "Tags:"
#: IDF/gettexttemplates/idf/user/changeemail-email.txt.php:3 #: IDF/gettexttemplates/idf/user/changeemail-email.txt.php:3
#, php-format #, php-format
@@ -3491,7 +3492,7 @@ msgstr ""
#: IDF/gettexttemplates/idf/user/changeemail.html.php:4 #: IDF/gettexttemplates/idf/user/changeemail.html.php:4
msgid "Confirm Your New Email Address" msgid "Confirm Your New Email Address"
msgstr "Bestätige deine neue Email-Adresse" msgstr "Bestätige Deine neue Email-Adresse"
#: IDF/gettexttemplates/idf/user/changeemail.html.php:7 #: IDF/gettexttemplates/idf/user/changeemail.html.php:7
#: IDF/gettexttemplates/idf/user/passrecovery-inputkey.html.php:7 #: IDF/gettexttemplates/idf/user/passrecovery-inputkey.html.php:7
@@ -3508,7 +3509,7 @@ msgstr ""
#: IDF/gettexttemplates/idf/user/dashboard.html.php:3 #: IDF/gettexttemplates/idf/user/dashboard.html.php:3
#, php-format #, php-format
msgid "<a href=\"%%url%%\">Update your account</a>." msgid "<a href=\"%%url%%\">Update your account</a>."
msgstr "<a href=\"%%url%%\">Aktualisiere Dein Konto</a>." msgstr "<a href=\"%%url%%\">Aktualisiere Dein Benutzerkonto</a>."
#: IDF/gettexttemplates/idf/user/dashboard.html.php:4 #: IDF/gettexttemplates/idf/user/dashboard.html.php:4
#, php-format #, php-format
@@ -3578,7 +3579,7 @@ msgid ""
"If possible, use your real name. By using your real name, people will have " "If possible, use your real name. By using your real name, people will have "
"more trust in your comments and remarks." "more trust in your comments and remarks."
msgstr "" msgstr ""
"Wenn möglich, nutze Deinen realen Namen. Dadurch wird deinen Kommentaren und " "Wenn möglich, nutze Deinen realen Namen. Dadurch wird Deinen Kommentaren und "
"Bemerkungen mehr Vertrauen geschenkt." "Bemerkungen mehr Vertrauen geschenkt."
#: IDF/gettexttemplates/idf/user/myaccount.html.php:19 #: IDF/gettexttemplates/idf/user/myaccount.html.php:19
@@ -3646,7 +3647,7 @@ msgstr ""
"Hallo %%user%%,\n" "Hallo %%user%%,\n"
"\n" "\n"
"Du hast Dein Passwort verloren und möchtest es wiederherstellen.\n" "Du hast Dein Passwort verloren und möchtest es wiederherstellen.\n"
"Um ein neues Passwort für Dein Konto anzugeben, musst Du lediglich\n" "Um ein neues Passwort für Dein Benutzerkonto anzugeben, musst Du lediglich\n"
"dem folgenden Link folgen. Sie führt zu einer einfachen Eingabemaske,\n" "dem folgenden Link folgen. Sie führt zu einer einfachen Eingabemaske,\n"
"mit der Du ein neues Passwort setzen kannst.\n" "mit der Du ein neues Passwort setzen kannst.\n"
"\n" "\n"
@@ -3801,7 +3802,7 @@ msgstr ""
"a> mit der <a href=\"%%eurl%%\"><em>Extra</em>-Erweiterung</a> verwendet " "a> mit der <a href=\"%%eurl%%\"><em>Extra</em>-Erweiterung</a> verwendet "
"werden.</p>\n" "werden.</p>\n"
"<p>Website-Adresses werden automatisch verlinkt und Du kannst außerdem zu " "<p>Website-Adresses werden automatisch verlinkt und Du kannst außerdem zu "
"anderen Dokumentations-Seiten durch die Benutzung von doppelten, eckige " "anderen Dokumentations-Seiten durch die Benutzung von doppelten, eckigen "
"Klammern verlinken, etwa so: [[AndereSeite]].</p>\n" "Klammern verlinken, etwa so: [[AndereSeite]].</p>\n"
"<p>Um direkt die Inhalte einer Datei aus dem Depot einzubinden, umklammere " "<p>Um direkt die Inhalte einer Datei aus dem Depot einzubinden, umklammere "
"den Pfad zur Datei mit dreifachen, eckigen Klammern: [[[Pfad/zu/Datei.txt]]]." "den Pfad zur Datei mit dreifachen, eckigen Klammern: [[[Pfad/zu/Datei.txt]]]."
@@ -4388,7 +4389,7 @@ msgstr "Administrator"
#: IDF/Views/Admin.php:214 #: IDF/Views/Admin.php:214
msgid "Last Login" msgid "Last Login"
msgstr "Letzter Login" msgstr "Letztes Login"
#: IDF/Views/Admin.php:221 #: IDF/Views/Admin.php:221
msgid "No users were found." msgid "No users were found."
@@ -4954,7 +4955,7 @@ msgstr ""
#: IDF/Views.php:284 #: IDF/Views.php:284
msgid "Here to Help You!" msgid "Here to Help You!"
msgstr "Hier, um dir zu helfen!" msgstr "Hier, um Dir zu helfen!"
#: IDF/Views.php:300 #: IDF/Views.php:300
msgid "InDefero API (Application Programming Interface)" msgid "InDefero API (Application Programming Interface)"

View File

@@ -2,7 +2,7 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Indefero\n" "Project-Id-Version: Indefero\n"
@@ -11,10 +11,10 @@ msgstr ""
"PO-Revision-Date: 2011-03-28 14:19+0000\n" "PO-Revision-Date: 2011-03-28 14:19+0000\n"
"Last-Translator: Mika <mikados.mikados@gmail.com>\n" "Last-Translator: Mika <mikados.mikados@gmail.com>\n"
"Language-Team: Mika <mikados.mikados@gmail.com>\n" "Language-Team: Mika <mikados.mikados@gmail.com>\n"
"Language: es_ES\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: es_ES\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: IDF/Commit.php:55 IDF/Conf.php:54 IDF/Issue.php:52 IDF/Review.php:65 #: IDF/Commit.php:55 IDF/Conf.php:54 IDF/Issue.php:52 IDF/Review.php:65
@@ -144,8 +144,8 @@ msgid ""
"This should be a world-wide unique identifier for your project. A reverse " "This should be a world-wide unique identifier for your project. A reverse "
"DNS notation like \"com.my-domain.my-project\" is a good idea." "DNS notation like \"com.my-domain.my-project\" is a good idea."
msgstr "" msgstr ""
"Este debe ser un identificador único para su proyecto. Una notación tipo DNS" "Este debe ser un identificador único para su proyecto. Una notación tipo DNS "
" inversa como \"com.mi-dominio.mi-proyecto\" es una buena elección." "inversa como \"com.mi-dominio.mi-proyecto\" es una buena elección."
#: IDF/Form/Admin/ProjectCreate.php:114 IDF/Form/Admin/ProjectUpdate.php:68 #: IDF/Form/Admin/ProjectCreate.php:114 IDF/Form/Admin/ProjectUpdate.php:68
#: IDF/Form/MembersConf.php:46 IDF/Form/TabsConf.php:53 #: IDF/Form/MembersConf.php:46 IDF/Form/TabsConf.php:53
@@ -279,8 +279,8 @@ msgstr "Iniciar sesión"
#: IDF/Form/Admin/UserCreate.php:60 #: IDF/Form/Admin/UserCreate.php:60
msgid "" msgid ""
"The login must be between 3 and 15 characters long and contains only letters" "The login must be between 3 and 15 characters long and contains only letters "
" and digits." "and digits."
msgstr "" msgstr ""
"El nombre de usuario debe tener entre 3 y 15 caracteres de largo y contener " "El nombre de usuario debe tener entre 3 y 15 caracteres de largo y contener "
"solamente letras y dígitos." "solamente letras y dígitos."
@@ -421,8 +421,8 @@ msgstr "--- No es un nombre válido."
msgid "" msgid ""
"A user with this email already exists, please provide another email address." "A user with this email already exists, please provide another email address."
msgstr "" msgstr ""
"Ya existe un usuario con este correo electrónico , por favor, introduce otro" "Ya existe un usuario con este correo electrónico , por favor, introduce otro "
" email." "email."
#: IDF/Form/Admin/UserUpdate.php:301 IDF/Form/UserAccount.php:389 #: IDF/Form/Admin/UserUpdate.php:301 IDF/Form/UserAccount.php:389
msgid "For security reason, you cannot upload a file with this extension." msgid "For security reason, you cannot upload a file with this extension."
@@ -452,7 +452,8 @@ msgstr "Resumen"
#: IDF/Form/IssueCreate.php:72 IDF/Form/IssueUpdate.php:65 #: IDF/Form/IssueCreate.php:72 IDF/Form/IssueUpdate.php:65
#: IDF/Form/ReviewCreate.php:83 #: IDF/Form/ReviewCreate.php:83
msgid "The \"upload_issue_path\" configuration variable was not set." msgid "The \"upload_issue_path\" configuration variable was not set."
msgstr "La variable de configuración \"upload_issue_path\" no se ha establecido." msgstr ""
"La variable de configuración \"upload_issue_path\" no se ha establecido."
#: IDF/Form/IssueCreate.php:82 IDF/Form/IssueUpdate.php:75 #: IDF/Form/IssueCreate.php:82 IDF/Form/IssueUpdate.php:75
msgid "Attach a file" msgid "Attach a file"
@@ -478,7 +479,8 @@ msgstr "Etiquetas"
#: IDF/Form/IssueCreate.php:192 #: IDF/Form/IssueCreate.php:192
msgid "You cannot add a label with the \"Status\" prefix to an issue." msgid "You cannot add a label with the \"Status\" prefix to an issue."
msgstr "No se puede agregar una etiqueta con el prefijo \"Status\" a un ticket." msgstr ""
"No se puede agregar una etiqueta con el prefijo \"Status\" a un ticket."
#: IDF/Form/IssueCreate.php:193 IDF/Form/IssueCreate.php:200 #: IDF/Form/IssueCreate.php:193 IDF/Form/IssueCreate.php:200
#: IDF/Form/UpdateUpload.php:110 IDF/Form/Upload.php:120 #: IDF/Form/UpdateUpload.php:110 IDF/Form/Upload.php:120
@@ -490,8 +492,7 @@ msgstr "Proporcionaste una etiqueta no válida."
#: IDF/Form/Upload.php:119 #: IDF/Form/Upload.php:119
#, php-format #, php-format
msgid "You cannot provide more than label from the %s class to an issue." msgid "You cannot provide more than label from the %s class to an issue."
msgstr "" msgstr "No puede proporcionar más de una etiqueta de la clase %s en un ticket."
"No puede proporcionar más de una etiqueta de la clase %s en un ticket."
#: IDF/Form/IssueCreate.php:210 IDF/Form/IssueUpdate.php:147 #: IDF/Form/IssueCreate.php:210 IDF/Form/IssueUpdate.php:147
msgid "You need to provide a description of the issue." msgid "You need to provide a description of the issue."
@@ -565,8 +566,8 @@ msgid ""
"Sorry, we cannot find a user with this email address or login. Feel free to " "Sorry, we cannot find a user with this email address or login. Feel free to "
"try again." "try again."
msgstr "" msgstr ""
"Lo sentimos, no hemos encontrado ningún usuario con esta dirección de correo" "Lo sentimos, no hemos encontrado ningún usuario con esta dirección de correo "
" electrónico o nombre de usuario. Vuelva a intentarlo." "electrónico o nombre de usuario. Vuelva a intentarlo."
#: IDF/Form/Password.php:100 #: IDF/Form/Password.php:100
msgid "Password Recovery - InDefero" msgid "Password Recovery - InDefero"
@@ -579,11 +580,11 @@ msgstr "Tu clave de verificación"
#: IDF/Form/PasswordInputKey.php:50 IDF/Form/PasswordReset.php:89 #: IDF/Form/PasswordInputKey.php:50 IDF/Form/PasswordReset.php:89
msgid "" msgid ""
"We are sorry but this validation key is not valid. Maybe you should directly" "We are sorry but this validation key is not valid. Maybe you should directly "
" copy/paste it from your validation email." "copy/paste it from your validation email."
msgstr "" msgstr ""
"Lo sentimos, pero esta clave de validación no es válida. Intente " "Lo sentimos, pero esta clave de validación no es válida. Intente copiar/"
"copiar/pegar desde el email de validación." "pegar desde el email de validación."
#: IDF/Form/PasswordInputKey.php:61 IDF/Form/PasswordReset.php:100 #: IDF/Form/PasswordInputKey.php:61 IDF/Form/PasswordReset.php:100
msgid "" msgid ""
@@ -639,8 +640,8 @@ msgid ""
"The login must be between 3 and 15 characters long and contain only letters " "The login must be between 3 and 15 characters long and contain only letters "
"and digits." "and digits."
msgstr "" msgstr ""
"El nombre de usuario debe tener entre 3 y 15 caracteres y contener solamente" "El nombre de usuario debe tener entre 3 y 15 caracteres y contener solamente "
" letras y dígitos." "letras y dígitos."
#: IDF/Form/Register.php:53 #: IDF/Form/Register.php:53
msgid "Your email" msgid "Your email"
@@ -658,8 +659,7 @@ msgstr "Estoy de acuerdo con los términos y condiciones."
#: IDF/Form/Register.php:88 #: IDF/Form/Register.php:88
msgid "" msgid ""
"We know, this is boring, but you need to agree with the terms and " "We know, this is boring, but you need to agree with the terms and conditions."
"conditions."
msgstr "" msgstr ""
"Lo sabemos, esto es aburrido, pero es necesario aceptar los términos y " "Lo sabemos, esto es aburrido, pero es necesario aceptar los términos y "
"condiciones." "condiciones."
@@ -686,16 +686,16 @@ msgid ""
"Your password must be hard for other people to guess, but easy for you to " "Your password must be hard for other people to guess, but easy for you to "
"remember." "remember."
msgstr "" msgstr ""
"Su contraseña debe ser difícil de averiguar para otras personas, pero que le" "Su contraseña debe ser difícil de averiguar para otras personas, pero que le "
" sea fácil de recordar." "sea fácil de recordar."
#: IDF/Form/RegisterConfirmation.php:99 IDF/Form/RegisterInputKey.php:50 #: IDF/Form/RegisterConfirmation.php:99 IDF/Form/RegisterInputKey.php:50
msgid "" msgid ""
"We are sorry but this confirmation key is not valid. Maybe you should " "We are sorry but this confirmation key is not valid. Maybe you should "
"directly copy/paste it from your confirmation email." "directly copy/paste it from your confirmation email."
msgstr "" msgstr ""
"Lo sentimos, pero esta clave de confirmación no es válida. Pruebe a copiar y" "Lo sentimos, pero esta clave de confirmación no es válida. Pruebe a copiar y "
" pegar directamente desde el email de confirmación." "pegar directamente desde el email de confirmación."
#: IDF/Form/RegisterConfirmation.php:110 #: IDF/Form/RegisterConfirmation.php:110
msgid "" msgid ""
@@ -886,8 +886,7 @@ msgstr ""
#: IDF/Form/UserAccount.php:354 #: IDF/Form/UserAccount.php:354
msgid "" msgid ""
"Please check the key as it does not appear to be a valid monotone public " "Please check the key as it does not appear to be a valid monotone public key."
"key."
msgstr "" msgstr ""
"Por favor, compruebe la clave, ya que parece no ser una clave pública " "Por favor, compruebe la clave, ya que parece no ser una clave pública "
"monotone válida ." "monotone válida ."
@@ -904,11 +903,11 @@ msgstr "Ya has subido esta clave."
#: IDF/Form/UserChangeEmail.php:63 #: IDF/Form/UserChangeEmail.php:63
msgid "" msgid ""
"The validation key is not valid. Please copy/paste it from your confirmation" "The validation key is not valid. Please copy/paste it from your confirmation "
" email." "email."
msgstr "" msgstr ""
"La clave de validación no es válido. Por favor, copia/pega desde su email de" "La clave de validación no es válido. Por favor, copia/pega desde su email de "
" confirmación." "confirmación."
#: IDF/Form/WikiConf.php:49 #: IDF/Form/WikiConf.php:49
msgid "Predefined documentation page labels" msgid "Predefined documentation page labels"
@@ -916,8 +915,7 @@ msgstr "Etiquetas predefinidas para página de documentación"
#: IDF/Form/WikiConf.php:58 #: IDF/Form/WikiConf.php:58
msgid "" msgid ""
"Each documentation page may have at most one label with each of these " "Each documentation page may have at most one label with each of these classes"
"classes"
msgstr "" msgstr ""
"Cada página de documentación podrá tener más de una etiqueta para cada una " "Cada página de documentación podrá tener más de una etiqueta para cada una "
"de estas clases" "de estas clases"
@@ -1034,12 +1032,14 @@ msgid ""
"\n" "\n"
"<p><strong>Instructions:</strong></p>\n" "<p><strong>Instructions:</strong></p>\n"
"<p>List one status value per line in desired sort-order.</p>\n" "<p>List one status value per line in desired sort-order.</p>\n"
"<p>Optionally, use an equals-sign to document the meaning of each status value.</p>\n" "<p>Optionally, use an equals-sign to document the meaning of each status "
"value.</p>\n"
msgstr "" msgstr ""
"\n" "\n"
"<p><strong>Instrucciones:</strong></p>\n" "<p><strong>Instrucciones:</strong></p>\n"
"<p>Lista un valor por línea en el orden deseado.</p>\n" "<p>Lista un valor por línea en el orden deseado.</p>\n"
"<p>Si lo desea, puede utilizar un signo de igual para documentar el valor de cada estado.</p>\n" "<p>Si lo desea, puede utilizar un signo de igual para documentar el valor de "
"cada estado.</p>\n"
#: IDF/gettexttemplates/idf/admin/downloads.html.php:8 #: IDF/gettexttemplates/idf/admin/downloads.html.php:8
#: IDF/gettexttemplates/idf/admin/issue-tracking.html.php:8 #: IDF/gettexttemplates/idf/admin/issue-tracking.html.php:8
@@ -1056,12 +1056,14 @@ msgstr "Guardar cambios"
msgid "" msgid ""
"\n" "\n"
"<p><strong>Instructions:</strong></p>\n" "<p><strong>Instructions:</strong></p>\n"
"<p>Specify each person by its login. Each person must have already registered with the given login.</p>\n" "<p>Specify each person by its login. Each person must have already "
"registered with the given login.</p>\n"
"<p>Separate the logins with commas and/or new lines.</p>\n" "<p>Separate the logins with commas and/or new lines.</p>\n"
msgstr "" msgstr ""
"\n" "\n"
"<p><strong>Instrucciones:</strong></p>\n" "<p><strong>Instrucciones:</strong></p>\n"
"<p>Especifique cada persona por su nombre de usuaro. Cada persona debe haberse registrado con el nombre de usuario dado.</p>\n" "<p>Especifique cada persona por su nombre de usuaro. Cada persona debe "
"haberse registrado con el nombre de usuario dado.</p>\n"
"<p>Separe los nombres de usuarios con comas y/o nuevas líneas.</p>\n" "<p>Separe los nombres de usuarios con comas y/o nuevas líneas.</p>\n"
#: IDF/gettexttemplates/idf/admin/members.html.php:8 #: IDF/gettexttemplates/idf/admin/members.html.php:8
@@ -1070,17 +1072,22 @@ msgstr ""
msgid "" msgid ""
"\n" "\n"
"<p><strong>Notes:</strong></p>\n" "<p><strong>Notes:</strong></p>\n"
"<p>A project owner may make any change to this project, including removing other project owners. You need to be carefull when you give owner rights.</p>\n" "<p>A project owner may make any change to this project, including removing "
"<p>A project member will not have access to the administration area but will have more options available in the use of the project.</p>\n" "other project owners. You need to be carefull when you give owner rights.</"
"p>\n"
"<p>A project member will not have access to the administration area but will "
"have more options available in the use of the project.</p>\n"
msgstr "" msgstr ""
"\n" "\n"
"<p><strong>Notas:</strong></p>\n" "<p><strong>Notas:</strong></p>\n"
"<p>El propietario de un proyecto puede hacer cualquier cambio a este proyecto, incluyendo la eliminación de otros propietarios del proyecto. ¡Tienes que tener cuidado cuando das permisos de propietario!.</p>\n" "<p>El propietario de un proyecto puede hacer cualquier cambio a este "
"<p>Un miembro del proyecto no tendrá acceso al área de administración, pero tendrá más opciones disponibles para usar en el proyecto.</p>\n" "proyecto, incluyendo la eliminación de otros propietarios del proyecto. "
"¡Tienes que tener cuidado cuando das permisos de propietario!.</p>\n"
"<p>Un miembro del proyecto no tendrá acceso al área de administración, pero "
"tendrá más opciones disponibles para usar en el proyecto.</p>\n"
#: IDF/gettexttemplates/idf/admin/source.html.php:3 #: IDF/gettexttemplates/idf/admin/source.html.php:3
msgid "" msgid "You can find here the current repository configuration of your project."
"You can find here the current repository configuration of your project."
msgstr "" msgstr ""
"Puede encontrar aquí la configuración actual del repositorio de su proyecto." "Puede encontrar aquí la configuración actual del repositorio de su proyecto."
@@ -1090,7 +1097,8 @@ msgid ""
"request is sent after each repository commit. If this field is empty,\n" "request is sent after each repository commit. If this field is empty,\n"
"notifications are disabled.</p>\n" "notifications are disabled.</p>\n"
"\n" "\n"
"<p>Only properly-escaped <strong>HTTP</strong> URLs are supported, for example:</p>\n" "<p>Only properly-escaped <strong>HTTP</strong> URLs are supported, for "
"example:</p>\n"
"\n" "\n"
"<ul>\n" "<ul>\n"
"<li>http://domain.com/commit</li>\n" "<li>http://domain.com/commit</li>\n"
@@ -1109,8 +1117,10 @@ msgid ""
"post-commit URL http://mydomain.com/%p/%r would send a request to\n" "post-commit URL http://mydomain.com/%p/%r would send a request to\n"
"http://mydomain.com/my-project/123.</p>" "http://mydomain.com/my-project/123.</p>"
msgstr "" msgstr ""
"<p>La configuración URL WebHook especifica una URL con una solicitud HTTP POST\n" "<p>La configuración URL WebHook especifica una URL con una solicitud HTTP "
"que se envía después de cada commit del repositorio. Si este campo está vacío,\n" "POST\n"
"que se envía después de cada commit del repositorio. Si este campo está "
"vacío,\n"
"las notificaciones están desactivadas.</p>\n" "las notificaciones están desactivadas.</p>\n"
"\n" "\n"
"<p>Únicamente las URLs <strong>HTTP</strong>, \n" "<p>Únicamente las URLs <strong>HTTP</strong>, \n"
@@ -1122,14 +1132,16 @@ msgstr ""
"</ul>\n" "</ul>\n"
"\n" "\n"
"<p>Además, la URL puede contener la siguiente notación: \"%\", que\n" "<p>Además, la URL puede contener la siguiente notación: \"%\", que\n"
"será reemplazada por los valores específicos del proyecto para cada commit:</p>\n" "será reemplazada por los valores específicos del proyecto para cada commit:</"
"p>\n"
"\n" "\n"
"<ul>\n" "<ul>\n"
"<li>%p - nombre del proyecto</li>\n" "<li>%p - nombre del proyecto</li>\n"
"<li>%r - número de revisión</li>\n" "<li>%r - número de revisión</li>\n"
"</ul>\n" "</ul>\n"
"\n" "\n"
"<p>Por ejemplo, el commit de la revisión 123 del proyecto 'mi-proyecto' con URL de post-commit http://midominio.com/%p/%r enviaría la solicitud\n" "<p>Por ejemplo, el commit de la revisión 123 del proyecto 'mi-proyecto' con "
"URL de post-commit http://midominio.com/%p/%r enviaría la solicitud\n"
"http://midominio.com/mi-proyecto/123.</p>" "http://midominio.com/mi-proyecto/123.</p>"
#: IDF/gettexttemplates/idf/admin/source.html.php:26 #: IDF/gettexttemplates/idf/admin/source.html.php:26
@@ -1161,11 +1173,13 @@ msgstr "Clave de autenticación Post-commit:"
msgid "" msgid ""
"\n" "\n"
"<p><strong>Instructions:</strong></p>\n" "<p><strong>Instructions:</strong></p>\n"
"<p>The description of the project can be improved using the <a href=\"%%url%%\">Markdown syntax</a>.</p>\n" "<p>The description of the project can be improved using the <a href=\"%%url%%"
"\">Markdown syntax</a>.</p>\n"
msgstr "" msgstr ""
"\n" "\n"
"<p><strong>Instrucciones:</strong></p>\n" "<p><strong>Instrucciones:</strong></p>\n"
"<p>La descripción del proyecto puede ser mejorada mediante la <a href=\"%%url%%\">Sintaxis de Marcado</a>.</p>\n" "<p>La descripción del proyecto puede ser mejorada mediante la <a href=\"%%url"
"%%\">Sintaxis de Marcado</a>.</p>\n"
#: IDF/gettexttemplates/idf/admin/summary.html.php:7 #: IDF/gettexttemplates/idf/admin/summary.html.php:7
msgid "" msgid ""
@@ -1183,7 +1197,8 @@ msgid ""
"password or SSH key." "password or SSH key."
msgstr "" msgstr ""
"\n" "\n"
"Solo los miembros del proyecto y los administradores tienen acceso de escritura sobre las fuentes.<br />\n" "Solo los miembros del proyecto y los administradores tienen acceso de "
"escritura sobre las fuentes.<br />\n"
"Si restringe el acceso a las fuentes, el acceso anónimo,<br />\n" "Si restringe el acceso a las fuentes, el acceso anónimo,<br />\n"
"no estará habilitado y los usuarios deberán autentificarse con su<br />\n" "no estará habilitado y los usuarios deberán autentificarse con su<br />\n"
"contraseña o clave SSH." "contraseña o clave SSH."
@@ -1202,12 +1217,12 @@ msgid ""
"Notification emails will be sent from the <strong>%%from_email%%</strong> " "Notification emails will be sent from the <strong>%%from_email%%</strong> "
"address, if you send the email to a mailing list, you may need to register " "address, if you send the email to a mailing list, you may need to register "
"this email address. Multiple email addresses must be separated through " "this email address. Multiple email addresses must be separated through "
"commas (','). If you do not want to send emails for a given type of changes," "commas (','). If you do not want to send emails for a given type of changes, "
" simply leave the corresponding field empty." "simply leave the corresponding field empty."
msgstr "" msgstr ""
"Las notificaciones de mensajes se enviarán desde la dirección " "Las notificaciones de mensajes se enviarán desde la dirección <strong>"
"<strong>%%from_email%%</strong>, si envía el mensaje a una lista de correo, " "%%from_email%%</strong>, si envía el mensaje a una lista de correo, puede "
"puede que tenga que registrar esta dirección de correo electrónico. Varias " "que tenga que registrar esta dirección de correo electrónico. Varias "
"direcciones de correo electrónico deben separarse por comas (','). Si no " "direcciones de correo electrónico deben separarse por comas (','). Si no "
"desea enviar mensajes de correo electrónico para un determinado tipo de " "desea enviar mensajes de correo electrónico para un determinado tipo de "
"cambio, simplemente deje el correspondiente campo vacío." "cambio, simplemente deje el correspondiente campo vacío."
@@ -1217,8 +1232,8 @@ msgid ""
"If you mark a project as private, only the project members and " "If you mark a project as private, only the project members and "
"administrators, together with the extra authorized users you provide will " "administrators, together with the extra authorized users you provide will "
"have access to the project. You will still be able to define further access " "have access to the project. You will still be able to define further access "
"rights for the different tabs but the \"Open to all\" and \"Signed in " "rights for the different tabs but the \"Open to all\" and \"Signed in users"
"users\" will default to authorized users only." "\" will default to authorized users only."
msgstr "" msgstr ""
"Si marca un proyecto como privado, sólo los miembros del proyecto y los " "Si marca un proyecto como privado, sólo los miembros del proyecto y los "
"administradores, junto con los usuarios adicionales autorizados tendrán " "administradores, junto con los usuarios adicionales autorizados tendrán "
@@ -1291,14 +1306,14 @@ msgstr "Nueva descarga"
#: IDF/gettexttemplates/idf/downloads/delete.html.php:3 #: IDF/gettexttemplates/idf/downloads/delete.html.php:3
msgid "" msgid ""
"<strong>Attention!</strong> If you want to delete a specific version of your" "<strong>Attention!</strong> If you want to delete a specific version of your "
" software, maybe, someone is depending on this specific version to run his " "software, maybe, someone is depending on this specific version to run his "
"systems. Are you sure, you will not affect anybody when removing this file?" "systems. Are you sure, you will not affect anybody when removing this file?"
msgstr "" msgstr ""
"<strong>¡Atención!</strong> Si desea eliminar una versión específica del " "<strong>¡Atención!</strong> Si desea eliminar una versión específica del "
"software, tal vez, alguien está utilizando esta versión específica para " "software, tal vez, alguien está utilizando esta versión específica para "
"ejecutarlo en su sistema. ¿Estás seguro de que no afectará a nadie cuando se" "ejecutarlo en su sistema. ¿Estás seguro de que no afectará a nadie cuando se "
" elimine este archivo?" "elimine este archivo?"
#: IDF/gettexttemplates/idf/downloads/delete.html.php:4 #: IDF/gettexttemplates/idf/downloads/delete.html.php:4
#, php-format #, php-format
@@ -1464,7 +1479,8 @@ msgid ""
"name." "name."
msgstr "" msgstr ""
"Cada archivo debe tener un nombre distinto y el contenido del archivo\n" "Cada archivo debe tener un nombre distinto y el contenido del archivo\n"
"no se puede cambiar, así que asegúrese de incluir los números de versión en el nombre de cada\n" "no se puede cambiar, así que asegúrese de incluir los números de versión en "
"el nombre de cada\n"
"archivo." "archivo."
#: IDF/gettexttemplates/idf/downloads/submit.html.php:6 #: IDF/gettexttemplates/idf/downloads/submit.html.php:6
@@ -1547,27 +1563,27 @@ msgstr "Proyectos"
msgid "" msgid ""
"<p>This is simple:</p>\n" "<p>This is simple:</p>\n"
"<ol>\n" "<ol>\n"
"<li>Write in the comments \"This is a duplicate of issue 123\", change 123 with the corresponding issue number.</li>\n" "<li>Write in the comments \"This is a duplicate of issue 123\", change 123 "
"with the corresponding issue number.</li>\n"
"<li>Change the status of the current issue to <em>Duplicate</em>.</li>\n" "<li>Change the status of the current issue to <em>Duplicate</em>.</li>\n"
"<li>Submit the changes.</li>\n" "<li>Submit the changes.</li>\n"
"</ol>" "</ol>"
msgstr "" msgstr ""
"<p>Es simple:</p>\n" "<p>Es simple:</p>\n"
"<ol>\n" "<ol>\n"
"<li>Escribe en los comentarios \"Esto es una duplicado del ticket 123\", cambia 123 por el número de ticket correspondiente.</li>\n" "<li>Escribe en los comentarios \"Esto es una duplicado del ticket 123\", "
"cambia 123 por el número de ticket correspondiente.</li>\n"
"<li>Cambia el estado del ticket actual a <em>Duplicado</em>.</li>\n" "<li>Cambia el estado del ticket actual a <em>Duplicado</em>.</li>\n"
"<li>Envíe los cambios.</li>\n" "<li>Envíe los cambios.</li>\n"
"</ol>" "</ol>"
#: IDF/gettexttemplates/idf/faq.html.php:9 #: IDF/gettexttemplates/idf/faq.html.php:9
msgid "" msgid ""
"You need to create an account on <a " "You need to create an account on <a href=\"http://en.gravatar.com/"
"href=\"http://en.gravatar.com/\">Gravatar</a>, this takes about 5 minutes " "\">Gravatar</a>, this takes about 5 minutes and is free."
"and is free."
msgstr "" msgstr ""
"Necesitas crear una cuenta en <a " "Necesitas crear una cuenta en <a href=\"http://en.gravatar.com/\">Gravatar</"
"href=\"http://en.gravatar.com/\">Gravatar</a> , tardarás 5 minutos y es " "a> , tardarás 5 minutos y es gratuito."
"gratuito."
#: IDF/gettexttemplates/idf/faq.html.php:10 #: IDF/gettexttemplates/idf/faq.html.php:10
msgid "" msgid ""
@@ -1708,8 +1724,8 @@ msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/create.html.php:4 #: IDF/gettexttemplates/idf/gadmin/projects/create.html.php:4
msgid "" msgid ""
"<strong>Once you have defined the repository type, you cannot change " "<strong>Once you have defined the repository type, you cannot change it</"
"it</strong>." "strong>."
msgstr "" msgstr ""
"<strong>Una vez que haya definido el tipo de repositorio, no se puede " "<strong>Una vez que haya definido el tipo de repositorio, no se puede "
"modificar</strong>." "modificar</strong>."
@@ -1717,11 +1733,13 @@ msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/create.html.php:5 #: IDF/gettexttemplates/idf/gadmin/projects/create.html.php:5
msgid "" msgid ""
"\n" "\n"
"<p>Specify each person by its login. Each person must have already registered with the given login.</p>\n" "<p>Specify each person by its login. Each person must have already "
"registered with the given login.</p>\n"
"<p>Separate the logins with commas and/or new lines.</p>\n" "<p>Separate the logins with commas and/or new lines.</p>\n"
msgstr "" msgstr ""
"\n" "\n"
"<p>Especifique cada persona por su nombre de usuario. Cada persona debe estar registrada con el nombre de usuario proporcionado.</p>\n" "<p>Especifique cada persona por su nombre de usuario. Cada persona debe "
"estar registrada con el nombre de usuario proporcionado.</p>\n"
"<p>Separe los nombres de usuario con comas y / o saltos de líneas.</p>\n" "<p>Separe los nombres de usuario con comas y / o saltos de líneas.</p>\n"
#: IDF/gettexttemplates/idf/gadmin/projects/create.html.php:14 #: IDF/gettexttemplates/idf/gadmin/projects/create.html.php:14
@@ -1734,8 +1752,7 @@ msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/create.html.php:15 #: IDF/gettexttemplates/idf/gadmin/projects/create.html.php:15
msgid "Provide at least one owner for the project or use a template." msgid "Provide at least one owner for the project or use a template."
msgstr "" msgstr ""
"Proporcione al menos un propietario para el proyecto o utilice una " "Proporcione al menos un propietario para el proyecto o utilice una plantilla."
"plantilla."
#: IDF/gettexttemplates/idf/gadmin/projects/delete.html.php:3 #: IDF/gettexttemplates/idf/gadmin/projects/delete.html.php:3
#, php-format #, php-format
@@ -1755,7 +1772,8 @@ msgid ""
msgstr "" msgstr ""
"\n" "\n"
"<strong>¡Atención!</strong> Eliminar un proyecto es una operación delicada,\n" "<strong>¡Atención!</strong> Eliminar un proyecto es una operación delicada,\n"
"que tiene como resultado <strong>borrar todos los datos</strong> del proyecto.\n" "que tiene como resultado <strong>borrar todos los datos</strong> del "
"proyecto.\n"
#: IDF/gettexttemplates/idf/gadmin/projects/delete.html.php:10 #: IDF/gettexttemplates/idf/gadmin/projects/delete.html.php:10
msgid "" msgid ""
@@ -1911,8 +1929,8 @@ msgstr "Ver <a href=\"%%url%%\">usuarios sin validar</a>."
#: IDF/gettexttemplates/idf/gadmin/users/index.html.php:4 #: IDF/gettexttemplates/idf/gadmin/users/index.html.php:4
msgid "<p>You have here an overview of the users registered in the forge.</p>" msgid "<p>You have here an overview of the users registered in the forge.</p>"
msgstr "" msgstr ""
"<p>Tiene aquí una visión general de los usuarios registrados en la forja. " "<p>Tiene aquí una visión general de los usuarios registrados en la forja. </"
"</p>" "p>"
#: IDF/gettexttemplates/idf/gadmin/users/index.html.php:5 #: IDF/gettexttemplates/idf/gadmin/users/index.html.php:5
msgid "Number of users:" msgid "Number of users:"
@@ -1934,7 +1952,8 @@ msgid ""
"able to create new projects and update other non staff users.\n" "able to create new projects and update other non staff users.\n"
msgstr "" msgstr ""
"Si da permisos de acceso tipo Staff, el usuario será capaz \n" "Si da permisos de acceso tipo Staff, el usuario será capaz \n"
"de crear nuevos proyectos y actualizar a otros usuarios que no sean del Staff.\n" "de crear nuevos proyectos y actualizar a otros usuarios que no sean del "
"Staff.\n"
#: IDF/gettexttemplates/idf/gadmin/users/update.html.php:9 #: IDF/gettexttemplates/idf/gadmin/users/update.html.php:9
msgid "The form contains some errors. Please correct them to update the user." msgid "The form contains some errors. Please correct them to update the user."
@@ -2185,10 +2204,13 @@ msgstr "Volver al ticket"
#, php-format #, php-format
msgid "" msgid ""
"<p><strong>Open issues:</strong> <a href=\"%%open_url%%\">%%open%%</a></p>\n" "<p><strong>Open issues:</strong> <a href=\"%%open_url%%\">%%open%%</a></p>\n"
"<p><strong>Closed issues:</strong> <a href=\"%%closed_url%%\">%%closed%%</a></p>\n" "<p><strong>Closed issues:</strong> <a href=\"%%closed_url%%\">%%closed%%</"
"a></p>\n"
msgstr "" msgstr ""
"<p><strong>Tickets abiertos:</strong> <a href=\"%%open_url%%\">%%open%%</a></p>\n" "<p><strong>Tickets abiertos:</strong> <a href=\"%%open_url%%\">%%open%%</a></"
"<p><strong>Tickets cerrados:</strong> <a href=\"%%closed_url%%\">%%closed%%</a></p>\n" "p>\n"
"<p><strong>Tickets cerrados:</strong> <a href=\"%%closed_url%%\">%%closed%%</"
"a></p>\n"
#: IDF/gettexttemplates/idf/issues/by-label.html.php:7 #: IDF/gettexttemplates/idf/issues/by-label.html.php:7
msgid "Label:" msgid "Label:"
@@ -2200,28 +2222,32 @@ msgstr "Finalizados:"
#: IDF/gettexttemplates/idf/issues/create.html.php:3 #: IDF/gettexttemplates/idf/issues/create.html.php:3
msgid "" msgid ""
"<p>When you submit the issue do not forget to provide the following information:</p>\n" "<p>When you submit the issue do not forget to provide the following "
"information:</p>\n"
"<ul>\n" "<ul>\n"
"<li>The steps to reproduce the problem.</li>\n" "<li>The steps to reproduce the problem.</li>\n"
"<li>The version of the software and your operating system.</li>\n" "<li>The version of the software and your operating system.</li>\n"
"<li>Any information that can help the developers to solve the issue.</li>\n" "<li>Any information that can help the developers to solve the issue.</li>\n"
"<li><strong>Do not provide any password or confidential information!</strong></li>\n" "<li><strong>Do not provide any password or confidential information!</"
"strong></li>\n"
"</ul>" "</ul>"
msgstr "" msgstr ""
"<p>Cuando envío el ticket, no se olvide proporcionar la siguiente información:</p>\n" "<p>Cuando envío el ticket, no se olvide proporcionar la siguiente "
"información:</p>\n"
"<ul>\n" "<ul>\n"
"<li>Los pasos para reproducir el problema.</li>\n" "<li>Los pasos para reproducir el problema.</li>\n"
"<li>La versión del software y tu sistema operativo.</li>\n" "<li>La versión del software y tu sistema operativo.</li>\n"
"<li>Cualquier información que pueda ayudar a los desarrolladores para resolver la incidencia.</li>\n" "<li>Cualquier información que pueda ayudar a los desarrolladores para "
"<li><strong>¡No proporcione ninguna contraseña o información confidencial!</strong></li>\n" "resolver la incidencia.</li>\n"
"<li><strong>¡No proporcione ninguna contraseña o información confidencial!</"
"strong></li>\n"
"</ul>" "</ul>"
#: IDF/gettexttemplates/idf/issues/create.html.php:10 #: IDF/gettexttemplates/idf/issues/create.html.php:10
msgid "" msgid "The form contains some errors. Please correct them to submit the issue."
"The form contains some errors. Please correct them to submit the issue."
msgstr "" msgstr ""
"El formulario contiene algunos errores. Por favor, corríjalos para enviar el" "El formulario contiene algunos errores. Por favor, corríjalos para enviar el "
" ticket." "ticket."
#: IDF/gettexttemplates/idf/issues/create.html.php:11 #: IDF/gettexttemplates/idf/issues/create.html.php:11
#: IDF/gettexttemplates/idf/issues/create.html.php:13 #: IDF/gettexttemplates/idf/issues/create.html.php:13
@@ -2297,10 +2323,13 @@ msgstr "Propietario:"
#, php-format #, php-format
msgid "" msgid ""
"<p><strong>Open issues:</strong> <a href=\"%%open_url%%\">%%open%%</a></p>\n" "<p><strong>Open issues:</strong> <a href=\"%%open_url%%\">%%open%%</a></p>\n"
"<p><strong>Closed issues:</strong> <a href=\"%%closed_url%%\">%%closed%%</a></p>" "<p><strong>Closed issues:</strong> <a href=\"%%closed_url%%\">%%closed%%</"
"a></p>"
msgstr "" msgstr ""
"<p><strong>Tickets abiertos:</strong> <a href=\"%%open_url%%\">%%open%%</a></p>\n" "<p><strong>Tickets abiertos:</strong> <a href=\"%%open_url%%\">%%open%%</a></"
"<p><strong>Tickets cerrados:</strong> <a href=\"%%closed_url%%\">%%closed%%</a></p>" "p>\n"
"<p><strong>Tickets cerrados:</strong> <a href=\"%%closed_url%%\">%%closed%%</"
"a></p>"
#: IDF/gettexttemplates/idf/issues/issue-created-email.txt.php:3 #: IDF/gettexttemplates/idf/issues/issue-created-email.txt.php:3
msgid "" msgid ""
@@ -2344,23 +2373,26 @@ msgstr "Comentarios (más reciente primero):"
#: IDF/gettexttemplates/idf/issues/my-issues.html.php:3 #: IDF/gettexttemplates/idf/issues/my-issues.html.php:3
#, php-format #, php-format
msgid "See the <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% closed</a>." msgid ""
"See the <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% closed</a>."
msgid_plural "" msgid_plural ""
"See the <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% closed</a>." "See the <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% closed</a>."
msgstr[0] "" msgstr[0] ""
"Ver <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% tickets " "Ver <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% tickets cerrado</"
"cerrado</a>." "a>."
msgstr[1] "Ver <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% cerrado</a>." msgstr[1] ""
"Ver <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% cerrado</a>."
#: IDF/gettexttemplates/idf/issues/my-issues.html.php:4 #: IDF/gettexttemplates/idf/issues/my-issues.html.php:4
#, php-format #, php-format
msgid "See the <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% closed</a>." msgid ""
"See the <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% closed</a>."
msgid_plural "" msgid_plural ""
"See the <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% closed</a>." "See the <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% closed</a>."
msgstr[0] "" msgstr[0] ""
"Ver <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% tickets " "Ver <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% tickets cerrado</a>."
"cerrado</a>." msgstr[1] ""
msgstr[1] "Ver <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% cerrado</a>." "Ver <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% cerrado</a>."
#: IDF/gettexttemplates/idf/issues/my-issues.html.php:6 #: IDF/gettexttemplates/idf/issues/my-issues.html.php:6
#: IDF/gettexttemplates/idf/user/dashboard.html.php:7 #: IDF/gettexttemplates/idf/user/dashboard.html.php:7
@@ -2389,8 +2421,8 @@ msgstr "Reportado por %%submitter%%, %%c.creation_dtime%%"
msgid "" msgid ""
"Comment <a href=\"%%url%%\">%%i%%</a> by %%submitter%%, %%c.creation_dtime%%" "Comment <a href=\"%%url%%\">%%i%%</a> by %%submitter%%, %%c.creation_dtime%%"
msgstr "" msgstr ""
"Comentario <a href=\"%%url%%\">%%i%%</a> por %%submitter%%, " "Comentario <a href=\"%%url%%\">%%i%%</a> por %%submitter%%, %%c."
"%%c.creation_dtime%%" "creation_dtime%%"
#: IDF/gettexttemplates/idf/issues/view.html.orig.php:5 #: IDF/gettexttemplates/idf/issues/view.html.orig.php:5
#: IDF/gettexttemplates/idf/issues/view.html.php:5 #: IDF/gettexttemplates/idf/issues/view.html.php:5
@@ -2466,8 +2498,7 @@ msgstr "ver"
#: IDF/gettexttemplates/idf/issues/view.html.orig.php:21 #: IDF/gettexttemplates/idf/issues/view.html.orig.php:21
#: IDF/gettexttemplates/idf/issues/view.html.php:21 #: IDF/gettexttemplates/idf/issues/view.html.php:21
msgid "" msgid "The form contains some errors. Please correct them to change the issue."
"The form contains some errors. Please correct them to change the issue."
msgstr "" msgstr ""
"El formulario contiene algunos errores. Por favor, corríjalos para " "El formulario contiene algunos errores. Por favor, corríjalos para "
"actualizar el ticket." "actualizar el ticket."
@@ -2485,8 +2516,8 @@ msgstr "Seguido por:"
#: IDF/gettexttemplates/idf/login_form.html.php:3 #: IDF/gettexttemplates/idf/login_form.html.php:3
#, php-format #, php-format
msgid "" msgid ""
"If you don't have an account yet, you can create one <a " "If you don't have an account yet, you can create one <a href=\"%%url%%"
"href=\"%%url%%\">here</a>." "\">here</a>."
msgstr "" msgstr ""
"Si aún no tienes una cuenta, puedes crear una <a href=\"%%url%%\">aquí</a>." "Si aún no tienes una cuenta, puedes crear una <a href=\"%%url%%\">aquí</a>."
@@ -2520,10 +2551,11 @@ msgstr "Se tarda menos de un minuto en crearte una cuenta."
#: IDF/gettexttemplates/idf/main-menu.html.php:3 #: IDF/gettexttemplates/idf/main-menu.html.php:3
#, php-format #, php-format
msgid "Welcome, <strong><a class=\"userw\" href=\"%%url%%\">%%user%%</a></strong>." msgid ""
"Welcome, <strong><a class=\"userw\" href=\"%%url%%\">%%user%%</a></strong>."
msgstr "" msgstr ""
"Bienvenido, <strong><a class=\"userw\" " "Bienvenido, <strong><a class=\"userw\" href=\"%%url%%\">%%user%%</a></"
"href=\"%%url%%\">%%user%%</a></strong>." "strong>."
#: IDF/gettexttemplates/idf/main-menu.html.php:4 #: IDF/gettexttemplates/idf/main-menu.html.php:4
msgid "Sign Out" msgid "Sign Out"
@@ -2668,8 +2700,8 @@ msgstr "Activar tu cuenta"
#: IDF/gettexttemplates/idf/register/confirmation.html.php:8 #: IDF/gettexttemplates/idf/register/confirmation.html.php:8
#: IDF/gettexttemplates/idf/user/passrecovery.html.php:8 #: IDF/gettexttemplates/idf/user/passrecovery.html.php:8
msgid "" msgid ""
"This is the last step, but just <strong>be sure to have the cookies " "This is the last step, but just <strong>be sure to have the cookies enabled</"
"enabled</strong> to log in afterwards." "strong> to log in afterwards."
msgstr "" msgstr ""
"Este es el último paso, pero <strong>asegúrese de tener las cookies " "Este es el último paso, pero <strong>asegúrese de tener las cookies "
"activadas</strong> para identificarte." "activadas</strong> para identificarte."
@@ -2690,8 +2722,8 @@ msgid ""
"create a new account. Just go <a href=\"%%url%%\">here</a> to recover your " "create a new account. Just go <a href=\"%%url%%\">here</a> to recover your "
"login name and password." "login name and password."
msgstr "" msgstr ""
"Si ha olvidado los datos de acceso, entonces no tiene porque crear una nueva" "Si ha olvidado los datos de acceso, entonces no tiene porque crear una nueva "
" cuenta. Simplemente haga click <a href=\"%%url%%\">aquí</a> para recuperar " "cuenta. Simplemente haga click <a href=\"%%url%%\">aquí</a> para recuperar "
"su nombre de usuario y contraseña." "su nombre de usuario y contraseña."
#: IDF/gettexttemplates/idf/register/index.html.php:5 #: IDF/gettexttemplates/idf/register/index.html.php:5
@@ -2699,15 +2731,15 @@ msgstr ""
#, php-format #, php-format
msgid "" msgid ""
"With your account, you will able to participate in the life of all the " "With your account, you will able to participate in the life of all the "
"projects hosted here. Participating in a software project must be fun, so if" "projects hosted here. Participating in a software project must be fun, so if "
" you have troubles, you can <a href=\"%%url%%\">let us know about your " "you have troubles, you can <a href=\"%%url%%\">let us know about your issues "
"issues at anytime</a>!" "at anytime</a>!"
msgstr "" msgstr ""
"Con su cuenta, será capaz de participar en el desarrollo de todos los " "Con su cuenta, será capaz de participar en el desarrollo de todos los "
"proyectos alojados aquí. Participar en un proyecto software tiene que ser " "proyectos alojados aquí. Participar en un proyecto software tiene que ser "
"algo divertido e interesante, así que si tiene problemas, puede <a " "algo divertido e interesante, así que si tiene problemas, puede <a href="
"href=\"%%url%%\">¡hacernos saber cuáles son sus inquietudes en cualquier " "\"%%url%%\">¡hacernos saber cuáles son sus inquietudes en cualquier momento</"
"momento</a>!" "a>!"
#: IDF/gettexttemplates/idf/register/index.html.php:6 #: IDF/gettexttemplates/idf/register/index.html.php:6
#: IDF/gettexttemplates/idf/register/index.html~.php:5 #: IDF/gettexttemplates/idf/register/index.html~.php:5
@@ -2727,8 +2759,8 @@ msgid ""
"Be sure to provide a valid email address, as we are sending a validation " "Be sure to provide a valid email address, as we are sending a validation "
"link by email." "link by email."
msgstr "" msgstr ""
"Asegúrese de proporcionar una dirección válida de correo electrónico, ya que" "Asegúrese de proporcionar una dirección válida de correo electrónico, ya que "
" se enviará un enlace de confirmación por correo electrónico." "se enviará un enlace de confirmación por correo electrónico."
#: IDF/gettexttemplates/idf/register/index.html.php:10 #: IDF/gettexttemplates/idf/register/index.html.php:10
#: IDF/gettexttemplates/idf/register/index.html~.php:9 #: IDF/gettexttemplates/idf/register/index.html~.php:9
@@ -2778,30 +2810,35 @@ msgstr "Iniciar revisión del Código"
msgid "" msgid ""
"<p>To start a code review, you need to provide:</p>\n" "<p>To start a code review, you need to provide:</p>\n"
"<ul>\n" "<ul>\n"
"<li>A commit or revision of the current code in the repository from which you started your work.</li>\n" "<li>A commit or revision of the current code in the repository from which "
"<li>A patch describing your changes with respect to the reference commit.</li>\n" "you started your work.</li>\n"
"<li><strong>Check your patch does not provide any password or confidential information!</strong></li>\n" "<li>A patch describing your changes with respect to the reference commit.</"
"li>\n"
"<li><strong>Check your patch does not provide any password or confidential "
"information!</strong></li>\n"
"</ul>" "</ul>"
msgstr "" msgstr ""
"<p>Para iniciar una revisión de código, debe proporcionar:</p>\n" "<p>Para iniciar una revisión de código, debe proporcionar:</p>\n"
"<ul>\n" "<ul>\n"
"<li>Un commit o revisión del actual código del repositorio, desde el que ha comenzado su trabajo.</li>\n" "<li>Un commit o revisión del actual código del repositorio, desde el que ha "
"<li>Un parche que describa los cambios con respecto al commit referenciado.</li>\n" "comenzado su trabajo.</li>\n"
"<li><strong>¡Asegúrese de que el parche no contiene ninguna contraseña o información confidencial!</strong></li>\n" "<li>Un parche que describa los cambios con respecto al commit referenciado.</"
"li>\n"
"<li><strong>¡Asegúrese de que el parche no contiene ninguna contraseña o "
"información confidencial!</strong></li>\n"
"</ul>" "</ul>"
#: IDF/gettexttemplates/idf/review/create.html.php:9 #: IDF/gettexttemplates/idf/review/create.html.php:9
msgid "" msgid ""
"The form contains some errors. Please correct them to submit the code " "The form contains some errors. Please correct them to submit the code review."
"review."
msgstr "" msgstr ""
"El formulario contiene algunos errores. Por favor, corríjalos para enviar la" "El formulario contiene algunos errores. Por favor, corríjalos para enviar la "
" revisión de código." "revisión de código."
#: IDF/gettexttemplates/idf/review/create.html.php:10 #: IDF/gettexttemplates/idf/review/create.html.php:10
msgid "" msgid ""
"Select the commit against which you created your patch to be sure it applies" "Select the commit against which you created your patch to be sure it applies "
" correctly." "correctly."
msgstr "" msgstr ""
"Seleccione el commit contra el que creó el parche para asegurarse de que se " "Seleccione el commit contra el que creó el parche para asegurarse de que se "
"aplica correctamente." "aplica correctamente."
@@ -2864,11 +2901,14 @@ msgid ""
msgstr "" msgstr ""
"La revisión de código es una proceso en el que\n" "La revisión de código es una proceso en el que\n"
"antes o después los cambios son commited en el código del repositorio,\n" "antes o después los cambios son commited en el código del repositorio,\n"
"diferentes personas discuten sobre los cambios en el código. El objetivo es \n" "diferentes personas discuten sobre los cambios en el código. El objetivo "
"es \n"
"<strong>mejorar la calidad del código y las\n" "<strong>mejorar la calidad del código y las\n"
"contribuciones</strong>, por esto, debe ser pragmático cuando escriba \n" "contribuciones</strong>, por esto, debe ser pragmático cuando escriba \n"
"sus comentarios. Menciona correctamente los números de línea (tanto en el antiguo como en el \n" "sus comentarios. Menciona correctamente los números de línea (tanto en el "
"nuevo código) y trata de mantener un buen equilibrio entre seriedad y diversión\n" "antiguo como en el \n"
"nuevo código) y trata de mantener un buen equilibrio entre seriedad y "
"diversión\n"
#: IDF/gettexttemplates/idf/review/view.html.php:13 #: IDF/gettexttemplates/idf/review/view.html.php:13
msgid "" msgid ""
@@ -2880,20 +2920,23 @@ msgid ""
"to propose more contributions</strong>.\n" "to propose more contributions</strong>.\n"
msgstr "" msgstr ""
"\n" "\n"
"<strong>La propuesta para revisar código es intimidante</strong>, debes saber \n" "<strong>La propuesta para revisar código es intimidante</strong>, debes "
"que recibirás críticas, así que por favor, como revisor, <strong>haz que este \n" "saber \n"
"proceso sea divertido</strong>, úsalo para para ayudar al colaborador a aprender tus \n" "que recibirás críticas, así que por favor, como revisor, <strong>haz que "
"este \n"
"proceso sea divertido</strong>, úsalo para para ayudar al colaborador a "
"aprender tus \n"
"normas de codificación y a estructurar el código y <strong>hacer que \n" "normas de codificación y a estructurar el código y <strong>hacer que \n"
"quieran proponer más contribuciones</strong>.\n" "quieran proponer más contribuciones</strong>.\n"
#: IDF/gettexttemplates/idf/review/view.html.php:20 #: IDF/gettexttemplates/idf/review/view.html.php:20
#, php-format #, php-format
msgid "" msgid ""
"Comment <a href=\"%%url%%\">%%i%%</a> by <a href=\"%%whourl%%\">%%who%%</a>," "Comment <a href=\"%%url%%\">%%i%%</a> by <a href=\"%%whourl%%\">%%who%%</a>, "
" %%c.creation_dtime%%" "%%c.creation_dtime%%"
msgstr "" msgstr ""
"Comentario <a href=\"%%url%%\">%%i%%</a> por <a " "Comentario <a href=\"%%url%%\">%%i%%</a> por <a href=\"%%whourl%%\">%%who%%</"
"href=\"%%whourl%%\">%%who%%</a>, %%c.creation_dtime%%" "a>, %%c.creation_dtime%%"
#: IDF/gettexttemplates/idf/review/view.html.php:21 #: IDF/gettexttemplates/idf/review/view.html.php:21
#, php-format #, php-format
@@ -2909,8 +2952,8 @@ msgstr "<a href=\"%%url%%\">Accede</a> para participar en la revisión."
msgid "" msgid ""
"The form contains some errors. Please correct them to submit your review." "The form contains some errors. Please correct them to submit your review."
msgstr "" msgstr ""
"El formulario contiene algunos errores. Por favor, corríjalos para enviar su" "El formulario contiene algunos errores. Por favor, corríjalos para enviar su "
" revisión." "revisión."
#: IDF/gettexttemplates/idf/review/view.html.php:27 #: IDF/gettexttemplates/idf/review/view.html.php:27
#: IDF/gettexttemplates/idf/source/commit.html.php:5 #: IDF/gettexttemplates/idf/source/commit.html.php:5
@@ -3217,15 +3260,13 @@ msgstr ""
msgid "" msgid ""
"You may need to <a href=\"%%url%%\">provide your SSH key</a>. The " "You may need to <a href=\"%%url%%\">provide your SSH key</a>. The "
"synchronization of your SSH key can take a couple of minutes. You can learn " "synchronization of your SSH key can take a couple of minutes. You can learn "
"more about <a " "more about <a href=\"http://www.google.com/search?q=public+ssh+key"
"href=\"http://www.google.com/search?q=public+ssh+key+authentication\">SSH " "+authentication\">SSH key authentication</a>."
"key authentication</a>."
msgstr "" msgstr ""
"Puede que tenga que <a href=\"%%url%%\">proporcionar su clave SSH</a>. La " "Puede que tenga que <a href=\"%%url%%\">proporcionar su clave SSH</a>. La "
"sincronización de la clave SSH puede tomar un par de minutos. Puede saber " "sincronización de la clave SSH puede tomar un par de minutos. Puede saber "
"más acerca de <a " "más acerca de <a href=\"http://www.google.com/search?q=public+ssh+key"
"href=\"http://www.google.com/search?q=public+ssh+key+authentication\">autenticación" "+authentication\">autenticación de claves SSH</a>."
" de claves SSH</a>."
#: IDF/gettexttemplates/idf/source/git/help.html.php:7 #: IDF/gettexttemplates/idf/source/git/help.html.php:7
#: IDF/gettexttemplates/idf/source/mtn/help.html.php:6 #: IDF/gettexttemplates/idf/source/mtn/help.html.php:6
@@ -3305,7 +3346,8 @@ msgid ""
msgstr "" msgstr ""
"Si se trata de un nuevo repositorio, la razón de este error\n" "Si se trata de un nuevo repositorio, la razón de este error\n"
"podría ser que no ha realizado ningún commit y / o push hasta el momento.\n" "podría ser que no ha realizado ningún commit y / o push hasta el momento.\n"
"En este caso, por favor eche un vistazo a la <a href=\"%%url%%\">Página de Ayuda</a>\n" "En este caso, por favor eche un vistazo a la <a href=\"%%url%%\">Página de "
"Ayuda</a>\n"
"sobre cómo tener acceso a su repositorio." "sobre cómo tener acceso a su repositorio."
#: IDF/gettexttemplates/idf/source/mercurial/help.html.php:3 #: IDF/gettexttemplates/idf/source/mercurial/help.html.php:3
@@ -3498,8 +3540,7 @@ msgstr "Clave de API"
msgid "" msgid ""
"Your API key will be regenerated automatically if you change your password." "Your API key will be regenerated automatically if you change your password."
msgstr "" msgstr ""
"La clave de la API se regenera automáticamente si usted cambia su " "La clave de la API se regenera automáticamente si usted cambia su contraseña."
"contraseña."
#: IDF/gettexttemplates/idf/user/myaccount.html.php:12 #: IDF/gettexttemplates/idf/user/myaccount.html.php:12
msgid "Update Your Account" msgid "Update Your Account"
@@ -3535,8 +3576,8 @@ msgid ""
"API key is used to interact with this website using a program." "API key is used to interact with this website using a program."
msgstr "" msgstr ""
"La contraseña adicional se utiliza para acceder a algunos de los sistemas " "La contraseña adicional se utiliza para acceder a algunos de los sistemas "
"externos y la clave de la API se utiliza para interactuar con este sitio web" "externos y la clave de la API se utiliza para interactuar con este sitio web "
" utilizando un programa." "utilizando un programa."
#: IDF/gettexttemplates/idf/user/myaccount.html.php:20 #: IDF/gettexttemplates/idf/user/myaccount.html.php:20
msgid "Show API key and extra password" msgid "Show API key and extra password"
@@ -3556,13 +3597,13 @@ msgstr "Recuperar mi contraseña"
#: IDF/gettexttemplates/idf/user/passrecovery-ask.html.php:6 #: IDF/gettexttemplates/idf/user/passrecovery-ask.html.php:6
msgid "" msgid ""
"Provide either your login or email address, if a corresponding user is found" "Provide either your login or email address, if a corresponding user is found "
" in the database, we will send you an email with the details on how to reset" "in the database, we will send you an email with the details on how to reset "
" your password." "your password."
msgstr "" msgstr ""
"Proporcione un usuario o dirección de correo electrónico, si el usuario " "Proporcione un usuario o dirección de correo electrónico, si el usuario "
"correspondiente se encuentra en la base de datos, le enviaremos un email con" "correspondiente se encuentra en la base de datos, le enviaremos un email con "
" los detalles sobre cómo restablecer su contraseña." "los detalles sobre cómo restablecer su contraseña."
#: IDF/gettexttemplates/idf/user/passrecovery-email.txt.php:3 #: IDF/gettexttemplates/idf/user/passrecovery-email.txt.php:3
#, php-format #, php-format
@@ -3688,12 +3729,15 @@ msgstr "Crear Página"
#: IDF/gettexttemplates/idf/wiki/delete.html.php:3 #: IDF/gettexttemplates/idf/wiki/delete.html.php:3
#, php-format #, php-format
msgid "" msgid ""
"You are looking at an old revision (<em>%%oldrev.summary%%</em>) of the page \n" "You are looking at an old revision (<em>%%oldrev.summary%%</em>) of the "
"page \n"
"<a href=\"%%url%%\">%%page.title%%</a>. This revision was created\n" "<a href=\"%%url%%\">%%page.title%%</a>. This revision was created\n"
"by %%submitter%%." "by %%submitter%%."
msgstr "" msgstr ""
"Está viendo una revisión antigua (<em>%%oldrev.summary%%</em>) de la página \n" "Está viendo una revisión antigua (<em>%%oldrev.summary%%</em>) de la "
"<a href=\"%%url%%\">%%page.title%%</a>. Esta revisión fue creada por %%submitter%%." "página \n"
"<a href=\"%%url%%\">%%page.title%%</a>. Esta revisión fue creada por "
"%%submitter%%."
#: IDF/gettexttemplates/idf/wiki/delete.html.php:6 #: IDF/gettexttemplates/idf/wiki/delete.html.php:6
msgid "" msgid ""
@@ -3720,8 +3764,8 @@ msgid ""
"recover it</strong>." "recover it</strong>."
msgstr "" msgstr ""
"Si elimina esta página de documentación, será eliminada de la base de datos " "Si elimina esta página de documentación, será eliminada de la base de datos "
"con todas las revisiones relacionadas y <strong>no será capaz de " "con todas las revisiones relacionadas y <strong>no será capaz de recuperarla."
"recuperarla.</strong>" "</strong>"
#: IDF/gettexttemplates/idf/wiki/deletepage.html.php:6 #: IDF/gettexttemplates/idf/wiki/deletepage.html.php:6
msgid "Delete Page" msgid "Delete Page"
@@ -3732,15 +3776,23 @@ msgstr "Eliminar página"
msgid "" msgid ""
"\n" "\n"
"<p><strong>Instructions:</strong></p>\n" "<p><strong>Instructions:</strong></p>\n"
"<p>The content of the page can use the <a href=\"%%burl%%\">Markdown syntax</a> with the <a href=\"%%eurl%%\"><em>Extra</em> extension</a>.</p>\n" "<p>The content of the page can use the <a href=\"%%burl%%\">Markdown syntax</"
"<p>Website addresses are automatically linked and you can link to another page in the documentation using double square brackets like that [[AnotherPage]].</p>\n" "a> with the <a href=\"%%eurl%%\"><em>Extra</em> extension</a>.</p>\n"
"<p>To directly include a file content from the repository, embrace its path with triple square brackets: [[[path/to/file.txt]]].</p>\n" "<p>Website addresses are automatically linked and you can link to another "
"page in the documentation using double square brackets like that "
"[[AnotherPage]].</p>\n"
"<p>To directly include a file content from the repository, embrace its path "
"with triple square brackets: [[[path/to/file.txt]]].</p>\n"
msgstr "" msgstr ""
"\n" "\n"
"<p><strong>Instrucciones:</strong></p>\n" "<p><strong>Instrucciones:</strong></p>\n"
"<p>El contenido de la página puede usar <a href=\"%%burl%%\">sintaxis de Marcado</a> con la extensión <a href=\"%%eurl%%\"><em>Extra</em></a>.</p>\n" "<p>El contenido de la página puede usar <a href=\"%%burl%%\">sintaxis de "
"<p>Las direcciones Web se enlazan automáticamente y se puede vincular con otras páginas de la documentación usando corchetes dobles como: [[OtraPágina]].</p>\n" "Marcado</a> con la extensión <a href=\"%%eurl%%\"><em>Extra</em></a>.</p>\n"
"<p>Para incluir directamente el contenido de un fichero del repositorio, rodee la ruta con corchetes triples como: [[[ruta/al/fichero.txt]]].</p>\n" "<p>Las direcciones Web se enlazan automáticamente y se puede vincular con "
"otras páginas de la documentación usando corchetes dobles como: "
"[[OtraPágina]].</p>\n"
"<p>Para incluir directamente el contenido de un fichero del repositorio, "
"rodee la ruta con corchetes triples como: [[[ruta/al/fichero.txt]]].</p>\n"
#: IDF/gettexttemplates/idf/wiki/index.html.php:3 #: IDF/gettexttemplates/idf/wiki/index.html.php:3
#, php-format #, php-format
@@ -3777,7 +3829,8 @@ msgid ""
"use it as reference only if you are sure you need these specific information." "use it as reference only if you are sure you need these specific information."
msgstr "" msgstr ""
"<strong>¡Atención!</strong> Esta página está marcada como obsoleta,\n" "<strong>¡Atención!</strong> Esta página está marcada como obsoleta,\n"
"úsala como referencia solamente si está seguro de que usted necesita específicamente esta información ." "úsala como referencia solamente si está seguro de que usted necesita "
"específicamente esta información ."
#: IDF/gettexttemplates/idf/wiki/view.html.php:5 #: IDF/gettexttemplates/idf/wiki/view.html.php:5
#, php-format #, php-format
@@ -3787,7 +3840,8 @@ msgid ""
"by %%submitter%%." "by %%submitter%%."
msgstr "" msgstr ""
"Está viendo una revisión antigua de la página \n" "Está viendo una revisión antigua de la página \n"
"<a href=\"%%url%%\">%%page.title%%</a>. Esta revisión fue creada por %%submitter%%." "<a href=\"%%url%%\">%%page.title%%</a>. Esta revisión fue creada por "
"%%submitter%%."
#: IDF/gettexttemplates/idf/wiki/view.html.php:10 #: IDF/gettexttemplates/idf/wiki/view.html.php:10
msgid "Table of Content" msgid "Table of Content"
@@ -3845,8 +3899,10 @@ msgstr "fecha de modificación"
#: IDF/Issue.php:194 IDF/IssueComment.php:143 #: IDF/Issue.php:194 IDF/IssueComment.php:143
#, php-format #, php-format
msgid "<a href=\"%1$s\" class=\"%2$s\" title=\"View issue\">Issue %3$d</a>, %4$s" msgid ""
msgstr "<a href=\"%1$s\" class=\"%2$s\" title=\"Ver ticket\">Ticket %3$d</a>, %4$s" "<a href=\"%1$s\" class=\"%2$s\" title=\"View issue\">Issue %3$d</a>, %4$s"
msgstr ""
"<a href=\"%1$s\" class=\"%2$s\" title=\"Ver ticket\">Ticket %3$d</a>, %4$s"
#: IDF/Issue.php:196 #: IDF/Issue.php:196
#, php-format #, php-format
@@ -4001,8 +4057,8 @@ msgstr "No se puede examinar la configuración usher en \"%s\": %s"
#, php-format #, php-format
msgid "usher configuration already contains a server entry named \"%s\"" msgid "usher configuration already contains a server entry named \"%s\""
msgstr "" msgstr ""
"La configuración usher ya contiene una entrada para el servidor llamada " "La configuración usher ya contiene una entrada para el servidor llamada \"%s"
"\"%s\"" "\""
#: IDF/Plugin/SyncMonotone.php:320 IDF/Plugin/SyncMonotone.php:510 #: IDF/Plugin/SyncMonotone.php:320 IDF/Plugin/SyncMonotone.php:510
#, php-format #, php-format
@@ -4047,7 +4103,8 @@ msgstr "No se pudo escribir read-permissions para el proyecto \"%s\""
#: IDF/Plugin/SyncMonotone.php:617 IDF/Plugin/SyncMonotone.php:717 #: IDF/Plugin/SyncMonotone.php:617 IDF/Plugin/SyncMonotone.php:717
#, php-format #, php-format
msgid "Could not write write-permissions file for project \"%s\"" msgid "Could not write write-permissions file for project \"%s\""
msgstr "No se pudo escribir el fichero write-permissions para el proyecto \"%s\"" msgstr ""
"No se pudo escribir el fichero write-permissions para el proyecto \"%s\""
#: IDF/Plugin/SyncMonotone.php:790 #: IDF/Plugin/SyncMonotone.php:790
#, php-format #, php-format
@@ -4106,13 +4163,17 @@ msgstr "voto"
#: IDF/Review/Comment.php:139 IDF/Review/Patch.php:151 #: IDF/Review/Comment.php:139 IDF/Review/Patch.php:151
#, php-format #, php-format
msgid "<a href=\"%1$s\" class=\"%2$s\" title=\"View review\">Review %3$d</a>, %4$s" msgid ""
msgstr "<a href=\"%1$s\" class=\"%2$s\" title=\"Ver revisión\">Revisión %3$d</a>, %4$s" "<a href=\"%1$s\" class=\"%2$s\" title=\"View review\">Review %3$d</a>, %4$s"
msgstr ""
"<a href=\"%1$s\" class=\"%2$s\" title=\"Ver revisión\">Revisión %3$d</a>, "
"%4$s"
#: IDF/Review/Comment.php:141 #: IDF/Review/Comment.php:141
#, php-format #, php-format
msgid "Update of <a href=\"%s\" class=\"%s\">review&nbsp;%d</a>, by %s" msgid "Update of <a href=\"%s\" class=\"%s\">review&nbsp;%d</a>, by %s"
msgstr "Actualización de <a href=\"%s\" class=\"%s\">revisión&nbsp;%d</a>, por %s" msgstr ""
"Actualización de <a href=\"%s\" class=\"%s\">revisión&nbsp;%d</a>, por %s"
#: IDF/Review/Comment.php:151 #: IDF/Review/Comment.php:151
#, php-format #, php-format
@@ -4467,8 +4528,8 @@ msgstr "Lista de mantenimiento: Tickets Cerrados por %s"
#, php-format #, php-format
msgid "This table shows the closed issues in your watch list for %s project." msgid "This table shows the closed issues in your watch list for %s project."
msgstr "" msgstr ""
"Esta tabla muestra los tickets cerrados en su lista de mantenimiento para el" "Esta tabla muestra los tickets cerrados en su lista de mantenimiento para el "
" proyecto %s." "proyecto %s."
#: IDF/Views/Issue.php:118 #: IDF/Views/Issue.php:118
#, php-format #, php-format
@@ -4479,8 +4540,8 @@ msgstr "Lista mantenimiento: Tickets Abiertos por %s"
#, php-format #, php-format
msgid "This table shows the open issues in your watch list for %s project." msgid "This table shows the open issues in your watch list for %s project."
msgstr "" msgstr ""
"Esta tabla muestra los tickets abiertos en su lista de mantenimiento para el" "Esta tabla muestra los tickets abiertos en su lista de mantenimiento para el "
" proyecto %s." "proyecto %s."
#: IDF/Views/Issue.php:195 #: IDF/Views/Issue.php:195
msgid "Watch List: Closed Issues" msgid "Watch List: Closed Issues"
@@ -4856,8 +4917,7 @@ msgid "Confirm Your Account Creation"
msgstr "Confirma la creación de tu cuenta" msgstr "Confirma la creación de tu cuenta"
#: IDF/Views.php:172 #: IDF/Views.php:172
msgid "" msgid "Welcome! You can now participate in the life of your project of choice."
"Welcome! You can now participate in the life of your project of choice."
msgstr "¡Bienvenido! Ahora puedes participar en el proyecto elegido." msgstr "¡Bienvenido! Ahora puedes participar en el proyecto elegido."
#: IDF/Views.php:198 IDF/Views.php:222 IDF/Views.php:263 #: IDF/Views.php:198 IDF/Views.php:222 IDF/Views.php:263

File diff suppressed because it is too large Load Diff

View File

@@ -45,6 +45,7 @@ $m['IDF_Scm_Cache_Git'] = array('relate_to' => array('IDF_Project'));
$m['IDF_UserData'] = array('relate_to' => array('Pluf_User')); $m['IDF_UserData'] = array('relate_to' => array('Pluf_User'));
$m['IDF_EmailAddress'] = array('relate_to' => array('Pluf_User')); $m['IDF_EmailAddress'] = array('relate_to' => array('Pluf_User'));
$m['IDF_IssueRelation'] = array('relate_to' => array('IDF_Issue', 'Pluf_User'));
Pluf_Signal::connect('Pluf_Template_Compiler::construct_template_tags_modifiers', Pluf_Signal::connect('Pluf_Template_Compiler::construct_template_tags_modifiers',
array('IDF_Middleware', 'updateTemplateTagsModifiers')); array('IDF_Middleware', 'updateTemplateTagsModifiers'));

View File

@@ -70,7 +70,6 @@
<div id="ft">{block foot}{/block}</div> <div id="ft">{block foot}{/block}</div>
</div> </div>
{include 'idf/js-hotkeys.html'} {include 'idf/js-hotkeys.html'}
{include 'idf/list-filter.html'}
{block javascript}{/block} {block javascript}{/block}
{if $project} {if $project}
<script type="text/javascript" charset="utf-8">{literal} <script type="text/javascript" charset="utf-8">{literal}

View File

@@ -49,7 +49,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="yui-b context">{block context}{/block}</div> <div class="yui-b context" id="context">{block context}{/block}</div>
</div> </div>
<div id="ft">{block foot}{/block}</div> <div id="ft">{block foot}{/block}</div>
</div> </div>

View File

@@ -66,7 +66,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="yui-b context">{block context}{/block}</div> <div class="yui-b context" id="context">{block context}{/block}</div>
</div> </div>
<div id="ft">{block foot}{/block}</div> <div id="ft">{block foot}{/block}</div>
</div> </div>
@@ -84,6 +84,21 @@ $(document).ready(function(){
else if (frag.length > 3 && frag.substring(0, 3) == '#ic') { else if (frag.length > 3 && frag.substring(0, 3) == '#ic') {
$(frag).addClass("issue-comment-focus"); $(frag).addClass("issue-comment-focus");
} }
var contextTop = $('div#context').position().top;
var contextFixEnabled = true;
$(window).scroll(function() {
if (!contextFixEnabled || $(window).scrollTop() < contextTop)
$('div#context').css('position', 'relative');
else
$('div#context').css('position', 'fixed');
});
$(window).resize(function() {
contextFixEnabled =
$('div#context').offset().top + $('div#context').height() <
$(window).height();
});
$(window).resize();
}); });
//]]>{/literal} //]]>{/literal}
</script>{/if} </script>{/if}

View File

@@ -3,7 +3,7 @@
{block body} {block body}
<div class="download-file"> <div class="download-file">
<a href="{url 'IDF_Views_Download::download', array($project.shortname, $file.id)}">{$file}</a> - {$file.filesize|size} <a href="{url 'IDF_Views_Download::download', array($project.shortname, $file.file)}">{$file}</a> - {$file.filesize|size}
</div> </div>
<p>{blocktrans}<strong>Attention!</strong> If you want to delete a specific version of your software, maybe, someone is depending on this specific version to run his systems. Are you sure, you will not affect anybody when removing this file?{/blocktrans}</p> <p>{blocktrans}<strong>Attention!</strong> If you want to delete a specific version of your software, maybe, someone is depending on this specific version to run his systems. Are you sure, you will not affect anybody when removing this file?{/blocktrans}</p>
@@ -15,7 +15,7 @@
<table class="form" summary=""> <table class="form" summary="">
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td><input type="submit" value="{trans 'Delete File'}" name="submit" /> | <a href="{url 'IDF_Views_Download::index', array($project.shortname)}">{trans 'Cancel'}</a> <td><input type="submit" value="{trans 'Delete File'}" name="submit" /> | <a href="{url 'IDF_Views_Download::index', array($project.shortname)}">{trans 'Cancel'}</a>
</td> </td>
</tr> </tr>
</table> </table>

View File

@@ -4,7 +4,9 @@
<div class="download-file"> <div class="download-file">
{if $deprecated}<p class="smaller">{blocktrans}<strong>Attention!</strong> This file is marked as deprecated, download it only if you are sure you need this specific version.{/blocktrans}</p>{/if} {if $deprecated}<p class="smaller">{blocktrans}<strong>Attention!</strong> This file is marked as deprecated, download it only if you are sure you need this specific version.{/blocktrans}</p>{/if}
<a href="{url 'IDF_Views_Download::download', array($project.shortname, $file.id)}">{$file}</a> - {$file.filesize|size} <a href="{url 'IDF_Views_Download::download', array($project.shortname, $file.file)}">{$file}</a> - {$file.filesize|size}
<br />
<span class="helptext">{trans 'md5:'} {$file.md5}</span>
</div> </div>
{if $file.changelog} {if $file.changelog}
<h2 class="changes">{trans 'Changes'}</h2> <h2 class="changes">{trans 'Changes'}</h2>

View File

@@ -8,12 +8,18 @@
<th>{trans "address"}</th> <th>{trans "address"}</th>
<th>{trans "port"}</th> <th>{trans "port"}</th>
</tr> </tr>
{if count($connections) == 0}
<tr>
<td colspan="2" align="center">{trans 'No connections found.'}</td>
</tr>
{else}
{foreach $connections as $connection} {foreach $connections as $connection}
<tr> <tr>
<td>{$connection.address}</td> <td>{$connection.address}</td>
<td>{$connection.port}</td> <td>{$connection.port}</td>
</tr> </tr>
{/foreach} {/foreach}
{/if}
</table> </table>
{/block} {/block}

View File

@@ -9,6 +9,11 @@
<th>{trans "status"}</th> <th>{trans "status"}</th>
<th>{trans "action"}</th> <th>{trans "action"}</th>
</tr> </tr>
{if count($servers) == 0}
<tr>
<td colspan="3" align="center">{trans 'No monotone servers configured.'}</td>
</tr>
{else}
{foreach $servers as $server} {foreach $servers as $server}
<tr> <tr>
<td>{$server.name}</td> <td>{$server.name}</td>
@@ -31,6 +36,7 @@
{/if} {/if}
</tr> </tr>
{/foreach} {/foreach}
{/if}
</table> </table>
{/block} {/block}

View File

@@ -2,11 +2,12 @@
{block tabissues} class="active"{/block} {block tabissues} class="active"{/block}
{block subtabs} {block subtabs}
<div id="sub-tabs"> <div id="sub-tabs">
<a {if $inOpenIssues}class="active" {/if}href="{url 'IDF_Views_Issue::index', array($project.shortname)}">{trans 'Open Issues'}</a> <a {if $inSummaryIssues}class="active" {/if}href="{url 'IDF_Views_Issue::summary', array($project.shortname)}">{trans 'Summary'}</a>
{if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a> | <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')}">{trans 'My Issues'}</a> | <a {if $inOpenIssues}class="active" {/if}href="{url 'IDF_Views_Issue::index', array($project.shortname)}">{trans 'Open Issues'}</a>
{if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a> | <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'submit')}">{trans 'My Issues'}</a>
| <a {if $inWatchList}class="active" {/if}href="{url 'IDF_Views_Issue::watchList', array($project.shortname, 'open')}">{trans 'My watch list'}</a>{/if} | | <a {if $inWatchList}class="active" {/if}href="{url 'IDF_Views_Issue::watchList', array($project.shortname, 'open')}">{trans 'My watch list'}</a>{/if} |
<form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get"> <form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get">
<input accesskey="4" type="text" value="{$q}" name="q" size="20" /> <input accesskey="4" type="text" value="{$query}" name="q" size="20" />
<input type="submit" name="s" value="{trans 'Search'}" /> <input type="submit" name="s" value="{trans 'Search'}" />
</form> </form>
{if $inIssue} | {if $inIssue} |

View File

@@ -8,16 +8,15 @@
{/block} {/block}
{block context} {block context}
<p><strong>{trans 'Label:'}</strong>
{aurl 'url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'open')}
<a href="{$url}" class="label"><strong>{$label.class}:</strong>{$label.name}</a></p>
{aurl 'open_url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'open')} {aurl 'open_url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'open')}
{aurl 'closed_url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'closed')} {aurl 'closed_url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'closed')}
{blocktrans}<p><strong>Open issues:</strong> <a href="{$open_url}">{$open}</a></p> {blocktrans}<p><strong>Open issues:</strong> <a href="{$open_url}">{$open}</a></p>
<p><strong>Closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p> <p><strong>Closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p>
{/blocktrans}{if $completion} {/blocktrans}
<p><strong>{trans 'Label:'}</strong>
{aurl 'url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'open')}
<a href="{$url}" class="label"><strong>{$label.class}:</strong>{$label.name}</a></p>
{if $completion}
<p><strong>{trans 'Completion:'}</strong> {$completion}</p> <p><strong>{trans 'Completion:'}</strong> {$completion}</p>
{/if} {/if}
{/block} {/block}

View File

@@ -63,12 +63,12 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<th>{$form.f.relation_type.labelTag}:</th> <th>{$form.f.relation_type0.labelTag}:</th>
<td> <td>
{if $form.f.relation_type.errors}{$form.f.relation_type.fieldErrors}{/if} {if $form.f.relation_type0.errors}{$form.f.relation_type0.fieldErrors}{/if}
{if $form.f.relation_issue.errors}{$form.f.relation_issue.fieldErrors}{/if} {if $form.f.relation_issue0.errors}{$form.f.relation_issue0.fieldErrors}{/if}
{$form.f.relation_type|unsafe} {$form.f.relation_type0|unsafe}
{$form.f.relation_issue|unsafe} {$form.f.relation_issue0|unsafe}
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@@ -10,12 +10,22 @@
{if $attachments.count() > 0} {if $attachments.count() > 0}
<hr align="left" class="attach" /> <hr align="left" class="attach" />
<ul> <ul>
{foreach $attachments as $a}<li><a href="{url 'IDF_Views_Issue::viewAttachment', array($project.shortname, $a.id, $a.filename)}">{$a.filename}</a> - {$a.filesize|ssize}</li>{/foreach} {foreach $attachments as $a}<li><a href="{url 'IDF_Views_Issue::viewAttachment', array($project.shortname, $a.id, $a.filename)}">{$a.filename}</a> - {$a.filesize|ssize}</li>{/foreach}
</ul>{/if} </ul>{/if}
{if $c.changes} {if $c.changes}
{foreach $c.changes as $w => $v} {foreach $c.changes as $w => $v}
<strong>{if $w == 'su'}{trans 'Summary:'}{/if}{if $w == 'st'}{trans 'Status:'}{/if}{if $w == 'ow'}{trans 'Owner:'}{/if}{if $w == 'lb'}{trans 'Labels:'}{/if}</strong> {if $w == 'lb'}{assign $l = implode(', ', $v)}{$l}{else}{$v}{/if}<br /> <strong>{if $w == 'su'}{trans 'Summary:'}{/if}{if $w == 'st'}{trans 'Status:'}{/if}{if $w == 'ow'}{trans 'Owner:'}{/if}{if $w == 'lb'}{trans 'Labels:'}{/if}{if $w == 'rel'}{trans 'Relations:'}{/if}</strong>
{if $w == 'lb' or $w == 'rel'}
{foreach $v as $t => $ls}
{foreach $ls as $l}
{if $t == 'rem'}<s>{/if}{$l}{if $t == 'rem'}</s>{/if}
{/foreach}
{/foreach}
{else}
{$v}
{/if}<br />
{/foreach} {/foreach}
{/if} {/if}
</div></content> </div></content>
</entry> </entry>

View File

@@ -13,6 +13,5 @@
{blocktrans}<p><strong>Open issues:</strong> <a href="{$open_url}">{$open}</a></p> {blocktrans}<p><strong>Open issues:</strong> <a href="{$open_url}">{$open}</a></p>
<p><strong>Closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p>{/blocktrans} <p><strong>Closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p>{/blocktrans}
{assign $cloud_url = 'IDF_Views_Issue::listLabel'} {assign $cloud_url = 'IDF_Views_Issue::listLabel'}
{assign $cloud = 'issues'}
{include 'idf/tags-cloud.html'} {include 'idf/tags-cloud.html'}
{/block} {/block}

View File

@@ -16,7 +16,7 @@
{if strlen($c.content) > 0}{$c.content|safe}{/if}{if $c.changedIssue()} {if strlen($c.content) > 0}{$c.content|safe}{/if}{if $c.changedIssue()}
{foreach $c.changes as $w => $v} {foreach $c.changes as $w => $v}
{if $w == 'su'}{trans 'Summary:'}{/if}{if $w == 'st'}{trans 'Status:'}{/if}{if $w == 'ow'}{trans 'Owner:'}{/if}{if $w == 'lb'}{trans 'Labels:'}{/if} {if $w == 'lb'}{assign $l = implode(', ', $v)}{$l}{else}{$v}{/if}{/foreach}{/if}{assign $attachments = $c.get_attachment_list()}{if $attachments.count() > 0} {if $w == 'su'}{trans 'Summary:'}{/if}{if $w == 'st'}{trans 'Status:'}{/if}{if $w == 'ow'}{trans 'Owner:'}{/if}{if $w == 'lb'}{trans 'Labels:'}{/if}{if $w == 'rel'}{trans 'Relations:'}{/if} {if $w == 'lb' or $w == 'rel'}{foreach $v as $t => $ls}{foreach $ls as $l}{if $t == 'rem'}-{/if}{$l} {/foreach}{/foreach}{else}{$v}{/if}{/foreach}{/if}{assign $attachments = $c.get_attachment_list()}{if $attachments.count() > 0}
{trans 'Attachments:'}{foreach $attachments as $a} {trans 'Attachments:'}{foreach $attachments as $a}
- {$a.filename|safe} - {$a.filesize|ssize} - {$a.filename|safe} - {$a.filesize|ssize}

View File

@@ -51,33 +51,45 @@
return row.to; return row.to;
} }
}); });
$("#id_relation_type").autocomplete(auto_relation_types, { for (var idx = 0; ; ++idx) {
minChars: 0, if ($("#id_relation_type" + idx).length == 0)
width: 310, break;
matchContains: true,
max: 50, $("#id_relation_type" + idx).autocomplete(auto_relation_types, {
highlightItem: false, minChars: 0,
formatItem: function(row, i, max, term) { width: 310,
return row.to.replace(new RegExp("(" + term + ")", "gi"), "<strong>$1</strong>") + " <span style='font-size: 80%;'>" + row.name + "</span>"; matchContains: true,
}, max: 50,
formatResult: function(row) { highlightItem: false,
return row.to; formatItem: function(row, i, max, term) {
} return row.to.replace(new RegExp("(" + term + ")", "gi"), "<strong>$1</strong>") + " <span style='font-size: 80%;'>" + row.name + "</span>";
}); },
$("#id_relation_issue").autocomplete("{/literal}{url 'IDF_Views_Issue::autoCompleteIssueList', array($project.shortname)}{literal}", { formatResult: function(row) {
minChars: 0, return row.to;
width: 310, }
matchContains: true, });
max: 10, {/literal}
delay: 500, {if $issue}
highlightItem: false, {literal}
formatItem: function(row, i, max, term) { $("#id_relation_issue" + idx).autocomplete("{/literal}{url 'IDF_Views_Issue::autoCompleteIssueList', array($project.shortname, $issue.id)}{literal}", {
return row[1].replace(new RegExp("(" + term + ")", "gi"), "<strong>$1</strong>") + " <span style='font-size: 80%;'>" + row[0] + "</span>"; minChars: 0,
}, width: 310,
formatResult: function(row) { matchContains: true,
return row[1]; max: 10,
} multiple: true,
}); delay: 500,
highlightItem: false,
formatItem: function(row, i, max, term) {
return row[1].replace(new RegExp("(" + term + ")", "gi"), "<strong>$1</strong>") + " <span style='font-size: 80%;'>" + row[0] + "</span>";
},
formatResult: function(row) {
return row[1];
}
});
{/literal}
{/if}
{literal}
}
}); });
{/literal} //--> {/literal} //-->
</script> </script>

View File

@@ -8,5 +8,25 @@
{/block} {/block}
{block context} {block context}
<p><strong>{trans 'Found issues:'}</strong> {$issues.nb_items}</p> {aurl 'open_url', 'IDF_Views_Issue::searchStatus', array($project.shortname, 'open'), array('q' => $query)}
{aurl 'closed_url', 'IDF_Views_Issue::searchStatus', array($project.shortname, 'closed'), array('q' => $query)}
{if $tag != null}
{aurl 'open_url', 'IDF_Views_Issue::searchLabel', array($project.shortname, $tag.id, 'open'), array('q' => $query)}
{aurl 'closed_url', 'IDF_Views_Issue::searchLabel', array($project.shortname, $tag.id, 'closed'), array('q' => $query)}
{/if}
{blocktrans}
<p><strong>Found open issues:</strong> <a href="{$open_url}">{$open}</a></p>
<p><strong>Found closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p>{/blocktrans}
{if $tag !== null}
{blocktrans}<p><strong>Label:</strong>
<a href="{$open_url}" class="label"><strong>{$tag.class}:</strong>{$tag.name}</a></p>{/blocktrans}
{else}
{* yes, this is duplicated from tags-cloud.html, but the code there cannot be easily overridden *}
<div id="tagscloud" class="smaller"><dl>{foreach $all_tags as $class => $labels}
<dt class="label">{$class}</dt>
{foreach $labels as $idx => $label}
{aurl 'url', 'IDF_Views_Issue::searchLabel', array($project.shortname, $label.id, $status), array('q'=> $query)}
<dd><a href="{$url}" class="label">{$label.name}{if $idx != count($labels) - 1},{/if}</a></dd>
{/foreach}{/foreach}</dl></p>
{/if}
{/block} {/block}

View File

@@ -0,0 +1,100 @@
{extends "idf/issues/base.html"}
{block docclass}yui-t2{assign $inSummaryIssues=true}{/block}
{block body}
{if $trackerEmpty}
{aurl 'create_url', 'IDF_Views_Issue::create', array($project.shortname)}
<p>{blocktrans}The issue tracker is empty.<br />You can create your first issue <a href="{$create_url}">here</a>.{/blocktrans}</p>
{else}
<div class='issue-summary'>
{foreach $tagStatistics as $key => $class}
<div>
<h2>{blocktrans}Unresolved: By {$key}{/blocktrans}</h2>
<table class='issue-summary'>
<tbody>
{foreach $class as $key => $value}
<tr>
<td class="name"><a href="{url 'IDF_Views_Issue::listLabel', array($project.shortname, $value[2], 'open')}">{$key}</a></td>
<td class="count">{$value[0]}</td>
<td class="graph">
<table class='graph'>
<tbody><tr>
<td style="width:{$value[1] * 0.8 + 1}%" class="graph-color" valign="center">
<div class="colour-bar"></div>
</td>
<td class="graph-percent">{$value[1]}%</td>
</tr>
</tbody>
</table>
</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
{/foreach}
</div>
<div class='issue-summary'>
{if $status}
<div>
<h2>{blocktrans}Status Summary{/blocktrans}</h2>
<table class='issue-summary'>
<tbody>
{foreach $status as $key => $value}
<tr>
<td class="name"><a href="{url 'IDF_Views_Issue::listStatus', array($project.shortname, $key)}">{$key}</a></td>
<td class="count">{$value[0]}</td>
<td class="graph">
<table class='graph'>
<tbody><tr>
<td style="width:{$value[1] * 0.8 + 1}%" class="graph-color" valign="center">
<div class="colour-bar"></div>
</td>
<td class="graph-percent">{$value[1]}%</td>
</tr>
</tbody>
</table>
</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
{/if}
{if $ownerStatistics}
<div>
<h2>{blocktrans}Unresolved: By Assignee{/blocktrans}</h2>
<table class='issue-summary'>
<tbody>
{foreach $ownerStatistics as $key => $value}
<tr>
<td class="name">
{if !empty($value[2])}
{aurl 'url', 'IDF_Views_Issue::userIssues', array($project.shortname, $value[2], 'owner')}
<a href="{$url}">{$key}</a>
{else}{$key}{/if}
</td>
<td class="count">{$value[0]}</td>
<td class="graph">
<table class='graph'>
<tbody><tr>
<td style="width:{$value[1] * 0.8 + 1}%" class="graph-color" valign="center">
<div class="colour-bar"></div>
</td>
<td class="graph-percent">{$value[1]}%</td>
</tr>
</tbody>
</table>
</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
{/if}
</div>
{/if}
{/block}

View File

@@ -1,5 +1,5 @@
{extends "idf/issues/base.html"} {extends "idf/issues/base.html"}
{block docclass}yui-t2{assign $inMyIssues = true}{/block} {block docclass}yui-t2{if $user.login == $login}{assign $inMyIssues = true}{/if}{/block}
{block body} {block body}
{$issues.render} {$issues.render}
{if !$user.isAnonymous()} {if !$user.isAnonymous()}
@@ -8,10 +8,10 @@
{/block} {/block}
{block context} {block context}
{aurl 'owner_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'owner')} {aurl 'owner_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'owner')}
{aurl 'submit_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')} {aurl 'submit_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'submit')}
{aurl 'owner_closed_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'ownerclosed')} {aurl 'owner_closed_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'ownerclosed')}
{aurl 'submit_closed_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submitclosed')} {aurl 'submit_closed_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'submitclosed')}
<p><strong>{trans 'Submitted issues:'}</strong> <a href="{$submit_url}">{$nb_submit}</a> <p><strong>{trans 'Submitted issues:'}</strong> <a href="{$submit_url}">{$nb_submit}</a>
{if $nb_submit_closed}<br /><span class="helptext">{blocktrans $nb_submit_closed}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{plural}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{/blocktrans}</span>{/if}</p> {if $nb_submit_closed}<br /><span class="helptext">{blocktrans $nb_submit_closed}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{plural}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{/blocktrans}</span>{/if}</p>
{if $nb_owner > 0} {if $nb_owner > 0}

View File

@@ -17,7 +17,7 @@
{assign $submitter_data = $c.get_submitter_data()} {assign $submitter_data = $c.get_submitter_data()}
<div class="issue-comment{if $i == 0} issue-comment-first{/if}{if $i == ($nc-1)} issue-comment-last{/if}" id="ic{$c.id}"> <div class="issue-comment{if $i == 0} issue-comment-first{/if}{if $i == ($nc-1)} issue-comment-last{/if}" id="ic{$c.id}">
{if $submitter_data.avatar != ''} {if $submitter_data.avatar != ''}
<img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{media}/upload/avatars/{$submitter_data.avatar}" alt=" " /> <img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{upload}/avatars/{$submitter_data.avatar}" alt=" " />
{else} {else}
<img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " /> <img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " />
{/if} {/if}
@@ -40,7 +40,16 @@
{if $i> 0 and $c.changedIssue()} {if $i> 0 and $c.changedIssue()}
<div class="issue-changes"> <div class="issue-changes">
{foreach $c.changes as $w => $v} {foreach $c.changes as $w => $v}
<strong>{if $w == 'su'}{trans 'Summary:'}{/if}{if $w == 'st'}{trans 'Status:'}{/if}{if $w == 'ow'}{trans 'Owner:'}{/if}{if $w == 'lb'}{trans 'Labels:'}{/if}</strong> {if $w == 'lb'}{assign $l = implode(', ', $v)}{$l}{else}{$v}{/if}<br /> <strong>{if $w == 'su'}{trans 'Summary:'}{/if}{if $w == 'st'}{trans 'Status:'}{/if}{if $w == 'ow'}{trans 'Owner:'}{/if}{if $w == 'lb'}{trans 'Labels:'}{/if}{if $w == 'rel'}{trans 'Relations:'}{/if}</strong>
{if $w == 'lb' or $w == 'rel'}
{foreach $v as $t => $ls}
{foreach $ls as $l}
{if $t == 'rem'}<s>{/if}{$l}{if $t == 'rem'}</s>{/if}
{/foreach}
{/foreach}
{else}
{$v}
{/if}<br />
{/foreach} {/foreach}
</div> </div>
{/if} {/if}
@@ -120,6 +129,23 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<th>{$form.f.relation_type0.labelTag}:</th>
<td>
{assign $prevField}
{foreach $form as $field}
{if strpos($field.name, 'relation_type') === 0}
{$field|unsafe}
{assign $prevField = $field}
{/if}
{if strpos($field.name, 'relation_issue') === 0}
{$field|unsafe}<br />
{if $prevField.errors}{$prevField.fieldErrors}{/if}
{if $field.errors}{$field.fieldErrors}{/if}
{/if}
{/foreach}
</td>
</tr>
<tr>
<th>{$form.f.label1.labelTag}:</th> <th>{$form.f.label1.labelTag}:</th>
<td> <td>
{if $form.f.label1.errors}{$form.f.label1.fieldErrors}{/if}{$form.f.label1|unsafe} {if $form.f.label1.errors}{$form.f.label1.fieldErrors}{/if}{$form.f.label1|unsafe}
@@ -161,6 +187,21 @@
<span class="label"><a href="{$url}" class="label"><strong>{$tag.class}:</strong>{$tag.name}</a></span><br /> <span class="label"><a href="{$url}" class="label"><strong>{$tag.class}:</strong>{$tag.name}</a></span><br />
{/foreach} {/foreach}
</p>{/if} </p>{/if}
{if count($related_issues) > 0}
{foreach $related_issues as $verb => $rel_issues}
<p>
<strong>{blocktrans}This issue {$verb}{/blocktrans}</strong><br />
{foreach $rel_issues as $rel_issue}
<span class="label">
<a href="{url 'IDF_Views_Issue::view', array($project.shortname, $rel_issue.other_issue)}"
class="label" title="{$rel_issue.other_summary}">
<strong>{$rel_issue.other_issue}</strong> - {$rel_issue.other_summary|shorten:30}
</a>
</span><br />
{/foreach}
</p>
{/foreach}
{/if}
</div> </div>
{/block} {/block}
{block javascript}{if $form}{include 'idf/issues/js-autocomplete.html'} {block javascript}{if $form}{include 'idf/issues/js-autocomplete.html'}

View File

@@ -10,8 +10,8 @@
{if $hasWikiAccess}{hotkey 'Shift+o', 'IDF_Views_Wiki::index', array($project.shortname)}{/if} {if $hasWikiAccess}{hotkey 'Shift+o', 'IDF_Views_Wiki::index', array($project.shortname)}{/if}
{if $hasSourceAccess}{hotkey 'Shift+s', 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}{/if} {if $hasSourceAccess}{hotkey 'Shift+s', 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}{/if}
{if $hasIssuesAccess and !$user.isAnonymous()} {if $hasIssuesAccess and !$user.isAnonymous()}
{hotkey 'Shift+m', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')} {hotkey 'Shift+m', 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'submit')}
{hotkey 'Shift+w', 'IDF_Views_Issue::myIssues', array($project.shortname, 'owner')} {hotkey 'Shift+w', 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'owner')}
{/if}{/if} //--> {/if}{/if} //-->
</script> </script>

View File

@@ -21,7 +21,9 @@
{/if} {/if}
{/foreach} {/foreach}
</p> </p>
<p><strong>Subscribe to this timeline</strong><br /><img src="{media '/idf/img/rss.png'}" alt="{trans 'RSS'}" /> <a href="{$feedurl}" >Atom feeds</a></p> <p><strong>{trans 'Subscribe to this timeline'}</strong><br />
<span class="label"><img src="{media '/idf/img/rss.png'}" alt="{trans 'RSS'}" /> <a href="{$feedurl}">{trans 'Atom feed'}</a></span>
</p>
{/block} {/block}

View File

@@ -4,7 +4,7 @@
<div id="sub-tabs"> <div id="sub-tabs">
<a {if $inOpenReviews}class="active" {/if}href="{url 'IDF_Views_Review::index', array($project.shortname)}">{trans 'Open Reviews'}</a> {* <a {if $inOpenReviews}class="active" {/if}href="{url 'IDF_Views_Review::index', array($project.shortname)}">{trans 'Open Reviews'}</a> {*
{if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a> | <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')}">{trans 'My Issues'}</a>{/if} | {if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a> | <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'submit')}">{trans 'My Issues'}</a>{/if} |
<form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get"> <form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get">
<input accesskey="4" type="text" value="{$q}" name="q" size="20" /> <input accesskey="4" type="text" value="{$q}" name="q" size="20" />
<input type="submit" name="s" value="{trans 'Search'}" /> <input type="submit" name="s" value="{trans 'Search'}" />

View File

@@ -5,17 +5,4 @@
{if !$user.isAnonymous()} {if !$user.isAnonymous()}
{aurl 'url', 'IDF_Views_Review::create', array($project.shortname)} {aurl 'url', 'IDF_Views_Review::create', array($project.shortname)}
<p><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/add.png'}" alt="+" align="bottom" /></a> <a href="{$url}">{trans 'Start Code Review'}</a></p>{/if} <p><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/add.png'}" alt="+" align="bottom" /></a> <a href="{$url}">{trans 'Start Code Review'}</a></p>{/if}
{/block} {/block}
{block context}
{*
{aurl 'open_url', 'IDF_Views_Issue::index', array($project.shortname)}
{aurl 'closed_url', 'IDF_Views_Issue::listStatus', array($project.shortname, 'closed')}
{blocktrans}<p><strong>Open issues:</strong> <a href="{$open_url}">{$open}</a></p>
<p><strong>Closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p>{/blocktrans}
{assign $class = ''}{assign $i = 0}
<p class="smaller">{foreach $project.getTagCloud($cloud) as $label}
{aurl 'url', 'IDF_Views_Issue::listLabel', array($project.shortname, $label.id, 'open')}
{if $class != $label.class}{if $i != 0}<br />{/if}<strong class="label">{$label.class}:</strong> {/if}
<a href="{$url}" class="label">{$label.name}</a>,{assign $class = $label.class}{assign $i = $i + 1}{/foreach}</p>
*}{/block}

View File

@@ -10,8 +10,26 @@
{/if} {/if}
</div> </div>
{/if} {/if}
<table class="disp" summary=""> {if !$user.isAnonymous()}
<tr><td> <div class="issue-submit-info" style="width: 50%; float: right; position: relative;">
<p><strong>{trans 'How to Participate in a Code Review'}</strong></p>
<p>{blocktrans}Code review is a process in which
after or before changes are commited into the code repository,
different people discuss the code changes. The goal is
to <strong>improve the quality of the code and the
contributions</strong>, as such, you must be pragmatic when writing
your review. Correctly mention the line numbers (in the old or in the
new file) and try to keep a good balance between seriousness and fun.
{/blocktrans}</p>
<p>{blocktrans}
<strong>Proposing code for review is intimidating</strong>, you know
you will receive critics, so please, as a reviewer, <strong>keep this
process fun</strong>, use it to help your contributor learn your
coding standards and the structure of the code and <strong>make them want
to propose more contributions</strong>.
{/blocktrans}</p></div>
{/if}
<table class="commit" summary=""> <table class="commit" summary="">
<tr> <tr>
<th><strong>{trans 'Created:'}</strong></th><td>{$patch.creation_dtime|date:"%Y-%m-%d %H:%M:%S"} ({$patch.creation_dtime|dateago})</td> <th><strong>{trans 'Created:'}</strong></th><td>{$patch.creation_dtime|date:"%Y-%m-%d %H:%M:%S"} ({$patch.creation_dtime|dateago})</td>
@@ -45,39 +63,12 @@
<th>&nbsp;</th><td><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/package-grey.png'}" alt="{trans 'Archive'}" align="bottom" /></a> <a href="{$url}" class="soft">{trans 'Download the corresponding diff file'}</a></td> <th>&nbsp;</th><td><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/package-grey.png'}" alt="{trans 'Archive'}" align="bottom" /></a> <a href="{$url}" class="soft">{trans 'Download the corresponding diff file'}</a></td>
</tr> </tr>
</table> </table>
</td><td>
{if !$user.isAnonymous()}
<div class="issue-submit-info" style="width: 90%; float: right; position: relative;">
<p><strong>{trans 'How to Participate in a Code Review'}</strong></p>
<p>{blocktrans}Code review is a process in which
after or before changes are commited into the code repository,
different people discuss the code changes. The goal is
to <strong>improve the quality of the code and the
contributions</strong>, as such, you must be pragmatic when writing
your review. Correctly mention the line numbers (in the old or in the
new file) and try to keep a good balance between seriousness and fun.
{/blocktrans}</p>
<p>{blocktrans}
<strong>Proposing code for review is intimidating</strong>, you know
you will receive critics, so please, as a reviewer, <strong>keep this
process fun</strong>, use it to help your contributor learn your
coding standards and the structure of the code and <strong>make them want
to propose more contributions</strong>.
{/blocktrans}</p></div>
{/if}
</td></tr>
</table>
<form method="post" action="."> <form method="post" action=".">
{foreach $files as $file=>$def} {foreach $files as $file=>$def}
<table class="diff" summary=" ">
<tbody> {$def[0]}
<tr id="diff-{$file|md5}"><th colspan="4">{$file}</th></tr>
<tr><th colspan="2">{trans 'Old'}</th><th colspan="2">{trans 'New'}</th></tr>
{$def[0]}
</tbody>
</table>
{assign $fcomments = $def[2]} {assign $fcomments = $def[2]}
{assign $nc = $fcomments.count()} {assign $nc = $fcomments.count()}
{assign $i = 1} {assign $i = 1}
@@ -109,7 +100,7 @@ to propose more contributions</strong>.
{foreach $comments as $c}{ashowuser 'submitter', $c.get_submitter(), $request}{assign $submitter = $c.get_submitter()}{assign $submitter_data = $c.get_submitter_data()} {foreach $comments as $c}{ashowuser 'submitter', $c.get_submitter(), $request}{assign $submitter = $c.get_submitter()}{assign $submitter_data = $c.get_submitter_data()}
<div class="issue-comment{if $i == 1} issue-comment-first{/if}{if $i == ($nc)} issue-comment-last{/if}" id="ic{$c.id}"> <div class="issue-comment{if $i == 1} issue-comment-first{/if}{if $i == ($nc)} issue-comment-last{/if}" id="ic{$c.id}">
{if $submitter_data.avatar != ''} {if $submitter_data.avatar != ''}
<img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{media}/upload/avatars/{$submitter_data.avatar}" alt=" " /> <img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{upload}/avatars/{$submitter_data.avatar}" alt=" " />
{else} {else}
<img style="float:right; position: relative;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " /> <img style="float:right; position: relative;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " />
{/if} {/if}

View File

@@ -34,6 +34,9 @@
{foreach $changes.renames as $oldname => $newname} {foreach $changes.renames as $oldname => $newname}
<tr><td><span class="scm-action renamed" title="{trans 'renamed'}">R</span></td><td><a href="{url 'IDF_Views_Source::tree', array($project.shortname, $commit, $newname)}">{$oldname} &rarr; {$newname}</a></td></tr> <tr><td><span class="scm-action renamed" title="{trans 'renamed'}">R</span></td><td><a href="{url 'IDF_Views_Source::tree', array($project.shortname, $commit, $newname)}">{$oldname} &rarr; {$newname}</a></td></tr>
{/foreach} {/foreach}
{foreach $changes.copies as $srcname => $destname}
<tr><td><span class="scm-action copied" title="{trans 'copied'}">C</span></td><td><a href="{url 'IDF_Views_Source::tree', array($project.shortname, $commit, $destname)}">{$srcname} &rarr; {$destname}</a></td></tr>
{/foreach}
{foreach $changes.additions as $filename} {foreach $changes.additions as $filename}
<tr><td><span class="scm-action added" title="{trans 'added'}">A</span></td><td><a href="{url 'IDF_Views_Source::tree', array($project.shortname, $commit, $filename)}">{$filename}</a>{if !empty($diff.files[$filename])} (<a href="#diff-{$filename|md5}">{trans 'full'}</a>){/if}</td></tr> <tr><td><span class="scm-action added" title="{trans 'added'}">A</span></td><td><a href="{url 'IDF_Views_Source::tree', array($project.shortname, $commit, $filename)}">{$filename}</a>{if !empty($diff.files[$filename])} (<a href="#diff-{$filename|md5}">{trans 'full'}</a>){/if}</td></tr>
{/foreach} {/foreach}

View File

@@ -0,0 +1,2 @@
{extends "idf/source/invalid_revision.html"}

View File

@@ -32,13 +32,13 @@
<tr> <tr>
<td class="fileicon"><img src="{media '/idf/img/'~$file.type~'.png'}" alt="{$file.type}" /></td> <td class="fileicon"><img src="{media '/idf/img/'~$file.type~'.png'}" alt="{$file.type}" /></td>
{if $file.type != 'extern'} {if $file.type != 'extern'}
<td{if $file.type == 'tree'} colspan="4"{/if}><a href="{$url}">{$file.file}</a></td>{else}<td><a href="#" title="{$file.hash}">{$file.file}</a></td>{/if} <td{if $file.type == 'tree'} colspan="4"{/if}><a href="{$url}"><nobr>{$file.file}</nobr></a></td>{else}<td><a href="#" title="{$file.hash}"><nobr>{$file.file}</nobr></a></td>{/if}
{if $file.type == 'blob'} {if $file.type == 'blob'}
{if isset($file.date) and $file.log != '----'} {if isset($file.date) and $file.log != '----'}
<td><span class="smaller">{$file.date|dateago:"without"}</span></td> <td><span class="smaller"><nobr>{$file.date|dateago:"without"}</nobr></span></td>
<td><span class="smaller">{$file.author|strip_tags|trim}{trans ':'} {issuetext $file.log, $request, true, false}</span></td> <td><span class="smaller">{$file.author|strip_tags|trim}{trans ':'} {issuetext $file.log, $request, true, false}</span></td>
{else}<td colspan="2"></td>{/if} {else}<td colspan="2"></td>{/if}
<td>{$file.size|size}</td>{/if} <td><nobr>{$file.size|size}</nobr></td>{/if}
{if $file.type == 'extern'} {if $file.type == 'extern'}
<td colspan="3">{$file.extern}</td> <td colspan="3">{$file.extern}</td>
{/if} {/if}

View File

@@ -5,6 +5,7 @@
<p>{blocktrans}The branch or revision <b>{$commit}</b> is not valid or does not exist <p>{blocktrans}The branch or revision <b>{$commit}</b> is not valid or does not exist
in this repository.{/blocktrans}</p> in this repository.{/blocktrans}</p>
{if count($branches) > 0}
<p>{blocktrans}The following list shows all available branches:{/blocktrans}</p> <p>{blocktrans}The following list shows all available branches:{/blocktrans}</p>
<ul> <ul>
{foreach $branches as $branch => $path} {foreach $branches as $branch => $path}
@@ -14,6 +15,7 @@ in this repository.{/blocktrans}</p>
</li> </li>
{/foreach} {/foreach}
</ul> </ul>
{/if}
{if $isOwner or $isMember} {if $isOwner or $isMember}
{aurl 'url', 'IDF_Views_Source::help', array($project.shortname)} {aurl 'url', 'IDF_Views_Source::help', array($project.shortname)}

View File

@@ -0,0 +1,2 @@
{extends "idf/source/invalid_revision.html"}

View File

@@ -5,12 +5,25 @@
<h2 class="top"><a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $commit)}">{trans 'Root'}</a><span class="sep">/</span>{if $breadcrumb}{$breadcrumb|safe}{/if}</h2> <h2 class="top"><a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $commit)}">{trans 'Root'}</a><span class="sep">/</span>{if $breadcrumb}{$breadcrumb|safe}{/if}</h2>
<table class="code" summary=" "> <table class="code" summary=" ">
{if !$tree_in and !$tags_in} {if (!$tree_in and !$tags_in) or $props}
{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)}
<tfoot> <tfoot>
<tr><th colspan="2">{blocktrans}Source at commit <a class="mono" href="{$url}">{$commit}</a> created {$cobject.date|dateago}.{/blocktrans}<br/> {if $props}
<span class="smaller">{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}</span> <tr><th colspan="2">
</th></tr> <ul>
{foreach $props as $prop => $val}
<li>{blocktrans}Property <strong>{$prop}</strong> set to <em>{$val}</em>{/blocktrans}</li>
{/foreach}
</ul>
</th></tr>
{/if}
{if !$tree_in and !$tags_in}
<tr>
<th colspan="2">{blocktrans}Source at commit <a class="mono" href="{$url}">{$commit}</a> created {$cobject.date|dateago}.{/blocktrans}<br/>
<span class="smaller">{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}</span>
</th>
</tr>
{/if}
</tfoot> </tfoot>
{/if} {/if}
<tbody> <tbody>

View File

@@ -0,0 +1,2 @@
{extends "idf/source/invalid_revision.html"}

View File

@@ -11,14 +11,27 @@
<th>{trans 'Message'}</th> <th>{trans 'Message'}</th>
<th>{trans 'Size'}</th> <th>{trans 'Size'}</th>
</tr> </tr>
</thead>{if !$tree_in and !$tags_in} </thead>
{if (!$tree_in and !$tags_in) or $props}
{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)}
<tfoot> <tfoot>
<tr><th colspan="5">{blocktrans}Source at commit <a class="mono" href="{$url}">{$commit}</a> created {$cobject.date|dateago}.{/blocktrans}<br/> {if $props}
<span class="smaller">{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}</span> <tr><th colspan="5">
</th></tr> <ul>
{foreach $props as $prop => $val}
<li>{blocktrans}Property <strong>{$prop}</strong> set to <em>{$val}</em>{/blocktrans}</li>
{/foreach}
</ul>
</th></tr>
{/if}
{if !$tree_in and !$tags_in}
<tr><th colspan="5">{blocktrans}Source at commit <a class="mono" href="{$url}">{$commit}</a> created {$cobject.date|dateago}.{/blocktrans}<br/>
<span class="smaller">{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}</span>
</th></tr>
{/if}
{/if}
</tfoot> </tfoot>
{/if}<tbody> <tbody>
{if $base} {if $base}
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>

View File

@@ -4,7 +4,7 @@
<p><strong>{trans 'Revision:'}</strong> {$commit}</p> <p><strong>{trans 'Revision:'}</strong> {$commit}</p>
<p> <p>
<input accesskey="4" type="text" value="{$commit}" name="rev" size="5" /> <input accesskey="4" type="text" value="{$commit}" name="rev" size="5" />
<input type="submit" name="s" value="{trans 'Go to revision'}" /> <input type="submit" name="s" value="{trans 'Switch'}" />
</p> </p>
</form> </form>
{/block} {/block}

View File

@@ -4,7 +4,7 @@
<p><strong>{trans 'Revision:'}</strong> {$commit}</p> <p><strong>{trans 'Revision:'}</strong> {$commit}</p>
<p> <p>
<input accesskey="4" type="text" value="{$commit}" name="rev" size="5"/> <input accesskey="4" type="text" value="{$commit}" name="rev" size="5"/>
<input type="submit" name="s" value="{trans 'Go to revision'}"/> <input type="submit" name="s" value="{trans 'Switch'}"/>
</p> </p>
</form> </form>
{/block} {/block}

View File

@@ -6,13 +6,13 @@
<table class="code" summary=" "> <table class="code" summary=" ">
{if !$tree_in || $props} {if !$tree_in || $props}
{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)}
<tfoot> <tfoot>
{if $props} {if $props}
<tr><th colspan="2"> <tr><th colspan="2">
<ul> <ul>
{foreach $props as $prop => $val} {foreach $props as $prop => $val}
<li>{trans 'Property'} <strong>{$prop}</strong> {trans 'set to:'} <em>{$val}</em></li> <li>{blocktrans}Property <strong>{$prop}</strong> set to <em>{$val}</em>{/blocktrans}</li>
{/foreach} {/foreach}
</ul> </ul>
</th></tr> </th></tr>
@@ -38,7 +38,7 @@
<p> <p>
<input accesskey="4" type="text" value="{$commit}" name="rev" size="5" /> <input accesskey="4" type="text" value="{$commit}" name="rev" size="5" />
<input type="hidden" name="sourcefile" value="{$base}"/> <input type="hidden" name="sourcefile" value="{$base}"/>
<input type="submit" name="s" value="{trans 'Go to revision'}" /></p> <input type="submit" name="s" value="{trans 'Switch'}" /></p>
</form> </form>
{/block} {/block}

View File

@@ -0,0 +1,30 @@
{extends "idf/source/base.html"}
{block docclass}yui-t2{assign $inError=true}{/block}
{block body}
<p>{blocktrans}The revision <b>{$commit}</b> is not valid or does not exist
in this repository.{/blocktrans}</p>
{if count($branches) > 0}
<p>{blocktrans}The following list shows all available branches:{/blocktrans}</p>
<ul>
{foreach $branches as $branch => $path}
{if $path}{aurl 'url', 'IDF_Views_Source::tree', array($project.shortname, 'HEAD', $path)}
{else}{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, 'HEAD')}{/if}
<li class="label">
<a href="{$url}" class="label">{$branch}</a>
</li>
{/foreach}
</ul>
{/if}
{if $isOwner or $isMember}
{aurl 'url', 'IDF_Views_Source::help', array($project.shortname)}
<p>{blocktrans}If this is a new repository, the reason for this error
could be that you have not committed and / or pushed any change so far.
In this case please take a look at the <a href="{$url}">Help</a> page
how to access your repository.{/blocktrans}</p>
{/if}
{/block}

View File

@@ -13,13 +13,13 @@
<th>{trans 'Size'}</th> <th>{trans 'Size'}</th>
</tr> </tr>
</thead>{if (!$tree_in and !$tags_in and $commit != 'HEAD') || $props} </thead>{if (!$tree_in and !$tags_in and $commit != 'HEAD') || $props}
{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)}
<tfoot> <tfoot>
{if $props} {if $props}
<tr><th colspan="6"> <tr><th colspan="6">
<ul> <ul>
{foreach $props as $prop => $val} {foreach $props as $prop => $val}
<li>{trans 'Property'} <strong>{$prop}</strong> {trans 'set to:'} <em>{$val}</em></li> <li>{blocktrans}Property <strong>{$prop}</strong> set to <em>{$val}</em>{/blocktrans}</li>
{/foreach} {/foreach}
</ul> </ul>
</th></tr> </th></tr>
@@ -45,7 +45,7 @@
<td class="fileicon"><img src="{media '/idf/img/'~$file.type~'.png'}" alt="{$file.type}" /></td> <td class="fileicon"><img src="{media '/idf/img/'~$file.type~'.png'}" alt="{$file.type}" /></td>
<td><a href="{$url}">{$file.file}</a></td> <td><a href="{$url}">{$file.file}</a></td>
<td><span class="smaller">{$file.date|dateago:"without"}</span></td> <td><span class="smaller">{$file.date|dateago:"without"}</span></td>
<td>{$file.rev}</td> <td>{$file.rev}</td>
<td{if $file.type != 'blob'} colspan="2"{/if}><span class="smaller">{$file.author|strip_tags|trim}{trans ':'} {issuetext $file.log, $request, true, false}</span></td> <td{if $file.type != 'blob'} colspan="2"{/if}><span class="smaller">{$file.author|strip_tags|trim}{trans ':'} {issuetext $file.log, $request, true, false}</span></td>
{if $file.type == 'blob'} {if $file.type == 'blob'}
@@ -66,7 +66,7 @@
<p> <p>
<input accesskey="4" type="text" value="{$commit}" name="rev" size="5" /> <input accesskey="4" type="text" value="{$commit}" name="rev" size="5" />
<input type="hidden" name="sourcefile" value="{$base}"/> <input type="hidden" name="sourcefile" value="{$base}"/>
<input type="submit" name="s" value="{trans 'Go to revision'}" /></p> <input type="submit" name="s" value="{trans 'Switch'}" /></p>
</form> </form>
<p><strong>{trans 'Branches:'}</strong><br /> <p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch => $path} {foreach $branches as $branch => $path}

View File

@@ -1,8 +1,6 @@
{assign $class = ''}{assign $i = 0} <div id="tagscloud" class="smaller"><dl>{foreach $project.getTagCloud($cloud) as $class => $labels}
<div id="tagscloud" class="smaller"><dl>{foreach $project.getTagCloud($cloud) as $label} <dt class="label">{$class}</dt>
{foreach $labels as $idx => $label}
{aurl 'url', $cloud_url, array($project.shortname, $label.id, 'open')} {aurl 'url', $cloud_url, array($project.shortname, $label.id, 'open')}
{if $class != $label.class}<dt class="label">{$label.class}</dt>{assign $i = 0}{/if} <dd><a href="{$url}" class="label">{$label.name}{if $idx != count($labels) - 1},{/if}</a></dd>
<dd><a href="{$url}" class="label">{$label.name},</a></dd> {/foreach}{/foreach}</dl></p>
{assign $class = $label.class}
{assign $i = $i + 1}
{/foreach}</dl></p>

View File

@@ -3,7 +3,7 @@
<table class="form" summary=""> <table class="form" summary="">
<tr> <tr>
<th style="text-align: right">{if $user_data.avatar != ''} <th style="text-align: right">{if $user_data.avatar != ''}
<img style="max-height: 60px; max-width: 60px;" src="{media}/upload/avatars/{$user_data.avatar}" alt=" " /> <img style="max-height: 60px; max-width: 60px;" src="{upload}/avatars/{$user_data.avatar}" alt=" " />
{else} {else}
<img src="http://www.gravatar.com/avatar/{$member.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " /> <img src="http://www.gravatar.com/avatar/{$member.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " />
{/if} {/if}

View File

@@ -1,5 +1,10 @@
{extends "idf/wiki/base.html"} {extends "idf/wiki/base.html"}
{block extraheader}{if $oldrev}<meta name="ROBOTS" content="NOINDEX" />{/if}{/block}
{block extraheader}
{if $oldrev}<meta name="ROBOTS" content="NOINDEX" />{/if}
<link rel="stylesheet" type="text/css" media="print" href="{media '/idf/css/print-wiki.css'}" />
{/block}
{block docclass}yui-t3{assign $inView=true}{/block} {block docclass}yui-t3{assign $inView=true}{/block}
{block body} {block body}

View File

@@ -38,11 +38,9 @@ class IDF_DiffTest extends PHPUnit_Framework_TestCase
} }
$expectedfile = str_replace('.diff', '.expected', $difffile); $expectedfile = str_replace('.diff', '.expected', $difffile);
$expectedcontent = @file_get_contents($expectedfile);
$diffcontent = file_get_contents($difffile); $diffcontent = file_get_contents($difffile);
$diff = new IDF_Diff($diffcontent, $diffprefix); $diff = new IDF_Diff($diffcontent, $diffprefix);
$this->assertEquals(unserialize($expectedcontent), $this->assertEquals(require_once($expectedfile),
$diff->parse(), $diff->parse(),
'parsed diff '.$difffile.' does not match'); 'parsed diff '.$difffile.' does not match');
} }

90
test/IDF/ProjectTest.php Normal file
View File

@@ -0,0 +1,90 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
#
# InDefero 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.
#
# InDefero 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 St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
class IDF_ProjectTest extends PHPUnit_Framework_TestCase
{
public function testGetIssueCountByOwner()
{
// Add users
$user1 = new Pluf_User();
$user1->login = 'user1';
$user1->create();
$user2 = new Pluf_User();
$user2->login = 'user2';
$user2->create();
// Add a project
$prj = new IDF_Project();
$prj->create();
$tag = $prj->getTagIdsByStatus('open');
// First test with no issue
$stats = $prj->getIssueCountByOwner();
$this->assertEquals($stats, array());
// Add some issues
$issue1 = new IDF_Issue();
$issue1->project = $prj;
$issue1->submitter = $user1;
$issue1->owner = $user1;
$issue1->status = new IDF_Tag($tag[0]);
$issue1->create();
$issue2 = new IDF_Issue();
$issue2->project = $prj;
$issue2->submitter = $user2;
$issue2->owner = $user1;
$issue2->status = new IDF_Tag($tag[0]);
$issue2->create();
$issue3 = new IDF_Issue();
$issue3->project = $prj;
$issue3->submitter = $user2;
$issue3->status = new IDF_Tag($tag[0]);
$issue3->create();
$issue4 = new IDF_Issue();
$issue4->project = $prj;
$issue4->submitter = $user2;
$issue4->owner = $user2;
$issue4->status = new IDF_Tag($tag[0]);
$issue4->create();
// 2nd test
$stats = $prj->getIssueCountByOwner();
$expected = array(0 => 1,
$user2->id => 1,
$user1->id => 2);
$this->assertEquals($stats, $expected);
// Clean DB
$issue4->delete();
$issue3->delete();
$issue2->delete();
$issue1->delete();
$prj->delete();
$user2->delete();
$user1->delete();
}
}

View File

@@ -625,6 +625,7 @@ END;
'additions' => array('new_dir', 'new_dir/new_file'), 'additions' => array('new_dir', 'new_dir/new_file'),
'deletions' => array('old_dir', 'old_dir/old_file'), 'deletions' => array('old_dir', 'old_dir/old_file'),
'renames' => array('dir_with_old_name' => 'new_dir/dir_with_new_name'), 'renames' => array('dir_with_old_name' => 'new_dir/dir_with_new_name'),
'copies' => array(), // this is always empty
'patches' => array('existing_file'), 'patches' => array('existing_file'),
'properties' => array( 'properties' => array(
'new_dir/dir_with_new_name' => array( 'new_dir/dir_with_new_name' => array(
@@ -714,6 +715,31 @@ END;
$this->assertEquals('', $commit->diff); $this->assertEquals('', $commit->diff);
} }
public function testGetProperties()
{
$rev = "2345678901234567890123456789012345678901";
$instance = $this->createMock();
$instance->getStdio()->setExpectedOutput(array('interface_version'), array(), '13.1');
$stdio =<<<END
attr "foo" "bar"
state "unchanged"
attr "some new
line" "and more <weird>-
nesses"
END;
$instance->getStdio()->setExpectedOutput(array('get_attributes', 'foo'), array('r' => $rev), $stdio);
$res = $instance->getProperties($rev, 'foo');
$this->assertEquals(2, count($res));
$this->assertEquals(array(
'foo' => 'bar',
"some new\nline" => "and more <weird>-\nnesses"
), $res);
}
public function testGetExtraProperties() public function testGetExtraProperties()
{ {
$instance = $this->createMock(); $instance = $this->createMock();

55
test/IDF/Scm/SvnTest.php Normal file
View File

@@ -0,0 +1,55 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2011 Céondo Ltd and contributors.
#
# InDefero 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.
#
# InDefero 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 St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
class IDF_Scm_SvnTest extends PHPUnit_Framework_TestCase
{
private $proj = null;
public function setUp()
{
$this->proj = new IDF_Project();
$this->proj->id = 1;
$this->proj->name = $this->proj->shortname = 'test';
$this->proj->create();
}
public function tearDown()
{
$this->proj->delete();
}
public function createMock($reponame)
{
$repourl = 'file://'.DATADIR.'/'.__CLASS__.'/'.$reponame;
$instance = new IDF_Scm_Svn($repourl, $this->proj);
return $instance;
}
public function testAccessHistoryOfRenamedAndDeletedFiles()
{
$instance = $this->createMock(__FUNCTION__);
$this->assertEquals('new-file', $instance->getPathInfo('new-file', 1)->fullpath);
$this->assertEquals('alternate-name', $instance->getPathInfo('alternate-name', 2)->fullpath);
}
}

View File

@@ -21,7 +21,7 @@ if (file_exists($testconfig['db_database'])) {
} }
echo ">>> creating empty test database...\n"; echo ">>> creating empty test database...\n";
passthru('php ' . PLUF_PATH . '/migrate.php --conf=' . TESTDIR . '/config.php -a -i'); passthru('php ' . escapeshellarg(PLUF_PATH.'/migrate.php') . ' --conf=' . escapeshellarg(TESTDIR.'/config.php').' -a -i');
echo ">>> setting up web application...\n"; echo ">>> setting up web application...\n";
require 'Pluf.php'; require 'Pluf.php';

File diff suppressed because one or more lines are too long

View File

@@ -15,375 +15,10 @@ Index: LinuxBIOSv1/src/include/cpu/i786/cpufixup.h
+ +
+ +
Eigenschafts<EFBFBD>nderungen: LinuxBIOSv1\src\include\cpu\i786\cpufixup.h Eigenschafts<EFBFBD>nderungen: LinuxBIOSv1\src\include\cpu\i786\cpufixup.h
___________________________________________________________________ ___________________________________________________________________
Hinzugef<EFBFBD>gt: svn:keywords Hinzugef<EFBFBD>gt: svn:keywords
+ Author Date Id Revision + Author Date Id Revision
Hinzugef<EFBFBD>gt: svn:eol-style Hinzugef<EFBFBD>gt: svn:eol-style
+ native
Index: LinuxBIOSv1/src/mainboard/tyan/guiness/cmos.layout
===================================================================
--- LinuxBIOSv1/src/mainboard/tyan/guiness/cmos.layout (Revision 0)
+++ LinuxBIOSv1/src/mainboard/tyan/guiness/cmos.layout (Revision 665)
@@ -0,0 +1,63 @@
+entries
+
+#start-bit length config config-ID name
+#0 8 r 0 seconds
+#8 8 r 0 alarm_seconds
+#16 8 r 0 minutes
+#24 8 r 0 alarm_minutes
+#32 8 r 0 hours
+#40 8 r 0 alarm_hours
+#48 8 r 0 day_of_week
+#56 8 r 0 day_of_month
+#64 8 r 0 month
+#72 8 r 0 year
+#80 4 r 0 rate_select
+#84 3 r 0 REF_Clock
+#87 1 r 0 UIP
+#88 1 r 0 auto_switch_DST
+#89 1 r 0 24_hour_mode
+#90 1 r 0 binary_values_enable
+#91 1 r 0 square-wave_out_enable
+#92 1 r 0 update_finished_enable
+#93 1 r 0 alarm_interrupt_enable
+#94 1 r 0 periodic_interrupt_enable
+#95 1 r 0 disable_clock_updates
+#96 288 r 0 temporary_filler
+0 384 r 0 reserved_memory
+384 1 e 4 boot_option
+385 1 e 4 last_boot
+386 3 e 5 baud_rate
+392 4 e 6 debug_level
+396 1 e 1 power_on_after_fail
+#401 1 e 1 ECC_memory
+#402 1 e 2 hda_disk
+#403 1 e 2 hdb_disk
+#404 1 e 2 hdc_disk
+#405 1 e 2 hdd_disk
+#406 2 e 7 boot_device
+
+enumerations
+
+#ID value text
+1 0 Disable
+1 1 Enable
+#2 0 No
+#2 1 Yes
+4 0 Fallback
+4 1 Normal
+5 0 115200
+5 1 57600
+5 2 38400
+5 3 19200
+5 4 9600
+5 5 4800
+5 6 2400
+5 7 1200
+6 6 Notice
+6 7 Info
+6 8 Debug
+6 9 Spew
+#7 0 Network
+#7 1 HDD
+#7 2 Floppy
+#7 3 ROM
Eigenschafts<EFBFBD>nderungen: LinuxBIOSv1\src\mainboard\tyan\guiness\cmos.layout
___________________________________________________________________
Hinzugef<EFBFBD>gt: svn:keywords
+ Author Date Id Revision
Hinzugef<EFBFBD>gt: svn:eol-style
+ native
Index: LinuxBIOSv1/src/config/linuxbios_c.ld
===================================================================
--- LinuxBIOSv1/src/config/linuxbios_c.ld (Revision 0)
+++ LinuxBIOSv1/src/config/linuxbios_c.ld (Revision 665)
@@ -0,0 +1,105 @@
+/*
+ * Memory map:
+ *
+ * _RAMBASE
+ * : data segment
+ * : bss segment
+ * : heap
+ * : stack
+ */
+/*
+ * Bootstrap code for the STPC Consumer
+ * Copyright (c) 1999 by Net Insight AB. All Rights Reserved.
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Written by Johan Rydberg, based on work by Daniel Kahlin.
+ * Rewritten by Eric Biederman
+ */
+/*
+ * We use ELF as output format. So that we can
+ * debug the code in some form.
+ */
+INCLUDE ldoptions
+
+ENTRY(_start)
+
+SECTIONS
+{
+ . = _RAMBASE;
+ /*
+ * First we place the code and read only data (typically const declared).
+ * This get placed in rom.
+ */
+ .text : {
+ _text = .;
+ *(.text);
+ *(.text.*);
+ . = ALIGN(16);
+ _etext = .;
+ }
+ .rodata : {
+ _rodata = .;
+ . = ALIGN(4);
+ streams = . ;
+ *(.rodata.streams)
+ estreams = .;
+ . = ALIGN(4);
+ pci_drivers = . ;
+ *(.rodata.pci_drivers)
+ epci_drivers = . ;
+ *(.rodata)
+ *(.rodata.*)
+ _erodata = .;
+ }
+ /*
+ * After the code we place initialized data (typically initialized
+ * global variables). This gets copied into ram by startup code.
+ * __data_start and __data_end shows where in ram this should be placed,
+ * whereas __data_loadstart and __data_loadend shows where in rom to
+ * copy from.
+ */
+ .data : {
+ _data = .;
+ *(.data)
+ _edata = .;
+ }
+ /*
+ * bss does not contain data, it is just a space that should be zero
+ * initialized on startup. (typically uninitialized global variables)
+ * crt0.S fills between _bss and _ebss with zeroes.
+ */
+ _bss = .;
+ .bss . : {
+ *(.bss)
+ *(.sbss)
+ *(COMMON)
+ }
+ _ebss = .;
+ _end = .;
+ _stack = .;
+ .stack . : {
+ /* Reserve a stack for each possible cpu, +1 extra */
+ . = ((MAX_CPUS * STACK_SIZE) + STACK_SIZE) ;
+ }
+ _estack = .;
+ _heap = .;
+ .heap . : {
+ /* Reserve 256K for the heap */
+ . = HEAP_SIZE ;
+ . = ALIGN(4);
+ }
+ _eheap = .;
+ /* The ram segment
+ * This is all address of the memory resident copy of linuxBIOS.
+ */
+ _ram_seg = _text;
+ _eram_seg = _eheap;
+ /DISCARD/ : {
+ *(.comment)
+ *(.note)
+ }
+}
Eigenschafts<EFBFBD>nderungen: LinuxBIOSv1\src\config\linuxbios_c.ld
___________________________________________________________________
Hinzugef<EFBFBD>gt: svn:keywords
+ Author Date Id Revision
Hinzugef<EFBFBD>gt: svn:eol-style
+ native
Index: LinuxBIOSv1/src/arch/i386/include/arch/rom_segs.h
===================================================================
--- LinuxBIOSv1/src/arch/i386/include/arch/rom_segs.h (Revision 0)
+++ LinuxBIOSv1/src/arch/i386/include/arch/rom_segs.h (Revision 665)
@@ -0,0 +1,10 @@
+#ifndef ROM_SEGS_H
+#define ROM_SEGS_H
+
+#define ROM_CODE_SEG 0x08
+#define ROM_DATA_SEG 0x10
+
+#define CACHE_RAM_CODE_SEG 0x18
+#define CACHE_RAM_DATA_SEG 0x20
+
+#endif /* ROM_SEGS_H */
Eigenschafts<EFBFBD>nderungen: LinuxBIOSv1\src\arch\i386\include\arch\rom_segs.h
___________________________________________________________________
Hinzugef<EFBFBD>gt: svn:keywords
+ Author Date Id Revision
Hinzugef<EFBFBD>gt: svn:eol-style
+ native
Index: LinuxBIOSv1/src/arch/i386/lib/c_start.S
===================================================================
--- LinuxBIOSv1/src/arch/i386/lib/c_start.S (Revision 0)
+++ LinuxBIOSv1/src/arch/i386/lib/c_start.S (Revision 665)
@@ -0,0 +1,135 @@
+#include <arch/asm.h>
+#include <arch/intel.h>
+#ifdef SMP
+#include <cpu/p6/apic.h>
+#endif
+ .section ".text"
+ .code32
+ .globl _start
+_start:
+ cli
+ lgdt %cs:gdtaddr
+ ljmp $0x10, $1f
+1: movl $0x18, %ax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+
+ intel_chip_post_macro(0x13) /* post 12 */
+
+ /** clear stack */
+ leal EXT(_stack), %edi
+ movl $EXT(_estack), %ecx
+ subl %edi, %ecx
+ xorl %eax, %eax
+ rep
+ stosb
+
+ /** clear bss */
+ leal EXT(_bss), %edi
+ movl $EXT(_ebss), %ecx
+ subl %edi, %ecx
+ jz .Lnobss
+ xorl %eax, %eax
+ rep
+ stosb
+.Lnobss:
+
+ /* set new stack */
+ movl $_estack, %esp
+#ifdef SMP
+ /* Get the cpu id */
+ movl $APIC_DEFAULT_BASE, %edi
+ movl APIC_ID(%edi), %eax
+ shrl $24, %eax
+
+ /* Get the cpu index (MAX_CPUS on error) */
+ movl $-4, %ebx
+1: addl $4, %ebx
+ cmpl $(MAX_CPUS << 2), %ebx
+ je 2
+ cmpl %eax, EXT(initial_apicid)(%ebx)
+ jne 1b
+2: shrl $2, %ebx
+
+ /* Now compute the appropriate stack */
+ movl %ebx, %eax
+ movl $STACK_SIZE, %ebx
+ mull %ebx
+ subl %eax, %esp
+
+ /* push the boot_complete flag */
+ pushl %ebp
+
+ /* Save the stack location */
+ movl %esp, %ebp
+
+ /*
+ * Now we are finished. Memory is up, data is copied and
+ * bss is cleared. Now we call the main routine and
+ * let it do the rest.
+ */
+ intel_chip_post_macro(0xfe) /* post fe */
+
+ /* Resort the stack location */
+ movl %ebp, %esp
+
+ /* The boot_complete flag has already been pushed */
+ call EXT(hardwaremain)
+ /*NOTREACHED*/
+.Lhlt:
+ intel_chip_post_macro(0xee) /* post fe */
+ hlt
+ jmp .Lhlt
+#endif
+
+
+ .globl gdt, gdt_end, gdt_limit
+
+gdt_limit = gdt_end - gdt - 1 /* compute the table limit */
+gdtaddr:
+ .word gdt_limit
+ .long gdt /* we know the offset */
+
+gdt:
+// selgdt 0
+ .word 0x0000, 0x0000 /* dummy */
+ .byte 0x00, 0x00, 0x00, 0x00
+
+// selgdt 8
+ .word 0x0000, 0x0000 /* dummy */
+ .byte 0x00, 0x00, 0x00, 0x00
+
+// selgdt 0x10
+/* flat code segment */
+ .word 0xffff, 0x0000
+ .byte 0x00, 0x9b, 0xcf, 0x00
+
+//selgdt 0x18
+/* flat data segment */
+ .word 0xffff, 0x0000
+ .byte 0x00, 0x93, 0xcf, 0x00
+
+//selgdt 0x20
+ .word 0x0000, 0x0000 /* dummy */
+ .byte 0x00, 0x00, 0x00, 0x00
+
+#if defined(CONFIG_VGABIOS) && (CONFIG_VGABIOS == 1)
+ // from monty:
+ /* 0x00009a00,0000ffffULL, 20h: 16-bit 64k code at 0x00000000 */
+ /* 0x00009200,0000ffffULL 28h: 16-bit 64k data at 0x00000000 */
+// selgdt 0x28
+/*16-bit 64k code at 0x00000000 */
+ .word 0xffff, 0x0000
+ .byte 0, 0x9a, 0, 0
+
+// selgdt 0x30
+/*16-bit 64k data at 0x00000000 */
+ .word 0xffff, 0x0000
+ .byte 0, 0x92, 0, 0
+#endif // defined(CONFIG_VGABIOS) && (CONFIG_VGABIOS == 1)
+gdt_end:
+
+.code32
Eigenschafts<EFBFBD>nderungen: LinuxBIOSv1\src\arch\i386\lib\c_start.S
___________________________________________________________________
Hinzugef<EFBFBD>gt: svn:keywords
+ Author Date Id Revision
Hinzugef<EFBFBD>gt: svn:eol-style
+ native + native

File diff suppressed because one or more lines are too long

View File

@@ -1 +1,33 @@
a:1:{s:1:"a";a:2:{s:6:"chunks";a:1:{i:0;a:1:{i:0;a:3:{i:0;s:0:"";i:1;s:1:"1";i:2;s:3:"abc";}}}s:10:"chunks_def";a:1:{i:0;a:2:{i:0;a:2:{i:0;s:1:"0";i:1;s:1:"0";}i:1;a:2:{i:0;s:1:"1";i:1;i:1;}}}}} <?php return array (
'a' =>
array (
'chunks' =>
array (
0 =>
array (
0 =>
array (
0 => '',
1 => '1',
2 => "abc\r\n",
),
),
),
'chunks_def' =>
array (
0 =>
array (
0 =>
array (
0 => '0',
1 => '0',
),
1 =>
array (
0 => '1',
1 => 1,
),
),
),
),
);

View File

@@ -1 +1,39 @@
a:1:{s:1:"a";a:2:{s:6:"chunks";a:1:{i:0;a:2:{i:0;a:3:{i:0;s:0:"";i:1;s:1:"1";i:2;s:3:"abc";}i:1;a:3:{i:0;s:0:"";i:1;i:2;i:2;s:3:"abc";}}}s:10:"chunks_def";a:1:{i:0;a:2:{i:0;a:2:{i:0;s:1:"0";i:1;s:1:"0";}i:1;a:2:{i:0;s:1:"1";i:1;s:1:"2";}}}}} <?php return array (
'a' =>
array (
'chunks' =>
array (
0 =>
array (
0 =>
array (
0 => '',
1 => '1',
2 => "abc\r\n",
),
1 =>
array (
0 => '',
1 => 2,
2 => "abc\r\n",
),
),
),
'chunks_def' =>
array (
0 =>
array (
0 =>
array (
0 => '0',
1 => '0',
),
1 =>
array (
0 => '1',
1 => '2',
),
),
),
),
);

View File

@@ -1 +1,39 @@
a:1:{s:1:"a";a:2:{s:6:"chunks";a:1:{i:0;a:2:{i:0;a:3:{i:0;s:1:"1";i:1;s:1:"1";i:2;s:3:"abc";}i:1;a:3:{i:0;i:2;i:1;s:0:"";i:2;s:3:"abc";}}}s:10:"chunks_def";a:1:{i:0;a:2:{i:0;a:2:{i:0;s:1:"1";i:1;s:1:"2";}i:1;a:2:{i:0;s:1:"1";i:1;i:1;}}}}} <?php return array (
'a' =>
array (
'chunks' =>
array (
0 =>
array (
0 =>
array (
0 => '1',
1 => '1',
2 => "abc\r\n",
),
1 =>
array (
0 => 2,
1 => '',
2 => "abc\r\n",
),
),
),
'chunks_def' =>
array (
0 =>
array (
0 =>
array (
0 => '1',
1 => '2',
),
1 =>
array (
0 => '1',
1 => 1,
),
),
),
),
);

View File

@@ -1 +1,39 @@
a:1:{s:1:"a";a:2:{s:6:"chunks";a:1:{i:0;a:2:{i:0;a:3:{i:0;s:1:"1";i:1;s:0:"";i:2;s:3:"abc";}i:1;a:3:{i:0;s:0:"";i:1;s:1:"1";i:2;s:2:"ls";}}}s:10:"chunks_def";a:1:{i:0;a:2:{i:0;a:2:{i:0;s:1:"1";i:1;i:1;}i:1;a:2:{i:0;s:1:"1";i:1;i:1;}}}}} <?php return array (
'a' =>
array (
'chunks' =>
array (
0 =>
array (
0 =>
array (
0 => '1',
1 => '',
2 => "abc\r\n",
),
1 =>
array (
0 => '',
1 => '1',
2 => "ls\r\n",
),
),
),
'chunks_def' =>
array (
0 =>
array (
0 =>
array (
0 => '1',
1 => 1,
),
1 =>
array (
0 => '1',
1 => 1,
),
),
),
),
);

View File

@@ -1 +1,39 @@
a:1:{s:1:"a";a:2:{s:6:"chunks";a:1:{i:0;a:2:{i:0;a:3:{i:0;s:1:"1";i:1;s:1:"1";i:2;s:2:"ls";}i:1;a:3:{i:0;s:0:"";i:1;i:2;i:2;s:2:"ls";}}}s:10:"chunks_def";a:1:{i:0;a:2:{i:0;a:2:{i:0;s:1:"1";i:1;i:1;}i:1;a:2:{i:0;s:1:"1";i:1;s:1:"2";}}}}} <?php return array (
'a' =>
array (
'chunks' =>
array (
0 =>
array (
0 =>
array (
0 => '1',
1 => '1',
2 => "ls\r\n",
),
1 =>
array (
0 => '',
1 => 2,
2 => "ls\r\n",
),
),
),
'chunks_def' =>
array (
0 =>
array (
0 =>
array (
0 => '1',
1 => 1,
),
1 =>
array (
0 => '1',
1 => '2',
),
),
),
),
);

View File

@@ -1 +1,45 @@
a:1:{s:1:"a";a:2:{s:6:"chunks";a:1:{i:0;a:3:{i:0;a:3:{i:0;s:1:"1";i:1;s:1:"1";i:2;s:2:"ls";}i:1;a:3:{i:0;i:2;i:1;i:2;i:2;s:2:"ls";}i:2;a:3:{i:0;s:0:"";i:1;i:3;i:2;s:1:"l";}}}s:10:"chunks_def";a:1:{i:0;a:2:{i:0;a:2:{i:0;s:1:"1";i:1;s:1:"2";}i:1;a:2:{i:0;s:1:"1";i:1;s:1:"3";}}}}} <?php return array (
'a' =>
array (
'chunks' =>
array (
0 =>
array (
0 =>
array (
0 => '1',
1 => '1',
2 => "ls\r\n",
),
1 =>
array (
0 => 2,
1 => 2,
2 => "ls\r\n",
),
2 =>
array (
0 => '',
1 => 3,
2 => "l\r\n",
),
),
),
'chunks_def' =>
array (
0 =>
array (
0 =>
array (
0 => '1',
1 => '2',
),
1 =>
array (
0 => '1',
1 => '3',
),
),
),
),
);

View File

@@ -1 +1,33 @@
a:1:{s:1:"a";a:2:{s:6:"chunks";a:1:{i:0;a:1:{i:0;a:3:{i:0;s:0:"";i:1;s:1:"1";i:2;s:3:"foo";}}}s:10:"chunks_def";a:1:{i:0;a:2:{i:0;a:2:{i:0;s:1:"0";i:1;s:1:"0";}i:1;a:2:{i:0;s:1:"1";i:1;s:1:"1";}}}}} <?php return array (
'a' =>
array (
'chunks' =>
array (
0 =>
array (
0 =>
array (
0 => '',
1 => '1',
2 => "foo\n",
),
),
),
'chunks_def' =>
array (
0 =>
array (
0 =>
array (
0 => '0',
1 => '0',
),
1 =>
array (
0 => '1',
1 => '1',
),
),
),
),
);

View File

@@ -1 +1,39 @@
a:1:{s:1:"a";a:2:{s:6:"chunks";a:1:{i:0;a:2:{i:0;a:3:{i:0;s:1:"1";i:1;s:1:"1";i:2;s:3:"foo";}i:1;a:3:{i:0;s:0:"";i:1;i:2;i:2;s:2:"bf";}}}s:10:"chunks_def";a:1:{i:0;a:2:{i:0;a:2:{i:0;s:1:"1";i:1;s:1:"1";}i:1;a:2:{i:0;s:1:"1";i:1;s:1:"2";}}}}} <?php return array (
'a' =>
array (
'chunks' =>
array (
0 =>
array (
0 =>
array (
0 => '1',
1 => '1',
2 => "foo\n",
),
1 =>
array (
0 => '',
1 => 2,
2 => "bf\n",
),
),
),
'chunks_def' =>
array (
0 =>
array (
0 =>
array (
0 => '1',
1 => '1',
),
1 =>
array (
0 => '1',
1 => '2',
),
),
),
),
);

View File

@@ -1 +1,33 @@
a:1:{s:1:"a";a:2:{s:6:"chunks";a:1:{i:0;a:1:{i:0;a:3:{i:0;s:0:"";i:1;s:1:"1";i:2;s:3:"abc";}}}s:10:"chunks_def";a:1:{i:0;a:2:{i:0;a:2:{i:0;s:1:"0";i:1;s:1:"0";}i:1;a:2:{i:0;s:1:"1";i:1;i:1;}}}}} <?php return array (
'a' =>
array (
'chunks' =>
array (
0 =>
array (
0 =>
array (
0 => '',
1 => '1',
2 => "abc\n",
),
),
),
'chunks_def' =>
array (
0 =>
array (
0 =>
array (
0 => '0',
1 => '0',
),
1 =>
array (
0 => '1',
1 => 1,
),
),
),
),
);

View File

@@ -1 +1,45 @@
a:1:{s:1:"b";a:2:{s:6:"chunks";a:1:{i:0;a:3:{i:0;a:3:{i:0;s:0:"";i:1;s:1:"1";i:2;s:1:"a";}i:1;a:3:{i:0;s:0:"";i:1;i:2;i:2;s:1:"b";}i:2;a:3:{i:0;s:0:"";i:1;i:3;i:2;b:0;}}}s:10:"chunks_def";a:1:{i:0;a:2:{i:0;a:2:{i:0;s:1:"0";i:1;s:1:"0";}i:1;a:2:{i:0;s:1:"1";i:1;s:1:"3";}}}}} <?php return array (
'b' =>
array (
'chunks' =>
array (
0 =>
array (
0 =>
array (
0 => '',
1 => '1',
2 => "a\n",
),
1 =>
array (
0 => '',
1 => 2,
2 => "b\n",
),
2 =>
array (
0 => '',
1 => 3,
2 => "\n",
),
),
),
'chunks_def' =>
array (
0 =>
array (
0 =>
array (
0 => '0',
1 => '0',
),
1 =>
array (
0 => '1',
1 => '3',
),
),
),
),
);

Some files were not shown because too many files have changed in this diff Show More