Merge branch 'master' of github.com:owncloud/core

This commit is contained in:
Jörn Friedrich Dreyer 2012-08-26 15:05:28 +02:00
commit 39a9a4e73e
363 changed files with 8925 additions and 524 deletions

View file

@ -0,0 +1,29 @@
<?php
/**
* @package SimpleTest
* @subpackage Extensions
*/
/**
* Include this in any file to start coverage, coverage will automatically end
* when process dies.
*/
require_once(dirname(__FILE__) .'/coverage.php');
if (CodeCoverage::isCoverageOn()) {
$coverage = CodeCoverage::getInstance();
$coverage->startCoverage();
register_shutdown_function("stop_coverage");
}
function stop_coverage() {
# hack until i can think of a way to run tests first and w/o exiting
$autorun = function_exists("run_local_tests");
if ($autorun) {
$result = run_local_tests();
}
CodeCoverage::getInstance()->stopCoverage();
if ($autorun) {
exit($result ? 0 : 1);
}
}
?>

View file

@ -0,0 +1,14 @@
<?php
/**
* Close code coverage data collection, next step is to generate report
* @package SimpleTest
* @subpackage Extensions
*/
/**
* include coverage files
*/
require_once(dirname(__FILE__) . '/../coverage.php');
$cc = CodeCoverage::getInstance();
$cc->readSettings();
$cc->writeUntouched();
?>

View file

@ -0,0 +1,31 @@
<?php
/**
* Initialize code coverage data collection, next step is to run your tests
* with ini setting auto_prepend_file=autocoverage.php ...
*
* @package SimpleTest
* @subpackage Extensions
*/
# optional arguments:
# --include=<some filepath regexp> these files should be included coverage report
# --exclude=<come filepath regexp> these files should not be included in coverage report
# --maxdepth=2 when considering which file were not touched, scan directories
#
# Example:
# php-coverage-open.php --include='.*\.php$' --include='.*\.inc$' --exclude='.*/tests/.*'
/**#@+
* include coverage files
*/
require_once(dirname(__FILE__) . '/../coverage_utils.php');
CoverageUtils::requireSqlite();
require_once(dirname(__FILE__) . '/../coverage.php');
/**#@-*/
$cc = new CodeCoverage();
$cc->log = 'coverage.sqlite';
$args = CoverageUtils::parseArguments($_SERVER['argv'], TRUE);
$cc->includes = CoverageUtils::issetOr($args['include[]'], array('.*\.php$'));
$cc->excludes = CoverageUtils::issetOr($args['exclude[]']);
$cc->maxDirectoryDepth = (int)CoverageUtils::issetOr($args['maxdepth'], '1');
$cc->resetLog();
$cc->writeSettings();
?>

View file

@ -0,0 +1,29 @@
<?php
/**
* Generate a code coverage report
*
* @package SimpleTest
* @subpackage Extensions
*/
# optional arguments:
# --reportDir=some/directory the default is ./coverage-report
# --title='My Coverage Report' title the main page of your report
/**#@+
* include coverage files
*/
require_once(dirname(__FILE__) . '/../coverage_utils.php');
require_once(dirname(__FILE__) . '/../coverage.php');
require_once(dirname(__FILE__) . '/../coverage_reporter.php');
/**#@-*/
$cc = CodeCoverage::getInstance();
$cc->readSettings();
$handler = new CoverageDataHandler($cc->log);
$report = new CoverageReporter();
$args = CoverageUtils::parseArguments($_SERVER['argv']);
$report->reportDir = CoverageUtils::issetOr($args['reportDir'], 'coverage-report');
$report->title = CoverageUtils::issetOr($args['title'], "Simpletest Coverage");
$report->coverage = $handler->read();
$report->untouched = $handler->readUntouchedFiles();
$report->generate();
?>

View file

@ -0,0 +1,196 @@
<?php
/**
* @package SimpleTest
* @subpackage Extensions
*/
/**
* load coverage data handle
*/
require_once dirname(__FILE__) . '/coverage_data_handler.php';
/**
* Orchestrates code coverage both in this thread and in subthread under apache
* Assumes this is running on same machine as apache.
* @package SimpleTest
* @subpackage Extensions
*/
class CodeCoverage {
var $log;
var $root;
var $includes;
var $excludes;
var $directoryDepth;
var $maxDirectoryDepth = 20; // reasonable, otherwise arbitrary
var $title = "Code Coverage";
# NOTE: This assumes all code shares the same current working directory.
var $settingsFile = './code-coverage-settings.dat';
static $instance;
function writeUntouched() {
$touched = array_flip($this->getTouchedFiles());
$untouched = array();
$this->getUntouchedFiles($untouched, $touched, '.', '.');
$this->includeUntouchedFiles($untouched);
}
function &getTouchedFiles() {
$handler = new CoverageDataHandler($this->log);
$touched = $handler->getFilenames();
return $touched;
}
function includeUntouchedFiles($untouched) {
$handler = new CoverageDataHandler($this->log);
foreach ($untouched as $file) {
$handler->writeUntouchedFile($file);
}
}
function getUntouchedFiles(&$untouched, $touched, $parentPath, $rootPath, $directoryDepth = 1) {
$parent = opendir($parentPath);
while ($file = readdir($parent)) {
$path = "$parentPath/$file";
if (is_dir($path)) {
if ($file != '.' && $file != '..') {
if ($this->isDirectoryIncluded($path, $directoryDepth)) {
$this->getUntouchedFiles($untouched, $touched, $path, $rootPath, $directoryDepth + 1);
}
}
}
else if ($this->isFileIncluded($path)) {
$relativePath = CoverageDataHandler::ltrim($rootPath .'/', $path);
if (!array_key_exists($relativePath, $touched)) {
$untouched[] = $relativePath;
}
}
}
closedir($parent);
}
function resetLog() {
error_log('reseting log');
$new_file = fopen($this->log, "w");
if (!$new_file) {
throw new Exception("Could not create ". $this->log);
}
fclose($new_file);
if (!chmod($this->log, 0666)) {
throw new Exception("Could not change ownership on file ". $this->log);
}
$handler = new CoverageDataHandler($this->log);
$handler->createSchema();
}
function startCoverage() {
$this->root = getcwd();
if(!extension_loaded("xdebug")) {
throw new Exception("Could not load xdebug extension");
};
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
}
function stopCoverage() {
$cov = xdebug_get_code_coverage();
$this->filter($cov);
$data = new CoverageDataHandler($this->log);
chdir($this->root);
$data->write($cov);
unset($data); // release sqlite connection
xdebug_stop_code_coverage();
// make sure we wind up on same current working directory, otherwise
// coverage handler writer doesn't know what directory to chop off
chdir($this->root);
}
function readSettings() {
if (file_exists($this->settingsFile)) {
$this->setSettings(file_get_contents($this->settingsFile));
} else {
error_log("could not find file ". $this->settingsFile);
}
}
function writeSettings() {
file_put_contents($this->settingsFile, $this->getSettings());
}
function getSettings() {
$data = array(
'log' => realpath($this->log),
'includes' => $this->includes,
'excludes' => $this->excludes);
return serialize($data);
}
function setSettings($settings) {
$data = unserialize($settings);
$this->log = $data['log'];
$this->includes = $data['includes'];
$this->excludes = $data['excludes'];
}
function filter(&$coverage) {
foreach ($coverage as $file => $line) {
if (!$this->isFileIncluded($file)) {
unset($coverage[$file]);
}
}
}
function isFileIncluded($file) {
if (!empty($this->excludes)) {
foreach ($this->excludes as $path) {
if (preg_match('|' . $path . '|', $file)) {
return False;
}
}
}
if (!empty($this->includes)) {
foreach ($this->includes as $path) {
if (preg_match('|' . $path . '|', $file)) {
return True;
}
}
return False;
}
return True;
}
function isDirectoryIncluded($dir, $directoryDepth) {
if ($directoryDepth >= $this->maxDirectoryDepth) {
return false;
}
if (isset($this->excludes)) {
foreach ($this->excludes as $path) {
if (preg_match('|' . $path . '|', $dir)) {
return False;
}
}
}
return True;
}
static function isCoverageOn() {
$coverage = self::getInstance();
$coverage->readSettings();
if (empty($coverage->log) || !file_exists($coverage->log)) {
trigger_error('No coverage log');
return False;
}
return True;
}
static function getInstance() {
if (self::$instance == NULL) {
self::$instance = new CodeCoverage();
self::$instance->readSettings();
}
return self::$instance;
}
}
?>

View file

@ -0,0 +1,98 @@
<?php
/**
* @package SimpleTest
* @subpackage Extensions
*/
/**
* @package SimpleTest
* @subpackage Extensions
*/
class CoverageCalculator {
function coverageByFileVariables($file, $coverage) {
$hnd = fopen($file, 'r');
if ($hnd == null) {
throw new Exception("File $file is missing");
}
$lines = array();
for ($i = 1; !feof($hnd); $i++) {
$line = fgets($hnd);
$lineCoverage = $this->lineCoverageCodeToStyleClass($coverage, $i);
$lines[$i] = array('lineCoverage' => $lineCoverage, 'code' => $line);
}
fclose($hnd);
$var = compact('file', 'lines', 'coverage');
return $var;
}
function lineCoverageCodeToStyleClass($coverage, $line) {
if (!array_key_exists($line, $coverage)) {
return "comment";
}
$code = $coverage[$line];
if (empty($code)) {
return "comment";
}
switch ($code) {
case -1:
return "missed";
case -2:
return "dead";
}
return "covered";
}
function totalLoc($total, $coverage) {
return $total + sizeof($coverage);
}
function lineCoverage($total, $line) {
# NOTE: counting dead code as covered, as it's almost always an executable line
# strange artifact of xdebug or underlying system
return $total + ($line > 0 || $line == -2 ? 1 : 0);
}
function totalCoverage($total, $coverage) {
return $total + array_reduce($coverage, array(&$this, "lineCoverage"));
}
static function reportFilename($filename) {
return preg_replace('|[/\\\\]|', '_', $filename) . '.html';
}
function percentCoverageByFile($coverage, $file, &$results) {
$byFileReport = self::reportFilename($file);
$loc = sizeof($coverage);
if ($loc == 0)
return 0;
$lineCoverage = array_reduce($coverage, array(&$this, "lineCoverage"));
$percentage = 100 * ($lineCoverage / $loc);
$results[0][$file] = array('byFileReport' => $byFileReport, 'percentage' => $percentage);
}
function variables($coverage, $untouched) {
$coverageByFile = array();
array_walk($coverage, array(&$this, "percentCoverageByFile"), array(&$coverageByFile));
$totalLoc = array_reduce($coverage, array(&$this, "totalLoc"));
if ($totalLoc > 0) {
$totalLinesOfCoverage = array_reduce($coverage, array(&$this, "totalCoverage"));
$totalPercentCoverage = 100 * ($totalLinesOfCoverage / $totalLoc);
}
$untouchedPercentageDenominator = sizeof($coverage) + sizeof($untouched);
if ($untouchedPercentageDenominator > 0) {
$filesTouchedPercentage = 100 * sizeof($coverage) / $untouchedPercentageDenominator;
}
$var = compact('coverageByFile', 'totalPercentCoverage', 'totalLoc', 'totalLinesOfCoverage', 'filesTouchedPercentage');
$var['untouched'] = $untouched;
return $var;
}
}
?>

View file

