Compare commits

..

No commits in common. "master" and "newdiff" have entirely different histories.

351 changed files with 1341 additions and 70065 deletions

7
.gitignore vendored
View File

@ -1,11 +1,4 @@
*~
tmp tmp
src/IDF/conf/idf.php src/IDF/conf/idf.php
src/IDF/conf/idf.test.php src/IDF/conf/idf.test.php
www/test.php www/test.php
www/media/upload
src/IDF/gettexttemplates
indefero-*.zip
src/IDF/conf/path.php
.tx/config
src/IDF/locale/idf.pot.bak

31
AUTHORS
View File

@ -1,31 +0,0 @@
InDefero was originally created during summer 2008
by Loïc d'Anterroches with the support of Céondo Ltd.
Much appreciated contributors:
Nicolas Lassalle <http://www.beroot.org/> - Subversion support
bohwaz <http://bohwaz.net/>
Benjamin Jorand <benjamin.jorand@gmail.com> - Mercurial support
Baptiste Michaud <bactisme@gmail.com> - Subversion synchronization
Julien Issler
Manuel Eidenberger <eidenberger@gmail.com>
Ciaran Gultnieks
Mehdi Kabab <http://pioupioum.fr/>
Sindre R. Myren
Patrick Georgi <patrick.georgi@coresystems.de>
Adrien Bustany
Charles Melbye
Baptiste Durand-Bret
Andrew Nguyen
David Feeney
Denis Kot <denis.kot@gmail.com>
Samuel Suther
Ludovic Bellière
Brian Armstrong
Raphaël Emourgeon
Jakub Viták
Vladimir Solomatin
And all the nice users who spent time reporting issues and promoting
the project. The project could not live without them.

View File

