aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files_versioning/ajax/gethead.php12
-rw-r--r--apps/files_versioning/ajax/sethead.php14
-rw-r--r--apps/files_versioning/appinfo/app.php20
-rw-r--r--apps/files_versioning/appinfo/info.xml12
-rw-r--r--apps/files_versioning/appinfo/version1
-rw-r--r--apps/files_versioning/css/settings.css3
-rw-r--r--apps/files_versioning/js/settings.js25
-rw-r--r--apps/files_versioning/lib_granite.php12
-rw-r--r--apps/files_versioning/settings.php34
-rw-r--r--apps/files_versioning/templates/settings.php12
-rw-r--r--apps/files_versioning/versionstorage.php386
-rw-r--r--apps/files_versioning/versionwrapper.php686
12 files changed, 0 insertions, 1217 deletions
diff --git a/apps/files_versioning/ajax/gethead.php b/apps/files_versioning/ajax/gethead.php
deleted file mode 100644
index cc93b7a1d17..00000000000
--- a/apps/files_versioning/ajax/gethead.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-/**
- * Copyright (c) 2011 Craig Roberts craig0990@googlemail.com
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- */
-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));
diff --git a/apps/files_versioning/ajax/sethead.php b/apps/files_versioning/ajax/sethead.php
deleted file mode 100644
index d1b2df9b00f..00000000000
--- a/apps/files_versioning/ajax/sethead.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-/**
- * Copyright (c) 2011 Craig Roberts craig0990@googlemail.com
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- */
-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();
-}
diff --git a/apps/files_versioning/appinfo/app.php b/apps/files_versioning/appinfo/app.php
deleted file mode 100644
index 24a8701dbb0..00000000000
--- a/apps/files_versioning/appinfo/app.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-// Include required files
-require_once('apps/files_versioning/versionstorage.php');
-require_once('apps/files_versioning/versionwrapper.php');
-// Register streamwrapper for versioned:// paths
-stream_wrapper_register('versioned', 'OC_VersionStreamWrapper');
-
-// Add an entry in the app list for versioning and backup
-OC_App::register( array(
- 'order' => 10,
- 'id' => 'files_versioning',
- 'name' => 'Versioning and Backup' ));
-
-// Include stylesheets for the settings page
-OC_Util::addStyle( 'files_versioning', 'settings' );
-OC_Util::addScript('files_versioning','settings');
-
-// Register a settings section in the Admin > Personal page
-OC_APP::registerPersonal('files_versioning','settings');
diff --git a/apps/files_versioning/appinfo/info.xml b/apps/files_versioning/appinfo/info.xml
deleted file mode 100644
index b9f56f674a0..00000000000
--- a/apps/files_versioning/appinfo/info.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<info>
- <id>files_versioning</id>
- <name>Versioning and Backup</name>
- <licence>GPLv2</licence>
- <author>Craig Roberts</author>
- <require>3</require>
- <description>Versions files using Git repositories, providing a simple backup facility. Currently in *beta* and explicitly without warranty of any kind.</description>
- <types>
- <filesystem/>
- </types>
-</info>
diff --git a/apps/files_versioning/appinfo/version b/apps/files_versioning/appinfo/version
deleted file mode 100644
index afaf360d37f..00000000000
--- a/apps/files_versioning/appinfo/version
+++ /dev/null
@@ -1 +0,0 @@
-1.0.0 \ No newline at end of file
diff --git a/apps/files_versioning/css/settings.css b/apps/files_versioning/css/settings.css
deleted file mode 100644
index afe2cd5508f..00000000000
--- a/apps/files_versioning/css/settings.css
+++ /dev/null
@@ -1,3 +0,0 @@
-#file_versioning_commit_chzn {
- width: 15em;
-}
diff --git a/apps/files_versioning/js/settings.js b/apps/files_versioning/js/settings.js
deleted file mode 100644
index 8dd13bac033..00000000000
--- a/apps/files_versioning/js/settings.js
+++ /dev/null
@@ -1,25 +0,0 @@
-$(document).ready(function(){
- $('#file_versioning_head').chosen();
-
- $.getJSON(OC.filePath('files_versioning', 'ajax', 'gethead.php'), function(jsondata, status) {
-
- if (jsondata.head == 'HEAD') {
- // Most recent commit, do nothing
- } else {
- $("#file_versioning_head").val(jsondata.head);
- // Trigger the chosen update call
- // See http://harvesthq.github.com/chosen/
- $("#file_versioning_head").trigger("liszt:updated");
- }
- });
-
- $('#file_versioning_head').change(function() {
-
- var data = $(this).serialize();
- $.post( OC.filePath('files_versioning', 'ajax', 'sethead.php'), data, function(data){
- if(data == 'error'){
- console.log('Saving new HEAD failed');
- }
- });
- });
-});
diff --git a/apps/files_versioning/lib_granite.php b/apps/files_versioning/lib_granite.php
deleted file mode 100644
index 571e5cea637..00000000000
--- a/apps/files_versioning/lib_granite.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-require_once('granite/git/blob.php');
-require_once('granite/git/commit.php');
-require_once('granite/git/repository.php');
-require_once('granite/git/tag.php');
-require_once('granite/git/tree.php');
-require_once('granite/git/tree/node.php');
-require_once('granite/git/object/index.php');
-require_once('granite/git/object/raw.php');
-require_once('granite/git/object/loose.php');
-require_once('granite/git/object/packed.php');
diff --git a/apps/files_versioning/settings.php b/apps/files_versioning/settings.php
deleted file mode 100644
index 94af587a215..00000000000
--- a/apps/files_versioning/settings.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-// Get the full path to the repository folder (FIXME: hard-coded to 'Backup')
-$path = OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data')
- . DIRECTORY_SEPARATOR
- . OC_User::getUser()
- . DIRECTORY_SEPARATOR
- . 'files'
- . DIRECTORY_SEPARATOR
- . 'Backup'
- . DIRECTORY_SEPARATOR
- . '.git'
- . DIRECTORY_SEPARATOR;
-
-$repository = new Granite\Git\Repository($path);
-
-$commits = array();
-// Fetch most recent 50 commits (FIXME - haven't tested this much)
-$commit = $repository->head();
-for ($i = 0; $i < 50; $i++) {
- $commits[] = $commit;
- $parents = $commit->parents();
- if (count($parents) > 0) {
- $parent = $parents[0];
- } else {
- break;
- }
-
- $commit = $repository->factory('commit', $parent);
-}
-
-$tmpl = new OC_Template( 'files_versioning', 'settings');
-$tmpl->assign('commits', $commits);
-return $tmpl->fetchPage();
diff --git a/apps/files_versioning/templates/settings.php b/apps/files_versioning/templates/settings.php
deleted file mode 100644
index 17f4cc7f77f..00000000000
--- a/apps/files_versioning/templates/settings.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<fieldset id="status_list" class="personalblock">
- <strong>Versioning and Backup</strong><br>
- <p><em>Please note: Backing up large files (around 16MB+) will cause your backup history to grow very large, very quickly.</em></p>
- <label class="bold">Backup Folder</label>
- <select name="file_versioning_head" id="file_versioning_head">
- <?php
- foreach ($_['commits'] as $commit):
- echo '<option value="' . $commit->sha() . '">' . $commit->message() . '</option>';
- endforeach;
- ?>
- </select>
-</fieldset>
diff --git a/apps/files_versioning/versionstorage.php b/apps/files_versioning/versionstorage.php
deleted file mode 100644
index d083e623df9..00000000000
--- a/apps/files_versioning/versionstorage.php
+++ /dev/null
@@ -1,386 +0,0 @@
-<?php
-/**
- * ownCloud file storage implementation for Git repositories
- * @author Craig Roberts
- * @copyright 2012 Craig Roberts craig0990@googlemail.com
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-// Include Granite
-require_once('lib_granite.php');
-
-// Create a top-level 'Backup' directory if it does not already exist
-$user = OC_User::getUser();
-if (OC_Filesystem::$loaded and !OC_Filesystem::is_dir('/Backup')) {
- OC_Filesystem::mkdir('/Backup');
- OC_Preferences::setValue(OC_User::getUser(), 'files_versioning', 'head', 'HEAD');
-}
-
-// Generate the repository path (currently using 'full' repositories, as opposed to bare ones)
-$repo_path = DIRECTORY_SEPARATOR
- . OC_User::getUser()
- . DIRECTORY_SEPARATOR
- . 'files'
- . DIRECTORY_SEPARATOR
- . 'Backup';
-
-// Mount the 'Backup' folder using the versioned storage provider below
-OC_Filesystem::mount('OC_Filestorage_Versioned', array('repo'=>$repo_path), $repo_path . DIRECTORY_SEPARATOR);
-
-class OC_Filestorage_Versioned extends OC_Filestorage {
-
- /**
- * Holds an instance of Granite\Git\Repository
- */
- protected $repo;
-
- /**
- * Constructs a new OC_Filestorage_Versioned instance, expects an associative
- * array with a `repo` key set to the path of the repository's `.git` folder
- *
- * @param array $parameters An array containing the key `repo` pointing to the
- * repository path.
- */
- public function __construct($parameters) {
- // Get the full path to the repository folder
- $path = OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data')
- . $parameters['repo']
- . DIRECTORY_SEPARATOR
- . '.git'
- . DIRECTORY_SEPARATOR;
-
- try {
- // Attempt to load the repository
- $this->repo = new Granite\Git\Repository($path);
- } catch (InvalidArgumentException $e) {
- // $path is not a valid Git repository, we must create one
- Granite\Git\Repository::init($path);
-
- // Load the newly-initialised repository
- $this->repo = new Granite\Git\Repository($path);
-
- /**
- * Create an initial commit with a README file
- * FIXME: This functionality should be transferred to the Granite library
- */
- $blob = new Granite\Git\Blob($this->repo->path());
- $blob->content('Your Backup directory is now ready for use.');
-
- // Create a new tree to hold the README file
- $tree = $this->repo->factory('tree');
- // Create a tree node to represent the README blob
- $tree_node = new Granite\Git\Tree\Node('README', '100644', $blob->sha());
- $tree->nodes(array($tree_node->name() => $tree_node));
-
- // Create an initial commit
- $commit = new Granite\Git\Commit($this->repo->path());
- $user_string = OC_User::getUser() . ' ' . time() . ' +0000';
- $commit->author($user_string);
- $commit->committer($user_string);
- $commit->message('Initial commit');
- $commit->tree($tree);
-
- // Write it all to disk
- $blob->write();
- $tree->write();
- $commit->write();
-
- // Update the HEAD for the 'master' branch
- $this->repo->head('master', $commit->sha());
- }
-
- // Update the class pointer to the HEAD
- $head = OC_Preferences::getValue(OC_User::getUser(), 'files_versioning', 'head', 'HEAD');
-
- // Load the most recent commit if the preference is not set
- if ($head == 'HEAD') {
- $this->head = $this->repo->head()->sha();
- } else {
- $this->head = $head;
- }
- }
-
- public function mkdir($path) {
- if (mkdir("versioned:/{$this->repo->path()}$path#{$this->head}")) {
- $this->head = $this->repo->head()->sha();
- OC_Preferences::setValue(OC_User::getUser(), 'files_versioning', 'head', $head);
- return true;
- }
-
- return false;
- }
-
- public function rmdir($path) {
-
- }
-
- /**
- * Returns a directory handle to the requested path, or FALSE on failure
- *
- * @param string $path The directory path to open
- *
- * @return boolean|resource A directory handle, or FALSE on failure
- */
- public function opendir($path) {
- return opendir("versioned:/{$this->repo->path()}$path#{$this->head}");
- }
-
- /**
- * Returns TRUE if $path is a directory, or FALSE if not
- *
- * @param string $path The path to check
- *
- * @return boolean
- */
- public function is_dir($path) {
- return $this->filetype($path) == 'dir';
- }
-
- /**
- * Returns TRUE if $path is a file, or FALSE if not
- *
- * @param string $path The path to check
- *
- * @return boolean
- */
- public function is_file($path) {
- return $this->filetype($path) == 'file';
- }
-
- public function stat($path)
- {
- return stat("versioned:/{$this->repo->path()}$path#{$this->head}");
- }
-
- /**
- * Returns the strings 'dir' or 'file', depending on the type of $path
- *
- * @param string $path The path to check
- *
- * @return string Returns 'dir' if a directory, 'file' otherwise
- */
- public function filetype($path) {
- if ($path == "" || $path == "/") {
- return 'dir';
- } else {
- if (substr($path, -1) == '/') {
- $path = substr($path, 0, -1);
- }
-
- $node = $this->tree_search($this->repo, $this->repo->factory('commit', $this->head)->tree(), $path);
-
- // Does it exist, or is it new?
- if ($node == null) {
- // New file
- return 'file';
- } else {
- // Is it a tree?
- try {
- $this->repo->factory('tree', $node);
- return 'dir';
- } catch (InvalidArgumentException $e) {
- // Nope, must be a blob
- return 'file';
- }
- }
- }
- }
-
- public function filesize($path) {
- return filesize("versioned:/{$this->repo->path()}$path#{$this->head}");
- }
-
- /**
- * Returns a boolean value representing whether $path is readable
- *
- * @param string $path The path to check
- *(
- * @return boolean Whether or not the path is readable
- */
- public function is_readable($path) {
- return true;
- }
-
- /**
- * Returns a boolean value representing whether $path is writable
- *
- * @param string $path The path to check
- *(
- * @return boolean Whether or not the path is writable
- */
- public function is_writable($path) {
-
- $head = OC_Preferences::getValue(OC_User::getUser(), 'files_versioning', 'head', 'HEAD');
- if ($head !== 'HEAD' && $head !== $this->repo->head()->sha()) {
- // Cannot modify previous commits
- return false;
- }
- return true;
- }
-
- /**
- * Returns a boolean value representing whether $path exists
- *
- * @param string $path The path to check
- *(
- * @return boolean Whether or not the path exists
- */
- public function file_exists($path) {
- return file_exists("versioned:/{$this->repo->path()}$path#{$this->head}");
- }
-
- /**
- * Returns an integer value representing the inode change time
- * (NOT IMPLEMENTED)
- *
- * @param string $path The path to check
- *(
- * @return int Timestamp of the last inode change
- */
- public function filectime($path) {
- return -1;
- }
-
- /**
- * Returns an integer value representing the file modification time
- *
- * @param string $path The path to check
- *(
- * @return int Timestamp of the last file modification
- */
- public function filemtime($path) {
- return filemtime("versioned:/{$this->repo->path()}$path#{$this->head}");
- }
-
- public function file_get_contents($path) {
- return file_get_contents("versioned:/{$this->repo->path()}$path#{$this->head}");
- }
-
- public function file_put_contents($path, $data) {
- $success = file_put_contents("versioned:/{$this->repo->path()}$path#{$this->head}", $data);
- if ($success !== false) {
- // Update the HEAD in the preferences
- OC_Preferences::setValue(OC_User::getUser(), 'files_versioning', 'head', $this->repo->head()->sha());
- return $success;
- }
-
- return false;
- }
-
- public function unlink($path) {
-
- }
-
- public function rename($path1, $path2) {
-
- }
-
- public function copy($path1, $path2) {
-
- }
-
- public function fopen($path, $mode) {
- return fopen("versioned:/{$this->repo->path()}$path#{$this->head}", $mode);
- }
-
- public function getMimeType($path) {
- if ($this->filetype($path) == 'dir') {
- return 'httpd/unix-directory';
- } elseif ($this->filesize($path) == 0) {
- // File's empty, returning text/plain allows opening in the web editor
- return 'text/plain';
- } else {
- $finfo = new finfo(FILEINFO_MIME_TYPE);
- /**
- * We need to represent the repository path, the file path, and the
- * revision, which can be simply achieved with a convention of using
- * `.git` in the repository directory (bare or not) and the '#part'
- * segment of a URL to specify the revision. For example
- *
- * versioned://var/www/myrepo.git/docs/README.md#HEAD ('bare' repo)
- * versioned://var/www/myrepo/.git/docs/README.md#HEAD ('full' repo)
- * versioned://var/www/myrepo/.git/docs/README.md#6a8f...8a54 ('full' repo and SHA-1 commit ID)
- */
- $mime = $finfo->buffer(file_get_contents("versioned:/{$this->repo->path()}$path#{$this->head}"));
- return $mime;
- }
- }
-
- /**
- * Generates a hash based on the file contents
- *
- * @param string $type The hashing algorithm to use (e.g. 'md5', 'sha256', etc.)
- * @param string $path The file to be hashed
- * @param boolean $raw Outputs binary data if true, lowercase hex digits otherwise
- *
- * @return string Hashed string representing the file contents
- */
- public function hash($type, $path, $raw) {
- return hash($type, file_get_contents($path), $raw);
- }
-
- public function free_space($path) {
- }
-
- public function search($query) {
-
- }
-
- public function touch($path, $mtime=null) {
-
- }
-
-
- public function getLocalFile($path) {
- }
-
- /**
- * Recursively searches a tree for a path, returning FALSE if is not found
- * or an SHA-1 id if it is found.
- *
- * @param string $repo The repository containing the tree object
- * @param string $tree The tree object to search
- * @param string $path The path to search for (relative to the tree)
- * @param int $depth The depth of the current search (for recursion)
- *
- * @return string|boolean The SHA-1 id of the sub-tree
- */
- private function tree_search($repo, $tree, $path, $depth = 0)
- {
- $paths = array_values(explode(DIRECTORY_SEPARATOR, $path));
-
- $current_path = $paths[$depth];
-
- $nodes = $tree->nodes();
- foreach ($nodes as $node) {
- if ($node->name() == $current_path) {
-
- if (count($paths)-1 == $depth) {
- // Stop, found it
- return $node->sha();
- }
-
- // Recurse if necessary
- if ($node->isDirectory()) {
- $tree = $this->repo->factory('tree', $node->sha());
- return $this->tree_search($repo, $tree, $path, $depth + 1);
- }
- }
- }
-
- return false;
- }
-
-}
diff --git a/apps/files_versioning/versionwrapper.php b/apps/files_versioning/versionwrapper.php
deleted file mode 100644
index b83a4fd3b22..00000000000
--- a/apps/files_versioning/versionwrapper.php
+++ /dev/null
@@ -1,686 +0,0 @@
-<?php
-
-final class OC_VersionStreamWrapper {
-
- /**
- * Determines whether or not to log debug messages with `OC_Log::write()`
- */
- private $debug = true;
-
- /**
- * The name of the ".empty" files created in new directories
- */
- const EMPTYFILE = '.empty';
-
- /**
- * Stores the current position for `readdir()` etc. calls
- */
- private $dir_position = 0;
-
- /**
- * Stores the current position for `fread()`, `fseek()` etc. calls
- */
- private $file_position = 0;
-
- /**
- * Stores the current directory tree for `readdir()` etc. directory traversal
- */
- private $tree;
-
- /**
- * Stores the current file for `fread()`, `fseek()`, etc. calls
- */
- private $blob;
-
- /**
- * Stores the current commit for `fstat()`, `stat()`, etc. calls
- */
- private $commit;
-
- /**
- * Stores the current path for `fwrite()`, `file_put_contents()` etc. calls
- */
- private $path;
-
- /**
- * Close directory handle
- */
- public function dir_closedir() {
- unset($this->tree);
- return true;
- }
-
- /**
- * Open directory handle
- */
- public function dir_opendir($path, $options) {
- // Parse the URL into a repository directory, file path and commit ID
- list($this->repo, $repo_file, $this->commit) = $this->parse_url($path);
-
- if ($repo_file == '' || $repo_file == '/') {
- // Set the tree property for the future `readdir()` etc. calls
- $this->tree = array_values($this->commit->tree()->nodes());
- return true;
- } elseif ($this->tree_search($this->repo, $this->commit->tree(), $repo_file) !== false) {
- // Something exists at this path, is it a directory though?
- try {
- $tree = $this->repo->factory(
- 'tree',
- $this->tree_search($this->repo, $this->commit->tree(), $repo_file)
- );
- $this->tree = array_values($tree->nodes());
- return true;
- } catch (InvalidArgumentException $e) {
- // Trying to call `opendir()` on a file, return false below
- }
- }
-
- // Unable to find the directory, return false
- return false;
- }
-
- /**
- * Read entry from directory handle
- */
- public function dir_readdir() {
- return isset($this->tree[$this->dir_position])
- ? $this->tree[$this->dir_position++]->name()
- : false;
- }
-
- /**
- * Rewind directory handle
- */
- public function dir_rewinddir() {
- $this->dir_position = 0;
- }
-
- /**
- * Create a directory
- * Git doesn't track empty directories, so a ".empty" file is added instead
- */
- public function mkdir($path, $mode, $options) {
- // Parse the URL into a repository directory, file path and commit ID
- list($this->repo, $repo_file, $this->commit) = $this->parse_url($path);
-
- // Create an empty file for Git
- $empty = new Granite\Git\Blob($this->repo->path());
- $empty->content('');
- $empty->write();
-
- if (dirname($repo_file) == '.') {
- // Adding a new directory to the root tree
- $tree = $this->repo->head()->tree();
- } else {
- $tree = $this->repo->factory('tree', $this->tree_search(
- $this->repo, $this->repo->head()->tree(), dirname($repo_file)
- )
- );
- }
-
- // Create our new tree, with our empty file
- $dir = $this->repo->factory('tree');
- $nodes = array();
- $nodes[self::EMPTYFILE] = new Granite\Git\Tree\Node(self::EMPTYFILE, '100644', $empty->sha());
- $dir->nodes($nodes);
- $dir->write();
-
- // Add our new tree to its parent
- $nodes = $tree->nodes();
- $nodes[basename($repo_file)] = new Granite\Git\Tree\Node(basename($repo_file), '040000', $dir->sha());
- $tree->nodes($nodes);
- $tree->write();
-
- // We need to recursively update each parent tree, since they are all
- // hashed and the changes will cascade back up the chain
-
- // So, we're currently at the bottom-most directory
- $current_dir = dirname($repo_file);
- $previous_tree = $tree;
-
- if ($current_dir !== '.') {
- do {
- // Determine the parent directory
- $previous_dir = $current_dir;
- $current_dir = dirname($current_dir);
-
- $current_tree = $current_dir !== '.'
- ? $this->repo->factory(
- 'tree', $this->tree_search(
- $this->repo,
- $this->repo->head()->tree(),
- $current_dir
- )
- )
- : $this->repo->head()->tree();
-
- $current_nodes = $current_tree->nodes();
- $current_nodes[basename($previous_dir)] = new Granite\Git\Tree\Node(
- basename($previous_dir), '040000', $previous_tree->sha()
- );
- $current_tree->nodes($current_nodes);
- $current_tree->write();
-
- $previous_tree = $current_tree;
- } while ($current_dir !== '.');
-
- $tree = $previous_tree;
- }
-
- // Create a new commit to represent this write
- $commit = $this->repo->factory('commit');
- $username = OC_User::getUser();
- $user_string = $username . ' ' . time() . ' +0000';
- $commit->author($user_string);
- $commit->committer($user_string);
- $commit->message("$username created the `$repo_file` directory, " . date('d F Y H:i', time()) . '.');
- $commit->parents(array($this->repo->head()->sha()));
- $commit->tree($tree);
-
- // Write it to disk
- $commit->write();
-
- // Update the HEAD for the 'master' branch
- $this->repo->head('master', $commit->sha());
-
- return true;
- }
-
- /**
- * Renames a file or directory
- */
- public function rename($path_from, $path_to) {
-
- }
-
- /**
- * Removes a directory
- */
- public function rmdir($path, $options) {
-
- }
-
- /**
- * Retrieve the underlaying resource (NOT IMPLEMENTED)
- */
- public function stream_cast($cast_as) {
- return false;
- }
-
- /**
- * Close a resource
- */
- public function stream_close() {
- unset($this->blob);
- return true;
- }
-
- /**
- * Tests for end-of-file on a file pointer
- */
- public function stream_eof() {
- return !($this->file_position < strlen($this->blob));
- }
-
- /**
- * Flushes the output (NOT IMPLEMENTED)
- */
- public function stream_flush() {
- return false;
- }
-
- /**
- * Advisory file locking (NOT IMPLEMENTED)
- */
- public function stream_lock($operation) {
- return false;
- }
-
- /**
- * Change stream options (NOT IMPLEMENTED)
- * Called in response to `chgrp()`, `chown()`, `chmod()` and `touch()`
- */
- public function stream_metadata($path, $option, $var) {
- return false;
- }
-
- /**
- * Opens file or URL
- */
- public function stream_open($path, $mode, $options, &$opened_path) {
- // Store the path, so we can use it later in `stream_write()` if necessary
- $this->path = $path;
- // Parse the URL into a repository directory, file path and commit ID
- list($this->repo, $repo_file, $this->commit) = $this->parse_url($path);
-
- $file = $this->tree_search($this->repo, $this->commit->tree(), $repo_file);
- if ($file !== false) {
- try {
- $this->blob = $this->repo->factory('blob', $file)->content();
- return true;
- } catch (InvalidArgumentException $e) {
- // Trying to open a directory, return false below
- }
- } elseif ($mode !== 'r') {
- // All other modes allow opening for reading and writing, clearly
- // some 'write' files may not exist yet...
- return true;
- }
-
- // File could not be found or is not actually a file
- return false;
- }
-
- /**
- * Read from stream
- */
- public function stream_read($count) {
- // Fetch the remaining set of bytes
- $bytes = substr($this->blob, $this->file_position, $count);
-
- // If EOF or empty string, return false
- if ($bytes == '' || $bytes == false) {
- return false;
- }
-
- // If $count does not extend past EOF, add $count to stream offset
- if ($this->file_position + $count < strlen($this->blob)) {
- $this->file_position += $count;
- } else {
- // Otherwise return all remaining bytes
- $this->file_position = strlen($this->blob);
- }
-
- return $bytes;
- }
-
- /**
- * Seeks to specific location in a stream
- */
- public function stream_seek($offset, $whence = SEEK_SET) {
- $new_offset = false;
-
- switch ($whence)
- {
- case SEEK_SET:
- $new_offset = $offset;
- break;
- case SEEK_CUR:
- $new_offset = $this->file_position += $offset;
- break;
- case SEEK_END:
- $new_offset = strlen($this->blob) + $offset;
- break;
- }
-
- $this->file_position = $offset;
-
- return ($new_offset !== false);
- }
-
- /**
- * Change stream options (NOT IMPLEMENTED)
- */
- public function stream_set_option($option, $arg1, $arg2) {
- return false;
- }
-
- /**
- * Retrieve information about a file resource (NOT IMPLEMENTED)
- */
- public function stream_stat() {
-
- }
-
- /**
- * Retrieve the current position of a stream
- */
- public function stream_tell() {
- return $this->file_position;
- }
-
- /**
- * Truncate stream
- */
- public function stream_truncate($new_size) {
-
- }
-
- /**
- * Write to stream
- * FIXME: Could use heavy refactoring
- */
- public function stream_write($data) {
- /**
- * FIXME: This also needs to be added to Granite, in the form of `add()`,
- * `rm()` and `commit()` calls
- */
-
- // Parse the URL into a repository directory, file path and commit ID
- list($this->repo, $repo_file, $this->commit) = $this->parse_url($this->path);
-
- $node = $this->tree_search($this->repo, $this->commit->tree(), $repo_file);
-
- if ($node !== false) {
- // File already exists, attempting modification of existing tree
- try {
- $this->repo->factory('blob', $node);
-
- // Create our new blob with the provided $data
- $blob = $this->repo->factory('blob');
- $blob->content($data);
- $blob->write();
-
- // We know the tree exists, so strip the filename from the path and
- // find it...
-
- if (dirname($repo_file) == '.' || dirname($repo_file) == '') {
- // Root directory
- $tree = $this->repo->head()->tree();
- } else {
- // Sub-directory
- $tree = $this->repo->factory('tree', $this->tree_search(
- $this->repo,
- $this->repo->head()->tree(),
- dirname($repo_file)
- )
- );
- }
-
- // Replace the old blob with our newly modified one
- $tree_nodes = $tree->nodes();
- $tree_nodes[basename($repo_file)] = new Granite\Git\Tree\Node(
- basename($repo_file), '100644', $blob->sha()
- );
- $tree->nodes($tree_nodes);
- $tree->write();
-
- // We need to recursively update each parent tree, since they are all
- // hashed and the changes will cascade back up the chain
-
- // So, we're currently at the bottom-most directory
- $current_dir = dirname($repo_file);
- $previous_tree = $tree;
-
- if ($current_dir !== '.') {
- do {
- // Determine the parent directory
- $previous_dir = $current_dir;
- $current_dir = dirname($current_dir);
-
- $current_tree = $current_dir !== '.'
- ? $this->repo->factory(
- 'tree', $this->tree_search(
- $this->repo,
- $this->repo->head()->tree(),
- $current_dir
- )
- )
- : $this->repo->head()->tree();
-
- $current_nodes = $current_tree->nodes();
- $current_nodes[basename($previous_dir)] = new Granite\Git\Tree\Node(
- basename($previous_dir), '040000', $previous_tree->sha()
- );
- $current_tree->nodes($current_nodes);
- $current_tree->write();
-
- $previous_tree = $current_tree;
- } while ($current_dir !== '.');
- }
-
- // Create a new commit to represent this write
- $commit = $this->repo->factory('commit');
- $username = OC_User::getUser();
- $user_string = $username . ' ' . time() . ' +0000';
- $commit->author($user_string);
- $commit->committer($user_string);
- $commit->message("$username modified the `$repo_file` file, " . date('d F Y H:i', time()) . '.');
- $commit->parents(array($this->repo->head()->sha()));
- $commit->tree($previous_tree);
-
- // Write it to disk
- $commit->write();
-
- // Update the HEAD for the 'master' branch
- $this->repo->head('master', $commit->sha());
-
- // If we made it this far, write was successful - update the stream
- // position and return the number of bytes written
- $this->file_position += strlen($data);
- return strlen($data);
-
- } catch (InvalidArgumentException $e) {
- // Attempting to write to a directory or other error, fail
- return 0;
- }
- } else {
- // File does not exist, needs to be created
-
- // Create our new blob with the provided $data
- $blob = $this->repo->factory('blob');
- $blob->content($data);
- $blob->write();
-
- if (dirname($repo_file) == '.') {
- // Trying to add a new file to the root tree, nice and easy
- $tree = $this->repo->head()->tree();
- $tree_nodes = $tree->nodes();
- $tree_nodes[basename($repo_file)] = new Granite\Git\Tree\Node(
- basename($repo_file), '100644', $blob->sha()
- );
- $tree->nodes($tree_nodes);
- $tree->write();
- } else {
- // Trying to add a new file to a subdirectory, try and find it
- $tree = $this->repo->factory('tree', $this->tree_search(
- $this->repo, $this->repo->head()->tree(), dirname($repo_file)
- )
- );
-
- // Add the blob to the tree
- $nodes = $tree->nodes();
- $nodes[basename($repo_file)] = new Granite\Git\Tree\Node(
- basename($repo_file), '100644', $blob->sha()
- );
- $tree->nodes($nodes);
- $tree->write();
-
- // We need to recursively update each parent tree, since they are all
- // hashed and the changes will cascade back up the chain
-
- // So, we're currently at the bottom-most directory
- $current_dir = dirname($repo_file);
- $previous_tree = $tree;
-
- if ($current_dir !== '.') {
- do {
- // Determine the parent directory
- $previous_dir = $current_dir;
- $current_dir = dirname($current_dir);
-
- $current_tree = $current_dir !== '.'
- ? $this->repo->factory(
- 'tree', $this->tree_search(
- $this->repo,
- $this->repo->head()->tree(),
- $current_dir
- )
- )
- : $this->repo->head()->tree();
-
- $current_nodes = $current_tree->nodes();
- $current_nodes[basename($previous_dir)] = new Granite\Git\Tree\Node(
- basename($previous_dir), '040000', $previous_tree->sha()
- );
- $current_tree->nodes($current_nodes);
- $current_tree->write();
-
- $previous_tree = $current_tree;
- } while ($current_dir !== '.');
-
- $tree = $previous_tree;
- }
- }
-
- // Create a new commit to represent this write
- $commit = $this->repo->factory('commit');
- $username = OC_User::getUser();
- $user_string = $username . ' ' . time() . ' +0000';
- $commit->author($user_string);
- $commit->committer($user_string);
- $commit->message("$username created the `$repo_file` file, " . date('d F Y H:i', time()) . '.');
- $commit->parents(array($this->repo->head()->sha()));
- $commit->tree($tree); // Top-level tree (NOT the newly modified tree)
-
- // Write it to disk
- $commit->write();
-
- // Update the HEAD for the 'master' branch
- $this->repo->head('master', $commit->sha());
-
- // If we made it this far, write was successful - update the stream
- // position and return the number of bytes written
- $this->file_position += strlen($data);
- return strlen($data);
- }
-
- // Write failed
- return 0;
- }
-
- /**
- * Delete a file
- */
- public function unlink($path) {
-
- }
-
- /**
- * Retrieve information about a file
- */
- public function url_stat($path, $flags) {
- // Parse the URL into a repository directory, file path and commit ID
- list($this->repo, $repo_file, $this->commit) = $this->parse_url($path);
-
- $node = $this->tree_search($this->repo, $this->commit->tree(), $repo_file);
-
- if ($node == false && $this->commit->sha() == $this->repo->head()->sha()) {
- // A new file - no information available
- $size = 0;
- $mtime = -1;
- } else {
-
- // Is it a directory?
- try {
- $this->repo->factory('tree', $node);
- $size = 4096; // FIXME
- } catch (InvalidArgumentException $e) {
- // Must be a file
- $size = strlen(file_get_contents($path));
- }
-
- // Parse the timestamp from the commit message
- preg_match('/[0-9]{10}+/', $this->commit->committer(), $matches);
- $mtime = $matches[0];
- }
-
- $stat["dev"] = "";
- $stat["ino"] = "";
- $stat["mode"] = "";
- $stat["nlink"] = "";
- $stat["uid"] = "";
- $stat["gid"] = "";
- $stat["rdev"] = "";
- $stat["size"] = $size;
- $stat["atime"] = $mtime;
- $stat["mtime"] = $mtime;
- $stat["ctime"] = $mtime;
- $stat["blksize"] = "";
- $stat["blocks"] = "";
-
- return $stat;
- }
-
- /**
- * Debug function for development purposes
- */
- private function debug($message, $level = OC_Log::DEBUG)
- {
- if ($this->debug) {
- OC_Log::write('files_versioning', $message, $level);
- }
- }
-
- /**
- * Parses a URL of the form:
- * `versioned://path/to/git/repository/.git/path/to/file#SHA-1-commit-id`
- * FIXME: Will throw an InvalidArgumentException if $path is invaid
- *
- * @param string $path The path to parse
- *
- * @return array An array containing an instance of Granite\Git\Repository,
- * the file path, and an instance of Granite\Git\Commit
- * @throws InvalidArgumentException If the repository cannot be loaded
- */
- private function parse_url($path)
- {
- preg_match('/\/([A-Za-z0-9\/]+\.git\/)([A-Za-z0-9\/\.\/]*)(#([A-Fa-f0-9]+))*/', $path, $matches);
-
- // Load up the repo
- $repo = new \Granite\Git\Repository($matches[1]);
- // Parse the filename (stripping any trailing slashes)
- $repo_file = $matches[2];
- if (substr($repo_file, -1) == '/') {
- $repo_file = substr($repo_file, 0, -1);
- }
-
- // Default to HEAD if no commit is provided
- $repo_commit = isset($matches[4])
- ? $matches[4]
- : $repo->head()->sha();
-
- // Load the relevant commit
- $commit = $repo->factory('commit', $repo_commit);
-
- return array($repo, $repo_file, $commit);
- }
-
- /**
- * Recursively searches a tree for a path, returning FALSE if is not found
- * or an SHA-1 id if it is found.
- *
- * @param string $repo The repository containing the tree object
- * @param string $tree The tree object to search
- * @param string $path The path to search for (relative to the tree)
- * @param int $depth The depth of the current search (for recursion)
- *
- * @return string|boolean The SHA-1 id of the sub-tree
- */
- private function tree_search($repo, $tree, $path, $depth = 0)
- {
- $paths = array_values(explode(DIRECTORY_SEPARATOR, $path));
-
- $current_path = $paths[$depth];
-
- $nodes = $tree->nodes();
- foreach ($nodes as $node) {
- if ($node->name() == $current_path) {
-
- if (count($paths)-1 == $depth) {
- // Stop, found it
- return $node->sha();
- }
-
- // Recurse if necessary
- if ($node->isDirectory()) {
- $tree = $this->repo->factory('tree', $node->sha());
- return $this->tree_search($repo, $tree, $path, $depth + 1);
- }
- }
- }
-
- return false;
- }
-
-}