Merge branch 'master' into calendar_sharing

This commit is contained in:
Georg Ehrke 2012-04-10 20:10:07 -04:00
commit ac2d14101c
104 changed files with 5526 additions and 615 deletions

162
3rdparty/granite/git/blob.php vendored Normal file
View File

@ -0,0 +1,162 @@
<?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);
}
}

232
3rdparty/granite/git/commit.php vendored Normal file
View File

@ -0,0 +1,232 @@
<?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;
}
}

210
3rdparty/granite/git/object/index.php vendored Normal file
View File

@ -0,0 +1,210 @@
<?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;
}
}

81
3rdparty/granite/git/object/loose.php vendored Normal file
View File

@ -0,0 +1,81 @@
<?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;
}
}
}

304
3rdparty/granite/git/object/packed.php vendored Normal file
View File

@ -0,0 +1,304 @@
<?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);
}
}

153
3rdparty/granite/git/object/raw.php vendored Normal file
View File

@ -0,0 +1,153 @@
<?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;
}
}
?>

293
3rdparty/granite/git/repository.php vendored Normal file
View File

@ -0,0 +1,293 @@
<?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;
}
}

38
3rdparty/granite/git/tag.php vendored Normal file
View File

@ -0,0 +1,38 @@
<?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;
}
}

198
3rdparty/granite/git/tree.php vendored Normal file
View File

@ -0,0 +1,198 @@
<?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;
}
}

126
3rdparty/granite/git/tree/node.php vendored Normal file
View File

@ -0,0 +1,126 @@
<?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';
}
}
}

5
README
View File

@ -3,10 +3,11 @@ A personal cloud which runs on your own server.
http://ownCloud.org http://ownCloud.org
Installation instructions: http://owncloud.org/support/setup-and-installation/ Installation instructions: http://owncloud.org/support
Source code: http://gitorious.org/owncloud
Source code: http://gitorious.org/owncloud
Mailing list: http://mail.kde.org/mailman/listinfo/owncloud Mailing list: http://mail.kde.org/mailman/listinfo/owncloud
IRC channel: http://webchat.freenode.net/?channels=owncloud IRC channel: http://webchat.freenode.net/?channels=owncloud
Diaspora: https://joindiaspora.com/u/owncloud Diaspora: https://joindiaspora.com/u/owncloud
Identi.ca: http://identi.ca/owncloud Identi.ca: http://identi.ca/owncloud

View File

@ -1,11 +0,0 @@
<?xml version="1.0"?>
<info>
<id>admin_export</id>
<name>Import/Export</name>
<description>Import/Export your owncloud data</description>
<version>0.1</version>
<licence>AGPL</licence>
<author>Thomas Schmidt</author>
<require>2</require>
<default_enable/>
</info>

View File

@ -1,96 +0,0 @@
<?php
/**
* ownCloud - admin export
*
* @author Thomas Schmidt
* @copyright 2011 Thomas Schmidt tom@opensuse.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 Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
OC_Util::checkAdminUser();
OC_Util::checkAppEnabled('admin_export');
if (isset($_POST['admin_export'])) {
$root = OC::$SERVERROOT . "/";
$zip = new ZipArchive();
$filename = get_temp_dir() . "/owncloud_export_" . date("y-m-d_H-i-s") . ".zip";
OC_Log::write('admin_export',"Creating export file at: " . $filename,OC_Log::INFO);
if ($zip->open($filename, ZIPARCHIVE::CREATE) !== TRUE) {
exit("Cannot open <$filename>\n");
}
if (isset($_POST['owncloud_system'])) {
// adding owncloud system files
OC_Log::write('admin_export',"Adding owncloud system files to export",OC_Log::INFO);
zipAddDir($root, $zip, false);
foreach (array(".git", "3rdparty", "apps", "core", "files", "l10n", "lib", "ocs", "search", "settings", "tests") as $dirname) {
zipAddDir($root . $dirname, $zip, true, basename($root) . "/");
}
}
if (isset($_POST['owncloud_config'])) {
// adding owncloud config
// todo: add database export
OC_Log::write('admin_export',"Adding owncloud config to export",OC_Log::INFO);
zipAddDir($root . "config/", $zip, true, basename($root) . "/");
$zip->addFile($root . '/data/.htaccess', basename($root) . "/data/owncloud.db");
}
if (isset($_POST['user_files'])) {
// adding user files
$zip->addFile($root . '/data/.htaccess', basename($root) . "/data/.htaccess");
$zip->addFile($root . '/data/index.html', basename($root) . "/data/index.html");
foreach (OC_User::getUsers() as $i) {
OC_Log::write('admin_export',"Adding owncloud user files of $i to export",OC_Log::INFO);
zipAddDir($root . "data/" . $i, $zip, true, basename($root) . "/data/");
}
}
$zip->close();
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=" . basename($filename));
header("Content-Length: " . filesize($filename));
@ob_end_clean();
readfile($filename);
unlink($filename);
} else {
// fill template
$tmpl = new OC_Template('admin_export', 'settings');
return $tmpl->fetchPage();
}
function zipAddDir($dir, $zip, $recursive=true, $internalDir='') {
$dirname = basename($dir);
$zip->addEmptyDir($internalDir . $dirname);
$internalDir.=$dirname.='/';
if ($dirhandle = opendir($dir)) {
while (false !== ( $file = readdir($dirhandle))) {
if (( $file != '.' ) && ( $file != '..' )) {
if (is_dir($dir . '/' . $file) && $recursive) {
zipAddDir($dir . '/' . $file, $zip, $recursive, $internalDir);
} elseif (is_file($dir . '/' . $file)) {
$zip->addFile($dir . '/' . $file, $internalDir . $file);
}
}
}
closedir($dirhandle);
} else {
OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR);
}
}

View File

@ -1,13 +0,0 @@
<form id="export" action="#" method="post">
<fieldset class="personalblock">
<legend><strong><?php echo $l->t('Export this ownCloud instance');?></strong></legend>
<p><?php echo $l->t('This will create a compressed file that contains the data of this owncloud instance.
Please choose which components should be included:');?>
</p>
<p><input type="checkbox" id="user_files" name="user_files" value="true"><label for="user_files"><?php echo $l->t('User files');?></label><br/>
<input type="checkbox" id="owncloud_system" name="owncloud_system" value="true"><label for="owncloud_system"><?php echo $l->t('ownCloud system files');?></label><br/>
<input type="checkbox" id="owncloud_config" name="owncloud_config" value="true"><label for="owncloud_config"><?php echo $l->t('ownCloud configuration');?></label>
</p>
<input type="submit" name="admin_export" value="Export" />
</fieldset>
</form>

View File

@ -1,10 +1,10 @@
<?php <?php
/** /**
* ownCloud - user_ldap * ownCloud - admin_migrate
* *
* @author Dominik Schmidt * @author Tom Needham
* @copyright 2011 Dominik Schmidt dev@dominik-schmidt.de * @copyright 2012 Tom Needham tom@owncloud.com
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@ -22,12 +22,12 @@
*/ */
OC_APP::registerAdmin('admin_export','settings'); OC_APP::registerAdmin('admin_migrate','settings');
// add settings page to navigation // add settings page to navigation
$entry = array( $entry = array(
'id' => "admin_export_settings", 'id' => "admin_migrate_settings",
'order'=>1, 'order'=>1,
'href' => OC_Helper::linkTo( "admin_export", "settings.php" ), 'href' => OC_Helper::linkTo( "admin_migrate", "settings.php" ),
'name' => 'Export' 'name' => 'Export'
); );

View File

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<info>
<id>admin_migrate</id>
<name>ownCloud Instance Migration</name>
<description>Import/Export your owncloud instance</description>
<version>0.1</version>
<licence>AGPL</licence>
<author>Thomas Schmidt and Tom Needham</author>
<require>2</require>
<default_enable/>
</info>

View File

@ -0,0 +1,57 @@
<?php
/**
* ownCloud - admin_migrate
*
* @author Thomas Schmidt
* @copyright 2011 Thomas Schmidt tom@opensuse.org
* @author Tom Needham
* @copyright 2012 Tom Needham tom@owncloud.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/>.
*
*/
OC_Util::checkAdminUser();
OC_Util::checkAppEnabled('admin_migrate');
// Export?
if (isset($_POST['admin_export'])) {
// Create the export zip
$response = json_decode( OC_Migrate::export( null, $_POST['export_type'] ) );
if( !$response->success ){
// Error
die('error');
} else {
$path = $response->data;
// Download it
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=" . basename($path));
header("Content-Length: " . filesize($path));
@ob_end_clean();
readfile( $path );
unlink( $path );
}
// Import?
} else if( isset($_POST['admin_import']) ){
$from = $_FILES['owncloud_import']['tmp_name'];
if( !OC_Migrate::import( $from, 'instance' ) ){
die('failed');
}
} else {
// fill template
$tmpl = new OC_Template('admin_migrate', 'settings');
return $tmpl->fetchPage();
}

View File

@ -0,0 +1,31 @@
<form id="export" action="#" method="post">
<fieldset class="personalblock">
<legend><strong><?php echo $l->t('Export this ownCloud instance');?></strong></legend>
<p><?php echo $l->t('This will create a compressed file that contains the data of this owncloud instance.
Please choose the export type:');?>
</p>
<h3>What would you like to export?</h3>
<p>
<input type="radio" name="export_type" value="instance" /> ownCloud instance (suitable for import )<br />
<input type="radio" name="export_type" value="system" /> ownCloud system files<br />
<input type="radio" name="export_type" value="userfiles" /> Just user files<br />
<input type="submit" name="admin_export" value="<?php echo $l->t('Export'); ?>" />
</fieldset>
</form>
<?php
/*
* EXPERIMENTAL
?>
<form id="import" action="#" method="post" enctype="multipart/form-data">
<fieldset class="personalblock">
<legend><strong><?php echo $l->t('Import an ownCloud instance. THIS WILL DELETE ALL CURRENT OWNCLOUD DATA');?></strong></legend>
<p><?php echo $l->t('All current ownCloud data will be replaced by the ownCloud instance that is uploaded.');?>
</p>
<p><input type="file" id="owncloud_import" name="owncloud_import"><label for="owncloud_import"><?php echo $l->t('ownCloud Export Zip File');?></label>
</p>
<input type="submit" name="admin_import" value="<?php echo $l->t('Import'); ?>" />
</fieldset>
</form>
<?php
*/
?>

View File

@ -17,4 +17,5 @@ OC_App::addNavigationEntry( array( 'id' => 'bookmarks_index', 'order' => 70, 'hr
OC_App::registerPersonal('bookmarks', 'settings'); OC_App::registerPersonal('bookmarks', 'settings');
OC_Util::addScript('bookmarks','bookmarksearch'); OC_Util::addScript('bookmarks','bookmarksearch');
OC_Search::registerProvider('OC_Search_Provider_Bookmarks'); OC_Search::registerProvider('OC_Search_Provider_Bookmarks');

View File

@ -0,0 +1,68 @@
<?php
class OC_Migration_Provider_Bookmarks extends OC_Migration_Provider{
// Create the xml for the user supplied
function export( ){
$options = array(
'table'=>'bookmarks',
'matchcol'=>'user_id',
'matchval'=>$this->uid,
'idcol'=>'id'
);
$ids = $this->content->copyRows( $options );
$options = array(
'table'=>'bookmarks_tags',
'matchcol'=>'bookmark_id',
'matchval'=>$ids
);
// Export tags
$ids2 = $this->content->copyRows( $options );
// If both returned some ids then they worked
if( is_array( $ids ) && is_array( $ids2 ) )
{
return true;
} else {
return false;
}
}
// Import function for bookmarks
function import( ){
switch( $this->appinfo->version ){
default:
// All versions of the app have had the same db structure, so all can use the same import function
$query = $this->content->prepare( "SELECT * FROM bookmarks WHERE user_id LIKE ?" );
$results = $query->execute( array( $this->olduid ) );
$idmap = array();
while( $row = $results->fetchRow() ){
// Import each bookmark, saving its id into the map
$query = OC_DB::prepare( "INSERT INTO *PREFIX*bookmarks(url, title, user_id, public, added, lastmodified) VALUES (?, ?, ?, ?, ?, ?)" );
$query->execute( array( $row['url'], $row['title'], $this->uid, $row['public'], $row['added'], $row['lastmodified'] ) );
// Map the id
$idmap[$row['id']] = OC_DB::insertid();
}
// Now tags
foreach($idmap as $oldid => $newid){
$query = $this->content->prepare( "SELECT * FROM bookmarks_tags WHERE bookmark_id LIKE ?" );
$results = $query->execute( array( $oldid ) );
while( $row = $results->fetchRow() ){
// Import the tags for this bookmark, using the new bookmark id
$query = OC_DB::prepare( "INSERT INTO *PREFIX*bookmarks_tags(bookmark_id, tag) VALUES (?, ?)" );
$query->execute( array( $newid, $row['tag'] ) );
}
}
// All done!
break;
}
return true;
}
}
// Load the provider
new OC_Migration_Provider_Bookmarks( 'bookmarks' );

View File

@ -39,13 +39,14 @@ foreach ($_POST as $key=>$element) {
debug('_POST: '.$key.'=>'.$element); debug('_POST: '.$key.'=>'.$element);
} }
$aid = $_POST['aid']; $aid = isset($_POST['aid'])?$_POST['aid']:null;
if(!$aid) {
$aid = min(OC_Contacts_Addressbook::activeIds()); // first active addressbook.
}
OC_Contacts_App::getAddressbook( $aid ); // is owner access check OC_Contacts_App::getAddressbook( $aid ); // is owner access check
$fn = trim($_POST['fn']); $fn = trim($_POST['fn']);
$n = trim($_POST['n']); $n = trim($_POST['n']);
debug('N: '.$n);
debug('FN: '.$fn);
$vcard = new OC_VObject('VCARD'); $vcard = new OC_VObject('VCARD');
$vcard->setUID(); $vcard->setUID();

View File

@ -96,40 +96,40 @@ switch($element) {
//$value = getOtherValue(); //$value = getOtherValue();
} }
break; break;
case 'CATEGORIES': //case 'CATEGORIES':
/* multi autocomplete triggers an save with empty value */ /* multi autocomplete triggers an save with empty value
if (!$value) { if (!$value) {
$value = $vcard->getAsString('CATEGORIES'); $value = $vcard->getAsString('CATEGORIES');
} }
break; break;*/
case 'EMAIL': case 'EMAIL':
$value = strtolower($value); $value = strtolower($value);
break; break;
} }
if(!$value) { if(!$value) {
bailOut(OC_Contacts_App::$l10n->t('Cannot save empty value.')); unset($vcard->children[$line]);
} $checksum = '';
} else {
/* setting value */ /* setting value */
switch($element) { switch($element) {
case 'BDAY': case 'BDAY':
case 'FN': case 'FN':
case 'N': case 'N':
case 'ORG': case 'ORG':
case 'NOTE': case 'NOTE':
case 'NICKNAME': case 'NICKNAME':
case 'CATEGORIES': debug('Setting string:'.$name.' '.$value);
debug('Setting string:'.$name.' '.$value); $vcard->setString($name, $value);
$vcard->setString($name, $value); break;
break; case 'CATEGORIES':
case 'EMAIL': debug('Setting string:'.$name.' '.$value);
case 'TEL': $vcard->children[$line]->setValue($value);
case 'ADR': // should I delete the property if empty or throw an error? break;
debug('Setting element: (EMAIL/TEL/ADR)'.$element); case 'EMAIL':
if(!$value) { case 'TEL':
unset($vcard->children[$line]); // Should never happen... case 'ADR': // should I delete the property if empty or throw an error?
} else { debug('Setting element: (EMAIL/TEL/ADR)'.$element);
$vcard->children[$line]->setValue($value); $vcard->children[$line]->setValue($value);
$vcard->children[$line]->parameters = array(); $vcard->children[$line]->parameters = array();
if(!is_null($parameters)) { if(!is_null($parameters)) {
@ -142,12 +142,12 @@ switch($element) {
} }
} }
} }
} break;
break; }
// Do checksum and be happy
$checksum = md5($vcard->children[$line]->serialize());
} }
// Do checksum and be happy //debug('New checksum: '.$checksum);
$checksum = md5($vcard->children[$line]->serialize());
debug('New checksum: '.$checksum);
if(!OC_Contacts_VCard::edit($id,$vcard)) { if(!OC_Contacts_VCard::edit($id,$vcard)) {
bailOut(OC_Contacts_App::$l10n->t('Error updating contact property.')); bailOut(OC_Contacts_App::$l10n->t('Error updating contact property.'));

View File

@ -0,0 +1,68 @@
<?php
class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
// Create the xml for the user supplied
function export( ){
$options = array(
'table'=>'contacts_addressbooks',
'matchcol'=>'userid',
'matchval'=>$this->uid,
'idcol'=>'id'
);
$ids = $this->content->copyRows( $options );
$options = array(
'table'=>'contacts_cards',
'matchcol'=>'addressbookid',
'matchval'=>$ids
);
// Export tags
$ids2 = $this->content->copyRows( $options );
// If both returned some ids then they worked
if( is_array( $ids ) && is_array( $ids2 ) )
{
return true;
} else {
return false;
}
}
// Import function for bookmarks
function import( ){
switch( $this->appinfo->version ){
default:
// All versions of the app have had the same db structure, so all can use the same import function
$query = $this->content->prepare( "SELECT * FROM contacts_addressbooks WHERE userid LIKE ?" );
$results = $query->execute( array( $this->olduid ) );
$idmap = array();
while( $row = $results->fetchRow() ){
// Import each bookmark, saving its id into the map
$query = OC_DB::prepare( "INSERT INTO *PREFIX*contacts_addressbooks (`userid`, `displayname`, `uri`, `description`, `ctag`) VALUES (?, ?, ?, ?, ?)" );
$query->execute( array( $this->uid, $row['displayname'], $row['uri'], $row['description'], $row['ctag'] ) );
// Map the id
$idmap[$row['id']] = OC_DB::insertid();
}
// Now tags
foreach($idmap as $oldid => $newid){
$query = $this->content->prepare( "SELECT * FROM contacts_cards WHERE addressbookid LIKE ?" );
$results = $query->execute( array( $oldid ) );
while( $row = $results->fetchRow() ){
// Import the tags for this bookmark, using the new bookmark id
$query = OC_DB::prepare( "INSERT INTO *PREFIX*contacts_cards (`addressbookid`, `fullname`, `carddata`, `uri`, `lastmodified`) VALUES (?, ?, ?, ?, ?)" );
$query->execute( array( $newid, $row['fullname'], $row['carddata'], $row['uri'], $row['lastmodified'] ) );
}
}
// All done!
break;
}
return true;
}
}
// Load the provider
new OC_Migration_Provider_Contacts( 'contacts' );

View File

@ -13,22 +13,26 @@
#contacts_propertymenu li a { padding: 3px; display: block } #contacts_propertymenu li a { padding: 3px; display: block }
#contacts_propertymenu li:hover { background-color: #1d2d44; } #contacts_propertymenu li:hover { background-color: #1d2d44; }
#contacts_propertymenu li a:hover { color: #fff } #contacts_propertymenu li a:hover { color: #fff }
#actionbar { height: 30px; width: 200px; position: fixed; right: 0px; top: 75px; margin: 0 0 0 0; padding: 0 0 0 0;} #actionbar { height: 30px; width: 200px; position: fixed; right: 0px; top: 75px; margin: 0 0 0 0; padding: 0 0 0 0; z-index: 1000; }
#card { /*max-width: 70em; border: thin solid lightgray; display: block;*/ } #card { width: auto;/*max-width: 70em; border: thin solid lightgray; display: block;*/ }
#firstrun { width: 100%; position: absolute; top: 5em; left: 0; text-align: center; font-weight:bold; font-size:1.5em; color:#777; } #firstrun { width: 100%; position: absolute; top: 5em; left: 0; text-align: center; font-weight:bold; font-size:1.5em; color:#777; }
#firstrun #selections { font-size:0.8em; margin: 2em auto auto auto; clear: both; } #firstrun #selections { font-size:0.8em; margin: 2em auto auto auto; clear: both; }
#card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 14em; } #card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 14em; float: left; }
.categories { float: left; width: 16em; } .categories { float: left; width: 16em; }
#card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select { background-color: #f8f8f8; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; } #card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select, textarea { background-color: #fefefe; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; }
#card input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active,input[type="email"]:hover,input[type="tel"]:hover,input[type="date"]:hover,input[type="date"],input[type="date"]:hover,input[type="date"]:active,input[type="date"]:active,input[type="date"]:active,input[type="email"]:active,input[type="tel"]:active, select:hover, select:focus, select:active { border: 0 !important; -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; background:#fff; color:#333; border:1px solid #ddd; -moz-box-shadow:0 1px 1px #fff, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; float: left; } #card input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active,input[type="email"]:hover,input[type="tel"]:hover,input[type="date"]:hover,input[type="date"],input[type="date"]:hover,input[type="date"]:active,input[type="date"]:active,input[type="date"]:active,input[type="email"]:active,input[type="tel"]:active, select:hover, select:focus, select:active, textarea:focus, textarea:hover { border: 0 !important; -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; background:#fff; color:#333; border:1px solid #ddd; -moz-box-shadow:0 1px 1px #fff, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; float: left; }
input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid { background-color: #ffc0c0 !important; } input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid, textarea:invalid { color: #bbb !important; }
textarea { min-height: 4em; }
dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; }
.form dt { display: table-cell; clear: left; float: left; width: 7em; margin: 0; padding: 0.8em 0.5em 0 0; font-weight: bold; text-align:right; text-overflow:ellipsis; o-text-overflow: ellipsis; vertical-align: text-bottom;/* white-space: pre-wrap; white-space: -moz-pre-wrap !important; white-space: -pre-wrap; white-space: -o-pre-wrap;*/ } .form dt { display: table-cell; clear: left; float: left; width: 7em; margin: 0; padding: 0.8em 0.5em 0 0; text-align:right; text-overflow:ellipsis; o-text-overflow: ellipsis; vertical-align: text-bottom; color: #bbb;/* white-space: pre-wrap; white-space: -moz-pre-wrap !important; white-space: -pre-wrap; white-space: -o-pre-wrap;*/ }
.form dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0px; white-space: nowrap; vertical-align: text-bottom; } .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 { background: url('../../../core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; } .loading { background: url('../../../core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; }
.ui-autocomplete-loading { background: url('../../../core/img/loading.gif') right center no-repeat; }
.float { float: left; }
.listactions { height: 1em; width:60px; float: left; clear: right; } .listactions { height: 1em; width:60px; float: left; clear: right; }
.add,.edit,.delete,.mail, .globe { cursor: pointer; width: 20px; height: 20px; margin: 0; float: left; position:relative; display: none; } .add,.edit,.delete,.mail, .globe { cursor: pointer; width: 20px; height: 20px; margin: 0; float: left; position:relative; display: none; }
.add { background:url('../../../core/img/actions/add.svg') no-repeat center; clear: both; } .add { background:url('../../../core/img/actions/add.svg') no-repeat center; clear: both; }
@ -43,18 +47,18 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; }
#edit_address_dialog { /*width: 30em;*/ } #edit_address_dialog { /*width: 30em;*/ }
#edit_address_dialog > input { width: 15em; } #edit_address_dialog > input { width: 15em; }
#edit_photo_dialog_img { display: block; width: 150; height: 200; border: thin solid black; } #edit_photo_dialog_img { display: block; width: 150; height: 200; border: thin solid black; }
#fn { float: left; } #fn { float: left !important; width: 18em !important; }
/** #name { /*position: absolute; top: 0px; left: 0px;*/ min-width: 25em; height: 2em; clear: right; display: block; }
* Create classes form, floateven and floatodd which flows left and right respectively. #identityprops { /*position: absolute; top: 2.5em; left: 0px;*/ }
*/ /*#contact_photo { max-width: 250px; }*/
.contactsection { float: left; min-width: 30em; max-width: 40em; margin: 0.5em; border: thin solid lightgray; -webkit-border-radius: 0.5em; -moz-border-radius: 0.5em; border-radius: 0.5em; background-color: #f8f8f8; } #contact_identity { min-width: 30em; }
.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; } .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; }
#cropbox { margin: auto; } #cropbox { margin: auto; }
#contacts_details_photo { border-radius: 0.5em; border: thin solid #bbb; margin: 0.3em; cursor: pointer; background: url(../../../core/img/loading.gif) no-repeat center center; display: block; /* clear: right;*/ }
#contacts_details_photo { border-radius: 0.5em; border: thin solid #bbb; padding: 0.5em; margin: 1em 1em 1em 7em; cursor: pointer; background: url(../../../core/img/loading.gif) no-repeat center center; clear: right; }
#contacts_details_photo:hover { background: #fff; } #contacts_details_photo:hover { background: #fff; }
#contacts_details_photo_progress { margin: 0.3em 0.3em 0.3em 7em; clear: left; } /*#contacts_details_photo_progress { margin: 0.3em 0.3em 0.3em 7em; clear: left; }*/
/* Address editor */ /* Address editor */
#addressdisplay { padding: 0.5em; } #addressdisplay { padding: 0.5em; }
dl.addresscard { background-color: #fff; float: left; width: 45%; margin: 0 0.3em 0.3em 0.3em; padding: 0; border: thin solid lightgray; } dl.addresscard { background-color: #fff; float: left; width: 45%; margin: 0 0.3em 0.3em 0.3em; padding: 0; border: thin solid lightgray; }
@ -72,8 +76,10 @@ dl.addresscard dd > ul { margin: 0.3em; padding: 0.3em; }
#file_upload_target, #crop_target { display:none; } #file_upload_target, #crop_target { display:none; }
#file_upload_start { opacity:0; filter:alpha(opacity=0); z-index:1; position:absolute; left:0; top:0; cursor:pointer; width:0; height:0;} #file_upload_start { opacity:0; filter:alpha(opacity=0); z-index:1; /*position:absolute; left:0; top:0;*/ cursor:pointer; width:0; height:0;}
input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; } input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; }
.big { font-weight:bold; font-size:1.2em; }
.huge { font-weight:bold; font-size:1.5em; }
.propertycontainer dd { float: left; width: 25em; } .propertycontainer dd { float: left; width: 25em; }
.propertylist { clear: none; max-width: 28em; } .propertylist { clear: none; max-width: 28em; }
.propertylist li { /*background-color: cyan; */ min-width: 25em; /*max-width: 30em;*/ display: block; clear: right; } .propertylist li { /*background-color: cyan; */ min-width: 25em; /*max-width: 30em;*/ display: block; clear: right; }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -34,7 +34,26 @@ if(!is_null($id)) {
} }
$property_types = OC_Contacts_App::getAddPropertyOptions(); $property_types = OC_Contacts_App::getAddPropertyOptions();
$phone_types = OC_Contacts_App::getTypesOfProperty('TEL'); $phone_types = OC_Contacts_App::getTypesOfProperty('TEL');
$categories = OC_Contacts_App::$categories->categories(); $categories = OC_Contacts_App::getCategories();
if(count($categories) == 0) {
$vcaddressbooks = OC_Contacts_Addressbook::all(OC_User::getUser());
if(count($vcaddressbooks) > 0) {
$vcaddressbookids = array();
foreach($vcaddressbooks as $vcaddressbook) {
$vcaddressbookids[] = $vcaddressbook['id'];
}
$vccontacts = OC_Contacts_VCard::all($vcaddressbookids);
if(count($vccontacts) > 0) {
$cards = array();
foreach($vccontacts as $vccontact) {
$cards[] = $vccontact['carddata'];
}
OC_Contacts_App::$categories->rescan($cards);
$categories = OC_Contacts_App::$categories->categories();
}
}
}
$upload_max_filesize = OC_Helper::computerFileSize(ini_get('upload_max_filesize')); $upload_max_filesize = OC_Helper::computerFileSize(ini_get('upload_max_filesize'));
$post_max_size = OC_Helper::computerFileSize(ini_get('post_max_size')); $post_max_size = OC_Helper::computerFileSize(ini_get('post_max_size'));
@ -61,7 +80,6 @@ $tmpl = new OC_Template( "contacts", "index", "user" );
$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize);
$tmpl->assign('uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize)); $tmpl->assign('uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize));
$tmpl->assign('property_types',$property_types); $tmpl->assign('property_types',$property_types);
$tmpl->assign('categories',OC_Contacts_App::getCategories());
$tmpl->assign('phone_types',$phone_types); $tmpl->assign('phone_types',$phone_types);
$tmpl->assign('categories',$categories); $tmpl->assign('categories',$categories);
$tmpl->assign('addressbooks', $addressbooks); $tmpl->assign('addressbooks', $addressbooks);