@ -1,115 +0,0 @@
[Indefero][idf] is not only a software you can use either hosted for
you or hosted by you, but also a free software you can contribute to.
Here you will get how to contribute and what to contribute.
[idf]: http://www.indefero.net
# Quick Way on How to Contribute
Simple contribution:
1. Open a ticket with your idea. You can directly propose a patch if
you have it.
2. Wait for it to be checked by the devs or meet us on the #indefero
channel on [FreeNode][freenode].
Bigger contribution:
1. Fork Indefero where you want (fork from the develop branch).
2. Code your change and document it.
3. Open a ticket with a pull request and talk about it on IRC.
# The General Contribution Workflow for Regular Contributors
1. Fork Indefero from the **develop** branch.
2. Request a pull request if you do not have write access on the repository.
3. Merge your changes without fast forward in develop. This keeps track of
the history of the changes and makes understanding what is going on easy.
4. Merge your changes with fast forward **only if a single commit**.
Indefero is composed of two main branches:
1. **master**: this is the shipped branch, only a select number of people
can push into it.
2. **develop**: this is the development branch, all the people having write
access to the repository are welcomed to push in.
**Note:** The branching model we use is [explained in details here][bmi]. You
**must** understand it to really contribute to the code base in an
efficient way.
[bmi]: http://nvie.com/git-model "A successful Git branching model"
# What to Contribute
Contribution is easy, you can contribute in a lot of different fields,
contributions small or big are always appreciated. Here is an example
list of what you can do:
- Install InDefero on your system and report the problem you had.
- Find the bad English and typos and propose corrections.
- Help with the translation effort.
- Find little bugs or usability problems and provide ideas on how to fix them.
- Register to the [discussion group][group] and help new users.
- Come and chat on IRC #indefero on the [FreeNode][freenode] servers.
- Find ways to improve the design while keeping it **beautifully simple**.
- Write a blog post about the project, what you think is good or bad.
- Translate InDefero for the sake of the community.
- Or maybe really hack into the code.
As you can see, the real hacking into the code is just a small part of the work, so even if you are not a coder you can do a lot.
[group]: http://groups.google.com/group/indefero-users
[freenode]: http://freenode.net/
## I am a simple user
Thanks a lot! Really! As a project leader, I consider **you** as
**the most important person in the success of the project**. So do not
worry, I will really listen to your needs and make you love this
project.
What you can do to help:
- Use the software and each time you find something a bit annoying in your daily use, report a bug. Usability issues are high priority issues.
- Find typos, grammar mistakes, etc. and report a bug.
- Write about InDefero on your blog/website.
- Read the issues submitted by the users and provide answers if you have them.
- ...
## I am a designer
A lot of things to do for you:
- Check the design and find the flaws in it. Is the space well used, does it look really nice and is it also functional for the first users?
- Do we have good support of all the major browsers?
- ...
## I am a coder
Checkout the code and have fun, but keep in mind that your results
must be simple to use. Do not worry about the beautiful part, the
designers can work on that.
## I am a security guy
Please, do try to break it, if you find a problem, come on IRC or
contact the developers to get the issue fixed as soon as
possible. Please, be nice, do not release the issue in the wild
without first talking to us.
## I am a translator
We currently use (transifex)[http://trac.transifex.org] to help our
users to translate indefero. You don't have to use it, but it's an
easy way to do the job. You can visit the indefero page at transifex
here : http://www.transifex.net/projects/p/indefero/c/indefero/
Please understand that your changes will not be commited instantly,
but are sent to the maintainers e-mails before. Then, your changes
will not be in the main repository until da-loic push the changes. In
that way, try to do big changes with less submissions.

280
COPYING
View File

@ -1,280 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

View File

@ -1,225 +0,0 @@
# Quick installation instruction
The installation of InDefero is composed of 2 parts, first the
installation of the [Pluf framework](http://www.pluf.org) and second,
the installation of InDefero by itself.
## Recommended Layout of the Files
If your server document root is in `/var/www` a good thing is to keep
the number of files under the `/var/www` folder to its minimum. So,
you should create a `/home/www` folder in which we are going to
install all but the files which need to be available under the
document root.
/home/www/pluf/src/
/home/www/pluf/src/Pluf.php
/home/www/pluf/src/migrate.php
/home/www/indefero/src
/home/www/indefero/www
/home/www/indefero/www/index.php
/home/www/indefero/www/media
The you need to link the `media` and `index.php` files into your
docroot.
$ cd /var/www
$ ln -s /home/www/indefero/www/index.php
$ ln -s /home/www/indefero/www/media
## Installation of Pluf
* Checkout the trunk of [Pluf](http://www.pluf.org).
* Install the `Mail` and `Mail_mime` classes from [PEAR](http://pear.php.net). You must use the `--alldeps` flag when installing these modules.
**Pear install/upgrade:**
$ sudo pear upgrade-all
$ sudo pear install --alldeps Mail
$ sudo pear install --alldeps Mail_mime
If you already have some of the PEAR packages installed with your
distribution, the `Mail` package is often not up-to-date,
[read more here](http://projects.ceondo.com/p/indefero/issues/104/#ic347).
The Pluf installation folder is the folder containing the file `Pluf.php`.
## Installation of InDefero
The installation is composed of the following steps:
* Get the InDefero archive.
* Configure it correctly.
* Installation the database with the `migrate.php` script.
* Bootstrap the application with a `bootstrap.php` script.
Here is the step-by-step installation procedure:
* Extract the InDefero archive somewhere.
* The InDefero installation folder is the folder containing this file INSTALL.mdtext.
* Make a copy of `src/IDF/conf/idf.php-dist` as `src/IDF/conf/idf.php`.
* Update the idf.php file to match your system.
* Make a copy of `src/IDF/conf/path.php-dist` as `src/IDF/conf/path.php`.
* Update the path.php file to match your installation paths. It should work out of the box if you followed the recommended file layout.
* Open a terminal/shell and go into the `src` folder in the InDefero installation folder.
**Command:**
$ cd /home/www/indefero/src
* Run `php /home/www/pluf/src/migrate.php --conf=IDF/conf/idf.php -a -i -d -u` to test the installation of the tables.
* Run `php /home/www/pluf/src/migrate.php --conf=IDF/conf/idf.php -a -i -d` to really install the tables.
* More details about the migration is available in the [migration documentation](http://pluf.org/doc/migrations.html) of the Pluf framework.
* Create a bootstrap file to create the admin user for example `www/bootstrap.php`. Do not forget to update the second line with your path to Pluf.
**Bootstrap script:**
<?php
require '/home/www/indefero/src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start('/home/www/indefero/src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
$user = new Pluf_User();
$user->first_name = 'John';
$user->last_name = 'Doe'; // Required!
$user->login = 'doe'; // must be lowercase!
$user->email = 'doe@example.com';
$user->password = 'yourpassword'; // the password is salted/hashed
// in the database, so do not worry :)
$user->administrator = true;
$user->active = true;
$user->create();
print "Bootstrap ok\n";
?>
* Run `php www/bootstrap.php`.
* Remove the `www/bootstrap.php` file.
* Open the `www/index.php` file and ensure that the path to Pluf and
Indefero are correctly set for your configuration.
* Now you can login with this user into the interface.
* Click on the Forge Management link on top and create your first project.
## Upgrade InDefero
To upgrade:
* Make a backup of your data, including the database.
* Extract the new archive on top of the current one.
* Update your version of Pluf.
* Check that the path in the `index.php` are still good.
* Remove all the `*.phps` files in your temp folder.
* Upgrade the database with the upgrade commands:
**Upgrade commands:**
$ cd /home/www/indefero/src
$ php /home/www/pluf/src/migrate.php --conf=IDF/conf/idf.php -a -d -u
$ php /home/www/pluf/src/migrate.php --conf=IDF/conf/idf.php -a -d
## Repository Synchronization
The documentation is available in the `doc` folder.
* Subversion: `doc/syncsvn.mdtext`.
* Mercurial: `doc/syncmercurial.mdtext`.
* Git: `doc/syncgit.mdtext`.
## For the Apache Webserver Users
If you are using [Apache](http://httpd.apache.org/) for your webserver
and want to have nice URLs like `http://yourdomain.com/p/yourproject/`
and not `http://yourdomain.com/index.php/p/yourproject/` you can use
the following `.htaccess` file to be put in the same folder of the
`www/index.php` file.
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*) /index.php/$1
`Options +FollowSymLinks` is only needed if you are using symlinks.
## For the Gentoo users
If you get the error:
T_CHARACTER Use of undefined constant T_CHARACTER - assumed 'T_CHARACTER'"
you need to compile PHP with the "tokenizer" flag.
## For People with open_basedir restriction error
If you get an error like:
file_get_contents(): open_basedir restriction in effect.
File(/etc/mime.types) is not within the
allowed path(s): (/srv/http/:/home/:/tmp/:/usr/share/pear/)
Just copy the file `/etc/mime.types` into the folder `/home` and put
this in your configuration file:
$cfg['idf_mimetypes_db'] = '/home/mime.types';
## FreeBSD Installation
You need to install `/usr/ports/lang/php5-extensions` which contains
the Standard PHP Library (SPL).
## Using a SMTP server with authentication
If your SMTP server requires authentication, for example,
*smtp.gmail.com*, you can use the following email configuration:
$cfg['send_emails'] = true;
$cfg['mail_backend'] = 'smtp';
$cfg['mail_auth'] = true;
$cfg['mail_host'] = 'ssl://smtp.gmail.com';
$cfg['mail_port'] = 465;
$cfg['mail_username'] = 'YOURGMAILADDRESS';
$cfg['mail_password'] = 'YOURPASSWORD';
Check with your provider to get the right settings.
## Git Daemon on Ubuntu Karmic
If you have problems getting it to run, you can follow this procedure
proposed by Mathias in ticket 369.
1. Install git-daemon-run in addition to git-core
2. Edit /etc/sv/git-daemon/run to look as follows:
#!/bin/sh
exec 2>&1
echo 'git-daemon starting.'
exec chpst -ugit:git \
/usr/lib/git-core/git-daemon \
--reuseaddr \
--syslog \
--verbose \
--base-path=/home/git/repositories \
/home/git/repositories
3. Restart git-daemon-run
sv restart git-daemon
## If Subversion is not working
If you access a Subversion server with a self-signed certificate, you
may have problems as your certificate is not trusted, check the
[procedure provided here][svnfix] to solve the problem.
[svnfix]: http://projects.ceondo.com/p/indefero/issues/319/#ic1358
## If the registration links are not working
If You have standard instalaction of PHP ie in Debian, php.ini sets
mbstring.func_overload to value "2" for overloading str*
functions. You need to prevent the overload as it does not make sense
anyway (magic in the background is bad!).
See the [corresponding ticket][reglink].
[reglink]: http://projects.ceondo.com/p/indefero/issues/481/

110
Makefile
View File

@ -1,110 +0,0 @@
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2010 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 ***** */
# Installation of external tools : transifex-client
# sudo apt-get install python-setuptools
# sudo easy_install -U transifex-client
PLUF_PATH=$(shell php -r "require_once('src/IDF/conf/path.php'); echo PLUF_PATH;")
all help:
@echo "Rules for generate tarball :"
@for b in `git branch | sed "s/^. //g"`; do \
echo -e "\t"$$b"_tarball - Generate a zip archive of the "$$b" branch."; \
done
@echo -e "\nRules for internationnalization :";
@echo -e "\tpot-update - Update the POT file from HTML template and PHP source, then merge it with PO file"
@echo -e "\tpot-push - Send the POT file on transifex server"
@echo -e "\tpo-update - Merge POT file into PO file. POT is not regenerated."
@echo -e "\tpo-push - Send the all PO file on transifex server"
@echo -e "\tpo-pull - Get all PO file from transifex server"
#
# Internationnalization rule, POT & PO file manipulation
#
.PHONY: pot-update po-update
pot-update:
# Backup pot file
@if [ -e src/IDF/locale/idf.pot ]; then \
mv -f src/IDF/locale/idf.pot src/IDF/locale/idf.pot.bak; \
fi
touch src/IDF/locale/idf.pot;
# Extract string
@cd src; php $(PLUF_PATH)/extracttemplates.php IDF/conf/idf.php IDF/gettexttemplates
@cd src; for phpfile in `find . -iname "*.php"`; do \
echo "Parsing file : "$$phpfile; \
xgettext -o idf.pot -p ./IDF/locale/ --from-code=UTF-8 -j --keyword --keyword=__ --keyword=_n:1,2 -L PHP $$phpfile ; \
done
# Remove tmp folder
rm -Rf src/IDF/gettexttemplates
# Update PO
@make po-update
po-update:
@for pofile in `ls src/IDF/locale/*/idf.po`; do \
echo "Updating file : "$$pofile; \
msgmerge -v -U $$pofile src/IDF/locale/idf.pot; \
echo ; \
done
#
# Transifex
#
.PHONY: check-tx-config
check-tx-config:
@if [ ! -e .tx/config ]; then \
mkdir -p .tx; \
touch .tx/config; \
echo "[main]" >> .tx/config; \
echo "host = http://www.transifex.net" >> .tx/config; \
echo "" >> .tx/config; \
echo "[indefero.idfpot]" >> .tx/config; \
echo "file_filter = src/IDF/locale/<lang>/idf.po" >> .tx/config; \
echo "source_file = src/IDF/locale/idf.pot" >> .tx/config; \
echo "source_lang = en" >> .tx/config; \
fi
@if [ ! -e $(HOME)/.transifexrc ]; then \
touch $(HOME)/.transifexrc; \
echo "[http://www.transifex.net]" >> $(HOME)/.transifexrc; \
echo "username = " >> $(HOME)/.transifexrc; \
echo "token = " >> $(HOME)/.transifexrc; \
echo "password = " >> $(HOME)/.transifexrc; \
echo "hostname = http://www.transifex.net" >> $(HOME)/.transifexrc; \
echo "You must edit the file ~/.transifexrc to setup your transifex account (login & password) !"; \
exit 1; \
fi
pot-push: check-tx-config
@tx push -s
po-push: check-tx-config
@tx push -t
po-pull: check-tx-config
@tx pull -a
#
# Generic rule to build a tarball of indefero for a specified branch
# ex: make master_tarball
# make dev_tarball
#
%_tarball:
@git archive --format=zip --prefix="indefero/" $(@:_tarball=) > indefero-$(@:_tarball=)-`git log $(@:_tarball=) -n 1 --pretty=format:%H`.zip

View File

@ -1,20 +0,0 @@
Copyright (C) 2007-2009 Paul Duncan <pabs@pablotron.org>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -1,42 +0,0 @@
ZipStream 0.2.2 README
======================
Please see the file COPYING for licensing and warranty information. The
latest version of this software is available at the following URL:
http://pablotron.org/software/zipstream-php/
Overview
========
A fast and simple streaming zip file downloader for PHP. Here's a
simple example:
# create a new zipstream object
$zip = new ZipStream('example.zip');
# create a file named 'hello.txt'
$zip->add_file('some_image.jpg', 'This is the contents of hello.txt');
# add a file named 'image.jpg' from a local file 'path/to/image.jpg'
$zip->add_file_from_path('some_image.jpg', 'path/to/image.jpg');
# finish the zip stream
$zip->finish();
You can also add comments, modify file timestamps, and customize (or
disable) the HTTP headers. See the class file for details. There are a
couple of additional examples in the initial release announcement at the
following URL:
http://pablotron.org/?cid=1535
Requirements
============
* PHP version 5.1.2 or newer (specifically, the hash_init and
hash_file functions).
About the Author
================
Paul Duncan <pabs@pablotron.org>
http://pablotron.org/

View File

@ -1,2 +0,0 @@
Based on PKZIP appnotes, which are included here.

View File

@ -1,52 +0,0 @@
<?php
# load zipstream class
require '../zipstream.php';
# get path to current file
$pwd = dirname(__FILE__);
# add some random files
$files = array(
'../extras/zip-appnote-6.3.1-20070411.txt',
'../zipstream.php',
);
# create new zip stream object
$zip = new ZipStream('test.zip', array(
'comment' => 'this is a zip file comment. hello?'
));
# common file options
$file_opt = array(
# file creation time (2 hours ago)
'time' => time() - 2 * 3600,
# file comment
'comment' => 'this is a file comment. hi!',
);
# add files under folder 'asdf'
foreach ($files as $file) {
# build absolute path and get file data
$path = ($file[0] == '/') ? $file : "$pwd/$file";
$data = file_get_contents($path);
# add file to archive
$zip->add_file('asdf/' . basename($file), $data, $file_opt);
}
# add same files again wihtout a folder
foreach ($files as $file) {
# build absolute path and get file data
$path = ($file[0] == '/') ? $file : "$pwd/$file";
$data = file_get_contents($path);
# add file to archive
$zip->add_file(basename($file), $data, $file_opt);
}
# finish archive
$zip->finish();
?>

View File

@ -1,580 +0,0 @@
<?php
##########################################################################
# ZipStream - Streamed, dynamically generated zip archives. #
# by Paul Duncan <pabs@pablotron.org> #
# #
# Copyright (C) 2007-2009 Paul Duncan <pabs@pablotron.org> #
# #
# Permission is hereby granted, free of charge, to any person obtaining #
# a copy of this software and associated documentation files (the #
# "Software"), to deal in the Software without restriction, including #
# without limitation the rights to use, copy, modify, merge, publish, #
# distribute, sublicense, and/or sell copies of the Software, and to #
# permit persons to whom the Software is furnished to do so, subject to #
# the following conditions: #
# #
# The above copyright notice and this permission notice shall be #
# included in all copies or substantial portions of the of the Software. #
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, #
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF #
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. #
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR #
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, #
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #
# OTHER DEALINGS IN THE SOFTWARE. #
##########################################################################
#
# ZipStream - Streamed, dynamically generated zip archives.
# by Paul Duncan <pabs@pablotron.org>
#
# Requirements:
#
# * PHP version 5.1.2 or newer.
#
# Usage:
#
# Streaming zip archives is a simple, three-step process:
#
# 1. Create the zip stream:
#
# $zip = new ZipStream('example.zip');
#
# 2. Add one or more files to the archive:
#
# # add first file
# $data = file_get_contents('some_file.gif');
# $zip->add_file('some_file.gif', $data);
#
# # add second file
# $data = file_get_contents('some_file.gif');
# $zip->add_file('another_file.png', $data);
#
# 3. Finish the zip stream:
#
# $zip->finish();
#
# You can also add an archive comment, add comments to individual files,
# and adjust the timestamp of files. See the API documentation for each
# method below for additional information.
#
# Example:
#
# # create a new zip stream object
# $zip = new ZipStream('some_files.zip');
#
# # list of local files
# $files = array('foo.txt', 'bar.jpg');
#
# # read and add each file to the archive
# foreach ($files as $path)
# $zip->add_file($path, file_get_contents($path));
#
# # write archive footer to stream
# $zip->finish();
#
class ZipStream {
const VERSION = '0.2.2';
var $opt = array(),
$files = array(),
$cdr_ofs = 0,
$need_headers = false,
$ofs = 0;
#
# Create a new ZipStream object.
#
# Parameters:
#
# $name - Name of output file (optional).
# $opt - Hash of archive options (optional, see "Archive Options"
# below).
#
# Archive Options:
#
# comment - Comment for this archive.
# content_type - HTTP Content-Type. Defaults to 'application/x-zip'.
# content_disposition - HTTP Content-Disposition. Defaults to
# 'attachment; filename=\"FILENAME\"', where
# FILENAME is the specified filename.
# large_file_size - Size, in bytes, of the largest file to try
# and load into memory (used by
# add_file_from_path()). Large files may also
# be compressed differently; see the
# 'large_file_method' option.
# large_file_method - How to handle large files. Legal values are
# 'store' (the default), or 'deflate'. Store
# sends the file raw and is significantly
# faster, while 'deflate' compresses the file
# and is much, much slower. Note that deflate
# must compress the file twice and extremely
# slow.
# send_http_headers - Boolean indicating whether or not to send
# the HTTP headers for this file.
#
# Note that content_type and content_disposition do nothing if you are
# not sending HTTP headers.
#
# Large File Support:
#
# By default, the method add_file_from_path() will send send files
# larger than 20 megabytes along raw rather than attempting to
# compress them. You can change both the maximum size and the
# compression behavior using the large_file_* options above, with the
# following caveats:
#
# * For "small" files (e.g. files smaller than large_file_size), the
# memory use can be up to twice that of the actual file. In other
# words, adding a 10 megabyte file to the archive could potentially
# occupty 20 megabytes of memory.
#
# * Enabling compression on large files (e.g. files larger than
# large_file_size) is extremely slow, because ZipStream has to pass
# over the large file once to calculate header information, and then
# again to compress and send the actual data.
#
# Examples:
#
# # create a new zip file named 'foo.zip'
# $zip = new ZipStream('foo.zip');
#
# # create a new zip file named 'bar.zip' with a comment
# $zip = new ZipStream('bar.zip', array(
# 'comment' => 'this is a comment for the zip file.',
# ));
#
# Notes:
#
# If you do not set a filename, then this library _DOES NOT_ send HTTP
# headers by default. This behavior is to allow software to send its
# own headers (including the filename), and still use this library.
#
function __construct($name = null, $opt = array()) {
# save options
$this->opt = $opt;
# set large file defaults: size = 20 megabytes, method = store
if (!isset($this->opt['large_file_size']))
$this->opt['large_file_size'] = 20 * 1024 * 1024;
if (!isset($this->opt['large_file_method']))
$this->opt['large_file_method'] = 'store';
$this->output_name = $name;
if ($name || (isset($opt['send_http_headers'])
&& $opt['send_http_headers']))
$this->need_headers = true;
}
#
# add_file - add a file to the archive
#
# Parameters:
#
# $name - path of file in archive (including directory).
# $data - contents of file
# $opt - Hash of options for file (optional, see "File Options"
# below).
#
# File Options:
# time - Last-modified timestamp (seconds since the epoch) of
# this file. Defaults to the current time.
# comment - Comment related to this file.
#
# Examples:
#
# # add a file named 'foo.txt'
# $data = file_get_contents('foo.txt');
# $zip->add_file('foo.txt', $data);
#
# # add a file named 'bar.jpg' with a comment and a last-modified
# # time of two hours ago
# $data = file_get_contents('bar.jpg');
# $zip->add_file('bar.jpg', $data, array(
# 'time' => time() - 2 * 3600,
# 'comment' => 'this is a comment about bar.jpg',
# ));
#
function add_file($name, $data, $opt = array()) {
# compress data
$zdata = gzdeflate($data);
# calculate header attributes
$crc = crc32($data);
$zlen = strlen($zdata);
$len = strlen($data);
$meth = 0x08;
# send file header
$this->add_file_header($name, $opt, $meth, $crc, $zlen, $len);
# print data
$this->send($zdata);
}
#
# add_file_from_path - add a file at path to the archive.
#
# Note that large files may be compresed differently than smaller
# files; see the "Large File Support" section above for more
# information.
#
# Parameters:
#
# $name - name of file in archive (including directory path).
# $path - path to file on disk (note: paths should be encoded using
# UNIX-style forward slashes -- e.g '/path/to/some/file').
# $opt - Hash of options for file (optional, see "File Options"
# below).
#
# File Options:
# time - Last-modified timestamp (seconds since the epoch) of
# this file. Defaults to the current time.
# comment - Comment related to this file.
#
# Examples:
#
# # add a file named 'foo.txt' from the local file '/tmp/foo.txt'
# $zip->add_file_from_path('foo.txt', '/tmp/foo.txt');
#
# # add a file named 'bigfile.rar' from the local file
# # '/usr/share/bigfile.rar' with a comment and a last-modified
# # time of two hours ago
# $path = '/usr/share/bigfile.rar';
# $zip->add_file_from_path('bigfile.rar', $path, array(
# 'time' => time() - 2 * 3600,
# 'comment' => 'this is a comment about bar.jpg',
# ));
#
function add_file_from_path($name, $path, $opt = array()) {
if ($this->is_large_file($path)) {
# file is too large to be read into memory; add progressively
$this->add_large_file($name, $path, $opt);
} else {
# file is small enough to read into memory; read file contents and
# handle with add_file()
$data = file_get_contents($path);
$this->add_file($name, $data, $opt);
}
}
#
# finish - Write zip footer to stream.
#
# Example:
#
# # add a list of files to the archive
# $files = array('foo.txt', 'bar.jpg');
# foreach ($files as $path)
# $zip->add_file($path, file_get_contents($path));
#
# # write footer to stream
# $zip->finish();
#
function finish() {
# add trailing cdr record
$this->add_cdr($this->opt);
$this->clear();
}
###################
# PRIVATE METHODS #
###################
#
# Create and send zip header for this file.
#
private function add_file_header($name, $opt, $meth, $crc, $zlen, $len) {
# strip leading slashes from file name
# (fixes bug in windows archive viewer)
$name = preg_replace('/^\\/+/', '', $name);
# calculate name length
$nlen = strlen($name);
# create dos timestamp
$opt['time'] = isset($opt['time']) ? $opt['time'] : time();
$dts = $this->dostime($opt['time']);
# build file header
$fields = array( # (from V.A of APPNOTE.TXT)
array('V', 0x04034b50), # local file header signature
array('v', (6 << 8) + 3), # version needed to extract
array('v', 0x00), # general purpose bit flag
array('v', $meth), # compresion method (deflate or store)
array('V', $dts), # dos timestamp
array('V', $crc), # crc32 of data
array('V', $zlen), # compressed data length
array('V', $len), # uncompressed data length
array('v', $nlen), # filename length
array('v', 0), # extra data len
);
# pack fields and calculate "total" length
$ret = $this->pack_fields($fields);
$cdr_len = strlen($ret) + $nlen + $zlen;
# print header and filename
$this->send($ret . $name);
# add to central directory record and increment offset
$this->add_to_cdr($name, $opt, $meth, $crc, $zlen, $len, $cdr_len);
}
#
# Add a large file from the given path.
#
private function add_large_file($name, $path, $opt = array()) {
$st = stat($path);
$block_size = 1048576; # process in 1 megabyte chunks
$algo = 'crc32b';
# calculate header attributes
$zlen = $len = $st['size'];
$meth_str = $this->opt['large_file_method'];
if ($meth_str == 'store') {
# store method
$meth = 0x00;
$crc = unpack('V', hash_file($algo, $path, true));
$crc = $crc[1];
} elseif ($meth_str == 'deflate') {
# deflate method
$meth = 0x08;
# open file, calculate crc and compressed file length
$fh = fopen($path, 'rb');
$hash_ctx = hash_init($algo);
$zlen = 0;
# read each block, update crc and zlen
while ($data = fgets($fh, $block_size)) {
hash_update($hash_ctx, $data);
$data = gzdeflate($data);
$zlen += strlen($data);
}
# close file and finalize crc
fclose($fh);
$crc = unpack('V', hash_final($hash_ctx, true));
$crc = $crc[1];
} else {
die("unknown large_file_method: $meth_str");
}
# send file header
$this->add_file_header($name, $opt, $meth, $crc, $zlen, $len);
# open input file
$fh = fopen($path, 'rb');
# send file blocks
while ($data = fgets($fh, $block_size)) {
if ($meth_str == 'deflate')
$data = gzdeflate($data);
# send data
$this->send($data);
}
# close input file
fclose($fh);
}
#
# Is this file larger than large_file_size?
#
function is_large_file($path) {
$st = stat($path);
return ($this->opt['large_file_size'] > 0) &&
($st['size'] > $this->opt['large_file_size']);
}
#
# Save file attributes for trailing CDR record.
#
private function add_to_cdr($name, $opt, $meth, $crc, $zlen, $len, $rec_len) {
$this->files[] = array($name, $opt, $meth, $crc, $zlen, $len, $this->ofs);
$this->ofs += $rec_len;
}
#
# Send CDR record for specified file.
#
private function add_cdr_file($args) {
list ($name, $opt, $meth, $crc, $zlen, $len, $ofs) = $args;
# get attributes
$comment = isset($opt['comment']) ? $opt['comment'] : '';
# get dos timestamp
$dts = $this->dostime($opt['time']);
$fields = array( # (from V,F of APPNOTE.TXT)
array('V', 0x02014b50), # central file header signature
array('v', (6 << 8) + 3), # version made by
array('v', (6 << 8) + 3), # version needed to extract
array('v', 0x00), # general purpose bit flag
array('v', $meth), # compresion method (deflate or store)
array('V', $dts), # dos timestamp
array('V', $crc), # crc32 of data
array('V', $zlen), # compressed data length
array('V', $len), # uncompressed data length
array('v', strlen($name)), # filename length
array('v', 0), # extra data len
array('v', strlen($comment)), # file comment length
array('v', 0), # disk number start
array('v', 0), # internal file attributes
array('V', 32), # external file attributes
array('V', $ofs), # relative offset of local header
);
# pack fields, then append name and comment
$ret = $this->pack_fields($fields) . $name . $comment;
$this->send($ret);
# increment cdr offset
$this->cdr_ofs += strlen($ret);
}
#
# Send CDR EOF (Central Directory Record End-of-File) record.
#
private function add_cdr_eof($opt = null) {
$num = count($this->files);
$cdr_len = $this->cdr_ofs;
$cdr_ofs = $this->ofs;
# grab comment (if specified)
$comment = '';
if ($opt && isset($opt['comment']))
$comment = $opt['comment'];
$fields = array( # (from V,F of APPNOTE.TXT)
array('V', 0x06054b50), # end of central file header signature
array('v', 0x00), # this disk number
array('v', 0x00), # number of disk with cdr
array('v', $num), # number of entries in the cdr on this disk
array('v', $num), # number of entries in the cdr
array('V', $cdr_len), # cdr size
array('V', $cdr_ofs), # cdr ofs
array('v', strlen($comment)), # zip file comment length
);
$ret = $this->pack_fields($fields) . $comment;
$this->send($ret);
}
#
# Add CDR (Central Directory Record) footer.
#
private function add_cdr($opt = null) {
foreach ($this->files as $file)
$this->add_cdr_file($file);
$this->add_cdr_eof($opt);
}
#
# Clear all internal variables. Note that the stream object is not
# usable after this.
#
function clear() {
$this->files = array();
$this->ofs = 0;
$this->cdr_ofs = 0;
$this->opt = array();
}
###########################
# PRIVATE UTILITY METHODS #
###########################
#
# Send HTTP headers for this stream.
#
private function send_http_headers() {
# grab options
$opt = $this->opt;
# grab content type from options
$content_type = 'application/x-zip';
if (isset($opt['content_type']))
$content_type = $this->opt['content_type'];
# grab content disposition
$disposition = 'attachment';
if (isset($opt['content_disposition']))
$disposition = $opt['content_disposition'];
if ($this->output_name)
$disposition .= "; filename=\"{$this->output_name}\"";
$headers = array(
'Content-Type' => $content_type,
'Content-Disposition' => $disposition,
'Pragma' => 'public',
'Cache-Control' => 'public, must-revalidate',
'Content-Transfer-Encoding' => 'binary',
);
foreach ($headers as $key => $val)
header("$key: $val");
}
#
# Send string, sending HTTP headers if necessary.
#
private function send($str) {
if ($this->need_headers)
$this->send_http_headers();
$this->need_headers = false;
echo $str;
}
#
# Convert a UNIX timestamp to a DOS timestamp.
#
function dostime($when = 0) {
# get date array for timestamp
$d = getdate($when);
# set lower-bound on dates
if ($d['year'] < 1980) {
$d = array('year' => 1980, 'mon' => 1, 'mday' => 1,
'hours' => 0, 'minutes' => 0, 'seconds' => 0);
}
# remove extra years from 1980
$d['year'] -= 1980;
# return date string
return ($d['year'] << 25) | ($d['mon'] << 21) | ($d['mday'] << 16) |
($d['hours'] << 11) | ($d['minutes'] << 5) | ($d['seconds'] >> 1);
}
#
# Create a format string and argument list for pack(), then call
# pack() and return the result.
#
function pack_fields($fields) {
list ($fmt, $args) = array('', array());
# populate format string and argument list
foreach ($fields as $field) {
$fmt .= $field[0];
$args[] = $field[1];
}
# prepend format string to argument list
array_unshift($args, $fmt);
# build output string from header and compressed data
return call_user_func_array('pack', $args);
}
};
?>

View File

@ -1,113 +0,0 @@
# Plugin SyncGit by Céondo Ltd
The SyncGit plugin allow the direct creation and synchronisation of
git repositories with the InDefero database. This requires giving
access to the repositories using a dedicated SSH account, usually the
`git` account.
## Prerequisites
A good understanding of:
* the security issues related to using a SSH account on a server;
* the principle of public/private SSH keys;
* the rights/ownership of files on a Linux/BSD/nix system;
Yes, what you are going to do has security implications.
## Git user configuration
On your system, you will need to create a new `git` account. This
account will only be used to access the git repositories and at the
moment cannot be shared for other use.
First create a new git account:
$ sudo adduser \
--system \
--shell /bin/sh \
--gecos 'git version control' \
--group \
--disabled-password \
--home /home/git \
git
Then, we need to create the base SSH files with the right permissions:
$ sudo su git
$ mkdir /home/git/.ssh
$ touch /home/git/.ssh/authorized_keys
$ chmod 0700 /home/git/.ssh
$ chmod 0600 /home/git/.ssh/authorized_keys
$ exit
We add the `www-data` user to the `git` group so it can access the
repositories to read the content:
$ sudo usermod -a -G git www-data
Do not forget to restart Apache or your fastcgi process to take the
group addition into account.
## Creation of the repositories base
For each project using git in InDefero a corresponding bare repository
will be created in `/home/git/repositories`. For example, if the
shortname of your project is `wonder`, it will be created in
`/home/git/repositories/wonder.git`
$ sudo -H -u git mkdir /home/git/repositories
## InDefero Configuration
First, you need to have python installed on your system to be able to
run the very small python script `gitserve.py` in the `scripts`
folder. Here is a configuration example:
$cfg['git_repositories'] = '/home/git/repositories/%s.git';
$cfg['git_remote_url'] = 'git://yourdomain.com/%s.git';
$cfg['idf_plugin_syncgit_path_gitserve'] = '/home/www/indefero/scripts/gitserve.py'; # yes .py
$cfg['idf_plugin_syncgit_path_authorized_keys'] = '/home/git/.ssh/authorized_keys';
$cfg['idf_plugin_syncgit_sync_file'] = '/tmp/SYNC-GIT';
# Remove the git repositories which do not have a corresponding project
# This is run at cron time
$cfg['idf_plugin_syncgit_remove_orphans'] = false;
# git account home dir
$cfg['idf_plugin_syncgit_git_home_dir'] = '/home/git';
# where are going to be the git repositories
$cfg['idf_plugin_syncgit_base_repositories'] = '/home/git/repositories';
When someone will change his SSH key or add a new one, the
`/tmp/SYNC-GIT` file will be created. The cron job
`/home/www/indefero/scripts/gitcron.php` will see the file and update
the content of the `authorized_keys` file.
## Cron Job Configuration
You need to run a cron job every now and then to synchronize the SSH
keys. The command to run in the cron job is:
php /home/www/indefero/scripts/gitcron.php
The user of the cron job must be `git`.
## Git daemon configuration
Put in `/etc/event.d/local-git-daemon` the following:
start on startup
stop on shutdown
exec /usr/bin/git-daemon \
--user=git --group=git \
--verbose \
--reuseaddr \
--base-path=/home/git/repositories/ \
/home/git/repositories/
respawn
Then run:
$ sudo start local-git-daemon

View File

@ -1,96 +0,0 @@
# Plugin SyncMercurial by Benjamin Jorand
The SyncMercurial plugin allows the direct creation and synchronisation of
mercurial repositories with the InDefero database. The repositories will be
published by hgwebdir.cgi using HTTP. It also handles private repositories.
SyncMercurial is adapted from SyncSvn by Baptiste Michaud.
## To Contact the Author
Benjamin Jorand <benjamin.jorand@gmail.com>
## Apache configuration
The simple way to share Mercurial repositories is to publish them
using HTTP and `hgwebdir.cgi`.
It first requires a config file called hgweb.config in the same
directory where you put hgwebdir.cgi (for example,
`/home/indefero/scripts`):
[collections]
/home/indefero/repositories/mercurial/ = /home/indefero/repositories/mercurial/
Then configure a vhost this way :
ScriptAliasMatch ^/hg(.*) /home/indefero/scripts/hgwebdir.cgi$1
<Directory /home/indefero/scripts>
Options +ExecCGI
AuthName "Restricted"
AuthType Basic
AuthUserFile /home/indefero/auth/.htpasswd
<Limit PUT POST>
Require valid-user
</Limit>
</Directory>
Enable the authentification for private repositories :
Include /home/indefero/scripts/private_indefero.conf
## InDefero configuration
First, you need to install the File_Passwd PEAR package:
$ sudo pear install File_Passwd
Then, based on the paths provided in the Apache configuration, you
need to put the following lines in your configuration file:
$cfg['idf_plugin_syncmercurial_passwd_file'] = '/home/indefero/auth/.htpasswd';
$cfg['idf_plugin_syncmercurial_path'] = '/home/indefero/repositories/mercurial';
$cfg['idf_plugin_syncmercurial_private_include'] = '/home/indefero/scripts/private_indefero.conf';
$cfg['idf_plugin_syncmercurial_private_notify'] = '/home/indefero/tmp/notify.tmp';
$cfg['idf_plugin_syncmercurial_private_url'] = '/hg/%s';
You also need to provide the base definition of the hgrc file. For example:
$cfg['idf_plugin_syncmercurial_hgrc'] = array(
'web' => array('push_ssl' => 'false',
'allow_push' => '',
'description' => '',
'allow_archive' => 'bz2, zip, gz',
'style' => 'gitweb',
'contact' => ''),
'hooks' => array(),
'extensions' => array(),
);
If you are note using Apache but Nginx, you may need to create the
passwords as plain text passwords (see ticket 391). You can configure
the password storage with the format you want. The default is `sha`
you can set it to `plain` for nginx.
$cfg['idf_plugin_syncmercurial_passwd_mode'] = 'sha';
See the [`FILE_PASSWD_*` constants](http://euk1.php.net/package/File_Passwd/docs/latest/File_Passwd/_File_Passwd-1.1.7---Passwd.php.html) for more choices.
## Cron configuration
As InDefero modifies the private_indefero.conf, apache needs to be reloaded.
Each time this file is modified, a temporary file is created.
*/5 * * * * /bin/sh /home/indefero/src/scripts/SyncMercurial.sh
Edit this script and add correct values to `private_notify` and `reload_cmd`.
## Hook configuratin
To get notifications sent directly when pushing in your repositories,
you need to add the following in your `.hgrc` file. The script will be
called onec per push and will automatically send the notifications and
sync the timeline.
[hooks]
changegroup = /home/indefero/src/scripts/hgchangegroup.php

View File

@ -1,272 +0,0 @@
# Plugin SyncMonotone by Thomas Keller (me@thomaskeller.biz)
The SyncMonotone plugin allow the direct creation and synchronisation of
monotone repositories with the InDefero database. It has been built to
work together with monotone's "super server" usher, which is used to control
several repositories at once, acts as proxy and single entrance.
## Prerequisites
* a unixoid operating system
* monotone >= 0.99.1
* for a proxy setup with usher:
* boost headers (for usher compilation)
* a current version of usher
* a daemonizer, like supervise
## Installation of monotone
If you install monotone from a distribution package, ensure you do not
install and / or activate the server component. We just need a plain
client installation which usually consists only of the `mtn` binary and
a few docs.
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
The monotone plugin can be used in several different ways:
1. One database for everything. This is the easiest setup and of possible
use in case you do not want indefero to manage the access to your project.
Your `idf.php` should look like this:
$ cat idf.php
...
$cfg['mtn_path'] = 'mtn';
$cfg['mtn_opts'] = array('--no-workspace', '--no-standard-rcfiles');
$cfg['mtn_repositories'] = '/home/monotone/all_projects.mtn';
$cfg['mtn_remote_url'] = 'ssh://monotone@my.server.com:~all_projects.mtn';
$cfg['mtn_db_access'] = 'local';
...
Pro:
* easy to setup and to manage
Con:
* you need to give committers SSH access to your machine
* database lock problem: the database from which
indefero reads its data might be locked in case a user
syncs at the very moment via SSH
2. One database for every project. Similar to the above setup, but this
time you use the '%s' placeholder which is replaced with the short name
of the indefero project:
$ cat idf.php
...
$cfg['mtn_path'] = 'mtn';
$cfg['mtn_opts'] = array('--no-workspace', '--no-standard-rcfiles');
$cfg['mtn_repositories'] = '/home/monotone/%s.mtn';
$cfg['mtn_remote_url'] = 'ssh://monotone@my.server.com:~%s.mtn';
$cfg['mtn_db_access'] = 'local';
...
The same pro's and con's apply. Additionally you have to be careful about
not giving people physical read/write access of another project's database.
Furthermore, if you do not want to use `ssh`, but `netsync` transport,
each project's database must be served over a separate port.
3. One database for every project, all managed with usher. This is the
recommended setup for a mid-size forge setup. The remaining part of this
document will describe the process to set this up in detail.
Pro:
* access rights can be granted per project and are automatically
managed by indefero, just like the user's public monotone keys
* no database locking issues
* one public server running on the one well-known port
Con:
* harder to setup
## Installation and configuration of usher
1. Clone usher's monotone repository:
$ mtn clone "mtn://monotone.ca?net.venge.monotone.contrib.usher"
2. Compile usher:
$ autoreconf -i
$ ./configure && make
$ sudo make install
This installs the usher binary in $prefix/bin.
3. Create a new usher user:
$ adduser --system --disabled-login --home /var/lib/usher usher
4. Create the basic usher setup:
$ cd /var/lib/usher
$ mkdir projects logs
$ cat > usher.conf
userpass "admin" "<secret-password>"
adminaddr "127.0.0.1:12345"
logdir "log"
^D
$ chmod 600 usher.conf
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:
$ setfacl -m u:www:rw usher.conf
$ setfacl -m d:u:www:rwx projects/
5. Wrap a daemonizer around usher, for example supervise from daemontools
(<http://cr.yp.to/damontools.html>):
$ cat > run
#!/bin/sh
cd /var/lib/usher
exec 2>&1
exec \
setuidgid usher \
usher usher.conf
^D
The service can now be started through supervise:
$ supervise /var/lib/usher
## Configuration of indefero
Based on the above setup, the configuration in `src/IDF/conf/idf.php` should
look like this:
$ cat idf.php
...
$cfg['mtn_path'] = 'mtn';
$cfg['mtn_opts'] = array('--no-workspace', '--no-standard-rcfiles');
$cfg['mtn_repositories'] = '/var/lib/usher/projects/%s/';
$cfg['mtn_remote_url'] = 'mtn://my.server.com/%s';
$cfg['mtn_db_access'] = 'remote';
$cfg['mtn_usher_conf'] = '/var/lib/usher/usher.conf';
...
The `%s` placeholders are automatically replaced by the name of the
indefero project. The plugin assumes that every project is separated
by a distinct server name in the monotone URL (hence the use of `/%s`),
so if a user calls
$ mtn sync mtn://my.server.com/project1
then the database / repository of the indefero `project1` is used.
Note that 'mtn_remote_url' is also used as internal URI to query the data
for indefero's source view, so it *must* be a valid host!
Usher also allows the identification of a project repository by hostname,
which would allow an URL template like `mtn://%s.my.server.com`, however
the plugin does not write out the configuration which is needed for this
yet.
For even more advanced setups, usher can also be used to forward sync
requests to other remote servers for load balancing, please consult the
README file for more information.
## Security and remote access
Indefero distinguishs between public and private projects and so does
the monotone plugin.
Public projects can be pulled by everybody and pushed by team members
or additional invited people. Remote command execution is enabled, but
only for read-only commands.
Remote commands can be helpful for a user or a 3rd party tool (like
[mtn-browse](http://mtn-browse.sourceforge.net) or
[guitone](http://guitone.thomaskeller.biz)) to browse the database
contents remotely without having to pull everything in first instance.
Private projects on the other hand can only be synced by team members
or additional invited people. Remote command execution is disabled
by default. If you want to enable that, simply put the keys of the users
you want to give access to in your project's `remote-automate-permissions`
file. In the future this plugin might handle this file just as it handles
`read-permissions` and `write-permissions`.
## Notifications
If you have successfully set up your monotone instance, you probably want
to notify 3rd party systems for incoming changes or simply mirror them
somewhere else for backup purposes. The monotone source tree already comes
with [many example scripts and hooks](http://code.monotone.ca/p/monotone/source/tree/h:net.venge.monotone/contrib)
which serve these purposes, after only little additional configuration.
The usher/indefero-controlled setup automatically looks for *.lua files
in a directory called `hooks.d` right under the project's base directory
(configured via $cfg['mtn_repositories']) and this is the ideal place to
put or link these additional lua sources.
## Custom project configurations and templates
If a new project is created in IDF, the SyncMonotone plugin creates a new
configuration tree for the project into the project's configuration directory,
determined by `$cfg['mtn_repositories']`. IDF ships with the minimum set of
files for this configuration tree and sets up everything automatically for you.
Even more, most of the configuration files from the newly created tree are only
symlinked to the original configuration directory which is configurable via
`$cfg['mtn_confdir']` and defaults to `src/IDF/Plugin/SyncMonotone/`. This has
the advantage that your standard IDF setup automatically receives updates to
existing (symlinked) configuration files as soon as you update to a newer
version.
You could, however, also choose to place the directory tree somewhere else
and adapt the contents of the individual files yourself, so these changes get
automatically applied to all new projects you create. You could even go so far
and add new files to the tree and let them be processed automatically just
as the basic files! All you need to do is to copy your files and / or directories
underknees your `$cfg['mtn_confdir']` and add their relative paths to
`$cfg['mtn_confdir_extra']`.
By convention, all entries which end with a slash are considered directories,
so mkdir(1) is issued for these entries, all files which do not end up with
".in" are considered to be static script files which are just symlinked from
the basic configuration dir and all entries ending on ".in" are considered
configuration files or templates, which are copied over to the project's
configuration tree and which get some basic project-specific values replaced.
The following placeholders are currently recognized and replaced for these files:
* %%PROJECT%% - the name of the created project
* %%MTNPOSTPUSH%% - the absolute path to the `mtn-post-push` script
* %%MTNCLIENTKEY%% - the public key hash of the key which is used by IDF
to authenticate remote stdio access
Thats it - I hope you find it useful :)
## Q&A
### After I created a new project, IDF throws an exception and tells me that it couldn't save the membership data with a cryptic error message. Whats wrong?
Multiple issues could cause that. If you've set up usher, make sure the usher
can fork your database at all and look out for specific errors in the log file
of your project. If you stumble upon permission issues, ensure that the user
who runs the usher can access all files in your project's configuration directory,
including symlinked files.
### I pushed a branch to my server, but it does not show up in IDF. Whats wrong?
Check if the heads of your branch are not suspended, i.e. do not carry a
`suspend` certificate. This usually hides the branch and all of its history
from monotone's eyes and therefor also from indefero. You can either choose
to "unsuspend" the branch simply by committing and pushing another head or
by letting monotone ignore all suspend certs. For the latter, its usually
enough to add `--ignore-suspend-certs` to the list of options in `$cfg['mtn_opts']`.
### I want to display another default branch when I click the "Source" tab. How can I do that?
Let the forge admin know the new master branch for your project. He is able
to change that quickly. Depending on the backend / server setup this might
also require some changes in the usher configuration, but only if usher
recognizes and proxies your database on a branch name level.

View File

@ -1,90 +0,0 @@
# Plugin SyncSvn by Baptiste Michaud
The SyncSvn plugin allow the direct creation and synchronisation of
subversion repositories with the InDefero database. This requires
giving access to the repositories using the DAV_SVN module of Apache2.
## To Contact the Author
Baptiste Michaud
bactisme@gmail.com
webplay.fr - frandroid.com - lost-in-translation.fr
## Apache configuration
You will first need to install the DAV_SVN module for Apache. On
Debian/Ubuntu based systems just run:
$ sudo apt-get install libapache2-svn
$ sudo a2enmod dav_svn
Then, you need to configure dav_svn, this is an example of
configuration:
<Location /svn>
DAV svn
SVNParentPath /home/svn/repositories
AuthzSVNAccessFile /home/svn/dav_svn.authz
Satisfy Any
Require valid-user
AuthType Basic
AuthName "Subversion Repository"
AuthUserFile /home/svn/dav_svn.passwd
</Location>
Be sure to [read the documentation before](http://svnbook.red-bean.com/en/1.5/svn.serverconfig.httpd.html).
The files `/home/svn/dav_svn.authz`, `/home/svn/dav_svn.passwd` and
the directory `/home/svn/repositories` must be writable by your
webserver process. To ensure that, do:
$ sudo mkdir --parents /home/svn/repositories
$ sudo touch /home/svn/dav_svn.authz
$ sudo touch /home/svn/dav_svn.passwd
$ sudo chown -R www-data:www-data /home/svn
Now, you need to restart apache:
$ sudo /etc/init.d/apache2 force-reload
## InDefero Configuration
First, you need to install the File_Passwd PEAR package:
$ sudo pear install File_Passwd
Then, based on the paths provided in the Apache configuration and if
your Apache server is serving the domain `www.mydomain.com`, the you
need to put the following in your configuration file:
$cfg['svn_repositories'] = 'file:///home/svn/repositories/%s';
// We add "trunk" to invite people to checkout the trunk of the
// project.
$cfg['svn_remote_url'] = 'http://www.mydomain.com/svn/%s/trunk';
// Synchronisation specific configuration variables
$cfg['idf_plugin_syncsvn_authz_file'] = '/home/svn/dav_svn.authz';
$cfg['idf_plugin_syncsvn_passwd_file'] = '/home/svn/dav_svn.passwd';
$cfg['idf_plugin_syncsvn_svn_path'] = '/home/svn/repositories';
// Delete the corresponding repository when deleting the project
$cfg['idf_plugin_syncsvn_remove_orphans'] = false;
You can have more control over the permissions given to the owners,
members, extra authorized users and anonymous users if you want with
the following configuration variables:
* **idf_plugin_syncsvn_access_owners ('rw')**: Access for the project owners.
* **idf_plugin_syncsvn_access_members ('rw')**: Access for the project members.
* **idf_plugin_syncsvn_access_extra ('r')**: Access for the extra authorized people in case of a private project.
* **idf_plugin_syncsvn_access_public ('r')**: Anonymous access.
* **idf_plugin_syncsvn_access_private ('')**: Anonymous access in the case of a private project.
## svn: Can't open file '/root/.subversion/servers': Permission denied error
If you get the error:
svn: Can't open file '/root/.subversion/servers': Permission denied
Check the [fix available](http://projects.ceondo.com/p/indefero/issues/458/)

View File

@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.48.0 r9654"
version="1.0"
sodipodi:docname="indefero-logo-lite.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
gridtolerance="10000"
guidetolerance="10"
objecttolerance="10"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.313708"
inkscape:cx="31.568929"
inkscape:cy="-0.35578703"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1280"
inkscape:window-height="723"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-728.09451)">
<g
id="g2401"
transform="matrix(0.13580542,0,0,0.13580542,-47.580342,708.10521)"
style="fill:#8ae234;stroke:#4e9a06;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:export-filename="/home/loa/Projects/indefero/logo/powered-by-indefero.png"
inkscape:export-xdpi="12.330909"
inkscape:export-ydpi="12.330909">
<path
id="path2383"
d="m 396.19089,173.14471 c -7.67621,0.80661 -14.40195,5.39406 -19.58101,10.89131 -7.23597,7.88004 -11.69742,18.07908 -13.32198,28.60362 -1.7236,11.28173 -0.25925,23.20635 5.07686,33.37271 3.78607,7.24384 9.53161,13.92339 17.29701,16.96772 3.86478,1.53937 8.98362,1.03284 11.67912,-2.41036 2.64357,-3.5671 2.69463,-8.234 2.85756,-12.48867 0.045,-7.61054 -0.54749,-15.25544 0.45618,-22.83193 0.87131,-9.50623 4.03944,-18.56751 6.71612,-27.66851 1.16242,-4.44333 2.25094,-9.02808 1.97499,-13.64988 -0.48817,-4.62476 -3.58059,-9.31042 -8.2964,-10.4067 -1.57489,-0.44882 -3.23412,-0.48948 -4.85845,-0.37931 z"
style="fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:#4e9a06;stroke-width:2.4000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
inkscape:connector-curvature="0" />
<path
id="path2391"
d="m 433.14691,149.28687 c 7.2059,2.76589 12.51512,8.93778 16.09494,15.58815 4.94991,9.48434 6.61962,20.49058 5.46486,31.07695 -1.25505,11.34342 -5.75582,22.48271 -13.54134,30.92159 -5.53192,6.01709 -12.81048,10.98198 -21.09918,11.91276 -4.13154,0.4866 -8.94486,-1.32748 -10.65734,-5.35104 -1.63027,-4.12976 -0.4717,-8.65084 0.47212,-12.80269 1.92628,-7.36287 4.47721,-14.59393 5.4687,-22.17201 1.61875,-9.40784 0.90381,-18.98034 0.67386,-28.46402 0.0272,-4.59278 0.1624,-9.30303 1.62515,-13.69592 1.66851,-4.34082 5.86829,-8.06645 10.70716,-7.90484 1.63738,-0.0259 3.25061,0.36424 4.79107,0.89107 z"
style="fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:#4e9a06;stroke-width:2.4000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,109 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1052.3622"
height="744.09448"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.46"
version="1.0"
sodipodi:docname="indefero-logo.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
gridtolerance="10000"
guidetolerance="10"
objecttolerance="10"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="538.31563"
inkscape:cy="375.31517"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1279"
inkscape:window-height="951"
inkscape:window-x="0"
inkscape:window-y="25" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g2401"
transform="matrix(1.21,0,0,1.21,-216.85419,33.719307)"
style="fill:#8ae234;stroke:#4e9a06;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:export-filename="/home/loa/Projects/indefero/logo/powered-by-indefero.png"
inkscape:export-xdpi="12.330909"
inkscape:export-ydpi="12.330909">
<path
id="path2383"
d="M 396.19089,173.14471 C 388.51468,173.95132 381.78894,178.53877 376.60988,184.03602 C 369.37391,191.91606 364.91246,202.1151 363.2879,212.63964 C 361.5643,223.92137 363.02865,235.84599 368.36476,246.01235 C 372.15083,253.25619 377.89637,259.93574 385.66177,262.98007 C 389.52655,264.51944 394.64539,264.01291 397.34089,260.56971 C 399.98446,257.00261 400.03552,252.33571 400.19845,248.08104 C 400.24345,240.4705 399.65096,232.8256 400.65463,225.24911 C 401.52594,215.74288 404.69407,206.6816 407.37075,197.5806 C 408.53317,193.13727 409.62169,188.55252 409.34574,183.93072 C 408.85757,179.30596 405.76515,174.6203 401.04934,173.52402 C 399.47445,173.0752 397.81522,173.03454 396.19089,173.14471 z"
style="fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:#4e9a06;stroke-width:2.4000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
id="path2391"
d="M 433.14691,149.28687 C 440.35281,152.05276 445.66203,158.22465 449.24185,164.87502 C 454.19176,174.35936 455.86147,185.3656 454.70671,195.95197 C 453.45166,207.29539 448.95089,218.43468 441.16537,226.87356 C 435.63345,232.89065 428.35489,237.85554 420.06619,238.78632 C 415.93465,239.27292 411.12133,237.45884 409.40885,233.43528 C 407.77858,229.30552 408.93715,224.78444 409.88097,220.63259 C 411.80725,213.26972 414.35818,206.03866 415.34967,198.46058 C 416.96842,189.05274 416.25348,179.48024 416.02353,169.99656 C 416.05073,165.40378 416.18593,160.69353 417.64868,156.30064 C 419.31719,151.95982 423.51697,148.23419 428.35584,148.3958 C 429.99322,148.36988 431.60645,148.76004 433.14691,149.28687 z"
style="fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:#4e9a06;stroke-width:2.4000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
</g>
<text
xml:space="preserve"
style="font-size:144px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:FromageCondOblique;-inkscape-font-specification:FromageCondOblique"
x="337"
y="355.09448"
id="text2397"
sodipodi:linespacing="100%"
inkscape:export-filename="/home/loa/Projects/indefero/logo/powered-by-indefero.png"
inkscape:export-xdpi="12.330909"
inkscape:export-ydpi="12.330909"><tspan
sodipodi:role="line"
id="tspan2399"
x="337"
y="355.09448">InDefero</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:semi-condensed;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans Semi-Condensed"
x="346"
y="241.09448"
id="text3195"
sodipodi:linespacing="125%"
inkscape:export-filename="/home/loa/Projects/indefero/logo/powered-by-indefero.png"
inkscape:export-xdpi="12.330909"
inkscape:export-ydpi="12.330909"><tspan
sodipodi:role="line"
id="tspan3197"
x="346"
y="241.09448">Powered by</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,248 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="2500.0901"
height="1052.3622"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="indefero-logo5.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.0">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient3535">
<stop
style="stop-color:#8ae234;stop-opacity:1;"
offset="0"
id="stop3537" />
<stop
style="stop-color:#8ae234;stop-opacity:0;"
offset="1"
id="stop3539" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3527">
<stop
style="stop-color:#9f5e21;stop-opacity:1;"
offset="0"
id="stop3529" />
<stop
style="stop-color:#9f5e21;stop-opacity:0;"
offset="1"
id="stop3531" />
</linearGradient>
<linearGradient
id="linearGradient3309">
<stop
style="stop-color:#73d213;stop-opacity:1;"
offset="0"
id="stop3311" />
<stop
style="stop-color:#4e9a06;stop-opacity:1;"
offset="1"
id="stop3313" />
</linearGradient>
<linearGradient
id="linearGradient3300">
<stop
style="stop-color:#a56223;stop-opacity:1;"
offset="0"
id="stop3302" />
<stop
style="stop-color:#492902;stop-opacity:1;"
offset="1"
id="stop3304" />
</linearGradient>
<linearGradient
id="linearGradient3214">
<stop
style="stop-color:#c17d11;stop-opacity:1;"
offset="0"
id="stop3216" />
<stop
style="stop-color:#c17d11;stop-opacity:0;"
offset="1"
id="stop3218" />
</linearGradient>
<linearGradient
id="linearGradient3200">
<stop
id="stop3204"
offset="1"
style="stop-color:#feaf36;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3178">
<stop
style="stop-color:#8f5902;stop-opacity:1;"
offset="0"
id="stop3180" />
<stop
style="stop-color:#8f5902;stop-opacity:0;"
offset="1"
id="stop3182" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3300"
id="linearGradient3502"
gradientUnits="userSpaceOnUse"
x1="255.2104"
y1="743.95398"
x2="517.18134"
y2="334.32626" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3309"
id="linearGradient3505"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.90055,0,0,0.9256685,8.702072,50.919096)"
x1="314.81146"
y1="549.55981"
x2="603.79364"
y2="130.80347" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3527"
id="linearGradient3533"
x1="344.48004"
y1="526.93384"
x2="279.66428"
y2="343.77176"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3535"
id="linearGradient3541"
x1="189.38306"
y1="542.79291"
x2="213.16689"
y2="515.03833"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
gridtolerance="10000"
guidetolerance="10"
objecttolerance="10"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.26121807"
inkscape:cx="54.404882"
inkscape:cy="468.48648"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1279"
inkscape:window-height="951"
inkscape:window-x="0"
inkscape:window-y="25" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="opacity:1;fill:#2e1c09;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect3479"
width="2327.0989"
height="738.08856"
x="28.861307"
y="47.247032"
ry="0"
inkscape:export-filename="/home/loa/Projects/indefero.net/media/idfn/img/indefero-logo.png"
inkscape:export-xdpi="10.974293"
inkscape:export-ydpi="10.974293" />
<path
style="fill:url(#linearGradient3505);fill-opacity:1;fill-rule:nonzero;stroke:#4e9a06;stroke-width:8.6737175;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 380.09452,80.164434 C 325.61878,80.164434 277.73831,109.63179 250.44346,154.01542 C 173.08362,161.38346 112.57489,224.77008 112.57489,301.77525 C 112.57489,328.86745 120.06848,354.27485 133.14683,376.14693 C 107.0966,405.29318 91.243109,443.82739 91.243109,486.07006 C 91.243109,576.90189 164.55915,650.60763 254.88993,650.60763 C 271.09473,650.60763 286.73713,648.22781 301.52154,643.80975 C 339.34139,666.58702 385.02955,679.88189 434.18381,679.88189 C 564.58392,679.88189 670.40931,586.17696 670.40933,470.70975 C 670.40933,424.81133 653.66603,382.36023 625.32555,347.85618 C 628.73221,333.4146 630.53185,318.20844 630.53185,302.49843 C 630.53185,212.12462 570.29216,138.17535 494.43623,133.1011 C 466.29611,100.61828 425.47491,80.164434 380.09452,80.164434 z"
id="path2397"
inkscape:export-filename="/home/loa/Projects/indefero.net/media/idfn/img/indefero-logo.png"
inkscape:export-xdpi="10.974293"
inkscape:export-ydpi="10.974293" />
<path
style="fill:url(#linearGradient3502);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.40000010000000019;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
d="M 480.78826,286.70465 C 471.74217,303.14801 466.59919,316.91871 462.21007,324.32034 C 458.4305,332.41806 437.64848,381.27668 422.04792,410.85567 C 411.43569,432.35724 400.69212,454.31445 384.28125,472.09375 C 378.04049,477.37818 367.78727,475.23418 363.26933,468.71428 C 353.17919,458.89555 343.45836,448.70244 333.24758,439.0075 C 317.77144,422.84753 305.56098,403.5827 297.62915,382.65768 C 289.88676,365.17614 282.06423,347.03016 268.29833,333.33399 C 266.87079,332.00892 257.87723,342.72971 251.17151,350.13535 C 257.91944,385.02379 261.45712,393.83832 272.16171,424.59112 C 277.97988,444.06591 287.52802,462.10601 295.01596,480.9416 C 301.95377,497.73482 307.73336,515.11749 310.44798,533.13227 C 316.97458,571.06863 313.78852,610.29702 303.54294,647.28446 C 295.53018,678.50408 283.87873,708.83248 268.40625,737.125 C 267.48586,741.82958 273.28765,745.0451 277.32903,743.60868 C 316.10441,740.6132 354.85515,736.72668 393.76368,736.01019 C 432.08809,735.62752 470.30781,739.07963 508.44819,742.41755 C 512.60098,742.7876 516.75356,743.16004 520.90625,743.53125 C 522.73666,738.32781 518.93119,733.37885 515.5,729.84375 C 505.37922,718.85052 495.85001,707.33025 486.04792,696.05659 C 472.12065,679.28224 459.49275,660.74518 453.48949,639.55179 C 445.74909,613.54378 446.01072,585.89039 449.3871,559.18722 C 454.13621,520.65521 457.70434,476.90793 468.12716,439.5747 C 475.03595,414.82825 499.26944,325.16045 505.28557,302.9463 C 506.6387,297.45482 507.70151,291.88947 508.4375,286.28125 C 508.3006,286.02979 501.31023,285.50092 480.78826,286.70465 z"
id="rect2383"
sodipodi:nodetypes="ccscscscccccccccccccccsccc"
inkscape:export-filename="/home/loa/Projects/indefero.net/media/idfn/img/indefero-logo.png"
inkscape:export-xdpi="10.974293"
inkscape:export-ydpi="10.974293" />
<path
style="fill:url(#linearGradient3533);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.25000000000000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 261.61445,343.77177 C 258.29796,347.16111 256.78029,348.5143 253.80525,350.94005 C 257.34812,367.72635 271.24491,410.33512 274.92704,420.91638 C 283.98781,446.95407 298.27703,470.44926 305.96388,496.81755 C 312.6285,519.6793 316.95796,541.99153 318.18734,565.63445 C 319.43684,589.66412 317.35056,617.16231 311.24725,640.25675 C 304.22544,666.82669 289.67219,709.81256 278.71119,735.38821 C 291.85032,736.74081 303.9092,734.30172 317.58611,733.28738 C 330.8032,732.91089 346.51711,728.41602 360.28427,728.58961 C 361.70248,692.98386 362.28914,639.87401 361.91883,606.08799 C 361.53577,571.13943 356.10739,550.49134 343.0934,518.4162 C 332.76842,492.96857 313.62977,457.39972 298.73254,430.34797 C 283.83531,403.29622 270.0195,371.02282 261.61445,343.77177 z"
id="path3450"
sodipodi:nodetypes="ccsssscccsssc"
inkscape:export-filename="/home/loa/Projects/indefero.net/media/idfn/img/indefero-logo.png"
inkscape:export-xdpi="10.974293"
inkscape:export-ydpi="10.974293" />
<path
style="fill:url(#linearGradient3541);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.25000000000000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 113.16529,429.39362 C 113.16529,429.39362 104.64326,454.1652 103.03794,468.69107 C 101.44344,483.11903 102.46825,506.23771 105.4804,520.43755 C 108.98801,536.97308 121.29761,558.93795 130.281,573.25668 C 138.61935,586.54728 158.09648,604.51753 171.10612,613.28778 C 185.25725,622.82754 205.57955,633.4095 222.19789,637.29436 C 239.00231,641.22271 255.43702,641.52676 272.63495,640.09436 C 278.21565,639.62955 302.26007,633.26385 302.26007,633.26385 L 308.87233,606.09973 L 113.16529,429.39362 z"
id="path3461"
sodipodi:nodetypes="cssssssccc"
inkscape:export-filename="/home/loa/Projects/indefero.net/media/idfn/img/indefero-logo.png"
inkscape:export-xdpi="10.974293"
inkscape:export-ydpi="10.974293" />
<text
xml:space="preserve"
style="font-size:500px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr-tb;text-anchor:start;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:FromageCondOblique;-inkscape-font-specification:FromageCondOblique"
x="739.95435"
y="534.80164"
id="text3481"
inkscape:export-filename="/home/loa/Projects/indefero.net/media/idfn/img/indefero-logo.png"
inkscape:export-xdpi="10.974293"
inkscape:export-ydpi="10.974293"><tspan
sodipodi:role="line"
id="tspan3483"
x="739.95435"
y="534.80164">indefero</tspan></text>
<text
xml:space="preserve"
style="font-size:150px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:FromageCondOblique;-inkscape-font-specification:FromageCondOblique"
x="744.05432"
y="711.55402"
id="text3485"
sodipodi:linespacing="125%"
inkscape:export-filename="/home/loa/Projects/indefero.net/media/idfn/img/indefero-logo.png"
inkscape:export-xdpi="10.974293"
inkscape:export-ydpi="10.974293"><tspan
sodipodi:role="line"
id="tspan3487"
x="744.05432"
y="711.55402"
style="letter-spacing:5.01085806">Better code management</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,172 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
width="96"
height="48"
id="svg0"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="star.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<metadata
id="metadata2536">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
inkscape:cy="15"
inkscape:cx="49.903061"
inkscape:zoom="4"
inkscape:window-height="755"
inkscape:window-width="969"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
showgrid="false"
inkscape:window-x="220"
inkscape:window-y="108"
inkscape:current-layer="svg0" />
<defs
id="defs">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 24 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="48 : 24 : 1"
inkscape:persp3d-origin="24 : 16 : 1"
id="perspective2538" />
<linearGradient
id="G0">
<stop
id="s1"
style="stop-color:#e6cf00;stop-opacity:1"
offset="0" />
<stop
id="s2"
style="stop-color:#fde94a;stop-opacity:1"
offset="1" />
</linearGradient>
<linearGradient
id="G1">
<stop
id="s3"
style="stop-color:#fcf9fb;stop-opacity:1"
offset="0" />
<stop
id="s4"
style="stop-color:#fcf9fb;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
id="G2">
<stop
id="s5"
style="stop-color:#000000;stop-opacity:0.63"
offset="0" />
<stop
id="s6"
style="stop-color:#000000;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
x1="14.660452"
y1="7.0243196"
x2="24.030643"
y2="34.826122"
id="G3"
xlink:href="#G1"
gradientUnits="userSpaceOnUse" />
<radialGradient
cx="24"
cy="22"
r="22"
fx="24"
fy="22"
id="R0"
xlink:href="#G0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,-0.2,0.2,1,-3.8,6.8)" />
<radialGradient
cx="17.3125"
cy="25.53125"
r="9.6875"
fx="17.3125"
fy="25.53125"
id="R1"
xlink:href="#G2"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.4,0,0,0.67,-17.1,22.4)" />
<linearGradient
inkscape:collect="always"
xlink:href="#G1"
id="linearGradient2548"
gradientUnits="userSpaceOnUse"
x1="14.660452"
y1="7.0243196"
x2="24.030643"
y2="34.826122"
gradientTransform="translate(47.948863,0.5)" />
</defs>
<path
d="M 37.310531,41.5 L 24.030644,34.826121 L 10.735101,41.471448 L 13.289408,27.369086 L 2.5511635,17.368344 L 17.409702,15.326301 L 24.068641,2.5 L 30.697416,15.340488 L 45.551111,17.414249 L 34.789382,27.392039 L 37.310531,41.5 z"
id="star"
style="fill:url(#R0);stroke:#c4a000"
inkscape:export-xdpi="36"
inkscape:export-ydpi="36" />
<path
d="M 17.731857,15.79089 L 24.06374,3.5868195 L 30.369452,15.794804 L 44.440148,17.761498 L 40.820395,21.121507 C 24.382895,17.434007 31.36502,28.341981 13.251748,30.364721 L 13.819799,27.184324 L 3.6604518,17.718688 C 3.6604518,17.718688 17.731857,15.79089 17.731857,15.79089 z"
id="hl"
style="opacity:0.8;fill:url(#G3);stroke:none"
inkscape:export-filename="/home/loa/Projects/indefero/www/media/idf/img/star.png"
inkscape:export-xdpi="36"
inkscape:export-ydpi="36" />
<path
d="M 35.973752,39.712833 L 24.028333,33.709386 L 12.082531,39.678326 L 14.362768,27.008543 L 4.7776396,18.070249 L 18.056076,16.247921 L 24.062792,4.6783526 L 30.045232,16.255741 L 43.322959,18.11306 L 33.713703,27.034785 L 35.973752,39.712833 z"
id="ol"
style="opacity:0.68999999;fill:none;stroke:#ffffff"
inkscape:export-filename="/home/loa/Projects/indefero/www/media/idf/img/star.png"
inkscape:export-xdpi="36"
inkscape:export-ydpi="36" />
<path
d="M 85.259394,42 L 71.979507,35.326121 L 58.683964,41.971448 L 61.238271,27.869086 L 50.500026,17.868344 L 65.358565,15.826301 L 72.017504,3 L 78.646279,15.840488 L 93.499974,17.914249 L 82.738245,27.892039 L 85.259394,42 z"
id="path2540"
style="fill:#d3d7cf;stroke:#babdb6"
inkscape:export-filename="/home/loa/Projects/indefero/www/media/idf/img/star-grey.png"
inkscape:export-xdpi="36"
inkscape:export-ydpi="36" />
<path
d="M 65.68072,16.29089 L 72.012603,4.08682 L 78.318315,16.294804 L 92.389011,18.261498 L 88.769258,21.621507 C 72.331758,17.934007 79.313883,28.841981 61.200611,30.864721 L 61.768662,27.684324 L 51.609315,18.218688 C 51.609315,18.218688 65.68072,16.29089 65.68072,16.29089 z"
id="path2542"
style="opacity:0.8;fill:url(#linearGradient2548);stroke:none"
inkscape:export-filename="/home/loa/Projects/indefero/www/media/idf/img/star-grey.png"
inkscape:export-xdpi="36"
inkscape:export-ydpi="36" />
<path
d="M 83.922615,40.212833 L 71.977196,34.209386 L 60.031394,40.178326 L 62.311631,27.508543 L 52.726502,18.570249 L 66.004939,16.747921 L 72.011655,5.178353 L 77.994095,16.755741 L 91.271822,18.61306 L 81.662566,27.534785 L 83.922615,40.212833 z"
id="path2544"
style="opacity:0.68999999;fill:none;stroke:#ffffff"
inkscape:export-filename="/home/loa/Projects/indefero/www/media/idf/img/star-grey.png"
inkscape:export-xdpi="36"
inkscape:export-ydpi="36" />
</svg>

Before

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -1,13 +0,0 @@
#!/bin/sh
last="$1"
new="$2"
PLUF_PATH=`php -r "require_once('./IDF/conf/path.php'); echo PLUF_PATH;"`
echo "php $PLUF_PATH/extracttemplates.php ./IDF/conf/idf.php ./IDF/gettexttemplates"
echo "xgettext -o idf.pot -p ./IDF/locale --force-po --from-code=UTF-8 --keyword --keyword=__ --keyword=_n:1,2 -L PHP ./IDF/*.php"
echo "find ./ -iname \"*.php\" -exec xgettext -o idf.pot -p ./IDF/locale/ --from-code=UTF-8 -j --keyword --keyword=__ --keyword=_n:1,2 -L PHP {} \;"
echo 'for pofile in `ls ./IDF/locale/*/idf.po`; do msgmerge -U $pofile ./IDF/locale/idf.pot; done'
echo "# git tag v$new"
echo "git archive --format=zip --prefix=indefero-$new/ v$new > indefero-$new.zip"
echo "git log --no-merges v$new ^v$last > ChangeLog-$new"
echo "git shortlog --no-merges v$new ^v$last > ShortLog"
echo "git diff --stat --summary -M v$last v$new > diffstat-$new"

View File

@ -1,10 +0,0 @@
#!/bin/sh
private_notify="/home/indefero/tmp/notify.tmp"
reload_cmd="/usr/sbin/apachectl -k graceful"
if [ -e $private_notify ]; then
rm -f $private_notify
$reload_cmd
fi

View File

@ -1,24 +0,0 @@
#!/bin/sh
#
# This hook does only one thing:
#
# 1. It calls the gitpostupdate.php script with the current $GIT_DIR
# as argument. The gitpostupdate.php script will then trigger
# the 'gitpostupdate.php::run' event with the $GIT_DIR as argument
# together with merged $_ENV and $_SERVER array.
#
# This hook is normally installed automatically at the creation of your
# repository if you have everything configured correctly. If you want
# to enable it later, you need to symlink it as "post-update" in your
# $GIT_DIR/hooks folder.
#
# www$ chmod +x /home/www/indefero/scripts/git-post-update
# git$ cd /home/git/repositories/project.git/hooks
# git$ ln -s /home/www/indefero/scripts/git-post-update post-update
#
SCRIPTDIR=$(dirname $(readlink $0))
FULL_GIT_DIR=$(cd "$GIT_DIR" && /bin/pwd || "$GIT_DIR")
PHP_POST_UPDATE=$SCRIPTDIR/gitpostupdate.php
echo php $PHP_POST_UPDATE $FULL_GIT_DIR | at now > /dev/null 2>&1

View File

@ -1,35 +0,0 @@
<?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 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 ***** */
/**
* This script is used to sync the SSH keys, mark the repositories for
* export and prune the deleted repositories.
*
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
IDF_Plugin_SyncGit_Cron::main();

View File

@ -1,59 +0,0 @@
<?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-2010 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 ***** */
/**
* This script will send the notifications after a push in your
* repository.
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
/**
* [signal]
*
* gitpostupdate.php::run
*
* [sender]
*
* gitpostupdate.php
*
* [description]
*
* This signal allows an application to perform a set of tasks on a
* post update of a git repository.
*
* [parameters]
*
* array('git_dir' => '/path/to/git/repository.git',
* 'env' => array_merge($_ENV, $_SERVER));
*
*/
$params = array('git_dir' => $argv[1],
'env' => array_merge($_ENV, $_SERVER));
Pluf_Log::event(array('gitpostupdate.php', 'Send run signal.', $params));
Pluf_Signal::send('gitpostupdate.php::run', 'gitpostupdate.php', $params);

View File

@ -1,36 +0,0 @@
<?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 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 ***** */
/**
* This script is used to control the access to the git repositories
* using a restricted shell access.
*
* The only argument must be the login of the user.
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
IDF_Plugin_SyncGit_Serve::main($argv, array_merge($_SERVER, $_ENV));

View File

@ -1,36 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 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 ***** */
import os
import sys
import commands
import traceback
n = len("/gitserve.py")
GITSERVEPHP = '%s/gitserve.php' % traceback.extract_stack(limit=1)[0][0][0:-n]
status, output = commands.getstatusoutput('php %s %s' % (GITSERVEPHP, sys.argv[1]))
if status == 0:
os.execvp('git', ['git', 'shell', '-c', output.strip()])
else:
sys.stderr.write("%s\n" % output)
sys.exit(1)

View File

@ -1,61 +0,0 @@
#!/usr/bin/env php
<?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-2010 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 ***** */
/**
* This script will send the notifications after a push in your
* repository.
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
/**
* [signal]
*
* hgchangegroup.php::run
*
* [sender]
*
* hgchangegroup.php
*
* [description]
*
* This signal allows an application to perform a set of tasks on a
* group change hook of a Mercurial repository. The rel_dir is a
* relative path to the root of your hg repositories but starting with
* a slash.
*
* [parameters]
*
* array('rel_dir' => '/relative/path/to/hg/repository',
* 'env' => array_merge($_ENV, $_SERVER));
*
*/
$params = array('rel_dir' => $_ENV['PATH_INFO'],
'env' => array_merge($_ENV, $_SERVER));
Pluf_Signal::send('hgchangegroup.php::run', 'hgchangegroup.php', $params);

View File

@ -1,22 +0,0 @@
#!/bin/sh
#
# This hook informs IDF that new revisions arrived in the database
# of the specified project.
#
# This hook is normally installed automatically at the creation of your
# repository if you have everything configured correctly. If you want
# to enable it later, you need to call it into your monotonerc file
# from the hook "note_netsync_end". (See chapter "Event Notifications
# and Triggers" on <http://monotone.ca/docs/Hooks.html#Hooks>.)
#
dir=$(dirname "$0")
res=$(cd "$dir" && /bin/pwd || "$dir")
SCRIPTDIR="$res/$(readlink $0)"
PHP_POST_PUSH=$SCRIPTDIR/mtnpostpush.php
TMPFILE=$(mktemp /tmp/mtn-post-push.XXXXXX) || exit 1
while read rev; do echo $rev >> $TMPFILE; done
echo php $PHP_POST_PUSH "$1" \< $TMPFILE \&\& rm -f $TMPFILE |\
at now > /dev/null 2>&1

View File

@ -1,63 +0,0 @@
<?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-2010 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 ***** */
/**
* This script will send the notifications after a push in your
* repository.
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
/**
* [signal]
*
* mtnpostpush.php::run
*
* [sender]
*
* mtnpostpush.php
*
* [description]
*
* This signal allows an application to perform a set of tasks
* after a push to a monotone repository.
*
* [parameters]
*
* array('project' => 'name-of-the-project',
* 'revisions' => array('123abc...', '456def...', ...));
*
*/
fwrite(STDERR, "waiting for revisions on STDIN...\n");
$stdin = file_get_contents('php://stdin');
$params = array('project' => $argv[1],
'revisions' => explode("\n", chop($stdin)));
Pluf_Signal::send('mtnpostpush.php::run', 'mtnpostpush.php', $params);

View File

@ -1,69 +0,0 @@
<?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 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 ***** */
/**
* This script process the queue of items.
*
* At the moment the queue is only used for the webhooks, but it would
* be good in the future to use it for indexing and email
* notifications.
*
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
#;*/ ::
$lock_file = Pluf::f('idf_queuecron_lock',
Pluf::f('tmp_folder', '/tmp').'/queuecron.lock');
if (file_exists($lock_file)) {
Pluf_Log::event(array('queuecron.php', 'skip'));
return;
}
file_put_contents($lock_file, time(), LOCK_EX);
/**
* [signal]
*
* queuecron.php::run
*
* [sender]
*
* queuecron.php
*
* [description]
*
* This signal allows an application to perform a set of tasks when
* the queue cron job is run. This is done usually every 5 minutes.
*
* [parameters]
*
* array()
*
*/
$params = array();
Pluf_Signal::send('queuecron.php::run', 'queuecron.php', $params);
unlink($lock_file);

View File

@ -1,24 +0,0 @@
#!/bin/sh
#
# This hook does only one thing:
#
# 1. It calls the svnpostcommit.php script with the current repository
# and revision as argument. The svnpostcommit.php script will then
# trigger the 'svnpostcommit.php::run' event with the repository path
# and revision as arguments together with merged $_ENV and $_SERVER
# array.
#
# This hook is normally installed automatically at the creation of your
# repository if you have everything configured correctly. If you want
# to enable it later, you need to symlink it as "post-commit" in your
# $REPOSITORY/hooks folder.
#
# www$ chmod +x /home/www/indefero/scripts/svn-post-commit
# www$ cd /home/svn/repositories/project/hooks
# www$ ln -s /home/www/indefero/scripts/svn-post-commit post-commit
#
SCRIPTDIR=$(dirname $(readlink $0))
PHP_POST_COMMIT=$SCRIPTDIR/svnpostcommit.php
echo php $PHP_POST_COMMIT "$1" "$2" | at now > /dev/null 2>&1

View File

@ -1,29 +0,0 @@
#!/bin/sh
#
# This hook does only one thing:
#
# 1. It calls the svnpostrevpropchange.php script with the current repository
# and revision as argument. The svnpostrevpropchange.php script will then
# trigger the 'svnpostrevpropchange.php::run' event with the repository
# path, revision, username, property name and action as arguments together
# with merged $_ENV and $_SERVER array.
#
# This hook is normally installed automatically at the creation of your
# repository if you have everything configured correctly. If you want
# to enable it later, you need to symlink it as "post-revprop-change" in your
# $REPOSITORY/hooks folder. It needs to be executable.
#
# www$ chmod +x /home/www/indefero/scripts/svn-post-revprop-change
# www$ cd /home/svn/repositories/project/hooks
# www$ ln -s /home/www/indefero/scripts/svn-post-revprop-change post-revprop-change
#
SCRIPTDIR=$(dirname $(readlink $0))
PHP_POST_REVPROP=$SCRIPTDIR/svnpostrevpropchange.php
echo php $PHP_POST_REVPROP "$1" "$2" "$3" "$4" "$5" | at now > /dev/null 2>&1
REPOS="$1"
REV="$2"
USER="$3"
PROPNAME="$4"
ACTION="$5"

View File

@ -1,62 +0,0 @@
<?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-2010 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 ***** */
/**
* This script will send the notifications after a push in your
* repository.
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
/**
* [signal]
*
* svnpostcommit.php::run
*
* [sender]
*
* svnpostcommit.php
*
* [description]
*
* This signal allows an application to perform a set of tasks on a
* post commit of a subversion repository.
*
* [parameters]
*
* array('repo_dir' => '/path/to/subversion/repository',
* 'revision' => 1234,
* 'env' => array_merge($_ENV, $_SERVER));
*
*/
$params = array('repo_dir' => $argv[1],
'revision' => $argv[2],
'env' => array_merge($_ENV, $_SERVER));
Pluf_Log::event(array('svnpostcommit.php', 'Send run signal.', $params));
Pluf_Signal::send('svnpostcommit.php::run', 'svnpostcommit.php', $params);

View File

@ -1,70 +0,0 @@
<?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-2010 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 ***** */
/**
* This script allows you to hook into the post-revprop-change action
* of your subversion repository. I am using it to perform near real
* time backup of the repositories on indefero.net.
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
/**
* [signal]
*
* svnpostrevpropchange.php::run
*
* [sender]
*
* svnpostrevpropchange.php
*
* [description]
*
* This signal allows an application to perform a set of tasks on a
* post property revision change of a subversion repository.
*
* [parameters]
*
* array('repo_dir' => '/path/to/subversion/repository',
* 'revision' => 1234,
* 'user' => 'username',
* 'propname' => 'changed-property',
* 'action' => 'the action M, A or D',
* 'env' => array_merge($_ENV, $_SERVER));
*
*/
$params = array('repo_dir' => $argv[1],
'revision' => $argv[2],
'user' => $argv[3],
'propname' => $argv[4],
'action' => $argv[5],
'env' => array_merge($_ENV, $_SERVER));
Pluf_Signal::send('svnpostrevpropchange.php::run', 'svnpostrevpropchange.php',
$params);

View File

@ -1,337 +0,0 @@
<?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 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
n# 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 ***** */
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
Pluf::loadFunction('Pluf_Template_dateAgo');
/**
* Base definition of a commit.
*
* By having a reference in the database for each commit, one can
* easily generate a timeline or use the search engine. Commit details
* are normally always taken from the underlining SCM.
*/
class IDF_Commit extends Pluf_Model
{
public $_model = __CLASS__;
public $extra = null; /**< Extra data as IDF_Gconf object */
function init()
{
$this->_a['table'] = 'idf_commits';
$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,
),
'project' =>
array(
'type' => 'Pluf_DB_Field_Foreignkey',
'model' => 'IDF_Project',
'blank' => false,
'verbose' => __('project'),
'relate_name' => 'commits',
),
'author' =>
array(
'type' => 'Pluf_DB_Field_Foreignkey',
'model' => 'Pluf_User',
'is_null' => true,
'verbose' => __('submitter'),
'relate_name' => 'submitted_commit',
'help_text' => 'This will allow us to list the latest commits of a user in its profile.',
),
'origauthor' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 150,
'help_text' => 'As we do not necessary have the mapping between the author in the database and the scm, we store the scm author commit information here. That way we can update the author info later in the process.',
),
'scm_id' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 50,
'index' => true,
'help_text' => 'The id of the commit. For git, it will be the SHA1 hash, for subversion it will be the revision id.',
),
'summary' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 250,
'verbose' => __('summary'),
),
'fullmessage' =>
array(
'type' => 'Pluf_DB_Field_Text',
'blank' => true,
'verbose' => __('changelog'),
'help_text' => 'This is the full message of the commit.',
),
'creation_dtime' =>
array(
'type' => 'Pluf_DB_Field_Datetime',
'blank' => true,
'verbose' => __('creation date'),
'index' => true,
'help_text' => 'Date of creation by the scm',
),
);
}
function __toString()
{
return $this->summary.' - ('.$this->scm_id.')';
}
function _toIndex()
{
$str = str_repeat($this->summary.' ', 4).' '.$this->fullmessage;
return Pluf_Text::cleanString(html_entity_decode($str, ENT_QUOTES, 'UTF-8'));
}
function postSave($create=false)
{
IDF_Search::index($this);
if ($create) {
IDF_Timeline::insert($this, $this->get_project(),
$this->get_author(), $this->creation_dtime);
}
}
function preDelete()
{
IDF_Timeline::remove($this);
IDF_Search::remove($this);
IDF_Gconf::dropForModel($this);
}
/**
* Create a commit from a simple class commit info of a changelog.
*
* @param stdClass Commit info
* @param IDF_Project Current project
* @return IDF_Commit
*/
public static function getOrAdd($change, $project)
{
$sql = new Pluf_SQL('project=%s AND scm_id=%s',
array($project->id, $change->commit));
$r = Pluf::factory('IDF_Commit')->getList(array('filter'=>$sql->gen()));
if ($r->count() > 0) {
$r[0]->extra = new IDF_Gconf();
$r[0]->extra->serialize = true;
$r[0]->extra->setModel($r[0]);
$r[0]->extra->initCache();
return $r[0];
}
if (!isset($change->full_message)) {
$change->full_message = '';
}
$scm = IDF_Scm::get($project);
$commit = new IDF_Commit();
$commit->project = $project;
$commit->scm_id = $change->commit;
$commit->summary = self::toUTF8($change->title);
$commit->fullmessage = self::toUTF8($change->full_message);
$commit->author = $scm->findAuthor($change->author);
$commit->origauthor = self::toUTF8($change->author);
$commit->creation_dtime = $change->date;
$commit->create();
$extra = $scm->getExtraProperties($change);
$commit->extra = new IDF_Gconf();
$commit->extra->serialize = true; // As we can store arrays
$commit->extra->setModel($commit);
foreach ($extra as $key => $val) {
$commit->extra->setVal($key, $val);
}
$commit->notify($project->getConf());
return $commit;
}
/**
* Convert encoding to UTF8.
*
* If an array is given, the encoding is detected only on the
* first value and then used to convert all the strings.
*
* @param mixed String or array of string to be converted
* @param bool Returns the encoding together with the converted text (false)
* @return mixed String or array of string or array of res + encoding
*/
public static function toUTF8($text, $get_encoding=False)
{
$enc = 'ASCII, UTF-8, ISO-8859-1, JIS, EUC-JP, SJIS';
$ref = $text;
if (is_array($text)) {
$ref = $text[0];
}
if (Pluf_Text_UTF8::check($ref)) {
return (!$get_encoding) ? $text : array($text, 'UTF-8');
}
$encoding = mb_detect_encoding($ref, $enc, true);
if ($encoding == false) {
$encoding = Pluf_Text_UTF8::detect_cyr_charset($ref);
}
if (is_array($text)) {
foreach ($text as $t) {
$res[] = mb_convert_encoding($t, 'UTF-8', $encoding);
}
return (!$get_encoding) ? $res : array($res, $encoding);
} else {
$res = mb_convert_encoding($text, 'UTF-8', $encoding);
return (!$get_encoding) ? $res : array($res, $encoding);
}
}
/**
* Returns the timeline fragment for the commit.
*
*
* @param Pluf_HTTP_Request
* @return Pluf_Template_SafeString
*/
public function timelineFragment($request)
{
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::commit',
array($request->project->shortname,
$this->scm_id));
$out = '<tr class="log"><td><a href="'.$url.'">'.
Pluf_esc(Pluf_Template_dateAgo($this->creation_dtime, 'without')).
'</a></td><td>';
$stag = new IDF_Template_ShowUser();
$user = $stag->start($this->get_author(), $request, $this->origauthor, false);
$tag = new IDF_Template_IssueComment();
$out .= $tag->start($this->summary, $request, false);
if (0 && $this->fullmessage) {
$out .= '<br /><br />'.$tag->start($this->fullmessage, $request, false);
}
$out .= '</td>
</tr>
<tr class="extra">
<td colspan="2">
<div class="helptext right">'.sprintf(__('Commit&nbsp;%s, by %s'), '<a href="'.$url.'" class="mono">'.$this->scm_id.'</a>', $user).'</div></td></tr>';
return Pluf_Template::markSafe($out);
}
/**
* Returns the feed fragment for the commit.
*
* @param Pluf_HTTP_Request
* @return Pluf_Template_SafeString
*/
public function feedFragment($request)
{
$url = Pluf::f('url_base')
.Pluf_HTTP_URL_urlForView('IDF_Views_Source::commit',
array($request->project->shortname,
$this->scm_id));
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
$author = ($this->get_author()) ?
$this->get_author() : $this->origauthor;
$cproject = $this->get_project();
$context = new Pluf_Template_Context_Request(
$request,
array(
'c' => $this,
'cproject' => $cproject,
'url' => $url,
'date' => $date,
'author' => $author,
)
);
$tmpl = new Pluf_Template('idf/source/feedfragment.xml');
return $tmpl->render($context);
}
/**
* Notification of change of the object.
*
* @param IDF_Conf Current configuration
* @param bool Creation (true)
*/
public function notify($conf, $create=true)
{
// Now we add to the queue, soon we will push everything in
// the queue, including email notifications and indexing.
// Even if the url is empty, we add to the queue as some
// plugins may want to do something with this information in
// an asynchronous way.
$project = $this->get_project();
$scm = $project->getConf()->getVal('scm', 'git');
$url = str_replace(array('%p', '%r'),
array($project->shortname, $this->scm_id),
$conf->getVal('webhook_url', ''));
$payload = array('to_send' => array(
'project' => $project->shortname,
'rev' => $this->scm_id,
'scm' => $scm,
'summary' => $this->summary,
'fullmessage' => $this->fullmessage,
'author' => $this->origauthor,
'creation_date' => $this->creation_dtime,
),
'project_id' => $project->id,
'authkey' => $project->getPostCommitHookKey(),
'url' => $url,
);
$item = new IDF_Queue();
$item->type = 'new_commit';
$item->payload = $payload;
$item->create();
if ('' == $conf->getVal('source_notification_email', '')) {
return;
}
$current_locale = Pluf_Translation::getLocale();
$langs = Pluf::f('languages', array('en'));
Pluf_Translation::loadSetLocale($langs[0]);
$context = new Pluf_Template_Context(
array(
'c' => $this,
'project' => $this->get_project(),
'url_base' => Pluf::f('url_base'),
)
);
$tmpl = new Pluf_Template('idf/source/commit-created-email.txt');
$text_email = $tmpl->render($context);
$addresses = explode(',', $conf->getVal('source_notification_email'));
foreach ($addresses as $address) {
$email = new Pluf_Mail(Pluf::f('from_email'),
$address,
sprintf(__('New Commit %s - %s (%s)'),
$this->scm_id, $this->summary,
$this->get_project()->shortname));
$email->addTextMessage($text_email);
$email->sendMail();
}
Pluf_Translation::loadSetLocale($current_locale);
}
}

View File

@ -62,8 +62,9 @@ class IDF_Conf extends Pluf_Model
), ),
'vdesc' => 'vdesc' =>
array( array(
'type' => 'Pluf_DB_Field_Text', 'type' => 'Pluf_DB_Field_Varchar',
'blank' => false, 'blank' => false,
'size' => 250,
'verbose' => __('value'), 'verbose' => __('value'),
), ),
); );
@ -73,7 +74,7 @@ class IDF_Conf extends Pluf_Model
'type' => 'unique', 'type' => 'unique',
), ),
); );
$this->f = new IDF_Config_DataProxy($this); $this->f = new IDF_Conf_DataProxy($this);
} }
function setProject($project) function setProject($project)
@ -85,7 +86,7 @@ class IDF_Conf extends Pluf_Model
function initCache() function initCache()
{ {
$this->datacache = new ArrayObject(); $this->datacache = new ArrayObject();
$sql = new Pluf_SQL('project=%s', $this->_project->id); $sql = new Pluf_SQL('project=%s', $this->_project);
foreach ($this->getList(array('filter' => $sql->gen())) as $val) { foreach ($this->getList(array('filter' => $sql->gen())) as $val) {
$this->datacache[$val->vkey] = $val->vdesc; $this->datacache[$val->vkey] = $val->vdesc;
} }
@ -121,7 +122,7 @@ class IDF_Conf extends Pluf_Model
function delVal($key, $initcache=true) function delVal($key, $initcache=true)
{ {
$gconf = new IDF_Conf(); $gconf = new IDF_Conf();
$sql = new Pluf_SQL('vkey=%s AND project=%s', array($key, $this->_project->id)); $sql = new Pluf_SQL('vkey=%s AND project=%s', array($key, $this->_project));
foreach ($gconf->getList(array('filter' => $sql->gen())) as $c) { foreach ($gconf->getList(array('filter' => $sql->gen())) as $c) {
$c->delete(); $c->delete();
} }

View File

@ -26,7 +26,7 @@
* {$conf.f.fieldname} in a template. * {$conf.f.fieldname} in a template.
*/ */
class IDF_Config_DataProxy class IDF_Conf_DataProxy
{ {
protected $obj = null; protected $obj = null;

View File

@ -46,63 +46,13 @@ class IDF_Diff
$current_chunk = 0; $current_chunk = 0;
$lline = 0; $lline = 0;
$rline = 0; $rline = 0;
$files = array();
$indiff = false; // Used to skip the headers in the git patches
$i = 0; // Used to skip the end of a git patch with --\nversion number
foreach ($this->lines as $line) { foreach ($this->lines as $line) {
$i++;
if (0 === strpos($line, '--') and isset($this->lines[$i])
and preg_match('/^\d+\.\d+\.\d+\.\d+$/', $this->lines[$i])) {
break;
}
if (0 === strpos($line, 'diff --git a')) { if (0 === strpos($line, 'diff --git a')) {
$current_file = self::getFile($line); $current_file = self::getFile($line);
$files[$current_file] = array(); $files[$current_file] = array();
$files[$current_file]['chunks'] = array(); $files[$current_file]['chunks'] = array();
$files[$current_file]['chunks_def'] = array(); $files[$current_file]['chunks_def'] = array();
$current_chunk = 0; $current_chunk = 0;
$indiff = true;
continue;
} else if (preg_match('#^diff -r [^\s]+ -r [^\s]+ (.+)$#', $line, $matches)) {
$current_file = $matches[1];
$files[$current_file] = array();
$files[$current_file]['chunks'] = array();
$files[$current_file]['chunks_def'] = array();
$current_chunk = 0;
$indiff = true;
continue;
} else if (!$indiff && 0 === strpos($line, '=========')) {
// ignore pseudo stanzas with a hint of a binary file
if (preg_match("/^# (.+) is binary/", $this->lines[$i]))
continue;
// by default always use the new name of a possibly renamed file
$current_file = self::getMtnFile($this->lines[$i+1]);
// mtn 0.48 and newer set /dev/null as file path for dropped files
// so we display the old name here
if ($current_file == "/dev/null") {
$current_file = self::getMtnFile($this->lines[$i]);
}
if ($current_file == "/dev/null") {
throw new Exception(
"could not determine path from diff"
);
}
$files[$current_file] = array();
$files[$current_file]['chunks'] = array();
$files[$current_file]['chunks_def'] = array();
$current_chunk = 0;
$indiff = true;
continue;
} else if (0 === strpos($line, 'Index: ')) {
$current_file = self::getSvnFile($line);
$files[$current_file] = array();
$files[$current_file]['chunks'] = array();
$files[$current_file]['chunks_def'] = array();
$current_chunk = 0;
$indiff = true;
continue;
}
if (!$indiff) {
continue; continue;
} }
if (0 === strpos($line, '@@ ')) { if (0 === strpos($line, '@@ ')) {
@ -132,12 +82,6 @@ class IDF_Diff
$lline++; $lline++;
continue; continue;
} }
if ($line == '') {
$files[$current_file]['chunks'][$current_chunk-1][] = array($lline, $rline, $line);
$rline++;
$lline++;
continue;
}
} }
$this->files = $files; $this->files = $files;
return $files; return $files;
@ -150,17 +94,6 @@ class IDF_Diff
return trim(substr($line, 3, $n-3)); return trim(substr($line, 3, $n-3));
} }
public static function getSvnFile($line)
{
return substr(trim($line), 7);
}
public static function getMtnFile($line)
{
preg_match("/^[+-]{3} ([^\t]+)/", $line, $m);
return $m[1];
}
/** /**
* Return the html version of a parsed diff. * Return the html version of a parsed diff.
*/ */
@ -168,11 +101,6 @@ class IDF_Diff
{ {
$out = ''; $out = '';
foreach ($this->files as $filename=>$file) { foreach ($this->files as $filename=>$file) {
$pretty = '';
$fileinfo = IDF_FileUtil::getMimeType($filename);
if (IDF_FileUtil::isSupportedExtension($fileinfo[2])) {
$pretty = ' prettyprint';
}
$out .= "\n".'<table class="diff" summary="">'."\n"; $out .= "\n".'<table class="diff" summary="">'."\n";
$out .= '<tr id="diff-'.md5($filename).'"><th colspan="3">'.Pluf_esc($filename).'</th></tr>'."\n"; $out .= '<tr id="diff-'.md5($filename).'"><th colspan="3">'.Pluf_esc($filename).'</th></tr>'."\n";
$cc = 1; $cc = 1;
@ -185,8 +113,8 @@ class IDF_Diff
} else { } else {
$class = 'diff-a'; $class = 'diff-a';
} }
$line_content = self::padLine(Pluf_esc($line[2])); $line_content = $this->padLine(Pluf_esc($line[2]));
$out .= sprintf('<tr class="diff-line"><td class="diff-lc">%s</td><td class="diff-lc">%s</td><td class="%s%s mono">%s</td></tr>'."\n", $line[0], $line[1], $class, $pretty, $line_content); $out .= sprintf('<tr class="diff-line"><td class="diff-lc">%s</td><td class="diff-lc">%s</td><td class="%s mono">%s</td></tr>'."\n", $line[0], $line[1], $class, $line_content);
} }
if (count($file['chunks']) > $cc) if (count($file['chunks']) > $cc)
$out .= '<tr class="diff-next"><td>...</td><td>...</td><td>&nbsp;</td></tr>'."\n"; $out .= '<tr class="diff-next"><td>...</td><td>...</td><td>&nbsp;</td></tr>'."\n";
@ -194,13 +122,12 @@ class IDF_Diff
} }
$out .= '</table>'; $out .= '</table>';
} }
return Pluf_Template::markSafe($out); return $out;
} }
public static function padLine($line) public function padLine($line)
{ {
$line = str_replace("\t", ' ', $line);
$n = strlen($line); $n = strlen($line);
for ($i=0;$i<$n;$i++) { for ($i=0;$i<$n;$i++) {
if (substr($line, $i, 1) != ' ') { if (substr($line, $i, 1) != ' ') {
@ -215,172 +142,12 @@ class IDF_Diff
*/ */
public static function getChunk($line) public static function getChunk($line)
{ {
$elts = explode(' ', $line); $elts = split(' ', $line);
$res = array(); $res = array();
for ($i=1;$i<3;$i++) { for ($i=1;$i<3;$i++) {
$res[] = explode(',', trim(substr($elts[$i], 1))); $res[] = split(',', trim(substr($elts[$i], 1)));
} }
return $res; return $res;
} }
/**
* Review patch.
*
* Given the original file as a string and the parsed
* corresponding diff chunks, generate a side by side view of the
* original file and new file with added/removed lines.
*
* Example of use:
*
* $diff = new IDF_Diff(file_get_contents($diff_file));
* $orig = file_get_contents($orig_file);
* $diff->parse();
* echo $diff->fileCompare($orig, $diff->files[$orig_file], $diff_file);
*
* @param string Original file
* @param array Chunk description of the diff corresponding to the file
* @param string Original file name
* @param int Number of lines before/after the chunk to be displayed (10)
* @return Pluf_Template_SafeString The table body
*/
public function fileCompare($orig, $chunks, $filename, $context=10)
{
$orig_lines = preg_split("/\015\012|\015|\012/", $orig);
$new_chunks = $this->mergeChunks($orig_lines, $chunks, $context);
return $this->renderCompared($new_chunks, $filename);
}
public function mergeChunks($orig_lines, $chunks, $context=10)
{
$spans = array();
$new_chunks = array();
$min_line = 0;
$max_line = 0;
//if (count($chunks['chunks_def']) == 0) return '';
foreach ($chunks['chunks_def'] as $chunk) {
$start = ($chunk[0][0] > $context) ? $chunk[0][0]-$context : 0;
$end = (($chunk[0][0]+$chunk[0][1]+$context-1) < count($orig_lines)) ? $chunk[0][0]+$chunk[0][1]+$context-1 : count($orig_lines);
$spans[] = array($start, $end);
}
// merge chunks/get the chunk lines
// these are reference lines
$chunk_lines = array();
foreach ($chunks['chunks'] as $chunk) {
foreach ($chunk as $line) {
$chunk_lines[] = $line;
}
}
$i = 0;
foreach ($chunks['chunks'] as $chunk) {
$n_chunk = array();
// add lines before
if ($chunk[0][0] > $spans[$i][0]) {
for ($lc=$spans[$i][0];$lc<$chunk[0][0];$lc++) {
$exists = false;
foreach ($chunk_lines as $line) {
if ($lc == $line[0]
or ($chunk[0][1]-$chunk[0][0]+$lc) == $line[1]) {
$exists = true;
break;
}
}
if (!$exists) {
$orig = isset($orig_lines[$lc-1]) ? $orig_lines[$lc-1] : '';
$n_chunk[] = array(
$lc,
$chunk[0][1]-$chunk[0][0]+$lc,
$orig
);
}
}
}
// add chunk lines
foreach ($chunk as $line) {
$n_chunk[] = $line;
}
// add lines after
$lline = $line;
if (!empty($lline[0]) and $lline[0] < $spans[$i][1]) {
for ($lc=$lline[0];$lc<=$spans[$i][1];$lc++) {
$exists = false;
foreach ($chunk_lines as $line) {
if ($lc == $line[0] or ($lline[1]-$lline[0]+$lc) == $line[1]) {
$exists = true;
break;
}
}
if (!$exists) {
$n_chunk[] = array(
$lc,
$lline[1]-$lline[0]+$lc,
$orig_lines[$lc-1]
);
}
}
}
$new_chunks[] = $n_chunk;
$i++;
}
// Now, each chunk has the right length, we need to merge them
// when needed
$nnew_chunks = array();
$i = 0;
foreach ($new_chunks as $chunk) {
if ($i>0) {
$lline = end($nnew_chunks[$i-1]);
if ($chunk[0][0] <= $lline[0]+1) {
// need merging
foreach ($chunk as $line) {
if ($line[0] > $lline[0] or empty($line[0])) {
$nnew_chunks[$i-1][] = $line;
}
}
} else {
$nnew_chunks[] = $chunk;
$i++;
}
} else {
$nnew_chunks[] = $chunk;
$i++;
}
}
return $nnew_chunks;
}
public function renderCompared($chunks, $filename)
{
$fileinfo = IDF_FileUtil::getMimeType($filename);
$pretty = '';
if (IDF_FileUtil::isSupportedExtension($fileinfo[2])) {
$pretty = ' prettyprint';
}
$out = '';
$cc = 1;
$i = 0;
foreach ($chunks as $chunk) {
foreach ($chunk as $line) {
$line1 = '&nbsp;';
$line2 = '&nbsp;';
$line[2] = (strlen($line[2])) ? self::padLine(Pluf_esc($line[2])) : '&nbsp;';
if ($line[0] and $line[1]) {
$class = 'diff-c';
$line1 = $line2 = $line[2];
} elseif ($line[0]) {
$class = 'diff-r';
$line1 = $line[2];
} else {
$class = 'diff-a';
$line2 = $line[2];
}
$out .= sprintf('<tr class="diff-line"><td class="diff-lc">%s</td><td class="%s mono%s"><code>%s</code></td><td class="diff-lc">%s</td><td class="%s mono%s"><code>%s</code></td></tr>'."\n", $line[0], $class, $pretty, $line1, $line[1], $class, $pretty, $line2);
}
if (count($chunks) > $cc)
$out .= '<tr class="diff-next"><td>...</td><td>&nbsp;</td><td>...</td><td>&nbsp;</td></tr>'."\n";
$cc++;
$i++;
}
return Pluf_Template::markSafe($out);
}
} }

View File

@ -1,165 +0,0 @@
<?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) 2010 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 ***** */
/**
* File utilities.
*
*/
class IDF_FileUtil
{
/**
* Extension supported by the syntax highlighter.
*/
public static $supportedExtenstions = array(
'ascx', 'ashx', 'asmx', 'aspx', 'browser', 'bsh', 'c', 'cl', 'cc',
'config', 'cpp', 'cs', 'csh', 'csproj', 'css', 'cv', 'cyc', 'el', 'fs',
'h', 'hh', 'hpp', 'hs', 'html', 'html', 'java', 'js', 'lisp', 'master',
'pas', 'perl', 'php', 'pl', 'pm', 'py', 'rb', 'scm', 'sh', 'sitemap',
'skin', 'sln', 'svc', 'vala', 'vb', 'vbproj', 'vbs', 'wsdl', 'xhtml',
'xml', 'xsd', 'xsl', 'xslt');
/**
* Test if an extension is supported by the syntax highlighter.
*
* @param string The extension to test
* @return bool
*/
public static function isSupportedExtension($extension)
{
return in_array($extension, self::$supportedExtenstions);
}
/**
* Returns a HTML snippet with a line-by-line pre-rendered table
* for the given source content
*
* @param array file information as returned by getMimeType or getMimeTypeFromContent
* @param string the content of the file
* @return string
*/
public static function highLight($fileinfo, $content)
{
$pretty = '';
if (self::isSupportedExtension($fileinfo[2])) {
$pretty = ' prettyprint';
}
$table = array();
$i = 1;
foreach (preg_split("/\015\012|\015|\012/", $content) as $line) {
$table[] = '<tr class="c-line"><td class="code-lc" id="L'.$i.'"><a href="#L'.$i.'">'.$i.'</a></td>'
.'<td class="code mono'.$pretty.'">'.IDF_Diff::padLine(Pluf_esc($line)).'</td></tr>';
$i++;
}
return Pluf_Template::markSafe(implode("\n", $table));
}
/**
* Find the mime type of a file.
*
* Use /etc/mime.types to find the type.
*
* @param string Filename/Filepath
* @param array Mime type found or 'application/octet-stream', basename, extension
*/
public static function getMimeType($file)
{
static $mimes = null;
if ($mimes == null) {
$mimes = array();
$src = Pluf::f('idf_mimetypes_db', '/etc/mime.types');
$filecontent = @file_get_contents($src);
if ($filecontent !== false) {
$mimes = preg_split("/\015\012|\015|\012/", $filecontent);
}
}
$info = pathinfo($file);
if (isset($info['extension'])) {
foreach ($mimes as $mime) {
if ('#' != substr($mime, 0, 1)) {
$elts = preg_split('/ |\t/', $mime, -1, PREG_SPLIT_NO_EMPTY);
if (in_array($info['extension'], $elts)) {
return array($elts[0], $info['basename'], $info['extension']);
}
}
}
} else {
// we consider that if no extension and base name is all
// uppercase, then we have a text file.
if ($info['basename'] == strtoupper($info['basename'])) {
return array('text/plain', $info['basename'], 'txt');
}
$info['extension'] = 'bin';
}
return array('application/octet-stream', $info['basename'], $info['extension']);
}
/**
* Find the mime type of a file using the fileinfo class.
*
* @param string Filename/Filepath
* @param string File content
* @return array Mime type found or 'application/octet-stream', basename, extension
*/
public static function getMimeTypeFromContent($file, $filedata)
{
$info = pathinfo($file);
$res = array('application/octet-stream',
$info['basename'],
isset($info['extension']) ? $info['extension'] : 'bin');
if (function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME);
$mime = finfo_buffer($finfo, $filedata);
finfo_close($finfo);
if ($mime) {
$res[0] = $mime;
}
if (!isset($info['extension']) && $mime) {
$res[2] = (0 === strpos($mime, 'text/')) ? 'txt' : 'bin';
} elseif (!isset($info['extension'])) {
$res[2] = 'bin';
}
}
return $res;
}
/**
* Find if a given mime type is a text file.
* This uses the output of the self::getMimeType function.
*
* @param array (Mime type, file name, extension)
* @return bool Is text
*/
public static function isText($fileinfo)
{
if (0 === strpos($fileinfo[0], 'text/')) {
return true;
}
$ext = 'mdtext php-dist h gitignore diff patch';
$extra_ext = trim(Pluf::f('idf_extra_text_ext', ''));
if (!empty($extra_ext))
$ext .= ' ' . $extra_ext;
$ext = array_merge(self::$supportedExtenstions, explode(' ' , $ext));
return (in_array($fileinfo[2], $ext));
}
}

View File

@ -1,368 +0,0 @@
<?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 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 ***** */
/**
* Create a project.
*
* A kind of merge of the member configuration, overview and the
* former source tab.
*
*/
class IDF_Form_Admin_ProjectCreate extends Pluf_Form
{
public function initFields($extra=array())
{
$choices = array();
$options = array(
'git' => __('git'),
'svn' => __('Subversion'),
'mercurial' => __('mercurial'),
'mtn' => __('monotone'),
);
foreach (Pluf::f('allowed_scm', array()) as $key => $class) {
$choices[$options[$key]] = $key;
}
$this->fields['name'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Name'),
'initial' => '',
));
$this->fields['private_project'] = new Pluf_Form_Field_Boolean(
array('required' => false,
'label' => __('Private project'),
'initial' => false,
'widget' => 'Pluf_Form_Widget_CheckboxInput',
));
$this->fields['shortname'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Shortname'),
'initial' => '',
'help_text' => __('It must be unique for each project and composed only of letters, digits and dash (-) like "my-project".'),
));
$this->fields['shortdesc'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Short description'),
'help_text' => __('A one line description of the project.'),
'initial' => '',
'widget_attrs' => array('size' => '35'),
));
$this->fields['scm'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Repository type'),
'initial' => 'git',
'widget_attrs' => array('choices' => $choices),
'widget' => 'Pluf_Form_Widget_SelectInput',
));
$this->fields['svn_remote_url'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Remote Subversion repository'),
'initial' => '',
'widget_attrs' => array('size' => '30'),
));
$this->fields['svn_username'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Repository username'),
'initial' => '',
'widget_attrs' => array('size' => '15'),
));
$this->fields['svn_password'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Repository password'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_PasswordInput',
));
$this->fields['mtn_master_branch'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Master branch'),
'initial' => '',
'widget_attrs' => array('size' => '35'),
'help_text' => __('This should be a world-wide unique identifier for your project. A reverse DNS notation like "com.my-domain.my-project" is a good idea.'),
));
$this->fields['owners'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Project owners'),
'initial' => $extra['user']->login,
'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array('rows' => 5,
'cols' => 40),
));
$this->fields['members'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Project members'),
'initial' => '',
'widget_attrs' => array('rows' => 7,
'cols' => 40),
'widget' => 'Pluf_Form_Widget_TextareaInput',
));
$projects = array('--' => '--');
foreach (Pluf::factory('IDF_Project')->getList(array('order' => 'name ASC')) as $proj) {
$projects[$proj->name] = $proj->shortname;
}
$this->fields['template'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Project template'),
'initial' => '--',
'help_text' => __('Use the given project to initialize the new project. Access rights and general configuration will be taken from the template project.'),
'widget' => 'Pluf_Form_Widget_SelectInput',
'widget_attrs' => array('choices' => $projects),
));
/**
* [signal]
*
* IDF_Form_Admin_ProjectCreate::initFields
*
* [sender]
*
* IDF_Form_Admin_ProjectCreate
*
* [description]
*
* This signal allows an application to modify the form
* for the creation of a project.
*
* [parameters]
*
* array('form' => $form)
*
*/
$params = array('form' => $this);
Pluf_Signal::send('IDF_Form_Admin_ProjectCreate::initFields',
'IDF_Form_Admin_ProjectCreate', $params);
}
public function clean_owners()
{
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['owners']);
}
public function clean_members()
{
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['members']);
}
public function clean_svn_remote_url()
{
$this->cleaned_data['svn_remote_url'] = (!empty($this->cleaned_data['svn_remote_url'])) ? $this->cleaned_data['svn_remote_url'] : '';
$url = trim($this->cleaned_data['svn_remote_url']);
if (strlen($url) == 0) return $url;
// we accept only starting with http(s):// to avoid people
// trying to access the local filesystem.
if (!preg_match('#^(http|https)://#', $url)) {
throw new Pluf_Form_Invalid(__('Only a remote repository available throught http or https are allowed. For example "http://somewhere.com/svn/trunk".'));
}
return $url;
}
public function clean_mtn_master_branch()
{
// do not validate, but empty the field if a different
// SCM should be used
if ($this->cleaned_data['scm'] != 'mtn')
return '';
$mtn_master_branch = mb_strtolower($this->cleaned_data['mtn_master_branch']);
if (!preg_match('/^([\w\d]+([-][\w\d]+)*)(\.[\w\d]+([-][\w\d]+)*)*$/',
$mtn_master_branch)) {
throw new Pluf_Form_Invalid(__(
'The master branch is empty or contains illegal characters, '.
'please use only letters, digits, dashs and dots as separators.'
));
}
$sql = new Pluf_SQL('vkey=%s AND vdesc=%s',
array('mtn_master_branch', $mtn_master_branch));
$l = Pluf::factory('IDF_Conf')->getList(array('filter'=>$sql->gen()));
if ($l->count() > 0) {
throw new Pluf_Form_Invalid(__(
'This master branch is already used. Please select another one.'
));
}
return $mtn_master_branch;
}
public function clean_shortname()
{
$shortname = mb_strtolower($this->cleaned_data['shortname']);
if (preg_match('/[^\-A-Za-z0-9]/', $shortname)) {
throw new Pluf_Form_Invalid(__('This shortname contains illegal characters, please use only letters, digits and dash (-).'));
}
if (mb_substr($shortname, 0, 1) == '-') {
throw new Pluf_Form_Invalid(__('The shortname cannot start with the dash (-) character.'));
}
if (mb_substr($shortname, -1) == '-') {
throw new Pluf_Form_Invalid(__('The shortname cannot end with the dash (-) character.'));
}
$sql = new Pluf_SQL('shortname=%s', array($shortname));
$l = Pluf::factory('IDF_Project')->getList(array('filter'=>$sql->gen()));
if ($l->count() > 0) {
throw new Pluf_Form_Invalid(__('This shortname is already used. Please select another one.'));
}
return $shortname;
}
public function clean()
{
if ($this->cleaned_data['scm'] != 'svn') {
foreach (array('svn_remote_url', 'svn_username', 'svn_password')
as $key) {
$this->cleaned_data[$key] = '';
}
}
if ($this->cleaned_data['scm'] != 'mtn') {
$this->cleaned_data['mtn_master_branch'] = '';
}
/**
* [signal]
*
* IDF_Form_Admin_ProjectCreate::clean
*
* [sender]
*
* IDF_Form_Admin_ProjectCreate
*
* [description]
*
* This signal allows an application to clean the form
* for the creation of a project.
*
* [parameters]
*
* array('cleaned_data' => $cleaned_data)
*
*/
$params = array('cleaned_data' => $this->cleaned_data);
Pluf_Signal::send('IDF_Form_Admin_ProjectCreate::clean',
'IDF_Form_Admin_ProjectCreate', $params);
return $this->cleaned_data;
}
public function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
$project = new IDF_Project();
$project->name = $this->cleaned_data['name'];
$project->shortname = $this->cleaned_data['shortname'];
$project->shortdesc = $this->cleaned_data['shortdesc'];
if ($this->cleaned_data['template'] != '--') {
// Find the template project
$sql = new Pluf_SQL('shortname=%s',
array($this->cleaned_data['template']));
$tmpl = Pluf::factory('IDF_Project')->getOne(array('filter' => $sql->gen()));
$project->private = $tmpl->private;
$project->description = $tmpl->description;
} else {
$project->private = $this->cleaned_data['private_project'];
$project->description = __('Click on the Project Management tab to set the description of your project.');
}
$project->create();
$conf = new IDF_Conf();
$conf->setProject($project);
$keys = array('scm', 'svn_remote_url', 'svn_username',
'svn_password', 'mtn_master_branch');
foreach ($keys as $key) {
$this->cleaned_data[$key] = (!empty($this->cleaned_data[$key])) ?
$this->cleaned_data[$key] : '';
$conf->setVal($key, $this->cleaned_data[$key]);
}
if ($this->cleaned_data['template'] != '--') {
$tmplconf = new IDF_Conf();
$tmplconf->setProject($tmpl);
// We need to get all the configuration variables we want from
// the old project and put them into the new project.
$props = array(
'labels_download_predefined' => IDF_Form_UploadConf::init_predefined,
'labels_download_one_max' => IDF_Form_UploadConf::init_one_max,
'labels_wiki_predefined' => IDF_Form_WikiConf::init_predefined,
'labels_wiki_one_max' => IDF_Form_WikiConf::init_one_max,
'labels_issue_template' => IDF_Form_IssueTrackingConf::init_template,
'labels_issue_open' => IDF_Form_IssueTrackingConf::init_open,
'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,
'webhook_url' => '',
'downloads_access_rights' => 'all',
'review_access_rights' => 'all',
'wiki_access_rights' => 'all',
'source_access_rights' => 'all',
'issues_access_rights' => 'all',
'downloads_notification_email' => '',
'review_notification_email' => '',
'wiki_notification_email' => '',
'source_notification_email' => '',
'issues_notification_email' => '',
);
foreach ($props as $prop => $def) {
$conf->setVal($prop, $tmplconf->getVal($prop, $def));
}
}
$project->created();
if ($this->cleaned_data['template'] == '--') {
IDF_Form_MembersConf::updateMemberships($project,
$this->cleaned_data);
} else {
// Get the membership of the template $tmpl
IDF_Form_MembersConf::updateMemberships($project,
$tmpl->getMembershipData('string'));
}
$project->membershipsUpdated();
return $project;
}
/**
* Check that the template project exists.
*/
public function clean_template()
{
if ($this->cleaned_data['template'] == '--') {
return $this->cleaned_data['template'];
}
$sql = new Pluf_SQL('shortname=%s', array($this->cleaned_data['template']));
if (Pluf::factory('IDF_Project')->getOne(array('filter' => $sql->gen())) == null) {
throw new Pluf_Form_Invalid(__('This project is not available.'));
}
return $this->cleaned_data['template'];
}
}

