Merge branch 'feature.scilab' into develop
This commit is contained in:
commit
b529c05d11
5
AUTHORS
5
AUTHORS
@ -17,20 +17,25 @@ Much appreciated contributors (in alphabetical order):
|
||||
Denis Kot <denis.kot@gmail.com> - Russian translation
|
||||
Dmitry Dulepov <dmitryd>
|
||||
Fernando Sayago Gil <mikados.mikados@gmail.com> - Spanish translation
|
||||
Gert van Valkenhoef <gertvv>
|
||||
Jakub Viták <mainiak@gmail.com> - Czech translation
|
||||
Janez Troha <http://www.dz0ny.info> - Slovenian translation
|
||||
Jean-Philippe Fleury <jpfleury>
|
||||
Jerry <lxb429@gmail.com> - Chinese translation
|
||||
Julien Issler <julien@issler.net>
|
||||
Litew <litew9@gmail.com> - Russian translation
|
||||
Ludovic Bellière <xrogaan>
|
||||
Manuel Eidenberger <eidenberger@gmail.com>
|
||||
Matthew Dawson <mjd>
|
||||
Matías Halles <matias@halles.cl>
|
||||
Mehdi Kabab <http://pioupioum.fr/>
|
||||
Nicolas Lassalle <nicolas@beroot.org> - Subversion support
|
||||
Ozan <uobasar@gmail.com> - Turkish translation
|
||||
Patrick Georgi <patrick.georgi@coresystems.de>
|
||||
Pedro Kiefer <pedro@kiefer.com.br> - Brazilian Portuguese translation
|
||||
Raphaël Emourgeon <raphael>
|
||||
Samuel Suther <info@suther.de> - German translation
|
||||
Simon Holywell <treffynnon@php.net>
|
||||
Sindre R. Myren <sindrero@stud.ntnu.no>
|
||||
Stewart Platt <stew@futurete.ch>
|
||||
Stéphane Baron <sbaron>
|
||||
|
@ -6,9 +6,15 @@ the installation of InDefero by itself.
|
||||
|
||||
## PHP modules for indefero
|
||||
|
||||
Indefero need the GD module for PHP. It's named "php5-gd" in debian.
|
||||
Indefero needs additional PHP modules to function correctly, namely
|
||||
|
||||
$ apt-get install php5-gd
|
||||
- gd (for graphic operations)
|
||||
- zip (for upload archive processing)
|
||||
|
||||
The package names of these modules might vary between distributions,
|
||||
for Debian they are
|
||||
|
||||
$ apt-get install php5-gd php5-zip
|
||||
|
||||
## Recommended Layout of the Files
|
||||
|
||||
|
10
Makefile
10
Makefile
@ -62,11 +62,11 @@ pot-update: pluf_path
|
||||
fi
|
||||
touch src/IDF/locale/idf.pot;
|
||||
# Extract string
|
||||
@cd src; php $(PLUF_PATH)/extracttemplates.php IDF/conf/idf.php IDF/gettexttemplates
|
||||
@cd src; php "$(PLUF_PATH)/extracttemplates.php" IDF/conf/idf.php IDF/gettexttemplates
|
||||
@cd src; for phpfile in `find . -iname "*.php"`; do \
|
||||
printf "Parsing file : "$$phpfile"\n"; \
|
||||
xgettext -o idf.pot -p ./IDF/locale/ --from-code=UTF-8 -j \
|
||||
--keyword --keyword=__ --keyword=_n:1,2 -L PHP $$phpfile ; \
|
||||
--keyword --keyword=__ --keyword=_n:1,2 -L PHP "$$phpfile" ; \
|
||||
done
|
||||
# Remove tmp folder
|
||||
rm -Rf src/IDF/gettexttemplates
|
||||
@ -76,7 +76,7 @@ pot-update: pluf_path
|
||||
po-update: pluf_path
|
||||
@for pofile in `ls src/IDF/locale/*/idf.po`; do \
|
||||
printf "Updating file : "$$pofile"\n"; \
|
||||
msgmerge -v -U $$pofile src/IDF/locale/idf.pot; \
|
||||
msgmerge -v -U "$$pofile" src/IDF/locale/idf.pot; \
|
||||
printf "\n"; \
|
||||
done
|
||||
|
||||
@ -144,7 +144,7 @@ po-stats:
|
||||
--pretty=format:%h`.zip
|
||||
|
||||
db-install:
|
||||
@cd src && php $(PLUF_PATH)/migrate.php --conf=IDF/conf/idf.php -a -d -i
|
||||
@cd src && php "$(PLUF_PATH)/migrate.php" --conf=IDF/conf/idf.php -a -d -i
|
||||
|
||||
db-update:
|
||||
@cd src && php $(PLUF_PATH)/migrate.php --conf=IDF/conf/idf.php -a -d
|
||||
@cd src && php "$(PLUF_PATH)/migrate.php" --conf=IDF/conf/idf.php -a -d
|
||||
|
85
NEWS.mdtext
85
NEWS.mdtext
@ -1,4 +1,76 @@
|
||||
# InDefero 1.2 - xxx xxx xx xx:xx 2011 UTC
|
||||
# InDefero 1.3 - xxx xxx xx xx:xx 2011 UTC
|
||||
|
||||
The development of this version of Indefero has been sponsored
|
||||
by the friendly folks from Scilab <http://www.scilab.org/>!
|
||||
|
||||
ATTENTION: You need Pluf [4121ca4](http://projects.ceondo.com/p/pluf/source/commit/4121ca4)
|
||||
or newer to properly run this version of Indefero!
|
||||
|
||||
## Changes
|
||||
|
||||
- Indefero's post-commit web hook now by default issues HTTP PUT instead of
|
||||
HTTP POST requests and carries the authentication digest in the new
|
||||
`Web-Hook-Hmac` header. The old behaviour can be re-enabled by setting the
|
||||
`$cfg['webhook_processing']` flag to "compat", we urge you to change the
|
||||
implementations of this web hook as this setting is likely to be removed
|
||||
in future versions of Indefero.
|
||||
- Indefero now needs PHP's zip module which is not enabled by default.
|
||||
- Existing email notifications now have to be explicitely activated in the
|
||||
project's administrative area.
|
||||
|
||||
## New Features
|
||||
|
||||
- It is now possible to upload and embed resources like images or text
|
||||
files into wiki pages. If no preview for a resource's mime type is
|
||||
available, than a download link is provided for it instead.
|
||||
- The notification system has been overhauled; it is now possible to configure
|
||||
what kind of user group, project administrators, members and / or additional
|
||||
mail addresses are notified about updates in a certain section, such as
|
||||
issues, downloads, reviews, and so on. We now also ensure that notification
|
||||
emails for one object are uniquely identifyable to support a grouped view
|
||||
in email clients that support that. (fixes issues 334, 452, and 480)
|
||||
- Indefero can now be configured to record activity metrics for all projects
|
||||
in a forge. This needs a special cron job named 'activitycron.php`
|
||||
(under `scripts`) that is run on a regular basis. The metrics can be
|
||||
fine-tuned via `activity_section_weights` and `activity_lookback` in
|
||||
`idf.php` and the result is visible as green bar in the project list view.
|
||||
- The forge's project list has been overhauled - its now possible to attach
|
||||
labels on projects and to filter and order the project list by various
|
||||
criteria. Additionally, projects can now get an external project URL
|
||||
configured that is displayed as linkable icon right beside the project name
|
||||
(if available)
|
||||
- Forge administrators can furthermore configure an alternative entry page
|
||||
for the forge that is displayed instead of the plain project list. This
|
||||
page accepts standard Markdown syntax and has support for the new
|
||||
`projectlist` macro that allows the (partial) inline rendering of the
|
||||
known global project list.
|
||||
- It is now also possible to configure a web hook that informs an external
|
||||
URL about new and updated downloads for a specific project, similar to the
|
||||
available post-commit web hook.
|
||||
- One can now upload multiple files at once by using a special archive format
|
||||
which Indefero processes in the background and for which individual upload
|
||||
records are created.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Ensure that IDF does not break UTF-8 encoded strings when
|
||||
shortening them for view rendering (issue 785)
|
||||
- Indefero no longer confuses a non-owner of an issue with a notification that
|
||||
a particular ticket has been opened and assigned to him (fixes issue 562)
|
||||
|
||||
# InDefero 1.2.1 - XXX XXX XX XX:XX:XX UTC 201X
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- The diff view now renders properly in Firefox when a minimum font size
|
||||
is configured or the user zooms the web page (fixes issue 773)
|
||||
|
||||
## Language and Translations
|
||||
|
||||
- Multiple fixes to English source strings (fixes issues 763, 766, and 772,
|
||||
thanks to JP Fleury!)
|
||||
|
||||
# InDefero 1.2 - Sun Nov 6 23:04:00 UTC 2011
|
||||
|
||||
ATTENTION: You need Pluf [46b7f251](http://projects.ceondo.com/p/pluf/source/commit/46b7f251)
|
||||
or newer to properly run this version of Indefero!
|
||||
@ -50,9 +122,13 @@ or newer to properly run this version of Indefero!
|
||||
middle (issue 697)
|
||||
- Indefero now sends the MD5 checksum as HTTP header when downloading a file from the
|
||||
download area; additionally, a unneeded redirect has been removed (issue 716)
|
||||
- Avatar URL generation use correctly the configuration (issue 732)
|
||||
- Source links without a specific revision did not work due to a wrong regex
|
||||
(issue 730)
|
||||
- Avatar URL generation use correctly the configuration (issue 732)
|
||||
- The SyncGit plugin no longer fails to remove a non-existing post-update hook
|
||||
on repository creation (issue 752)
|
||||
- When uploading a project logo, an existing uploaded file with the same name
|
||||
no longer leads to an error, but is simple overwritten (fixes issue 740)
|
||||
- The error detection and reporting in the SyncMonotone plugin has been improved
|
||||
- The branch links users of the Subversion frontend get when they enter a wrong
|
||||
revision are fixed; this list is now also only displayed (for any SCM) if
|
||||
@ -71,6 +147,11 @@ or newer to properly run this version of Indefero!
|
||||
|
||||
## Translations
|
||||
|
||||
- The Russian translation has been enabled by default (thanks for all the great
|
||||
work, Denis Kot and Litew!)
|
||||
- Brazilian Portuguese translation started (thanks to Pedro Kiefer!)
|
||||
- Turkish translation started (thanks to Ozan!)
|
||||
|
||||
# InDefero 1.1.2 - Thu May 26 07:42:25 2011 UTC
|
||||
|
||||
## Bugfixes
|
||||
|
284
logo/external_link.svg
Normal file
284
logo/external_link.svg
Normal file
@ -0,0 +1,284 @@
|
||||
<?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="190.51302"
|
||||
height="182.16527"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="external_link.svg"
|
||||
inkscape:export-filename="/Users/tommyd/Entwicklung/Open Source/indefero/www/media/idf/img/external_link.png"
|
||||
inkscape:export-xdpi="7.4108529"
|
||||
inkscape:export-ydpi="7.4108529"
|
||||
enable-background="new">
|
||||
<defs
|
||||
id="defs4">
|
||||
<inkscape:path-effect
|
||||
effect="skeletal"
|
||||
id="path-effect4079"
|
||||
is_visible="true"
|
||||
pattern="M 0,5 10,10 10,0 z"
|
||||
copytype="single_stretched"
|
||||
prop_scale="1"
|
||||
scale_y_rel="false"
|
||||
spacing="0"
|
||||
normal_offset="0"
|
||||
tang_offset="0"
|
||||
prop_units="false"
|
||||
vertical_pattern="false"
|
||||
fuse_tolerance="0" />
|
||||
<inkscape:path-effect
|
||||
effect="skeletal"
|
||||
id="path-effect4071"
|
||||
is_visible="true"
|
||||
pattern="M 0,5 10,10 10,0 z"
|
||||
copytype="single_stretched"
|
||||
prop_scale="1"
|
||||
scale_y_rel="false"
|
||||
spacing="0"
|
||||
normal_offset="0"
|
||||
tang_offset="0"
|
||||
prop_units="false"
|
||||
vertical_pattern="false"
|
||||
fuse_tolerance="0" />
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Lend"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path3627"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)" />
|
||||
</marker>
|
||||
<inkscape:path-effect
|
||||
effect="skeletal"
|
||||
id="path-effect3619"
|
||||
is_visible="true"
|
||||
pattern="M 0,5 10,10 10,0 z"
|
||||
copytype="single_stretched"
|
||||
prop_scale="1"
|
||||
scale_y_rel="false"
|
||||
spacing="0"
|
||||
normal_offset="0"
|
||||
tang_offset="0"
|
||||
prop_units="false"
|
||||
vertical_pattern="false"
|
||||
fuse_tolerance="0" />
|
||||
<inkscape:path-effect
|
||||
effect="skeletal"
|
||||
id="path-effect3615"
|
||||
is_visible="true"
|
||||
pattern="M 0,5 10,10 10,0 z"
|
||||
copytype="single_stretched"
|
||||
prop_scale="1"
|
||||
scale_y_rel="false"
|
||||
spacing="0"
|
||||
normal_offset="0"
|
||||
tang_offset="0"
|
||||
prop_units="false"
|
||||
vertical_pattern="false"
|
||||
fuse_tolerance="0" />
|
||||
<inkscape:path-effect
|
||||
effect="skeletal"
|
||||
id="path-effect3611"
|
||||
is_visible="true"
|
||||
pattern="M 0,5 10,10 10,0 z"
|
||||
copytype="single_stretched"
|
||||
prop_scale="1"
|
||||
scale_y_rel="false"
|
||||
spacing="0"
|
||||
normal_offset="0"
|
||||
tang_offset="0"
|
||||
prop_units="false"
|
||||
vertical_pattern="false"
|
||||
fuse_tolerance="0" />
|
||||
<inkscape:path-effect
|
||||
effect="skeletal"
|
||||
id="path-effect3607"
|
||||
is_visible="true"
|
||||
pattern="M 0,5 10,10 10,0 z"
|
||||
copytype="single_stretched"
|
||||
prop_scale="1"
|
||||
scale_y_rel="false"
|
||||
spacing="0"
|
||||
normal_offset="0"
|
||||
tang_offset="0"
|
||||
prop_units="false"
|
||||
vertical_pattern="false"
|
||||
fuse_tolerance="0" />
|
||||
<inkscape:path-effect
|
||||
effect="skeletal"
|
||||
id="path-effect3603"
|
||||
is_visible="true"
|
||||
pattern="M 0,5 10,10 10,0 z"
|
||||
copytype="single_stretched"
|
||||
prop_scale="1"
|
||||
scale_y_rel="false"
|
||||
spacing="0"
|
||||
normal_offset="0"
|
||||
tang_offset="0"
|
||||
prop_units="false"
|
||||
vertical_pattern="false"
|
||||
fuse_tolerance="0" />
|
||||
<inkscape:path-effect
|
||||
effect="skeletal"
|
||||
id="path-effect3599"
|
||||
is_visible="true"
|
||||
pattern="M 0,5 10,10 10,0 z"
|
||||
copytype="single_stretched"
|
||||
prop_scale="1"
|
||||
scale_y_rel="false"
|
||||
spacing="0"
|
||||
normal_offset="0"
|
||||
tang_offset="0"
|
||||
prop_units="false"
|
||||
vertical_pattern="false"
|
||||
fuse_tolerance="0" />
|
||||
<inkscape:path-effect
|
||||
effect="skeletal"
|
||||
id="path-effect3595"
|
||||
is_visible="true"
|
||||
pattern="M 0,5 10,10 10,0 z"
|
||||
copytype="single_stretched"
|
||||
prop_scale="1"
|
||||
scale_y_rel="false"
|
||||
spacing="0"
|
||||
normal_offset="0"
|
||||
tang_offset="0"
|
||||
prop_units="false"
|
||||
vertical_pattern="false"
|
||||
fuse_tolerance="0" />
|
||||
<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" />
|
||||
<inkscape:perspective
|
||||
id="perspective4900"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter4921"
|
||||
x="-0.12982728"
|
||||
width="1.2596545"
|
||||
y="-0.092135489"
|
||||
height="1.184271"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="6.8004762"
|
||||
id="feGaussianBlur4923" />
|
||||
</filter>
|
||||
<inkscape:perspective
|
||||
id="perspective4933"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter4921-3"
|
||||
x="-0.12982728"
|
||||
width="1.2596545"
|
||||
y="-0.092135489"
|
||||
height="1.184271">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="6.8004762"
|
||||
id="feGaussianBlur4923-8" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.78"
|
||||
inkscape:cx="138.94497"
|
||||
inkscape:cy="111.09667"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1389"
|
||||
inkscape:window-height="803"
|
||||
inkscape:window-x="47"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0" />
|
||||
<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" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Ebene"
|
||||
transform="translate(16.297732,13.906157)"
|
||||
style="display:inline"
|
||||
sodipodi:insensitive="true">
|
||||
<g
|
||||
id="g2818-7"
|
||||
style="fill:#e6e6e6;fill-opacity:1;stroke:#a0a0a0;stroke-width:2.26057386;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter4921-3)"
|
||||
transform="matrix(0.97722285,0,0,-0.85847241,-166.837,487.59193)">
|
||||
<rect
|
||||
ry="9.2957697"
|
||||
rx="8.07693"
|
||||
y="389.50504"
|
||||
x="171.42857"
|
||||
height="177.14285"
|
||||
width="125.71429"
|
||||
id="rect2816-9"
|
||||
style="opacity:0.79710143;fill:#000000;fill-opacity:1;stroke:#a0a0a0;stroke-width:2.26057386;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-155.26623,-395.13431)"
|
||||
style="display:inline;">
|
||||
<rect
|
||||
style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#a0a0a0;stroke-width:2.41343927;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="rect4985"
|
||||
width="129.92365"
|
||||
height="152.95734"
|
||||
x="168.88869"
|
||||
y="407.28156"
|
||||
rx="9.9356585"
|
||||
ry="11.003931" />
|
||||
<path
|
||||
style="fill:#00000f;fill-opacity:0.94117647;stroke:none;stroke-width:2.17028474999999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 205.32036,460.1042 67.83031,0 0,-32.45771 71.54343,60.58773 -72.50161,61.39918 0,-33.26916 -66.75258,0 0,-56.26004"
|
||||
id="path4081"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.6 KiB |
48
scripts/activitycron.php
Normal file
48
scripts/activitycron.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* This script recalculates the "project activity" for all of the
|
||||
* forge's projects for the given date.
|
||||
* If no date is given, yesterday's date is used.
|
||||
*
|
||||
* This script should run once a day. You can configure its behaviour
|
||||
* with $cfg['activity_section_weights'] and $cfg['activity_lookback'].
|
||||
*
|
||||
* If the script runs more than once with the same date argument,
|
||||
* previously recorded project activity values are replaced with the
|
||||
* newly created ones.
|
||||
*/
|
||||
|
||||
require dirname(__FILE__).'/../src/IDF/conf/path.php';
|
||||
require 'Pluf.php';
|
||||
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
|
||||
|
||||
$date = new DateTime('yesterday');
|
||||
if (count($_SERVER['argv']) > 1) {
|
||||
$date = new DateTime($_SERVER['argv'][1]);
|
||||
}
|
||||
|
||||
echo 'recalculating project activity for '.$date->format('Y-m-d')."\n";
|
||||
IDF_ActivityTaxonomy::recalculateTaxnomies($date);
|
||||
|
156
src/IDF/ActivityTaxonomy.php
Normal file
156
src/IDF/ActivityTaxonomy.php
Normal file
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Class that calculates the activity value for all projects on a
|
||||
* specific date and time.
|
||||
*
|
||||
* We do this by counting adds or updates of database objects in
|
||||
* the particular section (according to the timeline) and relate this
|
||||
* value to the overall activity of a section in the forge.
|
||||
*
|
||||
* To illustrate the behaviour, a simple example could be a forge with
|
||||
* only two projects that both have only issue tracking enabled.
|
||||
* The first project created or updated 10 tickets during the past period,
|
||||
* the other 20. The activity index for the first should therefor be
|
||||
* calculated as 0.33 and the second as 0.66.
|
||||
* Note that this simple example doesn't take activity in other
|
||||
* sections into account, so the the total activity of all projects
|
||||
* for a certain time period might add up to more than 1.0.
|
||||
*
|
||||
* @author tommyd
|
||||
*/
|
||||
class IDF_ActivityTaxonomy
|
||||
{
|
||||
public static function recalculateTaxnomies(DateTime $date)
|
||||
{
|
||||
//
|
||||
// query and normalize the section weights
|
||||
//
|
||||
$sectionWeights = Pluf::f('activity_section_weights', array());
|
||||
$allWeights = array_sum($sectionWeights);
|
||||
if ($allWeights == 0) {
|
||||
throw new LogicException('the sum of all "activity_section_weights" must not be 0');
|
||||
}
|
||||
foreach ($sectionWeights as $section => $weight) {
|
||||
$sectionWeights[$section] = $weight / (float) $allWeights;
|
||||
}
|
||||
|
||||
//
|
||||
// determine the date boundaries
|
||||
//
|
||||
$lookback = Pluf::f('activity_lookback', 0);
|
||||
if ($lookback < 1) {
|
||||
throw new LogicException('lookback must be greater or equal to 1');
|
||||
}
|
||||
$dateCopy = new DateTime();
|
||||
$dateCopy->setTimestamp($date->getTimestamp());
|
||||
$dateBoundaries = array(
|
||||
$dateCopy->format('Y-m-d 23:59:59'),
|
||||
$dateCopy->sub(new DateInterval('P'.$lookback.'D'))->format('Y-m-d 00:00:00')
|
||||
);
|
||||
|
||||
//
|
||||
// now recalculate the values for all projects
|
||||
//
|
||||
$projects = Pluf::factory('IDF_Project')->getList();
|
||||
foreach ($projects as $project) {
|
||||
self::recalculateTaxonomy($date, $project, $dateBoundaries, $sectionWeights);
|
||||
}
|
||||
}
|
||||
|
||||
private static function recalculateTaxonomy(DateTime $date, IDF_Project $project, array $dateBoundaries, array $sectionWeights)
|
||||
{
|
||||
$conf = new IDF_Conf();
|
||||
$conf->setProject($project);
|
||||
|
||||
$sectionClasses = array(
|
||||
'source' => array('IDF_Commit'),
|
||||
'issues' => array('IDF_Issue'),
|
||||
'wiki' => array('IDF_Wiki_Page', 'IDF_Wiki_Resource'),
|
||||
'review' => array('IDF_Review'),
|
||||
'downloads' => array('IDF_Upload')
|
||||
);
|
||||
|
||||
$value = 0;
|
||||
foreach ($sectionWeights as $section => $weight) {
|
||||
// skip closed / non-existant sections
|
||||
if ($conf->getVal($section.'_access_rights') === 'none')
|
||||
continue;
|
||||
|
||||
if (!array_key_exists($section, $sectionClasses))
|
||||
continue;
|
||||
|
||||
$sectionValue = self::calculateActivityValue(
|
||||
$dateBoundaries, $sectionClasses[$section], $project->id);
|
||||
$value = ((1 - $weight) * $value) + ($weight * $sectionValue);
|
||||
}
|
||||
|
||||
echo "project {$project->name} has an activity value of $value\n";
|
||||
|
||||
$sql = new Pluf_SQL('project=%s AND date=%s', array($project->id, $date->format('Y-m-d')));
|
||||
$activity = Pluf::factory('IDF_ProjectActivity')->getOne(array('filter' => $sql->gen()));
|
||||
|
||||
if ($activity == null) {
|
||||
$activity = new IDF_ProjectActivity();
|
||||
$activity->project = $project;
|
||||
$activity->date = $date->format('Y-m-d');
|
||||
$activity->value = $value;
|
||||
$activity->create();
|
||||
} else {
|
||||
$activity->value = $value;
|
||||
$activity->update();
|
||||
}
|
||||
}
|
||||
|
||||
private static function calculateActivityValue(array $dateBoundaries, array $classes, $projectId)
|
||||
{
|
||||
$allCount = self::countActivityFor($dateBoundaries, $classes);
|
||||
if ($allCount == 0) return 0;
|
||||
$prjCount = self::countActivityFor($dateBoundaries, $classes, $projectId);
|
||||
return $prjCount / (float) $allCount;
|
||||
}
|
||||
|
||||
private static function countActivityFor(array $dateBoundaries, array $classes, $projectId = null)
|
||||
{
|
||||
static $cache = array();
|
||||
$argIdent = md5(serialize(func_get_args()));
|
||||
if (array_key_exists($argIdent, $cache)) {
|
||||
return $cache[$argIdent];
|
||||
}
|
||||
|
||||
$cache[$argIdent] = 0;
|
||||
list($higher, $lower) = $dateBoundaries;
|
||||
$sql = new Pluf_SQL('model_class IN ("'.implode('","', $classes).'") '.
|
||||
'AND creation_dtime >= %s AND creation_dtime <= %s',
|
||||
array($lower, $higher));
|
||||
|
||||
if ($projectId !== null) {
|
||||
$sql->SAnd(new Pluf_SQL('project=%s', array($projectId)));
|
||||
}
|
||||
|
||||
$cache[$argIdent] = Pluf::factory('IDF_Timeline')->getCount(array('filter' => $sql->gen()));
|
||||
|
||||
return $cache[$argIdent];
|
||||
}
|
||||
}
|
@ -235,7 +235,7 @@ class IDF_Commit extends Pluf_Model
|
||||
</tr>
|
||||
<tr class="extra">
|
||||
<td colspan="2">
|
||||
<div class="helptext right">'.sprintf(__('Commit %s, by %s'), '<a href="'.$url.'" class="mono">'.$this->scm_id.'</a>', $user).'</div></td></tr>';
|
||||
<div class="helptext right">'.sprintf(__('Commit %1$s, by %2$s'), '<a href="'.$url.'" class="mono">'.$this->scm_id.'</a>', $user).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
@ -287,6 +287,12 @@ class IDF_Commit extends Pluf_Model
|
||||
$url = str_replace(array('%p', '%r'),
|
||||
array($project->shortname, $this->scm_id),
|
||||
$conf->getVal('webhook_url', ''));
|
||||
|
||||
// trigger a POST instead of the standard PUT if we're asked for
|
||||
$method = 'PUT';
|
||||
if (Pluf::f('webhook_processing', '') === 'compat') {
|
||||
$method = 'POST';
|
||||
}
|
||||
$payload = array('to_send' => array(
|
||||
'project' => $project->shortname,
|
||||
'rev' => $this->scm_id,
|
||||
@ -297,41 +303,51 @@ class IDF_Commit extends Pluf_Model
|
||||
'creation_date' => $this->creation_dtime,
|
||||
),
|
||||
'project_id' => $project->id,
|
||||
'authkey' => $project->getPostCommitHookKey(),
|
||||
'authkey' => $project->getWebHookKey(),
|
||||
'url' => $url,
|
||||
'method' => $method,
|
||||
);
|
||||
$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();
|
||||
|
||||
$from_email = Pluf::f('from_email');
|
||||
$recipients = $project->getNotificationRecipientsForTab('source');
|
||||
|
||||
foreach ($recipients as $address => $language) {
|
||||
|
||||
if (!empty($this->author) && $this->author->email === $address) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$current_locale = Pluf_Translation::getLocale();
|
||||
$langs = Pluf::f('languages', array('en'));
|
||||
Pluf_Translation::loadSetLocale($langs[0]);
|
||||
Pluf_Translation::loadSetLocale($language);
|
||||
|
||||
$context = new Pluf_Template_Context(
|
||||
array(
|
||||
'c' => $this,
|
||||
'project' => $this->get_project(),
|
||||
$context = new Pluf_Template_Context(array(
|
||||
'commit' => $this,
|
||||
'project' => $project,
|
||||
'url_base' => Pluf::f('url_base'),
|
||||
)
|
||||
);
|
||||
$tmpl = new Pluf_Template('idf/source/commit-created-email.txt');
|
||||
));
|
||||
|
||||
// commits are usually not updated, therefor we do not
|
||||
// distinguish between create and update here
|
||||
$tplfile = 'idf/source/commit-created-email.txt';
|
||||
$subject = __('New Commit %1$s - %2$s (%3$s)');
|
||||
|
||||
$tmpl = new Pluf_Template($tplfile);
|
||||
$text_email = $tmpl->render($context);
|
||||
$addresses = explode(',', $conf->getVal('source_notification_email'));
|
||||
foreach ($addresses as $address) {
|
||||
$email = new Pluf_Mail(Pluf::f('from_email'),
|
||||
|
||||
$email = new Pluf_Mail($from_email,
|
||||
$address,
|
||||
sprintf(__('New Commit %s - %s (%s)'),
|
||||
sprintf($subject,
|
||||
$this->scm_id, $this->summary,
|
||||
$this->get_project()->shortname));
|
||||
$project->shortname));
|
||||
$email->addTextMessage($text_email);
|
||||
$email->sendMail();
|
||||
}
|
||||
|
||||
Pluf_Translation::loadSetLocale($current_locale);
|
||||
}
|
||||
}
|
||||
|
@ -184,12 +184,12 @@ class IDF_Diff
|
||||
$added = $added[0] + $added[1];
|
||||
$leftwidth = 0;
|
||||
if ($added > 0)
|
||||
$leftwidth = ((ceil(log10($added)) + 1) * 8) + 12;
|
||||
$leftwidth = ((ceil(log10($added)) + 1) * 8) + 17;
|
||||
|
||||
$removed = $removed[0] + $removed[1];
|
||||
$rightwidth = 0;
|
||||
if ($removed > 0)
|
||||
$rightwidth = ((ceil(log10($removed)) + 1) * 8) + 12;
|
||||
$rightwidth = ((ceil(log10($removed)) + 1) * 8) + 17;
|
||||
|
||||
// we need to correct the width of a single column a little
|
||||
// to take less space and to hide the empty one
|
||||
@ -411,11 +411,11 @@ class IDF_Diff
|
||||
|
||||
$leftwidth = 1;
|
||||
if ($max_lineno_left > 0)
|
||||
$leftwidth = ((ceil(log10($max_lineno_left)) + 1) * 8) + 12;
|
||||
$leftwidth = ((ceil(log10($max_lineno_left)) + 1) * 8) + 17;
|
||||
|
||||
$rightwidth = 1;
|
||||
if ($max_lineno_right > 0)
|
||||
$rightwidth = ((ceil(log10($max_lineno_right)) + 1) * 8) + 12;
|
||||
$rightwidth = ((ceil(log10($max_lineno_right)) + 1) * 8) + 17;
|
||||
|
||||
$inner_linecounts_left =
|
||||
'<table class="diff-linecounts">' ."\n".
|
||||
|
95
src/IDF/Forge.php
Normal file
95
src/IDF/Forge.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* A lightweight model for the singleton forge entity
|
||||
*/
|
||||
class IDF_Forge
|
||||
{
|
||||
public $_model = __CLASS__;
|
||||
|
||||
public $id = 1;
|
||||
|
||||
/**
|
||||
* @var IDF_Gconf
|
||||
*/
|
||||
private $conf;
|
||||
|
||||
private function __construct() {
|
||||
$this->conf = new IDF_Gconf();
|
||||
$this->conf->setModel($this);
|
||||
}
|
||||
|
||||
public static function instance() {
|
||||
return new IDF_Forge();
|
||||
}
|
||||
|
||||
public function getProjectLabels($default = '') {
|
||||
return $this->conf->getVal('project_labels', $default);
|
||||
}
|
||||
|
||||
public function setProjectLabels($labels) {
|
||||
$this->conf->setVal('project_labels', $labels);
|
||||
}
|
||||
|
||||
public function getProjectLabelsWithCounts() {
|
||||
$sql = new Pluf_SQL('project=0');
|
||||
$tagList = Pluf::factory('IDF_Tag')->getList(array(
|
||||
'filter' => $sql->gen(),
|
||||
'view' => 'join_projects',
|
||||
'order' => 'class ASC, lcname ASC'
|
||||
));
|
||||
|
||||
$maxProjectCount = 0;
|
||||
foreach ($tagList as $tag) {
|
||||
$maxProjectCount = max($maxProjectCount, $tag->project_count);
|
||||
}
|
||||
|
||||
$tags = array();
|
||||
foreach ($tagList as $tag) {
|
||||
// group by class
|
||||
if (!array_key_exists($tag->class, $tags)) {
|
||||
$tags[$tag->class] = array();
|
||||
}
|
||||
$tag->rel_project_count = $tag->project_count / (double) $maxProjectCount;
|
||||
$tags[$tag->class][] = $tag;
|
||||
}
|
||||
return $tags;
|
||||
}
|
||||
|
||||
public function setCustomForgePageEnabled($enabled) {
|
||||
$this->conf->setVal('custom_forge_page_enabled', $enabled);
|
||||
}
|
||||
|
||||
public function isCustomForgePageEnabled($default = false) {
|
||||
return $this->conf->getVal('custom_forge_page_enabled', $default);
|
||||
}
|
||||
|
||||
public function getCustomForgePageContent($default = '') {
|
||||
return $this->conf->getVal('custom_forge_page_content', $default);
|
||||
}
|
||||
|
||||
public function setCustomForgePageContent($content) {
|
||||
$this->conf->setVal('custom_forge_page_content', $content);
|
||||
}
|
||||
}
|
47
src/IDF/Form/Admin/ForgeConf.php
Normal file
47
src/IDF/Form/Admin/ForgeConf.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright(C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
/**
|
||||
* Configuration of the forge's start page.
|
||||
*/
|
||||
class IDF_Form_Admin_ForgeConf extends Pluf_Form
|
||||
{
|
||||
public function initFields($extra=array())
|
||||
{
|
||||
$this->fields['enabled'] = new Pluf_Form_Field_Boolean(
|
||||
array('required' => false,
|
||||
'label' => __('Custom forge page enabled'),
|
||||
'widget' => 'Pluf_Form_Widget_CheckboxInput',
|
||||
));
|
||||
$this->fields['content'] = new Pluf_Form_Field_Varchar(
|
||||
array('required' => true,
|
||||
'label' => __('Content'),
|
||||
'widget' => 'Pluf_Form_Widget_TextareaInput',
|
||||
'widget_attrs' => array(
|
||||
'cols' => 68,
|
||||
'rows' => 26,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
62
src/IDF/Form/Admin/LabelConf.php
Normal file
62
src/IDF/Form/Admin/LabelConf.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright(C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
/**
|
||||
* Configuration of forge labels.
|
||||
*/
|
||||
class IDF_Form_Admin_LabelConf extends Pluf_Form
|
||||
{
|
||||
const init_project_labels = 'UI:GUI = Applications with graphical user interfaces
|
||||
UI:CLI = Applications with no graphical user interfaces
|
||||
License:BSD = Applications with BSD license
|
||||
License:GPL = Applications with GPL license
|
||||
';
|
||||
|
||||
public function initFields($extra=array())
|
||||
{
|
||||
$this->fields['project_labels'] = new Pluf_Form_Field_Varchar(
|
||||
array('required' => true,
|
||||
'label' => __('Predefined project labels'),
|
||||
'initial' => self::init_project_labels,
|
||||
'widget_attrs' => array('rows' => 13,
|
||||
'cols' => 75),
|
||||
'widget' => 'Pluf_Form_Widget_TextareaInput',
|
||||
));
|
||||
}
|
||||
|
||||
public function clean_project_labels()
|
||||
{
|
||||
$labels = preg_split("/\s*\n\s*/", $this->cleaned_data['project_labels'], -1, PREG_SPLIT_NO_EMPTY);
|
||||
for ($i=0; $i<count($labels); ++$i) {
|
||||
$labels[$i] = trim($labels[$i]);
|
||||
if (!preg_match('/^[\w-]+(:[\w-]+)?(\s*=\s*[^=]+)?$/', $labels[$i])) {
|
||||
throw new Pluf_Form_Invalid(sprintf(
|
||||
__('The label "%s" is invalid: A label must only consist of alphanumeric '.
|
||||
'characters and dashes, and can optionally contain a ":" with a group prefix.'),
|
||||
$labels[$i]));
|
||||
}
|
||||
}
|
||||
return implode("\n", $labels);
|
||||
}
|
||||
}
|
@ -72,6 +72,13 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
||||
'widget_attrs' => array('size' => '35'),
|
||||
));
|
||||
|
||||
$this->fields['external_project_url'] = new Pluf_Form_Field_Varchar(
|
||||
array('required' => false,
|
||||
'label' => __('External URL'),
|
||||
'widget_attrs' => array('size' => '35'),
|
||||
'initial' => '',
|
||||
));
|
||||
|
||||
$this->fields['scm'] = new Pluf_Form_Field_Varchar(
|
||||
array('required' => true,
|
||||
'label' => __('Repository type'),
|
||||
@ -127,6 +134,18 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
||||
'widget' => 'Pluf_Form_Widget_TextareaInput',
|
||||
));
|
||||
|
||||
for ($i=1;$i<7;$i++) {
|
||||
$this->fields['label'.$i] = new Pluf_Form_Field_Varchar(
|
||||
array('required' => false,
|
||||
'label' => __('Labels'),
|
||||
'initial' => '',
|
||||
'widget_attrs' => array(
|
||||
'maxlength' => 50,
|
||||
'size' => 20,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
$projects = array('--' => '--');
|
||||
foreach (Pluf::factory('IDF_Project')->getList(array('order' => 'name ASC')) as $proj) {
|
||||
$projects[$proj->name] = $proj->shortname;
|
||||
@ -199,7 +218,7 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
||||
$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.'
|
||||
'please use only letters, digits, dashes and dots as separators.'
|
||||
));
|
||||
}
|
||||
|
||||
@ -235,6 +254,11 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
||||
return $shortname;
|
||||
}
|
||||
|
||||
public function clean_external_project_url()
|
||||
{
|
||||
return IDF_Form_ProjectConf::checkWebURL($this->cleaned_data['external_project_url']);
|
||||
}
|
||||
|
||||
public function clean()
|
||||
{
|
||||
if ($this->cleaned_data['scm'] != 'svn') {
|
||||
@ -278,11 +302,29 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
||||
if (!$this->isValid()) {
|
||||
throw new Exception(__('Cannot save the model from an invalid form.'));
|
||||
}
|
||||
|
||||
// Add a tag for each label
|
||||
$tagids = 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::addGlobal($name, $class);
|
||||
$tagids[] = $tag->id;
|
||||
}
|
||||
}
|
||||
|
||||
$project = new IDF_Project();
|
||||
$project->name = $this->cleaned_data['name'];
|
||||
$project->shortname = $this->cleaned_data['shortname'];
|
||||
$project->shortdesc = $this->cleaned_data['shortdesc'];
|
||||
|
||||
$tagids = array();
|
||||
if ($this->cleaned_data['template'] != '--') {
|
||||
// Find the template project
|
||||
$sql = new Pluf_SQL('shortname=%s',
|
||||
@ -290,15 +332,36 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
||||
$tmpl = Pluf::factory('IDF_Project')->getOne(array('filter' => $sql->gen()));
|
||||
$project->private = $tmpl->private;
|
||||
$project->description = $tmpl->description;
|
||||
|
||||
foreach ($tmpl->get_tags_list() as $tag) {
|
||||
$tagids[] = $tag->id;
|
||||
}
|
||||
} else {
|
||||
$project->private = $this->cleaned_data['private_project'];
|
||||
$project->description = __('Click on the Project Management tab to set the description of your project.');
|
||||
|
||||
// Add a tag for each label
|
||||
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::addGlobal($name, $class);
|
||||
$tagids[] = $tag->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
$project->create();
|
||||
$project->batchAssoc('IDF_Tag', $tagids);
|
||||
|
||||
$conf = new IDF_Conf();
|
||||
$conf->setProject($project);
|
||||
$keys = array('scm', 'svn_remote_url', 'svn_username',
|
||||
'svn_password', 'mtn_master_branch');
|
||||
'svn_password', 'mtn_master_branch', 'external_project_url');
|
||||
foreach ($keys as $key) {
|
||||
$this->cleaned_data[$key] = (!empty($this->cleaned_data[$key])) ?
|
||||
$this->cleaned_data[$key] : '';
|
||||
|
@ -53,6 +53,13 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
|
||||
'widget_attrs' => array('size' => '35'),
|
||||
));
|
||||
|
||||
$this->fields['external_project_url'] = new Pluf_Form_Field_Varchar(
|
||||
array('required' => false,
|
||||
'label' => __('External URL'),
|
||||
'widget_attrs' => array('size' => '35'),
|
||||
'initial' => $conf->getVal('external_project_url'),
|
||||
));
|
||||
|
||||
if ($this->project->getConf()->getVal('scm') == 'mtn') {
|
||||
$this->fields['mtn_master_branch'] = new Pluf_Form_Field_Varchar(
|
||||
array('required' => false,
|
||||
@ -63,6 +70,26 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
|
||||
));
|
||||
}
|
||||
|
||||
$tags = $this->project->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,
|
||||
),
|
||||
));
|
||||
}
|
||||
$this->fields['owners'] = new Pluf_Form_Field_Varchar(
|
||||
array('required' => false,
|
||||
'label' => __('Project owners'),
|
||||
@ -115,22 +142,52 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
|
||||
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['members']);
|
||||
}
|
||||
|
||||
public function clean_external_project_url()
|
||||
{
|
||||
return IDF_Form_ProjectConf::checkWebURL($this->cleaned_data['external_project_url']);
|
||||
}
|
||||
|
||||
public function save($commit=true)
|
||||
{
|
||||
if (!$this->isValid()) {
|
||||
throw new Exception(__('Cannot save the model from an invalid form.'));
|
||||
}
|
||||
|
||||
// Add a tag for each label
|
||||
$tagids = 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::addGlobal($name, $class);
|
||||
$tagids[] = $tag->id;
|
||||
}
|
||||
}
|
||||
$this->project->batchAssoc('IDF_Tag', $tagids);
|
||||
|
||||
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');
|
||||
$conf = $this->project->getConf();
|
||||
$keys = array('mtn_master_branch', 'external_project_url');
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $this->cleaned_data)) {
|
||||
if (!empty($this->cleaned_data[$key])) {
|
||||
$this->project->getConf()->setVal($key, $this->cleaned_data[$key]);
|
||||
$conf->setVal($key, $this->cleaned_data[$key]);
|
||||
}
|
||||
else {
|
||||
$conf->delVal($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ class IDF_Form_Admin_UserUpdate extends Pluf_Form
|
||||
'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 them.'),
|
||||
'help_text' => __('If you give staff rights to a user, you really need to trust him.'),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -214,7 +214,7 @@ class IDF_Form_IssueCreate extends Pluf_Form
|
||||
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);
|
||||
$this->errors['label'.$i][] = sprintf(__('You cannot provide more than one label from the %s class to an issue.'), $class);
|
||||
throw new Pluf_Form_Invalid(__('You provided an invalid label.'));
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ duplicates, is duplicated by';
|
||||
|
||||
$this->fields['labels_issue_one_max'] = new Pluf_Form_Field_Varchar(
|
||||
array('required' => false,
|
||||
'label' => __('Each issue may have at most one label with each of these classes'),
|
||||
'label' => __('Each issue may have at most one label with each of these classes.'),
|
||||
'initial' => self::init_one_max,
|
||||
'widget_attrs' => array('size' => 60),
|
||||
));
|
||||
@ -139,7 +139,7 @@ duplicates, is duplicated by';
|
||||
array('required' => true,
|
||||
'label' => __('Issue relations'),
|
||||
'initial' => self::init_relations,
|
||||
'help_text' => __('You can define bidirectional relations like "is related to" or "blocks, is blocked by".'),
|
||||
'help_text' => __('You can define bidirectional relations like "is related to" or "blocks, is blocked by". For standard relations pre-configured translations exist, new relations should however be defined in a language that is understood by all project members.'),
|
||||
'widget_attrs' => array('rows' => 7,
|
||||
'cols' => 75),
|
||||
'widget' => 'Pluf_Form_Widget_TextareaInput',
|
||||
|
@ -32,6 +32,7 @@ class IDF_Form_ProjectConf extends Pluf_Form
|
||||
public function initFields($extra=array())
|
||||
{
|
||||
$this->project = $extra['project'];
|
||||
$conf = $this->project->getConf();
|
||||
|
||||
// Basic part
|
||||
$this->fields['name'] = new Pluf_Form_Field_Varchar(array('required' => true,
|
||||
@ -51,6 +52,32 @@ class IDF_Form_ProjectConf extends Pluf_Form
|
||||
),
|
||||
'widget' => 'Pluf_Form_Widget_TextareaInput',
|
||||
));
|
||||
$this->fields['external_project_url'] = new Pluf_Form_Field_Varchar(array('required' => false,
|
||||
'label' => __('External URL'),
|
||||
'widget_attrs' => array('size' => '68'),
|
||||
'initial' => $conf->getVal('external_project_url'),
|
||||
));
|
||||
|
||||
$tags = $this->project->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,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
// Logo part
|
||||
$upload_path = Pluf::f('upload_path', false);
|
||||
@ -67,6 +94,7 @@ class IDF_Form_ProjectConf extends Pluf_Form
|
||||
'move_function_params' =>
|
||||
array('upload_path' => $upload_path,
|
||||
'upload_path_create' => true,
|
||||
'upload_overwrite' => true,
|
||||
'file_name' => $filename,
|
||||
)
|
||||
));
|
||||
@ -118,20 +146,63 @@ class IDF_Form_ProjectConf extends Pluf_Form
|
||||
return $this->cleaned_data['logo'];
|
||||
}
|
||||
|
||||
public function clean_external_project_url()
|
||||
{
|
||||
return self::checkWebURL($this->cleaned_data['external_project_url']);
|
||||
}
|
||||
|
||||
public static function checkWebURL($url)
|
||||
{
|
||||
$url = trim($url);
|
||||
if (empty($url)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$parsed = parse_url($url);
|
||||
if ($parsed === false || !array_key_exists('scheme', $parsed) ||
|
||||
($parsed['scheme'] != 'http' && $parsed['scheme'] != 'https')) {
|
||||
throw new Pluf_Form_Invalid(__('The entered URL is invalid. Only http and https URLs are allowed.'));
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
public function save($commit=true)
|
||||
{
|
||||
$conf = $this->project->getConf();
|
||||
// Add a tag for each label
|
||||
$tagids = 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::addGlobal($name, $class);
|
||||
$tagids[] = $tag->id;
|
||||
}
|
||||
}
|
||||
|
||||
// Basic part
|
||||
$this->project->name = $this->cleaned_data['name'];
|
||||
$this->project->shortdesc = $this->cleaned_data['shortdesc'];
|
||||
$this->project->description = $this->cleaned_data['description'];
|
||||
$this->project->batchAssoc('IDF_Tag', $tagids);
|
||||
$this->project->update();
|
||||
|
||||
// Logo part
|
||||
if ($this->cleaned_data['logo'] !== "") {
|
||||
$conf = $this->project->getConf();
|
||||
if (!empty($this->cleaned_data['logo'])) {
|
||||
$conf->setVal('logo', $this->cleaned_data['logo']);
|
||||
}
|
||||
if (!empty($this->cleaned_data['external_project_url'])) {
|
||||
$conf->setVal('external_project_url', $this->cleaned_data['external_project_url']);
|
||||
}
|
||||
else {
|
||||
$conf->delVal('external_project_url');
|
||||
}
|
||||
|
||||
if ($this->cleaned_data['logo_remove'] === true) {
|
||||
@unlink(Pluf::f('upload_path') . '/' . $this->project->shortname . $conf->getVal('logo'));
|
||||
$conf->delVal('logo');
|
||||
|
@ -94,7 +94,7 @@ class IDF_Form_Register extends Pluf_Form
|
||||
{
|
||||
$this->cleaned_data['email'] = mb_strtolower(trim($this->cleaned_data['email']));
|
||||
if (Pluf::factory('IDF_EmailAddress')->get_user_for_email_address($this->cleaned_data['email']) != null) {
|
||||
throw new Pluf_Form_Invalid(sprintf(__('The email "%s" is already used. If you need to, click on the help link to recover your password.'), $this->cleaned_data['email']));
|
||||
throw new Pluf_Form_Invalid(sprintf(__('The email "%1$s" is already used. If you need to, you can <a href="%2$s">recover your password</a>.'), $this->cleaned_data['email'], Pluf_HTTP_URL_urlForView('IDF_Views::passwordRecoveryAsk')));
|
||||
}
|
||||
return $this->cleaned_data['email'];
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ class IDF_Form_RegisterConfirmation extends Pluf_Form
|
||||
throw new Pluf_Form_Invalid($error);
|
||||
}
|
||||
if ($users[0]->active) {
|
||||
throw new Pluf_Form_Invalid(__('This account has already been confirmed. Maybe should you try to recover your password using the help link.'));
|
||||
throw new Pluf_Form_Invalid(sprintf(__('This account has already been confirmed. Maybe should you try to <a href="%s">recover your password</a>.'), Pluf_HTTP_URL_urlForView('IDF_Views::passwordRecoveryAsk')));
|
||||
}
|
||||
$this->_user_id = $email_id[1];
|
||||
return $this->cleaned_data['key'];
|
||||
|
@ -49,13 +49,10 @@ class IDF_Form_SourceConf extends Pluf_Form
|
||||
'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),
|
||||
));
|
||||
|
||||
|
@ -57,21 +57,45 @@ class IDF_Form_TabsConf extends Pluf_Form
|
||||
'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(
|
||||
|
||||
$sections = array(
|
||||
'downloads_notification',
|
||||
'review_notification',
|
||||
'wiki_notification',
|
||||
'source_notification',
|
||||
'issues_notification',
|
||||
);
|
||||
|
||||
foreach ($sections as $section) {
|
||||
$this->fields[$section.'_owners_enabled'] = new Pluf_Form_Field_Boolean(
|
||||
array('required' => false,
|
||||
'label' => $key,
|
||||
'initial' => $this->conf->getVal($key, ''),
|
||||
'widget_attrs' => array('size' => 40),
|
||||
'label' => __('Project owners'),
|
||||
'initial' => $this->conf->getVal($section.'_owners_enabled', false),
|
||||
'widget' => 'Pluf_Form_Widget_CheckboxInput',
|
||||
));
|
||||
$this->fields[$section.'_members_enabled'] = new Pluf_Form_Field_Boolean(
|
||||
array('required' => false,
|
||||
'label' => __('Project members'),
|
||||
'initial' => $this->conf->getVal($section.'_members_enabled', false),
|
||||
'widget' => 'Pluf_Form_Widget_CheckboxInput',
|
||||
));
|
||||
$this->fields[$section.'_email_enabled'] = new Pluf_Form_Field_Boolean(
|
||||
array('required' => false,
|
||||
'label' => __('Others'),
|
||||
'initial' => $this->conf->getVal($section.'_email_enabled', false),
|
||||
'widget' => 'Pluf_Form_Widget_CheckboxInput',
|
||||
));
|
||||
if ($this->conf->getVal($section.'_email_enabled', false)) {
|
||||
$attrs['readonly'] = 'readonly';
|
||||
}
|
||||
$this->fields[$section.'_email'] = new IDF_Form_Field_EmailList(
|
||||
array('required' => false,
|
||||
'label' => null,
|
||||
'initial' => $this->conf->getVal($section.'_email', ''),
|
||||
'widget_attrs' => array('size' => 20),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
$this->fields['private_project'] = new Pluf_Form_Field_Boolean(
|
||||
array('required' => false,
|
||||
'label' => __('Private project'),
|
||||
|
@ -106,7 +106,7 @@ class IDF_Form_UpdateUpload extends Pluf_Form
|
||||
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);
|
||||
$this->errors['label'.$i][] = sprintf(__('You cannot provide more than one label from the %s class to an issue.'), $class);
|
||||
throw new Pluf_Form_Invalid(__('You provided an invalid label.'));
|
||||
}
|
||||
}
|
||||
@ -146,6 +146,9 @@ class IDF_Form_UpdateUpload extends Pluf_Form
|
||||
$this->upload->modif_dtime = gmdate('Y-m-d H:i:s');
|
||||
$this->upload->update();
|
||||
$this->upload->batchAssoc('IDF_Tag', $tags);
|
||||
|
||||
// Send the notification
|
||||
$this->upload->notify($this->project->getConf(), false);
|
||||
/**
|
||||
* [signal]
|
||||
*
|
||||
|
@ -79,6 +79,7 @@ class IDF_Form_Upload extends Pluf_Form
|
||||
|
||||
public function clean_file()
|
||||
{
|
||||
// FIXME: we do the same in IDF_Form_WikiResourceCreate and a couple of other places as well
|
||||
$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'])) {
|
||||
@ -116,7 +117,7 @@ class IDF_Form_Upload extends Pluf_Form
|
||||
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);
|
||||
$this->errors['label'.$i][] = sprintf(__('You cannot provide more than one label from the %s class to a download.'), $class);
|
||||
throw new Pluf_Form_Invalid(__('You provided an invalid label.'));
|
||||
}
|
||||
}
|
||||
|
227
src/IDF/Form/UploadArchive.php
Normal file
227
src/IDF/Form/UploadArchive.php
Normal file
@ -0,0 +1,227 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Upload and process an archive file.
|
||||
*
|
||||
*/
|
||||
class IDF_Form_UploadArchive extends Pluf_Form
|
||||
{
|
||||
public $user = null;
|
||||
public $project = null;
|
||||
private $archiveHelper = null;
|
||||
|
||||
public function initFields($extra=array())
|
||||
{
|
||||
$this->user = $extra['user'];
|
||||
$this->project = $extra['project'];
|
||||
|
||||
$this->fields['archive'] = new Pluf_Form_Field_File(
|
||||
array('required' => true,
|
||||
'label' => __('Archive file'),
|
||||
'initial' => '',
|
||||
'max_size' => Pluf::f('max_upload_archive_size', 20971520),
|
||||
'move_function_params' => array(
|
||||
'upload_path' => Pluf::f('upload_path').'/'.$this->project->shortname.'/archives',
|
||||
'upload_path_create' => true,
|
||||
'upload_overwrite' => true,
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
public function clean_archive()
|
||||
{
|
||||
$this->archiveHelper = new IDF_Form_UploadArchiveHelper(
|
||||
Pluf::f('upload_path').'/'.$this->project->shortname.'/archives/'.$this->cleaned_data['archive']);
|
||||
|
||||
// basic archive validation
|
||||
$this->archiveHelper->validate();
|
||||
|
||||
// extension validation
|
||||
$fileNames = $this->archiveHelper->getEntryNames();
|
||||
foreach ($fileNames as $fileName) {
|
||||
$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', $fileName)) {
|
||||
@unlink(Pluf::f('upload_path').'/'.$this->project->shortname.'/files/'.$this->cleaned_data['archive']);
|
||||
throw new Pluf_Form_Invalid(sprintf(__('For security reasons, you cannot upload a file (%s) with this extension.'), $fileName));
|
||||
}
|
||||
}
|
||||
|
||||
// label and file name validation
|
||||
$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));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($fileNames as $fileName) {
|
||||
$meta = $this->archiveHelper->getMetaData($fileName);
|
||||
$count = array();
|
||||
foreach ($meta['labels'] as $label) {
|
||||
$label = trim($label);
|
||||
if (strpos($label, ':') !== false) {
|
||||
list($class, $name) = explode(':', $label, 2);
|
||||
list($class, $name) = array(mb_strtolower(trim($class)),
|
||||
trim($name));
|
||||
} else {
|
||||
$class = 'other';
|
||||
$name = $label;
|
||||
}
|
||||
if (!isset($count[$class])) $count[$class] = 1;
|
||||
else $count[$class] += 1;
|
||||
if (in_array($class, $onemax) and $count[$class] > 1) {
|
||||
throw new Pluf_Form_Invalid(
|
||||
sprintf(__('You cannot provide more than label from the %1$s class to a download (%2$s).'), $class, $name)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$sql = new Pluf_SQL('file=%s AND project=%s', array($fileName, $this->project->id));
|
||||
$upload = Pluf::factory('IDF_Upload')->getOne(array('filter' => $sql->gen()));
|
||||
|
||||
$meta = $this->archiveHelper->getMetaData($fileName);
|
||||
if ($upload != null && $meta['replaces'] !== $fileName) {
|
||||
throw new Pluf_Form_Invalid(
|
||||
sprintf(__('A file with the name "%s" has already been uploaded and is not marked to be replaced.'), $fileName));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->cleaned_data['archive'];
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have uploaded a file, but the form failed remove it.
|
||||
*
|
||||
*/
|
||||
function failed()
|
||||
{
|
||||
if (!empty($this->cleaned_data['archive'])
|
||||
and file_exists(Pluf::f('upload_path').'/'.$this->project->shortname.'/archives/'.$this->cleaned_data['archive'])) {
|
||||
@unlink(Pluf::f('upload_path').'/'.$this->project->shortname.'/archives/'.$this->cleaned_data['archive']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function save($commit=true)
|
||||
{
|
||||
if (!$this->isValid()) {
|
||||
throw new Exception(__('Cannot save the model from an invalid form.'));
|
||||
}
|
||||
|
||||
$uploadDir = Pluf::f('upload_path').'/'.$this->project->shortname.'/files/';
|
||||
$fileNames = $this->archiveHelper->getEntryNames();
|
||||
|
||||
foreach ($fileNames as $fileName) {
|
||||
$meta = $this->archiveHelper->getMetaData($fileName);
|
||||
|
||||
// add a tag for each label
|
||||
$tags = array();
|
||||
foreach ($meta['labels'] as $label) {
|
||||
$label = trim($label);
|
||||
if (strlen($label) > 0) {
|
||||
if (strpos($label, ':') !== false) {
|
||||
list($class, $name) = explode(':', $label, 2);
|
||||
list($class, $name) = array(trim($class), trim($name));
|
||||
} else {
|
||||
$class = 'Other';
|
||||
$name = $label;
|
||||
}
|
||||
$tags[] = IDF_Tag::add($name, $this->project, $class);
|
||||
}
|
||||
}
|
||||
|
||||
// process a possible replacement
|
||||
if (!empty($meta['replaces'])) {
|
||||
$sql = new Pluf_SQL('file=%s AND project=%s', array($meta['replaces'], $this->project->id));
|
||||
$oldUpload = Pluf::factory('IDF_Upload')->getOne(array('filter' => $sql->gen()));
|
||||
|
||||
if ($oldUpload) {
|
||||
if ($meta['replaces'] === $fileName) {
|
||||
$oldUpload->delete();
|
||||
} else {
|
||||
$tags = $this->project->getTagsFromConfig('labels_download_predefined',
|
||||
IDF_Form_UploadConf::init_predefined);
|
||||
// the deprecate tag is - by definition - always the last one
|
||||
$deprecatedTag = array_pop($tags);
|
||||
$oldUpload->setAssoc($deprecatedTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extract the file
|
||||
$this->archiveHelper->extract($fileName, $uploadDir);
|
||||
|
||||
// create the upload
|
||||
$upload = new IDF_Upload();
|
||||
$upload->project = $this->project;
|
||||
$upload->submitter = $this->user;
|
||||
$upload->summary = trim($meta['summary']);
|
||||
$upload->changelog = trim($meta['description']);
|
||||
$upload->file = $fileName;
|
||||
$upload->filesize = filesize($uploadDir.$fileName);
|
||||
$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);
|
||||
}
|
||||
|
||||
// finally unlink the uploaded archive
|
||||
@unlink(Pluf::f('upload_path').'/'.$this->project->shortname.'/archives/'.$this->cleaned_data['archive']);
|
||||
}
|
||||
}
|
||||
|
158
src/IDF/Form/UploadArchiveHelper.php
Normal file
158
src/IDF/Form/UploadArchiveHelper.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
class IDF_Form_UploadArchiveHelper
|
||||
{
|
||||
private $file = null;
|
||||
private $entries = array();
|
||||
|
||||
public function __construct($file)
|
||||
{
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the archive; throws a invalid form exception in case the
|
||||
* archive contains invalid data or cannot be read.
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!file_exists($this->file)) {
|
||||
throw new Pluf_Form_Invalid(__('The archive does not exist.'));
|
||||
}
|
||||
|
||||
$za = new ZipArchive();
|
||||
$res = $za->open($this->file);
|
||||
if ($res !== true) {
|
||||
throw new Pluf_Form_Invalid(
|
||||
sprintf(__('The archive could not be read (code %d).'), $res));
|
||||
}
|
||||
|
||||
$manifest = $za->getFromName('manifest.xml');
|
||||
if ($manifest === false) {
|
||||
throw new Pluf_Form_Invalid(__('The archive does not contain a manifest.xml.'));
|
||||
}
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
$xml = @simplexml_load_string($manifest);
|
||||
if ($xml === false) {
|
||||
$error = libxml_get_last_error();
|
||||
throw new Pluf_Form_Invalid(
|
||||
sprintf(__('The archive\'s manifest is invalid: %s'), $error->message));
|
||||
}
|
||||
|
||||
foreach (@$xml->file as $idx => $file)
|
||||
{
|
||||
$entry = array(
|
||||
'name' => (string)@$file->name,
|
||||
'summary' => (string)@$file->summary,
|
||||
'description' => (string)@$file->description,
|
||||
'replaces' => (string)@$file->replaces,
|
||||
'labels' => array(),
|
||||
'stream' => null
|
||||
);
|
||||
|
||||
if (empty($entry['name'])) {
|
||||
throw new Pluf_Form_Invalid(
|
||||
sprintf(__('The entry %d in the manifest is missing a file name.'), $idx));
|
||||
}
|
||||
|
||||
if (empty($entry['summary'])) {
|
||||
throw new Pluf_Form_Invalid(
|
||||
sprintf(__('The entry %d in the manifest is missing a summary.'), $idx));
|
||||
}
|
||||
|
||||
if ($entry['name'] === 'manifest.xml') {
|
||||
throw new Pluf_Form_Invalid(__('The manifest must not reference itself.'));
|
||||
}
|
||||
|
||||
if ($za->locateName($entry['name']) === false) {
|
||||
throw new Pluf_Form_Invalid(
|
||||
sprintf(__('The entry %s in the manifest does not exist in the archive.'), $entry['name']));
|
||||
}
|
||||
|
||||
if (in_array($entry['name'], $this->entries)) {
|
||||
throw new Pluf_Form_Invalid(
|
||||
sprintf(__('The entry %s in the manifest is referenced more than once.'), $entry['name']));
|
||||
}
|
||||
|
||||
if ($file->labels) {
|
||||
foreach (@$file->labels->label as $label) {
|
||||
$entry['labels'][] = (string)$label;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: remove this once we allow more than six labels everywhere
|
||||
if (count($entry['labels']) > 6) {
|
||||
throw new Pluf_Form_Invalid(
|
||||
sprintf(__('The entry %s in the manifest has more than the six allowed labels set.'), $entry['name']));
|
||||
}
|
||||
|
||||
$this->entries[$entry['name']] = $entry;
|
||||
}
|
||||
|
||||
$za->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all entry names
|
||||
*
|
||||
* @return array of string
|
||||
*/
|
||||
public function getEntryNames()
|
||||
{
|
||||
return array_keys($this->entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns meta data for the given entry
|
||||
*
|
||||
* @param string $name
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getMetaData($name)
|
||||
{
|
||||
if (!array_key_exists($name, $this->entries)) {
|
||||
throw new Exception('unknown file ' . $name);
|
||||
}
|
||||
return $this->entries[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the file entry $name at $path
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $path
|
||||
* @throws Exception
|
||||
*/
|
||||
public function extract($name, $path)
|
||||
{
|
||||
if (!array_key_exists($name, $this->entries)) {
|
||||
throw new Exception('unknown file ' . $name);
|
||||
}
|
||||
$za = new ZipArchive();
|
||||
$za->open($this->file);
|
||||
$za->extractTo($path, $name);
|
||||
$za->close();
|
||||
}
|
||||
}
|
@ -64,6 +64,14 @@ Deprecated = Most users should NOT download this';
|
||||
'widget_attrs' => array('size' => 60),
|
||||
));
|
||||
|
||||
$this->conf = $extra['conf'];
|
||||
$this->fields['upload_webhook_url'] = new Pluf_Form_Field_Url(
|
||||
array('required' => false,
|
||||
'label' => __('Webhook URL'),
|
||||
'initial' => $this->conf->getVal('upload_webhook_url', ''),
|
||||
'widget_attrs' => array('size' => 60),
|
||||
));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ class IDF_Form_UserAccount extends Pluf_Form
|
||||
|
||||
$this->fields['email'] = new Pluf_Form_Field_Email(
|
||||
array('required' => true,
|
||||
'label' => __('Your mail'),
|
||||
'label' => __('Your email'),
|
||||
'initial' => $this->user->email,
|
||||
'help_text' => __('If you change your email address, an email will be sent to the new address to confirm it.'),
|
||||
));
|
||||
@ -168,9 +168,9 @@ class IDF_Form_UserAccount extends Pluf_Form
|
||||
|
||||
$this->fields['secondary_mail'] = new Pluf_Form_Field_Email(
|
||||
array('required' => false,
|
||||
'label' => __('Add a secondary mail address'),
|
||||
'label' => __('Add a secondary email address'),
|
||||
'initial' => '',
|
||||
'help_text' => __('You will get a mail to confirm that you own the address you specify.'),
|
||||
'help_text' => __('You will get an email to confirm that you own the address you specify.'),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
* This create a new page and the corresponding revision.
|
||||
*
|
||||
*/
|
||||
class IDF_Form_WikiCreate extends Pluf_Form
|
||||
class IDF_Form_WikiPageCreate extends Pluf_Form
|
||||
{
|
||||
public $user = null;
|
||||
public $project = null;
|
||||
@ -109,7 +109,7 @@ Add your content here. Format your content with:
|
||||
}
|
||||
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||
array($this->project->id, $title));
|
||||
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
|
||||
$pages = Pluf::factory('IDF_Wiki_Page')->getList(array('filter'=>$sql->gen()));
|
||||
if ($pages->count() > 0) {
|
||||
throw new Pluf_Form_Invalid(__('A page with this title already exists.'));
|
||||
}
|
||||
@ -147,7 +147,7 @@ Add your content here. Format your content with:
|
||||
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);
|
||||
$this->errors['label'.$i][] = sprintf(__('You cannot provide more than one label from the %s class to a page.'), $class);
|
||||
throw new Pluf_Form_Invalid(__('You provided an invalid label.'));
|
||||
}
|
||||
}
|
||||
@ -183,7 +183,7 @@ Add your content here. Format your content with:
|
||||
}
|
||||
}
|
||||
// Create the page
|
||||
$page = new IDF_WikiPage();
|
||||
$page = new IDF_Wiki_Page();
|
||||
$page->project = $this->project;
|
||||
$page->submitter = $this->user;
|
||||
$page->summary = trim($this->cleaned_data['summary']);
|
||||
@ -193,7 +193,7 @@ Add your content here. Format your content with:
|
||||
$page->setAssoc($tag);
|
||||
}
|
||||
// add the first revision
|
||||
$rev = new IDF_WikiRevision();
|
||||
$rev = new IDF_Wiki_PageRevision();
|
||||
$rev->wikipage = $page;
|
||||
$rev->content = $this->cleaned_data['content'];
|
||||
$rev->submitter = $this->user;
|
@ -27,7 +27,7 @@
|
||||
* This is a hard delete of the page and the revisions.
|
||||
*
|
||||
*/
|
||||
class IDF_Form_WikiDelete extends Pluf_Form
|
||||
class IDF_Form_WikiPageDelete extends Pluf_Form
|
||||
{
|
||||
protected $page = null;
|
||||
|
@ -27,7 +27,7 @@
|
||||
* This add a corresponding revision.
|
||||
*
|
||||
*/
|
||||
class IDF_Form_WikiUpdate extends Pluf_Form
|
||||
class IDF_Form_WikiPageUpdate extends Pluf_Form
|
||||
{
|
||||
public $user = null;
|
||||
public $project = null;
|
||||
@ -120,7 +120,7 @@ class IDF_Form_WikiUpdate extends Pluf_Form
|
||||
}
|
||||
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||
array($this->project->id, $title));
|
||||
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
|
||||
$pages = Pluf::factory('IDF_Wiki_Page')->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.'));
|
||||
}
|
||||
@ -158,7 +158,7 @@ class IDF_Form_WikiUpdate extends Pluf_Form
|
||||
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);
|
||||
$this->errors['label'.$i][] = sprintf(__('You cannot provide more than one label from the %s class to a page.'), $class);
|
||||
throw new Pluf_Form_Invalid(__('You provided an invalid label.'));
|
||||
}
|
||||
}
|
||||
@ -229,7 +229,7 @@ class IDF_Form_WikiUpdate extends Pluf_Form
|
||||
}
|
||||
$this->page->update();
|
||||
// add the new revision
|
||||
$rev = new IDF_WikiRevision();
|
||||
$rev = new IDF_Wiki_PageRevision();
|
||||
$rev->wikipage = $this->page;
|
||||
$rev->content = $this->cleaned_data['content'];
|
||||
$rev->submitter = $this->user;
|
169
src/IDF/Form/WikiResourceCreate.php
Normal file
169
src/IDF/Form/WikiResourceCreate.php
Normal file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Create a new resource.
|
||||
*
|
||||
* This create a new resource and the corresponding revision.
|
||||
*
|
||||
*/
|
||||
class IDF_Form_WikiResourceCreate extends Pluf_Form
|
||||
{
|
||||
public $user = null;
|
||||
public $project = null;
|
||||
public $show_full = false;
|
||||
|
||||
public function initFields($extra=array())
|
||||
{
|
||||
$this->project = $extra['project'];
|
||||
$this->user = $extra['user'];
|
||||
$initname = (!empty($extra['name'])) ? $extra['name'] : __('ResourceName');
|
||||
|
||||
$this->fields['title'] = new Pluf_Form_Field_Varchar(
|
||||
array('required' => true,
|
||||
'label' => __('Resource title'),
|
||||
'initial' => $initname,
|
||||
'widget_attrs' => array(
|
||||
'maxlength' => 200,
|
||||
'size' => 67,
|
||||
),
|
||||
'help_text' => __('The resource 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 resources.'),
|
||||
'initial' => '',
|
||||
'widget_attrs' => array(
|
||||
'maxlength' => 200,
|
||||
'size' => 67,
|
||||
),
|
||||
));
|
||||
|
||||
$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' => $this->getTempUploadPath(),
|
||||
'upload_path_create' => true,
|
||||
'upload_overwrite' => true),
|
||||
));
|
||||
}
|
||||
|
||||
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));
|
||||
$resources = Pluf::factory('IDF_Wiki_Resource')->getList(array('filter'=>$sql->gen()));
|
||||
if ($resources->count() > 0) {
|
||||
throw new Pluf_Form_Invalid(__('A resource with this title already exists.'));
|
||||
}
|
||||
return $title;
|
||||
}
|
||||
|
||||
public function clean_file()
|
||||
{
|
||||
// FIXME: we do the same in IDF_Form_Upload and a couple of other places as well
|
||||
$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($this->getTempUploadPath().$this->cleaned_data['file']);
|
||||
throw new Pluf_Form_Invalid(__('For security reasons, you cannot upload a file with this extension.'));
|
||||
}
|
||||
return $this->cleaned_data['file'];
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have uploaded a file, but the form failed remove it.
|
||||
*
|
||||
*/
|
||||
function failed()
|
||||
{
|
||||
if (!empty($this->cleaned_data['file'])
|
||||
and file_exists($this->getTempUploadPath().$this->cleaned_data['file'])) {
|
||||
@unlink($this->getTempUploadPath().$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.'));
|
||||
}
|
||||
|
||||
$tempFile = $this->getTempUploadPath().$this->cleaned_data['file'];
|
||||
list($mimeType, , $extension) = IDF_FileUtil::getMimeType($tempFile);
|
||||
|
||||
// create the resource
|
||||
$resource = new IDF_Wiki_Resource();
|
||||
$resource->project = $this->project;
|
||||
$resource->submitter = $this->user;
|
||||
$resource->summary = trim($this->cleaned_data['summary']);
|
||||
$resource->title = trim($this->cleaned_data['title']);
|
||||
$resource->mime_type = $mimeType;
|
||||
$resource->create();
|
||||
|
||||
// add the first revision
|
||||
$rev = new IDF_Wiki_ResourceRevision();
|
||||
$rev->wikiresource = $resource;
|
||||
$rev->submitter = $this->user;
|
||||
$rev->summary = __('Initial resource creation');
|
||||
$rev->filesize = filesize($tempFile);
|
||||
$rev->fileext = $extension;
|
||||
$rev->create();
|
||||
|
||||
$finalFile = $rev->getFilePath();
|
||||
if (!@mkdir(dirname($finalFile), 0755, true)) {
|
||||
@unlink($tempFile);
|
||||
$rev->delete();
|
||||
$resource->delete();
|
||||
throw new Exception('could not create final resource path');
|
||||
}
|
||||
|
||||
if (!@rename($tempFile, $finalFile)) {
|
||||
@unlink($tempFile);
|
||||
$rev->delete();
|
||||
$resource->delete();
|
||||
throw new Exception('could not move resource to final location');
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
private function getTempUploadPath()
|
||||
{
|
||||
return Pluf::f('upload_path').'/'.$this->project->shortname.'/wiki/temp/';
|
||||
}
|
||||
}
|
64
src/IDF/Form/WikiResourceDelete.php
Normal file
64
src/IDF/Form/WikiResourceDelete.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Delete a documentation page.
|
||||
*
|
||||
* This is a hard delete of the page and the revisions.
|
||||
*
|
||||
*/
|
||||
class IDF_Form_WikiResourceDelete extends Pluf_Form
|
||||
{
|
||||
protected $resource = null;
|
||||
|
||||
public function initFields($extra=array())
|
||||
{
|
||||
$this->resource = $extra['resource'];
|
||||
$this->fields['confirm'] = new Pluf_Form_Field_Boolean(
|
||||
array('required' => true,
|
||||
'label' => __('Yes, I understand that the resource 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->resource->delete();
|
||||
return true;
|
||||
}
|
||||
}
|
161
src/IDF/Form/WikiResourceUpdate.php
Normal file
161
src/IDF/Form/WikiResourceUpdate.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Update a documentation page.
|
||||
*
|
||||
* This add a corresponding revision.
|
||||
*
|
||||
*/
|
||||
class IDF_Form_WikiResourceUpdate extends Pluf_Form
|
||||
{
|
||||
public $user = null;
|
||||
public $project = null;
|
||||
public $page = null;
|
||||
public $show_full = false;
|
||||
|
||||
|
||||
public function initFields($extra=array())
|
||||
{
|
||||
$this->resource = $extra['resource'];
|
||||
$this->user = $extra['user'];
|
||||
$this->project = $extra['project'];
|
||||
|
||||
$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 resources.'),
|
||||
'initial' => $this->resource->summary,
|
||||
'widget_attrs' => array(
|
||||
'maxlength' => 200,
|
||||
'size' => 67,
|
||||
),
|
||||
));
|
||||
$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' => $this->getTempUploadPath(),
|
||||
'upload_path_create' => true,
|
||||
'upload_overwrite' => true),
|
||||
));
|
||||
|
||||
$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,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function clean_file()
|
||||
{
|
||||
// FIXME: we do the same in IDF_Form_Upload and a couple of other places as well
|
||||
$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($this->getTempUploadPath().$this->cleaned_data['file']);
|
||||
throw new Pluf_Form_Invalid(__('For security reasons, you cannot upload a file with this extension.'));
|
||||
}
|
||||
|
||||
list($mimeType, , $extension) = IDF_FileUtil::getMimeType($this->getTempUploadPath().$this->cleaned_data['file']);
|
||||
if ($this->resource->mime_type != $mimeType) {
|
||||
throw new Pluf_Form_Invalid(sprintf(
|
||||
__('The mime type of the uploaded file "%1$s" does not match the mime type of this resource "%2$s"'),
|
||||
$mimeType, $this->resource->mime_type
|
||||
));
|
||||
}
|
||||
$this->cleaned_data['fileext'] = $extension;
|
||||
|
||||
if (md5_file($this->getTempUploadPath().$this->cleaned_data['file']) ===
|
||||
md5_file($this->resource->get_current_revision()->getFilePath())) {
|
||||
throw new Pluf_Form_Invalid(__('The current version of the resource and the uploaded file are equal.'));
|
||||
}
|
||||
return $this->cleaned_data['file'];
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have uploaded a file, but the form failed remove it.
|
||||
*
|
||||
*/
|
||||
function failed()
|
||||
{
|
||||
if (!empty($this->cleaned_data['file'])
|
||||
and file_exists($this->getTempUploadPath().$this->cleaned_data['file'])) {
|
||||
@unlink($this->getTempUploadPath().$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.'));
|
||||
}
|
||||
|
||||
$tempFile = $this->getTempUploadPath().$this->cleaned_data['file'];
|
||||
|
||||
$this->resource->summary = trim($this->cleaned_data['summary']);
|
||||
$this->resource->update();
|
||||
|
||||
// add the new revision
|
||||
$rev = new IDF_Wiki_ResourceRevision();
|
||||
$rev->wikiresource = $this->resource;
|
||||
$rev->submitter = $this->user;
|
||||
$rev->summary = $this->cleaned_data['comment'];
|
||||
$rev->filesize = filesize($tempFile);
|
||||
$rev->fileext = $this->cleaned_data['fileext'];
|
||||
$rev->create();
|
||||
|
||||
$finalFile = $rev->getFilePath();
|
||||
if (!is_dir(dirname($finalFile))) {
|
||||
@unlink($tempFile);
|
||||
$rev->delete();
|
||||
throw new Exception('resource path does not exist');
|
||||
}
|
||||
|
||||
if (!@rename($tempFile, $finalFile)) {
|
||||
@unlink($tempFile);
|
||||
$rev->delete();
|
||||
throw new Exception('could not move resource to final location');
|
||||
}
|
||||
|
||||
return $this->resource;
|
||||
}
|
||||
|
||||
private function getTempUploadPath()
|
||||
{
|
||||
return Pluf::f('upload_path').'/'.$this->project->shortname.'/wiki/temp/';
|
||||
}
|
||||
}
|
@ -211,7 +211,7 @@ class IDF_Issue extends Pluf_Model
|
||||
$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 %d</a>, by %s'), $url, $ic, $this->id, $user).'</div></td></tr>';
|
||||
<div class="helptext right">'.sprintf(__('Creation of <a href="%1$s" class="%2$s">issue %3$d</a>, by %4$s'), $url, $ic, $this->id, $user).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
@ -221,7 +221,7 @@ class IDF_Issue extends Pluf_Model
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
|
||||
array($request->project->shortname,
|
||||
$this->id));
|
||||
$title = sprintf(__('%s: Issue %d created - %s'),
|
||||
$title = sprintf(__('%1$s: Issue %2$d created - %3$s'),
|
||||
$request->project->name,
|
||||
$this->id, $this->summary);
|
||||
$cts = $this->get_comments_list(array('order' => 'id ASC',
|
||||
@ -256,91 +256,72 @@ class IDF_Issue extends Pluf_Model
|
||||
*/
|
||||
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]);
|
||||
}
|
||||
}
|
||||
$project = $this->get_project();
|
||||
$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 {
|
||||
|
||||
$from_email = Pluf::f('from_email');
|
||||
$comments = $this->get_comments_list(array('order' => 'id DESC'));
|
||||
$email_sender = '';
|
||||
if (isset($comments[0])) {
|
||||
$email_sender = $comments[0]->get_submitter()->email;
|
||||
$messageId = '<'.md5('issue'.$this->id.md5(Pluf::f('secret_key'))).'@'.Pluf::f('mail_host', 'localhost').'>';
|
||||
$recipients = $project->getNotificationRecipientsForTab('issues');
|
||||
|
||||
// the submitter (might be skipped later on if he is the one who also
|
||||
// submitted the last comment)
|
||||
if (!array_key_exists($this->get_submitter()->email, $recipients)) {
|
||||
$recipients[$this->get_submitter()->email] = $this->get_submitter()->language;
|
||||
}
|
||||
|
||||
// the owner of the issue, if we have one
|
||||
$owner = $this->get_owner();
|
||||
if (null != $owner && !array_key_exists($owner->email, $recipients)) {
|
||||
$recipients[$owner->email] = $owner->language;
|
||||
}
|
||||
|
||||
// additional users who starred the issue
|
||||
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;
|
||||
if (array_key_exists($interested->email, $recipients))
|
||||
continue;
|
||||
$recipients[$interested->email] = $interested->language;
|
||||
}
|
||||
|
||||
foreach ($recipients as $address => $language) {
|
||||
|
||||
// do not notify the creator of the last comment,
|
||||
// i.e. the user who triggered this notification
|
||||
if ($comments[0]->get_submitter()->email === $address) {
|
||||
continue;
|
||||
}
|
||||
$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(
|
||||
|
||||
Pluf_Translation::loadSetLocale($language);
|
||||
|
||||
$context = new Pluf_Template_Context(array(
|
||||
'issue' => $this,
|
||||
'owns_issue' => $owner !== null && $owner->email === $address,
|
||||
// the initial comment for create, the last for update
|
||||
'comment' => $comments[0],
|
||||
'comments' => $comments,
|
||||
'project' => $prj,
|
||||
'project' => $project,
|
||||
'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
|
||||
|
||||
$tplfile = 'idf/issues/issue-created-email.txt';
|
||||
$subject = __('Issue %1$s - %2$s (%3$s)');
|
||||
$headers = array('Message-ID' => $messageId);
|
||||
if (!$create) {
|
||||
$tplfile = 'idf/issues/issue-updated-email.txt';
|
||||
$subject = __('Updated Issue %1$s - %2$s (%3$s)');
|
||||
$headers = array('References' => $messageId);
|
||||
}
|
||||
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));
|
||||
|
||||
$tmpl = new Pluf_Template($tplfile);
|
||||
$text_email = $tmpl->render($context);
|
||||
|
||||
$email = new Pluf_Mail($from_email, $address,
|
||||
sprintf($subject, $this->id, $this->summary, $project->shortname));
|
||||
$email->addTextMessage($text_email);
|
||||
$email->addHeaders($headers);
|
||||
$email->sendMail();
|
||||
}
|
||||
}
|
||||
|
||||
Pluf_Translation::loadSetLocale($current_locale);
|
||||
}
|
||||
}
|
@ -177,7 +177,7 @@ class IDF_IssueComment extends Pluf_Model
|
||||
}
|
||||
$out .= '</td></tr>';
|
||||
$out .= "\n".'<tr class="extra"><td colspan="2">
|
||||
<div class="helptext right">'.sprintf(__('Comment on <a href="%s" class="%s">issue %d</a>, by %s'), $url, $ic, $issue->id, $user).'</div></td></tr>';
|
||||
<div class="helptext right">'.sprintf(__('Comment on <a href="%1$s" class="%2$s">issue %3$d</a>, by %4$s'), $url, $ic, $issue->id, $user).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
@ -188,7 +188,7 @@ class IDF_IssueComment extends Pluf_Model
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
|
||||
array($request->project->shortname,
|
||||
$issue->id));
|
||||
$title = sprintf(__('%s: Comment on issue %d - %s'),
|
||||
$title = sprintf(__('%1$s: Comment on issue %2$d - %3$s'),
|
||||
Pluf_esc($request->project->name),
|
||||
$issue->id, Pluf_esc($issue->summary));
|
||||
$url .= '#ic'.$this->id;
|
||||
|
@ -85,6 +85,7 @@ class IDF_Middleware
|
||||
'issuetext' => 'IDF_Template_IssueComment',
|
||||
'timeline' => 'IDF_Template_TimelineFragment',
|
||||
'markdown' => 'IDF_Template_Markdown',
|
||||
'markdown_forge' => 'IDF_Template_MarkdownForge',
|
||||
'showuser' => 'IDF_Template_ShowUser',
|
||||
'ashowuser' => 'IDF_Template_AssignShowUser',
|
||||
'appversion' => 'IDF_Template_AppVersion',
|
||||
@ -102,6 +103,7 @@ class IDF_Middleware
|
||||
|
||||
function IDF_Middleware_ContextPreProcessor($request)
|
||||
{
|
||||
$forge = IDF_Forge::instance();
|
||||
$c = array();
|
||||
$c['request'] = $request;
|
||||
$c['isAdmin'] = ($request->user->administrator or $request->user->staff);
|
||||
@ -115,6 +117,7 @@ function IDF_Middleware_ContextPreProcessor($request)
|
||||
}
|
||||
$c['usherConfigured'] = Pluf::f("mtn_usher_conf", null) !== null;
|
||||
$c['allProjects'] = IDF_Views::getProjects($request->user);
|
||||
$c['customForgePageEnabled'] = $forge->isCustomForgePageEnabled();
|
||||
return $c;
|
||||
}
|
||||
|
||||
|
74
src/IDF/Migrations/19WikiPageAssocs.php
Normal file
74
src/IDF/Migrations/19WikiPageAssocs.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function IDF_Migrations_19WikiPageAssocs_up($params=null)
|
||||
{
|
||||
$engine = Pluf::f('db_engine');
|
||||
if (!in_array($engine, array('MySQL', 'PostgreSQL'))) {
|
||||
throw new Exception('unsupported engine '.$engine);
|
||||
}
|
||||
|
||||
$db = Pluf::db();
|
||||
$intro = new Pluf_DB_Introspect($db);
|
||||
if (in_array($db->pfx.'idf_tag_idf_wiki_page_assoc', $intro->listTables())) {
|
||||
echo '19 skipping up migration - relation table has correct name already'."\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_tag_idf_wikipage_assoc RENAME TO '.$db->pfx.'idf_tag_idf_wiki_page_assoc');
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_wikipage_pluf_user_assoc RENAME TO '.$db->pfx.'idf_wiki_page_pluf_user_assoc');
|
||||
|
||||
if ($engine === 'PostgreSQL') {
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_tag_idf_wiki_page_assoc RENAME COLUMN idf_wikipage_id TO idf_wiki_page_id');
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_wiki_page_pluf_user_assoc RENAME COLUMN idf_wikipage_id TO idf_wiki_page_id');
|
||||
} else if ($engine === 'MySQL') {
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_tag_idf_wiki_page_assoc CHANGE idf_wikipage_id idf_wiki_page_id MEDIUMINT NOT NULL');
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_wiki_page_pluf_user_assoc CHANGE idf_wikipage_id idf_wiki_page_id MEDIUMINT NOT NULL');
|
||||
}
|
||||
}
|
||||
|
||||
function IDF_Migrations_19WikiPageAssocs_down($params=null)
|
||||
{
|
||||
$engine = Pluf::f('db_engine');
|
||||
if (!in_array($engine, array('MySQL', 'PostgreSQL'))) {
|
||||
throw new Exception('unsupported engine '.$engine);
|
||||
}
|
||||
|
||||
$db = Pluf::db();
|
||||
$intro = new Pluf_DB_Introspect($db);
|
||||
if (in_array($db->pfx.'idf_tag_idf_wikipage_assoc', $intro->listTables())) {
|
||||
echo '19 skipping down migration - relation table has correct name already'."\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_tag_idf_wiki_page_assoc RENAME TO '.$db->pfx.'idf_tag_idf_wikipage_assoc');
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_wiki_page_pluf_user_assoc RENAME TO '.$db->pfx.'idf_wikipage_pluf_user_assoc');
|
||||
|
||||
if ($engine === 'PostgreSQL') {
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_tag_idf_wikipage_assoc RENAME COLUMN idf_wiki_page_id TO idf_wikipage_id');
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_wikipage_pluf_user_assoc RENAME COLUMN idf_wiki_page_id TO idf_wikipage_id');
|
||||
} else if ($engine === 'MySQL') {
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_tag_idf_wikipage_assoc CHANGE idf_wiki_page_id idf_wikipage_id MEDIUMINT NOT NULL');
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_wikipage_pluf_user_assoc CHANGE idf_wiki_page_id idf_wikipage_id MEDIUMINT NOT NULL');
|
||||
}
|
||||
}
|
51
src/IDF/Migrations/20AddWikiResources.php
Normal file
51
src/IDF/Migrations/20AddWikiResources.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Add the new IDF_Wiki_Resource and IDF_Wiki_ResourceRevision models.
|
||||
*
|
||||
*/
|
||||
|
||||
function IDF_Migrations_20AddWikiResources_up($params=null)
|
||||
{
|
||||
$db = Pluf::db();
|
||||
$schema = new Pluf_DB_Schema($db);
|
||||
|
||||
$schema->model = new IDF_Wiki_Resource();
|
||||
$schema->createTables();
|
||||
|
||||
$schema->model = new IDF_Wiki_ResourceRevision();
|
||||
$schema->createTables();
|
||||
}
|
||||
|
||||
function IDF_Migrations_20AddWikiResources_down($params=null)
|
||||
{
|
||||
$db = Pluf::db();
|
||||
$schema = new Pluf_DB_Schema($db);
|
||||
|
||||
$schema->model = new IDF_Wiki_ResourceRevision();
|
||||
$schema->dropTables();
|
||||
|
||||
$schema->model = new IDF_Wiki_Resource();
|
||||
$schema->dropTables();
|
||||
}
|
60
src/IDF/Migrations/21WikiPageRevisionName.php
Normal file
60
src/IDF/Migrations/21WikiPageRevisionName.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function IDF_Migrations_21WikiPageRevisionName_up($params=null)
|
||||
{
|
||||
$engine = Pluf::f('db_engine');
|
||||
if (!in_array($engine, array('MySQL', 'PostgreSQL'))) {
|
||||
throw new Exception('unsupported engine '.$engine);
|
||||
}
|
||||
|
||||
$db = Pluf::db();
|
||||
$intro = new Pluf_DB_Introspect($db);
|
||||
if (in_array($db->pfx.'idf_wikipagerevs', $intro->listTables())) {
|
||||
echo '21 skipping up migration - table has correct name already'."\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_wikirevisions RENAME TO '.$db->pfx.'idf_wikipagerevs');
|
||||
$db->execute("UPDATE ".$db->pfx."idf_timeline SET model_class='IDF_Wiki_Page' WHERE model_class LIKE 'IDF_WikiPage'");
|
||||
$db->execute("UPDATE ".$db->pfx."idf_timeline SET model_class='IDF_Wiki_PageRevision' WHERE model_class LIKE 'IDF_WikiRevision'");
|
||||
}
|
||||
|
||||
function IDF_Migrations_21WikiPageRevisionName_down($params=null)
|
||||
{
|
||||
$engine = Pluf::f('db_engine');
|
||||
if (!in_array($engine, array('MySQL', 'PostgreSQL'))) {
|
||||
throw new Exception('unsupported engine '.$engine);
|
||||
}
|
||||
|
||||
$db = Pluf::db();
|
||||
$intro = new Pluf_DB_Introspect($db);
|
||||
if (in_array($db->pfx.'idf_wikirevisions', $intro->listTables())) {
|
||||
echo '21 skipping down migration - table has correct name already'."\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_wikipagerevs RENAME TO '.$db->pfx.'idf_wikirevisions');
|
||||
$db->execute("UPDATE ".$db->pfx."idf_timeline SET model_class='IDF_WikiPage' WHERE model_class LIKE 'IDF_Wiki_Page'");
|
||||
$db->execute("UPDATE ".$db->pfx."idf_timeline SET model_class='IDF_WikiRevision' WHERE model_class LIKE 'IDF_Wiki_PageRevision'");
|
||||
}
|
60
src/IDF/Migrations/22ProjectTagRelationTable.php
Normal file
60
src/IDF/Migrations/22ProjectTagRelationTable.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function IDF_Migrations_22ProjectTagRelationTable_up($params=null)
|
||||
{
|
||||
$db = Pluf::db();
|
||||
$table = $db->pfx.'idf_project_idf_tag_assoc';
|
||||
if (!in_array($db->engine, array('MySQL', 'PostgreSQL'))) {
|
||||
throw new Exception('unsupported engine '.$engine);
|
||||
}
|
||||
|
||||
$intro = new Pluf_DB_Introspect($db);
|
||||
if (in_array($table, $intro->listTables())) {
|
||||
echo '21 skipping up migration - table already exists'."\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$schema = Pluf::factory('Pluf_DB_Schema_'.$db->engine, $db);
|
||||
$sql = $schema->getSqlCreate(new IDF_Project());
|
||||
$db->execute($sql[$table]);
|
||||
}
|
||||
|
||||
function IDF_Migrations_22ProjectTagRelationTable_down($params=null)
|
||||
{
|
||||
$db = Pluf::db();
|
||||
$table = $db->pfx.'idf_project_idf_tag_assoc';
|
||||
if (!in_array($db->engine, array('MySQL', 'PostgreSQL'))) {
|
||||
throw new Exception('unsupported engine '.$engine);
|
||||
}
|
||||
|
||||
$intro = new Pluf_DB_Introspect($db);
|
||||
if (!in_array($table, $intro->listTables())) {
|
||||
echo '22 skipping down migration - table does not exist'."\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$schema = Pluf::factory('Pluf_DB_Schema_'.$db->engine, $db);
|
||||
$sql = $schema->getSqlDelete(new IDF_Project());
|
||||
$db->execute($sql[$table]);
|
||||
}
|
42
src/IDF/Migrations/23ProjectActivity.php
Normal file
42
src/IDF/Migrations/23ProjectActivity.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Add the new IDF_ProjectActivity model.
|
||||
*
|
||||
*/
|
||||
function IDF_Migrations_23ProjectActivity_up($params=null)
|
||||
{
|
||||
$db = Pluf::db();
|
||||
$schema = new Pluf_DB_Schema($db);
|
||||
$schema->model = new IDF_ProjectActivity();
|
||||
$schema->createTables();
|
||||
}
|
||||
|
||||
function IDF_Migrations_23ProjectActivity_down($params=null)
|
||||
{
|
||||
$db = Pluf::db();
|
||||
$schema = new Pluf_DB_Schema($db);
|
||||
$schema->model = new IDF_ProjectActivity();
|
||||
$schema->dropTables();
|
||||
}
|
40
src/IDF/Migrations/24CurrentProjectActivity.php
Normal file
40
src/IDF/Migrations/24CurrentProjectActivity.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function IDF_Migrations_24CurrentProjectActivity_up($params=null)
|
||||
{
|
||||
$engine = Pluf::f('db_engine');
|
||||
$db = Pluf::db();
|
||||
|
||||
if ($engine === 'PostgreSQL') {
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_projects ADD COLUMN current_activity INTEGER');
|
||||
} else if ($engine === 'MySQL') {
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_projects ADD COLUMN current_activity MEDIUMINT');
|
||||
}
|
||||
}
|
||||
|
||||
function IDF_Migrations_24CurrentProjectActivity_down($params=null)
|
||||
{
|
||||
$db = Pluf::db();
|
||||
$db->execute('ALTER TABLE '.$db->pfx.'idf_projects DROP COLUMN current_activity');
|
||||
}
|
@ -22,14 +22,14 @@
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Add the download of files.
|
||||
* Add wiki functionality.
|
||||
*/
|
||||
|
||||
function IDF_Migrations_7Wiki_up($params=null)
|
||||
{
|
||||
$models = array(
|
||||
'IDF_WikiPage',
|
||||
'IDF_WikiRevision',
|
||||
'IDF_Wiki_Page',
|
||||
'IDF_Wiki_PageRevision',
|
||||
);
|
||||
$db = Pluf::db();
|
||||
$schema = new Pluf_DB_Schema($db);
|
||||
@ -42,8 +42,8 @@ function IDF_Migrations_7Wiki_up($params=null)
|
||||
function IDF_Migrations_7Wiki_down($params=null)
|
||||
{
|
||||
$models = array(
|
||||
'IDF_WikiRevision',
|
||||
'IDF_WikiPage',
|
||||
'IDF_Wiki_PageRevision',
|
||||
'IDF_Wiki_Page',
|
||||
);
|
||||
$db = Pluf::db();
|
||||
$schema = new Pluf_DB_Schema($db);
|
||||
|
@ -34,6 +34,7 @@ function IDF_Migrations_Backup_run($folder, $name=null)
|
||||
{
|
||||
$models = array(
|
||||
'IDF_Project',
|
||||
'IDF_ProjectActivity',
|
||||
'IDF_Tag',
|
||||
'IDF_Issue',
|
||||
'IDF_IssueComment',
|
||||
@ -43,8 +44,10 @@ function IDF_Migrations_Backup_run($folder, $name=null)
|
||||
'IDF_IssueFile',
|
||||
'IDF_Commit',
|
||||
'IDF_Timeline',
|
||||
'IDF_WikiPage',
|
||||
'IDF_WikiRevision',
|
||||
'IDF_Wiki_Page',
|
||||
'IDF_Wiki_PageRevision',
|
||||
'IDF_Wiki_Resource',
|
||||
'IDF_Wiki_ResourceRevision',
|
||||
'IDF_Review',
|
||||
'IDF_Review_Patch',
|
||||
'IDF_Review_Comment',
|
||||
@ -81,6 +84,7 @@ function IDF_Migrations_Backup_restore($folder, $name)
|
||||
{
|
||||
$models = array(
|
||||
'IDF_Project',
|
||||
'IDF_ProjectActivity',
|
||||
'IDF_Tag',
|
||||
'IDF_Issue',
|
||||
'IDF_IssueComment',
|
||||
@ -90,8 +94,10 @@ function IDF_Migrations_Backup_restore($folder, $name)
|
||||
'IDF_IssueFile',
|
||||
'IDF_Commit',
|
||||
'IDF_Timeline',
|
||||
'IDF_WikiPage',
|
||||
'IDF_WikiRevision',
|
||||
'IDF_Wiki_Resource',
|
||||
'IDF_Wiki_ResourceRevision',
|
||||
'IDF_Wiki_Page',
|
||||
'IDF_Wiki_PageRevision',
|
||||
'IDF_Review',
|
||||
'IDF_Review_Patch',
|
||||
'IDF_Review_Comment',
|
||||
|
@ -31,6 +31,7 @@ function IDF_Migrations_Install_setup($params=null)
|
||||
{
|
||||
$models = array(
|
||||
'IDF_Project',
|
||||
'IDF_ProjectActivity',
|
||||
'IDF_Tag',
|
||||
'IDF_Issue',
|
||||
'IDF_IssueComment',
|
||||
@ -40,8 +41,10 @@ function IDF_Migrations_Install_setup($params=null)
|
||||
'IDF_IssueFile',
|
||||
'IDF_Commit',
|
||||
'IDF_Timeline',
|
||||
'IDF_WikiPage',
|
||||
'IDF_WikiRevision',
|
||||
'IDF_Wiki_Resource',
|
||||
'IDF_Wiki_ResourceRevision',
|
||||
'IDF_Wiki_Page',
|
||||
'IDF_Wiki_PageRevision',
|
||||
'IDF_Review',
|
||||
'IDF_Review_Patch',
|
||||
'IDF_Review_Comment',
|
||||
@ -97,8 +100,10 @@ function IDF_Migrations_Install_teardown($params=null)
|
||||
'IDF_Review_Comment',
|
||||
'IDF_Review_Patch',
|
||||
'IDF_Review',
|
||||
'IDF_WikiRevision',
|
||||
'IDF_WikiPage',
|
||||
'IDF_Wiki_PageRevision',
|
||||
'IDF_Wiki_Page',
|
||||
'IDF_Wiki_ResourceRevision',
|
||||
'IDF_Wiki_Resource',
|
||||
'IDF_Timeline',
|
||||
'IDF_IssueFile',
|
||||
'IDF_Search_Occ',
|
||||
@ -108,6 +113,7 @@ function IDF_Migrations_Install_teardown($params=null)
|
||||
'IDF_Issue',
|
||||
'IDF_Tag',
|
||||
'IDF_Commit',
|
||||
'IDF_ProjectActivity',
|
||||
'IDF_Project',
|
||||
'IDF_EmailAddress',
|
||||
'IDF_IssueRelation',
|
||||
|
@ -210,22 +210,23 @@ class IDF_Plugin_SyncGit_Serve
|
||||
// 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')) {
|
||||
$post_update_hook = $fullpath.'/hooks/post-update';
|
||||
if (file_exists($post_update_hook) && !@unlink($post_update_hook)) {
|
||||
Pluf_Log::warn(array('IDF_Plugin_Git_Serve::initRepository',
|
||||
'post-update hook removal error.',
|
||||
$fullpath.'/hooks/post-update'));
|
||||
$post_update_hook));
|
||||
return;
|
||||
}
|
||||
$out = array();
|
||||
$res = 0;
|
||||
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').'ln -s %s %s',
|
||||
escapeshellarg($p),
|
||||
escapeshellarg($fullpath.'/hooks/post-update')),
|
||||
escapeshellarg($post_update_hook)),
|
||||
$out, $res);
|
||||
if ($res != 0) {
|
||||
Pluf_Log::warn(array('IDF_Plugin_Git_Serve::initRepository',
|
||||
'post-update hook creation error.',
|
||||
$fullpath.'/hooks/post-update'));
|
||||
$post_update_hook));
|
||||
return;
|
||||
}
|
||||
Pluf_Log::debug(array('IDF_Plugin_Git_Serve::initRepository',
|
||||
|
@ -306,7 +306,7 @@ class IDF_Plugin_SyncMonotone
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->_diagnoseProblem(sprintf(
|
||||
__('Could not parse usher configuration in "%s": %s'),
|
||||
__('Could not parse usher configuration in "%1$s": %2$s'),
|
||||
$usher_config, $e->getMessage()
|
||||
));
|
||||
}
|
||||
@ -522,7 +522,7 @@ class IDF_Plugin_SyncMonotone
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->_diagnoseProblem(sprintf(
|
||||
__('Could not parse usher configuration in "%s": %s'),
|
||||
__('Could not parse usher configuration in "%1$s": %2$s'),
|
||||
$usher_config, $e->getMessage()
|
||||
));
|
||||
}
|
||||
@ -596,7 +596,7 @@ class IDF_Plugin_SyncMonotone
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->_diagnoseProblem(sprintf(
|
||||
__('Could not parse read-permissions for project "%s": %s'),
|
||||
__('Could not parse read-permissions for project "%1$s": %2$s'),
|
||||
$shortname, $e->getMessage()
|
||||
));
|
||||
}
|
||||
@ -715,7 +715,7 @@ class IDF_Plugin_SyncMonotone
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->_diagnoseProblem(sprintf(
|
||||
__('Could not parse read-permissions for project "%s": %s'),
|
||||
__('Could not parse read-permissions for project "%1$s": %2$s'),
|
||||
$shortname, $e->getMessage()
|
||||
));
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ class IDF_Project extends Pluf_Model
|
||||
'blank' => false,
|
||||
'size' => 50,
|
||||
'verbose' => __('short name'),
|
||||
'help_text' => __('Used in the url to access the project, must be short with only letters and numbers.'),
|
||||
'help_text' => __('Used in the URL to access the project, must be short with only letters and numbers.'),
|
||||
'unique' => true,
|
||||
),
|
||||
'shortdesc' =>
|
||||
@ -84,7 +84,14 @@ class IDF_Project extends Pluf_Model
|
||||
'blank' => false,
|
||||
'size' => 250,
|
||||
'verbose' => __('description'),
|
||||
'help_text' => __('The description can be extended using the markdown syntax.'),
|
||||
'help_text' => __('The description can be extended using the Markdown syntax.'),
|
||||
),
|
||||
'tags' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Manytomany',
|
||||
'blank' => true,
|
||||
'model' => 'IDF_Tag',
|
||||
'verbose' => __('labels'),
|
||||
),
|
||||
'private' =>
|
||||
array(
|
||||
@ -93,6 +100,28 @@ class IDF_Project extends Pluf_Model
|
||||
'verbose' => __('private'),
|
||||
'default' => 0,
|
||||
),
|
||||
'current_activity' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Foreignkey',
|
||||
'model' => 'IDF_ProjectActivity',
|
||||
'blank' => true,
|
||||
'verbose' => __('current project activity'),
|
||||
),
|
||||
);
|
||||
$activityTable = $this->_con->pfx.'idf_projectactivities';
|
||||
$tagTable = $this->_con->pfx.'idf_project_idf_tag_assoc';
|
||||
$this->_a['views'] = array(
|
||||
'join_activities_and_tags' =>
|
||||
array(
|
||||
'join' => 'LEFT JOIN '.$activityTable.' ON current_activity='.$activityTable.'.id '
|
||||
.'LEFT JOIN '.$tagTable.' ON idf_project_id='.$this->getSqlTable().'.id',
|
||||
'select' => $this->getSelect().', date, value',
|
||||
'group' => $this->getSqlTable().'.id',
|
||||
'props' => array(
|
||||
'date' => 'current_activity_date',
|
||||
'value' => 'current_activity_value'
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -427,13 +456,13 @@ GROUP BY uid";
|
||||
$dep_ids = IDF_Views_Wiki::getDeprecatedPagesIds($this);
|
||||
$extra = '';
|
||||
if (count($dep_ids)) {
|
||||
$extra = ' AND idf_wikipage_id NOT IN ('.implode(', ', $dep_ids).') ';
|
||||
$extra = ' AND idf_wiki_page_id NOT IN ('.implode(', ', $dep_ids).') ';
|
||||
}
|
||||
$what_t = Pluf::factory('IDF_WikiPage')->getSqlTable();
|
||||
$asso_t = $this->_con->pfx.'idf_tag_idf_wikipage_assoc';
|
||||
$what_t = Pluf::factory('IDF_Wiki_Page')->getSqlTable();
|
||||
$asso_t = $this->_con->pfx.'idf_tag_idf_wiki_page_assoc';
|
||||
$sql = 'SELECT '.$tag_t.'.id AS id, COUNT(*) AS nb_use FROM '.$tag_t.' '."\n".
|
||||
'LEFT JOIN '.$asso_t.' ON idf_tag_id='.$tag_t.'.id '."\n".
|
||||
'LEFT JOIN '.$what_t.' ON idf_wikipage_id='.$what_t.'.id '."\n".
|
||||
'LEFT JOIN '.$what_t.' ON idf_wiki_page_id='.$what_t.'.id '."\n".
|
||||
'WHERE idf_tag_id IS NOT NULL '.$extra.' AND '.$what_t.'.project='.$this->id.' GROUP BY '.$tag_t.'.id, '.$tag_t.'.class, '.$tag_t.'.name ORDER BY '.$tag_t.'.class ASC, '.$tag_t.'.name ASC';
|
||||
} elseif ($what == 'downloads') {
|
||||
$dep_ids = IDF_Views_Download::getDeprecatedFilesIds($this);
|
||||
@ -535,12 +564,12 @@ GROUP BY uid";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the post commit hook key.
|
||||
* Get the web hook key.
|
||||
*
|
||||
* The goal is to get something predictable but from which one
|
||||
* cannot reverse find the secret key.
|
||||
*/
|
||||
public function getPostCommitHookKey()
|
||||
public function getWebHookKey()
|
||||
{
|
||||
return md5($this->id.sha1(Pluf::f('secret_key')).$this->shortname);
|
||||
}
|
||||
@ -593,6 +622,22 @@ GROUP BY uid";
|
||||
return $this->_pconf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic overload that falls back to the values of the internal configuration
|
||||
* if no getter / caller matched
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
try {
|
||||
return parent::__get($key);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
return $this->getConf()->getVal($key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get simple statistics about the project.
|
||||
*
|
||||
@ -608,7 +653,7 @@ GROUP BY uid";
|
||||
$what = array('downloads' => 'IDF_Upload',
|
||||
'reviews' => 'IDF_Review',
|
||||
'issues' => 'IDF_Issue',
|
||||
'docpages' => 'IDF_WikiPage',
|
||||
'docpages' => 'IDF_Wiki_Page',
|
||||
'commits' => 'IDF_Commit',
|
||||
);
|
||||
foreach ($what as $key=>$m) {
|
||||
@ -739,7 +784,8 @@ GROUP BY uid";
|
||||
Pluf_Signal::send('IDF_Project::preDelete',
|
||||
'IDF_Project', $params);
|
||||
$what = array('IDF_Upload', 'IDF_Review', 'IDF_Issue',
|
||||
'IDF_WikiPage', 'IDF_Commit', 'IDF_Tag',
|
||||
'IDF_Wiki_Page', 'IDF_Wiki_Resource',
|
||||
'IDF_Commit', 'IDF_Tag',
|
||||
);
|
||||
foreach ($what as $m) {
|
||||
foreach (Pluf::factory($m)->getList(array('filter' => 'project='.(int)$this->id)) as $item) {
|
||||
@ -780,4 +826,52 @@ GROUP BY uid";
|
||||
$this->_isRestricted = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array of email addresses to notify about changes
|
||||
* in a certain tab like 'issues', 'source', and so on.
|
||||
*
|
||||
* @param string $tab
|
||||
* @return array Key is the email address, value is the preferred language setting
|
||||
*/
|
||||
public function getNotificationRecipientsForTab($tab)
|
||||
{
|
||||
if (!in_array($tab, array('source', 'issues', 'downloads', 'wiki', 'review'))) {
|
||||
throw new Exception(sprintf('unknown tab %s', $tab));
|
||||
}
|
||||
|
||||
$conf = $this->getConf();
|
||||
$recipients = array();
|
||||
$membership_data = $this->getMembershipData();
|
||||
|
||||
if ($conf->getVal($tab.'_notification_owners_enabled', false)) {
|
||||
foreach ($membership_data['owners'] as $owner) {
|
||||
$recipients[$owner->email] = $owner->language;
|
||||
}
|
||||
}
|
||||
|
||||
if ($conf->getVal($tab.'_notification_members_enabled', false)) {
|
||||
foreach ($membership_data['members'] as $member) {
|
||||
$recipients[$member->email] = $member->language;
|
||||
}
|
||||
}
|
||||
|
||||
if ($conf->getVal($tab.'_notification_email_enabled', false)) {
|
||||
$addresses = preg_split('/\s*,\s*/',
|
||||
$conf->getVal($tab.'_notification_email', ''),
|
||||
-1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
// we use a default language setting for this plain list of
|
||||
// addresses, but we ensure that we do not overwrite an existing
|
||||
// address which might come with a proper setting already
|
||||
$languages = Pluf::f('languages', array('en'));
|
||||
foreach ($addresses as $address) {
|
||||
if (array_key_exists($address, $recipients))
|
||||
continue;
|
||||
$recipients[$address] = $languages[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $recipients;
|
||||
}
|
||||
}
|
||||
|
78
src/IDF/ProjectActivity.php
Normal file
78
src/IDF/ProjectActivity.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Models the activity value for a project and a given date
|
||||
*
|
||||
* @author tommyd
|
||||
*/
|
||||
class IDF_ProjectActivity extends Pluf_Model
|
||||
{
|
||||
public $_model = __CLASS__;
|
||||
|
||||
function init()
|
||||
{
|
||||
$this->_a['table'] = 'idf_projectactivities';
|
||||
$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' => 'activities',
|
||||
),
|
||||
'date' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Datetime',
|
||||
'blank' => false,
|
||||
'verbose' => __('date'),
|
||||
),
|
||||
'value' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Float',
|
||||
'blank' => false,
|
||||
'verbose' => __('value'),
|
||||
'default' => 0,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function postSave($create=false)
|
||||
{
|
||||
$prj = $this->get_project();
|
||||
$sql = new Pluf_SQL('project=%s', array($prj->id));
|
||||
$list = Pluf::factory('IDF_ProjectActivity')->getList(array('filter' => $sql->gen(), 'order' => 'date desc'));
|
||||
if (count($list) > 0 && $prj->current_activity != $list[0]->id) {
|
||||
$prj->current_activity = $list[0];
|
||||
$prj->update();
|
||||
}
|
||||
}
|
||||
}
|
@ -138,7 +138,7 @@ class IDF_Review_Comment extends Pluf_Model
|
||||
$ic = (in_array($review->status, $request->project->getTagIdsByStatus('closed'))) ? 'issue-c' : 'issue-o';
|
||||
$out .= sprintf(__('<a href="%1$s" class="%2$s" title="View review">Review %3$d</a>, %4$s'), $url, $ic, $review->id, Pluf_esc($review->summary)).'</td>';
|
||||
$out .= "\n".'<tr class="extra"><td colspan="2">
|
||||
<div class="helptext right">'.sprintf(__('Update of <a href="%s" class="%s">review %d</a>, by %s'), $url, $ic, $review->id, $user).'</div></td></tr>';
|
||||
<div class="helptext right">'.sprintf(__('Update of <a href="%1$s" class="%2$s">review %3$d</a>, by %4$s'), $url, $ic, $review->id, $user).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
@ -148,7 +148,7 @@ class IDF_Review_Comment extends Pluf_Model
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Review::view',
|
||||
array($request->project->shortname,
|
||||
$review->id));
|
||||
$title = sprintf(__('%s: Updated review %d - %s'),
|
||||
$title = sprintf(__('%1$s: Updated review %2$d - %3$s'),
|
||||
Pluf_esc($request->project->name),
|
||||
$review->id, Pluf_esc($review->summary));
|
||||
$url .= '#ic'.$this->id;
|
||||
@ -178,47 +178,60 @@ class IDF_Review_Comment extends Pluf_Model
|
||||
$patch = $this->get_patch();
|
||||
$review = $patch->get_review();
|
||||
$prj = $review->get_project();
|
||||
$to_email = array();
|
||||
if ('' != $conf->getVal('review_notification_email', '')) {
|
||||
$langs = Pluf::f('languages', array('en'));
|
||||
$to_email[] = array($conf->getVal('issues_notification_email'),
|
||||
$langs[0]);
|
||||
}
|
||||
$current_locale = Pluf_Translation::getLocale();
|
||||
$reviewers = $review->getReviewers();
|
||||
|
||||
if (!Pluf_Model_InArray($review->get_submitter(), $reviewers)) {
|
||||
$reviewers[] = $review->get_submitter();
|
||||
}
|
||||
|
||||
$comments = $patch->getFileComments(array('order' => 'id DESC'));
|
||||
$gcomments = $patch->get_comments_list(array('order' => 'id DESC'));
|
||||
$context = new Pluf_Template_Context(
|
||||
array(
|
||||
|
||||
$recipients = $prj->getNotificationRecipientsForTab('review');
|
||||
|
||||
foreach ($reviewers as $user) {
|
||||
if (array_key_exists($user->email, $recipients))
|
||||
continue;
|
||||
$recipients[$user->email] = $user->language;
|
||||
}
|
||||
|
||||
$current_locale = Pluf_Translation::getLocale();
|
||||
|
||||
$from_email = Pluf::f('from_email');
|
||||
$messageId = '<'.md5('review'.$review->id.md5(Pluf::f('secret_key'))).'@'.Pluf::f('mail_host', 'localhost').'>';
|
||||
|
||||
foreach ($recipients as $address => $language) {
|
||||
|
||||
if ($this->get_submitter()->email === $address) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pluf_Translation::loadSetLocale($language);
|
||||
|
||||
$context = new Pluf_Template_Context(array(
|
||||
'review' => $review,
|
||||
'patch' => $patch,
|
||||
'comments' => $comments,
|
||||
'gcomments' => $gcomments,
|
||||
'project' => $prj,
|
||||
'url_base' => Pluf::f('url_base'),
|
||||
)
|
||||
);
|
||||
// build the list of emails and lang
|
||||
foreach ($reviewers as $user) {
|
||||
$email_lang = array($user->email,
|
||||
$user->language);
|
||||
if (!in_array($email_lang, $to_email)) {
|
||||
$to_email[] = $email_lang;
|
||||
}
|
||||
}
|
||||
$tmpl = new Pluf_Template('idf/review/review-updated-email.txt');
|
||||
foreach ($to_email as $email_lang) {
|
||||
Pluf_Translation::loadSetLocale($email_lang[1]);
|
||||
$email = new Pluf_Mail(Pluf::f('from_email'), $email_lang[0],
|
||||
sprintf(__('Updated Code Review %s - %s (%s)'),
|
||||
$review->id, $review->summary, $prj->shortname));
|
||||
));
|
||||
|
||||
$email->addTextMessage($tmpl->render($context));
|
||||
// reviews only updated through comments, see IDF_Review_Patch::notify()
|
||||
$tplfile = 'idf/review/review-updated-email.txt';
|
||||
$subject = __('Updated Code Review %1$s - %2$s (%3$s)');
|
||||
$headers = array('References' => $messageId);
|
||||
|
||||
$tmpl = new Pluf_Template($tplfile);
|
||||
$text_email = $tmpl->render($context);
|
||||
|
||||
$email = new Pluf_Mail($from_email, $address,
|
||||
sprintf($subject, $review->id, $review->summary, $prj->shortname));
|
||||
$email->addTextMessage($text_email);
|
||||
$email->addHeaders($headers);
|
||||
$email->sendMail();
|
||||
}
|
||||
|
||||
Pluf_Translation::loadSetLocale($current_locale);
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ class IDF_Review_Patch extends Pluf_Model
|
||||
$ic = (in_array($review->status, $request->project->getTagIdsByStatus('closed'))) ? 'issue-c' : 'issue-o';
|
||||
$out .= sprintf(__('<a href="%1$s" class="%2$s" title="View review">Review %3$d</a>, %4$s'), $url, $ic, $review->id, Pluf_esc($review->summary)).'</td>';
|
||||
$out .= "\n".'<tr class="extra"><td colspan="2">
|
||||
<div class="helptext right">'.sprintf(__('Creation of <a href="%s" class="%s">review %d</a>, by %s'), $url, $ic, $review->id, $user).'</div></td></tr>';
|
||||
<div class="helptext right">'.sprintf(__('Creation of <a href="%1$s" class="%2$s">review %3$d</a>, by %4$s'), $url, $ic, $review->id, $user).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@ class IDF_Review_Patch extends Pluf_Model
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Review::view',
|
||||
array($request->project->shortname,
|
||||
$review->id));
|
||||
$title = sprintf(__('%s: Creation of Review %d - %s'),
|
||||
$title = sprintf(__('%1$s: Creation of Review %2$d - %3$s'),
|
||||
Pluf_esc($request->project->name),
|
||||
$review->id, Pluf_esc($review->summary));
|
||||
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
|
||||
@ -179,35 +179,49 @@ class IDF_Review_Patch extends Pluf_Model
|
||||
|
||||
public function notify($conf, $create=true)
|
||||
{
|
||||
if ('' == $conf->getVal('review_notification_email', '')) {
|
||||
return;
|
||||
}
|
||||
$review = $this->get_review();
|
||||
$project = $review->get_project();
|
||||
$current_locale = Pluf_Translation::getLocale();
|
||||
$langs = Pluf::f('languages', array('en'));
|
||||
Pluf_Translation::loadSetLocale($langs[0]);
|
||||
|
||||
$context = new Pluf_Template_Context(
|
||||
array(
|
||||
'review' => $this->get_review(),
|
||||
$from_email = Pluf::f('from_email');
|
||||
$messageId = '<'.md5('review'.$review->id.md5(Pluf::f('secret_key'))).'@'.Pluf::f('mail_host', 'localhost').'>';
|
||||
$recipients = $project->getNotificationRecipientsForTab('review');
|
||||
|
||||
foreach ($recipients as $address => $language) {
|
||||
|
||||
if ($review->get_submitter()->email === $address) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pluf_Translation::loadSetLocale($language);
|
||||
|
||||
$context = new Pluf_Template_Context(array(
|
||||
'review' => $review,
|
||||
'patch' => $this,
|
||||
'comments' => array(),
|
||||
'project' => $this->get_review()->get_project(),
|
||||
'project' => $project,
|
||||
'url_base' => Pluf::f('url_base'),
|
||||
)
|
||||
);
|
||||
$tmpl = new Pluf_Template('idf/review/review-created-email.txt');
|
||||
));
|
||||
|
||||
// reviews are updated through comments, see IDF_Review_Comment::notify()
|
||||
$tplfile = 'idf/review/review-created-email.txt';
|
||||
$subject = __('New Code Review %1$s - %2$s (%3$s)');
|
||||
$headers = array('Message-ID' => $messageId);
|
||||
|
||||
$tmpl = new Pluf_Template($tplfile);
|
||||
$text_email = $tmpl->render($context);
|
||||
$addresses = explode(';',$conf->getVal('review_notification_email'));
|
||||
foreach ($addresses as $address) {
|
||||
$email = new Pluf_Mail(Pluf::f('from_email'),
|
||||
|
||||
$email = new Pluf_Mail($from_email,
|
||||
$address,
|
||||
sprintf(__('New Code Review %s - %s (%s)'),
|
||||
$this->get_review()->id,
|
||||
$this->get_review()->summary,
|
||||
$this->get_review()->get_project()->shortname));
|
||||
sprintf($subject,
|
||||
$review->id,
|
||||
$review->summary,
|
||||
$project->shortname));
|
||||
$email->addTextMessage($text_email);
|
||||
$email->addHeaders($headers);
|
||||
$email->sendMail();
|
||||
}
|
||||
|
||||
Pluf_Translation::loadSetLocale($current_locale);
|
||||
}
|
||||
}
|
||||
|
@ -497,5 +497,10 @@ class IDF_Scm
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function repository($request, $match)
|
||||
{
|
||||
throw new Exception('This repository does not support web based repository access');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -918,4 +918,82 @@ class IDF_Scm_Git extends IDF_Scm
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function repository($request, $match)
|
||||
{
|
||||
// authenticate: authenticate connection through "extra" password
|
||||
if (isset($_SERVER['HTTP_AUTHORIZATION']) && $_SERVER['HTTP_AUTHORIZATION'] != '')
|
||||
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':' , base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
|
||||
|
||||
if (isset($_SERVER['PHP_AUTH_USER'])) {
|
||||
$sql = new Pluf_SQL('login=%s', array($_SERVER['PHP_AUTH_USER']));
|
||||
$users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
|
||||
if ((count($users) == 1) && ($users[0]->active)) {
|
||||
$user = $users[0];
|
||||
$realkey = substr(sha1($user->password.Pluf::f('secret_key')), 0, 8);
|
||||
if ($_SERVER['PHP_AUTH_PW'] == $realkey) {
|
||||
$request->user = $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IDF_Precondition::accessSource($request) !== true) {
|
||||
$response = new Pluf_HTTP_Response("");
|
||||
$response->status_code = 401;
|
||||
$response->headers['WWW-Authenticate']='Basic realm="git for '.$this->project.'"';
|
||||
return $response;
|
||||
}
|
||||
|
||||
$path = $match[2];
|
||||
|
||||
// update files before delivering them
|
||||
if (($path == 'objects/info/pack') || ($path == 'info/refs')) {
|
||||
$cmd = sprintf(Pluf::f('idf_exec_cmd_prefix', '').
|
||||
'GIT_DIR=%s '.Pluf::f('git_path', 'git').' update-server-info -f',
|
||||
escapeshellarg($this->repo));
|
||||
self::shell_exec('IDF_Scm_Git::repository', $cmd);
|
||||
}
|
||||
|
||||
// smart HTTP discovery
|
||||
if (($path == 'info/refs') &&
|
||||
(array_key_exists('service', $request->GET))){
|
||||
$service = $request->GET["service"];
|
||||
switch ($service) {
|
||||
case 'git-upload-pack':
|
||||
case 'git-receive-pack':
|
||||
$content = sprintf('%04x',strlen($service)+15).
|
||||
'# service='.$service."\n0000";
|
||||
$content .= self::shell_exec('IDF_Scm_Git::repository',
|
||||
$service.' --stateless-rpc --advertise-refs '.
|
||||
$this->repo);
|
||||
$response = new Pluf_HTTP_Response($content,
|
||||
'application/x-'.$service.'-advertisement');
|
||||
return $response;
|
||||
default:
|
||||
throw new Exception('unknown service: '.$service);
|
||||
}
|
||||
}
|
||||
|
||||
switch($path) {
|
||||
// smart HTTP RPC
|
||||
case 'git-upload-pack':
|
||||
case 'git-receive-pack':
|
||||
$response = new Pluf_HTTP_Response_CommandPassThru($path.
|
||||
' --stateless-rpc '.$this->repo,
|
||||
'application/x-'.$path.'-result');
|
||||
$response->addStdin('php://input');
|
||||
return $response;
|
||||
|
||||
// regular file
|
||||
default:
|
||||
// make sure we're inside the repo hierarchy (ie. no break-out)
|
||||
if (is_file($this->repo.'/'.$path) &&
|
||||
strpos(realpath($this->repo.'/'.$path), $this->repo.'/') == 0) {
|
||||
return new Pluf_HTTP_Response_File($this->repo.'/'.$path,
|
||||
'application/octet-stream');
|
||||
} else {
|
||||
return new Pluf_HTTP_Response_NotFound($request);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ class IDF_Scm_Monotone_Stdio implements IDF_Scm_Monotone_IStdio
|
||||
$remote_db_access = Pluf::f('mtn_db_access', 'remote') == 'remote';
|
||||
|
||||
$cmd = Pluf::f('idf_exec_cmd_prefix', '') .
|
||||
Pluf::f('mtn_path', 'mtn') . ' ';
|
||||
escapeshellarg(Pluf::f('mtn_path', 'mtn')) . ' ';
|
||||
|
||||
$opts = Pluf::f('mtn_opts', array());
|
||||
foreach ($opts as $opt) {
|
||||
|
@ -22,7 +22,7 @@
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Storage of the occurence of the words.
|
||||
* Storage of the occurrence of the words.
|
||||
*/
|
||||
class IDF_Search_Occ extends Pluf_Model
|
||||
{
|
||||
@ -30,7 +30,7 @@ class IDF_Search_Occ extends Pluf_Model
|
||||
|
||||
function init()
|
||||
{
|
||||
$this->_a['verbose'] = __('occurence');
|
||||
$this->_a['verbose'] = __('occurrence');
|
||||
$this->_a['table'] = 'idf_search_occs';
|
||||
$this->_a['model'] = 'IDF_Search_Occ';
|
||||
$this->_a['cols'] = array(
|
||||
@ -72,13 +72,13 @@ class IDF_Search_Occ extends Pluf_Model
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Integer',
|
||||
'blank' => false,
|
||||
'verbose' => __('occurences'),
|
||||
'verbose' => __('occurrences'),
|
||||
),
|
||||
'pondocc' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Float',
|
||||
'blank' => false,
|
||||
'verbose' => __('ponderated occurence'),
|
||||
'verbose' => __('ponderated occurrence'),
|
||||
),
|
||||
);
|
||||
$this->_a['idx'] = array(
|
||||
|
@ -75,6 +75,18 @@ class IDF_Tag extends Pluf_Model
|
||||
),
|
||||
);
|
||||
|
||||
$table = $this->_con->pfx.'idf_project_idf_tag_assoc';
|
||||
$this->_a['views'] = array(
|
||||
'join_projects' =>
|
||||
array(
|
||||
'join' => 'LEFT JOIN '.$table
|
||||
.' ON idf_tag_id=id',
|
||||
'select' => $this->getSelect().',COUNT(idf_project_id) as project_count',
|
||||
'group' => 'idf_tag_id',
|
||||
'props' => array('project_count' => 'project_count'),
|
||||
),
|
||||
);
|
||||
|
||||
$this->_a['idx'] = array(
|
||||
'lcname_idx' =>
|
||||
array(
|
||||
@ -95,7 +107,7 @@ class IDF_Tag extends Pluf_Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a tag if not already existing.
|
||||
* Add a project-specific tag if not already existing.
|
||||
*
|
||||
* @param string Name of the tag.
|
||||
* @param IDF_Project Project of the tag.
|
||||
@ -122,6 +134,32 @@ class IDF_Tag extends Pluf_Model
|
||||
return $tags[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a global tag if not already existing
|
||||
*
|
||||
* @param string Name of the tag.
|
||||
* @param string Class of the tag (IDF_TAG_DEFAULT_CLASS)
|
||||
* @return IDF_Tag The tag.
|
||||
*/
|
||||
public static function addGlobal($name, $class=IDF_TAG_DEFAULT_CLASS)
|
||||
{
|
||||
$class = trim($class);
|
||||
$name = trim($name);
|
||||
$gtag = new IDF_Tag();
|
||||
$sql = new Pluf_SQL('class=%s AND lcname=%s AND project=0',
|
||||
array($class, mb_strtolower($name)));
|
||||
$tags = $gtag->getList(array('filter' => $sql->gen()));
|
||||
if ($tags->count() < 1) {
|
||||
// create a new tag
|
||||
$tag = new IDF_Tag();
|
||||
$tag->name = $name;
|
||||
$tag->class = $class;
|
||||
$tag->create();
|
||||
return $tag;
|
||||
}
|
||||
return $tags[0];
|
||||
}
|
||||
|
||||
function __toString()
|
||||
{
|
||||
if ($this->class != IDF_TAG_DEFAULT_CLASS) {
|
||||
|
@ -31,6 +31,6 @@ class IDF_Template_HotKey extends Pluf_Template_Tag
|
||||
function start($key, $view, $params=array(), $get_params=array())
|
||||
{
|
||||
$url = addslashes(Pluf_HTTP_URL_urlForView($view, $params, $get_params));
|
||||
echo "jQuery.hotkeys.add('$key',{disableInInput: true},function (){window.location.href='$url';});";
|
||||
echo "$(document).bind('keydown','$key',function (){window.location.href='$url';});";
|
||||
}
|
||||
}
|
||||
|
@ -55,8 +55,17 @@ class IDF_Template_Markdown extends Pluf_Template_Tag
|
||||
$text = IDF_Template_safePregReplace('#\[\[([A-Za-z0-9\-]+)\]\]#im',
|
||||
array($this, 'callbackWikiPageNoName'),
|
||||
$text);
|
||||
|
||||
$filter = new IDF_Template_MarkdownPrefilter();
|
||||
echo $filter->go(Pluf_Text_MarkDown_parse($text));
|
||||
$text = $filter->go(Pluf_Text_MarkDown_parse($text));
|
||||
|
||||
// Replace [[!ResourceName]] with corresponding HTML for the resource;
|
||||
// we need to do that after the HTML filtering as we'd otherwise be unable to use
|
||||
// certain HTML elements, such as iframes, that are used to display text content
|
||||
// FIXME: no support for escaping yet in place
|
||||
echo IDF_Template_safePregReplace('#\[\[!([A-Za-z0-9\-]+)(?:,\s*([^\]]+))?\]\]#im',
|
||||
array($this, 'callbackWikiResource'),
|
||||
$text);
|
||||
}
|
||||
|
||||
function callbackWikiPageNoName($m)
|
||||
@ -69,15 +78,99 @@ class IDF_Template_Markdown extends Pluf_Template_Tag
|
||||
{
|
||||
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||
array($this->project->id, $m[2]));
|
||||
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
|
||||
$pages = Pluf::factory('IDF_Wiki_Page')->getList(array('filter'=>$sql->gen()));
|
||||
if ($pages->count() != 1 and $this->request->rights['hasWikiAccess']
|
||||
and !$this->request->user->isAnonymous()) {
|
||||
return '<img style="vertical-align: text-bottom;" alt=" " src="'.Pluf::f('url_media').'/idf/img/add.png" /><a href="'.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::create', array($this->project->shortname), array('name'=>$m[2])).'" title="'.__('Create this documentation page').'">'.$m[1].'</a>';
|
||||
return '<img style="vertical-align: text-bottom;" alt=" " src="'.Pluf::f('url_media').'/idf/img/add.png" /><a href="'.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::createPage', array($this->project->shortname), array('name'=>$m[2])).'" title="'.__('Create this documentation page').'">'.$m[1].'</a>';
|
||||
}
|
||||
if (!$this->request->rights['hasWikiAccess'] or $pages->count() == 0) {
|
||||
return $m[1];
|
||||
}
|
||||
return '<a href="'.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view', array($this->project->shortname, $pages[0]->title)).'" title="'.Pluf_esc($pages[0]->summary).'">'.$m[1].'</a>';
|
||||
return '<a href="'.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage', array($this->project->shortname, $pages[0]->title)).'" title="'.Pluf_esc($pages[0]->summary).'">'.$m[1].'</a>';
|
||||
}
|
||||
|
||||
function callbackWikiResource($m)
|
||||
{
|
||||
@list($match, $resourceName, $opts) = $m;
|
||||
|
||||
if (!$this->request->rights['hasWikiAccess']) {
|
||||
return '<span title="'.__('You are not allowed to access the wiki.').'">'.$match.'</span>';
|
||||
}
|
||||
|
||||
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||
array($this->project->id, $resourceName));
|
||||
$resources = Pluf::factory('IDF_Wiki_Resource')->getList(array('filter'=>$sql->gen()));
|
||||
|
||||
if ($resources->count() == 0) {
|
||||
if ($this->request->user->isAnonymous()) {
|
||||
return '<span title="'.__('The wiki resource has not been found.').'">'.$match.'</span>';
|
||||
}
|
||||
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::createResource',
|
||||
array($this->project->shortname),
|
||||
array('name' => $resourceName));
|
||||
return '<img style="vertical-align: text-bottom;" alt=" " src="'.Pluf::f('url_media').'/idf/img/add.png" />'.
|
||||
'<a href="'.$url.'" title="'.__('The wiki resource has not been found. Create it!').'">'.$match.'</a>';
|
||||
}
|
||||
|
||||
// by default, render the most recent revision
|
||||
$resourceRevision = $resources[0]->get_current_revision();
|
||||
|
||||
list($urlConf, $urlMatches) = $this->request->view;
|
||||
|
||||
// if we currently look at an existing wiki page, look up its name and find the proper resource (if any)
|
||||
if ($urlConf['model'] == 'IDF_Views_Wiki' && $urlConf['method'] == 'viewPage') {
|
||||
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||
array($this->project->id, $urlMatches[2]));
|
||||
$pages = Pluf::factory('IDF_Wiki_Page')->getList(array('filter'=>$sql->gen()));
|
||||
if ($pages->count() == 0) throw new Exception('page not found');
|
||||
$pageRevision = $pages[0]->get_current_revision();
|
||||
|
||||
// if we look at an old version of the page, figure out the resource version back then
|
||||
if (isset($this->request->GET['rev']) and preg_match('/^[0-9]+$/', $this->request->GET['rev'])) {
|
||||
$pageRevision = Pluf_Shortcuts_GetObjectOr404('IDF_Wiki_PageRevision',
|
||||
$this->request->GET['rev']);
|
||||
// this is actually an invariant since we came so far looking at
|
||||
// and rendering the old revision already
|
||||
if ($pageRevision == null) {
|
||||
throw new Exception('page revision with id '.$this->request->GET['rev'].' not found');
|
||||
}
|
||||
}
|
||||
|
||||
$sql = new Pluf_SQL('wikiresource=%s AND idf_wiki_pagerevision_id=%s',
|
||||
array($resources[0]->id, $pageRevision->id));
|
||||
$resourceRevision = Pluf::factory('IDF_Wiki_ResourceRevision')->getOne(
|
||||
array('filter' => $sql->gen(), 'view' => 'join_pagerevision'));
|
||||
|
||||
if ($resourceRevision == null) {
|
||||
return '<span title="'.__('This revision of the resource is no longer available.').'">'.$match.'</span>';
|
||||
}
|
||||
}
|
||||
|
||||
$validOpts = array(
|
||||
'align' => '/^(left|right|center)$/',
|
||||
'width' => '/^\d+(%|px|em)?$/',
|
||||
'height' => '/^\d+(%|px|em)?$/',
|
||||
'preview' => '/^yes|no$/',
|
||||
'title' => '/.+/',
|
||||
);
|
||||
|
||||
$parsedOpts = array();
|
||||
// FIXME: no support for escaping yet in place
|
||||
$opts = preg_split('/\s*,\s*/', $opts, -1, PREG_SPLIT_NO_EMPTY);
|
||||
foreach ((array)@$opts as $opt)
|
||||
{
|
||||
list($key, $value) = preg_split('/\s*=\s*/', $opt, 2);
|
||||
if (!array_key_exists($key, $validOpts)) {
|
||||
continue;
|
||||
}
|
||||
if (!preg_match($validOpts[$key], $value)) {
|
||||
continue;
|
||||
}
|
||||
$parsedOpts[$key] = $value;
|
||||
}
|
||||
|
||||
return $resourceRevision->render($parsedOpts);
|
||||
}
|
||||
|
||||
function callbackEmbeddedDoc($m)
|
||||
|
118
src/IDF/Template/MarkdownForge.php
Normal file
118
src/IDF/Template/MarkdownForge.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
Pluf::loadFunction('Pluf_Text_MarkDown_parse');
|
||||
|
||||
class IDF_Template_MarkdownForge extends Pluf_Template_Tag
|
||||
{
|
||||
private $request;
|
||||
|
||||
public function start($text, $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
$filter = new IDF_Template_MarkdownPrefilter();
|
||||
$text = $filter->go(Pluf_Text_MarkDown_parse($text));
|
||||
|
||||
// replace {}-macros with the corresponding template rendering
|
||||
echo IDF_Template_safePregReplace('#\{(\w+)(?:,\s*([^\}]+))?\}#im',
|
||||
array($this, 'callbackMacros'),
|
||||
$text);
|
||||
}
|
||||
|
||||
public function callbackMacros($matches)
|
||||
{
|
||||
@list(, $macro, $opts) = $matches;
|
||||
$known_macros = array('projectlist');
|
||||
if (!in_array($macro, $known_macros)) {
|
||||
return $matches[0];
|
||||
}
|
||||
$callbackName = 'callback'.ucfirst(strtolower($macro)).'Macro';
|
||||
return $this->callbackProjectlistMacro($opts);
|
||||
}
|
||||
|
||||
public function callbackProjectlistMacro($opts)
|
||||
{
|
||||
$validOpts = array(
|
||||
'label' => '/^\d+|(\w+:)?\w+$/',
|
||||
'order' => '/^name|activity$/',
|
||||
'limit' => '/^\d+$/',
|
||||
);
|
||||
|
||||
$parsedOpts = array();
|
||||
// FIXME: no support for escaping yet in place
|
||||
$opts = preg_split('/\s*,\s*/', $opts, -1, PREG_SPLIT_NO_EMPTY);
|
||||
foreach ((array)@$opts as $opt)
|
||||
{
|
||||
list($key, $value) = preg_split('/\s*=\s*/', $opt, 2);
|
||||
if (!array_key_exists($key, $validOpts)) {
|
||||
continue;
|
||||
}
|
||||
if (!preg_match($validOpts[$key], $value)) {
|
||||
continue;
|
||||
}
|
||||
$parsedOpts[$key] = $value;
|
||||
}
|
||||
|
||||
$tag = false;
|
||||
if (!empty($parsedOpts['label'])) {
|
||||
if (is_numeric($parsedOpts['label'])) {
|
||||
$tag = Pluf::factory('IDF_Tag')->get($parsedOpts['label']);
|
||||
} else {
|
||||
@list($class, $name) = preg_split('/:/', $parsedOpts['label'], 2);
|
||||
if (empty($name)) {
|
||||
$name = $class;
|
||||
$class = IDF_TAG_DEFAULT_CLASS;
|
||||
}
|
||||
$sql = new Pluf_SQL('class=%s AND lcname=%s AND project=0',
|
||||
array(strtolower($class), mb_strtolower($name)));
|
||||
$tag = Pluf::factory('IDF_Tag')->getOne(array('filter' => $sql->gen()));
|
||||
}
|
||||
// ignore non-global tags
|
||||
if ($tag !== false && $tag->project > 0) {
|
||||
$tag = false;
|
||||
}
|
||||
}
|
||||
|
||||
$order = 'name';
|
||||
if (!empty($parsedOpts['order'])) {
|
||||
$order = $parsedOpts['order'];
|
||||
}
|
||||
|
||||
$projects = IDF_Views::getProjects($this->request->user, $tag, $order);
|
||||
if (!empty($parsedOpts['limit']) && $parsedOpts['limit'] < count($projects)) {
|
||||
// there is no array_slice on ArrayObject, do'h!
|
||||
$projectsCopy = array();
|
||||
for ($i=0; $i<$parsedOpts['limit']; ++$i)
|
||||
$projectsCopy[] = $projects[$i];
|
||||
$projects = $projectsCopy;
|
||||
}
|
||||
|
||||
$tmpl = new Pluf_Template('idf/project-list.html');
|
||||
$context = new Pluf_Template_Context(array(
|
||||
'projects' => $projects,
|
||||
'order' => 'name',
|
||||
));
|
||||
return $tmpl->render($context);
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ class IDF_Upload extends Pluf_Model
|
||||
$out .= sprintf(__('<a href="%1$s" title="View download">Download %2$d</a>, %3$s'), $url, $this->id, Pluf_esc($this->summary)).'</td>';
|
||||
$out .= '</tr>';
|
||||
$out .= "\n".'<tr class="extra"><td colspan="2">
|
||||
<div class="helptext right">'.sprintf(__('Addition of <a href="%s">download %d</a>, by %s'), $url, $this->id, $user).'</div></td></tr>';
|
||||
<div class="helptext right">'.sprintf(__('Addition of <a href="%1$s">download %2$d</a>, by %3$s'), $url, $this->id, $user).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
@ -211,7 +211,7 @@ class IDF_Upload extends Pluf_Model
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Download::view',
|
||||
array($request->project->shortname,
|
||||
$this->id));
|
||||
$title = sprintf(__('%s: Download %d added - %s'),
|
||||
$title = sprintf(__('%1$s: Download %2$d added - %3$s'),
|
||||
$request->project->name,
|
||||
$this->id, $this->summary);
|
||||
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
|
||||
@ -234,31 +234,91 @@ class IDF_Upload extends Pluf_Model
|
||||
*/
|
||||
public function notify($conf, $create=true)
|
||||
{
|
||||
if ('' == $conf->getVal('downloads_notification_email', '')) {
|
||||
return;
|
||||
}
|
||||
$current_locale = Pluf_Translation::getLocale();
|
||||
$langs = Pluf::f('languages', array('en'));
|
||||
Pluf_Translation::loadSetLocale($langs[0]);
|
||||
$project = $this->get_project();
|
||||
$url = str_replace(array('%p', '%d'),
|
||||
array($project->shortname, $this->id),
|
||||
$conf->getVal('upload_webhook_url', ''));
|
||||
|
||||
$context = new Pluf_Template_Context(
|
||||
array('file' => $this,
|
||||
'urlfile' => $this->getAbsoluteUrl($this->get_project()),
|
||||
'project' => $this->get_project(),
|
||||
$tags = array();
|
||||
foreach ($this->get_tags_list() as $tag) {
|
||||
$tags[] = $tag->class.':'.$tag->name;
|
||||
}
|
||||
|
||||
$submitter = $this->get_submitter();
|
||||
$payload = array(
|
||||
'to_send' => array(
|
||||
'project' => $project->shortname,
|
||||
'id' => $this->id,
|
||||
'summary' => $this->summary,
|
||||
'changelog' => $this->changelog,
|
||||
'filename' => $this->file,
|
||||
'filesize' => $this->filesize,
|
||||
'md5sum' => $this->md5,
|
||||
'submitter_login' => $submitter->login,
|
||||
'submitter_email' => $submitter->email,
|
||||
'tags' => $tags,
|
||||
),
|
||||
'project_id' => $project->id,
|
||||
'authkey' => $project->getWebHookKey(),
|
||||
'url' => $url,
|
||||
);
|
||||
|
||||
if ($create === true) {
|
||||
$payload['method'] = 'PUT';
|
||||
$payload['to_send']['creation_date'] = $this->creation_dtime;
|
||||
} else {
|
||||
$payload['method'] = 'POST';
|
||||
$payload['to_send']['update_date'] = $this->modif_dtime;
|
||||
}
|
||||
|
||||
$item = new IDF_Queue();
|
||||
$item->type = 'upload';
|
||||
$item->payload = $payload;
|
||||
$item->create();
|
||||
|
||||
$current_locale = Pluf_Translation::getLocale();
|
||||
|
||||
$from_email = Pluf::f('from_email');
|
||||
$messageId = '<'.md5('upload'.$this->id.md5(Pluf::f('secret_key'))).'@'.Pluf::f('mail_host', 'localhost').'>';
|
||||
$recipients = $project->getNotificationRecipientsForTab('downloads');
|
||||
|
||||
foreach ($recipients as $address => $language) {
|
||||
|
||||
if ($this->get_submitter()->email === $address) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pluf_Translation::loadSetLocale($language);
|
||||
|
||||
$context = new Pluf_Template_Context(array(
|
||||
'file' => $this,
|
||||
'urlfile' => $this->getAbsoluteUrl($project),
|
||||
'project' => $project,
|
||||
'tags' => $this->get_tags_list(),
|
||||
));
|
||||
$tmpl = new Pluf_Template('idf/downloads/download-created-email.txt');
|
||||
|
||||
$tplfile = 'idf/downloads/download-created-email.txt';
|
||||
$subject = __('New download - %1$s (%2$s)');
|
||||
$headers = array('Message-ID' => $messageId);
|
||||
if (!$create) {
|
||||
$tplfile = 'idf/downloads/download-updated-email.txt';
|
||||
$subject = __('Updated download - %1$s (%2$s)');
|
||||
$headers = array('References' => $messageId);
|
||||
}
|
||||
|
||||
$tmpl = new Pluf_Template($tplfile);
|
||||
$text_email = $tmpl->render($context);
|
||||
$addresses = explode(',', $conf->getVal('downloads_notification_email'));
|
||||
foreach ($addresses as $address) {
|
||||
$email = new Pluf_Mail(Pluf::f('from_email'),
|
||||
|
||||
$email = new Pluf_Mail($from_email,
|
||||
$address,
|
||||
sprintf(__('New download - %s (%s)'),
|
||||
sprintf($subject,
|
||||
$this->summary,
|
||||
$this->get_project()->shortname));
|
||||
$project->shortname));
|
||||
$email->addTextMessage($text_email);
|
||||
$email->addHeaders($headers);
|
||||
$email->sendMail();
|
||||
}
|
||||
|
||||
Pluf_Translation::loadSetLocale($current_locale);
|
||||
}
|
||||
}
|
||||
|
@ -32,20 +32,67 @@ Pluf::loadFunction('Pluf_Shortcuts_GetFormForModel');
|
||||
class IDF_Views
|
||||
{
|
||||
/**
|
||||
* List all the projects managed by InDefero.
|
||||
*
|
||||
* Only the public projects are listed or the private with correct
|
||||
* rights.
|
||||
* The index view.
|
||||
*/
|
||||
public function index($request, $match, $api=false)
|
||||
public function index($request, $match)
|
||||
{
|
||||
$projects = self::getProjects($request->user);
|
||||
$stats = self::getProjectsStatistics ($projects);
|
||||
$forge = IDF_Forge::instance();
|
||||
if (!$forge->isCustomForgePageEnabled()) {
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views::listProjects');
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
|
||||
if ($api == true) return $projects;
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/index.html',
|
||||
array('page_title' => __('Welcome'),
|
||||
'content' => $forge->getCustomForgePageContent(),
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all projects unfiltered
|
||||
*
|
||||
* @param unknown_type $request
|
||||
* @param unknown_type $match
|
||||
* @return Pluf_HTTP_Response
|
||||
*/
|
||||
public function listProjects($request, $match)
|
||||
{
|
||||
$match = array('', 'all', 'name');
|
||||
return $this->listProjectsByLabel($request, $match);
|
||||
}
|
||||
|
||||
/**
|
||||
* List projects, optionally filtered by label
|
||||
*
|
||||
* @param unknown_type $request
|
||||
* @param unknown_type $match
|
||||
* @return Pluf_HTTP_Response
|
||||
*/
|
||||
public function listProjectsByLabel($request, $match)
|
||||
{
|
||||
list(, $tagId, $order) = $match;
|
||||
|
||||
$tag = false;
|
||||
if ($tagId !== 'all') {
|
||||
$tag = Pluf::factory('IDF_Tag')->get($match[1]);
|
||||
// ignore non-global tags
|
||||
if ($tag !== false && $tag->project > 0) {
|
||||
$tag = false;
|
||||
}
|
||||
}
|
||||
$order = in_array($order, array('name', 'activity')) ? $order : 'name';
|
||||
|
||||
$projects = self::getProjects($request->user, $tag, $order);
|
||||
$stats = self::getProjectsStatistics($projects);
|
||||
$projectLabels = IDF_Forge::instance()->getProjectLabelsWithCounts();
|
||||
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/listProjects.html',
|
||||
array('page_title' => __('Projects'),
|
||||
'projects' => $projects,
|
||||
'projectLabels' => $projectLabels,
|
||||
'tag' => $tag,
|
||||
'order' => $order,
|
||||
'stats' => new Pluf_Template_ContextVars($stats)),
|
||||
$request);
|
||||
}
|
||||
@ -292,6 +339,22 @@ class IDF_Views
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Download archive FAQ.
|
||||
*/
|
||||
public function faqArchiveFormat($request, $match)
|
||||
{
|
||||
$title = __('InDefero Upload Archive Format');
|
||||
$projects = self::getProjects($request->user);
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/faq-archive-format.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'projects' => $projects,
|
||||
),
|
||||
$request);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* API FAQ.
|
||||
*/
|
||||
@ -309,43 +372,57 @@ class IDF_Views
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of projects accessible for the user.
|
||||
* Returns a list of projects accessible for the user and optionally filtered by tag.
|
||||
*
|
||||
* @param Pluf_User
|
||||
* @param IDF_Tag
|
||||
* @return ArrayObject IDF_Project
|
||||
*/
|
||||
public static function getProjects($user)
|
||||
public static function getProjects($user, $tag = false, $order = 'name')
|
||||
{
|
||||
$db =& Pluf::db();
|
||||
$false = Pluf_DB_BooleanToDb(false, $db);
|
||||
if ($user->isAnonymous()) {
|
||||
$sql = sprintf('%s=%s', $db->qn('private'), $false);
|
||||
return Pluf::factory('IDF_Project')->getList(array('filter'=> $sql,
|
||||
'order' => 'name ASC'));
|
||||
$sql = new Pluf_SQL(1);
|
||||
if ($tag !== false) {
|
||||
$sql->SAnd(new Pluf_SQL('idf_tag_id=%s', $tag->id));
|
||||
}
|
||||
if ($user->administrator) {
|
||||
return Pluf::factory('IDF_Project')->getList(array('order' => 'name ASC'));
|
||||
}
|
||||
// grab the list of projects where the user is admin, member
|
||||
// or authorized
|
||||
|
||||
if ($user->isAnonymous())
|
||||
{
|
||||
$authSql = new Pluf_SQL('private=%s', $false);
|
||||
$sql->SAnd($authSql);
|
||||
} else
|
||||
if (!$user->administrator) {
|
||||
// grab the list of projects where the user is admin,
|
||||
// member or authorized
|
||||
$perms = array(
|
||||
Pluf_Permission::getFromString('IDF.project-member'),
|
||||
Pluf_Permission::getFromString('IDF.project-owner'),
|
||||
Pluf_Permission::getFromString('IDF.project-authorized-user')
|
||||
);
|
||||
$sql = new Pluf_SQL("model_class='IDF_Project' AND owner_class='Pluf_User' AND owner_id=%s AND negative=".$false, $user->id);
|
||||
$rows = Pluf::factory('Pluf_RowPermission')->getList(array('filter' => $sql->gen()));
|
||||
$permSql = new Pluf_SQL("model_class='IDF_Project' AND owner_class='Pluf_User' AND owner_id=%s AND negative=".$false, $user->id);
|
||||
$rows = Pluf::factory('Pluf_RowPermission')->getList(array('filter' => $permSql->gen()));
|
||||
|
||||
$sql = sprintf('%s=%s', $db->qn('private'), $false);
|
||||
$authSql = new Pluf_SQL('private=%s', $false);
|
||||
if ($rows->count() > 0) {
|
||||
$ids = array();
|
||||
foreach ($rows as $row) {
|
||||
$ids[] = $row->model_id;
|
||||
}
|
||||
$sql .= sprintf(' OR id IN (%s)', implode(', ', $ids));
|
||||
$authSql->SOr(new Pluf_SQL(sprintf($db->pfx.'idf_projects.id IN (%s)', implode(', ', $ids))));
|
||||
}
|
||||
return Pluf::factory('IDF_Project')->getList(array('filter' => $sql,
|
||||
'order' => 'name ASC'));
|
||||
$sql->SAnd($authSql);
|
||||
}
|
||||
|
||||
$orderTypes = array(
|
||||
'name' => 'name ASC',
|
||||
'activity' => 'value DESC, name ASC',
|
||||
);
|
||||
return Pluf::factory('IDF_Project')->getList(array(
|
||||
'filter'=> $sql->gen(),
|
||||
'view' => 'join_activities_and_tags',
|
||||
'order' => $orderTypes[$order],
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -373,9 +450,6 @@ class IDF_Views
|
||||
$forgestats['commits'] += $pstats['commits'];
|
||||
}
|
||||
|
||||
// Count projects
|
||||
$forgestats['projects'] = count($projects);
|
||||
|
||||
// Count members
|
||||
$sql = new Pluf_SQL('first_name != %s', array('---'));
|
||||
$forgestats['members'] = Pluf::factory('Pluf_User')
|
||||
|
@ -32,17 +32,37 @@ Pluf::loadFunction('Pluf_Shortcuts_GetFormForModel');
|
||||
class IDF_Views_Admin
|
||||
{
|
||||
/**
|
||||
* Home page of the administration.
|
||||
*
|
||||
* It should provide an overview of the forge status.
|
||||
* Start page of the administration.
|
||||
*/
|
||||
public $home_precond = array('Pluf_Precondition::staffRequired');
|
||||
public function home($request, $match)
|
||||
public $forge_precond = array('Pluf_Precondition::staffRequired');
|
||||
public function forge($request, $match)
|
||||
{
|
||||
$title = __('Forge Management');
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/gadmin/home.html',
|
||||
$forge = IDF_Forge::instance();
|
||||
if ($request->method == 'POST') {
|
||||
$form = new IDF_Form_Admin_ForgeConf($request->POST);
|
||||
if ($form->isValid()) {
|
||||
$forge->setCustomForgePageEnabled($form->cleaned_data['enabled']);
|
||||
$forge->setCustomForgePageContent($form->cleaned_data['content']);
|
||||
$request->user->setMessage(__('The forge configuration has been saved.'));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Admin::forge');
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
} else {
|
||||
$params = array();
|
||||
$params['enabled'] = $forge->isCustomForgePageEnabled();
|
||||
if (($content = $forge->getCustomForgePageContent(false)) !== false) {
|
||||
$params['content'] = $content;
|
||||
}
|
||||
if (count($params) == 0) {
|
||||
$params = null; //Nothing in the db, so new form.
|
||||
}
|
||||
$form = new IDF_Form_Admin_ForgeConf($params);
|
||||
}
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/gadmin/forge/index.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'form' => $form,
|
||||
),
|
||||
$request);
|
||||
}
|
||||
@ -81,6 +101,40 @@ class IDF_Views_Admin
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Administrate the labels of a project.
|
||||
*/
|
||||
public $projectLabels_precond = array('Pluf_Precondition::staffRequired');
|
||||
public function projectLabels($request, $match)
|
||||
{
|
||||
$title = __('Project Labels');
|
||||
$forge = IDF_Forge::instance();
|
||||
if ($request->method == 'POST') {
|
||||
$form = new IDF_Form_Admin_LabelConf($request->POST);
|
||||
if ($form->isValid()) {
|
||||
$forge->setProjectLabels($form->cleaned_data['project_labels']);
|
||||
$request->user->setMessage(__('The label configuration has been saved.'));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Admin::projectLabels');
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
} else {
|
||||
$params = array();
|
||||
if (($labels = $forge->getProjectLabels(false)) !== false) {
|
||||
$params['project_labels'] = $labels;
|
||||
}
|
||||
if (count($params) == 0) {
|
||||
$params = null; //Nothing in the db, so new form.
|
||||
}
|
||||
$form = new IDF_Form_Admin_LabelConf($params);
|
||||
}
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/gadmin/projects/labels.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'form' => $form,
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edition of a project.
|
||||
*
|
||||
@ -106,12 +160,16 @@ class IDF_Views_Admin
|
||||
} else {
|
||||
$form = new IDF_Form_Admin_ProjectUpdate(null, $params);
|
||||
}
|
||||
$arrays = IDF_Views_Project::autoCompleteArrays();
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/gadmin/projects/update.html',
|
||||
array_merge(
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'project' => $project,
|
||||
'form' => $form,
|
||||
),
|
||||
$arrays
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
@ -139,12 +197,17 @@ class IDF_Views_Admin
|
||||
$form = new IDF_Form_Admin_ProjectCreate(null, $extra);
|
||||
}
|
||||
$base = Pluf::f('url_base').Pluf::f('idf_base').'/p/';
|
||||
|
||||
$arrays = IDF_Views_Project::autoCompleteArrays();
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/gadmin/projects/create.html',
|
||||
array_merge(
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'form' => $form,
|
||||
'base_url' => $base,
|
||||
),
|
||||
$arrays
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
|
@ -93,8 +93,7 @@ class IDF_Views_Api
|
||||
|
||||
public function projectIndex($request, $match)
|
||||
{
|
||||
$view = new IDF_Views();
|
||||
$projects = $view->index($request, $match, true);
|
||||
$projects = IDF_Views::getProjects($request->user);
|
||||
|
||||
$data = array();
|
||||
foreach ($projects as $p) {
|
||||
|
@ -205,7 +205,8 @@ class IDF_Views_Download
|
||||
$path = $upload->getFullPath();
|
||||
$mime = IDF_FileUtil::getMimeType($path);
|
||||
$render = new Pluf_HTTP_Response_File($path, $mime[0]);
|
||||
$render->headers["Content-MD5"] = $upload->md5;
|
||||
$render->headers['Content-MD5'] = $upload->md5;
|
||||
$render->headers['Content-Disposition'] = 'attachment; filename="'.$upload->file.'"';
|
||||
return $render;
|
||||
}
|
||||
|
||||
@ -224,11 +225,11 @@ class IDF_Views_Download
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a new file for download.
|
||||
* Create a new file for download.
|
||||
*/
|
||||
public $submit_precond = array('IDF_Precondition::accessDownloads',
|
||||
public $create_precond = array('IDF_Precondition::accessDownloads',
|
||||
'IDF_Precondition::projectMemberOrOwner');
|
||||
public function submit($request, $match)
|
||||
public function create($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$title = __('New Download');
|
||||
@ -250,7 +251,7 @@ class IDF_Views_Download
|
||||
array('project' => $prj,
|
||||
'user' => $request->user));
|
||||
}
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/downloads/submit.html',
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/downloads/create.html',
|
||||
array(
|
||||
'auto_labels' => self::autoCompleteArrays($prj),
|
||||
'page_title' => $title,
|
||||
@ -259,6 +260,39 @@ class IDF_Views_Download
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new downloads from an uploaded archive.
|
||||
*/
|
||||
public $createFromArchive_precond = array('IDF_Precondition::accessDownloads',
|
||||
'IDF_Precondition::projectMemberOrOwner');
|
||||
public function createFromArchive($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$title = __('New Downloads from Archive');
|
||||
if ($request->method == 'POST') {
|
||||
$form = new IDF_Form_UploadArchive(array_merge($request->POST, $request->FILES),
|
||||
array('project' => $prj,
|
||||
'user' => $request->user));
|
||||
if ($form->isValid()) {
|
||||
$upload = $form->save();
|
||||
$request->user->setMessage(__('The archive has been uploaded and processed.'));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::index',
|
||||
array($prj->shortname));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
} else {
|
||||
$form = new IDF_Form_UploadArchive(null,
|
||||
array('project' => $prj,
|
||||
'user' => $request->user));
|
||||
}
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/downloads/createFromArchive.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'form' => $form,
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the autocomplete arrays for the little AJAX stuff.
|
||||
*/
|
||||
|
@ -119,6 +119,7 @@ class IDF_Views_Issue
|
||||
}
|
||||
$ownerStatistics[$key] = array($nb, (int)(100 * $nb / $opened), $login);
|
||||
}
|
||||
arsort($ownerStatistics);
|
||||
|
||||
// Issue class tag statistics
|
||||
$grouped_tags = $prj->getTagCloud();
|
||||
@ -126,6 +127,12 @@ class IDF_Views_Issue
|
||||
foreach ($tags as $tag) {
|
||||
$tagStatistics[$class][$tag->name] = array($tag->nb_use, $tag->id);
|
||||
}
|
||||
uasort($tagStatistics[$class], function ($a, $b) {
|
||||
if ($a[0] === $b[0])
|
||||
return 0;
|
||||
|
||||
return ($a[0] > $b[0]) ? -1 : 1;
|
||||
});
|
||||
}
|
||||
foreach($tagStatistics as $k => $v) {
|
||||
$nbIssueInClass = 0;
|
||||
@ -136,10 +143,6 @@ class IDF_Views_Issue
|
||||
$tagStatistics[$k][$kk] = array($vv[0], (int)(100 * $vv[0] / $nbIssueInClass), $vv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort
|
||||
krsort($tagStatistics);
|
||||
arsort($ownerStatistics);
|
||||
}
|
||||
}
|
||||
|
||||
@ -338,19 +341,19 @@ class IDF_Views_Issue
|
||||
if (count($ctags) == 0) $ctags[] = 0;
|
||||
switch ($match[3]) {
|
||||
case 'submit':
|
||||
$titleFormat = __('%s %s Submitted %s Issues');
|
||||
$titleFormat = __('%1$s %2$s Submitted %3$s Issues');
|
||||
$f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
|
||||
break;
|
||||
case 'submitclosed':
|
||||
$titleFormat = __('%s %s Closed Submitted %s Issues');
|
||||
$titleFormat = __('%1$s %2$s Closed Submitted %3$s Issues');
|
||||
$f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
|
||||
break;
|
||||
case 'ownerclosed':
|
||||
$titleFormat = __('%s %s Closed Working %s Issues');
|
||||
$titleFormat = __('%1$s %2$s Closed Working %3$s Issues');
|
||||
$f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
|
||||
break;
|
||||
default:
|
||||
$titleFormat = __('%s %s Working %s Issues');
|
||||
$titleFormat = __('%1$s %2$s Working %3$s Issues');
|
||||
$f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
|
||||
break;
|
||||
}
|
||||
@ -427,7 +430,7 @@ class IDF_Views_Issue
|
||||
array($prj->shortname, $issue->id));
|
||||
$issue->notify($request->conf);
|
||||
if ($api) return $issue;
|
||||
$request->user->setMessage(sprintf(__('<a href="%s">Issue %d</a> has been created.'), $url, $issue->id));
|
||||
$request->user->setMessage(sprintf(__('<a href="%1$s">Issue %2$d</a> has been created.'), $url, $issue->id));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
} else {
|
||||
@ -598,7 +601,7 @@ class IDF_Views_Issue
|
||||
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
|
||||
array($prj->shortname, $issue->id));
|
||||
$title = Pluf_Template::markSafe(sprintf(__('Issue <a href="%s">%d</a>: %s'), $url, $issue->id, $issue->summary));
|
||||
$title = Pluf_Template::markSafe(sprintf(__('Issue <a href="%1$s">%2$d</a>: %3$s'), $url, $issue->id, $issue->summary));
|
||||
$form = false; // The form is available only if logged in.
|
||||
$starred = false;
|
||||
$closed = in_array($issue->status, $prj->getTagIdsByStatus('closed'));
|
||||
@ -622,7 +625,7 @@ class IDF_Views_Issue
|
||||
$issue->notify($request->conf, false);
|
||||
$comments = $issue->get_comments_list(array('order' => 'id DESC'));
|
||||
$url .= '#ic' . $comments[0]->id;
|
||||
$request->user->setMessage(sprintf(__('<a href="%s">Issue %d</a> has been updated.'), $url, $issue->id));
|
||||
$request->user->setMessage(sprintf(__('<a href="%1$s">Issue %2$d</a> has been updated.'), $url, $issue->id));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
} else {
|
||||
@ -993,9 +996,8 @@ class IDF_Views_Issue
|
||||
$r = $project->getRelationsFromConfig();
|
||||
$auto['auto_relation_types'] = '';
|
||||
foreach ($r as $rt) {
|
||||
$esc = Pluf_esc($rt);
|
||||
$auto['auto_relation_types'] .= sprintf('{ name: "%s", to: "%s" }, ',
|
||||
$esc, $esc);
|
||||
Pluf_esc(__($rt)), Pluf_esc($rt));
|
||||
}
|
||||
$auto['auto_relation_types'] = substr($auto['auto_relation_types'], 0, -2);
|
||||
return $auto;
|
||||
|
@ -68,7 +68,7 @@ class IDF_Views_Project
|
||||
$pages = array();
|
||||
if ($request->rights['hasWikiAccess']) {
|
||||
$tags = IDF_Views_Wiki::getWikiTags($prj);
|
||||
$pages = $tags[0]->get_idf_wikipage_list();
|
||||
$pages = $tags[0]->get_idf_wiki_page_list();
|
||||
}
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/project/home.html',
|
||||
array(
|
||||
@ -131,8 +131,10 @@ class IDF_Views_Project
|
||||
}
|
||||
if (true === IDF_Precondition::accessWiki($request) &&
|
||||
($model_filter == 'all' || $model_filter == 'documents')) {
|
||||
$classes[] = '\'IDF_WikiPage\'';
|
||||
$classes[] = '\'IDF_WikiRevision\'';
|
||||
$classes[] = '\'IDF_Wiki_Page\'';
|
||||
$classes[] = '\'IDF_Wiki_PageRevision\'';
|
||||
$classes[] = '\'IDF_Wiki_Resource\'';
|
||||
$classes[] = '\'IDF_Wiki_ResourceRevision\'';
|
||||
}
|
||||
if (true === IDF_Precondition::accessReview($request) &&
|
||||
($model_filter == 'all' || $model_filter == 'reviews')) {
|
||||
@ -305,17 +307,21 @@ class IDF_Views_Project
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
} else {
|
||||
$form = new IDF_Form_ProjectConf($prj->getData(), $extra);
|
||||
$form = new IDF_Form_ProjectConf(null, $extra);
|
||||
}
|
||||
|
||||
$logo = $prj->getConf()->getVal('logo');
|
||||
$arrays = self::autoCompleteArrays();
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/admin/summary.html',
|
||||
array_merge(
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'form' => $form,
|
||||
'project' => $prj,
|
||||
'logo' => $logo,
|
||||
),
|
||||
$arrays
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
@ -375,8 +381,11 @@ class IDF_Views_Project
|
||||
$title = sprintf(__('%s Downloads Configuration'), (string) $prj);
|
||||
$conf = new IDF_Conf();
|
||||
$conf->setProject($prj);
|
||||
$extra = array(
|
||||
'conf' => $conf,
|
||||
);
|
||||
if ($request->method == 'POST') {
|
||||
$form = new IDF_Form_UploadConf($request->POST);
|
||||
$form = new IDF_Form_UploadConf($request->POST, $extra);
|
||||
if ($form->isValid()) {
|
||||
foreach ($form->cleaned_data as $key=>$val) {
|
||||
$conf->setVal($key, $val);
|
||||
@ -388,7 +397,7 @@ class IDF_Views_Project
|
||||
}
|
||||
} else {
|
||||
$params = array();
|
||||
$keys = array('labels_download_predefined', 'labels_download_one_max');
|
||||
$keys = array('labels_download_predefined', 'labels_download_one_max', 'upload_webhook_url');
|
||||
foreach ($keys as $key) {
|
||||
$_val = $conf->getVal($key, false);
|
||||
if ($_val !== false) {
|
||||
@ -398,12 +407,13 @@ class IDF_Views_Project
|
||||
if (count($params) == 0) {
|
||||
$params = null; //Nothing in the db, so new form.
|
||||
}
|
||||
$form = new IDF_Form_UploadConf($params);
|
||||
$form = new IDF_Form_UploadConf($params, $extra);
|
||||
}
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/admin/downloads.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'form' => $form,
|
||||
'hookkey' => $prj->getWebHookKey(),
|
||||
),
|
||||
$request);
|
||||
}
|
||||
@ -504,21 +514,24 @@ class IDF_Views_Project
|
||||
}
|
||||
}
|
||||
$form->save(); // Save the authorized users.
|
||||
$request->user->setMessage(__('The project tabs access rights have been saved.'));
|
||||
$request->user->setMessage(__('The project tabs access rights and notification settings have been saved.'));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Project::adminTabs',
|
||||
array($prj->shortname));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
} else {
|
||||
$params = array();
|
||||
$keys = array('downloads_access_rights', 'source_access_rights',
|
||||
'issues_access_rights', 'review_access_rights',
|
||||
'wiki_access_rights',
|
||||
'downloads_notification_email',
|
||||
'review_notification_email',
|
||||
'wiki_notification_email',
|
||||
'source_notification_email',
|
||||
'issues_notification_email');
|
||||
$sections = array('downloads', 'wiki', 'source', 'issues', 'review');
|
||||
$keys = array();
|
||||
|
||||
foreach ($sections as $section) {
|
||||
$keys[] = $section.'_access_rights';
|
||||
$keys[] = $section.'_notification_owners_enabled';
|
||||
$keys[] = $section.'_notification_members_enabled';
|
||||
$keys[] = $section.'_notification_email_enabled';
|
||||
$keys[] = $section.'_notification_email';
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$_val = $request->conf->getVal($key, false);
|
||||
if ($_val !== false) {
|
||||
@ -590,6 +603,10 @@ class IDF_Views_Project
|
||||
'mtn' => __('monotone'),
|
||||
);
|
||||
$repository_type = $options[$scm];
|
||||
$hook_request_method = 'PUT';
|
||||
if (Pluf::f('webhook_processing','') === 'compat') {
|
||||
$hook_request_method = 'POST';
|
||||
}
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/admin/source.html',
|
||||
array(
|
||||
'remote_svn' => $remote_svn,
|
||||
@ -598,8 +615,41 @@ class IDF_Views_Project
|
||||
'repository_size' => $prj->getRepositorySize(),
|
||||
'page_title' => $title,
|
||||
'form' => $form,
|
||||
'hookkey' => $prj->getPostCommitHookKey(),
|
||||
'hookkey' => $prj->getWebHookKey(),
|
||||
'hook_request_method' => $hook_request_method,
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the autocomplete arrays for the little AJAX stuff.
|
||||
*/
|
||||
public static function autoCompleteArrays()
|
||||
{
|
||||
$forge = IDF_Forge::instance();
|
||||
$labels = $forge->getProjectLabels(IDF_Form_Admin_LabelConf::init_project_labels);
|
||||
|
||||
$auto = array('auto_labels' => '');
|
||||
$auto_raw = array('auto_labels' => $labels);
|
||||
foreach ($auto_raw as $key => $st) {
|
||||
$st = preg_split("/\015\012|\015|\012/", $st, -1, PREG_SPLIT_NO_EMPTY);
|
||||
foreach ($st as $s) {
|
||||
$v = '';
|
||||
$d = '';
|
||||
$_s = explode('=', $s, 2);
|
||||
if (count($_s) > 1) {
|
||||
$v = trim($_s[0]);
|
||||
$d = trim($_s[1]);
|
||||
} else {
|
||||
$v = trim($_s[0]);
|
||||
}
|
||||
$auto[$key] .= sprintf('{ name: "%s", to: "%s" }, ',
|
||||
Pluf_esc($d),
|
||||
Pluf_esc($v));
|
||||
}
|
||||
$auto[$key] = substr($auto[$key], 0, -2);
|
||||
}
|
||||
|
||||
return $auto;
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ class IDF_Views_Review
|
||||
$review = $form->save();
|
||||
$urlr = Pluf_HTTP_URL_urlForView('IDF_Views_Review::view',
|
||||
array($prj->shortname, $review->id));
|
||||
$request->user->setMessage(sprintf(__('The <a href="%s">code review %d</a> has been created.'), $urlr, $review->id));
|
||||
$request->user->setMessage(sprintf(__('The <a href="%1$s">code review %2$d</a> has been created.'), $urlr, $review->id));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Review::index',
|
||||
array($prj->shortname));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
@ -137,7 +137,7 @@ class IDF_Views_Review
|
||||
$prj->inOr404($review);
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Review::view',
|
||||
array($prj->shortname, $review->id));
|
||||
$title = Pluf_Template::markSafe(sprintf(__('Review <a href="%s">%d</a>: %s'), $url, $review->id, $review->summary));
|
||||
$title = Pluf_Template::markSafe(sprintf(__('Review <a href="%1$s">%2$d</a>: %3$s'), $url, $review->id, $review->summary));
|
||||
|
||||
$patches = $review->get_patches_list();
|
||||
$patch = $patches[0];
|
||||
@ -157,7 +157,7 @@ class IDF_Views_Review
|
||||
$review = $patch->get_review();
|
||||
$urlr = Pluf_HTTP_URL_urlForView('IDF_Views_Review::view',
|
||||
array($prj->shortname, $review->id));
|
||||
$request->user->setMessage(sprintf(__('Your <a href="%s">code review %d</a> has been published.'), $urlr, $review->id));
|
||||
$request->user->setMessage(sprintf(__('Your <a href="%1$s">code review %2$d</a> has been published.'), $urlr, $review->id));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Review::index',
|
||||
array($prj->shortname));
|
||||
$review_comment->notify($request->conf);
|
||||
|
@ -132,6 +132,12 @@ class IDF_Views_Source
|
||||
$request);
|
||||
}
|
||||
|
||||
public function repository($request, $match)
|
||||
{
|
||||
$scm = IDF_Scm::get($request->project);
|
||||
return $scm->repository($request, $match);
|
||||
}
|
||||
|
||||
public $treeBase_precond = array('IDF_Precondition::accessSource',
|
||||
'IDF_Views_Source_Precondition::scmAvailable',
|
||||
'IDF_Views_Source_Precondition::revisionValid');
|
||||
@ -302,7 +308,7 @@ class IDF_Views_Source
|
||||
throw new Exception('could not retrieve commit object for '. $commit);
|
||||
}
|
||||
$title = sprintf(__('%s Commit Details'), (string) $request->project);
|
||||
$page_title = sprintf(__('%s Commit Details - %s'), (string) $request->project, $commit);
|
||||
$page_title = sprintf(__('%1$s Commit Details - %2$s'), (string) $request->project, $commit);
|
||||
$rcommit = IDF_Commit::getOrAdd($cobject, $request->project);
|
||||
$diff = new IDF_Diff($cobject->diff, $scm->getDiffPathStripLevel());
|
||||
$cobject->diff = null;
|
||||
@ -506,12 +512,12 @@ function IDF_Views_Source_PrettySizeSimple($size)
|
||||
function IDF_Views_Source_ShortenString($string, $length)
|
||||
{
|
||||
$ellipse = "...";
|
||||
$length = max(strlen($ellipse) + 2, $length);
|
||||
$length = max(mb_strlen($ellipse) + 2, $length);
|
||||
$preflen = ceil($length / 10);
|
||||
|
||||
if (mb_strlen($string) < $length)
|
||||
return $string;
|
||||
|
||||
return substr($string, 0, $preflen).$ellipse.
|
||||
substr($string, -($length - $preflen - mb_strlen($ellipse)));
|
||||
return mb_substr($string, 0, $preflen).$ellipse.
|
||||
mb_substr($string, -($length - $preflen - mb_strlen($ellipse)));
|
||||
}
|
||||
|
@ -32,22 +32,22 @@ Pluf::loadFunction('Pluf_Shortcuts_GetFormForModel');
|
||||
class IDF_Views_Wiki
|
||||
{
|
||||
/**
|
||||
* View list of issues for a given project.
|
||||
* View list of pages for a given project.
|
||||
*/
|
||||
public $index_precond = array('IDF_Precondition::accessWiki');
|
||||
public function index($request, $match, $api=false)
|
||||
public $listPages_precond = array('IDF_Precondition::accessWiki');
|
||||
public function listPages($request, $match, $api=false)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$title = sprintf(__('%s Documentation'), (string) $prj);
|
||||
// Paginator to paginate the pages
|
||||
$pag = new Pluf_Paginator(new IDF_WikiPage());
|
||||
$pag = new Pluf_Paginator(new IDF_Wiki_Page());
|
||||
$pag->class = 'recent-issues';
|
||||
$pag->item_extra_props = array('project_m' => $prj,
|
||||
'shortname' => $prj->shortname,
|
||||
'current_user' => $request->user);
|
||||
$pag->summary = __('This table shows the documentation pages.');
|
||||
$pag->action = array('IDF_Views_Wiki::index', array($prj->shortname));
|
||||
$pag->edit_action = array('IDF_Views_Wiki::view', 'shortname', 'title');
|
||||
$pag->action = array('IDF_Views_Wiki::listPages', array($prj->shortname));
|
||||
$pag->edit_action = array('IDF_Views_Wiki::viewPage', 'shortname', 'title');
|
||||
$sql = 'project=%s';
|
||||
$ptags = self::getWikiTags($prj);
|
||||
$dtag = array_pop($ptags); // The last tag is the deprecated tag.
|
||||
@ -67,7 +67,7 @@ class IDF_Views_Wiki
|
||||
$pag->no_results_text = __('No documentation pages were found.');
|
||||
$pag->sort_order = array('title', 'ASC');
|
||||
$pag->setFromRequest($request);
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/index.html',
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/listPages.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'pages' => $pag,
|
||||
@ -77,18 +77,56 @@ class IDF_Views_Wiki
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* View list of resources for a given project.
|
||||
*/
|
||||
public $listResources_precond = array('IDF_Precondition::accessWiki',
|
||||
'Pluf_Precondition::loginRequired');
|
||||
public function listResources($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$title = sprintf(__('%s Documentation Resources'), (string) $prj);
|
||||
$pag = new Pluf_Paginator(new IDF_Wiki_Resource());
|
||||
$pag->class = 'recent-issues';
|
||||
$pag->item_extra_props = array('project_m' => $prj,
|
||||
'shortname' => $prj->shortname,
|
||||
'current_user' => $request->user);
|
||||
$pag->summary = __('This table shows the resources that can be used on documentation pages.');
|
||||
$pag->action = array('IDF_Views_Wiki::listResources', array($prj->shortname));
|
||||
$pag->edit_action = array('IDF_Views_Wiki::viewResource', 'shortname', 'title');
|
||||
$pag->forced_where = new Pluf_SQL('project=%s', array($prj->id));
|
||||
$pag->extra_classes = array('right', 'a-c', 'left', 'a-c');
|
||||
$list_display = array(
|
||||
'title' => __('Resource Title'),
|
||||
'mime_type' => __('MIME type'),
|
||||
'summary' => __('Description'),
|
||||
array('modif_dtime', 'Pluf_Paginator_DateYMD', __('Updated')),
|
||||
);
|
||||
$pag->configure($list_display, array(), array('title', 'modif_dtime'));
|
||||
$pag->items_per_page = 25;
|
||||
$pag->no_results_text = __('No resources were found.');
|
||||
$pag->sort_order = array('title', 'ASC');
|
||||
$pag->setFromRequest($request);
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/listResources.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'resources' => $pag,
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
public $search_precond = array('IDF_Precondition::accessWiki',);
|
||||
public function search($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
if (!isset($request->REQUEST['q']) or trim($request->REQUEST['q']) == '') {
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::index',
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listPages',
|
||||
array($prj->shortname));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
$q = $request->REQUEST['q'];
|
||||
$title = sprintf(__('Documentation Search - %s'), $q);
|
||||
$pages = new Pluf_Search_ResultSet(IDF_Search::mySearch($q, $prj, 'IDF_WikiPage'));
|
||||
$pages = new Pluf_Search_ResultSet(IDF_Search::mySearch($q, $prj, 'IDF_Wiki_Page'));
|
||||
if (count($pages) > 100) {
|
||||
$pages->results = array_slice($pages->results, 0, 100);
|
||||
}
|
||||
@ -100,7 +138,7 @@ class IDF_Views_Wiki
|
||||
'current_user' => $request->user);
|
||||
$pag->summary = __('This table shows the pages found.');
|
||||
$pag->action = array('IDF_Views_Wiki::search', array($prj->shortname), array('q'=> $q));
|
||||
$pag->edit_action = array('IDF_Views_Wiki::view', 'shortname', 'title');
|
||||
$pag->edit_action = array('IDF_Views_Wiki::viewPage', 'shortname', 'title');
|
||||
$pag->extra_classes = array('right', '', 'a-c');
|
||||
$list_display = array(
|
||||
'title' => __('Page Title'),
|
||||
@ -122,8 +160,8 @@ class IDF_Views_Wiki
|
||||
/**
|
||||
* View list of pages with a given label.
|
||||
*/
|
||||
public $listLabel_precond = array('IDF_Precondition::accessWiki');
|
||||
public function listLabel($request, $match)
|
||||
public $listPagesWithLabel_precond = array('IDF_Precondition::accessWiki');
|
||||
public function listPagesWithLabel($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$tag = Pluf_Shortcuts_GetObjectOr404('IDF_Tag', $match[2]);
|
||||
@ -133,15 +171,15 @@ class IDF_Views_Wiki
|
||||
// Paginator to paginate the pages
|
||||
$ptags = self::getWikiTags($prj);
|
||||
$dtag = array_pop($ptags); // The last tag is the deprecated tag.
|
||||
$pag = new Pluf_Paginator(new IDF_WikiPage());
|
||||
$pag = new Pluf_Paginator(new IDF_Wiki_Page());
|
||||
$pag->model_view = 'join_tags';
|
||||
$pag->class = 'recent-issues';
|
||||
$pag->item_extra_props = array('project_m' => $prj,
|
||||
'shortname' => $prj->shortname);
|
||||
$pag->summary = sprintf(__('This table shows the documentation pages with label %s.'), (string) $tag);
|
||||
$pag->forced_where = new Pluf_SQL('project=%s AND idf_tag_id=%s', array($prj->id, $tag->id));
|
||||
$pag->action = array('IDF_Views_Wiki::listLabel', array($prj->shortname, $tag->id));
|
||||
$pag->edit_action = array('IDF_Views_Wiki::view', 'shortname', 'title');
|
||||
$pag->action = array('IDF_Views_Wiki::listPagesWithLabel', array($prj->shortname, $tag->id));
|
||||
$pag->edit_action = array('IDF_Views_Wiki::viewPage', 'shortname', 'title');
|
||||
$pag->extra_classes = array('right', '', 'a-c');
|
||||
$list_display = array(
|
||||
'title' => __('Page Title'),
|
||||
@ -152,7 +190,7 @@ class IDF_Views_Wiki
|
||||
$pag->items_per_page = 25;
|
||||
$pag->no_results_text = __('No documentation pages were found.');
|
||||
$pag->setFromRequest($request);
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/index.html',
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/listPages.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'label' => $tag,
|
||||
@ -165,24 +203,25 @@ class IDF_Views_Wiki
|
||||
/**
|
||||
* Create a new documentation page.
|
||||
*/
|
||||
public $create_precond = array('IDF_Precondition::accessWiki',
|
||||
public $createPage_precond = array('IDF_Precondition::accessWiki',
|
||||
'Pluf_Precondition::loginRequired');
|
||||
public function create($request, $match)
|
||||
public function createPage($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$title = __('New Page');
|
||||
$preview = false;
|
||||
if ($request->method == 'POST') {
|
||||
$form = new IDF_Form_WikiCreate($request->POST,
|
||||
$form = new IDF_Form_WikiPageCreate($request->POST,
|
||||
array('project' => $prj,
|
||||
'user' => $request->user
|
||||
));
|
||||
if ($form->isValid() and !isset($request->POST['preview'])) {
|
||||
$page = $form->save();
|
||||
$urlpage = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||
$urlpage = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
|
||||
array($prj->shortname, $page->title));
|
||||
$request->user->setMessage(sprintf(__('The page <a href="%s">%s</a> has been created.'), $urlpage, Pluf_esc($page->title)));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::index',
|
||||
$request->user->setMessage(sprintf(__('The page <a href="%1$s">%2$s</a> has been created.'),
|
||||
$urlpage, Pluf_esc($page->title)));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listPages',
|
||||
array($prj->shortname));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
} elseif (isset($request->POST['preview'])) {
|
||||
@ -191,12 +230,12 @@ class IDF_Views_Wiki
|
||||
} else {
|
||||
$pagename = (isset($request->GET['name'])) ?
|
||||
$request->GET['name'] : '';
|
||||
$form = new IDF_Form_WikiCreate(null,
|
||||
$form = new IDF_Form_WikiPageCreate(null,
|
||||
array('name' => $pagename,
|
||||
'project' => $prj,
|
||||
'user' => $request->user));
|
||||
}
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/create.html',
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/createPage.html',
|
||||
array(
|
||||
'auto_labels' => self::autoCompleteArrays($prj),
|
||||
'page_title' => $title,
|
||||
@ -206,27 +245,65 @@ class IDF_Views_Wiki
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new resource.
|
||||
*/
|
||||
public $createResource_precond = array('IDF_Precondition::accessWiki',
|
||||
'Pluf_Precondition::loginRequired');
|
||||
public function createResource($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$title = __('New Resource');
|
||||
$preview = false;
|
||||
if ($request->method == 'POST') {
|
||||
$form = new IDF_Form_WikiResourceCreate(array_merge($request->POST, $request->FILES),
|
||||
array('project' => $prj, 'user' => $request->user));
|
||||
if ($form->isValid()) {
|
||||
$resource = $form->save();
|
||||
$urlresource = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource',
|
||||
array($prj->shortname, $resource->title));
|
||||
$request->user->setMessage(sprintf(__('The resource <a href="%1$s">%2$s</a> has been created.'), $urlresource, Pluf_esc($resource->title)));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listResources',
|
||||
array($prj->shortname));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
} else {
|
||||
$resourcename = (isset($request->GET['name'])) ?
|
||||
$request->GET['name'] : '';
|
||||
$form = new IDF_Form_WikiResourceCreate(null,
|
||||
array('name' => $resourcename,
|
||||
'project' => $prj, 'user' => $request->user));
|
||||
}
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/createResource.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'form' => $form,
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* View a documentation page.
|
||||
*/
|
||||
public $view_precond = array('IDF_Precondition::accessWiki');
|
||||
public function view($request, $match)
|
||||
public $viewPage_precond = array('IDF_Precondition::accessWiki');
|
||||
public function viewPage($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
// Find the page
|
||||
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||
array($prj->id, $match[2]));
|
||||
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
|
||||
$pages = Pluf::factory('IDF_Wiki_Page')->getList(array('filter'=>$sql->gen()));
|
||||
if ($pages->count() != 1) {
|
||||
return new Pluf_HTTP_Response_NotFound($request);
|
||||
}
|
||||
$page = $pages[0];
|
||||
$oldrev = false;
|
||||
$revision = $page->get_current_revision();
|
||||
|
||||
// We grab the old revision if requested.
|
||||
if (isset($request->GET['rev']) and preg_match('/^[0-9]+$/', $request->GET['rev'])) {
|
||||
$oldrev = Pluf_Shortcuts_GetObjectOr404('IDF_WikiRevision',
|
||||
$revision = Pluf_Shortcuts_GetObjectOr404('IDF_Wiki_PageRevision',
|
||||
$request->GET['rev']);
|
||||
if ($oldrev->wikipage != $page->id or $oldrev->is_head == true) {
|
||||
if ($revision->wikipage != $page->id) {
|
||||
return new Pluf_HTTP_Response_NotFound($request);
|
||||
}
|
||||
}
|
||||
@ -235,15 +312,13 @@ class IDF_Views_Wiki
|
||||
$tags = $page->get_tags_list();
|
||||
$dep = Pluf_Model_InArray($dtag, $tags);
|
||||
$title = $page->title;
|
||||
$revision = $page->get_current_revision();
|
||||
$false = Pluf_DB_BooleanToDb(false, $page->getDbConnection());
|
||||
$revs = $page->get_revisions_list(array('order' => 'creation_dtime DESC',
|
||||
'filter' => 'is_head='.$false));
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/view.html',
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/viewPage.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'page' => $page,
|
||||
'oldrev' => $oldrev,
|
||||
'rev' => $revision,
|
||||
'revs' => $revs,
|
||||
'tags' => $tags,
|
||||
@ -253,14 +328,77 @@ class IDF_Views_Wiki
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a revision of a page.
|
||||
* View a documentation resource.
|
||||
*/
|
||||
public $deleteRev_precond = array('IDF_Precondition::accessWiki',
|
||||
'IDF_Precondition::projectMemberOrOwner');
|
||||
public function deleteRev($request, $match)
|
||||
public $viewResource_precond = array('IDF_Precondition::accessWiki');
|
||||
public function viewResource($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$oldrev = Pluf_Shortcuts_GetObjectOr404('IDF_WikiRevision', $match[2]);
|
||||
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||
array($prj->id, $match[2]));
|
||||
$resources = Pluf::factory('IDF_Wiki_Resource')->getList(array('filter'=>$sql->gen()));
|
||||
if ($resources->count() != 1) {
|
||||
return new Pluf_HTTP_Response_NotFound($request);
|
||||
}
|
||||
$resource = $resources[0];
|
||||
$revision = $resource->get_current_revision();
|
||||
|
||||
// grab the old revision if requested.
|
||||
if (isset($request->GET['rev']) and preg_match('/^[0-9]+$/', $request->GET['rev'])) {
|
||||
$revision = Pluf_Shortcuts_GetObjectOr404('IDF_Wiki_ResourceRevision',
|
||||
$request->GET['rev']);
|
||||
if ($revision->wikiresource != $resource->id) {
|
||||
return new Pluf_HTTP_Response_NotFound($request);
|
||||
}
|
||||
}
|
||||
$pagerevs = $revision->getPageRevisions();
|
||||
$title = $resource->title;
|
||||
$false = Pluf_DB_BooleanToDb(false, $resource->getDbConnection());
|
||||
$revs = $resource->get_revisions_list(array('order' => 'creation_dtime DESC',
|
||||
'filter' => 'is_head='.$false));
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/viewResource.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'resource' => $resource,
|
||||
'rev' => $revision,
|
||||
'revs' => $revs,
|
||||
'pagerevs' => $pagerevs,
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a bytestream to the given raw resource revision
|
||||
*/
|
||||
public $rawResource_precond = array('IDF_Precondition::accessWiki');
|
||||
public function rawResource($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$rev = Pluf_Shortcuts_GetObjectOr404('IDF_Wiki_ResourceRevision',
|
||||
$match[2]);
|
||||
$res = $rev->get_wikiresource();
|
||||
if ($res->get_project()->id != $prj->id) {
|
||||
return new Pluf_HTTP_Response_NotFound($request);
|
||||
}
|
||||
|
||||
$response = new Pluf_HTTP_Response_File($rev->getFilePath(), $res->mime_type);
|
||||
if (isset($request->GET['attachment']) && $request->GET['attachment']) {
|
||||
$response->headers['Content-Disposition'] =
|
||||
'attachment; filename="'.$res->title.'.'.$rev->fileext.'"';
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a revision of a page.
|
||||
*/
|
||||
public $deletePageRev_precond = array('IDF_Precondition::accessWiki',
|
||||
'IDF_Precondition::projectMemberOrOwner');
|
||||
public function deletePageRev($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$oldrev = Pluf_Shortcuts_GetObjectOr404('IDF_Wiki_PageRevision', $match[2]);
|
||||
$page = $oldrev->get_wikipage();
|
||||
$prj->inOr404($page);
|
||||
if ($oldrev->is_head == true) {
|
||||
@ -269,7 +407,7 @@ class IDF_Views_Wiki
|
||||
if ($request->method == 'POST') {
|
||||
$oldrev->delete();
|
||||
$request->user->setMessage(__('The old revision has been deleted.'));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
|
||||
array($prj->shortname, $page->title));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
@ -279,7 +417,7 @@ class IDF_Views_Wiki
|
||||
$false = Pluf_DB_BooleanToDb(false, $page->getDbConnection());
|
||||
$revs = $page->get_revisions_list(array('order' => 'creation_dtime DESC',
|
||||
'filter' => 'is_head='.$false));
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/delete.html',
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/deletePageRev.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'page' => $page,
|
||||
@ -292,17 +430,53 @@ class IDF_Views_Wiki
|
||||
}
|
||||
|
||||
/**
|
||||
* View a documentation page.
|
||||
* Remove a revision of a resource.
|
||||
*/
|
||||
public $update_precond = array('IDF_Precondition::accessWiki',
|
||||
public $deleteResourceRev_precond = array('IDF_Precondition::accessWiki',
|
||||
'IDF_Precondition::projectMemberOrOwner');
|
||||
public function deleteResourceRev($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$oldrev = Pluf_Shortcuts_GetObjectOr404('IDF_Wiki_ResourceRevision', $match[2]);
|
||||
$resource = $oldrev->get_wikiresource();
|
||||
$prj->inOr404($resource);
|
||||
if ($oldrev->is_head == true) {
|
||||
return new Pluf_HTTP_Response_NotFound($request);
|
||||
}
|
||||
if ($request->method == 'POST') {
|
||||
$oldrev->delete();
|
||||
$request->user->setMessage(__('The old revision has been deleted.'));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource',
|
||||
array($prj->shortname, $resource->title));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
|
||||
$title = sprintf(__('Delete Old Revision of %s'), $resource->title);
|
||||
$false = Pluf_DB_BooleanToDb(false, $resource->getDbConnection());
|
||||
$revs = $resource->get_revisions_list(array('order' => 'creation_dtime DESC',
|
||||
'filter' => 'is_head='.$false));
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/deleteResourceRev.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'resource' => $resource,
|
||||
'oldrev' => $oldrev,
|
||||
'revs' => $revs,
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a documentation page.
|
||||
*/
|
||||
public $updatePage_precond = array('IDF_Precondition::accessWiki',
|
||||
'Pluf_Precondition::loginRequired');
|
||||
public function update($request, $match)
|
||||
public function updatePage($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
// Find the page
|
||||
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||
array($prj->id, $match[2]));
|
||||
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
|
||||
$pages = Pluf::factory('IDF_Wiki_Page')->getList(array('filter'=>$sql->gen()));
|
||||
if ($pages->count() != 1) {
|
||||
return new Pluf_HTTP_Response_NotFound($request);
|
||||
}
|
||||
@ -314,13 +488,14 @@ class IDF_Views_Wiki
|
||||
'user' => $request->user,
|
||||
'page' => $page);
|
||||
if ($request->method == 'POST') {
|
||||
$form = new IDF_Form_WikiUpdate($request->POST, $params);
|
||||
$form = new IDF_Form_WikiPageUpdate($request->POST, $params);
|
||||
if ($form->isValid() and !isset($request->POST['preview'])) {
|
||||
$page = $form->save();
|
||||
$urlpage = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||
$urlpage = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
|
||||
array($prj->shortname, $page->title));
|
||||
$request->user->setMessage(sprintf(__('The page <a href="%s">%s</a> has been updated.'), $urlpage, Pluf_esc($page->title)));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::index',
|
||||
$request->user->setMessage(sprintf(__('The page <a href="%1$s">%2$s</a> has been updated.'),
|
||||
$urlpage, Pluf_esc($page->title)));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listPages',
|
||||
array($prj->shortname));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
} elseif (isset($request->POST['preview'])) {
|
||||
@ -328,9 +503,9 @@ class IDF_Views_Wiki
|
||||
}
|
||||
} else {
|
||||
|
||||
$form = new IDF_Form_WikiUpdate(null, $params);
|
||||
$form = new IDF_Form_WikiPageUpdate(null, $params);
|
||||
}
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/update.html',
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/updatePage.html',
|
||||
array(
|
||||
'auto_labels' => self::autoCompleteArrays($prj),
|
||||
'page_title' => $title,
|
||||
@ -343,34 +518,82 @@ class IDF_Views_Wiki
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a Wiki page.
|
||||
* Update a documentation resource.
|
||||
*/
|
||||
public $delete_precond = array('IDF_Precondition::accessWiki',
|
||||
'IDF_Precondition::projectMemberOrOwner');
|
||||
public function delete($request, $match)
|
||||
public $updateResource_precond = array('IDF_Precondition::accessWiki',
|
||||
'Pluf_Precondition::loginRequired');
|
||||
public function updateResource($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$page = Pluf_Shortcuts_GetObjectOr404('IDF_WikiPage', $match[2]);
|
||||
$prj->inOr404($page);
|
||||
$params = array('page' => $page);
|
||||
// Find the page
|
||||
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||
array($prj->id, $match[2]));
|
||||
$resources = Pluf::factory('IDF_Wiki_Resource')->getList(array('filter'=>$sql->gen()));
|
||||
if ($resources->count() != 1) {
|
||||
return new Pluf_HTTP_Response_NotFound($request);
|
||||
}
|
||||
$resource = $resources[0];
|
||||
$title = sprintf(__('Update %s'), $resource->title);
|
||||
$revision = $resource->get_current_revision();
|
||||
$params = array('project' => $prj,
|
||||
'user' => $request->user,
|
||||
'resource' => $resource);
|
||||
if ($request->method == 'POST') {
|
||||
$form = new IDF_Form_WikiDelete($request->POST, $params);
|
||||
$form = new IDF_Form_WikiResourceUpdate(array_merge($request->POST, $request->FILES),
|
||||
$params);
|
||||
if ($form->isValid()) {
|
||||
$form->save();
|
||||
$request->user->setMessage(__('The documentation page has been deleted.'));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::index',
|
||||
$page = $form->save();
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource',
|
||||
array($prj->shortname, $resource->title));
|
||||
$request->user->setMessage(sprintf(__('The resource <a href="%1$s">%2$s</a> has been updated.'),
|
||||
$url, Pluf_esc($resource->title)));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listResources',
|
||||
array($prj->shortname));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
} else {
|
||||
$form = new IDF_Form_WikiDelete(null, $params);
|
||||
$form = new IDF_Form_WikiResourceUpdate(null, $params);
|
||||
}
|
||||
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/updateResource.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'resource' => $resource,
|
||||
'rev' => $revision,
|
||||
'form' => $form,
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a Wiki page.
|
||||
*/
|
||||
public $deletePage_precond = array('IDF_Precondition::accessWiki',
|
||||
'IDF_Precondition::projectMemberOrOwner');
|
||||
public function deletePage($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$page = Pluf_Shortcuts_GetObjectOr404('IDF_Wiki_Page', $match[2]);
|
||||
$prj->inOr404($page);
|
||||
$params = array('page' => $page);
|
||||
if ($request->method == 'POST') {
|
||||
$form = new IDF_Form_WikiPageDelete($request->POST, $params);
|
||||
if ($form->isValid()) {
|
||||
$form->save();
|
||||
$request->user->setMessage(__('The documentation page has been deleted.'));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listPages',
|
||||
array($prj->shortname));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
} else {
|
||||
$form = new IDF_Form_WikiPageDelete(null, $params);
|
||||
}
|
||||
$title = sprintf(__('Delete Page %s'), $page->title);
|
||||
$revision = $page->get_current_revision();
|
||||
$false = Pluf_DB_BooleanToDb(false, $page->getDbConnection());
|
||||
$revs = $page->get_revisions_list(array('order' => 'creation_dtime DESC',
|
||||
'filter' => 'is_head='.$false));
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/deletepage.html',
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/deletePage.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'page' => $page,
|
||||
@ -382,6 +605,45 @@ class IDF_Views_Wiki
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a Wiki resource.
|
||||
*/
|
||||
public $deleteResource_precond = array('IDF_Precondition::accessWiki',
|
||||
'IDF_Precondition::projectMemberOrOwner');
|
||||
public function deleteResource($request, $match)
|
||||
{
|
||||
$prj = $request->project;
|
||||
$resource = Pluf_Shortcuts_GetObjectOr404('IDF_Wiki_Resource', $match[2]);
|
||||
$prj->inOr404($resource);
|
||||
$params = array('resource' => $resource);
|
||||
if ($request->method == 'POST') {
|
||||
$form = new IDF_Form_WikiResourceDelete($request->POST, $params);
|
||||
if ($form->isValid()) {
|
||||
$form->save();
|
||||
$request->user->setMessage(__('The documentation resource has been deleted.'));
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listResources',
|
||||
array($prj->shortname));
|
||||
return new Pluf_HTTP_Response_Redirect($url);
|
||||
}
|
||||
} else {
|
||||
$form = new IDF_Form_WikiResourceDelete(null, $params);
|
||||
}
|
||||
$title = sprintf(__('Delete Resource %s'), $resource->title);
|
||||
$revision = $resource->get_current_revision();
|
||||
$false = Pluf_DB_BooleanToDb(false, $resource->getDbConnection());
|
||||
$revs = $resource->get_revisions_list(array('order' => 'creation_dtime DESC',
|
||||
'filter' => 'is_head='.$false));
|
||||
return Pluf_Shortcuts_RenderToResponse('idf/wiki/deleteResource.html',
|
||||
array(
|
||||
'page_title' => $title,
|
||||
'resource' => $resource,
|
||||
'form' => $form,
|
||||
'rev' => $revision,
|
||||
'revs' => $revs,
|
||||
),
|
||||
$request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wiki tags.
|
||||
*
|
||||
@ -411,7 +673,7 @@ class IDF_Views_Wiki
|
||||
$sql = new Pluf_SQL('project=%s AND idf_tag_id=%s', array($project->id,
|
||||
$dtag->id));
|
||||
$ids = array();
|
||||
foreach (Pluf::factory('IDF_WikiPage')->getList(array('filter' => $sql->gen(), 'view' => 'join_tags'))
|
||||
foreach (Pluf::factory('IDF_Wiki_Page')->getList(array('filter' => $sql->gen(), 'view' => 'join_tags'))
|
||||
as $file) {
|
||||
$ids[] = (int) $file->id;
|
||||
}
|
||||
|
@ -31,22 +31,28 @@
|
||||
class IDF_Webhook
|
||||
{
|
||||
/**
|
||||
* Perform the POST request given the webhook payload.
|
||||
* Perform the request given the webhook payload.
|
||||
*
|
||||
* @param array Payload
|
||||
* @return bool Success or error
|
||||
*/
|
||||
public static function postNotification($payload)
|
||||
public static function processNotification($payload)
|
||||
{
|
||||
$data = json_encode($payload['to_send']);
|
||||
$sign_header = 'Web-Hook-Hmac';
|
||||
// use the old signature header if we're asked for
|
||||
if (Pluf::f('webhook_processing', '') === 'compat') {
|
||||
$sign_header = 'Post-Commit-Hook-Hmac';
|
||||
}
|
||||
$sign = hash_hmac('md5', $data, $payload['authkey']);
|
||||
$params = array('http' => array(
|
||||
'method' => 'POST',
|
||||
// fall-back to POST for old queue items
|
||||
'method' => empty($payload['method']) ? 'POST' : $payload['method'],
|
||||
'content' => $data,
|
||||
'user_agent' => 'Indefero Hook Sender (http://www.indefero.net)',
|
||||
'max_redirects' => 0,
|
||||
'timeout' => 15,
|
||||
'header'=> 'Post-Commit-Hook-Hmac: '.$sign."\r\n"
|
||||
'header'=> $sign_header.': '.$sign."\r\n"
|
||||
.'Content-Type: application/json'."\r\n",
|
||||
)
|
||||
);
|
||||
@ -76,7 +82,7 @@ class IDF_Webhook
|
||||
public static function process($sender, &$params)
|
||||
{
|
||||
$item = $params['item'];
|
||||
if ($item->type != 'new_commit') {
|
||||
if (!in_array($item->type, array('new_commit', 'upload'))) {
|
||||
// We do nothing.
|
||||
return;
|
||||
}
|
||||
@ -90,7 +96,7 @@ class IDF_Webhook
|
||||
return;
|
||||
}
|
||||
// We have either to retry or to push for the first time.
|
||||
$res = self::postNotification($item->payload);
|
||||
$res = self::processNotification($item->payload);
|
||||
if ($res) {
|
||||
$params['res']['IDF_Webhook::process'] = true;
|
||||
} elseif ($item->trials >= 9) {
|
||||
|
@ -28,10 +28,10 @@ Pluf::loadFunction('Pluf_Template_dateAgo');
|
||||
* Base definition of a wiki page.
|
||||
*
|
||||
* A wiki page can have tags and be starred by the users. The real
|
||||
* content of the page is stored in the IDF_WikiRevision
|
||||
* content of the page is stored in the IDF_Wiki_PageRevision
|
||||
* object. Several revisions are associated to a given page.
|
||||
*/
|
||||
class IDF_WikiPage extends Pluf_Model
|
||||
class IDF_Wiki_Page extends Pluf_Model
|
||||
{
|
||||
public $_model = __CLASS__;
|
||||
|
||||
@ -113,12 +113,12 @@ class IDF_WikiPage extends Pluf_Model
|
||||
'type' => 'normal',
|
||||
),
|
||||
);
|
||||
$table = $this->_con->pfx.'idf_tag_idf_wikipage_assoc';
|
||||
$table = $this->_con->pfx.'idf_tag_idf_wiki_page_assoc';
|
||||
$this->_a['views'] = array(
|
||||
'join_tags' =>
|
||||
array(
|
||||
'join' => 'LEFT JOIN '.$table
|
||||
.' ON idf_wikipage_id=id',
|
||||
.' ON idf_wiki_page_id=id',
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -185,7 +185,7 @@ class IDF_WikiPage extends Pluf_Model
|
||||
*/
|
||||
public function timelineFragment($request)
|
||||
{
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
|
||||
array($request->project->shortname,
|
||||
$this->title));
|
||||
$out = '<tr class="log"><td><a href="'.$url.'">'.
|
||||
@ -195,17 +195,17 @@ class IDF_WikiPage extends Pluf_Model
|
||||
$user = $stag->start($this->get_submitter(), $request, '', false);
|
||||
$out .= sprintf(__('<a href="%1$s" title="View page">%2$s</a>, %3$s'), $url, Pluf_esc($this->title), Pluf_esc($this->summary)).'</td>';
|
||||
$out .= "\n".'<tr class="extra"><td colspan="2">
|
||||
<div class="helptext right">'.sprintf(__('Creation of <a href="%s">page %s</a>, by %s'), $url, Pluf_esc($this->title), $user).'</div></td></tr>';
|
||||
<div class="helptext right">'.sprintf(__('Creation of <a href="%1$s">page %2$s</a>, by %3$s'), $url, Pluf_esc($this->title), $user).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
$url = Pluf::f('url_base')
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
|
||||
array($request->project->shortname,
|
||||
$this->title));
|
||||
$title = sprintf(__('%s: Documentation page %s added - %s'),
|
||||
$title = sprintf(__('%1$s: Documentation page %2$s added - %3$s'),
|
||||
$request->project->name,
|
||||
$this->title, $this->summary);
|
||||
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
|
||||
@ -218,7 +218,7 @@ class IDF_WikiPage extends Pluf_Model
|
||||
'create' => true,
|
||||
'date' => $date)
|
||||
);
|
||||
$tmpl = new Pluf_Template('idf/wiki/feedfragment.xml');
|
||||
$tmpl = new Pluf_Template('idf/wiki/feedfragment-page.xml');
|
||||
return $tmpl->render($context);
|
||||
}
|
||||
}
|
@ -25,13 +25,13 @@
|
||||
* A revision of a wiki page.
|
||||
*
|
||||
*/
|
||||
class IDF_WikiRevision extends Pluf_Model
|
||||
class IDF_Wiki_PageRevision extends Pluf_Model
|
||||
{
|
||||
public $_model = __CLASS__;
|
||||
|
||||
function init()
|
||||
{
|
||||
$this->_a['table'] = 'idf_wikirevisions';
|
||||
$this->_a['table'] = 'idf_wikipagerevs';
|
||||
$this->_a['model'] = __CLASS__;
|
||||
$this->_a['cols'] = array(
|
||||
// It is mandatory to have an "id" column.
|
||||
@ -43,7 +43,7 @@ class IDF_WikiRevision extends Pluf_Model
|
||||
'wikipage' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Foreignkey',
|
||||
'model' => 'IDF_WikiPage',
|
||||
'model' => 'IDF_Wiki_Page',
|
||||
'blank' => false,
|
||||
'verbose' => __('page'),
|
||||
'relate_name' => 'revisions',
|
||||
@ -83,7 +83,7 @@ class IDF_WikiRevision extends Pluf_Model
|
||||
'type' => 'Pluf_DB_Field_Serialized',
|
||||
'blank' => true,
|
||||
'verbose' => __('changes'),
|
||||
'help_text' => 'Serialized array of the changes in the issue.',
|
||||
'help_text' => 'Serialized array of the changes in the page.',
|
||||
),
|
||||
'creation_dtime' =>
|
||||
array(
|
||||
@ -99,6 +99,14 @@ class IDF_WikiRevision extends Pluf_Model
|
||||
'type' => 'normal',
|
||||
),
|
||||
);
|
||||
$table = $this->_con->pfx.'idf_wiki_pagerevision_idf_wiki_resourcerevision_assoc';
|
||||
$this->_a['views'] = array(
|
||||
'join_pagerevision' =>
|
||||
array(
|
||||
'join' => 'LEFT JOIN '.$table
|
||||
.' ON idf_wiki_pagerevision_id=id',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function changedRevision()
|
||||
@ -129,6 +137,8 @@ class IDF_WikiRevision extends Pluf_Model
|
||||
|
||||
function postSave($create=false)
|
||||
{
|
||||
$page = $this->get_wikipage();
|
||||
|
||||
if ($create) {
|
||||
// Check if more than one revision for this page. We do
|
||||
// not want to insert the first revision in the timeline
|
||||
@ -136,10 +146,9 @@ class IDF_WikiRevision extends Pluf_Model
|
||||
// update as update is performed to change the is_head
|
||||
// flag.
|
||||
$sql = new Pluf_SQL('wikipage=%s', array($this->wikipage));
|
||||
$rev = Pluf::factory('IDF_WikiRevision')->getList(array('filter'=>$sql->gen()));
|
||||
$rev = Pluf::factory('IDF_Wiki_PageRevision')->getList(array('filter'=>$sql->gen()));
|
||||
if ($rev->count() > 1) {
|
||||
IDF_Timeline::insert($this, $this->get_wikipage()->get_project(),
|
||||
$this->get_submitter());
|
||||
IDF_Timeline::insert($this, $page->get_project(), $this->get_submitter());
|
||||
foreach ($rev as $r) {
|
||||
if ($r->id != $this->id and $r->is_head) {
|
||||
$r->is_head = false;
|
||||
@ -147,18 +156,38 @@ class IDF_WikiRevision extends Pluf_Model
|
||||
}
|
||||
}
|
||||
}
|
||||
$page = $this->get_wikipage();
|
||||
$page->update(); // Will update the modification timestamp.
|
||||
}
|
||||
|
||||
IDF_Search::index($page);
|
||||
$page->update(); // Will update the modification timestamp.
|
||||
|
||||
// remember the resource revisions used in this page revision
|
||||
if ($this->is_head) {
|
||||
preg_match_all('#\[\[!([A-Za-z0-9\-]+)[^\]]*\]\]#im', $this->content, $matches, PREG_PATTERN_ORDER);
|
||||
if (count($matches) > 1 && count($matches[1]) > 0) {
|
||||
foreach ($matches[1] as $resourceName) {
|
||||
$sql = new Pluf_SQL('project=%s AND title=%s',
|
||||
array($prj->id, $resourceName));
|
||||
$resources = Pluf::factory('IDF_Wiki_Resource')->getList(array('filter'=>$sql->gen()));
|
||||
if ($resources->count() == 0)
|
||||
continue;
|
||||
|
||||
$current_revision = $resources[0]->get_current_revision();
|
||||
$current_revision->setAssoc($this);
|
||||
$this->setAssoc($current_revision);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function timelineFragment($request)
|
||||
{
|
||||
$page = $this->get_wikipage();
|
||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||
$url = Pluf::f('url_base')
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
|
||||
array($request->project->shortname,
|
||||
$page->title));
|
||||
$page->title),
|
||||
array('rev' => $this->id));
|
||||
$out = "\n".'<tr class="log"><td><a href="'.$url.'">'.
|
||||
Pluf_esc(Pluf_Template_dateAgo($this->creation_dtime, 'without')).
|
||||
'</a></td><td>';
|
||||
@ -186,26 +215,20 @@ class IDF_WikiRevision extends Pluf_Model
|
||||
}
|
||||
$out .= '</td></tr>';
|
||||
$out .= "\n".'<tr class="extra"><td colspan="2">
|
||||
<div class="helptext right">'.sprintf(__('Change of <a href="%s">%s</a>, by %s'), $url, Pluf_esc($page->title), $user).'</div></td></tr>';
|
||||
<div class="helptext right">'.sprintf(__('Change of <a href="%1$s">%2$s</a>, by %3$s'), $url, Pluf_esc($page->title), $user).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
$page = $this->get_wikipage();
|
||||
if (!$this->is_head) {
|
||||
$url = Pluf::f('url_base')
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewPage',
|
||||
array($request->project->shortname,
|
||||
$page->title),
|
||||
array('rev' => $this->id));
|
||||
} else {
|
||||
$url = Pluf::f('url_base')
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
|
||||
array($request->project->shortname,
|
||||
$page->title));
|
||||
}
|
||||
$title = sprintf(__('%s: Documentation page %s updated - %s'),
|
||||
|
||||
$title = sprintf(__('%1$s: Documentation page %2$s updated - %3$s'),
|
||||
$request->project->name,
|
||||
$page->title, $page->summary);
|
||||
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
|
||||
@ -218,7 +241,7 @@ class IDF_WikiRevision extends Pluf_Model
|
||||
'create' => false,
|
||||
'date' => $date)
|
||||
);
|
||||
$tmpl = new Pluf_Template('idf/wiki/feedfragment.xml');
|
||||
$tmpl = new Pluf_Template('idf/wiki/feedfragment-page.xml');
|
||||
return $tmpl->render($context);
|
||||
}
|
||||
|
||||
@ -243,42 +266,49 @@ class IDF_WikiRevision extends Pluf_Model
|
||||
*/
|
||||
public function notify($conf, $create=true)
|
||||
{
|
||||
if ('' == $conf->getVal('wiki_notification_email', '')) {
|
||||
return;
|
||||
}
|
||||
$wikipage = $this->get_wikipage();
|
||||
$project = $wikipage->get_project();
|
||||
$current_locale = Pluf_Translation::getLocale();
|
||||
$langs = Pluf::f('languages', array('en'));
|
||||
Pluf_Translation::loadSetLocale($langs[0]);
|
||||
$context = new Pluf_Template_Context(
|
||||
array(
|
||||
'page' => $this->get_wikipage(),
|
||||
'rev' => $this,
|
||||
'project' => $this->get_wikipage()->get_project(),
|
||||
'url_base' => Pluf::f('url_base'),
|
||||
)
|
||||
);
|
||||
if ($create) {
|
||||
$template = 'idf/wiki/wiki-created-email.txt';
|
||||
$title = sprintf(__('New Documentation Page %s - %s (%s)'),
|
||||
$this->get_wikipage()->title,
|
||||
$this->get_wikipage()->summary,
|
||||
$this->get_wikipage()->get_project()->shortname);
|
||||
} else {
|
||||
$template = 'idf/wiki/wiki-updated-email.txt';
|
||||
$title = sprintf(__('Documentation Page Changed %s - %s (%s)'),
|
||||
$this->get_wikipage()->title,
|
||||
$this->get_wikipage()->summary,
|
||||
$this->get_wikipage()->get_project()->shortname);
|
||||
|
||||
$from_email = Pluf::f('from_email');
|
||||
$messageId = '<'.md5('wiki'.$wikipage->id.md5(Pluf::f('secret_key'))).'@'.Pluf::f('mail_host', 'localhost').'>';
|
||||
$recipients = $project->getNotificationRecipientsForTab('wiki');
|
||||
|
||||
foreach ($recipients as $address => $language) {
|
||||
|
||||
if ($this->get_submitter()->email === $address) {
|
||||
continue;
|
||||
}
|
||||
$tmpl = new Pluf_Template($template);
|
||||
|
||||
Pluf_Translation::loadSetLocale($language);
|
||||
|
||||
$context = new Pluf_Template_Context(array(
|
||||
'page' => $wikipage,
|
||||
'rev' => $this,
|
||||
'project' => $project,
|
||||
'url_base' => Pluf::f('url_base'),
|
||||
));
|
||||
|
||||
$tplfile = 'idf/wiki/wiki-created-email.txt';
|
||||
$subject = __('New Documentation Page %1$s - %2$s (%3$s)');
|
||||
$headers = array('Message-ID' => $messageId);
|
||||
if (!$create) {
|
||||
$tplfile = 'idf/wiki/wiki-updated-email.txt';
|
||||
$subject = __('Documentation Page Changed %1$s - %2$s (%3$s)');
|
||||
$headers = array('References' => $messageId);
|
||||
}
|
||||
|
||||
$tmpl = new Pluf_Template($tplfile);
|
||||
$text_email = $tmpl->render($context);
|
||||
|
||||
$addresses = explode(',', $conf->getVal('wiki_notification_email'));
|
||||
foreach ($addresses as $address) {
|
||||
$email = new Pluf_Mail(Pluf::f('from_email'),
|
||||
$email = new Pluf_Mail($from_email,
|
||||
$address,
|
||||
$title);
|
||||
sprintf($subject,
|
||||
$wikipage->title,
|
||||
$wikipage->summary,
|
||||
$project->shortname));
|
||||
$email->addTextMessage($text_email);
|
||||
$email->addHeaders($headers);
|
||||
$email->sendMail();
|
||||
}
|
||||
|
204
src/IDF/Wiki/Resource.php
Normal file
204
src/IDF/Wiki/Resource.php
Normal file
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
|
||||
Pluf::loadFunction('Pluf_Template_dateAgo');
|
||||
|
||||
/**
|
||||
* Base definition of a wiki resource.
|
||||
*
|
||||
* The resource groups several logical versions of a file into one and gives
|
||||
* it a unique title across a project's wiki. The current file version is
|
||||
* saved in a IDF_Wiki_ResourceRevision, which is also the entity that is
|
||||
* linked with a specific IDF_Wiki_PageRevision on which the resource is
|
||||
* displayed.
|
||||
*/
|
||||
class IDF_Wiki_Resource extends Pluf_Model
|
||||
{
|
||||
public $_model = __CLASS__;
|
||||
|
||||
function init()
|
||||
{
|
||||
$this->_a['table'] = 'idf_wikiresources';
|
||||
$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' => 'wikipages',
|
||||
),
|
||||
'title' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Varchar',
|
||||
'blank' => false,
|
||||
'size' => 250,
|
||||
'verbose' => __('title'),
|
||||
'help_text' => __('The title of the resource must only contain letters, digits, dots or the dash character. For example: my-resource.png.'),
|
||||
),
|
||||
'mime_type' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Varchar',
|
||||
'blank' => false,
|
||||
'size' => 100,
|
||||
'verbose' => __('MIME media type'),
|
||||
'help_text' => __('The MIME media type of the resource.'),
|
||||
),
|
||||
'summary' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Varchar',
|
||||
'blank' => false,
|
||||
'size' => 250,
|
||||
'verbose' => __('summary'),
|
||||
'help_text' => __('A one line description of the resource.'),
|
||||
),
|
||||
'submitter' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Foreignkey',
|
||||
'model' => 'Pluf_User',
|
||||
'blank' => false,
|
||||
'verbose' => __('submitter'),
|
||||
'relate_name' => 'submitted_wikipages',
|
||||
),
|
||||
'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'),
|
||||
),
|
||||
);
|
||||
$this->_a['idx'] = array(
|
||||
'modif_dtime_idx' =>
|
||||
array(
|
||||
'col' => 'modif_dtime',
|
||||
'type' => 'normal',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function __toString()
|
||||
{
|
||||
return $this->title.' - '.$this->summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* We drop the information from the timeline.
|
||||
*/
|
||||
function preDelete()
|
||||
{
|
||||
IDF_Timeline::remove($this);
|
||||
IDF_Search::remove($this);
|
||||
}
|
||||
|
||||
function get_current_revision()
|
||||
{
|
||||
$true = Pluf_DB_BooleanToDb(true, $this->getDbConnection());
|
||||
$rev = $this->get_revisions_list(array('filter' => 'is_head='.$true,
|
||||
'nb' => 1));
|
||||
return ($rev->count() == 1) ? $rev[0] : null;
|
||||
}
|
||||
|
||||
function preSave($create=false)
|
||||
{
|
||||
if ($this->id == '') {
|
||||
$this->creation_dtime = gmdate('Y-m-d H:i:s');
|
||||
}
|
||||
$this->modif_dtime = gmdate('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
function postSave($create=false)
|
||||
{
|
||||
// Note: No indexing is performed here. The indexing is
|
||||
// triggered in the postSave step of the revision to ensure
|
||||
// that the page as a given revision in the database when
|
||||
// doing the indexing.
|
||||
if ($create) {
|
||||
IDF_Timeline::insert($this, $this->get_project(),
|
||||
$this->get_submitter());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an HTML fragment used to display this resource 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_Wiki::viewResource',
|
||||
array($request->project->shortname,
|
||||
$this->title));
|
||||
$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);
|
||||
$out .= sprintf(__('<a href="%1$s" title="View resource">%2$s</a>, %3$s'), $url, Pluf_esc($this->title), Pluf_esc($this->summary)).'</td>';
|
||||
$out .= "\n".'<tr class="extra"><td colspan="2">
|
||||
<div class="helptext right">'.sprintf(__('Creation of <a href="%1$s">resource %2$s</a>, by %3$s'), $url, Pluf_esc($this->title), $user).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
$url = Pluf::f('url_base')
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource',
|
||||
array($request->project->shortname,
|
||||
$this->title));
|
||||
$title = sprintf(__('%1$s: Documentation resource %2$s added - %3$s'),
|
||||
$request->project->name,
|
||||
$this->title, $this->summary);
|
||||
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
|
||||
$context = new Pluf_Template_Context_Request(
|
||||
$request,
|
||||
array('url' => $url,
|
||||
'title' => $title,
|
||||
'resource' => $this,
|
||||
'rev' => $this->get_current_revision(),
|
||||
'create' => true,
|
||||
'date' => $date)
|
||||
);
|
||||
$tmpl = new Pluf_Template('idf/wiki/feedfragment-resource.xml');
|
||||
return $tmpl->render($context);
|
||||
}
|
||||
}
|
340
src/IDF/Wiki/ResourceRevision.php
Normal file
340
src/IDF/Wiki/ResourceRevision.php
Normal file
@ -0,0 +1,340 @@
|
||||
<?php
|
||||
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# This file is part of InDefero, an open source project management application.
|
||||
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
|
||||
#
|
||||
# InDefero is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# InDefero is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* A single resource revision.
|
||||
*/
|
||||
class IDF_Wiki_ResourceRevision extends Pluf_Model
|
||||
{
|
||||
public $_model = __CLASS__;
|
||||
|
||||
function init()
|
||||
{
|
||||
$this->_a['table'] = 'idf_wikiresourcerevs';
|
||||
$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,
|
||||
),
|
||||
'wikiresource' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Foreignkey',
|
||||
'model' => 'IDF_Wiki_Resource',
|
||||
'blank' => false,
|
||||
'verbose' => __('resource'),
|
||||
'relate_name' => 'revisions',
|
||||
),
|
||||
'is_head' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Boolean',
|
||||
'blank' => false,
|
||||
'default' => false,
|
||||
'help_text' => 'If this revision is the latest, we mark it as being the head revision.',
|
||||
'index' => true,
|
||||
),
|
||||
'summary' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Varchar',
|
||||
'blank' => false,
|
||||
'size' => 250,
|
||||
'verbose' => __('summary'),
|
||||
'help_text' => __('A one line description of the changes.'),
|
||||
),
|
||||
'filesize' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Integer',
|
||||
'blank' => false,
|
||||
'default' => 0,
|
||||
'verbose' => __('file size in bytes'),
|
||||
),
|
||||
'fileext' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Varchar',
|
||||
'blank' => false,
|
||||
'size' => 10,
|
||||
'verbose' => __('File extension'),
|
||||
'help_text' => __('The file extension of the uploaded resource.'),
|
||||
),
|
||||
'submitter' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Foreignkey',
|
||||
'model' => 'Pluf_User',
|
||||
'blank' => false,
|
||||
'verbose' => __('submitter'),
|
||||
'relate_name' => 'submitted_downloads',
|
||||
),
|
||||
'pageusage' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Manytomany',
|
||||
'model' => 'IDF_Wiki_PageRevision',
|
||||
'blank' => true,
|
||||
'verbose' => __('page usage'),
|
||||
'help_text' => 'Records on which pages this resource revision is used.',
|
||||
),
|
||||
'creation_dtime' =>
|
||||
array(
|
||||
'type' => 'Pluf_DB_Field_Datetime',
|
||||
'blank' => true,
|
||||
'verbose' => __('creation date'),
|
||||
),
|
||||
);
|
||||
$table = $this->_con->pfx.'idf_wiki_pagerevision_idf_wiki_resourcerevision_assoc';
|
||||
$this->_a['views'] = array(
|
||||
'join_pagerevision' =>
|
||||
array(
|
||||
'join' => 'LEFT JOIN '.$table
|
||||
.' ON idf_wiki_resourcerevision_id=id',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function __toString()
|
||||
{
|
||||
return sprintf(__('id %d: %s'), $this->id, $this->summary);
|
||||
}
|
||||
|
||||
function _toIndex()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
function preDelete()
|
||||
{
|
||||
// if we kill off a head revision, ensure that we either mark a previous revision as head
|
||||
if ($this->is_head) {
|
||||
$sql = new Pluf_SQL('wikiresource=%s and id!=%s', array($this->wikiresource, $this->id));
|
||||
$revs = Pluf::factory('IDF_Wiki_ResourceRevision')->getList(array('filter'=>$sql->gen(), 'order'=>'id DESC'));
|
||||
if ($revs->count() > 0) {
|
||||
$previous = $revs[0];
|
||||
$previous->is_head = true;
|
||||
$previous->update();
|
||||
}
|
||||
}
|
||||
|
||||
@unlink($this->getFilePath());
|
||||
IDF_Timeline::remove($this);
|
||||
}
|
||||
|
||||
function preSave($create=false)
|
||||
{
|
||||
if ($this->id == '') {
|
||||
$this->creation_dtime = gmdate('Y-m-d H:i:s');
|
||||
$this->is_head = true;
|
||||
}
|
||||
}
|
||||
|
||||
function postSave($create=false)
|
||||
{
|
||||
$resource = $this->get_wikiresource();
|
||||
|
||||
if ($create) {
|
||||
$sql = new Pluf_SQL('wikiresource=%s', array($this->wikiresource));
|
||||
$rev = Pluf::factory('IDF_Wiki_ResourceRevision')->getList(array('filter'=>$sql->gen()));
|
||||
if ($rev->count() > 1) {
|
||||
IDF_Timeline::insert($this, $resource->get_project(), $this->get_submitter());
|
||||
foreach ($rev as $r) {
|
||||
if ($r->id != $this->id and $r->is_head) {
|
||||
$r->is_head = false;
|
||||
$r->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the modification timestamp
|
||||
$resource->update();
|
||||
}
|
||||
|
||||
function getFilePath()
|
||||
{
|
||||
return sprintf(Pluf::f('upload_path').'/'.$this->get_wikiresource()->get_project()->shortname.'/wiki/res/%d/%d.%s',
|
||||
$this->get_wikiresource()->id, $this->id, $this->fileext);
|
||||
}
|
||||
|
||||
function getViewURL()
|
||||
{
|
||||
$prj = $this->get_wikiresource()->get_project();
|
||||
$resource = $this->get_wikiresource();
|
||||
return Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource',
|
||||
array($prj->shortname, $resource->title),
|
||||
array('rev' => $this->id));
|
||||
}
|
||||
|
||||
function getRawURL($attachment = false)
|
||||
{
|
||||
$query = $attachment ? array('attachment' => 1) : array();
|
||||
$prj = $this->get_wikiresource()->get_project();
|
||||
return Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::rawResource',
|
||||
array($prj->shortname, $this->id),
|
||||
$query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page revisions which contain references to this resource revision
|
||||
*/
|
||||
function getPageRevisions()
|
||||
{
|
||||
$db =& Pluf::db();
|
||||
$sql_results = $db->select(
|
||||
'SELECT idf_wiki_pagerevision_id as id '.
|
||||
'FROM '.Pluf::f('db_table_prefix', '').'idf_wiki_pagerevision_idf_wiki_resourcerevision_assoc '.
|
||||
'WHERE idf_wiki_resourcerevision_id='.$this->id
|
||||
);
|
||||
$ids = array(0);
|
||||
foreach ($sql_results as $id) {
|
||||
$ids[] = $id['id'];
|
||||
}
|
||||
$ids = implode (',', $ids);
|
||||
|
||||
$sql = new Pluf_SQL('id IN ('.$ids.')');
|
||||
return Pluf::factory('IDF_Wiki_PageRevision')
|
||||
->getList(array('filter' => $sql->gen()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the resource with the given view options, including a link to the resource' detail page
|
||||
*/
|
||||
function render($opts = array())
|
||||
{
|
||||
// give some reasonable defaults
|
||||
$opts = array_merge(array(
|
||||
'align' => 'left',
|
||||
'width' => '',
|
||||
'height' => '',
|
||||
'preview' => 'yes', // if possible
|
||||
'title' => '',
|
||||
), $opts);
|
||||
|
||||
$attrs = array('class="resource-container"');
|
||||
$styles = array();
|
||||
if (!empty($opts['align'])) {
|
||||
switch ($opts['align']) {
|
||||
case 'left':
|
||||
$styles[] = 'float: left';
|
||||
$styles[] = 'margin-right: 10px';
|
||||
break;
|
||||
case 'center':
|
||||
$styles[] = 'margin: 0 auto 0 auto';
|
||||
break;
|
||||
case 'right':
|
||||
$styles[] = 'float: right';
|
||||
$styles[] = 'margin-left: 10px';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty($opts['width'])) {
|
||||
$styles[] = 'width:'.$opts['width'];
|
||||
}
|
||||
if (!empty($opts['height'])) {
|
||||
$styles[] = 'height:'.$opts['height'];
|
||||
}
|
||||
|
||||
$raw = $this->renderRaw();
|
||||
$viewUrl = $this->getViewURL();
|
||||
$download = '';
|
||||
$html = '<div class="resource-container" style="'.implode(';', $styles).'">';
|
||||
if ($opts['preview'] == 'yes' && !empty($raw)) {
|
||||
$html .= '<div class="preview">'.$raw.'</div>'."\n";
|
||||
} else {
|
||||
$rawUrl = $this->getRawURL(true);
|
||||
$download = '<a href="'.$rawUrl.'" class="download" title="'.sprintf(__('Download (%s)'), Pluf_Utils::prettySize($this->filesize)).'"></a>';
|
||||
}
|
||||
$resource = $this->get_wikiresource();
|
||||
$title = $opts['title'];
|
||||
if (empty($title)) {
|
||||
$title = $resource->title.' - '.$resource->mime_type.' - '.Pluf_Utils::prettySize($this->filesize);
|
||||
}
|
||||
$html .= '<div class="title">'.$download.'<a href="'.$viewUrl.'" title="'.__('View resource details').'">'.$title.'</a></div>'."\n";
|
||||
$html .= '</div>';
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a raw version of the resource, without any possibilities of formatting or the like
|
||||
*/
|
||||
function renderRaw()
|
||||
{
|
||||
$resource = $this->get_wikiresource();
|
||||
$url = $this->getRawURL();
|
||||
if (preg_match('#^image/(gif|jpeg|png|tiff)$#', $resource->mime_type)) {
|
||||
return sprintf('<img src="%s" alt="%s" />', $url, $resource->title);
|
||||
}
|
||||
|
||||
if (preg_match('#^text/(plain|xml|html|sgml|javascript|ecmascript|css)$#', $resource->mime_type)) {
|
||||
return sprintf('<iframe src="%s" alt="%s"></iframe>', $url, $resource->title);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
public function timelineFragment($request)
|
||||
{
|
||||
$resource = $this->get_wikiresource();
|
||||
$url = Pluf::f('url_base')
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource',
|
||||
array($request->project->shortname,
|
||||
$resource->title),
|
||||
array('rev' => $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);
|
||||
$out .= sprintf(__('<a href="%1$s" title="View resource">%2$s</a>, %3$s'), $url, Pluf_esc($resource->title), Pluf_esc($this->summary));
|
||||
$out .= '</td></tr>';
|
||||
$out .= "\n".'<tr class="extra"><td colspan="2">
|
||||
<div class="helptext right">'.sprintf(__('Change of <a href="%1$s">%2$s</a>, by %3$s'), $url, Pluf_esc($resource->title), $user).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
$resource = $this->get_wikiresource();
|
||||
$url = Pluf::f('url_base')
|
||||
.Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource',
|
||||
array($request->project->shortname,
|
||||
$resource->title),
|
||||
array('rev' => $this->id));
|
||||
|
||||
$title = sprintf(__('%1$s: Documentation resource %2$s updated - %3$s'),
|
||||
$request->project->name,
|
||||
$resource->title, $resource->summary);
|
||||
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
|
||||
$context = new Pluf_Template_Context_Request(
|
||||
$request,
|
||||
array('url' => $url,
|
||||
'title' => $title,
|
||||
'resource' => $resource,
|
||||
'rev' => $this,
|
||||
'create' => false,
|
||||
'date' => $date)
|
||||
);
|
||||
$tmpl = new Pluf_Template('idf/wiki/feedfragment-resource.xml');
|
||||
return $tmpl->render($context);
|
||||
}
|
||||
}
|
@ -119,7 +119,7 @@ $cfg['time_zone'] = 'Europe/Berlin';
|
||||
# Configure which languages should be available in your forge.
|
||||
# If you want to enable an additional language, ensure that the
|
||||
# language file in question resides in 'src/IDF/locale'.
|
||||
$cfg['languages'] = array('en', 'fr', 'de', 'es_ES');
|
||||
$cfg['languages'] = array('en', 'fr', 'de', 'es_ES', 'ru');
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------- #
|
||||
@ -495,5 +495,54 @@ $cfg['idf_strong_key_check'] = false;
|
||||
# always have precedence.
|
||||
# $cfg['max_upload_size'] = 2097152; // Size in bytes
|
||||
|
||||
# If a download archive is uploaded, the size of the archive is limited to 20MB.
|
||||
# The php.ini upload_max_filesize and post_max_size configuration setting will
|
||||
# always have precedence.
|
||||
# $cfg['max_upload_archive_size'] = 20971520; // Size in bytes
|
||||
|
||||
# Older versions of Indefero submitted a POST request to a configured
|
||||
# post-commit web hook when new revisions arrived, whereas a PUT request
|
||||
# would have been more appropriate. Also, the payload's HMAC digest was
|
||||
# submitted as value of the HTTP header 'Post-Commit-Hook-Hmac' during
|
||||
# such a request. Since newer versions of Indefero use the same authentication
|
||||
# mechanism (based on the same secret key) for other web hooks of the same
|
||||
# project as well, the name of this HTTP header was no longer appropriate
|
||||
# and as such changed to simply 'Web-Hook-Hmac'.
|
||||
#
|
||||
# Setting the following configuration option to 'compat' now restores the
|
||||
# old behaviour in both cases. Please notice however that this compatibility
|
||||
# option is likely to go away in the next major version of Indefero, so you
|
||||
# should really change the other end of your web hooks!
|
||||
$cfg['webhook_processing'] = 'compat';
|
||||
|
||||
# If IDF recalculates the activity index of the forge's projects, it does so
|
||||
# by looking at the created and updated items in a particular tab / section
|
||||
# for each project.
|
||||
#
|
||||
# You can now edit the weights that are applied to the calculation for each
|
||||
# section in order to give other things more precendence. For example, if you
|
||||
# do not use the documentation part to a great extent in most of your projects,
|
||||
# you can weight this section lower and get an overall better activity value.
|
||||
#
|
||||
# If a section is removed, then activity in this section is neglected during
|
||||
# the calculation. The same is true in case a section is disabled in the
|
||||
# project administration.
|
||||
$cfg['activity_section_weights'] = array(
|
||||
'source' => 4,
|
||||
'issues' => 2,
|
||||
'wiki' => 2,
|
||||
'downloads' => 1,
|
||||
'review' => 1,
|
||||
);
|
||||
|
||||
# Here you can define the timespan in days how long the activity calculation
|
||||
# process should look into the history to get meaningful activity values for
|
||||
# each project.
|
||||
#
|
||||
# If you have many low-profile projects in your forge, i.e. projects that only
|
||||
# record very little activity, then it might be a good idea to bump this value
|
||||
# high enough to show a proper activity index for those projects as well.
|
||||
$cfg['activity_lookback'] = 7;
|
||||
|
||||
return $cfg;
|
||||
|
||||
|
@ -29,6 +29,16 @@ $ctl[] = array('regex' => '#^/$#',
|
||||
'model' => 'IDF_Views',
|
||||
'method' => 'index');
|
||||
|
||||
$ctl[] = array('regex' => '#^/projects/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views',
|
||||
'method' => 'listProjects');
|
||||
|
||||
$ctl[] = array('regex' => '#^/projects/label/(\w+)/(\w+)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views',
|
||||
'method' => 'listProjectsByLabel');
|
||||
|
||||
$ctl[] = array('regex' => '#^/login/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views',
|
||||
@ -255,17 +265,32 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/source/changesrev/$#',
|
||||
'model' => 'IDF_Views_Source_Svn',
|
||||
'method' => 'changelogRev');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/repo/(.*)$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Source',
|
||||
'method' => 'repository');
|
||||
|
||||
// ---------- WIKI -----------------------------------------
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'index');
|
||||
'method' => 'listPages');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/res/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'listResources');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/create/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'create');
|
||||
'method' => 'createPage');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/res/create/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'createResource');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/search/$#',
|
||||
'base' => $base,
|
||||
@ -275,30 +300,60 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/search/$#',
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/label/(\d+)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'listLabel');
|
||||
'method' => 'listPagesWithLabel');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/update/(.*)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'update');
|
||||
'method' => 'updatePage');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/res/update/(.*)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'updateResource');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/delrev/(\d+)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'deleteRev');
|
||||
'method' => 'deletePageRev');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/res/delrev/(\d+)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'deleteResourceRev');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/delete/(\d+)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'delete');
|
||||
'method' => 'deletePage');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/res/delete/(\d+)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'deleteResource');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/res/raw/(.*)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'rawResource');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/page/(.*)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'view');
|
||||
'method' => 'viewPage');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/resource/(.*)/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Wiki',
|
||||
'method' => 'viewResource');
|
||||
|
||||
// ---------- Downloads ------------------------------------
|
||||
|
||||
$ctl[] = array('regex' => '#^/help/archive-format/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views',
|
||||
'method' => 'faqArchiveFormat');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Download',
|
||||
@ -327,7 +382,12 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/(\d+)/get/$#',
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/create/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Download',
|
||||
'method' => 'submit');
|
||||
'method' => 'create');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/create/archive/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Download',
|
||||
'method' => 'createFromArchive');
|
||||
|
||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/(\d+)/delete/$#',
|
||||
'base' => $base,
|
||||
@ -418,6 +478,11 @@ $ctl[] = array('regex' => '#^/api/$#',
|
||||
|
||||
// ---------- FORGE ADMIN --------------------------------
|
||||
|
||||
$ctl[] = array('regex' => '#^/admin/forge/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Admin',
|
||||
'method' => 'forge');
|
||||
|
||||
$ctl[] = array('regex' => '#^/admin/projects/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Admin',
|
||||
@ -428,6 +493,11 @@ $ctl[] = array('regex' => '#^/admin/projects/(\d+)/$#',
|
||||
'model' => 'IDF_Views_Admin',
|
||||
'method' => 'projectUpdate');
|
||||
|
||||
$ctl[] = array('regex' => '#^/admin/projects/labels/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Admin',
|
||||
'method' => 'projectLabels');
|
||||
|
||||
$ctl[] = array('regex' => '#^/admin/projects/create/$#',
|
||||
'base' => $base,
|
||||
'model' => 'IDF_Views_Admin',
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4587
src/IDF/locale/pt_BR/idf.po
Normal file
4587
src/IDF/locale/pt_BR/idf.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4581
src/IDF/locale/tr/idf.po
Normal file
4581
src/IDF/locale/tr/idf.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,9 @@
|
||||
# ***** END LICENSE BLOCK ***** */
|
||||
|
||||
$m = array();
|
||||
$m['IDF_Tag'] = array('relate_to' => array('IDF_Project'));
|
||||
$m['IDF_ProjectActivity'] = array('relate_to' => array('IDF_Project'));
|
||||
$m['IDF_Tag'] = array('relate_to' => array('IDF_Project'),
|
||||
'relate_to_many' => array('IDF_Project'));
|
||||
$m['IDF_Issue'] = array('relate_to' => array('IDF_Project', 'Pluf_User', 'IDF_Tag'),
|
||||
'relate_to_many' => array('IDF_Tag', 'Pluf_User'));
|
||||
$m['IDF_IssueComment'] = array('relate_to' => array('IDF_Issue', 'Pluf_User'));
|
||||
@ -30,9 +32,12 @@ $m['IDF_IssueFile'] = array('relate_to' => array('IDF_IssueComment', 'Pluf_User'
|
||||
$m['IDF_Upload'] = array('relate_to' => array('IDF_Project', 'Pluf_User'),
|
||||
'relate_to_many' => array('IDF_Tag'));
|
||||
$m['IDF_Search_Occ'] = array('relate_to' => array('IDF_Project'),);
|
||||
$m['IDF_WikiPage'] = array('relate_to' => array('IDF_Project', 'Pluf_User'),
|
||||
$m['IDF_Wiki_Page'] = array('relate_to' => array('IDF_Project', 'Pluf_User'),
|
||||
'relate_to_many' => array('IDF_Tag', 'Pluf_User'));
|
||||
$m['IDF_WikiRevision'] = array('relate_to' => array('IDF_WikiPage', 'Pluf_User'));
|
||||
$m['IDF_Wiki_PageRevision'] = array('relate_to' => array('IDF_Wiki_Page', 'Pluf_User'));
|
||||
$m['IDF_Wiki_Resource'] = array('relate_to' => array('IDF_Project', 'Pluf_User'));
|
||||
$m['IDF_Wiki_ResourceRevision'] = array('relate_to' => array('IDF_Wiki_Resource', 'Pluf_User'),
|
||||
'relate_to_many' => array('IDF_PageRevision', 'Pluf_User'));
|
||||
$m['IDF_Review'] = array('relate_to' => array('IDF_Project', 'Pluf_User', 'IDF_Tag'),
|
||||
'relate_to_many' => array('IDF_Tag', 'Pluf_User'));
|
||||
$m['IDF_Review_Patch'] = array('relate_to' => array('IDF_Review', 'Pluf_User'));
|
||||
|
@ -1,5 +1,5 @@
|
||||
{extends "idf/admin/base.html"}
|
||||
{block docclass}yui-t1{assign $inDownloads = true}{/block}
|
||||
{block docclass}yui-t3{assign $inDownloads = true}{/block}
|
||||
{block body}
|
||||
<form method="post" action=".">
|
||||
<table class="form" summary="">
|
||||
@ -16,6 +16,15 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{$form.f.upload_webhook_url.labelTag}:<br />
|
||||
{if $form.f.upload_webhook_url.errors}{$form.f.upload_webhook_url.fieldErrors}{/if}
|
||||
{$form.f.upload_webhook_url|unsafe}<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{trans 'Web-Hook authentication key:'} {$hookkey}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<input type="submit" value="{trans 'Save Changes'}" name="submit" />
|
||||
</td>
|
||||
@ -31,4 +40,29 @@
|
||||
<p>Optionally, use an equals-sign to document the meaning of each status value.</p>
|
||||
{/blocktrans}
|
||||
</div>
|
||||
<div class="issue-submit-info">
|
||||
|
||||
{blocktrans}<p>The webhook URL setting specifies an URL to which a HTTP <strong>PUT</strong>
|
||||
request is sent after a new download has been added or to which a HTTP <strong>POST</strong>
|
||||
request is sent after an existing download has been updated.
|
||||
If this field is empty, notifications are disabled.</p>
|
||||
|
||||
<p>Only properly-escaped <strong>HTTP</strong> URLs are supported, for example:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>http://domain.com/upload</code></li>
|
||||
<li><code>http://domain.com/upload?my%20param</code></li>
|
||||
</ul>
|
||||
|
||||
<p>In addition, the URL may contain the following "%" notation, which
|
||||
will be replaced with specific project values for each download:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>%p</code> - project name</li>
|
||||
<li><code>%d</code> - download id</li>
|
||||
</ul>
|
||||
|
||||
<p>For example, updating download 123 of project 'my-project' with
|
||||
web hook URL <code>http://mydomain.com/%p/%d</code> would send a POST request to
|
||||
<code>http://mydomain.com/my-project/123</code>.</p>{/blocktrans}</div>
|
||||
{/block}
|
||||
|
@ -41,12 +41,11 @@
|
||||
<tr>
|
||||
<th>{$form.f.webhook_url.labelTag}:</th>
|
||||
<td>{if $form.f.webhook_url.errors}{$form.f.webhook_url.fieldErrors}{/if}
|
||||
{$form.f.webhook_url|unsafe}<br>
|
||||
|
||||
{$form.f.webhook_url|unsafe}<br />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{trans 'Post-commit authentication key:'}</th>
|
||||
<th>{trans 'Web-Hook authentication key:'}</th>
|
||||
<td>{$hookkey}
|
||||
</td>
|
||||
</tr>
|
||||
@ -68,26 +67,26 @@
|
||||
<br>
|
||||
<div class="issue-submit-info">
|
||||
|
||||
{blocktrans}<p>The webhook URL setting specifies a URL to which a HTTP POST
|
||||
request is sent after each repository commit. If this field is empty,
|
||||
notifications are disabled.</p>
|
||||
{blocktrans}<p>The webhook URL setting specifies an URL to which a HTTP
|
||||
<strong>{$hook_request_method}</strong> request is sent after each repository
|
||||
commit. If this field is empty, notifications are disabled.</p>
|
||||
|
||||
<p>Only properly-escaped <strong>HTTP</strong> URLs are supported, for example:</p>
|
||||
|
||||
<ul>
|
||||
<li>http://domain.com/commit</li>
|
||||
<li>http://domain.com/commit?my%20param</li>
|
||||
<li><code>http://domain.com/commit</code></li>
|
||||
<li><code>http://domain.com/commit?my%20param</code></li>
|
||||
</ul>
|
||||
|
||||
<p>In addition, the URL may contain the following "%" notation, which
|
||||
will be replaced with specific project values for each commit:</p>
|
||||
|
||||
<ul>
|
||||
<li>%p - project name</li>
|
||||
<li>%r - revision number</li>
|
||||
<li><code>%p</code> - project name</li>
|
||||
<li><code>%r</code> - revision number</li>
|
||||
</ul>
|
||||
|
||||
<p>For example, committing revision 123 to project 'my-project' with
|
||||
post-commit URL http://mydomain.com/%p/%r would send a request to
|
||||
http://mydomain.com/my-project/123.</p>{/blocktrans}</div>
|
||||
post-commit URL <code>http://mydomain.com/%p/%r</code> would send a request to
|
||||
<code>http://mydomain.com/my-project/123</code>.</p>{/blocktrans}</div>
|
||||
{/block}
|
||||
|
@ -25,13 +25,30 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{$form.f.external_project_url.labelTag}:</th>
|
||||
<td>{if $form.f.external_project_url.errors}{$form.f.external_project_url.fieldErrors}{/if}
|
||||
{$form.f.external_project_url|unsafe}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><strong>{$form.f.description.labelTag}:</strong></th>
|
||||
<td>{if $form.f.description.errors}{$form.f.description.fieldErrors}{/if}
|
||||
{$form.f.description|unsafe}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><strong>{trans 'Current logo'}:</strong></th>
|
||||
<th>{$form.f.label1.labelTag}:</th>
|
||||
<td>
|
||||
{if $form.f.label1.errors}{$form.f.label1.fieldErrors}{/if}{$form.f.label1|unsafe}
|
||||
{if $form.f.label2.errors}{$form.f.label2.fieldErrors}{/if}{$form.f.label2|unsafe}
|
||||
{if $form.f.label3.errors}{$form.f.label3.fieldErrors}{/if}{$form.f.label3|unsafe}<br />
|
||||
{if $form.f.label4.errors}{$form.f.label4.fieldErrors}{/if}{$form.f.label4|unsafe}
|
||||
{if $form.f.label5.errors}{$form.f.label5.fieldErrors}{/if}{$form.f.label5|unsafe}
|
||||
{if $form.f.label6.errors}{$form.f.label6.fieldErrors}{/if}{$form.f.label6|unsafe}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{trans 'Current logo'}:</th>
|
||||
<td>
|
||||
{if $logo}
|
||||
<img src="{url 'IDF_Views_Project::logo', array($project.shortname)}" alt="{trans 'Project logo'}" />
|
||||
@ -41,14 +58,14 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><strong>{$form.f.logo.labelTag}:</strong></th>
|
||||
<th>{$form.f.logo.labelTag}:</th>
|
||||
<td>{if $form.f.logo.errors}{$form.f.logo.fieldErrors}{/if}
|
||||
{$form.f.logo|unsafe}
|
||||
</td>
|
||||
</tr>
|
||||
{if $logo}
|
||||
<tr>
|
||||
<th><strong>{$form.f.logo_remove.labelTag}:</strong></th>
|
||||
<th>{$form.f.logo_remove.labelTag}:</th>
|
||||
<td>{if $form.f.logo_remove.errors}{$form.f.logo_remove.fieldErrors}{/if}
|
||||
{$form.f.logo_remove|unsafe}
|
||||
</td>
|
||||
@ -60,6 +77,7 @@
|
||||
</td>
|
||||
</table>
|
||||
</form>
|
||||
{include 'idf/project/js-autocomplete.html'}{/block}
|
||||
{/block}
|
||||
|
||||
{block context}
|
||||
|
@ -10,11 +10,11 @@
|
||||
</div>
|
||||
{/if}
|
||||
<form method="post" action=".">
|
||||
<table class="form" summary="">
|
||||
<table class="form access-rights" summary="">
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th class="a-c"><strong>{trans 'Access Rights'}</strong></th>
|
||||
<th class="a-c"><strong>{trans 'Notification Email'}</strong></th>
|
||||
<th class="left"><strong>{trans 'Access Rights'}</strong></th>
|
||||
<th class="left"><strong>{trans 'Notifications'}</strong></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><strong>{$form.f.downloads_access_rights.labelTag}:</strong></th>
|
||||
@ -22,6 +22,12 @@
|
||||
{$form.f.downloads_access_rights|unsafe}
|
||||
</td>
|
||||
<td>{if $form.f.downloads_notification_email.errors}{$form.f.downloads_notification_email.fieldErrors}{/if}
|
||||
{$form.f.downloads_notification_owners_enabled|unsafe}
|
||||
{$form.f.downloads_notification_owners_enabled.labelTag}
|
||||
{$form.f.downloads_notification_members_enabled|unsafe}
|
||||
{$form.f.downloads_notification_members_enabled.labelTag}
|
||||
{$form.f.downloads_notification_email_enabled|unsafe}
|
||||
{$form.f.downloads_notification_email_enabled.labelTag}
|
||||
{$form.f.downloads_notification_email|unsafe}
|
||||
</td>
|
||||
</tr>
|
||||
@ -31,6 +37,12 @@
|
||||
{$form.f.wiki_access_rights|unsafe}
|
||||
</td>
|
||||
<td>{if $form.f.wiki_notification_email.errors}{$form.f.wiki_notification_email.fieldErrors}{/if}
|
||||
{$form.f.wiki_notification_owners_enabled|unsafe}
|
||||
{$form.f.wiki_notification_owners_enabled.labelTag}
|
||||
{$form.f.wiki_notification_members_enabled|unsafe}
|
||||
{$form.f.wiki_notification_members_enabled.labelTag}
|
||||
{$form.f.wiki_notification_email_enabled|unsafe}
|
||||
{$form.f.wiki_notification_email_enabled.labelTag}
|
||||
{$form.f.wiki_notification_email|unsafe}
|
||||
</td>
|
||||
</tr>
|
||||
@ -40,6 +52,12 @@
|
||||
{$form.f.issues_access_rights|unsafe}
|
||||
</td>
|
||||
<td>{if $form.f.issues_notification_email.errors}{$form.f.issues_notification_email.fieldErrors}{/if}
|
||||
{$form.f.issues_notification_owners_enabled|unsafe}
|
||||
{$form.f.issues_notification_owners_enabled.labelTag}
|
||||
{$form.f.issues_notification_members_enabled|unsafe}
|
||||
{$form.f.issues_notification_members_enabled.labelTag}
|
||||
{$form.f.issues_notification_email_enabled|unsafe}
|
||||
{$form.f.issues_notification_email_enabled.labelTag}
|
||||
{$form.f.issues_notification_email|unsafe}
|
||||
</td>
|
||||
</tr>
|
||||
@ -49,32 +67,37 @@
|
||||
{$form.f.source_access_rights|unsafe}
|
||||
</td>
|
||||
<td>{if $form.f.source_notification_email.errors}{$form.f.source_notification_email.fieldErrors}{/if}
|
||||
{$form.f.source_notification_owners_enabled|unsafe}
|
||||
{$form.f.source_notification_owners_enabled.labelTag}
|
||||
{$form.f.source_notification_members_enabled|unsafe}
|
||||
{$form.f.source_notification_members_enabled.labelTag}
|
||||
{$form.f.source_notification_email_enabled|unsafe}
|
||||
{$form.f.source_notification_email_enabled.labelTag}
|
||||
{$form.f.source_notification_email|unsafe}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td colspan="2" class="helptext">
|
||||
{blocktrans}
|
||||
Only project members and admins have write access to the source.<br />
|
||||
If you restrict the access to the source, anonymous access is<br />
|
||||
not provided and the users must authenticate themselves with their<br />
|
||||
password or SSH key.{/blocktrans}
|
||||
</td></tr>
|
||||
<tr>
|
||||
<th><strong>{$form.f.review_access_rights.labelTag}:</strong></th>
|
||||
<td>{if $form.f.review_access_rights.errors}{$form.f.review_access_rights.fieldErrors}{/if}
|
||||
{$form.f.review_access_rights|unsafe}
|
||||
</td>
|
||||
<td>{if $form.f.review_notification_email.errors}{$form.f.review_notification_email.fieldErrors}{/if}
|
||||
{$form.f.review_notification_owners_enabled|unsafe}
|
||||
{$form.f.review_notification_owners_enabled.labelTag}
|
||||
{$form.f.review_notification_members_enabled|unsafe}
|
||||
{$form.f.review_notification_members_enabled.labelTag}
|
||||
{$form.f.review_notification_email_enabled|unsafe}
|
||||
{$form.f.review_notification_email_enabled.labelTag}
|
||||
{$form.f.review_notification_email|unsafe}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{if $form.f.private_project.errors}{$form.f.private_project.fieldErrors}{/if}
|
||||
<th> </th>
|
||||
<td>
|
||||
{if $form.f.private_project.errors}{$form.f.private_project.fieldErrors}{/if}
|
||||
{$form.f.private_project|unsafe}
|
||||
</th>
|
||||
<td>{$form.f.private_project.labelTag}</td>
|
||||
{$form.f.private_project.labelTag}
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="authorized-users-row">
|
||||
<td> </td>
|
||||
@ -94,20 +117,24 @@ password or SSH key.{/blocktrans}
|
||||
{/block}
|
||||
{block context}
|
||||
<div class="issue-submit-info">
|
||||
<p><strong>{trans 'Instructions:'}</strong></p>
|
||||
<p>{blocktrans}You can configure here the project tabs access rights and notification emails.{/blocktrans}</p>
|
||||
<p>{blocktrans}Notification emails will be sent from the <strong>{$from_email}</strong> address, if you send the email to a mailing list, you may need to register this email address. Multiple email addresses must be separated through commas (','). If you do not want to send emails for a given type of changes, simply leave the corresponding field empty.{/blocktrans}</p>
|
||||
<p>{blocktrans}If you mark a project as private, only the project members and administrators, together with the extra authorized users you provide will have access to the project. You will still be able to define further access rights for the different tabs but the "Open to all" and "Signed in users" will default to authorized users only.{/blocktrans}</p>
|
||||
<p>{blocktrans}Specify each person by its login. Each person must have already registered with the given login. Separate the logins with commas and/or new lines.{/blocktrans}</p>
|
||||
<p>{blocktrans}This section allows you to configure project tabs access rights and notifications.{/blocktrans}</p>
|
||||
<p><strong>{trans 'Access Rights'}</strong></p>
|
||||
<p>{blocktrans}Tab access controls whether a single user can navigate into a particular section of your project via the main menu or automatically generated object links.{/blocktrans}</p>
|
||||
<p>{blocktrans}If you mark a project as private, only the project members and administrators, together with the extra authorized users you provide will have access to the project as a whole. You will still be able to define further access rights for the different tabs but the "Open to all" and "Signed in users" will default to authorized users only.{/blocktrans}</p>
|
||||
<p>{blocktrans}For the extra authorized user list, specify each person by its login. Each person must have already registered with the given login. Separate the logins with commas and/or new lines.{/blocktrans}</p>
|
||||
<p>{blocktrans}Only project members and admins have write access to the source. If you restrict the access to the source, anonymous access is not provided and the users must authenticate themselves with their password or SSH key.{/blocktrans}</p>
|
||||
<p><strong>{trans 'Notifications'}</strong></p>
|
||||
<p>{blocktrans}Here you can configure who should be notified about changes in a particular section. You can also configure additional addresses, like the one of a mailing list, that should be notified. (Keep in mind that you might have to register the sender address <strong>{$from_email}</strong> to let the mailing list actually accept notification emails.) Multiple email addresses must be separated through commas (',').{/blocktrans}</p>
|
||||
</div>
|
||||
{/block}
|
||||
|
||||
{block javascript}{literal}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
// If not checked, hide
|
||||
if (!$("#id_private_project").is(":checked"))
|
||||
// if not checked, hide
|
||||
if (!$("#id_private_project").is(":checked")) {
|
||||
$("#authorized-users-row").hide();
|
||||
}
|
||||
$("#id_private_project").click(function() {
|
||||
if ($("#id_private_project").is(":checked")) {
|
||||
$("#authorized-users-row").show();
|
||||
@ -115,6 +142,20 @@ $(document).ready(function(){
|
||||
$("#authorized-users-row").hide();
|
||||
}
|
||||
});
|
||||
|
||||
// if not checked, hide
|
||||
$.each(['downloads', 'wiki', 'issues', 'source', 'review'], function(index, section) {
|
||||
if (!$("#id_" + section + "_notification_email_enabled").is(":checked")) {
|
||||
$("#id_" + section + "_notification_email").hide();
|
||||
}
|
||||
$("#id_" + section + "_notification_email_enabled").click(function() {
|
||||
if ($("#id_" + section + "_notification_email_enabled").is(":checked")) {
|
||||
$("#id_" + section + "_notification_email").show();
|
||||
} else {
|
||||
$("#id_" + section + "_notification_email").hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{/literal}{/block}
|
||||
|
@ -31,20 +31,20 @@
|
||||
<![endif]-->
|
||||
{block extraheader}{/block}
|
||||
<title>{block pagetitle}{$page_title|strip_tags}{/block}{if $project} - {$project.shortdesc}{/if}</title>
|
||||
<script type="text/javascript" src="{media '/idf/js/jquery-1.2.6.min.js'}"></script>
|
||||
<script type="text/javascript" src="{media '/idf/js/jquery-1.7.1.min.js'}"></script>
|
||||
{appversion}
|
||||
</head>
|
||||
<body>
|
||||
<div id="{block docid}doc3{/block}">
|
||||
<div id="hd">
|
||||
{if $project}<h1 class="project-title">{$project}<img class="logo" src="{url 'IDF_Views_Project::logo', array($project.shortname)}" alt="{trans 'Project logo'}" />{if $project.private}<img class="lock" src="{media '/idf/img/lock.png'}" alt="{trans 'Private project'}" />{/if}{$p}</h1>{/if}
|
||||
{if $project}<h1 class="project-title">{$project}<img class="logo" src="{url 'IDF_Views_Project::logo', array($project.shortname)}" alt="{trans 'Project logo'}" />{if $project.private}<img class="lock" src="{media '/idf/img/lock.png'}" alt="{trans 'Private project'}" />{/if}{$p}{assign $url = $project.external_project_url}{if $url != ''}<a href="{$url}" target="_blank" class="external-link" title="{trans 'External link to project'}" /> </a>{/if}</h1>{/if}
|
||||
{include 'idf/main-menu.html'}
|
||||
<div id="header">
|
||||
<div id="main-tabs">
|
||||
{if $project}
|
||||
<a accesskey="1" href="{url 'IDF_Views_Project::home', array($project.shortname)}"{block tabhome}{/block}>{trans 'Project Home'}</a>
|
||||
{if $hasDownloadsAccess} <a href="{url 'IDF_Views_Download::index', array($project.shortname)}"{block tabdownloads}{/block}>{trans 'Downloads'}</a>{/if}
|
||||
{if $hasWikiAccess} <a href="{url 'IDF_Views_Wiki::index', array($project.shortname)}"{block tabwiki}{/block}>{trans 'Documentation'}</a>{/if}
|
||||
{if $hasWikiAccess} <a href="{url 'IDF_Views_Wiki::listPages', array($project.shortname)}"{block tabwiki}{/block}>{trans 'Documentation'}</a>{/if}
|
||||
{if $hasIssuesAccess} <a href="{url 'IDF_Views_Issue::index', array($project.shortname)}"{block tabissues}{/block}>{trans 'Issues'}</a>{/if}
|
||||
{if $hasSourceAccess} <a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}"{block tabsource}{/block}>{trans 'Source'}</a>{/if}
|
||||
{if $hasReviewAccess} <a href="{url 'IDF_Views_Review::index', array($project.shortname)}"{block tabreview}{/block}>{trans 'Code Review'}</a>{/if}
|
||||
|
@ -31,7 +31,7 @@
|
||||
<![endif]-->
|
||||
{block extraheader}{/block}
|
||||
<title>{block pagetitle}{$page_title|strip_tags}{/block}</title>
|
||||
<script type="text/javascript" src="{media '/idf/js/jquery-1.2.6.min.js'}"></script>
|
||||
<script type="text/javascript" src="{media '/idf/js/jquery-1.7.1.min.js'}"></script>
|
||||
{appversion}
|
||||
</head>
|
||||
<body>
|
||||
|
@ -31,20 +31,20 @@
|
||||
<![endif]-->
|
||||
{block extraheader}{/block}
|
||||
<title>{block pagetitle}{$page_title|strip_tags}{/block}{if $project} - {$project.shortdesc}{/if}</title>
|
||||
<script type="text/javascript" src="{media '/idf/js/jquery-1.2.6.min.js'}"></script>
|
||||
<script type="text/javascript" src="{media '/idf/js/jquery-1.7.1.min.js'}"></script>
|
||||
{appversion}
|
||||
</head>
|
||||
<body>
|
||||
<div id="{block docid}doc3{/block}" class="{block docclass}yui-t3{/block}">
|
||||
<div id="hd">
|
||||
{if $project}<h1 class="project-title">{$project}<img class="logo" src="{url 'IDF_Views_Project::logo', array($project.shortname)}" alt="{trans 'Project logo'}" />{if $project.private}<img class="lock" src="{media '/idf/img/lock.png'}" alt="{trans 'Private project'}" />{/if}{$p}</h1>{/if}
|
||||
{if $project}<h1 class="project-title">{$project}<img class="logo" src="{url 'IDF_Views_Project::logo', array($project.shortname)}" alt="{trans 'Project logo'}" />{if $project.private}<img class="lock" src="{media '/idf/img/lock.png'}" alt="{trans 'Private project'}" />{/if}{$p}{assign $url = $project.external_project_url}{if $url != ''}<a href="{$url}" target="_blank" class="external-link" title="{trans 'External link to project'}" /> </a>{/if}</h1>{/if}
|
||||
{include 'idf/main-menu.html'}
|
||||
<div id="header">
|
||||
<div id="main-tabs">
|
||||
{if $project}
|
||||
<a accesskey="1" href="{url 'IDF_Views_Project::home', array($project.shortname)}"{block tabhome}{/block}>{trans 'Project Home'}</a>
|
||||
{if $hasDownloadsAccess} <a href="{url 'IDF_Views_Download::index', array($project.shortname)}"{block tabdownloads}{/block}>{trans 'Downloads'}</a>{/if}
|
||||
{if $hasWikiAccess} <a href="{url 'IDF_Views_Wiki::index', array($project.shortname)}"{block tabwiki}{/block}>{trans 'Documentation'}</a>{/if}
|
||||
{if $hasWikiAccess} <a href="{url 'IDF_Views_Wiki::listPages', array($project.shortname)}"{block tabwiki}{/block}>{trans 'Documentation'}</a>{/if}
|
||||
{if $hasIssuesAccess} <a href="{url 'IDF_Views_Issue::index', array($project.shortname)}"{block tabissues}{/block}>{trans 'Issues'}</a>{/if}
|
||||
{if $hasSourceAccess} <a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}"{block tabsource}{/block}>{trans 'Source'}</a>{/if}
|
||||
{if $hasReviewAccess} <a href="{url 'IDF_Views_Review::index', array($project.shortname)}"{block tabreview}{/block}>{trans 'Code Review'}</a>{/if}
|
||||
|
@ -2,6 +2,10 @@
|
||||
{block tabdownloads} class="active"{/block}
|
||||
{block subtabs}
|
||||
<div id="sub-tabs">
|
||||
<a {if $inDownloads}class="active" {/if}href="{url 'IDF_Views_Download::index', array($project.shortname)}">{trans 'Downloads'}</a> {if $isOwner or $isMember}| <a {if $inSubmit}class="active" {/if}href="{url 'IDF_Views_Download::submit', array($project.shortname)}">{trans 'New Download'}</a> {/if}
|
||||
<a {if $inDownloads}class="active" {/if}href="{url 'IDF_Views_Download::index', array($project.shortname)}">{trans 'Downloads'}</a>
|
||||
{if $isOwner or $isMember}
|
||||
| <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Download::create', array($project.shortname)}">{trans 'New Download'}</a>
|
||||
| <a {if $inCreateFromArchive}class="active" {/if}href="{url 'IDF_Views_Download::createFromArchive', array($project.shortname)}">{trans 'Upload Archive'}</a>
|
||||
{/if}
|
||||
</div>
|
||||
{/block}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{extends "idf/downloads/base.html"}
|
||||
{block docclass}yui-t3{assign $inSubmit=true}{/block}
|
||||
{block docclass}yui-t3{assign $inCreate=true}{/block}
|
||||
{block body}
|
||||
{if $form.errors}
|
||||
<div class="px-message-error">
|
38
src/IDF/templates/idf/downloads/createFromArchive.html
Normal file
38
src/IDF/templates/idf/downloads/createFromArchive.html
Normal file
@ -0,0 +1,38 @@
|
||||
{extends "idf/downloads/base.html"}
|
||||
{block docclass}yui-t3{assign $inCreateFromArchive=true}{/block}
|
||||
{block body}
|
||||
{if $form.errors}
|
||||
<div class="px-message-error">
|
||||
<p>{trans 'The form contains some errors. Please correct them to submit the archive.'}</p>
|
||||
{if $form.get_top_errors}
|
||||
{$form.render_top_errors|unsafe}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form method="post" enctype="multipart/form-data" action=".">
|
||||
<table class="form" summary="">
|
||||
<tr>
|
||||
<th><strong>{$form.f.archive.labelTag}:</strong></th>
|
||||
<td>{if $form.f.archive.errors}{$form.f.archive.fieldErrors}{/if}
|
||||
{$form.f.archive|unsafe}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="submit" value="{trans 'Submit Archive'}" name="submit" /> | <a href="{url 'IDF_Views_Download::index', array($project.shortname)}">{trans 'Cancel'}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
{/block}
|
||||
{block context}
|
||||
<div class="issue-submit-info">
|
||||
<h2>{trans 'Instructions'}</h2>
|
||||
|
||||
<p>{blocktrans}The archive must include a <code>manifest.xml</code> file with meta information about the
|
||||
files to process inside the archive. All processed files must be unique or replace existing files explicitely.{/blocktrans}</p>
|
||||
{aurl 'url', 'IDF_Views::faqArchiveFormat'}
|
||||
<p>{blocktrans}You can learn more about the archive format <a href="{$url}">here</a>.{/blocktrans}</p>
|
||||
</div>
|
||||
{/block}
|
17
src/IDF/templates/idf/downloads/download-updated-email.txt
Normal file
17
src/IDF/templates/idf/downloads/download-updated-email.txt
Normal file
@ -0,0 +1,17 @@
|
||||
{trans 'Hello,'}
|
||||
|
||||
{blocktrans}A file download was updated:{/blocktrans}
|
||||
|
||||
{$file.summary|safe}
|
||||
{$file} - {$file.filesize|ssize}
|
||||
{trans 'Project:'} {$project.name|safe}
|
||||
{trans 'Submitted by:'} {$file.get_submitter|safe}
|
||||
{if $tags.count()}{trans 'Labels:'}
|
||||
{foreach $tags as $tag} {$tag.class|safe}:{$tag.name|safe}
|
||||
{/foreach}{/if}
|
||||
{trans 'Download:'} {$urlfile}
|
||||
{if $file.changelog}
|
||||
{trans 'Description:'}
|
||||
|
||||
{$file.changelog}
|
||||
{/if}
|
@ -3,7 +3,7 @@
|
||||
{block body}
|
||||
{$downloads.render}
|
||||
{if $isOwner or $isMember}
|
||||
{aurl 'url', 'IDF_Views_Download::submit', array($project.shortname)}
|
||||
{aurl 'url', 'IDF_Views_Download::create', array($project.shortname)}
|
||||
<p><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/add.png'}" alt="+" align="bottom" /></a> <a href="{$url}">{trans 'New Download'}</a></p>{/if}
|
||||
|
||||
{/block}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user