@ -0,0 +1,125 @@
<?php
/**
* @package SimpleTest
* @subpackage Extensions
*/
/**
* @todo which db abstraction layer is this?
*/
require_once 'DB/sqlite.php';
/**
* Persists code coverage data into SQLite database and aggregate data for convienent
* interpretation in report generator. Be sure to not to keep an instance longer
* than you have, otherwise you risk overwriting database edits from another process
* also trying to make updates.
* @package SimpleTest
* @subpackage Extensions
*/
class CoverageDataHandler {
var $db;
function __construct($filename) {
$this->filename = $filename;
$this->db = new SQLiteDatabase($filename);
if (empty($this->db)) {
throw new Exception("Could not create sqlite db ". $filename);
}
}
function createSchema() {
$this->db->queryExec("create table untouched (filename text)");
$this->db->queryExec("create table coverage (name text, coverage text)");
}
function &getFilenames() {
$filenames = array();
$cursor = $this->db->unbufferedQuery("select distinct name from coverage");
while ($row = $cursor->fetch()) {
$filenames[] = $row[0];
}
return $filenames;
}
function write($coverage) {
foreach ($coverage as $file => $lines) {
$coverageStr = serialize($lines);
$relativeFilename = self::ltrim(getcwd() . '/', $file);
$sql = "insert into coverage (name, coverage) values ('$relativeFilename', '$coverageStr')";
# if this fails, check you have write permission
$this->db->queryExec($sql);
}
}
function read() {
$coverage = array_flip($this->getFilenames());
foreach($coverage as $file => $garbage) {
$coverage[$file] = $this->readFile($file);
}
return $coverage;
}
function &readFile($file) {
$sql = "select coverage from coverage where name = '$file'";
$aggregate = array();
$result = $this->db->query($sql);
while ($result->valid()) {
$row = $result->current();
$this->aggregateCoverage($aggregate, unserialize($row[0]));
$result->next();
}
return $aggregate;
}
function aggregateCoverage(&$total, $next) {
foreach ($next as $lineno => $code) {
if (!isset($total[$lineno])) {
$total[$lineno] = $code;
} else {
$total[$lineno] = $this->aggregateCoverageCode($total[$lineno], $code);
}
}
}
function aggregateCoverageCode($code1, $code2) {
switch($code1) {
case -2: return -2;
case -1: return $code2;
default:
switch ($code2) {
case -2: return -2;
case -1: return $code1;
}
}
return $code1 + $code2;
}
static function ltrim($cruft, $pristine) {
if(stripos($pristine, $cruft) === 0) {
return substr($pristine, strlen($cruft));
}
return $pristine;
}
function writeUntouchedFile($file) {
$relativeFile = CoverageDataHandler::ltrim('./', $file);
$sql = "insert into untouched values ('$relativeFile')";
$this->db->queryExec($sql);
}
function &readUntouchedFiles() {
$untouched = array();
$result = $this->db->query("select filename from untouched order by filename");
while ($result->valid()) {
$row = $result->current();
$untouched[] = $row[0];
$result->next();
}
return $untouched;
}
}
?>

View file

@ -0,0 +1,68 @@
<?php
/**
* @package SimpleTest
* @subpackage Extensions
*/
/**#@+
* include additional coverage files
*/
require_once dirname(__FILE__) .'/coverage_calculator.php';
require_once dirname(__FILE__) .'/coverage_utils.php';
require_once dirname(__FILE__) .'/simple_coverage_writer.php';
/**#@-*/
/**
* Take aggregated coverage data and generate reports from it using smarty
* templates
* @package SimpleTest
* @subpackage Extensions
*/
class CoverageReporter {
var $coverage;
var $untouched;
var $reportDir;
var $title = 'Coverage';
var $writer;
var $calculator;
function __construct() {
$this->writer = new SimpleCoverageWriter();
$this->calculator = new CoverageCalculator();
}
function generateSummaryReport($out) {
$variables = $this->calculator->variables($this->coverage, $this->untouched);
$variables['title'] = $this->title;
$report = $this->writer->writeSummary($out, $variables);
fwrite($out, $report);
}
function generate() {
CoverageUtils::mkdir($this->reportDir);
$index = $this->reportDir .'/index.html';
$hnd = fopen($index, 'w');
$this->generateSummaryReport($hnd);
fclose($hnd);
foreach ($this->coverage as $file => $cov) {
$byFile = $this->reportDir .'/'. self::reportFilename($file);
$byFileHnd = fopen($byFile, 'w');
$this->generateCoverageByFile($byFileHnd, $file, $cov);
fclose($byFileHnd);
}
echo "generated report $index\n";
}
function generateCoverageByFile($out, $file, $cov) {
$variables = $this->calculator->coverageByFileVariables($file, $cov);
$variables['title'] = $this->title .' - '. $file;
$this->writer->writeByFile($out, $variables);
}
static function reportFilename($filename) {
return preg_replace('|[/\\\\]|', '_', $filename) . '.html';
}
}
?>

View file

@ -0,0 +1,114 @@
<?php
/**
* @package SimpleTest
* @subpackage Extensions
*/
/**
* @package SimpleTest
* @subpackage Extensions
*/
class CoverageUtils {
static function mkdir($dir) {
if (!file_exists($dir)) {
mkdir($dir, 0777, True);
} else {
if (!is_dir($dir)) {
throw new Exception($dir .' exists as a file, not a directory');
}
}
}
static function requireSqlite() {
if (!self::isPackageClassAvailable('DB/sqlite.php', 'SQLiteDatabase')) {
echo "sqlite library is required to be installed and available in include_path";
exit(1);
}
}
static function isPackageClassAvailable($includeFile, $class) {
@include_once($includeFile);
return class_exists($class);
}
/**
* Parses simple parameters from CLI.
*
* Puts trailing parameters into string array in 'extraArguments'
*
* Example:
* $args = CoverageUtil::parseArguments($_SERVER['argv']);
* if ($args['verbose']) echo "Verbose Mode On\n";
* $files = $args['extraArguments'];
*
* Example CLI:
* --foo=blah -x -h some trailing arguments
*
* if multiValueMode is true
* Example CLI:
* --include=a --include=b --exclude=c
* Then
* $args = CoverageUtil::parseArguments($_SERVER['argv']);
* $args['include[]'] will equal array('a', 'b')
* $args['exclude[]'] will equal array('c')
* $args['exclude'] will equal c
* $args['include'] will equal b NOTE: only keeps last value
*
* @param unknown_type $argv
* @param supportMutliValue - will store 2nd copy of value in an array with key "foo[]"
* @return unknown
*/
static public function parseArguments($argv, $mutliValueMode = False) {
$args = array();
$args['extraArguments'] = array();
array_shift($argv); // scriptname
foreach ($argv as $arg) {
if (ereg('^--([^=]+)=(.*)', $arg, $reg)) {
$args[$reg[1]] = $reg[2];
if ($mutliValueMode) {
self::addItemAsArray($args, $reg[1], $reg[2]);
}
} elseif (ereg('^[-]{1,2}([^[:blank:]]+)', $arg, $reg)) {
$nonnull = '';
$args[$reg[1]] = $nonnull;
if ($mutliValueMode) {
self::addItemAsArray($args, $reg[1], $nonnull);
}
} else {
$args['extraArguments'][] = $arg;
}
}
return $args;
}
/**
* Adds a value as an array of one, or appends to an existing array elements
*
* @param unknown_type $array
* @param unknown_type $item
*/
static function addItemAsArray(&$array, $key, $item) {
$array_key = $key .'[]';
if (array_key_exists($array_key, $array)) {
$array[$array_key][] = $item;
} else {
$array[$array_key] = array($item);
}
}
/**
* isset function with default value
*
* Example: $z = CoverageUtils::issetOr($array[$key], 'no value given')
*
* @param unknown_type $val
* @param unknown_type $default
* @return first value unless value is not set then returns 2nd arg or null if no 2nd arg
*/
static public function issetOr(&$val, $default = null)
{
return isset($val) ? $val : $default;
}
}
?>

View file

@ -0,0 +1,16 @@
<?php
/**
* @package SimpleTest
* @subpackage Extensions
*/
/**
* @package SimpleTest
* @subpackage Extensions
*/
interface CoverageWriter {
function writeSummary($out, $variables);
function writeByFile($out, $variables);
}
?>

View file

@ -0,0 +1,39 @@
<?php
/**
* SimpleCoverageWriter class file
* @package SimpleTest
* @subpackage UnitTester
* @version $Id: unit_tester.php 1882 2009-07-01 14:30:05Z lastcraft $
*/
/**
* base coverage writer class
*/
require_once dirname(__FILE__) .'/coverage_writer.php';
/**
* SimpleCoverageWriter class
* @package SimpleTest
* @subpackage UnitTester
*/
class SimpleCoverageWriter implements CoverageWriter {
function writeSummary($out, $variables) {
extract($variables);
$now = date("F j, Y, g:i a");
ob_start();
include dirname(__FILE__) . '/templates/index.php';
$contents = ob_get_contents();
fwrite ($out, $contents);
ob_end_clean();
}
function writeByFile($out, $variables) {
extract($variables);
ob_start();
include dirname(__FILE__) . '/templates/file.php';
$contents = ob_get_contents();
fwrite ($out, $contents);
ob_end_clean();
}
}
?>

View file

@ -0,0 +1,60 @@
<html>
<head>
<title><?php echo $title ?></title>
</head>
<style type="text/css">
body {
font-family: "Gill Sans MT", "Gill Sans", GillSans, Arial, Helvetica, sans-serif;
}
h1 {
font-size: medium;
}
#code {
border-spacing: 0;
}
.lineNo {
color: #ccc;
}
.code, .lineNo {
white-space: pre;
font-family: monospace;
}
.covered {
color: #090;
}
.missed {
color: #f00;
}
.dead {
color: #00f;
}
.comment {
color: #333;
}
</style>
<body>
<h1 id="title"><?php echo $title ?></h1>
<table id="code">
<tbody>
<?php foreach ($lines as $lineNo => $line) { ?>
<tr>
<td><span class="lineNo"><?php echo $lineNo ?></span></td>
<td><span class="<?php echo $line['lineCoverage'] ?> code"><?php echo htmlentities($line['code']) ?></span></td>
</tr>
<?php } ?>
</tbody>
</table>
<h2>Legend</h2>
<dl>
<dt><span class="missed">Missed</span></dt>
<dd>lines code that <strong>were not</strong> excersized during program execution.</dd>
<dt><span class="covered">Covered</span></dt>
<dd>lines code <strong>were</strong> excersized during program execution.</dd>
<dt><span class="comment">Comment/non executable</span></dt>
<dd>Comment or non-executable line of code.</dd>
<dt><span class="dead">Dead</span></dt>
<dd>lines of code that according to xdebug could not be executed. This is counted as coverage code because
in almost all cases it is code that runnable.</dd>
</dl>
</body>
</html>

View file

@ -0,0 +1,106 @@
<html>
<head>
<title><?php echo $title ?></title>
</head>
<style type="text/css">
h1 {
font-size: medium;
}
body {
font-family: "Gill Sans MT", "Gill Sans", GillSans, Arial, Helvetica,
sans-serif;
}
td.percentage {
text-align: right;
}
caption {
border-bottom: thin solid;
font-weight: bolder;
}
dt {
font-weight: bolder;
}
table {
margin: 1em;
}
</style>
<body>
<h1 id="title"><?php echo $title ?></h1>
<table>
<caption>Summary</caption>
<tbody>
<tr>
<td>Total Coverage (<a href="#total-coverage">?</a>) :</td>
<td class="percentage"><span class="totalPercentCoverage"><?php echo number_format($totalPercentCoverage, 0) ?>%</span></td>
</tr>
<tr>
<td>Total Files Covered (<a href="#total-files-covered">?</a>) :</td>
<td class="percentage"><span class="filesTouchedPercentage"><?php echo number_format($filesTouchedPercentage, 0) ?>%</span></td>
</tr>
<tr>
<td>Report Generation Date :</td>
<td><?php echo $now ?></td>
</tr>
</tbody>
</table>
<table id="covered-files">
<caption>Coverage (<a href="#coverage">?</a>)</caption>
<thead>
<tr>
<th>File</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<?php foreach ($coverageByFile as $file => $coverage) { ?>
<tr>
<td><a class="byFileReportLink" href="<?php echo $coverage['byFileReport'] ?>"><?php echo $file ?></a></td>
<td class="percentage"><span class="percentCoverage"><?php echo number_format($coverage['percentage'], 0) ?>%</span></td>
</tr>
<?php } ?>
</tbody>
</table>
<table>
<caption>Files Not Covered (<a href="#untouched">?</a>)</caption>
<tbody>
<?php foreach ($untouched as $key => $file) { ?>
<tr>
<td><span class="untouchedFile"><?php echo $file ?></span></td>
</tr>
<?php } ?>
</tbody>
</table>
<h2>Glossary</h2>
<dl>
<dt><a name="total-coverage">Total Coverage</a></dt>
<dd>Ratio of all the lines of executable code that were executed to the
lines of code that were not executed. This does not include the files
that were not covered at all.</dd>
<dt><a name="total-files-covered">Total Files Covered</a></dt>
<dd>This is the ratio of the number of files tested, to the number of
files not tested at all.</dd>
<dt><a name="coverage">Coverage</a></dt>
<dd>These files were parsed and loaded by the php interpreter while
running the tests. Percentage is determined by the ratio of number of
lines of code executed to the number of possible executable lines of
code. "dead" lines of code, or code that could not be executed
according to xdebug, are counted as covered because in almost all cases
it is the end of a logical loop.</dd>
<dt><a name="untouched">Files Not Covered</a></dt>
<dd>These files were not loaded by the php interpreter at anytime
during a unit test. You could consider these files having 0% coverage,
but because it is difficult to determine the total coverage unless you
could count the lines for executable code, this is not reflected in the
Total Coverage calculation.</dd>
</dl>
<p>Code coverage generated by <a href="http://www.simpletest.org">SimpleTest</a></p>
</body>
</html>