View File

@ -1,88 +0,0 @@
<?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 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 ***** */
/**
* Delete a project.
*
* It is also removing the SCM files, so handle with care.
*
*/
class IDF_Form_Admin_ProjectDelete extends Pluf_Form
{
public $project = null;
public function initFields($extra=array())
{
$this->project = $extra['project'];
$this->user = $extra['user'];
$this->fields['code'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Confirmation code'),
'initial' => '',
));
$this->fields['agree'] = new Pluf_Form_Field_Boolean(
array('required' => true,
'label' => __('I have made a backup of all the important data of this project.'),
'initial' => '',
));
}
public function clean_code()
{
$code = $this->cleaned_data['code'];
if ($code != $this->getCode()) {
throw new Pluf_Form_Invalid(__('The confirmation code does not match. Please provide a valid confirmation code to delete the project.'));
}
return $code;
}
public function clean_agree()
{
if (!$this->cleaned_data['agree']) {
throw new Pluf_Form_Invalid(__('Sorry, you really need to backup your data before deletion.'));
}
return $this->cleaned_data['agree'];
}
public function getCode()
{
return substr(md5(Pluf::f('secret_key').$this->user->id.'.'.$this->project->id),
0, 8);
}
public function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
// So, we drop the project, it will cascade and delete all the
// elements of the project. For large projects, this may use
// quite some memory.
$this->project->delete();
return true;
}
}

