Compare commits
	
		
			128 Commits
		
	
	
		
			feature.be
			...
			feature.we
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					cdb8dbafe2 | ||
| 
						 | 
					33b22f95ab | ||
| 
						 | 
					39f77886db | ||
| 
						 | 
					f7470e4a7a | ||
| 
						 | 
					c10c002ee3 | ||
| 
						 | 
					a47ec0df0a | ||
| 
						 | 
					be95050a4b | ||
| 
						 | 
					5043c4e845 | ||
| 
						 | 
					dacbf0707b | ||
| 
						 | 
					34c9d04a35 | ||
| 
						 | 
					aa2868eb17 | ||
| 
						 | 
					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 | ||
| 
						 | 
					9bcb5f9456 | ||
| 
						 | 
					f412099f69 | ||
| 
						 | 
					0aa5999bb3 | ||
| 
						 | 
					16dda0743c | ||
| 
						 | 
					d079838818 | ||
| 
						 | 
					81ce4688df | ||
| 
						 | 
					ee33cc1832 | ||
| 
						 | 
					82bb18fe10 | ||
| 
						 | 
					8987ca7db6 | ||
| 
						 | 
					8066fd8982 | ||
| 
						 | 
					a96d8c05a7 | ||
| 
						 | 
					4238a5dd50 | ||
| 
						 | 
					94da55d15e | ||
| 
						 | 
					09979b8551 | ||
| 
						 | 
					5b82efa0be | ||
| 
						 | 
					8502a36481 | ||
| 
						 | 
					bcba64b2a1 | ||
| 
						 | 
					e40d922eef | ||
| 
						 | 
					7e226b43d3 | ||
| 
						 | 
					9171bfd1ab | ||
| 
						 | 
					be4774c95c | ||
| 
						 | 
					bbf1a1882a | ||
| 
						 | 
					9644784a79 | ||
| 
						 | 
					1940d5c0b5 | ||
| 
						 | 
					7c7e3cd1f1 | ||
| 
						 | 
					3e2f95a152 | ||
| 
						 | 
					92de88ba13 | ||
| 
						 | 
					5322cdf609 | ||
| 
						 | 
					02d0f0923e | ||
| 
						 | 
					765a86ba90 | ||
| 
						 | 
					34309e34c3 | ||
| 
						 | 
					2176d1cde2 | ||
| 
						 | 
					45d53e8d21 | ||
| 
						 | 
					801af66a4e | ||
| 
						 | 
					df6ffdf420 | ||
| 
						 | 
					b3368071ac | ||
| 
						 | 
					67b80ee11c | ||
| 
						 | 
					0afa07b2bd | ||
| 
						 | 
					4ee3d471fe | ||
| 
						 | 
					29c7fed81b | ||
| 
						 | 
					fb62061e5a | ||
| 
						 | 
					9b92b7139f | ||
| 
						 | 
					5ea4b02205 | ||
| 
						 | 
					12d3eef3d1 | ||
| 
						 | 
					ecef510f78 | ||
| 
						 | 
					4976c20935 | ||
| 
						 | 
					5553c37ccd | ||
| 
						 | 
					346b2c6cf8 | 
							
								
								
									
										1
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								AUTHORS
									
									
									
									
									
								
							@@ -12,6 +12,7 @@ Much appreciated contributors (in alphabetical order):
 | 
			
		||||
    Brian Armstrong <brianar>
 | 
			
		||||
    Charles Melbye <charlie@yourwiki.net>
 | 
			
		||||
    Ciaran Gultnieks <ciaran@ciarang.com>
 | 
			
		||||
    Daniel Steudler <steudlerdaniel@gmail.com>
 | 
			
		||||
    David Feeney <davidf>
 | 
			
		||||
    Denis Kot <denis.kot@gmail.com>                 - Russian translation
 | 
			
		||||
    Dmitry Dulepov <dmitryd>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								Makefile
									
									
									
									
									
								
							@@ -37,6 +37,10 @@ help:
 | 
			
		||||
	@printf "\tpo-push    - Send the all PO files to the transifex server.\n"
 | 
			
		||||
	@printf "\tpo-pull    - Get all PO files from the transifex server.\n"
 | 
			
		||||
	@printf "\tpo-stats   - Show translation statistics of all PO files.\n"
 | 
			
		||||
	@printf "\nMisc Rules :\n";
 | 
			
		||||
	@printf "\tdb-install - Install the database schema.\n"
 | 
			
		||||
	@printf "\tdb-update  - Update the database schema.\n"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#   Internationalization rule, POT & PO file manipulation
 | 
			
		||||
@@ -139,3 +143,8 @@ po-stats:
 | 
			
		||||
		> indefero-$(@:-zipfile=)-`git log $(@:-zipfile=) -n 1 \
 | 
			
		||||
		--pretty=format:%h`.zip
 | 
			
		||||
 | 
			
		||||
db-install:
 | 
			
		||||
	@cd src && php $(PLUF_PATH)/migrate.php --conf=IDF/conf/idf.php -a -d -i
 | 
			
		||||
 | 
			
		||||
db-update:
 | 
			
		||||
	@cd src && php $(PLUF_PATH)/migrate.php --conf=IDF/conf/idf.php -a -d
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										52
									
								
								NEWS.mdtext
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								NEWS.mdtext
									
									
									
									
									
								
							@@ -1,16 +1,66 @@
 | 
			
		||||
# InDefero 1.2 - xxx xxx xx xx:xx 2011 UTC
 | 
			
		||||
 | 
			
		||||
ATTENTION: You need Pluf [324ae60b](http://projects.ceondo.com/p/pluf/source/commit/324ae60b)
 | 
			
		||||
or newer to properly run this version of Indefero!
 | 
			
		||||
 | 
			
		||||
## New Features
 | 
			
		||||
 | 
			
		||||
## Bugfixes 
 | 
			
		||||
- Indefero's issue tracker can now bi-directionally link issues with variable, configurable
 | 
			
		||||
  terms, such as "is related to", "is blocked by" or "is duplicated by" (issue 638)
 | 
			
		||||
- Mercurial source views now show parent revisions (if any) and detailed change information
 | 
			
		||||
- Subversion source views now show detailed change information (issue 622)
 | 
			
		||||
- File download URLs now contain the file name rather than the upload id; old links still work though (issues 559 and 686)
 | 
			
		||||
- Display monotone file and directory attributes in the tree and file view
 | 
			
		||||
  (needs a monotone with an interface version of 13.1 or newer)
 | 
			
		||||
- The context area is now kept in view when a page scrolls down several pages
 | 
			
		||||
- git repositories are now available under /r/projectname/
 | 
			
		||||
 | 
			
		||||
## Bugfixes
 | 
			
		||||
 | 
			
		||||
- The SVN interface acts more robust if an underlying repository has been restructured (issues 364 and 721)
 | 
			
		||||
- monotone zip archive entries now all carry the revision date as mtime (issue 645)
 | 
			
		||||
- Timeline 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)
 | 
			
		||||
- Fix SSH public key parsing issues and improve the check for existing, uploaded keys (issue 679)
 | 
			
		||||
- Diff views now show empty context lines for git and hg again (issue 688)
 | 
			
		||||
- Let the SVN command line client not store the login credentials we give him as arguments
 | 
			
		||||
- The usher section in the forge administration no longer displays a bogus
 | 
			
		||||
  server enty in case no monotone server is configured in the connected
 | 
			
		||||
  usher instance
 | 
			
		||||
- Prevent a timeout from popping up when Usher is restarted (issue 695)
 | 
			
		||||
- The SyncMonotone plugin now cleans up partial artifacts it created during the addition of
 | 
			
		||||
  a new project or monotone key, in case an error popped up in the middle (issue 697)
 | 
			
		||||
- Indefero now sends the MD5 checksum as HTTP header when downloading a file from the
 | 
			
		||||
  download area. Additionally, a unneeded redirect has been removed. (issue 716)
 | 
			
		||||
- Better error detection and reporting in the SyncMonotone plugin
 | 
			
		||||
  ATTENTION: This needs Pluf 46b7f251 or newer!
 | 
			
		||||
- Fix the branch links users of the Subversion frontend get when they enter a wrong revision
 | 
			
		||||
  and only display this list if there are any branches available for all SCMs
 | 
			
		||||
- If git's author name is not encoded in an UTF-8 compatible encoding, skip the author lookup,
 | 
			
		||||
  as we have no information what the author string is actually encoded in
 | 
			
		||||
- Indefero no longer displays an empty parents paragraph in the commit view for root revisions of
 | 
			
		||||
  a git repository
 | 
			
		||||
 | 
			
		||||
## Documentation
 | 
			
		||||
 | 
			
		||||
- The documentation on the setup of the monotone plugin has been improved.
 | 
			
		||||
 | 
			
		||||
## Translations
 | 
			
		||||
 | 
			
		||||
# InDefero 1.1.2 - Thu May 26 07:42:25 2011 UTC
 | 
			
		||||
 | 
			
		||||
## Bugfixes
 | 
			
		||||
 | 
			
		||||
- Fix tags extraction from git repository (issue 675)
 | 
			
		||||
- Fix SSH validation method (issue 671)
 | 
			
		||||
- Fix malformed URL in the RSS (issue 666)
 | 
			
		||||
- Fix validateRevision call for Mercurial Scm (issue 657)
 | 
			
		||||
 | 
			
		||||
## Translations
 | 
			
		||||
 | 
			
		||||
- Missing word in French translation (issue 672)
 | 
			
		||||
- Update Spanish translation
 | 
			
		||||
 | 
			
		||||
# InDefero 1.1.1 - Mon Mar 28 15:52 2011 UTC
 | 
			
		||||
 | 
			
		||||
## Bugfixes
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										57
									
								
								doc/httprepos-git.mdtext
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								doc/httprepos-git.mdtext
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
# Accessing git repositories over HTTP
 | 
			
		||||
 | 
			
		||||
Starting with indefero 1.2, git repositories are provided via http,
 | 
			
		||||
featuring read-only and read-write access with full integration with
 | 
			
		||||
indefero's access control mechanisms.
 | 
			
		||||
 | 
			
		||||
## Access git repositories
 | 
			
		||||
 | 
			
		||||
The repositories are available under http://YOURHOST/BASEPATH/r/PROJECT.
 | 
			
		||||
 | 
			
		||||
For authentication, use the "extra password" which you can find on your
 | 
			
		||||
profile page.
 | 
			
		||||
 | 
			
		||||
## Setup
 | 
			
		||||
 | 
			
		||||
The main thing to setup is git_repositories and git_remote_url in
 | 
			
		||||
src/IDF/conf/idf.php
 | 
			
		||||
 | 
			
		||||
git_remote_url should match http://YOURHOST/BASEPATH/r/%s, while
 | 
			
		||||
git_repositories points to the local directory supposed to contain the
 | 
			
		||||
repositories like /PATH/TO/REPOSITORIES/%s.git.
 | 
			
		||||
 | 
			
		||||
## Setup requirements when using PHP over FastCGI
 | 
			
		||||
 | 
			
		||||
There are a couple of things to setup when using PHP over FastCGI, as compared
 | 
			
		||||
to mod_php or similar integrated mechanisms:
 | 
			
		||||
 | 
			
		||||
- You will need to setup a RewriteRule for mod_rewrite (in case of Apache,
 | 
			
		||||
  analogous mechanisms might need to be setup for other http daemons), which
 | 
			
		||||
  passes through the Authorization HTTP Header of a request.
 | 
			
		||||
 | 
			
		||||
  In case of mod_rewrite, the necessary line is
 | 
			
		||||
  one of (depending on server configuration):
 | 
			
		||||
      RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
 | 
			
		||||
      RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization},L]
 | 
			
		||||
 | 
			
		||||
- The FastCGI adaptor must allow large requests to be handled by PHP,
 | 
			
		||||
  otherwise push might fail.
 | 
			
		||||
 | 
			
		||||
  For mod_fcgid, this is the FcgidMaxRequestLen option, or MaxRequestLen in
 | 
			
		||||
  older versions. Set this option to some large enough value - there is no
 | 
			
		||||
  issue with RAM use, as another option defines the limit after which the
 | 
			
		||||
  request is backed on disk, which can remain small.
 | 
			
		||||
 | 
			
		||||
## When migrating from syncgit
 | 
			
		||||
 | 
			
		||||
HTTP access can be used in parallel to syncgit.
 | 
			
		||||
 | 
			
		||||
If you want to disable syncgit, just undo the changes detailled
 | 
			
		||||
in doc/syncgit.mdtext:
 | 
			
		||||
- In src/IDF/conf/idf.php keep git_repositories
 | 
			
		||||
- In src/IDF/conf/idf.php adapt git_remote_url to http://host/basepath/r/%s 
 | 
			
		||||
- In src/IDF/conf/idf.php remove idf_plugin_syncgit*
 | 
			
		||||
- Remove the cronjob that called gitcron.php
 | 
			
		||||