View file

@ -0,0 +1,65 @@
<?php
require_once(dirname(__FILE__) . '/../../../autorun.php');
class CoverageCalculatorTest extends UnitTestCase {
function skip() {
$this->skipIf(
!file_exists('DB/sqlite.php'),
'The Coverage extension needs to have PEAR installed');
}
function setUp() {
require_once dirname(__FILE__) .'/../coverage_calculator.php';
$this->calc = new CoverageCalculator();
}
function testVariables() {
$coverage = array('file' => array(1,1,1,1));
$untouched = array('missed-file');
$variables = $this->calc->variables($coverage, $untouched);
$this->assertEqual(4, $variables['totalLoc']);
$this->assertEqual(100, $variables['totalPercentCoverage']);
$this->assertEqual(4, $variables['totalLinesOfCoverage']);
$expected = array('file' => array('byFileReport' => 'file.html', 'percentage' => 100));
$this->assertEqual($expected, $variables['coverageByFile']);
$this->assertEqual(50, $variables['filesTouchedPercentage']);
$this->assertEqual($untouched, $variables['untouched']);
}
function testPercentageCoverageByFile() {
$coverage = array(0,0,0,1,1,1);
$results = array();
$this->calc->percentCoverageByFile($coverage, 'file', $results);
$pct = $results[0];
$this->assertEqual(50, $pct['file']['percentage']);
$this->assertEqual('file.html', $pct['file']['byFileReport']);
}
function testTotalLoc() {
$this->assertEqual(13, $this->calc->totalLoc(10, array(1,2,3)));
}
function testLineCoverage() {
$this->assertEqual(10, $this->calc->lineCoverage(10, -1));
$this->assertEqual(10, $this->calc->lineCoverage(10, 0));
$this->assertEqual(11, $this->calc->lineCoverage(10, 1));
}
function testTotalCoverage() {
$this->assertEqual(11, $this->calc->totalCoverage(10, array(-1,1)));
}
static function getAttribute($element, $attribute) {
$a = $element->attributes();
return $a[$attribute];
}
static function dom($stream) {
rewind($stream);
$actual = stream_get_contents($stream);
$html = DOMDocument::loadHTML($actual);
return simplexml_import_dom($html);
}
}
?>

View file

@ -0,0 +1,83 @@
<?php
require_once(dirname(__FILE__) . '/../../../autorun.php');
class CoverageDataHandlerTest extends UnitTestCase {
function skip() {
$this->skipIf(
!file_exists('DB/sqlite.php'),
'The Coverage extension needs to have PEAR installed');
}
function setUp() {
require_once dirname(__FILE__) .'/../coverage_data_handler.php';
}
function testAggregateCoverageCode() {
$handler = new CoverageDataHandler($this->tempdb());
$this->assertEqual(-2, $handler->aggregateCoverageCode(-2, -2));
$this->assertEqual(-2, $handler->aggregateCoverageCode(-2, 10));
$this->assertEqual(-2, $handler->aggregateCoverageCode(10, -2));
$this->assertEqual(-1, $handler->aggregateCoverageCode(-1, -1));
$this->assertEqual(10, $handler->aggregateCoverageCode(-1, 10));
$this->assertEqual(10, $handler->aggregateCoverageCode(10, -1));
$this->assertEqual(20, $handler->aggregateCoverageCode(10, 10));
}
function testSimpleWriteRead() {
$handler = new CoverageDataHandler($this->tempdb());
$handler->createSchema();
$coverage = array(10 => -2, 20 => -1, 30 => 0, 40 => 1);
$handler->write(array('file' => $coverage));
$actual = $handler->readFile('file');
$expected = array(10 => -2, 20 => -1, 30 => 0, 40 => 1);
$this->assertEqual($expected, $actual);
}
function testMultiFileWriteRead() {
$handler = new CoverageDataHandler($this->tempdb());
$handler->createSchema();
$handler->write(array(
'file1' => array(-2, -1, 1),
'file2' => array(-2, -1, 1)
));
$handler->write(array(
'file1' => array(-2, -1, 1)
));
$expected = array(
'file1' => array(-2, -1, 2),
'file2' => array(-2, -1, 1)
);
$actual = $handler->read();
$this->assertEqual($expected, $actual);
}
function testGetfilenames() {
$handler = new CoverageDataHandler($this->tempdb());
$handler->createSchema();
$rawCoverage = array('file0' => array(), 'file1' => array());
$handler->write($rawCoverage);
$actual = $handler->getFilenames();
$this->assertEqual(array('file0', 'file1'), $actual);
}
function testWriteUntouchedFiles() {
$handler = new CoverageDataHandler($this->tempdb());
$handler->createSchema();
$handler->writeUntouchedFile('bluejay');
$handler->writeUntouchedFile('robin');
$this->assertEqual(array('bluejay', 'robin'), $handler->readUntouchedFiles());
}
function testLtrim() {
$this->assertEqual('ber', CoverageDataHandler::ltrim('goo', 'goober'));
$this->assertEqual('some/file', CoverageDataHandler::ltrim('./', './some/file'));
$this->assertEqual('/x/y/z/a/b/c', CoverageDataHandler::ltrim('/a/b/', '/x/y/z/a/b/c'));
}
function tempdb() {
return tempnam(NULL, 'coverage.test.db');
}
}
?>

View file

@ -0,0 +1,22 @@
<?php
require_once(dirname(__FILE__) . '/../../../autorun.php');
class CoverageReporterTest extends UnitTestCase {
function skip() {
$this->skipIf(
!file_exists('DB/sqlite.php'),
'The Coverage extension needs to have PEAR installed');
}
function setUp() {
require_once dirname(__FILE__) .'/../coverage_reporter.php';
new CoverageReporter();
}
function testreportFilename() {
$this->assertEqual("parula.php.html", CoverageReporter::reportFilename("parula.php"));
$this->assertEqual("warbler_parula.php.html", CoverageReporter::reportFilename("warbler/parula.php"));
$this->assertEqual("warbler_parula.php.html", CoverageReporter::reportFilename("warbler\\parula.php"));
}
}
?>

View file

@ -0,0 +1,109 @@
<?php
require_once(dirname(__FILE__) . '/../../../autorun.php');
require_once(dirname(__FILE__) . '/../../../mock_objects.php');
class CodeCoverageTest extends UnitTestCase {
function skip() {
$this->skipIf(
!file_exists('DB/sqlite.php'),
'The Coverage extension needs to have PEAR installed');
}
function setUp() {
require_once dirname(__FILE__) .'/../coverage.php';
}
function testIsFileIncluded() {
$coverage = new CodeCoverage();
$this->assertTrue($coverage->isFileIncluded('aaa'));
$coverage->includes = array('a');
$this->assertTrue($coverage->isFileIncluded('aaa'));
$coverage->includes = array('x');
$this->assertFalse($coverage->isFileIncluded('aaa'));
$coverage->excludes = array('aa');
$this->assertFalse($coverage->isFileIncluded('aaa'));
}
function testIsFileIncludedRegexp() {
$coverage = new CodeCoverage();
$coverage->includes = array('modules/.*\.php$');
$coverage->excludes = array('bad-bunny.php');
$this->assertFalse($coverage->isFileIncluded('modules/a.test'));
$this->assertFalse($coverage->isFileIncluded('modules/bad-bunny.test'));
$this->assertTrue($coverage->isFileIncluded('modules/test.php'));
$this->assertFalse($coverage->isFileIncluded('module-bad/good-bunny.php'));
$this->assertTrue($coverage->isFileIncluded('modules/good-bunny.php'));
}
function testIsDirectoryIncludedPastMaxDepth() {
$coverage = new CodeCoverage();
$coverage->maxDirectoryDepth = 5;
$this->assertTrue($coverage->isDirectoryIncluded('aaa', 1));
$this->assertFalse($coverage->isDirectoryIncluded('aaa', 5));
}
function testIsDirectoryIncluded() {
$coverage = new CodeCoverage();
$this->assertTrue($coverage->isDirectoryIncluded('aaa', 0));
$coverage->excludes = array('b$');
$this->assertTrue($coverage->isDirectoryIncluded('aaa', 0));
$coverage->includes = array('a$'); // includes are ignore, all dirs are included unless excluded
$this->assertTrue($coverage->isDirectoryIncluded('aaa', 0));
$coverage->excludes = array('.*a$');
$this->assertFalse($coverage->isDirectoryIncluded('aaa', 0));
}
function testFilter() {
$coverage = new CodeCoverage();
$data = array('a' => 0, 'b' => 0, 'c' => 0);
$coverage->includes = array('b');
$coverage->filter($data);
$this->assertEqual(array('b' => 0), $data);
}
function testUntouchedFiles() {
$coverage = new CodeCoverage();
$touched = array_flip(array("test/coverage_test.php"));
$actual = array();
$coverage->includes = array('coverage_test\.php$');
$parentDir = realpath(dirname(__FILE__));
$coverage->getUntouchedFiles($actual, $touched, $parentDir, $parentDir);
$this->assertEqual(array("coverage_test.php"), $actual);
}
function testResetLog() {
$coverage = new CodeCoverage();
$coverage->log = tempnam(NULL, 'php.xdebug.coverage.test.');
$coverage->resetLog();
$this->assertTrue(file_exists($coverage->log));
}
function testSettingsSerialization() {
$coverage = new CodeCoverage();
$coverage->log = '/banana/boat';
$coverage->includes = array('apple', 'orange');
$coverage->excludes = array('tomato', 'pea');
$data = $coverage->getSettings();
$this->assertNotNull($data);
$actual = new CodeCoverage();
$actual->setSettings($data);
$this->assertEqual('/banana/boat', $actual->log);
$this->assertEqual(array('apple', 'orange'), $actual->includes);
$this->assertEqual(array('tomato', 'pea'), $actual->excludes);
}
function testSettingsCanBeReadWrittenToDisk() {
$settings_file = 'banana-boat-coverage-settings-test.dat';
$coverage = new CodeCoverage();
$coverage->log = '/banana/boat';
$coverage->settingsFile = $settings_file;
$coverage->writeSettings();
$actual = new CodeCoverage();
$actual->settingsFile = $settings_file;
$actual->readSettings();
$this->assertEqual('/banana/boat', $actual->log);
}
}
?>

View file

