diff options
author | Georg Ehrke <dev@georgswebsite.de> | 2012-04-24 21:59:56 +0200 |
---|---|---|
committer | Georg Ehrke <dev@georgswebsite.de> | 2012-04-24 21:59:56 +0200 |
commit | f17eea506afaab0c0755d0fefe3a3f62eeb38e9d (patch) | |
tree | b5c1f8a33aef46f1a718d5313bef8f4e52700acf | |
parent | d6346b5b0bece4feecc36b7b97308dd3a5a4d6cc (diff) | |
parent | 9b134b063683aca678e19912ebc3928321751714 (diff) | |
download | nextcloud-server-f17eea506afaab0c0755d0fefe3a3f62eeb38e9d.tar.gz nextcloud-server-f17eea506afaab0c0755d0fefe3a3f62eeb38e9d.zip |
fix merge conflicts
42 files changed, 589 insertions, 3039 deletions
diff --git a/3rdparty/granite/git/blob.php b/3rdparty/granite/git/blob.php deleted file mode 100644 index 781a697d560..00000000000 --- a/3rdparty/granite/git/blob.php +++ /dev/null @@ -1,162 +0,0 @@ -<?php -/** - * Blob - provides a Git blob object - * - * PHP version 5.3 - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ - -namespace Granite\Git; -use \Granite\Git\Object\Raw as Raw; -use \InvalidArgumentException as InvalidArgumentException; -use \finfo as finfo; -/** - * **Granite\Git\Blob** represents the raw content of an object in a Git repository, - * typically a **file**. This class provides methods related to the handling of - * blob content, mimetypes, sizes and write support. - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ -class Blob -{ - - /** - * Stores the SHA-1 id of the object requested; accessed through the `sha()` - * method where it is recalculated based on the blob content. - */ - private $sha = null; - /** - * The raw binary string of the file contents. - */ - private $content = ""; - /** - * The path to the repository location. - */ - private $path; - - /** - * Fetches a raw Git object and parses the result. Throws an - * InvalidArgumentException if the object is not of the correct type, - * or cannot be found. - * - * @param string $path The path to the repository root. - * @param string $sha The SHA-1 id of the requested object, or `null` if - * creating a new blob object. - * - * @throws InvalidArgumentException If the SHA-1 id provided is not a blob. - */ - public function __construct($path, $sha = NULL) - { - $this->path = $path; - if ($sha !== NULL) { - $this->sha = $sha; - $object = Raw::factory($path, $sha); - - if ($object->type() !== Raw::OBJ_BLOB) { - throw new InvalidArgumentException( - "The object $sha is not a blob, type is {$object->type()}" - ); - } - - $this->content = $object->content(); - unset($object); - } - } - - /** - * Sets or returns the raw file content, depending whether the parameter is - * provided. - * - * @param string $content The object content to set, or `null` if requesting the - * current content. - * - * @return string The raw binary string of the file contents. - */ - public function content($content = NULL) - { - if ($content == NULL) { - return $this->content; - } - $this->content = $content; - } - - /** - * Returns the size of the file content in bytes, equivalent to - * `strlen($blob->content())`. - * - * @return int The size of the object in bytes. - */ - public function size() - { - return strlen($this->content); - } - - /** - * Updates and returns the SHA-1 id of the object, based on it's contents. - * - * @return int The SHA-1 id of the object. - */ - public function sha() - { - $sha = hash_init('sha1'); - $header = 'blob ' . strlen($this->content) . "\0"; - hash_update($sha, $header); - hash_update($sha, $this->content); - $this->sha = hash_final($sha); - return $this->sha; - } - - /** - * Returns the mimetype of the object, using `finfo()` to determine the mimetype - * of the string. - * - * @return string The object mimetype. - * @see http://php.net/manual/en/function.finfo-open.php - */ - public function mimetype() - { - $finfo = new finfo(FILEINFO_MIME); - return $finfo->buffer($this->content); - } - - /** - * Encode and compress the object content, saving it to a 'loose' file. - * - * @return boolean True on success, false on failure. - */ - public function write() - { - $sha = $this->sha(TRUE); - $path = $this->path - . 'objects' - . DIRECTORY_SEPARATOR - . substr($sha, 0, 2) - . DIRECTORY_SEPARATOR - . substr($sha, 2); - // FIXME: currently writes loose objects only - if (file_exists($path)) { - return FALSE; - } - - if (!is_dir(dirname($path))) { - mkdir(dirname($path), 0777, TRUE); - } - - $loose = fopen($path, 'wb'); - $data = 'blob ' . strlen($this->content) . "\0" . $this->content; - $write = fwrite($loose, gzcompress($data)); - fclose($loose); - - return ($write !== FALSE); - } - -} diff --git a/3rdparty/granite/git/commit.php b/3rdparty/granite/git/commit.php deleted file mode 100644 index 51077e89f3c..00000000000 --- a/3rdparty/granite/git/commit.php +++ /dev/null @@ -1,232 +0,0 @@ -<?php -/** - * Commit - provides a 'commit' object - * - * PHP version 5.3 - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ - -namespace Granite\Git; -use \Granite\Git\Object\Raw as Raw; -use \InvalidArgumentException as InvalidArgumentException; - -/** - * Commit represents a full commit object - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ -class Commit -{ - - /** - * The path to the repository root - */ - private $path; - /** - * The SHA-1 id of the requested commit - */ - private $sha; - /** - * The size of the commit in bytes - */ - private $size; - /** - * The commit message - */ - private $message; - /** - * The full committer string - */ - private $committer; - /** - * The full author string - */ - private $author; - /** - * The SHA-1 ids of the parent commits - */ - private $parents = array(); - - /** - * Fetches a raw Git object and parses the result. Throws an - * InvalidArgumentException if the object is not of the correct type, - * or cannot be found. - * - * @param string $path The path to the repository root - * @param string $sha The SHA-1 id of the requested object - * - * @throws InvalidArgumentException - */ - public function __construct($path, $sha = NULL) - { - $this->path = $path; - if ($sha !== NULL) { - $this->sha = $sha; - $object = Raw::factory($path, $sha); - $this->size = $object->size(); - - if ($object->type() !== Raw::OBJ_COMMIT) { - throw new InvalidArgumentException( - "The object $sha is not a commit, type is " . $object->type() - ); - } - - // Parse headers and commit message (delimited with "\n\n") - list($headers, $this->message) = explode("\n\n", $object->content(), 2); - $headers = explode("\n", $headers); - - foreach ($headers as $header) { - list($header, $value) = explode(' ', $header, 2); - if ($header == 'parent') { - $this->parents[] = $value; - } else { - $this->$header = $value; - } - } - - $this->tree = new Tree($this->path, $this->tree); - } - } - - /** - * Returns the message stored in the commit - * - * @return string The commit message - */ - public function message($message = NULL) - { - if ($message !== NULL) { - $this->message = $message; - return $this; - } - return $this->message; - } - - /** - * Returns the commiter string - * - * @return string The committer string - */ - public function committer($committer = NULL) - { - if ($committer !== NULL) { - $this->committer = $committer; - return $this; - } - return $this->committer; - } - - /** - * Returns the author string - * - * @return string The author string - */ - public function author($author = NULL) - { - if ($author !== NULL) { - $this->author = $author; - return $this; - } - return $this->author; - } - - /** - * Returns the parents of the commit, or an empty array if none - * - * @return array The parents of the commit - */ - public function parents($parents = NULL) - { - if ($parents !== NULL) { - $this->parents = $parents; - return $this; - } - return $this->parents; - } - - /** - * Returns a tree object associated with the commit - * - * @return Tree - */ - public function tree(Tree $tree = NULL) - { - if ($tree !== NULL) { - $this->tree = $tree; - return $this; - } - return $this->tree; - } - - /** - * Returns the size of the commit in bytes (Git header + data) - * - * @return int - */ - public function size() - { - return $this->size; - } - - /** - * Returns the size of the commit in bytes (Git header + data) - * - * @return int - */ - public function sha() - { - $this->sha = hash('sha1', $this->_raw()); - return $this->sha; - } - - public function write() - { - $sha = $this->sha(); - $path = $this->path - . 'objects' - . DIRECTORY_SEPARATOR - . substr($sha, 0, 2) - . DIRECTORY_SEPARATOR - . substr($sha, 2); - // FIXME: currently writes loose objects only - if (file_exists($path)) { - return FALSE; - } - - if (!is_dir(dirname($path))) { - mkdir(dirname($path), 0777, TRUE); - } - - $loose = fopen($path, 'wb'); - $data = $this->_raw(); - $write = fwrite($loose, gzcompress($data)); - fclose($loose); - - return ($write !== FALSE); - } - - public function _raw() - { - $data = 'tree ' . $this->tree->sha() . "\n"; - foreach ($this->parents as $parent) - { - $data .= "parent $parent\n"; - } - $data .= 'author ' . $this->author . "\n"; - $data .= 'committer ' . $this->committer . "\n\n"; - $data .= $this->message; - - $data = 'commit ' . strlen($data) . "\0" . $data; - return $data; - } - -} diff --git a/3rdparty/granite/git/object/index.php b/3rdparty/granite/git/object/index.php deleted file mode 100644 index 239706d4efd..00000000000 --- a/3rdparty/granite/git/object/index.php +++ /dev/null @@ -1,210 +0,0 @@ -<?php -/** - * Index - provides an 'index' object for packfile indexes - * - * PHP version 5.3 - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT License - * @link http://craig0990.github.com/Granite/ - */ - -namespace Granite\Git\Object; -use \UnexpectedValueException as UnexpectedValueException; - -/** - * Index represents a packfile index - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT License - * @link http://craig0990.github.com/Granite/ - */ -class Index -{ - const INDEX_MAGIC = "\377tOc"; - - /** - * The full path to the packfile index - */ - private $path; - /** - * The offset at which the fanout begins, version 2+ indexes have a 2-byte header - */ - private $offset = 8; - /** - * The size of the SHA-1 entries, version 1 stores 4-byte offsets alongside to - * total 24 bytes, version 2+ stores offsets separately - */ - private $size = 20; - /** - * The version of the index file format, versions 1 and 2 are in use and - * currently supported - */ - private $version; - - /** - * Fetches a raw Git object and parses the result - * - * @param string $path The path to the repository root - * @param string $packname The name of the packfile index to read - */ - public function __construct($path, $packname) - { - $this->path = $path - . 'objects' - . DIRECTORY_SEPARATOR - . 'pack' - . DIRECTORY_SEPARATOR - . 'pack-' . $packname . '.idx'; - - $this->version = $this->_readVersion(); - if ($this->version !== 1 && $this->version !== 2) { - throw new UnexpectedValueException( - "Unsupported index version (version $version)" - ); - } - - if ($this->version == 1) { - $this->offset = 0; // Version 1 index has no header/version - $this->size = 24; // Offsets + SHA-1 ids are stored together - } - } - - /** - * Returns the offset of the object stored in the index - * - * @param string $sha The SHA-1 id of the object being requested - * - * @return int The offset of the object in the packfile - */ - public function find($sha) - { - $index = fopen($this->path, 'rb'); - $offset = false; // Offset for object in packfile not found by default - - // Read the fanout to skip to the start char in the sorted SHA-1 list - list($start, $after) = $this->_readFanout($index, $sha); - - if ($start == $after) { - fclose($index); - return false; // Object is apparently located in a 0-length section - } - - // Seek $offset + 255 4-byte fanout entries and read 256th entry - fseek($index, $this->offset + 4 * 255); - $totalObjects = $this->_uint32($index); - - // Look up the SHA-1 id of the object - // TODO: Binary search - fseek($index, $this->offset + 1024 + $this->size * $start); - for ($i = $start; $i < $after; $i++) { - if ($this->version == 1) { - $offset = $this->_uint32($index); - } - - $name = fread($index, 20); - if ($name == pack('H40', $sha)) { - break; // Found it - } - } - - if ($i == $after) { - fclose($index); - return false; // Scanned entire section, couldn't find it - } - - if ($this->version == 2) { - // Jump to the offset location and read it - fseek($index, 1032 + 24 * $totalObjects + 4 * $i); - $offset = $this->_uint32($index); - if ($offset & 0x80000000) { - // Offset is a 64-bit integer; packfile is larger than 2GB - fclose($index); - throw new UnexpectedValueException( - "Packfile larger than 2GB, currently unsupported" - ); - } - } - - fclose($index); - return $offset; - } - - /** - * Converts a binary string into a 32-bit unsigned integer - * - * @param handle $file Binary string to convert - * - * @return int Integer value - */ - private function _uint32($file) - { - $val = unpack('Nx', fread($file, 4)); - return $val['x']; - } - - /** - * Reads the fanout for a particular SHA-1 id - * - * Largely modified from Glip, with some reference to Grit - largely because I - * can't see how to re-implement this in PHP - * - * @param handle $file File handle to the index file - * @param string $sha The SHA-1 id to search for - * @param int $offset The offset at which the fanout begins - * - * @return array Array containing integer 'start' and - * 'past-the-end' locations - */ - private function _readFanout($file, $sha) - { - $sha = pack('H40', $sha); - fseek($file, $this->offset); - if ($sha{0} == "\00") { - /** - * First character is 0, read first fanout entry to provide - * 'past-the-end' location (since first fanout entry provides start - * point for '1'-prefixed SHA-1 ids) - */ - $start = 0; - fseek($file, $this->offset); // Jump to start of fanout, $offset bytes in - $after = $this->_uint32($file); - } else { - /** - * Take ASCII value of first character, minus one to get the fanout - * position of the offset (minus one because the fanout does not - * contain an entry for "\00"), multiplied by four bytes per entry - */ - fseek($file, $this->offset + (ord($sha{0}) - 1) * 4); - $start = $this->_uint32($file); - $after = $this->_uint32($file); - } - - return array($start, $after); - } - - /** - * Returns the version number of the index file, or 1 if there is no version - * information - * - * @return int - */ - private function _readVersion() - { - $file = fopen($this->path, 'rb'); - $magic = fread($file, 4); - $version = $this->_uint32($file); - - if ($magic !== self::INDEX_MAGIC) { - $version = 1; - } - - fclose($file); - return $version; - } - -} diff --git a/3rdparty/granite/git/object/loose.php b/3rdparty/granite/git/object/loose.php deleted file mode 100644 index 32f894845b9..00000000000 --- a/3rdparty/granite/git/object/loose.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php -/** - * Loose - provides a 'loose object' object - * - * PHP version 5.3 - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ - -namespace Granite\Git\Object; -use \UnexpectedValueException as UnexpectedValueException; - -/** - * Loose represents a loose object in the Git repository - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ -class Loose extends Raw -{ - - /** - * Reads an object from a loose object file based on the SHA-1 id - * - * @param string $path The path to the repository root - * @param string $sha The SHA-1 id of the requested object - * - * @throws UnexpectedValueException If the type is not 'commit', 'tree', - * 'tag' or 'blob' - */ - public function __construct($path, $sha) - { - $this->sha = $sha; - - $loose_path = $path - . 'objects/' - . substr($sha, 0, 2) - . '/' - . substr($sha, 2); - - if (!file_exists($loose_path)) { - throw new InvalidArgumentException("Cannot open loose object file for $sha"); - } - - $raw = gzuncompress(file_get_contents($loose_path)); - $data = explode("\0", $raw, 2); - - $header = $data[0]; - $this->content = $data[1]; - - list($this->type, $this->size) = explode(' ', $header); - - switch ($this->type) { - case 'commit': - $this->type = Raw::OBJ_COMMIT; - break; - case 'tree': - $this->type = Raw::OBJ_TREE; - break; - case 'blob': - $this->type = Raw::OBJ_BLOB; - break; - case 'tag': - $this->type = Raw::OBJ_TAG; - break; - default: - throw new UnexpectedValueException( - "Unexpected type '{$this->type}'" - ); - break; - } - } - -} diff --git a/3rdparty/granite/git/object/packed.php b/3rdparty/granite/git/object/packed.php deleted file mode 100644 index 7e8d663b32e..00000000000 --- a/3rdparty/granite/git/object/packed.php +++ /dev/null @@ -1,304 +0,0 @@ -<?php -/** - * Packed - provides a 'packed object' object - * - * PHP version 5.3 - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ - -namespace Granite\Git\Object; -use \UnexpectedValueException as UnexpectedValueException; - -/** - * Packed represents a packed object in the Git repository - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ -class Packed extends Raw -{ - - /** - * The name of the packfile being read - */ - private $_packfile; - - /** - * Added to the object size to make a 'best-guess' effort at how much compressed - * data to read - should be reimplemented, ideally with streams. - */ - const OBJ_PADDING = 512; - - /** - * Reads the object data from the compressed data at $offset in $packfile - * - * @param string $packfile The path to the packfile - * @param int $offset The offset of the object data - */ - public function __construct($packfile, $offset) - { - $this->_packfile = $packfile; - - list($this->type, $this->size, $this->content) - = $this->_readPackedObject($offset); - } - - /** - * Reads the object data at $this->_offset - * - * @param int $offset Offset of the object header - * - * @return array Containing the type, size and object data - */ - private function _readPackedObject($offset) - { - $file = fopen($this->_packfile, 'rb'); - fseek($file, $offset); - // Read the type and uncompressed size from the object header - list($type, $size) = $this->_readHeader($file, $offset); - $object_offset = ftell($file); - - if ($type == self::OBJ_OFS_DELTA || $type == self::OBJ_REF_DELTA) { - return $this->_unpackDeltified( - $file, $offset, $object_offset, $type, $size - ); - } - - $content = gzuncompress(fread($file, $size + self::OBJ_PADDING), $size); - - return array($type, $size, $content); - } - - /** - * Reads a packed object header, returning the type and the size. For more - * detailed information, refer to the @see tag. - * - * From the @see tag: "Each byte is really 7 bits of data, with the first bit - * being used to say if that hunk is the last one or not before the data starts. - * If the first bit is a 1, you will read another byte, otherwise the data starts - * next. The first 3 bits in the first byte specifies the type of data..." - * - * @param handle $file File handle to read - * @param int $offset Offset of the object header - * - * @return array Containing the type and the size - * @see http://book.git-scm.com/7_the_packfile.html - */ - private function _readHeader($file, $offset) - { - // Read the object header byte-by-byte - fseek($file, $offset); - $byte = ord(fgetc($file)); - /** - * Bit-shift right by four, then ignore the first bit with a bitwise AND - * This gives us the object type in binary: - * 001 commit self::OBJ_COMMIT - * 010 tree self::OBJ_TREE - * 011 blob self::OBJ_BLOB - * 100 tag self::OBJ_TAG - * 110 offset delta self::OBJ_OFS_DELTA - * 111 ref delta self::OBJ_REF_DELTA - * - * (000 is undefined, 101 is not currently in use) - * See http://book.git-scm.com/7_the_packfile.html for details - */ - $type = ($byte >> 4) & 0x07; - - // Read the last four bits of the first byte, used to find the size - $size = $byte & 0x0F; - - /** - * $shift initially set to four, since we use the last four bits of the first - * byte - * - * $byte & 0x80 checks the initial bit is set to 1 (i.e. keep reading data) - * - * Finally, $shift is incremented by seven for each consecutive byte (because - * we ignore the initial bit) - */ - for ($shift = 4; $byte & 0x80; $shift += 7) { - $byte = ord(fgetc($file)); - /** - * The size is ANDed against 0x7F to strip the initial bit, then - * bitshifted by left $shift (4 or 7, depending on whether it's the - * initial byte) and ORed against the existing binary $size. This - * continuously increments the $size variable. - */ - $size |= (($byte & 0x7F) << $shift); - } - - return array($type, $size); - } - - /** - * Unpacks a deltified object located at $offset in $file - * - * @param handle $file File handle to read - * @param int $offset Offset of the object data - * @param int $object_offset Offset of the object data, past the header - * @param int $type The object type, either OBJ_REF_DELTA - or OBJ_OFS_DELTA - * @param int $size The expected size of the uncompressed data - * - * @return array Containing the type, size and object data - */ - private function _unpackDeltified($file, $offset, $object_offset, $type, $size) - { - fseek($file, $object_offset); - - if ($type == self::OBJ_REF_DELTA) { - - $base_sha = bin2hex(fread($file, 20)); - - $path = substr($this->_packfile, 0, strpos($this->_packfile, '.git')+5); - $base = Raw::factory($path, $base_sha); - $type = $base->type(); - $base = $base->content(); - - $delta = gzuncompress( - fread($file, $size + self::OBJ_PADDING), $size - ); - - $content = $this->_applyDelta($base, $delta); - - } elseif ($type == self::OBJ_OFS_DELTA) { - - // 20 = maximum varint size according to Glip - $data = fread($file, $size + self::OBJ_PADDING + 20); - - list($base_offset, $length) = $this->_bigEndianNumber($data); - - $delta = gzuncompress(substr($data, $length), $size); - unset($data); - - $base_offset = $offset - $base_offset; - list($type, $size, $base) = $this->_readPackedObject($base_offset); - - $content = $this->_applyDelta($base, $delta); - - } else { - throw new UnexpectedValueException( - "Unknown type $type for deltified object" - ); - } - - return array($type, strlen($content), $content); - } - - /** - * Applies the $delta byte-sequence to $base and returns the - * resultant binary string. - * - * This code is modified from Grit (see below), the Ruby - * implementation used for GitHub under an MIT license. - * - * @param string $base The base string for the delta to be applied to - * @param string $delta The delta string to apply - * - * @return string The patched binary string - * @see - * https://github.com/mojombo/grit/blob/master/lib/grit/git-ruby/internal/pack.rb - */ - private function _applyDelta($base, $delta) - { - $pos = 0; - $src_size = $this->_varint($delta, $pos); - $dst_size = $this->_varint($delta, $pos); - - if ($src_size !== strlen($base)) { - throw new UnexpectedValueException( - 'Expected base delta size ' . strlen($base) . ' does not match the expected ' - . "value $src_size" - ); - } - - $dest = ""; - while ($pos < strlen($delta)) { - $byte = ord($delta{$pos++}); - - if ($byte & 0x80) { - /* copy a part of $base */ - $offset = 0; - if ($byte & 0x01) $offset = ord($delta{$pos++}); - if ($byte & 0x02) $offset |= ord($delta{$pos++}) << 8; - if ($byte & 0x04) $offset |= ord($delta{$pos++}) << 16; - if ($byte & 0x08) $offset |= ord($delta{$pos++}) << 24; - $length = 0; - if ($byte & 0x10) $length = ord($delta{$pos++}); - if ($byte & 0x20) $length |= ord($delta{$pos++}) << 8; - if ($byte & 0x40) $length |= ord($delta{$pos++}) << 16; - if ($length == 0) $length = 0x10000; - $dest .= substr($base, $offset, $length); - } else { - /* take the next $byte bytes as they are */ - $dest .= substr($delta, $pos, $byte); - $pos += $byte; - } - } - - if (strlen($dest) !== $dst_size) { - throw new UnexpectedValueException( - "Deltified string expected to be $dst_size bytes, but actually " - . strlen($dest) . ' bytes' - ); - } - - return $dest; - } - - /** - * Parse a Git varint (variable-length integer). Used in the `_applyDelta()` - * method to read the delta header. - * - * @param string $string The string to parse - * @param int &$pos The position in the string to read from - * - * @return int The integer value - */ - private function _varint($string, &$pos = 0) - { - $varint = 0; - $bitmask = 0x80; - for ($i = 0; $bitmask & 0x80; $i += 7) { - $bitmask = ord($string{$pos++}); - $varint |= (($bitmask & 0x7F) << $i); - } - return $varint; - } - - /** - * Decodes a big endian modified base 128 number (refer to @see tag); this only - * appears to be used in one place, the offset delta in packfiles. The offset - * is the number of bytes to seek back from the start of the delta object to find - * the base object. - * - * This code has been implemented using the C code given in the @see tag below. - * - * @param string &$data The data to read from and decode the number - * - * @return Array Containing the base offset (number of bytes to seek back) and - * the length to use when reading the delta - * @see http://git.rsbx.net/Documents/Git_Data_Formats.txt - */ - private function _bigEndianNumber(&$data) - { - $i = 0; - $byte = ord($data{$i++}); - $number = $byte & 0x7F; - while ($byte & 0x80) { - $byte = ord($data{$i++}); - $number = (($number + 1) << 7) | ($byte & 0x7F); - } - - return array($number, $i); - } - -} diff --git a/3rdparty/granite/git/object/raw.php b/3rdparty/granite/git/object/raw.php deleted file mode 100644 index 56f363c37b2..00000000000 --- a/3rdparty/granite/git/object/raw.php +++ /dev/null @@ -1,153 +0,0 @@ -<?php -/** - * Raw - provides a raw Git object - * - * PHP version 5.3 - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ - -namespace Granite\Git\Object; -use \InvalidArgumentException as InvalidArgumentException; - -/** - * Raw represents a raw Git object, using Index to locate - * packed objects. - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ -class Raw -{ - /** - * Integer values for Git objects - * @see http://book.git-scm.com/7_the_packfile.html - */ - const OBJ_COMMIT = 1; - const OBJ_TREE = 2; - const OBJ_BLOB = 3; - const OBJ_TAG = 4; - const OBJ_OFS_DELTA = 6; - const OBJ_REF_DELTA = 7; - - /** - * The SHA-1 id of the requested object - */ - protected $sha; - /** - * The type of the requested object (see class constants) - */ - protected $type; - /** - * The binary string content of the requested object - */ - protected $content; - - /** - * Returns an instance of a raw Git object - * - * @param string $path The path to the repository root - * @param string $sha The SHA-1 id of the requested object - * - * @return Packed|Loose - */ - public static function factory($path, $sha) - { - $loose_path = $path - . 'objects/' - . substr($sha, 0, 2) - . '/' - . substr($sha, 2); - if (file_exists($loose_path)) { - return new Loose($path, $sha); - } else { - return self::_findPackedObject($path, $sha); - } - } - - /** - * Returns the raw content of the Git object requested - * - * @return string Raw object content - */ - public function content() - { - return $this->content; - } - - /** - * Returns the size of the Git object - * - * @return int The size of the object in bytes - */ - public function size() - { - return strlen($this->content); - } - - /** - * Returns the type of the object as either commit, tag, blob or tree - * - * @return string The object type - */ - public function type() - { - return $this->type; - } - - /** - * Searches a packfile for the SHA id and reads the object from the packfile - * - * @param string $path The path to the repository - * @param string $sha The SHA-1 id of the object being requested - * - * @throws \InvalidArgumentException - * @return array An array containing the type, size and object data - */ - private static function _findPackedObject($path, $sha) - { - $packfiles = glob( - $path - . 'objects' - . DIRECTORY_SEPARATOR - . 'pack' - . DIRECTORY_SEPARATOR - . 'pack-*.pack' - ); - - $offset = false; - foreach ($packfiles as $packfile) { - $packname = substr(basename($packfile, '.pack'), 5); - $idx = new Index($path, $packname); - $offset = $idx->find($sha); - - if ($offset !== false) { - break; // Found it - } - } - - if ($offset == false) { - throw new InvalidArgumentException("Could not find packed object $sha"); - } - - $packname = $path - . 'objects' - . DIRECTORY_SEPARATOR - . 'pack' - . DIRECTORY_SEPARATOR - . 'pack-' . $packname . '.pack'; - $object = new Packed($packname, $offset); - - return $object; - } - -} - -?> diff --git a/3rdparty/granite/git/repository.php b/3rdparty/granite/git/repository.php deleted file mode 100644 index 30b58a39f5a..00000000000 --- a/3rdparty/granite/git/repository.php +++ /dev/null @@ -1,293 +0,0 @@ -<?php -/** - * Repository - provides a 'repository' object with a set of helper methods - * - * PHP version 5.3 - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ - -namespace Granite\Git; -use \InvalidArgumentException as InvalidArgumentException; -use \UnexpectedValueException as UnexpectedValueException; - -/** - * Repository represents a Git repository, providing a variety of methods for - * fetching objects from SHA-1 ids or the tip of a branch with `head()` - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ -class Repository -{ - - /** - * The path to the repository root - */ - private $_path; - /** - * The indexed version of a commit, ready to write with `commit()` - */ - private $idx_commit; - /** - * The indexed version of a tree, modified to with `add()` and `remove()` - */ - private $idx_tree; - - /** - * Sets the repository path - * - * @param string $path The path to the repository root (i.e. /repo/.git/) - */ - public function __construct($path) - { - if (!is_dir($path)) { - throw new InvalidArgumentException("Unable to find directory $path"); - } elseif (!is_readable($path)) { - throw new InvalidArgumentException("Unable to read directory $path"); - } elseif (!is_dir($path . DIRECTORY_SEPARATOR . 'objects') - || !is_dir($path . DIRECTORY_SEPARATOR . 'refs') - ) { - throw new UnexpectedValueException( - "Invalid directory, could not find 'objects' or 'refs' in $path" - ); - } - - $this->_path = $path; - $this->idx_commit = $this->factory('commit'); - $this->idx_tree = $this->factory('tree'); - } - - /** - * Returns an object from the Repository of the given type, with the given - * SHA-1 id, or false if it cannot be found - * - * @param string $type The type (blob, commit, tag or tree) of object being - * requested - * @param string $sha The SHA-1 id of the object (or the name of a tag) - * - * @return Blob|Commit|Tag|Tree - */ - public function factory($type, $sha = null) - { - if (!in_array($type, array('blob', 'commit', 'tag', 'tree'))) { - throw new InvalidArgumentException("Invalid type: $type"); - } - - if ($type == 'tag') { - $sha = $this->_ref('tags' . DIRECTORY_SEPARATOR . $sha); - } - $type = 'Granite\\Git\\' . ucwords($type); - - return new $type($this->_path, $sha); - } - - /** - * Returns a Commit object representing the HEAD commit - * - * @param string $branch The branch name to lookup, defaults to 'master' - * - * @return Commit An object representing the HEAD commit - */ - public function head($branch = 'master', $value = NULL) - { - if ($value == NULL) - return $this->factory( - 'commit', $this->_ref('heads' . DIRECTORY_SEPARATOR . $branch) - ); - - file_put_contents( - $this->_path . DIRECTORY_SEPARATOR - . 'refs' . DIRECTORY_SEPARATOR - . 'heads' . DIRECTORY_SEPARATOR . 'master', - $value - ); - } - - /** - * Returns a string representing the repository's location, which may or may - * not be initialised - * - * @return string A string representing the repository's location - */ - public function path() - { - return $this->_path; - } - - /** - * Returns an array of the local branches under `refs/heads` - * - * @return array - */ - public function tags() - { - return $this->_refs('tags'); - } - - /** - * Returns an array of the local tags under `refs/tags` - * - * @return array - */ - public function branches() - { - return $this->_refs('heads'); - } - - private function _refs($type) - { - $dir = $this->_path . 'refs' . DIRECTORY_SEPARATOR . $type; - $refs = glob($dir . DIRECTORY_SEPARATOR . '*'); - foreach ($refs as &$ref) { - $ref = basename($ref); - } - return $refs; - } - - /** - * Initialises a Git repository - * - * @return boolean Returns true on success, false on error - */ - public static function init($path) - { - $path .= '/'; - if (!is_dir($path)) { - mkdir($path); - } elseif (is_dir($path . 'objects')) { - return false; - } - - mkdir($path . 'objects'); - mkdir($path . 'objects/info'); - mkdir($path . 'objects/pack'); - mkdir($path . 'refs'); - mkdir($path . 'refs/heads'); - mkdir($path . 'refs/tags'); - - file_put_contents($path . 'HEAD', 'ref: refs/heads/master'); - - return true; - } - - /** - * Writes the indexed commit to disk, with blobs added/removed via `add()` and - * `rm()` - * - * @param string $message The commit message - * @param string $author The author name - * - * @return boolean True on success, or false on failure - */ - public function commit($message, $author) - { - $user_string = $username . ' ' . time() . ' +0000'; - - try { - $parents = array($this->repo->head()->sha()); - } catch (InvalidArgumentException $e) { - $parents = array(); - } - - $this->idx_commit->message($message); - $this->idx_commit->author($user_string); - $this->idx_commit->committer($user_string); - $this->idx_commit->tree($this->idx_tree); - $commit->parents($parents); - - $this->idx_tree->write(); - $this->idx_commit->write(); - - $this->repo->head('master', $this->idx_commit->sha()); - - $this->idx_commit = $this->factory('commit'); - $this->idx_tree = $this->factory('tree'); - } - - /** - * Adds a file to the indexed commit, to be written to disk with `commit()` - * - * @param string $filename The filename to save it under - * @param Granite\Git\Blob $blob The raw blob object to add to the tree - */ - public function add($filename, Granite\Git\Blob $blob) - { - $blob->write(); - $nodes = $this->idx_tree->nodes(); - $nodes[$filename] = new Granite\Git\Tree\Node($filename, '100644', $blob->sha()); - $this->idx_tree->nodes($nodes); - } - - /** - * Removes a file from the indexed commit - */ - public function rm($filename) - { - $nodes = $this->idx_tree->nodes(); - unset($nodes[$filename]); - $this->idx_tree->nodes($nodes); - } - - /** - * Returns an SHA-1 id of the ref resource - * - * @param string $ref The ref name to lookup - * - * @return string An SHA-1 id of the ref resource - */ - private function _ref($ref) - { - // All refs are stored in `.git/refs` - $file = $this->_path . 'refs' . DIRECTORY_SEPARATOR . $ref; - - if (file_exists($file)) { - return trim(file_get_contents($file)); - } - - $sha = $this->_packedRef($ref); - - if ($sha == false) { - throw new InvalidArgumentException("The ref $ref could not be found"); - } - - return $sha; - } - - /** - * Returns an SHA-1 id of the ref resource, or false if it cannot be found - * - * @param string $ref The ref name to lookup - * - * @return string An SHA-1 id of the ref resource - */ - private function _packedRef($ref) - { - $sha = false; - if (file_exists($this->_path . 'packed-refs')) { - $file = fopen($this->_path . 'packed-refs', 'r'); - - while (($line = fgets($file)) !== false) { - $info = explode(' ', $line); - if (count($info) == 2 - && trim($info[1]) == 'refs' . DIRECTORY_SEPARATOR . $ref - ) { - $sha = trim($info[0]); - break; - } - } - - fclose($file); - } - - return $sha; - } - -} diff --git a/3rdparty/granite/git/tag.php b/3rdparty/granite/git/tag.php deleted file mode 100644 index e26ddaffa6d..00000000000 --- a/3rdparty/granite/git/tag.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Tag - provides a 'tag' object - * - * PHP version 5.3 - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ - -namespace Granite\Git; - -/** - * Tag represents a full tag object - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ -class Tag -{ - - public function __construct($path, $sha) - { - $this->sha = $sha; - } - - public function sha() - { - return $this->sha; - } - -} diff --git a/3rdparty/granite/git/tree.php b/3rdparty/granite/git/tree.php deleted file mode 100644 index 2de72274532..00000000000 --- a/3rdparty/granite/git/tree.php +++ /dev/null @@ -1,198 +0,0 @@ -<?php -/** - * Tree - provides a 'tree' object - * - * PHP version 5.3 - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ - -namespace Granite\Git; -use \Granite\Git\Tree\Node as Node; - -/** - * Tree represents a full tree object, with nodes pointing to other tree objects - * and file blobs - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ -class Tree -{ - - /** - * The SHA-1 id of the requested tree - */ - private $sha; - /** - * The nodes/entries for the requested tree - */ - private $nodes = array(); - /** - * The path to the repository - */ - private $path; - - /** - * Reads a tree object by fetching the raw object - * - * @param string $path The path to the repository root - * @param string $sha The SHA-1 id of the requested object - */ - public function __construct($path, $sha = NULL, $dbg = FALSE) - { - $this->path = $path; - if ($sha !== NULL) { - $object = Object\Raw::factory($path, $sha); - $this->sha = $sha; - - if ($object->type() !== Object\Raw::OBJ_TREE) { - throw new \InvalidArgumentException( - "The object $sha is not a tree, type is " . $object->type() - ); - } - - $content = $object->content(); - file_put_contents('/tmp/tree_from_real_repo'.time(), $content); - $nodes = array(); - - for ($i = 0; $i < strlen($content); $i = $data_start + 21) { - $data_start = strpos($content, "\0", $i); - $info = substr($content, $i, $data_start-$i); - list($mode, $name) = explode(' ', $info, 2); - // Read the object SHA-1 id - $sha = bin2hex(substr($content, $data_start + 1, 20)); - - $this->nodes[$name] = new Node($name, $mode, $sha); - } - } - } - - /** - * Returns an array of Tree and Granite\Git\Blob objects, - * representing subdirectories and files - * - * @return array Array of Tree and Granite\Git\Blob objects - */ - public function nodes($nodes = null) - { - if ($nodes == null) { - return $this->nodes; - } - $this->nodes = $nodes; - } - - /** - * Adds a blob or a tree to the list of nodes - * - * @param string $name The basename (filename) of the blob or tree - * @param string $mode The mode of the blob or tree (see above) - * @param string $sha The SHA-1 id of the blob or tree to add - */ - public function add($name, $mode, $sha) - { - $this->nodes[$name] = new Node($name, $mode, $sha); - uasort($this->nodes, array($this, '_sort')); - } - - public function write() - { - $sha = $this->sha(); - $path = $this->path - . 'objects' - . DIRECTORY_SEPARATOR - . substr($sha, 0, 2) - . DIRECTORY_SEPARATOR - . substr($sha, 2); - // FIXME: currently writes loose objects only - if (file_exists($path)) { - return FALSE; - } - - if (!is_dir(dirname($path))) { - mkdir(dirname($path), 0777, TRUE); - } - - $loose = fopen($path, 'wb'); - $data = $this->_raw(); - $data = 'tree ' . strlen($data) . "\0" . $data; - $write = fwrite($loose, gzcompress($data)); - fclose($loose); - - return ($write !== FALSE); - } - - /** - * Returns the SHA-1 id of the Tree - * - * @return string SHA-1 id of the Tree - */ - public function sha() - { - $data = $this->_raw(); - $raw = 'tree ' . strlen($data) . "\0" . $data; - $this->sha = hash('sha1', $raw); - return $this->sha; - } - - /** - * Generates the raw object content to be saved to disk - */ - public function _raw() - { - uasort($this->nodes, array($this, '_sort')); - $data = ''; - foreach ($this->nodes as $node) - { - $data .= base_convert($node->mode(), 10, 8) . ' ' . $node->name() . "\0"; - $data .= pack('H40', $node->sha()); - } - file_put_contents('/tmp/tree_made'.time(), $data); - return $data; - } - - /** - * Sorts the node entries in a tree, general sort method adapted from original - * Git C code (see @see tag below). - * - * @return 1, 0 or -1 if the first entry is greater than, the same as, or less - * than the second, respectively. - * @see https://github.com/gitster/git/blob/master/read-cache.c Around line 352, - * the `base_name_compare` function - */ - public function _sort(&$a, &$b) - { - $length = strlen($a->name()) < strlen($b->name()) ? strlen($a->name()) : strlen($b->name()); - - $cmp = strncmp($a->name(), $b->name(), $length); - if ($cmp) { - return $cmp; - } - - $suffix1 = $a->name(); - $suffix1 = (strlen($suffix1) > $length) ? $suffix1{$length} : FALSE; - $suffix2 = $b->name(); - $suffix2 = (strlen($suffix2) > $length) ? $suffix2{$length} : FALSE; - if (!$suffix1 && $a->isDirectory()) { - $suffix1 = '/'; - } - if (!$suffix2 && $b->isDirectory()) { - $suffix2 = '/'; - } - if ($suffix1 < $suffix2) { - return -1; - } elseif ($suffix1 > $suffix2) { - return 1; - } - - return 0; - } - -} diff --git a/3rdparty/granite/git/tree/node.php b/3rdparty/granite/git/tree/node.php deleted file mode 100644 index f99eb1ae281..00000000000 --- a/3rdparty/granite/git/tree/node.php +++ /dev/null @@ -1,126 +0,0 @@ -<?php -/** - * Node - provides a tree node object for tree entries - * - * PHP version 5.3 - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ - -namespace Granite\Git\Tree; - -/** - * Node represents an entry in a Tree - * - * @category Git - * @package Granite - * @author Craig Roberts <craig0990@googlemail.com> - * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License - * @link http://craig0990.github.com/Granite/ - */ -class Node -{ - - /** - * Name of the file, directory or submodule - */ - private $_name; - /** - * Mode of the object, in octal - */ - private $_mode; - /** - * SHA-1 id of the tree - */ - private $_sha; - /** - * Boolean value for whether the entry represents a directory - */ - private $_is_dir; - /** - * Boolean value for whether the entry represents a submodule - */ - private $_is_submodule; - - /** - * Sets up a Node class with properties corresponding to the $mode parameter - * - * @param string $name The name of the object (file, directory or submodule name) - * @param int $mode The mode of the object, retrieved from the repository - * @param string $sha The SHA-1 id of the object - */ - public function __construct($name, $mode, $sha) - { - $this->_name = $name; - $this->_mode = intval($mode, 8); - $this->_sha = $sha; - - $this->_is_dir = (bool) ($this->_mode & 0x4000); - $this->_is_submodule = ($this->_mode == 0xE000); - } - - /** - * Returns a boolean value indicating whether the node is a directory - * - * @return boolean - */ - public function isDirectory() - { - return $this->_is_dir; - } - - /** - * Returns a boolean value indicating whether the node is a submodule - * - * @return boolean - */ - public function isSubmodule() - { - return $this->_is_submodule; - } - - /** - * Returns the object name - * - * @return string - */ - public function name() - { - return $this->_name; - } - - /** - * Returns the object's SHA-1 id - * - * @return string - */ - public function sha() - { - return $this->_sha; - } - - /** - * Returns the octal value of the file mode - * - * @return int - */ - public function mode() - { - return $this->_mode; - } - - public function type() - { - if ($this->isDirectory()) { - return 'tree'; - } elseif ($this->isSubmodule()) { - return 'commit'; - } else { - return 'blob'; - } - } -} diff --git a/apps/contacts/ajax/importaddressbook.php b/apps/contacts/ajax/importaddressbook.php new file mode 100644 index 00000000000..5776c801a76 --- /dev/null +++ b/apps/contacts/ajax/importaddressbook.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +require_once('../../../lib/base.php'); +OC_JSON::checkLoggedIn(); +OC_Util::checkAppEnabled('contacts'); +$upload_max_filesize = OC_Helper::computerFileSize(ini_get('upload_max_filesize')); +$post_max_size = OC_Helper::computerFileSize(ini_get('post_max_size')); +$maxUploadFilesize = min($upload_max_filesize, $post_max_size); + +$freeSpace=OC_Filesystem::free_space('/'); +$freeSpace=max($freeSpace,0); +$maxUploadFilesize = min($maxUploadFilesize ,$freeSpace); + +$tmpl = new OC_Template('contacts', 'part.importaddressbook'); +$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); +$tmpl->assign('uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize)); +$tmpl->printpage(); +?> diff --git a/apps/contacts/ajax/uploadimport.php b/apps/contacts/ajax/uploadimport.php new file mode 100644 index 00000000000..ab680c8823f --- /dev/null +++ b/apps/contacts/ajax/uploadimport.php @@ -0,0 +1,50 @@ +<?php +/** + * ownCloud - Addressbook + * + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus <thomas@tanghus.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ +// Init owncloud +require_once('../../../lib/base.php'); + +// Check if we are a user +OC_JSON::checkLoggedIn(); +OC_JSON::checkAppEnabled('contacts'); +function bailOut($msg) { + OC_JSON::error(array('data' => array('message' => $msg))); + OC_Log::write('contacts','ajax/uploadimport.php: '.$msg, OC_Log::ERROR); + exit(); +} +function debug($msg) { + OC_Log::write('contacts','ajax/uploadimport.php: '.$msg, OC_Log::DEBUG); +} + +// If it is a Drag'n'Drop transfer it's handled here. +$fn = (isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : false); +if($fn) { + $view = OC_App::getStorage('contacts'); + $tmpfile = md5(rand()); + if($view->file_put_contents('/'.$tmpfile, file_get_contents('php://input'))) { + debug($fn.' uploaded'); + OC_JSON::success(array('data' => array('path'=>'', 'file'=>$tmpfile))); + } else { + bailOut(OC_Contacts_App::$l10n->t('Error uploading contacts to storage.')); + } +} + +?> diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css index 15f98cffbfb..2ea99a56a7b 100644 --- a/apps/contacts/css/contacts.css +++ b/apps/contacts/css/contacts.css @@ -33,8 +33,9 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } .form dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0px; white-space: nowrap; vertical-align: text-bottom; } #address.form dt { min-width: 5em; } #address.form dl { min-width: 10em; } - .loading {/*cursor: progress; */ cursor: wait; } +.droptarget { margin: 0.5em; padding: 0.5em; border: thin solid #ccc; -moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em; } +.droppable { margin: 0.5em; padding: 0.5em; border: thin dashed #333; -moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em; } .float { float: left; } .listactions { height: 1em; width:60px; float: left; clear: right; } .add,.edit,.delete,.mail, .globe, .upload, .cloud { cursor: pointer; width: 20px; height: 20px; margin: 0; float: left; position:relative; opacity: 0.1; } @@ -51,6 +52,7 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } #identityprops { /*position: absolute; top: 2.5em; left: 0px;*/ } /*#contact_photo { max-width: 250px; }*/ #contact_identity { min-width: 30em; } +#note { min-width: 200px; } .contactsection { position: relative; float: left; /*max-width: 40em;*/ padding: 0.5em; height: auto; border: thin solid lightgray;/* -webkit-border-radius: 0.5em; -moz-border-radius: 0.5em; border-radius: 0.5em; background-color: #f8f8f8;*/ } .contactpart legend { width:auto; padding:.3em; border:1px solid #ddd; font-weight:bold; cursor:pointer; background:#f8f8f8; color:#555; text-shadow:#fff 0 1px 0; -moz-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; } @@ -90,5 +92,4 @@ input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; } .propertylist li > input[type="checkbox"],input[type="radio"] { float: left; clear: left; width: 20px; height: 20px; vertical-align: middle; } .propertylist li > select { float: left; max-width: 8em; } .typelist { float: left; max-width: 10em; } /* for multiselect */ -.addresslist { clear: both; } - +.addresslist { clear: both; }
\ No newline at end of file diff --git a/apps/contacts/import.php b/apps/contacts/import.php index b80021e00ea..ca2c1e1605d 100644 --- a/apps/contacts/import.php +++ b/apps/contacts/import.php @@ -17,8 +17,14 @@ if(is_writable('import_tmp/')){ fwrite($progressfopen, '10'); fclose($progressfopen); } -$file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']); -if($_POST['method'] == 'new'){ +$view = $file = null; +if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') { + $view = OC_App::getStorage('contacts'); + $file = $view->file_get_contents('/' . $_POST['file']); +} else { + $file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']); +} +if(isset($_POST['method']) && $_POST['method'] == 'new'){ $id = OC_Contacts_Addressbook::add(OC_User::getUser(), $_POST['addressbookname']); OC_Contacts_Addressbook::setActive($id, 1); }else{ @@ -99,12 +105,16 @@ if(is_writable('import_tmp/')){ if(count($parts) == 1){ $importready = array($file); } +$imported = 0; +$failed = 0; foreach($importready as $import){ $card = OC_VObject::parse($import); if (!$card) { + $failed += 1; OC_Log::write('contacts','Import: skipping card. Error parsing VCard: '.$import, OC_Log::ERROR); continue; // Ditch cards that can't be parsed by Sabre. } + $imported += 1; OC_Contacts_VCard::add($id, $card); } //done the import @@ -117,4 +127,9 @@ sleep(3); if(is_writable('import_tmp/')){ unlink($progressfile); } -OC_JSON::success(); +if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') { + if(!$view->unlink('/' . $_POST['file'])) { + OC_Log::write('contacts','Import: Error unlinking OC_FilesystemView ' . '/' . $_POST['file'], OC_Log::ERROR); + } +} +OC_JSON::success(array('data' => array('imported'=>$imported, 'failed'=>$failed))); diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index f684576b787..b94bf77c5ec 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -1263,7 +1263,8 @@ Contacts={ for(ptype in this.data.TEL[phone]['parameters'][param]) { var pt = this.data.TEL[phone]['parameters'][param][ptype]; $('#phonelist li:last-child').find('select option').each(function(){ - if ($(this).val().toUpperCase() == pt.toUpperCase()) { + //if ($(this).val().toUpperCase() == pt.toUpperCase()) { + if ($.inArray($(this).val().toUpperCase(), pt.toUpperCase().split(',')) > -1) { $(this).attr('selected', 'selected'); } }); @@ -1285,6 +1286,7 @@ Contacts={ }, }, Addressbooks:{ + droptarget:undefined, overview:function(){ if($('#chooseaddressbook_dialog').dialog('isOpen') == true){ $('#chooseaddressbook_dialog').dialog('moveToTop'); @@ -1317,14 +1319,13 @@ Contacts={ var tr = $(document.createElement('tr')) .load(OC.filePath('contacts', 'ajax', 'addbook.php')); $(object).closest('tr').after(tr).hide(); - /* TODO: Shouldn't there be some kinda error checking here? */ }, editAddressbook:function(object, bookid){ var tr = $(document.createElement('tr')) .load(OC.filePath('contacts', 'ajax', 'editaddressbook.php') + "?bookid="+bookid); $(object).closest('tr').after(tr).hide(); }, - deleteAddressbook:function(bookid){ + deleteAddressbook:function(obj, bookid){ var check = confirm("Do you really want to delete this address book?"); if(check == false){ return false; @@ -1332,9 +1333,10 @@ Contacts={ $.post(OC.filePath('contacts', 'ajax', 'deletebook.php'), { id: bookid}, function(jsondata) { if (jsondata.status == 'success'){ - $('#chooseaddressbook_dialog').dialog('destroy').remove(); + $(obj).closest('tr').remove(); + //$('#chooseaddressbook_dialog').dialog('destroy').remove(); Contacts.UI.Contacts.update(); - Contacts.UI.Addressbooks.overview(); + //Contacts.UI.Addressbooks.overview(); } else { OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); //alert('Error: ' + data.message); @@ -1342,8 +1344,95 @@ Contacts={ }); } }, - doImport:function(){ - Contacts.UI.notImplemented(); + loadImportHandlers:function() { + this.droptarget = $('#import_drop_target'); + console.log($('#import_drop_target').html()); + $(this.droptarget).bind('dragover',function(event){ + $(event.target).addClass('droppable'); + event.stopPropagation(); + event.preventDefault(); + }); + $(this.droptarget).bind('dragleave',function(event){ + $(event.target).removeClass('droppable'); + }); + $(this.droptarget).bind('drop',function(event){ + event.stopPropagation(); + event.preventDefault(); + console.log('drop'); + $(event.target).removeClass('droppable'); + $(event.target).html(t('contacts', 'Uploading...')); + Contacts.UI.loading(event.target, true); + $.fileUpload(event.originalEvent.dataTransfer.files); + }); + + $.fileUpload = function(files){ + console.log(files + ', ' + files.length); + var file = files[0]; + console.log('size: '+file.size+', type: '+file.type); + if(file.size > $('#max_upload').val()){ + OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large')); + $(Contacts.UI.Addressbooks.droptarget).html(t('contacts', 'Drop a VCF file to import contacts.')); + Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false); + return; + } + if(file.type.indexOf('text') != 0) { + OC.dialogs.alert(t('contacts','You have dropped a file type that cannot be imported: ') + file.type, t('contacts','Wrong file type')); + $(Contacts.UI.Addressbooks.droptarget).html(t('contacts', 'Drop a VCF file to import contacts.')); + Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false); + return; + } + var xhr = new XMLHttpRequest(); + + if (!xhr.upload) { + OC.dialogs.alert(t('contacts', 'Your browser doesn\'t support AJAX upload. Please upload the contacts file to ownCloud and import that way.'), t('contacts', 'Error')) + } + fileUpload = xhr.upload, + xhr.onreadystatechange = function() { + if (xhr.readyState == 4){ + response = $.parseJSON(xhr.responseText); + if(response.status == 'success') { + if(xhr.status == 200) { + $(Contacts.UI.Addressbooks.droptarget).html(t('contacts', 'Importing...')); + Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, true); + Contacts.UI.Addressbooks.doImport(response.data.path, response.data.file); + } else { + $(Contacts.UI.Addressbooks.droptarget).html(t('contacts', 'Drop a VCF file to import contacts.')); + Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false); + OC.dialogs.alert(xhr.status + ': ' + xhr.responseText, t('contacts', 'Error')); + } + } else { + OC.dialogs.alert(response.data.message, t('contacts', 'Error')); + } + } + }; + xhr.open("POST", 'ajax/uploadimport.php?file='+encodeURIComponent(file.name), true); + xhr.setRequestHeader('Cache-Control', 'no-cache'); + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + xhr.setRequestHeader('X_FILE_NAME', encodeURIComponent(file.name)); + xhr.setRequestHeader('X-File-Size', file.size); + xhr.setRequestHeader('Content-Type', file.type); + xhr.send(file); + } + }, + importAddressbook:function(object){ + var tr = $(document.createElement('tr')) + .load(OC.filePath('contacts', 'ajax', 'importaddressbook.php')); + $(object).closest('tr').after(tr).hide(); + }, + doImport:function(path, file){ + var id = $('#importaddressbook_dialog').find('#book').val(); + console.log('Selected book: ' + id); + $.post(OC.filePath('contacts', '', 'import.php'), { id: id, path: path, file: file, fstype: 'OC_FilesystemView' }, + function(jsondata){ + if(jsondata.status == 'success'){ + Contacts.UI.Addressbooks.droptarget.html(t('contacts', 'Import done. Success/Failure: ')+jsondata.data.imported+'/'+jsondata.data.failed); + $('#chooseaddressbook_dialog').find('#close_button').val(t('contacts', 'OK')); + Contacts.UI.Contacts.update(); + } else { + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false); }, submit:function(button, bookid){ var displayname = $("#displayname_"+bookid).val().trim(); @@ -1368,7 +1457,7 @@ Contacts={ } else { OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } - }); + }); }, cancel:function(button, bookid){ $(button).closest('tr').prev().show().next().remove(); @@ -1385,7 +1474,6 @@ Contacts={ } else{ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - //alert(jsondata.data.message); } }); setTimeout(Contacts.UI.Contacts.lazyupdate, 500); @@ -1507,13 +1595,13 @@ $(document).ready(function(){ }); $('#contacts_details_photo_wrapper').bind('dragover',function(event){ console.log('dragover'); - $(event.target).css('background-color','red'); + $(event.target).addClass('droppable'); event.stopPropagation(); event.preventDefault(); }); $('#contacts_details_photo_wrapper').bind('dragleave',function(event){ console.log('dragleave'); - $(event.target).css('background-color','white'); + $(event.target).removeClass('droppable'); //event.stopPropagation(); //event.preventDefault(); }); @@ -1521,7 +1609,7 @@ $(document).ready(function(){ event.stopPropagation(); event.preventDefault(); console.log('drop'); - $(event.target).css('background-color','white') + $(event.target).removeClass('droppable'); $.fileUpload(event.originalEvent.dataTransfer.files); }); @@ -1618,4 +1706,4 @@ $(document).ready(function() { $('.ui-autocomplete-loading').css('background', 'url('+OC.filePath('core', 'img', 'loading.gif')+' right center no-repeat'); $('#contacts_details_photo').css('background', 'url('+OC.filePath('core', 'img', 'loading.gif')+' no-repeat center center'); $('#contacts_propertymenu_button').css('background', 'url('+OC.filePath('core', 'img/actions', 'add.svg')+') no-repeat center'); -});
\ No newline at end of file +}); diff --git a/apps/contacts/lib/app.php b/apps/contacts/lib/app.php index 475d2c8dc2e..2c2cc331ed7 100644 --- a/apps/contacts/lib/app.php +++ b/apps/contacts/lib/app.php @@ -149,6 +149,7 @@ class OC_Contacts_App { 'WORK' => $l->t('Work'), 'TEXT' => $l->t('Text'), 'VOICE' => $l->t('Voice'), + 'MSG' => $l->t('Message'), 'FAX' => $l->t('Fax'), 'VIDEO' => $l->t('Video'), 'PAGER' => $l->t('Pager'), diff --git a/apps/contacts/lib/vcard.php b/apps/contacts/lib/vcard.php index 90b037f76ee..96fc8cf7121 100644 --- a/apps/contacts/lib/vcard.php +++ b/apps/contacts/lib/vcard.php @@ -141,10 +141,38 @@ class OC_Contacts_VCard{ } /** - * @brief Tries to update imported VCards to adhere to rfc2426 (VERSION: 3.0) + * @brief Checks if a contact with the same UID already exist in the address book. + * @param $aid Address book ID. + * @param $uid UID (passed by reference). + * @returns true if the UID has been changed. + */ + protected static function trueUID($aid, &$uid) { + $stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE addressbookid = ? AND uri = ?' ); + $uri = $uid.'.vcf'; + $result = $stmt->execute(array($aid,$uri)); + if($result->numRows() > 0){ + while(true) { + $tmpuid = substr(md5(rand().time()),0,10); + $uri = $tmpuid.'.vcf'; + $result = $stmt->execute(array($aid,$uri)); + if($result->numRows() > 0){ + continue; + } else { + $uid = $tmpuid; + return true; + } + } + } else { + return false; + } + } + + /** + * @brief Tries to update imported VCards to adhere to rfc2426 (VERSION: 3.0) and add mandatory fields if missing. + * @param aid Address book id. * @param vcard An OC_VObject of type VCARD (passed by reference). */ - protected static function updateValuesFromAdd(&$vcard) { // any suggestions for a better method name? ;-) + protected static function updateValuesFromAdd($aid, &$vcard) { // any suggestions for a better method name? ;-) $stringprops = array('N', 'FN', 'ORG', 'NICK', 'ADR', 'NOTE'); $typeprops = array('ADR', 'TEL', 'EMAIL'); $upgrade = false; @@ -207,14 +235,19 @@ class OC_Contacts_VCard{ } if(!$uid) { $vcard->setUID(); + $uid = $vcard->getAsString('UID'); OC_Log::write('contacts','OC_Contacts_VCard::updateValuesFromAdd. Added missing \'UID\' field: '.$uid,OC_Log::DEBUG); } + if(self::trueUID($aid, $uid)) { + $vcard->setString('UID', $uid); + } $vcard->setString('VERSION','3.0'); // Add product ID is missing. $prodid = trim($vcard->getAsString('PRODID')); if(!$prodid) { $appinfo = OC_App::getAppInfo('contacts'); - $prodid = '-//ownCloud//NONSGML '.$appinfo['name'].' '.$appinfo['version'].'//EN'; + $appversion = OC_App::getAppVersion('contacts'); + $prodid = '-//ownCloud//NONSGML '.$appinfo['name'].' '.$appversion.'//EN'; $vcard->setString('PRODID', $prodid); } $now = new DateTime; @@ -236,7 +269,7 @@ class OC_Contacts_VCard{ OC_Contacts_App::loadCategoriesFromVCard($card); - self::updateValuesFromAdd($card); + self::updateValuesFromAdd($aid, $card); $fn = $card->getAsString('FN'); if (empty($fn)) { diff --git a/apps/contacts/templates/part.chooseaddressbook.php b/apps/contacts/templates/part.chooseaddressbook.php index 90894220ef8..adfc8c15161 100644 --- a/apps/contacts/templates/part.chooseaddressbook.php +++ b/apps/contacts/templates/part.chooseaddressbook.php @@ -1,4 +1,4 @@ -<div id="chooseaddressbook_dialog" title="<?php echo $l->t("Choose active Address Books"); ?>"> +<div id="chooseaddressbook_dialog" title="<?php echo $l->t("Configure Address Books"); ?>"> <table width="100%" style="border: 0;"> <?php $option_addressbooks = OC_Contacts_Addressbook::all(OC_User::getUser()); @@ -14,6 +14,7 @@ for($i = 0; $i < count($option_addressbooks); $i++){ <tr> <td colspan="5" style="padding: 0.5em;"> <a class="button" href="#" onclick="Contacts.UI.Addressbooks.newAddressbook(this);"><?php echo $l->t('New Address Book') ?></a> + <a class="button" href="#" onclick="Contacts.UI.Addressbooks.importAddressbook(this);"><?php echo $l->t('Import from VCF') ?></a> </td> </tr> <tr> diff --git a/apps/contacts/templates/part.chooseaddressbook.rowfields.php b/apps/contacts/templates/part.chooseaddressbook.rowfields.php index 8518c1acd13..20b67a4161e 100644 --- a/apps/contacts/templates/part.chooseaddressbook.rowfields.php +++ b/apps/contacts/templates/part.chooseaddressbook.rowfields.php @@ -2,4 +2,4 @@ // FIXME: Make this readable. echo "<td width=\"20px\"><input id=\"active_" . $_['addressbook']["id"] . "\" type=\"checkbox\" onClick=\"Contacts.UI.Addressbooks.activation(this, " . $_['addressbook']["id"] . ")\"" . (OC_Contacts_Addressbook::isActive($_['addressbook']["id"]) ? ' checked="checked"' : '') . "></td>"; echo "<td><label for=\"active_" . $_['addressbook']["id"] . "\">" . htmlspecialchars($_['addressbook']["displayname"]) . "</label></td>"; - echo "<td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.showCardDAVUrl('" . OC_User::getUser() . "', '" . rawurlencode($_['addressbook']["uri"]) . "');\" title=\"" . $l->t("CardDav Link") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/public.svg\"></a></td><td width=\"20px\"><a href=\"export.php?bookid=" . $_['addressbook']["id"] . "\" title=\"" . $l->t("Download") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/download.svg\"></a></td><td width=\"20px\"><a href=\"#\" title=\"" . $l->t("Edit") . "\" class=\"action\" onclick=\"Contacts.UI.Addressbooks.editAddressbook(this, " . $_['addressbook']["id"] . ");\"><img class=\"svg action\" src=\"../../core/img/actions/rename.svg\"></a></td><td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.Addressbooks.deleteAddressbook('" . $_['addressbook']["id"] . "');\" title=\"" . $l->t("Delete") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/delete.svg\"></a></td>"; + echo "<td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.showCardDAVUrl('" . OC_User::getUser() . "', '" . rawurlencode($_['addressbook']["uri"]) . "');\" title=\"" . $l->t("CardDav Link") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/public.svg\"></a></td><td width=\"20px\"><a href=\"export.php?bookid=" . $_['addressbook']["id"] . "\" title=\"" . $l->t("Download") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/download.svg\"></a></td><td width=\"20px\"><a href=\"#\" title=\"" . $l->t("Edit") . "\" class=\"action\" onclick=\"Contacts.UI.Addressbooks.editAddressbook(this, " . $_['addressbook']["id"] . ");\"><img class=\"svg action\" src=\"../../core/img/actions/rename.svg\"></a></td><td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.Addressbooks.deleteAddressbook(this, '" . $_['addressbook']["id"] . "');\" title=\"" . $l->t("Delete") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/delete.svg\"></a></td>"; diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php index b90fa92c2c5..dec081a9b89 100644 --- a/apps/contacts/templates/part.contact.php +++ b/apps/contacts/templates/part.contact.php @@ -112,7 +112,7 @@ $id = isset($_['id']) ? $_['id'] : ''; <div id="contact_note" class="contactsection"> <form class="float" method="post"> <fieldset id="note" class="formfloat propertycontainer contactpart" data-element="NOTE"> - <textarea class="contacts_property note" name="value" cols="40" rows="10" required="required" placeholder="<?php echo $l->t('Add notes here.'); ?>"></textarea> + <textarea class="contacts_property note" name="value" cols="60" rows="15" required="required" placeholder="<?php echo $l->t('Add notes here.'); ?>"></textarea> </fieldset> </form> </div> <!-- contact_note --> diff --git a/apps/files_versioning/ajax/gethead.php b/apps/files_versioning/ajax/gethead.php deleted file mode 100644 index a0bfe77db51..00000000000 --- a/apps/files_versioning/ajax/gethead.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php -/** - * Copyright (c) 2011 Craig Roberts craig0990@googlemail.com - * This file is licensed under the Affero General Public License version 3 or - * later. - */ - - -OC_JSON::checkLoggedIn(); -// Fetch current commit (or HEAD if not yet set) -$head = OC_Preferences::getValue(OC_User::getUser(), 'files_versioning', 'head', 'HEAD'); -OC_JSON::encodedPrint(array("head" => $head)); diff --git a/apps/files_versioning/ajax/sethead.php b/apps/files_versioning/ajax/sethead.php deleted file mode 100644 index dd8b924b118..00000000000 --- a/apps/files_versioning/ajax/sethead.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php -/** - * Copyright (c) 2011 Craig Roberts craig0990@googlemail.com - * This file is licensed under the Affero General Public License version 3 or - * later. - */ - -OC_JSON::checkLoggedIn(); -if(isset($_POST["file_versioning_head"])){ - OC_Preferences::setValue(OC_User::getUser(), 'files_versioning', 'head', $_POST["file_versioning_head"]); - OC_JSON::success(); -}else{ - OC_JSON::error(); -} diff --git a/apps/files_versioning/appinfo/app.php b/apps/files_versioning/appinfo/app.php deleted file mode 100644 index 24a8701dbb0..00000000000 --- a/apps/files_versioning/appinfo/app.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -// Include required files -require_once('apps/files_versioning/versionstorage.php'); -require_once('apps/files_versioning/versionwrapper.php'); -// Register streamwrapper for versioned:// paths -stream_wrapper_register('versioned', 'OC_VersionStreamWrapper'); - -// Add an entry in the app list for versioning and backup -OC_App::register( array( - 'order' => 10, - 'id' => 'files_versioning', - 'name' => 'Versioning and Backup' )); - -// Include stylesheets for the settings page -OC_Util::addStyle( 'files_versioning', 'settings' ); -OC_Util::addScript('files_versioning','settings'); - -// Register a settings section in the Admin > Personal page -OC_APP::registerPersonal('files_versioning','settings'); diff --git a/apps/files_versioning/appinfo/info.xml b/apps/files_versioning/appinfo/info.xml deleted file mode 100644 index b9f56f674a0..00000000000 --- a/apps/files_versioning/appinfo/info.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0"?> -<info> - <id>files_versioning</id> - <name>Versioning and Backup</name> - <licence>GPLv2</licence> - <author>Craig Roberts</author> - <require>3</require> - <description>Versions files using Git repositories, providing a simple backup facility. Currently in *beta* and explicitly without warranty of any kind.</description> - <types> - <filesystem/> - </types> -</info> diff --git a/apps/files_versioning/css/settings.css b/apps/files_versioning/css/settings.css deleted file mode 100644 index afe2cd5508f..00000000000 --- a/apps/files_versioning/css/settings.css +++ /dev/null @@ -1,3 +0,0 @@ -#file_versioning_commit_chzn { - width: 15em; -} diff --git a/apps/files_versioning/js/settings.js b/apps/files_versioning/js/settings.js deleted file mode 100644 index 8dd13bac033..00000000000 --- a/apps/files_versioning/js/settings.js +++ /dev/null @@ -1,25 +0,0 @@ -$(document).ready(function(){ - $('#file_versioning_head').chosen(); - - $.getJSON(OC.filePath('files_versioning', 'ajax', 'gethead.php'), function(jsondata, status) { - - if (jsondata.head == 'HEAD') { - // Most recent commit, do nothing - } else { - $("#file_versioning_head").val(jsondata.head); - // Trigger the chosen update call - // See http://harvesthq.github.com/chosen/ - $("#file_versioning_head").trigger("liszt:updated"); - } - }); - - $('#file_versioning_head').change(function() { - - var data = $(this).serialize(); - $.post( OC.filePath('files_versioning', 'ajax', 'sethead.php'), data, function(data){ - if(data == 'error'){ - console.log('Saving new HEAD failed'); - } - }); - }); -}); diff --git a/apps/files_versioning/lib_granite.php b/apps/files_versioning/lib_granite.php deleted file mode 100644 index 571e5cea637..00000000000 --- a/apps/files_versioning/lib_granite.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php - -require_once('granite/git/blob.php'); -require_once('granite/git/commit.php'); -require_once('granite/git/repository.php'); -require_once('granite/git/tag.php'); -require_once('granite/git/tree.php'); -require_once('granite/git/tree/node.php'); -require_once('granite/git/object/index.php'); -require_once('granite/git/object/raw.php'); -require_once('granite/git/object/loose.php'); -require_once('granite/git/object/packed.php'); diff --git a/apps/files_versioning/settings.php b/apps/files_versioning/settings.php deleted file mode 100644 index 94af587a215..00000000000 --- a/apps/files_versioning/settings.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -// Get the full path to the repository folder (FIXME: hard-coded to 'Backup') -$path = OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data') - . DIRECTORY_SEPARATOR - . OC_User::getUser() - . DIRECTORY_SEPARATOR - . 'files' - . DIRECTORY_SEPARATOR - . 'Backup' - . DIRECTORY_SEPARATOR - . '.git' - . DIRECTORY_SEPARATOR; - -$repository = new Granite\Git\Repository($path); - -$commits = array(); -// Fetch most recent 50 commits (FIXME - haven't tested this much) -$commit = $repository->head(); -for ($i = 0; $i < 50; $i++) { - $commits[] = $commit; - $parents = $commit->parents(); - if (count($parents) > 0) { - $parent = $parents[0]; - } else { - break; - } - - $commit = $repository->factory('commit', $parent); -} - -$tmpl = new OC_Template( 'files_versioning', 'settings'); -$tmpl->assign('commits', $commits); -return $tmpl->fetchPage(); diff --git a/apps/files_versioning/templates/settings.php b/apps/files_versioning/templates/settings.php deleted file mode 100644 index 17f4cc7f77f..00000000000 --- a/apps/files_versioning/templates/settings.php +++ /dev/null @@ -1,12 +0,0 @@ -<fieldset id="status_list" class="personalblock"> - <strong>Versioning and Backup</strong><br> - <p><em>Please note: Backing up large files (around 16MB+) will cause your backup history to grow very large, very quickly.</em></p> - <label class="bold">Backup Folder</label> - <select name="file_versioning_head" id="file_versioning_head"> - <?php - foreach ($_['commits'] as $commit): - echo '<option value="' . $commit->sha() . '">' . $commit->message() . '</option>'; - endforeach; - ?> - </select> -</fieldset> diff --git a/apps/files_versioning/versionstorage.php b/apps/files_versioning/versionstorage.php deleted file mode 100644 index d083e623df9..00000000000 --- a/apps/files_versioning/versionstorage.php +++ /dev/null @@ -1,386 +0,0 @@ -<?php -/** - * ownCloud file storage implementation for Git repositories - * @author Craig Roberts - * @copyright 2012 Craig Roberts craig0990@googlemail.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ - -// Include Granite -require_once('lib_granite.php'); - -// Create a top-level 'Backup' directory if it does not already exist -$user = OC_User::getUser(); -if (OC_Filesystem::$loaded and !OC_Filesystem::is_dir('/Backup')) { - OC_Filesystem::mkdir('/Backup'); - OC_Preferences::setValue(OC_User::getUser(), 'files_versioning', 'head', 'HEAD'); -} - -// Generate the repository path (currently using 'full' repositories, as opposed to bare ones) -$repo_path = DIRECTORY_SEPARATOR - . OC_User::getUser() - . DIRECTORY_SEPARATOR - . 'files' - . DIRECTORY_SEPARATOR - . 'Backup'; - -// Mount the 'Backup' folder using the versioned storage provider below -OC_Filesystem::mount('OC_Filestorage_Versioned', array('repo'=>$repo_path), $repo_path . DIRECTORY_SEPARATOR); - -class OC_Filestorage_Versioned extends OC_Filestorage { - - /** - * Holds an instance of Granite\Git\Repository - */ - protected $repo; - - /** - * Constructs a new OC_Filestorage_Versioned instance, expects an associative - * array with a `repo` key set to the path of the repository's `.git` folder - * - * @param array $parameters An array containing the key `repo` pointing to the - * repository path. - */ - public function __construct($parameters) { - // Get the full path to the repository folder - $path = OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data') - . $parameters['repo'] - . DIRECTORY_SEPARATOR - . '.git' - . DIRECTORY_SEPARATOR; - - try { - // Attempt to load the repository - $this->repo = new Granite\Git\Repository($path); - } catch (InvalidArgumentException $e) { - // $path is not a valid Git repository, we must create one - Granite\Git\Repository::init($path); - - // Load the newly-initialised repository - $this->repo = new Granite\Git\Repository($path); - - /** - * Create an initial commit with a README file - * FIXME: This functionality should be transferred to the Granite library - */ - $blob = new Granite\Git\Blob($this->repo->path()); - $blob->content('Your Backup directory is now ready for use.'); - - // Create a new tree to hold the README file - $tree = $this->repo->factory('tree'); - // Create a tree node to represent the README blob - $tree_node = new Granite\Git\Tree\Node('README', '100644', $blob->sha()); - $tree->nodes(array($tree_node->name() => $tree_node)); - - // Create an initial commit - $commit = new Granite\Git\Commit($this->repo->path()); - $user_string = OC_User::getUser() . ' ' . time() . ' +0000'; - $commit->author($user_string); - $commit->committer($user_string); - $commit->message('Initial commit'); - $commit->tree($tree); - - // Write it all to disk - $blob->write(); - $tree->write(); - $commit->write(); - - // Update the HEAD for the 'master' branch - $this->repo->head('master', $commit->sha()); - } - - // Update the class pointer to the HEAD - $head = OC_Preferences::getValue(OC_User::getUser(), 'files_versioning', 'head', 'HEAD'); - - // Load the most recent commit if the preference is not set - if ($head == 'HEAD') { - $this->head = $this->repo->head()->sha(); - } else { - $this->head = $head; - } - } - - public function mkdir($path) { - if (mkdir("versioned:/{$this->repo->path()}$path#{$this->head}")) { - $this->head = $this->repo->head()->sha(); - OC_Preferences::setValue(OC_User::getUser(), 'files_versioning', 'head', $head); - return true; - } - - return false; - } - - public function rmdir($path) { - - } - - /** - * Returns a directory handle to the requested path, or FALSE on failure - * - * @param string $path The directory path to open - * - * @return boolean|resource A directory handle, or FALSE on failure - */ - public function opendir($path) { - return opendir("versioned:/{$this->repo->path()}$path#{$this->head}"); - } - - /** - * Returns TRUE if $path is a directory, or FALSE if not - * - * @param string $path The path to check - * - * @return boolean - */ - public function is_dir($path) { - return $this->filetype($path) == 'dir'; - } - - /** - * Returns TRUE if $path is a file, or FALSE if not - * - * @param string $path The path to check - * - * @return boolean - */ - public function is_file($path) { - return $this->filetype($path) == 'file'; - } - - public function stat($path) - { - return stat("versioned:/{$this->repo->path()}$path#{$this->head}"); - } - - /** - * Returns the strings 'dir' or 'file', depending on the type of $path - * - * @param string $path The path to check - * - * @return string Returns 'dir' if a directory, 'file' otherwise - */ - public function filetype($path) { - if ($path == "" || $path == "/") { - return 'dir'; - } else { - if (substr($path, -1) == '/') { - $path = substr($path, 0, -1); - } - - $node = $this->tree_search($this->repo, $this->repo->factory('commit', $this->head)->tree(), $path); - - // Does it exist, or is it new? - if ($node == null) { - // New file - return 'file'; - } else { - // Is it a tree? - try { - $this->repo->factory('tree', $node); - return 'dir'; - } catch (InvalidArgumentException $e) { - // Nope, must be a blob - return 'file'; - } - } - } - } - - public function filesize($path) { - return filesize("versioned:/{$this->repo->path()}$path#{$this->head}"); - } - - /** - * Returns a boolean value representing whether $path is readable - * - * @param string $path The path to check - *( - * @return boolean Whether or not the path is readable - */ - public function is_readable($path) { - return true; - } - - /** - * Returns a boolean value representing whether $path is writable - * - * @param string $path The path to check - *( - * @return boolean Whether or not the path is writable - */ - public function is_writable($path) { - - $head = OC_Preferences::getValue(OC_User::getUser(), 'files_versioning', 'head', 'HEAD'); - if ($head !== 'HEAD' && $head !== $this->repo->head()->sha()) { - // Cannot modify previous commits - return false; - } - return true; - } - - /** - * Returns a boolean value representing whether $path exists - * - * @param string $path The path to check - *( - * @return boolean Whether or not the path exists - */ - public function file_exists($path) { - return file_exists("versioned:/{$this->repo->path()}$path#{$this->head}"); - } - - /** - * Returns an integer value representing the inode change time - * (NOT IMPLEMENTED) - * - * @param string $path The path to check - *( - * @return int Timestamp of the last inode change - */ - public function filectime($path) { - return -1; - } - - /** - * Returns an integer value representing the file modification time - * - * @param string $path The path to check - *( - * @return int Timestamp of the last file modification - */ - public function filemtime($path) { - return filemtime("versioned:/{$this->repo->path()}$path#{$this->head}"); - } - - public function file_get_contents($path) { - return file_get_contents("versioned:/{$this->repo->path()}$path#{$this->head}"); - } - - public function file_put_contents($path, $data) { - $success = file_put_contents("versioned:/{$this->repo->path()}$path#{$this->head}", $data); - if ($success !== false) { - // Update the HEAD in the preferences - OC_Preferences::setValue(OC_User::getUser(), 'files_versioning', 'head', $this->repo->head()->sha()); - return $success; - } - - return false; - } - - public function unlink($path) { - - } - - public function rename($path1, $path2) { - - } - - public function copy($path1, $path2) { - - } - - public function fopen($path, $mode) { - return fopen("versioned:/{$this->repo->path()}$path#{$this->head}", $mode); - } - - public function getMimeType($path) { - if ($this->filetype($path) == 'dir') { - return 'httpd/unix-directory'; - } elseif ($this->filesize($path) == 0) { - // File's empty, returning text/plain allows opening in the web editor - return 'text/plain'; - } else { - $finfo = new finfo(FILEINFO_MIME_TYPE); - /** - * We need to represent the repository path, the file path, and the - * revision, which can be simply achieved with a convention of using - * `.git` in the repository directory (bare or not) and the '#part' - * segment of a URL to specify the revision. For example - * - * versioned://var/www/myrepo.git/docs/README.md#HEAD ('bare' repo) - * versioned://var/www/myrepo/.git/docs/README.md#HEAD ('full' repo) - * versioned://var/www/myrepo/.git/docs/README.md#6a8f...8a54 ('full' repo and SHA-1 commit ID) - */ - $mime = $finfo->buffer(file_get_contents("versioned:/{$this->repo->path()}$path#{$this->head}")); - return $mime; - } - } - - /** - * Generates a hash based on the file contents - * - * @param string $type The hashing algorithm to use (e.g. 'md5', 'sha256', etc.) - * @param string $path The file to be hashed - * @param boolean $raw Outputs binary data if true, lowercase hex digits otherwise - * - * @return string Hashed string representing the file contents - */ - public function hash($type, $path, $raw) { - return hash($type, file_get_contents($path), $raw); - } - - public function free_space($path) { - } - - public function search($query) { - - } - - public function touch($path, $mtime=null) { - - } - - - public function getLocalFile($path) { - } - - /** - * Recursively searches a tree for a path, returning FALSE if is not found - * or an SHA-1 id if it is found. - * - * @param string $repo The repository containing the tree object - * @param string $tree The tree object to search - * @param string $path The path to search for (relative to the tree) - * @param int $depth The depth of the current search (for recursion) - * - * @return string|boolean The SHA-1 id of the sub-tree - */ - private function tree_search($repo, $tree, $path, $depth = 0) - { - $paths = array_values(explode(DIRECTORY_SEPARATOR, $path)); - - $current_path = $paths[$depth]; - - $nodes = $tree->nodes(); - foreach ($nodes as $node) { - if ($node->name() == $current_path) { - - if (count($paths)-1 == $depth) { - // Stop, found it - return $node->sha(); - } - - // Recurse if necessary - if ($node->isDirectory()) { - $tree = $this->repo->factory('tree', $node->sha()); - return $this->tree_search($repo, $tree, $path, $depth + 1); - } - } - } - - return false; - } - -} diff --git a/apps/files_versioning/versionwrapper.php b/apps/files_versioning/versionwrapper.php deleted file mode 100644 index b83a4fd3b22..00000000000 --- a/apps/files_versioning/versionwrapper.php +++ /dev/null @@ -1,686 +0,0 @@ -<?php - -final class OC_VersionStreamWrapper { - - /** - * Determines whether or not to log debug messages with `OC_Log::write()` - */ - private $debug = true; - - /** - * The name of the ".empty" files created in new directories - */ - const EMPTYFILE = '.empty'; - - /** - * Stores the current position for `readdir()` etc. calls - */ - private $dir_position = 0; - - /** - * Stores the current position for `fread()`, `fseek()` etc. calls - */ - private $file_position = 0; - - /** - * Stores the current directory tree for `readdir()` etc. directory traversal - */ - private $tree; - - /** - * Stores the current file for `fread()`, `fseek()`, etc. calls - */ - private $blob; - - /** - * Stores the current commit for `fstat()`, `stat()`, etc. calls - */ - private $commit; - - /** - * Stores the current path for `fwrite()`, `file_put_contents()` etc. calls - */ - private $path; - - /** - * Close directory handle - */ - public function dir_closedir() { - unset($this->tree); - return true; - } - - /** - * Open directory handle - */ - public function dir_opendir($path, $options) { - // Parse the URL into a repository directory, file path and commit ID - list($this->repo, $repo_file, $this->commit) = $this->parse_url($path); - - if ($repo_file == '' || $repo_file == '/') { - // Set the tree property for the future `readdir()` etc. calls - $this->tree = array_values($this->commit->tree()->nodes()); - return true; - } elseif ($this->tree_search($this->repo, $this->commit->tree(), $repo_file) !== false) { - // Something exists at this path, is it a directory though? - try { - $tree = $this->repo->factory( - 'tree', - $this->tree_search($this->repo, $this->commit->tree(), $repo_file) - ); - $this->tree = array_values($tree->nodes()); - return true; - } catch (InvalidArgumentException $e) { - // Trying to call `opendir()` on a file, return false below - } - } - - // Unable to find the directory, return false - return false; - } - - /** - * Read entry from directory handle - */ - public function dir_readdir() { - return isset($this->tree[$this->dir_position]) - ? $this->tree[$this->dir_position++]->name() - : false; - } - - /** - * Rewind directory handle - */ - public function dir_rewinddir() { - $this->dir_position = 0; - } - - /** - * Create a directory - * Git doesn't track empty directories, so a ".empty" file is added instead - */ - public function mkdir($path, $mode, $options) { - // Parse the URL into a repository directory, file path and commit ID - list($this->repo, $repo_file, $this->commit) = $this->parse_url($path); - - // Create an empty file for Git - $empty = new Granite\Git\Blob($this->repo->path()); - $empty->content(''); - $empty->write(); - - if (dirname($repo_file) == '.') { - // Adding a new directory to the root tree - $tree = $this->repo->head()->tree(); - } else { - $tree = $this->repo->factory('tree', $this->tree_search( - $this->repo, $this->repo->head()->tree(), dirname($repo_file) - ) - ); - } - - // Create our new tree, with our empty file - $dir = $this->repo->factory('tree'); - $nodes = array(); - $nodes[self::EMPTYFILE] = new Granite\Git\Tree\Node(self::EMPTYFILE, '100644', $empty->sha()); - $dir->nodes($nodes); - $dir->write(); - - // Add our new tree to its parent - $nodes = $tree->nodes(); - $nodes[basename($repo_file)] = new Granite\Git\Tree\Node(basename($repo_file), '040000', $dir->sha()); - $tree->nodes($nodes); - $tree->write(); - - // We need to recursively update each parent tree, since they are all - // hashed and the changes will cascade back up the chain - - // So, we're currently at the bottom-most directory - $current_dir = dirname($repo_file); - $previous_tree = $tree; - - if ($current_dir !== '.') { - do { - // Determine the parent directory - $previous_dir = $current_dir; - $current_dir = dirname($current_dir); - - $current_tree = $current_dir !== '.' - ? $this->repo->factory( - 'tree', $this->tree_search( - $this->repo, - $this->repo->head()->tree(), - $current_dir - ) - ) - : $this->repo->head()->tree(); - - $current_nodes = $current_tree->nodes(); - $current_nodes[basename($previous_dir)] = new Granite\Git\Tree\Node( - basename($previous_dir), '040000', $previous_tree->sha() - ); - $current_tree->nodes($current_nodes); - $current_tree->write(); - - $previous_tree = $current_tree; - } while ($current_dir !== '.'); - - $tree = $previous_tree; - } - - // Create a new commit to represent this write - $commit = $this->repo->factory('commit'); - $username = OC_User::getUser(); - $user_string = $username . ' ' . time() . ' +0000'; - $commit->author($user_string); - $commit->committer($user_string); - $commit->message("$username created the `$repo_file` directory, " . date('d F Y H:i', time()) . '.'); - $commit->parents(array($this->repo->head()->sha())); - $commit->tree($tree); - - // Write it to disk - $commit->write(); - - // Update the HEAD for the 'master' branch - $this->repo->head('master', $commit->sha()); - - return true; - } - - /** - * Renames a file or directory - */ - public function rename($path_from, $path_to) { - - } - - /** - * Removes a directory - */ - public function rmdir($path, $options) { - - } - - /** - * Retrieve the underlaying resource (NOT IMPLEMENTED) - */ - public function stream_cast($cast_as) { - return false; - } - - /** - * Close a resource - */ - public function stream_close() { - unset($this->blob); - return true; - } - - /** - * Tests for end-of-file on a file pointer - */ - public function stream_eof() { - return !($this->file_position < strlen($this->blob)); - } - - /** - * Flushes the output (NOT IMPLEMENTED) - */ - public function stream_flush() { - return false; - } - - /** - * Advisory file locking (NOT IMPLEMENTED) - */ - public function stream_lock($operation) { - return false; - } - - /** - * Change stream options (NOT IMPLEMENTED) - * Called in response to `chgrp()`, `chown()`, `chmod()` and `touch()` - */ - public function stream_metadata($path, $option, $var) { - return false; - } - - /** - * Opens file or URL - */ - public function stream_open($path, $mode, $options, &$opened_path) { - // Store the path, so we can use it later in `stream_write()` if necessary - $this->path = $path; - // Parse the URL into a repository directory, file path and commit ID - list($this->repo, $repo_file, $this->commit) = $this->parse_url($path); - - $file = $this->tree_search($this->repo, $this->commit->tree(), $repo_file); - if ($file !== false) { - try { - $this->blob = $this->repo->factory('blob', $file)->content(); - return true; - } catch (InvalidArgumentException $e) { - // Trying to open a directory, return false below - } - } elseif ($mode !== 'r') { - // All other modes allow opening for reading and writing, clearly - // some 'write' files may not exist yet... - return true; - } - - // File could not be found or is not actually a file - return false; - } - - /** - * Read from stream - */ - public function stream_read($count) { - // Fetch the remaining set of bytes - $bytes = substr($this->blob, $this->file_position, $count); - - // If EOF or empty string, return false - if ($bytes == '' || $bytes == false) { - return false; - } - - // If $count does not extend past EOF, add $count to stream offset - if ($this->file_position + $count < strlen($this->blob)) { - $this->file_position += $count; - } else { - // Otherwise return all remaining bytes - $this->file_position = strlen($this->blob); - } - - return $bytes; - } - - /** - * Seeks to specific location in a stream - */ - public function stream_seek($offset, $whence = SEEK_SET) { - $new_offset = false; - - switch ($whence) - { - case SEEK_SET: - $new_offset = $offset; - break; - case SEEK_CUR: - $new_offset = $this->file_position += $offset; - break; - case SEEK_END: - $new_offset = strlen($this->blob) + $offset; - break; - } - - $this->file_position = $offset; - - return ($new_offset !== false); - } - - /** - * Change stream options (NOT IMPLEMENTED) - */ - public function stream_set_option($option, $arg1, $arg2) { - return false; - } - - /** - * Retrieve information about a file resource (NOT IMPLEMENTED) - */ - public function stream_stat() { - - } - - /** - * Retrieve the current position of a stream - */ - public function stream_tell() { - return $this->file_position; - } - - /** - * Truncate stream - */ - public function stream_truncate($new_size) { - - } - - /** - * Write to stream - * FIXME: Could use heavy refactoring - */ - public function stream_write($data) { - /** - * FIXME: This also needs to be added to Granite, in the form of `add()`, - * `rm()` and `commit()` calls - */ - - // Parse the URL into a repository directory, file path and commit ID - list($this->repo, $repo_file, $this->commit) = $this->parse_url($this->path); - - $node = $this->tree_search($this->repo, $this->commit->tree(), $repo_file); - - if ($node !== false) { - // File already exists, attempting modification of existing tree - try { - $this->repo->factory('blob', $node); - - // Create our new blob with the provided $data - $blob = $this->repo->factory('blob'); - $blob->content($data); - $blob->write(); - - // We know the tree exists, so strip the filename from the path and - // find it... - - if (dirname($repo_file) == '.' || dirname($repo_file) == '') { - // Root directory - $tree = $this->repo->head()->tree(); - } else { - // Sub-directory - $tree = $this->repo->factory('tree', $this->tree_search( - $this->repo, - $this->repo->head()->tree(), - dirname($repo_file) - ) - ); - } - - // Replace the old blob with our newly modified one - $tree_nodes = $tree->nodes(); - $tree_nodes[basename($repo_file)] = new Granite\Git\Tree\Node( - basename($repo_file), '100644', $blob->sha() - ); - $tree->nodes($tree_nodes); - $tree->write(); - - // We need to recursively update each parent tree, since they are all - // hashed and the changes will cascade back up the chain - - // So, we're currently at the bottom-most directory - $current_dir = dirname($repo_file); - $previous_tree = $tree; - - if ($current_dir !== '.') { - do { - // Determine the parent directory - $previous_dir = $current_dir; - $current_dir = dirname($current_dir); - - $current_tree = $current_dir !== '.' - ? $this->repo->factory( - 'tree', $this->tree_search( - $this->repo, - $this->repo->head()->tree(), - $current_dir - ) - ) - : $this->repo->head()->tree(); - - $current_nodes = $current_tree->nodes(); - $current_nodes[basename($previous_dir)] = new Granite\Git\Tree\Node( - basename($previous_dir), '040000', $previous_tree->sha() - ); - $current_tree->nodes($current_nodes); - $current_tree->write(); - - $previous_tree = $current_tree; - } while ($current_dir !== '.'); - } - - // Create a new commit to represent this write - $commit = $this->repo->factory('commit'); - $username = OC_User::getUser(); - $user_string = $username . ' ' . time() . ' +0000'; - $commit->author($user_string); - $commit->committer($user_string); - $commit->message("$username modified the `$repo_file` file, " . date('d F Y H:i', time()) . '.'); - $commit->parents(array($this->repo->head()->sha())); - $commit->tree($previous_tree); - - // Write it to disk - $commit->write(); - - // Update the HEAD for the 'master' branch - $this->repo->head('master', $commit->sha()); - - // If we made it this far, write was successful - update the stream - // position and return the number of bytes written - $this->file_position += strlen($data); - return strlen($data); - - } catch (InvalidArgumentException $e) { - // Attempting to write to a directory or other error, fail - return 0; - } - } else { - // File does not exist, needs to be created - - // Create our new blob with the provided $data - $blob = $this->repo->factory('blob'); - $blob->content($data); - $blob->write(); - - if (dirname($repo_file) == '.') { - // Trying to add a new file to the root tree, nice and easy - $tree = $this->repo->head()->tree(); - $tree_nodes = $tree->nodes(); - $tree_nodes[basename($repo_file)] = new Granite\Git\Tree\Node( - basename($repo_file), '100644', $blob->sha() - ); - $tree->nodes($tree_nodes); - $tree->write(); - } else { - // Trying to add a new file to a subdirectory, try and find it - $tree = $this->repo->factory('tree', $this->tree_search( - $this->repo, $this->repo->head()->tree(), dirname($repo_file) - ) - ); - - // Add the blob to the tree - $nodes = $tree->nodes(); - $nodes[basename($repo_file)] = new Granite\Git\Tree\Node( - basename($repo_file), '100644', $blob->sha() - ); - $tree->nodes($nodes); - $tree->write(); - - // We need to recursively update each parent tree, since they are all - // hashed and the changes will cascade back up the chain - - // So, we're currently at the bottom-most directory - $current_dir = dirname($repo_file); - $previous_tree = $tree; - - if ($current_dir !== '.') { - do { - // Determine the parent directory - $previous_dir = $current_dir; - $current_dir = dirname($current_dir); - - $current_tree = $current_dir !== '.' - ? $this->repo->factory( - 'tree', $this->tree_search( - $this->repo, - $this->repo->head()->tree(), - $current_dir - ) - ) - : $this->repo->head()->tree(); - - $current_nodes = $current_tree->nodes(); - $current_nodes[basename($previous_dir)] = new Granite\Git\Tree\Node( - basename($previous_dir), '040000', $previous_tree->sha() - ); - $current_tree->nodes($current_nodes); - $current_tree->write(); - - $previous_tree = $current_tree; - } while ($current_dir !== '.'); - - $tree = $previous_tree; - } - } - - // Create a new commit to represent this write - $commit = $this->repo->factory('commit'); - $username = OC_User::getUser(); - $user_string = $username . ' ' . time() . ' +0000'; - $commit->author($user_string); - $commit->committer($user_string); - $commit->message("$username created the `$repo_file` file, " . date('d F Y H:i', time()) . '.'); - $commit->parents(array($this->repo->head()->sha())); - $commit->tree($tree); // Top-level tree (NOT the newly modified tree) - - // Write it to disk - $commit->write(); - - // Update the HEAD for the 'master' branch - $this->repo->head('master', $commit->sha()); - - // If we made it this far, write was successful - update the stream - // position and return the number of bytes written - $this->file_position += strlen($data); - return strlen($data); - } - - // Write failed - return 0; - } - - /** - * Delete a file - */ - public function unlink($path) { - - } - - /** - * Retrieve information about a file - */ - public function url_stat($path, $flags) { - // Parse the URL into a repository directory, file path and commit ID - list($this->repo, $repo_file, $this->commit) = $this->parse_url($path); - - $node = $this->tree_search($this->repo, $this->commit->tree(), $repo_file); - - if ($node == false && $this->commit->sha() == $this->repo->head()->sha()) { - // A new file - no information available - $size = 0; - $mtime = -1; - } else { - - // Is it a directory? - try { - $this->repo->factory('tree', $node); - $size = 4096; // FIXME - } catch (InvalidArgumentException $e) { - // Must be a file - $size = strlen(file_get_contents($path)); - } - - // Parse the timestamp from the commit message - preg_match('/[0-9]{10}+/', $this->commit->committer(), $matches); - $mtime = $matches[0]; - } - - $stat["dev"] = ""; - $stat["ino"] = ""; - $stat["mode"] = ""; - $stat["nlink"] = ""; - $stat["uid"] = ""; - $stat["gid"] = ""; - $stat["rdev"] = ""; - $stat["size"] = $size; - $stat["atime"] = $mtime; - $stat["mtime"] = $mtime; - $stat["ctime"] = $mtime; - $stat["blksize"] = ""; - $stat["blocks"] = ""; - - return $stat; - } - - /** - * Debug function for development purposes - */ - private function debug($message, $level = OC_Log::DEBUG) - { - if ($this->debug) { - OC_Log::write('files_versioning', $message, $level); - } - } - - /** - * Parses a URL of the form: - * `versioned://path/to/git/repository/.git/path/to/file#SHA-1-commit-id` - * FIXME: Will throw an InvalidArgumentException if $path is invaid - * - * @param string $path The path to parse - * - * @return array An array containing an instance of Granite\Git\Repository, - * the file path, and an instance of Granite\Git\Commit - * @throws InvalidArgumentException If the repository cannot be loaded - */ - private function parse_url($path) - { - preg_match('/\/([A-Za-z0-9\/]+\.git\/)([A-Za-z0-9\/\.\/]*)(#([A-Fa-f0-9]+))*/', $path, $matches); - - // Load up the repo - $repo = new \Granite\Git\Repository($matches[1]); - // Parse the filename (stripping any trailing slashes) - $repo_file = $matches[2]; - if (substr($repo_file, -1) == '/') { - $repo_file = substr($repo_file, 0, -1); - } - - // Default to HEAD if no commit is provided - $repo_commit = isset($matches[4]) - ? $matches[4] - : $repo->head()->sha(); - - // Load the relevant commit - $commit = $repo->factory('commit', $repo_commit); - - return array($repo, $repo_file, $commit); - } - - /** - * Recursively searches a tree for a path, returning FALSE if is not found - * or an SHA-1 id if it is found. - * - * @param string $repo The repository containing the tree object - * @param string $tree The tree object to search - * @param string $path The path to search for (relative to the tree) - * @param int $depth The depth of the current search (for recursion) - * - * @return string|boolean The SHA-1 id of the sub-tree - */ - private function tree_search($repo, $tree, $path, $depth = 0) - { - $paths = array_values(explode(DIRECTORY_SEPARATOR, $path)); - - $current_path = $paths[$depth]; - - $nodes = $tree->nodes(); - foreach ($nodes as $node) { - if ($node->name() == $current_path) { - - if (count($paths)-1 == $depth) { - // Stop, found it - return $node->sha(); - } - - // Recurse if necessary - if ($node->isDirectory()) { - $tree = $this->repo->factory('tree', $node->sha()); - return $this->tree_search($repo, $tree, $path, $depth + 1); - } - } - } - - return false; - } - -} diff --git a/apps/files_versions/appinfo/app.php b/apps/files_versions/appinfo/app.php new file mode 100644 index 00000000000..6e7a803252e --- /dev/null +++ b/apps/files_versions/appinfo/app.php @@ -0,0 +1,19 @@ +<?php + +require_once('apps/files_versions/versions.php'); + +// Add an entry in the app list +OC_App::register( array( + 'order' => 10, + 'id' => 'files_versions', + 'name' => 'Versioning' )); + +OC_APP::registerAdmin('files_versions', 'settings'); + +// Listen to write signals +OC_Hook::connect(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, "OCA_Versions\Storage", "write_hook"); + + + + +?> diff --git a/apps/files_versions/appinfo/info.xml b/apps/files_versions/appinfo/info.xml new file mode 100644 index 00000000000..9936a2ad8b2 --- /dev/null +++ b/apps/files_versions/appinfo/info.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<info> + <id>files_versions</id> + <name>Versions</name> + <licence>AGPL</licence> + <author>Frank Karlitschek</author> + <require>3</require> + <description>Versioning of files</description> + <types> + <filesystem/> + </types> +</info> diff --git a/apps/files_versioning/appinfo/version b/apps/files_versions/appinfo/version index afaf360d37f..afaf360d37f 100644 --- a/apps/files_versioning/appinfo/version +++ b/apps/files_versions/appinfo/version diff --git a/apps/files_versions/css/versions.css b/apps/files_versions/css/versions.css new file mode 100644 index 00000000000..139597f9cb0 --- /dev/null +++ b/apps/files_versions/css/versions.css @@ -0,0 +1,2 @@ + + diff --git a/apps/files_versions/history.php b/apps/files_versions/history.php new file mode 100644 index 00000000000..6c7626ca4ed --- /dev/null +++ b/apps/files_versions/history.php @@ -0,0 +1,60 @@ +<?php + +/** + * ownCloud - History page of the Versions App + * + * @author Frank Karlitschek + * @copyright 2011 Frank Karlitschek karlitschek@kde.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ +require_once('../../lib/base.php'); + +OC_Util::checkLoggedIn(); + +if (isset($_GET['path'])) { + + $path = $_GET['path']; + $path = strip_tags($path); + + // roll back to old version if button clicked + if(isset($_GET['revert'])) { + \OCA_Versions\Storage::rollback($path,$_GET['revert']); + } + + // show the history only if there is something to show + if(OCA_Versions\Storage::isversioned($path)) { + + $count=5; //show the newest revisions + $versions=OCA_Versions\Storage::getversions($path,$count); + + $tmpl = new OC_Template('files_versions', 'history', 'user'); + $tmpl->assign('path', $path); + $tmpl->assign('versions', array_reverse($versions)); + $tmpl->printPage(); + }else{ + $tmpl = new OC_Template('files_versions', 'history', 'user'); + $tmpl->assign('path', $path); + $tmpl->assign('message', 'No old versions available'); + $tmpl->printPage(); + } +}else{ + $tmpl = new OC_Template('files_versions', 'history', 'user'); + $tmpl->assign('message', 'No path specified'); + $tmpl->printPage(); +} + + +?> diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js new file mode 100644 index 00000000000..139597f9cb0 --- /dev/null +++ b/apps/files_versions/js/versions.js @@ -0,0 +1,2 @@ + + diff --git a/apps/files_versions/settings.php b/apps/files_versions/settings.php new file mode 100644 index 00000000000..eb154d3edd3 --- /dev/null +++ b/apps/files_versions/settings.php @@ -0,0 +1,10 @@ +<?php + +OC_Util::checkAdminUser(); + +OC_Util::addScript( 'files_versions', 'versions' ); + +$tmpl = new OC_Template( 'files_versions', 'settings'); + +return $tmpl->fetchPage(); +?> diff --git a/apps/files_versions/templates/history.php b/apps/files_versions/templates/history.php new file mode 100644 index 00000000000..1b3de9ce77c --- /dev/null +++ b/apps/files_versions/templates/history.php @@ -0,0 +1,18 @@ +<?php + if(isset($_['message'])){ + + + if(isset($_['path'])) echo('<strong>File: '.$_['path']).'</strong><br>'; + echo('<strong>'.$_['message']).'</strong><br>'; + + }else{ + + echo('<strong>Versions of '.$_['path']).'</strong><br>'; + echo('<p><em>You can click on the revert button to revert to the specific verson.</em></p><br />'); + foreach ($_['versions'] as $v){ + echo(' '.OC_Util::formatDate($v).' <a href="history.php?path='.urlencode($_['path']).'&revert='.$v.'" class="button">revert</a><br /><br />'); + } + + } + +?> diff --git a/apps/files_versions/templates/settings.php b/apps/files_versions/templates/settings.php new file mode 100644 index 00000000000..8c8def94429 --- /dev/null +++ b/apps/files_versions/templates/settings.php @@ -0,0 +1,7 @@ +<form id="external"> + <fieldset class="personalblock"> + <strong>Versions</strong><br /> + + Configuration goes here... + </fieldset> +</form> diff --git a/apps/files_versions/versions.php b/apps/files_versions/versions.php new file mode 100644 index 00000000000..156a4f59c73 --- /dev/null +++ b/apps/files_versions/versions.php @@ -0,0 +1,216 @@ +<?php +/** + * Copyright (c) 2012 Frank Karlitschek <frank@owncloud.org> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +/** + * Versions + * + * A class to handle the versioning of files. + */ + +namespace OCA_Versions; + +class Storage { + + + // config.php configuration: + // - files_versions + // - files_versionsfolder + // - files_versionsblacklist + // - files_versionsmaxfilesize + // - files_versionsinterval + // - files_versionmaxversions + // + // todo: + // - port to oc_filesystem to enable network transparency + // - check if it works well together with encryption + // - do configuration web interface + // - implement expire all function. And find a place to call it ;-) + // - add transparent compression. first test if it´s worth it. + + const DEFAULTENABLED=true; + const DEFAULTFOLDER='versions'; + const DEFAULTBLACKLIST='avi mp3 mpg mp4'; + const DEFAULTMAXFILESIZE=1048576; // 10MB + const DEFAULTMININTERVAL=300; // 5 min + const DEFAULTMAXVERSIONS=50; + + /** + * init the versioning and create the versions folder. + */ + public static function init() { + if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { + // create versions folder + $foldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); + if(!is_dir($foldername)){ + mkdir($foldername); + } + } + } + + + /** + * listen to write event. + */ + public static function write_hook($params) { + if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { + $path = $params[\OC_Filesystem::signal_param_path]; + if($path<>'') Storage::store($path); + } + } + + + + /** + * store a new version of a file. + */ + public static function store($filename) { + if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { + $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); + $filesfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/files'; + Storage::init(); + + // check if filename is a directory + if(is_dir($filesfoldername.$filename)){ + return false; + } + + // check filetype blacklist + $blacklist=explode(' ',\OC_Config::getValue('files_versionsblacklist', Storage::DEFAULTBLACKLIST)); + foreach($blacklist as $bl) { + $parts=explode('.', $filename); + $ext=end($parts); + if(strtolower($ext)==$bl) { + return false; + } + } + + // check filesize + if(filesize($filesfoldername.$filename)>\OC_Config::getValue('files_versionsmaxfilesize', Storage::DEFAULTMAXFILESIZE)){ + return false; + } + + + // check mininterval + $matches=glob($versionsfoldername.$filename.'.v*'); + sort($matches); + $parts=explode('.v',end($matches)); + if((end($parts)+Storage::DEFAULTMININTERVAL)>time()){ + return false; + } + + + // create all parent folders + $info=pathinfo($filename); + @mkdir($versionsfoldername.$info['dirname'],0700,true); + + + // store a new version of a file + copy($filesfoldername.$filename,$versionsfoldername.$filename.'.v'.time()); + + // expire old revisions + Storage::expire($filename); + } + } + + + /** + * rollback to an old version of a file. + */ + public static function rollback($filename,$revision) { + if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { + $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); + $filesfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/files'; + // rollback + @copy($versionsfoldername.$filename.'.v'.$revision,$filesfoldername.$filename); + } + } + + /** + * check if old versions of a file exist. + */ + public static function isversioned($filename) { + if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { + $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); + + // check for old versions + $matches=glob($versionsfoldername.$filename.'.v*'); + if(count($matches)>1){ + return true; + }else{ + return false; + } + }else{ + return(false); + } + } + + + + /** + * get a list of old versions of a file. + */ + public static function getversions($filename,$count=0) { + if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { + $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); + $versions=array(); + + // fetch for old versions + $matches=glob($versionsfoldername.$filename.'.v*'); + sort($matches); + foreach($matches as $ma) { + $parts=explode('.v',$ma); + $versions[]=(end($parts)); + } + + // only show the newest commits + if($count<>0 and (count($versions)>$count)) { + $versions=array_slice($versions,count($versions)-$count); + } + + return($versions); + + + }else{ + return(array()); + } + } + + + + /** + * expire old versions of a file. + */ + public static function expire($filename) { + if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { + + $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); + + // check for old versions + $matches=glob($versionsfoldername.$filename.'.v*'); + if(count($matches)>\OC_Config::getValue('files_versionmaxversions', Storage::DEFAULTMAXVERSIONS)){ + $numbertodelete=count($matches-\OC_Config::getValue('files_versionmaxversions', Storage::DEFAULTMAXVERSIONS)); + + // delete old versions of a file + $deleteitems=array_slice($matches,0,$numbertodelete); + foreach($deleteitems as $de){ + unlink($versionsfoldername.$filename.'.v'.$de); + } + } + } + } + + /** + * expire all old versions. + */ + public static function expireall($filename) { + // todo this should go through all the versions directories and delete all the not needed files and not needed directories. + // useful to be included in a cleanup cronjob. + } + + +} diff --git a/lib/app.php b/lib/app.php index 9f7d23872f3..29653775253 100755 --- a/lib/app.php +++ b/lib/app.php @@ -519,6 +519,10 @@ class OC_App{ public static function getStorage($appid){ if(OC_App::isEnabled($appid)){//sanity check if(OC_User::isLoggedIn()){ + $view = new OC_FilesystemView('/'.OC_User::getUser()); + if(!$view->file_exists($appid)) { + $view->mkdir($appid); + } return new OC_FilesystemView('/'.OC_User::getUser().'/'.$appid); }else{ OC_Log::write('core','Can\'t get app storage, app, user not logged in',OC_Log::ERROR); |