View File

@ -1,139 +0,0 @@
<?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 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 ***** */
/**
* Update a project.
*
* A kind of merge of the member configuration and overview in the
* project administration area.
*
*/
class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
{
public $project = null;
public function initFields($extra=array())
{
$this->project = $extra['project'];
$members = $this->project->getMembershipData('string');
$conf = $this->project->getConf();
$this->fields['name'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Name'),
'initial' => $this->project->name,
));
$this->fields['shortdesc'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Short description'),
'help_text' => __('A one line description of the project.'),
'initial' => $this->project->shortdesc,
'widget_attrs' => array('size' => '35'),
));
if ($this->project->getConf()->getVal('scm') == 'mtn') {
$this->fields['mtn_master_branch'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Master branch'),
'initial' => $conf->getVal('mtn_master_branch'),
'widget_attrs' => array('size' => '35'),
'help_text' => __('This should be a world-wide unique identifier for your project. A reverse DNS notation like "com.my-domain.my-project" is a good idea.'),
));
}
$this->fields['owners'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Project owners'),
'initial' => $members['owners'],
'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array('rows' => 5,
'cols' => 40),
));
$this->fields['members'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Project members'),
'initial' => $members['members'],
'widget_attrs' => array('rows' => 7,
'cols' => 40),
'widget' => 'Pluf_Form_Widget_TextareaInput',
));
}
public function clean_mtn_master_branch()
{
$mtn_master_branch = mb_strtolower($this->cleaned_data['mtn_master_branch']);
if (!preg_match('/^([\w\d]+([-][\w\d]+)*)(\.[\w\d]+([-][\w\d]+)*)*$/',
$mtn_master_branch)) {
throw new Pluf_Form_Invalid(__(
'The master branch is empty or contains illegal characters, '.
'please use only letters, digits, dashs and dots as separators.'
));
}
$sql = new Pluf_SQL('vkey=%s AND vdesc=%s AND project!=%s',
array('mtn_master_branch', $mtn_master_branch,
(string)$this->project->id));
$l = Pluf::factory('IDF_Conf')->getList(array('filter'=>$sql->gen()));
if ($l->count() > 0) {
throw new Pluf_Form_Invalid(__(
'This master branch is already used. Please select another one.'
));
}
return $mtn_master_branch;
}
public function clean_owners()
{
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['owners']);
}
public function clean_members()
{
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['members']);
}
public function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
IDF_Form_MembersConf::updateMemberships($this->project,
$this->cleaned_data);
$this->project->membershipsUpdated();
$this->project->name = $this->cleaned_data['name'];
$this->project->shortdesc = $this->cleaned_data['shortdesc'];
$this->project->update();
$keys = array('mtn_master_branch');
foreach ($keys as $key) {
if (!empty($this->cleaned_data[$key])) {
$this->project->getConf()->setVal($key, $this->cleaned_data[$key]);
}
}
}
}

View File

@ -1,215 +0,0 @@
<?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 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 ***** */
/**
* Allow an admin to create a user.
*/
class IDF_Form_Admin_UserCreate extends Pluf_Form
{
public $request = null;
public function initFields($extra=array())
{
$this->request = $extra['request'];
$this->fields['first_name'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('First name'),
'initial' => '',
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['last_name'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Last name'),
'initial' => '',
'widget_attrs' => array(
'maxlength' => 50,
'size' => 20,
),
));
$this->fields['login'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Login'),
'max_length' => 15,
'min_length' => 3,
'initial' => '',
'help_text' => __('The login must be between 3 and 15 characters long and contains only letters and digits.'),
'widget_attrs' => array(
'maxlength' => 15,
'size' => 10,
),
));
$this->fields['email'] = new Pluf_Form_Field_Email(
array('required' => true,
'label' => __('Email'),
'initial' => '',
'help_text' => __('Double check the email address as the password is directly sent to the user.'),
));
$this->fields['language'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Language'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_SelectInput',
'widget_attrs' => array(
'choices' =>
Pluf_L10n::getInstalledLanguages()
),
));
$this->fields['public_key'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Add a public key'),
'initial' => '',
'widget_attrs' => array('rows' => 3,
'cols' => 40),
'widget' => 'Pluf_Form_Widget_TextareaInput',
'help_text' => __('Paste a SSH or monotone public key. Be careful to not provide your private key here!')
));
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return Object Model with data set from the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
$password = Pluf_Utils::getPassword();
$user = new Pluf_User();
$user->setFromFormData($this->cleaned_data);
$user->active = true;
$user->staff = false;
$user->administrator = false;
$user->setPassword($password);
$user->create();
/**
* [signal]
*
* Pluf_User::passwordUpdated
*
* [sender]
*
* IDF_Form_Admin_UserCreate
*
* [description]
*
* This signal is sent when a user is created
* by the staff.
*
* [parameters]
*
* array('user' => $user)
*
*/
$params = array('user' => $user);
Pluf_Signal::send('Pluf_User::passwordUpdated',
'IDF_Form_Admin_UserCreate', $params);
// Create the public key as needed
if ('' !== $this->cleaned_data['public_key']) {
$key = new IDF_Key();
$key->user = $user;
$key->content = $this->cleaned_data['public_key'];
$key->create();
}
// Send an email to the user with the password
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
$url = Pluf::f('url_base').Pluf_HTTP_URL_urlForView('IDF_Views::login', array(), array(), false);
$context = new Pluf_Template_Context(
array('password' => Pluf_Template::markSafe($password),
'user' => $user,
'url' => Pluf_Template::markSafe($url),
'admin' => $this->request->user,
));
$tmpl = new Pluf_Template('idf/gadmin/users/createuser-email.txt');
$text_email = $tmpl->render($context);
$email = new Pluf_Mail(Pluf::f('from_email'), $user->email,
__('Your details to access your forge.'));
$email->addTextMessage($text_email);
$email->sendMail();
return $user;
}
function clean_last_name()
{
$last_name = trim($this->cleaned_data['last_name']);
if ($last_name == mb_strtoupper($last_name)) {
return mb_convert_case(mb_strtolower($last_name),
MB_CASE_TITLE, 'UTF-8');
}
return $last_name;
}
function clean_first_name()
{
$first_name = trim($this->cleaned_data['first_name']);
if ($first_name == mb_strtoupper($first_name)) {
return mb_convert_case(mb_strtolower($first_name),
MB_CASE_TITLE, 'UTF-8');
}
return $first_name;
}
function clean_email()
{
$this->cleaned_data['email'] = mb_strtolower(trim($this->cleaned_data['email']));
$guser = new Pluf_User();
$sql = new Pluf_SQL('email=%s', array($this->cleaned_data['email']));
if ($guser->getCount(array('filter' => $sql->gen())) > 0) {
throw new Pluf_Form_Invalid(sprintf(__('The email "%s" is already used.'), $this->cleaned_data['email']));
}
return $this->cleaned_data['email'];
}
public function clean_login()
{
$this->cleaned_data['login'] = mb_strtolower(trim($this->cleaned_data['login']));
if (preg_match('/[^a-z0-9]/', $this->cleaned_data['login'])) {
throw new Pluf_Form_Invalid(sprintf(__('The login "%s" can only contain letters and digits.'), $this->cleaned_data['login']));
}
$guser = new Pluf_User();
$sql = new Pluf_SQL('login=%s', $this->cleaned_data['login']);
if ($guser->getCount(array('filter' => $sql->gen())) > 0) {
throw new Pluf_Form_Invalid(sprintf(__('The login "%s" is already used, please find another one.'), $this->cleaned_data['login']));
}
return $this->cleaned_data['login'];
}
public function clean_public_key()
{
$this->cleaned_data['public_key'] =
IDF_Form_UserAccount::checkPublicKey($this->cleaned_data['public_key']);
return $this->cleaned_data['public_key'];
}
}

View File

@ -1,321 +0,0 @@
<?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 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 ***** */
/**
* Update user's details.
*/
class IDF_Form_Admin_UserUpdate extends Pluf_Form
{
public $user = null;
public function initFields($extra=array())
{
$this->user = $extra['user'];
$user_data = IDF_UserData::factory($this->user);
$this->fields['first_name'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('First name'),
'initial' => $this->user->first_name,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['last_name'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Last name'),
'initial' => $this->user->last_name,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 20,
),
));
$this->fields['email'] = new Pluf_Form_Field_Email(
array('required' => true,
'label' => __('Email'),
'initial' => $this->user->email,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 20,
),
));
$this->fields['language'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Language'),
'initial' => $this->user->language,
'widget' => 'Pluf_Form_Widget_SelectInput',
'widget_attrs' => array(
'choices' =>
Pluf_L10n::getInstalledLanguages()
),
));
$this->fields['password'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Password'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_PasswordInput',
'help_text' => Pluf_Template::markSafe(__('Leave blank if you do not want to change the password.').'<br />'.__('The password must be hard for other people to find it, but easy for the user to remember.')),
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['password2'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Confirm password'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_PasswordInput',
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['description'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Description'),
'initial' => $user_data->description,
'widget_attrs' => array('rows' => 3,
'cols' => 40),
'widget' => 'Pluf_Form_Widget_TextareaInput',
));
$this->fields['twitter'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Twitter username'),
'initial' => $user_data->twitter,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['public_email'] = new Pluf_Form_Field_Email(
array('required' => false,
'label' => __('Public email address'),
'initial' => $user_data->public_email,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['website'] = new Pluf_Form_Field_Url(
array('required' => false,
'label' => __('Website URL'),
'initial' => $user_data->website,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['custom_avatar'] = new Pluf_Form_Field_File(
array('required' => false,
'label' => __('Upload custom avatar'),
'initial' => '',
'max_size' => Pluf::f('max_upload_size', 2097152),
'move_function_params' => array('upload_path' => Pluf::f('upload_path').'/avatars',
'upload_path_create' => true,
'upload_overwrite' => true,
'file_name' => 'user_'.$this->user->id.'_%s'),
'help_text' => __('An image file with a width and height not larger than 60 pixels (bigger images are scaled down).'),
));
$this->fields['remove_custom_avatar'] = new Pluf_Form_Field_Boolean(
array('required' => false,
'label' => __('Remove custom avatar'),
'initial' => false,
'widget' => 'Pluf_Form_Widget_CheckboxInput',
'widget_attrs' => array(),
'help_text' => __('Tick this to delete the custom avatar.'),
));
if ($extra['request']->user->administrator) {
$this->fields['staff'] = new Pluf_Form_Field_Boolean(
array('required' => false,
'label' => __('Staff'),
'initial' => $this->user->staff,
'widget' => 'Pluf_Form_Widget_CheckboxInput',
'help_text' => __('If you give staff rights to a user, you really need to trust him.'),
));
}
$attrs = ($extra['request']->user->id == $this->user->id) ?
array('readonly' => 'readonly') : array();
$this->fields['active'] = new Pluf_Form_Field_Boolean(
array('required' => false,
'label' => __('Active'),
'initial' => $this->user->active,
'widget' => 'Pluf_Form_Widget_CheckboxInput',
'widget_attrs' => $attrs,
'help_text' => __('If the user is not getting the confirmation email or is abusing the system, you can directly enable or disable his account here.'),
));
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return Object Model with data set from the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
unset($this->cleaned_data['password2']);
$update_pass = false;
if (strlen($this->cleaned_data['password']) == 0) {
unset($this->cleaned_data['password']);
} else {
$update_pass = true;
}
$this->user->setFromFormData($this->cleaned_data);
if ($commit) {
$this->user->update();
// FIXME: go the extra mile and check the input lengths for
// all fields here!
// FIXME: this is all doubled in UserAccount!
$user_data = IDF_UserData::factory($this->user);
// Add or remove avatar - we need to do this here because every
// single setter directly leads to a save in the database
if ($user_data->avatar != '' &&
($this->cleaned_data['remove_custom_avatar'] == 1 ||
$this->cleaned_data['custom_avatar'] != '')) {
$avatar_path = Pluf::f('upload_path').'/avatars/'.basename($user_data->avatar);
if (basename($avatar_path) != '' && is_file($avatar_path)) {
unlink($avatar_path);
}
$user_data->avatar = '';
}
if ($this->cleaned_data['custom_avatar'] != '') {
$user_data->avatar = $this->cleaned_data['custom_avatar'];
}
$user_data->description = $this->cleaned_data['description'];
$user_data->twitter = $this->cleaned_data['twitter'];
$user_data->public_email = $this->cleaned_data['public_email'];
$user_data->website = $this->cleaned_data['website'];
if ($update_pass) {
/**
* [signal]
*
* Pluf_User::passwordUpdated
*
* [sender]
*
* IDF_Form_UserAccount
*
* [description]
*
* This signal is sent when the user updated his
* password from his account page.
*
* [parameters]
*
* array('user' => $user)
*
*/
$params = array('user' => $this->user);
Pluf_Signal::send('Pluf_User::passwordUpdated',
'IDF_Form_Admin_UserUpdate', $params);
}
}
return $this->user;
}
function clean_last_name()
{
$last_name = trim($this->cleaned_data['last_name']);
if ($last_name == mb_strtoupper($last_name)) {
return mb_convert_case(mb_strtolower($last_name),
MB_CASE_TITLE, 'UTF-8');
}
return $last_name;
}
function clean_first_name()
{
$first_name = trim($this->cleaned_data['first_name']);
if ($first_name == '---') {
throw new Pluf_Form_Invalid(__('--- is not a valid first name.'));
}
if ($first_name == mb_strtoupper($first_name)) {
$first_name = mb_convert_case(mb_strtolower($first_name),
MB_CASE_TITLE, 'UTF-8');
}
return $first_name;
}
function clean_email()
{
$email = mb_strtolower(trim($this->cleaned_data['email']));
$sql = new Pluf_SQL('email=%s AND id!=%s',
array($email, $this->user->id));
$users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
if ($users->count() > 0) {
throw new Pluf_Form_Invalid(__('A user with this email already exists, please provide another email address.'));
}
return $email;
}
function clean_custom_avatar()
{
// Just png, jpeg/jpg or gif
if (!preg_match('/\.(png|jpg|jpeg|gif)$/i', $this->cleaned_data['custom_avatar']) &&
$this->cleaned_data['custom_avatar'] != '') {
@unlink(Pluf::f('upload_path').'/avatars/'.$this->cleaned_data['custom_avatar']);
throw new Pluf_Form_Invalid(__('For security reason, you cannot upload a file with this extension.'));
}
return $this->cleaned_data['custom_avatar'];
}
/**
* Check to see if the two passwords are the same.
*/
public function clean()
{
if (!isset($this->errors['password'])
&& !isset($this->errors['password2'])) {
$password1 = $this->cleaned_data['password'];
$password2 = $this->cleaned_data['password2'];
if ($password1 != $password2) {
throw new Pluf_Form_Invalid(__('The passwords do not match. Please give them again.'));
}
}
return $this->cleaned_data;
}
}

View File

@ -1,51 +0,0 @@
<?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) 2010 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
# (at your option) any later version.
#
# Plume Framework 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.
#
# You should have received a copy of the GNU Lesser 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 ***** */
/**
* Similar to Pluf_Form_Field_Email, this form field validates one or more
* email addresses separated by a comma
*/
class IDF_Form_Field_EmailList extends Pluf_Form_Field
{
public $widget = 'Pluf_Form_Widget_TextInput';
public function clean($value)
{
parent::clean($value);
if (in_array($value, $this->empty_values)) {
$value = '';
}
if ($value == '') {
return $value;
}
$emails = preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
foreach ($emails as $email) {
if (!Pluf_Utils::isValidEmail($email)) {
throw new Pluf_Form_Invalid(__(
'Please enter one or more valid email addresses.'
));
}
}
return implode(',', $emails);
}
}

View File

@ -45,9 +45,6 @@ class IDF_Form_IssueCreate extends Pluf_Form
or $this->user->hasPerm('IDF.project-member', $this->project)) { or $this->user->hasPerm('IDF.project-member', $this->project)) {
$this->show_full = true; $this->show_full = true;
} }
$contentTemplate = $this->project->getConf()->getVal(
'labels_issue_template', IDF_Form_IssueTrackingConf::init_template
);
$this->fields['summary'] = new Pluf_Form_Field_Varchar( $this->fields['summary'] = new Pluf_Form_Field_Varchar(
array('required' => true, array('required' => true,
'label' => __('Summary'), 'label' => __('Summary'),
@ -60,35 +57,13 @@ class IDF_Form_IssueCreate extends Pluf_Form
$this->fields['content'] = new Pluf_Form_Field_Varchar( $this->fields['content'] = new Pluf_Form_Field_Varchar(
array('required' => true, array('required' => true,
'label' => __('Description'), 'label' => __('Description'),
'initial' => $contentTemplate, 'initial' => '',
'widget' => 'Pluf_Form_Widget_TextareaInput', 'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array( 'widget_attrs' => array(
'cols' => 58, 'cols' => 58,
'rows' => 13, 'rows' => 13,
), ),
)); ));
$upload_path = Pluf::f('upload_issue_path', false);
if (false === $upload_path) {
throw new Pluf_Exception_SettingError(__('The "upload_issue_path" configuration variable was not set.'));
}
$md5 = md5(rand().microtime().Pluf_Utils::getRandomString());
// We add .dummy to try to mitigate security issues in the
// 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';
$this->fields['attachment'.$i] = new Pluf_Form_Field_File(
array('required' => false,
'label' => __('Attach a file'),
'move_function_params' =>
array('upload_path' => $upload_path,
'upload_path_create' => true,
'file_name' => $filename,
)
)
);
}
if ($this->show_full) { if ($this->show_full) {
$this->fields['status'] = new Pluf_Form_Field_Varchar( $this->fields['status'] = new Pluf_Form_Field_Varchar(
array('required' => true, array('required' => true,
@ -108,41 +83,14 @@ class IDF_Form_IssueCreate extends Pluf_Form
'size' => 15, 'size' => 15,
), ),
)); ));
/*
* get predefined tags for issues from current project
*
* first Type:<...> and Priority:<...> will be used
*
*/
$predefined = preg_split("/[\r\n]+/", $extra['project']->getConf()->getVal(
'labels_issue_predefined'
));
$predefined_type = 'Type:Defect';
foreach ($predefined as $tag) {
if (strpos($tag, 'Type:') === 0) {
$predefined_type = explode('=', $tag, 2);
$predefined_type = trim($predefined_type[0]);
break;
}
}
$predefined_priority = 'Priority:Medium';
foreach ($predefined as $tag) {
if (strpos($tag, 'Priority:') === 0) {
$predefined_priority = explode('=', $tag, 2);
$predefined_priority = trim($predefined_priority[0]);
break;
}
}
for ($i=1;$i<7;$i++) { for ($i=1;$i<7;$i++) {
$initial = ''; $initial = '';
switch ($i) { switch ($i) {
case 1: case 1:
$initial = $predefined_type; $initial = 'Type:Defect';
break; break;
case 2: case 2:
$initial = $predefined_priority; $initial = 'Priority:Medium';
break; break;
} }
$this->fields['label'.$i] = new Pluf_Form_Field_Varchar( $this->fields['label'.$i] = new Pluf_Form_Field_Varchar(
@ -171,7 +119,7 @@ class IDF_Form_IssueCreate extends Pluf_Form
$conf = new IDF_Conf(); $conf = new IDF_Conf();
$conf->setProject($this->project); $conf->setProject($this->project);
$onemax = array(); $onemax = array();
foreach (explode(',', $conf->getVal('labels_issue_one_max', IDF_Form_IssueTrackingConf::init_one_max)) as $class) { foreach (split(',', $conf->getVal('labels_issue_one_max', IDF_Form_IssueTrackingConf::init_one_max)) as $class) {
if (trim($class) != '') { if (trim($class) != '') {
$onemax[] = mb_strtolower(trim($class)); $onemax[] = mb_strtolower(trim($class));
} }
@ -203,15 +151,6 @@ class IDF_Form_IssueCreate extends Pluf_Form
return $this->cleaned_data; return $this->cleaned_data;
} }
function clean_content()
{
$content = trim($this->cleaned_data['content']);
if (strlen($content) == 0) {
throw new Pluf_Form_Invalid(__('You need to provide a description of the issue.'));
}
return $content;
}
function clean_status() function clean_status()
{ {
// Check that the status is in the list of official status // Check that the status is in the list of official status
@ -235,21 +174,6 @@ class IDF_Form_IssueCreate extends Pluf_Form
return $this->cleaned_data['status']; return $this->cleaned_data['status'];
} }
/**
* Clean the attachments post failure.
*/
function failed()
{
$upload_path = Pluf::f('upload_issue_path', false);
if ($upload_path == false) return;
for ($i=1;$i<4;$i++) {
if (!empty($this->cleaned_data['attachment'.$i]) and
file_exists($upload_path.'/'.$this->cleaned_data['attachment'.$i])) {
@unlink($upload_path.'/'.$this->cleaned_data['attachment'.$i]);
}
}
}
/** /**
* Save the model in the database. * Save the model in the database.
* *
@ -259,9 +183,7 @@ class IDF_Form_IssueCreate extends Pluf_Form
*/ */
function save($commit=true) function save($commit=true)
{ {
if (!$this->isValid()) { if ($this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
// Add a tag for each label // Add a tag for each label
$tags = array(); $tags = array();
if ($this->show_full) { if ($this->show_full) {
@ -298,54 +220,19 @@ class IDF_Form_IssueCreate extends Pluf_Form
foreach ($tags as $tag) { foreach ($tags as $tag) {
$issue->setAssoc($tag); $issue->setAssoc($tag);
} }
$issue->setAssoc($this->user); // the user is
// automatically
// interested.
// add the first comment // add the first comment
$comment = new IDF_IssueComment(); $comment = new IDF_IssueComment();
$comment->issue = $issue; $comment->issue = $issue;
$comment->content = $this->cleaned_data['content']; $comment->content = $this->cleaned_data['content'];
$comment->submitter = $this->user; $comment->submitter = $this->user;
$comment->create(); $comment->create();
// If we have a file, create the IDF_IssueFile and attach
// it to the comment.
$created_files = array();
for ($i=1;$i<4;$i++) {
if ($this->cleaned_data['attachment'.$i]) {
$file = new IDF_IssueFile();
$file->attachment = $this->cleaned_data['attachment'.$i];
$file->submitter = $this->user;
$file->comment = $comment;
$file->create();
$created_files[] = $file;
}
}
/**
* [signal]
*
* IDF_Issue::create
*
* [sender]
*
* IDF_Form_IssueCreate
*
* [description]
*
* This signal allows an application to perform a set of tasks
* just after the creation of an issue. The comment contains
* the description of the issue.
*
* [parameters]
*
* array('issue' => $issue,
* 'comment' => $comment,
* 'files' => $attached_files);
*
*/
$params = array('issue' => $issue,
'comment' => $comment,
'files' => $created_files);
Pluf_Signal::send('IDF_Issue::create', 'IDF_Form_IssueCreate',
$params);
return $issue; return $issue;
} }
throw new Exception(__('Cannot save the model from an invalid form.'));
}
/** /**
* Based on the given string, try to find the matching user. * Based on the given string, try to find the matching user.

View File

@ -31,15 +31,6 @@ class IDF_Form_IssueTrackingConf extends Pluf_Form
* Defined as constants to easily access the value in the * Defined as constants to easily access the value in the
* IssueUpdate/Create form in the case nothing is in the db yet. * IssueUpdate/Create form in the case nothing is in the db yet.
*/ */
const init_template = 'Steps to reproduce the problem:
1.
2.
3.
Expected result:
Actual result:
';
const init_open = 'New = Issue has not had initial review yet const init_open = 'New = Issue has not had initial review yet
Accepted = Problem reproduced / Need acknowledged Accepted = Problem reproduced / Need acknowledged
Started = Work on this issue has begun'; Started = Work on this issue has begun';
@ -75,15 +66,6 @@ Maintainability = Hinders future changes';
public function initFields($extra=array()) 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 the reporter to provide certain information'),
'initial' => self::init_template,
'widget_attrs' => array('rows' => 7,
'cols' => 75),
'widget' => 'Pluf_Form_Widget_TextareaInput',
));
$this->fields['labels_issue_open'] = new Pluf_Form_Field_Varchar( $this->fields['labels_issue_open'] = new Pluf_Form_Field_Varchar(
array('required' => true, array('required' => true,
'label' => __('Open issue status values'), 'label' => __('Open issue status values'),
@ -105,7 +87,6 @@ Maintainability = Hinders future changes';
array('required' => true, array('required' => true,
'label' => __('Predefined issue labels'), 'label' => __('Predefined issue labels'),
'initial' => self::init_predefined, 'initial' => self::init_predefined,
'help_text' => __('The first "Type:" and "Priority:" entries found in this list are automatically chosen as defaults for new issues.'),
'widget_attrs' => array('rows' => 7, 'widget_attrs' => array('rows' => 7,
'cols' => 75), 'cols' => 75),
'widget' => 'Pluf_Form_Widget_TextareaInput', 'widget' => 'Pluf_Form_Widget_TextareaInput',
@ -118,6 +99,8 @@ Maintainability = Hinders future changes';
'widget_attrs' => array('size' => 60), 'widget_attrs' => array('size' => 60),
)); ));
} }
} }

View File

@ -60,28 +60,6 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
'rows' => 9, 'rows' => 9,
), ),
)); ));
$upload_path = Pluf::f('upload_issue_path', false);
if (false === $upload_path) {
throw new Pluf_Exception_SettingError(__('The "upload_issue_path" configuration variable was not set.'));
}
$md5 = md5(rand().microtime().Pluf_Utils::getRandomString());
// We add .dummy to try to mitigate security issues in the
// 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';
$this->fields['attachment'.$i] = new Pluf_Form_Field_File(
array('required' => false,
'label' => __('Attach a file'),
'move_function_params' =>
array('upload_path' => $upload_path,
'upload_path_create' => true,
'file_name' => $filename,
)
)
);
}
if ($this->show_full) { if ($this->show_full) {
$this->fields['status'] = new Pluf_Form_Field_Varchar( $this->fields['status'] = new Pluf_Form_Field_Varchar(
array('required' => true, array('required' => true,
@ -125,30 +103,6 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
} }
} }
/**
* Clean the attachments post failure.
*/
function failed()
{
$upload_path = Pluf::f('upload_issue_path', false);
if ($upload_path == false) return;
for ($i=1;$i<4;$i++) {
if (!empty($this->cleaned_data['attachment'.$i]) and
file_exists($upload_path.'/'.$this->cleaned_data['attachment'.$i])) {
@unlink($upload_path.'/'.$this->cleaned_data['attachment'.$i]);
}
}
}
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 a description of the issue.'));
}
return $content;
}
/** /**
* We check that something is really changed. * We check that something is really changed.
*/ */
@ -228,9 +182,7 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
*/ */
function save($commit=true) function save($commit=true)
{ {
if (!$this->isValid()) { if ($this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
if ($this->show_full) { if ($this->show_full) {
// Add a tag for each label // Add a tag for each label
$tags = array(); $tags = array();
@ -301,48 +253,8 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
$comment->changes = $changes; $comment->changes = $changes;
$comment->create(); $comment->create();
$this->issue->update(); $this->issue->update();
if ($this->issue->owner != $this->user->id and
$this->issue->submitter != $this->user->id) {
$this->issue->setAssoc($this->user); // interested user.
}
$attached_files = array();
for ($i=1;$i<4;$i++) {
if ($this->cleaned_data['attachment'.$i]) {
$file = new IDF_IssueFile();
$file->attachment = $this->cleaned_data['attachment'.$i];
$file->submitter = $this->user;
$file->comment = $comment;
$file->create();
$attached_files[] = $file;
}
}
/**
* [signal]
*
* IDF_Issue::update
*
* [sender]
*
* IDF_Form_IssueUpdate
*
* [description]
*
* This signal allows an application to perform a set of tasks
* just after the update of an issue.
*
* [parameters]
*
* array('issue' => $issue,
* 'comment' => $comment,
* 'files' => $attached_files);
*
*/
$params = array('issue' => $this->issue,
'comment' => $comment,
'files' => $attached_files);
Pluf_Signal::send('IDF_Issue::update', 'IDF_Form_IssueUpdate',
$params);
return $this->issue; return $this->issue;
} }
throw new Exception(__('Cannot save the model from an invalid form.'));
}
} }

View File

@ -63,72 +63,20 @@ class IDF_Form_MembersConf extends Pluf_Form
if (!$this->isValid()) { if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.')); throw new Exception(__('Cannot save the model from an invalid form.'));
} }
self::updateMemberships($this->project, $this->cleaned_data);
$this->project->membershipsUpdated();
}
public function clean_owners()
{
return self::checkBadLogins($this->cleaned_data['owners']);
}
public function clean_members()
{
return self::checkBadLogins($this->cleaned_data['members']);
}
/**
* From the input, find the bad logins.
*
* @throws Pluf_Form_Invalid exception when bad logins are found
* @param string Comma, new line delimited list of logins
* @return string Comma, new line delimited list of logins
*/
public static function checkBadLogins($logins)
{
$bad = array();
foreach (preg_split("/\015\012|\015|\012|\,/", $logins, -1, PREG_SPLIT_NO_EMPTY) as $login) {
$sql = new Pluf_SQL('login=%s', array(trim($login)));
try {
$user = Pluf::factory('Pluf_User')->getOne(array('filter'=>$sql->gen()));
if (null == $user) {
$bad[] = $login;
}
} catch (Exception $e) {
$bad[] = $login;
}
}
$n = count($bad);
if ($n) {
$badlogins = Pluf_esc(implode(', ', $bad));
throw new Pluf_Form_Invalid(sprintf(_n('The following login is invalid: %s.', 'The following logins are invalid: %s.', $n), $badlogins));
}
return $logins;
}
/**
* The update of the memberships is done in different places. This
* avoids duplicating code.
*
* @param IDF_Project The project
* @param array The new memberships data in 'owners' and 'members' keys
*/
public static function updateMemberships($project, $cleaned_data)
{
// remove all the permissions // remove all the permissions
$cm = $project->getMembershipData(); $cm = $this->project->getMembershipData();
$def = array('owners' => Pluf_Permission::getFromString('IDF.project-owner'), $def = array('owners' => Pluf_Permission::getFromString('IDF.project-owner'),
'members' => Pluf_Permission::getFromString('IDF.project-member')); 'members' => Pluf_Permission::getFromString('IDF.project-member'));
$guser = new Pluf_User(); $guser = new Pluf_User();
foreach ($def as $key=>$perm) { foreach ($def as $key=>$perm) {
foreach ($cm[$key] as $user) { foreach ($cm[$key] as $user) {
Pluf_RowPermission::remove($user, $project, $perm); Pluf_RowPermission::remove($user, $this->project, $perm);
} }
foreach (preg_split("/\015\012|\015|\012|\,/", $cleaned_data[$key], -1, PREG_SPLIT_NO_EMPTY) as $login) { foreach (preg_split("/\015\012|\015|\012|\,/", $this->cleaned_data[$key], -1, PREG_SPLIT_NO_EMPTY) as $login) {
$sql = new Pluf_SQL('login=%s', array(trim($login))); $sql = new Pluf_SQL('login=%s', array(trim($login)));
$users = $guser->getList(array('filter'=>$sql->gen())); $users = $guser->getList(array('filter'=>$sql->gen()));
if ($users->count() == 1) { if ($users->count() == 1) {
Pluf_RowPermission::add($users[0], $project, $perm); Pluf_RowPermission::add($users[0], $this->project, $perm);
} }
} }
} }

View File

@ -1,112 +0,0 @@
<?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 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 ***** */
/**
* Ask a password recovery.
*
*/
class IDF_Form_Password extends Pluf_Form
{
public function initFields($extra=array())
{
$this->fields['account'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Your login or email'),
'help_text' => __('Provide either your login or your email to recover your password.'),
));
}
/**
* Validate that a user with this login or email exists.
*/
public function clean_account()
{
$account = mb_strtolower(trim($this->cleaned_data['account']));
$sql = new Pluf_SQL('email=%s OR login=%s',
array($account, $account));
$users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
if ($users->count() == 0) {
throw new Pluf_Form_Invalid(__('Sorry, we cannot find a user with this email address or login. Feel free to try again.'));
}
$ok = false;
foreach ($users as $user) {
if ($user->active) {
$ok = true;
continue;
}
if (!$user->active and $user->first_name == '---') {
$ok = true;
continue;
}
$ok = false; // This ensures an all or nothing ok.
}
if (!$ok) {
throw new Pluf_Form_Invalid(__('Sorry, we cannot find a user with this email address or login. Feel free to try again.'));
}
return $account;
}
/**
* Send the reminder email.
*
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
$account = $this->cleaned_data['account'];
$sql = new Pluf_SQL('email=%s OR login=%s',
array($account, $account));
$users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
$return_url = '';
foreach ($users as $user) {
if ($user->active) {
$return_url = Pluf_HTTP_URL_urlForView('IDF_Views::passwordRecoveryInputCode');
$tmpl = new Pluf_Template('idf/user/passrecovery-email.txt');
$cr = new Pluf_Crypt(md5(Pluf::f('secret_key')));
$code = trim($cr->encrypt($user->email.':'.$user->id.':'.time()),
'~');
$code = substr(md5(Pluf::f('secret_key').$code), 0, 2).$code;
$url = Pluf::f('url_base').Pluf_HTTP_URL_urlForView('IDF_Views::passwordRecovery', array($code), array(), false);
$urlic = Pluf::f('url_base').Pluf_HTTP_URL_urlForView('IDF_Views::passwordRecoveryInputCode', array(), array(), false);
$context = new Pluf_Template_Context(
array('url' => Pluf_Template::markSafe($url),
'urlik' => Pluf_Template::markSafe($urlic),
'user' => Pluf_Template::markSafe($user),
'key' => Pluf_Template::markSafe($code)));
$email = new Pluf_Mail(Pluf::f('from_email'), $user->email,
__('Password Recovery - InDefero'));
$email->setReturnPath(Pluf::f('bounce_email', Pluf::f('from_email')));
$email->addTextMessage($tmpl->render($context));
$email->sendMail();
}
if (!$user->active and $user->first_name == '---') {
$return_url = Pluf_HTTP_URL_urlForView('IDF_Views::registerInputKey');
IDF_Form_Register::sendVerificationEmail($user);
}
}
return $return_url;
}
}

View File

@ -1,104 +0,0 @@
<?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 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 ***** */
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
/**
* Check the validity of a confirmation key to reset a password.
*
*/
class IDF_Form_PasswordInputKey extends Pluf_Form
{
public function initFields($extra=array())
{
$this->fields['key'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Your verification key'),
'initial' => '',
'widget_attrs' => array(
'size' => 50,
),
));
}
/**
* Validate the key.
*/
public function clean_key()
{
$this->cleaned_data['key'] = trim($this->cleaned_data['key']);
$error = __('We are sorry but this validation key is not valid. Maybe you should directly copy/paste it from your validation email.');
if (false === ($cres=self::checkKeyHash($this->cleaned_data['key']))) {
throw new Pluf_Form_Invalid($error);
}
$guser = new Pluf_User();
$sql = new Pluf_SQL('email=%s AND id=%s',
array($cres[0], $cres[1]));
if ($guser->getCount(array('filter' => $sql->gen())) != 1) {
throw new Pluf_Form_Invalid($error);
}
if ((time() - $cres[2]) > 86400) {
throw new Pluf_Form_Invalid(__('Sorry, but this verification key has expired, please restart the password recovery sequence. For security reasons, the verification key is only valid 24h.'));
}
return $this->cleaned_data['key'];
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return string Url to redirect to the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save an invalid form.'));
}
return Pluf_HTTP_URL_urlForView('IDF_Views::passwordRecovery',
array($this->cleaned_data['key']));
}
/**
* Return false or an array with the email, id and timestamp.
*
* This is a static function to be reused by other forms.
*
* @param string Confirmation key
* @return mixed Either false or array(email, id, timestamp)
*/
public static function checkKeyHash($key)
{
$hash = substr($key, 0, 2);
$encrypted = substr($key, 2);
if ($hash != substr(md5(Pluf::f('secret_key').$encrypted), 0, 2)) {
return false;
}
$cr = new Pluf_Crypt(md5(Pluf::f('secret_key')));
$f = explode(':', $cr->decrypt($encrypted), 3);
if (count($f) != 3) {
return false;
}
return $f;
}
}