@ -0,0 +1,70 @@
<?php
require_once dirname(__FILE__) . '/../../../autorun.php';
class CoverageUtilsTest extends UnitTestCase {
function skip() {
$this->skipIf(
!file_exists('DB/sqlite.php'),
'The Coverage extension needs to have PEAR installed');
}
function setUp() {
require_once dirname(__FILE__) .'/../coverage_utils.php';
}
function testMkdir() {
CoverageUtils::mkdir(dirname(__FILE__));
try {
CoverageUtils::mkdir(__FILE__);
$this->fail("Should give error about cannot create dir of a file");
} catch (Exception $expected) {
}
}
function testIsPackageClassAvailable() {
$coverageSource = dirname(__FILE__) .'/../coverage_calculator.php';
$this->assertTrue(CoverageUtils::isPackageClassAvailable($coverageSource, 'CoverageCalculator'));
$this->assertFalse(CoverageUtils::isPackageClassAvailable($coverageSource, 'BogusCoverage'));
$this->assertFalse(CoverageUtils::isPackageClassAvailable('bogus-file', 'BogusCoverage'));
$this->assertTrue(CoverageUtils::isPackageClassAvailable('bogus-file', 'CoverageUtils'));
}
function testParseArgumentsMultiValue() {
$actual = CoverageUtils::parseArguments(array('scriptname', '--a=b', '--a=c'), True);
$expected = array('extraArguments' => array(), 'a' => 'c', 'a[]' => array('b', 'c'));
$this->assertEqual($expected, $actual);
}
function testParseArguments() {
$actual = CoverageUtils::parseArguments(array('scriptname', '--a=b', '-c', 'xxx'));
$expected = array('a' => 'b', 'c' => '', 'extraArguments' => array('xxx'));
$this->assertEqual($expected, $actual);
}
function testParseDoubleDashNoArguments() {
$actual = CoverageUtils::parseArguments(array('scriptname', '--aa'));
$this->assertTrue(isset($actual['aa']));
}
function testParseHyphenedExtraArguments() {
$actual = CoverageUtils::parseArguments(array('scriptname', '--alpha-beta=b', 'gamma-lambda'));
$expected = array('alpha-beta' => 'b', 'extraArguments' => array('gamma-lambda'));
$this->assertEqual($expected, $actual);
}
function testAddItemAsArray() {
$actual = array();
CoverageUtils::addItemAsArray($actual, 'bird', 'duck');
$this->assertEqual(array('bird[]' => array('duck')), $actual);
CoverageUtils::addItemAsArray(&$actual, 'bird', 'pigeon');
$this->assertEqual(array('bird[]' => array('duck', 'pigeon')), $actual);
}
function testIssetOr() {
$data = array('bird' => 'gull');
$this->assertEqual('lab', CoverageUtils::issetOr($data['dog'], 'lab'));
$this->assertEqual('gull', CoverageUtils::issetOr($data['bird'], 'sparrow'));
}
}
?>

View file

@ -0,0 +1,4 @@
<?php
// sample code
$x = 1 + 2;
if (false) echo "dead";

View file

@ -0,0 +1,69 @@
<?php
require_once(dirname(__FILE__) . '/../../../autorun.php');
class SimpleCoverageWriterTest extends UnitTestCase {
function skip() {
$this->skipIf(
!file_exists('DB/sqlite.php'),
'The Coverage extension needs to have PEAR installed');
}
function setUp() {
require_once dirname(__FILE__) .'/../simple_coverage_writer.php';
require_once dirname(__FILE__) .'/../coverage_calculator.php';
}
function testGenerateSummaryReport() {
$writer = new SimpleCoverageWriter();
$coverage = array('file' => array(0, 1));
$untouched = array('missed-file');
$calc = new CoverageCalculator();
$variables = $calc->variables($coverage, $untouched);
$variables['title'] = 'coverage';
$out = fopen("php://memory", 'w');
$writer->writeSummary($out, $variables);
$dom = self::dom($out);
$totalPercentCoverage = $dom->elements->xpath("//span[@class='totalPercentCoverage']");
$this->assertEqual('50%', (string)$totalPercentCoverage[0]);
$fileLinks = $dom->elements->xpath("//a[@class='byFileReportLink']");
$fileLinkAttr = $fileLinks[0]->attributes();
$this->assertEqual('file.html', $fileLinkAttr['href']);
$this->assertEqual('file', (string)($fileLinks[0]));
$untouchedFile = $dom->elements->xpath("//span[@class='untouchedFile']");
$this->assertEqual('missed-file', (string)$untouchedFile[0]);
}
function testGenerateCoverageByFile() {
$writer = new SimpleCoverageWriter();
$cov = array(3 => 1, 4 => -2); // 2 comments, 1 code, 1 dead (1-based indexes)
$out = fopen("php://memory", 'w');
$file = dirname(__FILE__) .'/sample/code.php';
$calc = new CoverageCalculator();
$variables = $calc->coverageByFileVariables($file, $cov);
$variables['title'] = 'coverage';
$writer->writeByFile($out, $variables);
$dom = self::dom($out);
$cells = $dom->elements->xpath("//table[@id='code']/tbody/tr/td/span");
$this->assertEqual("comment code", self::getAttribute($cells[1], 'class'));
$this->assertEqual("comment code", self::getAttribute($cells[3], 'class'));
$this->assertEqual("covered code", self::getAttribute($cells[5], 'class'));
$this->assertEqual("dead code", self::getAttribute($cells[7], 'class'));
}
static function getAttribute($element, $attribute) {
$a = $element->attributes();
return $a[$attribute];
}
static function dom($stream) {
rewind($stream);
$actual = stream_get_contents($stream);
$html = DOMDocument::loadHTML($actual);
return simplexml_import_dom($html);
}
}
?>

View file

@ -0,0 +1,14 @@
<?php
// $Id: $
require_once(dirname(__FILE__) . '/../../../autorun.php');
class CoverageUnitTests extends TestSuite {
function CoverageUnitTests() {
$this->TestSuite('Coverage Unit tests');
$path = dirname(__FILE__) . '/*_test.php';
foreach(glob($path) as $test) {
$this->addFile($test);
}
}
}
?>

8
README
View file

@ -5,9 +5,9 @@ http://ownCloud.org
Installation instructions: http://owncloud.org/support
Source code: http://gitorious.org/owncloud
Mailing list: http://mail.kde.org/mailman/listinfo/owncloud
IRC channel: http://webchat.freenode.net/?channels=owncloud
Source code: https://github.com/owncloud
Mailing list: https://mail.kde.org/mailman/listinfo/owncloud
IRC channel: https://webchat.freenode.net/?channels=owncloud
Diaspora: https://joindiaspora.com/u/owncloud
Identi.ca: http://identi.ca/owncloud
Identi.ca: https://identi.ca/owncloud

View file

@ -0,0 +1,14 @@
<?php $TRANSLATIONS = array(
"The php-json module is needed by the many applications for inter communications" => "Modulen php-jason blir benyttet til inter kommunikasjon",
"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Modulen php-curl blir brukt til å hente sidetittelen når bokmerke blir lagt til",
"The php-gd module is needed to create thumbnails of your images" => "Modulen php-gd blir benyttet til å lage miniatyr av bildene dine",
"The php-ldap module is needed connect to your ldap server" => "Modulen php-ldap benyttes for å koble til din ldapserver",
"The php-zip module is needed download multiple files at once" => "Modulen php-zup benyttes til å laste ned flere filer på en gang.",
"The php-mb_multibyte module is needed to manage correctly the encoding." => "Modulen php-mb_multibyte benyttes til å håndtere korrekt tegnkoding.",
"The php-ctype module is needed validate data." => "Modulen php-ctype benyttes til å validere data.",
"The php-xml module is needed to share files with webdav." => "Modulen php-xml benyttes til å dele filer med webdav",
"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Direktivet allow_url_fopen i php.ini bør settes til 1 for å kunne hente kunnskapsbasen fra OCS-servere",
"The php-pdo module is needed to store owncloud data into a database." => "Modulen php-pdo benyttes til å lagre ownCloud data i en database.",
"Dependencies status" => "Avhengighetsstatus",
"Used by :" => "Benyttes av:"
);

View file

@ -7,5 +7,6 @@
"Title" => "Tittel",
"Tags" => "Etikett",
"Save bookmark" => "Lagre bokmerke",
"You have no bookmarks" => "Du har ingen bokmerker"
"You have no bookmarks" => "Du har ingen bokmerker",
"Bookmarklet <br />" => "Bokmerke <br />"
);

View file

@ -166,8 +166,6 @@
"Name of new calendar" => "Navn på ny kalender",
"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "En kalender med dette navn findes allerede. Hvis du fortsætter alligevel, vil disse kalendere blive sammenlagt.",
"Import" => "Importer",
"Importing calendar" => "Importerer kalender",
"Calendar imported successfully" => "Kalender importeret korrekt",
"Close Dialog" => "Luk dialog",
"Create a new event" => "Opret en ny begivenhed",
"View an event" => "Vis en begivenhed",

View file

@ -168,13 +168,10 @@
"Take an available name!" => "Wählen Sie einen verfügbaren Namen.",
"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Ein Kalender mit diesem Namen existiert bereits. Sollten Sie fortfahren, werden die beiden Kalender zusammengeführt.",
"Import" => "Importieren",
"Importing calendar" => "Kalender wird importiert.",
"Calendar imported successfully" => "Kalender erfolgreich importiert",
"Close Dialog" => "Dialog schließen",
"Create a new event" => "Neues Ereignis",
"View an event" => "Termin öffnen",
"No categories selected" => "Keine Kategorie ausgewählt",
"Select category" => "Kategorie auswählen",
"of" => "von",
"at" => "um",
"General" => "Allgemein",

View file

@ -86,9 +86,6 @@
"Month" => "Mes",
"List" => "Lista",
"Today" => "Hoxe",
"Calendars" => "Calendarios",
"There was a fail, while parsing the file." => "Produciuse un erro ao procesar o ficheiro",
"Choose active calendars" => "Escolla os calendarios activos",
"Your calendars" => "Os seus calendarios",
"CalDav Link" => "Ligazón CalDav",
"Shared calendars" => "Calendarios compartidos",
@ -139,12 +136,8 @@
"occurrences" => "acontecementos",
"create a new calendar" => "crear un novo calendario",
"Import a calendar file" => "Importar un ficheiro de calendario",
"Please choose the calendar" => "Por favor, seleccione o calendario",
"create a new calendar" => "crear un novo calendario",
"Name of new calendar" => "Nome do novo calendario",
"Import" => "Importar",
"Importing calendar" => "Importar calendario",
"Calendar imported successfully" => "Calendario importado correctamente",
"Close Dialog" => "Pechar diálogo",
"Create a new event" => "Crear un novo evento",
"View an event" => "Ver un evento",
@ -152,8 +145,6 @@
"of" => "de",
"at" => "a",
"Timezone" => "Fuso horario",
"Check always for changes of the timezone" => "Comprobar sempre cambios de fuso horario",
"Timeformat" => "Formato de hora",
"24h" => "24h",
"12h" => "12h",
"Users" => "Usuarios",

View file

@ -136,7 +136,6 @@
"of" => "od",
"at" => "na",
"Timezone" => "Vremenska zona",
"Timeformat" => "Format vremena",
"24h" => "24h",
"12h" => "12h",
"Users" => "Korisnici",

View file

@ -100,7 +100,6 @@
"Nov." => "11月",
"Dec." => "12月",
"All day" => "終日",
"New Calendar" => "新しくカレンダーを作成する",
"Missing fields" => "項目がありません",
"Title" => "タイトル",
"From Date" => "開始日",

View file

@ -111,19 +111,13 @@
"Interval" => "Intervall",
"End" => "Enn",
"occurrences" => "Virkommes",
"Import a calendar file" => "E Kalenner Fichier importéieren",
"Please choose the calendar" => "Wiel den Kalenner aus",
"create a new calendar" => "E neie Kalenner uleeën",
"Import a calendar file" => "E Kalenner Fichier importéieren",
"Name of new calendar" => "Numm vum neie Kalenner",
"Import" => "Import",
"Importing calendar" => "Importéiert Kalenner",
"Calendar imported successfully" => "Kalenner erfollegräich importéiert",
"Close Dialog" => "Dialog zoumaachen",
"Create a new event" => "En Evenement maachen",
"Select category" => "Kategorie auswielen",
"Timezone" => "Zäitzon",
"Timeformat" => "Zäit Format",
"24h" => "24h",
"12h" => "12h",
"Calendar CalDAV syncing address:" => "CalDAV Kalenner Synchronisatioun's Adress:"
"12h" => "12h"
);

View file