View File

@ -65,7 +65,7 @@ Contacts={
propertyTypeFor:function(obj) { propertyTypeFor:function(obj) {
return $(obj).parents('.propertycontainer').first().data('element'); return $(obj).parents('.propertycontainer').first().data('element');
}, },
showHideContactInfo:function() { /*showHideContactInfo:function() {
var show = ($('#emaillist li.propertycontainer').length > 0 || $('#phonelist li.propertycontainer').length > 0 || $('#addressdisplay dl.propertycontainer').length > 0); var show = ($('#emaillist li.propertycontainer').length > 0 || $('#phonelist li.propertycontainer').length > 0 || $('#addressdisplay dl.propertycontainer').length > 0);
console.log('showHideContactInfo: ' + show); console.log('showHideContactInfo: ' + show);
if(show) { if(show) {
@ -73,8 +73,8 @@ Contacts={
} else { } else {
$('#contact_communication').hide(); $('#contact_communication').hide();
} }
}, },*/
checkListFor:function(obj) { /*checkListFor:function(obj) {
var type = $(obj).parents('.propertycontainer').first().data('element'); var type = $(obj).parents('.propertycontainer').first().data('element');
console.log('checkListFor: ' + type); console.log('checkListFor: ' + type);
switch (type) { switch (type) {
@ -101,7 +101,7 @@ Contacts={
case 'BDAY': case 'BDAY':
break; break;
} }
}, },*/
loading:function(obj, state) { loading:function(obj, state) {
if(state) { if(state) {
$(obj).addClass('loading'); $(obj).addClass('loading');
@ -116,7 +116,7 @@ Contacts={
}, },
loadListHandlers:function() { loadListHandlers:function() {
//$('.add,.delete').hide(); //$('.add,.delete').hide();
$('.globe,.mail,.delete,.edit').tipsy(); $('.globe,.mail,.delete,.edit,.tip').tipsy();
$('.addresscard,.propertylist li,.propertycontainer').hover( $('.addresscard,.propertylist li,.propertycontainer').hover(
function () { function () {
$(this).find('.globe,.mail,.delete,.edit').fadeIn(100); $(this).find('.globe,.mail,.delete,.edit').fadeIn(100);
@ -137,18 +137,14 @@ Contacts={
$(this).find('.add').fadeOut(500); $(this).find('.add').fadeOut(500);
} }
);*/ );*/
$('.button,.action').tipsy();
$('#contacts_deletecard').tipsy({gravity: 'ne'});
$('#contacts_downloadcard').tipsy({gravity: 'ne'});
//$('#fn').jec(); //$('#fn').jec();
$('#fn_select').combobox({ $('#fn_select').combobox({
'id': 'fn', 'id': 'fn',
'name': 'value', 'name': 'value',
'classes': ['contacts_property'], 'classes': ['contacts_property', 'huge', 'tip', 'float'],
'attributes': {'placeholder': t('contacts', 'Enter name')},
'title': t('contacts', 'Format custom, Short name, Full name, Reverse or Reverse with comma')}); 'title': t('contacts', 'Format custom, Short name, Full name, Reverse or Reverse with comma')});
//$('.jecEditableOption').attr('title', t('contacts','Custom')); //$('.jecEditableOption').attr('title', t('contacts','Custom'));
$('#fn').tipsy();
$('#contacts_details_photo_wrapper').tipsy();
$('#bday').datepicker({ $('#bday').datepicker({
dateFormat : 'dd-mm-yy' dateFormat : 'dd-mm-yy'
}); });
@ -175,10 +171,6 @@ Contacts={
// Contacts.UI.Card.editAddress(); // Contacts.UI.Card.editAddress();
// return false; // return false;
// }); // });
$('#n').click(function(){
Contacts.UI.Card.editName();
//return false;
});
$('#edit_name').click(function(){ $('#edit_name').click(function(){
Contacts.UI.Card.editName(); Contacts.UI.Card.editName();
return false; return false;
@ -200,6 +192,9 @@ Contacts={
} }
] ); ] );
$('#categories').multiple_autocomplete({source: categories}); $('#categories').multiple_autocomplete({source: categories});
$('.button,.action,.tip').tipsy();
$('#contacts_deletecard').tipsy({gravity: 'ne'});
$('#contacts_downloadcard').tipsy({gravity: 'ne'});
Contacts.UI.loadListHandlers(); Contacts.UI.loadListHandlers();
}, },
Card:{ Card:{
@ -259,16 +254,28 @@ Contacts={
}); });
} }
}, },
export:function() { doExport:function() {
document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + this.id; document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + this.id;
//$.get(OC.linkTo('contacts', 'export.php'),{'contactid':this.id},function(jsondata){ //$.get(OC.linkTo('contacts', 'export.php'),{'contactid':this.id},function(jsondata){
//}); //});
}, },
import:function(){ doImport:function(){
Contacts.UI.notImplemented(); Contacts.UI.notImplemented();
}, },
add:function(n, fn, aid){ // add a new contact add:function(n, fn, aid, isnew){ // add a new contact
console.log('Add contact: ' + n + ', ' + fn + ' ' + aid); console.log('Add contact: ' + n + ', ' + fn + ' ' + aid);
var card = $('#card')[0];
if(!card) {
console.log('Loading proper card DOM');
$.getJSON(OC.filePath('contacts', 'ajax', 'loadcard.php'),{},function(jsondata){
if(jsondata.status == 'success'){
$('#rightcontent').html(jsondata.data.page);
Contacts.UI.loadHandlers();
} else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
}
$.post(OC.filePath('contacts', 'ajax', 'addcontact.php'), { n: n, fn: fn, aid: aid }, $.post(OC.filePath('contacts', 'ajax', 'addcontact.php'), { n: n, fn: fn, aid: aid },
function(jsondata) { function(jsondata) {
if (jsondata.status == 'success'){ if (jsondata.status == 'success'){
@ -291,7 +298,15 @@ Contacts={
if(!added) { if(!added) {
$('#leftcontent ul').append(item); $('#leftcontent ul').append(item);
} }
if(isnew) {
Contacts.UI.Card.addProperty('EMAIL');
Contacts.UI.Card.addProperty('TEL');
Contacts.UI.Card.addProperty('NICKNAME');
Contacts.UI.Card.addProperty('ORG');
Contacts.UI.Card.addProperty('CATEGORIES');
$('#fn').focus();
$('#fn').select();
}
} }
else{ else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
@ -308,7 +323,7 @@ Contacts={
} }
}); });
}, },
delete:function() { doDelete:function() {
$('#contacts_deletecard').tipsy('hide'); $('#contacts_deletecard').tipsy('hide');
OC.dialogs.confirm(t('contacts', 'Are you sure you want to delete this contact?'), t('contacts', 'Warning'), function(answer) { OC.dialogs.confirm(t('contacts', 'Are you sure you want to delete this contact?'), t('contacts', 'Warning'), function(answer) {
if(answer == true) { if(answer == true) {
@ -356,7 +371,7 @@ Contacts={
return false; return false;
}, },
loadContact:function(jsondata){ loadContact:function(jsondata){
$('#contact_communication').hide(); //$('#contact_communication').hide();
this.data = jsondata; this.data = jsondata;
this.id = this.data.id; this.id = this.data.id;
$('#rightcontent').data('id',this.id); $('#rightcontent').data('id',this.id);
@ -368,7 +383,6 @@ Contacts={
this.loadPhones(); this.loadPhones();
this.loadAddresses(); this.loadAddresses();
this.loadSingleProperties(); this.loadSingleProperties();
// TODO: load NOTE ;-)
if(this.data.NOTE) { if(this.data.NOTE) {
$('#note').data('checksum', this.data.NOTE[0]['checksum']); $('#note').data('checksum', this.data.NOTE[0]['checksum']);
$('#note').find('textarea').val(this.data.NOTE[0]['value']); $('#note').find('textarea').val(this.data.NOTE[0]['value']);
@ -376,7 +390,7 @@ Contacts={
} else { } else {
$('#note').data('checksum', ''); $('#note').data('checksum', '');
$('#note').find('textarea').val(''); $('#note').find('textarea').val('');
$('#note').hide(); //$('#note').hide();
} }
}, },
loadSingleProperties:function() { loadSingleProperties:function() {
@ -521,17 +535,18 @@ Contacts={
},*/ },*/
editNew:function(){ // add a new contact editNew:function(){ // add a new contact
this.id = ''; this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = ''; this.id = ''; this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = '';
$.getJSON(OC.filePath('contacts', 'ajax', 'newcontact.php'),{},function(jsondata){ Contacts.UI.Card.add(';;;;', '', '', true);
/*$.getJSON(OC.filePath('contacts', 'ajax', 'newcontact.php'),{},function(jsondata){
if(jsondata.status == 'success'){ if(jsondata.status == 'success'){
id = ''; id = '';
$('#rightcontent').data('id',''); $('#rightcontent').data('id','');
$('#rightcontent').html(jsondata.data.page); $('#rightcontent').html(jsondata.data.page);
Contacts.UI.Card.editName(); //Contacts.UI.Card.editName();
} else { } else {
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
//alert(jsondata.data.message); //alert(jsondata.data.message);
} }
}); });*/
}, },
savePropertyInternal:function(name, fields, oldchecksum, checksum){ savePropertyInternal:function(name, fields, oldchecksum, checksum){
// TODO: Add functionality for new fields. // TODO: Add functionality for new fields.
@ -627,8 +642,8 @@ Contacts={
},'json'); },'json');
} }
}, },
addProperty:function(obj){ addProperty:function(type){
var type = $(obj).data('type'); //var type = $(obj).data('type');
console.log('addProperty:' + type); console.log('addProperty:' + type);
switch (type) { switch (type) {
case 'PHOTO': case 'PHOTO':
@ -647,21 +662,21 @@ Contacts={
$('#emails').show(); $('#emails').show();
} }
Contacts.UI.Card.addMail(); Contacts.UI.Card.addMail();
Contacts.UI.showHideContactInfo(); //Contacts.UI.showHideContactInfo();
break; break;
case 'TEL': case 'TEL':
if($('#phonelist>li').length == 1) { if($('#phonelist>li').length == 1) {
$('#phones').show(); $('#phones').show();
} }
Contacts.UI.Card.addPhone(); Contacts.UI.Card.addPhone();
Contacts.UI.showHideContactInfo(); //Contacts.UI.showHideContactInfo();
break; break;
case 'ADR': case 'ADR':
if($('#addressdisplay>dl').length == 1) { if($('#addressdisplay>dl').length == 1) {
$('#addresses').show(); $('#addresses').show();
} }
Contacts.UI.Card.editAddress('new', true); Contacts.UI.Card.editAddress('new', true);
Contacts.UI.showHideContactInfo(); //Contacts.UI.showHideContactInfo();
break; break;
case 'NICKNAME': case 'NICKNAME':
case 'ORG': case 'ORG':
@ -682,8 +697,8 @@ Contacts={
if(jsondata.status == 'success'){ if(jsondata.status == 'success'){
if(type == 'list') { if(type == 'list') {
Contacts.UI.propertyContainerFor(obj).remove(); Contacts.UI.propertyContainerFor(obj).remove();
Contacts.UI.showHideContactInfo(); //Contacts.UI.showHideContactInfo();
Contacts.UI.checkListFor(obj); //Contacts.UI.checkListFor(obj);
} else if(type == 'single') { } else if(type == 'single') {
var proptype = Contacts.UI.propertyTypeFor(obj); var proptype = Contacts.UI.propertyTypeFor(obj);
console.log('deleteProperty, hiding: ' + proptype); console.log('deleteProperty, hiding: ' + proptype);
@ -718,8 +733,8 @@ Contacts={
} else { // Property hasn't been saved so there's nothing to delete. } else { // Property hasn't been saved so there's nothing to delete.
if(type == 'list') { if(type == 'list') {
Contacts.UI.propertyContainerFor(obj).remove(); Contacts.UI.propertyContainerFor(obj).remove();
Contacts.UI.showHideContactInfo(); //Contacts.UI.showHideContactInfo();
Contacts.UI.checkListFor(obj); //Contacts.UI.checkListFor(obj);
} else if(type == 'single') { } else if(type == 'single') {
var proptype = Contacts.UI.propertyTypeFor(obj); var proptype = Contacts.UI.propertyTypeFor(obj);
console.log('deleteProperty, hiding: ' + proptype); console.log('deleteProperty, hiding: ' + proptype);
@ -891,7 +906,7 @@ Contacts={
if(isnew) { if(isnew) {
container.remove(); container.remove();
} }
Contacts.UI.showHideContactInfo(); //Contacts.UI.showHideContactInfo();
} }
}, },
close : function(event, ui) { close : function(event, ui) {
@ -900,11 +915,95 @@ Contacts={
if(isnew) { if(isnew) {
container.remove(); container.remove();
} }
Contacts.UI.showHideContactInfo(); //Contacts.UI.showHideContactInfo();
}/*, },
open : function(event, ui) { open : function(event, ui) {
// load 'ADR' property - maybe :-P $( "#adr_city" ).autocomplete({
}*/ source: function( request, response ) {
$.ajax({
url: "http://ws.geonames.org/searchJSON",
dataType: "jsonp",
data: {
featureClass: "P",
style: "full",
maxRows: 12,
lang: lang,
name_startsWith: request.term
},
success: function( data ) {
response( $.map( data.geonames, function( item ) {
/*for(var key in item) {
console.log(key + ': ' + item[key]);
}*/
return {
label: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName,
value: item.name,
country: item.countryName
}
}));
}
});
},
minLength: 2,
select: function( event, ui ) {
if(ui.item && $('#adr_country').val().trim().length == 0) {
$('#adr_country').val(ui.item.country);
}
/*log( ui.item ?
"Selected: " + ui.item.label :
"Nothing selected, input was " + this.value);*/
},
open: function() {
$( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );
},
close: function() {
$( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );
}
});
$( "#adr_country" ).autocomplete({
source: function( request, response ) {
$.ajax({
url: "http://ws.geonames.org/searchJSON",
dataType: "jsonp",
data: {
/*featureClass: "A",*/
featureCode: "PCLI",
/*countryBias: "true",*/
/*style: "full",*/
lang: lang,
maxRows: 12,
name_startsWith: request.term
},
success: function( data ) {
response( $.map( data.geonames, function( item ) {
for(var key in item) {
console.log(key + ': ' + item[key]);
}
return {
label: item.name,
value: item.name
}
}));
}
});
},
minLength: 2,
select: function( event, ui ) {
/*if(ui.item) {
$('#adr_country').val(ui.item.country);
}
log( ui.item ?
"Selected: " + ui.item.label :
"Nothing selected, input was " + this.value);*/
},
open: function() {
$( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );
},
close: function() {
$( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );
}
});
}
}); });
} else { } else {
alert(jsondata.data.message); alert(jsondata.data.message);
@ -973,7 +1072,7 @@ Contacts={
} }
}, },
loadPhoto:function(force){ loadPhoto:function(force){
if(this.data.PHOTO||force==true) { //if(this.data.PHOTO||force==true) {
$.getJSON('ajax/loadphoto.php',{'id':this.id},function(jsondata){ $.getJSON('ajax/loadphoto.php',{'id':this.id},function(jsondata){
if(jsondata.status == 'success'){ if(jsondata.status == 'success'){
//alert(jsondata.data.page); //alert(jsondata.data.page);
@ -986,11 +1085,11 @@ Contacts={
}); });
$('#file_upload_form').show(); $('#file_upload_form').show();
$('#contacts_propertymenu a[data-type="PHOTO"]').parent().hide(); $('#contacts_propertymenu a[data-type="PHOTO"]').parent().hide();
} else { /*} else {
$('#contacts_details_photo_wrapper').empty(); $('#contacts_details_photo_wrapper').empty();
$('#file_upload_form').hide(); $('#file_upload_form').hide();
$('#contacts_propertymenu a[data-type="PHOTO"]').parent().show(); $('#contacts_propertymenu a[data-type="PHOTO"]').parent().show();
} }*/
}, },
editPhoto:function(id, tmp_path){ editPhoto:function(id, tmp_path){
//alert('editPhoto: ' + tmp_path); //alert('editPhoto: ' + tmp_path);
@ -1165,7 +1264,7 @@ Contacts={
}); });
} }
}, },
import:function(){ doImport:function(){
Contacts.UI.notImplemented(); Contacts.UI.notImplemented();
}, },
submit:function(button, bookid){ submit:function(button, bookid){
@ -1198,9 +1297,7 @@ Contacts={
} }
}, },
Contacts:{ Contacts:{
/** // Reload the contacts list.
* Reload the contacts list.
*/
update:function(){ update:function(){
console.log('Contacts.update, start'); console.log('Contacts.update, start');
$.getJSON('ajax/contacts.php',{},function(jsondata){ $.getJSON('ajax/contacts.php',{},function(jsondata){
@ -1215,9 +1312,7 @@ Contacts={
}); });
setTimeout(Contacts.UI.Contacts.lazyupdate, 500); setTimeout(Contacts.UI.Contacts.lazyupdate, 500);
}, },
/** // Add thumbnails to the contact list as they become visible in the viewport.
* Add thumbnails to the contact list as they become visible in the viewport.
*/
lazyupdate:function(){ lazyupdate:function(){
$('#contacts li').live('inview', function(){ $('#contacts li').live('inview', function(){
if (!$(this).find('a').attr('style')) { if (!$(this).find('a').attr('style')) {
@ -1237,9 +1332,6 @@ $(document).ready(function(){
OCCategories.changed = Contacts.UI.Card.categoriesChanged; OCCategories.changed = Contacts.UI.Card.categoriesChanged;
OCCategories.app = 'contacts'; OCCategories.app = 'contacts';
/**
* Show the Addressbook chooser
*/
$('#chooseaddressbook').click(function(){ $('#chooseaddressbook').click(function(){
Contacts.UI.Addressbooks.overview(); Contacts.UI.Addressbooks.overview();
return false; return false;
@ -1272,7 +1364,7 @@ $(document).ready(function(){
}); });
$('#contacts_deletecard').live('click',function(){ $('#contacts_deletecard').live('click',function(){
Contacts.UI.Card.delete(); Contacts.UI.Card.doDelete();
}); });
$('#contacts li').bind('inview', function(event, isInView, visiblePartX, visiblePartY) { $('#contacts li').bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
@ -1430,7 +1522,8 @@ $(document).ready(function(){
} }
}); });
$('#contacts_propertymenu a').live('click',function(){ $('#contacts_propertymenu a').live('click',function(){
Contacts.UI.Card.addProperty(this); var type = $(this).data('type');
Contacts.UI.Card.addProperty(type);
$('#contacts_propertymenu').hide(); $('#contacts_propertymenu').hide();
}); });
}); });

View File

@ -72,17 +72,10 @@
.appendTo( ul ); .appendTo( ul );
}; };
this.button = $( "<button type='button'>&nbsp;</button>" ) /*this.button = $( "<button type='button'>&nbsp;</button>" )
.attr( "tabIndex", -1 ) .attr( "tabIndex", -1 )
.attr( "title", "Show All Items" ) .attr( "title", "Show All Items" )
.insertAfter( input ) .insertAfter( input )
/*.button({
icons: {
primary: "ui-icon-triangle-1-s"
},
text: false
})
.removeClass( "ui-corner-all" )*/
.addClass('svg') .addClass('svg')
.addClass('action') .addClass('action')
.addClass('combo-button') .addClass('combo-button')
@ -99,7 +92,7 @@
// pass empty string as value to search for, displaying all results // pass empty string as value to search for, displaying all results
input.autocomplete( "search", "" ); input.autocomplete( "search", "" );
input.focus(); input.focus();
}); });*/
$.each(this.options, function(key, value) { $.each(this.options, function(key, value) {
self._setOption(key, value); self._setOption(key, value);
}); });
@ -123,17 +116,23 @@
case "id": case "id":
this.options['id'] = value; this.options['id'] = value;
this.input.attr('id', value); this.input.attr('id', value);
break; break;
case "name": case "name":
this.options['name'] = value; this.options['name'] = value;
this.input.attr('name', value); this.input.attr('name', value);
break; break;
case "attributes":
var input = this.input;
$.each(this.options['attributes'], function(key, value) {
input.attr(key, value);
});
break;
case "classes": case "classes":
var input = this.input; var input = this.input;
$.each(this.options['classes'], function(key, value) { $.each(this.options['classes'], function(key, value) {
input.addClass(value); input.addClass(value);
}); });
break; break;
} }
// In jQuery UI 1.8, you have to manually invoke the _setOption method from the base widget // In jQuery UI 1.8, you have to manually invoke the _setOption method from the base widget
$.Widget.prototype._setOption.apply( this, arguments ); $.Widget.prototype._setOption.apply( this, arguments );

View File

@ -62,7 +62,7 @@
return false; return false;
} }
}); });
this.button = $( "<button type='button'>&nbsp;</button>" ) /*this.button = $( "<button type='button'>&nbsp;</button>" )
.attr( "tabIndex", -1 ) .attr( "tabIndex", -1 )
.attr( "title", "Show All Items" ) .attr( "title", "Show All Items" )
.insertAfter( this.element ) .insertAfter( this.element )
@ -86,7 +86,7 @@
// pass empty string as value to search for, displaying all results // pass empty string as value to search for, displaying all results
self.element.autocomplete( "search", "" ); self.element.autocomplete( "search", "" );
self.element.focus(); self.element.focus();
}); });*/
}, },
}); });
})( jQuery ); })( jQuery );