View File

@ -1,138 +0,0 @@
<?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 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 ***** */
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
/**
* Reset the password of a user.
*
*/
class IDF_Form_PasswordReset extends Pluf_Form
{
protected $user = null;
public function initFields($extra=array())
{
$this->user = $extra['user'];
$this->fields['key'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Your verification key'),
'initial' => $extra['key'],
'widget' => 'Pluf_Form_Widget_HiddenInput',
));
$this->fields['password'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Your password'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_PasswordInput',
'help_text' => __('Your password must be hard for other people to find it, but easy for you to remember.'),
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['password2'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Confirm your password'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_PasswordInput',
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
}
/**
* Check the passwords.
*/
public function clean()
{
if ($this->cleaned_data['password'] != $this->cleaned_data['password2']) {
throw new Pluf_Form_Invalid(__('The two passwords must be the same.'));
}
if (!$this->user->active) {
throw new Pluf_Form_Invalid(__('This account is not active. Please contact the forge administrator to activate it.'));
}
return $this->cleaned_data;
}
/**
* Validate the key.
*/
public function clean_key()
{
$this->cleaned_data['key'] = trim($this->cleaned_data['key']);
$error = __('We are sorry but this validation key is not valid. Maybe you should directly copy/paste it from your validation email.');
if (false === ($cres=IDF_Form_PasswordInputKey::checkKeyHash($this->cleaned_data['key']))) {
throw new Pluf_Form_Invalid($error);
}
$guser = new Pluf_User();
$sql = new Pluf_SQL('email=%s AND id=%s',
array($cres[0], $cres[1]));
if ($guser->getCount(array('filter' => $sql->gen())) != 1) {
throw new Pluf_Form_Invalid($error);
}
if ((time() - $cres[2]) > 86400) {
throw new Pluf_Form_Invalid(__('Sorry, but this verification key has expired, please restart the password recovery sequence. For security reasons, the verification key is only valid 24h.'));
}
return $this->cleaned_data['key'];
}
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save an invalid form.'));
}
$this->user->setFromFormData($this->cleaned_data);
if ($commit) {
$this->user->update();
/**
* [signal]
*
* Pluf_User::passwordUpdated
*
* [sender]
*
* IDF_Form_PasswordReset
*
* [description]
*
* This signal is sent when the user reset his
* password from the password recovery page.
*
* [parameters]
*
* array('user' => $user)
*
*/
$params = array('user' => $this->user);
Pluf_Signal::send('Pluf_User::passwordUpdated',
'IDF_Form_PasswordReset', $params);
}
return $this->user;
}
}

View File

@ -27,11 +27,8 @@
*/ */
class IDF_Form_Register extends Pluf_Form class IDF_Form_Register extends Pluf_Form
{ {
protected $request;
public function initFields($extra=array()) public function initFields($extra=array())
{ {
$this->request = $extra['request'];
$login = ''; $login = '';
if (isset($extra['initial']['login'])) { if (isset($extra['initial']['login'])) {
$login = $extra['initial']['login']; $login = $extra['initial']['login'];
@ -39,12 +36,12 @@ class IDF_Form_Register extends Pluf_Form
$this->fields['login'] = new Pluf_Form_Field_Varchar( $this->fields['login'] = new Pluf_Form_Field_Varchar(
array('required' => true, array('required' => true,
'label' => __('Your login'), 'label' => __('Your login'),
'max_length' => 15, 'max_length' => 8,
'min_length' => 3, 'min_length' => 3,
'initial' => $login, 'initial' => $login,
'help_text' => __('The login must be between 3 and 15 characters long and contains only letters and digits.'), 'help_text' => __('The login must be between 3 and 8 characters long and contains only letters and digits.'),
'widget_attrs' => array( 'widget_attrs' => array(
'maxlength' => 15, 'maxlength' => 8,
'size' => 10, 'size' => 10,
), ),
)); ));
@ -68,9 +65,6 @@ class IDF_Form_Register extends Pluf_Form
public function clean_login() public function clean_login()
{ {
$this->cleaned_data['login'] = mb_strtolower(trim($this->cleaned_data['login'])); $this->cleaned_data['login'] = mb_strtolower(trim($this->cleaned_data['login']));
if (preg_match('/[^A-Za-z0-9]/', $this->cleaned_data['login'])) {
throw new Pluf_Form_Invalid(sprintf(__('The login "%s" can only contain letters and digits.'), $this->cleaned_data['login']));
}
$guser = new Pluf_User(); $guser = new Pluf_User();
$sql = new Pluf_SQL('login=%s', $this->cleaned_data['login']); $sql = new Pluf_SQL('login=%s', $this->cleaned_data['login']);
if ($guser->getCount(array('filter' => $sql->gen())) > 0) { if ($guser->getCount(array('filter' => $sql->gen())) > 0) {
@ -121,17 +115,10 @@ class IDF_Form_Register extends Pluf_Form
$user->last_name = $this->cleaned_data['login']; $user->last_name = $this->cleaned_data['login'];
$user->login = $this->cleaned_data['login']; $user->login = $this->cleaned_data['login'];
$user->email = $this->cleaned_data['email']; $user->email = $this->cleaned_data['email'];
$user->language = $this->request->language_code;
$user->active = false; $user->active = false;
$user->create(); $user->create();
self::sendVerificationEmail($user);
return $user;
}
public static function sendVerificationEmail($user)
{
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
$from_email = Pluf::f('from_email'); $from_email = Pluf::f('from_email');
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
$cr = new Pluf_Crypt(md5(Pluf::f('secret_key'))); $cr = new Pluf_Crypt(md5(Pluf::f('secret_key')));
$encrypted = trim($cr->encrypt($user->email.':'.$user->id), '~'); $encrypted = trim($cr->encrypt($user->email.':'.$user->id), '~');
$key = substr(md5(Pluf::f('secret_key').$encrypted), 0, 2).$encrypted; $key = substr(md5(Pluf::f('secret_key').$encrypted), 0, 2).$encrypted;
@ -144,11 +131,12 @@ class IDF_Form_Register extends Pluf_Form
'user'=> $user, 'user'=> $user,
) )
); );
$tmpl = new Pluf_Template('idf/register/confirmation-email.txt'); $tmpl = new Pluf_Template('register/confirmation-email.txt');
$text_email = $tmpl->render($context); $text_email = $tmpl->render($context);
$email = new Pluf_Mail($from_email, $user->email, $email = new Pluf_Mail($from_email, $user->email,
__('Confirm the creation of your account.')); __('Confirm the creation of your account.'));
$email->addTextMessage($text_email); $email->addTextMessage($text_email);
$email->sendMail(); $email->sendMail();
return $user;
} }
} }

View File

@ -63,7 +63,6 @@ class IDF_Form_RegisterConfirmation extends Pluf_Form
'size' => 15, 'size' => 15,
), ),
)); ));
$this->fields['password'] = new Pluf_Form_Field_Varchar( $this->fields['password'] = new Pluf_Form_Field_Varchar(
array('required' => true, array('required' => true,
'label' => __('Your password'), 'label' => __('Your password'),
@ -142,28 +141,6 @@ class IDF_Form_RegisterConfirmation extends Pluf_Form
$this->_user->staff = false; $this->_user->staff = false;
if ($commit) { if ($commit) {
$this->_user->update(); $this->_user->update();
/**
* [signal]
*
* Pluf_User::passwordUpdated
*
* [sender]
*
* IDF_Form_RegisterConfirmation
*
* [description]
*
* This signal is sent when the user updated his
* password from his account page.
*
* [parameters]
*
* array('user' => $user)
*
*/
$params = array('user' => $this->_user);
Pluf_Signal::send('Pluf_User::passwordUpdated',
'IDF_Form_RegisterConfirmation', $params);
} }
return $this->_user; return $this->_user;
} }

View File

@ -91,6 +91,6 @@ class IDF_Form_RegisterInputKey extends Pluf_Form
return false; return false;
} }
$cr = new Pluf_Crypt(md5(Pluf::f('secret_key'))); $cr = new Pluf_Crypt(md5(Pluf::f('secret_key')));
return explode(':', $cr->decrypt($encrypted), 2); return split(':', $cr->decrypt($encrypted), 2);
} }
} }

View File

@ -1,255 +0,0 @@
<?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 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 ***** */
/**
* Create a new code review.
*
* This creates an IDF_Review and the corresponding IDF_Review_Patch.
*/
class IDF_Form_ReviewCreate extends Pluf_Form
{
public $user = null;
public $project = null;
public $show_full = false;
public function initFields($extra=array())
{
$this->user = $extra['user'];
$this->project = $extra['project'];
if ($this->user->hasPerm('IDF.project-owner', $this->project)
or $this->user->hasPerm('IDF.project-member', $this->project)) {
$this->show_full = true;
}
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Summary'),
'initial' => '',
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
));
$this->fields['description'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Description'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array(
'cols' => 58,
'rows' => 7,
),
));
$sql = new Pluf_SQL('project=%s', array($this->project->id));
$commits = Pluf::factory('IDF_Commit')->getList(array('order' => 'creation_dtime DESC',
'nb' => 10,
'filter' => $sql->gen()));
$choices = array();
foreach ($commits as $c) {
$id = (strlen($c->scm_id) > 10) ? substr($c->scm_id, 0, 10) : $c->scm_id;
$ext = (mb_strlen($c->summary) > 50) ? mb_substr($c->summary, 0, 47).'...' : $c->summary;
$choices[$id.' - '.$ext] = $c->scm_id;
}
$this->fields['commit'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Commit'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_SelectInput',
'widget_attrs' => array(
'choices' => $choices,
),
));
$upload_path = Pluf::f('upload_issue_path', false);
if (false === $upload_path) {
throw new Pluf_Exception_SettingError(__('The "upload_issue_path" configuration variable was not set.'));
}
$md5 = md5(rand().microtime().Pluf_Utils::getRandomString());
// We add .dummy to try to mitigate security issues in the
// case of someone allowing the upload path to be accessible
// to everybody.
$filename = substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/'.substr($md5, 4).'/%s.dummy';
$this->fields['patch'] = new Pluf_Form_Field_File(
array('required' => true,
'label' => __('Patch'),
'move_function_params' =>
array('upload_path' => $upload_path,
'upload_path_create' => true,
'file_name' => $filename,
)
)
);
if ($this->show_full) {
$this->fields['status'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Status'),
'initial' => 'New',
'widget_attrs' => array(
'maxlength' => 20,
'size' => 15,
),
));
}
}
public function clean_patch()
{
$diff = new IDF_Diff(file_get_contents(Pluf::f('upload_issue_path').'/'
.$this->cleaned_data['patch']));
$diff->parse();
if (count($diff->files) == 0) {
throw new Pluf_Form_Invalid(__('We were not able to parse your patch. Please provide a valid patch.'));
}
return $this->cleaned_data['patch'];
}
public function clean_commit()
{
$commit = self::findCommit($this->cleaned_data['commit']);
if (null == $commit) {
throw new Pluf_Form_Invalid(__('You provided an invalid commit.'));
}
return $this->cleaned_data['commit'];
}
/**
* Validate the interconnection in the form.
*/
public function clean()
{
return $this->cleaned_data;
}
function clean_status()
{
// Check that the status is in the list of official status
$tags = $this->project->getTagsFromConfig('labels_issue_open',
IDF_Form_IssueTrackingConf::init_open,
'Status');
$tags = array_merge($this->project->getTagsFromConfig('labels_issue_closed',
IDF_Form_IssueTrackingConf::init_closed,
'Status')
, $tags);
$found = false;
foreach ($tags as $tag) {
if ($tag->name == trim($this->cleaned_data['status'])) {
$found = true;
break;
}
}
if (!$found) {
throw new Pluf_Form_Invalid(__('You provided an invalid status.'));
}
return $this->cleaned_data['status'];
}
/**
* Clean the attachments post failure.
*/
function failed()
{
$upload_path = Pluf::f('upload_issue_path', false);
if ($upload_path == false) return;
if (!empty($this->cleaned_data['patch']) and
file_exists($upload_path.'/'.$this->cleaned_data['patch'])) {
@unlink($upload_path.'/'.$this->cleaned_data['patch']);
}
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return Object Model with data set from the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
// Create the review
$review = new IDF_Review();
$review->project = $this->project;
$review->summary = $this->cleaned_data['summary'];
$review->submitter = $this->user;
if (!isset($this->cleaned_data['status'])) {
$this->cleaned_data['status'] = 'New';
}
$review->status = IDF_Tag::add(trim($this->cleaned_data['status']), $this->project, 'Status');
$review->create();
// add the first patch
$patch = new IDF_Review_Patch();
$patch->review = $review;
$patch->summary = __('Initial patch to be reviewed.');
$patch->description = $this->cleaned_data['description'];
$patch->commit = self::findCommit($this->cleaned_data['commit']);
$patch->patch = $this->cleaned_data['patch'];
$patch->create();
$patch->notify($this->project->getConf());
/**
* [signal]
*
* IDF_Review::create
*
* [sender]
*
* IDF_Form_ReviewCreate
*
* [description]
*
* This signal allows an application to perform a set of tasks
* just after the creation of a review and the notification.
*
* [parameters]
*
* array('review' => $review,
* 'patch' => $patch);
*
*/
$params = array('review' => $review,
'patch' => $patch);
Pluf_Signal::send('IDF_Review::create', 'IDF_Form_ReviewCreate',
$params);
return $review;
}
/**
* Based on the given string, try to find the matching commit.
*
* If no user found, simply returns null.
*
* @param string Commit
* @return IDF_Commit or null
*/
public static function findCommit($string)
{
$string = trim($string);
if (strlen($string) == 0) return null;
$gc = new IDF_Commit();
$sql = new Pluf_SQL('scm_id=%s', array($string));
$gcs = $gc->getList(array('filter' => $sql->gen()));
if ($gcs->count() > 0) {
return $gcs[0];
}
return null;
}
}

View File

@ -1,179 +0,0 @@
<?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 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 comments to files in a review.
*
*/
class IDF_Form_ReviewFileComment extends Pluf_Form
{
public $files = null;
public $patch = null;
public $user = null;
public $project = null;
public function initFields($extra=array())
{
$this->files = $extra['files'];
$this->patch = $extra['patch'];
$this->user = $extra['user'];
$this->project = $extra['project'];
foreach ($this->files as $filename => $def) {
$this->fields[md5($filename)] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Comment'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array(
'cols' => 58,
'rows' => 9,
),
));
}
$this->fields['content'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('General comment'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array(
'cols' => 58,
'rows' => 9,
),
));
if ($this->user->hasPerm('IDF.project-owner', $this->project)
or $this->user->hasPerm('IDF.project-member', $this->project)) {
$this->show_full = true;
}
if ($this->show_full) {
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Summary'),
'initial' => $this->patch->get_review()->summary,
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
));
$this->fields['status'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Status'),
'initial' => $this->patch->get_review()->get_status()->name,
'widget_attrs' => array(
'maxlength' => 20,
'size' => 15,
),
));
}
}
/**
* Validate the interconnection in the form.
*/
public function clean()
{
$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;
}
}
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(empty($content)) {
if ($this->fields['status']->initial != $this->fields['status']->value) {
return __('The status have been updated.');
}
} else {
return $content;
}
throw new Pluf_Form_Invalid(__('This field is required.'));
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return Object Model with data set from the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
// create a base comment
$bc = new IDF_Review_Comment();
$bc->patch = $this->patch;
$bc->submitter = $this->user;
$bc->content = $this->cleaned_data['content'];
$review = $this->patch->get_review();
if ($this->show_full) {
// Compare between the old and the new data
// Status, summary
$changes = array();
$status = IDF_Tag::add(trim($this->cleaned_data['status']), $this->project, 'Status');
if ($status->id != $this->patch->get_review()->status) {
$changes['st'] = $status->name;
}
if (trim($this->patch->get_review()->summary) != trim($this->cleaned_data['summary'])) {
$changes['su'] = trim($this->cleaned_data['summary']);
}
// Update the review
$review->summary = trim($this->cleaned_data['summary']);
$review->status = $status;
$bc->changes = $changes;
}
$bc->create();
foreach ($this->files as $filename => $def) {
if (!empty($this->cleaned_data[md5($filename)])) {
// Add a comment.
$c = new IDF_Review_FileComment();
$c->comment = $bc;
$c->cfile = $filename;
$c->content = $this->cleaned_data[md5($filename)];
$c->create();
}
}
$review->update(); // reindex and put up in the list.
return $bc;
}
}

View File

@ -1,65 +0,0 @@
<?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 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 ***** */
/**
* Configuration of the source.
*
* Only the modification of the login/password for subversion is
* authorized.
*/
class IDF_Form_SourceConf extends Pluf_Form
{
public $conf = null;
public function initFields($extra=array())
{
$this->conf = $extra['conf'];
if ($extra['remote_svn']) {
$this->fields['svn_username'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Repository username'),
'initial' => $this->conf->getVal('svn_username', ''),
'widget_attrs' => array('size' => '15'),
));
$this->fields['svn_password'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Repository password'),
'initial' => $this->conf->getVal('svn_password', ''),
'widget' => 'Pluf_Form_Widget_PasswordInput',
));
}
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
$url = Pluf_HTTP_URL_urlForView('idf_faq').'#webhooks';
$this->fields['webhook_url'] = new Pluf_Form_Field_Url(
array('required' => false,
'label' => __('Webhook URL'),
'initial' => $this->conf->getVal('webhook_url', ''),
'help_text' => sprintf(__('Learn more about the <a href="%s">post-commit web hooks</a>.'), $url),
'widget_attrs' => array('size' => 35),
));
}
}

View File

@ -1,125 +0,0 @@
<?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 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 ***** */
/**
* Configuration of the tabs access.
*/
class IDF_Form_TabsConf extends Pluf_Form
{
public $conf = null;
public $project = null;
public function initFields($extra=array())
{
$this->conf = $extra['conf'];
$this->project = $extra['project'];
$ak = array('downloads_access_rights' => __('Downloads'),
'review_access_rights' => __('Code Review'),
'wiki_access_rights' => __('Documentation'),
'source_access_rights' => __('Source'),
'issues_access_rights' => __('Issues'),);
foreach ($ak as $key=>$label) {
$this->fields[$key] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => $label,
'initial' => $this->conf->getVal($key, 'all'),
'widget_attrs' => array('choices' =>
array(
__('Open to all') => 'all',
__('Signed in users') => 'login',
__('Project members') => 'members',
__('Project owners') => 'owners',
__('Closed') => 'none',
)
),
'widget' => 'Pluf_Form_Widget_SelectInput',
));
}
$ak = array('downloads_notification_email',
'review_notification_email',
'wiki_notification_email',
'source_notification_email',
'issues_notification_email',);
foreach ($ak as $key) {
$this->fields[$key] = new IDF_Form_Field_EmailList(
array('required' => false,
'label' => $key,
'initial' => $this->conf->getVal($key, ''),
'widget_attrs' => array('size' => 40),
));
}
$this->fields['private_project'] = new Pluf_Form_Field_Boolean(
array('required' => false,
'label' => __('Private project'),
'initial' => $this->project->private,
'widget' => 'Pluf_Form_Widget_CheckboxInput',
));
$this->fields['authorized_users'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Extra authorized users'),
'widget_attrs' => array('rows' => 7,
'cols' => 40),
'widget' => 'Pluf_Form_Widget_TextareaInput',
));
}
public function clean_authorized_users()
{
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['authorized_users']);
}
public function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
// remove all the permissions
$perm = Pluf_Permission::getFromString('IDF.project-authorized-user');
$cm = $this->project->getMembershipData();
$guser = new Pluf_User();
foreach ($cm['authorized'] as $user) {
Pluf_RowPermission::remove($user, $this->project, $perm);
}
if ($this->cleaned_data['private_project']) {
foreach (preg_split("/\015\012|\015|\012|\,/", $this->cleaned_data['authorized_users'], -1, PREG_SPLIT_NO_EMPTY) as $login) {
$sql = new Pluf_SQL('login=%s', array(trim($login)));
$users = $guser->getList(array('filter'=>$sql->gen()));
if ($users->count() == 1) {
Pluf_RowPermission::add($users[0], $this->project, $perm);
}
}
$this->project->private = 1;
} else {
$this->project->private = 0;
}
$this->project->update();
$this->project->membershipsUpdated();
}
}

View File

@ -1,174 +0,0 @@
<?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 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 ***** */
/**
* Update a file for download.
*
*/
class IDF_Form_UpdateUpload extends Pluf_Form
{
public $user = null;
public $project = null;
public $upload = null;
public function initFields($extra=array())
{
$this->user = $extra['user'];
$this->project = $extra['project'];
$this->upload = $extra['upload'];
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Summary'),
'initial' => $this->upload->summary,
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
));
$this->fields['changelog'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Description'),
'initial' => $this->upload->changelog,
'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array(
'cols' => 58,
'rows' => 13,
),
));
$tags = $this->upload->get_tags_list();
for ($i=1;$i<7;$i++) {
$initial = '';
if (isset($tags[$i-1])) {
if ($tags[$i-1]->class != 'Other') {
$initial = (string) $tags[$i-1];
} else {
$initial = $tags[$i-1]->name;
}
}
$this->fields['label'.$i] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Labels'),
'initial' => $initial,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 20,
),
));
}
}
/**
* Validate the interconnection in the form.
*/
public function clean()
{
$conf = new IDF_Conf();
$conf->setProject($this->project);
$onemax = array();
foreach (explode(',', $conf->getVal('labels_download_one_max', IDF_Form_UploadConf::init_one_max)) as $class) {
if (trim($class) != '') {
$onemax[] = mb_strtolower(trim($class));
}
}
$count = array();
for ($i=1;$i<7;$i++) {
$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)),
trim($name));
} else {
$class = 'other';
$name = $this->cleaned_data['label'.$i];
}
if (!isset($count[$class])) $count[$class] = 1;
else $count[$class] += 1;
if (in_array($class, $onemax) and $count[$class] > 1) {
if (!isset($this->errors['label'.$i])) $this->errors['label'.$i] = array();
$this->errors['label'.$i][] = sprintf(__('You cannot provide more than label from the %s class to an issue.'), $class);
throw new Pluf_Form_Invalid(__('You provided an invalid label.'));
}
}
return $this->cleaned_data;
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return Object Model with data set from the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
// Add a tag for each label
$tags = array();
for ($i=1;$i<7;$i++) {
if (strlen($this->cleaned_data['label'.$i]) > 0) {
if (strpos($this->cleaned_data['label'.$i], ':') !== false) {
list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2);
list($class, $name) = array(trim($class), trim($name));
} else {
$class = 'Other';
$name = trim($this->cleaned_data['label'.$i]);
}
$tag = IDF_Tag::add($name, $this->project, $class);
$tags[] = $tag->id;
}
}
// Create the upload
$this->upload->summary = trim($this->cleaned_data['summary']);
$this->upload->changelog = trim($this->cleaned_data['changelog']);
$this->upload->modif_dtime = gmdate('Y-m-d H:i:s');
$this->upload->update();
$this->upload->batchAssoc('IDF_Tag', $tags);
/**
* [signal]
*
* IDF_Upload::update
*
* [sender]
*
* IDF_Form_UpdateUpload
*
* [description]
*
* This signal allows an application to perform a set of tasks
* just after the update of an uploaded file.
*
* [parameters]
*
* array('upload' => $upload);
*
*/
$params = array('upload' => $this->upload);
Pluf_Signal::send('IDF_Upload::update',
'IDF_Form_UpdateUpload', $params);
return $this->upload;
}
}

View File

@ -1,204 +0,0 @@
<?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 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 ***** */
/**
* Upload a file for download.
*
*/
class IDF_Form_Upload extends Pluf_Form
{
public $user = null;
public $project = null;
public function initFields($extra=array())
{
$this->user = $extra['user'];
$this->project = $extra['project'];
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Summary'),
'initial' => '',
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
));
$this->fields['changelog'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Description'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array(
'cols' => 58,
'rows' => 13,
),
));
$this->fields['file'] = new Pluf_Form_Field_File(
array('required' => true,
'label' => __('File'),
'initial' => '',
'max_size' => Pluf::f('max_upload_size', 2097152),
'move_function_params' => array('upload_path' => Pluf::f('upload_path').'/'.$this->project->shortname.'/files',
'upload_path_create' => true,
'upload_overwrite' => false),
));
for ($i=1;$i<7;$i++) {
$this->fields['label'.$i] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Labels'),
'widget_attrs' => array(
'maxlength' => 50,
'size' => 20,
),
));
}
}
public function clean_file()
{
$extra = strtolower(implode('|', explode(' ', Pluf::f('idf_extra_upload_ext'))));
if (strlen($extra)) $extra .= '|';
if (!preg_match('/\.('.$extra.'png|jpg|jpeg|gif|bmp|psd|tif|aiff|asf|avi|bz2|css|doc|eps|gz|jar|mdtext|mid|mov|mp3|mpg|ogg|pdf|ppt|ps|qt|ra|ram|rm|rtf|sdd|sdw|sit|sxi|sxw|swf|tgz|txt|wav|xls|xml|war|wmv|zip)$/i', $this->cleaned_data['file'])) {
@unlink(Pluf::f('upload_path').'/'.$this->project->shortname.'/files/'.$this->cleaned_data['file']);
throw new Pluf_Form_Invalid(__('For security reason, you cannot upload a file with this extension.'));
}
return $this->cleaned_data['file'];
}
/**
* Validate the interconnection in the form.
*/
public function clean()
{
$conf = new IDF_Conf();
$conf->setProject($this->project);
$onemax = array();
foreach (explode(',', $conf->getVal('labels_download_one_max', IDF_Form_UploadConf::init_one_max)) as $class) {
if (trim($class) != '') {
$onemax[] = mb_strtolower(trim($class));
}
}
$count = array();
for ($i=1;$i<7;$i++) {
$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)),
trim($name));
} else {
$class = 'other';
$name = $this->cleaned_data['label'.$i];
}
if (!isset($count[$class])) $count[$class] = 1;
else $count[$class] += 1;
if (in_array($class, $onemax) and $count[$class] > 1) {
if (!isset($this->errors['label'.$i])) $this->errors['label'.$i] = array();
$this->errors['label'.$i][] = sprintf(__('You cannot provide more than label from the %s class to an issue.'), $class);
throw new Pluf_Form_Invalid(__('You provided an invalid label.'));
}
}
return $this->cleaned_data;
}
/**
* If we have uploaded a file, but the form failed remove it.
*
*/
function failed()
{
if (!empty($this->cleaned_data['file'])
and file_exists(Pluf::f('upload_path').'/'.$this->project->shortname.'/files/'.$this->cleaned_data['file'])) {
@unlink(Pluf::f('upload_path').'/'.$this->project->shortname.'/files/'.$this->cleaned_data['file']);
}
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return Object Model with data set from the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
// Add a tag for each label
$tags = array();
for ($i=1;$i<7;$i++) {
if (strlen($this->cleaned_data['label'.$i]) > 0) {
if (strpos($this->cleaned_data['label'.$i], ':') !== false) {
list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2);
list($class, $name) = array(trim($class), trim($name));
} else {
$class = 'Other';
$name = trim($this->cleaned_data['label'.$i]);
}
$tags[] = IDF_Tag::add($name, $this->project, $class);
}
}
// Create the upload
$upload = new IDF_Upload();
$upload->project = $this->project;
$upload->submitter = $this->user;
$upload->summary = trim($this->cleaned_data['summary']);
$upload->changelog = trim($this->cleaned_data['changelog']);
$upload->file = $this->cleaned_data['file'];
$upload->filesize = filesize(Pluf::f('upload_path').'/'.$this->project->shortname.'/files/'.$this->cleaned_data['file']);
$upload->downloads = 0;
$upload->create();
foreach ($tags as $tag) {
$upload->setAssoc($tag);
}
// Send the notification
$upload->notify($this->project->getConf());
/**
* [signal]
*
* IDF_Upload::create
*
* [sender]
*
* IDF_Form_Upload
*
* [description]
*
* This signal allows an application to perform a set of tasks
* just after the upload of a file and after the notification run.
*
* [parameters]
*
* array('upload' => $upload);
*
*/
$params = array('upload' => $upload);
Pluf_Signal::send('IDF_Upload::create', 'IDF_Form_Upload',
$params);
return $upload;
}
}