@ -113,19 +113,13 @@
"End" => "Pabaiga",
"create a new calendar" => "sukurti naują kalendorių",
"Import a calendar file" => "Importuoti kalendoriaus failą",
"Please choose the calendar" => "Pasirinkite kalendorių",
"create a new calendar" => "sukurti naują kalendorių",
"Name of new calendar" => "Naujo kalendoriaus pavadinimas",
"Import" => "Importuoti",
"Importing calendar" => "Importuojamas kalendorius",
"Calendar imported successfully" => "Kalendorius sėkmingai importuotas",
"Close Dialog" => "Uždaryti",
"Create a new event" => "Sukurti naują įvykį",
"View an event" => "Peržiūrėti įvykį",
"No categories selected" => "Nepasirinktos jokios katagorijos",
"Timezone" => "Laiko juosta",
"Check always for changes of the timezone" => "Visada tikrinti laiko zonos pasikeitimus",
"Timeformat" => "Laiko formatas",
"24h" => "24val",
"12h" => "12val",
"Users" => "Vartotojai",

View file

@ -67,7 +67,6 @@
"Date" => "Dato",
"Cal." => "Kal.",
"All day" => "Hele dagen ",
"New Calendar" => "Ny kalender",
"Missing fields" => "Manglende felt",
"Title" => "Tittel",
"From Date" => "Fra dato",
@ -80,9 +79,6 @@
"Month" => "ned",
"List" => "Liste",
"Today" => "I dag",
"Calendars" => "Kalendre",
"There was a fail, while parsing the file." => "Det oppstod en feil under åpningen av filen.",
"Choose active calendars" => "Velg en aktiv kalender",
"Your calendars" => "Dine kalendere",
"CalDav Link" => "CalDav-lenke",
"Shared calendars" => "Delte kalendere",
@ -133,24 +129,15 @@
"occurrences" => "forekomster",
"create a new calendar" => "Lag en ny kalender",
"Import a calendar file" => "Importer en kalenderfil",
"Please choose the calendar" => "Vennligst velg kalenderen",
"create a new calendar" => "Lag en ny kalender",
"Name of new calendar" => "Navn på ny kalender:",
"Import" => "Importer",
"Importing calendar" => "Importerer kalender",
"Calendar imported successfully" => "Kalenderen ble importert uten feil",
"Close Dialog" => "Lukk dialog",
"Create a new event" => "Opprett en ny hendelse",
"View an event" => "Se på hendelse",
"No categories selected" => "Ingen kategorier valgt",
"Select category" => "Velg kategori",
"Timezone" => "Tidssone",
"Check always for changes of the timezone" => "Se alltid etter endringer i tidssone",
"Timeformat" => "Tidsformat:",
"24h" => "24 t",
"12h" => "12 t",
"First day of the week" => "Ukens første dag",
"Calendar CalDAV syncing address:" => "Kalender CalDAV synkroniseringsadresse",
"Users" => "Brukere",
"select users" => "valgte brukere",
"Editable" => "Redigerbar",

View file

@ -161,8 +161,6 @@
"Please choose a calendar" => "Proszę wybierz kalendarz",
"Name of new calendar" => "Nazwa kalendarza",
"Import" => "Import",
"Importing calendar" => "Importuje kalendarz",
"Calendar imported successfully" => "Zaimportowano kalendarz",
"Close Dialog" => "Zamknij okno",
"Create a new event" => "Tworzenie nowego wydarzenia",
"View an event" => "Zobacz wydarzenie",
@ -170,8 +168,6 @@
"of" => "z",
"at" => "w",
"Timezone" => "Strefa czasowa",
"Check always for changes of the timezone" => "Zawsze sprawdzaj zmiany strefy czasowej",
"Timeformat" => "Format czasu",
"24h" => "24h",
"12h" => "12h",
"more info" => "więcej informacji",

View file

@ -168,8 +168,6 @@
"Take an available name!" => "Escolha um nome disponível!",
"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Já existe um Calendário com esse nome. Se mesmo assim continuar, esses calendários serão fundidos.",
"Import" => "Importar",
"Importing calendar" => "A importar calendário",
"Calendar imported successfully" => "Calendário importado com sucesso",
"Close Dialog" => "Fechar diálogo",
"Create a new event" => "Criar novo evento",
"View an event" => "Ver um evento",

View file

@ -131,12 +131,8 @@
"occurrences" => "repetiții",
"create a new calendar" => "crează un calendar nou",
"Import a calendar file" => "Importă un calendar",
"Please choose the calendar" => "Alegeți calendarul",
"create a new calendar" => "crează un calendar nou",
"Name of new calendar" => "Numele noului calendar",
"Import" => "Importă",
"Importing calendar" => "Importă calendar",
"Calendar imported successfully" => "Calendarul a fost importat cu succes",
"Close Dialog" => "Închide",
"Create a new event" => "Crează un eveniment nou",
"View an event" => "Vizualizează un eveniment",
@ -144,8 +140,6 @@
"of" => "din",
"at" => "la",
"Timezone" => "Fus orar",
"Check always for changes of the timezone" => "Verifică mereu pentru schimbări ale fusului orar",
"Timeformat" => "Forma de afișare a orei",
"24h" => "24h",
"12h" => "12h",
"Users" => "Utilizatori",

View file

@ -168,8 +168,6 @@
"Take an available name!" => "Возьмите разрешенное имя!",
"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Календарь с таким именем уже существует. Если вы продолжите, одноименный календарь будет удален.",
"Import" => "Импортировать",
"Importing calendar" => "Импортируется календарь",
"Calendar imported successfully" => "Календарь успешно импортирован",
"Close Dialog" => "Закрыть Сообщение",
"Create a new event" => "Создать новое событие",
"View an event" => "Показать событие",

View file

@ -167,8 +167,6 @@
"Take an available name!" => "Ta ett ledigt namn!",
"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "En kalender med detta namn finns redan. Om du fortsätter ändå så kommer dessa kalendrar att slås samman.",
"Import" => "Importera",
"Importing calendar" => "Importerar kalender",
"Calendar imported successfully" => "Kalender importerades utan problem",
"Close Dialog" => "Stäng ",
"Create a new event" => "Skapa en ny händelse",
"View an event" => "Visa en händelse",
@ -176,8 +174,6 @@
"of" => "av",
"at" => "",
"Timezone" => "Tidszon",
"Check always for changes of the timezone" => "Kontrollera alltid ändringar i tidszon.",
"Timeformat" => "Tidsformat",
"24h" => "24h",
"12h" => "12h",
"Cache" => "Cache",

View file

@ -82,9 +82,6 @@
"Month" => "",
"List" => "列表",
"Today" => "今天",
"Calendars" => "日历",
"There was a fail, while parsing the file." => "解析文件失败",
"Choose active calendars" => "选择活动日历",
"Your calendars" => "您的日历",
"CalDav Link" => "CalDav 链接",
"Shared calendars" => "共享的日历",
@ -135,12 +132,8 @@
"occurrences" => "",
"create a new calendar" => "创建新日历",
"Import a calendar file" => "导入日历文件",
"Please choose the calendar" => "请选择日历",
"create a new calendar" => "创建新日历",
"Name of new calendar" => "新日历名称",
"Import" => "导入",
"Importing calendar" => "导入日历",
"Calendar imported successfully" => "导入日历成功",
"Close Dialog" => "关闭对话框",
"Create a new event" => "创建新事件",
"View an event" => "查看事件",
@ -148,12 +141,8 @@
"of" => "",
"at" => "",
"Timezone" => "时区",
"Check always for changes of the timezone" => "选中则总是按照时区变化",
"Timeformat" => "时间格式",
"24h" => "24小时",
"12h" => "12小时",
"First day of the week" => "每周的第一天",
"Calendar CalDAV syncing address:" => "日历CalDAV 同步地址:",
"Users" => "用户",
"select users" => "选中用户",
"Editable" => "可编辑",

View file

@ -131,12 +131,8 @@
"occurrences" => "事件",
"create a new calendar" => "建立新日曆",
"Import a calendar file" => "匯入日曆檔案",
"Please choose the calendar" => "請選擇日曆",
"create a new calendar" => "建立新日曆",
"Name of new calendar" => "新日曆名稱",
"Import" => "匯入",
"Importing calendar" => "匯入日曆",
"Calendar imported successfully" => "已成功匯入日曆",
"Close Dialog" => "關閉對話",
"Create a new event" => "建立一個新事件",
"View an event" => "觀看一個活動",
@ -144,11 +140,8 @@
"of" => "",
"at" => "",
"Timezone" => "時區",
"Check always for changes of the timezone" => "總是檢查是否變更了時區",
"Timeformat" => "日期格式",
"24h" => "24小時制",
"12h" => "12小時制",
"Calendar CalDAV syncing address:" => "CalDAV 的日曆同步地址:",
"Users" => "使用者",
"select users" => "選擇使用者",
"Editable" => "可編輯",

View file

@ -206,12 +206,6 @@
"create a new addressbook" => "crea una llibreta d'adreces nova",
"Name of new addressbook" => "Nom de la nova llibreta d'adreces",
"Importing contacts" => "S'estan important contactes",
"Contacts imported successfully" => "Els contactes s'han importat correctament",
"Close Dialog" => "Tanca el diàleg",
"Import Addressbook" => "Importa la llibreta d'adreces",
"Select address book to import to:" => "Seleccioneu la llibreta d'adreces a la que voleu importar:",
"Drop a VCF file to import contacts." => "Elimina un fitxer VCF per importar contactes.",
"Select from HD" => "Selecciona de HD",
"You have no contacts in your addressbook." => "No teniu contactes a la llibreta d'adreces.",
"Add contact" => "Afegeix un contacte",
"Select Address Books" => "Selecccioneu llibretes d'adreces",

View file

@ -206,12 +206,6 @@
"create a new addressbook" => "vytvořit nový adresář",
"Name of new addressbook" => "Jméno nového adresáře",
"Importing contacts" => "Importování kontaktů",
"Contacts imported successfully" => "Kontakty úspěšně importovány",
"Close Dialog" => "Zavírací dialog",
"Import Addressbook" => "Importovat adresář",
"Select address book to import to:" => "Vyberte adresář do kterého chcete importovat:",
"Drop a VCF file to import contacts." => "Pro import kontaktů sem přetáhněte soubor VCF",
"Select from HD" => "Vybrat z disku",
"You have no contacts in your addressbook." => "Nemáte žádné kontakty v adresáři.",
"Add contact" => "Přidat kontakt",
"Select Address Books" => "Vybrat Adresář",

View file

@ -206,12 +206,6 @@
"create a new addressbook" => "Δημιουργία νέου βιβλίου διευθύνσεων",
"Name of new addressbook" => "Όνομα νέου βιβλίου διευθύνσεων",
"Importing contacts" => "Εισαγωγή επαφών",
"Contacts imported successfully" => "Οι επαφές εισήχθησαν επιτυχώς",
"Close Dialog" => "Κλείσιμο διαλόγου",
"Import Addressbook" => "Εισαγωγή βιβλίου διευθύνσεων",
"Select address book to import to:" => "Επέλεξε σε ποιο βιβλίο διευθύνσεων για εισαγωγή:",
"Drop a VCF file to import contacts." => "Εισάγεται ένα VCF αρχείο για εισαγωγή επαφών",
"Select from HD" => "Επιλογή από HD",
"You have no contacts in your addressbook." => "Δεν έχεις επαφές στο βιβλίο διευθύνσεων",
"Add contact" => "Προσθήκη επαφής",
"Select Address Books" => "Επέλεξε βιβλίο διευθύνσεων",

View file

@ -188,12 +188,6 @@
"create a new addressbook" => "crear una nueva agenda",
"Name of new addressbook" => "Nombre de la nueva agenda",
"Importing contacts" => "Importando contactos",
"Contacts imported successfully" => "Contactos importados correctamente",
"Close Dialog" => "Cerrar Diálogo",
"Import Addressbook" => "Importar agenda",
"Select address book to import to:" => "Selecciona una agenda para importar a:",
"Drop a VCF file to import contacts." => "Suelta un archivo VCF para importar contactos.",
"Select from HD" => "Seleccionar del disco duro",
"You have no contacts in your addressbook." => "No hay contactos en tu agenda.",
"Add contact" => "Añadir contacto",
"Enter name" => "Introducir nombre",

View file