View File

@ -169,7 +169,7 @@ class OC_Contacts_Addressbook{
$uid = OC_User::getUser(); $uid = OC_User::getUser();
} }
$prefbooks = OC_Preferences::getValue($uid,'contacts','openaddressbooks',null); $prefbooks = OC_Preferences::getValue($uid,'contacts','openaddressbooks',null);
if(is_null($prefbooks)){ if(!$prefbooks){
$addressbooks = OC_Contacts_Addressbook::all($uid); $addressbooks = OC_Contacts_Addressbook::all($uid);
if(count($addressbooks) == 0){ if(count($addressbooks) == 0){
OC_Contacts_Addressbook::add($uid,'default','Default Address Book'); OC_Contacts_Addressbook::add($uid,'default','Default Address Book');

View File

@ -1,6 +1,7 @@
<script type='text/javascript'> <script type='text/javascript'>
var totalurl = '<?php echo OC_Helper::linkToAbsolute('contacts', 'carddav.php'); ?>/addressbooks'; var totalurl = '<?php echo OC_Helper::linkToAbsolute('contacts', 'carddav.php'); ?>/addressbooks';
var categories = <?php sort($_['categories']); echo json_encode($_['categories']); ?>; var categories = <?php sort($_['categories']); echo json_encode($_['categories']); ?>;
var lang = '<?php echo OC_Preferences::getValue(OC_User::getUser(), 'core', 'lang', 'en'); ?>';
</script> </script>
<div id="controls"> <div id="controls">
<form> <form>

View File

@ -17,16 +17,16 @@ $id = isset($_['id']) ? $_['id'] : '';
<li><a data-type="CATEGORIES"><?php echo $l->t('Categories'); ?></a></li> <li><a data-type="CATEGORIES"><?php echo $l->t('Categories'); ?></a></li>
</ul> </ul>
</div> </div>
<img onclick="Contacts.UI.Card.export();" class="svg action" id="contacts_downloadcard" src="<?php echo image_path('', 'actions/download.svg'); ?>" title="<?php echo $l->t('Download contact');?>" /> <img onclick="Contacts.UI.Card.doExport();" class="svg action" id="contacts_downloadcard" src="<?php echo image_path('', 'actions/download.svg'); ?>" title="<?php echo $l->t('Download contact');?>" />
<img class="svg action" id="contacts_deletecard" src="<?php echo image_path('', 'actions/delete.svg'); ?>" title="<?php echo $l->t('Delete contact');?>" /> <img class="svg action" id="contacts_deletecard" src="<?php echo image_path('', 'actions/delete.svg'); ?>" title="<?php echo $l->t('Delete contact');?>" />
</div> </div>
<div class="contactsection"> <div id="contact_photo" class="contactsection">
<form style="display:none;" id="file_upload_form" action="ajax/uploadphoto.php" method="post" enctype="multipart/form-data" target="file_upload_target" class="propertycontainer" data-element="PHOTO"> <form class="float" id="file_upload_form" action="ajax/uploadphoto.php" method="post" enctype="multipart/form-data" target="file_upload_target" class="propertycontainer" data-element="PHOTO">
<fieldset id="photo" class="formfloat"> <fieldset id="photo">
<a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a> <a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a>
<div id="contacts_details_photo_wrapper" title="<?php echo $l->t('Click or drop to upload picture'); ?> (max <?php echo $_['uploadMaxHumanFilesize']; ?>)"> <div class="tip" id="contacts_details_photo_wrapper" title="<?php echo $l->t('Click or drop to upload picture'); ?> (max <?php echo $_['uploadMaxHumanFilesize']; ?>)">
<!-- img style="padding: 1em;" id="contacts_details_photo" alt="Profile picture" src="photo.php?id=<?php echo $_['id']; ?>" / --> <!-- img style="padding: 1em;" id="contacts_details_photo" alt="Profile picture" src="photo.php?id=<?php echo $_['id']; ?>" / -->
<progress id="contacts_details_photo_progress" style="display:none;" value="0" max="100">0 %</progress> <progress id="contacts_details_photo_progress" style="display:none;" value="0" max="100">0 %</progress>
</div> </div>
@ -37,58 +37,55 @@ $id = isset($_['id']) ? $_['id'] : '';
<iframe name="file_upload_target" id='file_upload_target' src=""></iframe> <iframe name="file_upload_target" id='file_upload_target' src=""></iframe>
</fieldset> </fieldset>
</form> </form>
<form id="contact_identity" method="post" <?php echo ($_['id']==''||!isset($_['id'])?'style="display:none;"':''); ?>> </div> <!-- contact_photo -->
<div id="contact_identity" class="contactsection">
<form method="post">
<input type="hidden" name="id" value="<?php echo $_['id'] ?>"> <input type="hidden" name="id" value="<?php echo $_['id'] ?>">
<fieldset class="propertycontainer" data-element="N"><input type="hidden" id="n" class="contacts_property" name="value" value="" /></fieldset> <fieldset id="ident" class="contactpart">
<fieldset id="ident" class="formfloat">
<!-- legend>Name</legend --> <!-- legend>Name</legend -->
<dl class="form"> <span class="propertycontainer" data-element="N"><input type="hidden" id="n" class="contacts_property" name="value" value="" /></span>
<!-- dt><label for="n"><?php echo $l->t('Name'); ?></label></dt> <span id="name" class="propertycontainer" data-element="FN">
<dd style="padding-top: 0.8em;vertical-align: text-bottom;"><span id="n" type="text"></span></dd --> <select class="float" id="fn_select" title="<?php echo $l->t('Format custom, Short name, Full name, Reverse or Reverse with comma'); ?>" style="width:16em;">
<dt><label for="fn"><?php echo $l->t('Display name'); ?></label></dt> </select><a id="edit_name" class="action edit" title="<?php echo $l->t('Edit name details'); ?>"></a>
<dd class="propertycontainer" data-element="FN"> </span>
<select id="fn_select" title="<?php echo $l->t('Format custom, Short name, Full name, Reverse or Reverse with comma'); ?>" style="width:16em;"> <dl id="identityprops" class="form">
</select><a id="edit_name" class="action edit" title="<?php echo $l->t('Edit name details'); ?>"></a>
</dd>
<dt style="display:none;" id="org_label" data-element="ORG"><label for="org"><?php echo $l->t('Organization'); ?></label></dt> <dt style="display:none;" id="org_label" data-element="ORG"><label for="org"><?php echo $l->t('Organization'); ?></label></dt>
<dd style="display:none;" class="propertycontainer" id="org_value" data-element="ORG"><input id="org" required="required" name="value[ORG]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Organization'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd> <dd style="display:none;" class="propertycontainer" id="org_value" data-element="ORG"><input id="org" required="required" name="value[ORG]" type="text" class="contacts_property big" name="value" value="" placeholder="<?php echo $l->t('Organization'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
<dt style="display:none;" id="nickname_label" data-element="NICKNAME"><label for="nickname"><?php echo $l->t('Nickname'); ?></label></dt> <dt style="display:none;" id="nickname_label" data-element="NICKNAME"><label for="nickname"><?php echo $l->t('Nickname'); ?></label></dt>
<dd style="display:none;" class="propertycontainer" id="nickname_value" data-element="NICKNAME"><input id="nickname" required="required" name="value[NICKNAME]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Enter nickname'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd> <dd style="display:none;" class="propertycontainer" id="nickname_value" data-element="NICKNAME"><input id="nickname" required="required" name="value[NICKNAME]" type="text" class="contacts_property big" name="value" value="" placeholder="<?php echo $l->t('Enter nickname'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
<dt style="display:none;" id="bday_label" data-element="BDAY"><label for="bday"><?php echo $l->t('Birthday'); ?></label></dt> <dt style="display:none;" id="bday_label" data-element="BDAY"><label for="bday"><?php echo $l->t('Birthday'); ?></label></dt>
<dd style="display:none;" class="propertycontainer" id="bday_value" data-element="BDAY"><input id="bday" required="required" name="value" type="text" class="contacts_property" value="" placeholder="<?php echo $l->t('dd-mm-yyyy'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd> <dd style="display:none;" class="propertycontainer" id="bday_value" data-element="BDAY"><input id="bday" required="required" name="value" type="text" class="contacts_property big" value="" placeholder="<?php echo $l->t('dd-mm-yyyy'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
<dt style="display:none;" id="categories_label" data-element="CATEGORIES"><label for="categories"><?php echo $l->t('Categories'); ?></label></dt> <dt style="display:none;" id="categories_label" data-element="CATEGORIES"><label for="categories"><?php echo $l->t('Groups'); ?></label></dt>
<dd style="display:none;" class="propertycontainer" id="categories_value" data-element="CATEGORIES"><input id="categories" required="required" name="value[CATEGORIES]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Categories'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a><a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"></a></dd> <dd style="display:none;" class="propertycontainer" id="categories_value" data-element="CATEGORIES"><input id="categories" required="required" name="value[CATEGORIES]" type="text" class="contacts_property bold" name="value" value="" placeholder="
<?php echo $l->t('Separate groups with commas'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a><a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"></a></dd>
</dl> </dl>
</fieldset> </fieldset>
<fieldset id="note" class="formfloat propertycontainer contactpart" style="display:none;" data-element="NOTE">
<legend><?php echo $l->t('Note'); ?><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></legend>
<textarea class="contacts_property note" name="value" cols="60" rows="10"></textarea>
</fieldset>
</form> </form>
</div> </div> <!-- contact_identity -->
<!-- div class="delimiter"></div --> <!-- div class="delimiter"></div -->
<form id="contact_communication" method="post" style="display: none;"> <div id="contact_communication" class="contactsection">
<div class="contactsection"> <form method="post">
<!-- email addresses --> <!-- email addresses -->
<div id="emails" style="display:none;"> <div id="emails">
<fieldset class="contactpart"> <fieldset class="contactpart">
<legend><?php echo $l->t('Email'); ?></legend> <!-- legend><?php echo $l->t('Email'); ?></legend -->
<ul id="emaillist" class="propertylist"> <ul id="emaillist" class="propertylist">
<li class="template" style="white-space: nowrap; display: none;" data-element="EMAIL"> <li class="template" style="white-space: nowrap; display: none;" data-element="EMAIL">
<input type="checkbox" class="contacts_property" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" /> <input type="checkbox" class="contacts_property tip" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" />
<input type="email" required="required" class="nonempty contacts_property" style="width:15em;" name="value" value="" x-moz-errormessage="<?php echo $l->t('Please specify a valid email address.'); ?>" placeholder="<?php echo $l->t('Enter email address'); ?>" /><span class="listactions"><a onclick="Contacts.UI.mailTo(this)" class="action mail" title="<?php echo $l->t('Mail to address'); ?>"></a> <input type="email" required="required" class="nonempty contacts_property" style="width:15em;" name="value" value="" x-moz-errormessage="<?php echo $l->t('Please specify a valid email address.'); ?>" placeholder="<?php echo $l->t('Enter email address'); ?>" /><span class="listactions"><a onclick="Contacts.UI.mailTo(this)" class="action mail" title="<?php echo $l->t('Mail to address'); ?>"></a>
<a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="<?php echo $l->t('Delete email address'); ?>"></a></span></li> <a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="<?php echo $l->t('Delete email address'); ?>"></a></span></li>
</ul><!-- a id="add_email" class="add" title="<?php echo $l->t('Add email address'); ?>"></a --> </ul><!-- a id="add_email" class="add" title="<?php echo $l->t('Add email address'); ?>"></a -->
</div> <!-- email addresses--> </div> <!-- email addresses-->
<!-- Phone numbers --> <!-- Phone numbers -->
<div id="phones" style="display:none;"> <div id="phones">
<fieldset class="contactpart"> <fieldset class="contactpart">
<legend><?php echo $l->t('Phone'); ?></legend> <!-- legend><?php echo $l->t('Phone'); ?></legend -->
<ul id="phonelist" class="propertylist"> <ul id="phonelist" class="propertylist">
<li class="template" style="white-space: nowrap; display: none;" data-element="TEL"> <li class="template" style="white-space: nowrap; display: none;" data-element="TEL">
<input type="checkbox" class="contacts_property" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" /> <input type="checkbox" class="contacts_property tip" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" />
<input type="text" required="required" class="nonempty contacts_property" style="width:10em; border: 0px;" name="value" value="" placeholder="<?php echo $l->t('Enter phone number'); ?>" /> <input type="text" required="required" class="nonempty contacts_property" style="width:10em; border: 0px;" name="value" value="" placeholder="<?php echo $l->t('Enter phone number'); ?>" />
<select multiple="multiple" name="parameters[TYPE][]"> <select multiple="multiple" name="parameters[TYPE][]">
<?php echo html_select_options($_['phone_types'], array()) ?> <?php echo html_select_options($_['phone_types'], array()) ?>
@ -100,7 +97,7 @@ $id = isset($_['id']) ? $_['id'] : '';
<!-- Addresses --> <!-- Addresses -->
<div id="addresses" style="display:none;"> <div id="addresses" style="display:none;">
<fieldset class="contactpart"> <fieldset class="contactpart">
<legend><?php echo $l->t('Address'); ?></legend> <!-- legend><?php echo $l->t('Address'); ?></legend -->
<div id="addressdisplay"> <div id="addressdisplay">
<dl class="addresscard template" style="display: none;" data-element="ADR"><dt> <dl class="addresscard template" style="display: none;" data-element="ADR"><dt>
<input class="adr contacts_property" name="value" type="hidden" value="" /> <input class="adr contacts_property" name="value" type="hidden" value="" />
@ -109,13 +106,18 @@ $id = isset($_['id']) ? $_['id'] : '';
</dt><dd><ul class="addresslist"></ul></dd></dl> </dt><dd><ul class="addresslist"></ul></dd></dl>
</fieldset> </fieldset>
</div> </div> <!-- addressdisplay -->
</div> <!-- Addresses --> </div> <!-- Addresses -->
</div>
<!-- a id="add_address" onclick="Contacts.UI.Card.editAddress('new', true)" class="add" title="<?php echo $l->t('Add address'); ?>"></a -->
</div>
</form> </form>
</div> </div> <!-- contact_communication -->
<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>
</fieldset>
</form>
</div> <!-- contact_note -->
</div> <!-- card -->
<div id="edit_photo_dialog" title="Edit photo"> <div id="edit_photo_dialog" title="Edit photo">
<div id="edit_photo_dialog_img"></div> <div id="edit_photo_dialog_img"></div>
</div> </div>
@ -128,7 +130,7 @@ $(document).ready(function(){
Contacts.UI.Card.loadContact(jsondata.data); Contacts.UI.Card.loadContact(jsondata.data);
} }
else{ else{
Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
} }
}); });
} }

View File

@ -22,44 +22,44 @@ foreach(isset($adr['parameters']['TYPE'])?array($adr['parameters']['TYPE']):arra
<label class="label" for="adr_pobox"><?php echo $l->t('PO Box'); ?></label> <label class="label" for="adr_pobox"><?php echo $l->t('PO Box'); ?></label>
</dt> </dt>
<dd> <dd>
<input type="text" id="adr_pobox" name="value[ADR][0]" value="<?php echo isset($adr['value'][0])?$adr['value'][0]:''; ?>"> <input type="text" id="adr_pobox" name="value[ADR][0]" placeholder="<?php echo $l->t('PO Box'); ?>" value="<?php echo isset($adr['value'][0])?$adr['value'][0]:''; ?>">
</dd> </dd>
<dd> <dd>
<dt> <dt>
<label class="label" for="adr_extended"><?php echo $l->t('Extended'); ?></label> <label class="label" for="adr_extended"><?php echo $l->t('Extended'); ?></label>
</dt> </dt>
<dd> <dd>
<input type="text" id="adr_extended" name="value[ADR][1]" value="<?php echo isset($adr['value'][1])?$adr['value'][1]:''; ?>"> <input type="text" id="adr_extended" name="value[ADR][1]" placeholder="<?php echo $l->t('Extended'); ?>" value="<?php echo isset($adr['value'][1])?$adr['value'][1]:''; ?>">
</dd> </dd>
<dt> <dt>
<label class="label" for="adr_street"><?php echo $l->t('Street'); ?></label> <label class="label" for="adr_street"><?php echo $l->t('Street'); ?></label>
</dt> </dt>
<dd> <dd>
<input type="text" id="adr_street" name="value[ADR][2]" value="<?php echo isset($adr['value'][2])?$adr['value'][2]:''; ?>"> <input type="text" id="adr_street" name="value[ADR][2]" placeholder="<?php echo $l->t('Street'); ?>" value="<?php echo isset($adr['value'][2])?$adr['value'][2]:''; ?>">
</dd> </dd>
<dt> <dt>
<label class="label" for="adr_city"><?php echo $l->t('City'); ?></label> <label class="label" for="adr_city"><?php echo $l->t('City'); ?></label>
</dt> </dt>
<dd> <dd>
<input type="text" id="adr_city" name="value[ADR][3]" value="<?php echo isset($adr['value'][3])?$adr['value'][3]:''; ?>"> <input type="text" id="adr_city" name="value[ADR][3]" placeholder="<?php echo $l->t('City'); ?>" value="<?php echo isset($adr['value'][3])?$adr['value'][3]:''; ?>">
</dd> </dd>
<dt> <dt>
<label class="label" for="adr_region"><?php echo $l->t('Region'); ?></label> <label class="label" for="adr_region"><?php echo $l->t('Region'); ?></label>
</dt> </dt>
<dd> <dd>
<input type="text" id="adr_region" name="value[ADR][4]" value="<?php echo isset($adr['value'][4])?$adr['value'][4]:''; ?>"> <input type="text" id="adr_region" name="value[ADR][4]" placeholder="<?php echo $l->t('Region'); ?>" value="<?php echo isset($adr['value'][4])?$adr['value'][4]:''; ?>">
</dd> </dd>
<dt> <dt>
<label class="label" for="adr_zipcode"><?php echo $l->t('Zipcode'); ?></label> <label class="label" for="adr_zipcode"><?php echo $l->t('Zipcode'); ?></label>
</dt> </dt>
<dd> <dd>
<input type="text" id="adr_zipcode" name="value[ADR][5]" value="<?php echo isset($adr['value'][5])?$adr['value'][5]:''; ?>"> <input type="text" id="adr_zipcode" name="value[ADR][5]" placeholder="<?php echo $l->t('Zipcode'); ?>" value="<?php echo isset($adr['value'][5])?$adr['value'][5]:''; ?>">
</dd> </dd>
<dt> <dt>
<label class="label" for="adr_country"><?php echo $l->t('Country'); ?></label> <label class="label" for="adr_country"><?php echo $l->t('Country'); ?></label>
</dt> </dt>
<dd> <dd>
<input type="text" id="adr_country" name="value[ADR][6]" value="<?php echo isset($adr['value'][6])?$adr['value'][6]:''; ?>"> <input type="text" id="adr_country" name="value[ADR][6]" placeholder="<?php echo $l->t('Country'); ?>" value="<?php echo isset($adr['value'][6])?$adr['value'][6]:''; ?>">
</dd> </dd>
</dl> </dl>
</fieldset> </fieldset>

View File

@ -0,0 +1,8 @@
<div id="firstrun">
<?php echo $l->t('You have no contacts in your list.') ?>
<div id="selections">
<input type="button" value="<?php echo $l->t('Import contacts') ?>" onclick="Contacts.UI.Addressbooks.doImport()" />
<input type="button" value="<?php echo $l->t('Add contact') ?>" onclick="Contacts.UI.Card.editNew()" />
<input type="button" value="<?php echo $l->t('Edit addressbooks') ?>" onclick="Contacts.UI.Addressbooks.overview()" />
</div>
</div>

View File

@ -8,5 +8,6 @@
<dt><?php echo $l->t('iOS/OS X'); ?></dt> <dt><?php echo $l->t('iOS/OS X'); ?></dt>
<dd><code><?php echo OC_Helper::linkToAbsolute('contacts', 'carddav.php'); ?>/principals/<?php echo OC_User::getUser(); ?></code>/</dd> <dd><code><?php echo OC_Helper::linkToAbsolute('contacts', 'carddav.php'); ?>/principals/<?php echo OC_User::getUser(); ?></code>/</dd>
</dl> </dl>
Powered by <a href="http://geonames.org/" target="_blank">geonames.org webservice</a>
</fieldset> </fieldset>
</form> </form>

View File

@ -22,9 +22,11 @@ function setSyntaxMode(ext){
filetype["css"] = "css"; filetype["css"] = "css";
filetype["groovy"] = "groovy"; filetype["groovy"] = "groovy";
filetype["haxe"] = "hx"; filetype["haxe"] = "hx";
filetype["htm"] = "html";
filetype["html"] = "html"; filetype["html"] = "html";
filetype["java"] = "java"; filetype["java"] = "java";
filetype["js"] = "javascript"; filetype["js"] = "javascript";
filetype["jsm"] = "javascript";
filetype["json"] = "json"; filetype["json"] = "json";
filetype["latex"] = "latex"; filetype["latex"] = "latex";
filetype["ly"] = "latex"; filetype["ly"] = "latex";
@ -141,32 +143,40 @@ function doSearch(){
// Tries to save the file. // Tries to save the file.
function doFileSave(){ function doFileSave(){
if(editorIsShown()){ if(editorIsShown()){
// Get file path // Changed contents?
var path = $('#editor').attr('data-dir')+'/'+$('#editor').attr('data-filename'); if($('#editor').attr('data-edited')=='true'){
// Get original mtime // Get file path
var mtime = $('#editor').attr('data-mtime'); var path = $('#editor').attr('data-dir')+'/'+$('#editor').attr('data-filename');
// Show saving spinner // Get original mtime
$("#editor_save").die('click',doFileSave); var mtime = $('#editor').attr('data-mtime');
$('#save_result').remove(); // Show saving spinner
$('#editor_save').text(t('files_texteditor','Saving...')); $("#editor_save").die('click',doFileSave);
// Get the data $('#save_result').remove();
var filecontents = window.aceEditor.getSession().getValue(); $('#editor_save').text(t('files_texteditor','Saving...'));
// Send the data // Get the data
$.post(OC.filePath('files_texteditor','ajax','savefile.php'), { filecontents: filecontents, path: path, mtime: mtime },function(jsondata){ var filecontents = window.aceEditor.getSession().getValue();
if(jsondata.status!='success'){ // Send the data
// Save failed $.post(OC.filePath('files_texteditor','ajax','savefile.php'), { filecontents: filecontents, path: path, mtime: mtime },function(jsondata){
$('#editor_save').text(t('files_texteditor','Save')); if(jsondata.status!='success'){
$('#editor_save').after('<p id="save_result" style="float: left">Failed to save file</p>'); // Save failed
$("#editor_save").live('click',doFileSave); $('#editor_save').text(t('files_texteditor','Save'));
} else { $('#editor_save').after('<p id="save_result" style="float: left">Failed to save file</p>');
// Save OK $("#editor_save").live('click',doFileSave);
// Update mtime } else {
$('#editor').attr('data-mtime',jsondata.data.mtime); // Save OK
$('#editor_save').text(t('files_texteditor','Save')); // Update mtime
$("#editor_save").live('click',doFileSave); $('#editor').attr('data-mtime',jsondata.data.mtime);
} $('#editor_save').text(t('files_texteditor','Save'));
},'json'); $("#editor_save").live('click',doFileSave);
// Update titles
$('#editor').attr('data-edited', 'false');
$('#breadcrumb_file').text($('#editor').attr('data-filename'));
document.title = $('#editor').attr('data-filename')+' - ownCloud';
}
},'json');
}
} }
giveEditorFocus();
}; };
// Gives the editor focus // Gives the editor focus
@ -176,6 +186,8 @@ function giveEditorFocus(){
// Loads the file editor. Accepts two parameters, dir and filename. // Loads the file editor. Accepts two parameters, dir and filename.
function showFileEditor(dir,filename){ function showFileEditor(dir,filename){
// Delete any old editors
$('#editor').remove();
if(!editorIsShown()){ if(!editorIsShown()){
// Loads the file editor and display it. // Loads the file editor and display it.
$('#content').append('<div id="editor"></div>'); $('#content').append('<div id="editor"></div>');
@ -192,10 +204,11 @@ function showFileEditor(dir,filename){
// Show the control bar // Show the control bar
showControls(filename,result.data.write); showControls(filename,result.data.write);
// Update document title // Update document title
document.title = filename; document.title = filename+' - ownCloud';
$('#editor').text(result.data.filecontents); $('#editor').text(result.data.filecontents);
$('#editor').attr('data-dir', dir); $('#editor').attr('data-dir', dir);
$('#editor').attr('data-filename', filename); $('#editor').attr('data-filename', filename);
$('#editor').attr('data-edited', 'false');
window.aceEditor = ace.edit("editor"); window.aceEditor = ace.edit("editor");
aceEditor.setShowPrintMargin(false); aceEditor.setShowPrintMargin(false);
aceEditor.getSession().setUseWrapMode(true); aceEditor.getSession().setUseWrapMode(true);
@ -207,10 +220,17 @@ function showFileEditor(dir,filename){
OC.addScript('files_texteditor','aceeditor/theme-clouds', function(){ OC.addScript('files_texteditor','aceeditor/theme-clouds', function(){
window.aceEditor.setTheme("ace/theme/clouds"); window.aceEditor.setTheme("ace/theme/clouds");
}); });
window.aceEditor.getSession().on('change', function(){
if($('#editor').attr('data-edited')!='true'){
$('#editor').attr('data-edited', 'true');
$('#breadcrumb_file').text($('#breadcrumb_file').text()+' *');
document.title = $('#editor').attr('data-filename')+' * - ownCloud';
}
});
}); });
} else { } else {
// Failed to get the file. // Failed to get the file.
alert(result.data.message); OC.dialogs.alert(result.data.message, t('files_texteditor','An error occurred!'));
} }
// End success // End success
} }
@ -222,37 +242,54 @@ function showFileEditor(dir,filename){
// Fades out the editor. // Fades out the editor.
function hideFileEditor(){ function hideFileEditor(){
// Fades out editor controls if($('#editor').attr('data-edited') == 'true'){
$('#editorcontrols').fadeOut('slow',function(){ // Hide, not remove
$(this).remove(); $('#editorcontrols').fadeOut('slow',function(){
$(".crumb:last").addClass('last'); // Check if there is a folder in the breadcrumb
}); if($('.crumb.ui-droppable').length){
// Fade out editor $('.crumb.ui-droppable:last').addClass('last');
$('#editor').fadeOut('slow', function(){ }
$(this).remove(); });
// Reset document title // Fade out editor
document.title = "ownCloud"; $('#editor').fadeOut('slow', function(){
var editorhtml = '<div id="editor"></div>'; // Reset document title
$('table').after(editorhtml); document.title = "ownCloud";
$('.actions,#file_access_panel').fadeIn('slow'); $('.actions,#file_access_panel').fadeIn('slow');
$('table').fadeIn('slow'); $('table').fadeIn('slow');
}); });
is_editor_shown = false; $('#notification').text(t('files_texteditor','There were unsaved changes, click here to go back'));
$('#notification').data('reopeneditor',true);
$('#notification').fadeIn();
is_editor_shown = false;
} else {
// Remove editor
$('#editorcontrols').fadeOut('slow',function(){
$(this).remove();
$(".crumb:last").addClass('last');
});
// Fade out editor
$('#editor').fadeOut('slow', function(){
$(this).remove();
// Reset document title
document.title = "ownCloud";
$('.actions,#file_access_panel').fadeIn('slow');
$('table').fadeIn('slow');
});
is_editor_shown = false;
}
} }
// Keyboard Shortcuts // Reopens the last document
var ctrlBtn = false; function reopenEditor(){
$('.actions,#file_action_panel').fadeOut('slow');
$('table').fadeOut('slow', function(){
$('#controls .last').not('#breadcrumb_file').removeClass('last');
$('#editor').fadeIn('fast');
$('#editorcontrols').fadeIn('fast', function(){
// TODO fix detection of ctrl keyup });
// returns true if ctrl+s or cmd+s is being pressed });
function checkForSaveKeyPress(e){ is_editor_shown = true;
if(e.which == 17 || e.which == 91) ctrlBtn=true;
if(e.which == 83 && ctrlBtn == true) {
e.preventDefault();
$('#editor_save').trigger('click');
return false;
}
} }
// resizes the editor window // resizes the editor window
@ -285,6 +322,11 @@ $(document).ready(function(){
// Binds the file save and close editor events, and gotoline button // Binds the file save and close editor events, and gotoline button
bindControlEvents(); bindControlEvents();
$('#editor').remove(); $('#editor').remove();
// Binds the save keyboard shortcut events $('#notification').click(function(){
//$(document).unbind('keydown').bind('keydown',checkForSaveKeyPress); if($('#notification').data('reopeneditor'))
{
reopenEditor();
}
$('#notification').fadeOut();
});
}); });

View File

@ -0,0 +1,12 @@
<?php
/**
* Copyright (c) 2011 Craig Roberts craig0990@googlemail.com
* This file is licensed under the Affero General Public License version 3 or
* later.
*/
require_once('../../../lib/base.php');
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));