View File

@ -1,70 +0,0 @@
<?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 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 ***** */
/**
* Configuration of the labels etc. for the uploaded files.
*/
class IDF_Form_UploadConf extends Pluf_Form
{
/**
* Defined as constants to easily access the value in the
* form in the case nothing is in the db yet.
*/
const init_predefined = 'Featured = Listed on project home page
Type:Executable = Executable application
Type:Installer = Download and run to install application
Type:Package = Your OS package manager installs this
Type:Archive = Download, unarchive, then follow directions
Type:Source = Source code archive
Type:Docs = This file contains documentation
OpSys:All = Works with all operating systems
OpSys:Windows = Works with Windows
OpSys:Linux = Works with Linux
OpSys:OSX = Works with Mac OS X
Deprecated = Most users should NOT download this';
const init_one_max = 'Type';
public function initFields($extra=array())
{
$this->fields['labels_download_predefined'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Predefined download labels'),
'initial' => self::init_predefined,
'widget_attrs' => array('rows' => 13,
'cols' => 75),
'widget' => 'Pluf_Form_Widget_TextareaInput',
));
$this->fields['labels_download_one_max'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Each download may have at most one label with each of these classes'),
'initial' => self::init_one_max,
'widget_attrs' => array('size' => 60),
));
}
}

View File

@ -1,431 +0,0 @@
<?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 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 ***** */
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
/**
* Allow a user to update its details.
*/
class IDF_Form_UserAccount extends Pluf_Form
{
public $user = null;
public function initFields($extra=array())
{
$this->user = $extra['user'];
$user_data = IDF_UserData::factory($this->user);
$this->fields['first_name'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('First name'),
'initial' => $this->user->first_name,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['last_name'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Last name'),
'initial' => $this->user->last_name,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 20,
),
));
$this->fields['email'] = new Pluf_Form_Field_Email(
array('required' => true,
'label' => __('Your mail'),
'initial' => $this->user->email,
'help_text' => __('If you change your email address, an email will be sent to the new address to confirm it.'),
));
$this->fields['language'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Language'),
'initial' => $this->user->language,
'widget' => 'Pluf_Form_Widget_SelectInput',
'widget_attrs' => array(
'choices' =>
Pluf_L10n::getInstalledLanguages()
),
));
$this->fields['password'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Your password'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_PasswordInput',
'help_text' => Pluf_Template::markSafe(__('Leave blank if you do not want to change your password.').'<br />'.__('Your password must be hard for other people to find it, but easy for you to remember.')),
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['password2'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Confirm your password'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_PasswordInput',
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['description'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Description'),
'initial' => $user_data->description,
'widget_attrs' => array('rows' => 3,
'cols' => 40),
'widget' => 'Pluf_Form_Widget_TextareaInput',
));
$this->fields['twitter'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Twitter username'),
'initial' => $user_data->twitter,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['public_email'] = new Pluf_Form_Field_Email(
array('required' => false,
'label' => __('Public email address'),
'initial' => $user_data->public_email,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['website'] = new Pluf_Form_Field_Url(
array('required' => false,
'label' => __('Website URL'),
'initial' => $user_data->website,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 15,
),
));
$this->fields['custom_avatar'] = new Pluf_Form_Field_File(
array('required' => false,
'label' => __('Upload custom avatar'),
'initial' => '',
'max_size' => Pluf::f('max_upload_size', 2097152),
'move_function_params' => array('upload_path' => Pluf::f('upload_path').'/avatars',
'upload_path_create' => true,
'upload_overwrite' => true,
'file_name' => 'user_'.$this->user->id.'_%s'),
'help_text' => __('An image file with a width and height not larger than 60 pixels (bigger images are scaled down).'),
));
$this->fields['remove_custom_avatar'] = new Pluf_Form_Field_Boolean(
array('required' => false,
'label' => __('Remove custom avatar'),
'initial' => false,
'widget' => 'Pluf_Form_Widget_CheckboxInput',
'widget_attrs' => array(),
'help_text' => __('Tick this to delete the custom avatar.'),
));
$this->fields['public_key'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Add a public key'),
'initial' => '',
'widget_attrs' => array('rows' => 3,
'cols' => 40),
'widget' => 'Pluf_Form_Widget_TextareaInput',
'help_text' => __('Paste a SSH or monotone public key. Be careful to not provide your private key here!')
));
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return Object Model with data set from the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
unset($this->cleaned_data['password2']);
$update_pass = false;
if (strlen($this->cleaned_data['password']) == 0) {
unset($this->cleaned_data['password']);
} else {
$update_pass = true;
}
$old_email = $this->user->email;
$new_email = $this->cleaned_data['email'];
unset($this->cleaned_data['email']);
if ($old_email != $new_email) {
$cr = new Pluf_Crypt(md5(Pluf::f('secret_key')));
$encrypted = trim($cr->encrypt($new_email.':'.$this->user->id.':'.time()), '~');
$key = substr(md5(Pluf::f('secret_key').$encrypted), 0, 2).$encrypted;
$url = Pluf::f('url_base').Pluf_HTTP_URL_urlForView('IDF_Views_User::changeEmailDo', array($key), array(), false);
$urlik = Pluf::f('url_base').Pluf_HTTP_URL_urlForView('IDF_Views_User::changeEmailInputKey', array(), array(), false);
$context = new Pluf_Template_Context(
array('key' => Pluf_Template::markSafe($key),
'url' => Pluf_Template::markSafe($url),
'urlik' => Pluf_Template::markSafe($urlik),
'email' => $new_email,
'user'=> $this->user,
)
);
$tmpl = new Pluf_Template('idf/user/changeemail-email.txt');
$text_email = $tmpl->render($context);
$email = new Pluf_Mail(Pluf::f('from_email'), $new_email,
__('Confirm your new email address.'));
$email->addTextMessage($text_email);
$email->sendMail();
$this->user->setMessage(sprintf(__('A validation email has been sent to "%s" to validate the email address change.'), Pluf_esc($new_email)));
}
$this->user->setFromFormData($this->cleaned_data);
// Add key as needed.
if ('' !== $this->cleaned_data['public_key']) {
$key = new IDF_Key();
$key->user = $this->user;
$key->content = $this->cleaned_data['public_key'];
if ($commit) {
$key->create();
}
}
if ($commit) {
$this->user->update();
// FIXME: go the extra mile and check the input lengths for
// all fields here!
// FIXME: this is all doubled in admin/UserUpdate!
$user_data = IDF_UserData::factory($this->user);
// Add or remove avatar - we need to do this here because every
// single setter directly leads to a save in the database
if ($user_data->avatar != '' &&
($this->cleaned_data['remove_custom_avatar'] == 1 ||
$this->cleaned_data['custom_avatar'] != '')) {
$avatar_path = Pluf::f('upload_path').'/avatars/'.basename($user_data->avatar);
if (basename($avatar_path) != '' && is_file($avatar_path)) {
unlink($avatar_path);
}
$user_data->avatar = '';
}
if ($this->cleaned_data['custom_avatar'] != '') {
$user_data->avatar = $this->cleaned_data['custom_avatar'];
}
$user_data->description = $this->cleaned_data['description'];
$user_data->twitter = $this->cleaned_data['twitter'];
$user_data->public_email = $this->cleaned_data['public_email'];
$user_data->website = $this->cleaned_data['website'];
if ($update_pass) {
/**
* [signal]
*
* Pluf_User::passwordUpdated
*
* [sender]
*
* IDF_Form_UserAccount
*
* [description]
*
* This signal is sent when the user updated his
* password from his account page.
*
* [parameters]
*
* array('user' => $user)
*
*/
$params = array('user' => $this->user);
Pluf_Signal::send('Pluf_User::passwordUpdated',
'IDF_Form_UserAccount', $params);
}
}
return $this->user;
}
/**
* Check arbitrary public keys.
*
* It will throw a Pluf_Form_Invalid exception if it cannot
* validate the key.
*
* @param $key string The key
* @param $user int The user id of the user of the key (0)
* @return string The clean key
*/
public static function checkPublicKey($key, $user=0)
{
$key = trim($key);
if (strlen($key) == 0) {
return '';
}
if (preg_match('#^ssh\-[a-z]{3}\s\S+(\s\S+)?$#', $key)) {
$key = str_replace(array("\n", "\r"), '', $key);
if (Pluf::f('idf_strong_key_check', false)) {
$tmpfile = Pluf::f('tmp_folder', '/tmp').'/'.$user.'-key';
file_put_contents($tmpfile, $key, LOCK_EX);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').
'ssh-keygen -l -f '.escapeshellarg($tmpfile).' > /dev/null 2>&1';
exec($cmd, $out, $return);
unlink($tmpfile);
if ($return != 0) {
throw new Pluf_Form_Invalid(
__('Please check the key as it does not appear '.
'to be a valid SSH public key.')
);
}
}
}
else if (preg_match('#^\[pubkey [^\]]+\]\s*\S+\s*\[end\]$#', $key)) {
if (Pluf::f('idf_strong_key_check', false)) {
// if monotone can read it, it should be valid
$mtn_opts = implode(' ', Pluf::f('mtn_opts', array()));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').
sprintf('%s %s -d :memory: read >/tmp/php-out 2>&1',
Pluf::f('mtn_path', 'mtn'), $mtn_opts);
$fp = popen($cmd, 'w');
fwrite($fp, $key);
$return = pclose($fp);
if ($return != 0) {
throw new Pluf_Form_Invalid(
__('Please check the key as it does not appear '.
'to be a valid monotone public key.')
);
}
}
}
else {
throw new Pluf_Form_Invalid(
__('Public key looks neither like a SSH '.
'nor monotone public key.'));
}
// If $user, then check if not the same key stored
if ($user) {
$ruser = Pluf::factory('Pluf_User', $user);
if ($ruser->id > 0) {
$sql = new Pluf_SQL('content=%s', array($key));
$keys = Pluf::factory('IDF_Key')->getList(array('filter' => $sql->gen()));
if (count($keys) > 0) {
throw new Pluf_Form_Invalid(
__('You already have uploaded this key.')
);
}
}
}
return $key;
}
function clean_custom_avatar()
{
// Just png, jpeg/jpg or gif
if (!preg_match('/\.(png|jpg|jpeg|gif)$/i', $this->cleaned_data['custom_avatar']) &&
$this->cleaned_data['custom_avatar'] != '') {
@unlink(Pluf::f('upload_path').'/avatars/'.$this->cleaned_data['custom_avatar']);
throw new Pluf_Form_Invalid(__('For security reason, you cannot upload a file with this extension.'));
}
return $this->cleaned_data['custom_avatar'];
}
function clean_last_name()
{
$last_name = trim($this->cleaned_data['last_name']);
if ($last_name == mb_strtoupper($last_name)) {
return mb_convert_case(mb_strtolower($last_name),
MB_CASE_TITLE, 'UTF-8');
}
return $last_name;
}
function clean_first_name()
{
$first_name = trim($this->cleaned_data['first_name']);
if ($first_name == mb_strtoupper($first_name)) {
return mb_convert_case(mb_strtolower($first_name),
MB_CASE_TITLE, 'UTF-8');
}
return $first_name;
}
function clean_email()
{
$this->cleaned_data['email'] = mb_strtolower(trim($this->cleaned_data['email']));
$guser = new Pluf_User();
$sql = new Pluf_SQL('email=%s AND id!=%s',
array($this->cleaned_data['email'], $this->user->id));
if ($guser->getCount(array('filter' => $sql->gen())) > 0) {
throw new Pluf_Form_Invalid(sprintf(__('The email "%s" is already used.'), $this->cleaned_data['email']));
}
return $this->cleaned_data['email'];
}
function clean_public_key()
{
$this->cleaned_data['public_key'] =
self::checkPublicKey($this->cleaned_data['public_key'],
$this->user->id);
return $this->cleaned_data['public_key'];
}
/**
* Check to see if the 2 passwords are the same
*/
public function clean()
{
if (!isset($this->errors['password'])
&& !isset($this->errors['password2'])) {
$password1 = $this->cleaned_data['password'];
$password2 = $this->cleaned_data['password2'];
if ($password1 != $password2) {
throw new Pluf_Form_Invalid(__('The passwords do not match. Please give them again.'));
}
}
return $this->cleaned_data;
}
}

View File

@ -1,84 +0,0 @@
<?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 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 ***** */
/**
* Change the email address of a user.
*
*/
class IDF_Form_UserChangeEmail extends Pluf_Form
{
protected $user;
public function initFields($extra=array())
{
$this->fields['key'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Your verification key'),
'initial' => '',
'widget_attrs' => array(
'size' => 50,
),
));
}
function clean_key()
{
self::validateKey($this->cleaned_data['key']);
return $this->cleaned_data['key'];
}
/**
* Validate the key.
*
* Throw a Pluf_Form_Invalid exception if the key is not valid.
*
* @param string Key
* @return array array($new_email, $user_id, time())
*/
public static function validateKey($key)
{
$hash = substr($key, 0, 2);
$encrypted = substr($key, 2);
if ($hash != substr(md5(Pluf::f('secret_key').$encrypted), 0, 2)) {
throw new Pluf_Form_Invalid(__('The validation key is not valid. Please copy/paste it from your confirmation email.'));
}
$cr = new Pluf_Crypt(md5(Pluf::f('secret_key')));
return explode(':', $cr->decrypt($encrypted), 3);
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return Object Model with data set from the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
return Pluf::f('url_base').Pluf_HTTP_URL_urlForView('IDF_Views_User::changeEmailDo', array($this->cleaned_data['key']));
}
}

View File

@ -1,66 +0,0 @@
<?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 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 ***** */
/**
* Configuration of the labels etc. for the wiki pages.
*/
class IDF_Form_WikiConf extends Pluf_Form
{
/**
* Defined as constants to easily access the value in the
* form in the case nothing is in the db yet.
*/
const init_predefined = 'Featured = Listed on project home page
Phase:Requirements = Project vision and requirements
Phase:Design = Project design and key concerns
Phase:Implementation = Developers\' guide
Phase:QA = Testing plans and QA strategies
Phase:Deploy = How to install and configure the program
Phase:Support = Plans for user support and advocacy
Deprecated = Most users should NOT reference this';
const init_one_max = '';
public function initFields($extra=array())
{
$this->fields['labels_wiki_predefined'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Predefined documentation page labels'),
'initial' => self::init_predefined,
'widget_attrs' => array('rows' => 13,
'cols' => 75),
'widget' => 'Pluf_Form_Widget_TextareaInput',
));
$this->fields['labels_wiki_one_max'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Each documentation page may have at most one label with each of these classes'),
'initial' => self::init_one_max,
'widget_attrs' => array('size' => 60),
));
}
}

View File

@ -1,205 +0,0 @@
<?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 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 ***** */
/**
* Create a new documentation page.
*
* This create a new page and the corresponding revision.
*
*/
class IDF_Form_WikiCreate extends Pluf_Form
{
public $user = null;
public $project = null;
public $show_full = false;
public function initFields($extra=array())
{
$initial = __('# Introduction
Add your content here.
# Details
Add your content here. Format your content with:
* Text in **bold** or *italic*.
* Headings, paragraphs, and lists.
* Links to other [[WikiPage]].
');
$this->user = $extra['user'];
$this->project = $extra['project'];
if ($this->user->hasPerm('IDF.project-owner', $this->project)
or $this->user->hasPerm('IDF.project-member', $this->project)) {
$this->show_full = true;
}
$initname = (!empty($extra['name'])) ? $extra['name'] : __('PageName');
$this->fields['title'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Page title'),
'initial' => $initname,
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
'help_text' => __('The page name must contains only letters, digits and the dash (-) character.'),
));
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Description'),
'help_text' => __('This one line description is displayed in the list of pages.'),
'initial' => '',
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
));
$this->fields['content'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Content'),
'initial' => $initial,
'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array(
'cols' => 68,
'rows' => 26,
),
));
if ($this->show_full) {
for ($i=1;$i<4;$i++) {
$this->fields['label'.$i] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Labels'),
'initial' => '',
'widget_attrs' => array(
'maxlength' => 50,
'size' => 20,
),
));
}
}
}
public function clean_title()
{
$title = $this->cleaned_data['title'];
if (preg_match('/[^a-zA-Z0-9\-]/', $title)) {
throw new Pluf_Form_Invalid(__('The title contains invalid characters.'));
}
$sql = new Pluf_SQL('project=%s AND title=%s',
array($this->project->id, $title));
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
if ($pages->count() > 0) {
throw new Pluf_Form_Invalid(__('A page with this title already exists.'));
}
return $title;
}
/**
* Validate the interconnection in the form.
*/
public function clean()
{
if (!$this->show_full) {
return $this->cleaned_data;
}
$conf = new IDF_Conf();
$conf->setProject($this->project);
$onemax = array();
foreach (explode(',', $conf->getVal('labels_wiki_one_max', IDF_Form_WikiConf::init_one_max)) as $class) {
if (trim($class) != '') {
$onemax[] = mb_strtolower(trim($class));
}
}
$count = array();
for ($i=1;$i<4;$i++) {
$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)),
trim($name));
} else {
$class = 'other';
$name = $this->cleaned_data['label'.$i];
}
if (!isset($count[$class])) $count[$class] = 1;
else $count[$class] += 1;
if (in_array($class, $onemax) and $count[$class] > 1) {
if (!isset($this->errors['label'.$i])) $this->errors['label'.$i] = array();
$this->errors['label'.$i][] = sprintf(__('You cannot provide more than label from the %s class to a page.'), $class);
throw new Pluf_Form_Invalid(__('You provided an invalid label.'));
}
}
return $this->cleaned_data;
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return Object Model with data set from the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
// Add a tag for each label
$tags = array();
if ($this->show_full) {
for ($i=1;$i<4;$i++) {
if (strlen($this->cleaned_data['label'.$i]) > 0) {
if (strpos($this->cleaned_data['label'.$i], ':') !== false) {
list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2);
list($class, $name) = array(trim($class), trim($name));
} else {
$class = 'Other';
$name = trim($this->cleaned_data['label'.$i]);
}
$tags[] = IDF_Tag::add($name, $this->project, $class);
}
}
}
// Create the page
$page = new IDF_WikiPage();
$page->project = $this->project;
$page->submitter = $this->user;
$page->summary = trim($this->cleaned_data['summary']);
$page->title = trim($this->cleaned_data['title']);
$page->create();
foreach ($tags as $tag) {
$page->setAssoc($tag);
}
// add the first revision
$rev = new IDF_WikiRevision();
$rev->wikipage = $page;
$rev->content = $this->cleaned_data['content'];
$rev->submitter = $this->user;
$rev->summary = __('Initial page creation');
$rev->create();
$rev->notify($this->project->getConf());
return $page;
}
}

View File

@ -1,64 +0,0 @@
<?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 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 ***** */
/**
* Delete a documentation page.
*
* This is a hard delete of the page and the revisions.
*
*/
class IDF_Form_WikiDelete extends Pluf_Form
{
protected $page = null;
public function initFields($extra=array())
{
$this->page = $extra['page'];
$this->fields['confirm'] = new Pluf_Form_Field_Boolean(
array('required' => true,
'label' => __('Yes, I understand that the page and all its revisions will be deleted.'),
'initial' => '',
));
}
/**
* Check the confirmation.
*/
public function clean_confirm()
{
if (!$this->cleaned_data['confirm']) {
throw new Pluf_Form_Invalid(__('You need to confirm the deletion.'));
}
return $this->cleaned_data['confirm'];
}
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
$this->page->delete();
return true;
}
}

View File

@ -1,242 +0,0 @@
<?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 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 ***** */
/**
* Update a documentation page.
*
* This add a corresponding revision.
*
*/
class IDF_Form_WikiUpdate extends Pluf_Form
{
public $user = null;
public $project = null;
public $page = null;
public $show_full = false;
public function initFields($extra=array())
{
$this->page = $extra['page'];
$this->user = $extra['user'];
$this->project = $extra['project'];
if ($this->user->hasPerm('IDF.project-owner', $this->project)
or $this->user->hasPerm('IDF.project-member', $this->project)) {
$this->show_full = true;
}
if ($this->show_full) {
$this->fields['title'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Page title'),
'initial' => $this->page->title,
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
'help_text' => __('The page name must contains only letters, digits and the dash (-) character.'),
));
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Description'),
'help_text' => __('This one line description is displayed in the list of pages.'),
'initial' => $this->page->summary,
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
));
}
$rev = $this->page->get_current_revision();
$this->fields['content'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Content'),
'initial' => $rev->content,
'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array(
'cols' => 68,
'rows' => 26,
),
));
$this->fields['comment'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Comment'),
'help_text' => __('One line to describe the changes you made.'),
'initial' => '',
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
));
if ($this->show_full) {
$tags = $this->page->get_tags_list();
for ($i=1;$i<4;$i++) {
$initial = '';
if (isset($tags[$i-1])) {
if ($tags[$i-1]->class != 'Other') {
$initial = (string) $tags[$i-1];
} else {
$initial = $tags[$i-1]->name;
}
}
$this->fields['label'.$i] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Labels'),
'initial' => $initial,
'widget_attrs' => array(
'maxlength' => 50,
'size' => 20,
),
));
}
}
}
public function clean_title()
{
$title = $this->cleaned_data['title'];
if (preg_match('/[^a-zA-Z0-9\-]/', $title)) {
throw new Pluf_Form_Invalid(__('The title contains invalid characters.'));
}
$sql = new Pluf_SQL('project=%s AND title=%s',
array($this->project->id, $title));
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
if ($pages->count() > 0 and $pages[0]->id != $this->page->id) {
throw new Pluf_Form_Invalid(__('A page with this title already exists.'));
}
return $title;
}
/**
* Validate the interconnection in the form.
*/
public function clean()
{
if (!$this->show_full) {
return $this->cleaned_data;
}
$conf = new IDF_Conf();
$conf->setProject($this->project);
$onemax = array();
foreach (explode(',', $conf->getVal('labels_wiki_one_max', IDF_Form_WikiConf::init_one_max)) as $class) {
if (trim($class) != '') {
$onemax[] = mb_strtolower(trim($class));
}
}
$count = array();
for ($i=1;$i<4;$i++) {
$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)),
trim($name));
} else {
$class = 'other';
$name = $this->cleaned_data['label'.$i];
}
if (!isset($count[$class])) $count[$class] = 1;
else $count[$class] += 1;
if (in_array($class, $onemax) and $count[$class] > 1) {
if (!isset($this->errors['label'.$i])) $this->errors['label'.$i] = array();
$this->errors['label'.$i][] = sprintf(__('You cannot provide more than label from the %s class to a page.'), $class);
throw new Pluf_Form_Invalid(__('You provided an invalid label.'));
}
}
return $this->cleaned_data;
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return Object Model with data set from the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
if ($this->show_full) {
$tagids = array();
$tags = array();
for ($i=1;$i<4;$i++) {
if (strlen($this->cleaned_data['label'.$i]) > 0) {
if (strpos($this->cleaned_data['label'.$i], ':') !== false) {
list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2);
list($class, $name) = array(trim($class), trim($name));
} else {
$class = 'Other';
$name = trim($this->cleaned_data['label'.$i]);
}
$tag = IDF_Tag::add($name, $this->project, $class);
$tags[] = $tag;
$tagids[] = $tag->id;
}
}
// Compare between the old and the new data
$changes = array();
$oldtags = $this->page->get_tags_list();
foreach ($tags as $tag) {
if (!Pluf_Model_InArray($tag, $oldtags)) {
if (!isset($changes['lb'])) $changes['lb'] = array();
if ($tag->class != 'Other') {
$changes['lb'][] = (string) $tag; //new tag
} else {
$changes['lb'][] = (string) $tag->name;
}
}
}
foreach ($oldtags as $tag) {
if (!Pluf_Model_InArray($tag, $tags)) {
if (!isset($changes['lb'])) $changes['lb'] = array();
if ($tag->class != 'Other') {
$changes['lb'][] = '-'.(string) $tag; //new tag
} else {
$changes['lb'][] = '-'.(string) $tag->name;
}
}
}
if (trim($this->page->summary) != trim($this->cleaned_data['summary'])) {
$changes['su'] = trim($this->cleaned_data['summary']);
}
// Update the page
$this->page->batchAssoc('IDF_Tag', $tagids);
$this->page->summary = trim($this->cleaned_data['summary']);
$this->page->title = trim($this->cleaned_data['title']);
} else {
$changes = array();
}
$this->page->update();
// add the new revision
$rev = new IDF_WikiRevision();
$rev->wikipage = $this->page;
$rev->content = $this->cleaned_data['content'];
$rev->submitter = $this->user;
$rev->summary = $this->cleaned_data['comment'];
$rev->changes = $changes;
$rev->create();
$rev->notify($this->project->getConf(), false);
return $this->page;
}
}

View File

@ -1,210 +0,0 @@
<?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 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 ***** */
/**
* Configuration of the objects.
*
* It is just storing a list of key/value pairs associated to
* different objects. If you use this table for your model, do not
* forget to drop the corresponding keys in your preDelete call.
*/
class IDF_Gconf extends Pluf_Model
{
public $_model = __CLASS__;
public $datacache = null;
public $dirty = array();
public $f = null;
protected $_mod = null;
/**
* Do we (un)serialize the data when getting/setting them.
*/
public $serialize = false;
function init()
{
$this->_a['table'] = 'idf_gconf';
$this->_a['model'] = __CLASS__;
$this->_a['cols'] = array(
// It is mandatory to have an "id" column.
'id' =>
array(
'type' => 'Pluf_DB_Field_Sequence',
//It is automatically added.
'blank' => true,
),
'model_class' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 150,
'verbose' => __('model class'),
),
'model_id' =>
array(
'type' => 'Pluf_DB_Field_Integer',
'blank' => false,
'verbose' => __('model id'),
),
'vkey' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 50,
'verbose' => __('key'),
),
'vdesc' =>
array(
'type' => 'Pluf_DB_Field_Text',
'blank' => false,
'verbose' => __('value'),
),
);
$this->_a['idx'] = array('model_vkey_idx' =>
array(
'col' => 'model_class, model_id, vkey',
'type' => 'unique',
),
);
$this->f = new IDF_Config_DataProxy($this);
}
function setModel($model)
{
$this->datacache = null;
$this->_mod = $model;
}
function initCache()
{
$this->datacache = array();
$this->dirty = array();
$sql = new Pluf_SQL('model_class=%s AND model_id=%s',
array($this->_mod->_model, $this->_mod->id));
foreach ($this->getList(array('filter' => $sql->gen())) as $val) {
$this->datacache[$val->vkey] = ($this->serialize) ? unserialize($val->vdesc) : $val->vdesc;
$this->dirty[$val->vkey] = $val->id;
}
}
/**
* FIXME: This is not efficient when setting a large number of
* values in a loop.
*/
function setVal($key, $value)
{
if (!is_null($this->getVal($key, null))
and $value == $this->getVal($key)) {
return;
}
$svalue = ($this->serialize) ? serialize($value) : $value;
if (isset($this->dirty[$key])) {
// we get to check if deleted by other process + update
$conf = new IDF_Gconf($this->dirty[$key]);
if ($conf->id == $this->dirty[$key]) {
$conf->vdesc = $svalue;
$conf->update();
$this->datacache[$key] = $value;
return;
}
}
// we insert
$conf = new IDF_Gconf();
$conf->model_class = $this->_mod->_model;
$conf->model_id = $this->_mod->id;
$conf->vkey = $key;
$conf->vdesc = $svalue;
$conf->create();
$this->datacache[$key] = $value;
$this->dirty[$key] = $conf->id;
}
function getVal($key, $default='')
{
if ($this->datacache === null) {
$this->initCache();
}
return (isset($this->datacache[$key])) ? $this->datacache[$key] : $default;
}
function delVal($key, $initcache=true)
{
$gconf = new IDF_Gconf();
$sql = new Pluf_SQL('vkey=%s AND model_class=%s AND model_id=%s', array($key, $this->_mod->_model, $this->_mod->id));
foreach ($gconf->getList(array('filter' => $sql->gen())) as $c) {
$c->delete();
}
if ($initcache) {
$this->initCache();
}
}
/**
* Collection selection.
*
* Suppose you have 5 objects with associated meta data in the
* Gconf storage, if you load the data independently for each
* object, you end up with 5 SELECT queries. With 25 objects, 25
* SELECT. You can select with one query all the data and merge in
* the code. It is faster. The collection selection get a
* model_class and a list of ids and returns an id indexed array
* of associative array data. This is for read only access as you
* do not get a series of Gconf objects.
*/
public static function collect($class, $ids)
{
$gconf = new IDF_Gconf();
$stmpl = sprintf('model_class=%%s AND model_id IN (%s)',
implode(',' , $ids));
$sql = new Pluf_SQL($stmpl, array($class));
$out = array_fill_keys($ids, array());
foreach ($gconf->getList(array('filter' => $sql->gen())) as $c) {
$out[$c->model_id][$c->vkey] = $c->vdesc;
}
return $out;
}
/**
* Drop the conf of a model.
*
* If your model is using this table, just add the following line
* in your preDelete() method:
*
* IDF_Gconf::dropForModel($this)
*
* It will take care of the cleaning.
*/
static public function dropForModel($model)
{
$table = Pluf::factory(__CLASS__)->getSqlTable();
$sql = new Pluf_SQL('model_class=%s AND model_id=%s',
array($model->_model, $model->id));
$db = &Pluf::db();
$db->execute('DELETE FROM '.$table.' WHERE '.$sql->gen());
}
static public function dropUser($signal, &$params)
{
self::dropForModel($params['user']);
}
}

304
src/IDF/Git.php Normal file
View File

@ -0,0 +1,304 @@
<?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 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 ***** */
/**
* Git utils.
*
*/
class IDF_Git
{
public $repo = '';
public $mediumtree_fmt = 'commit %H%nAuthor: %an <%ae>%nTree: %T%nDate: %ai%n%n%s%n%n%b';
public function __construct($repo)
{
$this->repo = $repo;
}
/**
* Test a given object hash.
*
* @param string Object hash.
* @return mixed false if not valid or 'blob', 'tree', 'commit'
*/
public function testHash($hash)
{
$cmd = sprintf('GIT_DIR=%s git cat-file -t %s',
escapeshellarg($this->repo),
escapeshellarg($hash));
$ret = 0; $out = array();
exec($cmd, &$out, &$ret);
if ($ret != 0) return false;
return trim($out[0]);
}
/**
* Given a commit hash returns an array of files in it.
*
* A file is a class with the following properties:
*
* 'perm', 'type', 'size', 'hash', 'file'
*
* @param string Commit ('HEAD')
* @param string Base folder ('')
* @return array
*/
public function filesAtCommit($commit='HEAD', $folder='')
{
// now we grab the info about this commit including its tree.
$co = $this->getCommit($commit);
if ($folder) {
// As we are limiting to a given folder, we need to find
// the tree corresponding to this folder.
$found = false;
foreach ($this->getTreeInfo($co->tree) as $file) {
if ($file->type == 'tree' and $file->file == $folder) {
$found = true;
$tree = $file->hash;
break;
}
}
if (!$found) {
throw new Exception(sprintf(__('Folder %1$s not found in commit %2$s.'), $folder, $commit));
}
} else {
$tree = $co->tree;
}
$res = array();
// get the raw log corresponding to this commit to find the
// origin of each file.
$rawlog = array();
$cmd = sprintf('GIT_DIR=%s git log --raw --abbrev=40 --pretty=oneline %s',
escapeshellarg($this->repo), escapeshellarg($commit));
exec($cmd, &$rawlog);
// We reverse the log to be able to use a fixed efficient
// regex without back tracking.
$rawlog = implode("\n", array_reverse($rawlog));
foreach ($this->getTreeInfo($tree, false) as $file) {
// Now we grab the files in the current tree with as much
// information as possible.
$matches = array();
if ($file->type == 'blob' and preg_match('/^\:\d{6} \d{6} [0-9a-f]{40} '.$file->hash.' .*^([0-9a-f]{40})/msU',
$rawlog, &$matches)) {
$fc = $this->getCommit($matches[1]);
$file->date = $fc->date;
$file->log = $fc->title;
} else if ($file->type == 'blob') {
$file->date = $co->date;
$file->log = $co->title;
}
$file->fullpath = ($folder) ? $folder.'/'.$file->file : $file->file;
$res[] = $file;
}
return $res;
}
/**
* Get the tree info.
*
* @param string Tree hash
* @param bool Do we recurse in subtrees (true)
* @return array Array of file information.
*/
public function getTreeInfo($tree, $recurse=true)
{
if ('tree' != $this->testHash($tree)) {
throw new Exception(sprintf(__('Not a valid tree: %s.'), $tree));
}
$cmd_tmpl = 'GIT_DIR=%s git-ls-tree%s -t -l %s';
$cmd = sprintf($cmd_tmpl,
escapeshellarg($this->repo),
($recurse) ? ' -r' : '',
escapeshellarg($tree));
$out = array();
$res = array();
exec($cmd, &$out);
foreach ($out as $line) {
list($perm, $type, $hash, $size, $file) = preg_split('/ |\t/', $line, 5, PREG_SPLIT_NO_EMPTY);
$res[] = (object) array('perm' => $perm, 'type' => $type,
'size' => $size, 'hash' => $hash,
'file' => $file);
}
return $res;
}
/**
* Get the file info.
*
* @param string File
* @param string Commit ('HEAD')
* @return false Information
*/
public function getFileInfo($totest, $commit='HEAD')
{
$cmd_tmpl = 'GIT_DIR=%s git-ls-tree -r -t -l %s';
$cmd = sprintf($cmd_tmpl,
escapeshellarg($this->repo),
escapeshellarg($commit));
$out = array();
exec($cmd, &$out);
foreach ($out as $line) {
list($perm, $type, $hash, $size, $file) = preg_split('/ |\t/', $line, 5, PREG_SPLIT_NO_EMPTY);
if ($totest == $file) {
return (object) array('perm' => $perm, 'type' => $type,
'size' => $size, 'hash' => $hash,
'file' => $file);
}
}
return false;
}
/**
* Get a blob.
*
* @param string Blob hash
* @return string Raw blob
*/
public function getBlob($hash)
{
return shell_exec(sprintf('GIT_DIR=%s git-cat-file blob %s',
escapeshellarg($this->repo),
escapeshellarg($hash)));
}
/**
* Get the branches.
*
* @return array Branches.
*/
public function getBranches()
{
$out = array();
exec(sprintf('GIT_DIR=%s git branch',
escapeshellarg($this->repo)), &$out);
$res = array();
foreach ($out as $b) {
$res[] = substr($b, 2);
}
return $res;
}
/**
* Get commit details.
*
* @param string Commit ('HEAD').
* @return array Changes.
*/
public function getCommit($commit='HEAD')
{
$cmd = sprintf('GIT_DIR=%s git show --date=iso --pretty=format:%s %s',
escapeshellarg($this->repo),
"'".$this->mediumtree_fmt."'",
escapeshellarg($commit));
$out = array();
exec($cmd, &$out);
$log = array();
$change = array();
$inchange = false;
foreach ($out as $line) {
if (!$inchange and 0 === strpos($line, 'diff --git a')) {
$inchange = true;
}
if ($inchange) {
$change[] = $line;
} else {
$log[] = $line;
}
}
$out = self::parseLog($log, 4);
$out[0]->changes = implode("\n", $change);
return $out[0];
}
/**
* Get latest changes.
*
* @param string Commit ('HEAD').
* @param int Number of changes (10).
* @return array Changes.
*/
public function getChangeLog($commit='HEAD', $n=10)
{
if ($n === null) $n = '';
else $n = ' -'.$n;
$cmd = sprintf('GIT_DIR=%s git log%s --date=iso --pretty=format:\'%s\' %s',
escapeshellarg($this->repo), $n, $this->mediumtree_fmt,
escapeshellarg($commit));
$out = array();
exec($cmd, &$out);
return self::parseLog($out, 4);
}
/**
* Parse the log lines of a --pretty=medium log output.
*
* @param array Lines.
* @param int Number of lines in the headers (3)
* @return array Change log.
*/
public static function parseLog($lines, $hdrs=3)
{
$res = array();
$c = array();
$i = 0;
$hdrs += 2;
foreach ($lines as $line) {
$i++;
if (0 === strpos($line, 'commit')) {
if (count($c) > 0) {
$c['full_message'] = trim($c['full_message']);
$res[] = (object) $c;
}
$c = array();
$c['commit'] = trim(substr($line, 7));
$c['full_message'] = '';
$i=1;
continue;
}
if ($i == $hdrs) {
$c['title'] = trim($line);
continue;
}
$match = array();
if (preg_match('/(\S+)\s*:\s*(.*)/', $line, $match)) {
$match[1] = strtolower($match[1]);
$c[$match[1]] = trim($match[2]);
if ($match[1] == 'date') {
$c['date'] = gmdate('Y-m-d H:i:s', strtotime($match[2]));
}
continue;
}
if ($i > ($hdrs+1)) {
$c['full_message'] .= trim($line)."\n";
continue;
}
}
$c['full_message'] = trim($c['full_message']);
$res[] = (object) $c;
return $res;
}
}