@ -141,12 +141,6 @@
"create a new addressbook" => "loo uus aadressiraamat",
"Name of new addressbook" => "Uue aadressiraamatu nimi",
"Importing contacts" => "Kontaktide importimine",
"Contacts imported successfully" => "Kontaktid on imporditud",
"Close Dialog" => "Sulge dialoog",
"Import Addressbook" => "Impordi aadressiraamat",
"Select address book to import to:" => "Vali aadressiraamat, millesse importida:",
"Drop a VCF file to import contacts." => "Lohista siia VCF-fail, millest kontakte importida.",
"Select from HD" => "Vali kõvakettalt",
"You have no contacts in your addressbook." => "Sinu aadressiraamatus pole ühtegi kontakti.",
"Add contact" => "Lisa kontakt",
"CardDAV syncing addresses" => "CardDAV sünkroniseerimise aadressid",

View file

@ -152,12 +152,6 @@
"create a new addressbook" => "sortu helbide liburu berria",
"Name of new addressbook" => "Helbide liburuaren izena",
"Importing contacts" => "Kontaktuak inportatzen",
"Contacts imported successfully" => "Kontaktuak ongi inportatu dira",
"Close Dialog" => "Dialogoa itxi",
"Import Addressbook" => "Inporatu helbide liburua",
"Select address book to import to:" => "Hautau helburuko helbide liburua:",
"Drop a VCF file to import contacts." => "Askatu VCF fitxategia kontaktuak inportatzeko.",
"Select from HD" => "Hautatu disko gogorretik",
"You have no contacts in your addressbook." => "Ez duzu kontakturik zure helbide liburuan.",
"Add contact" => "Gehitu kontaktua",
"Select Address Books" => "Hautatu helbide-liburuak",

View file

@ -144,12 +144,6 @@
"create a new addressbook" => "یک کتابچه نشانی بسازید",
"Name of new addressbook" => "نام کتابچه نشانی جدید",
"Importing contacts" => "وارد کردن اشخاص",
"Contacts imported successfully" => "اشخاص با موفقیت افزوده شدند",
"Close Dialog" => "بستن دیالوگ",
"Import Addressbook" => "وارد کردن کتابچه نشانی",
"Select address book to import to:" => "یک کتابچه نشانی انتخاب کنید تا وارد شود",
"Drop a VCF file to import contacts." => "یک پرونده VCF را به اینجا بکشید تا اشخاص افزوده شوند",
"Select from HD" => "انتخاب از دیسک سخت",
"You have no contacts in your addressbook." => "شماهیچ شخصی در کتابچه نشانی خود ندارید",
"Add contact" => "افزودن اطلاعات شخص مورد نظر",
"CardDAV syncing addresses" => "CardDAV syncing addresses ",

View file

@ -206,12 +206,6 @@
"create a new addressbook" => "Créer un nouveau carnet d'adresses",
"Name of new addressbook" => "Nom du nouveau carnet d'adresses",
"Importing contacts" => "Importation des contacts",
"Contacts imported successfully" => "Contacts importés avec succes",
"Close Dialog" => "Fermer la boite de dialogue",
"Import Addressbook" => "Importer un carnet d'adresses.",
"Select address book to import to:" => "Selectionner le carnet d'adresses à importer vers:",
"Drop a VCF file to import contacts." => "Glisser un fichier VCF pour importer des contacts.",
"Select from HD" => "Selectionner depuis le disque dur",
"You have no contacts in your addressbook." => "Il n'y a pas de contact dans votre carnet d'adresses.",
"Add contact" => "Ajouter un contact",
"Select Address Books" => "Choix du carnet d'adresses",

View file

@ -70,10 +70,6 @@
"Please choose the addressbook" => "Per favor selige le adressario",
"create a new addressbook" => "Crear un nove adressario",
"Name of new addressbook" => "Nomine del nove gruppo:",
"Import" => "Importar",
"Contacts imported successfully" => "Contactos importate con successo.",
"Close Dialog" => "Clauder dialogo",
"Import Addressbook" => "Importar adressario.",
"Add contact" => "Adder adressario",
"more info" => "plus info",
"iOS/OS X" => "iOS/OS X",

View file

@ -206,12 +206,6 @@
"create a new addressbook" => "crea una nuova rubrica",
"Name of new addressbook" => "Nome della nuova rubrica",
"Importing contacts" => "Importazione contatti",
"Contacts imported successfully" => "Contatti importati correttamente",
"Close Dialog" => "Chiudi finestra",
"Import Addressbook" => "Importa rubrica",
"Select address book to import to:" => "Seleziona la rubrica di destinazione:",
"Drop a VCF file to import contacts." => "Rilascia un file VCF per importare i contatti.",
"Select from HD" => "Seleziona da disco",
"You have no contacts in your addressbook." => "Non hai contatti nella rubrica.",
"Add contact" => "Aggiungi contatto",
"Select Address Books" => "Seleziona rubriche",

View file

@ -144,12 +144,6 @@
"create a new addressbook" => "креирај нов адресар",
"Name of new addressbook" => "Име на новиот адресар",
"Importing contacts" => "Внесување контакти",
"Contacts imported successfully" => "Контаките беа внесени успешно",
"Close Dialog" => "Дијалог за затварање",
"Import Addressbook" => "Внеси адресар",
"Select address book to import to:" => "Изберете адресар да се внесе:",
"Drop a VCF file to import contacts." => "Довлечкај VCF датотека да се внесат контакти.",
"Select from HD" => "Изберете од хард диск",
"You have no contacts in your addressbook." => "Немате контакти во Вашиот адресар.",
"Add contact" => "Додади контакт",
"CardDAV syncing addresses" => "Адреса за синхронизација со CardDAV",

View file

@ -116,13 +116,6 @@
"Ph.D." => "Stipendiat",
"Jr." => "Jr.",
"Sn." => "Sr.",
"New Addressbook" => "Ny adressebok",
"Edit Addressbook" => "Endre adressebok",
"Displayname" => "Visningsnavn",
"Active" => "Aktiv",
"Save" => "Lagre",
"Submit" => "Send inn",
"Cancel" => "Avbryt",
"Import a contacts file" => "Importer en fil med kontakter.",
"Please choose the addressbook" => "Vennligst velg adressebok",
"create a new addressbook" => "Lag ny adressebok",

View file

@ -197,12 +197,6 @@
"create a new addressbook" => "Maak een nieuw adresboek",
"Name of new addressbook" => "Naam van nieuw adresboek",
"Importing contacts" => "Importeren van contacten",
"Contacts imported successfully" => "Contacten zijn geïmporteerd",
"Close Dialog" => "Sluit venster",
"Import Addressbook" => "Importeer adresboek",
"Select address book to import to:" => "Selecteer adresboek voor import:",
"Drop a VCF file to import contacts." => "Sleep een VCF bestand om de contacten te importeren.",
"Select from HD" => "Selecteer van schijf",
"You have no contacts in your addressbook." => "Je hebt geen contacten in je adresboek",
"Add contact" => "Contactpersoon toevoegen",
"Select Address Books" => "Selecteer adresboeken",

View file

@ -163,12 +163,6 @@
"create a new addressbook" => "vytvoriť nový adresár",
"Name of new addressbook" => "Meno nového adresára",
"Importing contacts" => "Importovanie kontaktov",
"Contacts imported successfully" => "Kontakty úspešne importované",
"Close Dialog" => "Zatvoriť ponuku",
"Import Addressbook" => "Importovanie adresára",
"Select address book to import to:" => "Vyberte adresár, do ktorého chcete importovať:",
"Drop a VCF file to import contacts." => "Pretiahnite VCF súbor pre import kontaktov.",
"Select from HD" => "Vyberte z pevného disku",
"You have no contacts in your addressbook." => "Nemáte žiadne kontakty v adresári.",
"Add contact" => "Pridať kontakt",
"Enter name" => "Zadaj meno",

View file

@ -206,12 +206,6 @@
"create a new addressbook" => "Ustvari nov imenik",
"Name of new addressbook" => "Ime novega imenika",
"Importing contacts" => "Uvažam stike",
"Contacts imported successfully" => "Stiki so bili uspešno uvoženi",
"Close Dialog" => "Zapri dialog",
"Import Addressbook" => "Uvozi imenik",
"Select address book to import to:" => "Izberite imenik v katerega boste uvažali:",
"Drop a VCF file to import contacts." => "Za uvoz stikov spustite VCF datoteko tukaj.",
"Select from HD" => "Izberi iz HD",
"You have no contacts in your addressbook." => "V vašem imeniku ni stikov.",
"Add contact" => "Dodaj stik",
"Select Address Books" => "Izberite adresarje",

View file

@ -184,12 +184,6 @@
"create a new addressbook" => "สร้างสมุดบันทึกที่อยู่ใหม่",
"Name of new addressbook" => "กำหนดชื่อของสมุดที่อยู่ที่สร้างใหม่",
"Importing contacts" => "นำเข้าข้อมูลการติดต่อ",
"Contacts imported successfully" => "ข้อมูลการติดต่อถูกนำเข้าข้อมูลเรียบร้อยแล้ว",
"Close Dialog" => "ปิดกล่องข้อความ",
"Import Addressbook" => "นำเข้าข้อมูลสมุดบันทึกที่อยู่",
"Select address book to import to:" => "เลือกสมุดบันทึกที่อยู่ที่ต้องการนำเข้า:",
"Drop a VCF file to import contacts." => "วางไฟล์ VCF ที่ต้องการนำเข้าข้อมูลการติดต่อ",
"Select from HD" => "เลือกจากฮาร์ดดิส",
"You have no contacts in your addressbook." => "คุณยังไม่มีข้อมูลการติดต่อใดๆในสมุดบันทึกที่อยู่ของคุณ",
"Add contact" => "เพิ่มชื่อผู้ติดต่อ",
"Select Address Books" => "เลือกสมุดบันทึกที่อยู่",

View file

@ -176,12 +176,6 @@
"create a new addressbook" => "Yeni adres defteri oluştur",
"Name of new addressbook" => "Yeni adres defteri için isim",
"Importing contacts" => "Bağlantıları içe aktar",
"Contacts imported successfully" => "Bağlantılar başarıyla içe aktarıldı",
"Close Dialog" => "Diyaloğu kapat",
"Import Addressbook" => "Adres defterini içeri aktar",
"Select address book to import to:" => "İçe aktarılacak adres defterini seçin:",
"Drop a VCF file to import contacts." => "Bağlantıları içe aktarmak için bir VCF dosyası bırakın.",
"Select from HD" => "HD'den seç",
"You have no contacts in your addressbook." => "Adres defterinizde hiç bağlantı yok.",
"Add contact" => "Bağlatı ekle",
"Select Address Books" => "Adres deftelerini seçiniz",

View file

@ -6,10 +6,6 @@
"There was an error adding the contact." => "添加通訊錄發生錯誤",
"Cannot add empty property." => "不可添加空白內容",
"At least one of the address fields has to be filled out." => "至少必須填寫一欄地址",
"Error adding contact property." => "添加通訊錄內容中發生錯誤",
"No ID provided" => "未提供 ID",
"Error adding addressbook." => "添加電話簿中發生錯誤",
"Error activating addressbook." => "啟用電話簿中發生錯誤",
"Information about vCard is incorrect. Please reload the page." => "有關 vCard 的資訊不正確,請重新載入此頁。",
"Missing ID" => "遺失ID",
"No file was uploaded" => "沒有已上傳的檔案",

View file

@ -17,9 +17,6 @@
"Nothing in here. Upload something!" => "لا يوجد شيء هنا. إرفع بعض الملفات!",
"Name" => "الاسم",
"Download" => "تحميل",
"Size" => "حجم",
"Modified" => "معدل",
"Delete" => "محذوف",
"Upload too large" => "حجم الترفيع أعلى من المسموح",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "حجم الملفات التي تريد ترفيعها أعلى من المسموح على الخادم."
);

View file

@ -28,9 +28,6 @@
"Name" => "Име",
"Share" => "Споделяне",
"Download" => "Изтегляне",
"Size" => "Размер",
"Modified" => "Променено",
"Delete" => "Изтриване",
"Upload too large" => "Файлът е прекалено голям",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Файловете които се опитвате да качите са по-големи от позволеното за сървъра.",
"Files are being scanned, please wait." => "Файловете се претърсват, изчакайте."

View file