View File

@ -0,0 +1,14 @@
<?php
/**
* Copyright (c) 2011 Craig Roberts craig0990@googlemail.com
* This file is licensed under the Affero General Public License version 3 or
* later.
*/
require_once('../../../lib/base.php');
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();
}

View File

@ -0,0 +1,20 @@
<?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');

View File

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<info>
<id>files_versioning</id>
<name>Versioning and Backup</name>
<version>1.0.0</version>
<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>

View File

@ -0,0 +1,3 @@
#file_versioning_commit_chzn {
width: 15em;
}

View File

@ -0,0 +1,25 @@
$(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');
}
});
});
});

View File

@ -0,0 +1,12 @@
<?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');

View File

@ -0,0 +1,34 @@
<?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();

View File

@ -0,0 +1,12 @@
<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>

View File

@ -0,0 +1,386 @@
<?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;
}
}

View File

@ -0,0 +1,686 @@
<?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;
}
}

View File

@ -43,8 +43,9 @@ function shareGallery() {
{text: 'Shared gallery address', name: 'address', type: 'text', value: existing_token}]; {text: 'Shared gallery address', name: 'address', type: 'text', value: existing_token}];
OC.dialogs.form(form_fields, t('gallery', 'Share gallery'), function(values){ OC.dialogs.form(form_fields, t('gallery', 'Share gallery'), function(values){
var p = ''; var p = '';
for (var i in paths) p += '/'+paths[i]; for (var i in paths) p += paths[i]+'/';
if (p == '') p = '/'; if (p == '') p = '/';
alert(p);
$.getJSON(OC.filePath('gallery', 'ajax', 'galleryOp.php'), {operation: 'share', path: p, share: values[0].value, recursive: values[1].value}, function(r) { $.getJSON(OC.filePath('gallery', 'ajax', 'galleryOp.php'), {operation: 'share', path: p, share: values[0].value, recursive: values[1].value}, function(r) {
if (r.status == 'success') { if (r.status == 'success') {
Albums.shared = r.sharing; Albums.shared = r.sharing;
@ -112,42 +113,28 @@ function scanForAlbums(cleanup) {
} }
function settings() { function settings() {
$( '#g-dialog-settings' ).dialog({ OC.dialogs.form([{text: t('gallery', 'Scanning root'), name: 'root', type:'text', value:gallery_scanning_root},
height: 180, {text: t('gallery', 'Default order'), name: 'order', type:'select', value:gallery_default_order, options:[
width: 350, {text:t('gallery', 'Ascending'), value:'ASC'}, {text: t('gallery', 'Descending'), value:'DESC'} ]}],
modal: false, t('gallery', 'Settings'),
buttons: [ function(values) {
{ var scanning_root = values[0].value;
text: t('gallery', 'Apply'), var disp_order = values[1].value;
click: function() {
var scanning_root = $('#g-scanning-root').val();
var disp_order = $('#g-display-order option:selected').val();
if (scanning_root == '') { if (scanning_root == '') {
alert('Scanning root cannot be empty'); OC.dialogs.alert(t('gallery', 'Scanning root cannot be empty'), t('gallery', 'Error'));
return; return;
} }
$.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'store_settings', root: scanning_root, order: disp_order}, function(r) { $.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'store_settings', root: scanning_root, order: disp_order}, function(r) {
if (r.status == 'success') { if (r.status == 'success') {
if (r.rescan == 'yes') { if (r.rescan == 'yes') {
$('#g-dialog-settings').dialog('close'); Albums.clear(document.getElementById('gallery_list'));
Albums.clear(document.getElementById('gallery_list')); scanForAlbums(true);
scanForAlbums(true); }
return; gallery_scanning_root = scanning_root;
}
} else { } else {
alert('Error: ' + r.cause); OC.dialogs.alert(t('gallery', 'Error: ') + r.cause, t('gallery', 'Error'));
return; return;
} }
$('#g-dialog-settings').dialog('close');
}); });
} });
},
{
text: t('gallery', 'Cancel'),
click: function() {
$(this).dialog('close');
}
}
],
});
} }

View File

@ -9,7 +9,7 @@ OC_Util::addScript('files_imageviewer', 'jquery.fancybox-1.3.4.pack');
OC_Util::addStyle( 'files_imageviewer', 'jquery.fancybox-1.3.4' ); OC_Util::addStyle( 'files_imageviewer', 'jquery.fancybox-1.3.4' );
$l = new OC_L10N('gallery'); $l = new OC_L10N('gallery');
?> ?>
<script type="text/javascript">var gallery_scanning_root='<? echo OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '/'); ?>'; var gallery_default_order = '<? echo OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'order', 'ASC'); ?>';</script>
<div id="controls"> <div id="controls">
<div id="scan"> <div id="scan">
<div id="scanprogressbar"></div> <div id="scanprogressbar"></div>
@ -29,40 +29,3 @@ $l = new OC_L10N('gallery');
</div> </div>
<div id="gallery_list"> <div id="gallery_list">
</div> </div>
<div id="dialog-confirm" title="<?php echo $l->t('Remove confirmation');?>" style="display: none">
<p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span><?php echo $l->t('Do you want to remove album');?> <span id="albumName"></span>?</p>
</div>
<div id="dialog-form" title="<?php echo $l->t('Change album name');?>" style="display:none">
<form>
<fieldset>
<label for="name"><?php echo $l->t('New album name');?></label>
<input type="text" name="name" id="name" class="text ui-widget-content ui-corner-all" />
</fieldset>
</form>
</div>
<div id="g-dialog-settings" title="<?php echo $l->t('Settings');?>" style="display:none">
<form>
<fieldset><?php $root = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '/'); $order = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'order', 'ASC');?>
<label for="name"><?php echo $l->t('Scanning root');?></label>
<input type="text" name="g-scanning-root" id="g-scanning-root" class="text ui-widget-content ui-corner-all" value="<?php echo $root;?>" /><br/>
<label for="sort"><?php echo $l->t('Default sorting'); ?></label>
<select id="g-display-order">
<option value="ASC"<?php echo $order=='ASC'?'selected':'';?>><?php echo $l->t('Ascending'); ?></option>
<option value="DESC"<?php echo $order=='DESC'?'selected':'';?>><?php echo $l->t('Descending'); ?></option>
</select><br/>
<!--
<label for="sort"><?php echo $l->t('Thumbnails size'); ?></label>
<select>
<option value="100">100px</option>
<option value="150">150px</option>
<option value="200">200px</option>
</select>
-->
</fieldset>
</form>
</div>

View File

@ -2,8 +2,8 @@
<info> <info>
<id>remoteStorage</id> <id>remoteStorage</id>
<name>remoteStorage compatibility</name> <name>remoteStorage compatibility</name>
<description>Enables you to use ownCloud as their remote storage for unhosted applications. This app requires the Webfinger app to be enabled as well. More info on <a href="http://unhosted.org">the website of the unhosted movement</a>.</description> <description>Enables you to use ownCloud as their remote storage for unhosted applications. This app requires the Webfinger app to be installed and enabled correctly. More info on <a href="http://unhosted.org">the website of the unhosted movement</a>.</description>
<version>0.5</version> <version>0.6</version>
<licence>AGPL or MIT</licence> <licence>AGPL or MIT</licence>
<author>Michiel de Jong</author> <author>Michiel de Jong</author>
<require>2</require> <require>2</require>

View File

@ -0,0 +1,6 @@
<Link
rel="remoteStorage"
template="<?php echo WF_BASEURL; ?>/apps/remoteStorage/WebDAV.php/<?php echo WF_USER; ?>/remoteStorage/{category}/"
api="WebDAV"
auth="<?php echo WF_BASEURL; ?>/apps/remoteStorage/auth.php/<?php echo WF_USER; ?>">
</Link>

View File

@ -7,4 +7,7 @@
<licence>AGPL</licence> <licence>AGPL</licence>
<author>Dominik Schmidt</author> <author>Dominik Schmidt</author>
<require>2</require> <require>2</require>
<types>
<authentication/>
</types>
</info> </info>

View File

@ -0,0 +1,87 @@
<?php
/**
* ownCloud - user_migrate
*
* @author Tom Needham
* @copyright 2012 Tom Needham tom@owncloud.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/>.
*
*/
OC_Util::checkAdminUser();
OC_Util::checkAppEnabled('user_migrate');
// Import?
if (isset($_POST['user_import'])) {
$root = OC::$SERVERROOT . "/";
$importname = "owncloud_import_" . date("y-m-d_H-i-s");
// Save data dir for later
$datadir = OC_Config::getValue( 'datadirectory' );
// Copy the uploaded file
$from = $_FILES['owncloud_import']['tmp_name'];
$to = get_temp_dir().'/'.$importname.'.zip';
if( !move_uploaded_file( $from, $to ) ){
$error = array('error'=>'Failed to move the uploaded file','hint'=>'Try checking the permissions of the '.get_temp_dir().' dir.');
OC_Log::write( 'user_migrate', "Failed to copy the uploaded file", OC_Log::ERROR );
$tmpl = new OC_Template('user_migrate', 'admin');
$tmpl->assign('error',$error);
return $tmpl->fetchPage();
}
$response = json_decode( OC_Migrate::import( $to, 'user' ) );
if( !$response->success ){
$error = array('error'=>'There was an error while importing the user!','hint'=>'Please check the logs for a more detailed explaination');
$tmpl = new OC_Template('user_migrate', 'admin');
$tmpl->assign('error',$error);
return $tmpl->fetchPage();
} else {
// Check import status
foreach( $response->data as $app => $status ){
if( $status != 'true' ){
// It failed for some reason
if( $status == 'notsupported' ){
$notsupported[] = $app;
} else if( !$status ){
$failed[] = $app;
}
}
}
// Any problems?
if( isset( $notsupported ) || isset( $failed ) ){
if( count( $failed ) > 0 ){
$error = array('error'=>'Some app data failed to import','hint'=>'App data for: '.implode(', ', $failed).' failed to import.');
$tmpl = new OC_Template('user_migrate', 'admin');
$tmpl->assign('error',$error);
return $tmpl->fetchPage();
} else if( count( $notsupported ) > 0 ){
$error = array('error'=>'Some app data could not be imported, as the apps are not installed on this instance','hint'=>'App data for: '.implode(', ', $notsupported).' failed to import as they were not found. Please install the apps and try again');
$tmpl = new OC_Template('user_migrate', 'admin');
$tmpl->assign('error',$error);
return $tmpl->fetchPage();
}
} else {
// Went swimmingly!
$tmpl = new OC_Template('user_migrate', 'admin');
return $tmpl->fetchPage();
}
}
} else {
// fill template
$tmpl = new OC_Template('user_migrate', 'admin');
return $tmpl->fetchPage();
}

View File

@ -0,0 +1,62 @@
<?php
/**
* ownCloud - user_migrate
*
* @author Tom Needham
* @copyright 2012 Tom Needham tom@owncloud.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/>.
*
*/
// Init owncloud
require_once('../../../lib/base.php');
// Check if we are a user
OC_JSON::checkLoggedIn();
OC_Util::checkAppEnabled('user_migrate');
// Which operation
if( $_GET['operation']=='create' ){
$uid = !empty( $_POST['uid'] ) ? $_POST['uid'] : OC_User::getUser();
if( $uid != OC_User::getUser() ){
// Needs to be admin to export someone elses account
OC_JSON::error();
die();
}
// Create the export zip
$response = json_decode( OC_Migrate::export( $uid ) );
if( !$response->success ){
// Error
OC_JSON::error();
die();
} else {
// Save path in session
$_SESSION['ocuserexportpath'] = $response->data;
}
OC_JSON::success();
die();
} else if( $_GET['operation']=='download' ){
// Download the export
$path = isset( $_SESSION['ocuserexportpath'] ) ? $_SESSION['ocuserexportpath'] : false;
if( !$path ){
OC_JSON::error();
}
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=" . basename($path));
header("Content-Length: " . filesize($path));
@ob_end_clean();
readfile($path);
unlink( $path );
$_SESSION['ocuserexportpath'] = '';
}

View File

@ -0,0 +1,35 @@
<?php
/**
* ownCloud - user_migrate
*
* @author Tom Needham
* @copyright 2012 Tom Needham tom@owncloud.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/>.
*
*/
OC_APP::registerPersonal( 'user_migrate', 'settings' );
OC_APP::registerAdmin( 'user_migrate', 'admin' );
OC_Util::addScript( 'user_migrate', 'export');
// add settings page to navigation
$entry = array(
'id' => "user_migrate_settings",
'order'=>1,
'href' => OC_Helper::linkTo( "user_migrate", "admin.php" ),
'name' => 'Import'
);
?>

View File

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<info>
<id>user_migrate</id>
<name>User Account Migration</name>
<description>Migrate your user accounts</description>
<version>0.1</version>
<licence>AGPL</licence>
<author>Tom Needham</author>
<require>2</require>
<default_enable/>
</info>

View File

@ -0,0 +1,27 @@
$(document).ready(function(){
// Do the export
$('#exportbtn').click(function(){
// Show loader
$('.loading').show();
$.getJSON(
OC.filePath('user_migrate','ajax','export.php'),
{operation:'create'},
function(result){
if(result.status == 'success'){
// Download the file
window.location = OC.filePath('user_migrate','ajax','export.php?operation=download') ;
$('.loading').hide();
$('#exportbtn').val(t('user_migrate', 'Export'));
} else {
// Cancel loading
$('#exportbtn').html('Failed');
// Show Dialog
OC.dialogs.alert(t('user_migrate', 'Something went wrong while the export file was being generated'), t('user_migrate', 'An error has occurred'), function(){
$('#exportbtn').html(t('user_migrate', 'Export')+'<img style="display: none;" class="loading" src="'+OC.filePath('core','img','loading.gif')+'" />');
});
}
}
// End ajax
);
});
});

View File

@ -0,0 +1,29 @@
<?php
/**
* ownCloud - user_migrate
*
* @author Thomas Schmidt
* @copyright 2011 Thomas Schmidt tom@opensuse.org
* @author Tom Needham
* @copyright 2012 Tom Needham tom@owncloud.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/>.
*
*/
OC_Util::checkAppEnabled('user_migrate');
// fill template
$tmpl = new OC_Template('user_migrate', 'settings');
return $tmpl->fetchPage();

View File

@ -0,0 +1,13 @@
<form id="import" action="#" method="post" enctype="multipart/form-data">
<fieldset class="personalblock">
<?php if(isset($_['error'])){ ?>
<h3><?php echo $_['error']['error']; ?></h3>
<p><?php echo $_['error']['hint']; ?></p>
<?php } ?>
<legend><strong><?php echo $l->t('Import user account');?></strong></legend>
</p>
<p><input type="file" id="owncloud_import" name="owncloud_import"><label for="owncloud_import"><?php echo $l->t('ownCloud User Zip');?></label>
</p>
<input type="submit" name="user_import" value="<?php echo $l->t('Import'); ?>" />
</fieldset>
</form>

View File

@ -0,0 +1,6 @@
<fieldset class="personalblock">
<legend><strong><?php echo $l->t('Export your user account');?></strong></legend>
<p><?php echo $l->t('This will create a compressed file that contains your ownCloud account.');?>
</p>
<button id="exportbtn">Export<img style="display: none;" class="loading" src="<?php echo OC_Helper::linkTo('core', 'img/loading.gif'); ?>" /></button>
</fieldset>

View File

@ -7,4 +7,7 @@
<licence>AGPL</licence> <licence>AGPL</licence>
<author>Robin Appelman</author> <author>Robin Appelman</author>
<require>2</require> <require>2</require>
<types>
<authentication/>
</types>
</info> </info>

View File

@ -1,3 +0,0 @@
RewriteEngine On
RewriteBase /
RewriteRule host-meta$ \/\.well-known\/host-meta\.php [L]

View File

@ -2,9 +2,9 @@
<info> <info>
<id>user_webfinger</id> <id>user_webfinger</id>
<name>Webfinger</name> <name>Webfinger</name>
<description>Provide WebFinger for all users so they get a user address like user@owncloudinstance which can be used for unhosted applications. If you don't run ownCloud in the root of your domain, for instance if you run it on example.com/owncloud/, then make sure you link example.com/.well-known/ to example.com/owncloud/apps/user_webfinger/ - by running something like "ln -s /var/www/owncloud/apps/user_webfinger /var/www/.well-known". Only enable this app if you run this ownCloud installation on a public web address, not if you run it on an intranet or on localhost.</description> <description>Provide WebFinger for all users so they get a user address like user@owncloudinstance which can be used for external applications. Other apps can provide information for webfinger requests, such as remoteStorage compatibility.</description>
<version>0.2</version> <version>0.3</version>
<licence>AGPL or MIT</licence> <licence>AGPL or MIT</licence>
<author>Michiel de Jong</author> <author>Michiel de Jong, Florian Hülsmann</author>
<require>2</require> <require>2</require>
</info> </info>

View File

@ -1,6 +1,51 @@
<?php <?php
$hostMetaHeader = array(
'Access-Control-Allow-Origin' => '*',
'Content-Type' => 'application/xml+xrd'
);
$appInfoDir = __DIR__; $appInfoDir = __DIR__;
$thisAppDir = dirname($appInfoDir); $thisAppDir = dirname($appInfoDir);
$appsDir = dirname($thisAppDir); $appsDir = dirname($thisAppDir);
$ownCloudDir = dirname($appsDir); $ownCloudDir = dirname($appsDir);
@symlink($thisAppDir, $ownCloudDir.'/.well-known'); $docRoot = $_SERVER['DOCUMENT_ROOT'];
try {
$webRoot = substr(realpath($ownCloudDir), strlen(realpath($docRoot)));
} catch(Exception $e) {
// some servers fail on realpath(), let's try it the unsecure way:
$webRoot = substr($ownCloudDir, strlen($docRoot));
}
$serverName = $_SERVER['SERVER_NAME'];
$lrddTmpl = 'http';
if(isset($_SERVER['HTTPS'])) {
$lrddTmpl .= 's';
}
$lrddTmpl .= '://' . $serverName . $webRoot . '/apps/user_webfinger/webfinger.php?q={uri}';
$hostMetaPath = $docRoot . '/.well-known/host-meta';
$hostMetaDir = $docRoot . '/.well-known';
$hostMetaContents = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<XRD xmlns=\"http://docs.oasis-open.org/ns/xri/xrd-1.0\" xmlns:hm=\"http://host-meta.net/xrd/1.0\">
<hm:Host xmlns=\"http://host-meta.net/xrd/1.0\">" . $serverName . "</hm:Host>
<Link rel=\"lrdd\" template=\"" . $lrddTmpl . "\">
<Title>Resource Descriptor</Title>
</Link>
</XRD>";
@mkdir($hostMetaDir);
$hostMeta = fopen($hostMetaPath, 'w');
if(!$hostMeta) {
die("Could not open " . $hostMetaPath . " for writing, please check permissions!");
}
if(!fwrite($hostMeta, $hostMetaContents, strlen($hostMetaContents))) {
die("Could not write to " . $hostMetaPath . ", please check permissions!");
}
fclose($hostMeta);
// write custom headers into .htaccess:
$htaccess = fopen($hostMetaDir . '/.htaccess', 'w');
//TODO: check compatibility!
fwrite($htaccess, "<filesMatch \"^host-meta$\">
<ifModule mod_headers.c>\n");
foreach($hostMetaHeader as $header => $value) {
fwrite($htaccess, "Header set " . $header . " \"" . $value . "\"\n");
}
fwrite($htaccess, "</ifModule>\n</filesMatch>");
fclose($htaccess);

View File

@ -1 +0,0 @@
please run 'a2enmod rewrite' on your server, set 'AllowOverride All' for /var/www in /etc/apache2/sites-enabled/000-default or equivalent, and then run '/etc/init.d/apache2 restart'

View File

@ -1,16 +0,0 @@
<?php
if($_SERVER['SCRIPT_NAME'] == '/.well-known/host-meta.php') {
header("Access-Control-Allow-Origin: *");
} else {
header('Please-first: activate');
}
header("Content-Type: application/xrd+xml");
echo "<";
?>
?xml version="1.0" encoding="UTF-8"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0" xmlns:hm="http://host-meta.net/xrd/1.0">
<hm:Host xmlns="http://host-meta.net/xrd/1.0"><?php echo $_SERVER['SERVER_NAME'] ?></hm:Host>
<Link rel="lrdd" template="http<?php echo (isset($_SERVER['HTTPS'])?'s':''); ?>://<?php echo $_SERVER['SERVER_NAME'] ?>/.well-known/webfinger.php?q={uri}">
</Link>
</XRD>

View File

@ -1,41 +1,71 @@
<?php <?php
if($_SERVER['SCRIPT_NAME'] == '/.well-known/webfinger.php') { header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Origin: *");
} else {
header('Please-first: activate');
}
header("Content-Type: application/xrd+xml"); header("Content-Type: application/xrd+xml");
/**
* To include your app in the webfinger XML, add a new script with file name
* 'webfinger.php' to /apps/yourapp/appinfo/, which prints out the XML parts
* to be included. That script can make use of the constants WF_USER (e. g.
* "user"), WF_ID (user@host) and WF_BASEURL (e. g. https://host/owncloud).
* An example could look like this:
*
* <Link
* rel="myProfile"
* type="text/html"
* href="<?php echo WF_BASEURL; ?>/apps/myApp/profile.php?user=<?php echo WF_USER; ?>">
* </Link>
*
'* but can also use complex database queries to generate the webfinger result
**/
// calculate the documentroot // calculate the documentroot
// modified version of the one in lib/base.php that takes the .well-known symlink into account // modified version of the one in lib/base.php that takes the .well-known symlink into account
$DOCUMENTROOT=realpath($_SERVER['DOCUMENT_ROOT']); /*$DOCUMENTROOT=realpath($_SERVER['DOCUMENT_ROOT']);
$SERVERROOT=str_replace("\\",'/',dirname(dirname(dirname(dirname(__FILE__))))); $SERVERROOT=str_replace("\\",'/',dirname(dirname(dirname(dirname(__FILE__)))));
$SUBURI=substr(realpath($_SERVER["SCRIPT_FILENAME"]),strlen($SERVERROOT)); $SUBURI=substr(realpath($_SERVER["SCRIPT_FILENAME"]),strlen($SERVERROOT));
$WEBROOT=substr($SUBURI,0,-34); $WEBROOT=substr($SUBURI,0,-34);
*/
require_once('../../lib/base.php');
$request = urldecode($_GET['q']);
if($_GET['q']) { if($_GET['q']) {
$bits = explode('@', $_GET['q']); $reqParts = explode('@', $request);
$userName = $bits[0]; $userName = $reqParts[0];
$hostName = $reqParts[1];
} else { } else {
$userName = ''; $userName = '';
$hostName = '';
} }
if(substr($userName, 0, 5) == 'acct:') { if(substr($userName, 0, 5) == 'acct:') {
$userName = substr($userName, 5); $userName = substr($userName, 5);
} }
if(isset($_SERVER['HTTPS'])) { if($userName == "") {
$baseAddress = 'https://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/'; $id = "";
} else { } else {
$baseAddress = 'http://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/'; $id = $userName . '@' . $hostName;
} }
if(isset($_SERVER['HTTPS'])) {
$baseAddress = 'https://';
} else {
$baseAddress = 'http://';
}
$baseAddress .= $_SERVER['SERVER_NAME'].OC::$WEBROOT;
define('WF_USER', $userName);
define('WF_ID', $id);
define('WF_BASEURL', $baseAddress);
echo "<"; echo "<";
?> ?>
?xml version="1.0" encoding="UTF-8"?> ?xml version="1.0" encoding="UTF-8"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0" xmlns:hm="http://host-meta.net/xrd/1.0"> <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0" xmlns:hm="http://host-meta.net/xrd/1.0">
<hm:Host xmlns="http://host-meta.net/xrd/1.0"><?php echo $_SERVER['SERVER_NAME'] ?></hm:Host> <hm:Host xmlns="http://host-meta.net/xrd/1.0"><?php echo $_SERVER['SERVER_NAME']; ?></hm:Host>
<Link <Subject>acct:<?php echo $id ?></Subject>
rel="remoteStorage" <?php
template="<?php echo $baseAddress ?>WebDAV.php/<?php echo $userName ?>/remoteStorage/{category}/" $apps = OC_Appconfig::getApps();
api="WebDAV" foreach($apps as $app) {
auth="<?php echo $baseAddress; ?>auth.php/<?php echo $userName ?>" if(OC_App::isEnabled($app)) {
></Link> if(is_file(OC::$APPSROOT . '/apps/' . $app . '/appinfo/webfinger.php')) {
require($app . '/appinfo/webfinger.php');
}
}
}
?>
</XRD> </XRD>

View File

@ -131,3 +131,10 @@ li.error { width:640px; margin:4em auto; padding:1em 1em 1em 4em; background:#ff
.separator { display: inline; border-left: 1px solid #d3d3d3; border-right: 1px solid #fff; height: 10px; width:0px; margin: 4px; } .separator { display: inline; border-left: 1px solid #d3d3d3; border-right: 1px solid #fff; height: 10px; width:0px; margin: 4px; }
a.bookmarklet { background-color: #ddd; border:1px solid #ccc; padding: 5px;padding-top: 0px;padding-bottom: 2px; text-decoration: none; margin-top: 5px } a.bookmarklet { background-color: #ddd; border:1px solid #ccc; padding: 5px;padding-top: 0px;padding-bottom: 2px; text-decoration: none; margin-top: 5px }
/* ---- DIALOGS ---- */
#dirtree {width: 100%;}
#filelist {height: 270px; overflow:scroll; background-color: white;}
.filepicker_element_selected { background-color: lightblue;}
.filepicker_loader {height: 120px; width: 100%; background-color: #333; opacity: 0.3; visibility: visible; position:absolute; top:0; left:0; text-align:center; padding-top: 150px;}

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 B

View File

@ -33,8 +33,12 @@
*/ */
OC.EventSource=function(src,data){ OC.EventSource=function(src,data){
var dataStr=''; var dataStr='';
for(name in data){ this.typelessListeners=[];
dataStr+=name+'='+encodeURIComponent(data[name])+'&'; this.listeners={};
if(data){
for(name in data){
dataStr+=name+'='+encodeURIComponent(data[name])+'&';
}
} }
if(!this.useFallBack && typeof EventSource !='undefined'){ if(!this.useFallBack && typeof EventSource !='undefined'){
this.source=new EventSource(src+'?'+dataStr); this.source=new EventSource(src+'?'+dataStr);
@ -42,7 +46,7 @@ OC.EventSource=function(src,data){
for(var i=0;i<this.typelessListeners.length;i++){ for(var i=0;i<this.typelessListeners.length;i++){
this.typelessListeners[i](JSON.parse(e.data)); this.typelessListeners[i](JSON.parse(e.data));
} }
} }.bind(this);
}else{ }else{
iframeId='oc_eventsource_iframe_'+OC.EventSource.iframeCount; iframeId='oc_eventsource_iframe_'+OC.EventSource.iframeCount;
OC.EventSource.fallBackSources[OC.EventSource.iframeCount]=this; OC.EventSource.fallBackSources[OC.EventSource.iframeCount]=this;
@ -64,7 +68,7 @@ OC.EventSource=function(src,data){
OC.EventSource.fallBackSources=[]; OC.EventSource.fallBackSources=[];
OC.EventSource.iframeCount=0;//number of fallback iframes OC.EventSource.iframeCount=0;//number of fallback iframes
OC.EventSource.fallBackCallBack=function(id,type,data){ OC.EventSource.fallBackCallBack=function(id,type,data){
OC.EventSource.fallBackSources[id].fallBackCallBack(type,JSON.parse(data)); OC.EventSource.fallBackSources[id].fallBackCallBack(type,data);
} }
OC.EventSource.prototype={ OC.EventSource.prototype={
typelessListeners:[], typelessListeners:[],

View File

@ -126,7 +126,13 @@ OC={
}); });
} }
}, },
dialogs:OCdialogs dialogs:OCdialogs,
mtime2date:function(mtime) {
mtime = parseInt(mtime);
var date = new Date(1000*mtime);
var ret = date.getDate()+'.'+(date.getMonth()+1)+'.'+date.getFullYear()+', '+date.getHours()+':'+date.getMinutes();
return ret;
}
}; };
OC.search.customResults={}; OC.search.customResults={};
OC.search.currentResult=-1; OC.search.currentResult=-1;

View File

@ -17,7 +17,6 @@
* You should have received a copy of the GNU Affero General Public * 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/>. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
* *
* todo(bartek): add select option in form
*/ */
/** /**
@ -30,9 +29,9 @@ OCdialogs = {
* @param title dialog title * @param title dialog title
* @param callback which will be triggered when user press OK * @param callback which will be triggered when user press OK
*/ */
alert:function(text, title, callback) { alert:function(text, title, callback, modal) {
var content = '<p><span class="ui-icon ui-icon-alert"></span>'+text+'</p>'; var content = '<p><span class="ui-icon ui-icon-alert"></span>'+text+'</p>';
OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback); OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback, modal);
}, },
/** /**
* displays info dialog * displays info dialog
@ -40,9 +39,9 @@ OCdialogs = {
* @param title dialog title * @param title dialog title
* @param callback which will be triggered when user press OK * @param callback which will be triggered when user press OK
*/ */
info:function(text, title, callback) { info:function(text, title, callback, modal) {
var content = '<p><span class="ui-icon ui-icon-info"></span>'+text+'</p>'; var content = '<p><span class="ui-icon ui-icon-info"></span>'+text+'</p>';
OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback); OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback, modal);
}, },
/** /**
* displays confirmation dialog * displays confirmation dialog
@ -50,9 +49,9 @@ OCdialogs = {
* @param title dialog title * @param title dialog title
* @param callback which will be triggered when user press YES or NO (true or false would be passed to callback respectively) * @param callback which will be triggered when user press YES or NO (true or false would be passed to callback respectively)
*/ */
confirm:function(text, title, callback) { confirm:function(text, title, callback, modal) {
var content = '<p><span class="ui-icon ui-icon-notice"></span>'+text+'</p>'; var content = '<p><span class="ui-icon ui-icon-notice"></span>'+text+'</p>';
OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.YES_NO_BUTTONS, callback); OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.YES_NO_BUTTONS, callback, modal);
}, },
/** /**
* prompt for user input * prompt for user input
@ -60,9 +59,9 @@ OCdialogs = {
* @param title dialog title * @param title dialog title
* @param callback which will be triggered when user press OK (input text will be passed to callback) * @param callback which will be triggered when user press OK (input text will be passed to callback)
*/ */
prompt:function(text, title, default_value, callback) { prompt:function(text, title, default_value, callback, modal) {
var content = '<p><span class="ui-icon ui-icon-pencil"></span>'+text+':<br/><input type="text" id="oc-dialog-prompt-input" value="'+default_value+'" style="width:90%"></p>'; var content = '<p><span class="ui-icon ui-icon-pencil"></span>'+text+':<br/><input type="text" id="oc-dialog-prompt-input" value="'+default_value+'" style="width:90%"></p>';
OCdialogs.message(content, title, OCdialogs.PROMPT_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback); OCdialogs.message(content, title, OCdialogs.PROMPT_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback, modal);
}, },
/** /**
* prompt user for input with custom form * prompt user for input with custom form
@ -71,7 +70,7 @@ OCdialogs = {
* @param title dialog title * @param title dialog title
* @param callback which will be triggered when user press OK (user answers will be passed to callback in following format: [{name:'return name', value: 'user value'},...]) * @param callback which will be triggered when user press OK (user answers will be passed to callback in following format: [{name:'return name', value: 'user value'},...])
*/ */
form:function(fields, title, callback) { form:function(fields, title, callback, modal) {
var content = '<table>'; var content = '<table>';
for (var a in fields) { for (var a in fields) {
content += '<tr><td>'+fields[a].text+'</td><td>'; content += '<tr><td>'+fields[a].text+'</td><td>';
@ -84,16 +83,63 @@ OCdialogs = {
} else content += '>'; } else content += '>';
} else if (type == 'text' || type == 'password' && fields[a].value) } else if (type == 'text' || type == 'password' && fields[a].value)
content += ' value="'+fields[a].value+'">'; content += ' value="'+fields[a].value+'">';
} else if (type == 'select') {
content += '<select name="'+fields[a].name+'"';
if (fields[a].value != undefined)
content += ' value="'+fields[a].value+'"';
content += '>';
for (var o in fields[a].options)
content += '<option value="'+fields[a].options[o].value+'">'+fields[a].options[o].text+'</option>';
content += '</select>';
} }
content += "</td></tr>" content += '</td></tr>';
} }
content += "</table>"; content += '</table>';
OCdialogs.message(content, title, OCdialogs.FORM_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback); OCdialogs.message(content, title, OCdialogs.FORM_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback, modal);
}, },
message:function(content, title, dialog_type, buttons, callback) { filepicker:function(title, callback, multiselect, mimetype_filter, modal) {
var c_name = 'oc-dialog-'+OCdialogs.dialogs_counter+'-content';
var c_id = '#'+c_name;
var d = '<div id="'+c_name+'" title="'+title+'"><select id="dirtree"><option value="0">'+OC.currentUser+'</option></select><div id="filelist"></div><div class="filepicker_loader"><img src="'+OC.filePath('gallery','img','loading.gif')+'"></div></div>';
if (!modal) modal = false;
if (!multiselect) multiselect = false;
$('body').append(d);
$(c_id + ' #dirtree').focus(function() { var t = $(this); t.data('oldval', t.val())})
.change({dcid: c_id}, OC.dialogs.handleTreeListSelect);
$(c_id).ready(function(){
$.getJSON(OC.webroot+'/files/ajax/rawlist.php', {mimetype: mimetype_filter} ,function(r){OC.dialogs.fillFilePicker(r, c_id, callback)});
}).data('multiselect', multiselect).data('mimetype',mimetype_filter);
// build buttons
var b = [
{text: t('dialogs', 'Choose'), click: function(){
if (callback != undefined) {
var p;
if ($(c_id).data('multiselect') == true) {
p = [];
$(c_id+' .filepicker_element_selected #filename').each(function(i, elem) {
p.push(($(c_id).data('path')?$(c_id).data('path'):'')+'/'+$(elem).text());
});
} else {
var p = $(c_id).data('path');
if (p == undefined) p = '';
p = p+'/'+$(c_id+' .filepicker_element_selected #filename').text()
}
callback(p);
$(c_id).dialog('close');
}
}
},
{text: t('dialogs', 'Cancel'), click: function(){$(c_id).dialog('close'); }}
];
$(c_id).dialog({width: 4*$(document).width()/9, height: 400, modal: modal, buttons: b});
OCdialogs.dialogs_counter++;
},
// guts, dont use, dont touch
message:function(content, title, dialog_type, buttons, callback, modal) {
var c_name = 'oc-dialog-'+OCdialogs.dialogs_counter+'-content'; var c_name = 'oc-dialog-'+OCdialogs.dialogs_counter+'-content';
var c_id = '#'+c_name; var c_id = '#'+c_name;
var d = '<div id="'+c_name+'" title="'+title+'">'+content+'</div>'; var d = '<div id="'+c_name+'" title="'+title+'">'+content+'</div>';
if (modal == undefined) modal = false;
$('body').append(d); $('body').append(d);
var b = []; var b = [];
switch (buttons) { switch (buttons) {
@ -107,7 +153,7 @@ OCdialogs = {
var f; var f;
switch(dialog_type) { switch(dialog_type) {
case OCdialogs.ALERT_DIALOG: case OCdialogs.ALERT_DIALOG:
f = function(){$(c_id).dialog('close'); }; f = function(){$(c_id).dialog('close'); callback();};
break; break;
case OCdialogs.PROMPT_DIALOG: case OCdialogs.PROMPT_DIALOG:
f = function(){OCdialogs.prompt_ok_handler(callback, c_id)}; f = function(){OCdialogs.prompt_ok_handler(callback, c_id)};
@ -120,7 +166,7 @@ OCdialogs = {
break; break;
} }
var possible_height = ($('tr', d).size()+1)*30; var possible_height = ($('tr', d).size()+1)*30;
$(c_id).dialog({width: 4*$(document).width()/9, height: possible_height + 120, modal: false, buttons: b}); $(c_id).dialog({width: 4*$(document).width()/9, height: possible_height + 120, modal: modal, buttons: b});
OCdialogs.dialogs_counter++; OCdialogs.dialogs_counter++;
}, },
// dialogs buttons types // dialogs buttons types
@ -144,7 +190,7 @@ OCdialogs = {
if (callback != undefined) { if (callback != undefined) {
var r = []; var r = [];
var c = 0; var c = 0;
$(c_id + ' input').each(function(i, elem) { $(c_id + ' input, '+c_id+' select').each(function(i, elem) {
r[c] = {name: $(elem).attr('name'), value: OCdialogs.determineValue(elem)}; r[c] = {name: $(elem).attr('name'), value: OCdialogs.determineValue(elem)};
c++; c++;
}); });
@ -153,5 +199,47 @@ OCdialogs = {
} else { } else {
$(c_id).dialog('close'); $(c_id).dialog('close');
} }
},
fillFilePicker:function(r, dialog_content_id) {
var entry_template = '<div onclick="javascript:OC.dialogs.handlePickerClick(this, \'*ENTRYNAME*\',\''+dialog_content_id+'\')" data="*ENTRYTYPE*"><img src="*MIMETYPEICON*" style="margin-right:1em;"><span id="filename">*NAME*</span><div style="float:right;margin-right:1em;">*LASTMODDATE*</div></div>';
var names = '';
for (var a in r.data) {
names += entry_template.replace('*LASTMODDATE*', OC.mtime2date(r.data[a].mtime)).replace('*NAME*', r.data[a].name).replace('*MIMETYPEICON*', r.data[a].mimetype_icon).replace('*ENTRYNAME*', r.data[a].name).replace('*ENTRYTYPE*', r.data[a].type);
}
$(dialog_content_id + ' #filelist').html(names);
$(dialog_content_id + ' .filepicker_loader').css('visibility', 'hidden');
},
handleTreeListSelect:function(event) {
var newval = parseInt($(this).val());
var oldval = parseInt($(this).data('oldval'));
while (newval != oldval && oldval > 0) {
$('option:last', this).remove();
$('option:last', this).attr('selected','selected');
oldval--;
}
var skip_first = true;
var path = '';
$(this).children().each(function(i, element) { if (skip_first) {skip_first = false; return; }path += '/'+$(element).text(); });
$(event.data.dcid).data('path', path);
$(event.data.dcid + ' .filepicker_loader').css('visibility', 'visible');
$.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: path, mimetype: $(event.data.dcid).data('mimetype')}, function(r){OC.dialogs.fillFilePicker(r, event.data.dcid)});
},
// this function is in early development state, please dont use it unlsess you know what you are doing
handlePickerClick:function(element, name, dcid) {
var p = $(dcid).data('path');
if (p == undefined) p = '';
p = p+'/'+name;
if ($(element).attr('data') == 'file'){
if ($(dcid).data('multiselect') != true)
$(dcid+' .filepicker_element_selected').removeClass('filepicker_element_selected');
$(element).toggleClass('filepicker_element_selected');
return;
}
$(dcid).data('path', p);
$(dcid + ' #dirtree option:last').removeAttr('selected');
var newval = parseInt($(dcid + ' #dirtree option:last').val())+1;
$(dcid + ' #dirtree').append('<option selected="selected" value="'+newval+'">'+name+'</option>');
$(dcid + ' .filepicker_loader').css('visibility', 'visible');
$.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: p, mimetype: $(dcid).data('mimetype')}, function(r){OC.dialogs.fillFilePicker(r, dcid)});
} }
}; };