View File

@ -21,9 +21,6 @@
# #
# ***** END LICENSE BLOCK ***** */ # ***** END LICENSE BLOCK ***** */
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
Pluf::loadFunction('Pluf_Template_dateAgo');
/** /**
* Base definition of an issue. * Base definition of an issue.
* *
@ -135,21 +132,11 @@ class IDF_Issue extends Pluf_Model
function _toIndex() function _toIndex()
{ {
$r = array(); return '';
foreach ($this->get_comments_list() as $c) {
$r[] = $c->_toIndex();
}
$str = str_repeat($this->summary.' ', 4).' '.implode(' ', $r);
return Pluf_Text::cleanString(html_entity_decode($str, ENT_QUOTES, 'UTF-8'));
} }
function preDelete()
{
IDF_Timeline::remove($this);
IDF_Search::remove($this);
}
function preSave($create=false) function preSave()
{ {
if ($this->id == '') { if ($this->id == '') {
$this->creation_dtime = gmdate('Y-m-d H:i:s'); $this->creation_dtime = gmdate('Y-m-d H:i:s');
@ -157,172 +144,15 @@ class IDF_Issue extends Pluf_Model
$this->modif_dtime = gmdate('Y-m-d H:i:s'); $this->modif_dtime = gmdate('Y-m-d H:i:s');
} }
function postSave($create=false) function postSave()
{ {
// Note: No indexing is performed here. The indexing is // This will be used to fire the indexing or send a
// triggered in the postSave step of the comment to ensure // notification email to the interested people, etc.
// that the issue as at least one comment in the database when $q = new Pluf_Queue();
// doing the indexing. $q->model_class = __CLASS__;
if ($create) { $q->model_id = $this->id;
IDF_Timeline::insert($this, $this->get_project(), $q->action = 'updated';
$this->get_submitter()); $q->lock = 0;
} $q->create();
}
/**
* Returns an HTML fragment used to display this issue in the
* timeline.
*
* The request object is given to be able to check the rights and
* as such create links to other items etc. You can consider that
* if displayed, you can create a link to it.
*
* @param Pluf_HTTP_Request
* @return Pluf_Template_SafeString
*/
public function timelineFragment($request)
{
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
array($request->project->shortname,
$this->id));
$out = '<tr class="log"><td><a href="'.$url.'">'.
Pluf_esc(Pluf_Template_dateAgo($this->creation_dtime, 'without')).
'</a></td><td>';
$stag = new IDF_Template_ShowUser();
$user = $stag->start($this->get_submitter(), $request, '', false);
$ic = (in_array($this->status, $request->project->getTagIdsByStatus('closed'))) ? 'issue-c' : 'issue-o';
$out .= sprintf(__('<a href="%1$s" class="%2$s" title="View issue">Issue %3$d</a>, %4$s'), $url, $ic, $this->id, Pluf_esc($this->summary)).'</td>';
$out .= "\n".'<tr class="extra"><td colspan="2">
<div class="helptext right">'.sprintf(__('Creation of <a href="%s" class="%s">issue&nbsp;%d</a>, by %s'), $url, $ic, $this->id, $user).'</div></td></tr>';
return Pluf_Template::markSafe($out);
}
public function feedFragment($request)
{
$url = Pluf::f('url_base')
.Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
array($request->project->shortname,
$this->id));
$title = sprintf(__('%s: Issue %d created - %s'),
$request->project->name,
$this->id, $this->summary);
$cts = $this->get_comments_list(array('order' => 'id ASC',
'nb' => 1));
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
$context = new Pluf_Template_Context_Request(
$request,
array('url' => $url,
'author' => $this->get_submitter(),
'title' => $title,
'c' => $cts[0],
'issue' => $this,
'date' => $date)
);
$tmpl = new Pluf_Template('idf/issues/feedfragment.xml');
return $tmpl->render($context);
}
/**
* Notification of change of the object.
*
* For the moment, only email, but one can add webhooks later.
*
* Usage:
* <pre>
* $this->notify($conf); // Notify the creation
* $this->notify($conf, false); // Notify the update of the object
* </pre>
*
* @param IDF_Conf Current configuration
* @param bool Creation (true)
*/
public function notify($conf, $create=true)
{
$prj = $this->get_project();
$to_email = array();
if ('' != $conf->getVal('issues_notification_email', '')) {
$langs = Pluf::f('languages', array('en'));
$addresses = explode(',', $conf->getVal('issues_notification_email'));
foreach ($addresses as $address) {
$to_email[] = array($address, $langs[0]);
}
}
$current_locale = Pluf_Translation::getLocale();
$id = '<'.md5($this->id.md5(Pluf::f('secret_key'))).'@'.Pluf::f('mail_host', 'localhost').'>';
if ($create) {
if (null != $this->get_owner() and $this->owner != $this->submitter) {
$email_lang = array($this->get_owner()->email,
$this->get_owner()->language);
if (!in_array($email_lang, $to_email)) {
$to_email[] = $email_lang;
}
}
$comments = $this->get_comments_list(array('order' => 'id ASC'));
$context = new Pluf_Template_Context(
array(
'issue' => $this,
'comment' => $comments[0],
'project' => $prj,
'url_base' => Pluf::f('url_base'),
)
);
foreach ($to_email as $email_lang) {
Pluf_Translation::loadSetLocale($email_lang[1]);
$email = new Pluf_Mail(Pluf::f('from_email'), $email_lang[0],
sprintf(__('Issue %s - %s (%s)'),
$this->id, $this->summary, $prj->shortname));
$tmpl = new Pluf_Template('idf/issues/issue-created-email.txt');
$email->addTextMessage($tmpl->render($context));
$email->addHeaders(array('Message-ID'=>$id));
$email->sendMail();
}
} else {
$comments = $this->get_comments_list(array('order' => 'id DESC'));
$email_sender = '';
if (isset($comments[0])) {
$email_sender = $comments[0]->get_submitter()->email;
}
foreach ($this->get_interested_list() as $interested) {
$email_lang = array($interested->email,
$interested->language);
if (!in_array($email_lang, $to_email)) {
$to_email[] = $email_lang;
}
}
$email_lang = array($this->get_submitter()->email,
$this->get_submitter()->language);
if (!in_array($email_lang, $to_email)) {
$to_email[] = $email_lang;
}
if (null != $this->get_owner()) {
$email_lang = array($this->get_owner()->email,
$this->get_owner()->language);
if (!in_array($email_lang, $to_email)) {
$to_email[] = $email_lang;
}
}
$context = new Pluf_Template_Context(
array(
'issue' => $this,
'comments' => $comments,
'project' => $prj,
'url_base' => Pluf::f('url_base'),
));
foreach ($to_email as $email_lang) {
if ($email_lang[0] == $email_sender) {
continue; // Do not notify the one having created
// the comment
}
Pluf_Translation::loadSetLocale($email_lang[1]);
$email = new Pluf_Mail(Pluf::f('from_email'), $email_lang[0],
sprintf(__('Updated Issue %s - %s (%s)'),
$this->id, $this->summary, $prj->shortname));
$tmpl = new Pluf_Template('idf/issues/issue-updated-email.txt');
$email->addTextMessage($tmpl->render($context));
$email->addHeaders(array('References'=>$id));
$email->sendMail();
}
}
Pluf_Translation::loadSetLocale($current_locale);
} }
} }

View File

@ -90,7 +90,7 @@ class IDF_IssueComment extends Pluf_Model
function changedIssue() function changedIssue()
{ {
return (is_array($this->changes) and count($this->changes) > 0); return count($this->changes) > 0;
} }
function _toIndex() function _toIndex()
@ -98,107 +98,27 @@ class IDF_IssueComment extends Pluf_Model
return $this->content; return $this->content;
} }
function preDelete() function preSave()
{
IDF_Timeline::remove($this);
}
function preSave($create=false)
{ {
if ($this->id == '') { if ($this->id == '') {
$this->creation_dtime = gmdate('Y-m-d H:i:s'); $this->creation_dtime = gmdate('Y-m-d H:i:s');
} }
} }
function postSave($create=false) function postSave()
{ {
if ($create) { // This will be used to fire the indexing or send a
// Check if more than one comment for this issue. We do // notification email to the interested people, etc.
// not want to insert the first comment in the timeline as $q = new Pluf_Queue();
// the issue itself is inserted. $q->model_class = __CLASS__;
$sql = new Pluf_SQL('issue=%s', array($this->issue)); $q->model_id = $this->id;
$co = Pluf::factory('IDF_IssueComment')->getList(array('filter'=>$sql->gen())); $q->action = 'updated';
if ($co->count() > 1) { $q->lock = 0;
IDF_Timeline::insert($this, $this->get_issue()->get_project(), $q->create();
$this->get_submitter());
} }
} }
IDF_Search::index($this->get_issue());
}
public function timelineFragment($request) function IDF_IssueComment_Filter($text)
{ {
$issue = $this->get_issue(); return wordwrap($text, 80, "\n", true);
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
array($request->project->shortname,
$issue->id));
$url .= '#ic'.$this->id;
$out = "\n".'<tr class="log"><td><a href="'.$url.'">'.
Pluf_esc(Pluf_Template_dateAgo($this->creation_dtime, 'without')).
'</a></td><td>';
$stag = new IDF_Template_ShowUser();
$user = $stag->start($this->get_submitter(), $request, '', false);
$ic = (in_array($issue->status, $request->project->getTagIdsByStatus('closed'))) ? 'issue-c' : 'issue-o';
$out .= sprintf(__('<a href="%1$s" class="%2$s" title="View issue">Issue %3$d</a>, %4$s'), $url, $ic, $issue->id, Pluf_esc($issue->summary));
if ($this->changedIssue()) {
$out .= '<div class="issue-changes-timeline">';
foreach ($this->changes as $w => $v) {
$out .= '<strong>';
switch ($w) {
case 'su':
$out .= __('Summary:'); break;
case 'st':
$out .= __('Status:'); break;
case 'ow':
$out .= __('Owner:'); break;
case 'lb':
$out .= __('Labels:'); break;
}
$out .= '</strong>&nbsp;';
if ($w == 'lb') {
$out .= Pluf_esc(implode(', ', $v));
} else {
$out .= Pluf_esc($v);
}
$out .= ' ';
}
$out .= '</div>';
}
$out .= '</td></tr>';
$out .= "\n".'<tr class="extra"><td colspan="2">
<div class="helptext right">'.sprintf(__('Comment on <a href="%s" class="%s">issue&nbsp;%d</a>, by %s'), $url, $ic, $issue->id, $user).'</div></td></tr>';
return Pluf_Template::markSafe($out);
}
public function feedFragment($request)
{
$issue = $this->get_issue();
$url = Pluf::f('url_base')
.Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
array($request->project->shortname,
$issue->id));
$title = sprintf(__('%s: Comment on issue %d - %s'),
Pluf_esc($request->project->name),
$issue->id, Pluf_esc($issue->summary));
$url .= '#ic'.$this->id;
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
$context = new Pluf_Template_Context_Request(
$request,
array('url' => $url,
'author' => $issue->get_submitter(),
'title' => $title,
'c' => $this,
'issue' => $issue,
'date' => $date)
);
$tmpl = new Pluf_Template('idf/issues/feedfragment.xml');
return $tmpl->render($context);
}
public function get_submitter_data()
{
return IDF_UserData::factory($this->get_submitter());
}
} }

View File

@ -1,137 +0,0 @@
<?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 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 file uploaded with an issue or a comment to an issue.
*
*/
class IDF_IssueFile extends Pluf_Model
{
public $_model = __CLASS__;
function init()
{
$this->_a['table'] = 'idf_issuefiles';
$this->_a['model'] = __CLASS__;
$this->_a['cols'] = array(
// It is mandatory to have an "id" column.
'id' =>
array(
'type' => 'Pluf_DB_Field_Sequence',
//It is automatically added.
'blank' => true,
),
'comment' =>
array(
'type' => 'Pluf_DB_Field_Foreignkey',
'model' => 'IDF_IssueComment',
'blank' => false,
'verbose' => __('comment'),
'relate_name' => 'attachment',
),
'submitter' =>
array(
'type' => 'Pluf_DB_Field_Foreignkey',
'model' => 'Pluf_User',
'blank' => false,
'verbose' => __('submitter'),
),
'filename' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => true,
'size' => 100,
'verbose' => __('file name'),
),
'attachment' =>
array(
'type' => 'Pluf_DB_Field_File',
'blank' => false,
'verbose' => __('the file'),
),
'filesize' =>
array(
'type' => 'Pluf_DB_Field_Integer',
'blank' => true,
'verbose' => __('file size'),
'help_text' => 'Size in bytes.',
),
'type' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 10,
'verbose' => __('type'),
'choices' => array(
__('Image') => 'img',
__('Other') => 'other',
),
'default' => 'other',
'help_text' => 'The type is to display a thumbnail of the image.',
),
'creation_dtime' =>
array(
'type' => 'Pluf_DB_Field_Datetime',
'blank' => true,
'verbose' => __('creation date'),
),
'modif_dtime' =>
array(
'type' => 'Pluf_DB_Field_Datetime',
'blank' => true,
'verbose' => __('modification date'),
),
);
}
function preSave($create=false)
{
if ($this->id == '') {
$this->creation_dtime = gmdate('Y-m-d H:i:s');
$file = Pluf::f('upload_issue_path').'/'.$this->attachment;
$this->filesize = filesize($file);
// remove .dummy
$this->filename = substr(basename($file), 0, -6);
$img_extensions = array('jpeg', 'jpg', 'png', 'gif');
$info = pathinfo($this->filename);
if (!isset($info['extension'])) $info['extension'] = '';
if (in_array(strtolower($info['extension']), $img_extensions)) {
$this->type = 'img';
} else {
$this->type = 'other';
}
}
$this->modif_dtime = gmdate('Y-m-d H:i:s');
}
function preDelete()
{
@unlink(Pluf::f('upload_issue_path').'/'.$this->attachment);
}
function isText()
{
$info = IDF_FileUtil::getMimeType($this->filename);
return IDF_FileUtil::isText($info);
}
}

View File

@ -1,182 +0,0 @@
<?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 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 ***** */
/**
* Storage of the public keys (ssh or monotone).
*
*/
class IDF_Key extends Pluf_Model
{
public $_model = __CLASS__;
function init()
{
$this->_a['table'] = 'idf_keys';
$this->_a['model'] = __CLASS__;
$this->_a['cols'] = array(
// It is mandatory to have an "id" column.
'id' =>
array(
'type' => 'Pluf_DB_Field_Sequence',
//It is automatically added.
'blank' => true,
),
'user' =>
array(
'type' => 'Pluf_DB_Field_Foreignkey',
'model' => 'Pluf_User',
'blank' => false,
'verbose' => __('user'),
),
'content' =>
array(
'type' => 'Pluf_DB_Field_Text',
'blank' => false,
'verbose' => __('public key'),
),
);
// WARNING: Not using getSqlTable on the Pluf_User object to
// avoid recursion.
$t_users = $this->_con->pfx.'users';
$this->_a['views'] = array(
'join_user' =>
array(
'join' => 'LEFT JOIN '.$t_users
.' ON '.$t_users.'.id='.$this->_con->qn('user'),
'select' => $this->getSelect().', '
.$t_users.'.login AS login',
'props' => array('login' => 'login'),
)
);
}
function showCompact()
{
return Pluf_Template::markSafe(Pluf_esc(substr($this->content, 0, 25)).' [...] '.Pluf_esc(substr($this->content, -55)));
}
private function parseContent()
{
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)) {
return array('ssh', $m[2], $m[1]);
}
throw new Exception(__('Invalid or unknown key data detected.'));
}
/**
* Returns the type of the public key
*
* @return string 'ssh' or 'mtn'
*/
function getType()
{
list($type, , ) = $this->parseContent();
return $type;
}
/**
* Returns the key name of the key
*
* @return string
*/
function getName()
{
list(, $keyName, ) = $this->parseContent();
return $keyName;
}
/**
* This function should be used to calculate the key id from the
* public key hash for authentication purposes. This avoids clashes
* in case the key name is not unique across the project
*
* And yes, this is actually how monotone itself calculates the key
* id...
*
* @return string
*/
function getMtnId()
{
list($type, $keyName, $keyData) = $this->parseContent();
if ($type != 'mtn')
throw new Exception('key is not a monotone public key');
return sha1($keyName.":".$keyData);
}
function postSave($create=false)
{
/**
* [signal]
*
* IDF_Key::postSave
*
* [sender]
*
* IDF_Key
*
* [description]
*
* This signal allows an application to perform special
* operations after the saving of a public Key.
*
* [parameters]
*
* array('key' => $key,
* 'created' => true/false)
*
*/
$params = array('key' => $this, 'created' => $create);
Pluf_Signal::send('IDF_Key::postSave',
'IDF_Key', $params);
}
function preDelete()
{
/**
* [signal]
*
* IDF_Key::preDelete
*
* [sender]
*
* IDF_Key
*
* [description]
*
* This signal allows an application to perform special
* operations before a key is deleted.
*
* [parameters]
*
* array('key' => $key)
*
*/
$params = array('key' => $this);
Pluf_Signal::send('IDF_Key::preDelete',
'IDF_Key', $params);
}
}

View File

@ -24,7 +24,6 @@
/** /**
* Project middleware. * Project middleware.
* *
* It must be after the session middleware.
*/ */
class IDF_Middleware class IDF_Middleware
{ {
@ -34,9 +33,9 @@ class IDF_Middleware
* When processing the request, check if matching a project. If * When processing the request, check if matching a project. If
* so, directly set $request->project to the project. * so, directly set $request->project to the project.
* *
* The url to match a project is in the format /p/(\w+)/whatever * The url to match a project is in the format
* or /api/p/(\w+)/whatever. This means that it will not try to * /p/(\w+)/whatever. This means that it will not try to match on
* match on /login/ or /logout/. * /login/ or /logout/.
* *
* @param Pluf_HTTP_Request The request * @param Pluf_HTTP_Request The request
* @return bool false or redirect. * @return bool false or redirect.
@ -44,75 +43,24 @@ class IDF_Middleware
function process_request(&$request) function process_request(&$request)
{ {
$match = array(); $match = array();
if (preg_match('#^/(?:api/p|p)/([\-\w]+)/?#', $request->query, $match)) { if (preg_match('#^/p/(\w+)/#', $request->query, $match)) {
try {
$request->project = IDF_Project::getOr404($match[1]); $request->project = IDF_Project::getOr404($match[1]);
} catch (Pluf_HTTP_Error404 $e) {
return new Pluf_HTTP_Response_NotFound($request);
}
$request->conf = new IDF_Conf();
$request->conf->setProject($request->project);
self::setRights($request);
} }
return false; return false;
} }
public static function setRights(&$request)
{
$ak = array('downloads_access_rights' => 'hasDownloadsAccess',
'wiki_access_rights' => 'hasWikiAccess',
'review_access_rights' => 'hasReviewAccess',
'source_access_rights' => 'hasSourceAccess',
'issues_access_rights' => 'hasIssuesAccess');
$request->rights = array();
foreach ($ak as $key=>$val) {
$request->rights[$val] = (true === IDF_Precondition::accessTabGeneric($request, $key));
}
}
/**
* Update the template tags and modifiers to not have them in the config.
*
* This is here at the moment because we do not want to put that
* in a IDF_Template class just for one method.
*
*/
public static function updateTemplateTagsModifiers($sender, &$params)
{
$params['tags'] = array_merge($params['tags'],
array(
'hotkey' => 'IDF_Template_HotKey',
'issuetext' => 'IDF_Template_IssueComment',
'timeline' => 'IDF_Template_TimelineFragment',
'markdown' => 'IDF_Template_Markdown',
'showuser' => 'IDF_Template_ShowUser',
'ashowuser' => 'IDF_Template_AssignShowUser',
));
$params['modifiers'] = array_merge($params['modifiers'],
array(
'size' => 'IDF_Views_Source_PrettySize',
'ssize' => 'IDF_Views_Source_PrettySizeSimple',
'shorten' => 'IDF_Views_Source_ShortenString',
));
}
} }
function IDF_Middleware_ContextPreProcessor($request) function IDF_Middleware_ContextPreProcessor($request)
{ {
$c = array(); $c = array();
$c['request'] = $request;
$c['isAdmin'] = ($request->user->administrator or $request->user->staff);
if (isset($request->project)) { if (isset($request->project)) {
$c['project'] = $request->project; $c['project'] = $request->project;
$c['isOwner'] = $request->user->hasPerm('IDF.project-owner', $c['isOwner'] = $request->user->hasPerm('IDF.project-owner',
$request->project); $request->project);
$c['isMember'] = $request->user->hasPerm('IDF.project-member', $c['isMember'] = $request->user->hasPerm('IDF.project-member',
$request->project); $request->project);
$c = array_merge($c, $request->rights);
} }
$c['usherConfigured'] = Pluf::f("mtn_usher_conf", null) !== null;
$c['allProjects'] = IDF_Views::getProjects($request->user);
return $c; return $c;
} }

View File