@ -44,10 +44,6 @@
"Name" => "Nom",
"Share" => "Comparteix",
"Download" => "Baixa",
"Size" => "Mida",
"Modified" => "Modificat",
"Delete all" => "Esborra-ho tot",
"Delete" => "Suprimeix",
"Upload too large" => "La pujada és massa gran",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Els fitxers que esteu intentant pujar excedeixen la mida màxima de pujada del servidor",
"Files are being scanned, please wait." => "S'estan escanejant els fitxers, espereu",

View file

@ -44,10 +44,6 @@
"Name" => "Název",
"Share" => "Sdílet",
"Download" => "Stáhnout",
"Size" => "Velikost",
"Modified" => "Změněno",
"Delete all" => "Smazat vše",
"Delete" => "Vymazat",
"Upload too large" => "Příliš velký soubor",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Soubory, které se snažíte uložit, překračují maximální velikosti uploadu na tomto serveru.",
"Files are being scanned, please wait." => "Soubory se prohledávají, prosím čekejte.",

View file

@ -44,9 +44,6 @@
"Name" => "Navn",
"Share" => "Del",
"Download" => "Download",
"Size" => "Størrelse",
"Modified" => "Ændret",
"Delete" => "Slet",
"Upload too large" => "Upload for stor",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filerne, du prøver at uploade, er større end den maksimale størrelse for fil-upload på denne server.",
"Files are being scanned, please wait." => "Filerne bliver indlæst, vent venligst.",

View file

@ -44,10 +44,6 @@
"Name" => "Name",
"Share" => "Teilen",
"Download" => "Herunterladen",
"Size" => "Größe",
"Modified" => "Bearbeitet",
"Delete all" => "Alle löschen",
"Delete" => "Löschen",
"Upload too large" => "Upload zu groß",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server.",
"Files are being scanned, please wait." => "Dateien werden gescannt, bitte warten.",

View file

@ -44,10 +44,6 @@
"Name" => "Όνομα",
"Share" => "Διαμοίρασε",
"Download" => "Λήψη",
"Size" => "Μέγεθος",
"Modified" => "Τροποποιήθηκε",
"Delete all" => "Διαγραφή όλων",
"Delete" => "Διαγραφή",
"Upload too large" => "Πολύ μεγάλο το αρχείο προς μεταφόρτωση",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Τα αρχεία που προσπαθείτε να ανεβάσετε υπερβαίνουν το μέγιστο μέγεθος μεταφόρτωσης αρχείων σε αυτόν το διακομιστή.",
"Files are being scanned, please wait." => "Τα αρχεία ανιχνεύονται, παρακαλώ περιμένετε",

View file

@ -44,10 +44,6 @@
"Name" => "Nomo",
"Share" => "Kunhavigi",
"Download" => "Elŝuti",
"Size" => "Grando",
"Modified" => "Modifita",
"Delete all" => "Forigi ĉion",
"Delete" => "Forigi",
"Upload too large" => "Elŝuto tro larĝa",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "La dosieroj, kiujn vi provas alŝuti, transpasas la maksimuman grandon por dosieralŝutoj en ĉi tiu servilo.",
"Files are being scanned, please wait." => "Dosieroj estas skanataj, bonvolu atendi.",

View file

@ -44,10 +44,6 @@
"Name" => "Nombre",
"Share" => "Compartir",
"Download" => "Descargar",
"Size" => "Tamaño",
"Modified" => "Modificado",
"Delete all" => "Eliminar todo",
"Delete" => "Eliminado",
"Upload too large" => "El archivo es demasiado grande",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los archivos que estás intentando subir sobrepasan el tamaño máximo permitido por este servidor.",
"Files are being scanned, please wait." => "Se están escaneando los archivos, por favor espere.",

View file

@ -37,10 +37,6 @@
"Name" => "Nimi",
"Share" => "Jaga",
"Download" => "Lae alla",
"Size" => "Suurus",
"Modified" => "Muudetud",
"Delete all" => "Kustuta kõik",
"Delete" => "Kustuta",
"Upload too large" => "Üleslaadimine on liiga suur",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Failid, mida sa proovid üles laadida, ületab serveri poolt üleslaetavatele failidele määratud maksimaalse suuruse.",
"Files are being scanned, please wait." => "Faile skannitakse, palun oota",

View file

@ -44,10 +44,6 @@
"Name" => "Izena",
"Share" => "Elkarbanatu",
"Download" => "Deskargatu",
"Size" => "Tamaina",
"Modified" => "Aldatuta",
"Delete all" => "Ezabatu dena",
"Delete" => "Ezabatu",
"Upload too large" => "Igotakoa handiegia da",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Igotzen saiatzen ari zaren fitxategiak zerbitzari honek igotzeko onartzen duena baino handiagoak dira.",
"Files are being scanned, please wait." => "Fitxategiak eskaneatzen ari da, itxoin mezedez.",

View file

@ -44,10 +44,6 @@
"Name" => "نام",
"Share" => "به اشتراک گذاری",
"Download" => "بارگیری",
"Size" => "اندازه",
"Modified" => "تغییر یافته",
"Delete all" => "پاک کردن همه",
"Delete" => "پاک کردن",
"Upload too large" => "حجم بارگذاری بسیار زیاد است",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "فایلها بیش از حد تعیین شده در این سرور هستند\nمترجم:با تغییر فایل php,ini میتوان این محدودیت را برطرف کرد",
"Files are being scanned, please wait." => "پرونده ها در حال بازرسی هستند لطفا صبر کنید",

View file

@ -44,10 +44,6 @@
"Name" => "Nom",
"Share" => "Partager",
"Download" => "Téléchargement",
"Size" => "Taille",
"Modified" => "Modifié",
"Delete all" => "Supprimer tout",
"Delete" => "Supprimer",
"Upload too large" => "Fichier trop volumineux",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Les fichiers que vous essayez d'envoyer dépassent la taille maximale permise par ce serveur.",
"Files are being scanned, please wait." => "Les fichiers sont analysés, patientez svp.",

View file

@ -44,9 +44,6 @@
"Name" => "Nome",
"Share" => "Compartir",
"Download" => "Descargar",
"Size" => "Tamaño",
"Modified" => "Modificado",
"Delete" => "Eliminar",
"Upload too large" => "Envío demasiado grande",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiros que trata de subir superan o tamaño máximo permitido neste servidor",
"Files are being scanned, please wait." => "Estanse analizando os ficheiros, espere por favor.",

View file

@ -37,9 +37,6 @@
"Name" => "שם",
"Share" => "שיתוף",
"Download" => "הורדה",
"Size" => "גודל",
"Modified" => "זמן שינוי",
"Delete" => "מחיקה",
"Upload too large" => "העלאה גדולה מידי",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "הקבצים שניסית להעלות חרגו מהגודל המקסימלי להעלאת קבצים על שרת זה.",
"Files are being scanned, please wait." => "הקבצים נסרקים, נא להמתין.",

View file

@ -37,9 +37,6 @@
"Name" => "Naziv",
"Share" => "podjeli",
"Download" => "Preuzmi",
"Size" => "Veličina",
"Modified" => "Zadnja promjena",
"Delete" => "Briši",
"Upload too large" => "Prijenos je preobiman",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteke koje pokušavate prenijeti prelaze maksimalnu veličinu za prijenos datoteka na ovom poslužitelju.",
"Files are being scanned, please wait." => "Datoteke se skeniraju, molimo pričekajte.",

View file

@ -44,10 +44,6 @@
"Name" => "Név",
"Share" => "Megosztás",
"Download" => "Letöltés",
"Size" => "Méret",
"Modified" => "Módosítva",
"Delete all" => "Mindent töröl",
"Delete" => "Törlés",
"Upload too large" => "Feltöltés túl nagy",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "A fájlokat amit próbálsz feltölteni meghaladta a legnagyobb fájlméretet ezen a szerveren.",
"Files are being scanned, please wait." => "File-ok vizsgálata, kis türelmet",

View file

@ -13,8 +13,5 @@
"Nothing in here. Upload something!" => "Nihil hic. Incarga alcun cosa!",
"Name" => "Nomine",
"Download" => "Discargar",
"Size" => "Dimension",
"Modified" => "Modificate",
"Delete" => "Deler",
"Upload too large" => "Incargamento troppo longe"
);

View file

@ -25,9 +25,6 @@
"Name" => "Nama",
"Share" => "Bagikan",
"Download" => "Unduh",
"Size" => "Ukuran",
"Modified" => "Dimodifikasi",
"Delete" => "Hapus",
"Upload too large" => "Unggahan terlalu besar",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Berkas yang anda coba unggah melebihi ukuran maksimum untuk pengunggahan berkas di server ini.",
"Files are being scanned, please wait." => "Berkas sedang dipindai, silahkan tunggu.",

View file

@ -44,10 +44,6 @@
"Name" => "Nome",
"Share" => "Condividi",
"Download" => "Scarica",
"Size" => "Dimensione",
"Modified" => "Modificato",
"Delete all" => "Elimina tutto",
"Delete" => "Elimina",
"Upload too large" => "Il file caricato è troppo grande",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "I file che stai provando a caricare superano la dimensione massima consentita su questo server.",
"Files are being scanned, please wait." => "Scansione dei file in corso, attendi",

View file

@ -44,9 +44,6 @@
"Name" => "名前",
"Share" => "共有",
"Download" => "ダウンロード",
"Size" => "サイズ",
"Modified" => "更新日時",
"Delete" => "削除",
"Upload too large" => "ファイルサイズが大きすぎます",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "アップロードしようとしているファイルは、サーバで規定された最大サイズを超えています。",
"Files are being scanned, please wait." => "ファイルをスキャンしています、しばらくお待ちください。",

View file

@ -36,10 +36,6 @@
"Name" => "이름",
"Share" => "공유",
"Download" => "다운로드",
"Size" => "크기",
"Modified" => "수정됨",
"Delete all" => "모두 삭제",
"Delete" => "삭제",
"Upload too large" => "업로드 용량 초과",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "이 파일이 서버에서 허용하는 최대 업로드 가능 용량보다 큽니다.",
"Files are being scanned, please wait." => "파일을 검색중입니다, 기다려 주십시오.",

View file

@ -27,9 +27,6 @@
"Name" => "Numm",
"Share" => "Share",
"Download" => "Eroflueden",
"Size" => "Gréisst",
"Modified" => "Geännert",
"Delete" => "Läschen",
"Upload too large" => "Upload ze grouss",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Déi Dateien déi Dir probéiert erop ze lueden sinn méi grouss wei déi Maximal Gréisst déi op dësem Server erlaabt ass.",
"Files are being scanned, please wait." => "Fichieren gi gescannt, war weg.",

View file

@ -36,9 +36,6 @@
"Name" => "Pavadinimas",
"Share" => "Dalintis",
"Download" => "Atsisiųsti",
"Size" => "Dydis",
"Modified" => "Pakeista",
"Delete" => "Ištrinti",
"Upload too large" => "Įkėlimui failas per didelis",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Bandomų įkelti failų dydis viršija maksimalų leidžiamą šiame serveryje",
"Files are being scanned, please wait." => "Skenuojami failai, prašome palaukti.",

View file

@ -37,10 +37,6 @@
"Name" => "Име",
"Share" => "Сподели",
"Download" => "Преземи",
"Size" => "Големина",
"Modified" => "Променето",
"Delete all" => "Избриши сѐ",
"Delete" => "Избриши",
"Upload too large" => "Датотеката е премногу голема",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Датотеките кои се обидувате да ги подигнете ја надминуваат максималната големина за подигнување датотеки на овој сервер.",
"Files are being scanned, please wait." => "Се скенираат датотеки, ве молам почекајте.",

View file

@ -43,9 +43,6 @@
"Name" => "Nama ",
"Share" => "Kongsi",
"Download" => "Muat turun",
"Size" => "Saiz",
"Modified" => "Dimodifikasi",
"Delete" => "Padam",
"Upload too large" => "Muat naik terlalu besar",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Fail yang cuba dimuat naik melebihi saiz maksimum fail upload server",
"Files are being scanned, please wait." => "Fail sedang diimbas, harap bersabar.",

View file

