Merge branch 'develop' of projects.ceondo.com:indefero into develop
This commit is contained in:
commit
3aac4d528a
20
contrib/zipstream-php-0.2.2/COPYING
Normal file
20
contrib/zipstream-php-0.2.2/COPYING
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (C) 2007-2009 Paul Duncan <pabs@pablotron.org>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
42
contrib/zipstream-php-0.2.2/README
Normal file
42
contrib/zipstream-php-0.2.2/README
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
ZipStream 0.2.2 README
|
||||||
|
======================
|
||||||
|
|
||||||
|
Please see the file COPYING for licensing and warranty information. The
|
||||||
|
latest version of this software is available at the following URL:
|
||||||
|
|
||||||
|
http://pablotron.org/software/zipstream-php/
|
||||||
|
|
||||||
|
Overview
|
||||||
|
========
|
||||||
|
A fast and simple streaming zip file downloader for PHP. Here's a
|
||||||
|
simple example:
|
||||||
|
|
||||||
|
# create a new zipstream object
|
||||||
|
$zip = new ZipStream('example.zip');
|
||||||
|
|
||||||
|
# create a file named 'hello.txt'
|
||||||
|
$zip->add_file('some_image.jpg', 'This is the contents of hello.txt');
|
||||||
|
|
||||||
|
# add a file named 'image.jpg' from a local file 'path/to/image.jpg'
|
||||||
|
$zip->add_file_from_path('some_image.jpg', 'path/to/image.jpg');
|
||||||
|
|
||||||
|
# finish the zip stream
|
||||||
|
$zip->finish();
|
||||||
|
|
||||||
|
You can also add comments, modify file timestamps, and customize (or
|
||||||
|
disable) the HTTP headers. See the class file for details. There are a
|
||||||
|
couple of additional examples in the initial release announcement at the
|
||||||
|
following URL:
|
||||||
|
|
||||||
|
http://pablotron.org/?cid=1535
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
============
|
||||||
|
|
||||||
|
* PHP version 5.1.2 or newer (specifically, the hash_init and
|
||||||
|
hash_file functions).
|
||||||
|
|
||||||
|
About the Author
|
||||||
|
================
|
||||||
|
Paul Duncan <pabs@pablotron.org>
|
||||||
|
http://pablotron.org/
|
2
contrib/zipstream-php-0.2.2/extras/README
Normal file
2
contrib/zipstream-php-0.2.2/extras/README
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Based on PKZIP appnotes, which are included here.
|
||||||
|
|
3071
contrib/zipstream-php-0.2.2/extras/zip-appnote-6.3.1-20070411.txt
Normal file
3071
contrib/zipstream-php-0.2.2/extras/zip-appnote-6.3.1-20070411.txt
Normal file
File diff suppressed because it is too large
Load Diff
52
contrib/zipstream-php-0.2.2/test/index.php
Normal file
52
contrib/zipstream-php-0.2.2/test/index.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
# load zipstream class
|
||||||
|
require '../zipstream.php';
|
||||||
|
|
||||||
|
# get path to current file
|
||||||
|
$pwd = dirname(__FILE__);
|
||||||
|
|
||||||
|
# add some random files
|
||||||
|
$files = array(
|
||||||
|
'../extras/zip-appnote-6.3.1-20070411.txt',
|
||||||
|
'../zipstream.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
# create new zip stream object
|
||||||
|
$zip = new ZipStream('test.zip', array(
|
||||||
|
'comment' => 'this is a zip file comment. hello?'
|
||||||
|
));
|
||||||
|
|
||||||
|
# common file options
|
||||||
|
$file_opt = array(
|
||||||
|
# file creation time (2 hours ago)
|
||||||
|
'time' => time() - 2 * 3600,
|
||||||
|
|
||||||
|
# file comment
|
||||||
|
'comment' => 'this is a file comment. hi!',
|
||||||
|
);
|
||||||
|
|
||||||
|
# add files under folder 'asdf'
|
||||||
|
foreach ($files as $file) {
|
||||||
|
# build absolute path and get file data
|
||||||
|
$path = ($file[0] == '/') ? $file : "$pwd/$file";
|
||||||
|
$data = file_get_contents($path);
|
||||||
|
|
||||||
|
# add file to archive
|
||||||
|
$zip->add_file('asdf/' . basename($file), $data, $file_opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
# add same files again wihtout a folder
|
||||||
|
foreach ($files as $file) {
|
||||||
|
# build absolute path and get file data
|
||||||
|
$path = ($file[0] == '/') ? $file : "$pwd/$file";
|
||||||
|
$data = file_get_contents($path);
|
||||||
|
|
||||||
|
# add file to archive
|
||||||
|
$zip->add_file(basename($file), $data, $file_opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
# finish archive
|
||||||
|
$zip->finish();
|
||||||
|
|
||||||
|
?>
|
580
contrib/zipstream-php-0.2.2/zipstream.php
Normal file
580
contrib/zipstream-php-0.2.2/zipstream.php
Normal file
@ -0,0 +1,580 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# ZipStream - Streamed, dynamically generated zip archives. #
|
||||||
|
# by Paul Duncan <pabs@pablotron.org> #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2007-2009 Paul Duncan <pabs@pablotron.org> #
|
||||||
|
# #
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining #
|
||||||
|
# a copy of this software and associated documentation files (the #
|
||||||
|
# "Software"), to deal in the Software without restriction, including #
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish, #
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to #
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to #
|
||||||
|
# the following conditions: #
|
||||||
|
# #
|
||||||
|
# The above copyright notice and this permission notice shall be #
|
||||||
|
# included in all copies or substantial portions of the of the Software. #
|
||||||
|
# #
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, #
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF #
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. #
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR #
|
||||||
|
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, #
|
||||||
|
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #
|
||||||
|
# OTHER DEALINGS IN THE SOFTWARE. #
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
#
|
||||||
|
# ZipStream - Streamed, dynamically generated zip archives.
|
||||||
|
# by Paul Duncan <pabs@pablotron.org>
|
||||||
|
#
|
||||||
|
# Requirements:
|
||||||
|
#
|
||||||
|
# * PHP version 5.1.2 or newer.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# Streaming zip archives is a simple, three-step process:
|
||||||
|
#
|
||||||
|
# 1. Create the zip stream:
|
||||||
|
#
|
||||||
|
# $zip = new ZipStream('example.zip');
|
||||||
|
#
|
||||||
|
# 2. Add one or more files to the archive:
|
||||||
|
#
|
||||||
|
# # add first file
|
||||||
|
# $data = file_get_contents('some_file.gif');
|
||||||
|
# $zip->add_file('some_file.gif', $data);
|
||||||
|
#
|
||||||
|
# # add second file
|
||||||
|
# $data = file_get_contents('some_file.gif');
|
||||||
|
# $zip->add_file('another_file.png', $data);
|
||||||
|
#
|
||||||
|
# 3. Finish the zip stream:
|
||||||
|
#
|
||||||
|
# $zip->finish();
|
||||||
|
#
|
||||||
|
# You can also add an archive comment, add comments to individual files,
|
||||||
|
# and adjust the timestamp of files. See the API documentation for each
|
||||||
|
# method below for additional information.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# # create a new zip stream object
|
||||||
|
# $zip = new ZipStream('some_files.zip');
|
||||||
|
#
|
||||||
|
# # list of local files
|
||||||
|
# $files = array('foo.txt', 'bar.jpg');
|
||||||
|
#
|
||||||
|
# # read and add each file to the archive
|
||||||
|
# foreach ($files as $path)
|
||||||
|
# $zip->add_file($path, file_get_contents($path));
|
||||||
|
#
|
||||||
|
# # write archive footer to stream
|
||||||
|
# $zip->finish();
|
||||||
|
#
|
||||||
|
class ZipStream {
|
||||||
|
const VERSION = '0.2.2';
|
||||||
|
|
||||||
|
var $opt = array(),
|
||||||
|
$files = array(),
|
||||||
|
$cdr_ofs = 0,
|
||||||
|
$need_headers = false,
|
||||||
|
$ofs = 0;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Create a new ZipStream object.
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
#
|
||||||
|
# $name - Name of output file (optional).
|
||||||
|
# $opt - Hash of archive options (optional, see "Archive Options"
|
||||||
|
# below).
|
||||||
|
#
|
||||||
|
# Archive Options:
|
||||||
|
#
|
||||||
|
# comment - Comment for this archive.
|
||||||
|
# content_type - HTTP Content-Type. Defaults to 'application/x-zip'.
|
||||||
|
# content_disposition - HTTP Content-Disposition. Defaults to
|
||||||
|
# 'attachment; filename=\"FILENAME\"', where
|
||||||
|
# FILENAME is the specified filename.
|
||||||
|
# large_file_size - Size, in bytes, of the largest file to try
|
||||||
|
# and load into memory (used by
|
||||||
|
# add_file_from_path()). Large files may also
|
||||||
|
# be compressed differently; see the
|
||||||
|
# 'large_file_method' option.
|
||||||
|
# large_file_method - How to handle large files. Legal values are
|
||||||
|
# 'store' (the default), or 'deflate'. Store
|
||||||
|
# sends the file raw and is significantly
|
||||||
|
# faster, while 'deflate' compresses the file
|
||||||
|
# and is much, much slower. Note that deflate
|
||||||
|
# must compress the file twice and extremely
|
||||||
|
# slow.
|
||||||
|
# send_http_headers - Boolean indicating whether or not to send
|
||||||
|
# the HTTP headers for this file.
|
||||||
|
#
|
||||||
|
# Note that content_type and content_disposition do nothing if you are
|
||||||
|
# not sending HTTP headers.
|
||||||
|
#
|
||||||
|
# Large File Support:
|
||||||
|
#
|
||||||
|
# By default, the method add_file_from_path() will send send files
|
||||||
|
# larger than 20 megabytes along raw rather than attempting to
|
||||||
|
# compress them. You can change both the maximum size and the
|
||||||
|
# compression behavior using the large_file_* options above, with the
|
||||||
|
# following caveats:
|
||||||
|
#
|
||||||
|
# * For "small" files (e.g. files smaller than large_file_size), the
|
||||||
|
# memory use can be up to twice that of the actual file. In other
|
||||||
|
# words, adding a 10 megabyte file to the archive could potentially
|
||||||
|
# occupty 20 megabytes of memory.
|
||||||
|
#
|
||||||
|
# * Enabling compression on large files (e.g. files larger than
|
||||||
|
# large_file_size) is extremely slow, because ZipStream has to pass
|
||||||
|
# over the large file once to calculate header information, and then
|
||||||
|
# again to compress and send the actual data.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
#
|
||||||
|
# # create a new zip file named 'foo.zip'
|
||||||
|
# $zip = new ZipStream('foo.zip');
|
||||||
|
#
|
||||||
|
# # create a new zip file named 'bar.zip' with a comment
|
||||||
|
# $zip = new ZipStream('bar.zip', array(
|
||||||
|
# 'comment' => 'this is a comment for the zip file.',
|
||||||
|
# ));
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
#
|
||||||
|
# If you do not set a filename, then this library _DOES NOT_ send HTTP
|
||||||
|
# headers by default. This behavior is to allow software to send its
|
||||||
|
# own headers (including the filename), and still use this library.
|
||||||
|
#
|
||||||
|
function __construct($name = null, $opt = array()) {
|
||||||
|
# save options
|
||||||
|
$this->opt = $opt;
|
||||||
|
|
||||||
|
# set large file defaults: size = 20 megabytes, method = store
|
||||||
|
if (!isset($this->opt['large_file_size']))
|
||||||
|
$this->opt['large_file_size'] = 20 * 1024 * 1024;
|
||||||
|
if (!isset($this->opt['large_file_method']))
|
||||||
|
$this->opt['large_file_method'] = 'store';
|
||||||
|
|
||||||
|
$this->output_name = $name;
|
||||||
|
if ($name || (isset($opt['send_http_headers'])
|
||||||
|
&& $opt['send_http_headers']))
|
||||||
|
$this->need_headers = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# add_file - add a file to the archive
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
#
|
||||||
|
# $name - path of file in archive (including directory).
|
||||||
|
# $data - contents of file
|
||||||
|
# $opt - Hash of options for file (optional, see "File Options"
|
||||||
|
# below).
|
||||||
|
#
|
||||||
|
# File Options:
|
||||||
|
# time - Last-modified timestamp (seconds since the epoch) of
|
||||||
|
# this file. Defaults to the current time.
|
||||||
|
# comment - Comment related to this file.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
#
|
||||||
|
# # add a file named 'foo.txt'
|
||||||
|
# $data = file_get_contents('foo.txt');
|
||||||
|
# $zip->add_file('foo.txt', $data);
|
||||||
|
#
|
||||||
|
# # add a file named 'bar.jpg' with a comment and a last-modified
|
||||||
|
# # time of two hours ago
|
||||||
|
# $data = file_get_contents('bar.jpg');
|
||||||
|
# $zip->add_file('bar.jpg', $data, array(
|
||||||
|
# 'time' => time() - 2 * 3600,
|
||||||
|
# 'comment' => 'this is a comment about bar.jpg',
|
||||||
|
# ));
|
||||||
|
#
|
||||||
|
function add_file($name, $data, $opt = array()) {
|
||||||
|
# compress data
|
||||||
|
$zdata = gzdeflate($data);
|
||||||
|
|
||||||
|
# calculate header attributes
|
||||||
|
$crc = crc32($data);
|
||||||
|
$zlen = strlen($zdata);
|
||||||
|
$len = strlen($data);
|
||||||
|
$meth = 0x08;
|
||||||
|
|
||||||
|
# send file header
|
||||||
|
$this->add_file_header($name, $opt, $meth, $crc, $zlen, $len);
|
||||||
|
|
||||||
|
# print data
|
||||||
|
$this->send($zdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# add_file_from_path - add a file at path to the archive.
|
||||||
|
#
|
||||||
|
# Note that large files may be compresed differently than smaller
|
||||||
|
# files; see the "Large File Support" section above for more
|
||||||
|
# information.
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
#
|
||||||
|
# $name - name of file in archive (including directory path).
|
||||||
|
# $path - path to file on disk (note: paths should be encoded using
|
||||||
|
# UNIX-style forward slashes -- e.g '/path/to/some/file').
|
||||||
|
# $opt - Hash of options for file (optional, see "File Options"
|
||||||
|
# below).
|
||||||
|
#
|
||||||
|
# File Options:
|
||||||
|
# time - Last-modified timestamp (seconds since the epoch) of
|
||||||
|
# this file. Defaults to the current time.
|
||||||
|
# comment - Comment related to this file.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
#
|
||||||
|
# # add a file named 'foo.txt' from the local file '/tmp/foo.txt'
|
||||||
|
# $zip->add_file_from_path('foo.txt', '/tmp/foo.txt');
|
||||||
|
#
|
||||||
|
# # add a file named 'bigfile.rar' from the local file
|
||||||
|
# # '/usr/share/bigfile.rar' with a comment and a last-modified
|
||||||
|
# # time of two hours ago
|
||||||
|
# $path = '/usr/share/bigfile.rar';
|
||||||
|
# $zip->add_file_from_path('bigfile.rar', $path, array(
|
||||||
|
# 'time' => time() - 2 * 3600,
|
||||||
|
# 'comment' => 'this is a comment about bar.jpg',
|
||||||
|
# ));
|
||||||
|
#
|
||||||
|
function add_file_from_path($name, $path, $opt = array()) {
|
||||||
|
if ($this->is_large_file($path)) {
|
||||||
|
# file is too large to be read into memory; add progressively
|
||||||
|
$this->add_large_file($name, $path, $opt);
|
||||||
|
} else {
|
||||||
|
# file is small enough to read into memory; read file contents and
|
||||||
|
# handle with add_file()
|
||||||
|
$data = file_get_contents($path);
|
||||||
|
$this->add_file($name, $data, $opt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# finish - Write zip footer to stream.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# # add a list of files to the archive
|
||||||
|
# $files = array('foo.txt', 'bar.jpg');
|
||||||
|
# foreach ($files as $path)
|
||||||
|
# $zip->add_file($path, file_get_contents($path));
|
||||||
|
#
|
||||||
|
# # write footer to stream
|
||||||
|
# $zip->finish();
|
||||||
|
#
|
||||||
|
function finish() {
|
||||||
|
# add trailing cdr record
|
||||||
|
$this->add_cdr($this->opt);
|
||||||
|
$this->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
###################
|
||||||
|
# PRIVATE METHODS #
|
||||||
|
###################
|
||||||
|
|
||||||
|
#
|
||||||
|
# Create and send zip header for this file.
|
||||||
|
#
|
||||||
|
private function add_file_header($name, $opt, $meth, $crc, $zlen, $len) {
|
||||||
|
# strip leading slashes from file name
|
||||||
|
# (fixes bug in windows archive viewer)
|
||||||
|
$name = preg_replace('/^\\/+/', '', $name);
|
||||||
|
|
||||||
|
# calculate name length
|
||||||
|
$nlen = strlen($name);
|
||||||
|
|
||||||
|
# create dos timestamp
|
||||||
|
$opt['time'] = isset($opt['time']) ? $opt['time'] : time();
|
||||||
|
$dts = $this->dostime($opt['time']);
|
||||||
|
|
||||||
|
# build file header
|
||||||
|
$fields = array( # (from V.A of APPNOTE.TXT)
|
||||||
|
array('V', 0x04034b50), # local file header signature
|
||||||
|
array('v', (6 << 8) + 3), # version needed to extract
|
||||||
|
array('v', 0x00), # general purpose bit flag
|
||||||
|
array('v', $meth), # compresion method (deflate or store)
|
||||||
|
array('V', $dts), # dos timestamp
|
||||||
|
array('V', $crc), # crc32 of data
|
||||||
|
array('V', $zlen), # compressed data length
|
||||||
|
array('V', $len), # uncompressed data length
|
||||||
|
array('v', $nlen), # filename length
|
||||||
|
array('v', 0), # extra data len
|
||||||
|
);
|
||||||
|
|
||||||
|
# pack fields and calculate "total" length
|
||||||
|
$ret = $this->pack_fields($fields);
|
||||||
|
$cdr_len = strlen($ret) + $nlen + $zlen;
|
||||||
|
|
||||||
|
# print header and filename
|
||||||
|
$this->send($ret . $name);
|
||||||
|
|
||||||
|
# add to central directory record and increment offset
|
||||||
|
$this->add_to_cdr($name, $opt, $meth, $crc, $zlen, $len, $cdr_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Add a large file from the given path.
|
||||||
|
#
|
||||||
|
private function add_large_file($name, $path, $opt = array()) {
|
||||||
|
$st = stat($path);
|
||||||
|
$block_size = 1048576; # process in 1 megabyte chunks
|
||||||
|
$algo = 'crc32b';
|
||||||
|
|
||||||
|
# calculate header attributes
|
||||||
|
$zlen = $len = $st['size'];
|
||||||
|
|
||||||
|
$meth_str = $this->opt['large_file_method'];
|
||||||
|
if ($meth_str == 'store') {
|
||||||
|
# store method
|
||||||
|
$meth = 0x00;
|
||||||
|
$crc = unpack('V', hash_file($algo, $path, true));
|
||||||
|
$crc = $crc[1];
|
||||||
|
} elseif ($meth_str == 'deflate') {
|
||||||
|
# deflate method
|
||||||
|
$meth = 0x08;
|
||||||
|
|
||||||
|
# open file, calculate crc and compressed file length
|
||||||
|
$fh = fopen($path, 'rb');
|
||||||
|
$hash_ctx = hash_init($algo);
|
||||||
|
$zlen = 0;
|
||||||
|
|
||||||
|
# read each block, update crc and zlen
|
||||||
|
while ($data = fgets($fh, $block_size)) {
|
||||||
|
hash_update($hash_ctx, $data);
|
||||||
|
$data = gzdeflate($data);
|
||||||
|
$zlen += strlen($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
# close file and finalize crc
|
||||||
|
fclose($fh);
|
||||||
|
$crc = unpack('V', hash_final($hash_ctx, true));
|
||||||
|
$crc = $crc[1];
|
||||||
|
} else {
|
||||||
|
die("unknown large_file_method: $meth_str");
|
||||||
|
}
|
||||||
|
|
||||||
|
# send file header
|
||||||
|
$this->add_file_header($name, $opt, $meth, $crc, $zlen, $len);
|
||||||
|
|
||||||
|
# open input file
|
||||||
|
$fh = fopen($path, 'rb');
|
||||||
|
|
||||||
|
# send file blocks
|
||||||
|
while ($data = fgets($fh, $block_size)) {
|
||||||
|
if ($meth_str == 'deflate')
|
||||||
|
$data = gzdeflate($data);
|
||||||
|
|
||||||
|
# send data
|
||||||
|
$this->send($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
# close input file
|
||||||
|
fclose($fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Is this file larger than large_file_size?
|
||||||
|
#
|
||||||
|
function is_large_file($path) {
|
||||||
|
$st = stat($path);
|
||||||
|
return ($this->opt['large_file_size'] > 0) &&
|
||||||
|
($st['size'] > $this->opt['large_file_size']);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Save file attributes for trailing CDR record.
|
||||||
|
#
|
||||||
|
private function add_to_cdr($name, $opt, $meth, $crc, $zlen, $len, $rec_len) {
|
||||||
|
$this->files[] = array($name, $opt, $meth, $crc, $zlen, $len, $this->ofs);
|
||||||
|
$this->ofs += $rec_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Send CDR record for specified file.
|
||||||
|
#
|
||||||
|
private function add_cdr_file($args) {
|
||||||
|
list ($name, $opt, $meth, $crc, $zlen, $len, $ofs) = $args;
|
||||||
|
|
||||||
|
# get attributes
|
||||||
|
$comment = isset($opt['comment']) ? $opt['comment'] : '';
|
||||||
|
|
||||||
|
# get dos timestamp
|
||||||
|
$dts = $this->dostime($opt['time']);
|
||||||
|
|
||||||
|
$fields = array( # (from V,F of APPNOTE.TXT)
|
||||||
|
array('V', 0x02014b50), # central file header signature
|
||||||
|
array('v', (6 << 8) + 3), # version made by
|
||||||
|
array('v', (6 << 8) + 3), # version needed to extract
|
||||||
|
array('v', 0x00), # general purpose bit flag
|
||||||
|
array('v', $meth), # compresion method (deflate or store)
|
||||||
|
array('V', $dts), # dos timestamp
|
||||||
|
array('V', $crc), # crc32 of data
|
||||||
|
array('V', $zlen), # compressed data length
|
||||||
|
array('V', $len), # uncompressed data length
|
||||||
|
array('v', strlen($name)), # filename length
|
||||||
|
array('v', 0), # extra data len
|
||||||
|
array('v', strlen($comment)), # file comment length
|
||||||
|
array('v', 0), # disk number start
|
||||||
|
array('v', 0), # internal file attributes
|
||||||
|
array('V', 32), # external file attributes
|
||||||
|
array('V', $ofs), # relative offset of local header
|
||||||
|
);
|
||||||
|
|
||||||
|
# pack fields, then append name and comment
|
||||||
|
$ret = $this->pack_fields($fields) . $name . $comment;
|
||||||
|
|
||||||
|
$this->send($ret);
|
||||||
|
|
||||||
|
# increment cdr offset
|
||||||
|
$this->cdr_ofs += strlen($ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Send CDR EOF (Central Directory Record End-of-File) record.
|
||||||
|
#
|
||||||
|
private function add_cdr_eof($opt = null) {
|
||||||
|
$num = count($this->files);
|
||||||
|
$cdr_len = $this->cdr_ofs;
|
||||||
|
$cdr_ofs = $this->ofs;
|
||||||
|
|
||||||
|
# grab comment (if specified)
|
||||||
|
$comment = '';
|
||||||
|
if ($opt && isset($opt['comment']))
|
||||||
|
$comment = $opt['comment'];
|
||||||
|
|
||||||
|
$fields = array( # (from V,F of APPNOTE.TXT)
|
||||||
|
array('V', 0x06054b50), # end of central file header signature
|
||||||
|
array('v', 0x00), # this disk number
|
||||||
|
array('v', 0x00), # number of disk with cdr
|
||||||
|
array('v', $num), # number of entries in the cdr on this disk
|
||||||
|
array('v', $num), # number of entries in the cdr
|
||||||
|
array('V', $cdr_len), # cdr size
|
||||||
|
array('V', $cdr_ofs), # cdr ofs
|
||||||
|
array('v', strlen($comment)), # zip file comment length
|
||||||
|
);
|
||||||
|
|
||||||
|
$ret = $this->pack_fields($fields) . $comment;
|
||||||
|
$this->send($ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Add CDR (Central Directory Record) footer.
|
||||||
|
#
|
||||||
|
private function add_cdr($opt = null) {
|
||||||
|
foreach ($this->files as $file)
|
||||||
|
$this->add_cdr_file($file);
|
||||||
|
$this->add_cdr_eof($opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Clear all internal variables. Note that the stream object is not
|
||||||
|
# usable after this.
|
||||||
|
#
|
||||||
|
function clear() {
|
||||||
|
$this->files = array();
|
||||||
|
$this->ofs = 0;
|
||||||
|
$this->cdr_ofs = 0;
|
||||||
|
$this->opt = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# PRIVATE UTILITY METHODS #
|
||||||
|
###########################
|
||||||
|
|
||||||
|
#
|
||||||
|
# Send HTTP headers for this stream.
|
||||||
|
#
|
||||||
|
private function send_http_headers() {
|
||||||
|
# grab options
|
||||||
|
$opt = $this->opt;
|
||||||
|
|
||||||
|
# grab content type from options
|
||||||
|
$content_type = 'application/x-zip';
|
||||||
|
if (isset($opt['content_type']))
|
||||||
|
$content_type = $this->opt['content_type'];
|
||||||
|
|
||||||
|
# grab content disposition
|
||||||
|
$disposition = 'attachment';
|
||||||
|
if (isset($opt['content_disposition']))
|
||||||
|
$disposition = $opt['content_disposition'];
|
||||||
|
|
||||||
|
if ($this->output_name)
|
||||||
|
$disposition .= "; filename=\"{$this->output_name}\"";
|
||||||
|
|
||||||
|
$headers = array(
|
||||||
|
'Content-Type' => $content_type,
|
||||||
|
'Content-Disposition' => $disposition,
|
||||||
|
'Pragma' => 'public',
|
||||||
|
'Cache-Control' => 'public, must-revalidate',
|
||||||
|
'Content-Transfer-Encoding' => 'binary',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($headers as $key => $val)
|
||||||
|
header("$key: $val");
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Send string, sending HTTP headers if necessary.
|
||||||
|
#
|
||||||
|
private function send($str) {
|
||||||
|
if ($this->need_headers)
|
||||||
|
$this->send_http_headers();
|
||||||
|
$this->need_headers = false;
|
||||||
|
|
||||||
|
echo $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Convert a UNIX timestamp to a DOS timestamp.
|
||||||
|
#
|
||||||
|
function dostime($when = 0) {
|
||||||
|
# get date array for timestamp
|
||||||
|
$d = getdate($when);
|
||||||
|
|
||||||
|
# set lower-bound on dates
|
||||||
|
if ($d['year'] < 1980) {
|
||||||
|
$d = array('year' => 1980, 'mon' => 1, 'mday' => 1,
|
||||||
|
'hours' => 0, 'minutes' => 0, 'seconds' => 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove extra years from 1980
|
||||||
|
$d['year'] -= 1980;
|
||||||
|
|
||||||
|
# return date string
|
||||||
|
return ($d['year'] << 25) | ($d['mon'] << 21) | ($d['mday'] << 16) |
|
||||||
|
($d['hours'] << 11) | ($d['minutes'] << 5) | ($d['seconds'] >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Create a format string and argument list for pack(), then call
|
||||||
|
# pack() and return the result.
|
||||||
|
#
|
||||||
|
function pack_fields($fields) {
|
||||||
|
list ($fmt, $args) = array('', array());
|
||||||
|
|
||||||
|
# populate format string and argument list
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$fmt .= $field[0];
|
||||||
|
$args[] = $field[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
# prepend format string to argument list
|
||||||
|
array_unshift($args, $fmt);
|
||||||
|
|
||||||
|
# build output string from header and compressed data
|
||||||
|
return call_user_func_array('pack', $args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
?>
|
@ -173,3 +173,52 @@ For even more advanced setups, usher can also be used to forward sync
|
|||||||
requests to other remote servers for load balancing, please consult the
|
requests to other remote servers for load balancing, please consult the
|
||||||
README file for more information.
|
README file for more information.
|
||||||
|
|
||||||
|
## Security and remote access
|
||||||
|
|
||||||
|
Indefero distinguishs between public and private projects and so does
|
||||||
|
the monotone plugin.
|
||||||
|
|
||||||
|
Public projects can be pulled by everybody and pushed by team members
|
||||||
|
or additional invited people. Remote command execution is enabled, but
|
||||||
|
only for read-only commands.
|
||||||
|
|
||||||
|
Remote commands can be helpful for a user or a 3rd party tool (like
|
||||||
|
[mtn-browse](http://mtn-browse.sourceforge.net) or
|
||||||
|
[guitone](http://guitone.thomaskeller.biz)) to browse the database
|
||||||
|
contents remotely without having to pull everything in first instance.
|
||||||
|
|
||||||
|
Private projects on the other hand can only be synced by team members
|
||||||
|
or additional invited people. Also noo remote command execution is enabled
|
||||||
|
by default.
|
||||||
|
|
||||||
|
## Notifications
|
||||||
|
|
||||||
|
If you have successfully set up your monotone instance, you probably want
|
||||||
|
to notify 3rd party systems for incoming changes or simply mirror them
|
||||||
|
somewhere else for backup purposes. The monotone source tree already comes
|
||||||
|
with [many example scripts and hooks](http://code.monotone.ca/p/monotone/source/tree/h:net.venge.monotone/contrib)
|
||||||
|
which serve these purposes, after only little additional configuration.
|
||||||
|
|
||||||
|
The usher/indefero-controlled setup automatically looks for *.lua files
|
||||||
|
in a directory called `hooks.d` right under the project's base directory
|
||||||
|
(configured via $cfg['mtn_repositories']) and this is the ideal place to
|
||||||
|
put or link these additional lua sources.
|
||||||
|
|
||||||
|
## Q&A
|
||||||
|
|
||||||
|
### I pushed a branch to my server, but it does not show up in IDF. Whats wrong?
|
||||||
|
|
||||||
|
Check if the heads of your branch are not suspended, i.e. do not carry a
|
||||||
|
`suspend` certificate. This usually hides the branch and all of its history
|
||||||
|
from monotone's eyes and therefor also from indefero. You can either choose
|
||||||
|
to "unsuspend" the branch simply by committing and pushing another head or
|
||||||
|
by letting monotone ignore all suspend certs. For the latter, its usually
|
||||||
|
enough to add `--ignore-suspend-certs` to the list of options in `$cfg['mtn_opts']`.
|
||||||
|
|
||||||
|
### I want to display another default branch when I click the "Source" tab. How can I do that?
|
||||||
|
|
||||||
|
Let the forge admin know the new master branch for your project. He is able
|
||||||
|
to change that quickly. Depending on the backend / server setup this might
|
||||||
|
also require some changes in the usher configuration, but only if usher
|
||||||
|
recognizes and proxies your database on a branch name level.
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
|||||||
|
|
||||||
$this->fields['shortdesc'] = new Pluf_Form_Field_Varchar(
|
$this->fields['shortdesc'] = new Pluf_Form_Field_Varchar(
|
||||||
array('required' => true,
|
array('required' => true,
|
||||||
'label' => __('short description'),
|
'label' => __('Short description'),
|
||||||
'help_text' => __('A one line description of the project.'),
|
'help_text' => __('A one line description of the project.'),
|
||||||
'initial' => '',
|
'initial' => '',
|
||||||
'widget_attrs' => array('size' => '35'),
|
'widget_attrs' => array('size' => '35'),
|
||||||
@ -105,6 +105,7 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
|||||||
array('required' => false,
|
array('required' => false,
|
||||||
'label' => __('Master branch'),
|
'label' => __('Master branch'),
|
||||||
'initial' => '',
|
'initial' => '',
|
||||||
|
'widget_attrs' => array('size' => '35'),
|
||||||
'help_text' => __('This should be a world-wide unique identifier for your project. A reverse DNS notation like "com.my-domain.my-project" is a good idea.'),
|
'help_text' => __('This should be a world-wide unique identifier for your project. A reverse DNS notation like "com.my-domain.my-project" is a good idea.'),
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -203,7 +204,7 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
|
|||||||
}
|
}
|
||||||
|
|
||||||
$sql = new Pluf_SQL('vkey=%s AND vdesc=%s',
|
$sql = new Pluf_SQL('vkey=%s AND vdesc=%s',
|
||||||
array("mtn_master_branch", $mtn_master_branch));
|
array('mtn_master_branch', $mtn_master_branch));
|
||||||
$l = Pluf::factory('IDF_Conf')->getList(array('filter'=>$sql->gen()));
|
$l = Pluf::factory('IDF_Conf')->getList(array('filter'=>$sql->gen()));
|
||||||
if ($l->count() > 0) {
|
if ($l->count() > 0) {
|
||||||
throw new Pluf_Form_Invalid(__(
|
throw new Pluf_Form_Invalid(__(
|
||||||
|
@ -37,6 +37,8 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
|
|||||||
{
|
{
|
||||||
$this->project = $extra['project'];
|
$this->project = $extra['project'];
|
||||||
$members = $this->project->getMembershipData('string');
|
$members = $this->project->getMembershipData('string');
|
||||||
|
$conf = $this->project->getConf();
|
||||||
|
|
||||||
$this->fields['name'] = new Pluf_Form_Field_Varchar(
|
$this->fields['name'] = new Pluf_Form_Field_Varchar(
|
||||||
array('required' => true,
|
array('required' => true,
|
||||||
'label' => __('Name'),
|
'label' => __('Name'),
|
||||||
@ -45,12 +47,22 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
|
|||||||
|
|
||||||
$this->fields['shortdesc'] = new Pluf_Form_Field_Varchar(
|
$this->fields['shortdesc'] = new Pluf_Form_Field_Varchar(
|
||||||
array('required' => true,
|
array('required' => true,
|
||||||
'label' => __('short description'),
|
'label' => __('Short description'),
|
||||||
'help_text' => __('A one line description of the project.'),
|
'help_text' => __('A one line description of the project.'),
|
||||||
'initial' => $this->project->shortdesc,
|
'initial' => $this->project->shortdesc,
|
||||||
'widget_attrs' => array('size' => '35'),
|
'widget_attrs' => array('size' => '35'),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
if ($this->project->getConf()->getVal('scm') == 'mtn') {
|
||||||
|
$this->fields['mtn_master_branch'] = new Pluf_Form_Field_Varchar(
|
||||||
|
array('required' => false,
|
||||||
|
'label' => __('Master branch'),
|
||||||
|
'initial' => $conf->getVal('mtn_master_branch'),
|
||||||
|
'widget_attrs' => array('size' => '35'),
|
||||||
|
'help_text' => __('This should be a world-wide unique identifier for your project. A reverse DNS notation like "com.my-domain.my-project" is a good idea.'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
$this->fields['owners'] = new Pluf_Form_Field_Varchar(
|
$this->fields['owners'] = new Pluf_Form_Field_Varchar(
|
||||||
array('required' => false,
|
array('required' => false,
|
||||||
'label' => __('Project owners'),
|
'label' => __('Project owners'),
|
||||||
@ -69,6 +81,30 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clean_mtn_master_branch()
|
||||||
|
{
|
||||||
|
$mtn_master_branch = mb_strtolower($this->cleaned_data['mtn_master_branch']);
|
||||||
|
if (!preg_match('/^([\w\d]+([-][\w\d]+)*)(\.[\w\d]+([-][\w\d]+)*)*$/',
|
||||||
|
$mtn_master_branch)) {
|
||||||
|
throw new Pluf_Form_Invalid(__(
|
||||||
|
'The master branch is empty or contains illegal characters, '.
|
||||||
|
'please use only letters, digits, dashs and dots as separators.'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = new Pluf_SQL('vkey=%s AND vdesc=%s AND project!=%s',
|
||||||
|
array('mtn_master_branch', $mtn_master_branch,
|
||||||
|
(string)$this->project->id));
|
||||||
|
$l = Pluf::factory('IDF_Conf')->getList(array('filter'=>$sql->gen()));
|
||||||
|
if ($l->count() > 0) {
|
||||||
|
throw new Pluf_Form_Invalid(__(
|
||||||
|
'This master branch is already used. Please select another one.'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mtn_master_branch;
|
||||||
|
}
|
||||||
|
|
||||||
public function clean_owners()
|
public function clean_owners()
|
||||||
{
|
{
|
||||||
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['owners']);
|
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['owners']);
|
||||||
@ -90,6 +126,13 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
|
|||||||
$this->project->name = $this->cleaned_data['name'];
|
$this->project->name = $this->cleaned_data['name'];
|
||||||
$this->project->shortdesc = $this->cleaned_data['shortdesc'];
|
$this->project->shortdesc = $this->cleaned_data['shortdesc'];
|
||||||
$this->project->update();
|
$this->project->update();
|
||||||
|
|
||||||
|
$keys = array('mtn_master_branch');
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (!empty($this->cleaned_data[$key])) {
|
||||||
|
$this->project->getConf()->setVal($key, $this->cleaned_data[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,14 +108,39 @@ class IDF_Form_IssueCreate extends Pluf_Form
|
|||||||
'size' => 15,
|
'size' => 15,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get predefined tags for issues from current project
|
||||||
|
*
|
||||||
|
* first Type:<...> and Priority:<...> will be used
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
$predefined = preg_split("/[\r\n]+/", $extra['project']->getConf()->getVal(
|
||||||
|
'labels_issue_predefined'
|
||||||
|
));
|
||||||
|
$predefined_type = 'Type:Defect';
|
||||||
|
foreach ($predefined as $tag) {
|
||||||
|
if (strpos($tag, 'Type:') === 0) {
|
||||||
|
$predefined_type = $tag;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$predefined_priority = 'Priority:Medium';
|
||||||
|
foreach ($predefined as $tag) {
|
||||||
|
if (strpos($tag, 'Priority:') === 0) {
|
||||||
|
$predefined_priority = $tag;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for ($i=1;$i<7;$i++) {
|
for ($i=1;$i<7;$i++) {
|
||||||
$initial = '';
|
$initial = '';
|
||||||
switch ($i) {
|
switch ($i) {
|
||||||
case 1:
|
case 1:
|
||||||
$initial = 'Type:Defect';
|
$initial = $predefined_type;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
$initial = 'Priority:Medium';
|
$initial = $predefined_priority;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$this->fields['label'.$i] = new Pluf_Form_Field_Varchar(
|
$this->fields['label'.$i] = new Pluf_Form_Field_Varchar(
|
||||||
|
@ -105,6 +105,7 @@ Maintainability = Hinders future changes';
|
|||||||
array('required' => true,
|
array('required' => true,
|
||||||
'label' => __('Predefined issue labels'),
|
'label' => __('Predefined issue labels'),
|
||||||
'initial' => self::init_predefined,
|
'initial' => self::init_predefined,
|
||||||
|
'help_text' => __('The first "Type:" and "Priority:" entries found in this list are automatically chosen as defaults for new issues.'),
|
||||||
'widget_attrs' => array('rows' => 7,
|
'widget_attrs' => array('rows' => 7,
|
||||||
'cols' => 75),
|
'cols' => 75),
|
||||||
'widget' => 'Pluf_Form_Widget_TextareaInput',
|
'widget' => 'Pluf_Form_Widget_TextareaInput',
|
||||||
|
@ -34,22 +34,23 @@ end
|
|||||||
-- let IDF know of new arriving revisions to fill its timeline
|
-- let IDF know of new arriving revisions to fill its timeline
|
||||||
--
|
--
|
||||||
_idf_revs = {}
|
_idf_revs = {}
|
||||||
function note_netsync_start(session_id)
|
push_hook_functions({
|
||||||
|
["start"] = function (session_id)
|
||||||
_idf_revs[session_id] = {}
|
_idf_revs[session_id] = {}
|
||||||
end
|
return "continue",nil
|
||||||
|
end,
|
||||||
function note_netsync_revision_received(new_id, revision, certs, session_id)
|
["revision_received"] = function (new_id, revision, certs, session_id)
|
||||||
table.insert(_idf_revs[session_id], new_id)
|
table.insert(_idf_revs[session_id], new_id)
|
||||||
end
|
return "continue",nil
|
||||||
|
end,
|
||||||
function note_netsync_end (session_id, ...)
|
["end"] = function (session_id, ...)
|
||||||
if table.getn(_idf_revs[session_id]) == 0 then
|
if table.getn(_idf_revs[session_id]) == 0 then
|
||||||
return
|
return "continue",nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local pin,pout,pid = spawn_pipe("%%MTNPOSTPUSH%%", "%%PROJECT%%");
|
local pin,pout,pid = spawn_pipe("%%MTNPOSTPUSH%%", "%%PROJECT%%");
|
||||||
if pid == -1 then
|
if pid == -1 then
|
||||||
print("could execute %%MTNPOSTPUSH%%")
|
print("could not execute %%MTNPOSTPUSH%%")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -59,5 +60,20 @@ function note_netsync_end (session_id, ...)
|
|||||||
pin:close()
|
pin:close()
|
||||||
|
|
||||||
wait(pid)
|
wait(pid)
|
||||||
|
return "continue",nil
|
||||||
end
|
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")
|
||||||
|
|
||||||
|
@ -31,7 +31,8 @@ function get_remote_automate_permitted(key_identity, command, options)
|
|||||||
"leaves", "ancestry_difference", "toposort", "erase_ancestors",
|
"leaves", "ancestry_difference", "toposort", "erase_ancestors",
|
||||||
"descendents", "ancestors", "heads", "get_file_of", "get_file",
|
"descendents", "ancestors", "heads", "get_file_of", "get_file",
|
||||||
"interface_version", "get_attributes", "content_diff",
|
"interface_version", "get_attributes", "content_diff",
|
||||||
"file_merge", "show_conflicts", "certs", "keys"
|
"file_merge", "show_conflicts", "certs", "keys", "get_file_size",
|
||||||
|
"get_extended_manifest_of"
|
||||||
}
|
}
|
||||||
|
|
||||||
for _,v in ipairs(read_only_commands) do
|
for _,v in ipairs(read_only_commands) do
|
||||||
@ -47,22 +48,23 @@ end
|
|||||||
-- let IDF know of new arriving revisions to fill its timeline
|
-- let IDF know of new arriving revisions to fill its timeline
|
||||||
--
|
--
|
||||||
_idf_revs = {}
|
_idf_revs = {}
|
||||||
function note_netsync_start(session_id)
|
push_hook_functions({
|
||||||
|
["start"] = function (session_id)
|
||||||
_idf_revs[session_id] = {}
|
_idf_revs[session_id] = {}
|
||||||
end
|
return "continue",nil
|
||||||
|
end,
|
||||||
function note_netsync_revision_received(new_id, revision, certs, session_id)
|
["revision_received"] = function (new_id, revision, certs, session_id)
|
||||||
table.insert(_idf_revs[session_id], new_id)
|
table.insert(_idf_revs[session_id], new_id)
|
||||||
end
|
return "continue",nil
|
||||||
|
end,
|
||||||
function note_netsync_end (session_id, ...)
|
["end"] = function (session_id, ...)
|
||||||
if table.getn(_idf_revs[session_id]) == 0 then
|
if table.getn(_idf_revs[session_id]) == 0 then
|
||||||
return
|
return "continue",nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local pin,pout,pid = spawn_pipe("%%MTNPOSTPUSH%%", "%%PROJECT%%");
|
local pin,pout,pid = spawn_pipe("%%MTNPOSTPUSH%%", "%%PROJECT%%");
|
||||||
if pid == -1 then
|
if pid == -1 then
|
||||||
print("could execute %%MTNPOSTPUSH%%")
|
print("could not execute %%MTNPOSTPUSH%%")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -72,4 +74,19 @@ function note_netsync_end (session_id, ...)
|
|||||||
pin:close()
|
pin:close()
|
||||||
|
|
||||||
wait(pid)
|
wait(pid)
|
||||||
|
return "continue",nil
|
||||||
end
|
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")
|
||||||
|
@ -393,13 +393,13 @@ class IDF_Scm
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the command to create a zip archive at a given commit.
|
* Generate a zip archive at a given commit, wrapped in a HTTP response, suitable for pushing to client.
|
||||||
*
|
*
|
||||||
* @param string Commit
|
* @param string Commit
|
||||||
* @param string Prefix ('repository/')
|
* @param string Prefix ('repository/')
|
||||||
* @return string Command
|
* @return Pluf_HTTP_Response The HTTP Response containing the zip archive
|
||||||
*/
|
*/
|
||||||
public function getArchiveCommand($commit, $prefix='repository/')
|
public function getArchiveStream($commit, $prefix='repository/')
|
||||||
{
|
{
|
||||||
throw new Pluf_Exception_NotImplemented();
|
throw new Pluf_Exception_NotImplemented();
|
||||||
}
|
}
|
||||||
|
@ -436,6 +436,8 @@ class IDF_Scm_Git extends IDF_Scm
|
|||||||
$out = self::parseLog($out);
|
$out = self::parseLog($out);
|
||||||
$out[0]->changes = '';
|
$out[0]->changes = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$out[0]['branch'] = $this->inBranches($commit, null);
|
||||||
return $out[0];
|
return $out[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,13 +549,14 @@ class IDF_Scm_Git extends IDF_Scm
|
|||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getArchiveCommand($commit, $prefix='repository/')
|
public function getArchiveStream($commit, $prefix='repository/')
|
||||||
{
|
{
|
||||||
return sprintf(Pluf::f('idf_exec_cmd_prefix', '').
|
$cmd = sprintf(Pluf::f('idf_exec_cmd_prefix', '').
|
||||||
'GIT_DIR=%s '.Pluf::f('git_path', 'git').' archive --format=zip --prefix=%s %s',
|
'GIT_DIR=%s '.Pluf::f('git_path', 'git').' archive --format=zip --prefix=%s %s',
|
||||||
escapeshellarg($this->repo),
|
escapeshellarg($this->repo),
|
||||||
escapeshellarg($prefix),
|
escapeshellarg($prefix),
|
||||||
escapeshellarg($commit));
|
escapeshellarg($commit));
|
||||||
|
return new Pluf_HTTP_Response_CommandPassThru($cmd, 'application/x-zip');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -429,6 +429,8 @@ class IDF_Scm_Mercurial extends IDF_Scm
|
|||||||
$c['author'] = $match[2];
|
$c['author'] = $match[2];
|
||||||
} elseif ($match[1] == 'summary') {
|
} elseif ($match[1] == 'summary') {
|
||||||
$c['title'] = $match[2];
|
$c['title'] = $match[2];
|
||||||
|
} elseif ($match[1] == 'branch') {
|
||||||
|
$c['branch'] = $match[2];
|
||||||
} else {
|
} else {
|
||||||
$c[$match[1]] = trim($match[2]);
|
$c[$match[1]] = trim($match[2]);
|
||||||
}
|
}
|
||||||
@ -443,23 +445,25 @@ class IDF_Scm_Mercurial extends IDF_Scm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$c['tree'] = !empty($c['commit']) ? trim($c['commit']) : '';
|
$c['tree'] = !empty($c['commit']) ? trim($c['commit']) : '';
|
||||||
|
$c['branch'] = empty($c['branch']) ? 'default' : $c['branch'];
|
||||||
$c['full_message'] = !empty($c['full_message']) ? trim($c['full_message']) : '';
|
$c['full_message'] = !empty($c['full_message']) ? trim($c['full_message']) : '';
|
||||||
$res[] = (object) $c;
|
$res[] = (object) $c;
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the command to create a zip archive at a given commit.
|
* Generate a zip archive at a given commit.
|
||||||
*
|
*
|
||||||
* @param string Commit
|
* @param string Commit
|
||||||
* @param string Prefix ('git-repo-dump')
|
* @param string Prefix ('git-repo-dump')
|
||||||
* @return string Command
|
* @return Pluf_HTTP_Response The HTTP response containing the zip archive
|
||||||
*/
|
*/
|
||||||
public function getArchiveCommand($commit, $prefix='')
|
protected function getArchiveStream($commit, $prefix='')
|
||||||
{
|
{
|
||||||
return sprintf(Pluf::f('idf_exec_cmd_prefix', '').
|
$cmd = sprintf(Pluf::f('idf_exec_cmd_prefix', '').
|
||||||
Pluf::f('hg_path', 'hg').' archive --type=zip -R %s -r %s -',
|
Pluf::f('hg_path', 'hg').' archive --type=zip -R %s -r %s -',
|
||||||
escapeshellarg($this->repo),
|
escapeshellarg($this->repo),
|
||||||
escapeshellarg($commit));
|
escapeshellarg($commit));
|
||||||
|
return new Pluf_HTTP_Response_CommandPassThru($cmd, 'application/x-zip');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@
|
|||||||
#
|
#
|
||||||
# ***** END LICENSE BLOCK ***** */
|
# ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
require_once(dirname(__FILE__) . "/Monotone/Stdio.php");
|
//require_once(dirname(__FILE__) . "/Monotone/Stdio.php");
|
||||||
require_once(dirname(__FILE__) . "/Monotone/BasicIO.php");
|
//require_once(dirname(__FILE__) . "/Monotone/BasicIO.php");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monotone scm class
|
* Monotone scm class
|
||||||
@ -135,6 +135,20 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
return $branch;
|
return $branch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see IDF_Scm::getArchiveStream
|
||||||
|
*/
|
||||||
|
public function getArchiveStream($commit, $prefix='repository/')
|
||||||
|
{
|
||||||
|
$revs = $this->_resolveSelector($commit);
|
||||||
|
// sanity: this should actually not happen, because the
|
||||||
|
// revision is validated before already
|
||||||
|
if (count($revs) == 0) {
|
||||||
|
return new Pluf_HTTP_Response_NotFound();
|
||||||
|
}
|
||||||
|
return new IDF_Scm_Monotone_ZipRender($this->stdio, $revs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* expands a selector or a partial revision id to zero, one or
|
* expands a selector or a partial revision id to zero, one or
|
||||||
* multiple 40 byte revision ids
|
* multiple 40 byte revision ids
|
||||||
@ -609,7 +623,9 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$res['title'] = $split[0];
|
$res['title'] = $split[0];
|
||||||
$res['full_message'] = (isset($split[1])) ? trim($split[1]) : '';
|
$res['full_message'] = (isset($split[1])) ? trim($split[1]) : '';
|
||||||
|
|
||||||
|
$res['branch'] = implode(', ', $certs['branch']);
|
||||||
$res['commit'] = $revs[0];
|
$res['commit'] = $revs[0];
|
||||||
|
|
||||||
$res['changes'] = ($getdiff) ? $this->_getDiff($revs[0]) : '';
|
$res['changes'] = ($getdiff) ? $this->_getDiff($revs[0]) : '';
|
||||||
|
|
||||||
return (object) $res;
|
return (object) $res;
|
||||||
|
78
src/IDF/Scm/Monotone/ZipRender.php
Normal file
78
src/IDF/Scm/Monotone/ZipRender.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) 2010 Céondo Ltd and contributors.
|
||||||
|
#
|
||||||
|
# Plume Framework is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Plume Framework is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
require_once(IDF_PATH.'/../contrib/zipstream-php-0.2.2/zipstream.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special response object to output
|
||||||
|
*
|
||||||
|
* The Content-Length will not be set as it is not possible to predict it.
|
||||||
|
*
|
||||||
|
* Note: The ZipArchive version 0.2.2 has been patched in-tree with this
|
||||||
|
* patch http://pastebin.ca/1977584 to avoid a couple of PHP notices
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class IDF_Scm_Monotone_ZipRender extends Pluf_HTTP_Response
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The revision argument must be a safe string!
|
||||||
|
*
|
||||||
|
* @param Object stdio context
|
||||||
|
* @param string revision
|
||||||
|
* @param string Mimetype (null)
|
||||||
|
*/
|
||||||
|
|
||||||
|
private $stdio = null;
|
||||||
|
private $revision = null;
|
||||||
|
|
||||||
|
function __construct($stdio, $revision)
|
||||||
|
{
|
||||||
|
parent::__construct($revision, 'application/x-zip');
|
||||||
|
$this->stdio = $stdio;
|
||||||
|
$this->revision = $revision;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a response object.
|
||||||
|
*/
|
||||||
|
function render($output_body=true)
|
||||||
|
{
|
||||||
|
$this->outputHeaders();
|
||||||
|
|
||||||
|
if ($output_body) {
|
||||||
|
$manifest = $this->stdio->exec(array('get_manifest_of', $this->revision));
|
||||||
|
$stanzas = IDF_Scm_Monotone_BasicIO::parse($manifest);
|
||||||
|
|
||||||
|
$zip = new ZipStream();
|
||||||
|
|
||||||
|
foreach ($stanzas as $stanza) {
|
||||||
|
if ($stanza[0]['key'] != 'file')
|
||||||
|
continue;
|
||||||
|
$content = $this->stdio->exec(array('get_file', $stanza[1]['hash']));
|
||||||
|
$zip->add_file($stanza[0]['values'][0], $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
$zip->finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -415,6 +415,7 @@ class IDF_Scm_Svn extends IDF_Scm
|
|||||||
$res['commit'] = (string) $xml->logentry['revision'];
|
$res['commit'] = (string) $xml->logentry['revision'];
|
||||||
$res['changes'] = ($getdiff) ? $this->getDiff($commit) : '';
|
$res['changes'] = ($getdiff) ? $this->getDiff($commit) : '';
|
||||||
$res['tree'] = '';
|
$res['tree'] = '';
|
||||||
|
$res['branch'] = '';
|
||||||
return (object) $res;
|
return (object) $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,11 +35,12 @@ class IDF_Views_Source
|
|||||||
* Extension supported by the syntax highlighter.
|
* Extension supported by the syntax highlighter.
|
||||||
*/
|
*/
|
||||||
public static $supportedExtenstions = array(
|
public static $supportedExtenstions = array(
|
||||||
'ascx', 'ashx', 'asmx', 'aspx', 'browser', 'bsh', 'c', 'cc',
|
'ascx', 'ashx', 'asmx', 'aspx', 'browser', 'bsh', 'c', 'cl', 'cc',
|
||||||
'config', 'cpp', 'cs', 'csh', 'csproj', 'css', 'cv', 'cyc',
|
'config', 'cpp', 'cs', 'csh', 'csproj', 'css', 'cv', 'cyc', 'el', 'fs',
|
||||||
'html', 'html', 'java', 'js', 'master', 'pas', 'perl', 'php', 'pl',
|
'h', 'hh', 'hpp', 'hs', 'html', 'html', 'java', 'js', 'lisp', 'master',
|
||||||
'pm', 'py', 'rb', 'sh', 'sitemap', 'skin', 'sln', 'svc', 'vala',
|
'pas', 'perl', 'php', 'pl', 'pm', 'py', 'rb', 'scm', 'sh', 'sitemap',
|
||||||
'vb', 'vbproj', 'wsdl', 'xhtml', 'xml', 'xsd', 'xsl', 'xslt');
|
'skin', 'sln', 'svc', 'vala', 'vb', 'vbproj', 'vbs', 'wsdl', 'xhtml',
|
||||||
|
'xml', 'xsd', 'xsl', 'xslt');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display help on how to checkout etc.
|
* Display help on how to checkout etc.
|
||||||
@ -434,8 +435,7 @@ class IDF_Views_Source
|
|||||||
$commit = trim($match[2]);
|
$commit = trim($match[2]);
|
||||||
$scm = IDF_Scm::get($request->project);
|
$scm = IDF_Scm::get($request->project);
|
||||||
$base = $request->project->shortname.'-'.$commit;
|
$base = $request->project->shortname.'-'.$commit;
|
||||||
$cmd = $scm->getArchiveCommand($commit, $base.'/');
|
$rep = $scm->getArchiveStream($commit, $base.'/');
|
||||||
$rep = new Pluf_HTTP_Response_CommandPassThru($cmd, 'application/x-zip');
|
|
||||||
$rep->headers['Content-Transfer-Encoding'] = 'binary';
|
$rep->headers['Content-Transfer-Encoding'] = 'binary';
|
||||||
$rep->headers['Content-Disposition'] = 'attachment; filename="'.$base.'.zip"';
|
$rep->headers['Content-Disposition'] = 'attachment; filename="'.$base.'.zip"';
|
||||||
return $rep;
|
return $rep;
|
||||||
|
@ -24,7 +24,8 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td colspan="2"><strong>{$form.f.labels_issue_predefined.labelTag}:</strong><br />
|
<td colspan="2"><strong>{$form.f.labels_issue_predefined.labelTag}:</strong><br />
|
||||||
{if $form.f.labels_issue_predefined.errors}{$form.f.labels_issue_predefined.fieldErrors}{/if}
|
{if $form.f.labels_issue_predefined.errors}{$form.f.labels_issue_predefined.fieldErrors}{/if}
|
||||||
{$form.f.labels_issue_predefined|unsafe}
|
{$form.f.labels_issue_predefined|unsafe}<br />
|
||||||
|
<span class="helptext">{$form.f.labels_issue_predefined.help_text}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
{*
|
{*
|
||||||
# ***** BEGIN LICENSE BLOCK *****
|
# ***** BEGIN LICENSE BLOCK *****
|
||||||
# This file is part of InDefero, an open source project management application.
|
# This file is part of InDefero, an open source project management application.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
{*
|
{*
|
||||||
# ***** BEGIN LICENSE BLOCK *****
|
# ***** BEGIN LICENSE BLOCK *****
|
||||||
# This file is part of InDefero, an open source project management application.
|
# This file is part of InDefero, an open source project management application.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
{*
|
{*
|
||||||
# ***** BEGIN LICENSE BLOCK *****
|
# ***** BEGIN LICENSE BLOCK *****
|
||||||
# This file is part of InDefero, an open source project management application.
|
# This file is part of InDefero, an open source project management application.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
{*
|
{*
|
||||||
# ***** BEGIN LICENSE BLOCK *****
|
# ***** BEGIN LICENSE BLOCK *****
|
||||||
# This file is part of InDefero, an open source project management application.
|
# This file is part of InDefero, an open source project management application.
|
||||||
|
@ -25,6 +25,15 @@
|
|||||||
<span class="helptext">{$form.f.shortdesc.help_text}</span>
|
<span class="helptext">{$form.f.shortdesc.help_text}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{if $project.getConf().getVal('scm') == 'mtn'}
|
||||||
|
<tr class="mtn-form">
|
||||||
|
<th><strong>{$form.f.mtn_master_branch.labelTag}:</strong></th>
|
||||||
|
<td>{if $form.f.mtn_master_branch.errors}{$form.f.mtn_master_branch.fieldErrors}{/if}
|
||||||
|
{$form.f.mtn_master_branch|unsafe}<br />
|
||||||
|
<span class="helptext">{$form.f.mtn_master_branch.help_text}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
<tr>
|
<tr>
|
||||||
<th><strong>{$form.f.owners.labelTag}:</strong></th>
|
<th><strong>{$form.f.owners.labelTag}:</strong></th>
|
||||||
<td>
|
<td>
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
<th><strong>{trans 'Author:'}</strong></th><td>{showuser $rcommit.get_author(), $request, $cobject.author}</td>
|
<th><strong>{trans 'Author:'}</strong></th><td>{showuser $rcommit.get_author(), $request, $cobject.author}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th><strong>{trans 'Branch:'}</strong></th><td>{$cobject.branch}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<th><strong>{trans 'Commit:'}</strong></th><td class="mono"><a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $commit)}" title="{trans 'View corresponding source tree'}">{$cobject.commit}</a></td>
|
<th><strong>{trans 'Commit:'}</strong></th><td class="mono"><a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $commit)}" title="{trans 'View corresponding source tree'}">{$cobject.commit}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
</table>
|
</table>
|
||||||
{aurl 'url', 'IDF_Views_Source::download', array($project.shortname, $commit)}
|
{aurl 'url', 'IDF_Views_Source::download', array($project.shortname, $commit)}
|
||||||
<p class="right soft">
|
<p class="right soft">
|
||||||
{* <a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/package-grey.png'}" alt="{trans 'Archive'}" align="bottom" /></a> <a href="{$url}">{trans 'Download this version'}</a> {trans 'or'} *}
|
<a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/package-grey.png'}" alt="{trans 'Archive'}" align="bottom" /></a> <a href="{$url}">{trans 'Download this version'}</a> {trans 'or'}
|
||||||
<kbd>mtn clone {$project.getSourceAccessUrl($user, $commit)}</kbd> <a href="{url 'IDF_Views_Source::help', array($project.shortname)}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/help.png'}" alt="{trans 'Help'}" /></a>
|
<kbd>mtn clone {$project.getSourceAccessUrl($user, $commit)}</kbd> <a href="{url 'IDF_Views_Source::help', array($project.shortname)}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/help.png'}" alt="{trans 'Help'}" /></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user