@ -1,52 +0,0 @@
<?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 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 SSH key.
*/
function IDF_Migrations_10SshKey_up($params=null)
{
$models = array(
'IDF_Key',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_10SshKey_down($params=null)
{
$models = array(
'IDF_Key',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -1,52 +0,0 @@
<?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 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 DB based Git cache.
*/
function IDF_Migrations_11GitCache_up($params=null)
{
$models = array(
'IDF_Scm_Cache_Git',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_11GitCache_down($params=null)
{
$models = array(
'IDF_Scm_Cache_Git',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -1,55 +0,0 @@
<?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 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 private column for the project.
*/
function IDF_Migrations_12DownloadDesc_up($params=null)
{
$table = Pluf::factory('IDF_Upload')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' ADD COLUMN "changelog" TEXT DEFAULT \'\'';
$sql['MySQL'] = 'ALTER TABLE '.$table.' ADD COLUMN `changelog` LONGTEXT 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]);
}
function IDF_Migrations_12DownloadDesc_down($params=null)
{
$table = Pluf::factory('IDF_Upload')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' DROP COLUMN "changelog"';
$sql['MySQL'] = 'ALTER TABLE '.$table.' DROP COLUMN `changelog`';
$db = Pluf::db();
$engine = Pluf::f('db_engine');
if (!isset($sql[$engine])) {
throw new Exception('SQLite complex migration not supported.');
}
$db->execute($sql[$engine]);
}

View File

@ -1,60 +0,0 @@
<?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 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 ***** */
/**
* Remove the old review and add the new one.
*
* This is a destructive operation.
*/
function IDF_Migrations_13NewReview_up($params=null)
{
$extra = (Pluf::f('db_engine') == 'PostgreSQL') ? ' CASCADE' : '';
$pfx = Pluf::f('db_table_prefix');
$tables = array('idf_review_filecomments',
'idf_review_patches',
'idf_review_pluf_user_assoc',
'idf_review_idf_tag_assoc',
'idf_reviews');
$db = Pluf::db();
foreach ($tables as $table) {
$db->execute('DROP TABLE IF EXISTS '.$pfx.$table.$extra);
}
$models = array(
'IDF_Review',
'IDF_Review_Patch',
'IDF_Review_Comment',
'IDF_Review_FileComment',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_13NewReview_down($params=null)
{
// We do nothing as we cannot go back to the old reviews
}

View File

@ -1,53 +0,0 @@
<?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 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_Queue model.
*
*/
function IDF_Migrations_14Queue_up($params=null)
{
$models = array(
'IDF_Queue',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_14Queue_down($params=null)
{
$models = array(
'IDF_Queue',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -1,53 +0,0 @@
<?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 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_Gconf model.
*
*/
function IDF_Migrations_15AddGconf_up($params=null)
{
$models = array(
'IDF_Gconf',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_15AddGconf_down($params=null)
{
$models = array(
'IDF_Gconf',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -1,52 +0,0 @@
<?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 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 download of files.
*/
function IDF_Migrations_1Download_up($params=null)
{
$models = array(
'IDF_Upload',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_1Download_down($params=null)
{
$models = array(
'IDF_Upload',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -1,55 +0,0 @@
<?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 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 download of files.
*/
function IDF_Migrations_2Search_up($params=null)
{
$models = array(
'IDF_Search_Occ',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
foreach (Pluf::factory('IDF_Issue')->getList() as $i) {
IDF_Search::index($i);
}
}
function IDF_Migrations_2Search_down($params=null)
{
$models = array(
'IDF_Search_Occ',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -1,52 +0,0 @@
<?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 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 download of files.
*/
function IDF_Migrations_3Attachments_up($params=null)
{
$models = array(
'IDF_IssueFile',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_3Attachments_down($params=null)
{
$models = array(
'IDF_IssueFile',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -1,54 +0,0 @@
<?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 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 download of files.
*/
function IDF_Migrations_4Timeline_up($params=null)
{
$models = array(
'IDF_Commit',
'IDF_Timeline',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_4Timeline_down($params=null)
{
$models = array(
'IDF_Timeline',
'IDF_Commit',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -1,47 +0,0 @@
<?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 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 download of files.
*/
function IDF_Migrations_5DescToText_up($params=null)
{
$table = Pluf::factory('IDF_Conf')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' ALTER vdesc TYPE text';
$sql['MySQL'] = 'ALTER TABLE '.$table.' CHANGE vdesc TYPE text';
$db = Pluf::db();
$engine = Pluf::f('db_engine');
if (!isset($sql[$engine])) {
echo 'Skip SQLite upgrade as not needed.'."\n";
return;
}
$db->execute($sql[$engine]);
}
function IDF_Migrations_5DescToText_down($params=null)
{
// lazy, do not care
return;
}

View File

@ -1,63 +0,0 @@
<?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 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 private column for the project.
*/
function IDF_Migrations_6PrivateProject_up($params=null)
{
$table = Pluf::factory('IDF_Project')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' ADD COLUMN "private" INTEGER DEFAULT 0';
$sql['MySQL'] = 'ALTER TABLE '.$table.' ADD COLUMN `private` INTEGER DEFAULT 0';
$db = Pluf::db();
$engine = Pluf::f('db_engine');
if (!isset($sql[$engine])) {
throw new Exception('SQLite complex migration not supported.');
}
$db->execute($sql[$engine]);
$perm = new Pluf_Permission();
$perm->name = 'Project authorized users';
$perm->code_name = 'project-authorized-user';
$perm->description = 'Permission given to users allowed to access a project.';
$perm->application = 'IDF';
$perm->create();
}
function IDF_Migrations_6PrivateProject_down($params=null)
{
$perm = Pluf_Permission::getFromString('IDF.project-authorized-user');
if ($perm) $perm->delete();
$table = Pluf::factory('IDF_Project')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' DROP COLUMN "private"';
$sql['MySQL'] = 'ALTER TABLE '.$table.' DROP COLUMN `private`';
$db = Pluf::db();
$engine = Pluf::f('db_engine');
if (!isset($sql[$engine])) {
throw new Exception('SQLite complex migration not supported.');
}
$db->execute($sql[$engine]);
}

View File

@ -1,54 +0,0 @@
<?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 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 download of files.
*/
function IDF_Migrations_7Wiki_up($params=null)
{
$models = array(
'IDF_WikiPage',
'IDF_WikiRevision',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_7Wiki_down($params=null)
{
$models = array(
'IDF_WikiRevision',
'IDF_WikiPage',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -1,56 +0,0 @@
<?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 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 code review.
*/
function IDF_Migrations_8CodeReview_up($params=null)
{
$models = array(
'IDF_Review',
'IDF_Review_Patch',
'IDF_Review_FileComment',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_8CodeReview_down($params=null)
{
$models = array(
'IDF_Review_FileComment',
'IDF_Review_Patch',
'IDF_Review',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -1,55 +0,0 @@
<?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 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 private column for the project.
*/
function IDF_Migrations_9ShortDescription_up($params=null)
{
$table = Pluf::factory('IDF_Project')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' ADD COLUMN "shortdesc" VARCHAR(255) DEFAULT \'\'';
$sql['MySQL'] = 'ALTER TABLE '.$table.' ADD COLUMN `shortdesc` VARCHAR(255) 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]);
}
function IDF_Migrations_9ShortDescription_down($params=null)
{
$table = Pluf::factory('IDF_Project')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' DROP COLUMN "shortdesc"';
$sql['MySQL'] = 'ALTER TABLE '.$table.' DROP COLUMN `shortdesc`';
$db = Pluf::db();
$engine = Pluf::f('db_engine');
if (!isset($sql[$engine])) {
throw new Exception('SQLite complex migration not supported.');
}
$db->execute($sql[$engine]);
}

View File

@ -1,113 +0,0 @@
<?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 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 ***** */
/**
* Backup of InDefero.
*
* !! You need also to backup Pluf if you want the full backup. !!
*
* @param string Path to the folder where to store the backup
* @param string Name of the backup (null)
* @return int The backup was correctly written
*/
function IDF_Migrations_Backup_run($folder, $name=null)
{
$models = array(
'IDF_Project',
'IDF_Tag',
'IDF_Issue',
'IDF_IssueComment',
'IDF_Conf',
'IDF_Upload',
'IDF_Search_Occ',
'IDF_IssueFile',
'IDF_Commit',
'IDF_Timeline',
'IDF_WikiPage',
'IDF_WikiRevision',
'IDF_Review',
'IDF_Review_Patch',
'IDF_Review_Comment',
'IDF_Review_FileComment',
'IDF_Key',
'IDF_Scm_Cache_Git',
'IDF_Queue',
'IDF_Gconf',
);
$db = Pluf::db();
// Now, for each table, we dump the content in json, this is a
// memory intensive operation
$to_json = array();
foreach ($models as $model) {
$to_json[$model] = Pluf_Test_Fixture::dump($model, false);
}
if (null == $name) {
$name = date('Y-m-d');
}
return file_put_contents(sprintf('%s/%s-IDF.json', $folder, $name),
json_encode($to_json), LOCK_EX);
}
/**
* Restore IDF from a backup.
*
* @param string Path to the backup folder
* @param string Backup name
* @return bool Success
*/
function IDF_Migrations_Backup_restore($folder, $name)
{
$models = array(
'IDF_Project',
'IDF_Tag',
'IDF_Issue',
'IDF_IssueComment',
'IDF_Conf',
'IDF_Upload',
'IDF_Search_Occ',
'IDF_IssueFile',
'IDF_Commit',
'IDF_Timeline',
'IDF_WikiPage',
'IDF_WikiRevision',
'IDF_Review',
'IDF_Review_Patch',
'IDF_Review_Comment',
'IDF_Review_FileComment',
'IDF_Key',
'IDF_Scm_Cache_Git',
'IDF_Queue',
'IDF_Gconf',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
$full_data = json_decode(file_get_contents(sprintf('%s/%s-IDF.json', $folder, $name)), true);
foreach ($full_data as $model => $data) {
Pluf_Test_Fixture::load($data, false);
}
return true;
}

View File

@ -35,21 +35,6 @@ function IDF_Migrations_Install_setup($params=null)
'IDF_Issue', 'IDF_Issue',
'IDF_IssueComment', 'IDF_IssueComment',
'IDF_Conf', 'IDF_Conf',
'IDF_Upload',
'IDF_Search_Occ',
'IDF_IssueFile',
'IDF_Commit',
'IDF_Timeline',
'IDF_WikiPage',
'IDF_WikiRevision',
'IDF_Review',
'IDF_Review_Patch',
'IDF_Review_Comment',
'IDF_Review_FileComment',
'IDF_Key',
'IDF_Scm_Cache_Git',
'IDF_Queue',
'IDF_Gconf',
); );
$db = Pluf::db(); $db = Pluf::db();
$schema = new Pluf_DB_Schema($db); $schema = new Pluf_DB_Schema($db);
@ -70,12 +55,6 @@ function IDF_Migrations_Install_setup($params=null)
$perm->description = 'Permission given to project owners.'; $perm->description = 'Permission given to project owners.';
$perm->application = 'IDF'; $perm->application = 'IDF';
$perm->create(); $perm->create();
$perm = new Pluf_Permission();
$perm->name = 'Project authorized users';
$perm->code_name = 'project-authorized-user';
$perm->description = 'Permission given to users allowed to access a project.';
$perm->application = 'IDF';
$perm->create();
} }
function IDF_Migrations_Install_teardown($params=null) function IDF_Migrations_Install_teardown($params=null)
@ -84,28 +63,11 @@ function IDF_Migrations_Install_teardown($params=null)
if ($perm) $perm->delete(); if ($perm) $perm->delete();
$perm = Pluf_Permission::getFromString('IDF.project-owner'); $perm = Pluf_Permission::getFromString('IDF.project-owner');
if ($perm) $perm->delete(); if ($perm) $perm->delete();
$perm = Pluf_Permission::getFromString('IDF.project-authorized-user');
if ($perm) $perm->delete();
$models = array( $models = array(
'IDF_Gconf',
'IDF_Queue',
'IDF_Scm_Cache_Git',
'IDF_Key',
'IDF_Review_FileComment',
'IDF_Review_Comment',
'IDF_Review_Patch',
'IDF_Review',
'IDF_WikiRevision',
'IDF_WikiPage',
'IDF_Timeline',
'IDF_IssueFile',
'IDF_Search_Occ',
'IDF_Upload',
'IDF_Conf', 'IDF_Conf',
'IDF_IssueComment', 'IDF_IssueComment',
'IDF_Issue', 'IDF_Issue',
'IDF_Tag', 'IDF_Tag',
'IDF_Commit',
'IDF_Project', 'IDF_Project',
); );
$db = Pluf::db(); $db = Pluf::db();

View File

@ -1,81 +0,0 @@
<?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 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 ***** */
/**
* This class is a plugin which allows to synchronise access riths
* between InDefero and a common restricted SSH account for git
* access.
*
* As the authentication is directly performed by accessing the
* InDefero database, we only need to synchronize the SSH keys. This
* synchronization process can only be performed by a process running
* under the git user as we need to write in
* /home/git/.ssh/authorized_keys
*
* So, here, we are just creating a file informing that a sync needs
* to be done. We connect this plugin to the IDF_Key::postSave signal.
*/
class IDF_Plugin_SyncGit
{
/**
* Entry point of the plugin.
*/
static public function entry($signal, &$params)
{
// First check for the single mandatory config variable.
if (!Pluf::f('idf_plugin_syncgit_sync_file', false)) {
Pluf_Log::debug('IDF_Plugin_SyncGit plugin not configured.');
return;
}
if ($signal != 'gitpostupdate.php::run') {
Pluf_Log::event('IDF_Plugin_SyncGit', 'create',
Pluf::f('idf_plugin_syncgit_sync_file'));
@touch(Pluf::f('idf_plugin_syncgit_sync_file'));
@chmod(Pluf::f('idf_plugin_syncgit_sync_file'), 0777);
} else {
self::postUpdate($signal, $params);
}
}
/**
* Entry point for the post-update signal.
*
* It tries to find the name of the project, when found it runs an
* update of the timeline.
*/
static public function postUpdate($signal, &$params)
{
// Chop the ".git" and get what is left
$pname = basename($params['git_dir'], '.git');
try {
$project = IDF_Project::getOr404($pname);
} catch (Pluf_HTTP_Error404 $e) {
Pluf_Log::event(array('IDF_Plugin_SyncGit::postUpdate', 'Project not found.', array($pname, $params)));
return false; // Project not found
}
// Now we have the project and can update the timeline
Pluf_Log::debug(array('IDF_Plugin_SyncGit::postUpdate', 'Project found', $pname, $project->id));
IDF_Scm::syncTimeline($project, true);
Pluf_Log::event(array('IDF_Plugin_SyncGit::postUpdate', 'sync', array($pname, $project->id)));
}
}

View File

@ -1,131 +0,0 @@
<?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 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 ***** */
/**
* Synchronize the SSH keys with InDefero.
*/
class IDF_Plugin_SyncGit_Cron
{
/**
* Template for the SSH key.
*/
public $template = 'command="python %s %s",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s';
/**
* Synchronize.
*/
public static function sync()
{
$template = Pluf::factory(__CLASS__)->template;
$cmd = Pluf::f('idf_plugin_syncgit_path_gitserve', '/dev/null');
$authorized_keys = Pluf::f('idf_plugin_syncgit_path_authorized_keys', false);
if (false == $authorized_keys) {
throw new Pluf_Exception_SettingError('Setting idf_plugin_syncgit_path_authorized_keys not set.');
}
if (!is_writable($authorized_keys)) {
throw new Exception('Cannot create file: '.$authorized_keys);
}
$out = '';
$keys = Pluf::factory('IDF_Key')->getList(array('view'=>'join_user'));
foreach ($keys as $key) {
try {
$key_type = $key->getType();
} catch (Exception $e) {
// The key is a bad key, skip it
continue;
}
if ($key_type == 'ssh' and preg_match('/^[a-zA-Z][a-zA-Z0-9_.-]*(@[a-zA-Z][a-zA-Z0-9.-]*)?$/', $key->login)) {
$content = trim(str_replace(array("\n", "\r"), '', $key->content));
$out .= sprintf($template, $cmd, $key->login, $content)."\n";
}
}
file_put_contents($authorized_keys, $out, LOCK_EX);
}
/**
* Mark export of git repositories for the daemon.
*/
public static function markExport()
{
foreach (Pluf::factory('IDF_Project')->getList() as $project) {
$rep = sprintf(Pluf::f('git_repositories'), $project->shortname);
$serve = new IDF_Plugin_SyncGit_Serve();
$serve->setGitExport($project->shortname, $rep);
}
}
/**
* Remove orphan repositories.
*/
public static function removeOrphanRepositories()
{
$path = Pluf::f('idf_plugin_syncgit_base_repositories', '/home/git/repositories');
if (!is_dir($path) || is_link($path)) {
throw new Pluf_Exception_SettingError(sprintf(
'Directory %s does not exist! Setting "idf_plugin_syncgit_base_repositories not set.',
$path));
}
if (!is_writable($path)) {
throw new Exception(sprintf('Repository %s is not writable.', $path));
}
$projects = array();
foreach (Pluf::factory('IDF_Project')->getList() as $project) {
$projects[] = $project->shortname;
}
unset($project);
$it = new DirectoryIterator($path);
$orphans = array();
while ($it->valid()) {
if (!$it->isDot() && $it->isDir() && !in_array(basename($it->getFileName(), '.git'), $projects)) {
$orphans[] = $it->getPathName();
}
$it->next();
}
if (count($orphans)) {
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'rm -rf '.implode(' ', $orphans);
exec($cmd);
clearstatcache();
while (list(, $project) = each($orphans)) {
if (is_dir($project)) {
throw new Exception(sprintf('Cannot remove %s directory.', $project));
}
}
}
}
/**
* Check if a sync is needed.
*
*/
public static function main()
{
if (file_exists(Pluf::f('idf_plugin_syncgit_sync_file'))) {
@unlink(Pluf::f('idf_plugin_syncgit_sync_file'));
self::sync();
self::markExport();
if (Pluf::f('idf_plugin_syncgit_remove_orphans', false)) {
self::removeOrphanRepositories();
}
}
}
}

View File

@ -1,316 +0,0 @@
<?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 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 ***** */
/**
* Main application to serve git repositories through a restricted SSH
* access.
*/
class IDF_Plugin_SyncGit_Serve
{
/**
* Regular expression to match the path in the git command.
*/
public $preg = '#^\'/*(?P<path>[a-zA-Z0-9][a-zA-Z0-9@._-]*(/[a-zA-Z0-9][a-zA-Z0-9@._-]*)*)\'$#';
public $commands_readonly = array('git-upload-pack', 'git upload-pack');
public $commands_write = array('git-receive-pack', 'git receive-pack');
/**
* Serve a git request.
*
* @param string Username.
* @param string Command to be run.
*/
public function serve($username, $cmd)
{
if (false !== strpos($cmd, "\n")) {
throw new Exception('Command may not contain newline.');
}
$splitted = preg_split('/\s/', $cmd, 2);
if (count($splitted) != 2) {
throw new Exception('Unknown command denied.');
}
if ($splitted[0] == 'git') {
$sub_splitted = preg_split('/\s/', $splitted[1], 2);
if (count($sub_splitted) != 2) {
throw new Exception('Unknown command denied.');
}
$verb = sprintf('%s %s', $splitted[0], $sub_splitted[0]);
$args = $sub_splitted[1];
} else {
$verb = $splitted[0];
$args = $splitted[1];
}
if (!in_array($verb, $this->commands_write)
and !in_array($verb, $this->commands_readonly)) {
throw new Exception('Unknown command denied.');
}
if (!preg_match($this->preg, $args, $matches)) {
throw new Exception('Arguments to command look dangerous.');
}
$path = $matches['path'];
// Check read/write rights
$new_path = $this->haveAccess($username, $path, 'writable');
if ($new_path == false) {
$new_path = $this->haveAccess($username, $path, 'readonly');
if ($new_path == false) {
throw new Exception('Repository read access denied.');
}
if (in_array($verb, $this->commands_write)) {
throw new Exception('Repository write access denied.');
}
}
list($topdir, $relpath) = $new_path;
$repopath = sprintf('%s.git', $relpath);
$fullpath = $topdir.DIRECTORY_SEPARATOR.$repopath;
if (!file_exists($fullpath)
and in_array($verb, $this->commands_write)) {
// it doesn't exist on the filesystem, but the
// configuration refers to it, we're serving a write
// request, and the user is authorized to do that: create
// the repository on the fly
$p = explode(DIRECTORY_SEPARATOR, $fullpath);
$mpath = implode(DIRECTORY_SEPARATOR, array_slice($p, 0, -1));
if (!file_exists($mpath)) {
mkdir($mpath, 0750, true);
}
$this->initRepository($fullpath);
$this->setGitExport($relpath, $fullpath);
}
$new_cmd = sprintf("%s '%s'", $verb, $fullpath);
Pluf_Log::info(array('IDF_Plugin_Git_Serve::serve', $username, $cmd, $new_cmd));
return $new_cmd;
}
/**
* Main function called by the serve script.
*/
public static function main($argv, $env)
{
if (count($argv) != 2) {
self::fatalError('Missing argument USER.');
}
$username = $argv[1];
umask(0022);
if (!isset($env['SSH_ORIGINAL_COMMAND'])) {
self::fatalError('Need SSH_ORIGINAL_COMMAND in environment.');
}
$cmd = $env['SSH_ORIGINAL_COMMAND'];
chdir(Pluf::f('idf_plugin_syncgit_git_home_dir', '/home/git'));
$serve = new IDF_Plugin_SyncGit_Serve();
try {
$new_cmd = $serve->serve($username, $cmd);
} catch (Exception $e) {
self::fatalError($e->getMessage());
}
print $new_cmd;
exit(0);
}
/**
* Control the access rights to the repository.
*
* @param string Username
* @param string Path including the possible .git
* @param string Type of access. 'readonly' or ('writable')
* @return mixed False or array(base_git_reps, relative path to repo)
*/
public function haveAccess($username, $path, $mode='writable')
{
if ('.git' == substr($path, -4)) {
$path = substr($path, 0, -4);
}
$sql = new Pluf_SQL('shortname=%s', array($path));
$projects = Pluf::factory('IDF_Project')->getList(array('filter'=>$sql->gen()));
if ($projects->count() != 1) {
return false;
}
$project = $projects[0];
$conf = new IDF_Conf();
$conf->setProject($project);
$scm = $conf->getVal('scm', 'git');
if ($scm != 'git') {
return false;
}
$sql = new Pluf_SQL('login=%s', array($username));
$users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
if ($users->count() != 1 or !$users[0]->active) {
return false;
}
$user = $users[0];
$request = new StdClass();
$request->user = $user;
$request->conf = $conf;
$request->project = $project;
if (true === IDF_Precondition::accessSource($request)) {
if ($mode == 'readonly') {
return array(Pluf::f('idf_plugin_syncgit_base_repositories', '/home/git/repositories'),
$project->shortname);
}
if (true === IDF_Precondition::projectMemberOrOwner($request)) {
return array(Pluf::f('idf_plugin_syncgit_base_repositories', '/home/git/repositories'),
$project->shortname);
}
}
return false;
}
/**
* Die on a message on stderr.
*
* @param string Message
*/
public static function fatalError($mess)
{
fwrite(STDERR, $mess."\n");
exit(1);
}
/**
* Init a new empty bare repository.
*
* @param string Full path to the repository
*/
public function initRepository($fullpath)
{
if (!file_exists($fullpath)) {
mkdir($fullpath, 0750, true);
}
$out = array();
$res = 0;
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').
Pluf::f('git_path', 'git').' --git-dir=%s init', escapeshellarg($fullpath)),
$out, $res);
if ($res != 0) {
Pluf_Log::error(array('IDF_Plugin_Git_Serve::initRepository', $res, $fullpath));
throw new Exception(sprintf('Init repository error, exit status %d.', $res));
}
Pluf_Log::event(array('IDF_Plugin_Git_Serve::initRepository', 'success', $fullpath));
// Add the post-update hook by removing the original one and add the
// Indefero's one.
$p = realpath(dirname(__FILE__).'/../../../../scripts/git-post-update');
$p = Pluf::f('idf_plugin_syncgit_post_update', $p);
if (!@unlink($fullpath.'/hooks/post-update')) {
Pluf_Log::warn(array('IDF_Plugin_Git_Serve::initRepository',
'post-update hook removal error.',
$fullpath.'/hooks/post-update'));
return;
}
$out = array();
$res = 0;
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').'ln -s %s %s',
escapeshellarg($p),
escapeshellarg($fullpath.'/hooks/post-update')),
$out, $res);
if ($res != 0) {
Pluf_Log::warn(array('IDF_Plugin_Git_Serve::initRepository',
'post-update hook creation error.',
$fullpath.'/hooks/post-update'));
return;
}
Pluf_Log::debug(array('IDF_Plugin_Git_Serve::initRepository',
'Added post-update hook.', $fullpath));
// Configure the core.quotepath option
$quotepath = (Pluf::f('git_core_quotepath', true) == true) ? 'true' : 'false';
$out = array();
$res = 0;
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').
Pluf::f('git_path', 'git').' config -f %s/config --add core.quotepath %s',
escapeshellarg($fullpath),
escapeshellarg($quotepath)
),
$out, $res);
if ($res != 0) {
Pluf_Log::warn(array('IDF_Plugin_Git_Serve::initRepository',
'core.quotepath configuration error.',
$quotepath));
return;
}
Pluf_Log::debug(array('IDF_Plugin_Git_Serve::initRepository',
'core.quotepath configured.', $quotepath));
}
/**
* Set the git export value.
*
* @param string Relative path of the repository (not .git)
* @param string Full path of the repository with .git
*/
public function setGitExport($relpath, $fullpath)
{
$sql = new Pluf_SQL('shortname=%s', array($relpath));
$projects = Pluf::factory('IDF_Project')->getList(array('filter'=>$sql->gen()));
if ($projects->count() != 1 and file_exists($fullpath)) {
return $this->gitExportDeny($fullpath);
}
$project = $projects[0];
$conf = new IDF_Conf();
$conf->setProject($project);
$scm = $conf->getVal('scm', 'git');
if ($scm == 'git' and !file_exists($fullpath)) {
// No repository yet, just skip
return false;
}
if ($scm != 'git' or $project->private) {
return $this->gitExportDeny($fullpath);
}
if ('all' == $conf->getVal('source_access_rights', 'all')) {
return $this->gitExportAllow($fullpath);
}
return $this->gitExportDeny($fullpath);
}
/**
* Remove the export flag.
*
* @param string Full path to the repository
*/
public function gitExportDeny($fullpath)
{
if (!file_exists($fullpath)) {
return; // Not created yet.
}
@unlink($fullpath.DIRECTORY_SEPARATOR.'git-daemon-export-ok');
if (file_exists($fullpath.DIRECTORY_SEPARATOR.'git-daemon-export-ok')) {
throw new Exception('Cannot remove git-daemon-export-ok file.');
}
return true;
}
/**
* Set the export flag.
*
* @param string Full path to the repository
*/
public function gitExportAllow($fullpath)
{
if (!file_exists($fullpath)) {
return; // Not created yet.
}
touch($fullpath.DIRECTORY_SEPARATOR.'git-daemon-export-ok');
if (!file_exists($fullpath.DIRECTORY_SEPARATOR.'git-daemon-export-ok')) {
throw new Exception('Cannot create git-daemon-export-ok file.');
}
return true;
}
}

View File

@ -1,249 +0,0 @@
<?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 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 ***** */
/**
* This classes is a plugin which allows to synchronise access rights
* between indefero and mercurial web-published repositories.
*/
class IDF_Plugin_SyncMercurial
{
/**
* Entry point of the plugin.
*/
static public function entry($signal, &$params)
{
// First check for the 3 mandatory config variables.
if (!Pluf::f('idf_plugin_syncmercurial_passwd_file', false) or
!Pluf::f('idf_plugin_syncmercurial_path', false) or
!Pluf::f('idf_plugin_syncmercurial_hgrc', false)) {
return;
}
include_once 'File/Passwd/Authdigest.php';
$plug = new IDF_Plugin_SyncMercurial();
switch ($signal) {
case 'IDF_Project::created':
$plug->processMercurialCreate($params['project']);
break;
case 'IDF_Project::membershipsUpdated':
$plug->processSyncAuthz($params['project']);
break;
case 'Pluf_User::passwordUpdated':
$plug->processSyncPasswd($params['user']);
break;
case 'hgchangegroup.php::run':
$plug->processSyncTimeline($params);
break;
}
}
/**
* Run hg init command to create the corresponding Mercurial
* repository.
*
* @param IDF_Project
* @return bool Success
*/
function processMercurialCreate($project)
{
if ($project->getConf()->getVal('scm') != 'mercurial') {
return false;
}
$shortname = $project->shortname;
if (false===($mercurial_path=Pluf::f('idf_plugin_syncmercurial_path',false))) {
throw new Pluf_Exception_SettingError("'idf_plugin_syncmercurial_path' must be defined in your configuration file.");
}
if (file_exists($mercurial_path.'/'.$shortname)) {
throw new Exception(sprintf(__('The repository %s already exists.'),
$mercurial_path.'/'.$shortname));
}
$return = 0;
$output = array();
$cmd = sprintf(Pluf::f('hg_path', 'hg').' init %s',
escapeshellarg($mercurial_path.'/'.$shortname));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$ll = exec($cmd, $output, $return);
return ($return == 0);
}
/**
* Synchronise an user's password.
*
* @param Pluf_User
*/
function processSyncPasswd($user)
{
$passwd_file = Pluf::f('idf_plugin_syncmercurial_passwd_file');
if (!file_exists($passwd_file) or !is_writable($passwd_file)) {
return false;
}
$ht = new File_Passwd_Authbasic($passwd_file);
$ht->load();
$ht->setMode(Pluf::f('idf_plugin_syncmercurial_passwd_mode',
FILE_PASSWD_SHA));
if ($ht->userExists($user->login)) {
$ht->changePasswd($user->login, $this->getMercurialPass($user));
} else {
$ht->addUser($user->login, $this->getMercurialPass($user));
}
$ht->save();
return true;
}
/**
* Synchronize the hgrc file and the passwd file for the project.
*
* @param IDF_Project
*/
function processSyncAuthz($project)
{
if ($project->getConf()->getVal('scm') != 'mercurial') {
return false;
}
$this->SyncAccess($project);
$this->generateProjectPasswd($project);
}
/**
* Get the repository password for the user
*/
function getMercurialPass($user){
return substr(sha1($user->password.Pluf::f('secret_key')), 0, 8);
}
/**
* For a particular project: update all passwd information
*/
function generateProjectPasswd($project)
{
$passwd_file = Pluf::f('idf_plugin_syncmercurial_passwd_file');
if (!file_exists($passwd_file) or !is_writable($passwd_file)) {
throw new Exception (sprintf(__('%s does not exist or is not writable.'), $passwd_file));
}
$ht = new File_Passwd_Authbasic($passwd_file);
$ht->setMode(Pluf::f('idf_plugin_syncmercurial_passwd_mode',
FILE_PASSWD_SHA));
$ht->load();
$mem = $project->getMembershipData();
$members = array_merge((array)$mem['members'], (array)$mem['owners'],
(array)$mem['authorized']);
foreach($members as $user) {
if ($ht->userExists($user->login)) {
$ht->changePasswd($user->login, $this->getMercurialPass($user));
} else {
$ht->addUser($user->login, $this->getMercurialPass($user));
}
}
$ht->save();
}
/**
* Generate the hgrc file
*/
function SyncAccess($project)
{
$shortname = $project->shortname;
$hgrc_file = Pluf::f('idf_plugin_syncmercurial_path').sprintf('/%s/.hg/hgrc', $shortname);
// Get allow_push list
$allow_push = '';
$mem = $project->getMembershipData();
foreach ($mem['owners'] as $v) {
$allow_push .= $v->login.' ';
}
foreach ($mem['members'] as $v) {
$allow_push .= $v->login.' ';
}
// Generate hgrc content
if (is_file($hgrc_file)) {
$tmp_content = parse_ini_file($hgrc_file, true);
$tmp_content['web']['allow_push'] = $allow_push;
}
else {
$tmp_content = Pluf::f('idf_plugin_syncmercurial_hgrc');
$tmp_content['web']['allow_push'] = $allow_push;
}
$fcontent = '';
foreach ($tmp_content as $key => $elem){
$fcontent .= '['.$key."]\n";
foreach ($elem as $key2 => $elem2){
$fcontent .= $key2.' = '.$elem2."\n";
}
}
file_put_contents($hgrc_file, $fcontent, LOCK_EX);
// Generate private repository config file
$private_file = Pluf::f('idf_plugin_syncmercurial_private_include');
$notify_file = Pluf::f('idf_plugin_syncmercurial_private_notify');
$fcontent = '';
foreach (Pluf::factory('IDF_Project')->getList() as $project) {
$conf = new IDF_Conf();
$conf->setProject($project);
if ($project->private == true){
$mem = $project->getMembershipData();
$user = '';
foreach ($mem['owners'] as $v) {
$user .= $v->login.' ';
}
foreach ($mem['members'] as $v) {
$user .= $v->login.' ';
}
foreach ($mem['authorized'] as $v) {
$user .= $v->login.' ';
}
$fcontent .= '<Location '. sprintf(Pluf::f('idf_plugin_syncmercurial_private_url'), $project->shortname).'>'."\n";
$fcontent .= 'AuthType Basic'."\n";
$fcontent .= 'AuthName "Restricted"'."\n";
$fcontent .= sprintf('AuthUserFile %s', Pluf::f('idf_plugin_syncmercurial_passwd_file'))."\n";
$fcontent .= sprintf('Require user %s', $user)."\n";
$fcontent .= '</Location>'."\n\n";
}
}
file_put_contents($private_file, $fcontent, LOCK_EX);
file_put_contents($notify_file, ' ', LOCK_EX);
return true;
}
/**
* Update the timeline in post commit.
*
*/
public function processSyncTimeline($params)
{
$pname = basename($params['rel_dir']);
try {
$project = IDF_Project::getOr404($pname);
} catch (Pluf_HTTP_Error404 $e) {
Pluf_Log::event(array('IDF_Plugin_SyncMercurial::processSyncTimeline', 'Project not found.', array($pname, $params)));
return false; // Project not found
}
// Now we have the project and can update the timeline
Pluf_Log::debug(array('IDF_Plugin_SyncMercurial::processSyncTimeline', 'Project found', $pname, $project->id));
IDF_Scm::syncTimeline($project, true);
Pluf_Log::event(array('IDF_Plugin_SyncMercurial::processSyncTimeline', 'sync', array($pname, $project->id)));
}
}

View File

@ -1,830 +0,0 @@
<?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) 2010 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 ***** */
/**
* This classes is a plugin which allows to synchronise access rights
* between indefero and monotone usher setups.
*/
class IDF_Plugin_SyncMonotone
{
/**
* Entry point of the plugin.
*/
static public function entry($signal, &$params)
{
$plug = new IDF_Plugin_SyncMonotone();
switch ($signal) {
case 'IDF_Project::created':
$plug->processProjectCreate($params['project']);
break;
case 'IDF_Project::membershipsUpdated':
$plug->processMembershipsUpdated($params['project']);
break;
case 'IDF_Project::preDelete':
$plug->processProjectDelete($params['project']);
break;
case 'IDF_Key::postSave':
$plug->processKeyCreate($params['key']);
break;
case 'IDF_Key::preDelete':
$plug->processKeyDelete($params['key']);
break;
case 'mtnpostpush.php::run':
$plug->processSyncTimeline($params['project']);
break;
}
}
/**
* Initial steps to setup a new monotone project:
*
* 1) run mtn db init to initialize a new database underknees
* 'mtn_repositories'
* 2) create a new server key in the same directory
* 3) create a new client key for IDF and store it in the project conf
* 4) setup the configuration
* 5) add the database as new local server in the usher configuration
* 6) reload the running usher instance so it acknowledges the new server
*
* The initial right setup happens in processMembershipsUpdated()
*
* @param IDF_Project
*/
function processProjectCreate($project)
{
if ($project->getConf()->getVal('scm') != 'mtn') {
return;
}
if (Pluf::f('mtn_db_access', 'local') == 'local') {
return;
}
$projecttempl = Pluf::f('mtn_repositories', false);
if ($projecttempl === false) {
throw new IDF_Scm_Exception(
__('"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.')
);
}
$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
));
}
// check some static configuration files
$confdir = Pluf::f('mtn_confdir', false);
if ($confdir === false) {
$confdir = dirname(__FILE__).'/SyncMonotone/';
}
$confdir_contents = array(
'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',
);
if (!$project->private) {
// this is linked and not copied to be able to update
// the list of read-only commands on upgrades
$confdir_contents[] = 'hooks.d/indefero_authorize_remote_automate.conf';
}
// check whether we should handle additional files in the config directory
$confdir_extra_contents = Pluf::f('mtn_confdir_extra', false);
if ($confdir_extra_contents !== false) {
$confdir_contents =
array_merge($confdir_contents, $confdir_extra_contents);
}
foreach ($confdir_contents as $content) {
if (!file_exists($confdir.$content)) {
throw new IDF_Scm_Exception(sprintf(
__('The configuration file %s is missing.'), $content
));
}
}
$shortname = $project->shortname;
$projectpath = sprintf($projecttempl, $shortname);
if (file_exists($projectpath)) {
throw new IDF_Scm_Exception(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
));
}
//
// step 1) create a new database
//
$dbfile = $projectpath.'/database.mtn';
$cmd = sprintf('db init -d %s', escapeshellarg($dbfile));
self::_mtn_exec($cmd);
//
// step 2) create a server key
//
// try to parse the key's domain part from the remote_url's host
// name, otherwise fall back to the configured Apache server name
$server = $_SERVER['SERVER_NAME'];
$remote_url = Pluf::f('mtn_remote_url');
if (($parsed = parse_url($remote_url)) !== false &&
!empty($parsed['host'])) {
$server = $parsed['host'];
}
$serverkey = $shortname.'-server@'.$server;
$cmd = sprintf('au generate_key --confdir=%s %s ""',
escapeshellarg($projectpath),
escapeshellarg($serverkey)
);
self::_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
));
}
}
$clientkey_name = $shortname.'-client@'.$server;
$cmd = sprintf('au generate_key --keydir=%s %s ""',
escapeshellarg($keydir),
escapeshellarg($clientkey_name)
);
$keyinfo = self::_mtn_exec($cmd);
$parsed_keyinfo = array();
try {
$parsed_keyinfo = IDF_Scm_Monotone_BasicIO::parse($keyinfo);
}
catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf(
__('Could not parse key information: %s'), $e->getMessage()
));
}
$clientkey_hash = $parsed_keyinfo[0][1]['hash'];
$clientkey_file = $keydir . '/' . $clientkey_name . '.' . $clientkey_hash;
$clientkey_data = file_get_contents($clientkey_file);
$project->getConf()->setVal('mtn_client_key_name', $clientkey_name);
$project->getConf()->setVal('mtn_client_key_hash', $clientkey_hash);
$project->getConf()->setVal('mtn_client_key_data', $clientkey_data);
// add the public client key to the server
$cmd = sprintf('au get_public_key --keydir=%s %s',
escapeshellarg($keydir),
escapeshellarg($clientkey_hash)
);
$clientkey_pubdata = self::_mtn_exec($cmd);
$cmd = sprintf('au put_public_key --db=%s %s',
escapeshellarg($dbfile),
escapeshellarg($clientkey_pubdata)
);
self::_mtn_exec($cmd);
//
// step 4) setup the configuration
//
// we assume that all confdir entries ending with a slash mean a
// directory that has to be created, that all files ending on ".in"
// have to be processed and copied in place and that all other files
// just need to be symlinked from the original location
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
));
}
continue;
}
if (substr($content, -3) != '.in') {
if (!symlink($confdir.$content, $filepath)) {
IDF_Scm_Exception(sprintf(
__('Could not create symlink "%s"'), $filepath
));
}
continue;
}
$filecontents = file_get_contents($confdir.'/'.$content);
$filecontents = str_replace(
array('%%MTNPOSTPUSH%%', '%%PROJECT%%', '%%MTNCLIENTKEY%%'),
array($mtnpostpush, $shortname, $clientkey_hash),
$filecontents
);
// 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
));
}
}
//
// step 5) read in and append the usher config with the new server
//
$usher_rc = file_get_contents($usher_config);
$parsed_config = array();
try {
$parsed_config = IDF_Scm_Monotone_BasicIO::parse($usher_rc);
}
catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf(
__('Could not parse usher configuration in "%s": %s'),
$usher_config, $e->getMessage()
));
}
// ensure we haven't configured a server with this name already
foreach ($parsed_config as $stanzas) {
foreach ($stanzas as $stanza_line) {
if ($stanza_line['key'] == 'server' &&
$stanza_line['values'][0] == $shortname) {
throw new IDF_Scm_Exception(sprintf(
__('usher configuration already contains a server '.
'entry named "%s"'),
$shortname
));
}
}
}
$new_server = array(
array('key' => 'server', 'values' => array($shortname)),
array('key' => 'local', 'values' => array(
'--confdir', $projectpath,
'-d', $dbfile,
'--timestamps',
'--ticker=dot'
)),
);
$parsed_config[] = $new_server;
$usher_rc = IDF_Scm_Monotone_BasicIO::compile($parsed_config);
// 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
));
}
//
// step 6) reload usher to pick up the new configuration
//
IDF_Scm_Monotone_Usher::reload();
}
/**
* Updates the read / write permissions for the monotone database
*
* @param IDF_Project
*/
public function processMembershipsUpdated($project)
{
if ($project->getConf()->getVal('scm') != 'mtn') {
return;
}
if (Pluf::f('mtn_db_access', 'local') == 'local') {
return;
}
$mtn = IDF_Scm_Monotone::factory($project);
$stdio = $mtn->getStdio();
$projectpath = self::_get_project_path($project);
$auth_ids = self::_get_authorized_user_ids($project);
$key_ids = array();
foreach ($auth_ids as $auth_id) {
$sql = new Pluf_SQL('user=%s', array($auth_id));
$keys = Pluf::factory('IDF_Key')->getList(array('filter' => $sql->gen()));
foreach ($keys as $key) {
if ($key->getType() != 'mtn')
continue;
$stdio->exec(array('put_public_key', $key->content));
$key_ids[] = $key->getMtnId();
}
}
$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 ($project->private) {
$stanza = array(
array('key' => 'pattern', 'values' => array('*')),
);
foreach ($key_ids as $key_id)
{
$stanza[] = array('key' => 'allow', 'values' => array($key_id));
}
}
else {
$stanza = array(
array('key' => 'pattern', 'values' => array('*')),
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
));
}
// link / unlink the read-only automate permissions for the project
$confdir = Pluf::f('mtn_confdir', false);
if ($confdir === false) {
$confdir = dirname(__FILE__).'/SyncMonotone/';
}
$file = 'hooks.d/indefero_authorize_remote_automate.conf';
$projectfile = $projectpath.'/'.$file;
$templatefile = $confdir.'/'.$file;
$serverRestartRequired = false;
if ($project->private && file_exists($projectfile) && is_link($projectfile)) {
if (!unlink($projectfile)) {
IDF_Scm_Exception(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(
__('Could not create symlink "%s"'), $projectfile
));
}
$serverRestartRequired = true;
}
if ($serverRestartRequired) {
// FIXME: we should actually use stopServer() here, but this
// seems to be ignored when the server should be started
// again immediately afterwards
IDF_Scm_Monotone_Usher::killServer($project->shortname);
IDF_Scm_Monotone_Usher::startServer($project->shortname);
}
}
/**
* Clean up after a mtn project was deleted
*
* @param IDF_Project
*/
public function processProjectDelete($project)
{
if ($project->getConf()->getVal('scm') != 'mtn') {
return;
}
if (Pluf::f('mtn_db_access', 'local') == 'local') {
return;
}
$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.')
);
}
$shortname = $project->shortname;
IDF_Scm_Monotone_Usher::killServer($shortname);
$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, $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
));
}
}
$keydir = Pluf::f('tmp_folder').'/mtn-client-keys';
$keyname = $project->getConf()->getVal('mtn_client_key_name', false);
$keyhash = $project->getConf()->getVal('mtn_client_key_hash', false);
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
));
}
}
$usher_rc = file_get_contents($usher_config);
$parsed_config = array();
try {
$parsed_config = IDF_Scm_Monotone_BasicIO::parse($usher_rc);
}
catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf(
__('Could not parse usher configuration in "%s": %s'),
$usher_config, $e->getMessage()
));
}
foreach ($parsed_config as $idx => $stanzas) {
foreach ($stanzas as $stanza_line) {
if ($stanza_line['key'] == 'server' &&
$stanza_line['values'][0] == $shortname) {
unset($parsed_config[$idx]);
break;
}
}
}
$usher_rc = IDF_Scm_Monotone_BasicIO::compile($parsed_config);
// 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
));
}
IDF_Scm_Monotone_Usher::reload();
}
/**
* Adds the (monotone) key to all monotone projects of this forge
* where the user of the key has write access to
*/
public function processKeyCreate($key)
{
if ($key->getType() != 'mtn') {
return;
}
if (Pluf::f('mtn_db_access', 'local') == 'local') {
return;
}
foreach (Pluf::factory('IDF_Project')->getList() as $project) {
$conf = new IDF_Conf();
$conf->setProject($project);
$scm = $conf->getVal('scm', 'mtn');
if ($scm != 'mtn')
continue;
$projectpath = self::_get_project_path($project);
$auth_ids = self::_get_authorized_user_ids($project);
if (!in_array($key->user, $auth_ids))
continue;
$mtn_key_id = $key->getMtnId();
// if the project is not defined as private, all people have
// read access already, so we don't need to write anything
// and we currently do not check if read-permissions really
// contains
// pattern "*"
// allow "*"
// which is the default for non-private projects
if ($project->private == true) {
$read_perms = file_get_contents($projectpath.'/read-permissions');
$parsed_read_perms = array();
try {
$parsed_read_perms = IDF_Scm_Monotone_BasicIO::parse($read_perms);
}
catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf(
__('Could not parse read-permissions for project "%s": %s'),
$shortname, $e->getMessage()
));
}
$wildcard_section = null;
for ($i=0; $i<count($parsed_read_perms); ++$i) {
foreach ($parsed_read_perms[$i] as $stanza_line) {
if ($stanza_line['key'] == 'pattern' &&
$stanza_line['values'][0] == '*') {
$wildcard_section =& $parsed_read_perms[$i];
break;
}
}
}
if ($wildcard_section == null)
{
$wildcard_section = array(
array('key' => 'pattern', 'values' => array('*'))
);
$parsed_read_perms[] =& $wildcard_section;
}
$key_found = false;
foreach ($wildcard_section as $line)
{
if ($line['key'] == 'allow' && $line['values'][0] == $mtn_key_id) {
$key_found = true;
break;
}
}
if (!$key_found) {
$wildcard_section[] = array(
'key' => 'allow', 'values' => array($mtn_key_id)
);
}
$read_perms = IDF_Scm_Monotone_BasicIO::compile($parsed_read_perms);
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
));
}
}
$write_perms = file_get_contents($projectpath.'/write-permissions');
$lines = preg_split("/(\n|\r\n)/", $write_perms, -1, PREG_SPLIT_NO_EMPTY);
if (!in_array('*', $lines) && !in_array($mtn_key_id, $lines)) {
$lines[] = $mtn_key_id;
}
if (file_put_contents($projectpath.'/write-permissions',
implode("\n", $lines) . "\n", LOCK_EX) === false) {
throw new IDF_Scm_Exception(sprintf(
__('Could not write write-permissions file for project "%s"'),
$shortname
));
}
$mtn = IDF_Scm_Monotone::factory($project);
$stdio = $mtn->getStdio();
$stdio->exec(array('put_public_key', $key->content));
}
}
/**
* Removes the (monotone) key from all monotone projects of this forge
* where the user of the key has write access to
*/
public function processKeyDelete($key)
{
try {
if ($key->getType() != 'mtn') {
return;
}
} catch (Exception $e) {
// bad key type, skip it.
return;
}
if (Pluf::f('mtn_db_access', 'local') == 'local') {
return;
}
foreach (Pluf::factory('IDF_Project')->getList() as $project) {
$conf = new IDF_Conf();
$conf->setProject($project);
$scm = $conf->getVal('scm', 'mtn');
if ($scm != 'mtn')
continue;
$projectpath = self::_get_project_path($project);
$auth_ids = self::_get_authorized_user_ids($project);
if (!in_array($key->user, $auth_ids))
continue;
$mtn_key_id = $key->getMtnId();
// if the project is not defined as private, all people have
// read access already, so we don't need to write anything
// and we currently do not check if read-permissions really
// contains
// pattern "*"
// allow "*"
// which is the default for non-private projects
if ($project->private) {
$read_perms = file_get_contents($projectpath.'/read-permissions');
$parsed_read_perms = array();
try {
$parsed_read_perms = IDF_Scm_Monotone_BasicIO::parse($read_perms);
}
catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf(
__('Could not parse read-permissions for project "%s": %s'),
$shortname, $e->getMessage()
));
}
// while we add new keys only to an existing wild-card entry
// we remove dropped keys from all sections since the key
// should be simply unavailable for all of them
for ($h=0; $h<count($parsed_read_perms); ++$h) {
for ($i=0; $i<count($parsed_read_perms[$h]); ++$i) {
if ($parsed_read_perms[$h][$i]['key'] == 'allow' &&
$parsed_read_perms[$h][$i]['values'][0] == $mtn_key_id) {
unset($parsed_read_perms[$h][$i]);
continue;
}
}
}
$read_perms = IDF_Scm_Monotone_BasicIO::compile($parsed_read_perms);
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
));
}
}
$write_perms = file_get_contents($projectpath.'/write-permissions');
$lines = preg_split("/(\n|\r\n)/", $write_perms, -1, PREG_SPLIT_NO_EMPTY);
for ($i=0; $i<count($lines); ++$i) {
if ($lines[$i] == $mtn_key_id) {
unset($lines[$i]);
// the key should actually only exist once in the
// file, but we're paranoid
continue;
}
}
if (file_put_contents($projectpath.'/write-permissions',
implode("\n", $lines) . "\n", LOCK_EX) === false) {
throw new IDF_Scm_Exception(sprintf(
__('Could not write write-permissions file for project "%s"'),
$shortname
));
}
$mtn = IDF_Scm_Monotone::factory($project);
$stdio = $mtn->getStdio();
// if the public key did not sign any revisions, drop it from
// the database as well
try {
if (strlen($stdio->exec(array('select', 'k:' . $mtn_key_id))) == 0) {
$stdio->exec(array('drop_public_key', $mtn_key_id));
}
} catch (IDF_Scm_Exception $e) {
if (strpos($e->getMessage(), 'there is no key named') === false)
throw $e;
}
}
}
/**
* Update the timeline after a push
*
*/
public function processSyncTimeline($project_name)
{
try {
$project = IDF_Project::getOr404($project_name);
} catch (Pluf_HTTP_Error404 $e) {
Pluf_Log::event(array(
'IDF_Plugin_SyncMonotone::processSyncTimeline',
'Project not found.',
array($project_name, $params)
));
return false; // Project not found
}
Pluf_Log::debug(array(
'IDF_Plugin_SyncMonotone::processSyncTimeline',
'Project found', $project_name, $project->id
));
IDF_Scm::syncTimeline($project, true);
Pluf_Log::event(array(
'IDF_Plugin_SyncMonotone::processSyncTimeline',
'sync', array($project_name, $project->id)
));
}
private static function _get_authorized_user_ids($project)
{
$mem = $project->getMembershipData();
$members = array_merge((array)$mem['members'],
(array)$mem['owners'],
(array)$mem['authorized']);
$userids = array();
foreach ($members as $member) {
$userids[] = $member->id;
}
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)
{
if (is_file($path) || is_link($path)) {
return @unlink($path);
}
if (is_dir($path)) {
$scan = glob(rtrim($path, '/') . '/*');
$status = 0;
foreach ($scan as $subpath) {
$status |= self::_delete_recursive($subpath);
}
$status |= rmdir($path);
return $status;
}
}
}

View File

@ -1,10 +0,0 @@
ARA_safe_commands = {
"get_corresponding_path", "get_content_changed", "tags", "branches",
"common_ancestors", "packet_for_fdelta", "packet_for_fdata",
"packets_for_certs", "packet_for_rdata", "get_manifest_of",
"get_revision", "select", "graph", "children", "parents", "roots",
"leaves", "ancestry_difference", "toposort", "erase_ancestors",
"descendents", "ancestors", "heads", "get_file_of", "get_file",
"interface_version", "get_attributes", "content_diff",
"file_merge", "show_conflicts", "certs", "keys", "get_extended_manifest_of"
}

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