@ -8,8 +8,19 @@
"Failed to write to disk" => "Klarte ikke å skrive til disk",
"Files" => "Filer",
"Delete" => "Slett",
"already exists" => "eksisterer allerede",
"replace" => "erstatt",
"cancel" => "avbryt",
"replaced" => "erstattet",
"with" => "med",
"undo" => "angre",
"deleted" => "slettet",
"generating ZIP-file, it may take some time." => "opprettet ZIP-fil, dette kan ta litt tid",
"Unable to upload your file as it is a directory or has 0 bytes" => "Kan ikke laste opp filen din siden det er en mappe eller den har 0 bytes",
"Upload Error" => "Opplasting feilet",
"Pending" => "Ventende",
"Upload cancelled." => "Opplasting avbrutt.",
"Invalid name, '/' is not allowed." => "Ugyldig navn, '/' er ikke tillatt. ",
"Size" => "Størrelse",
"Modified" => "Endret",
"folder" => "mappe",
@ -33,10 +44,6 @@
"Name" => "Navn",
"Share" => "Del",
"Download" => "Last ned",
"Size" => "Størrelse",
"Modified" => "Endret",
"Delete all" => "Slett alle",
"Delete" => "Slett",
"Upload too large" => "Opplasting for stor",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filene du prøver å laste opp er for store for å laste opp til denne serveren.",
"Files are being scanned, please wait." => "Skanner etter filer, vennligst vent.",

View file

@ -44,10 +44,6 @@
"Name" => "Naam",
"Share" => "Delen",
"Download" => "Download",
"Size" => "Bestandsgrootte",
"Modified" => "Laatst aangepast",
"Delete all" => "Alles verwijderen",
"Delete" => "Verwijder",
"Upload too large" => "Bestanden te groot",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "De bestanden die u probeert te uploaden zijn groter dan de maximaal toegestane bestandsgrootte voor deze server.",
"Files are being scanned, please wait." => "Bestanden worden gescand, even wachten.",

View file

@ -17,9 +17,6 @@
"Nothing in here. Upload something!" => "Ingenting her. Last noko opp!",
"Name" => "Namn",
"Download" => "Last ned",
"Size" => "Storleik",
"Modified" => "Endra",
"Delete" => "Slett",
"Upload too large" => "For stor opplasting",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filene du prøver å laste opp er større enn maksgrensa til denne tenaren."
);

View file

@ -44,9 +44,6 @@
"Name" => "Nazwa",
"Share" => "Współdziel",
"Download" => "Pobiera element",
"Size" => "Rozmiar",
"Modified" => "Czas modyfikacji",
"Delete" => "Usuwa element",
"Upload too large" => "Wysyłany plik ma za duży rozmiar",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Pliki które próbujesz przesłać, przekraczają maksymalną, dopuszczalną wielkość.",
"Files are being scanned, please wait." => "Skanowanie plików, proszę czekać.",

View file

@ -44,10 +44,6 @@
"Name" => "Nome",
"Share" => "Compartilhar",
"Download" => "Baixar",
"Size" => "Tamanho",
"Modified" => "Modificado",
"Delete all" => "Deletar Tudo",
"Delete" => "Excluir",
"Upload too large" => "Arquivo muito grande",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os arquivos que você está tentando carregar excedeu o tamanho máximo para arquivos no servidor.",
"Files are being scanned, please wait." => "Arquivos sendo escaneados, por favor aguarde.",

View file

@ -44,9 +44,6 @@
"Name" => "Nome",
"Share" => "Partilhar",
"Download" => "Transferir",
"Size" => "Tamanho",
"Modified" => "Modificado",
"Delete" => "Apagar",
"Upload too large" => "Envio muito grande",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os ficheiro que estás a tentar enviar excedem o tamanho máximo de envio neste servidor.",
"Files are being scanned, please wait." => "Os ficheiros estão a ser analisados, por favor aguarde.",

View file

@ -37,7 +37,6 @@
"Name" => "Meno",
"Share" => "Zdielať",
"Download" => "Stiahnuť",
"Delete all" => "Odstrániť všetko",
"Upload too large" => "Nahrávanie príliš veľké",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Súbory ktoré sa snažíte nahrať presahujú maximálnu veľkosť pre nahratie súborov na tento server.",
"Files are being scanned, please wait." => "Súbory sa práve prehľadávajú, prosím čakajte.",

View file

@ -43,8 +43,7 @@
"Nothing in here. Upload something!" => "Tukaj ni ničesar. Naložite kaj!",
"Name" => "Ime",
"Share" => "Souporaba",
"Download" => "Prejmi",
"Delete all" => "Izbriši vse",
"Download" => "Prenesi",
"Upload too large" => "Nalaganje ni mogoče, ker je preveliko",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Datoteke, ki jih želite naložiti, presegajo največjo dovoljeno velikost na tem strežniku.",
"Files are being scanned, please wait." => "Preiskujem datoteke, prosimo počakajte.",

View file

@ -44,7 +44,6 @@
"Name" => "ชื่อ",
"Share" => "แชร์",
"Download" => "ดาวน์โหลด",
"Delete all" => "ลบทั้งหมด",
"Upload too large" => "ไฟล์ที่อัพโหลดมีขนาดใหญ่เกินไป",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "ไฟล์ที่คุณพยายามที่จะอัพโหลดมีขนาดเกินกว่าขนาดสูงสุดที่กำหนดไว้ให้อัพโหลดได้สำหรับเซิร์ฟเวอร์นี้",
"Files are being scanned, please wait." => "ไฟล์กำลังอยู่ระหว่างการสแกน, กรุณารอสักครู่.",

View file

@ -39,7 +39,6 @@
"Name" => "Ad",
"Share" => "Paylaş",
"Download" => "İndir",
"Delete all" => "Hepsini sil",
"Upload too large" => "Yüklemeniz çok büyük",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Yüklemeye çalıştığınız dosyalar bu sunucudaki maksimum yükleme boyutunu aşıyor.",
"Files are being scanned, please wait." => "Dosyalar taranıyor, lütfen bekleyin.",

View file

@ -44,7 +44,6 @@
"Name" => "名称",
"Share" => "共享",
"Download" => "下载",
"Delete all" => "删除所有",
"Upload too large" => "上传文件过大",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "您正尝试上传的文件超过了此服务器可以上传的最大大小",
"Files are being scanned, please wait." => "文件正在被扫描,请稍候。",

View file

@ -27,7 +27,6 @@
"Name" => "名稱",
"Share" => "分享",
"Download" => "下載",
"Delete all" => "全部刪除",
"Upload too large" => "上傳過大",
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "你試圖上傳的檔案已超過伺服器的最大容量限制。 ",
"Files are being scanned, please wait." => "正在掃描檔案,請稍等。",

View file

@ -6,7 +6,7 @@ function viewOdf(dir, file) {
// odf action toolbar
var odfToolbarHtml =
'<div id="odf-toolbar">' +
'<input type="button" id="odf_close" value="Close">' +
'<input type="button" id="odf_close">'+t('files_odfviewer','Close')+
'</div>';
$('#controls').append(odfToolbarHtml);

View file

@ -0,0 +1,3 @@
<?php $TRANSLATIONS = array(
"Close" => "Schliessen"
);

View file

@ -0,0 +1,8 @@
src/webodf/webodf/tests/core/Base64Tests.js
src/webodf/webodf/tests/core/CursorTests.js
src/webodf/webodf/tests/gui/CaretTests.js
src/webodf/webodf/tests/gui/SelectionMoverTests.js
src/webodf/webodf/tests/core/PointWalkerTests.js
src/webodf/webodf/tests/core/ZipTests.js
src/webodf/webodf/tests/xmldom/XPathTests.js
src/webodf/webodf/tests/core/RuntimeTests.js

View file

@ -18,9 +18,18 @@ function showPDFviewer(dir,filename){
function im(path) { return OC.filePath('files_pdfviewer','js','pdfjs/web/images/'+path); }
showPDFviewer.oldcode = $("#controls").html();
$("#controls").empty();
$("#controls").html('<button id="previous" onclick="PDFView.page--;" oncontextmenu="return false;"><img src="'+im('go-up.svg')+'" align="top" height="10"/>Previous</button><button id="next" onclick="PDFView.page++;" oncontextmenu="return false;"><img src="'+im('go-down.svg')+'" align="top" height="10"/>Next</button><div class="separator"></div><input style="width:25px;" type="number" id="pageNumber" onchange="PDFView.page = this.value;" value="1" size="4" min="1" /><span>/</span><span id="numPages">--</span><div class="separator"></div><button id="zoomOut" title="Zoom Out" onclick="PDFView.zoomOut();" oncontextmenu="return false;"><img src="'+im('zoom-out.svg')+'" align="top" height="10"/></button><button id="zoomIn" title="Zoom In" onclick="PDFView.zoomIn();" oncontextmenu="return false;"><img src="'+im('zoom-in.svg')+
'" align="top" height="10"/></button><div class="separator"></div><select id="scaleSelect" onchange="PDFView.parseScale(this.value);" oncontextmenu="return false;"><option id="customScaleOption" value="custom"></option><option value="0.5">50%</option><option value="0.75">75%</option><option value="1">100%</option><option value="1.25" selected="selected">125%</option><option value="1.5">150%</option><option value="2">200%</option><option id="pageWidthOption" value="page-width">Page Width</option><option id="pageFitOption" value="page-fit">Page Fit</option></select><div class="separator"></div><button id="print" onclick="window.print();" oncontextmenu="return false;"><img src="'+im('document-print.svg')+'" align="top" height="10"/>Print</button><button id="download" title="Download" onclick="PDFView.download();" oncontextmenu="return false;">'+
'<img src="'+im('download.svg')+'" align="top" height="10"/>Download</button><button id="close" title="Close viewer" onclick="hidePDFviewer();" oncontextmenu="return false;">x</button><span id="info">--</span></div>');
$("#controls").html(
'<button id="previous" onclick="PDFView.page--;" oncontextmenu="return false;"><img src="'+im('go-up.svg')+'" align="top" height="10"/>'+t('files_odfviewer','Previous')+'</button>'+
'<button id="next" onclick="PDFView.page++;" oncontextmenu="return false;"><img src="'+im('go-down.svg')+'" align="top" height="10"/>'+t('files_odfviewer','Next')+'</button>'+
'<div class="separator"></div><input style="width:25px;" type="number" id="pageNumber" onchange="PDFView.page = this.value;" value="1" size="4" min="1" /><span>/</span><span id="numPages">--</span><div class="separator"></div>'+
'<button id="zoomOut" title="Zoom Out" onclick="PDFView.zoomOut();" oncontextmenu="return false;"><img src="'+im('zoom-out.svg')+'" align="top" height="10"/></button>'+
'<button id="zoomIn" title="Zoom In" onclick="PDFView.zoomIn();" oncontextmenu="return false;"><img src="'+im('zoom-in.svg')+ '" align="top" height="10"/></button>'+
'<div class="separator"></div><select id="scaleSelect" onchange="PDFView.parseScale(this.value);" oncontextmenu="return false;"><option id="customScaleOption" value="custom"></option>'+
'<option value="0.5">50%</option><option value="0.75">75%</option><option value="1">100%</option><option value="1.25" selected="selected">125%</option><option value="1.5">150%</option><option value="2">200%</option>'+
'<option id="pageWidthOption" value="page-width">'+t('files_odfviewer', 'Page Width')+'</option><option id="pageFitOption" value="page-fit">'+t('files_odfviewer', 'Page Fit')+'</option></select>'+
'<div class="separator"></div><button id="print" onclick="window.print();" oncontextmenu="return false;"><img src="'+im('document-print.svg')+'" align="top" height="10"/>'+t('files_odfviewer', 'Print')+'</button>'+
'<button id="download" title="Download" onclick="PDFView.download();" oncontextmenu="return false;">'+
'<img src="'+im('download.svg')+'" align="top" height="10"/>'+t('files_odfviewer', 'Download')+'</button><button id="close" title="Close viewer" onclick="hidePDFviewer();" oncontextmenu="return false;">x</button><span id="info">--</span></div>');
var oldcontent = $("#content").html();
$("#content").html(oldcontent+'<div id="loading">Loading... 0%</div><div id="viewer"></div>');
showPDFviewer.lastTitle = document.title;

View file

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