Merge branch 'develop'
This commit is contained in:
commit
023a3ce879
@ -149,7 +149,6 @@ look like this:
|
|||||||
$cfg['mtn_repositories'] = '/var/lib/usher/projects/%s/';
|
$cfg['mtn_repositories'] = '/var/lib/usher/projects/%s/';
|
||||||
$cfg['mtn_remote_url'] = 'mtn://my.server.com/%s';
|
$cfg['mtn_remote_url'] = 'mtn://my.server.com/%s';
|
||||||
$cfg['mtn_db_access'] = 'remote';
|
$cfg['mtn_db_access'] = 'remote';
|
||||||
$cfg['mtn_remote_auth'] = true;
|
|
||||||
$cfg['mtn_usher_conf'] = '/var/lib/usher/usher.conf';
|
$cfg['mtn_usher_conf'] = '/var/lib/usher/usher.conf';
|
||||||
...
|
...
|
||||||
|
|
||||||
@ -188,8 +187,10 @@ Remote commands can be helpful for a user or a 3rd party tool (like
|
|||||||
contents remotely without having to pull everything in first instance.
|
contents remotely without having to pull everything in first instance.
|
||||||
|
|
||||||
Private projects on the other hand can only be synced by team members
|
Private projects on the other hand can only be synced by team members
|
||||||
or additional invited people. Also noo remote command execution is enabled
|
or additional invited people. Remote command execution is still enabled
|
||||||
by default.
|
by default - if you want to disable that, simply remove the symlink to
|
||||||
|
the file `indefero_authorize_remote_automate.conf` in your project's `hooks.d`
|
||||||
|
directory or copy the file from the original location and adapt it.
|
||||||
|
|
||||||
## Notifications
|
## Notifications
|
||||||
|
|
||||||
@ -204,8 +205,54 @@ in a directory called `hooks.d` right under the project's base directory
|
|||||||
(configured via $cfg['mtn_repositories']) and this is the ideal place to
|
(configured via $cfg['mtn_repositories']) and this is the ideal place to
|
||||||
put or link these additional lua sources.
|
put or link these additional lua sources.
|
||||||
|
|
||||||
|
## Custom project configurations and templates
|
||||||
|
|
||||||
|
If a new project is created in IDF, the SyncMonotone plugin creates a new
|
||||||
|
configuration tree for the project into the project's configuration directory,
|
||||||
|
determined by `$cfg['mtn_repositories']`. IDF ships with the minimum set of
|
||||||
|
files for this configuration tree and sets up everything automatically for you.
|
||||||
|
|
||||||
|
Even more, most of the configuration files from the newly created tree are only
|
||||||
|
symlinked to the original configuration directory which is configurable via
|
||||||
|
`$cfg['mtn_confdir']` and defaults to `src/IDF/Plugin/SyncMonotone/`. This has
|
||||||
|
the advantage that your standard IDF setup automatically receives updates to
|
||||||
|
existing (symlinked) configuration files as soon as you update to a newer
|
||||||
|
version.
|
||||||
|
|
||||||
|
You could, however, also choose to place the directory tree somewhere else
|
||||||
|
and adapt the contents of the individual files yourself, so these changes get
|
||||||
|
automatically applied to all new projects you create. You could even go so far
|
||||||
|
and add new files to the tree and let them be processed automatically just
|
||||||
|
as the basic files! All you need to do is to copy your files and / or directories
|
||||||
|
underknees your `$cfg['mtn_confdir']` and add their relative paths to
|
||||||
|
`$cfg['mtn_confdir_extra']`.
|
||||||
|
|
||||||
|
By convention, all entries which end with a slash are considered directories,
|
||||||
|
so mkdir(1) is issued for these entries, all files which do not end up with
|
||||||
|
".in" are considered to be static script files which are just symlinked from
|
||||||
|
the basic configuration dir and all entries ending on ".in" are considered
|
||||||
|
configuration files or templates, which are copied over to the project's
|
||||||
|
configuration tree and which get some basic project-specific values replaced.
|
||||||
|
|
||||||
|
The following placeholders are currently recognized and replaced for these files:
|
||||||
|
|
||||||
|
* %%PROJECT%% - the name of the created project
|
||||||
|
* %%MTNPOSTPUSH%% - the absolute path to the `mtn-post-push` script
|
||||||
|
* %%MTNCLIENTKEY%% - the public key hash of the key which is used by IDF
|
||||||
|
to authenticate remote stdio access
|
||||||
|
|
||||||
|
Thats it - I hope you find it useful :)
|
||||||
|
|
||||||
## Q&A
|
## Q&A
|
||||||
|
|
||||||
|
### After I created a new project, IDF throws an exception and tells me that it couldn't save the membership data with a cryptic error message. Whats wrong?
|
||||||
|
|
||||||
|
Multiple issues could cause that. If you've set up usher, make sure the usher
|
||||||
|
can fork your database at all and look out for specific errors in the log file
|
||||||
|
of your project. If you stumble upon permission issues, ensure that the user
|
||||||
|
who runs the usher can access all files in your project's configuration directory,
|
||||||
|
including symlinked files.
|
||||||
|
|
||||||
### I pushed a branch to my server, but it does not show up in IDF. Whats wrong?
|
### I pushed a branch to my server, but it does not show up in IDF. Whats wrong?
|
||||||
|
|
||||||
Check if the heads of your branch are not suspended, i.e. do not carry a
|
Check if the heads of your branch are not suspended, i.e. do not carry a
|
||||||
|
86
logo/indefero-logo-lite.svg
Normal file
86
logo/indefero-logo-lite.svg
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
id="svg2"
|
||||||
|
sodipodi:version="0.32"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
version="1.0"
|
||||||
|
sodipodi:docname="indefero-logo-lite.svg"
|
||||||
|
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<inkscape:perspective
|
||||||
|
sodipodi:type="inkscape:persp3d"
|
||||||
|
inkscape:vp_x="0 : 526.18109 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||||
|
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||||
|
id="perspective10" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
gridtolerance="10000"
|
||||||
|
guidetolerance="10"
|
||||||
|
objecttolerance="10"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="11.313708"
|
||||||
|
inkscape:cx="31.568929"
|
||||||
|
inkscape:cy="-0.35578703"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="1280"
|
||||||
|
inkscape:window-height="723"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="24"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-728.09451)">
|
||||||
|
<g
|
||||||
|
id="g2401"
|
||||||
|
transform="matrix(0.13580542,0,0,0.13580542,-47.580342,708.10521)"
|
||||||
|
style="fill:#8ae234;stroke:#4e9a06;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
inkscape:export-filename="/home/loa/Projects/indefero/logo/powered-by-indefero.png"
|
||||||
|
inkscape:export-xdpi="12.330909"
|
||||||
|
inkscape:export-ydpi="12.330909">
|
||||||
|
<path
|
||||||
|
id="path2383"
|
||||||
|
d="m 396.19089,173.14471 c -7.67621,0.80661 -14.40195,5.39406 -19.58101,10.89131 -7.23597,7.88004 -11.69742,18.07908 -13.32198,28.60362 -1.7236,11.28173 -0.25925,23.20635 5.07686,33.37271 3.78607,7.24384 9.53161,13.92339 17.29701,16.96772 3.86478,1.53937 8.98362,1.03284 11.67912,-2.41036 2.64357,-3.5671 2.69463,-8.234 2.85756,-12.48867 0.045,-7.61054 -0.54749,-15.25544 0.45618,-22.83193 0.87131,-9.50623 4.03944,-18.56751 6.71612,-27.66851 1.16242,-4.44333 2.25094,-9.02808 1.97499,-13.64988 -0.48817,-4.62476 -3.58059,-9.31042 -8.2964,-10.4067 -1.57489,-0.44882 -3.23412,-0.48948 -4.85845,-0.37931 z"
|
||||||
|
style="fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:#4e9a06;stroke-width:2.4000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
id="path2391"
|
||||||
|
d="m 433.14691,149.28687 c 7.2059,2.76589 12.51512,8.93778 16.09494,15.58815 4.94991,9.48434 6.61962,20.49058 5.46486,31.07695 -1.25505,11.34342 -5.75582,22.48271 -13.54134,30.92159 -5.53192,6.01709 -12.81048,10.98198 -21.09918,11.91276 -4.13154,0.4866 -8.94486,-1.32748 -10.65734,-5.35104 -1.63027,-4.12976 -0.4717,-8.65084 0.47212,-12.80269 1.92628,-7.36287 4.47721,-14.59393 5.4687,-22.17201 1.61875,-9.40784 0.90381,-18.98034 0.67386,-28.46402 0.0272,-4.59278 0.1624,-9.30303 1.62515,-13.69592 1.66851,-4.34082 5.86829,-8.06645 10.70716,-7.90484 1.63738,-0.0259 3.25061,0.36424 4.79107,0.89107 z"
|
||||||
|
style="fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:#4e9a06;stroke-width:2.4000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.1 KiB |
@ -55,6 +55,7 @@ Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
|
|||||||
$params = array('repo_dir' => $argv[1],
|
$params = array('repo_dir' => $argv[1],
|
||||||
'revision' => $argv[2],
|
'revision' => $argv[2],
|
||||||
'env' => array_merge($_ENV, $_SERVER));
|
'env' => array_merge($_ENV, $_SERVER));
|
||||||
|
Pluf_Log::event(array('svnpostcommit.php', 'Send run signal.', $params));
|
||||||
Pluf_Signal::send('svnpostcommit.php::run', 'svnpostcommit.php', $params);
|
Pluf_Signal::send('svnpostcommit.php::run', 'svnpostcommit.php', $params);
|
||||||
|
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ class IDF_Form_UserAccount extends Pluf_Form
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('#^ssh\-[a-z]{3}\s\S+==(\s\S+)?$#', $key)) {
|
if (preg_match('#^ssh\-[a-z]{3}\s\S+(\s\S+)?$#', $key)) {
|
||||||
$key = str_replace(array("\n", "\r"), '', $key);
|
$key = str_replace(array("\n", "\r"), '', $key);
|
||||||
|
|
||||||
if (Pluf::f('idf_strong_key_check', false)) {
|
if (Pluf::f('idf_strong_key_check', false)) {
|
||||||
|
@ -230,6 +230,24 @@ class IDF_Plugin_SyncGit_Serve
|
|||||||
}
|
}
|
||||||
Pluf_Log::debug(array('IDF_Plugin_Git_Serve::initRepository',
|
Pluf_Log::debug(array('IDF_Plugin_Git_Serve::initRepository',
|
||||||
'Added post-update hook.', $fullpath));
|
'Added post-update hook.', $fullpath));
|
||||||
|
// Configure the core.quotepath option
|
||||||
|
$quotepath = (Pluf::f('git_core_quotepath', true) == true) ? 'true' : 'false';
|
||||||
|
$out = array();
|
||||||
|
$res = 0;
|
||||||
|
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').
|
||||||
|
Pluf::f('git_path', 'git').' config -f %s/config --add core.quotepath %s',
|
||||||
|
escapeshellarg($fullpath),
|
||||||
|
escapeshellarg($quotepath)
|
||||||
|
),
|
||||||
|
$out, $res);
|
||||||
|
if ($res != 0) {
|
||||||
|
Pluf_Log::warn(array('IDF_Plugin_Git_Serve::initRepository',
|
||||||
|
'core.quotepath configuration error.',
|
||||||
|
$quotepath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Pluf_Log::debug(array('IDF_Plugin_Git_Serve::initRepository',
|
||||||
|
'core.quotepath configured.', $quotepath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,7 +62,7 @@ class IDF_Plugin_SyncMonotone
|
|||||||
* 'mtn_repositories'
|
* 'mtn_repositories'
|
||||||
* 2) create a new server key in the same directory
|
* 2) create a new server key in the same directory
|
||||||
* 3) create a new client key for IDF and store it in the project conf
|
* 3) create a new client key for IDF and store it in the project conf
|
||||||
* 4) write monotonerc
|
* 4) setup the configuration
|
||||||
* 5) add the database as new local server in the usher configuration
|
* 5) add the database as new local server in the usher configuration
|
||||||
* 6) reload the running usher instance so it acknowledges the new server
|
* 6) reload the running usher instance so it acknowledges the new server
|
||||||
*
|
*
|
||||||
@ -101,6 +101,36 @@ class IDF_Plugin_SyncMonotone
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check some static configuration files
|
||||||
|
$confdir = Pluf::f('mtn_confdir', false);
|
||||||
|
if ($confdir === false) {
|
||||||
|
$confdir = dirname(__FILE__).'/SyncMonotone/';
|
||||||
|
}
|
||||||
|
$confdir_contents = array(
|
||||||
|
'monotonerc.in',
|
||||||
|
'remote-automate-permissions.in',
|
||||||
|
'hooks.d/',
|
||||||
|
// this is linked and not copied to be able to update
|
||||||
|
// the list of read-only commands on upgrades
|
||||||
|
'hooks.d/indefero_authorize_remote_automate.conf',
|
||||||
|
'hooks.d/indefero_authorize_remote_automate.lua',
|
||||||
|
'hooks.d/indefero_post_push.conf.in',
|
||||||
|
'hooks.d/indefero_post_push.lua',
|
||||||
|
);
|
||||||
|
// check whether we should handle additional files in the config directory
|
||||||
|
$confdir_extra_contents = Pluf::f('mtn_confdir_extra', false);
|
||||||
|
if ($confdir_extra_contents !== false) {
|
||||||
|
$confdir_contents =
|
||||||
|
array_merge($confdir_contents, $confdir_extra_contents);
|
||||||
|
}
|
||||||
|
foreach ($confdir_contents as $content) {
|
||||||
|
if (!file_exists($confdir.$content)) {
|
||||||
|
throw new IDF_Scm_Exception(sprintf(
|
||||||
|
__('The configuration file %s is missing.'), $content
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$shortname = $project->shortname;
|
$shortname = $project->shortname;
|
||||||
$projectpath = sprintf($projecttempl, $shortname);
|
$projectpath = sprintf($projecttempl, $shortname);
|
||||||
if (file_exists($projectpath)) {
|
if (file_exists($projectpath)) {
|
||||||
@ -144,79 +174,97 @@ class IDF_Plugin_SyncMonotone
|
|||||||
//
|
//
|
||||||
// step 3) create a client key, and save it in IDF
|
// step 3) create a client key, and save it in IDF
|
||||||
//
|
//
|
||||||
$clientkey_hash = '';
|
$keydir = Pluf::f('tmp_folder').'/mtn-client-keys';
|
||||||
$monotonerc_tpl = 'monotonerc-noauth.tpl';
|
if (!file_exists($keydir)) {
|
||||||
|
if (!mkdir($keydir)) {
|
||||||
if (Pluf::f('mtn_remote_auth', true)) {
|
|
||||||
$monotonerc_tpl = 'monotonerc-auth.tpl';
|
|
||||||
$keydir = Pluf::f('tmp_folder').'/mtn-client-keys';
|
|
||||||
if (!file_exists($keydir)) {
|
|
||||||
if (!mkdir($keydir)) {
|
|
||||||
throw new IDF_Scm_Exception(sprintf(
|
|
||||||
__('The key directory %s could not be created.'), $keydir
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$clientkey_name = $shortname.'-client@'.$server;
|
|
||||||
$cmd = sprintf('au generate_key --keydir=%s %s ""',
|
|
||||||
escapeshellarg($keydir),
|
|
||||||
escapeshellarg($clientkey_name)
|
|
||||||
);
|
|
||||||
$keyinfo = self::_mtn_exec($cmd);
|
|
||||||
|
|
||||||
$parsed_keyinfo = array();
|
|
||||||
try {
|
|
||||||
$parsed_keyinfo = IDF_Scm_Monotone_BasicIO::parse($keyinfo);
|
|
||||||
}
|
|
||||||
catch (Exception $e) {
|
|
||||||
throw new IDF_Scm_Exception(sprintf(
|
throw new IDF_Scm_Exception(sprintf(
|
||||||
__('Could not parse key information: %s'), $e->getMessage()
|
__('The key directory %s could not be created.'), $keydir
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
$clientkey_hash = $parsed_keyinfo[0][1]['hash'];
|
|
||||||
$clientkey_file = $keydir . '/' . $clientkey_name . '.' . $clientkey_hash;
|
|
||||||
$clientkey_data = file_get_contents($clientkey_file);
|
|
||||||
|
|
||||||
$project->getConf()->setVal('mtn_client_key_name', $clientkey_name);
|
|
||||||
$project->getConf()->setVal('mtn_client_key_hash', $clientkey_hash);
|
|
||||||
$project->getConf()->setVal('mtn_client_key_data', $clientkey_data);
|
|
||||||
|
|
||||||
// add the public client key to the server
|
|
||||||
$cmd = sprintf('au get_public_key --keydir=%s %s',
|
|
||||||
escapeshellarg($keydir),
|
|
||||||
escapeshellarg($clientkey_hash)
|
|
||||||
);
|
|
||||||
$clientkey_pubdata = self::_mtn_exec($cmd);
|
|
||||||
|
|
||||||
$cmd = sprintf('au put_public_key --db=%s %s',
|
|
||||||
escapeshellarg($dbfile),
|
|
||||||
escapeshellarg($clientkey_pubdata)
|
|
||||||
);
|
|
||||||
self::_mtn_exec($cmd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
$clientkey_name = $shortname.'-client@'.$server;
|
||||||
// step 4) write monotonerc
|
$cmd = sprintf('au generate_key --keydir=%s %s ""',
|
||||||
//
|
escapeshellarg($keydir),
|
||||||
$monotonerc = file_get_contents(
|
escapeshellarg($clientkey_name)
|
||||||
dirname(__FILE__).'/SyncMonotone/'.$monotonerc_tpl
|
|
||||||
);
|
|
||||||
$monotonerc = str_replace(
|
|
||||||
array('%%MTNPOSTPUSH%%', '%%PROJECT%%', '%%MTNCLIENTKEY%%'),
|
|
||||||
array($mtnpostpush, $shortname, $clientkey_hash),
|
|
||||||
$monotonerc
|
|
||||||
);
|
);
|
||||||
|
$keyinfo = self::_mtn_exec($cmd);
|
||||||
|
|
||||||
$rcfile = $projectpath.'/monotonerc';
|
$parsed_keyinfo = array();
|
||||||
|
try {
|
||||||
if (file_put_contents($rcfile, $monotonerc, LOCK_EX) === false) {
|
$parsed_keyinfo = IDF_Scm_Monotone_BasicIO::parse($keyinfo);
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
throw new IDF_Scm_Exception(sprintf(
|
throw new IDF_Scm_Exception(sprintf(
|
||||||
__('Could not write mtn configuration file "%s"'), $rcfile
|
__('Could not parse key information: %s'), $e->getMessage()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$clientkey_hash = $parsed_keyinfo[0][1]['hash'];
|
||||||
|
$clientkey_file = $keydir . '/' . $clientkey_name . '.' . $clientkey_hash;
|
||||||
|
$clientkey_data = file_get_contents($clientkey_file);
|
||||||
|
|
||||||
|
$project->getConf()->setVal('mtn_client_key_name', $clientkey_name);
|
||||||
|
$project->getConf()->setVal('mtn_client_key_hash', $clientkey_hash);
|
||||||
|
$project->getConf()->setVal('mtn_client_key_data', $clientkey_data);
|
||||||
|
|
||||||
|
// add the public client key to the server
|
||||||
|
$cmd = sprintf('au get_public_key --keydir=%s %s',
|
||||||
|
escapeshellarg($keydir),
|
||||||
|
escapeshellarg($clientkey_hash)
|
||||||
|
);
|
||||||
|
$clientkey_pubdata = self::_mtn_exec($cmd);
|
||||||
|
|
||||||
|
$cmd = sprintf('au put_public_key --db=%s %s',
|
||||||
|
escapeshellarg($dbfile),
|
||||||
|
escapeshellarg($clientkey_pubdata)
|
||||||
|
);
|
||||||
|
self::_mtn_exec($cmd);
|
||||||
|
|
||||||
|
//
|
||||||
|
// step 4) setup the configuration
|
||||||
|
//
|
||||||
|
|
||||||
|
// we assume that all confdir entries ending with a slash mean a
|
||||||
|
// directory that has to be created, that all files ending on ".in"
|
||||||
|
// have to be processed and copied in place and that all other files
|
||||||
|
// just need to be symlinked from the original location
|
||||||
|
foreach ($confdir_contents as $content) {
|
||||||
|
$filepath = $projectpath.'/'.$content;
|
||||||
|
if (substr($content, -1) == '/') {
|
||||||
|
if (!mkdir($filepath)) {
|
||||||
|
throw new IDF_Scm_Exception(sprintf(
|
||||||
|
__('Could not create configuration directory "%s"'), $filepath
|
||||||
|
));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (substr($content, -3) != '.in') {
|
||||||
|
if (!symlink($confdir.$content, $filepath)) {
|
||||||
|
IDF_Scm_Exception(sprintf(
|
||||||
|
__('Could not create symlink "%s"'), $filepath
|
||||||
|
));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filecontents = file_get_contents($confdir.'/'.$content);
|
||||||
|
$filecontents = str_replace(
|
||||||
|
array('%%MTNPOSTPUSH%%', '%%PROJECT%%', '%%MTNCLIENTKEY%%'),
|
||||||
|
array($mtnpostpush, $shortname, $clientkey_hash),
|
||||||
|
$filecontents
|
||||||
|
);
|
||||||
|
|
||||||
|
// remove the .in
|
||||||
|
$filepath = substr($filepath, 0, -3);
|
||||||
|
if (file_put_contents($filepath, $filecontents, LOCK_EX) === false) {
|
||||||
|
throw new IDF_Scm_Exception(sprintf(
|
||||||
|
__('Could not write configuration file "%s"'), $filepath
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// step 5) read in and append the usher config with the new server
|
// step 5) read in and append the usher config with the new server
|
||||||
//
|
//
|
||||||
@ -252,7 +300,7 @@ class IDF_Plugin_SyncMonotone
|
|||||||
'--confdir', $projectpath,
|
'--confdir', $projectpath,
|
||||||
'-d', $dbfile,
|
'-d', $dbfile,
|
||||||
'--timestamps',
|
'--timestamps',
|
||||||
'--ticker=dot'
|
'--ticker=dot'
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -378,17 +426,15 @@ class IDF_Plugin_SyncMonotone
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Pluf::f('mtn_remote_auth', true)) {
|
$keydir = Pluf::f('tmp_folder').'/mtn-client-keys';
|
||||||
$keydir = Pluf::f('tmp_folder').'/mtn-client-keys';
|
$keyname = $project->getConf()->getVal('mtn_client_key_name', false);
|
||||||
$keyname = $project->getConf()->getVal('mtn_client_key_name', false);
|
$keyhash = $project->getConf()->getVal('mtn_client_key_hash', false);
|
||||||
$keyhash = $project->getConf()->getVal('mtn_client_key_hash', false);
|
if ($keyname && $keyhash &&
|
||||||
if ($keyname && $keyhash &&
|
file_exists($keydir .'/'. $keyname . '.' . $keyhash)) {
|
||||||
file_exists($keydir .'/'. $keyname . '.' . $keyhash)) {
|
if (!@unlink($keydir .'/'. $keyname . '.' . $keyhash)) {
|
||||||
if (!@unlink($keydir .'/'. $keyname . '.' . $keyhash)) {
|
throw new IDF_Scm_Exception(sprintf(
|
||||||
throw new IDF_Scm_Exception(sprintf(
|
__('Could not delete client private key %s'), $keyname
|
||||||
__('Could not delete client private key %s'), $keyname
|
));
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -721,7 +767,7 @@ class IDF_Plugin_SyncMonotone
|
|||||||
|
|
||||||
private static function _delete_recursive($path)
|
private static function _delete_recursive($path)
|
||||||
{
|
{
|
||||||
if (is_file($path)) {
|
if (is_file($path) || is_link($path)) {
|
||||||
return @unlink($path);
|
return @unlink($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
ARA_safe_commands = {
|
||||||
|
"get_corresponding_path", "get_content_changed", "tags", "branches",
|
||||||
|
"common_ancestors", "packet_for_fdelta", "packet_for_fdata",
|
||||||
|
"packets_for_certs", "packet_for_rdata", "get_manifest_of",
|
||||||
|
"get_revision", "select", "graph", "children", "parents", "roots",
|
||||||
|
"leaves", "ancestry_difference", "toposort", "erase_ancestors",
|
||||||
|
"descendents", "ancestors", "heads", "get_file_of", "get_file",
|
||||||
|
"interface_version", "get_attributes", "content_diff",
|
||||||
|
"file_merge", "show_conflicts", "certs", "keys", "get_extended_manifest_of"
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
-- ***** BEGIN LICENSE BLOCK *****
|
||||||
|
-- This file is part of InDefero, an open source project management application.
|
||||||
|
-- Copyright (C) 2011 Céondo Ltd and contributors.
|
||||||
|
-- Copyright (C) 2010 Thomas Keller <me@thomaskeller.biz>
|
||||||
|
-- Richard Levitte <richard@levitte.org>
|
||||||
|
--
|
||||||
|
-- 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 reads key identities from a file "remote-automate-permissions"
|
||||||
|
-- in the configuration directory and permits those authenticating with one
|
||||||
|
-- of those keys to perform dangerous (read/write) remote automate operations.
|
||||||
|
-- The format of the file is very simple, one key identity on every line.
|
||||||
|
-- Lines starting with # are ignore, as well as empty lines.
|
||||||
|
--
|
||||||
|
-- It's possible to configure this script to allow the performance of some
|
||||||
|
-- remote automate commands anonymously, through the variable
|
||||||
|
-- ARA_safe_commands, which has to be a table of commands as strings.
|
||||||
|
-- One example configuration, taken from the setup at code.monotone.ca, could
|
||||||
|
-- be this:
|
||||||
|
--
|
||||||
|
-- ARA_safe_commands = {
|
||||||
|
-- "get_corresponding_path", "get_content_changed", "tags", "branches",
|
||||||
|
-- "common_ancestors", "packet_for_fdelta", "packet_for_fdata",
|
||||||
|
-- "packets_for_certs", "packet_for_rdata", "get_manifest_of",
|
||||||
|
-- "get_revision", "select", "graph", "children", "parents", "roots",
|
||||||
|
-- "leaves", "ancestry_difference", "toposort", "erase_ancestors",
|
||||||
|
-- "descendents", "ancestors", "heads", "get_file_of", "get_file",
|
||||||
|
-- "interface_version", "get_attributes", "content_diff",
|
||||||
|
-- "file_merge", "show_conflicts", "certs", "keys", "get_extended_manifest_of"
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
do
|
||||||
|
local _safe_commands = {}
|
||||||
|
if ARA_safe_commands then
|
||||||
|
_safe_commands = ARA_safe_commands
|
||||||
|
end
|
||||||
|
|
||||||
|
local _save_get_remote_automate_permitted = get_remote_automate_permitted
|
||||||
|
function get_remote_automate_permitted(key_identity, command, options)
|
||||||
|
local permfile =
|
||||||
|
io.open(get_confdir() .. "/remote-automate-permissions", "r")
|
||||||
|
if (permfile == nil) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- See if the incoming key matches any of the key identities or
|
||||||
|
-- patterns found in the permissions file.
|
||||||
|
local matches = false
|
||||||
|
local line = permfile:read()
|
||||||
|
while (not matches and line ~= nil) do
|
||||||
|
if not globish_match("#*", line) then
|
||||||
|
local _, _, ln = string.find(line, "%s*([^%s]*)%s*")
|
||||||
|
if ln == "*" then matches = true end
|
||||||
|
if ln == key_identity.id then matches = true end
|
||||||
|
if globish_match(ln, key_identity.name) then matches = true end
|
||||||
|
line = permfile:read()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
io.close(permfile)
|
||||||
|
if matches then return true end
|
||||||
|
|
||||||
|
-- No matching key found, let's see if the command matches one the
|
||||||
|
-- admin allowed to be performed anonymously
|
||||||
|
for _,v in ipairs(_safe_commands) do
|
||||||
|
if (v == command[1]) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- No matches found anywhere, then don't permit this operation
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,2 @@
|
|||||||
|
IDF_project = "%%PROJECT%%"
|
||||||
|
IDF_push_script = "%%MTNPOSTPUSH%%"
|
58
src/IDF/Plugin/SyncMonotone/hooks.d/indefero_post_push.lua
Normal file
58
src/IDF/Plugin/SyncMonotone/hooks.d/indefero_post_push.lua
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
-- ***** BEGIN LICENSE BLOCK *****
|
||||||
|
-- This file is part of InDefero, an open source project management application.
|
||||||
|
-- Copyright (C) 2011 Céondo Ltd and contributors.
|
||||||
|
--
|
||||||
|
-- InDefero is free software; you can redistribute it and/or modify
|
||||||
|
-- it under the terms of the GNU General Public License as published by
|
||||||
|
-- the Free Software Foundation; either version 2 of the License, or
|
||||||
|
-- (at your option) any later version.
|
||||||
|
--
|
||||||
|
-- InDefero is distributed in the hope that it will be useful,
|
||||||
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
-- GNU General Public License for more details.
|
||||||
|
--
|
||||||
|
-- You should have received a copy of the GNU General Public License
|
||||||
|
-- along with this program; if not, write to the Free Software
|
||||||
|
-- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
--
|
||||||
|
-- ***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
|
--
|
||||||
|
-- let IDF know of new arriving revisions to fill its timeline
|
||||||
|
--
|
||||||
|
_idf_revs = {}
|
||||||
|
push_hook_functions(
|
||||||
|
{
|
||||||
|
start =
|
||||||
|
function (session_id)
|
||||||
|
_idf_revs[session_id] = {}
|
||||||
|
return "continue",nil
|
||||||
|
end,
|
||||||
|
revision_received =
|
||||||
|
function (new_id, revision, certs, session_id)
|
||||||
|
table.insert(_idf_revs[session_id], new_id)
|
||||||
|
return "continue",nil
|
||||||
|
end,
|
||||||
|
["end"] =
|
||||||
|
function (session_id, ...)
|
||||||
|
if table.getn(_idf_revs[session_id]) == 0 then
|
||||||
|
return "continue",nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local pin,pout,pid = spawn_pipe(IDF_push_script, IDF_project);
|
||||||
|
if pid == -1 then
|
||||||
|
print("could not execute " .. IDF_push_script)
|
||||||
|
return "continue",nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for _,r in ipairs(_idf_revs[session_id]) do
|
||||||
|
pin:write(r .. "\n")
|
||||||
|
end
|
||||||
|
pin:close()
|
||||||
|
|
||||||
|
wait(pid)
|
||||||
|
return "continue",nil
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
@ -1,79 +0,0 @@
|
|||||||
-- ***** BEGIN LICENSE BLOCK *****
|
|
||||||
-- This file is part of InDefero, an open source project management application.
|
|
||||||
-- Copyright (C) 2010 Céondo Ltd and contributors.
|
|
||||||
--
|
|
||||||
-- InDefero is free software; you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation; either version 2 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
--
|
|
||||||
-- InDefero is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
--
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program; if not, write to the Free Software
|
|
||||||
-- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
--
|
|
||||||
-- ***** END LICENSE BLOCK *****
|
|
||||||
|
|
||||||
--
|
|
||||||
-- controls the access rights for remote_stdio which is used by IDFs frontend
|
|
||||||
-- and other interested parties
|
|
||||||
--
|
|
||||||
function get_remote_automate_permitted(key_identity, command, options)
|
|
||||||
if (key_identity.id == "%%MTNCLIENTKEY%%") then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- let IDF know of new arriving revisions to fill its timeline
|
|
||||||
--
|
|
||||||
_idf_revs = {}
|
|
||||||
push_hook_functions({
|
|
||||||
["start"] = function (session_id)
|
|
||||||
_idf_revs[session_id] = {}
|
|
||||||
return "continue",nil
|
|
||||||
end,
|
|
||||||
["revision_received"] = function (new_id, revision, certs, session_id)
|
|
||||||
table.insert(_idf_revs[session_id], new_id)
|
|
||||||
return "continue",nil
|
|
||||||
end,
|
|
||||||
["end"] = function (session_id, ...)
|
|
||||||
if table.getn(_idf_revs[session_id]) == 0 then
|
|
||||||
return "continue",nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local pin,pout,pid = spawn_pipe("%%MTNPOSTPUSH%%", "%%PROJECT%%");
|
|
||||||
if pid == -1 then
|
|
||||||
print("could not execute %%MTNPOSTPUSH%%")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for _,r in ipairs(_idf_revs[session_id]) do
|
|
||||||
pin:write(r .. "\n")
|
|
||||||
end
|
|
||||||
pin:close()
|
|
||||||
|
|
||||||
wait(pid)
|
|
||||||
return "continue",nil
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Load local hooks if they exist.
|
|
||||||
--
|
|
||||||
-- The way this is supposed to work is that hooks.d can contain symbolic
|
|
||||||
-- links to lua scripts. These links MUST have the extension .lua
|
|
||||||
-- If the script needs some configuration, a corresponding file with
|
|
||||||
-- the extension .conf is the right spot.
|
|
||||||
--
|
|
||||||
-- First load the configuration of the hooks, if applicable
|
|
||||||
includedirpattern(get_confdir() .. "/hooks.d/", "*.conf")
|
|
||||||
-- Then load the hooks themselves
|
|
||||||
includedirpattern(get_confdir() .. "/hooks.d/", "*.lua")
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
|||||||
-- ***** BEGIN LICENSE BLOCK *****
|
|
||||||
-- This file is part of InDefero, an open source project management application.
|
|
||||||
-- Copyright (C) 2010 Céondo Ltd and contributors.
|
|
||||||
--
|
|
||||||
-- InDefero is free software; you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation; either version 2 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
--
|
|
||||||
-- InDefero is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
--
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program; if not, write to the Free Software
|
|
||||||
-- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
--
|
|
||||||
-- ***** END LICENSE BLOCK *****
|
|
||||||
|
|
||||||
--
|
|
||||||
-- controls the access rights for remote_stdio which is used by IDFs frontend
|
|
||||||
-- and other interested parties
|
|
||||||
--
|
|
||||||
function get_remote_automate_permitted(key_identity, command, options)
|
|
||||||
local read_only_commands = {
|
|
||||||
"get_corresponding_path", "get_content_changed", "tags", "branches",
|
|
||||||
"common_ancestors", "packet_for_fdelta", "packet_for_fdata",
|
|
||||||
"packets_for_certs", "packet_for_rdata", "get_manifest_of",
|
|
||||||
"get_revision", "select", "graph", "children", "parents", "roots",
|
|
||||||
"leaves", "ancestry_difference", "toposort", "erase_ancestors",
|
|
||||||
"descendents", "ancestors", "heads", "get_file_of", "get_file",
|
|
||||||
"interface_version", "get_attributes", "content_diff",
|
|
||||||
"file_merge", "show_conflicts", "certs", "keys", "get_file_size",
|
|
||||||
"get_extended_manifest_of"
|
|
||||||
}
|
|
||||||
|
|
||||||
for _,v in ipairs(read_only_commands) do
|
|
||||||
if (v == command[1]) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- let IDF know of new arriving revisions to fill its timeline
|
|
||||||
--
|
|
||||||
_idf_revs = {}
|
|
||||||
push_hook_functions({
|
|
||||||
["start"] = function (session_id)
|
|
||||||
_idf_revs[session_id] = {}
|
|
||||||
return "continue",nil
|
|
||||||
end,
|
|
||||||
["revision_received"] = function (new_id, revision, certs, session_id)
|
|
||||||
table.insert(_idf_revs[session_id], new_id)
|
|
||||||
return "continue",nil
|
|
||||||
end,
|
|
||||||
["end"] = function (session_id, ...)
|
|
||||||
if table.getn(_idf_revs[session_id]) == 0 then
|
|
||||||
return "continue",nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local pin,pout,pid = spawn_pipe("%%MTNPOSTPUSH%%", "%%PROJECT%%");
|
|
||||||
if pid == -1 then
|
|
||||||
print("could not execute %%MTNPOSTPUSH%%")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for _,r in ipairs(_idf_revs[session_id]) do
|
|
||||||
pin:write(r .. "\n")
|
|
||||||
end
|
|
||||||
pin:close()
|
|
||||||
|
|
||||||
wait(pid)
|
|
||||||
return "continue",nil
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Load local hooks if they exist.
|
|
||||||
--
|
|
||||||
-- The way this is supposed to work is that hooks.d can contain symbolic
|
|
||||||
-- links to lua scripts. These links MUST have the extension .lua
|
|
||||||
-- If the script needs some configuration, a corresponding file with
|
|
||||||
-- the extension .conf is the right spot.
|
|
||||||
--
|
|
||||||
-- First load the configuration of the hooks, if applicable
|
|
||||||
includedirpattern(get_confdir() .. "/hooks.d/", "*.conf")
|
|
||||||
-- Then load the hooks themselves
|
|
||||||
includedirpattern(get_confdir() .. "/hooks.d/", "*.lua")
|
|
30
src/IDF/Plugin/SyncMonotone/monotonerc.in
Normal file
30
src/IDF/Plugin/SyncMonotone/monotonerc.in
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
-- ***** BEGIN LICENSE BLOCK *****
|
||||||
|
-- This file is part of InDefero, an open source project management application.
|
||||||
|
-- Copyright (C) 2011 Céondo Ltd and contributors.
|
||||||
|
--
|
||||||
|
-- InDefero is free software; you can redistribute it and/or modify
|
||||||
|
-- it under the terms of the GNU General Public License as published by
|
||||||
|
-- the Free Software Foundation; either version 2 of the License, or
|
||||||
|
-- (at your option) any later version.
|
||||||
|
--
|
||||||
|
-- InDefero is distributed in the hope that it will be useful,
|
||||||
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
-- GNU General Public License for more details.
|
||||||
|
--
|
||||||
|
-- You should have received a copy of the GNU General Public License
|
||||||
|
-- along with this program; if not, write to the Free Software
|
||||||
|
-- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
--
|
||||||
|
-- ***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
|
---- Load local hooks if they exist.
|
||||||
|
-- The way this is supposed to work is that hooks.d can contain symbolic
|
||||||
|
-- links to lua scripts. These links MUST have the extension .lua
|
||||||
|
-- If the script needs some configuration, a corresponding file with
|
||||||
|
-- the extension .conf is the right spot.
|
||||||
|
----
|
||||||
|
-- First load the configuration of the hooks, if applicable
|
||||||
|
includedirpattern(get_confdir() .. "/hooks.d/","*.conf")
|
||||||
|
-- Then load the hooks themselves
|
||||||
|
includedirpattern(get_confdir() .. "/hooks.d/","*.lua")
|
@ -0,0 +1 @@
|
|||||||
|
%%MTNCLIENTKEY%%
|
@ -280,6 +280,14 @@ class IDF_Scm_Git extends IDF_Scm
|
|||||||
|
|
||||||
public static function getAuthAccessUrl($project, $user, $commit=null)
|
public static function getAuthAccessUrl($project, $user, $commit=null)
|
||||||
{
|
{
|
||||||
|
// if the user haven't registred a public ssh key,
|
||||||
|
// he can't use the write url which use the SSH authentification
|
||||||
|
if ($user != null) {
|
||||||
|
$keys = $user->get_idf_key_list();
|
||||||
|
if (count ($keys) == 0)
|
||||||
|
return self::getAnonymousAccessUrl($project);
|
||||||
|
}
|
||||||
|
|
||||||
return sprintf(Pluf::f('git_write_remote_url'), $project->shortname);
|
return sprintf(Pluf::f('git_write_remote_url'), $project->shortname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,11 +70,6 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
*/
|
*/
|
||||||
public function _getAuthOptions()
|
public function _getAuthOptions()
|
||||||
{
|
{
|
||||||
// no remote authentication - the simple case
|
|
||||||
if (!Pluf::f('mtn_remote_auth', true)) {
|
|
||||||
return '--key= ';
|
|
||||||
}
|
|
||||||
|
|
||||||
$prjconf = $this->project->getConf();
|
$prjconf = $this->project->getConf();
|
||||||
$name = $prjconf->getVal('mtn_client_key_name', false);
|
$name = $prjconf->getVal('mtn_client_key_name', false);
|
||||||
$hash = $prjconf->getVal('mtn_client_key_hash', false);
|
$hash = $prjconf->getVal('mtn_client_key_hash', false);
|
||||||
|
@ -168,6 +168,7 @@ class IDF_Template_MarkdownPrefilter extends Pluf_Text_HTML_Filter
|
|||||||
'https',
|
'https',
|
||||||
'ftp',
|
'ftp',
|
||||||
'mailto',
|
'mailto',
|
||||||
|
'irc'
|
||||||
);
|
);
|
||||||
// tags which should be removed if they contain no content
|
// tags which should be removed if they contain no content
|
||||||
// (e.g. "<b></b>" or "<b />")
|
// (e.g. "<b></b>" or "<b />")
|
||||||
|
@ -78,6 +78,164 @@ class IDF_Views_Issue
|
|||||||
$params, $request);
|
$params, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View the issues watch list of a given user.
|
||||||
|
* Limited to a specified project
|
||||||
|
*/
|
||||||
|
public $watchList_precond = array('IDF_Precondition::accessIssues',
|
||||||
|
'Pluf_Precondition::loginRequired');
|
||||||
|
public function watchList($request, $match)
|
||||||
|
{
|
||||||
|
$prj = $request->project;
|
||||||
|
$otags = $prj->getTagIdsByStatus('open');
|
||||||
|
$ctags = $prj->getTagIdsByStatus('closed');
|
||||||
|
if (count($otags) == 0) $otags[] = 0;
|
||||||
|
if (count($ctags) == 0) $ctags[] = 0;
|
||||||
|
|
||||||
|
// Get the id list of issue in the user watch list (for all projects !)
|
||||||
|
$db =& Pluf::db();
|
||||||
|
$sql_results = $db->select('SELECT idf_issue_id as id FROM '.Pluf::f('db_table_prefix', '').'idf_issue_pluf_user_assoc WHERE pluf_user_id='.$request->user->id);
|
||||||
|
$issue_ids = array(0);
|
||||||
|
foreach ($sql_results as $id) {
|
||||||
|
$issue_ids[] = $id['id'];
|
||||||
|
}
|
||||||
|
$issue_ids = implode (',', $issue_ids);
|
||||||
|
|
||||||
|
// Count open and close issues
|
||||||
|
$sql = new Pluf_SQL('project=%s AND id IN ('.$issue_ids.') AND status IN ('.implode(', ', $otags).')', array($prj->id));
|
||||||
|
$nb_open = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
|
||||||
|
$sql = new Pluf_SQL('project=%s AND id IN ('.$issue_ids.') AND status IN ('.implode(', ', $ctags).')', array($prj->id));
|
||||||
|
$nb_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
|
||||||
|
|
||||||
|
// Generate a filter for the paginator
|
||||||
|
switch ($match[2]) {
|
||||||
|
case 'closed':
|
||||||
|
$title = sprintf(__('Watch List: Closed Issues for %s'), (string) $prj);
|
||||||
|
$summary = __('This table shows the closed issues in your watch list for %s project.', (string) $prj);
|
||||||
|
$f_sql = new Pluf_SQL('project=%s AND id IN ('.$issue_ids.') AND status IN ('.implode(', ', $ctags).')', array($prj->id));
|
||||||
|
break;
|
||||||
|
case 'open':
|
||||||
|
default:
|
||||||
|
$title = sprintf(__('Watch List: Open Issues for %s'), (string) $prj);
|
||||||
|
$summary = __('This table shows the open issues in your watch list for %s project.', (string) $prj);
|
||||||
|
$f_sql = new Pluf_SQL('project=%s AND id IN ('.$issue_ids.') AND status IN ('.implode(', ', $otags).')', array($prj->id));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paginator to paginate the issues
|
||||||
|
$pag = new Pluf_Paginator(new IDF_Issue());
|
||||||
|
$pag->class = 'recent-issues';
|
||||||
|
$pag->item_extra_props = array('project_m' => $prj,
|
||||||
|
'shortname' => $prj->shortname,
|
||||||
|
'current_user' => $request->user);
|
||||||
|
$pag->summary = $summary;
|
||||||
|
$pag->forced_where = $f_sql;
|
||||||
|
$pag->action = array('IDF_Views_Issue::watchList', array($prj->shortname, $match[1]));
|
||||||
|
$pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted
|
||||||
|
$pag->sort_reverse_order = array('modif_dtime');
|
||||||
|
$pag->sort_link_title = true;
|
||||||
|
$pag->extra_classes = array('a-c', '', 'a-c', '');
|
||||||
|
$list_display = array(
|
||||||
|
'id' => __('Id'),
|
||||||
|
array('summary', 'IDF_Views_Issue_SummaryAndLabels', __('Summary')),
|
||||||
|
array('status', 'IDF_Views_Issue_ShowStatus', __('Status')),
|
||||||
|
array('modif_dtime', 'Pluf_Paginator_DateAgo', __('Last Updated')),
|
||||||
|
);
|
||||||
|
$pag->configure($list_display, array(), array('id', 'status', 'modif_dtime'));
|
||||||
|
$pag->items_per_page = 10;
|
||||||
|
$pag->no_results_text = __('No issues were found.');
|
||||||
|
$pag->setFromRequest($request);
|
||||||
|
return Pluf_Shortcuts_RenderToResponse('idf/issues/project-watchlist.html',
|
||||||
|
array('project' => $prj,
|
||||||
|
'page_title' => $title,
|
||||||
|
'open' => $nb_open,
|
||||||
|
'closed' => $nb_closed,
|
||||||
|
'issues' => $pag,
|
||||||
|
),
|
||||||
|
$request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View the issues watch list of a given user.
|
||||||
|
* For all projects
|
||||||
|
*/
|
||||||
|
public $forgeWatchList_precond = array('Pluf_Precondition::loginRequired');
|
||||||
|
public function forgeWatchList($request, $match)
|
||||||
|
{
|
||||||
|
$otags = array();
|
||||||
|
$ctags = array();
|
||||||
|
// Note that this approach does not scale, we will need to add
|
||||||
|
// a table to cache the meaning of the tags for large forges.
|
||||||
|
foreach (IDF_Views::getProjects($request->user) as $project) {
|
||||||
|
$otags = array_merge($otags, $project->getTagIdsByStatus('open'));
|
||||||
|
}
|
||||||
|
foreach (IDF_Views::getProjects($request->user) as $project) {
|
||||||
|
$ctags = array_merge($ctags, $project->getTagIdsByStatus('closed'));
|
||||||
|
}
|
||||||
|
if (count($otags) == 0) $otags[] = 0;
|
||||||
|
if (count($ctags) == 0) $ctags[] = 0;
|
||||||
|
|
||||||
|
// Get the id list of issue in the user watch list (for all projects !)
|
||||||
|
$db =& Pluf::db();
|
||||||
|
$sql_results = $db->select('SELECT idf_issue_id as id FROM '.Pluf::f('db_table_prefix', '').'idf_issue_pluf_user_assoc WHERE pluf_user_id='.$request->user->id);
|
||||||
|
$issue_ids = array(0);
|
||||||
|
foreach ($sql_results as $id) {
|
||||||
|
$issue_ids[] = $id['id'];
|
||||||
|
}
|
||||||
|
$issue_ids = implode (',', $issue_ids);
|
||||||
|
|
||||||
|
// Count open and close issues
|
||||||
|
$sql = new Pluf_SQL('id IN ('.$issue_ids.') AND status IN ('.implode(', ', $otags).')', array());
|
||||||
|
$nb_open = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
|
||||||
|
$sql = new Pluf_SQL('id IN ('.$issue_ids.') AND status IN ('.implode(', ', $ctags).')', array());
|
||||||
|
$nb_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
|
||||||
|
|
||||||
|
// Generate a filter for the paginator
|
||||||
|
switch ($match[1]) {
|
||||||
|
case 'closed':
|
||||||
|
$title = sprintf(__('Watch List: Closed Issues'));
|
||||||
|
$summary = __('This table shows the closed issues in your watch list.');
|
||||||
|
$f_sql = new Pluf_SQL('id IN ('.$issue_ids.') AND status IN ('.implode(', ', $ctags).')', array());
|
||||||
|
break;
|
||||||
|
case 'open':
|
||||||
|
default:
|
||||||
|
$title = sprintf(__('Watch List: Open Issues'));
|
||||||
|
$summary = __('This table shows the open issues in your watch list.');
|
||||||
|
$f_sql = new Pluf_SQL('id IN ('.$issue_ids.') AND status IN ('.implode(', ', $otags).')', array());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paginator to paginate the issues
|
||||||
|
$pag = new Pluf_Paginator(new IDF_Issue());
|
||||||
|
$pag->class = 'recent-issues';
|
||||||
|
$pag->item_extra_props = array('current_user' => $request->user);
|
||||||
|
$pag->summary = $summary;
|
||||||
|
$pag->forced_where = $f_sql;
|
||||||
|
$pag->action = array('IDF_Views_Issue::forgeWatchList', array($match[1]));
|
||||||
|
$pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted
|
||||||
|
$pag->sort_reverse_order = array('modif_dtime');
|
||||||
|
$pag->sort_link_title = true;
|
||||||
|
$pag->extra_classes = array('a-c', '', 'a-c', 'a-c', 'a-c');
|
||||||
|
$list_display = array(
|
||||||
|
'id' => __('Id'),
|
||||||
|
array('summary', 'IDF_Views_Issue_SummaryAndLabelsUnknownProject', __('Summary')),
|
||||||
|
array('project', 'Pluf_Paginator_FkToString', __('Project')),
|
||||||
|
array('status', 'IDF_Views_Issue_ShowStatus', __('Status')),
|
||||||
|
array('modif_dtime', 'Pluf_Paginator_DateAgo', __('Last Updated')),
|
||||||
|
);
|
||||||
|
$pag->configure($list_display, array(), array('id', 'project', 'status', 'modif_dtime'));
|
||||||
|
$pag->items_per_page = 10;
|
||||||
|
$pag->no_results_text = __('No issues were found.');
|
||||||
|
$pag->setFromRequest($request);
|
||||||
|
return Pluf_Shortcuts_RenderToResponse('idf/issues/forge-watchlist.html',
|
||||||
|
array('page_title' => $title,
|
||||||
|
'open' => $nb_open,
|
||||||
|
'closed' => $nb_closed,
|
||||||
|
'issues' => $pag,
|
||||||
|
),
|
||||||
|
$request);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View the issues of a given user.
|
* View the issues of a given user.
|
||||||
*
|
*
|
||||||
@ -280,6 +438,26 @@ class IDF_Views_Issue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search previous and next issue id
|
||||||
|
$octags = $prj->getTagIdsByStatus(($closed) ? 'closed' : 'open');
|
||||||
|
if (count($octags) == 0) $octags[] = 0;
|
||||||
|
$sql_previous = new Pluf_SQL('project=%s AND status IN ('.implode(', ', $octags).') AND id<%s',
|
||||||
|
array($prj->id, $match[2])
|
||||||
|
);
|
||||||
|
$sql_next = new Pluf_SQL('project=%s AND status IN ('.implode(', ', $octags).') AND id>%s',
|
||||||
|
array($prj->id, $match[2])
|
||||||
|
);
|
||||||
|
$previous_issue = Pluf::factory('IDF_Issue')->getList(array('filter' => $sql_previous->gen(),
|
||||||
|
'order' => 'id DESC',
|
||||||
|
'nb' => 1
|
||||||
|
));
|
||||||
|
$next_issue = Pluf::factory('IDF_Issue')->getList(array('filter' => $sql_next->gen(),
|
||||||
|
'order' => 'id ASC',
|
||||||
|
'nb' => 1
|
||||||
|
));
|
||||||
|
$previous_issue_id = (isset($previous_issue[0])) ? $previous_issue[0]->id : 0;
|
||||||
|
$next_issue_id = (isset($next_issue[0])) ? $next_issue[0]->id : 0;
|
||||||
|
|
||||||
$arrays = self::autoCompleteArrays($prj);
|
$arrays = self::autoCompleteArrays($prj);
|
||||||
return Pluf_Shortcuts_RenderToResponse('idf/issues/view.html',
|
return Pluf_Shortcuts_RenderToResponse('idf/issues/view.html',
|
||||||
array_merge(
|
array_merge(
|
||||||
@ -292,6 +470,8 @@ class IDF_Views_Issue
|
|||||||
'closed' => $closed,
|
'closed' => $closed,
|
||||||
'preview' => $preview,
|
'preview' => $preview,
|
||||||
'interested' => $interested->count(),
|
'interested' => $interested->count(),
|
||||||
|
'previous_issue_id' => $previous_issue_id,
|
||||||
|
'next_issue_id' => $next_issue_id
|
||||||
),
|
),
|
||||||
$arrays),
|
$arrays),
|
||||||
$request);
|
$request);
|
||||||
@ -540,6 +720,17 @@ class IDF_Views_Issue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When you access to your forge watch list, issue don't known
|
||||||
|
* the project shortname.
|
||||||
|
*/
|
||||||
|
function IDF_Views_Issue_SummaryAndLabelsUnknownProject($field, $issue, $extra='')
|
||||||
|
{
|
||||||
|
$shortname = $issue->get_project()->shortname;
|
||||||
|
$issue->__set('shortname', $shortname);
|
||||||
|
return IDF_Views_Issue_SummaryAndLabels ($field, $issue, $extra);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the summary of an issue, then on a new line, display the
|
* Display the summary of an issue, then on a new line, display the
|
||||||
* list of labels with a link to a view "by label only".
|
* list of labels with a link to a view "by label only".
|
||||||
@ -576,3 +767,5 @@ function IDF_Views_Issue_ShowStatus($field, $issue, $extra='')
|
|||||||
{
|
{
|
||||||
return Pluf_esc($issue->get_status()->name);
|
return Pluf_esc($issue->get_status()->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,17 +98,16 @@ $cfg['mtn_remote_url'] = 'mtn://my-host.biz/%s';
|
|||||||
# choosed for manual setups and / or ssh access.
|
# choosed for manual setups and / or ssh access.
|
||||||
$cfg['mtn_db_access'] = 'local';
|
$cfg['mtn_db_access'] = 'local';
|
||||||
|
|
||||||
# If true, each access to the database is authenticated with an auto-generated
|
# Full path to the directory tree which contains default configuration files
|
||||||
# project key which is stored in the IDF project configuration
|
# that are automatically created for new projects. This is only needed
|
||||||
# ('mtn_client_key_*') and written out to $cfg['tmp_folder']/mtn-client-keys
|
# if $cfg['mtn_db_access'] is set to remote, i.e. in case the SyncMonotone
|
||||||
# for its actual use. This key is then configured on the server to have
|
# plugin should be used. If unset, it defaults to the tree underknees
|
||||||
# full read / write access to all functions, while anonymous access can be
|
# src/IDF/Plugin/SyncMonotone/. Don't forget the trailing slash!
|
||||||
# completely disabled.
|
#$cfg['mtn_confdir'] = '/path/to/dir/tree/';
|
||||||
# If false, IDF tries to connect anonymously, without authentication, to
|
|
||||||
# the remote monotone server instance. In this case no project-specific
|
# Additional configuration files you want to create / copy for new setups.
|
||||||
# keys are generated and the server must be configured to allow at least
|
# All these file paths have to be relative to $cfg['mtn_confdir'].
|
||||||
# anonymous read access to the main functions.
|
#$cfg['mtn_confdir_extra'] = array('hooks.d/something.lua')
|
||||||
#$cfg['mtn_remote_auth'] = true;
|
|
||||||
|
|
||||||
# Needs to be configured for remote / usher usage.
|
# Needs to be configured for remote / usher usage.
|
||||||
# This allows basic control of a running usher process via the forge
|
# This allows basic control of a running usher process via the forge
|
||||||
@ -256,11 +255,17 @@ $cfg['allowed_scm'] = array('git' => 'IDF_Scm_Git',
|
|||||||
'mtn' => 'IDF_Scm_Monotone',
|
'mtn' => 'IDF_Scm_Monotone',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# Specific git config
|
||||||
|
# The core.quotepath is configured on new repository
|
||||||
|
# True -> All characters upper than 0x80 will be escape (default)
|
||||||
|
# False -> Characters is print directly, enable accented character in a UTF-8 shell
|
||||||
|
# $cfg['git_core_quotepath'] = false;
|
||||||
|
|
||||||
# Set to true when uploaded public keys should not only be validated
|
# Set to true when uploaded public keys should not only be validated
|
||||||
# syntactically, but also by the specific backend. For SSH public
|
# syntactically, but also by the specific backend. For SSH public
|
||||||
# keys, ssh-keygen(3) must be available and usable in PATH, for
|
# keys, ssh-keygen(3) must be available and usable in PATH, for
|
||||||
# monotone public keys, the monotone binary (as configured above)
|
# monotone public keys, the monotone binary (as configured above)
|
||||||
# is used.
|
# is used.
|
||||||
# $cfg['idf_strong_key_check'] = false;
|
# $cfg['idf_strong_key_check'] = false;
|
||||||
|
|
||||||
# If you want to use another memtypes database
|
# If you want to use another memtypes database
|
||||||
|
@ -141,6 +141,16 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/view/attachment/(\d+)/(.*)$#',
|
|||||||
'model' => 'IDF_Views_Issue',
|
'model' => 'IDF_Views_Issue',
|
||||||
'method' => 'viewAttachment');
|
'method' => 'viewAttachment');
|
||||||
|
|
||||||
|
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/watchlist/(\w+)$#',
|
||||||
|
'base' => $base,
|
||||||
|
'model' => 'IDF_Views_Issue',
|
||||||
|
'method' => 'watchList');
|
||||||
|
|
||||||
|
$ctl[] = array('regex' => '#^/watchlist/(\w+)$#',
|
||||||
|
'base' => $base,
|
||||||
|
'model' => 'IDF_Views_Issue',
|
||||||
|
'method' => 'forgeWatchList');
|
||||||
|
|
||||||
// ---------- SCM ----------------------------------------
|
// ---------- SCM ----------------------------------------
|
||||||
|
|
||||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/help/$#',
|
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/help/$#',
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<link rel="stylesheet" type="text/css" href="{media '/idf/css/yui.css'}" />
|
<link rel="stylesheet" type="text/css" href="{media '/idf/css/yui.css'}" />
|
||||||
<link rel="stylesheet" type="text/css" href="{media '/idf/css/style.css'}" />
|
<link rel="stylesheet" type="text/css" href="{media '/idf/css/style.css'}" />
|
||||||
|
<link rel="icon" type="image/png" href="{media '/idf/img/favicon.png'}" />
|
||||||
<!--[if lt IE 7]>
|
<!--[if lt IE 7]>
|
||||||
<link rel="stylesheet" type="text/css" href="{media '/idf/css/ie6.css'}" />
|
<link rel="stylesheet" type="text/css" href="{media '/idf/css/ie6.css'}" />
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<link rel="stylesheet" type="text/css" href="{media '/idf/css/yui.css'}" />
|
<link rel="stylesheet" type="text/css" href="{media '/idf/css/yui.css'}" />
|
||||||
<link rel="stylesheet" type="text/css" href="{media '/idf/css/style.css'}" />
|
<link rel="stylesheet" type="text/css" href="{media '/idf/css/style.css'}" />
|
||||||
|
<link rel="icon" type="image/png" href="{media '/idf/img/favicon.png'}" />
|
||||||
<!--[if lt IE 7]>
|
<!--[if lt IE 7]>
|
||||||
<link rel="stylesheet" type="text/css" href="{media '/idf/css/ie6.css'}" />
|
<link rel="stylesheet" type="text/css" href="{media '/idf/css/ie6.css'}" />
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<link rel="stylesheet" type="text/css" href="{media '/idf/css/yui.css'}" />
|
<link rel="stylesheet" type="text/css" href="{media '/idf/css/yui.css'}" />
|
||||||
<link rel="stylesheet" type="text/css" href="{media '/idf/css/style.css'}" />
|
<link rel="stylesheet" type="text/css" href="{media '/idf/css/style.css'}" />
|
||||||
|
<link rel="icon" type="image/png" href="{media '/idf/img/favicon.png'}" />
|
||||||
<!--[if lt IE 7]>
|
<!--[if lt IE 7]>
|
||||||
<link rel="stylesheet" type="text/css" href="{media '/idf/css/ie6.css'}" />
|
<link rel="stylesheet" type="text/css" href="{media '/idf/css/ie6.css'}" />
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
{block subtabs}
|
{block subtabs}
|
||||||
<div id="sub-tabs">
|
<div id="sub-tabs">
|
||||||
<a {if $inOpenIssues}class="active" {/if}href="{url 'IDF_Views_Issue::index', array($project.shortname)}">{trans 'Open Issues'}</a>
|
<a {if $inOpenIssues}class="active" {/if}href="{url 'IDF_Views_Issue::index', array($project.shortname)}">{trans 'Open Issues'}</a>
|
||||||
{if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a> | <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')}">{trans 'My Issues'}</a>{/if} |
|
{if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a> | <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')}">{trans 'My Issues'}</a>
|
||||||
|
| <a {if $inWatchList}class="active" {/if}href="{url 'IDF_Views_Issue::watchList', array($project.shortname, 'open')}">{trans 'My watch list'}</a>{/if} |
|
||||||
<form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get">
|
<form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get">
|
||||||
<input accesskey="4" type="text" value="{$q}" name="q" size="20" />
|
<input accesskey="4" type="text" value="{$q}" name="q" size="20" />
|
||||||
<input type="submit" name="s" value="{trans 'Search'}" />
|
<input type="submit" name="s" value="{trans 'Search'}" />
|
||||||
|
12
src/IDF/templates/idf/issues/forge-watchlist.html
Normal file
12
src/IDF/templates/idf/issues/forge-watchlist.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{extends "idf/base-simple.html"}
|
||||||
|
|
||||||
|
{block body}
|
||||||
|
{$issues.render}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block context}
|
||||||
|
{aurl 'open_url', 'IDF_Views_Issue::forgeWatchList', array('open')}
|
||||||
|
{aurl 'closed_url', 'IDF_Views_Issue::forgeWatchList', array('closed')}
|
||||||
|
{blocktrans}<p><strong>Open issues:</strong> <a href="{$open_url}">{$open}</a></p>
|
||||||
|
<p><strong>Closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p>{/blocktrans}
|
||||||
|
{/block}
|
17
src/IDF/templates/idf/issues/project-watchlist.html
Normal file
17
src/IDF/templates/idf/issues/project-watchlist.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{extends "idf/issues/base.html"}
|
||||||
|
|
||||||
|
{block docclass}yui-t2{assign $inWatchList = true}{/block}
|
||||||
|
|
||||||
|
{block body}
|
||||||
|
{$issues.render}
|
||||||
|
{if !$user.isAnonymous()}
|
||||||
|
{aurl 'url', 'IDF_Views_Issue::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 Issue'}</a></p>{/if}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block context}
|
||||||
|
{aurl 'open_url', 'IDF_Views_Issue::watchList', array($project.shortname, 'open')}
|
||||||
|
{aurl 'closed_url', 'IDF_Views_Issue::watchList', array($project.shortname, 'closed')}
|
||||||
|
{blocktrans}<p><strong>Open issues:</strong> <a href="{$open_url}">{$open}</a></p>
|
||||||
|
<p><strong>Closed issues:</strong> <a href="{$closed_url}">{$closed}</a></p>{/blocktrans}
|
||||||
|
{/block}
|
@ -1,6 +1,15 @@
|
|||||||
{extends "idf/issues/base.html"}
|
{extends "idf/issues/base.html"}
|
||||||
{block titleicon}{if $form}<form class="star" method="post" action="{url 'IDF_Views_Issue::star', array($project.shortname, $issue.id)}"><input type="image" src="{if $starred}{media '/idf/img/star.png'}{else}{media '/idf/img/star-grey.png'}{/if}" name="submit" title="{if $starred}{trans 'Remove this issue from your watch list'}{else}{trans 'Add this issue to your watch list'}{/if}" /></form> {/if}{/block}
|
{block titleicon}{if $form}<form class="star" method="post" action="{url 'IDF_Views_Issue::star', array($project.shortname, $issue.id)}"><input type="image" src="{if $starred}{media '/idf/img/star.png'}{else}{media '/idf/img/star-grey.png'}{/if}" name="submit" title="{if $starred}{trans 'Remove this issue from your watch list'}{else}{trans 'Add this issue to your watch list'}{/if}" /></form> {/if}{/block}
|
||||||
{block body}
|
{block body}
|
||||||
|
<div style="float:right;">
|
||||||
|
{if $previous_issue_id}
|
||||||
|
<a href="{url 'IDF_Views_Issue::view', array($project.shortname, $previous_issue_id)}" title="{if $closed}{trans 'Click here to view the previous closed issue'}{else}{trans 'Click here to view the previous open issue'}{/if}">Previous issue</a>
|
||||||
|
{/if}
|
||||||
|
{if $previous_issue_id and $next_issue_id} - {/if}
|
||||||
|
{if $next_issue_id}
|
||||||
|
<a href="{url 'IDF_Views_Issue::view', array($project.shortname, $next_issue_id)}" title="{if $closed}{trans 'Click here to view the next closed issue'}{else}{trans 'Click here to view the next open issue'}{/if}">Next issue</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{assign $i = 0}
|
{assign $i = 0}
|
||||||
{assign $nc = $comments.count()}
|
{assign $nc = $comments.count()}
|
||||||
{foreach $comments as $c}{ashowuser 'submitter', $c.get_submitter(), $request}
|
{foreach $comments as $c}{ashowuser 'submitter', $c.get_submitter(), $request}
|
||||||
|
@ -11,5 +11,7 @@
|
|||||||
<p>{blocktrans}<a href="{$url}">Update your account</a>.{/blocktrans}</p>
|
<p>{blocktrans}<a href="{$url}">Update your account</a>.{/blocktrans}</p>
|
||||||
{aurl 'url', 'IDF_Views_User::view', array($user.login)}
|
{aurl 'url', 'IDF_Views_User::view', array($user.login)}
|
||||||
<p>{blocktrans}<a href="{$url}">See your public profile</a>.{/blocktrans}</p>
|
<p>{blocktrans}<a href="{$url}">See your public profile</a>.{/blocktrans}</p>
|
||||||
|
{aurl 'url', 'IDF_Views_Issue::forgeWatchList', array('open')}
|
||||||
|
<p>{blocktrans}<a href="{$url}">See your forge issue watch list</a>.{/blocktrans}</p>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
@ -18,6 +18,9 @@ by {$submitter}.{/blocktrans}</p>
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<div id="wiki-content">
|
||||||
|
<div id="wiki-toc"><span id="contentheader">{trans 'Table of Content'}</span><div id="wiki-toc-content"></div></div>
|
||||||
|
<script type="text/javascript" src="{media '/idf/js/wiki-toc.js'}"></script>
|
||||||
<p class="desc">{$page.summary}</p>
|
<p class="desc">{$page.summary}</p>
|
||||||
|
|
||||||
{if !$oldrev}
|
{if !$oldrev}
|
||||||
@ -29,6 +32,7 @@ by {$submitter}.{/blocktrans}</p>
|
|||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
{block context}
|
{block context}
|
||||||
{ashowuser 'submitter', $page.get_submitter(), $request}
|
{ashowuser 'submitter', $page.get_submitter(), $request}
|
||||||
|
@ -225,6 +225,7 @@ div.issue-comment {
|
|||||||
|
|
||||||
div.issue-comment-first {
|
div.issue-comment-first {
|
||||||
border-top: 1px solid #d3d7cf;
|
border-top: 1px solid #d3d7cf;
|
||||||
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.issue-comment-signin {
|
div.issue-comment-signin {
|
||||||
@ -702,6 +703,14 @@ div.deprecated-page {
|
|||||||
color: #a00;
|
color: #a00;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul > li {
|
||||||
|
list-style: disc outside none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol > li {
|
||||||
|
list-style: decimal outside none;
|
||||||
|
}
|
||||||
|
|
||||||
#branding {
|
#branding {
|
||||||
float: right;
|
float: right;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -737,6 +746,32 @@ div.deprecated-page {
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#wiki-toc {
|
||||||
|
float: right;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-toc-content {
|
||||||
|
border: 1px solid #999999;
|
||||||
|
border-width: 1px 0;
|
||||||
|
padding: 10px 0;
|
||||||
|
padding-bottom: 25px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-toc-content a {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-toc-content a.wiki-h2 {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-toc-content a.wiki-h3 {
|
||||||
|
margin-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* main menu
|
* main menu
|
||||||
*/
|
*/
|
||||||
|
BIN
www/media/idf/img/favicon.png
Normal file
BIN
www/media/idf/img/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 677 B |
7
www/media/idf/js/wiki-toc.js
Normal file
7
www/media/idf/js/wiki-toc.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
$(document).ready(function() {
|
||||||
|
$(":header", "#wiki-content").map(function (index) {
|
||||||
|
this.id = "wikititle_" + index;
|
||||||
|
$("<a href='#" + this.id + "'>" + this.innerText + "</a>").addClass("wiki-" + this.tagName.toLowerCase()).appendTo('#wiki-toc-content');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user