Compare commits
163 Commits
feature.is
...
feature.wi
Author | SHA1 | Date | |
---|---|---|---|
|
9a6a6c21f5 | ||
|
69d0384ec3 | ||
|
af956d01dd | ||
|
0002e9bd21 | ||
|
6c62fbd19f | ||
|
7abcc29e60 | ||
|
2e1369184b | ||
|
b39fe8595c | ||
|
5fefc26543 | ||
|
a37b222878 | ||
|
c9f2575469 | ||
|
5101ae5f35 | ||
|
bd94d5bf68 | ||
|
aa09862059 | ||
|
f9629f3f7b | ||
|
884f50155c | ||
|
cf684c1514 | ||
|
6c9b6b5309 | ||
|
a7f283256a | ||
|
4506e1d2b5 | ||
|
70f67e6bc6 | ||
|
05816cb75a | ||
|
91b189b75f | ||
|
623c562054 | ||
|
2a55024640 | ||
|
8a9f8c66e8 | ||
|
81f433085a | ||
|
6e59a0a526 | ||
|
8397d86313 | ||
|
b0ac05b608 | ||
|
48c3989ae3 | ||
|
b949b7e83a | ||
|
ad7fad9fbe | ||
|
d2db3b16d2 | ||
|
ae11b1de4a | ||
|
6f620e3f54 | ||
|
810b753edf | ||
|
958fb1b9ad | ||
|
48992adefa | ||
|
d95e1e13e5 | ||
|
381dc5b8f7 | ||
|
464c1a8ef5 | ||
|
e5b10a8494 | ||
|
ffc49b9ea6 | ||
|
0efc14dd6f | ||
|
4fb15ccb7d | ||
|
234b70845c | ||
|
6abd0b6faa | ||
|
fef2bd15bf | ||
|
74d07d8fb8 | ||
|
473e9153ed | ||
|
efa10c9afd | ||
|
7438a2bf19 | ||
|
2e0995abac | ||
|
c84afd0f78 | ||
|
b1276dff6c | ||
|
b413b7ee89 | ||
|
f19f07ec59 | ||
|
83761c66c5 | ||
|
d0e2977746 | ||
|
1be91e5a2a | ||
|
15d4d1aa7d | ||
|
708b90fccd | ||
|
ac7a4c4aa5 | ||
|
b90246a239 | ||
|
a9d327d54f | ||
|
eb8fd0aa55 | ||
|
813184f06c | ||
|
ef2d3a9af9 | ||
|
9a8bd464a3 | ||
|
9e2ea7404b | ||
|
160d11b89b | ||
|
d860f299fd | ||
|
33882d4fa7 | ||
|
85978a4d18 | ||
|
e1e7696d53 | ||
|
695428075b | ||
|
e7c2e721b4 | ||
|
13fad756ab | ||
|
1f0791df0e | ||
|
a2c832a130 | ||
|
b17de014ec | ||
|
b1b72190e1 | ||
|
2ff2f888bc | ||
|
57c2389aae | ||
|
d54c86f813 | ||
|
05a9697007 | ||
|
945429abf0 | ||
|
a016bcb51b | ||
|
f2b1ce795c | ||
|
3a8c56acc4 | ||
|
7b2552f940 | ||
|
324b202215 | ||
|
2c2da6082a | ||
|
dd3fbbd7e4 | ||
|
9bbcd571ec | ||
|
bbc185bf3b | ||
|
d1bcdcda20 | ||
|
6d55602ef3 | ||
|
6e7c9f7c4b | ||
|
5427aab456 | ||
|
4879d64989 | ||
|
dab8ea63fc | ||
|
b03d7a04a0 | ||
|
ef5b93e3f7 | ||
|
69ae1c08ef | ||
|
8e4f828cc6 | ||
|
c4f92f4569 | ||
|
c4d2b99656 | ||
|
d4fe88adab | ||
|
69d0e8313a | ||
|
11a234e135 | ||
|
2f30e4e2f6 | ||
|
118ca9f11f | ||
|
24fc41ee0d | ||
|
c00dbac5e0 | ||
|
d7857c5126 | ||
|
ac6be0d3c0 | ||
|
7ff6f09f67 | ||
|
00b576c5a3 | ||
|
2a33510c96 | ||
|
d1f79d906d | ||
|
aa043f14ac | ||
|
638b28399e | ||
|
1d86f036a3 | ||
|
2f6e0f0a22 | ||
|
1b1b00a10c | ||
|
8693418d39 | ||
|
8ff15368ce | ||
|
32cde534bd | ||
|
c0e26133bd | ||
|
592c2ff9ff | ||
|
30efd0a2db | ||
|
20c3f14cc8 | ||
|
80313c88c8 | ||
|
cab1c09ffc | ||
|
c421919092 | ||
|
d350876bc1 | ||
|
de09c8af56 | ||
|
998f4576fe | ||
|
06c57f7da6 | ||
|
4d5418a601 | ||
|
95cc7f627f | ||
|
2aab4eea3b | ||
|
5bbff9f5a6 | ||
|
b5fcf1636a | ||
|
13012be5d7 | ||
|
ca2ef814fb | ||
|
b9407f6aee | ||
|
d079838818 | ||
|
81ce4688df | ||
|
ee33cc1832 | ||
|
82bb18fe10 | ||
|
8987ca7db6 | ||
|
8066fd8982 | ||
|
a96d8c05a7 | ||
|
4238a5dd50 | ||
|
94da55d15e | ||
|
09979b8551 | ||
|
5b82efa0be | ||
|
8502a36481 | ||
|
bbf1a1882a | ||
|
5322cdf609 |
19
.gitignore
vendored
19
.gitignore
vendored
@@ -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
|
||||||
|
6
AUTHORS
6
AUTHORS
@@ -12,26 +12,32 @@ 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>
|
||||||
Fernando Sayago Gil <mikados.mikados@gmail.com> - Spanish translation
|
Fernando Sayago Gil <mikados.mikados@gmail.com> - Spanish translation
|
||||||
|
Gert van Valkenhoef <gertvv>
|
||||||
Jakub Viták <mainiak@gmail.com> - Czech translation
|
Jakub Viták <mainiak@gmail.com> - Czech translation
|
||||||
Janez Troha <http://www.dz0ny.info> - Slovenian translation
|
Janez Troha <http://www.dz0ny.info> - Slovenian translation
|
||||||
Jean-Philippe Fleury <jpfleury>
|
Jean-Philippe Fleury <jpfleury>
|
||||||
Jerry <lxb429@gmail.com> - Chinese translation
|
Jerry <lxb429@gmail.com> - Chinese translation
|
||||||
Julien Issler <julien@issler.net>
|
Julien Issler <julien@issler.net>
|
||||||
|
Litew <litew9@gmail.com> - Russian translation
|
||||||
Ludovic Bellière <xrogaan>
|
Ludovic Bellière <xrogaan>
|
||||||
Manuel Eidenberger <eidenberger@gmail.com>
|
Manuel Eidenberger <eidenberger@gmail.com>
|
||||||
Matthew Dawson <mjd>
|
Matthew Dawson <mjd>
|
||||||
Matías Halles <matias@halles.cl>
|
Matías Halles <matias@halles.cl>
|
||||||
Mehdi Kabab <http://pioupioum.fr/>
|
Mehdi Kabab <http://pioupioum.fr/>
|
||||||
Nicolas Lassalle <nicolas@beroot.org> - Subversion support
|
Nicolas Lassalle <nicolas@beroot.org> - Subversion support
|
||||||
|
Ozan <uobasar@gmail.com> - Turkish translation
|
||||||
Patrick Georgi <patrick.georgi@coresystems.de>
|
Patrick Georgi <patrick.georgi@coresystems.de>
|
||||||
|
Pedro Kiefer <pedro@kiefer.com.br> - Brazilian Portuguese translation
|
||||||
Raphaël Emourgeon <raphael>
|
Raphaël Emourgeon <raphael>
|
||||||
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>
|
||||||
|
10
Makefile
10
Makefile
@@ -62,11 +62,11 @@ pot-update: pluf_path
|
|||||||
fi
|
fi
|
||||||
touch src/IDF/locale/idf.pot;
|
touch src/IDF/locale/idf.pot;
|
||||||
# Extract string
|
# Extract string
|
||||||
@cd src; php $(PLUF_PATH)/extracttemplates.php IDF/conf/idf.php IDF/gettexttemplates
|
@cd src; php "$(PLUF_PATH)/extracttemplates.php" IDF/conf/idf.php IDF/gettexttemplates
|
||||||
@cd src; for phpfile in `find . -iname "*.php"`; do \
|
@cd src; for phpfile in `find . -iname "*.php"`; do \
|
||||||
printf "Parsing file : "$$phpfile"\n"; \
|
printf "Parsing file : "$$phpfile"\n"; \
|
||||||
xgettext -o idf.pot -p ./IDF/locale/ --from-code=UTF-8 -j \
|
xgettext -o idf.pot -p ./IDF/locale/ --from-code=UTF-8 -j \
|
||||||
--keyword --keyword=__ --keyword=_n:1,2 -L PHP $$phpfile ; \
|
--keyword --keyword=__ --keyword=_n:1,2 -L PHP "$$phpfile" ; \
|
||||||
done
|
done
|
||||||
# Remove tmp folder
|
# Remove tmp folder
|
||||||
rm -Rf src/IDF/gettexttemplates
|
rm -Rf src/IDF/gettexttemplates
|
||||||
@@ -76,7 +76,7 @@ pot-update: pluf_path
|
|||||||
po-update: pluf_path
|
po-update: pluf_path
|
||||||
@for pofile in `ls src/IDF/locale/*/idf.po`; do \
|
@for pofile in `ls src/IDF/locale/*/idf.po`; do \
|
||||||
printf "Updating file : "$$pofile"\n"; \
|
printf "Updating file : "$$pofile"\n"; \
|
||||||
msgmerge -v -U $$pofile src/IDF/locale/idf.pot; \
|
msgmerge -v -U "$$pofile" src/IDF/locale/idf.pot; \
|
||||||
printf "\n"; \
|
printf "\n"; \
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ po-stats:
|
|||||||
--pretty=format:%h`.zip
|
--pretty=format:%h`.zip
|
||||||
|
|
||||||
db-install:
|
db-install:
|
||||||
@cd src && php $(PLUF_PATH)/migrate.php --conf=IDF/conf/idf.php -a -d -i
|
@cd src && php "$(PLUF_PATH)/migrate.php" --conf=IDF/conf/idf.php -a -d -i
|
||||||
|
|
||||||
db-update:
|
db-update:
|
||||||
@cd src && php $(PLUF_PATH)/migrate.php --conf=IDF/conf/idf.php -a -d
|
@cd src && php "$(PLUF_PATH)/migrate.php" --conf=IDF/conf/idf.php -a -d
|
||||||
|
108
NEWS.mdtext
108
NEWS.mdtext
@@ -1,23 +1,109 @@
|
|||||||
# InDefero 1.2 - xxx xxx xx xx:xx 2011 UTC
|
# InDefero 1.3 - xxx xxx xx xx:xx:xx UTC 201X
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
- 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)
|
|
||||||
|
|
||||||
## Bugfixes
|
## Bugfixes
|
||||||
|
|
||||||
- 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)
|
|
||||||
- Fix the self-link of the RSS feed (issue 666)
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
|
# InDefero 1.2 - Sun Nov 6 23:04:00 UTC 2011
|
||||||
|
|
||||||
|
ATTENTION: You need Pluf [46b7f251](http://projects.ceondo.com/p/pluf/source/commit/46b7f251)
|
||||||
|
or newer to properly run this version of Indefero!
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
|
- 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)
|
||||||
|
- Source and diff views now make characters like line endings, tabs and other
|
||||||
|
"invisible" control characters visible on hover and cope with long lines much
|
||||||
|
better (issue 636)
|
||||||
|
- 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)
|
||||||
|
- monotone file and directory attributes are displayed 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
|
||||||
|
- A summary section has been added to the issue tracker with statistics about
|
||||||
|
open / closed issues, unresolved issues grouped by tags and owners
|
||||||
|
- The project list and title has gathered a customizable icon for each project
|
||||||
|
- The download section now provides MD5 checksums for uploaded files
|
||||||
|
- Wiki pages now come with a designated stylesheet for printer output (issue 713)
|
||||||
|
|
||||||
|
## Bugfixes
|
||||||
|
|
||||||
|
- Git's cron job doesn't erase manually added keys anymore (issue 247)
|
||||||
|
- 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)
|
||||||
|
- The timeline only now only displays filter options for items a user has
|
||||||
|
actually access to (issue 655)
|
||||||
|
- The log, tags and branches parsers for Mercurial are more robust now (issue 663)
|
||||||
|
- Several SSH public key parsing issues have been fixed and the check for existing,
|
||||||
|
uploaded keys has been improved (issue 679)
|
||||||
|
- Diff views now show empty context lines for git and hg again (issue 688)
|
||||||
|
- The SVN command line client no longer accidential tries to store the login
|
||||||
|
credentials we give him as arguments for the user executing the SVN command
|
||||||
|
- 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
|
||||||
|
- A timeout that popped up when Usher is restarted has been fixed (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)
|
||||||
|
- Avatar URL generation use correctly the configuration (issue 732)
|
||||||
|
- The SyncGit plugin no longer fails to remove a non-existing post-update hook
|
||||||
|
on repository creation (issue 752)
|
||||||
|
- When uploading a project logo, an existing uploaded file with the same name
|
||||||
|
no longer leads to an error, but is simple overwritten (fixes issue 740)
|
||||||
|
- The error detection and reporting in the SyncMonotone plugin has been improved
|
||||||
|
- The branch links users of the Subversion frontend get when they enter a wrong
|
||||||
|
revision are fixed; this list is now also only displayed (for any SCM) if
|
||||||
|
there are actually branches available in the repository
|
||||||
|
- 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
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- The documentation on the setup of the monotone plugin has been improved.
|
||||||
|
|
||||||
|
## Translations
|
||||||
|
|
||||||
|
- The Russian translation has been enabled by default (thanks for all the great
|
||||||
|
work, Denis Kot and Litew!)
|
||||||
|
- Brazilian Portuguese translation started (thanks to Pedro Kiefer!)
|
||||||
|
- Turkish translation started (thanks to Ozan!)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
@@ -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>):
|
||||||
|
231
src/IDF/Diff.php
231
src/IDF/Diff.php
@@ -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,92 @@ 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> </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 = 0;
|
||||||
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 = 0;
|
||||||
|
if ($removed > 0)
|
||||||
|
$rightwidth = ((ceil(log10($removed)) + 1) * 8) + 12;
|
||||||
|
|
||||||
|
// we need to correct the width of a single column a little
|
||||||
|
// to take less space and to hide the empty one
|
||||||
|
$class = '';
|
||||||
|
if ($leftwidth == 0) {
|
||||||
|
$class = 'left-hidden';
|
||||||
|
$rightwidth -= floor(log10($removed));
|
||||||
}
|
}
|
||||||
|
else if ($rightwidth == 0) {
|
||||||
|
$class = 'right-hidden';
|
||||||
|
$leftwidth -= floor(log10($added));
|
||||||
|
}
|
||||||
|
|
||||||
|
$inner_linecounts =
|
||||||
|
'<table class="diff-linecounts '.$class.'">' ."\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(' ', $i).substr($line, $i);
|
|
||||||
|
return Pluf_Template::markSafe($out);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -208,12 +256,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 +358,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 = ' ';
|
$left = '';
|
||||||
$line2 = ' ';
|
$right = '';
|
||||||
$line[2] = (strlen($line[2])) ? self::padLine(Pluf_esc($line[2])) : ' ';
|
$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> </td><td>...</td><td> </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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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")."\">$".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.
|
||||||
|
@@ -31,8 +31,11 @@
|
|||||||
*/
|
*/
|
||||||
class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
||||||
{
|
{
|
||||||
|
public $user = null;
|
||||||
|
|
||||||
public function initFields($extra=array())
|
public function initFields($extra=array())
|
||||||
{
|
{
|
||||||
|
$this->user = $extra['user'];
|
||||||
$choices = array();
|
$choices = array();
|
||||||
$options = array(
|
$options = array(
|
||||||
'git' => __('git'),
|
'git' => __('git'),
|
||||||
@@ -336,7 +339,6 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
|||||||
$conf->setVal($prop, $tmplconf->getVal($prop, $def));
|
$conf->setVal($prop, $tmplconf->getVal($prop, $def));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$project->created();
|
|
||||||
|
|
||||||
if ($this->cleaned_data['template'] == '--') {
|
if ($this->cleaned_data['template'] == '--') {
|
||||||
IDF_Form_MembersConf::updateMemberships($project,
|
IDF_Form_MembersConf::updateMemberships($project,
|
||||||
@@ -347,6 +349,8 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
|||||||
$tmpl->getMembershipData('string'));
|
$tmpl->getMembershipData('string'));
|
||||||
}
|
}
|
||||||
$project->membershipsUpdated();
|
$project->membershipsUpdated();
|
||||||
|
$project->created();
|
||||||
|
|
||||||
return $project;
|
return $project;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -94,7 +94,7 @@ duplicates, is duplicated by';
|
|||||||
{
|
{
|
||||||
$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),
|
||||||
@@ -130,7 +130,7 @@ duplicates, is duplicated by';
|
|||||||
|
|
||||||
$this->fields['labels_issue_one_max'] = new Pluf_Form_Field_Varchar(
|
$this->fields['labels_issue_one_max'] = new Pluf_Form_Field_Varchar(
|
||||||
array('required' => false,
|
array('required' => false,
|
||||||
'label' => __('Each issue may have at most one label with each of these classes'),
|
'label' => __('Each issue may have at most one label with each of these classes.'),
|
||||||
'initial' => self::init_one_max,
|
'initial' => self::init_one_max,
|
||||||
'widget_attrs' => array('size' => 60),
|
'widget_attrs' => array('size' => 60),
|
||||||
));
|
));
|
||||||
@@ -139,7 +139,7 @@ duplicates, is duplicated by';
|
|||||||
array('required' => true,
|
array('required' => true,
|
||||||
'label' => __('Issue relations'),
|
'label' => __('Issue relations'),
|
||||||
'initial' => self::init_relations,
|
'initial' => self::init_relations,
|
||||||
'help_text' => __('You can define bidirectional relations like "is related to" or "blocks, is blocked by".'),
|
'help_text' => __('You can define bidirectional relations like "is related to" or "blocks, is blocked by". For standard relations pre-configured translations exist, new relations should however be defined in a language that is understood by all project members.'),
|
||||||
'widget_attrs' => array('rows' => 7,
|
'widget_attrs' => array('rows' => 7,
|
||||||
'cols' => 75),
|
'cols' => 75),
|
||||||
'widget' => 'Pluf_Form_Widget_TextareaInput',
|
'widget' => 'Pluf_Form_Widget_TextareaInput',
|
||||||
|
@@ -67,6 +67,7 @@ class IDF_Form_ProjectConf extends Pluf_Form
|
|||||||
'move_function_params' =>
|
'move_function_params' =>
|
||||||
array('upload_path' => $upload_path,
|
array('upload_path' => $upload_path,
|
||||||
'upload_path_create' => true,
|
'upload_path_create' => true,
|
||||||
|
'upload_overwrite' => true,
|
||||||
'file_name' => $filename,
|
'file_name' => $filename,
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
@@ -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(
|
||||||
|
@@ -41,6 +41,7 @@ Phase:Support = Plans for user support and advocacy
|
|||||||
Deprecated = Most users should NOT reference this';
|
Deprecated = Most users should NOT reference this';
|
||||||
|
|
||||||
const init_one_max = '';
|
const init_one_max = '';
|
||||||
|
const wiki_default_page = '';
|
||||||
|
|
||||||
public function initFields($extra=array())
|
public function initFields($extra=array())
|
||||||
{
|
{
|
||||||
@@ -59,8 +60,30 @@ Deprecated = Most users should NOT reference this';
|
|||||||
'initial' => self::init_one_max,
|
'initial' => self::init_one_max,
|
||||||
'widget_attrs' => array('size' => 60),
|
'widget_attrs' => array('size' => 60),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$this->fields['wiki_default_page'] = new Pluf_Form_Field_Varchar(
|
||||||
|
array('required' => false,
|
||||||
|
'label' => __('Set a default wiki page instead of the page listing'),
|
||||||
|
'initial' => self::wiki_default_page,
|
||||||
|
'widget_attrs' => array('size' => 60),
|
||||||
|
));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clean_wiki_default_page()
|
||||||
|
{
|
||||||
|
$pageName = trim($this->cleaned_data['wiki_default_page']);
|
||||||
|
if (empty($pageName)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$sql = new Pluf_SQL('project=%s AND title=%s', array($this->data['projectId'], $pageName));
|
||||||
|
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
|
||||||
|
|
||||||
|
if ($pages->count() != 1) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return $pageName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -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] = "";
|
||||||
}
|
}
|
||||||
|
@@ -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(
|
||||||
|
63
src/IDF/Migrations/18DownloadMD5.php
Normal file
63
src/IDF/Migrations/18DownloadMD5.php
Normal 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]);
|
||||||
|
}
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -210,22 +210,23 @@ class IDF_Plugin_SyncGit_Serve
|
|||||||
// Indefero's one.
|
// Indefero's one.
|
||||||
$p = realpath(dirname(__FILE__).'/../../../../scripts/git-post-update');
|
$p = realpath(dirname(__FILE__).'/../../../../scripts/git-post-update');
|
||||||
$p = Pluf::f('idf_plugin_syncgit_post_update', $p);
|
$p = Pluf::f('idf_plugin_syncgit_post_update', $p);
|
||||||
if (!@unlink($fullpath.'/hooks/post-update')) {
|
$post_update_hook = $fullpath.'/hooks/post-update';
|
||||||
|
if (file_exists($post_update_hook) && !@unlink($post_update_hook)) {
|
||||||
Pluf_Log::warn(array('IDF_Plugin_Git_Serve::initRepository',
|
Pluf_Log::warn(array('IDF_Plugin_Git_Serve::initRepository',
|
||||||
'post-update hook removal error.',
|
'post-update hook removal error.',
|
||||||
$fullpath.'/hooks/post-update'));
|
$post_update_hook));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$out = array();
|
$out = array();
|
||||||
$res = 0;
|
$res = 0;
|
||||||
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').'ln -s %s %s',
|
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').'ln -s %s %s',
|
||||||
escapeshellarg($p),
|
escapeshellarg($p),
|
||||||
escapeshellarg($fullpath.'/hooks/post-update')),
|
escapeshellarg($post_update_hook)),
|
||||||
$out, $res);
|
$out, $res);
|
||||||
if ($res != 0) {
|
if ($res != 0) {
|
||||||
Pluf_Log::warn(array('IDF_Plugin_Git_Serve::initRepository',
|
Pluf_Log::warn(array('IDF_Plugin_Git_Serve::initRepository',
|
||||||
'post-update hook creation error.',
|
'post-update hook creation error.',
|
||||||
$fullpath.'/hooks/post-update'));
|
$post_update_hook));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Pluf_Log::debug(array('IDF_Plugin_Git_Serve::initRepository',
|
Pluf_Log::debug(array('IDF_Plugin_Git_Serve::initRepository',
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -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.
|
||||||
@@ -375,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);
|
||||||
}
|
}
|
||||||
|
@@ -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, ...)
|
||||||
|
@@ -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(),
|
||||||
);
|
);
|
||||||
@@ -86,7 +87,7 @@ class IDF_Scm_Git extends IDF_Scm
|
|||||||
$filename = trim(substr($line, 1));
|
$filename = trim(substr($line, 1));
|
||||||
$return->patches[] = $filename;
|
$return->patches[] = $filename;
|
||||||
} else if ($action == 'R') {
|
} else if ($action == 'R') {
|
||||||
$matches = split ("\t", $line);
|
$matches = preg_split("/\t/", $line);
|
||||||
$return->renames[$matches[1]] = $matches[2];
|
$return->renames[$matches[1]] = $matches[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
*/
|
*/
|
||||||
|
@@ -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],
|
||||||
|
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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");
|
||||||
|
@@ -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(
|
||||||
|
35
src/IDF/Template/Tag/UploadUrl.php
Normal file
35
src/IDF/Template/Tag/UploadUrl.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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()
|
||||||
{
|
{
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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));
|
||||||
}
|
}
|
||||||
|
@@ -71,12 +71,94 @@ 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);
|
||||||
|
}
|
||||||
|
arsort($ownerStatistics);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
uasort($tagStatistics[$class], function ($a, $b) {
|
||||||
|
if ($a[0] === $b[0])
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return ($a[0] > $b[0]) ? -1 : 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$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 +322,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 +381,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 +396,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,
|
||||||
@@ -356,45 +452,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');
|
||||||
@@ -542,6 +735,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');
|
||||||
@@ -674,7 +874,13 @@ class IDF_Views_Issue
|
|||||||
else {
|
else {
|
||||||
// ID-based search
|
// ID-based search
|
||||||
if (is_numeric($query)) {
|
if (is_numeric($query)) {
|
||||||
$sql = new Pluf_SQL('project=%s AND id LIKE %s', array($prj->id, $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(
|
$tmp = Pluf::factory('IDF_Issue')->getList(array(
|
||||||
'filter' => $sql->gen(),
|
'filter' => $sql->gen(),
|
||||||
'order' => 'id ASC'
|
'order' => 'id ASC'
|
||||||
@@ -790,9 +996,8 @@ class IDF_Views_Issue
|
|||||||
$r = $project->getRelationsFromConfig();
|
$r = $project->getRelationsFromConfig();
|
||||||
$auto['auto_relation_types'] = '';
|
$auto['auto_relation_types'] = '';
|
||||||
foreach ($r as $rt) {
|
foreach ($r as $rt) {
|
||||||
$esc = Pluf_esc($rt);
|
|
||||||
$auto['auto_relation_types'] .= sprintf('{ name: "%s", to: "%s" }, ',
|
$auto['auto_relation_types'] .= sprintf('{ name: "%s", to: "%s" }, ',
|
||||||
$esc, $esc);
|
Pluf_esc(__($rt)), Pluf_esc($rt));
|
||||||
}
|
}
|
||||||
$auto['auto_relation_types'] = substr($auto['auto_relation_types'], 0, -2);
|
$auto['auto_relation_types'] = substr($auto['auto_relation_types'], 0, -2);
|
||||||
return $auto;
|
return $auto;
|
||||||
|
@@ -418,8 +418,17 @@ class IDF_Views_Project
|
|||||||
$title = sprintf(__('%s Documentation Configuration'), (string) $prj);
|
$title = sprintf(__('%s Documentation Configuration'), (string) $prj);
|
||||||
$conf = new IDF_Conf();
|
$conf = new IDF_Conf();
|
||||||
$conf->setProject($prj);
|
$conf->setProject($prj);
|
||||||
|
|
||||||
|
// Get the Wiki list pages
|
||||||
|
$sql = new Pluf_SQL('project=%s', array($prj->id));
|
||||||
|
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
|
||||||
|
$auto_wiki_page_name = "";
|
||||||
|
foreach ($pages as $p) {
|
||||||
|
$auto_wiki_page_name .= '{ name: "' . $p->summary . '", to: "' . $p->title . '" }, ';
|
||||||
|
}
|
||||||
|
|
||||||
if ($request->method == 'POST') {
|
if ($request->method == 'POST') {
|
||||||
$form = new IDF_Form_WikiConf($request->POST);
|
$form = new IDF_Form_WikiConf(array_merge($request->POST, array('projectId' => $prj->id)));
|
||||||
if ($form->isValid()) {
|
if ($form->isValid()) {
|
||||||
foreach ($form->cleaned_data as $key=>$val) {
|
foreach ($form->cleaned_data as $key=>$val) {
|
||||||
$conf->setVal($key, $val);
|
$conf->setVal($key, $val);
|
||||||
@@ -431,7 +440,7 @@ class IDF_Views_Project
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$params = array();
|
$params = array();
|
||||||
$keys = array('labels_wiki_predefined', 'labels_wiki_one_max');
|
$keys = array('labels_wiki_predefined', 'labels_wiki_one_max', 'wiki_default_page');
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
$_val = $conf->getVal($key, false);
|
$_val = $conf->getVal($key, false);
|
||||||
if ($_val !== false) {
|
if ($_val !== false) {
|
||||||
@@ -447,6 +456,7 @@ class IDF_Views_Project
|
|||||||
array(
|
array(
|
||||||
'page_title' => $title,
|
'page_title' => $title,
|
||||||
'form' => $form,
|
'form' => $form,
|
||||||
|
'auto_wiki_page_name' => $auto_wiki_page_name,
|
||||||
),
|
),
|
||||||
$request);
|
$request);
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -35,7 +35,43 @@ class IDF_Views_Wiki
|
|||||||
* View list of issues for a given project.
|
* View list of issues for a given project.
|
||||||
*/
|
*/
|
||||||
public $index_precond = array('IDF_Precondition::accessWiki');
|
public $index_precond = array('IDF_Precondition::accessWiki');
|
||||||
public function index($request, $match, $api=false)
|
public function index($request, $match)
|
||||||
|
{
|
||||||
|
$project = $request->project;
|
||||||
|
|
||||||
|
// Search for the default page
|
||||||
|
$conf = new IDF_Conf();
|
||||||
|
$conf->setProject($project);
|
||||||
|
$page = $conf->getVal('wiki_default_page', null);
|
||||||
|
if ($page === null) {
|
||||||
|
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listing',
|
||||||
|
array($project->shortname));
|
||||||
|
return new Pluf_HTTP_Response_Redirect($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the page
|
||||||
|
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||||
|
array($project->id, $page));
|
||||||
|
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
|
||||||
|
if ($pages->count() != 1) {
|
||||||
|
// The default page have been delete
|
||||||
|
$conf->setVal('wiki_default_page', null);
|
||||||
|
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listing',
|
||||||
|
array($project->shortname));
|
||||||
|
return new Pluf_HTTP_Response_Redirect($url);
|
||||||
|
}
|
||||||
|
$page = $pages[0];
|
||||||
|
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||||
|
array($project->shortname, $page->title));
|
||||||
|
return new Pluf_HTTP_Response_Redirect($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View list of issues for a given project.
|
||||||
|
*/
|
||||||
|
public $listing_precond = array('IDF_Precondition::accessWiki');
|
||||||
|
public function listing($request, $match, $api=false)
|
||||||
{
|
{
|
||||||
$prj = $request->project;
|
$prj = $request->project;
|
||||||
$title = sprintf(__('%s Documentation'), (string) $prj);
|
$title = sprintf(__('%s Documentation'), (string) $prj);
|
||||||
@@ -82,7 +118,7 @@ class IDF_Views_Wiki
|
|||||||
{
|
{
|
||||||
$prj = $request->project;
|
$prj = $request->project;
|
||||||
if (!isset($request->REQUEST['q']) or trim($request->REQUEST['q']) == '') {
|
if (!isset($request->REQUEST['q']) or trim($request->REQUEST['q']) == '') {
|
||||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::index',
|
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listing',
|
||||||
array($prj->shortname));
|
array($prj->shortname));
|
||||||
return new Pluf_HTTP_Response_Redirect($url);
|
return new Pluf_HTTP_Response_Redirect($url);
|
||||||
}
|
}
|
||||||
@@ -152,13 +188,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);
|
||||||
@@ -184,7 +218,7 @@ class IDF_Views_Wiki
|
|||||||
$urlpage = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
$urlpage = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||||
array($prj->shortname, $page->title));
|
array($prj->shortname, $page->title));
|
||||||
$request->user->setMessage(sprintf(__('The page <a href="%s">%s</a> has been created.'), $urlpage, Pluf_esc($page->title)));
|
$request->user->setMessage(sprintf(__('The page <a href="%s">%s</a> has been created.'), $urlpage, Pluf_esc($page->title)));
|
||||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::index',
|
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listing',
|
||||||
array($prj->shortname));
|
array($prj->shortname));
|
||||||
return new Pluf_HTTP_Response_Redirect($url);
|
return new Pluf_HTTP_Response_Redirect($url);
|
||||||
} elseif (isset($request->POST['preview'])) {
|
} elseif (isset($request->POST['preview'])) {
|
||||||
@@ -228,7 +262,13 @@ class IDF_Views_Wiki
|
|||||||
if (isset($request->GET['rev']) and preg_match('/^[0-9]+$/', $request->GET['rev'])) {
|
if (isset($request->GET['rev']) and preg_match('/^[0-9]+$/', $request->GET['rev'])) {
|
||||||
$oldrev = Pluf_Shortcuts_GetObjectOr404('IDF_WikiRevision',
|
$oldrev = Pluf_Shortcuts_GetObjectOr404('IDF_WikiRevision',
|
||||||
$request->GET['rev']);
|
$request->GET['rev']);
|
||||||
if ($oldrev->wikipage != $page->id or $oldrev->is_head == true) {
|
if ($oldrev->is_head == true) {
|
||||||
|
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||||
|
array($prj->shortname, $page->title));
|
||||||
|
return new Pluf_HTTP_Response_Redirect($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($oldrev->wikipage != $page->id) {
|
||||||
return new Pluf_HTTP_Response_NotFound($request);
|
return new Pluf_HTTP_Response_NotFound($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,6 +294,39 @@ class IDF_Views_Wiki
|
|||||||
$request);
|
$request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the revision list of a documentation page.
|
||||||
|
*/
|
||||||
|
public $history_precond = array('IDF_Precondition::accessWiki');
|
||||||
|
public function history($request, $match)
|
||||||
|
{
|
||||||
|
$prj = $request->project;
|
||||||
|
// Find the page
|
||||||
|
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||||
|
array($prj->id, $match[2]));
|
||||||
|
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
|
||||||
|
if ($pages->count() != 1) {
|
||||||
|
return new Pluf_HTTP_Response_NotFound($request);
|
||||||
|
}
|
||||||
|
$page = $pages[0];
|
||||||
|
$ptags = self::getWikiTags($prj);
|
||||||
|
$dtag = array_pop($ptags); // The last tag is the deprecated tag.
|
||||||
|
$tags = $page->get_tags_list();
|
||||||
|
$dep = Pluf_Model_InArray($dtag, $tags);
|
||||||
|
$title = sprintf(__('History of the wiki page %s'), $page->title);
|
||||||
|
$revision = $page->get_current_revision();
|
||||||
|
$revs = $page->get_revisions_list(array('order' => 'creation_dtime DESC'));
|
||||||
|
return Pluf_Shortcuts_RenderToResponse('idf/wiki/history.html',
|
||||||
|
array(
|
||||||
|
'page' => $page,
|
||||||
|
'page_title' => $title,
|
||||||
|
'rev' => $revision,
|
||||||
|
'revs' => $revs,
|
||||||
|
'tags' => $tags,
|
||||||
|
),
|
||||||
|
$request);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a revision of a page.
|
* Remove a revision of a page.
|
||||||
*/
|
*/
|
||||||
@@ -293,6 +366,57 @@ class IDF_Views_Wiki
|
|||||||
$request);
|
$request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public $restoreRev_precond = array('IDF_Precondition::accessWiki',
|
||||||
|
'IDF_Precondition::projectMemberOrOwner');
|
||||||
|
public function restoreRev($request, $match)
|
||||||
|
{
|
||||||
|
$prj = $request->project;
|
||||||
|
$oldrev = Pluf_Shortcuts_GetObjectOr404('IDF_WikiRevision', $match[2]);
|
||||||
|
$page = $oldrev->get_wikipage();
|
||||||
|
$prj->inOr404($page);
|
||||||
|
|
||||||
|
// Prevent restore the current version
|
||||||
|
if ($oldrev->is_head == true) {
|
||||||
|
$request->user->setMessage(__('This revision is already the current revision.'));
|
||||||
|
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||||
|
array($prj->shortname, $page->title));
|
||||||
|
return new Pluf_HTTP_Response_Redirect($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = array(
|
||||||
|
'project' => $prj,
|
||||||
|
'user' => $request->user,
|
||||||
|
'page' => $page,
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'title' => $page->title,
|
||||||
|
'summary' => $page->summary,
|
||||||
|
'content' => $oldrev->content,
|
||||||
|
'comment' => sprintf(__('Restore old revision (%s)'), $oldrev->id),
|
||||||
|
);
|
||||||
|
|
||||||
|
$tags = $page->get_tags_list();
|
||||||
|
for ($i=1;$i<4;$i++) {
|
||||||
|
if (isset($tags[$i-1])) {
|
||||||
|
if ($tags[$i-1]->class != 'Other') {
|
||||||
|
$data['label'.$i] = (string) $tags[$i-1];
|
||||||
|
} else {
|
||||||
|
$data['label'.$i] = $tags[$i-1]->name;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$data['label'.$i] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = new IDF_Form_WikiUpdate($data, $params);
|
||||||
|
$page = $form->save();
|
||||||
|
|
||||||
|
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||||
|
array($prj->shortname, $page->title));
|
||||||
|
return new Pluf_HTTP_Response_Redirect($url);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View a documentation page.
|
* View a documentation page.
|
||||||
*/
|
*/
|
||||||
@@ -322,7 +446,7 @@ class IDF_Views_Wiki
|
|||||||
$urlpage = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
$urlpage = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||||
array($prj->shortname, $page->title));
|
array($prj->shortname, $page->title));
|
||||||
$request->user->setMessage(sprintf(__('The page <a href="%s">%s</a> has been updated.'), $urlpage, Pluf_esc($page->title)));
|
$request->user->setMessage(sprintf(__('The page <a href="%s">%s</a> has been updated.'), $urlpage, Pluf_esc($page->title)));
|
||||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::index',
|
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listing',
|
||||||
array($prj->shortname));
|
array($prj->shortname));
|
||||||
return new Pluf_HTTP_Response_Redirect($url);
|
return new Pluf_HTTP_Response_Redirect($url);
|
||||||
} elseif (isset($request->POST['preview'])) {
|
} elseif (isset($request->POST['preview'])) {
|
||||||
@@ -360,7 +484,7 @@ class IDF_Views_Wiki
|
|||||||
if ($form->isValid()) {
|
if ($form->isValid()) {
|
||||||
$form->save();
|
$form->save();
|
||||||
$request->user->setMessage(__('The documentation page has been deleted.'));
|
$request->user->setMessage(__('The documentation page has been deleted.'));
|
||||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::index',
|
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listing',
|
||||||
array($prj->shortname));
|
array($prj->shortname));
|
||||||
return new Pluf_HTTP_Response_Redirect($url);
|
return new Pluf_HTTP_Response_Redirect($url);
|
||||||
}
|
}
|
||||||
|
@@ -221,4 +221,51 @@ class IDF_WikiPage extends Pluf_Model
|
|||||||
$tmpl = new Pluf_Template('idf/wiki/feedfragment.xml');
|
$tmpl = new Pluf_Template('idf/wiki/feedfragment.xml');
|
||||||
return $tmpl->render($context);
|
return $tmpl->render($context);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
function projectCreated($signal, &$params)
|
||||||
|
{
|
||||||
|
$project = $params['project'];
|
||||||
|
$users = $project->getMembershipData();
|
||||||
|
$user = $users['owners'][0];
|
||||||
|
$conf = $project->getConf();
|
||||||
|
|
||||||
|
// Insert default wiki page
|
||||||
|
$tmpl = new Pluf_Template('idf/wiki/wiki-default-page.mdtext');
|
||||||
|
$context = new Pluf_Template_Context(array('project' => $project));
|
||||||
|
$content = $tmpl->render($context);
|
||||||
|
$page = new IDF_WikiPage();
|
||||||
|
$page->project = $project;
|
||||||
|
$page->submitter = $user;
|
||||||
|
$page->summary = __('Default page for your project Wiki.');
|
||||||
|
$page->title = 'IndeferoSummaryDefault';
|
||||||
|
$page->create();
|
||||||
|
$rev = new IDF_WikiRevision();
|
||||||
|
$rev->wikipage = $page;
|
||||||
|
$rev->content = $content;
|
||||||
|
$rev->submitter = $user;
|
||||||
|
$rev->summary = __('Initial page creation');
|
||||||
|
$rev->create();
|
||||||
|
$rev->notify($project->getConf());
|
||||||
|
|
||||||
|
// Insert markdown help wiki page
|
||||||
|
$tmpl = new Pluf_Template('idf/wiki/wiki-markdown-help.mdtext');
|
||||||
|
$context = new Pluf_Template_Context(array('project' => $project));
|
||||||
|
$content = $tmpl->render($context);
|
||||||
|
$page = new IDF_WikiPage();
|
||||||
|
$page->project = $project;
|
||||||
|
$page->submitter = $user;
|
||||||
|
$page->summary = __('Help about Markdown syntax.');
|
||||||
|
$page->title = 'IndeferoMarkdownHelp';
|
||||||
|
$page->create();
|
||||||
|
$rev = new IDF_WikiRevision();
|
||||||
|
$rev->wikipage = $page;
|
||||||
|
$rev->content = $content;
|
||||||
|
$rev->submitter = $user;
|
||||||
|
$rev->summary = __('Initial page creation');
|
||||||
|
$rev->create();
|
||||||
|
$rev->notify($project->getConf());
|
||||||
|
|
||||||
|
$conf->setVal('wiki_default_page', 'IndeferoSummaryDefault');
|
||||||
|
$conf->setVal('labels_wiki_predefined', IDF_Form_WikiConf::init_predefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -119,7 +119,7 @@ $cfg['time_zone'] = 'Europe/Berlin';
|
|||||||
# Configure which languages should be available in your forge.
|
# Configure which languages should be available in your forge.
|
||||||
# If you want to enable an additional language, ensure that the
|
# If you want to enable an additional language, ensure that the
|
||||||
# language file in question resides in 'src/IDF/locale'.
|
# language file in question resides in 'src/IDF/locale'.
|
||||||
$cfg['languages'] = array('en', 'fr', 'de', 'es_ES');
|
$cfg['languages'] = array('en', 'fr', 'de', 'es_ES', 'ru');
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------- #
|
# ---------------------------------------------------------------------------- #
|
||||||
|
@@ -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,
|
||||||
@@ -247,6 +262,11 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/$#',
|
|||||||
'model' => 'IDF_Views_Wiki',
|
'model' => 'IDF_Views_Wiki',
|
||||||
'method' => 'index');
|
'method' => 'index');
|
||||||
|
|
||||||
|
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/list/$#',
|
||||||
|
'base' => $base,
|
||||||
|
'model' => 'IDF_Views_Wiki',
|
||||||
|
'method' => 'listing');
|
||||||
|
|
||||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/create/$#',
|
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/create/$#',
|
||||||
'base' => $base,
|
'base' => $base,
|
||||||
'model' => 'IDF_Views_Wiki',
|
'model' => 'IDF_Views_Wiki',
|
||||||
@@ -272,11 +292,21 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/delrev/(\d+)/$#',
|
|||||||
'model' => 'IDF_Views_Wiki',
|
'model' => 'IDF_Views_Wiki',
|
||||||
'method' => 'deleteRev');
|
'method' => 'deleteRev');
|
||||||
|
|
||||||
|
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/resrev/(\d+)/$#',
|
||||||
|
'base' => $base,
|
||||||
|
'model' => 'IDF_Views_Wiki',
|
||||||
|
'method' => 'restoreRev');
|
||||||
|
|
||||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/delete/(\d+)/$#',
|
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/delete/(\d+)/$#',
|
||||||
'base' => $base,
|
'base' => $base,
|
||||||
'model' => 'IDF_Views_Wiki',
|
'model' => 'IDF_Views_Wiki',
|
||||||
'method' => 'delete');
|
'method' => 'delete');
|
||||||
|
|
||||||
|
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/history/(.*)/$#',
|
||||||
|
'base' => $base,
|
||||||
|
'model' => 'IDF_Views_Wiki',
|
||||||
|
'method' => 'history');
|
||||||
|
|
||||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/page/(.*)/$#',
|
$ctl[] = array('regex' => '#^/p/([\-\w]+)/page/(.*)/$#',
|
||||||
'base' => $base,
|
'base' => $base,
|
||||||
'model' => 'IDF_Views_Wiki',
|
'model' => 'IDF_Views_Wiki',
|
||||||
@@ -299,11 +329,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',
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4587
src/IDF/locale/pt_BR/idf.po
Normal file
4587
src/IDF/locale/pt_BR/idf.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4581
src/IDF/locale/tr/idf.po
Normal file
4581
src/IDF/locale/tr/idf.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -115,4 +115,10 @@ Pluf_Signal::connect('queuecron.php::run',
|
|||||||
Pluf_Signal::connect('IDF_Queue::processItem',
|
Pluf_Signal::connect('IDF_Queue::processItem',
|
||||||
Pluf::f('idf_hook_process_item',
|
Pluf::f('idf_hook_process_item',
|
||||||
array('IDF_Webhook', 'process')));
|
array('IDF_Webhook', 'process')));
|
||||||
|
|
||||||
|
#
|
||||||
|
# Wiki init
|
||||||
|
Pluf_Signal::connect('IDF_Project::created',
|
||||||
|
array('IDF_WikiPage', 'projectCreated'));
|
||||||
|
|
||||||
return $m;
|
return $m;
|
||||||
|
@@ -16,6 +16,34 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td colspan="2">{$form.f.wiki_default_page.labelTag}:<br />
|
||||||
|
{if $form.f.wiki_default_page.errors}{$form.f.wiki_default_page.fieldErrors}{/if}
|
||||||
|
{$form.f.wiki_default_page|unsafe}
|
||||||
|
</td>
|
||||||
|
<script type="text/javascript" src="{media '/idf/js/jquery.bgiframe.min.js'}"></script>
|
||||||
|
<script type="text/javascript" src="{media '/idf/js/jquery.autocomplete.min.js'}"></script>
|
||||||
|
<script type="text/javascript" charset="utf-8">
|
||||||
|
{literal}
|
||||||
|
$(document).ready(function(){
|
||||||
|
var auto_wiki_page_name = [{/literal}{$auto_wiki_page_name|safe}{literal}];
|
||||||
|
$("#id_wiki_default_page").autocomplete(auto_wiki_page_name, {
|
||||||
|
minChars: 0,
|
||||||
|
width: 310,
|
||||||
|
matchContains: true,
|
||||||
|
max: 50,
|
||||||
|
highlightItem: false,
|
||||||
|
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>";
|
||||||
|
},
|
||||||
|
formatResult: function(row) {
|
||||||
|
return row.to;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
{/literal}
|
||||||
|
</script>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<input type="submit" value="{trans 'Save Changes'}" name="submit" />
|
<input type="submit" value="{trans 'Save Changes'}" name="submit" />
|
||||||
</td>
|
</td>
|
||||||
|
@@ -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}
|
||||||
|
@@ -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>
|
||||||
|
@@ -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}
|
||||||
|
@@ -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> </td>
|
<td> </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>
|
||||||
|
@@ -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>
|
||||||
|
@@ -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}
|
||||||
|
|
||||||
|
@@ -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}
|
||||||
|
|
||||||
|
@@ -2,11 +2,15 @@
|
|||||||
{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 $inAllIssues}class="active" {/if}href="{url 'IDF_Views_Issue::index', array($project.shortname)}">{trans 'All Issues'}</a>
|
||||||
| <a {if $inWatchList}class="active" {/if}href="{url 'IDF_Views_Issue::watchList', array($project.shortname, 'open')}">{trans 'My watch list'}</a>{/if} |
|
{if !$user.isAnonymous()}
|
||||||
<form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get">
|
| <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'submit')}">{trans 'My Issues'}</a>
|
||||||
<input accesskey="4" type="text" value="{$q}" name="q" size="20" />
|
| <a {if $inWatchList}class="active" {/if}href="{url 'IDF_Views_Issue::watchList', array($project.shortname, 'open')}">{trans 'My watch list'}</a>
|
||||||
|
| <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a>
|
||||||
|
{/if}
|
||||||
|
| <form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get">
|
||||||
|
<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} |
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{extends "idf/issues/base.html"}
|
{extends "idf/issues/base.html"}
|
||||||
{block docclass}yui-t1{/block}
|
{block docclass}yui-t1{assign $inAllIssues=true}{/block}
|
||||||
{block body}
|
{block body}
|
||||||
{$issues.render}
|
{$issues.render}
|
||||||
{if !$user.isAnonymous()}
|
{if !$user.isAnonymous()}
|
||||||
@@ -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}
|
||||||
|
@@ -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>
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{extends "idf/issues/base.html"}
|
{extends "idf/issues/base.html"}
|
||||||
{block docclass}yui-t2{assign $inOpenIssues=true}{/block}
|
{block docclass}yui-t2{assign $inAllIssues=true}{/block}
|
||||||
{block body}
|
{block body}
|
||||||
{$issues.render}
|
{$issues.render}
|
||||||
{if !$user.isAnonymous()}
|
{if !$user.isAnonymous()}
|
||||||
@@ -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}
|
||||||
|
@@ -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}
|
||||||
|
@@ -68,6 +68,9 @@
|
|||||||
return row.to;
|
return row.to;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
{/literal}
|
||||||
|
{if $issue}
|
||||||
|
{literal}
|
||||||
$("#id_relation_issue" + idx).autocomplete("{/literal}{url 'IDF_Views_Issue::autoCompleteIssueList', array($project.shortname, $issue.id)}{literal}", {
|
$("#id_relation_issue" + idx).autocomplete("{/literal}{url 'IDF_Views_Issue::autoCompleteIssueList', array($project.shortname, $issue.id)}{literal}", {
|
||||||
minChars: 0,
|
minChars: 0,
|
||||||
width: 310,
|
width: 310,
|
||||||
@@ -83,6 +86,9 @@
|
|||||||
return row[1];
|
return row[1];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
{/literal}
|
||||||
|
{/if}
|
||||||
|
{literal}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
{/literal} //-->
|
{/literal} //-->
|
||||||
|
@@ -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}
|
||||||
|
100
src/IDF/templates/idf/issues/summary.html
Normal file
100
src/IDF/templates/idf/issues/summary.html
Normal 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}
|
@@ -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}
|
@@ -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&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&d={media}/idf/img/spacer.gif" alt=" " />
|
||||||
{/if}
|
{/if}
|
||||||
@@ -190,6 +190,7 @@
|
|||||||
{if count($related_issues) > 0}
|
{if count($related_issues) > 0}
|
||||||
{foreach $related_issues as $verb => $rel_issues}
|
{foreach $related_issues as $verb => $rel_issues}
|
||||||
<p>
|
<p>
|
||||||
|
{assign $verb = __($verb)}
|
||||||
<strong>{blocktrans}This issue {$verb}{/blocktrans}</strong><br />
|
<strong>{blocktrans}This issue {$verb}{/blocktrans}</strong><br />
|
||||||
{foreach $rel_issues as $rel_issue}
|
{foreach $rel_issues as $rel_issue}
|
||||||
<span class="label">
|
<span class="label">
|
||||||
|
@@ -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>
|
||||||
|
|
||||||
|
@@ -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}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -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'}" />
|
||||||
|
@@ -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}
|
|
||||||
|
@@ -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> </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> </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&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&d={media}/idf/img/spacer.gif" alt=" " />
|
||||||
{/if}
|
{/if}
|
||||||
|
@@ -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} → {$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} → {$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} → {$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}
|
||||||
|
2
src/IDF/templates/idf/source/git/invalid_revision.html
Normal file
2
src/IDF/templates/idf/source/git/invalid_revision.html
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
{extends "idf/source/invalid_revision.html"}
|
||||||
|
|
@@ -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}
|
||||||
|
@@ -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)}
|
||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
{extends "idf/source/invalid_revision.html"}
|
||||||
|
|
@@ -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>
|
||||||
|
2
src/IDF/templates/idf/source/mtn/invalid_revision.html
Normal file
2
src/IDF/templates/idf/source/mtn/invalid_revision.html
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
{extends "idf/source/invalid_revision.html"}
|
||||||
|
|
@@ -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> </td>
|
<td> </td>
|
||||||
|
@@ -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}
|
||||||
|
@@ -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}
|
||||||
|
@@ -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}
|
||||||
|
|
||||||
|
30
src/IDF/templates/idf/source/svn/invalid_revision.html
Normal file
30
src/IDF/templates/idf/source/svn/invalid_revision.html
Normal 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}
|
||||||
|
|
@@ -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}
|
||||||
|
@@ -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>
|
|
||||||
|
@@ -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&d={media}/idf/img/spacer.gif" alt=" " />
|
<img src="http://www.gravatar.com/avatar/{$member.email|md5}.jpg?s=60&d={media}/idf/img/spacer.gif" alt=" " />
|
||||||
{/if}
|
{/if}
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
{block tabwiki} class="active"{/block}
|
{block tabwiki} class="active"{/block}
|
||||||
{block subtabs}
|
{block subtabs}
|
||||||
<div id="sub-tabs">
|
<div id="sub-tabs">
|
||||||
<a {if $inWiki}class="active" {/if}href="{url 'IDF_Views_Wiki::index', array($project.shortname)}">{trans 'List Pages'}</a>
|
<a {if $inWiki}class="active" {/if}href="{url 'IDF_Views_Wiki::listing', array($project.shortname)}">{trans 'List Pages'}</a>
|
||||||
{if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Wiki::create', array($project.shortname)}">{trans 'New Page'}</a> {/if}
|
{if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Wiki::create', array($project.shortname)}">{trans 'New Page'}</a> {/if}
|
||||||
{if !$user.isAnonymous() and $inView} | <a href="{url 'IDF_Views_Wiki::update', array($project.shortname, $page.title)}">{trans 'Update This Page'}</a> {/if}
|
{if !$user.isAnonymous() and $inView} | <a href="{url 'IDF_Views_Wiki::update', array($project.shortname, $page.title)}">{trans 'Update This Page'}</a> {/if}
|
||||||
|
|
|
|
||||||
|
38
src/IDF/templates/idf/wiki/history.html
Normal file
38
src/IDF/templates/idf/wiki/history.html
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{extends "idf/wiki/base.html"}
|
||||||
|
|
||||||
|
{block extraheader}
|
||||||
|
<meta name="ROBOTS" content="NOINDEX" />
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block docclass}yui-t3{assign $inView=true}{/block}
|
||||||
|
|
||||||
|
{block body}
|
||||||
|
<table class="recent-issues">
|
||||||
|
<tr><th>Id</th><th>Who</th><th>When</th><th>Summary</th><th>Actions</th></tr>
|
||||||
|
{foreach $revs as $r}
|
||||||
|
{ashowuser 'submitter', $rev.get_submitter(), $request}
|
||||||
|
{aurl 'view_url', 'IDF_Views_Wiki::view', array($project.shortname, $page.title), array('rev' => $r->id)}
|
||||||
|
{aurl 'delete_url', 'IDF_Views_Wiki::deleteRev', array($project.shortname, $r->id)}
|
||||||
|
{aurl 'restore_url', 'IDF_Views_Wiki::restoreRev', array($project.shortname, $r->id)}
|
||||||
|
<tr><td><a href="{$view_url}">{$r->id}</a></td><td>{$submitter}</td><td class="a-c">{$r->creation_dtime|dateago}</td><td>{$r->summary}</td><td>{if $r->is_head == false}<a href="{$view_url}">View</a> - <a href="{$delete_url}">Delete</a> - <a href="{$restore_url}">Restore</a>{/if}</td></tr>
|
||||||
|
{/foreach}
|
||||||
|
</table>
|
||||||
|
{aurl 'add_rev', 'IDF_Views_Wiki::update', array($project.shortname, $page.title)}
|
||||||
|
<p><a href="{$add_rev}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/add.png'}" alt="+" align="bottom"></a> <a href="{$add_rev}">{trans 'Update This Page'}</a></p>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
|
||||||
|
{block context}
|
||||||
|
{ashowuser 'submitter', $page.get_submitter(), $request}
|
||||||
|
<p><strong>{trans 'Created:'}</strong> <span class="nobrk">{$page.creation_dtime|dateago}</span><br /><span class="nobrk">{blocktrans}by {$submitter}{/blocktrans}</span></p>
|
||||||
|
{if $rev.creation_dtime != $page.creation_dtime}<p>{ashowuser 'submitter', $rev.get_submitter(), $request}
|
||||||
|
<strong>{trans 'Updated:'}</strong> <span class="nobrk">{$rev.creation_dtime|dateago}</span><br /><span class="nobrk">{blocktrans}by {$submitter}{/blocktrans}</span></p>{/if}
|
||||||
|
{if $tags.count()}
|
||||||
|
<p>
|
||||||
|
<strong>{trans 'Labels:'}</strong><br />
|
||||||
|
{foreach $tags as $tag}
|
||||||
|
<span class="label"><strong>{$tag.class}:</strong>{$tag.name}</span><br />
|
||||||
|
{/foreach}
|
||||||
|
</p>{/if}
|
||||||
|
<p class="helptext"><a href="{url 'IDF_Views_Wiki::view', array($project.shortname, $page.title)}">{trans 'See current revision'}</a></p>
|
||||||
|
{/block}
|
@@ -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}
|
||||||
@@ -47,9 +52,6 @@ by {$submitter}.{/blocktrans}</p>
|
|||||||
{/foreach}
|
{/foreach}
|
||||||
</p>{/if}
|
</p>{/if}
|
||||||
{if $revs.count() > 0}
|
{if $revs.count() > 0}
|
||||||
<p><strong>{trans 'Old Revisions'}</strong></p>
|
<p class="helptext"><a href="{url 'IDF_Views_Wiki::history', array($project.shortname, $page.title)}">{trans 'See older revision'}</a></p>
|
||||||
<ul>{foreach $revs as $old}
|
|
||||||
<li><a href="{url 'IDF_Views_Wiki::view', array($project.shortname, $page.title), array('rev'=>$old.id)}">{$old.summary}</a></li>
|
|
||||||
{/foreach}</ul>
|
|
||||||
{/if}
|
{/if}
|
||||||
{/block}
|
{/block}
|
||||||
|
17
src/IDF/templates/idf/wiki/wiki-default-page.mdtext
Normal file
17
src/IDF/templates/idf/wiki/wiki-default-page.mdtext
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{aurl 'syntax_url', 'IDF_Views_Wiki::view', array($project.shortname, 'IndeferoMarkdownHelp')}
|
||||||
|
{aurl 'wiki_add', 'IDF_Views_Wiki::create', array($project.shortname)}
|
||||||
|
{aurl 'wiki_update', 'IDF_Views_Wiki::update', array($project.shortname, 'IndeferoSummaryDefault')}
|
||||||
|
{aurl 'wiki_history', 'IDF_Views_Wiki::history', array($project.shortname, 'IndeferoSummaryDefault')}
|
||||||
|
{aurl 'wiki_admin', 'IDF_Views_Project::adminWiki', array($project.shortname)}
|
||||||
|
{blocktrans}
|
||||||
|
Welcome on the documentation section of the project {$project->name}.
|
||||||
|
All documentation page use the markdown syntax, you can find help about this syntax on this [page]({$syntax_url}).
|
||||||
|
|
||||||
|
- You can another Wiki page [here]({$wiki_add}).
|
||||||
|
- You can update this page [here]({$wiki_update}).
|
||||||
|
- You can select an other default wiki page [here]({$wiki_admin}). You need to be admin.
|
||||||
|
|
||||||
|
|
||||||
|
All modification on a wiki page are saved, and you can see this history for each pages.
|
||||||
|
The history of the current page can be see [here]({$wiki_history}).
|
||||||
|
{/blocktrans}
|
569
src/IDF/templates/idf/wiki/wiki-markdown-help.mdtext
Normal file
569
src/IDF/templates/idf/wiki/wiki-markdown-help.mdtext
Normal file
@@ -0,0 +1,569 @@
|
|||||||
|
Block Elements
|
||||||
|
==============
|
||||||
|
|
||||||
|
|
||||||
|
Paragraphs and Line Breaks
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
A paragraph is simply one or more consecutive lines of text, separated
|
||||||
|
by one or more blank lines. (A blank line is any line that looks like a
|
||||||
|
blank line -- a line containing nothing but spaces or tabs is considered
|
||||||
|
blank.) Normal paragraphs should not be indented with spaces or tabs.
|
||||||
|
|
||||||
|
The implication of the "one or more consecutive lines of text" rule is
|
||||||
|
that Markdown supports "hard-wrapped" text paragraphs. This differs
|
||||||
|
significantly from most other text-to-HTML formatters (including Movable
|
||||||
|
Type's "Convert Line Breaks" option) which translate every line break
|
||||||
|
character in a paragraph into a `<br />` tag.
|
||||||
|
|
||||||
|
When you *do* want to insert a `<br />` break tag using Markdown, you
|
||||||
|
end a line with two or more spaces, then type return.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Headers
|
||||||
|
-------
|
||||||
|
|
||||||
|
Markdown supports two styles of headers, [Setext] [1] and [atx] [2].
|
||||||
|
|
||||||
|
Setext-style headers are "underlined" using equal signs (for first-level
|
||||||
|
headers) and dashes (for second-level headers). For example:
|
||||||
|
|
||||||
|
This is an H1
|
||||||
|
=============
|
||||||
|
|
||||||
|
This is an H2
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any number of underlining `=`'s or `-`'s will work.
|
||||||
|
|
||||||
|
Atx-style headers use 1-6 hash characters at the start of the line,
|
||||||
|
corresponding to header levels 1-6. For example:
|
||||||
|
|
||||||
|
# This is an H1
|
||||||
|
|
||||||
|
## This is an H2
|
||||||
|
|
||||||
|
###### This is an H6
|
||||||
|
|
||||||
|
Optionally, you may "close" atx-style headers. This is purely
|
||||||
|
cosmetic -- you can use this if you think it looks better. The
|
||||||
|
closing hashes don't even need to match the number of hashes
|
||||||
|
used to open the header. (The number of opening hashes
|
||||||
|
determines the header level.) :
|
||||||
|
|
||||||
|
# This is an H1 #
|
||||||
|
|
||||||
|
## This is an H2 ##
|
||||||
|
|
||||||
|
### This is an H3 ######
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Blockquotes
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Markdown uses email-style `>` characters for blockquoting. If you're
|
||||||
|
familiar with quoting passages of text in an email message, then you
|
||||||
|
know how to create a blockquote in Markdown. It looks best if you hard
|
||||||
|
wrap the text and put a `>` before every line:
|
||||||
|
|
||||||
|
> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
|
||||||
|
> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
|
||||||
|
> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
|
||||||
|
>
|
||||||
|
> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
|
||||||
|
> id sem consectetuer libero luctus adipiscing.
|
||||||
|
|
||||||
|
Markdown allows you to be lazy and only put the `>` before the first
|
||||||
|
line of a hard-wrapped paragraph:
|
||||||
|
|
||||||
|
> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
|
||||||
|
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
|
||||||
|
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
|
||||||
|
|
||||||
|
> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
|
||||||
|
id sem consectetuer libero luctus adipiscing.
|
||||||
|
|
||||||
|
Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by
|
||||||
|
adding additional levels of `>`:
|
||||||
|
|
||||||
|
> This is the first level of quoting.
|
||||||
|
>
|
||||||
|
> > This is nested blockquote.
|
||||||
|
>
|
||||||
|
> Back to the first level.
|
||||||
|
|
||||||
|
Blockquotes can contain other Markdown elements, including headers, lists,
|
||||||
|
and code blocks:
|
||||||
|
|
||||||
|
> ## This is a header.
|
||||||
|
>
|
||||||
|
> 1. This is the first list item.
|
||||||
|
> 2. This is the second list item.
|
||||||
|
>
|
||||||
|
> Here's some example code:
|
||||||
|
>
|
||||||
|
> return shell_exec("echo $input | $markdown_script");
|
||||||
|
|
||||||
|
Any decent text editor should make email-style quoting easy. For
|
||||||
|
example, with BBEdit, you can make a selection and choose Increase
|
||||||
|
Quote Level from the Text menu.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Lists
|
||||||
|
-----
|
||||||
|
|
||||||
|
Markdown supports ordered (numbered) and unordered (bulleted) lists.
|
||||||
|
|
||||||
|
Unordered lists use asterisks, pluses, and hyphens -- interchangably
|
||||||
|
-- as list markers:
|
||||||
|
|
||||||
|
* Red
|
||||||
|
* Green
|
||||||
|
* Blue
|
||||||
|
|
||||||
|
is equivalent to:
|
||||||
|
|
||||||
|
+ Red
|
||||||
|
+ Green
|
||||||
|
+ Blue
|
||||||
|
|
||||||
|
and:
|
||||||
|
|
||||||
|
- Red
|
||||||
|
- Green
|
||||||
|
- Blue
|
||||||
|
|
||||||
|
Ordered lists use numbers followed by periods:
|
||||||
|
|
||||||
|
1. Bird
|
||||||
|
2. McHale
|
||||||
|
3. Parish
|
||||||
|
|
||||||
|
It's important to note that the actual numbers you use to mark the
|
||||||
|
list have no effect on the HTML output Markdown produces. The HTML
|
||||||
|
Markdown produces from the above list is:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li>Bird</li>
|
||||||
|
<li>McHale</li>
|
||||||
|
<li>Parish</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
If you instead wrote the list in Markdown like this:
|
||||||
|
|
||||||
|
1. Bird
|
||||||
|
1. McHale
|
||||||
|
1. Parish
|
||||||
|
|
||||||
|
or even:
|
||||||
|
|
||||||
|
3. Bird
|
||||||
|
1. McHale
|
||||||
|
8. Parish
|
||||||
|
|
||||||
|
you'd get the exact same HTML output. The point is, if you want to,
|
||||||
|
you can use ordinal numbers in your ordered Markdown lists, so that
|
||||||
|
the numbers in your source match the numbers in your published HTML.
|
||||||
|
But if you want to be lazy, you don't have to.
|
||||||
|
|
||||||
|
If you do use lazy list numbering, however, you should still start the
|
||||||
|
list with the number 1. At some point in the future, Markdown may support
|
||||||
|
starting ordered lists at an arbitrary number.
|
||||||
|
|
||||||
|
List markers typically start at the left margin, but may be indented by
|
||||||
|
up to three spaces. List markers must be followed by one or more spaces
|
||||||
|
or a tab.
|
||||||
|
|
||||||
|
To make lists look nice, you can wrap items with hanging indents:
|
||||||
|
|
||||||
|
* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
|
||||||
|
Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
|
||||||
|
viverra nec, fringilla in, laoreet vitae, risus.
|
||||||
|
* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
|
||||||
|
Suspendisse id sem consectetuer libero luctus adipiscing.
|
||||||
|
|
||||||
|
But if you want to be lazy, you don't have to:
|
||||||
|
|
||||||
|
* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
|
||||||
|
Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
|
||||||
|
viverra nec, fringilla in, laoreet vitae, risus.
|
||||||
|
* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
|
||||||
|
Suspendisse id sem consectetuer libero luctus adipiscing.
|
||||||
|
|
||||||
|
If list items are separated by blank lines, Markdown will wrap the
|
||||||
|
items in `<p>` tags in the HTML output. For example, this input:
|
||||||
|
|
||||||
|
* Bird
|
||||||
|
* Magic
|
||||||
|
|
||||||
|
will turn into:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Bird</li>
|
||||||
|
<li>Magic</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
But this:
|
||||||
|
|
||||||
|
* Bird
|
||||||
|
|
||||||
|
* Magic
|
||||||
|
|
||||||
|
will turn into:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><p>Bird</p></li>
|
||||||
|
<li><p>Magic</p></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
List items may consist of multiple paragraphs. Each subsequent
|
||||||
|
paragraph in a list item must be indented by either 4 spaces
|
||||||
|
or one tab:
|
||||||
|
|
||||||
|
1. This is a list item with two paragraphs. Lorem ipsum dolor
|
||||||
|
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
|
||||||
|
mi posuere lectus.
|
||||||
|
|
||||||
|
Vestibulum enim wisi, viverra nec, fringilla in, laoreet
|
||||||
|
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
|
||||||
|
sit amet velit.
|
||||||
|
|
||||||
|
2. Suspendisse id sem consectetuer libero luctus adipiscing.
|
||||||
|
|
||||||
|
It looks nice if you indent every line of the subsequent
|
||||||
|
paragraphs, but here again, Markdown will allow you to be
|
||||||
|
lazy:
|
||||||
|
|
||||||
|
* This is a list item with two paragraphs.
|
||||||
|
|
||||||
|
This is the second paragraph in the list item. You're
|
||||||
|
only required to indent the first line. Lorem ipsum dolor
|
||||||
|
sit amet, consectetuer adipiscing elit.
|
||||||
|
|
||||||
|
* Another item in the same list.
|
||||||
|
|
||||||
|
To put a blockquote within a list item, the blockquote's `>`
|
||||||
|
delimiters need to be indented:
|
||||||
|
|
||||||
|
* A list item with a blockquote:
|
||||||
|
|
||||||
|
> This is a blockquote
|
||||||
|
> inside a list item.
|
||||||
|
|
||||||
|
To put a code block within a list item, the code block needs
|
||||||
|
to be indented *twice* -- 8 spaces or two tabs:
|
||||||
|
|
||||||
|
* A list item with a code block:
|
||||||
|
|
||||||
|
<code goes here>
|
||||||
|
|
||||||
|
|
||||||
|
It's worth noting that it's possible to trigger an ordered list by
|
||||||
|
accident, by writing something like this:
|
||||||
|
|
||||||
|
1986. What a great season.
|
||||||
|
|
||||||
|
In other words, a *number-period-space* sequence at the beginning of a
|
||||||
|
line. To avoid this, you can backslash-escape the period:
|
||||||
|
|
||||||
|
1986\. What a great season.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Code Blocks
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Pre-formatted code blocks are used for writing about programming or
|
||||||
|
markup source code. Rather than forming normal paragraphs, the lines
|
||||||
|
of a code block are interpreted literally. Markdown wraps a code block
|
||||||
|
in both `<pre>` and `<code>` tags.
|
||||||
|
|
||||||
|
To produce a code block in Markdown, simply indent every line of the
|
||||||
|
block by at least 4 spaces or 1 tab. For example, given this input:
|
||||||
|
|
||||||
|
This is a normal paragraph:
|
||||||
|
|
||||||
|
This is a code block.
|
||||||
|
|
||||||
|
Markdown will generate:
|
||||||
|
|
||||||
|
<p>This is a normal paragraph:</p>
|
||||||
|
|
||||||
|
<pre><code>This is a code block.
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
One level of indentation -- 4 spaces or 1 tab -- is removed from each
|
||||||
|
line of the code block. For example, this:
|
||||||
|
|
||||||
|
Here is an example of AppleScript:
|
||||||
|
|
||||||
|
tell application "Foo"
|
||||||
|
beep
|
||||||
|
end tell
|
||||||
|
|
||||||
|
will turn into:
|
||||||
|
|
||||||
|
<p>Here is an example of AppleScript:</p>
|
||||||
|
|
||||||
|
<pre><code>tell application "Foo"
|
||||||
|
beep
|
||||||
|
end tell
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
A code block continues until it reaches a line that is not indented
|
||||||
|
(or the end of the article).
|
||||||
|
|
||||||
|
Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
|
||||||
|
are automatically converted into HTML entities. This makes it very
|
||||||
|
easy to include example HTML source code using Markdown -- just paste
|
||||||
|
it and indent it, and Markdown will handle the hassle of encoding the
|
||||||
|
ampersands and angle brackets. For example, this:
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
© 2004 Foo Corporation
|
||||||
|
</div>
|
||||||
|
|
||||||
|
will turn into:
|
||||||
|
|
||||||
|
<pre><code><div class="footer">
|
||||||
|
&copy; 2004 Foo Corporation
|
||||||
|
</div>
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Regular Markdown syntax is not processed within code blocks. E.g.,
|
||||||
|
asterisks are just literal asterisks within a code block. This means
|
||||||
|
it's also easy to use Markdown to write about Markdown's own syntax.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Span Elements
|
||||||
|
=============
|
||||||
|
|
||||||
|
|
||||||
|
Links
|
||||||
|
-----
|
||||||
|
|
||||||
|
Markdown supports two style of links: *inline* and *reference*.
|
||||||
|
|
||||||
|
In both styles, the link text is delimited by [square brackets].
|
||||||
|
|
||||||
|
To create an inline link, use a set of regular parentheses immediately
|
||||||
|
after the link text's closing square bracket. Inside the parentheses,
|
||||||
|
put the URL where you want the link to point, along with an *optional*
|
||||||
|
title for the link, surrounded in quotes. For example:
|
||||||
|
|
||||||
|
This is [an example](http://example.com/ "Title") inline link.
|
||||||
|
|
||||||
|
[This link](http://example.net/) has no title attribute.
|
||||||
|
|
||||||
|
Will produce:
|
||||||
|
|
||||||
|
<p>This is <a href="http://example.com/" title="Title">
|
||||||
|
an example</a> inline link.</p>
|
||||||
|
|
||||||
|
<p><a href="http://example.net/">This link</a> has no
|
||||||
|
title attribute.</p>
|
||||||
|
|
||||||
|
If you're referring to a local resource on the same server, you can
|
||||||
|
use relative paths:
|
||||||
|
|
||||||
|
See my [About](/about/) page for details.
|
||||||
|
|
||||||
|
Reference-style links use a second set of square brackets, inside
|
||||||
|
which you place a label of your choosing to identify the link:
|
||||||
|
|
||||||
|
This is [an example][id] reference-style link.
|
||||||
|
|
||||||
|
You can optionally use a space to separate the sets of brackets:
|
||||||
|
|
||||||
|
This is [an example] [id] reference-style link.
|
||||||
|
|
||||||
|
Then, anywhere in the document, you define your link label like this,
|
||||||
|
on a line by itself:
|
||||||
|
|
||||||
|
[id]: http://example.com/ "Optional Title Here"
|
||||||
|
|
||||||
|
That is:
|
||||||
|
|
||||||
|
* Square brackets containing the link identifier (optionally
|
||||||
|
indented from the left margin using up to three spaces);
|
||||||
|
* followed by a colon;
|
||||||
|
* followed by one or more spaces (or tabs);
|
||||||
|
* followed by the URL for the link;
|
||||||
|
* optionally followed by a title attribute for the link, enclosed
|
||||||
|
in double or single quotes, or enclosed in parentheses.
|
||||||
|
|
||||||
|
The following three link definitions are equivalent:
|
||||||
|
|
||||||
|
[foo]: http://example.com/ "Optional Title Here"
|
||||||
|
[foo]: http://example.com/ 'Optional Title Here'
|
||||||
|
[foo]: http://example.com/ (Optional Title Here)
|
||||||
|
|
||||||
|
**Note:** There is a known bug in Markdown.pl 1.0.1 which prevents
|
||||||
|
single quotes from being used to delimit link titles.
|
||||||
|
|
||||||
|
The link URL may, optionally, be surrounded by angle brackets:
|
||||||
|
|
||||||
|
[id]: <http://example.com/> "Optional Title Here"
|
||||||
|
|
||||||
|
You can put the title attribute on the next line and use extra spaces
|
||||||
|
or tabs for padding, which tends to look better with longer URLs:
|
||||||
|
|
||||||
|
[id]: http://example.com/longish/path/to/resource/here
|
||||||
|
"Optional Title Here"
|
||||||
|
|
||||||
|
Link definitions are only used for creating links during Markdown
|
||||||
|
processing, and are stripped from your document in the HTML output.
|
||||||
|
|
||||||
|
Link definition names may consist of letters, numbers, spaces, and
|
||||||
|
punctuation -- but they are *not* case sensitive. E.g. these two
|
||||||
|
links:
|
||||||
|
|
||||||
|
[link text][a]
|
||||||
|
[link text][A]
|
||||||
|
|
||||||
|
are equivalent.
|
||||||
|
|
||||||
|
The *implicit link name* shortcut allows you to omit the name of the
|
||||||
|
link, in which case the link text itself is used as the name.
|
||||||
|
Just use an empty set of square brackets -- e.g., to link the word
|
||||||
|
"Google" to the google.com web site, you could simply write:
|
||||||
|
|
||||||
|
[Google][]
|
||||||
|
|
||||||
|
And then define the link:
|
||||||
|
|
||||||
|
[Google]: http://google.com/
|
||||||
|
|
||||||
|
Because link names may contain spaces, this shortcut even works for
|
||||||
|
multiple words in the link text:
|
||||||
|
|
||||||
|
Visit [Daring Fireball][] for more information.
|
||||||
|
|
||||||
|
And then define the link:
|
||||||
|
|
||||||
|
[Daring Fireball]: http://daringfireball.net/
|
||||||
|
|
||||||
|
Link definitions can be placed anywhere in your Markdown document. I
|
||||||
|
tend to put them immediately after each paragraph in which they're
|
||||||
|
used, but if you want, you can put them all at the end of your
|
||||||
|
document, sort of like footnotes.
|
||||||
|
|
||||||
|
Here's an example of reference links in action:
|
||||||
|
|
||||||
|
I get 10 times more traffic from [Google] [1] than from
|
||||||
|
[Yahoo] [2] or [MSN] [3].
|
||||||
|
|
||||||
|
[1]: http://google.com/ "Google"
|
||||||
|
[2]: http://search.yahoo.com/ "Yahoo Search"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Emphasis
|
||||||
|
--------
|
||||||
|
|
||||||
|
Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
|
||||||
|
emphasis. Text wrapped with one `*` or `_` will be wrapped with an
|
||||||
|
HTML `<em>` tag; double `*`'s or `_`'s will be wrapped with an HTML
|
||||||
|
`<strong>` tag. E.g., this input:
|
||||||
|
|
||||||
|
*single asterisks*
|
||||||
|
|
||||||
|
_single underscores_
|
||||||
|
|
||||||
|
**double asterisks**
|
||||||
|
|
||||||
|
__double underscores__
|
||||||
|
|
||||||
|
will produce:
|
||||||
|
|
||||||
|
<em>single asterisks</em>
|
||||||
|
|
||||||
|
<em>single underscores</em>
|
||||||
|
|
||||||
|
<strong>double asterisks</strong>
|
||||||
|
|
||||||
|
<strong>double underscores</strong>
|
||||||
|
|
||||||
|
You can use whichever style you prefer; the lone restriction is that
|
||||||
|
the same character must be used to open and close an emphasis span.
|
||||||
|
|
||||||
|
Emphasis can be used in the middle of a word:
|
||||||
|
|
||||||
|
un*frigging*believable
|
||||||
|
|
||||||
|
But if you surround an `*` or `_` with spaces, it'll be treated as a
|
||||||
|
literal asterisk or underscore.
|
||||||
|
|
||||||
|
To produce a literal asterisk or underscore at a position where it
|
||||||
|
would otherwise be used as an emphasis delimiter, you can backslash
|
||||||
|
escape it:
|
||||||
|
|
||||||
|
\*this text is surrounded by literal asterisks\*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Code
|
||||||
|
----
|
||||||
|
|
||||||
|
To indicate a span of code, wrap it with backtick quotes (`` ` ``).
|
||||||
|
Unlike a pre-formatted code block, a code span indicates code within a
|
||||||
|
normal paragraph. For example:
|
||||||
|
|
||||||
|
Use the `printf()` function.
|
||||||
|
|
||||||
|
will produce:
|
||||||
|
|
||||||
|
<p>Use the <code>printf()</code> function.</p>
|
||||||
|
|
||||||
|
To include a literal backtick character within a code span, you can use
|
||||||
|
multiple backticks as the opening and closing delimiters:
|
||||||
|
|
||||||
|
``There is a literal backtick (`) here.``
|
||||||
|
|
||||||
|
which will produce this:
|
||||||
|
|
||||||
|
<p><code>There is a literal backtick (`) here.</code></p>
|
||||||
|
|
||||||
|
The backtick delimiters surrounding a code span may include spaces --
|
||||||
|
one after the opening, one before the closing. This allows you to place
|
||||||
|
literal backtick characters at the beginning or end of a code span:
|
||||||
|
|
||||||
|
A single backtick in a code span: `` ` ``
|
||||||
|
|
||||||
|
A backtick-delimited string in a code span: `` `foo` ``
|
||||||
|
|
||||||
|
will produce:
|
||||||
|
|
||||||
|
<p>A single backtick in a code span: <code>`</code></p>
|
||||||
|
|
||||||
|
<p>A backtick-delimited string in a code span: <code>`foo`</code></p>
|
||||||
|
|
||||||
|
With a code span, ampersands and angle brackets are encoded as HTML
|
||||||
|
entities automatically, which makes it easy to include example HTML
|
||||||
|
tags. Markdown will turn this:
|
||||||
|
|
||||||
|
Please don't use any `<blink>` tags.
|
||||||
|
|
||||||
|
into:
|
||||||
|
|
||||||
|
<p>Please don't use any <code><blink></code> tags.</p>
|
||||||
|
|
||||||
|
You can write this:
|
||||||
|
|
||||||
|
`—` is the decimal-encoded equivalent of `—`.
|
||||||
|
|
||||||
|
to produce:
|
||||||
|
|
||||||
|
<p><code>&#8212;</code> is the decimal-encoded
|
||||||
|
equivalent of <code>&mdash;</code>.</p>
|
||||||
|
|
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
return array(
|
return array(
|
||||||
'version' => '1.2-dev',
|
'version' => '1.3-dev',
|
||||||
'revision' => '$Format:%H$',
|
'revision' => '$Format:%H$',
|
||||||
);
|
);
|
||||||
|
@@ -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
90
test/IDF/ProjectTest.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -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
55
test/IDF/Scm/SvnTest.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -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
@@ -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
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user