- Disable the git daemon (eg. /etc/event.d/local-git-daemon)
 | 
			
		||||
- You can remove the git user and group, if you also adapt the git repositories
 | 
			
		||||
  to be owned by the www user.
 | 
			
		||||
@@ -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.
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
@@ -112,14 +112,40 @@ The monotone plugin can be used in several different ways:
 | 
			
		||||
            ^D
 | 
			
		||||
            $ 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
 | 
			
		||||
    `projects/`. There are two ways of setting this up:
 | 
			
		||||
 | 
			
		||||
    * Make the usher user the web user, for example via Apache's `suexec`
 | 
			
		||||
    * Use acls, like this:
 | 
			
		||||
    * Make the usher user the web user, for example via Apache's `suexec`.
 | 
			
		||||
      This is however a bit clumsy.
 | 
			
		||||
    * Preferred: Use Access Control Lists (ACLs), like this:
 | 
			
		||||
 | 
			
		||||
            #
 | 
			
		||||
            # Linux
 | 
			
		||||
            #
 | 
			
		||||
            $ setfacl -m u:www:rw usher.conf
 | 
			
		||||
            $ 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
 | 
			
		||||
   (<http://cr.yp.to/damontools.html>):
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008-2010 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
#
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008-2010 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008-2010 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008-2010 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008-2010 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -101,7 +101,7 @@ class IDF_Diff
 | 
			
		||||
                $files[$current_file]['chunks'][] = array();
 | 
			
		||||
 | 
			
		||||
                while ($i < $diffsize && ($addlines >= 0 || $dellines >= 0)) {
 | 
			
		||||
                    $linetype = $this->lines[$i] != '' ? $this->lines[$i][0] : ' ';
 | 
			
		||||
                    $linetype = $this->lines[$i] != '' ? $this->lines[$i][0] : false;
 | 
			
		||||
                    switch ($linetype) {
 | 
			
		||||
                        case ' ':
 | 
			
		||||
                            $files[$current_file]['chunks'][$current_chunk][] =
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2010 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -319,6 +319,7 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
 | 
			
		||||
                           'labels_issue_closed' => IDF_Form_IssueTrackingConf::init_closed,
 | 
			
		||||
                           'labels_issue_predefined' =>  IDF_Form_IssueTrackingConf::init_predefined,
 | 
			
		||||
                           'labels_issue_one_max' => IDF_Form_IssueTrackingConf::init_one_max,
 | 
			
		||||
                           'issue_relations' => IDF_Form_IssueTrackingConf::init_relations,
 | 
			
		||||
                           'webhook_url' => '',
 | 
			
		||||
                           'downloads_access_rights' => 'all',
 | 
			
		||||
                           'review_access_rights' => 'all',
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,19 +3,19 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2010 Céondo Ltd and contributors.
 | 
			
		||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
 | 
			
		||||
#
 | 
			
		||||
# Plume Framework is free software; you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
# the Free Software Foundation; either version 2.1 of the License, or
 | 
			
		||||
# 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.
 | 
			
		||||
#
 | 
			
		||||
# Plume Framework is distributed in the hope that it will be useful,
 | 
			
		||||
# 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 Lesser General Public License for more details.
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
# 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
 | 
			
		||||
#
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -36,6 +36,7 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
    public $user = null;
 | 
			
		||||
    public $project = null;
 | 
			
		||||
    public $show_full = false;
 | 
			
		||||
    public $relation_types = null;
 | 
			
		||||
 | 
			
		||||
    public function initFields($extra=array())
 | 
			
		||||
    {
 | 
			
		||||
@@ -45,9 +46,12 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
            or $this->user->hasPerm('IDF.project-member', $this->project)) {
 | 
			
		||||
            $this->show_full = true;
 | 
			
		||||
        }
 | 
			
		||||
        $this->relation_types = $this->project->getRelationsFromConfig();
 | 
			
		||||
 | 
			
		||||
        $contentTemplate = $this->project->getConf()->getVal(
 | 
			
		||||
            'labels_issue_template', IDF_Form_IssueTrackingConf::init_template
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $this->fields['summary'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => true,
 | 
			
		||||
                                            'label' => __('Summary'),
 | 
			
		||||
@@ -76,11 +80,11 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
        // case of someone allowing the upload path to be accessible
 | 
			
		||||
        // to everybody.
 | 
			
		||||
        for ($i=1;$i<4;$i++) {
 | 
			
		||||
            $filename = substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/'.substr($md5, 4).'/%s.dummy'; 
 | 
			
		||||
            $filename = substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/'.substr($md5, 4).'/%s.dummy';
 | 
			
		||||
            $this->fields['attachment'.$i] = new Pluf_Form_Field_File(
 | 
			
		||||
                array('required' => false,
 | 
			
		||||
                      'label' => __('Attach a file'),
 | 
			
		||||
                      'move_function_params' => 
 | 
			
		||||
                      'move_function_params' =>
 | 
			
		||||
                      array('upload_path' => $upload_path,
 | 
			
		||||
                            'upload_path_create' => true,
 | 
			
		||||
                            'file_name' => $filename,
 | 
			
		||||
@@ -109,6 +113,20 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                            ));
 | 
			
		||||
 | 
			
		||||
            $this->fields['relation_type0'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                          array('required' => false,
 | 
			
		||||
                                'label' => __('This issue'),
 | 
			
		||||
                                'initial' => current($this->relation_types),
 | 
			
		||||
                                'widget_attrs' => array('size' => 15),
 | 
			
		||||
                                ));
 | 
			
		||||
 | 
			
		||||
            $this->fields['relation_issue0'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                          array('required' => false,
 | 
			
		||||
                                'label' => null,
 | 
			
		||||
                                'initial' => '',
 | 
			
		||||
                                'widget_attrs' => array('size' => 10),
 | 
			
		||||
                                ));
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * get predefined tags for issues from current project
 | 
			
		||||
             *
 | 
			
		||||
@@ -181,7 +199,7 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
            $this->cleaned_data['label'.$i] = trim($this->cleaned_data['label'.$i]);
 | 
			
		||||
            if (strpos($this->cleaned_data['label'.$i], ':') !== false) {
 | 
			
		||||
                list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2);
 | 
			
		||||
                list($class, $name) = array(mb_strtolower(trim($class)), 
 | 
			
		||||
                list($class, $name) = array(mb_strtolower(trim($class)),
 | 
			
		||||
                                            trim($name));
 | 
			
		||||
            } else {
 | 
			
		||||
                $class = 'other';
 | 
			
		||||
@@ -215,10 +233,10 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
    function clean_status()
 | 
			
		||||
    {
 | 
			
		||||
        // Check that the status is in the list of official status
 | 
			
		||||
        $tags = $this->project->getTagsFromConfig('labels_issue_open', 
 | 
			
		||||
        $tags = $this->project->getTagsFromConfig('labels_issue_open',
 | 
			
		||||
                                          IDF_Form_IssueTrackingConf::init_open,
 | 
			
		||||
                                          'Status');
 | 
			
		||||
        $tags = array_merge($this->project->getTagsFromConfig('labels_issue_closed', 
 | 
			
		||||
        $tags = array_merge($this->project->getTagsFromConfig('labels_issue_closed',
 | 
			
		||||
                                          IDF_Form_IssueTrackingConf::init_closed,
 | 
			
		||||
                                          'Status')
 | 
			
		||||
                            , $tags);
 | 
			
		||||
@@ -235,6 +253,63 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
        return $this->cleaned_data['status'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // this method is not called from Pluf_Form directly, but shared for
 | 
			
		||||
    // among all similar fields
 | 
			
		||||
    function clean_relation_type($value)
 | 
			
		||||
    {
 | 
			
		||||
        $relation_type = trim($value);
 | 
			
		||||
        if (empty($relation_type))
 | 
			
		||||
            return '';
 | 
			
		||||
 | 
			
		||||
        $found = false;
 | 
			
		||||
        foreach ($this->relation_types as $type) {
 | 
			
		||||
            if ($type == $relation_type) {
 | 
			
		||||
                $found = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!$found) {
 | 
			
		||||
            throw new Pluf_Form_Invalid(__('You provided an invalid relation type.'));
 | 
			
		||||
        }
 | 
			
		||||
        return $relation_type;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function clean_relation_type0()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->clean_relation_type($this->cleaned_data['relation_type0']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // this method is not called from Pluf_Form directly, but shared for
 | 
			
		||||
    // among all similar fields
 | 
			
		||||
    function clean_relation_issue($value)
 | 
			
		||||
    {
 | 
			
		||||
        $issues = trim($value);
 | 
			
		||||
        if (empty($issues))
 | 
			
		||||
            return '';
 | 
			
		||||
 | 
			
		||||
        $issue_ids = preg_split('/\s*,\s*/', $issues, -1, PREG_SPLIT_NO_EMPTY);
 | 
			
		||||
        foreach ($issue_ids as $issue_id) {
 | 
			
		||||
            if (!ctype_digit($issue_id) || (int)$issue_id < 1) {
 | 
			
		||||
                throw new Pluf_Form_Invalid(sprintf(
 | 
			
		||||
                    __('The value "%s" is not a valid issue id.'), $issue_id
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
            $issue = new IDF_Issue($issue_id);
 | 
			
		||||
            if ($issue->id != $issue_id || $issue->project != $this->project->id) {
 | 
			
		||||
                throw new Pluf_Form_Invalid(sprintf(
 | 
			
		||||
                    __('The issue "%s" does not exist.'), $issue_id
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return implode(', ', $issue_ids);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function clean_relation_issue0()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->clean_relation_issue($this->cleaned_data['relation_issue0']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clean the attachments post failure.
 | 
			
		||||
     */
 | 
			
		||||
@@ -298,6 +373,30 @@ class IDF_Form_IssueCreate extends Pluf_Form
 | 
			
		||||
        foreach ($tags as $tag) {
 | 
			
		||||
            $issue->setAssoc($tag);
 | 
			
		||||
        }
 | 
			
		||||
        // add relations (if any)
 | 
			
		||||
        if (!empty($this->cleaned_data['relation_type0'])) {
 | 
			
		||||
            $verb = $this->cleaned_data['relation_type0'];
 | 
			
		||||
            $other_verb = $this->relation_types[$verb];
 | 
			
		||||
            $related_issues = preg_split('/\s*,\s*/', $this->cleaned_data['relation_issue0'], -1, PREG_SPLIT_NO_EMPTY);
 | 
			
		||||
 | 
			
		||||
            foreach ($related_issues as $related_issue_id) {
 | 
			
		||||
                $related_issue = new IDF_Issue($related_issue_id);
 | 
			
		||||
                $rel = new IDF_IssueRelation();
 | 
			
		||||
                $rel->issue = $issue;
 | 
			
		||||
                $rel->verb = $verb;
 | 
			
		||||
                $rel->other_issue = $related_issue;
 | 
			
		||||
                $rel->submitter = $this->user;
 | 
			
		||||
                $rel->create();
 | 
			
		||||
 | 
			
		||||
                $other_rel = new IDF_IssueRelation();
 | 
			
		||||
                $other_rel->issue = $related_issue;
 | 
			
		||||
                $other_rel->verb = $other_verb;
 | 
			
		||||
                $other_rel->other_issue = $issue;
 | 
			
		||||
                $other_rel->submitter = $this->user;
 | 
			
		||||
                $other_rel->create();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // add the first comment
 | 
			
		||||
        $comment = new IDF_IssueComment();
 | 
			
		||||
        $comment->issue = $issue;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -72,12 +72,29 @@ Performance          = Performance issue
 | 
			
		||||
Usability            = Affects program usability
 | 
			
		||||
Maintainability      = Hinders future changes';
 | 
			
		||||
    const init_one_max = 'Type, Priority, Milestone';
 | 
			
		||||
    // ATTENTION: if you change something here, change the values below as well!
 | 
			
		||||
    const init_relations = 'is related to
 | 
			
		||||
blocks, is blocked by
 | 
			
		||||
duplicates, is duplicated by';
 | 
			
		||||
 | 
			
		||||
    // These are actually all noop's, but we have no other chance to
 | 
			
		||||
    // tell IDF's translation mechanism to mark the strings as translatable
 | 
			
		||||
    // FIXME: IDF should get a internal translation system for strings like
 | 
			
		||||
    // that, that can also be easily expanded by users
 | 
			
		||||
    private function noop()
 | 
			
		||||
    {
 | 
			
		||||
        __('is related to');
 | 
			
		||||
        __('blocks');
 | 
			
		||||
        __('is blocked by');
 | 
			
		||||
        __('duplicates');
 | 
			
		||||
        __('is duplicated by');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function initFields($extra=array())
 | 
			
		||||
    {
 | 
			
		||||
        $this->fields['labels_issue_template'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      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,
 | 
			
		||||
                                            'widget_attrs' => array('rows' => 7,
 | 
			
		||||
                                                                    'cols' => 75),
 | 
			
		||||
@@ -114,10 +131,19 @@ Maintainability      = Hinders future changes';
 | 
			
		||||
        $this->fields['labels_issue_one_max'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => false,
 | 
			
		||||
                                            '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),
 | 
			
		||||
                                            ));
 | 
			
		||||
 | 
			
		||||
        $this->fields['issue_relations'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => true,
 | 
			
		||||
                                            'label' => __('Issue relations'),
 | 
			
		||||
                                            'initial' => self::init_relations,
 | 
			
		||||
                                            'help_text' => __('You can define bidirectional relations like "is related to" or "blocks, is blocked by".'),
 | 
			
		||||
                                            'widget_attrs' => array('rows' => 7,
 | 
			
		||||
                                                                    'cols' => 75),
 | 
			
		||||
                                            'widget' => 'Pluf_Form_Widget_TextareaInput',
 | 
			
		||||
                                            ));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -39,6 +39,7 @@ class IDF_Form_IssueUpdate  extends IDF_Form_IssueCreate
 | 
			
		||||
            or $this->user->hasPerm('IDF.project-member', $this->project)) {
 | 
			
		||||
            $this->show_full = true;
 | 
			
		||||
        }
 | 
			
		||||
        $this->relation_types = $this->project->getRelationsFromConfig();
 | 
			
		||||
        if ($this->show_full) {
 | 
			
		||||
            $this->fields['summary'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => true,
 | 
			
		||||
@@ -69,11 +70,11 @@ class IDF_Form_IssueUpdate  extends IDF_Form_IssueCreate
 | 
			
		||||
        // case of someone allowing the upload path to be accessible
 | 
			
		||||
        // to everybody.
 | 
			
		||||
        for ($i=1;$i<4;$i++) {
 | 
			
		||||
            $filename = substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/'.substr($md5, 4).'/%s.dummy'; 
 | 
			
		||||
            $filename = substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/'.substr($md5, 4).'/%s.dummy';
 | 
			
		||||
            $this->fields['attachment'.$i] = new Pluf_Form_Field_File(
 | 
			
		||||
                array('required' => false,
 | 
			
		||||
                      'label' => __('Attach a file'),
 | 
			
		||||
                      'move_function_params' => 
 | 
			
		||||
                      'move_function_params' =>
 | 
			
		||||
                      array('upload_path' => $upload_path,
 | 
			
		||||
                            'upload_path_create' => true,
 | 
			
		||||
                            'file_name' => $filename,
 | 
			
		||||
@@ -102,6 +103,52 @@ class IDF_Form_IssueUpdate  extends IDF_Form_IssueCreate
 | 
			
		||||
                                                       'size' => 15,
 | 
			
		||||
                                                                    ),
 | 
			
		||||
                                            ));
 | 
			
		||||
 | 
			
		||||
            $idx = 0;
 | 
			
		||||
            // note: clean_relation_type0 and clean_relation_issue0 already
 | 
			
		||||
            //       exist in the base class
 | 
			
		||||
            $this->fields['relation_type'.$idx] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                          array('required' => false,
 | 
			
		||||
                                'label' => __('This issue'),
 | 
			
		||||
                                'initial' => current($this->relation_types),
 | 
			
		||||
                                'widget_attrs' => array('size' => 15),
 | 
			
		||||
                                ));
 | 
			
		||||
 | 
			
		||||
            $this->fields['relation_issue'.$idx] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                          array('required' => false,
 | 
			
		||||
                                'label' => null,
 | 
			
		||||
                                'initial' => '',
 | 
			
		||||
                                'widget_attrs' => array('size' => 10),
 | 
			
		||||
                                ));
 | 
			
		||||
 | 
			
		||||
            ++$idx;
 | 
			
		||||
            $relatedIssues = $this->issue->getGroupedRelatedIssues(array(), true);
 | 
			
		||||
            foreach ($relatedIssues as $verb => $ids) {
 | 
			
		||||
                $this->fields['relation_type'.$idx] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                          array('required' => false,
 | 
			
		||||
                                'label' => __('This issue'),
 | 
			
		||||
                                'initial' => $verb,
 | 
			
		||||
                                'widget_attrs' => array('size' => 15),
 | 
			
		||||
                                ));
 | 
			
		||||
                $m = 'clean_relation_type'.$idx;
 | 
			
		||||
                $this->$m = create_function('$form', '
 | 
			
		||||
                    return $form->clean_relation_type($form->cleaned_data["relation_type'.$idx.'"]);
 | 
			
		||||
                ');
 | 
			
		||||
 | 
			
		||||
                $this->fields['relation_issue'.$idx] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                              array('required' => false,
 | 
			
		||||
                                    'label' => null,
 | 
			
		||||
                                    'initial' => implode(', ', $ids),
 | 
			
		||||
                                    'widget_attrs' => array('size' => 10),
 | 
			
		||||
                                    ));
 | 
			
		||||
                $m = 'clean_relation_issue'.$idx;
 | 
			
		||||
                $this->$m = create_function('$form', '
 | 
			
		||||
                    return $form->clean_relation_issue($form->cleaned_data["relation_issue'.$idx.'"]);
 | 
			
		||||
                ');
 | 
			
		||||
 | 
			
		||||
                ++$idx;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $tags = $this->issue->get_tags_list();
 | 
			
		||||
            for ($i=1;$i<7;$i++) {
 | 
			
		||||
                $initial = '';
 | 
			
		||||
@@ -155,6 +202,51 @@ class IDF_Form_IssueUpdate  extends IDF_Form_IssueCreate
 | 
			
		||||
    public function clean()
 | 
			
		||||
    {
 | 
			
		||||
        $this->cleaned_data = parent::clean();
 | 
			
		||||
 | 
			
		||||
        // normalize the user's input by removing dublettes and by combining
 | 
			
		||||
        // ids from identical verbs in different input fields into one array
 | 
			
		||||
        $normRelatedIssues = array();
 | 
			
		||||
        for ($idx = 0; isset($this->cleaned_data['relation_type'.$idx]); ++$idx) {
 | 
			
		||||
            $verb = $this->cleaned_data['relation_type'.$idx];
 | 
			
		||||
            if (empty($verb))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            $ids = preg_split('/\s*,\s*/', $this->cleaned_data['relation_issue'.$idx],
 | 
			
		||||
                              -1, PREG_SPLIT_NO_EMPTY);
 | 
			
		||||
            if (count($ids) == 0)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (!array_key_exists($verb, $normRelatedIssues))
 | 
			
		||||
                $normRelatedIssues[$verb] = array();
 | 
			
		||||
            foreach ($ids as $id) {
 | 
			
		||||
                if (!in_array($id, $normRelatedIssues[$verb]))
 | 
			
		||||
                    $normRelatedIssues[$verb][] = $id;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // now look at any added / removed ids
 | 
			
		||||
        $added = $removed = array();
 | 
			
		||||
        $relatedIssues = $this->issue->getGroupedRelatedIssues(array(), true);
 | 
			
		||||
        $added = array_diff_key($normRelatedIssues, $relatedIssues);
 | 
			
		||||
        $removed = array_diff_key($relatedIssues, $normRelatedIssues);
 | 
			
		||||
 | 
			
		||||
        $keysToLookAt = array_keys(
 | 
			
		||||
            array_intersect_key($relatedIssues, $normRelatedIssues)
 | 
			
		||||
        );
 | 
			
		||||
        foreach ($keysToLookAt as $key) {
 | 
			
		||||
            $a = array_diff($normRelatedIssues[$key], $relatedIssues[$key]);
 | 
			
		||||
            if (count($a) > 0)
 | 
			
		||||
                $added[$key] = $a;
 | 
			
		||||
            $r = array_diff($relatedIssues[$key], $normRelatedIssues[$key]);
 | 
			
		||||
            if (count($r) > 0)
 | 
			
		||||
                $removed[$key] = $r;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // cache the added / removed data, so we do not have to
 | 
			
		||||
        // calculate that again
 | 
			
		||||
        $this->cleaned_data['_added_issue_relations'] = $added;
 | 
			
		||||
        $this->cleaned_data['_removed_issue_relations'] = $removed;
 | 
			
		||||
 | 
			
		||||
        // As soon as we know that at least one change was done, we
 | 
			
		||||
        // return the cleaned data and do not go further.
 | 
			
		||||
        if (strlen(trim($this->cleaned_data['content']))) {
 | 
			
		||||
@@ -214,6 +306,11 @@ class IDF_Form_IssueUpdate  extends IDF_Form_IssueCreate
 | 
			
		||||
                    return $this->cleaned_data;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (count($this->cleaned_data['_added_issue_relations']) != 0 ||
 | 
			
		||||
                count($this->cleaned_data['_removed_issue_relations']) != 0) {
 | 
			
		||||
                return $this->cleaned_data;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // no changes!
 | 
			
		||||
        throw new Pluf_Form_Invalid(__('No changes were entered.'));
 | 
			
		||||
@@ -255,20 +352,22 @@ class IDF_Form_IssueUpdate  extends IDF_Form_IssueCreate
 | 
			
		||||
            foreach ($tags as $tag) {
 | 
			
		||||
                if (!Pluf_Model_InArray($tag, $oldtags)) {
 | 
			
		||||
                    if (!isset($changes['lb'])) $changes['lb'] = array();
 | 
			
		||||
                    if (!isset($changes['lb']['add'])) $changes['lb']['add'] = array();
 | 
			
		||||
                    if ($tag->class != 'Other') {
 | 
			
		||||
                        $changes['lb'][] = (string) $tag; //new tag
 | 
			
		||||
                        $changes['lb']['add'][] = (string) $tag; //new tag
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $changes['lb'][] = (string) $tag->name;
 | 
			
		||||
                        $changes['lb']['add'][] = (string) $tag->name;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            foreach ($oldtags as $tag) {
 | 
			
		||||
                if (!Pluf_Model_InArray($tag, $tags)) {
 | 
			
		||||
                    if (!isset($changes['lb'])) $changes['lb'] = array();
 | 
			
		||||
                    if (!isset($changes['lb']['rem'])) $changes['lb']['rem'] = array();
 | 
			
		||||
                    if ($tag->class != 'Other') {
 | 
			
		||||
                        $changes['lb'][] = '-'.(string) $tag; //new tag
 | 
			
		||||
                        $changes['lb']['rem'][] = (string) $tag; //new tag
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $changes['lb'][] = '-'.(string) $tag->name;
 | 
			
		||||
                        $changes['lb']['rem'][] = (string) $tag->name;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -286,6 +385,47 @@ class IDF_Form_IssueUpdate  extends IDF_Form_IssueCreate
 | 
			
		||||
                or ((!is_null($owner) and !is_null($this->issue->get_owner())) and $owner->id != $this->issue->get_owner()->id)) {
 | 
			
		||||
                $changes['ow'] = (is_null($owner)) ? '---' : $owner->login;
 | 
			
		||||
            }
 | 
			
		||||
            // Issue relations - additions
 | 
			
		||||
            foreach ($this->cleaned_data['_added_issue_relations'] as $verb => $ids) {
 | 
			
		||||
                $other_verb = $this->relation_types[$verb];
 | 
			
		||||
                foreach ($ids as $id) {
 | 
			
		||||
                    $related_issue = new IDF_Issue($id);
 | 
			
		||||
                    $rel = new IDF_IssueRelation();
 | 
			
		||||
                    $rel->issue = $this->issue;
 | 
			
		||||
                    $rel->verb = $verb;
 | 
			
		||||
                    $rel->other_issue = $related_issue;
 | 
			
		||||
                    $rel->submitter = $this->user;
 | 
			
		||||
                    $rel->create();
 | 
			
		||||
 | 
			
		||||
                    $other_rel = new IDF_IssueRelation();
 | 
			
		||||
                    $other_rel->issue = $related_issue;
 | 
			
		||||
                    $other_rel->verb = $other_verb;
 | 
			
		||||
                    $other_rel->other_issue = $this->issue;
 | 
			
		||||
                    $other_rel->submitter = $this->user;
 | 
			
		||||
                    $other_rel->create();
 | 
			
		||||
                }
 | 
			
		||||
                if (!isset($changes['rel'])) $changes['rel'] = array();
 | 
			
		||||
                if (!isset($changes['rel']['add'])) $changes['rel']['add'] = array();
 | 
			
		||||
                $changes['rel']['add'][] = $verb.' '.implode(', ', $ids);
 | 
			
		||||
            }
 | 
			
		||||
            // Issue relations - removals
 | 
			
		||||
            foreach ($this->cleaned_data['_removed_issue_relations'] as $verb => $ids) {
 | 
			
		||||
                foreach ($ids as $id) {
 | 
			
		||||
                    $db = &Pluf::db();
 | 
			
		||||
                    $table = Pluf::factory('IDF_IssueRelation')->getSqlTable();
 | 
			
		||||
                    $sql = new Pluf_SQL('verb=%s AND (
 | 
			
		||||
                                        (issue=%s AND other_issue=%s) OR
 | 
			
		||||
                                        (other_issue=%s AND issue=%s))',
 | 
			
		||||
                                        array($verb,
 | 
			
		||||
                                              $this->issue->id, $id,
 | 
			
		||||
                                              $this->issue->id, $id));
 | 
			
		||||
                    $db->execute('DELETE FROM '.$table.' WHERE '.$sql->gen());
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!isset($changes['rel'])) $changes['rel'] = array();
 | 
			
		||||
                if (!isset($changes['rel']['rem'])) $changes['rel']['rem'] = array();
 | 
			
		||||
                $changes['rel']['rem'][] = $verb.' '.implode(', ', $ids);
 | 
			
		||||
            }
 | 
			
		||||
            // Update the issue
 | 
			
		||||
            $this->issue->batchAssoc('IDF_Tag', $tagids);
 | 
			
		||||
            $this->issue->summary = trim($this->cleaned_data['summary']);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -51,68 +51,68 @@ class IDF_Form_ProjectConf extends Pluf_Form
 | 
			
		||||
                                                                                                ),
 | 
			
		||||
                                                                         'widget' => 'Pluf_Form_Widget_TextareaInput',
 | 
			
		||||
                                                                        ));
 | 
			
		||||
                                                                        
 | 
			
		||||
 | 
			
		||||
        // Logo part
 | 
			
		||||
        $upload_path = Pluf::f('upload_path', false);
 | 
			
		||||
        if (false === $upload_path) {
 | 
			
		||||
            throw new Pluf_Exception_SettingError(__('The "upload_path" configuration variable was not set.'));
 | 
			
		||||
        }
 | 
			
		||||
        $upload_path .= '/' . $this->project->shortname;
 | 
			
		||||
        $filename = '/%s'; 
 | 
			
		||||
        $filename = '/%s';
 | 
			
		||||
        $this->fields['logo'] = new Pluf_Form_Field_File(array('required' => false,
 | 
			
		||||
                                                         'label' => __('Update the logo'),
 | 
			
		||||
                                                         'initial' => '',
 | 
			
		||||
                                                         'help_text' => __('The logo must be a picture with a size of 32 by 32.'),
 | 
			
		||||
                                                         'max_size' => Pluf::f('max_upload_size', 5 * 1024),
 | 
			
		||||
                                                         'move_function_params' => 
 | 
			
		||||
                                                         'move_function_params' =>
 | 
			
		||||
                                                         array('upload_path' => $upload_path,
 | 
			
		||||
                                                               'upload_path_create' => true,
 | 
			
		||||
                                                               'file_name' => $filename,
 | 
			
		||||
                                                               )
 | 
			
		||||
                                                         ));
 | 
			
		||||
                                                         
 | 
			
		||||
 | 
			
		||||
        $this->fields['logo_remove'] = new Pluf_Form_Field_Boolean(array('required' => false,
 | 
			
		||||
                                                                         'label' => __('Remove the current logo'),
 | 
			
		||||
                                                                         'initial' => false,
 | 
			
		||||
                                                                         'widget' => 'Pluf_Form_Widget_CheckboxInput',
 | 
			
		||||
                                                                         ));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * If we have uploaded a file, but the form failed remove it.
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    function failed()
 | 
			
		||||
    {
 | 
			
		||||
        if (!empty($this->cleaned_data['logo']) 
 | 
			
		||||
        if (!empty($this->cleaned_data['logo'])
 | 
			
		||||
            && file_exists(Pluf::f('upload_path').'/'.$this->cleaned_data['logo'])) {
 | 
			
		||||
            unlink(Pluf::f('upload_path').'/'.$this->cleaned_data['logo']);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public function clean()
 | 
			
		||||
    {
 | 
			
		||||
        if (!isset($this->cleaned_data['logo_remove'])) {
 | 
			
		||||
            $this->cleaned_data['logo_remove'] = false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return $this->cleaned_data;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public function clean_logo()
 | 
			
		||||
    {
 | 
			
		||||
        if (empty($this->cleaned_data['logo'])) {
 | 
			
		||||
            return '';
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $meta = getimagesize(Pluf::f('upload_path') . '/' . $this->project->shortname . $this->cleaned_data['logo']);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if ($meta === false) {
 | 
			
		||||
            throw new Pluf_Form_Invalid("Could not determine the size of the uploaded picture.");
 | 
			
		||||
            throw new Pluf_Form_Invalid(__('Could not determine the size of the uploaded picture.'));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if ($meta[0] !== 32 || $meta[1] !== 32) {
 | 
			
		||||
            throw new Pluf_Form_Invalid("The picture must have a size of 32 by 32.");
 | 
			
		||||
            throw new Pluf_Form_Invalid(__('The picture must have a size of 32 by 32.'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->cleaned_data['logo'];
 | 
			
		||||
@@ -121,13 +121,13 @@ class IDF_Form_ProjectConf extends Pluf_Form
 | 
			
		||||
    public function save($commit=true)
 | 
			
		||||
    {
 | 
			
		||||
        $conf = $this->project->getConf();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // Basic part
 | 
			
		||||
        $this->project->name = $this->cleaned_data['name'];
 | 
			
		||||
        $this->project->shortdesc = $this->cleaned_data['shortdesc'];
 | 
			
		||||
        $this->project->description = $this->cleaned_data['description'];
 | 
			
		||||
        $this->project->update();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // Logo part
 | 
			
		||||
        if ($this->cleaned_data['logo'] !== "") {
 | 
			
		||||
            $conf->setVal('logo', $this->cleaned_data['logo']);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -52,7 +52,7 @@ class IDF_Form_ReviewFileComment extends Pluf_Form
 | 
			
		||||
                                            ));
 | 
			
		||||
        }
 | 
			
		||||
        $this->fields['content'] = new Pluf_Form_Field_Varchar(
 | 
			
		||||
                                      array('required' => true,
 | 
			
		||||
                                      array('required' => false,
 | 
			
		||||
                                            'label' => __('General comment'),
 | 
			
		||||
                                            'initial' => '',
 | 
			
		||||
                                            'widget' => 'Pluf_Form_Widget_TextareaInput',
 | 
			
		||||
@@ -96,24 +96,40 @@ class IDF_Form_ReviewFileComment extends Pluf_Form
 | 
			
		||||
     */
 | 
			
		||||
    public function clean()
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($this->files as $filename => $def) {
 | 
			
		||||
            if (!empty($this->cleaned_data[md5($filename)])) {
 | 
			
		||||
                return $this->cleaned_data;
 | 
			
		||||
        $isOk = false;
 | 
			
		||||
        
 | 
			
		||||
        foreach($this->files as $filename => $def) {
 | 
			
		||||
            $this->cleaned_data[md5($filename)] = trim($this->cleaned_data[md5($filename)]);
 | 
			
		||||
            if(!empty($this->cleaned_data[md5($filename)])) {
 | 
			
		||||
                $isOk = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        throw new Pluf_Form_Invalid(__('You need to provide comments on at least one file.'));
 | 
			
		||||
        
 | 
			
		||||
        if(!empty($this->cleaned_data['content'])) {
 | 
			
		||||
            $isOk = true;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (!$isOk) {
 | 
			
		||||
            throw new Pluf_Form_Invalid(__('You need to provide your general comment about the proposal, or comments on at least one file.'));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return $this->cleaned_data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function clean_content()
 | 
			
		||||
    {
 | 
			
		||||
        $content = trim($this->cleaned_data['content']);
 | 
			
		||||
        if (!$this->show_full and strlen($content) == 0) {
 | 
			
		||||
            throw new Pluf_Form_Invalid(__('You need to provide your general comment about the proposal.'));
 | 
			
		||||
        if(empty($content)) {
 | 
			
		||||
            if ($this->fields['status']->initial != $this->fields['status']->value) {
 | 
			
		||||
                return __('The status have been updated.');
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return $content;
 | 
			
		||||
        }
 | 
			
		||||
        return $content;
 | 
			
		||||
        
 | 
			
		||||
        throw new Pluf_Form_Invalid(__('This field is required.'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Save the model in the database.
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -317,8 +317,15 @@ class IDF_Form_UserAccount  extends Pluf_Form
 | 
			
		||||
            return '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (preg_match('#^ssh\-[a-z]{3}\s\S+(\s\S+)?$#', $key)) {
 | 
			
		||||
            $key = str_replace(array("\n", "\r"), '', $key);
 | 
			
		||||
        $keysearch = '';
 | 
			
		||||
        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)) {
 | 
			
		||||
 | 
			
		||||
@@ -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 monotone can read it, it should be valid
 | 
			
		||||
@@ -367,7 +376,7 @@ class IDF_Form_UserAccount  extends Pluf_Form
 | 
			
		||||
        if ($user) {
 | 
			
		||||
            $ruser = Pluf::factory('Pluf_User', $user);
 | 
			
		||||
            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()));
 | 
			
		||||
                if (count($keys) > 0) {
 | 
			
		||||
                    throw new Pluf_Form_Invalid(
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -169,6 +169,24 @@ class IDF_Issue extends Pluf_Model
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getGroupedRelatedIssues($opts = array(), $idsOnly = false)
 | 
			
		||||
    {
 | 
			
		||||
        $rels = $this->get_related_issues_list(array_merge($opts, array(
 | 
			
		||||
               'view' => 'with_other_issue',
 | 
			
		||||
        )));
 | 
			
		||||
 | 
			
		||||
        $res = array();
 | 
			
		||||
        foreach ($rels as $rel) {
 | 
			
		||||
            $verb = $rel->verb;
 | 
			
		||||
            if (!array_key_exists($verb, $res)) {
 | 
			
		||||
                $res[$verb] = array();
 | 
			
		||||
            }
 | 
			
		||||
            $res[$verb][] = $idsOnly ? $rel->other_issue : $rel;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $res;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns an HTML fragment used to display this issue in the
 | 
			
		||||
     * timeline.
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -155,10 +155,19 @@ class IDF_IssueComment extends Pluf_Model
 | 
			
		||||
                    $out .= __('Owner:'); break;
 | 
			
		||||
                case 'lb':
 | 
			
		||||
                    $out .= __('Labels:'); break;
 | 
			
		||||
                case 'rel':
 | 
			
		||||
                    $out .= __('Relations:'); break;
 | 
			
		||||
                }
 | 
			
		||||
                $out .= '</strong> ';
 | 
			
		||||
                if ($w == 'lb') {
 | 
			
		||||
                    $out .= Pluf_esc(implode(', ', $v));
 | 
			
		||||
                if ($w == 'lb' || $w == 'rel') {
 | 
			
		||||
                    foreach ($v as $t => $ls) {
 | 
			
		||||
                        foreach ($ls as $l) {
 | 
			
		||||
                            if ($t == 'rem') $out .= '<s>';
 | 
			
		||||
                            $out .= Pluf_esc($l);
 | 
			
		||||
                            if ($t == 'rem') $out .= '</s>';
 | 
			
		||||
                            $out .= ' ';
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    $out .= Pluf_esc($v);
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										100
									
								
								src/IDF/IssueRelation.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/IDF/IssueRelation.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
<?php
 | 
			
		||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
 | 
			
		||||
#
 | 
			
		||||
# InDefero is free software; you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# InDefero is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program; if not, write to the Free Software
 | 
			
		||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
#
 | 
			
		||||
# ***** END LICENSE BLOCK ***** */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A relation of one issue to another
 | 
			
		||||
 */
 | 
			
		||||
class IDF_IssueRelation extends Pluf_Model
 | 
			
		||||
{
 | 
			
		||||
    public $_model = __CLASS__;
 | 
			
		||||
 | 
			
		||||
    function init()
 | 
			
		||||
    {
 | 
			
		||||
        $this->_a['table'] = 'idf_issuerelations';
 | 
			
		||||
        $this->_a['model'] = __CLASS__;
 | 
			
		||||
        $this->_a['cols'] = array(
 | 
			
		||||
                             // It is mandatory to have an "id" column.
 | 
			
		||||
                            'id' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Sequence',
 | 
			
		||||
                                  'blank' => true,
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'issue' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Foreignkey',
 | 
			
		||||
                                  'model' => 'IDF_Issue',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('issue'),
 | 
			
		||||
                                  'relate_name' => 'related_issues',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'verb' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Text',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('verb'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'other_issue' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Foreignkey',
 | 
			
		||||
                                  'model' => 'IDF_Issue',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('other issue'),
 | 
			
		||||
                                  'relate_name' => 'related_other_issues',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            'submitter' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Foreignkey',
 | 
			
		||||
                                  'model' => 'Pluf_User',
 | 
			
		||||
                                  'blank' => false,
 | 
			
		||||
                                  'verbose' => __('submitter'),
 | 
			
		||||
                                   ),
 | 
			
		||||
                            'creation_dtime' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'type' => 'Pluf_DB_Field_Datetime',
 | 
			
		||||
                                  'blank' => true,
 | 
			
		||||
                                  'verbose' => __('creation date'),
 | 
			
		||||
                                  ),
 | 
			
		||||
                            );
 | 
			
		||||
        $this->_a['idx'] = array(
 | 
			
		||||
                            'creation_dtime_idx' =>
 | 
			
		||||
                            array(
 | 
			
		||||
                                  'col' => 'creation_dtime',
 | 
			
		||||
                                  'type' => 'normal',
 | 
			
		||||
                                  ),
 | 
			
		||||
                            );
 | 
			
		||||
        $issuetbl = $this->_con->pfx.'idf_issues';
 | 
			
		||||
        $this->_a['views'] = array(
 | 
			
		||||
            'with_other_issue' => array(
 | 
			
		||||
                'join' => 'INNER JOIN '.$issuetbl.' ON other_issue='.$issuetbl.'.id',
 | 
			
		||||
                'select' => $this->getSelect().', summary',
 | 
			
		||||
                'props' => array('summary' => 'other_summary'),
 | 
			
		||||
        ));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function preSave($create=false)
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->id == '') {
 | 
			
		||||
            $this->creation_dtime = gmdate('Y-m-d H:i:s');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -80,7 +80,10 @@ class IDF_Key extends Pluf_Model
 | 
			
		||||
        if (preg_match('#^\[pubkey ([^\]]+)\]\s*(\S+)\s*\[end\]$#', $this->content, $m)) {
 | 
			
		||||
            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])) {
 | 
			
		||||
                $m[2] = "";
 | 
			
		||||
            }
 | 
			
		||||
            return array('ssh', $m[2], $m[1]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -44,7 +44,7 @@ class IDF_Middleware
 | 
			
		||||
    function process_request(&$request)
 | 
			
		||||
    {
 | 
			
		||||
        $match = array();
 | 
			
		||||
        if (preg_match('#^/(?:api/p|p)/([\-\w]+)/?#', $request->query, $match)) {
 | 
			
		||||
        if (preg_match('#^/(?:api/p|p|r)/([\-\w]+)/?#', $request->query, $match)) {
 | 
			
		||||
            try {
 | 
			
		||||
                $request->project = IDF_Project::getOr404($match[1]);
 | 
			
		||||
            } catch (Pluf_HTTP_Error404 $e) {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								src/IDF/Migrations/17AddIssueRelations.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/IDF/Migrations/17AddIssueRelations.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 ***** */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add the new IDF_IssueRelation model.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
function IDF_Migrations_17AddIssueRelations_up($params=null)
 | 
			
		||||
{
 | 
			
		||||
    $db = Pluf::db();
 | 
			
		||||
    $schema = new Pluf_DB_Schema($db);
 | 
			
		||||
    $schema->model = new IDF_IssueRelation();
 | 
			
		||||
    $schema->createTables();
 | 
			
		||||
 | 
			
		||||
    // change the serialization format for added / removed labels in IDF_IssueComment
 | 
			
		||||
    $comments = Pluf::factory('IDF_IssueComment')->getList();
 | 
			
		||||
    foreach ($comments as $comment) {
 | 
			
		||||
        if (!isset($comment->changes['lb'])) continue;
 | 
			
		||||
        $changes = $comment->changes;
 | 
			
		||||
        $adds = $removals = array();
 | 
			
		||||
        foreach ($comment->changes['lb'] as $lb) {
 | 
			
		||||
            if (substr($lb, 0, 1) == '-')
 | 
			
		||||
                $removals[] = substr($lb, 1);
 | 
			
		||||
            else
 | 
			
		||||
                $adds[] = $lb;
 | 
			
		||||
        }
 | 
			
		||||
        $changes['lb'] = array();
 | 
			
		||||
        if (count($adds) > 0)
 | 
			
		||||
            $changes['lb']['add'] = $adds;
 | 
			
		||||
        if (count($removals) > 0)
 | 
			
		||||
            $changes['lb']['rem'] = $removals;
 | 
			
		||||
        $comment->changes = $changes;
 | 
			
		||||
        $comment->update();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function IDF_Migrations_17AddIssueRelations_down($params=null)
 | 
			
		||||
{
 | 
			
		||||
    $db = Pluf::db();
 | 
			
		||||
    $schema = new Pluf_DB_Schema($db);
 | 
			
		||||
    $schema->model = new IDF_IssueRelation();
 | 
			
		||||
    $schema->dropTables();
 | 
			
		||||
 | 
			
		||||
    // change the serialization format for added / removed labels in IDF_IssueComment
 | 
			
		||||
    $comments = Pluf::factory('IDF_IssueComment')->getList();
 | 
			
		||||
    foreach ($comments as $comment) {
 | 
			
		||||
        $changes = $comment->changes;
 | 
			
		||||
        if (empty($changes))
 | 
			
		||||
            continue;
 | 
			
		||||
        if (isset($changes['lb'])) {
 | 
			
		||||
            $labels = array();
 | 
			
		||||
            foreach ($changes['lb'] as $type => $lbs) {
 | 
			
		||||
                if (!is_array($lbs)) {
 | 
			
		||||
                    $labels[] = $lbs;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                foreach ($lbs as $lb) {
 | 
			
		||||
                    $labels[] = ($type == 'rem' ? '-' : '') . $lb;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $changes['lb'] = $labels;
 | 
			
		||||
        }
 | 
			
		||||
        // while we're at it, remove any 'rel' changes
 | 
			
		||||
        unset($changes['rel']);
 | 
			
		||||
        $comment->changes = $changes;
 | 
			
		||||
        $comment->update();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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]);
 | 
			
		||||
}
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -54,6 +54,7 @@ function IDF_Migrations_Backup_run($folder, $name=null)
 | 
			
		||||
                    'IDF_Queue',
 | 
			
		||||
                    'IDF_Gconf',
 | 
			
		||||
                    'IDF_EmailAddress',
 | 
			
		||||
                    'IDF_IssueRelation',
 | 
			
		||||
                    );
 | 
			
		||||
    $db = Pluf::db();
 | 
			
		||||
    // Now, for each table, we dump the content in json, this is a
 | 
			
		||||
@@ -100,6 +101,7 @@ function IDF_Migrations_Backup_restore($folder, $name)
 | 
			
		||||
                    'IDF_Queue',
 | 
			
		||||
                    'IDF_Gconf',
 | 
			
		||||
                    'IDF_EmailAddress',
 | 
			
		||||
                    'IDF_IssueRelation',
 | 
			
		||||
                    );
 | 
			
		||||
    $db = Pluf::db();
 | 
			
		||||
    $schema = new Pluf_DB_Schema($db);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -51,6 +51,7 @@ function IDF_Migrations_Install_setup($params=null)
 | 
			
		||||
                    'IDF_Queue',
 | 
			
		||||
                    'IDF_Gconf',
 | 
			
		||||
                    'IDF_EmailAddress',
 | 
			
		||||
                    'IDF_IssueRelation',
 | 
			
		||||
                    );
 | 
			
		||||
    $db = Pluf::db();
 | 
			
		||||
    $schema = new Pluf_DB_Schema($db);
 | 
			
		||||
@@ -109,6 +110,7 @@ function IDF_Migrations_Install_teardown($params=null)
 | 
			
		||||
                    'IDF_Commit',
 | 
			
		||||
                    'IDF_Project',
 | 
			
		||||
                    'IDF_EmailAddress',
 | 
			
		||||
                    'IDF_IssueRelation',
 | 
			
		||||
                    );
 | 
			
		||||
    $db = Pluf::db();
 | 
			
		||||
    $schema = new Pluf_DB_Schema($db);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -59,6 +59,16 @@ class IDF_Plugin_SyncGit_Cron
 | 
			
		||||
                $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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2010 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -27,6 +27,18 @@
 | 
			
		||||
 */
 | 
			
		||||
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.
 | 
			
		||||
     */
 | 
			
		||||
@@ -80,24 +92,33 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            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);
 | 
			
		||||
        if ($projecttempl === false) {
 | 
			
		||||
            throw new IDF_Scm_Exception(
 | 
			
		||||
                 __('"mtn_repositories" must be defined in your configuration file.')
 | 
			
		||||
            $this->_diagnoseProblem(
 | 
			
		||||
                 __('"mtn_repositories" must be defined in your configuration file')
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $usher_config = Pluf::f('mtn_usher_conf', false);
 | 
			
		||||
        if (!$usher_config || !is_writable($usher_config)) {
 | 
			
		||||
            throw new IDF_Scm_Exception(
 | 
			
		||||
                 __('"mtn_usher_conf" does not exist or is not writable.')
 | 
			
		||||
            $this->_diagnoseProblem(
 | 
			
		||||
                 __('"mtn_usher_conf" does not exist or is not writable')
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $mtnpostpush = realpath(dirname(__FILE__) . '/../../../scripts/mtn-post-push');
 | 
			
		||||
        if (!file_exists($mtnpostpush)) {
 | 
			
		||||
            throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                __('Could not find mtn-post-push script "%s".'), $mtnpostpush
 | 
			
		||||
            $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                __('Could not find mtn-post-push script "%s"'), $mtnpostpush
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -110,13 +131,12 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
             'monotonerc.in',
 | 
			
		||||
             'remote-automate-permissions.in',
 | 
			
		||||
             '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_post_push.conf.in',
 | 
			
		||||
             'hooks.d/indefero_post_push.lua',
 | 
			
		||||
        );
 | 
			
		||||
        // enable remote command execution of read-only commands
 | 
			
		||||
        // only for public projects
 | 
			
		||||
        if (!$project->private) {
 | 
			
		||||
            // this is linked and not copied to be able to update
 | 
			
		||||
            // the list of read-only commands on upgrades
 | 
			
		||||
@@ -131,8 +151,8 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
        }
 | 
			
		||||
        foreach ($confdir_contents as $content) {
 | 
			
		||||
            if (!file_exists($confdir.$content)) {
 | 
			
		||||
                throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                    __('The configuration file %s is missing.'), $content
 | 
			
		||||
                $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                    __('The configuration file "%s" is missing'), $content
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -140,14 +160,15 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
        $shortname = $project->shortname;
 | 
			
		||||
        $projectpath = sprintf($projecttempl, $shortname);
 | 
			
		||||
        if (file_exists($projectpath)) {
 | 
			
		||||
            throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                __('The project path %s already exists.'), $projectpath
 | 
			
		||||
            $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                __('The project path "%s" already exists'), $projectpath
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!mkdir($projectpath)) {
 | 
			
		||||
            throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                __('The project path %s could not be created.'), $projectpath
 | 
			
		||||
        if (!@mkdir($projectpath)) {
 | 
			
		||||
            $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                __('The project path "%s" could not be created'),
 | 
			
		||||
                $projectpath
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -156,7 +177,7 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
        //
 | 
			
		||||
        $dbfile = $projectpath.'/database.mtn';
 | 
			
		||||
        $cmd = sprintf('db init -d %s', escapeshellarg($dbfile));
 | 
			
		||||
        self::_mtn_exec($cmd);
 | 
			
		||||
        $this->_mtn_exec($cmd);
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        // step 2) create a server key
 | 
			
		||||
@@ -175,16 +196,17 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            escapeshellarg($projectpath),
 | 
			
		||||
            escapeshellarg($serverkey)
 | 
			
		||||
        );
 | 
			
		||||
        self::_mtn_exec($cmd);
 | 
			
		||||
        $this->_mtn_exec($cmd);
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        // step 3) create a client key, and save it in IDF
 | 
			
		||||
        //
 | 
			
		||||
        $keydir = Pluf::f('tmp_folder').'/mtn-client-keys';
 | 
			
		||||
        if (!file_exists($keydir)) {
 | 
			
		||||
            if (!mkdir($keydir)) {
 | 
			
		||||
                throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                    __('The key directory %s could not be created.'), $keydir
 | 
			
		||||
            if (!@mkdir($keydir)) {
 | 
			
		||||
                $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                    __('The key directory "%s" could not be created'),
 | 
			
		||||
                    $keydir
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -194,14 +216,14 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            escapeshellarg($keydir),
 | 
			
		||||
            escapeshellarg($clientkey_name)
 | 
			
		||||
        );
 | 
			
		||||
        $keyinfo = self::_mtn_exec($cmd);
 | 
			
		||||
        $keyinfo = $this->_mtn_exec($cmd);
 | 
			
		||||
 | 
			
		||||
        $parsed_keyinfo = array();
 | 
			
		||||
        try {
 | 
			
		||||
            $parsed_keyinfo = IDF_Scm_Monotone_BasicIO::parse($keyinfo);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception $e) {
 | 
			
		||||
            throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
            $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                __('Could not parse key information: %s'), $e->getMessage()
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
@@ -219,13 +241,13 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            escapeshellarg($keydir),
 | 
			
		||||
            escapeshellarg($clientkey_hash)
 | 
			
		||||
        );
 | 
			
		||||
        $clientkey_pubdata = self::_mtn_exec($cmd);
 | 
			
		||||
        $clientkey_pubdata = $this->_mtn_exec($cmd);
 | 
			
		||||
 | 
			
		||||
        $cmd = sprintf('au put_public_key --db=%s %s',
 | 
			
		||||
            escapeshellarg($dbfile),
 | 
			
		||||
            escapeshellarg($clientkey_pubdata)
 | 
			
		||||
        );
 | 
			
		||||
        self::_mtn_exec($cmd);
 | 
			
		||||
        $this->_mtn_exec($cmd);
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        // step 4) setup the configuration
 | 
			
		||||
@@ -238,18 +260,20 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
        foreach ($confdir_contents as $content) {
 | 
			
		||||
            $filepath = $projectpath.'/'.$content;
 | 
			
		||||
            if (substr($content, -1) == '/') {
 | 
			
		||||
                if (!mkdir($filepath)) {
 | 
			
		||||
                    throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                        __('Could not create configuration directory "%s"'), $filepath
 | 
			
		||||
                if (!@mkdir($filepath)) {
 | 
			
		||||
                    $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                        __('Could not create configuration directory "%s"'),
 | 
			
		||||
                        $filepath
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (substr($content, -3) != '.in') {
 | 
			
		||||
                if (!symlink($confdir.$content, $filepath)) {
 | 
			
		||||
                    IDF_Scm_Exception(sprintf(
 | 
			
		||||
                        __('Could not create symlink "%s"'), $filepath
 | 
			
		||||
                if (!@symlink($confdir.$content, $filepath)) {
 | 
			
		||||
                    $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                        __('Could not create symlink for configuration file "%s"'),
 | 
			
		||||
                        $filepath
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
                continue;
 | 
			
		||||
@@ -264,9 +288,10 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
 | 
			
		||||
            // remove the .in
 | 
			
		||||
            $filepath = substr($filepath, 0, -3);
 | 
			
		||||
            if (file_put_contents($filepath, $filecontents, LOCK_EX) === false) {
 | 
			
		||||
                throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                    __('Could not write configuration file "%s"'), $filepath
 | 
			
		||||
            if (@file_put_contents($filepath, $filecontents, LOCK_EX) === false) {
 | 
			
		||||
                $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                    __('Could not write configuration file "%s"'),
 | 
			
		||||
                    $filepath
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -280,7 +305,7 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            $parsed_config = IDF_Scm_Monotone_BasicIO::parse($usher_rc);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception $e) {
 | 
			
		||||
            throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
            $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                __('Could not parse usher configuration in "%s": %s'),
 | 
			
		||||
                $usher_config, $e->getMessage()
 | 
			
		||||
            ));
 | 
			
		||||
@@ -291,7 +316,7 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            foreach ($stanzas as $stanza_line) {
 | 
			
		||||
                if ($stanza_line['key'] == 'server' &&
 | 
			
		||||
                    $stanza_line['values'][0] == $shortname) {
 | 
			
		||||
                    throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                    $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                        __('usher configuration already contains a server '.
 | 
			
		||||
                           'entry named "%s"'),
 | 
			
		||||
                        $shortname
 | 
			
		||||
@@ -315,9 +340,10 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
 | 
			
		||||
        // FIXME: more sanity - what happens on failing writes? we do not
 | 
			
		||||
        // have a backup copy of usher.conf around...
 | 
			
		||||
        if (file_put_contents($usher_config, $usher_rc, LOCK_EX) === false) {
 | 
			
		||||
            throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                __('Could not write usher configuration file "%s"'), $usher_config
 | 
			
		||||
        if (@file_put_contents($usher_config, $usher_rc, LOCK_EX) === false) {
 | 
			
		||||
            $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                __('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
 | 
			
		||||
        //
 | 
			
		||||
        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);
 | 
			
		||||
        $stdio = $mtn->getStdio();
 | 
			
		||||
 | 
			
		||||
        $projectpath = self::_get_project_path($project);
 | 
			
		||||
        $auth_ids    = self::_get_authorized_user_ids($project);
 | 
			
		||||
        $projectpath = $this->_get_project_path($project);
 | 
			
		||||
        $auth_ids    = $this->_get_authorized_user_ids($project);
 | 
			
		||||
        $key_ids     = array();
 | 
			
		||||
        foreach ($auth_ids as $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);
 | 
			
		||||
        $rcfile = $projectpath.'/write-permissions';
 | 
			
		||||
        if (file_put_contents($rcfile, $write_permissions, LOCK_EX) === false) {
 | 
			
		||||
            throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                __('Could not write write-permissions file "%s"'), $rcfile
 | 
			
		||||
        if (@file_put_contents($rcfile, $write_permissions, LOCK_EX) === false) {
 | 
			
		||||
            $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                __('Could not write write-permissions file "%s"'),
 | 
			
		||||
                $rcfile
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -382,11 +412,13 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
                array('key' => 'allow', 'values' => array('*')),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $read_permissions = IDF_Scm_Monotone_BasicIO::compile(array($stanza));
 | 
			
		||||
        $rcfile = $projectpath.'/read-permissions';
 | 
			
		||||
        if (file_put_contents($rcfile, $read_permissions, LOCK_EX) === false) {
 | 
			
		||||
            throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                __('Could not write read-permissions file "%s"'), $rcfile
 | 
			
		||||
        if (@file_put_contents($rcfile, $read_permissions, LOCK_EX) === false) {
 | 
			
		||||
            $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                __('Could not write read-permissions file "%s"'),
 | 
			
		||||
                $rcfile
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -401,16 +433,16 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
 | 
			
		||||
        $serverRestartRequired = false;
 | 
			
		||||
        if ($project->private && file_exists($projectfile) && is_link($projectfile)) {
 | 
			
		||||
            if (!unlink($projectfile)) {
 | 
			
		||||
                IDF_Scm_Exception(sprintf(
 | 
			
		||||
            if (!@unlink($projectfile)) {
 | 
			
		||||
                $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                    __('Could not remove symlink "%s"'), $projectfile
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
            $serverRestartRequired = true;
 | 
			
		||||
        } else
 | 
			
		||||
        if (!$project->private && !file_exists($projectfile)) {
 | 
			
		||||
            if (!symlink($templatefile, $projectfile)) {
 | 
			
		||||
                throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
            if (!@symlink($templatefile, $projectfile)) {
 | 
			
		||||
                $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                    __('Could not create symlink "%s"'), $projectfile
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
@@ -422,6 +454,9 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            // seems to be ignored when the server should be started
 | 
			
		||||
            // again immediately afterwards
 | 
			
		||||
            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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -443,8 +478,8 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
 | 
			
		||||
        $usher_config = Pluf::f('mtn_usher_conf', false);
 | 
			
		||||
        if (!$usher_config || !is_writable($usher_config)) {
 | 
			
		||||
            throw new IDF_Scm_Exception(
 | 
			
		||||
                 __('"mtn_usher_conf" does not exist or is not writable.')
 | 
			
		||||
            $this->_diagnoseProblem(
 | 
			
		||||
                 __('"mtn_usher_conf" does not exist or is not writable')
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -453,16 +488,16 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
 | 
			
		||||
        $projecttempl = Pluf::f('mtn_repositories', false);
 | 
			
		||||
        if ($projecttempl === false) {
 | 
			
		||||
            throw new IDF_Scm_Exception(
 | 
			
		||||
                 __('"mtn_repositories" must be defined in your configuration file.')
 | 
			
		||||
            $this->_diagnoseProblem(
 | 
			
		||||
                 __('"mtn_repositories" must be defined in your configuration file')
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $projectpath = sprintf($projecttempl, $shortname);
 | 
			
		||||
        if (file_exists($projectpath)) {
 | 
			
		||||
            if (!self::_delete_recursive($projectpath)) {
 | 
			
		||||
                throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                    __('One or more paths underknees %s could not be deleted.'), $projectpath
 | 
			
		||||
            if (!$this->_delete_recursive($projectpath)) {
 | 
			
		||||
                $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                    __('One or more paths underneath %s could not be deleted'), $projectpath
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -473,8 +508,9 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
        if ($keyname && $keyhash &&
 | 
			
		||||
            file_exists($keydir .'/'. $keyname . '.' . $keyhash)) {
 | 
			
		||||
            if (!@unlink($keydir .'/'. $keyname . '.' . $keyhash)) {
 | 
			
		||||
                throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                    __('Could not delete client private key %s'), $keyname
 | 
			
		||||
                $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                    __('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);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception $e) {
 | 
			
		||||
            throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
            $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                __('Could not parse usher configuration in "%s": %s'),
 | 
			
		||||
                $usher_config, $e->getMessage()
 | 
			
		||||
            ));
 | 
			
		||||
@@ -505,9 +541,10 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
 | 
			
		||||
        // FIXME: more sanity - what happens on failing writes? we do not
 | 
			
		||||
        // have a backup copy of usher.conf around...
 | 
			
		||||
        if (file_put_contents($usher_config, $usher_rc, LOCK_EX) === false) {
 | 
			
		||||
            throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                __('Could not write usher configuration file "%s"'), $usher_config
 | 
			
		||||
        if (@file_put_contents($usher_config, $usher_rc, LOCK_EX) === false) {
 | 
			
		||||
            $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                __('Could not write usher configuration file "%s"'),
 | 
			
		||||
                $usher_config
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -528,6 +565,8 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $keyGuard = new IDF_Plugin_SyncMonotone_ModelGuard($key);
 | 
			
		||||
 | 
			
		||||
        foreach (Pluf::factory('IDF_Project')->getList() as $project) {
 | 
			
		||||
            $conf = new IDF_Conf();
 | 
			
		||||
            $conf->setProject($project);
 | 
			
		||||
@@ -535,8 +574,8 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            if ($scm != 'mtn')
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            $projectpath = self::_get_project_path($project);
 | 
			
		||||
            $auth_ids    = self::_get_authorized_user_ids($project);
 | 
			
		||||
            $projectpath = $this->_get_project_path($project);
 | 
			
		||||
            $auth_ids    = $this->_get_authorized_user_ids($project);
 | 
			
		||||
            if (!in_array($key->user, $auth_ids))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
@@ -556,7 +595,7 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
                    $parsed_read_perms = IDF_Scm_Monotone_BasicIO::parse($read_perms);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception $e) {
 | 
			
		||||
                    throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                    $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                        __('Could not parse read-permissions for project "%s": %s'),
 | 
			
		||||
                        $shortname, $e->getMessage()
 | 
			
		||||
                    ));
 | 
			
		||||
@@ -598,10 +637,11 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
 | 
			
		||||
                $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) {
 | 
			
		||||
                    throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                        __('Could not write read-permissions for project "%s"'), $shortname
 | 
			
		||||
                    $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                        __('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)) {
 | 
			
		||||
                $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) {
 | 
			
		||||
                throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                    __('Could not write write-permissions file for project "%s"'),
 | 
			
		||||
                    $shortname
 | 
			
		||||
                ));
 | 
			
		||||
@@ -623,6 +663,8 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            $stdio = $mtn->getStdio();
 | 
			
		||||
            $stdio->exec(array('put_public_key', $key->content));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $keyGuard->commit();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -651,8 +693,8 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            if ($scm != 'mtn')
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            $projectpath = self::_get_project_path($project);
 | 
			
		||||
            $auth_ids    = self::_get_authorized_user_ids($project);
 | 
			
		||||
            $projectpath = $this->_get_project_path($project);
 | 
			
		||||
            $auth_ids    = $this->_get_authorized_user_ids($project);
 | 
			
		||||
            if (!in_array($key->user, $auth_ids))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
@@ -672,7 +714,7 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
                    $parsed_read_perms = IDF_Scm_Monotone_BasicIO::parse($read_perms);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception $e) {
 | 
			
		||||
                    throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                    $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                        __('Could not parse read-permissions for project "%s": %s'),
 | 
			
		||||
                        $shortname, $e->getMessage()
 | 
			
		||||
                    ));
 | 
			
		||||
@@ -693,10 +735,11 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
 | 
			
		||||
                $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) {
 | 
			
		||||
                    throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
                        __('Could not write read-permissions for project "%s"'), $shortname
 | 
			
		||||
                    $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                        __('Could not write read-permissions for project "%s"'),
 | 
			
		||||
                        $shortname
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -711,9 +754,9 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (file_put_contents($projectpath.'/write-permissions',
 | 
			
		||||
                                  implode("\n", $lines) . "\n", LOCK_EX) === false) {
 | 
			
		||||
                throw new IDF_Scm_Exception(sprintf(
 | 
			
		||||
            if (@file_put_contents($projectpath.'/write-permissions',
 | 
			
		||||
                                   implode("\n", $lines) . "\n", LOCK_EX) === false) {
 | 
			
		||||
                $this->_diagnoseProblem(sprintf(
 | 
			
		||||
                    __('Could not write write-permissions file for project "%s"'),
 | 
			
		||||
                    $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();
 | 
			
		||||
        $members = array_merge((array)$mem['members'],
 | 
			
		||||
@@ -775,43 +854,7 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
        return $userids;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static function _get_project_path($project)
 | 
			
		||||
    {
 | 
			
		||||
        $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)
 | 
			
		||||
    private function _delete_recursive($path)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_file($path) || is_link($path)) {
 | 
			
		||||
            return @unlink($path);
 | 
			
		||||
@@ -821,10 +864,48 @@ class IDF_Plugin_SyncMonotone
 | 
			
		||||
            $scan = glob(rtrim($path, '/') . '/*');
 | 
			
		||||
            $status = 0;
 | 
			
		||||
            foreach ($scan as $subpath) {
 | 
			
		||||
                $status |= self::_delete_recursive($subpath);
 | 
			
		||||
                $status |= $this->_delete_recursive($subpath);
 | 
			
		||||
            }
 | 
			
		||||
            $status |= rmdir($path);
 | 
			
		||||
            $status |= @rmdir($path);
 | 
			
		||||
            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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
-- ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
-- This file is part of InDefero, an open source project management application.
 | 
			
		||||
-- Copyright (C) 2011 Céondo Ltd and contributors.
 | 
			
		||||
-- Copyright (C) 2008-2011 Céondo Ltd and contributors.
 | 
			
		||||
-- Copyright (C) 2010 Thomas Keller <me@thomaskeller.biz>
 | 
			
		||||
--                    Richard Levitte <richard@levitte.org>
 | 
			
		||||
--
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
-- ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
-- This file is part of InDefero, an open source project management application.
 | 
			
		||||
-- Copyright (C) 2011 Céondo Ltd and contributors.
 | 
			
		||||
-- 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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
-- ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
-- This file is part of InDefero, an open source project management application.
 | 
			
		||||
-- Copyright (C) 2011 Céondo Ltd and contributors.
 | 
			
		||||
-- 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -132,6 +132,46 @@ class IDF_Project extends Pluf_Model
 | 
			
		||||
        }
 | 
			
		||||
        return $projects[0];
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the number of open/closed issues.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string Status ('open'), 'closed'
 | 
			
		||||
     * @param IDF_Tag Subfilter with a label (null)
 | 
			
		||||
     * @return int Count
 | 
			
		||||
     */
 | 
			
		||||
    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.
 | 
			
		||||
@@ -233,6 +273,29 @@ class IDF_Project extends Pluf_Model
 | 
			
		||||
        return $tags;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns a list of relations which are available in this project as
 | 
			
		||||
     * associative array. Each key-value pair marks a set of orthogonal
 | 
			
		||||
     * relations. To ease processing, each of these pairs is included twice
 | 
			
		||||
     * in the array, once as key1 => key2 and once as key2 => key1.
 | 
			
		||||
     *
 | 
			
		||||
     * @return array List of relation names
 | 
			
		||||
     */
 | 
			
		||||
    public function getRelationsFromConfig()
 | 
			
		||||
    {
 | 
			
		||||
        $conf = $this->getConf();
 | 
			
		||||
        $rel = $conf->getVal('issue_relations', IDF_Form_IssueTrackingConf::init_relations);
 | 
			
		||||
        $relations = array();
 | 
			
		||||
        foreach (preg_split("/\015\012|\015|\012/", $rel, -1, PREG_SPLIT_NO_EMPTY) as $s) {
 | 
			
		||||
            $verbs = preg_split("/\s*,\s*/", $s, 2);
 | 
			
		||||
            if (count($verbs) == 1)
 | 
			
		||||
                $relations += array($verbs[0] => $verbs[0]);
 | 
			
		||||
            else
 | 
			
		||||
                $relations += array($verbs[0] => $verbs[1], $verbs[1] => $verbs[0]);
 | 
			
		||||
        }
 | 
			
		||||
        return $relations;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return membership data.
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -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 $cmd Command to run
 | 
			
		||||
     * @param &$out Array of output
 | 
			
		||||
     * @param &$return Return value
 | 
			
		||||
     * @return string Last line of the command
 | 
			
		||||
     */
 | 
			
		||||
    public static function exec($caller, $cmd, &$out=null, &$return=null)
 | 
			
		||||
    {
 | 
			
		||||
        $return = -1;
 | 
			
		||||
        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::debug(array($caller, $cmd, $out));
 | 
			
		||||
        Pluf_Log::inc('exec_calls');
 | 
			
		||||
        return $ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -325,7 +339,8 @@ class IDF_Scm
 | 
			
		||||
     * stdClass object {
 | 
			
		||||
     *  'additions' => 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', ...),
 | 
			
		||||
     *  'properties' => array('path/to/file' => array(
 | 
			
		||||
     *              'propname' => 'propvalue', 'deletedprop' => null, ...)
 | 
			
		||||
@@ -482,5 +497,10 @@ class IDF_Scm
 | 
			
		||||
    {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function repository($request, $match)
 | 
			
		||||
    {
 | 
			
		||||
        throw new Exception('This repository does not support web based repository access');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -67,6 +67,7 @@ class IDF_Scm_Git extends IDF_Scm
 | 
			
		||||
            'additions'  => array(),
 | 
			
		||||
            'deletions'  => array(),
 | 
			
		||||
            'renames'    => array(),
 | 
			
		||||
            'copies'     => array(),
 | 
			
		||||
            'patches'    => array(),
 | 
			
		||||
            'properties' => array(),
 | 
			
		||||
        );
 | 
			
		||||
@@ -209,7 +210,7 @@ class IDF_Scm_Git extends IDF_Scm
 | 
			
		||||
            return $this->cache['tags'];
 | 
			
		||||
        }
 | 
			
		||||
        $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),
 | 
			
		||||
                     Pluf::f('git_path', 'git'));
 | 
			
		||||
        self::exec('IDF_Scm_Git::getTags', $cmd, $out, $return);
 | 
			
		||||
@@ -218,18 +219,15 @@ class IDF_Scm_Git extends IDF_Scm
 | 
			
		||||
                                                $cmd, $return,
 | 
			
		||||
                                                implode("\n", $out)));
 | 
			
		||||
        }
 | 
			
		||||
        rsort($out);
 | 
			
		||||
        $res = array();
 | 
			
		||||
        foreach ($out as $b) {
 | 
			
		||||
            $elts = explode(' ', $b, 5);
 | 
			
		||||
            $tag = substr(trim($elts[4]), 10);
 | 
			
		||||
            if (false !== strpos($tag, '/')) {
 | 
			
		||||
                $res[$elts[3]] = $b;
 | 
			
		||||
            } else {
 | 
			
		||||
                $res[$tag] = '';
 | 
			
		||||
            }
 | 
			
		||||
            $elts = explode(' ', $b, 2);
 | 
			
		||||
            $tag = substr(trim($elts[1]), 10);  // Remove refs/tags/ prefix
 | 
			
		||||
            $res[$tag] = '';
 | 
			
		||||
        }
 | 
			
		||||
        krsort($res);
 | 
			
		||||
        $this->cache['tags'] = $res;
 | 
			
		||||
 | 
			
		||||
        return $res;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -348,6 +346,14 @@ class IDF_Scm_Git extends IDF_Scm
 | 
			
		||||
        if (!preg_match('/<(.*)>/', $author, $match)) {
 | 
			
		||||
            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]));
 | 
			
		||||
        $users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
 | 
			
		||||
        if ($users->count() > 0) {
 | 
			
		||||
@@ -640,7 +646,11 @@ class IDF_Scm_Git extends IDF_Scm
 | 
			
		||||
        $c['full_message'] = IDF_Commit::toUTF8($c['full_message']);
 | 
			
		||||
        $c['title'] = IDF_Commit::toUTF8($c['title']);
 | 
			
		||||
        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;
 | 
			
		||||
        return $res;
 | 
			
		||||
@@ -914,4 +924,113 @@ class IDF_Scm_Git extends IDF_Scm
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function repository($request, $match)
 | 
			
		||||
    {
 | 
			
		||||
        // handle a couple of workarounds for authenticating with FastCGI/PHP
 | 
			
		||||
        if (!empty($_SERVER['HTTP_AUTHORIZATION']))
 | 
			
		||||
            list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':' , base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
 | 
			
		||||
        elseif (!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
 | 
			
		||||
            list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':' , base64_decode(substr($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 6)));
 | 
			
		||||
        elseif (!empty($_SERVER['REDIRECT_REMOTE_USER']))
 | 
			
		||||
            list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':' , base64_decode(substr($_SERVER['REDIRECT_REMOTE_USER'], 6)));
 | 
			
		||||
 | 
			
		||||
        // authenticate: authenticate connection through "extra" password
 | 
			
		||||
        if (!empty($_SERVER['PHP_AUTH_USER'])) {
 | 
			
		||||
            $sql = new Pluf_SQL('login=%s', array($_SERVER['PHP_AUTH_USER']));
 | 
			
		||||
            $users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
 | 
			
		||||
            if (count($users) == 1 && $users[0]->active) {
 | 
			
		||||
                $user = $users[0];
 | 
			
		||||
                $realkey = substr(sha1($user->password.Pluf::f('secret_key')), 0, 8);
 | 
			
		||||
                if ($_SERVER['PHP_AUTH_PW'] == $realkey) {
 | 
			
		||||
                    $request->user = $user;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (IDF_Precondition::accessSource($request) !== true) {
 | 
			
		||||
            $response = new Pluf_HTTP_Response("");
 | 
			
		||||
            $response->status_code = 401;
 | 
			
		||||
            $response->headers['WWW-Authenticate']='Basic realm="git for '.$this->project.'"';
 | 
			
		||||
            return $response;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $path = $match[2];
 | 
			
		||||
 
 | 
			
		||||
        if (!file_exists($this->repo)) {
 | 
			
		||||
            mkdir($this->repo, 0750, true);
 | 
			
		||||
            $out = array();
 | 
			
		||||
            $res = 0;
 | 
			
		||||
            exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').
 | 
			
		||||
                         Pluf::f('git_path', 'git').' --git-dir=%s init', escapeshellarg($this->repo)),
 | 
			
		||||
                 $out, $res);
 | 
			
		||||
            if ($res != 0) {
 | 
			
		||||
                Pluf_Log::error(array('IDF_Scm_Git::repository', $res, $this->repo));
 | 
			
		||||
                throw new Exception(sprintf('Init repository error, exit status %d.', $res));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // update files before delivering them
 | 
			
		||||
        if (($path == 'objects/info/pack') || ($path == 'info/refs')) {
 | 
			
		||||
            $cmd = sprintf(Pluf::f('idf_exec_cmd_prefix', '').
 | 
			
		||||
                       'GIT_DIR=%s '.Pluf::f('git_path', 'git').' update-server-info -f',
 | 
			
		||||
                       escapeshellarg($this->repo));
 | 
			
		||||
            self::shell_exec('IDF_Scm_Git::repository', $cmd);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // smart HTTP discovery
 | 
			
		||||
        if ($path == 'info/refs' && !empty($request->GET['service'])){
 | 
			
		||||
            $service = $request->GET['service'];
 | 
			
		||||
            switch ($service) {
 | 
			
		||||
            case 'git-receive-pack':
 | 
			
		||||
                if (IDF_Precondition::projectMemberOrOwner($request) !== true) {
 | 
			
		||||
                    $response = new Pluf_HTTP_Response("");
 | 
			
		||||
                    $response->status_code = 401;
 | 
			
		||||
                    $response->headers['WWW-Authenticate']='Basic realm="git for '.$this->project.'"';
 | 
			
		||||
                    return $response;
 | 
			
		||||
                }
 | 
			
		||||
            case 'git-upload-pack':
 | 
			
		||||
                $content = sprintf('%04x',strlen($service)+15).
 | 
			
		||||
                         '# service='.$service."\n0000";
 | 
			
		||||
                $content .= self::shell_exec('IDF_Scm_Git::repository',
 | 
			
		||||
                         Pluf::f('idf_exec_cmd_prefix', '').
 | 
			
		||||
                         $service.' --stateless-rpc --advertise-refs '.
 | 
			
		||||
                         $this->repo);
 | 
			
		||||
                $response = new Pluf_HTTP_Response($content,
 | 
			
		||||
                         'application/x-'.$service.'-advertisement');
 | 
			
		||||
                return $response;
 | 
			
		||||
            default:
 | 
			
		||||
                throw new Exception('unknown service: '.$service);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch($path) {
 | 
			
		||||
        // smart HTTP RPC
 | 
			
		||||
        case 'git-receive-pack':
 | 
			
		||||
                if (IDF_Precondition::projectMemberOrOwner($request) !== true) {
 | 
			
		||||
                    $response = new Pluf_HTTP_Response("");
 | 
			
		||||
                    $response->status_code = 401;
 | 
			
		||||
                    $response->headers['WWW-Authenticate']='Basic realm="git for '.$this->project.'"';
 | 
			
		||||
                    return $response;
 | 
			
		||||
                }
 | 
			
		||||
        case 'git-upload-pack':
 | 
			
		||||
            $response = new Pluf_HTTP_Response_CommandPassThru(
 | 
			
		||||
                   Pluf::f('idf_exec_cmd_prefix', '').$path.
 | 
			
		||||
                   ' --stateless-rpc '.$this->repo,
 | 
			
		||||
                   'application/x-'.$path.'-result');
 | 
			
		||||
            $response->setStdin(fopen('php://input', 'rb'));
 | 
			
		||||
            return $response;
 | 
			
		||||
 | 
			
		||||
        // regular file
 | 
			
		||||
        default:
 | 
			
		||||
            // make sure we're inside the repo hierarchy (ie. no break-out)
 | 
			
		||||
            if (is_file($this->repo.'/'.$path) &&
 | 
			
		||||
              strpos(realpath($this->repo.'/'.$path), $this->repo.'/') == 0) {
 | 
			
		||||
                return new Pluf_HTTP_Response_File($this->repo.'/'.$path,
 | 
			
		||||
                       'application/octet-stream');
 | 
			
		||||
            } else {
 | 
			
		||||
                return new Pluf_HTTP_Response_NotFound($request);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2008 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -22,19 +22,77 @@
 | 
			
		||||
# ***** 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
 | 
			
		||||
{
 | 
			
		||||
    protected $hg_log_template;
 | 
			
		||||
 | 
			
		||||
    public function __construct($repo, $project=null)
 | 
			
		||||
    {
 | 
			
		||||
        $this->repo = $repo;
 | 
			
		||||
        $this->project = $project;
 | 
			
		||||
        $this->hg_log_template = "'".'changeset: {rev}:{node|short}\nauthor: {author}\ndate: {date|isodate}\nfiles: {files}\n{desc}\n'."'";
 | 
			
		||||
    }
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
    public function getRepositorySize()
 | 
			
		||||
    {
 | 
			
		||||
@@ -94,7 +152,7 @@ class IDF_Scm_Mercurial extends IDF_Scm
 | 
			
		||||
                       escapeshellarg($this->repo),
 | 
			
		||||
                       escapeshellarg($rev));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        self::exec('IDF_Scm_Mercurial::isValidRevision', $cmd, $out, $ret);
 | 
			
		||||
        self::exec('IDF_Scm_Mercurial::validateRevision', $cmd, $out, $ret);
 | 
			
		||||
 | 
			
		||||
        // FIXME: apparently a given hg revision can also be ambigious -
 | 
			
		||||
        //        handle this case here sometime
 | 
			
		||||
@@ -158,7 +216,8 @@ class IDF_Scm_Mercurial extends IDF_Scm
 | 
			
		||||
            throw new Exception(sprintf(__('Not a valid tree: %s.'), $tree));
 | 
			
		||||
        }
 | 
			
		||||
        $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();
 | 
			
		||||
        $res = array();
 | 
			
		||||
        $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')
 | 
			
		||||
    {
 | 
			
		||||
        $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();
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        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);
 | 
			
		||||
        $res = array();
 | 
			
		||||
        foreach ($out as $b) {
 | 
			
		||||
            preg_match('/(\S+).*\S+:(\S+)/', $b, $match);
 | 
			
		||||
            preg_match('/(.+?)\s+\S+:(\S+)/', $b, $match);
 | 
			
		||||
            $res[$match[1]] = '';
 | 
			
		||||
        }
 | 
			
		||||
        $this->cache['branches'] = $res;
 | 
			
		||||
@@ -308,7 +368,7 @@ class IDF_Scm_Mercurial extends IDF_Scm
 | 
			
		||||
        self::exec('IDF_Scm_Mercurial::getTags', $cmd, $out);
 | 
			
		||||
        $res = array();
 | 
			
		||||
        foreach ($out as $b) {
 | 
			
		||||
            preg_match('/(\S+).*\S+:(\S+)/', $b, $match);
 | 
			
		||||
            preg_match('/(.+?)\s+\S+:(\S+)/', $b, $match);
 | 
			
		||||
            $res[$match[1]] = '';
 | 
			
		||||
        }
 | 
			
		||||
        $this->cache['tags'] = $res;
 | 
			
		||||
@@ -336,17 +396,18 @@ class IDF_Scm_Mercurial extends IDF_Scm
 | 
			
		||||
     */
 | 
			
		||||
    public function getCommit($commit, $getdiff=false)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->isValidRevision($commit)) {
 | 
			
		||||
        if ($this->validateRevision($commit) != IDF_Scm::REVISION_VALID) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $logStyle = new IDF_Scm_Mercurial_LogStyle(IDF_Scm_Mercurial_LogStyle::FULL_LOG);
 | 
			
		||||
        $tmpl = ($getdiff)
 | 
			
		||||
            ? Pluf::f('hg_path', 'hg').' log -p -r %s -R %s --template %s'
 | 
			
		||||
            : Pluf::f('hg_path', 'hg').' log -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 --debug -r %s -R %s --style %s';
 | 
			
		||||
        $cmd = sprintf($tmpl,
 | 
			
		||||
                       escapeshellarg($commit),
 | 
			
		||||
                       escapeshellarg($this->repo),
 | 
			
		||||
                       $this->hg_log_template);
 | 
			
		||||
 | 
			
		||||
                       escapeshellarg($logStyle->get()));
 | 
			
		||||
        $out = array();
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        self::exec('IDF_Scm_Mercurial::getCommit', $cmd, $out);
 | 
			
		||||
@@ -363,11 +424,70 @@ class IDF_Scm_Mercurial extends IDF_Scm
 | 
			
		||||
                $log[] = $line;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $out = self::parseLog($log, 4);
 | 
			
		||||
        $out = self::parseLog($log);
 | 
			
		||||
        $out[0]->diff = implode("\n", $change);
 | 
			
		||||
        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();
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        self::exec('IDF_Scm_Mercurial::getChanges', $cmd, $out);
 | 
			
		||||
        $log = self::parseLog($out);
 | 
			
		||||
        // we expect only one log entry that contains all the needed information
 | 
			
		||||
        $log = $log[0];
 | 
			
		||||
 | 
			
		||||
        $return = (object) array(
 | 
			
		||||
            '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 ($srcidx === false && $trgidx !== false) {
 | 
			
		||||
                $return->copies[$src] = $trg;
 | 
			
		||||
                unset($return->additions[$trgidx]);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            // file sutures (counter-operation to copy) not supported
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a commit is big.
 | 
			
		||||
     *
 | 
			
		||||
@@ -388,54 +508,65 @@ class IDF_Scm_Mercurial extends IDF_Scm
 | 
			
		||||
     */
 | 
			
		||||
    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();
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        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 int Number of lines in the headers (3)
 | 
			
		||||
     * @return array Change log.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    public static function parseLog($lines, $hdrs=3)
 | 
			
		||||
    public static function parseLog($lines)
 | 
			
		||||
    {
 | 
			
		||||
        $res = array();
 | 
			
		||||
        $c = array();
 | 
			
		||||
        $i = 0;
 | 
			
		||||
        $hdrs += 1;
 | 
			
		||||
        $headers_processed = false;
 | 
			
		||||
        foreach ($lines as $line) {
 | 
			
		||||
            $i++;
 | 
			
		||||
            if (0 === strpos($line, 'changeset:')) {
 | 
			
		||||
            if ($line == "\0") {
 | 
			
		||||
                $headers_processed = false;
 | 
			
		||||
                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;
 | 
			
		||||
                }
 | 
			
		||||
                $c = array();
 | 
			
		||||
                $c['commit'] = substr(strrchr($line, ':'), 1);
 | 
			
		||||
                $c['full_message'] = '';
 | 
			
		||||
                $i=1;
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            if ($i == $hdrs) {
 | 
			
		||||
                $c['title'] = trim($line);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            $match = array();
 | 
			
		||||
            if (preg_match('/^(\S+):\s*(.*)/', $line, $match)) {
 | 
			
		||||
            if (!$headers_processed && empty($line)) {
 | 
			
		||||
                $headers_processed = true;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            if (!$headers_processed && preg_match('/^(\S+):\s*(.*)/', $line, $match)) {
 | 
			
		||||
                $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];
 | 
			
		||||
                } elseif ($match[1] == 'summary') {
 | 
			
		||||
                    $c['title'] = $match[2];
 | 
			
		||||
                } 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 {
 | 
			
		||||
                    $c[$match[1]] = trim($match[2]);
 | 
			
		||||
                }
 | 
			
		||||
@@ -444,15 +575,14 @@ class IDF_Scm_Mercurial extends IDF_Scm
 | 
			
		||||
                }
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            if ($i > ($hdrs+1)) {
 | 
			
		||||
                $c['full_message'] .= trim($line)."\n";
 | 
			
		||||
            if ($headers_processed) {
 | 
			
		||||
                if (empty($c['title']))
 | 
			
		||||
                    $c['title'] = trim($line);
 | 
			
		||||
                else
 | 
			
		||||
                    $c['full_message'] .= trim($line)."\n";
 | 
			
		||||
                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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2010 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
@@ -29,12 +29,12 @@
 | 
			
		||||
class IDF_Scm_Monotone extends IDF_Scm
 | 
			
		||||
{
 | 
			
		||||
    /** the minimum supported interface version */
 | 
			
		||||
    public static $MIN_INTERFACE_VERSION = 12.0;
 | 
			
		||||
 | 
			
		||||
    private $stdio;
 | 
			
		||||
    public static $MIN_INTERFACE_VERSION = 13.0;
 | 
			
		||||
 | 
			
		||||
    private static $instances = array();
 | 
			
		||||
 | 
			
		||||
    private $stdio;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     */
 | 
			
		||||
@@ -135,13 +135,13 @@ class IDF_Scm_Monotone extends IDF_Scm
 | 
			
		||||
    /**
 | 
			
		||||
     * @see IDF_Scm::getArchiveStream
 | 
			
		||||
     */
 | 
			
		||||
    public function getArchiveStream($commit, $prefix='repository/')
 | 
			
		||||
    public function getArchiveStream($commit, $prefix = null)
 | 
			
		||||
    {
 | 
			
		||||
        $revs = $this->_resolveSelector($commit);
 | 
			
		||||
        // sanity: this should actually not happen, because the
 | 
			
		||||
        // revision is validated before already
 | 
			
		||||
        if (count($revs) == 0) {
 | 
			
		||||
            return new Pluf_HTTP_Response_NotFound();
 | 
			
		||||
            throw new IDF_Scm_Exception("$commit is not a valid revision");
 | 
			
		||||
        }
 | 
			
		||||
        return new IDF_Scm_Monotone_ZipRender($this->stdio, $revs[0]);
 | 
			
		||||
    }
 | 
			
		||||
@@ -536,9 +536,6 @@ class IDF_Scm_Monotone extends IDF_Scm
 | 
			
		||||
        $stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
 | 
			
		||||
 | 
			
		||||
        foreach ($stanzas as $stanza) {
 | 
			
		||||
            if ($stanza[0]['key'] == 'format_version')
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if ($stanza[0]['values'][0] != $file)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
@@ -602,7 +599,7 @@ class IDF_Scm_Monotone extends IDF_Scm
 | 
			
		||||
    {
 | 
			
		||||
        $revs = $this->_resolveSelector($commit);
 | 
			
		||||
        if (count($revs) == 0)
 | 
			
		||||
            return null;
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        $revision = $revs[0];
 | 
			
		||||
        $out = $this->stdio->exec(array('get_revision', $revision));
 | 
			
		||||
@@ -612,6 +609,7 @@ class IDF_Scm_Monotone extends IDF_Scm
 | 
			
		||||
            'additions'  => array(),
 | 
			
		||||
            'deletions'  => array(),
 | 
			
		||||
            'renames'    => array(),
 | 
			
		||||
            'copies'     => array(),
 | 
			
		||||
            'patches'    => array(),
 | 
			
		||||
            'properties' => array(),
 | 
			
		||||
        );
 | 
			
		||||
@@ -671,7 +669,7 @@ class IDF_Scm_Monotone extends IDF_Scm
 | 
			
		||||
    {
 | 
			
		||||
        $revs = $this->_resolveSelector($commit);
 | 
			
		||||
        if (count($revs) == 0)
 | 
			
		||||
            return array();
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        $res = array();
 | 
			
		||||
 | 
			
		||||
@@ -679,6 +677,7 @@ class IDF_Scm_Monotone extends IDF_Scm
 | 
			
		||||
        $res['parents'] = preg_split("/\n/", $parents, -1, PREG_SPLIT_NO_EMPTY);
 | 
			
		||||
 | 
			
		||||
        $certs = $this->_getCerts($revs[0]);
 | 
			
		||||
 | 
			
		||||
        // FIXME: this assumes that author, date and changelog are always given
 | 
			
		||||
        $res['author'] = implode(', ', $certs['author']);
 | 
			
		||||
 | 
			
		||||
@@ -700,6 +699,29 @@ class IDF_Scm_Monotone extends IDF_Scm
 | 
			
		||||
        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
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
/*
 | 
			
		||||
# ***** BEGIN LICENSE BLOCK *****
 | 
			
		||||
# This file is part of InDefero, an open source project management application.
 | 
			
		||||
# Copyright (C) 2010 Céondo Ltd and contributors.
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user