View File

@ -19,7 +19,7 @@ OCCategories={
height: 350, minHeight:200, width: 250, minWidth: 200, height: 350, minHeight:200, width: 250, minWidth: 200,
buttons: { buttons: {
'Delete':function() { 'Delete':function() {
OCCategories.delete(); OCCategories.doDelete();
}, },
'Rescan':function() { 'Rescan':function() {
OCCategories.rescan(); OCCategories.rescan();
@ -53,7 +53,7 @@ OCCategories={
} }
}); });
}, },
delete:function(){ doDelete:function(){
var categories = $('#categorylist').find('input[type="checkbox"]').serialize(); var categories = $('#categorylist').find('input[type="checkbox"]').serialize();
categories += '&app=' + OCCategories.app; categories += '&app=' + OCCategories.app;
console.log('OCCategories.delete: ' + categories); console.log('OCCategories.delete: ' + categories);

View File

@ -12,7 +12,7 @@ require_once('../../lib/base.php');
// Someone lost their password: // Someone lost their password:
if (isset($_POST['user'])) { if (isset($_POST['user'])) {
if (OC_User::userExists($_POST['user'])) { if (OC_User::userExists($_POST['user'])) {
$token = sha1($_POST['user']+uniqId()); $token = sha1($_POST['user'].md5(uniqid(rand(), true)));
OC_Preferences::setValue($_POST['user'], 'owncloud', 'lostpassword', $token); OC_Preferences::setValue($_POST['user'], 'owncloud', 'lostpassword', $token);
$email = OC_Preferences::getValue($_POST['user'], 'settings', 'email', ''); $email = OC_Preferences::getValue($_POST['user'], 'settings', 'email', '');
if (!empty($email)) { if (!empty($email)) {

View File

@ -7,7 +7,7 @@
<?php endif; ?> <?php endif; ?>
<p class="infield"> <p class="infield">
<label for="user" class="infield"><?php echo $l->t( 'Username' ); ?></label> <label for="user" class="infield"><?php echo $l->t( 'Username' ); ?></label>
<input type="text" name="user" id="user" value="<?php echo !empty($_POST['user'])?$_POST['user'].'"':'" autofocus'; ?> autocomplete="off" required /> <input type="text" name="user" id="user" value="<?php echo !empty($_POST['user'])?htmlentities($_POST['user']).'"':'" autofocus'; ?> autocomplete="off" required />
</p> </p>
<p class="infield"> <p class="infield">
<label for="password" class="infield"><?php echo $l->t( 'Password' ); ?></label> <label for="password" class="infield"><?php echo $l->t( 'Password' ); ?></label>

View File

@ -67,8 +67,7 @@
<field> <field>
<name>path_hash</name> <name>path_hash</name>
<type>text</type> <type>text</type>
<default> <default></default>
</default>
<notnull>true</notnull> <notnull>true</notnull>
<length>32</length> <length>32</length>
</field> </field>

26
files/ajax/rawlist.php Normal file
View File

@ -0,0 +1,26 @@
<?php
// only need filesystem apps
$RUNTIME_APPTYPES=array('filesystem');
// Init owncloud
require_once('../../lib/base.php');
require_once('../../lib/template.php');
OC_JSON::checkLoggedIn();
// Load the files
$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : '';
$mimetype = isset($_GET['mimetype']) ? $_GET['mimetype'] : '';
// make filelist
$files = array();
foreach( OC_Files::getdirectorycontent( $dir, $mimetype ) as $i ){
$i["date"] = OC_Util::formatDate($i["mtime"] );
$i['mimetype_icon'] = $i['type'] == 'dir' ? mimetype_icon('dir'): mimetype_icon($i['mimetype']);
$files[] = $i;
}
OC_JSON::success(array('data' => $files));
?>

View File

@ -17,6 +17,7 @@ if($force or !OC_FileCache::inCache('')){
if(!$checkOnly){ if(!$checkOnly){
OC_DB::beginTransaction(); OC_DB::beginTransaction();
OC_FileCache::scan('',$eventSource); OC_FileCache::scan('',$eventSource);
OC_FileCache::clean();
OC_DB::commit(); OC_DB::commit();
$eventSource->send('success',true); $eventSource->send('success',true);
}else{ }else{

View File

@ -417,7 +417,7 @@ var folderDropOptions={
var dir=$('#dir').val(); var dir=$('#dir').val();
$.ajax({ $.ajax({
url: 'ajax/move.php', url: 'ajax/move.php',
data: "dir="+dir+"&file="+file+'&target='+dir+'/'+target, data: "dir="+encodeURIComponent(dir)+"&file="+encodeURIComponent(file)+'&target='+encodeURIComponent(dir)+'/'+encodeURIComponent(target),
complete: function(data){boolOperationFinished(data, function(){ complete: function(data){boolOperationFinished(data, function(){
var el = $('#fileList tr').filterAttr('data-file',file).find('td.filename'); var el = $('#fileList tr').filterAttr('data-file',file).find('td.filename');
el.draggable('destroy'); el.draggable('destroy');
@ -443,7 +443,7 @@ var crumbDropOptions={
} }
$.ajax({ $.ajax({
url: 'ajax/move.php', url: 'ajax/move.php',
data: "dir="+dir+"&file="+file+'&target='+target, data: "dir="+encodeURIComponent(dir)+"&file="+encodeURIComponent(file)+'&target='+encodeURIComponent(target),
complete: function(data){boolOperationFinished(data, function(){ complete: function(data){boolOperationFinished(data, function(){
FileList.remove(file); FileList.remove(file);
});} });}

View File

@ -63,12 +63,12 @@
</div> </div>
<div id="scanning-message"> <div id="scanning-message">
<h3> <h3>
<?php echo $l->t('Files are being scanned, please wait.');?> <span id='scan-count'></spann> <?php echo $l->t('Files are being scanned, please wait.');?> <span id='scan-count'></span>
</h3> </h3>
<p> <p>
<?php echo $l->t('Current scanning');?> <span id='scan-current'></spann> <?php echo $l->t('Current scanning');?> <span id='scan-current'></span>
</p> </p>
</div> </div>
<!-- config hints for javascript --> <!-- config hints for javascript -->
<input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php echo $_['allowZipDownload']; ?>" /> <input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php echo $_['allowZipDownload']; ?>" />

View File

@ -27,7 +27,7 @@
$RUNTIME_NOSETUPFS = true; $RUNTIME_NOSETUPFS = true;
// only need filesystem apps // only need filesystem apps
$RUNTIME_APPTYPES=array('filesystem'); $RUNTIME_APPTYPES=array('filesystem','authentication');
require_once('../lib/base.php'); require_once('../lib/base.php');

View File

@ -55,7 +55,7 @@ class OC_App{
// Our very own core apps are hardcoded // Our very own core apps are hardcoded
foreach( array('files', 'settings') as $app ){ foreach( array('files', 'settings') as $app ){
if(is_null($types) or self::isType($app,$types)){ if(is_null($types)){
require( $app.'/appinfo/app.php' ); require( $app.'/appinfo/app.php' );
} }
} }
@ -103,9 +103,9 @@ class OC_App{
*/ */
public static function getEnabledApps(){ public static function getEnabledApps(){
$apps=array(); $apps=array();
$query = OC_DB::prepare( 'SELECT appid FROM *PREFIX*appconfig WHERE configkey = "enabled" AND configvalue="yes"' ); $query = OC_DB::prepare( 'SELECT appid FROM *PREFIX*appconfig WHERE configkey = \'enabled\' AND configvalue=\'yes\'' );
$query->execute(); $result=$query->execute();
while($row=$query->fetchRow()){ while($row=$result->fetchRow()){
$apps[]=$row['appid']; $apps[]=$row['appid'];
} }
return $apps; return $apps;
@ -319,7 +319,7 @@ class OC_App{
$file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/info.xml'; $file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/info.xml';
} }
$data=array(); $data=array();
$content=file_get_contents($file); $content=@file_get_contents($file);
if(!$content){ if(!$content){
return; return;
} }
@ -452,7 +452,7 @@ class OC_App{
*/ */
public static function getAppVersions(){ public static function getAppVersions(){
$versions=array(); $versions=array();
$query = OC_DB::prepare( 'SELECT appid, configvalue FROM *PREFIX*appconfig WHERE configkey = "installed_version"' ); $query = OC_DB::prepare( 'SELECT appid, configvalue FROM *PREFIX*appconfig WHERE configkey = \'installed_version\'' );
$result = $query->execute(); $result = $query->execute();
while($row = $result->fetchRow()){ while($row = $result->fetchRow()){
$versions[$row['appid']]=$row['configvalue']; $versions[$row['appid']]=$row['configvalue'];

View File

@ -229,6 +229,39 @@ class OC{
} }
} }
public static function initTemplateEngine() {
// if the formfactor is not yet autodetected do the autodetection now. For possible forfactors check the detectFormfactor documentation
if(!isset($_SESSION['formfactor'])){
$_SESSION['formfactor']=OC::detectFormfactor();
}
// allow manual override via GET parameter
if(isset($_GET['formfactor'])){
$_SESSION['formfactor']=$_GET['formfactor'];
}
// Add the stuff we need always
OC_Util::addScript( "jquery-1.6.4.min" );
OC_Util::addScript( "jquery-ui-1.8.16.custom.min" );
OC_Util::addScript( "jquery-showpassword" );
OC_Util::addScript( "jquery.infieldlabel.min" );
OC_Util::addScript( "jquery-tipsy" );
OC_Util::addScript( "oc-dialogs" );
OC_Util::addScript( "js" );
OC_Util::addScript( "eventsource" );
OC_Util::addScript( "config" );
//OC_Util::addScript( "multiselect" );
OC_Util::addScript('search','result');
OC_Util::addStyle( "styles" );
OC_Util::addStyle( "multiselect" );
OC_Util::addStyle( "jquery-ui-1.8.16.custom" );
OC_Util::addStyle( "jquery-tipsy" );
}
public static function initSession() {
ini_set('session.cookie_httponly','1;');
session_start();
}
public static function init(){ public static function init(){
// register autoloader // register autoloader
spl_autoload_register(array('OC','autoload')); spl_autoload_register(array('OC','autoload'));
@ -244,6 +277,24 @@ class OC{
date_default_timezone_set('Europe/Berlin'); date_default_timezone_set('Europe/Berlin');
ini_set('arg_separator.output','&amp;'); ini_set('arg_separator.output','&amp;');
//try to configure php to enable big file uploads.
//this doesn´t work always depending on the webserver and php configuration.
//Let´s try to overwrite some defaults anyways
//try to set the maximum execution time to 60min
@set_time_limit(3600);
@ini_set('max_execution_time',3600);
@ini_set('max_input_time',3600);
//try to set the maximum filesize to 10G
@ini_set('upload_max_filesize','10G');
@ini_set('post_max_size','10G');
@ini_set('file_uploads','50');
//try to set the session lifetime to 60min
@ini_set('gc_maxlifetime','3600');
//set http auth headers for apache+php-cgi work around //set http auth headers for apache+php-cgi work around
if (isset($_SERVER['HTTP_AUTHORIZATION']) && preg_match('/Basic\s+(.*)$/i', $_SERVER['HTTP_AUTHORIZATION'], $matches)) if (isset($_SERVER['HTTP_AUTHORIZATION']) && preg_match('/Basic\s+(.*)$/i', $_SERVER['HTTP_AUTHORIZATION'], $matches))
{ {
@ -270,38 +321,11 @@ class OC{
self::checkInstalled(); self::checkInstalled();
self::checkSSL(); self::checkSSL();
self::initSession();
self::initTemplateEngine();
self::checkUpgrade(); self::checkUpgrade();
ini_set('session.cookie_httponly','1;');
session_start();
// if the formfactor is not yet autodetected do the autodetection now. For possible forfactors check the detectFormfactor documentation
if(!isset($_SESSION['formfactor'])){
$_SESSION['formfactor']=OC::detectFormfactor();
}
// allow manual override via GET parameter
if(isset($_GET['formfactor'])){
$_SESSION['formfactor']=$_GET['formfactor'];
}
// Add the stuff we need always
OC_Util::addScript( "jquery-1.6.4.min" );
OC_Util::addScript( "jquery-ui-1.8.16.custom.min" );
OC_Util::addScript( "jquery-showpassword" );
OC_Util::addScript( "jquery.infieldlabel.min" );
OC_Util::addScript( "jquery-tipsy" );
OC_Util::addScript( "oc-dialogs" );
OC_Util::addScript( "js" );
OC_Util::addScript( "eventsource" );
OC_Util::addScript( "config" );
//OC_Util::addScript( "multiselect" );
OC_Util::addScript('search','result');
OC_Util::addStyle( "styles" );
OC_Util::addStyle( "multiselect" );
OC_Util::addStyle( "jquery-ui-1.8.16.custom" );
OC_Util::addStyle( "jquery-tipsy" );
$errors=OC_Util::checkServer(); $errors=OC_Util::checkServer();
if(count($errors)>0) { if(count($errors)>0) {
OC_Template::printGuestPage('', 'error', array('errors' => $errors)); OC_Template::printGuestPage('', 'error', array('errors' => $errors));
@ -341,6 +365,9 @@ class OC{
OC_App::loadApps(); OC_App::loadApps();
} }
} }
// Check for blacklisted files
OC_Hook::connect('OC_Filesystem','write','OC_Filesystem','isBlacklisted');
//make sure temporary files are cleaned up //make sure temporary files are cleaned up
register_shutdown_function(array('OC_Helper','cleanTmp')); register_shutdown_function(array('OC_Helper','cleanTmp'));

View File

@ -482,6 +482,30 @@ class OC_DB {
} }
} }
/**
* @breif replaces the owncloud tables with a new set
* @param $file string path to the MDB2 xml db export file
*/
public static function replaceDB( $file ){
$apps = OC_App::getAllApps();
self::beginTransaction();
// Delete the old tables
self::removeDBStructure( OC::$SERVERROOT . '/db_structure.xml' );
foreach($apps as $app){
$path = OC::$SERVERROOT.'/apps/'.$app.'/appinfo/database.xml';
if(file_exists($path)){
self::removeDBStructure( $path );
}
}
// Create new tables
self::createDBFromStructure( $file );
self::commit();
}
/** /**
* Start a transaction * Start a transaction
*/ */
@ -586,3 +610,4 @@ class PDOStatementWrapper{
return $this->statement->fetchColumn($colnum); return $this->statement->fetchColumn($colnum);
} }
} }

View File

@ -32,6 +32,7 @@ class OC_EventSource{
private $fallBackId=0; private $fallBackId=0;
public function __construct(){ public function __construct(){
@ob_end_clean();
header('Cache-Control: no-cache'); header('Cache-Control: no-cache');
$this->fallback=isset($_GET['fallback']) and $_GET['fallback']=='true'; $this->fallback=isset($_GET['fallback']) and $_GET['fallback']=='true';
if($this->fallback){ if($this->fallback){
@ -58,7 +59,7 @@ class OC_EventSource{
$type=null; $type=null;
} }
if($this->fallback){ if($this->fallback){
$response='<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('.$this->fallBackId.',"'.$type.'","'.json_encode($data).'")</script>'.PHP_EOL; $response='<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('.$this->fallBackId.',"'.$type.'",'.json_encode($data).')</script>'.PHP_EOL;
echo $response; echo $response;
}else{ }else{
if($type){ if($type){

View File

@ -240,7 +240,7 @@ class OC_FileCache{
* - encrypted * - encrypted
* - versioned * - versioned
*/ */
public static function getFolderContent($path,$root=''){ public static function getFolderContent($path,$root='',$mimetype_filter=''){
if(self::isUpdated($path,$root)){ if(self::isUpdated($path,$root)){
self::updateFolder($path,$root); self::updateFolder($path,$root);
} }
@ -252,8 +252,8 @@ class OC_FileCache{
} }
$path=$root.$path; $path=$root.$path;
$parent=self::getFileId($path); $parent=self::getFileId($path);
$query=OC_DB::prepare('SELECT name,ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE parent=?'); $query=OC_DB::prepare('SELECT name,ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE parent=? AND (mimetype LIKE ? OR mimetype = ?)');
$result=$query->execute(array($parent))->fetchAll(); $result=$query->execute(array($parent, $mimetype_filter.'%', 'httpd/unix-directory'))->fetchAll();
if(is_array($result)){ if(is_array($result)){
return $result; return $result;
}else{ }else{
@ -469,6 +469,10 @@ class OC_FileCache{
* @param string root (optionak) * @param string root (optionak)
*/ */
public static function scan($path,$eventSource=false,&$count=0,$root=''){ public static function scan($path,$eventSource=false,&$count=0,$root=''){
if($eventSource){
$eventSource->send('scanning',array('file'=>$path,'count'=>$count));
}
$lastSend=$count;
if(!$root){ if(!$root){
$view=OC_Filesystem::getView(); $view=OC_Filesystem::getView();
}else{ }else{
@ -482,13 +486,14 @@ class OC_FileCache{
if($filename != '.' and $filename != '..'){ if($filename != '.' and $filename != '..'){
$file=$path.'/'.$filename; $file=$path.'/'.$filename;
if($view->is_dir($file.'/')){ if($view->is_dir($file.'/')){
if($eventSource){
$eventSource->send('scanning',array('file'=>$file,'count'=>$count));
}
self::scan($file,$eventSource,$count,$root); self::scan($file,$eventSource,$count,$root);
}else{ }else{
$totalSize+=self::scanFile($file,$root); $totalSize+=self::scanFile($file,$root);
$count++; $count++;
if($count>$lastSend+25 and $eventSource){
$lastSend=$count;
$eventSource->send('scanning',array('file'=>$path,'count'=>$count));
}
} }
} }
} }
@ -637,6 +642,14 @@ class OC_FileCache{
self::fileSystemWatcherWrite(array('path'=>$path),$root); self::fileSystemWatcherWrite(array('path'=>$path),$root);
} }
} }
/**
* clean old pre-path_hash entries
*/
public static function clean(){
$query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE LENGTH(path_hash)<30');
$query->execute();
}
} }
//watch for changes and try to keep the cache up to date //watch for changes and try to keep the cache up to date

View File

@ -32,11 +32,11 @@ class OC_Files {
* get the content of a directory * get the content of a directory
* @param dir $directory * @param dir $directory
*/ */
public static function getDirectoryContent($directory){ public static function getDirectoryContent($directory, $mimetype_filter = ''){
if(strpos($directory,OC::$CONFIG_DATADIRECTORY)===0){ if(strpos($directory,OC::$CONFIG_DATADIRECTORY)===0){
$directory=substr($directory,strlen(OC::$CONFIG_DATADIRECTORY)); $directory=substr($directory,strlen(OC::$CONFIG_DATADIRECTORY));
} }
$files=OC_FileCache::getFolderContent($directory); $files=OC_FileCache::getFolderContent($directory, '', $mimetype_filter);
foreach($files as &$file){ foreach($files as &$file){
$file['directory']=$directory; $file['directory']=$directory;
$file['type']=($file['mimetype']=='httpd/unix-directory')?'dir':'file'; $file['type']=($file['mimetype']=='httpd/unix-directory')?'dir':'file';

View File

@ -86,6 +86,10 @@ class OC_Filestorage_Local extends OC_Filestorage{
return $this->delTree($path); return $this->delTree($path);
} }
public function rename($path1,$path2){ public function rename($path1,$path2){
if (!$this->is_writable($path1)) {
OC_Log::write('core','unable to rename, file is not writable : '.$path1,OC_Log::ERROR);
return false;
}
if(! $this->file_exists($path1)){ if(! $this->file_exists($path1)){
OC_Log::write('core','unable to rename, file does not exists : '.$path1,OC_Log::ERROR); OC_Log::write('core','unable to rename, file does not exists : '.$path1,OC_Log::ERROR);
return false; return false;

View File

@ -298,6 +298,19 @@ class OC_Filesystem{
} }
return true; return true;
} }
/**
* checks if a file is blacklsited for storage in the filesystem
* @param array $data from hook
*/
static public function isBlacklisted($data){
$blacklist = array('.htaccess');
$filename = strtolower(basename($data['path']));
if(in_array($filename,$blacklist)){
$data['run'] = false;
}
}
/** /**
* following functions are equivilent to their php buildin equivilents for arguments/return values. * following functions are equivilent to their php buildin equivilents for arguments/return values.
*/ */

View File

@ -137,13 +137,16 @@ class OC_FilesystemView {
} }
public function readfile($path){ public function readfile($path){
$handle=$this->fopen($path,'r'); $handle=$this->fopen($path,'r');
$chunkSize = 1024*1024;// 1 MB chunks if ($handle) {
while (!feof($handle)) { $chunkSize = 1024*1024;// 1 MB chunks
echo fread($handle, $chunkSize); while (!feof($handle)) {
@ob_flush(); echo fread($handle, $chunkSize);
flush(); @ob_flush();
flush();
}
return $this->filesize($path);
} }
return $this->filesize($path); return false;
} }
public function is_readable($path){ public function is_readable($path){
return $this->basicOperation('is_readable',$path); return $this->basicOperation('is_readable',$path);
@ -189,7 +192,7 @@ class OC_FilesystemView {
return $this->basicOperation('unlink',$path,array('delete')); return $this->basicOperation('unlink',$path,array('delete'));
} }
public function rename($path1,$path2){ public function rename($path1,$path2){
if(OC_FileProxy::runPreProxies('rename',$path1,$path2) and $this->is_writable($path1) and OC_Filesystem::isValidPath($path2)){ if(OC_FileProxy::runPreProxies('rename',$path1,$path2) and OC_Filesystem::isValidPath($path2)){
$run=true; $run=true;
OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run)); OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run));
if($run){ if($run){

View File

@ -1,78 +1,39 @@
<?php <?php
/** /**
* ownCloud * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
* * This file is licensed under the Affero General Public License version 3 or
* @author Robin Appelman * later.
* @copyright 2012 Robin Appelman icewind1991@gmail.com * See the COPYING-README file.
*
* 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/>.
*
*/ */
/** /**
*logging utilities * logging utilities
* *
* Log is saved at data/owncloud.log (on default) * Log is saved by default at data/owncloud.log using OC_Log_Owncloud.
* Selecting other backend is done with a config option 'log_type'.
*/ */
class OC_Log{ class OC_Log {
const DEBUG=0; const DEBUG=0;
const INFO=1; const INFO=1;
const WARN=2; const WARN=2;
const ERROR=3; const ERROR=3;
const FATAL=4; const FATAL=4;
static protected $class = null;
/** /**
* write a message in the log * write a message in the log
* @param string $app * @param string $app
* @param string $message * @param string $message
* @param int level * @param int level
*/ */
public static function write($app,$message,$level){ public static function write($app, $message, $level) {
$minLevel=OC_Config::getValue( "loglevel", 2 ); if (!self::$class) {
if($level>=$minLevel){ self::$class = 'OC_Log_'.ucfirst(OC_Config::getValue('log_type', 'owncloud'));
$datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); call_user_func(array(self::$class, 'init'));
$logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' );
$entry=array('app'=>$app,'message'=>$message,'level'=>$level,'time'=>time());
$fh=fopen($logFile,'a');
fwrite($fh,json_encode($entry)."\n");
fclose($fh);
} }
} $log_class=self::$class;
$log_class::write($app, $message, $level);
/**
* get entries from the log in reverse chronological order
* @param int limit
* @param int offset
* @return array
*/
public static function getEntries($limit=50,$offset=0){
$datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' );
$logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' );
$entries=array();
if(!file_exists($logFile)){
return array();
}
$contents=file($logFile);
if(!$contents){//error while reading log
return array();
}
$end=max(count($contents)-$offset-1,0);
$start=max($end-$limit,0);
for($i=$end;$i>$start;$i--){
$entries[]=json_decode($contents[$i]);
}
return $entries;
} }
} }

79
lib/log/owncloud.php Normal file
View File

@ -0,0 +1,79 @@
<?php
/**
* ownCloud
*
* @author Robin Appelman
* @copyright 2012 Robin Appelman icewind1991@gmail.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/>.
*
*/
/**
* logging utilities
*
* Log is saved at data/owncloud.log (on default)
*/
class OC_Log_Owncloud {
static protected $logFile;
/**
* Init class data
*/
public static function init() {
$datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' );
self::$logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' );
}
/**
* write a message in the log
* @param string $app
* @param string $message
* @param int level
*/
public static function write($app, $message, $level) {
$minLevel=OC_Config::getValue( "loglevel", 2 );
if($level>=$minLevel){
$entry=array('app'=>$app, 'message'=>$message, 'level'=>$level,'time'=>time());
$fh=fopen(self::$logFile, 'a');
fwrite($fh, json_encode($entry)."\n");
fclose($fh);
}
}
/**
* get entries from the log in reverse chronological order
* @param int limit
* @param int offset
* @return array
*/
public static function getEntries($limit=50, $offset=0){
self::init();
$entries=array();
if(!file_exists(self::$logFile)) {
return array();
}
$contents=file(self::$logFile);
if(!$contents) {//error while reading log
return array();
}
$end=max(count($contents)-$offset-1, 0);
$start=max($end-$limit,0);
for($i=$end;$i>$start;$i--) {
$entries[]=json_decode($contents[$i]);
}
return $entries;
}
}

37
lib/log/syslog.php Normal file
View File

@ -0,0 +1,37 @@
<?php
/**
* Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
class OC_Log_Syslog {
static protected $levels = array(
OC_Log::DEBUG => LOG_DEBUG,
OC_Log::INFO => LOG_INFO,
OC_Log::WARN => LOG_WARNING,
OC_Log::ERROR => LOG_ERR,
OC_Log::FATAL => LOG_CRIT,
);
/**
* Init class data
*/
public static function init() {
openlog('ownCloud', LOG_PID | LOG_CONS, LOG_USER);
// Close at shutdown
register_shutdown_function('closelog');
}
/**
* write a message in the log
* @param string $app
* @param string $message
* @param int level
*/
public static function write($app, $message, $level) {
$syslog_level = self::$levels[$level];
syslog($syslog_level, '{'.$app.'} '.$message);
}
}

714
lib/migrate.php Normal file
View File

@ -0,0 +1,714 @@
<?php
/**
* ownCloud
*
* @author Tom Needham
* @copyright 2012 Tom Needham tom@owncloud.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/>.
*
*/
/**
* provides an interface to migrate users and whole ownclouds
*/
class OC_Migrate{
// Array of OC_Migration_Provider objects
static private $providers=array();
// User id of the user to import/export
static private $uid=false;
// Holds the ZipArchive object
static private $zip=false;
// Stores the type of export
static private $exporttype=false;
// Array of temp files to be deleted after zip creation
static private $tmpfiles=array();
// Holds the db object
static private $MDB2=false;
// Schema db object
static private $schema=false;
// Path to the sqlite db
static private $dbpath=false;
// Holds the path to the zip file
static private $zippath=false;
// Holds the OC_Migration_Content object
static private $content=false;
/**
* register a new migration provider
* @param OC_Migrate_Provider $provider
*/
public static function registerProvider($provider){
self::$providers[]=$provider;
}
/**
* @breif finds and loads the providers
*/
static private function findProviders(){
// Find the providers
$apps = OC_App::getAllApps();
foreach($apps as $app){
$path = OC::$SERVERROOT . '/apps/' . $app . '/appinfo/migrate.php';
if( file_exists( $path ) ){
include( $path );
}
}
}
/**
* @breif exports a user, or owncloud instance
* @param optional $uid string user id of user to export if export type is user, defaults to current
* @param ootional $type string type of export, defualts to user
* @param otional $path string path to zip output folder
* @return false on error, path to zip on success
*/
public static function export( $uid=null, $type='user', $path=null ){
$datadir = OC_Config::getValue( 'datadirectory' );
// Validate export type
$types = array( 'user', 'instance', 'system', 'userfiles' );
if( !in_array( $type, $types ) ){
OC_Log::write( 'migration', 'Invalid export type', OC_Log::ERROR );
return json_encode( array( array( 'success' => false ) ) );
}
self::$exporttype = $type;
// Userid?
if( self::$exporttype == 'user' ){
// Check user exists
if( !is_null($uid) ){
if( !OC_User_Database::userExists( $uid ) ){
OC_Log::write('migration', 'User: '.$uid.' is not in the database and so cannot be exported.', OC_Log::ERROR);
return json_encode( array( 'success' => false ) );
}
self::$uid = $uid;
} else {
self::$uid = OC_User::getUser();
}
}
// Calculate zipname
if( self::$exporttype == 'user' ){
$zipname = 'oc_export_' . self::$uid . '_' . date("y-m-d_H-i-s") . '.zip';
} else {
$zipname = 'oc_export_' . self::$exporttype . '_' . date("y-m-d_H-i-s") . '.zip';
}
// Calculate path
if( self::$exporttype == 'user' ){
self::$zippath = $datadir . '/' . self::$uid . '/' . $zipname;
} else {
if( !is_null( $path ) ){
// Validate custom path
if( !file_exists( $path ) || !is_writeable( $path ) ){
OC_Log::write( 'migration', 'Path supplied is invalid.', OC_Log::ERROR );
return json_encode( array( 'success' => false ) );
}
self::$zippath = $path . $zipname;
} else {
// Default path
self::$zippath = get_temp_dir() . '/' . $zipname;
}
}
// Create the zip object
if( !self::createZip() ){
return json_encode( array( 'success' => false ) );
}
// Do the export
self::findProviders();
$exportdata = array();
switch( self::$exporttype ){
case 'user':
// Connect to the db
self::$dbpath = $datadir . '/' . self::$uid . '/migration.db';
if( !self::connectDB() ){
return json_encode( array( 'success' => false ) );
}
self::$content = new OC_Migration_Content( self::$zip, self::$MDB2 );
// Export the app info
$exportdata = self::exportAppData();
// Add the data dir to the zip
self::$content->addDir( $datadir . '/' . self::$uid, true, '/' );
break;
case 'instance':
self::$content = new OC_Migration_Content( self::$zip );
// Creates a zip that is compatable with the import function
$dbfile = tempnam( "/tmp", "owncloud_export_data_" );
OC_DB::getDbStructure( $dbfile, 'MDB2_SCHEMA_DUMP_ALL');
// Now add in *dbname* and *dbprefix*
$dbexport = file_get_contents( $dbfile );
$dbnamestring = "<database>\n\n <name>" . OC_Config::getValue( "dbname", "owncloud" );
$dbtableprefixstring = "<table>\n\n <name>" . OC_Config::getValue( "dbtableprefix", "oc_" );
$dbexport = str_replace( $dbnamestring, "<database>\n\n <name>*dbname*", $dbexport );
$dbexport = str_replace( $dbtableprefixstring, "<table>\n\n <name>*dbprefix*", $dbexport );
// Add the export to the zip
self::$content->addFromString( $dbexport, "dbexport.xml" );
// Add user data
foreach(OC_User::getUsers() as $user){
self::$content->addDir( $datadir . '/' . $user . '/', true, "/userdata/" );
}
break;
case 'userfiles':
self::$content = new OC_Migration_Content( self::$zip );
// Creates a zip with all of the users files
foreach(OC_User::getUsers() as $user){
self::$content->addDir( $datadir . '/' . $user . '/', true, "/" );
}
break;
case 'system':
self::$content = new OC_Migration_Content( self::$zip );
// Creates a zip with the owncloud system files
self::$content->addDir( OC::$SERVERROOT . '/', false, '/');
foreach (array(".git", "3rdparty", "apps", "core", "files", "l10n", "lib", "ocs", "search", "settings", "tests") as $dir) {
self::$content->addDir( OC::$SERVERROOT . '/' . $dir, true, "/");
}
break;
}
if( !$info = self::getExportInfo( $exportdata ) ){
return json_encode( array( 'success' => false ) );
}
// Add the export info json to the export zip
self::$content->addFromString( $info, 'export_info.json' );
if( !self::$content->finish() ){
return json_encode( array( 'success' => false ) );
}
return json_encode( array( 'success' => true, 'data' => self::$zippath ) );
}
/**
* @breif imports a user, or owncloud instance
* @param $path string path to zip
* @param optional $type type of import (user or instance)
* @param optional $uid userid of new user
*/
public static function import( $path, $type='user', $uid=null ){
OC_Util::checkAdminUser();
$datadir = OC_Config::getValue( 'datadirectory' );
// Extract the zip
if( !$extractpath = self::extractZip( $path ) ){
return json_encode( array( 'success' => false ) );
}
// Get export_info.json
$scan = scandir( $extractpath );
// Check for export_info.json
if( !in_array( 'export_info.json', $scan ) ){
OC_Log::write( 'migration', 'Invalid import file, export_info.json note found', OC_Log::ERROR );
return json_encode( array( 'success' => false ) );
}
$json = json_decode( file_get_contents( $extractpath . 'export_info.json' ) );
if( $json->exporttype != $type ){
OC_Log::write( 'migration', 'Invalid import file', OC_Log::ERROR );
return json_encode( array( 'success' => false ) );
}
self::$exporttype = $type;
// Have we got a user if type is user
if( self::$exporttype == 'user' ){
if( !$uid ){
self::$uid = $json->exporteduser;
} else {
self::$uid = $uid;
}
}
// Handle export types
switch( self::$exporttype ){
case 'user':
// Check user availability
if( OC_User::userExists( self::$uid ) ){
OC_Log::write( 'migration', 'User already exists', OC_Log::ERROR );
return json_encode( array( 'success' => false ) );
}
$run = true;
OC_Hook::emit( "OC_User", "pre_createUser", array( "run" => &$run, "uid" => self::$uid, "password" => $json->hash ));
if( !$run ){
// Something stopped the user creation
OC_Log::write( 'migration', 'User creation failed', OC_Log::ERROR );
return json_encode( array( 'success' => false ) );
}
// Create the user
if( !self::createUser( self::$uid, $json->hash ) ){
return json_encode( array( 'success' => false ) );
}
// Emit the post_createUser hook (password is already hashed, will cause problems
OC_Hook::emit( "OC_User", "post_createUser", array( "uid" => self::$uid, "password" => $json->hash ));
// Make the new users data dir
$path = $datadir . '/' . self::$uid;
if( !mkdir( $path, 0755, true ) ){
OC_Log::write( 'migration', 'Failed to create users data dir: '.$path, OC_Log::ERROR );
return json_encode( array( 'success' => false ) );
}
// Copy data
if( !self::copy_r( $extractpath . $json->exporteduser, $datadir . '/' . self::$uid ) ){
return json_encode( array( 'success' => false ) );
}
// Import user app data
if( !$appsimported = self::importAppData( $extractpath . $json->exporteduser . '/migration.db', $json, self::$uid ) ){
return json_encode( array( 'success' => false ) );
}
// All done!
if( !self::unlink_r( $extractpath ) ){
OC_Log::write( 'migration', 'Failed to delete the extracted zip', OC_Log::ERROR );
}
return json_encode( array( 'success' => true, 'data' => $appsimported ) );
break;
case 'instance':
/*
* EXPERIMENTAL
// Check for new data dir and dbexport before doing anything
// TODO
// Delete current data folder.
OC_Log::write( 'migration', "Deleting current data dir", OC_Log::INFO );
if( !self::unlink_r( $datadir, false ) ){
OC_Log::write( 'migration', 'Failed to delete the current data dir', OC_Log::ERROR );
return json_encode( array( 'success' => false ) );
}
// Copy over data
if( !self::copy_r( $extractpath . 'userdata', $datadir ) ){
OC_Log::write( 'migration', 'Failed to copy over data directory', OC_Log::ERROR );
return json_encode( array( 'success' => false ) );
}
// Import the db
if( !OC_DB::replaceDB( $extractpath . 'dbexport.xml' ) ){
return json_encode( array( 'success' => false ) );
}
// Done
return json_encode( 'success' => true );
*/
break;
}
}
/**
* @breif recursively deletes a directory
* @param $dir string path of dir to delete
* $param optional $deleteRootToo bool delete the root directory
* @return bool
*/
private static function unlink_r( $dir, $deleteRootToo=true ){
if( !$dh = @opendir( $dir ) ){
return false;
}
while (false !== ($obj = readdir($dh))){
if($obj == '.' || $obj == '..') {
continue;
}
if (!@unlink($dir . '/' . $obj)){
self::unlink_r($dir.'/'.$obj, true);
}
}
closedir($dh);
if ( $deleteRootToo ) {
@rmdir($dir);
}
return true;
}
/**
* @breif copies recursively
* @param $path string path to source folder
* @param $dest string path to destination
* @return bool
*/
private static function copy_r( $path, $dest ){
if( is_dir($path) ){
@mkdir( $dest );
$objects = scandir( $path );
if( sizeof( $objects ) > 0 ){
foreach( $objects as $file ){
if( $file == "." || $file == ".." )
continue;
// go on
if( is_dir( $path . '/' . $file ) ){
self::copy_r( $path .'/' . $file, $dest . '/' . $file );
} else {
copy( $path . '/' . $file, $dest . '/' . $file );
}
}
}
return true;
}
elseif( is_file( $path ) ){
return copy( $path, $dest );
} else {
return false;
}
}
/**
* @breif tries to extract the import zip
* @param $path string path to the zip
* @return string path to extract location (with a trailing slash) or false on failure
*/
static private function extractZip( $path ){
self::$zip = new ZipArchive;
// Validate path
if( !file_exists( $path ) ){
OC_Log::write( 'migration', 'Zip not found', OC_Log::ERROR );
return false;
}
if ( self::$zip->open( $path ) != TRUE ) {
OC_Log::write( 'migration', "Failed to open zip file", OC_Log::ERROR );
return false;
}
$to = get_temp_dir() . '/oc_import_' . self::$exporttype . '_' . date("y-m-d_H-i-s") . '/';
if( !self::$zip->extractTo( $to ) ){
return false;
}
self::$zip->close();
return $to;
}
/**
* @brief connects to a MDB2 database scheme
* @returns bool
*/
static private function connectScheme(){
// We need a mdb2 database connection
self::$MDB2->loadModule( 'Manager' );
self::$MDB2->loadModule( 'Reverse' );
// Connect if this did not happen before
if( !self::$schema ){
require_once('MDB2/Schema.php');
self::$schema=MDB2_Schema::factory( self::$MDB2 );
}
return true;
}
/**
* @breif creates a migration.db in the users data dir with their app data in
* @return bool whether operation was successfull
*/
private static function exportAppData( ){
$success = true;
$return = array();
// Foreach provider
foreach( self::$providers as $provider ){
$success = true;
// Does this app use the database?
if( file_exists( OC::$SERVERROOT.'/apps/'.$provider->getID().'/appinfo/database.xml' ) ){
// Create some app tables
$tables = self::createAppTables( $provider->getID() );
if( is_array( $tables ) ){
// Save the table names
foreach($tables as $table){
$return['apps'][$provider->getID()]['tables'][] = $table;
}
} else {
// It failed to create the tables
$success = false;
}
}
// Run the export function?
if( $success ){
// Set the provider properties
$provider->setData( self::$uid, self::$content );
$return['apps'][$provider->getID()]['success'] = $provider->export();
} else {
$return['apps'][$provider->getID()]['success'] = false;
$return['apps'][$provider->getID()]['message'] = 'failed to create the app tables';
}
// Now add some app info the the return array
$appinfo = OC_App::getAppInfo( $provider->getID() );
$return['apps'][$provider->getID()]['version'] = $appinfo['version'];
}
return $return;
}
/**
* @breif generates json containing export info, and merges any data supplied
* @param optional $array array of data to include in the returned json
* @return bool
*/
static private function getExportInfo( $array=array() ){
$info = array(
'ocversion' => OC_Util::getVersion(),
'exporttime' => time(),
'exportedby' => OC_User::getUser(),
'exporttype' => self::$exporttype
);
// Add hash if user export
if( self::$exporttype == 'user' ){
$query = OC_DB::prepare( "SELECT password FROM *PREFIX*users WHERE uid LIKE ?" );
$result = $query->execute( array( self::$uid ) );
$row = $result->fetchRow();
$hash = $row ? $row['password'] : false;
if( !$hash ){
OC_Log::write( 'migration', 'Failed to get the users password hash', OC_log::ERROR);
return false;
}
$info['hash'] = $hash;
$info['exporteduser'] = self::$uid;
}
if( !is_array( $array ) ){
OC_Log::write( 'migration', 'Supplied $array was not an array in getExportInfo()', OC_Log::ERROR );
}
// Merge in other data
$info = array_merge( $info, (array)$array );
// Create json
$json = json_encode( $info );
return $json;
}
/**
* @breif connects to migration.db, or creates if not found
* @param $db optional path to migration.db, defaults to user data dir
* @return bool whether the operation was successful
*/
static private function connectDB( $path=null ){
// Has the dbpath been set?
self::$dbpath = !is_null( $path ) ? $path : self::$dbpath;
if( !self::$dbpath ){
OC_Log::write( 'migration', 'connectDB() was called without dbpath being set', OC_Log::ERROR );
return false;
}
// Already connected
if(!self::$MDB2){
require_once('MDB2.php');
$datadir = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" );
// DB type
if( class_exists( 'SQLite3' ) ){
$dbtype = 'sqlite3';
} else if( is_callable( 'sqlite_open' ) ){
$dbtype = 'sqlite';
} else {
OC_Log::write( 'migration', 'SQLite not found', OC_Log::ERROR );
return false;
}
// Prepare options array
$options = array(
'portability' => MDB2_PORTABILITY_ALL & (!MDB2_PORTABILITY_FIX_CASE),
'log_line_break' => '<br>',
'idxname_format' => '%s',
'debug' => true,
'quote_identifier' => true
);
$dsn = array(
'phptype' => $dbtype,
'database' => self::$dbpath,
'mode' => '0644'
);
// Try to establish connection
self::$MDB2 = MDB2::factory( $dsn, $options );
// Die if we could not connect
if( PEAR::isError( self::$MDB2 ) ){
die( self::$MDB2->getMessage() );
OC_Log::write( 'migration', 'Failed to create/connect to migration.db', OC_Log::FATAL );
OC_Log::write( 'migration', self::$MDB2->getUserInfo(), OC_Log::FATAL );
OC_Log::write( 'migration', self::$MDB2->getMessage(), OC_Log::FATAL );
return false;
}
// We always, really always want associative arrays
self::$MDB2->setFetchMode(MDB2_FETCHMODE_ASSOC);
}
return true;
}
/**
* @breif creates the tables in migration.db from an apps database.xml
* @param $appid string id of the app
* @return bool whether the operation was successful
*/
static private function createAppTables( $appid ){
if( !self::connectScheme() ){
return false;
}
// There is a database.xml file
$content = file_get_contents( OC::$SERVERROOT . '/apps/' . $appid . '/appinfo/database.xml' );
$file2 = 'static://db_scheme';
// TODO get the relative path to migration.db from the data dir
// For now just cheat
$path = pathinfo( self::$dbpath );
$content = str_replace( '*dbname*', self::$uid.'/migration', $content );
$content = str_replace( '*dbprefix*', '', $content );
$xml = new SimpleXMLElement($content);
foreach($xml->table as $table){
$tables[] = (string)$table->name;
}
file_put_contents( $file2, $content );
// Try to create tables
$definition = self::$schema->parseDatabaseDefinitionFile( $file2 );
unlink( $file2 );
// Die in case something went wrong
if( $definition instanceof MDB2_Schema_Error ){
OC_Log::write( 'migration', 'Failed to parse database.xml for: '.$appid, OC_Log::FATAL );
OC_Log::write( 'migration', $definition->getMessage().': '.$definition->getUserInfo(), OC_Log::FATAL );
return false;
}
$definition['overwrite'] = true;
$ret = self::$schema->createDatabase( $definition );
// Die in case something went wrong
if( $ret instanceof MDB2_Error ){
OC_Log::write( 'migration', 'Failed to create tables for: '.$appid, OC_Log::FATAL );
OC_Log::write( 'migration', $ret->getMessage().': '.$ret->getUserInfo(), OC_Log::FATAL );
return false;
}
return $tables;
}
/**
* @breif tries to create the zip
* @param $path string path to zip destination
* @return bool
*/
static private function createZip(){
self::$zip = new ZipArchive;
// Check if properties are set
if( !self::$zippath ){
OC_Log::write('migration', 'createZip() called but $zip and/or $zippath have not been set', OC_Log::ERROR);
return false;
}
if ( self::$zip->open( self::$zippath, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE ) !== TRUE ) {
OC_Log::write('migration', 'Failed to create the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR);
return false;
} else {
return true;
}
}
/**
* @breif returns an array of apps that support migration
* @return array
*/
static public function getApps(){
$allapps = OC_App::getAllApps();
foreach($allapps as $app){
$path = OC::$SERVERROOT . '/apps/' . $app . '/lib/migrate.php';
if( file_exists( $path ) ){
$supportsmigration[] = $app;
}
}
return $supportsmigration;
}
/**
* @breif imports a new user
* @param $db string path to migration.db
* @param $info object of migration info
* @param $uid optional uid to use
* @return array of apps with import statuses, or false on failure.
*/
public static function importAppData( $db, $info, $uid=null ){
// Check if the db exists
if( file_exists( $db ) ){
// Connect to the db
if(!self::connectDB( $db )){
OC_Log::write('migration','Failed to connect to migration.db',OC_Log::ERROR);
return false;
}
} else {
OC_Log::write('migration','Migration.db not found at: '.$db, OC_Log::FATAL );
return false;
}
// Find providers
self::findProviders();
// Generate importinfo array
$importinfo = array(
'olduid' => $info->exporteduser,
'newuid' => self::$uid
);
foreach( self::$providers as $provider){
// Is the app in the export?
$id = $provider->getID();
if( isset( $info->apps->$id ) ){
// Is the app installed
if( !OC_App::isEnabled( $id ) ){
OC_Log::write( 'migration', 'App: ' . $id . ' is not installed, can\'t import data.', OC_Log::INFO );
$appsstatus[$id] = 'notsupported';
} else {
// Did it succeed on export?
if( $info->apps->$id->success ){
// Give the provider the content object
if( !self::connectDB( $db ) ){
return false;
}
$content = new OC_Migration_Content( self::$zip, self::$MDB2 );
$provider->setData( self::$uid, $content, $info );
// Then do the import
if( !$appsstatus[$id] = $provider->import( $info->apps->$id, $importinfo ) ){
// Failed to import app
OC_Log::write( 'migration', 'Failed to import app data for user: ' . self::$uid . ' for app: ' . $id, OC_Log::ERROR );
}
} else {
// Add to failed list
$appsstatus[$id] = false;
}
}
}
}
return $appsstatus;
}
/*
* @breif creates a new user in the database
* @param $uid string user_id of the user to be created
* @param $hash string hash of the user to be created
* @return bool result of user creation
*/
public static function createUser( $uid, $hash ){
// Check if userid exists
if(OC_User::userExists( $uid )){
return false;
}
// Create the user
$query = OC_DB::prepare( "INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )" );
$result = $query->execute( array( $uid, $hash));
if( !$result ){
OC_Log::write('migration', 'Failed to create the new user "'.$uid."");
}
return $result ? true : false;
}
}

252
lib/migration/content.php Normal file
View File

@ -0,0 +1,252 @@
<?php
/**
* ownCloud
*
* @author Tom Needham
* @copyright 2012 Tom Needham tom@owncloud.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/>.
*
*/
/**
* provides methods to add and access data from the migration
*/
class OC_Migration_Content{
private $zip=false;
// Holds the MDB2 object
private $db=null;
// Holds an array of tmpfiles to delete after zip creation
private $tmpfiles=false;
/**
* @breif sets up the
* @param $zip ZipArchive object
* @param optional $db a MDB2 database object (required for exporttype user)
* @return bool
*/
public function __construct( $zip, $db=null ){
$this->zip = $zip;
$this->db = $db;
if( !is_null( $db ) ){
// Get db path
$db = $this->db->getDatabase();
$this->tmpfiles[] = $db;
}
}
// @breif prepares the db
// @param $query the sql query to prepare
public function prepare( $query ){
// Optimize the query
$query = $this->processQuery( $query );
// Optimize the query
$query = $this->db->prepare( $query );
// Die if we have an error (error means: bad query, not 0 results!)
if( PEAR::isError( $query ) ) {
$entry = 'DB Error: "'.$result->getMessage().'"<br />';
$entry .= 'Offending command was: '.$query.'<br />';
OC_Log::write( 'migration', $entry, OC_Log::FATAL );
return false;
} else {
return $query;
}
}
/**
* @breif processes the db query
* @param $query the query to process
* @return string of processed query
*/
private function processQuery( $query ){
$query = str_replace( '`', '\'', $query );
$query = str_replace( 'NOW()', 'datetime(\'now\')', $query );
$query = str_replace( 'now()', 'datetime(\'now\')', $query );
// remove table prefixes
$query = str_replace( '*PREFIX*', '', $query );
return $query;
}
/**
* @brief copys rows to migration.db from the main database
* @param $options array of options.
* @return bool
*/
public function copyRows( $options ){
if( !array_key_exists( 'table', $options ) ){
return false;
}
$return = array();
// Need to include 'where' in the query?
if( array_key_exists( 'matchval', $options ) && array_key_exists( 'matchcol', $options ) ){
// If only one matchval, create an array
if(!is_array($options['matchval'])){
$options['matchval'] = array( $options['matchval'] );
}
foreach( $options['matchval'] as $matchval ){
// Run the query for this match value (where x = y value)
$sql = "SELECT * FROM *PREFIX*" . $options['table'] . " WHERE " . $options['matchcol'] . " LIKE ?";
$query = OC_DB::prepare( $sql );
$results = $query->execute( array( $matchval ) );
$newreturns = $this->insertData( $results, $options );
$return = array_merge( $return, $newreturns );
}
} else {
// Just get everything
$sql = "SELECT * FROM *PREFIX*" . $options['table'];
$query = OC_DB::prepare( $sql );
$results = $query->execute();
$return = $this->insertData( $results, $options );
}
return $return;
}
/**
* @breif saves a sql data set into migration.db
* @param $data a sql data set returned from self::prepare()->query()
* @param $options array of copyRows options
* @return void
*/
private function insertData( $data, $options ){
$return = array();
// Foreach row of data to insert
while( $row = $data->fetchRow() ){
// Now save all this to the migration.db
foreach($row as $field=>$value){
$fields[] = $field;
$values[] = $value;
}
// Generate some sql
$sql = "INSERT INTO `" . $options['table'] . '` ( `';
$fieldssql = implode( '`, `', $fields );
$sql .= $fieldssql . "` ) VALUES( ";
$valuessql = substr( str_repeat( '?, ', count( $fields ) ),0,-2 );
$sql .= $valuessql . " )";
// Make the query
$query = $this->prepare( $sql );
if( !$query ){
OC_Log::write( 'migration', 'Invalid sql produced: '.$sql, OC_Log::FATAL );
return false;
exit();
} else {
$query->execute( $values );
// Do we need to return some values?
if( array_key_exists( 'idcol', $options ) ){
// Yes we do
$return[] = $row[$options['idcol']];
} else {
// Take a guess and return the first field :)
$return[] = reset($row);
}
}
$fields = '';
$values = '';
}
return $return;
}
/**
* @breif adds a directory to the zip object
* @param $dir string path of the directory to add
* @param $recursive bool
* @param $internaldir string path of folder to add dir to in zip
* @return bool
*/
public function addDir( $dir, $recursive=true, $internaldir='' ) {
$dirname = basename($dir);
$this->zip->addEmptyDir($internaldir . $dirname);
$internaldir.=$dirname.='/';
if( !file_exists( $dir ) ){
return false;
}
if ($dirhandle = opendir($dir)) {
while (false !== ( $file = readdir($dirhandle))) {
if (( $file != '.' ) && ( $file != '..' )) {
if (is_dir($dir . '/' . $file) && $recursive) {
$this->addDir($dir . '/' . $file, $recursive, $internaldir);
} elseif (is_file($dir . '/' . $file)) {
$this->zip->addFile($dir . '/' . $file, $internaldir . $file);
}
}
}
closedir($dirhandle);
} else {
OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR);
return false;
}
return true;
}
/**
* @breif adds a file to the zip from a given string
* @param $data string of data to add
* @param $path the relative path inside of the zip to save the file to
* @return bool
*/
public function addFromString( $data, $path ){
// Create a temp file
$file = tempnam( get_temp_dir(). '/', 'oc_export_tmp_' );
$this->tmpfiles[] = $file;
if( !file_put_contents( $file, $data ) ){
OC_Log::write( 'migation', 'Failed to save data to a temporary file', OC_Log::ERROR );
return false;
}
// Add file to the zip
$this->zip->addFile( $file, $path );
return true;
}
/**
* @breif closes the zip, removes temp files
* @return bool
*/
public function finish(){
if( !$this->zip->close() ){
OC_Log::write( 'migration', 'Failed to write the zip file with error: '.$this->zip->getStatusString(), OC_Log::ERROR );
return false;
}
$this->cleanup();
return true;
}
/**
* @breif cleans up after the zip
*/
private function cleanup(){
// Delete tmp files
foreach($this->tmpfiles as $i){
unlink( $i );
}
}
}

View File

@ -0,0 +1,52 @@
<?php
/**
* provides search functionalty
*/
abstract class OC_Migration_Provider{
protected $id=false;
protected $content=false;
protected $uid=false;
protected $olduid=false;
protected $appinfo=false;
public function __construct( $appid ){
// Set the id
$this->id = $appid;
OC_Migrate::registerProvider( $this );
}
/**
* @breif exports data for apps
* @return array appdata to be exported
*/
abstract function export( );
/**
* @breif imports data for the app
* @return void
*/
abstract function import( );
/**
* @breif sets the OC_Migration_Content object to $this->content
* @param $content a OC_Migration_Content object
*/
public function setData( $uid, $content, $info=null ){
$this->content = $content;
$this->uid = $uid;
$id = $this->id;
if( !is_null( $info ) ){
$this->olduid = $info->exporteduser;
$this->appinfo = $info->apps->$id;
}
}
/**
* @breif returns the appid of the provider
* @return string
*/
public function getID(){
return $this->id;
}
}

View File

@ -16,5 +16,6 @@ return array(
'xls'=>'application/msexcel', 'xls'=>'application/msexcel',
'xlsx'=>'application/msexcel', 'xlsx'=>'application/msexcel',
'ppt'=>'application/mspowerpoint', 'ppt'=>'application/mspowerpoint',
'pptx'=>'application/mspowerpoint' 'pptx'=>'application/mspowerpoint',
'sgf' => 'application/sgf'
); );

View File

@ -36,6 +36,7 @@ class OC_Updater{
$version['installed']=OC_Config::getValue('installedat'); $version['installed']=OC_Config::getValue('installedat');
$version['updated']=OC_Appconfig::getValue('core', 'lastupdatedat', OC_Config::getValue( 'lastupdatedat')); $version['updated']=OC_Appconfig::getValue('core', 'lastupdatedat', OC_Config::getValue( 'lastupdatedat'));
$version['updatechannel']='stable'; $version['updatechannel']='stable';
$version['edition']=OC_Util::getEditionString();
$versionstring=implode('x',$version); $versionstring=implode('x',$version);
//fetch xml data from updater //fetch xml data from updater

View File

@ -77,6 +77,14 @@ class OC_Util {
return '3'; return '3';
} }
/**
* get the current installed edition of ownCloud. There is the community edition that just returns an empty string and the enterprise edition that returns "Enterprise".
* @return string
*/
public static function getEditionString(){
return '';
}
/** /**
* add a javascript file * add a javascript file
* *

View File

@ -13,5 +13,5 @@ OC_JSON::checkAdminUser();
$count=(isset($_GET['count']))?$_GET['count']:50; $count=(isset($_GET['count']))?$_GET['count']:50;
$offset=(isset($_GET['offset']))?$_GET['offset']:0; $offset=(isset($_GET['offset']))?$_GET['offset']:0;
$entries=OC_Log::getEntries($count,$offset); $entries=OC_Log_Owncloud::getEntries($count,$offset);
OC_JSON::success(array("data" => $entries)); OC_JSON::success(array("data" => $entries));

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