Merge branch 'master' into calendar_sharing
This commit is contained in:
commit
ac2d14101c
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
5
README
|
@ -3,10 +3,11 @@ A personal cloud which runs on your own server.
|
|||
|
||||
http://ownCloud.org
|
||||
|
||||
Installation instructions: http://owncloud.org/support/setup-and-installation/
|
||||
Source code: http://gitorious.org/owncloud
|
||||
Installation instructions: http://owncloud.org/support
|
||||
|
||||
Source code: http://gitorious.org/owncloud
|
||||
Mailing list: http://mail.kde.org/mailman/listinfo/owncloud
|
||||
IRC channel: http://webchat.freenode.net/?channels=owncloud
|
||||
Diaspora: https://joindiaspora.com/u/owncloud
|
||||
Identi.ca: http://identi.ca/owncloud
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -1,10 +1,10 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - user_ldap
|
||||
* ownCloud - admin_migrate
|
||||
*
|
||||
* @author Dominik Schmidt
|
||||
* @copyright 2011 Dominik Schmidt dev@dominik-schmidt.de
|
||||
* @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
|
||||
|
@ -22,12 +22,12 @@
|
|||
*/
|
||||
|
||||
|
||||
OC_APP::registerAdmin('admin_export','settings');
|
||||
OC_APP::registerAdmin('admin_migrate','settings');
|
||||
|
||||
// add settings page to navigation
|
||||
$entry = array(
|
||||
'id' => "admin_export_settings",
|
||||
'id' => "admin_migrate_settings",
|
||||
'order'=>1,
|
||||
'href' => OC_Helper::linkTo( "admin_export", "settings.php" ),
|
||||
'href' => OC_Helper::linkTo( "admin_migrate", "settings.php" ),
|
||||
'name' => 'Export'
|
||||
);
|
|
@ -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>
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
?>
|
|
@ -17,4 +17,5 @@ OC_App::addNavigationEntry( array( 'id' => 'bookmarks_index', 'order' => 70, 'hr
|
|||
|
||||
OC_App::registerPersonal('bookmarks', 'settings');
|
||||
OC_Util::addScript('bookmarks','bookmarksearch');
|
||||
|
||||
OC_Search::registerProvider('OC_Search_Provider_Bookmarks');
|
||||
|
|
|
@ -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' );
|
|
@ -39,13 +39,14 @@ foreach ($_POST as $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
|
||||
|
||||
$fn = trim($_POST['fn']);
|
||||
$n = trim($_POST['n']);
|
||||
debug('N: '.$n);
|
||||
debug('FN: '.$fn);
|
||||
|
||||
$vcard = new OC_VObject('VCARD');
|
||||
$vcard->setUID();
|
||||
|
|
|
@ -96,40 +96,40 @@ switch($element) {
|
|||
//$value = getOtherValue();
|
||||
}
|
||||
break;
|
||||
case 'CATEGORIES':
|
||||
/* multi autocomplete triggers an save with empty value */
|
||||
//case 'CATEGORIES':
|
||||
/* multi autocomplete triggers an save with empty value
|
||||
if (!$value) {
|
||||
$value = $vcard->getAsString('CATEGORIES');
|
||||
}
|
||||
break;
|
||||
break;*/
|
||||
case 'EMAIL':
|
||||
$value = strtolower($value);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!$value) {
|
||||
bailOut(OC_Contacts_App::$l10n->t('Cannot save empty value.'));
|
||||
}
|
||||
|
||||
/* setting value */
|
||||
switch($element) {
|
||||
case 'BDAY':
|
||||
case 'FN':
|
||||
case 'N':
|
||||
case 'ORG':
|
||||
case 'NOTE':
|
||||
case 'NICKNAME':
|
||||
case 'CATEGORIES':
|
||||
debug('Setting string:'.$name.' '.$value);
|
||||
$vcard->setString($name, $value);
|
||||
break;
|
||||
case 'EMAIL':
|
||||
case 'TEL':
|
||||
case 'ADR': // should I delete the property if empty or throw an error?
|
||||
debug('Setting element: (EMAIL/TEL/ADR)'.$element);
|
||||
if(!$value) {
|
||||
unset($vcard->children[$line]); // Should never happen...
|
||||
} else {
|
||||
unset($vcard->children[$line]);
|
||||
$checksum = '';
|
||||
} else {
|
||||
/* setting value */
|
||||
switch($element) {
|
||||
case 'BDAY':
|
||||
case 'FN':
|
||||
case 'N':
|
||||
case 'ORG':
|
||||
case 'NOTE':
|
||||
case 'NICKNAME':
|
||||
debug('Setting string:'.$name.' '.$value);
|
||||
$vcard->setString($name, $value);
|
||||
break;
|
||||
case 'CATEGORIES':
|
||||
debug('Setting string:'.$name.' '.$value);
|
||||
$vcard->children[$line]->setValue($value);
|
||||
break;
|
||||
case 'EMAIL':
|
||||
case 'TEL':
|
||||
case 'ADR': // should I delete the property if empty or throw an error?
|
||||
debug('Setting element: (EMAIL/TEL/ADR)'.$element);
|
||||
$vcard->children[$line]->setValue($value);
|
||||
$vcard->children[$line]->parameters = array();
|
||||
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
|
||||
$checksum = md5($vcard->children[$line]->serialize());
|
||||
debug('New checksum: '.$checksum);
|
||||
//debug('New checksum: '.$checksum);
|
||||
|
||||
if(!OC_Contacts_VCard::edit($id,$vcard)) {
|
||||
bailOut(OC_Contacts_App::$l10n->t('Error updating contact property.'));
|
||||
|
|
|
@ -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' );
|
|
@ -13,22 +13,26 @@
|
|||
#contacts_propertymenu li a { padding: 3px; display: block }
|
||||
#contacts_propertymenu li:hover { background-color: #1d2d44; }
|
||||
#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;}
|
||||
#card { /*max-width: 70em; border: thin solid lightgray; display: block;*/ }
|
||||
#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 { 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 #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; }
|
||||
#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"]: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; }
|
||||
input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid { background-color: #ffc0c0 !important; }
|
||||
#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, 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, textarea:invalid { color: #bbb !important; }
|
||||
textarea { min-height: 4em; }
|
||||
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; }
|
||||
#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; }
|
||||
|
||||
.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; }
|
||||
.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; }
|
||||
|
@ -43,18 +47,18 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; }
|
|||
#edit_address_dialog { /*width: 30em;*/ }
|
||||
#edit_address_dialog > input { width: 15em; }
|
||||
#edit_photo_dialog_img { display: block; width: 150; height: 200; border: thin solid black; }
|
||||
#fn { float: left; }
|
||||
/**
|
||||
* Create classes form, floateven and floatodd which flows left and right respectively.
|
||||
*/
|
||||
.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; }
|
||||
#fn { float: left !important; width: 18em !important; }
|
||||
#name { /*position: absolute; top: 0px; left: 0px;*/ min-width: 25em; height: 2em; clear: right; display: block; }
|
||||
#identityprops { /*position: absolute; top: 2.5em; left: 0px;*/ }
|
||||
/*#contact_photo { max-width: 250px; }*/
|
||||
#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; }
|
||||
#cropbox { margin: auto; }
|
||||
|
||||
#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 { 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: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 */
|
||||
#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; }
|
||||
|
@ -72,8 +76,10 @@ dl.addresscard dd > ul { margin: 0.3em; padding: 0.3em; }
|
|||
|
||||
#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; }
|
||||
.big { font-weight:bold; font-size:1.2em; }
|
||||
.huge { font-weight:bold; font-size:1.5em; }
|
||||
.propertycontainer dd { float: left; width: 25em; }
|
||||
.propertylist { clear: none; max-width: 28em; }
|
||||
.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 |
|
@ -34,7 +34,26 @@ if(!is_null($id)) {
|
|||
}
|
||||
$property_types = OC_Contacts_App::getAddPropertyOptions();
|
||||
$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'));
|
||||
$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('uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize));
|
||||
$tmpl->assign('property_types',$property_types);
|
||||
$tmpl->assign('categories',OC_Contacts_App::getCategories());
|
||||
$tmpl->assign('phone_types',$phone_types);
|
||||
$tmpl->assign('categories',$categories);
|
||||
$tmpl->assign('addressbooks', $addressbooks);
|
||||
|
|
|
@ -65,7 +65,7 @@ Contacts={
|
|||
propertyTypeFor:function(obj) {
|
||||
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);
|
||||
console.log('showHideContactInfo: ' + show);
|
||||
if(show) {
|
||||
|
@ -73,8 +73,8 @@ Contacts={
|
|||
} else {
|
||||
$('#contact_communication').hide();
|
||||
}
|
||||
},
|
||||
checkListFor:function(obj) {
|
||||
},*/
|
||||
/*checkListFor:function(obj) {
|
||||
var type = $(obj).parents('.propertycontainer').first().data('element');
|
||||
console.log('checkListFor: ' + type);
|
||||
switch (type) {
|
||||
|
@ -101,7 +101,7 @@ Contacts={
|
|||
case 'BDAY':
|
||||
break;
|
||||
}
|
||||
},
|
||||
},*/
|
||||
loading:function(obj, state) {
|
||||
if(state) {
|
||||
$(obj).addClass('loading');
|
||||
|
@ -116,7 +116,7 @@ Contacts={
|
|||
},
|
||||
loadListHandlers:function() {
|
||||
//$('.add,.delete').hide();
|
||||
$('.globe,.mail,.delete,.edit').tipsy();
|
||||
$('.globe,.mail,.delete,.edit,.tip').tipsy();
|
||||
$('.addresscard,.propertylist li,.propertycontainer').hover(
|
||||
function () {
|
||||
$(this).find('.globe,.mail,.delete,.edit').fadeIn(100);
|
||||
|
@ -137,18 +137,14 @@ Contacts={
|
|||
$(this).find('.add').fadeOut(500);
|
||||
}
|
||||
);*/
|
||||
$('.button,.action').tipsy();
|
||||
$('#contacts_deletecard').tipsy({gravity: 'ne'});
|
||||
$('#contacts_downloadcard').tipsy({gravity: 'ne'});
|
||||
//$('#fn').jec();
|
||||
$('#fn_select').combobox({
|
||||
'id': 'fn',
|
||||
'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')});
|
||||
//$('.jecEditableOption').attr('title', t('contacts','Custom'));
|
||||
$('#fn').tipsy();
|
||||
$('#contacts_details_photo_wrapper').tipsy();
|
||||
$('#bday').datepicker({
|
||||
dateFormat : 'dd-mm-yy'
|
||||
});
|
||||
|
@ -175,10 +171,6 @@ Contacts={
|
|||
// Contacts.UI.Card.editAddress();
|
||||
// return false;
|
||||
// });
|
||||
$('#n').click(function(){
|
||||
Contacts.UI.Card.editName();
|
||||
//return false;
|
||||
});
|
||||
$('#edit_name').click(function(){
|
||||
Contacts.UI.Card.editName();
|
||||
return false;
|
||||
|
@ -200,6 +192,9 @@ Contacts={
|
|||
}
|
||||
] );
|
||||
$('#categories').multiple_autocomplete({source: categories});
|
||||
$('.button,.action,.tip').tipsy();
|
||||
$('#contacts_deletecard').tipsy({gravity: 'ne'});
|
||||
$('#contacts_downloadcard').tipsy({gravity: 'ne'});
|
||||
Contacts.UI.loadListHandlers();
|
||||
},
|
||||
Card:{
|
||||
|
@ -259,16 +254,28 @@ Contacts={
|
|||
});
|
||||
}
|
||||
},
|
||||
export:function() {
|
||||
doExport:function() {
|
||||
document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + this.id;
|
||||
//$.get(OC.linkTo('contacts', 'export.php'),{'contactid':this.id},function(jsondata){
|
||||
//});
|
||||
},
|
||||
import:function(){
|
||||
doImport:function(){
|
||||
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);
|
||||
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 },
|
||||
function(jsondata) {
|
||||
if (jsondata.status == 'success'){
|
||||
|
@ -291,7 +298,15 @@ Contacts={
|
|||
if(!added) {
|
||||
$('#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{
|
||||
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
|
||||
|
@ -308,7 +323,7 @@ Contacts={
|
|||
}
|
||||
});
|
||||
},
|
||||
delete:function() {
|
||||
doDelete:function() {
|
||||
$('#contacts_deletecard').tipsy('hide');
|
||||
OC.dialogs.confirm(t('contacts', 'Are you sure you want to delete this contact?'), t('contacts', 'Warning'), function(answer) {
|
||||
if(answer == true) {
|
||||
|
@ -356,7 +371,7 @@ Contacts={
|
|||
return false;
|
||||
},
|
||||
loadContact:function(jsondata){
|
||||
$('#contact_communication').hide();
|
||||
//$('#contact_communication').hide();
|
||||
this.data = jsondata;
|
||||
this.id = this.data.id;
|
||||
$('#rightcontent').data('id',this.id);
|
||||
|
@ -368,7 +383,6 @@ Contacts={
|
|||
this.loadPhones();
|
||||
this.loadAddresses();
|
||||
this.loadSingleProperties();
|
||||
// TODO: load NOTE ;-)
|
||||
if(this.data.NOTE) {
|
||||
$('#note').data('checksum', this.data.NOTE[0]['checksum']);
|
||||
$('#note').find('textarea').val(this.data.NOTE[0]['value']);
|
||||
|
@ -376,7 +390,7 @@ Contacts={
|
|||
} else {
|
||||
$('#note').data('checksum', '');
|
||||
$('#note').find('textarea').val('');
|
||||
$('#note').hide();
|
||||
//$('#note').hide();
|
||||
}
|
||||
},
|
||||
loadSingleProperties:function() {
|
||||
|
@ -521,17 +535,18 @@ Contacts={
|
|||
},*/
|
||||
editNew:function(){ // add a new contact
|
||||
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'){
|
||||
id = '';
|
||||
$('#rightcontent').data('id','');
|
||||
$('#rightcontent').html(jsondata.data.page);
|
||||
Contacts.UI.Card.editName();
|
||||
//Contacts.UI.Card.editName();
|
||||
} else {
|
||||
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
|
||||
//alert(jsondata.data.message);
|
||||
}
|
||||
});
|
||||
});*/
|
||||
},
|
||||
savePropertyInternal:function(name, fields, oldchecksum, checksum){
|
||||
// TODO: Add functionality for new fields.
|
||||
|
@ -627,8 +642,8 @@ Contacts={
|
|||
},'json');
|
||||
}
|
||||
},
|
||||
addProperty:function(obj){
|
||||
var type = $(obj).data('type');
|
||||
addProperty:function(type){
|
||||
//var type = $(obj).data('type');
|
||||
console.log('addProperty:' + type);
|
||||
switch (type) {
|
||||
case 'PHOTO':
|
||||
|
@ -647,21 +662,21 @@ Contacts={
|
|||
$('#emails').show();
|
||||
}
|
||||
Contacts.UI.Card.addMail();
|
||||
Contacts.UI.showHideContactInfo();
|
||||
//Contacts.UI.showHideContactInfo();
|
||||
break;
|
||||
case 'TEL':
|
||||
if($('#phonelist>li').length == 1) {
|
||||
$('#phones').show();
|
||||
}
|
||||
Contacts.UI.Card.addPhone();
|
||||
Contacts.UI.showHideContactInfo();
|
||||
//Contacts.UI.showHideContactInfo();
|
||||
break;
|
||||
case 'ADR':
|
||||
if($('#addressdisplay>dl').length == 1) {
|
||||
$('#addresses').show();
|
||||
}
|
||||
Contacts.UI.Card.editAddress('new', true);
|
||||
Contacts.UI.showHideContactInfo();
|
||||
//Contacts.UI.showHideContactInfo();
|
||||
break;
|
||||
case 'NICKNAME':
|
||||
case 'ORG':
|
||||
|
@ -682,8 +697,8 @@ Contacts={
|
|||
if(jsondata.status == 'success'){
|
||||
if(type == 'list') {
|
||||
Contacts.UI.propertyContainerFor(obj).remove();
|
||||
Contacts.UI.showHideContactInfo();
|
||||
Contacts.UI.checkListFor(obj);
|
||||
//Contacts.UI.showHideContactInfo();
|
||||
//Contacts.UI.checkListFor(obj);
|
||||
} else if(type == 'single') {
|
||||
var proptype = Contacts.UI.propertyTypeFor(obj);
|
||||
console.log('deleteProperty, hiding: ' + proptype);
|
||||
|
@ -718,8 +733,8 @@ Contacts={
|
|||
} else { // Property hasn't been saved so there's nothing to delete.
|
||||
if(type == 'list') {
|
||||
Contacts.UI.propertyContainerFor(obj).remove();
|
||||
Contacts.UI.showHideContactInfo();
|
||||
Contacts.UI.checkListFor(obj);
|
||||
//Contacts.UI.showHideContactInfo();
|
||||
//Contacts.UI.checkListFor(obj);
|
||||
} else if(type == 'single') {
|
||||
var proptype = Contacts.UI.propertyTypeFor(obj);
|
||||
console.log('deleteProperty, hiding: ' + proptype);
|
||||
|
@ -891,7 +906,7 @@ Contacts={
|
|||
if(isnew) {
|
||||
container.remove();
|
||||
}
|
||||
Contacts.UI.showHideContactInfo();
|
||||
//Contacts.UI.showHideContactInfo();
|
||||
}
|
||||
},
|
||||
close : function(event, ui) {
|
||||
|
@ -900,11 +915,95 @@ Contacts={
|
|||
if(isnew) {
|
||||
container.remove();
|
||||
}
|
||||
Contacts.UI.showHideContactInfo();
|
||||
}/*,
|
||||
//Contacts.UI.showHideContactInfo();
|
||||
},
|
||||
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 {
|
||||
alert(jsondata.data.message);
|
||||
|
@ -973,7 +1072,7 @@ Contacts={
|
|||
}
|
||||
},
|
||||
loadPhoto:function(force){
|
||||
if(this.data.PHOTO||force==true) {
|
||||
//if(this.data.PHOTO||force==true) {
|
||||
$.getJSON('ajax/loadphoto.php',{'id':this.id},function(jsondata){
|
||||
if(jsondata.status == 'success'){
|
||||
//alert(jsondata.data.page);
|
||||
|
@ -986,11 +1085,11 @@ Contacts={
|
|||
});
|
||||
$('#file_upload_form').show();
|
||||
$('#contacts_propertymenu a[data-type="PHOTO"]').parent().hide();
|
||||
} else {
|
||||
/*} else {
|
||||
$('#contacts_details_photo_wrapper').empty();
|
||||
$('#file_upload_form').hide();
|
||||
$('#contacts_propertymenu a[data-type="PHOTO"]').parent().show();
|
||||
}
|
||||
}*/
|
||||
},
|
||||
editPhoto:function(id, tmp_path){
|
||||
//alert('editPhoto: ' + tmp_path);
|
||||
|
@ -1165,7 +1264,7 @@ Contacts={
|
|||
});
|
||||
}
|
||||
},
|
||||
import:function(){
|
||||
doImport:function(){
|
||||
Contacts.UI.notImplemented();
|
||||
},
|
||||
submit:function(button, bookid){
|
||||
|
@ -1198,9 +1297,7 @@ Contacts={
|
|||
}
|
||||
},
|
||||
Contacts:{
|
||||
/**
|
||||
* Reload the contacts list.
|
||||
*/
|
||||
// Reload the contacts list.
|
||||
update:function(){
|
||||
console.log('Contacts.update, start');
|
||||
$.getJSON('ajax/contacts.php',{},function(jsondata){
|
||||
|
@ -1215,9 +1312,7 @@ Contacts={
|
|||
});
|
||||
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(){
|
||||
$('#contacts li').live('inview', function(){
|
||||
if (!$(this).find('a').attr('style')) {
|
||||
|
@ -1237,9 +1332,6 @@ $(document).ready(function(){
|
|||
OCCategories.changed = Contacts.UI.Card.categoriesChanged;
|
||||
OCCategories.app = 'contacts';
|
||||
|
||||
/**
|
||||
* Show the Addressbook chooser
|
||||
*/
|
||||
$('#chooseaddressbook').click(function(){
|
||||
Contacts.UI.Addressbooks.overview();
|
||||
return false;
|
||||
|
@ -1272,7 +1364,7 @@ $(document).ready(function(){
|
|||
});
|
||||
|
||||
$('#contacts_deletecard').live('click',function(){
|
||||
Contacts.UI.Card.delete();
|
||||
Contacts.UI.Card.doDelete();
|
||||
});
|
||||
|
||||
$('#contacts li').bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
|
||||
|
@ -1430,7 +1522,8 @@ $(document).ready(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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -72,17 +72,10 @@
|
|||
.appendTo( ul );
|
||||
};
|
||||
|
||||
this.button = $( "<button type='button'> </button>" )
|
||||
/*this.button = $( "<button type='button'> </button>" )
|
||||
.attr( "tabIndex", -1 )
|
||||
.attr( "title", "Show All Items" )
|
||||
.insertAfter( input )
|
||||
/*.button({
|
||||
icons: {
|
||||
primary: "ui-icon-triangle-1-s"
|
||||
},
|
||||
text: false
|
||||
})
|
||||
.removeClass( "ui-corner-all" )*/
|
||||
.addClass('svg')
|
||||
.addClass('action')
|
||||
.addClass('combo-button')
|
||||
|
@ -99,7 +92,7 @@
|
|||
// pass empty string as value to search for, displaying all results
|
||||
input.autocomplete( "search", "" );
|
||||
input.focus();
|
||||
});
|
||||
});*/
|
||||
$.each(this.options, function(key, value) {
|
||||
self._setOption(key, value);
|
||||
});
|
||||
|
@ -123,17 +116,23 @@
|
|||
case "id":
|
||||
this.options['id'] = value;
|
||||
this.input.attr('id', value);
|
||||
break;
|
||||
break;
|
||||
case "name":
|
||||
this.options['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":
|
||||
var input = this.input;
|
||||
$.each(this.options['classes'], function(key, value) {
|
||||
input.addClass(value);
|
||||
});
|
||||
break;
|
||||
break;
|
||||
}
|
||||
// In jQuery UI 1.8, you have to manually invoke the _setOption method from the base widget
|
||||
$.Widget.prototype._setOption.apply( this, arguments );
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
return false;
|
||||
}
|
||||
});
|
||||
this.button = $( "<button type='button'> </button>" )
|
||||
/*this.button = $( "<button type='button'> </button>" )
|
||||
.attr( "tabIndex", -1 )
|
||||
.attr( "title", "Show All Items" )
|
||||
.insertAfter( this.element )
|
||||
|
@ -86,7 +86,7 @@
|
|||
// pass empty string as value to search for, displaying all results
|
||||
self.element.autocomplete( "search", "" );
|
||||
self.element.focus();
|
||||
});
|
||||
});*/
|
||||
},
|
||||
});
|
||||
})( jQuery );
|
||||
|
|
|
@ -169,7 +169,7 @@ class OC_Contacts_Addressbook{
|
|||
$uid = OC_User::getUser();
|
||||
}
|
||||
$prefbooks = OC_Preferences::getValue($uid,'contacts','openaddressbooks',null);
|
||||
if(is_null($prefbooks)){
|
||||
if(!$prefbooks){
|
||||
$addressbooks = OC_Contacts_Addressbook::all($uid);
|
||||
if(count($addressbooks) == 0){
|
||||
OC_Contacts_Addressbook::add($uid,'default','Default Address Book');
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script type='text/javascript'>
|
||||
var totalurl = '<?php echo OC_Helper::linkToAbsolute('contacts', 'carddav.php'); ?>/addressbooks';
|
||||
var categories = <?php sort($_['categories']); echo json_encode($_['categories']); ?>;
|
||||
var lang = '<?php echo OC_Preferences::getValue(OC_User::getUser(), 'core', 'lang', 'en'); ?>';
|
||||
</script>
|
||||
<div id="controls">
|
||||
<form>
|
||||
|
|
|
@ -17,16 +17,16 @@ $id = isset($_['id']) ? $_['id'] : '';
|
|||
<li><a data-type="CATEGORIES"><?php echo $l->t('Categories'); ?></a></li>
|
||||
</ul>
|
||||
</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');?>" />
|
||||
</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">
|
||||
<fieldset id="photo" class="formfloat">
|
||||
<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">
|
||||
<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']; ?>" / -->
|
||||
<progress id="contacts_details_photo_progress" style="display:none;" value="0" max="100">0 %</progress>
|
||||
</div>
|
||||
|
@ -37,58 +37,55 @@ $id = isset($_['id']) ? $_['id'] : '';
|
|||
<iframe name="file_upload_target" id='file_upload_target' src=""></iframe>
|
||||
</fieldset>
|
||||
</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'] ?>">
|
||||
<fieldset class="propertycontainer" data-element="N"><input type="hidden" id="n" class="contacts_property" name="value" value="" /></fieldset>
|
||||
<fieldset id="ident" class="formfloat">
|
||||
<fieldset id="ident" class="contactpart">
|
||||
<!-- legend>Name</legend -->
|
||||
<dl class="form">
|
||||
<!-- dt><label for="n"><?php echo $l->t('Name'); ?></label></dt>
|
||||
<dd style="padding-top: 0.8em;vertical-align: text-bottom;"><span id="n" type="text"></span></dd -->
|
||||
<dt><label for="fn"><?php echo $l->t('Display name'); ?></label></dt>
|
||||
<dd class="propertycontainer" data-element="FN">
|
||||
<select id="fn_select" title="<?php echo $l->t('Format custom, Short name, Full name, Reverse or Reverse with comma'); ?>" style="width:16em;">
|
||||
</select><a id="edit_name" class="action edit" title="<?php echo $l->t('Edit name details'); ?>"></a>
|
||||
</dd>
|
||||
<span class="propertycontainer" data-element="N"><input type="hidden" id="n" class="contacts_property" name="value" value="" /></span>
|
||||
<span id="name" class="propertycontainer" data-element="FN">
|
||||
<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;">
|
||||
</select><a id="edit_name" class="action edit" title="<?php echo $l->t('Edit name details'); ?>"></a>
|
||||
</span>
|
||||
<dl id="identityprops" class="form">
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<dt style="display:none;" id="categories_label" data-element="CATEGORIES"><label for="categories"><?php echo $l->t('Categories'); ?></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="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('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 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>
|
||||
</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>
|
||||
</div>
|
||||
</div> <!-- contact_identity -->
|
||||
|
||||
<!-- div class="delimiter"></div -->
|
||||
<form id="contact_communication" method="post" style="display: none;">
|
||||
<div class="contactsection">
|
||||
<div id="contact_communication" class="contactsection">
|
||||
<form method="post">
|
||||
<!-- email addresses -->
|
||||
<div id="emails" style="display:none;">
|
||||
<div id="emails">
|
||||
<fieldset class="contactpart">
|
||||
<legend><?php echo $l->t('Email'); ?></legend>
|
||||
<!-- legend><?php echo $l->t('Email'); ?></legend -->
|
||||
<ul id="emaillist" class="propertylist">
|
||||
<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>
|
||||
<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 -->
|
||||
</div> <!-- email addresses-->
|
||||
|
||||
<!-- Phone numbers -->
|
||||
<div id="phones" style="display:none;">
|
||||
<div id="phones">
|
||||
<fieldset class="contactpart">
|
||||
<legend><?php echo $l->t('Phone'); ?></legend>
|
||||
<!-- legend><?php echo $l->t('Phone'); ?></legend -->
|
||||
<ul id="phonelist" class="propertylist">
|
||||
<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'); ?>" />
|
||||
<select multiple="multiple" name="parameters[TYPE][]">
|
||||
<?php echo html_select_options($_['phone_types'], array()) ?>
|
||||
|
@ -100,7 +97,7 @@ $id = isset($_['id']) ? $_['id'] : '';
|
|||
<!-- Addresses -->
|
||||
<div id="addresses" style="display:none;">
|
||||
<fieldset class="contactpart">
|
||||
<legend><?php echo $l->t('Address'); ?></legend>
|
||||
<!-- legend><?php echo $l->t('Address'); ?></legend -->
|
||||
<div id="addressdisplay">
|
||||
<dl class="addresscard template" style="display: none;" data-element="ADR"><dt>
|
||||
<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>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div> <!-- addressdisplay -->
|
||||
</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>
|
||||
</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_img"></div>
|
||||
</div>
|
||||
|
@ -128,7 +130,7 @@ $(document).ready(function(){
|
|||
Contacts.UI.Card.loadContact(jsondata.data);
|
||||
}
|
||||
else{
|
||||
Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
|
||||
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
</dt>
|
||||
<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>
|
||||
<dt>
|
||||
<label class="label" for="adr_extended"><?php echo $l->t('Extended'); ?></label>
|
||||
</dt>
|
||||
<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>
|
||||
<dt>
|
||||
<label class="label" for="adr_street"><?php echo $l->t('Street'); ?></label>
|
||||
</dt>
|
||||
<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>
|
||||
<dt>
|
||||
<label class="label" for="adr_city"><?php echo $l->t('City'); ?></label>
|
||||
</dt>
|
||||
<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>
|
||||
<dt>
|
||||
<label class="label" for="adr_region"><?php echo $l->t('Region'); ?></label>
|
||||
</dt>
|
||||
<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>
|
||||
<dt>
|
||||
<label class="label" for="adr_zipcode"><?php echo $l->t('Zipcode'); ?></label>
|
||||
</dt>
|
||||
<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>
|
||||
<dt>
|
||||
<label class="label" for="adr_country"><?php echo $l->t('Country'); ?></label>
|
||||
</dt>
|
||||
<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>
|
||||
</dl>
|
||||
</fieldset>
|
||||
|
|
|
@ -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>
|
|
@ -8,5 +8,6 @@
|
|||
<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>
|
||||
</dl>
|
||||
Powered by <a href="http://geonames.org/" target="_blank">geonames.org webservice</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
|
|
@ -22,9 +22,11 @@ function setSyntaxMode(ext){
|
|||
filetype["css"] = "css";
|
||||
filetype["groovy"] = "groovy";
|
||||
filetype["haxe"] = "hx";
|
||||
filetype["htm"] = "html";
|
||||
filetype["html"] = "html";
|
||||
filetype["java"] = "java";
|
||||
filetype["js"] = "javascript";
|
||||
filetype["jsm"] = "javascript";
|
||||
filetype["json"] = "json";
|
||||
filetype["latex"] = "latex";
|
||||
filetype["ly"] = "latex";
|
||||
|
@ -141,32 +143,40 @@ function doSearch(){
|
|||
// Tries to save the file.
|
||||
function doFileSave(){
|
||||
if(editorIsShown()){
|
||||
// Get file path
|
||||
var path = $('#editor').attr('data-dir')+'/'+$('#editor').attr('data-filename');
|
||||
// Get original mtime
|
||||
var mtime = $('#editor').attr('data-mtime');
|
||||
// Show saving spinner
|
||||
$("#editor_save").die('click',doFileSave);
|
||||
$('#save_result').remove();
|
||||
$('#editor_save').text(t('files_texteditor','Saving...'));
|
||||
// Get the data
|
||||
var filecontents = window.aceEditor.getSession().getValue();
|
||||
// Send the data
|
||||
$.post(OC.filePath('files_texteditor','ajax','savefile.php'), { filecontents: filecontents, path: path, mtime: mtime },function(jsondata){
|
||||
if(jsondata.status!='success'){
|
||||
// Save failed
|
||||
$('#editor_save').text(t('files_texteditor','Save'));
|
||||
$('#editor_save').after('<p id="save_result" style="float: left">Failed to save file</p>');
|
||||
$("#editor_save").live('click',doFileSave);
|
||||
} else {
|
||||
// Save OK
|
||||
// Update mtime
|
||||
$('#editor').attr('data-mtime',jsondata.data.mtime);
|
||||
$('#editor_save').text(t('files_texteditor','Save'));
|
||||
$("#editor_save").live('click',doFileSave);
|
||||
}
|
||||
},'json');
|
||||
// Changed contents?
|
||||
if($('#editor').attr('data-edited')=='true'){
|
||||
// Get file path
|
||||
var path = $('#editor').attr('data-dir')+'/'+$('#editor').attr('data-filename');
|
||||
// Get original mtime
|
||||
var mtime = $('#editor').attr('data-mtime');
|
||||
// Show saving spinner
|
||||
$("#editor_save").die('click',doFileSave);
|
||||
$('#save_result').remove();
|
||||
$('#editor_save').text(t('files_texteditor','Saving...'));
|
||||
// Get the data
|
||||
var filecontents = window.aceEditor.getSession().getValue();
|
||||
// Send the data
|
||||
$.post(OC.filePath('files_texteditor','ajax','savefile.php'), { filecontents: filecontents, path: path, mtime: mtime },function(jsondata){
|
||||
if(jsondata.status!='success'){
|
||||
// Save failed
|
||||
$('#editor_save').text(t('files_texteditor','Save'));
|
||||
$('#editor_save').after('<p id="save_result" style="float: left">Failed to save file</p>');
|
||||
$("#editor_save").live('click',doFileSave);
|
||||
} else {
|
||||
// Save OK
|
||||
// Update mtime
|
||||
$('#editor').attr('data-mtime',jsondata.data.mtime);
|
||||
$('#editor_save').text(t('files_texteditor','Save'));
|
||||
$("#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
|
||||
|
@ -176,6 +186,8 @@ function giveEditorFocus(){
|
|||
|
||||
// Loads the file editor. Accepts two parameters, dir and filename.
|
||||
function showFileEditor(dir,filename){
|
||||
// Delete any old editors
|
||||
$('#editor').remove();
|
||||
if(!editorIsShown()){
|
||||
// Loads the file editor and display it.
|
||||
$('#content').append('<div id="editor"></div>');
|
||||
|
@ -192,10 +204,11 @@ function showFileEditor(dir,filename){
|
|||
// Show the control bar
|
||||
showControls(filename,result.data.write);
|
||||
// Update document title
|
||||
document.title = filename;
|
||||
document.title = filename+' - ownCloud';
|
||||
$('#editor').text(result.data.filecontents);
|
||||
$('#editor').attr('data-dir', dir);
|
||||
$('#editor').attr('data-filename', filename);
|
||||
$('#editor').attr('data-edited', 'false');
|
||||
window.aceEditor = ace.edit("editor");
|
||||
aceEditor.setShowPrintMargin(false);
|
||||
aceEditor.getSession().setUseWrapMode(true);
|
||||
|
@ -207,10 +220,17 @@ function showFileEditor(dir,filename){
|
|||
OC.addScript('files_texteditor','aceeditor/theme-clouds', function(){
|
||||
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 {
|
||||
// Failed to get the file.
|
||||
alert(result.data.message);
|
||||
OC.dialogs.alert(result.data.message, t('files_texteditor','An error occurred!'));
|
||||
}
|
||||
// End success
|
||||
}
|
||||
|
@ -222,37 +242,54 @@ function showFileEditor(dir,filename){
|
|||
|
||||
// Fades out the editor.
|
||||
function hideFileEditor(){
|
||||
// Fades out editor controls
|
||||
$('#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";
|
||||
var editorhtml = '<div id="editor"></div>';
|
||||
$('table').after(editorhtml);
|
||||
$('.actions,#file_access_panel').fadeIn('slow');
|
||||
$('table').fadeIn('slow');
|
||||
});
|
||||
is_editor_shown = false;
|
||||
if($('#editor').attr('data-edited') == 'true'){
|
||||
// Hide, not remove
|
||||
$('#editorcontrols').fadeOut('slow',function(){
|
||||
// Check if there is a folder in the breadcrumb
|
||||
if($('.crumb.ui-droppable').length){
|
||||
$('.crumb.ui-droppable:last').addClass('last');
|
||||
}
|
||||
});
|
||||
// Fade out editor
|
||||
$('#editor').fadeOut('slow', function(){
|
||||
// Reset document title
|
||||
document.title = "ownCloud";
|
||||
$('.actions,#file_access_panel').fadeIn('slow');
|
||||
$('table').fadeIn('slow');
|
||||
});
|
||||
$('#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
|
||||
var ctrlBtn = false;
|
||||
// Reopens the last document
|
||||
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){
|
||||
if(e.which == 17 || e.which == 91) ctrlBtn=true;
|
||||
if(e.which == 83 && ctrlBtn == true) {
|
||||
e.preventDefault();
|
||||
$('#editor_save').trigger('click');
|
||||
return false;
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
is_editor_shown = true;
|
||||
}
|
||||
|
||||
// resizes the editor window
|
||||
|
@ -285,6 +322,11 @@ $(document).ready(function(){
|
|||
// Binds the file save and close editor events, and gotoline button
|
||||
bindControlEvents();
|
||||
$('#editor').remove();
|
||||
// Binds the save keyboard shortcut events
|
||||
//$(document).unbind('keydown').bind('keydown',checkForSaveKeyPress);
|
||||
$('#notification').click(function(){
|
||||
if($('#notification').data('reopeneditor'))
|
||||
{
|
||||
reopenEditor();
|
||||
}
|
||||
$('#notification').fadeOut();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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));
|
|
@ -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();
|
||||
}
|
|
@ -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');
|
|
@ -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>
|
|
@ -0,0 +1,3 @@
|
|||
#file_versioning_commit_chzn {
|
||||
width: 15em;
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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');
|
|
@ -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();
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -43,8 +43,9 @@ function shareGallery() {
|
|||
{text: 'Shared gallery address', name: 'address', type: 'text', value: existing_token}];
|
||||
OC.dialogs.form(form_fields, t('gallery', 'Share gallery'), function(values){
|
||||
var p = '';
|
||||
for (var i in paths) p += '/'+paths[i];
|
||||
for (var i in paths) p += paths[i]+'/';
|
||||
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) {
|
||||
if (r.status == 'success') {
|
||||
Albums.shared = r.sharing;
|
||||
|
@ -112,42 +113,28 @@ function scanForAlbums(cleanup) {
|
|||
}
|
||||
|
||||
function settings() {
|
||||
$( '#g-dialog-settings' ).dialog({
|
||||
height: 180,
|
||||
width: 350,
|
||||
modal: false,
|
||||
buttons: [
|
||||
{
|
||||
text: t('gallery', 'Apply'),
|
||||
click: function() {
|
||||
var scanning_root = $('#g-scanning-root').val();
|
||||
var disp_order = $('#g-display-order option:selected').val();
|
||||
OC.dialogs.form([{text: t('gallery', 'Scanning root'), name: 'root', type:'text', value:gallery_scanning_root},
|
||||
{text: t('gallery', 'Default order'), name: 'order', type:'select', value:gallery_default_order, options:[
|
||||
{text:t('gallery', 'Ascending'), value:'ASC'}, {text: t('gallery', 'Descending'), value:'DESC'} ]}],
|
||||
t('gallery', 'Settings'),
|
||||
function(values) {
|
||||
var scanning_root = values[0].value;
|
||||
var disp_order = values[1].value;
|
||||
if (scanning_root == '') {
|
||||
alert('Scanning root cannot be empty');
|
||||
OC.dialogs.alert(t('gallery', 'Scanning root cannot be empty'), t('gallery', 'Error'));
|
||||
return;
|
||||
}
|
||||
$.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'store_settings', root: scanning_root, order: disp_order}, function(r) {
|
||||
if (r.status == 'success') {
|
||||
if (r.rescan == 'yes') {
|
||||
$('#g-dialog-settings').dialog('close');
|
||||
Albums.clear(document.getElementById('gallery_list'));
|
||||
scanForAlbums(true);
|
||||
return;
|
||||
}
|
||||
if (r.rescan == 'yes') {
|
||||
Albums.clear(document.getElementById('gallery_list'));
|
||||
scanForAlbums(true);
|
||||
}
|
||||
gallery_scanning_root = scanning_root;
|
||||
} else {
|
||||
alert('Error: ' + r.cause);
|
||||
return;
|
||||
OC.dialogs.alert(t('gallery', 'Error: ') + r.cause, t('gallery', 'Error'));
|
||||
return;
|
||||
}
|
||||
$('#g-dialog-settings').dialog('close');
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
text: t('gallery', 'Cancel'),
|
||||
click: function() {
|
||||
$(this).dialog('close');
|
||||
}
|
||||
}
|
||||
],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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' );
|
||||
$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="scan">
|
||||
<div id="scanprogressbar"></div>
|
||||
|
@ -29,40 +29,3 @@ $l = new OC_L10N('gallery');
|
|||
</div>
|
||||
<div id="gallery_list">
|
||||
</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>
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<info>
|
||||
<id>remoteStorage</id>
|
||||
<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>
|
||||
<version>0.5</version>
|
||||
<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.6</version>
|
||||
<licence>AGPL or MIT</licence>
|
||||
<author>Michiel de Jong</author>
|
||||
<require>2</require>
|
||||
|
|
|
@ -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>
|
|
@ -7,4 +7,7 @@
|
|||
<licence>AGPL</licence>
|
||||
<author>Dominik Schmidt</author>
|
||||
<require>2</require>
|
||||
<types>
|
||||
<authentication/>
|
||||
</types>
|
||||
</info>
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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'] = '';
|
||||
}
|
|
@ -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'
|
||||
);
|
||||
?>
|
|
@ -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>
|
|
@ -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
|
||||
);
|
||||
});
|
||||
});
|
|
@ -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();
|
|
@ -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>
|
|
@ -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>
|
|
@ -7,4 +7,7 @@
|
|||
<licence>AGPL</licence>
|
||||
<author>Robin Appelman</author>
|
||||
<require>2</require>
|
||||
<types>
|
||||
<authentication/>
|
||||
</types>
|
||||
</info>
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteRule host-meta$ \/\.well-known\/host-meta\.php [L]
|
|
@ -2,9 +2,9 @@
|
|||
<info>
|
||||
<id>user_webfinger</id>
|
||||
<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>
|
||||
<version>0.2</version>
|
||||
<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.3</version>
|
||||
<licence>AGPL or MIT</licence>
|
||||
<author>Michiel de Jong</author>
|
||||
<author>Michiel de Jong, Florian Hülsmann</author>
|
||||
<require>2</require>
|
||||
</info>
|
||||
|
|
|
@ -1,6 +1,51 @@
|
|||
<?php
|
||||
$hostMetaHeader = array(
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Content-Type' => 'application/xml+xrd'
|
||||
);
|
||||
$appInfoDir = __DIR__;
|
||||
$thisAppDir = dirname($appInfoDir);
|
||||
$appsDir = dirname($thisAppDir);
|
||||
$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);
|
||||
|
|
|
@ -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'
|
|
@ -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>
|
||||
|
|
@ -1,41 +1,71 @@
|
|||
<?php
|
||||
if($_SERVER['SCRIPT_NAME'] == '/.well-known/webfinger.php') {
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
} else {
|
||||
header('Please-first: activate');
|
||||
}
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
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
|
||||
// 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__)))));
|
||||
$SUBURI=substr(realpath($_SERVER["SCRIPT_FILENAME"]),strlen($SERVERROOT));
|
||||
$WEBROOT=substr($SUBURI,0,-34);
|
||||
*/
|
||||
|
||||
require_once('../../lib/base.php');
|
||||
$request = urldecode($_GET['q']);
|
||||
if($_GET['q']) {
|
||||
$bits = explode('@', $_GET['q']);
|
||||
$userName = $bits[0];
|
||||
$reqParts = explode('@', $request);
|
||||
$userName = $reqParts[0];
|
||||
$hostName = $reqParts[1];
|
||||
} else {
|
||||
$userName = '';
|
||||
$hostName = '';
|
||||
}
|
||||
if(substr($userName, 0, 5) == 'acct:') {
|
||||
$userName = substr($userName, 5);
|
||||
}
|
||||
if(isset($_SERVER['HTTPS'])) {
|
||||
$baseAddress = 'https://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/';
|
||||
if($userName == "") {
|
||||
$id = "";
|
||||
} 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 "<";
|
||||
?>
|
||||
?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="remoteStorage"
|
||||
template="<?php echo $baseAddress ?>WebDAV.php/<?php echo $userName ?>/remoteStorage/{category}/"
|
||||
api="WebDAV"
|
||||
auth="<?php echo $baseAddress; ?>auth.php/<?php echo $userName ?>"
|
||||
></Link>
|
||||
<hm:Host xmlns="http://host-meta.net/xrd/1.0"><?php echo $_SERVER['SERVER_NAME']; ?></hm:Host>
|
||||
<Subject>acct:<?php echo $id ?></Subject>
|
||||
<?php
|
||||
$apps = OC_Appconfig::getApps();
|
||||
foreach($apps as $app) {
|
||||
if(OC_App::isEnabled($app)) {
|
||||
if(is_file(OC::$APPSROOT . '/apps/' . $app . '/appinfo/webfinger.php')) {
|
||||
require($app . '/appinfo/webfinger.php');
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</XRD>
|
||||
|
|
|
@ -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; }
|
||||
|
||||
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 |
|
@ -33,8 +33,12 @@
|
|||
*/
|
||||
OC.EventSource=function(src,data){
|
||||
var dataStr='';
|
||||
for(name in data){
|
||||
dataStr+=name+'='+encodeURIComponent(data[name])+'&';
|
||||
this.typelessListeners=[];
|
||||
this.listeners={};
|
||||
if(data){
|
||||
for(name in data){
|
||||
dataStr+=name+'='+encodeURIComponent(data[name])+'&';
|
||||
}
|
||||
}
|
||||
if(!this.useFallBack && typeof EventSource !='undefined'){
|
||||
this.source=new EventSource(src+'?'+dataStr);
|
||||
|
@ -42,7 +46,7 @@ OC.EventSource=function(src,data){
|
|||
for(var i=0;i<this.typelessListeners.length;i++){
|
||||
this.typelessListeners[i](JSON.parse(e.data));
|
||||
}
|
||||
}
|
||||
}.bind(this);
|
||||
}else{
|
||||
iframeId='oc_eventsource_iframe_'+OC.EventSource.iframeCount;
|
||||
OC.EventSource.fallBackSources[OC.EventSource.iframeCount]=this;
|
||||
|
@ -64,7 +68,7 @@ OC.EventSource=function(src,data){
|
|||
OC.EventSource.fallBackSources=[];
|
||||
OC.EventSource.iframeCount=0;//number of fallback iframes
|
||||
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={
|
||||
typelessListeners:[],
|
||||
|
|
|
@ -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.currentResult=-1;
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
* 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/>.
|
||||
*
|
||||
* todo(bartek): add select option in form
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -30,9 +29,9 @@ OCdialogs = {
|
|||
* @param title dialog title
|
||||
* @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>';
|
||||
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
|
||||
|
@ -40,9 +39,9 @@ OCdialogs = {
|
|||
* @param title dialog title
|
||||
* @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>';
|
||||
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
|
||||
|
@ -50,9 +49,9 @@ OCdialogs = {
|
|||
* @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)
|
||||
*/
|
||||
confirm:function(text, title, callback) {
|
||||
confirm:function(text, title, callback, modal) {
|
||||
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
|
||||
|
@ -60,9 +59,9 @@ OCdialogs = {
|
|||
* @param title dialog title
|
||||
* @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>';
|
||||
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
|
||||
|
@ -71,7 +70,7 @@ OCdialogs = {
|
|||
* @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'},...])
|
||||
*/
|
||||
form:function(fields, title, callback) {
|
||||
form:function(fields, title, callback, modal) {
|
||||
var content = '<table>';
|
||||
for (var a in fields) {
|
||||
content += '<tr><td>'+fields[a].text+'</td><td>';
|
||||
|
@ -84,16 +83,63 @@ OCdialogs = {
|
|||
} else content += '>';
|
||||
} else if (type == 'text' || type == 'password' && 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>";
|
||||
OCdialogs.message(content, title, OCdialogs.FORM_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback);
|
||||
content += '</table>';
|
||||
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_id = '#'+c_name;
|
||||
var d = '<div id="'+c_name+'" title="'+title+'">'+content+'</div>';
|
||||
if (modal == undefined) modal = false;
|
||||
$('body').append(d);
|
||||
var b = [];
|
||||
switch (buttons) {
|
||||
|
@ -107,7 +153,7 @@ OCdialogs = {
|
|||
var f;
|
||||
switch(dialog_type) {
|
||||
case OCdialogs.ALERT_DIALOG:
|
||||
f = function(){$(c_id).dialog('close'); };
|
||||
f = function(){$(c_id).dialog('close'); callback();};
|
||||
break;
|
||||
case OCdialogs.PROMPT_DIALOG:
|
||||
f = function(){OCdialogs.prompt_ok_handler(callback, c_id)};
|
||||
|
@ -120,7 +166,7 @@ OCdialogs = {
|
|||
break;
|
||||
}
|
||||
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++;
|
||||
},
|
||||
// dialogs buttons types
|
||||
|
@ -144,7 +190,7 @@ OCdialogs = {
|
|||
if (callback != undefined) {
|
||||
var r = [];
|
||||
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)};
|
||||
c++;
|
||||
});
|
||||
|
@ -153,5 +199,47 @@ OCdialogs = {
|
|||
} else {
|
||||
$(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)});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ OCCategories={
|
|||
height: 350, minHeight:200, width: 250, minWidth: 200,
|
||||
buttons: {
|
||||
'Delete':function() {
|
||||
OCCategories.delete();
|
||||
OCCategories.doDelete();
|
||||
},
|
||||
'Rescan':function() {
|
||||
OCCategories.rescan();
|
||||
|
@ -53,7 +53,7 @@ OCCategories={
|
|||
}
|
||||
});
|
||||
},
|
||||
delete:function(){
|
||||
doDelete:function(){
|
||||
var categories = $('#categorylist').find('input[type="checkbox"]').serialize();
|
||||
categories += '&app=' + OCCategories.app;
|
||||
console.log('OCCategories.delete: ' + categories);
|
||||
|
|
|
@ -12,7 +12,7 @@ require_once('../../lib/base.php');
|
|||
// Someone lost their password:
|
||||
if (isset($_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);
|
||||
$email = OC_Preferences::getValue($_POST['user'], 'settings', 'email', '');
|
||||
if (!empty($email)) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<?php endif; ?>
|
||||
<p class="infield">
|
||||
<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 class="infield">
|
||||
<label for="password" class="infield"><?php echo $l->t( 'Password' ); ?></label>
|
||||
|
|
|
@ -67,8 +67,7 @@
|
|||
<field>
|
||||
<name>path_hash</name>
|
||||
<type>text</type>
|
||||
<default>
|
||||
</default>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>32</length>
|
||||
</field>
|
||||
|
|
|
@ -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));
|
||||
|
||||
?>
|
|
@ -17,6 +17,7 @@ if($force or !OC_FileCache::inCache('')){
|
|||
if(!$checkOnly){
|
||||
OC_DB::beginTransaction();
|
||||
OC_FileCache::scan('',$eventSource);
|
||||
OC_FileCache::clean();
|
||||
OC_DB::commit();
|
||||
$eventSource->send('success',true);
|
||||
}else{
|
||||
|
|
|
@ -417,7 +417,7 @@ var folderDropOptions={
|
|||
var dir=$('#dir').val();
|
||||
$.ajax({
|
||||
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(){
|
||||
var el = $('#fileList tr').filterAttr('data-file',file).find('td.filename');
|
||||
el.draggable('destroy');
|
||||
|
@ -443,7 +443,7 @@ var crumbDropOptions={
|
|||
}
|
||||
$.ajax({
|
||||
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(){
|
||||
FileList.remove(file);
|
||||
});}
|
||||
|
|
|
@ -63,12 +63,12 @@
|
|||
</div>
|
||||
<div id="scanning-message">
|
||||
<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>
|
||||
<p>
|
||||
<?php echo $l->t('Current scanning');?> <span id='scan-current'></spann>
|
||||
<?php echo $l->t('Current scanning');?> <span id='scan-current'></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 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']; ?>" />
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
$RUNTIME_NOSETUPFS = true;
|
||||
|
||||
// only need filesystem apps
|
||||
$RUNTIME_APPTYPES=array('filesystem');
|
||||
$RUNTIME_APPTYPES=array('filesystem','authentication');
|
||||
|
||||
require_once('../lib/base.php');
|
||||
|
||||
|
|
12
lib/app.php
12
lib/app.php
|
@ -55,7 +55,7 @@ class OC_App{
|
|||
|
||||
// Our very own core apps are hardcoded
|
||||
foreach( array('files', 'settings') as $app ){
|
||||
if(is_null($types) or self::isType($app,$types)){
|
||||
if(is_null($types)){
|
||||
require( $app.'/appinfo/app.php' );
|
||||
}
|
||||
}
|
||||
|
@ -103,9 +103,9 @@ class OC_App{
|
|||
*/
|
||||
public static function getEnabledApps(){
|
||||
$apps=array();
|
||||
$query = OC_DB::prepare( 'SELECT appid FROM *PREFIX*appconfig WHERE configkey = "enabled" AND configvalue="yes"' );
|
||||
$query->execute();
|
||||
while($row=$query->fetchRow()){
|
||||
$query = OC_DB::prepare( 'SELECT appid FROM *PREFIX*appconfig WHERE configkey = \'enabled\' AND configvalue=\'yes\'' );
|
||||
$result=$query->execute();
|
||||
while($row=$result->fetchRow()){
|
||||
$apps[]=$row['appid'];
|
||||
}
|
||||
return $apps;
|
||||
|
@ -319,7 +319,7 @@ class OC_App{
|
|||
$file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/info.xml';
|
||||
}
|
||||
$data=array();
|
||||
$content=file_get_contents($file);
|
||||
$content=@file_get_contents($file);
|
||||
if(!$content){
|
||||
return;
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ class OC_App{
|
|||
*/
|
||||
public static function getAppVersions(){
|
||||
$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();
|
||||
while($row = $result->fetchRow()){
|
||||
$versions[$row['appid']]=$row['configvalue'];
|
||||
|
|
87
lib/base.php
87
lib/base.php
|
@ -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(){
|
||||
// register autoloader
|
||||
spl_autoload_register(array('OC','autoload'));
|
||||
|
@ -244,6 +277,24 @@ class OC{
|
|||
date_default_timezone_set('Europe/Berlin');
|
||||
ini_set('arg_separator.output','&');
|
||||
|
||||
//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
|
||||
if (isset($_SERVER['HTTP_AUTHORIZATION']) && preg_match('/Basic\s+(.*)$/i', $_SERVER['HTTP_AUTHORIZATION'], $matches))
|
||||
{
|
||||
|
@ -270,38 +321,11 @@ class OC{
|
|||
|
||||
self::checkInstalled();
|
||||
self::checkSSL();
|
||||
|
||||
self::initSession();
|
||||
self::initTemplateEngine();
|
||||
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();
|
||||
if(count($errors)>0) {
|
||||
OC_Template::printGuestPage('', 'error', array('errors' => $errors));
|
||||
|
@ -341,6 +365,9 @@ class OC{
|
|||
OC_App::loadApps();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for blacklisted files
|
||||
OC_Hook::connect('OC_Filesystem','write','OC_Filesystem','isBlacklisted');
|
||||
|
||||
//make sure temporary files are cleaned up
|
||||
register_shutdown_function(array('OC_Helper','cleanTmp'));
|
||||
|
|
25
lib/db.php
25
lib/db.php
|
@ -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
|
||||
*/
|
||||
|
@ -586,3 +610,4 @@ class PDOStatementWrapper{
|
|||
return $this->statement->fetchColumn($colnum);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ class OC_EventSource{
|
|||
private $fallBackId=0;
|
||||
|
||||
public function __construct(){
|
||||
@ob_end_clean();
|
||||
header('Cache-Control: no-cache');
|
||||
$this->fallback=isset($_GET['fallback']) and $_GET['fallback']=='true';
|
||||
if($this->fallback){
|
||||
|
@ -58,7 +59,7 @@ class OC_EventSource{
|
|||
$type=null;
|
||||
}
|
||||
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;
|
||||
}else{
|
||||
if($type){
|
||||
|
|
|
@ -240,7 +240,7 @@ class OC_FileCache{
|
|||
* - encrypted
|
||||
* - versioned
|
||||
*/
|
||||
public static function getFolderContent($path,$root=''){
|
||||
public static function getFolderContent($path,$root='',$mimetype_filter=''){
|
||||
if(self::isUpdated($path,$root)){
|
||||
self::updateFolder($path,$root);
|
||||
}
|
||||
|
@ -252,8 +252,8 @@ class OC_FileCache{
|
|||
}
|
||||
$path=$root.$path;
|
||||
$parent=self::getFileId($path);
|
||||
$query=OC_DB::prepare('SELECT name,ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE parent=?');
|
||||
$result=$query->execute(array($parent))->fetchAll();
|
||||
$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, $mimetype_filter.'%', 'httpd/unix-directory'))->fetchAll();
|
||||
if(is_array($result)){
|
||||
return $result;
|
||||
}else{
|
||||
|
@ -469,6 +469,10 @@ class OC_FileCache{
|
|||
* @param string root (optionak)
|
||||
*/
|
||||
public static function scan($path,$eventSource=false,&$count=0,$root=''){
|
||||
if($eventSource){
|
||||
$eventSource->send('scanning',array('file'=>$path,'count'=>$count));
|
||||
}
|
||||
$lastSend=$count;
|
||||
if(!$root){
|
||||
$view=OC_Filesystem::getView();
|
||||
}else{
|
||||
|
@ -482,13 +486,14 @@ class OC_FileCache{
|
|||
if($filename != '.' and $filename != '..'){
|
||||
$file=$path.'/'.$filename;
|
||||
if($view->is_dir($file.'/')){
|
||||
if($eventSource){
|
||||
$eventSource->send('scanning',array('file'=>$file,'count'=>$count));
|
||||
}
|
||||
self::scan($file,$eventSource,$count,$root);
|
||||
}else{
|
||||
$totalSize+=self::scanFile($file,$root);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
|
@ -32,11 +32,11 @@ class OC_Files {
|
|||
* get the content of a directory
|
||||
* @param dir $directory
|
||||
*/
|
||||
public static function getDirectoryContent($directory){
|
||||
public static function getDirectoryContent($directory, $mimetype_filter = ''){
|
||||
if(strpos($directory,OC::$CONFIG_DATADIRECTORY)===0){
|
||||
$directory=substr($directory,strlen(OC::$CONFIG_DATADIRECTORY));
|
||||
}
|
||||
$files=OC_FileCache::getFolderContent($directory);
|
||||
$files=OC_FileCache::getFolderContent($directory, '', $mimetype_filter);
|
||||
foreach($files as &$file){
|
||||
$file['directory']=$directory;
|
||||
$file['type']=($file['mimetype']=='httpd/unix-directory')?'dir':'file';
|
||||
|
|
|
@ -86,6 +86,10 @@ class OC_Filestorage_Local extends OC_Filestorage{
|
|||
return $this->delTree($path);
|
||||
}
|
||||
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)){
|
||||
OC_Log::write('core','unable to rename, file does not exists : '.$path1,OC_Log::ERROR);
|
||||
return false;
|
||||
|
|
|
@ -298,6 +298,19 @@ class OC_Filesystem{
|
|||
}
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -137,13 +137,16 @@ class OC_FilesystemView {
|
|||
}
|
||||
public function readfile($path){
|
||||
$handle=$this->fopen($path,'r');
|
||||
$chunkSize = 1024*1024;// 1 MB chunks
|
||||
while (!feof($handle)) {
|
||||
echo fread($handle, $chunkSize);
|
||||
@ob_flush();
|
||||
flush();
|
||||
if ($handle) {
|
||||
$chunkSize = 1024*1024;// 1 MB chunks
|
||||
while (!feof($handle)) {
|
||||
echo fread($handle, $chunkSize);
|
||||
@ob_flush();
|
||||
flush();
|
||||
}
|
||||
return $this->filesize($path);
|
||||
}
|
||||
return $this->filesize($path);
|
||||
return false;
|
||||
}
|
||||
public function is_readable($path){
|
||||
return $this->basicOperation('is_readable',$path);
|
||||
|
@ -189,7 +192,7 @@ class OC_FilesystemView {
|
|||
return $this->basicOperation('unlink',$path,array('delete'));
|
||||
}
|
||||
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;
|
||||
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){
|
||||
|
|
71
lib/log.php
71
lib/log.php
|
@ -1,78 +1,39 @@
|
|||
<?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/>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
*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 INFO=1;
|
||||
const WARN=2;
|
||||
const ERROR=3;
|
||||
const FATAL=4;
|
||||
|
||||
static protected $class = null;
|
||||
|
||||
/**
|
||||
* 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){
|
||||
$datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' );
|
||||
$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);
|
||||
public static function write($app, $message, $level) {
|
||||
if (!self::$class) {
|
||||
self::$class = 'OC_Log_'.ucfirst(OC_Config::getValue('log_type', 'owncloud'));
|
||||
call_user_func(array(self::$class, 'init'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
$log_class=self::$class;
|
||||
$log_class::write($app, $message, $level);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -16,5 +16,6 @@ return array(
|
|||
'xls'=>'application/msexcel',
|
||||
'xlsx'=>'application/msexcel',
|
||||
'ppt'=>'application/mspowerpoint',
|
||||
'pptx'=>'application/mspowerpoint'
|
||||
'pptx'=>'application/mspowerpoint',
|
||||
'sgf' => 'application/sgf'
|
||||
);
|
||||
|
|
|
@ -36,6 +36,7 @@ class OC_Updater{
|
|||
$version['installed']=OC_Config::getValue('installedat');
|
||||
$version['updated']=OC_Appconfig::getValue('core', 'lastupdatedat', OC_Config::getValue( 'lastupdatedat'));
|
||||
$version['updatechannel']='stable';
|
||||
$version['edition']=OC_Util::getEditionString();
|
||||
$versionstring=implode('x',$version);
|
||||
|
||||
//fetch xml data from updater
|
||||
|
|
|
@ -77,6 +77,14 @@ class OC_Util {
|
|||
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
|
||||
*
|
||||
|
|
|
@ -13,5 +13,5 @@ OC_JSON::checkAdminUser();
|
|||
$count=(isset($_GET['count']))?$_GET['count']:50;
|
||||
$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));
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue