aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.htaccess1
-rw-r--r--3rdparty/Archive/Tar.php1954
-rw-r--r--AUTHORS7
-rw-r--r--README5
-rw-r--r--apps/bookmarks/bookmarksHelper.php7
-rw-r--r--apps/bookmarks/js/bookmarks.js6
-rwxr-xr-xapps/calendar/ajax/events.php6
-rw-r--r--apps/calendar/ajax/import/import.php2
-rw-r--r--apps/calendar/export.php2
-rw-r--r--apps/contacts/ajax/addcontact.php5
-rw-r--r--apps/contacts/ajax/addproperty.php11
-rw-r--r--apps/contacts/ajax/deletecard.php10
-rw-r--r--apps/contacts/ajax/saveproperty.php29
-rw-r--r--apps/contacts/appinfo/migrate.php68
-rw-r--r--apps/contacts/css/contacts.css154
-rw-r--r--apps/contacts/img/person_large.pngbin11517 -> 7929 bytes
-rw-r--r--apps/contacts/index.php24
-rw-r--r--apps/contacts/js/contacts.js266
-rw-r--r--apps/contacts/js/jquery.combobox.js23
-rw-r--r--apps/contacts/js/jquery.multi-autocomplete.js17
-rw-r--r--apps/contacts/lib/vcard.php4
-rw-r--r--apps/contacts/templates/index.php1
-rw-r--r--apps/contacts/templates/part.contact.php87
-rw-r--r--apps/contacts/templates/settings.php1
-rw-r--r--apps/files_archive/appinfo/app.php3
-rw-r--r--apps/files_archive/appinfo/info.xml3
-rw-r--r--apps/files_archive/js/archive.js4
-rw-r--r--apps/files_archive/lib/archive.php16
-rw-r--r--apps/files_archive/lib/storage.php6
-rw-r--r--apps/files_archive/lib/tar.php277
-rw-r--r--apps/files_archive/lib/zip.php34
-rw-r--r--apps/files_archive/tests/archive.php40
-rw-r--r--apps/files_archive/tests/tar.php20
-rw-r--r--apps/files_encryption/appinfo/info.xml3
-rw-r--r--apps/files_external/appinfo/app.php11
-rw-r--r--apps/files_external/appinfo/info.xml13
-rw-r--r--apps/files_external/lib/ftp.php (renamed from apps/files_remote/lib/ftp.php)0
-rw-r--r--apps/files_external/lib/google.php (renamed from lib/filestorage/google.php)124
-rw-r--r--apps/files_external/lib/webdav.php293
-rw-r--r--apps/files_external/tests/config.php22
-rw-r--r--apps/files_external/tests/ftp.php (renamed from apps/files_remote/tests/ftp.php)7
-rw-r--r--apps/files_external/tests/google.php38
-rw-r--r--apps/files_external/tests/webdav.php23
-rw-r--r--apps/files_remote/appinfo/app.php9
-rw-r--r--apps/files_remote/appinfo/info.xml10
-rw-r--r--apps/files_remote/tests/config.php9
-rw-r--r--apps/files_sharing/ajax/userautocomplete.php22
-rw-r--r--apps/files_sharing/appinfo/info.xml3
-rw-r--r--apps/files_sharing/lib_share.php14
-rw-r--r--apps/files_texteditor/js/editor.js85
-rw-r--r--apps/gallery/ajax/galleryOp.php47
-rw-r--r--apps/gallery/ajax/sharing.php115
-rw-r--r--apps/gallery/appinfo/app.php5
-rw-r--r--apps/gallery/appinfo/database.xml24
-rw-r--r--apps/gallery/appinfo/info.xml4
-rw-r--r--apps/gallery/css/sharing.css8
-rw-r--r--apps/gallery/css/styles.css2
-rw-r--r--apps/gallery/img/breadcrumb.pngbin0 -> 220 bytes
-rw-r--r--apps/gallery/index.php5
-rw-r--r--apps/gallery/js/album_cover.js97
-rw-r--r--apps/gallery/js/albums.js11
-rw-r--r--apps/gallery/js/sharing.js57
-rw-r--r--apps/gallery/lib/album.php2
-rw-r--r--apps/gallery/lib/photo.php16
-rw-r--r--apps/gallery/lib/scanner.php2
-rw-r--r--apps/gallery/lib/sharing.php89
-rw-r--r--apps/gallery/sharing.php47
-rw-r--r--apps/gallery/templates/index.php42
-rw-r--r--apps/media/css/music.css1
-rw-r--r--apps/remoteStorage/appinfo/info.xml4
-rw-r--r--apps/remoteStorage/appinfo/webfinger.php6
-rw-r--r--apps/user_ldap/appinfo/info.xml3
-rw-r--r--apps/user_openid/appinfo/info.xml3
-rw-r--r--apps/user_webfinger/.htaccess3
-rw-r--r--apps/user_webfinger/appinfo/info.xml6
-rw-r--r--apps/user_webfinger/appinfo/install.php47
-rw-r--r--apps/user_webfinger/host-meta1
-rw-r--r--apps/user_webfinger/host-meta.php16
-rw-r--r--apps/user_webfinger/webfinger.php64
-rwxr-xr-xconfig/config.sample.php4
-rw-r--r--core/css/styles.css6
-rw-r--r--core/js/eventsource.js12
-rw-r--r--core/js/js.js8
-rw-r--r--core/js/oc-dialogs.js137
-rw-r--r--core/js/oc-vcategories.txt9
-rw-r--r--core/lostpassword/index.php2
-rw-r--r--core/templates/403.php15
-rw-r--r--core/templates/login.php3
-rw-r--r--db_structure.xml27
-rw-r--r--files/ajax/download.php3
-rw-r--r--files/ajax/list.php3
-rw-r--r--files/ajax/mimeicon.php3
-rw-r--r--files/ajax/rawlist.php25
-rw-r--r--files/ajax/scan.php1
-rw-r--r--files/css/files.css4
-rw-r--r--files/js/files.js2
-rw-r--r--files/templates/index.php7
-rw-r--r--files/webdav.php3
-rwxr-xr-x[-rw-r--r--]lib/app.php100
-rw-r--r--lib/base.php104
-rw-r--r--lib/db.php3
-rw-r--r--lib/eventsource.php3
-rw-r--r--lib/filecache.php47
-rw-r--r--lib/files.php8
-rw-r--r--lib/filestorage/local.php4
-rw-r--r--lib/filesystem.php13
-rw-r--r--lib/filesystemview.php24
-rwxr-xr-x[-rw-r--r--]lib/helper.php17
-rw-r--r--lib/image.php38
-rw-r--r--lib/installer.php39
-rw-r--r--lib/log.php71
-rw-r--r--lib/log/owncloud.php79
-rw-r--r--lib/log/syslog.php37
-rwxr-xr-x[-rw-r--r--]lib/ocsclient.php52
-rw-r--r--lib/updater.php1
-rw-r--r--lib/util.php10
-rw-r--r--settings/ajax/getlog.php2
-rw-r--r--settings/js/log.js2
-rw-r--r--settings/log.php2
-rw-r--r--settings/templates/personal.php2
-rw-r--r--status.php2
-rw-r--r--tests/index.php12
122 files changed, 4698 insertions, 754 deletions
diff --git a/.htaccess b/.htaccess
index ebb28b0887b..11520d743dc 100644
--- a/.htaccess
+++ b/.htaccess
@@ -1,3 +1,4 @@
+ErrorDocument 403 /core/templates/403.php
ErrorDocument 404 /core/templates/404.php
<IfModule mod_php5.c>
php_value upload_max_filesize 512M
diff --git a/3rdparty/Archive/Tar.php b/3rdparty/Archive/Tar.php
new file mode 100644
index 00000000000..d8eae851bdc
--- /dev/null
+++ b/3rdparty/Archive/Tar.php
@@ -0,0 +1,1954 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * File::CSV
+ *
+ * PHP versions 4 and 5
+ *
+ * Copyright (c) 1997-2008,
+ * Vincent Blavet <vincent@phpconcept.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category File_Formats
+ * @package Archive_Tar
+ * @author Vincent Blavet <vincent@phpconcept.net>
+ * @copyright 1997-2010 The Authors
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Tar.php 323476 2012-02-24 15:27:26Z mrook $
+ * @link http://pear.php.net/package/Archive_Tar
+ */
+
+require_once 'PEAR.php';
+
+define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
+define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
+
+/**
+* Creates a (compressed) Tar archive
+*
+* @package Archive_Tar
+* @author Vincent Blavet <vincent@phpconcept.net>
+* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+* @version $Revision: 323476 $
+*/
+class Archive_Tar extends PEAR
+{
+ /**
+ * @var string Name of the Tar
+ */
+ var $_tarname='';
+
+ /**
+ * @var boolean if true, the Tar file will be gzipped
+ */
+ var $_compress=false;
+
+ /**
+ * @var string Type of compression : 'none', 'gz' or 'bz2'
+ */
+ var $_compress_type='none';
+
+ /**
+ * @var string Explode separator
+ */
+ var $_separator=' ';
+
+ /**
+ * @var file descriptor
+ */
+ var $_file=0;
+
+ /**
+ * @var string Local Tar name of a remote Tar (http:// or ftp://)
+ */
+ var $_temp_tarname='';
+
+ /**
+ * @var string regular expression for ignoring files or directories
+ */
+ var $_ignore_regexp='';
+
+ /**
+ * @var object PEAR_Error object
+ */
+ var $error_object=null;
+
+ // {{{ constructor
+ /**
+ * Archive_Tar Class constructor. This flavour of the constructor only
+ * declare a new Archive_Tar object, identifying it by the name of the
+ * tar file.
+ * If the compress argument is set the tar will be read or created as a
+ * gzip or bz2 compressed TAR file.
+ *
+ * @param string $p_tarname The name of the tar archive to create
+ * @param string $p_compress can be null, 'gz' or 'bz2'. This
+ * parameter indicates if gzip or bz2 compression
+ * is required. For compatibility reason the
+ * boolean value 'true' means 'gz'.
+ *
+ * @access public
+ */
+ function Archive_Tar($p_tarname, $p_compress = null)
+ {
+ $this->PEAR();
+ $this->_compress = false;
+ $this->_compress_type = 'none';
+ if (($p_compress === null) || ($p_compress == '')) {
+ if (@file_exists($p_tarname)) {
+ if ($fp = @fopen($p_tarname, "rb")) {
+ // look for gzip magic cookie
+ $data = fread($fp, 2);
+ fclose($fp);
+ if ($data == "\37\213") {
+ $this->_compress = true;
+ $this->_compress_type = 'gz';
+ // No sure it's enought for a magic code ....
+ } elseif ($data == "BZ") {
+ $this->_compress = true;
+ $this->_compress_type = 'bz2';
+ }
+ }
+ } else {
+ // probably a remote file or some file accessible
+ // through a stream interface
+ if (substr($p_tarname, -2) == 'gz') {
+ $this->_compress = true;
+ $this->_compress_type = 'gz';
+ } elseif ((substr($p_tarname, -3) == 'bz2') ||
+ (substr($p_tarname, -2) == 'bz')) {
+ $this->_compress = true;
+ $this->_compress_type = 'bz2';
+ }
+ }
+ } else {
+ if (($p_compress === true) || ($p_compress == 'gz')) {
+ $this->_compress = true;
+ $this->_compress_type = 'gz';
+ } else if ($p_compress == 'bz2') {
+ $this->_compress = true;
+ $this->_compress_type = 'bz2';
+ } else {
+ $this->_error("Unsupported compression type '$p_compress'\n".
+ "Supported types are 'gz' and 'bz2'.\n");
+ return false;
+ }
+ }
+ $this->_tarname = $p_tarname;
+ if ($this->_compress) { // assert zlib or bz2 extension support
+ if ($this->_compress_type == 'gz')
+ $extname = 'zlib';
+ else if ($this->_compress_type == 'bz2')
+ $extname = 'bz2';
+
+ if (!extension_loaded($extname)) {
+ PEAR::loadExtension($extname);
+ }
+ if (!extension_loaded($extname)) {
+ $this->_error("The extension '$extname' couldn't be found.\n".
+ "Please make sure your version of PHP was built ".
+ "with '$extname' support.\n");
+ return false;
+ }
+ }
+ }
+ // }}}
+
+ // {{{ destructor
+ function _Archive_Tar()
+ {
+ $this->_close();
+ // ----- Look for a local copy to delete
+ if ($this->_temp_tarname != '')
+ @unlink($this->_temp_tarname);
+ $this->_PEAR();
+ }
+ // }}}
+
+ // {{{ create()
+ /**
+ * This method creates the archive file and add the files / directories
+ * that are listed in $p_filelist.
+ * If a file with the same name exist and is writable, it is replaced
+ * by the new tar.
+ * The method return false and a PEAR error text.
+ * The $p_filelist parameter can be an array of string, each string
+ * representing a filename or a directory name with their path if
+ * needed. It can also be a single string with names separated by a
+ * single blank.
+ * For each directory added in the archive, the files and
+ * sub-directories are also added.
+ * See also createModify() method for more details.
+ *
+ * @param array $p_filelist An array of filenames and directory names, or a
+ * single string with names separated by a single
+ * blank space.
+ *
+ * @return true on success, false on error.
+ * @see createModify()
+ * @access public
+ */
+ function create($p_filelist)
+ {
+ return $this->createModify($p_filelist, '', '');
+ }
+ // }}}
+
+ // {{{ add()
+ /**
+ * This method add the files / directories that are listed in $p_filelist in
+ * the archive. If the archive does not exist it is created.
+ * The method return false and a PEAR error text.
+ * The files and directories listed are only added at the end of the archive,
+ * even if a file with the same name is already archived.
+ * See also createModify() method for more details.
+ *
+ * @param array $p_filelist An array of filenames and directory names, or a
+ * single string with names separated by a single
+ * blank space.
+ *
+ * @return true on success, false on error.
+ * @see createModify()
+ * @access public
+ */
+ function add($p_filelist)
+ {
+ return $this->addModify($p_filelist, '', '');
+ }
+ // }}}
+
+ // {{{ extract()
+ function extract($p_path='', $p_preserve=false)
+ {
+ return $this->extractModify($p_path, '', $p_preserve);
+ }
+ // }}}
+
+ // {{{ listContent()
+ function listContent()
+ {
+ $v_list_detail = array();
+
+ if ($this->_openRead()) {
+ if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
+ unset($v_list_detail);
+ $v_list_detail = 0;
+ }
+ $this->_close();
+ }
+
+ return $v_list_detail;
+ }
+ // }}}
+
+ // {{{ createModify()
+ /**
+ * This method creates the archive file and add the files / directories
+ * that are listed in $p_filelist.
+ * If the file already exists and is writable, it is replaced by the
+ * new tar. It is a create and not an add. If the file exists and is
+ * read-only or is a directory it is not replaced. The method return
+ * false and a PEAR error text.
+ * The $p_filelist parameter can be an array of string, each string
+ * representing a filename or a directory name with their path if
+ * needed. It can also be a single string with names separated by a
+ * single blank.
+ * The path indicated in $p_remove_dir will be removed from the
+ * memorized path of each file / directory listed when this path
+ * exists. By default nothing is removed (empty path '')
+ * The path indicated in $p_add_dir will be added at the beginning of
+ * the memorized path of each file / directory listed. However it can
+ * be set to empty ''. The adding of a path is done after the removing
+ * of path.
+ * The path add/remove ability enables the user to prepare an archive
+ * for extraction in a different path than the origin files are.
+ * See also addModify() method for file adding properties.
+ *
+ * @param array $p_filelist An array of filenames and directory names,
+ * or a single string with names separated by
+ * a single blank space.
+ * @param string $p_add_dir A string which contains a path to be added
+ * to the memorized path of each element in
+ * the list.
+ * @param string $p_remove_dir A string which contains a path to be
+ * removed from the memorized path of each
+ * element in the list, when relevant.
+ *
+ * @return boolean true on success, false on error.
+ * @access public
+ * @see addModify()
+ */
+ function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
+ {
+ $v_result = true;
+
+ if (!$this->_openWrite())
+ return false;
+
+ if ($p_filelist != '') {
+ if (is_array($p_filelist))
+ $v_list = $p_filelist;
+ elseif (is_string($p_filelist))
+ $v_list = explode($this->_separator, $p_filelist);
+ else {
+ $this->_cleanFile();
+ $this->_error('Invalid file list');
+ return false;
+ }
+
+ $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
+ }
+
+ if ($v_result) {
+ $this->_writeFooter();
+ $this->_close();
+ } else
+ $this->_cleanFile();
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ addModify()
+ /**
+ * This method add the files / directories listed in $p_filelist at the
+ * end of the existing archive. If the archive does not yet exists it
+ * is created.
+ * The $p_filelist parameter can be an array of string, each string
+ * representing a filename or a directory name with their path if
+ * needed. It can also be a single string with names separated by a
+ * single blank.
+ * The path indicated in $p_remove_dir will be removed from the
+ * memorized path of each file / directory listed when this path
+ * exists. By default nothing is removed (empty path '')
+ * The path indicated in $p_add_dir will be added at the beginning of
+ * the memorized path of each file / directory listed. However it can
+ * be set to empty ''. The adding of a path is done after the removing
+ * of path.
+ * The path add/remove ability enables the user to prepare an archive
+ * for extraction in a different path than the origin files are.
+ * If a file/dir is already in the archive it will only be added at the
+ * end of the archive. There is no update of the existing archived
+ * file/dir. However while extracting the archive, the last file will
+ * replace the first one. This results in a none optimization of the
+ * archive size.
+ * If a file/dir does not exist the file/dir is ignored. However an
+ * error text is send to PEAR error.
+ * If a file/dir is not readable the file/dir is ignored. However an
+ * error text is send to PEAR error.
+ *
+ * @param array $p_filelist An array of filenames and directory
+ * names, or a single string with names
+ * separated by a single blank space.
+ * @param string $p_add_dir A string which contains a path to be
+ * added to the memorized path of each
+ * element in the list.
+ * @param string $p_remove_dir A string which contains a path to be
+ * removed from the memorized path of
+ * each element in the list, when
+ * relevant.
+ *
+ * @return true on success, false on error.
+ * @access public
+ */
+ function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
+ {
+ $v_result = true;
+
+ if (!$this->_isArchive())
+ $v_result = $this->createModify($p_filelist, $p_add_dir,
+ $p_remove_dir);
+ else {
+ if (is_array($p_filelist))
+ $v_list = $p_filelist;
+ elseif (is_string($p_filelist))
+ $v_list = explode($this->_separator, $p_filelist);
+ else {
+ $this->_error('Invalid file list');
+ return false;
+ }
+
+ $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ addString()
+ /**
+ * This method add a single string as a file at the
+ * end of the existing archive. If the archive does not yet exists it
+ * is created.
+ *
+ * @param string $p_filename A string which contains the full
+ * filename path that will be associated
+ * with the string.
+ * @param string $p_string The content of the file added in
+ * the archive.
+ *
+ * @return true on success, false on error.
+ * @access public
+ */
+ function addString($p_filename, $p_string)
+ {
+ $v_result = true;
+
+ if (!$this->_isArchive()) {
+ if (!$this->_openWrite()) {
+ return false;
+ }
+ $this->_close();
+ }
+
+ if (!$this->_openAppend())
+ return false;
+
+ // Need to check the get back to the temporary file ? ....
+ $v_result = $this->_addString($p_filename, $p_string);
+
+ $this->_writeFooter();
+
+ $this->_close();
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ extractModify()
+ /**
+ * This method extract all the content of the archive in the directory
+ * indicated by $p_path. When relevant the memorized path of the
+ * files/dir can be modified by removing the $p_remove_path path at the
+ * beginning of the file/dir path.
+ * While extracting a file, if the directory path does not exists it is
+ * created.
+ * While extracting a file, if the file already exists it is replaced
+ * without looking for last modification date.
+ * While extracting a file, if the file already exists and is write
+ * protected, the extraction is aborted.
+ * While extracting a file, if a directory with the same name already
+ * exists, the extraction is aborted.
+ * While extracting a directory, if a file with the same name already
+ * exists, the extraction is aborted.
+ * While extracting a file/directory if the destination directory exist
+ * and is write protected, or does not exist but can not be created,
+ * the extraction is aborted.
+ * If after extraction an extracted file does not show the correct
+ * stored file size, the extraction is aborted.
+ * When the extraction is aborted, a PEAR error text is set and false
+ * is returned. However the result can be a partial extraction that may
+ * need to be manually cleaned.
+ *
+ * @param string $p_path The path of the directory where the
+ * files/dir need to by extracted.
+ * @param string $p_remove_path Part of the memorized path that can be
+ * removed if present at the beginning of
+ * the file/dir path.
+ * @param boolean $p_preserve Preserve user/group ownership of files
+ *
+ * @return boolean true on success, false on error.
+ * @access public
+ * @see extractList()
+ */
+ function extractModify($p_path, $p_remove_path, $p_preserve=false)
+ {
+ $v_result = true;
+ $v_list_detail = array();
+
+ if ($v_result = $this->_openRead()) {
+ $v_result = $this->_extractList($p_path, $v_list_detail,
+ "complete", 0, $p_remove_path, $p_preserve);
+ $this->_close();
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ extractInString()
+ /**
+ * This method extract from the archive one file identified by $p_filename.
+ * The return value is a string with the file content, or NULL on error.
+ *
+ * @param string $p_filename The path of the file to extract in a string.
+ *
+ * @return a string with the file content or NULL.
+ * @access public
+ */
+ function extractInString($p_filename)
+ {
+ if ($this->_openRead()) {
+ $v_result = $this->_extractInString($p_filename);
+ $this->_close();
+ } else {
+ $v_result = null;
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ extractList()
+ /**
+ * This method extract from the archive only the files indicated in the
+ * $p_filelist. These files are extracted in the current directory or
+ * in the directory indicated by the optional $p_path parameter.
+ * If indicated the $p_remove_path can be used in the same way as it is
+ * used in extractModify() method.
+ *
+ * @param array $p_filelist An array of filenames and directory names,
+ * or a single string with names separated
+ * by a single blank space.
+ * @param string $p_path The path of the directory where the
+ * files/dir need to by extracted.
+ * @param string $p_remove_path Part of the memorized path that can be
+ * removed if present at the beginning of
+ * the file/dir path.
+ * @param boolean $p_preserve Preserve user/group ownership of files
+ *
+ * @return true on success, false on error.
+ * @access public
+ * @see extractModify()
+ */
+ function extractList($p_filelist, $p_path='', $p_remove_path='', $p_preserve=false)
+ {
+ $v_result = true;
+ $v_list_detail = array();
+
+ if (is_array($p_filelist))
+ $v_list = $p_filelist;
+ elseif (is_string($p_filelist))
+ $v_list = explode($this->_separator, $p_filelist);
+ else {
+ $this->_error('Invalid string list');
+ return false;
+ }
+
+ if ($v_result = $this->_openRead()) {
+ $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
+ $v_list, $p_remove_path, $p_preserve);
+ $this->_close();
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ setAttribute()
+ /**
+ * This method set specific attributes of the archive. It uses a variable
+ * list of parameters, in the format attribute code + attribute values :
+ * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
+ *
+ * @param mixed $argv variable list of attributes and values
+ *
+ * @return true on success, false on error.
+ * @access public
+ */
+ function setAttribute()
+ {
+ $v_result = true;
+
+ // ----- Get the number of variable list of arguments
+ if (($v_size = func_num_args()) == 0) {
+ return true;
+ }
+
+ // ----- Get the arguments
+ $v_att_list = &func_get_args();
+
+ // ----- Read the attributes
+ $i=0;
+ while ($i<$v_size) {
+
+ // ----- Look for next option
+ switch ($v_att_list[$i]) {
+ // ----- Look for options that request a string value
+ case ARCHIVE_TAR_ATT_SEPARATOR :
+ // ----- Check the number of parameters
+ if (($i+1) >= $v_size) {
+ $this->_error('Invalid number of parameters for '
+ .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
+ return false;
+ }
+
+ // ----- Get the value
+ $this->_separator = $v_att_list[$i+1];
+ $i++;
+ break;
+
+ default :
+ $this->_error('Unknow attribute code '.$v_att_list[$i].'');
+ return false;
+ }
+
+ // ----- Next attribute
+ $i++;
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ setIgnoreRegexp()
+ /**
+ * This method sets the regular expression for ignoring files and directories
+ * at import, for example:
+ * $arch->setIgnoreRegexp("#CVS|\.svn#");
+ *
+ * @param string $regexp regular expression defining which files or directories to ignore
+ *
+ * @access public
+ */
+ function setIgnoreRegexp($regexp)
+ {
+ $this->_ignore_regexp = $regexp;
+ }
+ // }}}
+
+ // {{{ setIgnoreList()
+ /**
+ * This method sets the regular expression for ignoring all files and directories
+ * matching the filenames in the array list at import, for example:
+ * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool'));
+ *
+ * @param array $list a list of file or directory names to ignore
+ *
+ * @access public
+ */
+ function setIgnoreList($list)
+ {
+ $regexp = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list);
+ $regexp = '#/'.join('$|/', $list).'#';
+ $this->setIgnoreRegexp($regexp);
+ }
+ // }}}
+
+ // {{{ _error()
+ function _error($p_message)
+ {
+ $this->error_object = $this->raiseError($p_message);
+ }
+ // }}}
+
+ // {{{ _warning()
+ function _warning($p_message)
+ {
+ $this->error_object = $this->raiseError($p_message);
+ }
+ // }}}
+
+ // {{{ _isArchive()
+ function _isArchive($p_filename=null)
+ {
+ if ($p_filename == null) {
+ $p_filename = $this->_tarname;
+ }
+ clearstatcache();
+ return @is_file($p_filename) && !@is_link($p_filename);
+ }
+ // }}}
+
+ // {{{ _openWrite()
+ function _openWrite()
+ {
+ if ($this->_compress_type == 'gz' && function_exists('gzopen'))
+ $this->_file = @gzopen($this->_tarname, "wb9");
+ else if ($this->_compress_type == 'bz2' && function_exists('bzopen'))
+ $this->_file = @bzopen($this->_tarname, "w");
+ else if ($this->_compress_type == 'none')
+ $this->_file = @fopen($this->_tarname, "wb");
+ else
+ $this->_error('Unknown or missing compression type ('
+ .$this->_compress_type.')');
+
+ if ($this->_file == 0) {
+ $this->_error('Unable to open in write mode \''
+ .$this->_tarname.'\'');
+ return false;
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _openRead()
+ function _openRead()
+ {
+ if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
+
+ // ----- Look if a local copy need to be done
+ if ($this->_temp_tarname == '') {
+ $this->_temp_tarname = uniqid('tar').'.tmp';
+ if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
+ $this->_error('Unable to open in read mode \''
+ .$this->_tarname.'\'');
+ $this->_temp_tarname = '';
+ return false;
+ }
+ if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
+ $this->_error('Unable to open in write mode \''
+ .$this->_temp_tarname.'\'');
+ $this->_temp_tarname = '';
+ return false;
+ }
+ while ($v_data = @fread($v_file_from, 1024))
+ @fwrite($v_file_to, $v_data);
+ @fclose($v_file_from);
+ @fclose($v_file_to);
+ }
+
+ // ----- File to open if the local copy
+ $v_filename = $this->_temp_tarname;
+
+ } else
+ // ----- File to open if the normal Tar file
+ $v_filename = $this->_tarname;
+
+ if ($this->_compress_type == 'gz')
+ $this->_file = @gzopen($v_filename, "rb");
+ else if ($this->_compress_type == 'bz2')
+ $this->_file = @bzopen($v_filename, "r");
+ else if ($this->_compress_type == 'none')
+ $this->_file = @fopen($v_filename, "rb");
+ else
+ $this->_error('Unknown or missing compression type ('
+ .$this->_compress_type.')');
+
+ if ($this->_file == 0) {
+ $this->_error('Unable to open in read mode \''.$v_filename.'\'');
+ return false;
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _openReadWrite()
+ function _openReadWrite()
+ {
+ if ($this->_compress_type == 'gz')
+ $this->_file = @gzopen($this->_tarname, "r+b");
+ else if ($this->_compress_type == 'bz2') {
+ $this->_error('Unable to open bz2 in read/write mode \''
+ .$this->_tarname.'\' (limitation of bz2 extension)');
+ return false;
+ } else if ($this->_compress_type == 'none')
+ $this->_file = @fopen($this->_tarname, "r+b");
+ else
+ $this->_error('Unknown or missing compression type ('
+ .$this->_compress_type.')');
+
+ if ($this->_file == 0) {
+ $this->_error('Unable to open in read/write mode \''
+ .$this->_tarname.'\'');
+ return false;
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _close()
+ function _close()
+ {
+ //if (isset($this->_file)) {
+ if (is_resource($this->_file)) {
+ if ($this->_compress_type == 'gz')
+ @gzclose($this->_file);
+ else if ($this->_compress_type == 'bz2')
+ @bzclose($this->_file);
+ else if ($this->_compress_type == 'none')
+ @fclose($this->_file);
+ else
+ $this->_error('Unknown or missing compression type ('
+ .$this->_compress_type.')');
+
+ $this->_file = 0;
+ }
+
+ // ----- Look if a local copy need to be erase
+ // Note that it might be interesting to keep the url for a time : ToDo
+ if ($this->_temp_tarname != '') {
+ @unlink($this->_temp_tarname);
+ $this->_temp_tarname = '';
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _cleanFile()
+ function _cleanFile()
+ {
+ $this->_close();
+
+ // ----- Look for a local copy
+ if ($this->_temp_tarname != '') {
+ // ----- Remove the local copy but not the remote tarname
+ @unlink($this->_temp_tarname);
+ $this->_temp_tarname = '';
+ } else {
+ // ----- Remove the local tarname file
+ @unlink($this->_tarname);
+ }
+ $this->_tarname = '';
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _writeBlock()
+ function _writeBlock($p_binary_data, $p_len=null)
+ {
+ if (is_resource($this->_file)) {
+ if ($p_len === null) {
+ if ($this->_compress_type == 'gz')
+ @gzputs($this->_file, $p_binary_data);
+ else if ($this->_compress_type == 'bz2')
+ @bzwrite($this->_file, $p_binary_data);
+ else if ($this->_compress_type == 'none')
+ @fputs($this->_file, $p_binary_data);
+ else
+ $this->_error('Unknown or missing compression type ('
+ .$this->_compress_type.')');
+ } else {
+ if ($this->_compress_type == 'gz')
+ @gzputs($this->_file, $p_binary_data, $p_len);
+ else if ($this->_compress_type == 'bz2')
+ @bzwrite($this->_file, $p_binary_data, $p_len);
+ else if ($this->_compress_type == 'none')
+ @fputs($this->_file, $p_binary_data, $p_len);
+ else
+ $this->_error('Unknown or missing compression type ('
+ .$this->_compress_type.')');
+
+ }
+ }
+ return true;
+ }
+ // }}}
+
+ // {{{ _readBlock()
+ function _readBlock()
+ {
+ $v_block = null;
+ if (is_resource($this->_file)) {
+ if ($this->_compress_type == 'gz')
+ $v_block = @gzread($this->_file, 512);
+ else if ($this->_compress_type == 'bz2')
+ $v_block = @bzread($this->_file, 512);
+ else if ($this->_compress_type == 'none')
+ $v_block = @fread($this->_file, 512);
+ else
+ $this->_error('Unknown or missing compression type ('
+ .$this->_compress_type.')');
+ }
+ return $v_block;
+ }
+ // }}}
+
+ // {{{ _jumpBlock()
+ function _jumpBlock($p_len=null)
+ {
+ if (is_resource($this->_file)) {
+ if ($p_len === null)
+ $p_len = 1;
+
+ if ($this->_compress_type == 'gz') {
+ @gzseek($this->_file, gztell($this->_file)+($p_len*512));
+ }
+ else if ($this->_compress_type == 'bz2') {
+ // ----- Replace missing bztell() and bzseek()
+ for ($i=0; $i<$p_len; $i++)
+ $this->_readBlock();
+ } else if ($this->_compress_type == 'none')
+ @fseek($this->_file, $p_len*512, SEEK_CUR);
+ else
+ $this->_error('Unknown or missing compression type ('
+ .$this->_compress_type.')');
+
+ }
+ return true;
+ }
+ // }}}
+
+ // {{{ _writeFooter()
+ function _writeFooter()
+ {
+ if (is_resource($this->_file)) {
+ // ----- Write the last 0 filled block for end of archive
+ $v_binary_data = pack('a1024', '');
+ $this->_writeBlock($v_binary_data);
+ }
+ return true;
+ }
+ // }}}
+
+ // {{{ _addList()
+ function _addList($p_list, $p_add_dir, $p_remove_dir)
+ {
+ $v_result=true;
+ $v_header = array();
+
+ // ----- Remove potential windows directory separator
+ $p_add_dir = $this->_translateWinPath($p_add_dir);
+ $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
+
+ if (!$this->_file) {
+ $this->_error('Invalid file descriptor');
+ return false;
+ }
+
+ if (sizeof($p_list) == 0)
+ return true;
+
+ foreach ($p_list as $v_filename) {
+ if (!$v_result) {
+ break;
+ }
+
+ // ----- Skip the current tar name
+ if ($v_filename == $this->_tarname)
+ continue;
+
+ if ($v_filename == '')
+ continue;
+
+ // ----- ignore files and directories matching the ignore regular expression
+ if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/'.$v_filename)) {
+ $this->_warning("File '$v_filename' ignored");
+ continue;
+ }
+
+ if (!file_exists($v_filename) && !is_link($v_filename)) {
+ $this->_warning("File '$v_filename' does not exist");
+ continue;
+ }
+
+ // ----- Add the file or directory header
+ if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
+ return false;
+
+ if (@is_dir($v_filename) && !@is_link($v_filename)) {
+ if (!($p_hdir = opendir($v_filename))) {
+ $this->_warning("Directory '$v_filename' can not be read");
+ continue;
+ }
+ while (false !== ($p_hitem = readdir($p_hdir))) {
+ if (($p_hitem != '.') && ($p_hitem != '..')) {
+ if ($v_filename != ".")
+ $p_temp_list[0] = $v_filename.'/'.$p_hitem;
+ else
+ $p_temp_list[0] = $p_hitem;
+
+ $v_result = $this->_addList($p_temp_list,
+ $p_add_dir,
+ $p_remove_dir);
+ }
+ }
+
+ unset($p_temp_list);
+ unset($p_hdir);
+ unset($p_hitem);
+ }
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ _addFile()
+ function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir,$v_stored_filename=null)
+ {
+ if (!$this->_file) {
+ $this->_error('Invalid file descriptor');
+ return false;
+ }
+
+ if ($p_filename == '') {
+ $this->_error('Invalid file name');
+ return false;
+ }
+ if(is_null($v_stored_filename)){
+
+ // ----- Calculate the stored filename
+ $p_filename = $this->_translateWinPath($p_filename, false);
+ $v_stored_filename = $p_filename;
+ if (strcmp($p_filename, $p_remove_dir) == 0) {
+ return true;
+ }
+ if ($p_remove_dir != '') {
+ if (substr($p_remove_dir, -1) != '/')
+ $p_remove_dir .= '/';
+
+ if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
+ $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
+ }
+ $v_stored_filename = $this->_translateWinPath($v_stored_filename);
+ if ($p_add_dir != '') {
+ if (substr($p_add_dir, -1) == '/')
+ $v_stored_filename = $p_add_dir.$v_stored_filename;
+ else
+ $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
+ }
+
+ $v_stored_filename = $this->_pathReduction($v_stored_filename);
+ }
+
+ if ($this->_isArchive($p_filename)) {
+ if (($v_file = @fopen($p_filename, "rb")) == 0) {
+ $this->_warning("Unable to open file '".$p_filename
+ ."' in binary read mode");
+ return true;
+ }
+
+ if (!$this->_writeHeader($p_filename, $v_stored_filename))
+ return false;
+
+ while (($v_buffer = fread($v_file, 512)) != '') {
+ $v_binary_data = pack("a512", "$v_buffer");
+ $this->_writeBlock($v_binary_data);
+ }
+
+ fclose($v_file);
+
+ } else {
+ // ----- Only header for dir
+ if (!$this->_writeHeader($p_filename, $v_stored_filename))
+ return false;
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _addString()
+ function _addString($p_filename, $p_string)
+ {
+ if (!$this->_file) {
+ $this->_error('Invalid file descriptor');
+ return false;
+ }
+
+ if ($p_filename == '') {
+ $this->_error('Invalid file name');
+ return false;
+ }
+
+ // ----- Calculate the stored filename
+ $p_filename = $this->_translateWinPath($p_filename, false);;
+
+ if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
+ time(), 384, "", 0, 0))
+ return false;
+
+ $i=0;
+ while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
+ $v_binary_data = pack("a512", $v_buffer);
+ $this->_writeBlock($v_binary_data);
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _writeHeader()
+ function _writeHeader($p_filename, $p_stored_filename)
+ {
+ if ($p_stored_filename == '')
+ $p_stored_filename = $p_filename;
+ $v_reduce_filename = $this->_pathReduction($p_stored_filename);
+
+ if (strlen($v_reduce_filename) > 99) {
+ if (!$this->_writeLongHeader($v_reduce_filename))
+ return false;
+ }
+
+ $v_info = lstat($p_filename);
+ $v_uid = sprintf("%07s", DecOct($v_info[4]));
+ $v_gid = sprintf("%07s", DecOct($v_info[5]));
+ $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
+
+ $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
+
+ $v_linkname = '';
+
+ if (@is_link($p_filename)) {
+ $v_typeflag = '2';
+ $v_linkname = readlink($p_filename);
+ $v_size = sprintf("%011s", DecOct(0));
+ } elseif (@is_dir($p_filename)) {
+ $v_typeflag = "5";
+ $v_size = sprintf("%011s", DecOct(0));
+ } else {
+ $v_typeflag = '0';
+ clearstatcache();
+ $v_size = sprintf("%011s", DecOct($v_info['size']));
+ }
+
+ $v_magic = 'ustar ';
+
+ $v_version = ' ';
+
+ if (function_exists('posix_getpwuid'))
+ {
+ $userinfo = posix_getpwuid($v_info[4]);
+ $groupinfo = posix_getgrgid($v_info[5]);
+
+ $v_uname = $userinfo['name'];
+ $v_gname = $groupinfo['name'];
+ }
+ else
+ {
+ $v_uname = '';
+ $v_gname = '';
+ }
+
+ $v_devmajor = '';
+
+ $v_devminor = '';
+
+ $v_prefix = '';
+
+ $v_binary_data_first = pack("a100a8a8a8a12a12",
+ $v_reduce_filename, $v_perms, $v_uid,
+ $v_gid, $v_size, $v_mtime);
+ $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
+ $v_typeflag, $v_linkname, $v_magic,
+ $v_version, $v_uname, $v_gname,
+ $v_devmajor, $v_devminor, $v_prefix, '');
+
+ // ----- Calculate the checksum
+ $v_checksum = 0;
+ // ..... First part of the header
+ for ($i=0; $i<148; $i++)
+ $v_checksum += ord(substr($v_binary_data_first,$i,1));
+ // ..... Ignore the checksum value and replace it by ' ' (space)
+ for ($i=148; $i<156; $i++)
+ $v_checksum += ord(' ');
+ // ..... Last part of the header
+ for ($i=156, $j=0; $i<512; $i++, $j++)
+ $v_checksum += ord(substr($v_binary_data_last,$j,1));
+
+ // ----- Write the first 148 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_first, 148);
+
+ // ----- Write the calculated checksum
+ $v_checksum = sprintf("%06s ", DecOct($v_checksum));
+ $v_binary_data = pack("a8", $v_checksum);
+ $this->_writeBlock($v_binary_data, 8);
+
+ // ----- Write the last 356 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_last, 356);
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _writeHeaderBlock()
+ function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
+ $p_type='', $p_uid=0, $p_gid=0)
+ {
+ $p_filename = $this->_pathReduction($p_filename);
+
+ if (strlen($p_filename) > 99) {
+ if (!$this->_writeLongHeader($p_filename))
+ return false;
+ }
+
+ if ($p_type == "5") {
+ $v_size = sprintf("%011s", DecOct(0));
+ } else {
+ $v_size = sprintf("%011s", DecOct($p_size));
+ }
+
+ $v_uid = sprintf("%07s", DecOct($p_uid));
+ $v_gid = sprintf("%07s", DecOct($p_gid));
+ $v_perms = sprintf("%07s", DecOct($p_perms & 000777));
+
+ $v_mtime = sprintf("%11s", DecOct($p_mtime));
+
+ $v_linkname = '';
+
+ $v_magic = 'ustar ';
+
+ $v_version = ' ';
+
+ if (function_exists('posix_getpwuid'))
+ {
+ $userinfo = posix_getpwuid($p_uid);
+ $groupinfo = posix_getgrgid($p_gid);
+
+ $v_uname = $userinfo['name'];
+ $v_gname = $groupinfo['name'];
+ }
+ else
+ {
+ $v_uname = '';
+ $v_gname = '';
+ }
+
+ $v_devmajor = '';
+
+ $v_devminor = '';
+
+ $v_prefix = '';
+
+ $v_binary_data_first = pack("a100a8a8a8a12A12",
+ $p_filename, $v_perms, $v_uid, $v_gid,
+ $v_size, $v_mtime);
+ $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
+ $p_type, $v_linkname, $v_magic,
+ $v_version, $v_uname, $v_gname,
+ $v_devmajor, $v_devminor, $v_prefix, '');
+
+ // ----- Calculate the checksum
+ $v_checksum = 0;
+ // ..... First part of the header
+ for ($i=0; $i<148; $i++)
+ $v_checksum += ord(substr($v_binary_data_first,$i,1));
+ // ..... Ignore the checksum value and replace it by ' ' (space)
+ for ($i=148; $i<156; $i++)
+ $v_checksum += ord(' ');
+ // ..... Last part of the header
+ for ($i=156, $j=0; $i<512; $i++, $j++)
+ $v_checksum += ord(substr($v_binary_data_last,$j,1));
+
+ // ----- Write the first 148 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_first, 148);
+
+ // ----- Write the calculated checksum
+ $v_checksum = sprintf("%06s ", DecOct($v_checksum));
+ $v_binary_data = pack("a8", $v_checksum);
+ $this->_writeBlock($v_binary_data, 8);
+
+ // ----- Write the last 356 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_last, 356);
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _writeLongHeader()
+ function _writeLongHeader($p_filename)
+ {
+ $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
+
+ $v_typeflag = 'L';
+
+ $v_linkname = '';
+
+ $v_magic = '';
+
+ $v_version = '';
+
+ $v_uname = '';
+
+ $v_gname = '';
+
+ $v_devmajor = '';
+
+ $v_devminor = '';
+
+ $v_prefix = '';
+
+ $v_binary_data_first = pack("a100a8a8a8a12a12",
+ '././@LongLink', 0, 0, 0, $v_size, 0);
+ $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
+ $v_typeflag, $v_linkname, $v_magic,
+ $v_version, $v_uname, $v_gname,
+ $v_devmajor, $v_devminor, $v_prefix, '');
+
+ // ----- Calculate the checksum
+ $v_checksum = 0;
+ // ..... First part of the header
+ for ($i=0; $i<148; $i++)
+ $v_checksum += ord(substr($v_binary_data_first,$i,1));
+ // ..... Ignore the checksum value and replace it by ' ' (space)
+ for ($i=148; $i<156; $i++)
+ $v_checksum += ord(' ');
+ // ..... Last part of the header
+ for ($i=156, $j=0; $i<512; $i++, $j++)
+ $v_checksum += ord(substr($v_binary_data_last,$j,1));
+
+ // ----- Write the first 148 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_first, 148);
+
+ // ----- Write the calculated checksum
+ $v_checksum = sprintf("%06s ", DecOct($v_checksum));
+ $v_binary_data = pack("a8", $v_checksum);
+ $this->_writeBlock($v_binary_data, 8);
+
+ // ----- Write the last 356 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_last, 356);
+
+ // ----- Write the filename as content of the block
+ $i=0;
+ while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
+ $v_binary_data = pack("a512", "$v_buffer");
+ $this->_writeBlock($v_binary_data);
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _readHeader()
+ function _readHeader($v_binary_data, &$v_header)
+ {
+ if (strlen($v_binary_data)==0) {
+ $v_header['filename'] = '';
+ return true;
+ }
+
+ if (strlen($v_binary_data) != 512) {
+ $v_header['filename'] = '';
+ $this->_error('Invalid block size : '.strlen($v_binary_data));
+ return false;
+ }
+
+ if (!is_array($v_header)) {
+ $v_header = array();
+ }
+ // ----- Calculate the checksum
+ $v_checksum = 0;
+ // ..... First part of the header
+ for ($i=0; $i<148; $i++)
+ $v_checksum+=ord(substr($v_binary_data,$i,1));
+ // ..... Ignore the checksum value and replace it by ' ' (space)
+ for ($i=148; $i<156; $i++)
+ $v_checksum += ord(' ');
+ // ..... Last part of the header
+ for ($i=156; $i<512; $i++)
+ $v_checksum+=ord(substr($v_binary_data,$i,1));
+
+ $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
+ "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
+ "a32uname/a32gname/a8devmajor/a8devminor/a131prefix",
+ $v_binary_data);
+
+ if (strlen($v_data["prefix"]) > 0) {
+ $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
+ }
+
+ // ----- Extract the checksum
+ $v_header['checksum'] = OctDec(trim($v_data['checksum']));
+ if ($v_header['checksum'] != $v_checksum) {
+ $v_header['filename'] = '';
+
+ // ----- Look for last block (empty block)
+ if (($v_checksum == 256) && ($v_header['checksum'] == 0))
+ return true;
+
+ $this->_error('Invalid checksum for file "'.$v_data['filename']
+ .'" : '.$v_checksum.' calculated, '
+ .$v_header['checksum'].' expected');
+ return false;
+ }
+
+ // ----- Extract the properties
+ $v_header['filename'] = $v_data['filename'];
+ if ($this->_maliciousFilename($v_header['filename'])) {
+ $this->_error('Malicious .tar detected, file "' . $v_header['filename'] .
+ '" will not install in desired directory tree');
+ return false;
+ }
+ $v_header['mode'] = OctDec(trim($v_data['mode']));
+ $v_header['uid'] = OctDec(trim($v_data['uid']));
+ $v_header['gid'] = OctDec(trim($v_data['gid']));
+ $v_header['size'] = OctDec(trim($v_data['size']));
+ $v_header['mtime'] = OctDec(trim($v_data['mtime']));
+ if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
+ $v_header['size'] = 0;
+ }
+ $v_header['link'] = trim($v_data['link']);
+ /* ----- All these fields are removed form the header because
+ they do not carry interesting info
+ $v_header[magic] = trim($v_data[magic]);
+ $v_header[version] = trim($v_data[version]);
+ $v_header[uname] = trim($v_data[uname]);
+ $v_header[gname] = trim($v_data[gname]);
+ $v_header[devmajor] = trim($v_data[devmajor]);
+ $v_header[devminor] = trim($v_data[devminor]);
+ */
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _maliciousFilename()
+ /**
+ * Detect and report a malicious file name
+ *
+ * @param string $file
+ *
+ * @return bool
+ * @access private
+ */
+ function _maliciousFilename($file)
+ {
+ if (strpos($file, '/../') !== false) {
+ return true;
+ }
+ if (strpos($file, '../') === 0) {
+ return true;
+ }
+ return false;
+ }
+ // }}}
+
+ // {{{ _readLongHeader()
+ function _readLongHeader(&$v_header)
+ {
+ $v_filename = '';
+ $n = floor($v_header['size']/512);
+ for ($i=0; $i<$n; $i++) {
+ $v_content = $this->_readBlock();
+ $v_filename .= $v_content;
+ }
+ if (($v_header['size'] % 512) != 0) {
+ $v_content = $this->_readBlock();
+ $v_filename .= trim($v_content);
+ }
+
+ // ----- Read the next header
+ $v_binary_data = $this->_readBlock();
+
+ if (!$this->_readHeader($v_binary_data, $v_header))
+ return false;
+
+ $v_filename = trim($v_filename);
+ $v_header['filename'] = $v_filename;
+ if ($this->_maliciousFilename($v_filename)) {
+ $this->_error('Malicious .tar detected, file "' . $v_filename .
+ '" will not install in desired directory tree');
+ return false;
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _extractInString()
+ /**
+ * This method extract from the archive one file identified by $p_filename.
+ * The return value is a string with the file content, or null on error.
+ *
+ * @param string $p_filename The path of the file to extract in a string.
+ *
+ * @return a string with the file content or null.
+ * @access private
+ */
+ function _extractInString($p_filename)
+ {
+ $v_result_str = "";
+
+ While (strlen($v_binary_data = $this->_readBlock()) != 0)
+ {
+ if (!$this->_readHeader($v_binary_data, $v_header))
+ return null;
+
+ if ($v_header['filename'] == '')
+ continue;
+
+ // ----- Look for long filename
+ if ($v_header['typeflag'] == 'L') {
+ if (!$this->_readLongHeader($v_header))
+ return null;
+ }
+
+ if ($v_header['filename'] == $p_filename) {
+ if ($v_header['typeflag'] == "5") {
+ $this->_error('Unable to extract in string a directory '
+ .'entry {'.$v_header['filename'].'}');
+ return null;
+ } else {
+ $n = floor($v_header['size']/512);
+ for ($i=0; $i<$n; $i++) {
+ $v_result_str .= $this->_readBlock();
+ }
+ if (($v_header['size'] % 512) != 0) {
+ $v_content = $this->_readBlock();
+ $v_result_str .= substr($v_content, 0,
+ ($v_header['size'] % 512));
+ }
+ return $v_result_str;
+ }
+ } else {
+ $this->_jumpBlock(ceil(($v_header['size']/512)));
+ }
+ }
+
+ return null;
+ }
+ // }}}
+
+ // {{{ _extractList()
+ function _extractList($p_path, &$p_list_detail, $p_mode,
+ $p_file_list, $p_remove_path, $p_preserve=false)
+ {
+ $v_result=true;
+ $v_nb = 0;
+ $v_extract_all = true;
+ $v_listing = false;
+
+ $p_path = $this->_translateWinPath($p_path, false);
+ if ($p_path == '' || (substr($p_path, 0, 1) != '/'
+ && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
+ $p_path = "./".$p_path;
+ }
+ $p_remove_path = $this->_translateWinPath($p_remove_path);
+
+ // ----- Look for path to remove format (should end by /)
+ if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
+ $p_remove_path .= '/';
+ $p_remove_path_size = strlen($p_remove_path);
+
+ switch ($p_mode) {
+ case "complete" :
+ $v_extract_all = true;
+ $v_listing = false;
+ break;
+ case "partial" :
+ $v_extract_all = false;
+ $v_listing = false;
+ break;
+ case "list" :
+ $v_extract_all = false;
+ $v_listing = true;
+ break;
+ default :
+ $this->_error('Invalid extract mode ('.$p_mode.')');
+ return false;
+ }
+
+ clearstatcache();
+
+ while (strlen($v_binary_data = $this->_readBlock()) != 0)
+ {
+ $v_extract_file = FALSE;
+ $v_extraction_stopped = 0;
+
+ if (!$this->_readHeader($v_binary_data, $v_header))
+ return false;
+
+ if ($v_header['filename'] == '') {
+ continue;
+ }
+
+ // ----- Look for long filename
+ if ($v_header['typeflag'] == 'L') {
+ if (!$this->_readLongHeader($v_header))
+ return false;
+ }
+
+ if ((!$v_extract_all) && (is_array($p_file_list))) {
+ // ----- By default no unzip if the file is not found
+ $v_extract_file = false;
+
+ for ($i=0; $i<sizeof($p_file_list); $i++) {
+ // ----- Look if it is a directory
+ if (substr($p_file_list[$i], -1) == '/') {
+ // ----- Look if the directory is in the filename path
+ if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
+ && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
+ == $p_file_list[$i])) {
+ $v_extract_file = true;
+ break;
+ }
+ }
+
+ // ----- It is a file, so compare the file names
+ elseif ($p_file_list[$i] == $v_header['filename']) {
+ $v_extract_file = true;
+ break;
+ }
+ }
+ } else {
+ $v_extract_file = true;
+ }
+
+ // ----- Look if this file need to be extracted
+ if (($v_extract_file) && (!$v_listing))
+ {
+ if (($p_remove_path != '')
+ && (substr($v_header['filename'], 0, $p_remove_path_size)
+ == $p_remove_path))
+ $v_header['filename'] = substr($v_header['filename'],
+ $p_remove_path_size);
+ if (($p_path != './') && ($p_path != '/')) {
+ while (substr($p_path, -1) == '/')
+ $p_path = substr($p_path, 0, strlen($p_path)-1);
+
+ if (substr($v_header['filename'], 0, 1) == '/')
+ $v_header['filename'] = $p_path.$v_header['filename'];
+ else
+ $v_header['filename'] = $p_path.'/'.$v_header['filename'];
+ }
+ if (file_exists($v_header['filename'])) {
+ if ( (@is_dir($v_header['filename']))
+ && ($v_header['typeflag'] == '')) {
+ $this->_error('File '.$v_header['filename']
+ .' already exists as a directory');
+ return false;
+ }
+ if ( ($this->_isArchive($v_header['filename']))
+ && ($v_header['typeflag'] == "5")) {
+ $this->_error('Directory '.$v_header['filename']
+ .' already exists as a file');
+ return false;
+ }
+ if (!is_writeable($v_header['filename'])) {
+ $this->_error('File '.$v_header['filename']
+ .' already exists and is write protected');
+ return false;
+ }
+ if (filemtime($v_header['filename']) > $v_header['mtime']) {
+ // To be completed : An error or silent no replace ?
+ }
+ }
+
+ // ----- Check the directory availability and create it if necessary
+ elseif (($v_result
+ = $this->_dirCheck(($v_header['typeflag'] == "5"
+ ?$v_header['filename']
+ :dirname($v_header['filename'])))) != 1) {
+ $this->_error('Unable to create path for '.$v_header['filename']);
+ return false;
+ }
+
+ if ($v_extract_file) {
+ if ($v_header['typeflag'] == "5") {
+ if (!@file_exists($v_header['filename'])) {
+ if (!@mkdir($v_header['filename'], 0777)) {
+ $this->_error('Unable to create directory {'
+ .$v_header['filename'].'}');
+ return false;
+ }
+ }
+ } elseif ($v_header['typeflag'] == "2") {
+ if (@file_exists($v_header['filename'])) {
+ @unlink($v_header['filename']);
+ }
+ if (!@symlink($v_header['link'], $v_header['filename'])) {
+ $this->_error('Unable to extract symbolic link {'
+ .$v_header['filename'].'}');
+ return false;
+ }
+ } else {
+ if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
+ $this->_error('Error while opening {'.$v_header['filename']
+ .'} in write binary mode');
+ return false;
+ } else {
+ $n = floor($v_header['size']/512);
+ for ($i=0; $i<$n; $i++) {
+ $v_content = $this->_readBlock();
+ fwrite($v_dest_file, $v_content, 512);
+ }
+ if (($v_header['size'] % 512) != 0) {
+ $v_content = $this->_readBlock();
+ fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
+ }
+
+ @fclose($v_dest_file);
+
+ if ($p_preserve) {
+ @chown($v_header['filename'], $v_header['uid']);
+ @chgrp($v_header['filename'], $v_header['gid']);
+ }
+
+ // ----- Change the file mode, mtime
+ @touch($v_header['filename'], $v_header['mtime']);
+ if ($v_header['mode'] & 0111) {
+ // make file executable, obey umask
+ $mode = fileperms($v_header['filename']) | (~umask() & 0111);
+ @chmod($v_header['filename'], $mode);
+ }
+ }
+
+ // ----- Check the file size
+ clearstatcache();
+ if (!is_file($v_header['filename'])) {
+ $this->_error('Extracted file '.$v_header['filename']
+ .'does not exist. Archive may be corrupted.');
+ return false;
+ }
+
+ $filesize = filesize($v_header['filename']);
+ if ($filesize != $v_header['size']) {
+ $this->_error('Extracted file '.$v_header['filename']
+ .' does not have the correct file size \''
+ .$filesize
+ .'\' ('.$v_header['size']
+ .' expected). Archive may be corrupted.');
+ return false;
+ }
+ }
+ } else {
+ $this->_jumpBlock(ceil(($v_header['size']/512)));
+ }
+ } else {
+ $this->_jumpBlock(ceil(($v_header['size']/512)));
+ }
+
+ /* TBC : Seems to be unused ...
+ if ($this->_compress)
+ $v_end_of_file = @gzeof($this->_file);
+ else
+ $v_end_of_file = @feof($this->_file);
+ */
+
+ if ($v_listing || $v_extract_file || $v_extraction_stopped) {
+ // ----- Log extracted files
+ if (($v_file_dir = dirname($v_header['filename']))
+ == $v_header['filename'])
+ $v_file_dir = '';
+ if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
+ $v_file_dir = '/';
+
+ $p_list_detail[$v_nb++] = $v_header;
+ if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
+ return true;
+ }
+ }
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _openAppend()
+ function _openAppend()
+ {
+ if (filesize($this->_tarname) == 0)
+ return $this->_openWrite();
+
+ if ($this->_compress) {
+ $this->_close();
+
+ if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
+ $this->_error('Error while renaming \''.$this->_tarname
+ .'\' to temporary file \''.$this->_tarname
+ .'.tmp\'');
+ return false;
+ }
+
+ if ($this->_compress_type == 'gz')
+ $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
+ elseif ($this->_compress_type == 'bz2')
+ $v_temp_tar = @bzopen($this->_tarname.".tmp", "r");
+
+ if ($v_temp_tar == 0) {
+ $this->_error('Unable to open file \''.$this->_tarname
+ .'.tmp\' in binary read mode');
+ @rename($this->_tarname.".tmp", $this->_tarname);
+ return false;
+ }
+
+ if (!$this->_openWrite()) {
+ @rename($this->_tarname.".tmp", $this->_tarname);
+ return false;
+ }
+
+ if ($this->_compress_type == 'gz') {
+ while (!@gzeof($v_temp_tar)) {
+ $v_buffer = @gzread($v_temp_tar, 512);
+ if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
+ // do not copy end blocks, we will re-make them
+ // after appending
+ continue;
+ }
+ $v_binary_data = pack("a512", $v_buffer);
+ $this->_writeBlock($v_binary_data);
+ }
+
+ @gzclose($v_temp_tar);
+ }
+ elseif ($this->_compress_type == 'bz2') {
+ while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
+ if ($v_buffer == ARCHIVE_TAR_END_BLOCK) {
+ continue;
+ }
+ $v_binary_data = pack("a512", $v_buffer);
+ $this->_writeBlock($v_binary_data);
+ }
+
+ @bzclose($v_temp_tar);
+ }
+
+ if (!@unlink($this->_tarname.".tmp")) {
+ $this->_error('Error while deleting temporary file \''
+ .$this->_tarname.'.tmp\'');
+ }
+
+ } else {
+ // ----- For not compressed tar, just add files before the last
+ // one or two 512 bytes block
+ if (!$this->_openReadWrite())
+ return false;
+
+ clearstatcache();
+ $v_size = filesize($this->_tarname);
+
+ // We might have zero, one or two end blocks.
+ // The standard is two, but we should try to handle
+ // other cases.
+ fseek($this->_file, $v_size - 1024);
+ if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
+ fseek($this->_file, $v_size - 1024);
+ }
+ elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
+ fseek($this->_file, $v_size - 512);
+ }
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _append()
+ function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
+ {
+ if (!$this->_openAppend())
+ return false;
+
+ if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
+ $this->_writeFooter();
+
+ $this->_close();
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _dirCheck()
+
+ /**
+ * Check if a directory exists and create it (including parent
+ * dirs) if not.
+ *
+ * @param string $p_dir directory to check
+ *
+ * @return bool true if the directory exists or was created
+ */
+ function _dirCheck($p_dir)
+ {
+ clearstatcache();
+ if ((@is_dir($p_dir)) || ($p_dir == ''))
+ return true;
+
+ $p_parent_dir = dirname($p_dir);
+
+ if (($p_parent_dir != $p_dir) &&
+ ($p_parent_dir != '') &&
+ (!$this->_dirCheck($p_parent_dir)))
+ return false;
+
+ if (!@mkdir($p_dir, 0777)) {
+ $this->_error("Unable to create directory '$p_dir'");
+ return false;
+ }
+
+ return true;
+ }
+
+ // }}}
+
+ // {{{ _pathReduction()
+
+ /**
+ * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
+ * rand emove double slashes.
+ *
+ * @param string $p_dir path to reduce
+ *
+ * @return string reduced path
+ *
+ * @access private
+ *
+ */
+ function _pathReduction($p_dir)
+ {
+ $v_result = '';
+
+ // ----- Look for not empty path
+ if ($p_dir != '') {
+ // ----- Explode path by directory names
+ $v_list = explode('/', $p_dir);
+
+ // ----- Study directories from last to first
+ for ($i=sizeof($v_list)-1; $i>=0; $i--) {
+ // ----- Look for current path
+ if ($v_list[$i] == ".") {
+ // ----- Ignore this directory
+ // Should be the first $i=0, but no check is done
+ }
+ else if ($v_list[$i] == "..") {
+ // ----- Ignore it and ignore the $i-1
+ $i--;
+ }
+ else if ( ($v_list[$i] == '')
+ && ($i!=(sizeof($v_list)-1))
+ && ($i!=0)) {
+ // ----- Ignore only the double '//' in path,
+ // but not the first and last /
+ } else {
+ $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'
+ .$v_result:'');
+ }
+ }
+ }
+
+ if (defined('OS_WINDOWS') && OS_WINDOWS) {
+ $v_result = strtr($v_result, '\\', '/');
+ }
+
+ return $v_result;
+ }
+
+ // }}}
+
+ // {{{ _translateWinPath()
+ function _translateWinPath($p_path, $p_remove_disk_letter=true)
+ {
+ if (defined('OS_WINDOWS') && OS_WINDOWS) {
+ // ----- Look for potential disk letter
+ if ( ($p_remove_disk_letter)
+ && (($v_position = strpos($p_path, ':')) != false)) {
+ $p_path = substr($p_path, $v_position+1);
+ }
+ // ----- Change potential windows directory separator
+ if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
+ $p_path = strtr($p_path, '\\', '/');
+ }
+ }
+ return $p_path;
+ }
+ // }}}
+
+}
+?>
diff --git a/AUTHORS b/AUTHORS
index 02439884d2f..f618df9311d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -5,6 +5,13 @@ ownCloud is written by:
Jan-Christoph Borchardt
Michael Gapczynski
Arthur Schiwon
+ Bart Visscher
+ Georg Ehrke
+ Brice Maron
+ Tom Needham
+ Marvin Thomas Rabe
+ Florian Pritz
+ Bartek Przybylski
With help from many libraries and frameworks including:
diff --git a/README b/README
index 4d4be2728e1..77379a46456 100644
--- a/README
+++ b/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
+
diff --git a/apps/bookmarks/bookmarksHelper.php b/apps/bookmarks/bookmarksHelper.php
index 8def7401e2f..7ada69014fb 100644
--- a/apps/bookmarks/bookmarksHelper.php
+++ b/apps/bookmarks/bookmarksHelper.php
@@ -71,7 +71,7 @@ function getURLMetadata($url) {
return $metadata;
}
-function addBookmark($url, $title='', $tags='') {
+function addBookmark($url, $title, $tags='') {
$CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" );
if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ){
$_ut = "strftime('%s','now')";
@@ -93,6 +93,11 @@ function addBookmark($url, $title='', $tags='') {
$title = $metadata['title'];
}
+ if(empty($title)) {
+ $l = new OC_L10N('bookmarks');
+ $title = $l->t('unnamed');
+ }
+
$params=array(
htmlspecialchars_decode($url),
htmlspecialchars_decode($title),
diff --git a/apps/bookmarks/js/bookmarks.js b/apps/bookmarks/js/bookmarks.js
index fa5adde2545..9502af0a00d 100644
--- a/apps/bookmarks/js/bookmarks.js
+++ b/apps/bookmarks/js/bookmarks.js
@@ -9,9 +9,7 @@ $(document).ready(function() {
fillWindow($('.bookmarks_list'));
});
$(window).resize();
- $($('.bookmarks_list')).scroll(updateOnBottom);
-
- $('.bookmarks_list').empty();
+ $('.bookmarks_list').scroll(updateOnBottom).empty().width($('#content').width());
getBookmarks();
});
@@ -145,7 +143,7 @@ function updateBookmarksList(bookmark) {
'<p class="bookmark_title">'+
'<a href="' + encodeEntities(bookmark.url) + '" target="_blank" class="bookmark_link">' + encodeEntities(bookmark.title) + '</a>' +
'</p>' +
- '<p class="bookmark_url">' + encodeEntities(bookmark.url) + '</p>' +
+ '<p class="bookmark_url"><a href="' + encodeEntities(bookmark.url) + '" target="_blank" class="bookmark_link">' + encodeEntities(bookmark.url) + '</a></p>' +
'</div>'
);
if(taglist != '') {
diff --git a/apps/calendar/ajax/events.php b/apps/calendar/ajax/events.php
index 922df90b76b..c62f93c540e 100755
--- a/apps/calendar/ajax/events.php
+++ b/apps/calendar/ajax/events.php
@@ -8,11 +8,13 @@
require_once ('../../../lib/base.php');
require_once('when/When.php');
-
+$l = new OC_L10N('calendar');
+$unnamed = $l->t('unnamed');
function create_return_event($event, $vevent){
$return_event = array();
+ global $unnamed;
$return_event['id'] = (int)$event['id'];
- $return_event['title'] = htmlspecialchars($event['summary']);
+ $return_event['title'] = htmlspecialchars(($event['summary']!=NULL || $event['summary'] != '')?$event['summary']: $unnamed);
$return_event['description'] = isset($vevent->DESCRIPTION)?htmlspecialchars($vevent->DESCRIPTION->value):'';
$last_modified = $vevent->__get('LAST-MODIFIED');
if ($last_modified){
diff --git a/apps/calendar/ajax/import/import.php b/apps/calendar/ajax/import/import.php
index c0797f6e425..d0bdab4f0d5 100644
--- a/apps/calendar/ajax/import/import.php
+++ b/apps/calendar/ajax/import/import.php
@@ -10,7 +10,7 @@ ob_start();
require_once('../../../../lib/base.php');
OC_JSON::checkLoggedIn();
OC_Util::checkAppEnabled('calendar');
-$nl = "\n";
+$nl = "\n\r";
$progressfile = OC::$APPSROOT . '/apps/calendar/import_tmp/' . md5(session_id()) . '.txt';
if(is_writable('import_tmp/')){
$progressfopen = fopen($progressfile, 'w');
diff --git a/apps/calendar/export.php b/apps/calendar/export.php
index 9886ad8e8cc..2736eec96c2 100644
--- a/apps/calendar/export.php
+++ b/apps/calendar/export.php
@@ -11,7 +11,7 @@ OC_Util::checkLoggedIn();
OC_Util::checkAppEnabled('calendar');
$cal = isset($_GET['calid']) ? $_GET['calid'] : NULL;
$event = isset($_GET['eventid']) ? $_GET['eventid'] : NULL;
-$nl = "\n";
+$nl = "\n\r";
if(isset($cal)){
$calendar = OC_Calendar_App::getCalendar($cal);
$calobjects = OC_Calendar_Object::all($cal);
diff --git a/apps/contacts/ajax/addcontact.php b/apps/contacts/ajax/addcontact.php
index 839a3919981..947b35bab51 100644
--- a/apps/contacts/ajax/addcontact.php
+++ b/apps/contacts/ajax/addcontact.php
@@ -39,7 +39,10 @@ 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']);
diff --git a/apps/contacts/ajax/addproperty.php b/apps/contacts/ajax/addproperty.php
index e1a31292837..d2c0291e8c6 100644
--- a/apps/contacts/ajax/addproperty.php
+++ b/apps/contacts/ajax/addproperty.php
@@ -122,13 +122,4 @@ if(!OC_Contacts_VCard::edit($id,$vcard)) {
exit();
}
-$adr_types = OC_Contacts_App::getTypesOfProperty('ADR');
-$phone_types = OC_Contacts_App::getTypesOfProperty('TEL');
-
-$tmpl = new OC_Template('contacts','part.property');
-$tmpl->assign('adr_types',$adr_types);
-$tmpl->assign('phone_types',$phone_types);
-$tmpl->assign('property',OC_Contacts_VCard::structureProperty($property,$line));
-$page = $tmpl->fetchPage();
-
-OC_JSON::success(array('data' => array( 'checksum' => $checksum, 'page' => $page )));
+OC_JSON::success(array('data' => array( 'checksum' => $checksum )));
diff --git a/apps/contacts/ajax/deletecard.php b/apps/contacts/ajax/deletecard.php
index e26dfd6ebfe..5675aef5f15 100644
--- a/apps/contacts/ajax/deletecard.php
+++ b/apps/contacts/ajax/deletecard.php
@@ -19,6 +19,11 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
+function bailOut($msg) {
+ OC_JSON::error(array('data' => array('message' => $msg)));
+ OC_Log::write('contacts','ajax/saveproperty.php: '.$msg, OC_Log::DEBUG);
+ exit();
+}
// Init owncloud
require_once('../../../lib/base.php');
@@ -27,7 +32,10 @@ require_once('../../../lib/base.php');
OC_JSON::checkLoggedIn();
OC_JSON::checkAppEnabled('contacts');
-$id = $_GET['id'];
+$id = isset($_GET['id'])?$_GET['id']:null;
+if(!$id) {
+ bailOut(OC_Contacts_App::$l10n->t('id is not set.'));
+}
$card = OC_Contacts_App::getContactObject( $id );
OC_Contacts_VCard::delete($id);
diff --git a/apps/contacts/ajax/saveproperty.php b/apps/contacts/ajax/saveproperty.php
index 95a7ac20193..924d873652c 100644
--- a/apps/contacts/ajax/saveproperty.php
+++ b/apps/contacts/ajax/saveproperty.php
@@ -35,9 +35,9 @@ function bailOut($msg) {
function debug($msg) {
OC_Log::write('contacts','ajax/saveproperty.php: '.$msg, OC_Log::DEBUG);
}
-foreach ($_POST as $key=>$element) {
- debug('_POST: '.$key.'=>'.print_r($element, true));
-}
+// foreach ($_POST as $key=>$element) {
+// debug('_POST: '.$key.'=>'.print_r($element, true));
+// }
$id = isset($_POST['id'])?$_POST['id']:null;
$name = isset($_POST['name'])?$_POST['name']:null;
@@ -83,16 +83,38 @@ if($element != $name) {
bailOut(OC_Contacts_App::$l10n->t('Something went FUBAR. ').$name.' != '.$element);
}
+/* preprocessing value */
switch($element) {
case 'BDAY':
$date = New DateTime($value);
//$vcard->setDateTime('BDAY', $date, Sabre_VObject_Element_DateTime::DATE);
$value = $date->format(DateTime::ATOM);
+ break;
case 'FN':
if(!$value) {
// create a method thats returns an alternative for FN.
//$value = getOtherValue();
}
+ break;
+ case 'CATEGORIES':
+ /* multi autocomplete triggers an save with empty value */
+ if (!$value) {
+ $value = $vcard->getAsString('CATEGORIES');
+ }
+ 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':
@@ -102,7 +124,6 @@ switch($element) {
$vcard->setString($name, $value);
break;
case 'EMAIL':
- $value = strtolower($value);
case 'TEL':
case 'ADR': // should I delete the property if empty or throw an error?
debug('Setting element: (EMAIL/TEL/ADR)'.$element);
diff --git a/apps/contacts/appinfo/migrate.php b/apps/contacts/appinfo/migrate.php
new file mode 100644
index 00000000000..a6c6bc20fa4
--- /dev/null
+++ b/apps/contacts/appinfo/migrate.php
@@ -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' ); \ No newline at end of file
diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css
index 7c36a511d6e..4e7d8c285a3 100644
--- a/apps/contacts/css/contacts.css
+++ b/apps/contacts/css/contacts.css
@@ -10,69 +10,26 @@
#contacts_propertymenu_button { position:absolute;top:15px;right:150px; background:url('../../../core/img/actions/add.svg') no-repeat center; }
#contacts_propertymenu { background-color: #fff; position:absolute;top:40px;right:150px; overflow:hidden; text-overflow:ellipsis; /*border: thin solid #1d2d44;*/ -moz-box-shadow:0 0 10px #000; -webkit-box-shadow:0 0 10px #000; box-shadow:0 0 10px #000; -moz-border-radius:0.5em; -webkit-border-radius:0.5em; border-radius:0.5em; -moz-border-radius:0.5em; -webkit-border-radius:0.5em; border-radius:0.5em; }
#contacts_propertymenu li { display: block; font-weight: bold; height: 20px; width: 100px; }
-/*#contacts_propertymenu li:first-child { border-top: thin solid #1d2d44; -moz-border-radius-topleft:0.5em; -webkit-border-top-left-radius:0.5em; border-top-left-radius:0.5em; -moz-border-radius-topright:0.5em; -webkit-border-top-right-radius:0.5em; border-top-right-radius:0.5em; }
-#contacts_propertymenu li:last-child { border-bottom: thin solid #1d2d44; -moz-border-radius-bottomleft:0.5em; -webkit-border-bottom-left-radius:0.5em; border-bottom-left-radius:0.5em; -moz-border-radius-bottomright:0.5em; -webkit-border-bottom-right-radius:0.5em; border-bottom-right-radius:0.5em; }*/
#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;*/ }
+#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"],input[type="email"],input[type="tel"],input[type="date"], select { background-color: #fefefe; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; }
#card input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active,input[type="email"]:hover,input[type="tel"]:hover,input[type="date"]:hover,input[type="date"],input[type="date"]:hover,input[type="date"]:active,input[type="date"]:active,input[type="date"]:active,input[type="email"]:active,input[type="tel"]:active, select:hover, select:focus, select:active { border: 0 !important; -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; background:#fff; color:#333; border:1px solid #ddd; -moz-box-shadow:0 1px 1px #fff, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; float: left; }
-input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid { background-color: #ffc0c0 !important; }
-/*input[type="text"]:valid,input[type="email"]:valid,input[type="tel"]:valid,input[type="date"]:valid { background-color: #b1d28f !important; }*/
-dl.form
-{
- width: 100%;
- float: left;
- clear: right;
- margin: 0;
- padding: 0;
-}
-
-.form dt
-{
- display: table-cell;
- clear: left;
- float: left;
- width: 7em;
- /*overflow: hidden;*/
- 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 dd
-{
- display: table-cell;
- clear: right;
- float: left;
- margin: 0;
- padding: 0px;
- white-space: nowrap;
- vertical-align: text-bottom;
- /*min-width: 20em;*/
- /*background-color: yellow;*/
-}
+input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid { color: #bbb !important; }
+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; 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; }
.loading { background: url('../../../core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; }
-
-/*.add { cursor: pointer; width: 25px; height: 25px; margin: 0px; float: right; position:relative; content: "\+"; font-weight: bold; color: #666; font-size: large; bottom: 0px; right: 0px; clear: both; text-align: center; vertical-align: bottom; display: none; }*/
-
+.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; }
@@ -82,77 +39,23 @@ dl.form
/*.globe { background:url('../img/globe.svg') no-repeat center; }*/
.globe { background:url('../../../core/img/actions/public.svg') no-repeat center; }
-#messagebox_msg { font-weight: bold; font-size: 1.2em; }
-
-/* Name editor */
-#edit_name_dialog {
- /*width: 25em;*/
- padding:0;
-}
-#edit_name_dialog > input {
- width: 15em;
-}
-/* Address editor */
-#edit_address_dialog {
- /*width: 30em;*/
-}
-#edit_address_dialog > input {
- width: 15em;
-}
+#edit_name_dialog { padding:0; }
+#edit_name_dialog > input { width: 15em; }
+#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;
-}
-
-.contactpart legend {
- /*background: #fff;
- font-weight: bold;
- left: 1em;
- border: thin solid gray;
- -webkit-border-radius: 0.5em;
- -moz-border-radius: 0.5em;
- border-radius: 0.5em;
- padding: 3px;*/
-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;
-}
-/*#contacts_details_photo {
- cursor: pointer;
- z-index:1;
- margin: auto;
-}
-*/
-#cropbox {
- margin: auto;
-}
+#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;*/ }
-/* Photo editor */
-/*#contacts_details_photo_wrapper {
- z-index: 1000;
-}*/
-#contacts_details_photo {
- border-radius: 0.5em;
- border: thin solid #bbb;
- padding: 0.5em;
- margin: 1em 1em 1em 7em;
- cursor: pointer;
- /*background: #f8f8f8;*/
- background: url(../../../core/img/loading.gif) no-repeat center center;
- clear: right;
-}
+.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: 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; }
@@ -168,17 +71,12 @@ dl.addresscard dd > ul { margin: 0.3em; padding: 0.3em; }
#adr_zipcode {}
#adr_country {}
-.delimiter {
- height: 10px;
- clear: both;
-}
-
-/*input[type="text"] { float: left; max-width: 15em; }
-input[type="radio"] { float: left; -khtml-appearance: none; width: 20px; height: 20px; vertical-align: middle; }*/
#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; }
diff --git a/apps/contacts/img/person_large.png b/apps/contacts/img/person_large.png
index f57511e1000..4edba0c5489 100644
--- a/apps/contacts/img/person_large.png
+++ b/apps/contacts/img/person_large.png
Binary files differ
diff --git a/apps/contacts/index.php b/apps/contacts/index.php
index 04f6c65a145..076b10c2ee2 100644
--- a/apps/contacts/index.php
+++ b/apps/contacts/index.php
@@ -34,7 +34,28 @@ 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) {
+ bailOut(OC_Contacts_App::$l10n->t('No contacts found.'));
+ }
+
+ $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 +82,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);
diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js
index 18214cb1cc5..cc4da5837e0 100644
--- a/apps/contacts/js/contacts.js
+++ b/apps/contacts/js/contacts.js
@@ -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');
@@ -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'
});
@@ -200,6 +196,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:{
@@ -213,19 +212,27 @@ Contacts={
honpre:'',
honsuf:'',
data:undefined,
- update:function() {
+ update:function(id) {
// Make sure proper DOM is loaded.
- console.log('Card.update(), #n: ' + $('#n').length);
+ var newid;
+ console.log('Card.update(), id: ' + id);
console.log('Card.update(), #contacts: ' + $('#contacts li').length);
- if($('#n').length == 0 && $('#contacts li').length > 0) {
+ if(id == undefined) {
+ newid = $('#contacts li:first-child').data('id');
+ } else {
+ newid = id;
+ }
+ if($('#contacts li').length > 0) {
$.getJSON(OC.filePath('contacts', 'ajax', 'loadcard.php'),{},function(jsondata){
if(jsondata.status == 'success'){
$('#rightcontent').html(jsondata.data.page);
Contacts.UI.loadHandlers();
if($('#contacts li').length > 0) {
- var firstid = $('#contacts li:first-child').data('id');
- console.log('trying to load: ' + firstid);
- $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':firstid},function(jsondata){
+ //var newid = $('#contacts li:first-child').data('id');
+ //$('#contacts li:first-child').addClass('active');
+ $('#leftcontent li[data-id="'+newid+'"]').addClass('active');
+ console.log('trying to load: ' + newid);
+ $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':newid},function(jsondata){
if(jsondata.status == 'success'){
Contacts.UI.Card.loadContact(jsondata.data);
} else{
@@ -259,7 +266,7 @@ Contacts={
import: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);
$.post(OC.filePath('contacts', 'ajax', 'addcontact.php'), { n: n, fn: fn, aid: aid },
function(jsondata) {
@@ -283,7 +290,15 @@ Contacts={
if(!added) {
$('#leftcontent ul').append(item);
}
-
+ if(isnew) {
+ Contacts.UI.Card.addProperty('EMAIL');
+ Contacts.UI.Card.addProperty('TEL');
+ Contacts.UI.Card.addProperty('BDAY');
+ Contacts.UI.Card.addProperty('NICKNAME');
+ Contacts.UI.Card.addProperty('ORG');
+ Contacts.UI.Card.addProperty('CATEGORIES');
+ $('#fn').focus();
+ }
}
else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
@@ -300,41 +315,55 @@ Contacts={
}
});
},
- delete: function() {
+ delete:function() {
$('#contacts_deletecard').tipsy('hide');
- $.getJSON('ajax/deletecard.php',{'id':this.id},function(jsondata){
- if(jsondata.status == 'success'){
- $('#leftcontent [data-id="'+jsondata.data.id+'"]').remove();
- $('#rightcontent').data('id','');
- //$('#rightcontent').empty();
- this.id = this.fn = this.fullname = this.shortname = this.famname = this.givname = this.addname = this.honpre = this.honsuf = '';
- this.data = undefined;
- // Load first in list.
- if($('#contacts li').length > 0) {
- Contacts.UI.Card.update();
- } else {
- // load intro page
- $.getJSON('ajax/loadintro.php',{},function(jsondata){
- if(jsondata.status == 'success'){
- id = '';
- $('#rightcontent').data('id','');
- $('#rightcontent').html(jsondata.data.page);
+ OC.dialogs.confirm(t('contacts', 'Are you sure you want to delete this contact?'), t('contacts', 'Warning'), function(answer) {
+ if(answer == true) {
+ $.getJSON('ajax/deletecard.php',{'id':Contacts.UI.Card.id},function(jsondata){
+ if(jsondata.status == 'success'){
+ var newid = '';
+ var curlistitem = $('#leftcontent [data-id="'+jsondata.data.id+'"]');
+ var newlistitem = curlistitem.prev();
+ console.log('Previous: ' + newlistitem);
+ if(newlistitem == undefined) {
+ newlistitem = curlistitem.next();
}
- else{
- OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+ curlistitem.remove();
+ if(newlistitem != undefined) {
+ newid = newlistitem.data('id');
}
- });
- }
- }
- else{
- OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
- //alert(jsondata.data.message);
+ $('#rightcontent').data('id',newid);
+ //$('#rightcontent').empty();
+ this.id = this.fn = this.fullname = this.shortname = this.famname = this.givname = this.addname = this.honpre = this.honsuf = '';
+ this.data = undefined;
+ // Load first in list.
+ if($('#contacts li').length > 0) {
+ Contacts.UI.Card.update(newid);
+ } else {
+ // load intro page
+ $.getJSON('ajax/loadintro.php',{},function(jsondata){
+ if(jsondata.status == 'success'){
+ id = '';
+ $('#rightcontent').data('id','');
+ $('#rightcontent').html(jsondata.data.page);
+ }
+ else{
+ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+ }
+ });
+ }
+ }
+ else{
+ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+ //alert(jsondata.data.message);
+ }
+ });
}
});
return false;
},
loadContact:function(jsondata){
- $('#contact_communication').hide();
+ //$('#contact_communication').hide();
this.data = jsondata;
this.id = this.data.id;
$('#rightcontent').data('id',this.id);
@@ -346,7 +375,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']);
@@ -354,7 +382,7 @@ Contacts={
} else {
$('#note').data('checksum', '');
$('#note').find('textarea').val('');
- $('#note').hide();
+ //$('#note').hide();
}
},
loadSingleProperties:function() {
@@ -499,17 +527,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.
@@ -605,45 +634,48 @@ 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':
this.loadPhoto(true);
$('#file_upload_form').show();
$('#contacts_propertymenu a[data-type="'+type+'"]').parent().hide();
+ $('#file_upload_start').trigger('click');
break;
case 'NOTE':
$('#note').show();
$('#contacts_propertymenu a[data-type="'+type+'"]').parent().hide();
+ $('#note').find('textarea').focus();
break;
case 'EMAIL':
if($('#emaillist>li').length == 1) {
$('#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':
case 'BDAY':
case 'CATEGORIES':
$('dl dt[data-element="'+type+'"],dd[data-element="'+type+'"]').show();
+ $('dd[data-element="'+type+'"]').find('input').focus();
$('#contacts_propertymenu a[data-type="'+type+'"]').parent().hide();
break;
}
@@ -657,8 +689,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);
@@ -667,9 +699,16 @@ Contacts={
console.log('NOTE or PHOTO');
Contacts.UI.propertyContainerFor(obj).hide();
Contacts.UI.propertyContainerFor(obj).data('checksum', '');
+ if(proptype == 'PHOTO') {
+ console.log('Delete PHOTO');
+ Contacts.UI.Contacts.refreshThumbnail(Contacts.UI.Card.id);
+ } else if(proptype == 'NOTE') {
+ $('#note').find('textarea').val('');
+ }
} else {
$('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide();
$('dl dd[data-element="'+proptype+'"]').data('checksum', '');
+ $('dl dd[data-element="'+proptype+'"]').find('input').val('');
}
$('#contacts_propertymenu a[data-type="'+proptype+'"]').parent().show();
Contacts.UI.loading(obj, false);
@@ -686,8 +725,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);
@@ -859,7 +898,7 @@ Contacts={
if(isnew) {
container.remove();
}
- Contacts.UI.showHideContactInfo();
+ //Contacts.UI.showHideContactInfo();
}
},
close : function(event, ui) {
@@ -868,11 +907,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);
@@ -941,7 +1064,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);
@@ -954,11 +1077,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);
@@ -990,7 +1113,7 @@ Contacts={
OC.dialogs.alert(response.data.message, t('contacts', 'Error'));
}
});
- $('#contacts [data-id="'+this.id+'"]').find('a').css('background','url(thumbnail.php?id='+this.id+'&refresh=1'+Math.random()+') no-repeat');
+ Contacts.UI.Contacts.refreshThumbnail(this.id);
},
addMail:function() {
//alert('addMail');
@@ -1192,6 +1315,9 @@ Contacts={
$(this).find('a').css('background','url(thumbnail.php?id='+$(this).data('id')+') no-repeat');
}
});
+ },
+ refreshThumbnail:function(id){
+ $('#contacts [data-id="'+id+'"]').find('a').css('background','url(thumbnail.php?id='+id+'&refresh=1'+Math.random()+') no-repeat');
}
}
}
@@ -1219,6 +1345,7 @@ $(document).ready(function(){
*/
$('#leftcontent li').live('click',function(){
var id = $(this).data('id');
+ $(this).addClass('active');
var oldid = $('#rightcontent').data('id');
if(oldid != 0){
$('#leftcontent li[data-id="'+oldid+'"]').removeClass('active');
@@ -1394,7 +1521,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();
});
});
diff --git a/apps/contacts/js/jquery.combobox.js b/apps/contacts/js/jquery.combobox.js
index 6da4ecb5147..f46d7c14c18 100644
--- a/apps/contacts/js/jquery.combobox.js
+++ b/apps/contacts/js/jquery.combobox.js
@@ -72,17 +72,10 @@
.appendTo( ul );
};
- this.button = $( "<button type='button'>&nbsp;</button>" )
+ /*this.button = $( "<button type='button'>&nbsp;</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 );
diff --git a/apps/contacts/js/jquery.multi-autocomplete.js b/apps/contacts/js/jquery.multi-autocomplete.js
index 1c923a2543d..e1c5d63dc5f 100644
--- a/apps/contacts/js/jquery.multi-autocomplete.js
+++ b/apps/contacts/js/jquery.multi-autocomplete.js
@@ -5,14 +5,25 @@
(function( $ ) {
$.widget('ui.multiple_autocomplete', {
_create: function() {
+ var self = this;
function split( val ) {
return val.split( /,\s*/ );
}
function extractLast( term ) {
return split( term ).pop();
}
+ function showOptions() {
+ if(!self.element.autocomplete('widget').is(':visible') && self.element.val().trim() == '') {
+ self.element.autocomplete('search', '');
+ }
+ }
//console.log('_create: ' + this.options['id']);
- var self = this;
+ this.element.bind('click', function( event ) {
+ showOptions();
+ });
+ this.element.bind('input', function( event ) {
+ showOptions();
+ });
this.element.bind('blur', function( event ) {
var tmp = self.element.val().trim();
if(tmp[tmp.length-1] == ',') {
@@ -51,7 +62,7 @@
return false;
}
});
- this.button = $( "<button type='button'>&nbsp;</button>" )
+ /*this.button = $( "<button type='button'>&nbsp;</button>" )
.attr( "tabIndex", -1 )
.attr( "title", "Show All Items" )
.insertAfter( this.element )
@@ -75,7 +86,7 @@
// pass empty string as value to search for, displaying all results
self.element.autocomplete( "search", "" );
self.element.focus();
- });
+ });*/
},
});
})( jQuery );
diff --git a/apps/contacts/lib/vcard.php b/apps/contacts/lib/vcard.php
index 3736f18c647..15a6176d40c 100644
--- a/apps/contacts/lib/vcard.php
+++ b/apps/contacts/lib/vcard.php
@@ -228,7 +228,7 @@ class OC_Contacts_VCard{
* @param string $uri the uri of the card, default based on the UID
* @return insertid on success or null if no card.
*/
- public static function add($aid, $card, $uri=null){
+ public static function add($aid, OC_VObject $card, $uri=null){
if(is_null($card)){
OC_Log::write('contacts','OC_Contacts_VCard::add. No vCard supplied', OC_Log::ERROR);
return null;
@@ -267,7 +267,7 @@ class OC_Contacts_VCard{
*/
public static function addFromDAVData($id,$uri,$data){
$card = OC_VObject::parse($data);
- return self::add($id, $data, $uri);
+ return self::add($id, $card, $uri);
}
/**
diff --git a/apps/contacts/templates/index.php b/apps/contacts/templates/index.php
index af159ce9c60..d68dd68f605 100644
--- a/apps/contacts/templates/index.php
+++ b/apps/contacts/templates/index.php
@@ -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>
diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php
index cb1e080a40a..961ce693e65 100644
--- a/apps/contacts/templates/part.contact.php
+++ b/apps/contacts/templates/part.contact.php
@@ -21,12 +21,12 @@ $id = isset($_['id']) ? $_['id'] : '';
<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,62 +37,52 @@ $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>
+
+ <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="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="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="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
- <!-- dt id="categories_label" data-element="CATEGORIES"><label for="categories"><?php echo $l->t('Categories'); ?></label></dt>
- <dd class="propertycontainer" id="categories_value" data-element="CATEGORIES">
- <select class="contacts_property" multiple="multiple" id="categories" name="value[]">
- <?php echo html_select_options($_['categories'], array(), array('combine'=>true)) ?>
- </select>
- <a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"></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="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 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="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="mail" title="<?php echo $l->t('Mail to address'); ?>"></a>
- <a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="<?php echo $l->t('Delete email address'); ?>"></a></span></li>
+ <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'); ?>" />
@@ -100,29 +90,35 @@ $id = isset($_['id']) ? $_['id'] : '';
<select multiple="multiple" name="parameters[TYPE][]">
<?php echo html_select_options($_['phone_types'], array()) ?>
</select>
- <a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="<?php echo $l->t('Delete phone number'); ?>"></a></li>
+ <a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="<?php echo $l->t('Delete phone number'); ?>"></a></li>
</ul><!-- a id="add_phone" class="add" title="<?php echo $l->t('Add phone number'); ?>"></a -->
</div> <!-- Phone numbers -->
<!-- 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="" />
<input type="hidden" class="adr_type contacts_property" name="parameters[TYPE][]" value="" />
- <span class="adr_type_label"></span><a class="globe" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.searchOSM(this);" title="<?php echo $l->t('View on map'); ?>"></a><a class="edit" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.Card.editAddress(this, false);" title="<?php echo $l->t('Edit address details'); ?>"></a><a class="delete" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="Delete address"></a>
+ <span class="adr_type_label"></span><a class="action globe" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.searchOSM(this);" title="<?php echo $l->t('View on map'); ?>"></a><a class="action edit" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.Card.editAddress(this, false);" title="<?php echo $l->t('Edit address details'); ?>"></a><a class="action delete" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="Delete address"></a>
</dt><dd><ul class="addresslist"></ul></dd></dl>
</fieldset>
</div>
</div> <!-- Addresses -->
+ </form>
</div>
- <!-- a id="add_address" onclick="Contacts.UI.Card.editAddress('new', true)" class="add" title="<?php echo $l->t('Add address'); ?>"></a -->
- </div>
+ <div id="contact_note" class="contactsection">
+ <form class="float" method="post">
+ <fieldset id="note" class="formfloat propertycontainer contactpart" 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="40" rows="10" placeholder="<?php echo $l->t('Notes'); ?>"></textarea>
+ </fieldset>
</form>
-</div>
+ </div> <!-- contact_note -->
+</div> <!-- card -->
<div id="edit_photo_dialog" title="Edit photo">
<div id="edit_photo_dialog_img"></div>
</div>
@@ -131,6 +127,7 @@ $(document).ready(function(){
if('<?php echo $id; ?>'!='') {
$.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':'<?php echo $id; ?>'},function(jsondata){
if(jsondata.status == 'success'){
+ $('#leftcontent li[data-id="<?php echo $id; ?>"]').addClass('active');
Contacts.UI.Card.loadContact(jsondata.data);
}
else{
diff --git a/apps/contacts/templates/settings.php b/apps/contacts/templates/settings.php
index f56de0ec8b0..d77c4d38027 100644
--- a/apps/contacts/templates/settings.php
+++ b/apps/contacts/templates/settings.php
@@ -9,4 +9,5 @@
<dd><code><?php echo OC_Helper::linkToAbsolute('contacts', 'carddav.php'); ?>/principals/<?php echo OC_User::getUser(); ?></code>/</dd>
</dl>
</fieldset>
+ Powered by <a href="http://geonames.org/" target="_blank">geonames.org webservice</a>
</form>
diff --git a/apps/files_archive/appinfo/app.php b/apps/files_archive/appinfo/app.php
index 693c28d98a0..67809ec980a 100644
--- a/apps/files_archive/appinfo/app.php
+++ b/apps/files_archive/appinfo/app.php
@@ -7,7 +7,8 @@
*/
OC::$CLASSPATH['OC_Archive'] = 'apps/files_archive/lib/archive.php';
-foreach(array('ZIP') as $type){
+OC::$CLASSPATH['Archive_Tar'] = '3rdparty/Archive/Tar.php';
+foreach(array('ZIP','TAR') as $type){
OC::$CLASSPATH['OC_Archive_'.$type] = 'apps/files_archive/lib/'.strtolower($type).'.php';
}
diff --git a/apps/files_archive/appinfo/info.xml b/apps/files_archive/appinfo/info.xml
index df767d39f6b..236b5a64b05 100644
--- a/apps/files_archive/appinfo/info.xml
+++ b/apps/files_archive/appinfo/info.xml
@@ -7,4 +7,7 @@
<licence>AGPL</licence>
<author>Robin Appelman</author>
<require>3</require>
+ <types>
+ <filesystem/>
+ </types>
</info>
diff --git a/apps/files_archive/js/archive.js b/apps/files_archive/js/archive.js
index ec316c7bf2c..531eb61c01a 100644
--- a/apps/files_archive/js/archive.js
+++ b/apps/files_archive/js/archive.js
@@ -11,5 +11,9 @@ $(document).ready(function() {
window.location='index.php?dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename);
});
FileActions.setDefault('application/zip','Open');
+ FileActions.register('application/x-gzip','Open','',function(filename){
+ window.location='index.php?dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename);
+ });
+ FileActions.setDefault('application/x-gzip','Open');
}
});
diff --git a/apps/files_archive/lib/archive.php b/apps/files_archive/lib/archive.php
index be89f894fb7..113f92e9604 100644
--- a/apps/files_archive/lib/archive.php
+++ b/apps/files_archive/lib/archive.php
@@ -17,6 +17,15 @@ abstract class OC_Archive{
switch($ext){
case '.zip':
return new OC_Archive_ZIP($path);
+ case '.gz':
+ case '.bz':
+ case '.bz2':
+ if(strpos($path,'.tar.')){
+ return new OC_Archive_TAR($path);
+ }
+ break;
+ case '.tgz':
+ return new OC_Archive_TAR($path);
}
}
@@ -78,6 +87,13 @@ abstract class OC_Archive{
*/
abstract function extractFile($path,$dest);
/**
+ * extract the archive
+ * @param string path
+ * @param string dest
+ * @return bool
+ */
+ abstract function extract($dest);
+ /**
* check if a file or folder exists in the archive
* @param string path
* @return bool
diff --git a/apps/files_archive/lib/storage.php b/apps/files_archive/lib/storage.php
index 72a96ca5a5d..700d9633042 100644
--- a/apps/files_archive/lib/storage.php
+++ b/apps/files_archive/lib/storage.php
@@ -125,7 +125,7 @@ class OC_Filestorage_Archive extends OC_Filestorage_Common{
self::$rootView=new OC_FilesystemView('');
}
self::$enableAutomount=false;//prevent recursion
- $supported=array('zip');
+ $supported=array('zip','tar.gz','tar.bz2','tgz');
foreach($supported as $type){
$ext='.'.$type.'/';
if(($pos=strpos(strtolower($path),$ext))!==false){
@@ -139,4 +139,8 @@ class OC_Filestorage_Archive extends OC_Filestorage_Common{
}
self::$enableAutomount=true;
}
+
+ public function rename($path1,$path2){
+ return $this->archive->rename($path1,$path2);
+ }
}
diff --git a/apps/files_archive/lib/tar.php b/apps/files_archive/lib/tar.php
new file mode 100644
index 00000000000..a5d54004788
--- /dev/null
+++ b/apps/files_archive/lib/tar.php
@@ -0,0 +1,277 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_Archive_TAR extends OC_Archive{
+ const PLAIN=0;
+ const GZIP=1;
+ const BZIP=2;
+
+ /**
+ * @var Archive_Tar tar
+ */
+ private $tar=null;
+ private $path;
+
+ function __construct($source){
+ $types=array(null,'gz','bz');
+ $this->path=$source;
+ $this->tar=new Archive_Tar($source,$types[self::getTarType($source)]);
+ }
+
+ /**
+ * try to detect the type of tar compression
+ * @param string file
+ * @return str
+ */
+ static public function getTarType($file){
+ if(strpos($file,'.')){
+ $extention=substr($file,strrpos($file,'.'));
+ switch($extention){
+ case 'gz':
+ case 'tgz':
+ return self::GZIP;
+ case 'bz':
+ case 'bz2':
+ return self::BZIP;
+ default:
+ return self::PLAIN;
+ }
+ }else{
+ return self::PLAIN;
+ }
+ }
+
+ /**
+ * add an empty folder to the archive
+ * @param string path
+ * @return bool
+ */
+ function addFolder($path){
+ $tmpBase=get_temp_dir().'/';
+ if(substr($path,-1,1)!='/'){
+ $path.='/';
+ }
+ if($this->fileExists($path)){
+ return false;
+ }
+ mkdir($tmpBase.$path);
+ $result=$this->tar->addModify(array($tmpBase.$path),'',$tmpBase);
+ rmdir($tmpBase.$path);
+ return $result;
+ }
+ /**
+ * add a file to the archive
+ * @param string path
+ * @param string source either a local file or string data
+ * @return bool
+ */
+ function addFile($path,$source=''){
+ if($this->fileExists($path)){
+ $this->remove($path);
+ }
+ if(file_exists($source)){
+ $header=array();
+ $dummy='';
+ $this->tar->_openAppend();
+ $result=$this->tar->_addfile($source,$header,$dummy,$dummy,$path);
+ }else{
+ $result=$this->tar->addString($path,$source);
+ }
+ return $result;
+ }
+
+ /**
+ * rename a file or folder in the archive
+ * @param string source
+ * @param string dest
+ * @return bool
+ */
+ function rename($source,$dest){
+ //no proper way to delete, rename entire archive, rename file and remake archive
+ $tmp=OC_Helper::tmpFolder();
+ $this->tar->extract($tmp);
+ rename($tmp.$source,$tmp.$dest);
+ $this->tar=null;
+ unlink($this->path);
+ $types=array(null,'gz','bz');
+ $this->tar=new Archive_Tar($this->path,$types[self::getTarType($this->path)]);
+ $this->tar->createModify(array($tmp),'',$tmp.'/');
+ }
+
+ private function getHeader($file){
+ $headers=$this->tar->listContent();
+ foreach($headers as $header){
+ if($file==$header['filename'] or $file.'/'==$header['filename']){
+ return $header;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * get the uncompressed size of a file in the archive
+ * @param string path
+ * @return int
+ */
+ function filesize($path){
+ $stat=$this->getHeader($path);
+ return $stat['size'];
+ }
+ /**
+ * get the last modified time of a file in the archive
+ * @param string path
+ * @return int
+ */
+ function mtime($path){
+ $stat=$this->getHeader($path);
+ return $stat['mtime'];
+ }
+
+ /**
+ * get the files in a folder
+ * @param path
+ * @return array
+ */
+ function getFolder($path){
+ $files=$this->getFiles();
+ $folderContent=array();
+ $pathLength=strlen($path);
+ foreach($files as $file){
+ if(substr($file,0,$pathLength)==$path and $file!=$path){
+ if(strrpos(substr($file,0,-1),'/')<=$pathLength){
+ $folderContent[]=substr($file,$pathLength);
+ }
+ }
+ }
+ return $folderContent;
+ }
+ /**
+ *get all files in the archive
+ * @return array
+ */
+ function getFiles(){
+ $headers=$this->tar->listContent();
+ $files=array();
+ foreach($headers as $header){
+ $files[]=$header['filename'];
+ }
+ return $files;
+ }
+ /**
+ * get the content of a file
+ * @param string path
+ * @return string
+ */
+ function getFile($path){
+ return $this->tar->extractInString($path);
+ }
+ /**
+ * extract a single file from the archive
+ * @param string path
+ * @param string dest
+ * @return bool
+ */
+ function extractFile($path,$dest){
+ $tmp=OC_Helper::tmpFolder();
+ if(!$this->fileExists($path)){
+ return false;
+ }
+ $success=$this->tar->extractList(array($path),$tmp);
+ if($success){
+ rename($tmp.$path,$dest);
+ }
+ OC_Helper::rmdirr($tmp);
+ return $success;
+ }
+ /**
+ * extract the archive
+ * @param string path
+ * @param string dest
+ * @return bool
+ */
+ function extract($dest){
+ return $this->tar->extract($dest);
+ }
+ /**
+ * check if a file or folder exists in the archive
+ * @param string path
+ * @return bool
+ */
+ function fileExists($path){
+ return $this->getHeader($path)!==null;
+ }
+
+ /**
+ * remove a file or folder from the archive
+ * @param string path
+ * @return bool
+ */
+ function remove($path){
+ if(!$this->fileExists($path)){
+ return false;
+ }
+ //no proper way to delete, extract entire archive, delete file and remake archive
+ $tmp=OC_Helper::tmpFolder();
+ $this->tar->extract($tmp);
+ OC_Helper::rmdirr($tmp.$path);
+ $this->tar=null;
+ unlink($this->path);
+ $this->reopen();
+ $this->tar->createModify(array($tmp),'',$tmp);
+ return true;
+ }
+ /**
+ * get a file handler
+ * @param string path
+ * @param string mode
+ * @return resource
+ */
+ function getStream($path,$mode){
+ if(strrpos($path,'.')!==false){
+ $ext=substr($path,strrpos($path,'.'));
+ }else{
+ $ext='';
+ }
+ $tmpFile=OC_Helper::tmpFile($ext);
+ if($this->fileExists($path)){
+ $this->extractFile($path,$tmpFile);
+ }elseif($mode=='r' or $mode=='rb'){
+ return false;
+ }
+ if($mode=='r' or $mode=='rb'){
+ return fopen($tmpFile,$mode);
+ }else{
+ OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack');
+ self::$tempFiles[$tmpFile]=$path;
+ return fopen('close://'.$tmpFile,$mode);
+ }
+ }
+
+ private static $tempFiles=array();
+ /**
+ * write back temporary files
+ */
+ function writeBack($tmpFile){
+ if(isset(self::$tempFiles[$tmpFile])){
+ $this->addFile(self::$tempFiles[$tmpFile],$tmpFile);
+ unlink($tmpFile);
+ }
+ }
+
+ /**
+ * reopen the archive to ensure everything is written
+ */
+ private function reopen(){
+ if($this->tar){
+ $this->tar->_close();
+ $this->tar=null;
+ }
+ $types=array(null,'gz','bz');
+ $this->tar=new Archive_Tar($this->path,$types[self::getTarType($this->path)]);
+ }
+}
diff --git a/apps/files_archive/lib/zip.php b/apps/files_archive/lib/zip.php
index eab101b3a5c..5a5bc766875 100644
--- a/apps/files_archive/lib/zip.php
+++ b/apps/files_archive/lib/zip.php
@@ -11,7 +11,6 @@ class OC_Archive_ZIP extends OC_Archive{
* @var ZipArchive zip
*/
private $zip=null;
- private $contents=array();
private $success=false;
private $path;
@@ -56,7 +55,9 @@ class OC_Archive_ZIP extends OC_Archive{
* @return bool
*/
function rename($source,$dest){
- return $this->zip->renameName($source,$dest);
+ $source=$this->stripPath($source);
+ $dest=$this->stripPath($dest);
+ $this->zip->renameName($source,$dest);
}
/**
* get the uncompressed size of a file in the archive
@@ -99,15 +100,11 @@ class OC_Archive_ZIP extends OC_Archive{
* @return array
*/
function getFiles(){
- if(count($this->contents)){
- return $this->contents;
- }
$fileCount=$this->zip->numFiles;
$files=array();
for($i=0;$i<$fileCount;$i++){
$files[]=$this->zip->getNameIndex($i);
}
- $this->contents=$files;
return $files;
}
/**
@@ -129,12 +126,21 @@ class OC_Archive_ZIP extends OC_Archive{
file_put_contents($dest,$fp);
}
/**
+ * extract the archive
+ * @param string path
+ * @param string dest
+ * @return bool
+ */
+ function extract($dest){
+ return $this->zip->extractTo($dest);
+ }
+ /**
* check if a file or folder exists in the archive
* @param string path
* @return bool
*/
function fileExists($path){
- return $this->zip->locateName($path)!==false;
+ return ($this->zip->locateName($path)!==false) or ($this->zip->locateName($path.'/')!==false);
}
/**
* remove a file or folder from the archive
@@ -142,7 +148,11 @@ class OC_Archive_ZIP extends OC_Archive{
* @return bool
*/
function remove($path){
- return $this->zip->deleteName($path);
+ if($this->fileExists($path.'/')){
+ return $this->zip->deleteName($path.'/');
+ }else{
+ return $this->zip->deleteName($path);
+ }
}
/**
* get a file handler
@@ -179,4 +189,12 @@ class OC_Archive_ZIP extends OC_Archive{
unlink($tmpFile);
}
}
+
+ private function stripPath($path){
+ if(substr($path,0,1)=='/'){
+ return substr($path,1);
+ }else{
+ return $path;
+ }
+ }
}
diff --git a/apps/files_archive/tests/archive.php b/apps/files_archive/tests/archive.php
index 2e26b5e03b5..9e99466a521 100644
--- a/apps/files_archive/tests/archive.php
+++ b/apps/files_archive/tests/archive.php
@@ -27,10 +27,10 @@ abstract class Test_Archive extends UnitTestCase {
$this->instance=$this->getExisting();
$allFiles=$this->instance->getFiles();
$expected=array('lorem.txt','logo-wide.png','dir/','dir/lorem.txt');
- $this->assertEqual(4,count($allFiles));
+ $this->assertEqual(4,count($allFiles),'only found '.count($allFiles).' out of 4 expected files');
foreach($expected as $file){
$this->assertNotIdentical(false,array_search($file,$allFiles),'cant find '.$file.' in archive');
- $this->assertTrue($this->instance->fileExists($file));
+ $this->assertTrue($this->instance->fileExists($file),'file '.$file.' does not exist in archive');
}
$this->assertFalse($this->instance->fileExists('non/existing/file'));
@@ -68,6 +68,7 @@ abstract class Test_Archive extends UnitTestCase {
$this->instance->addFile('lorem.txt',$textFile);
$this->assertEqual(1,count($this->instance->getFiles()));
$this->assertTrue($this->instance->fileExists('lorem.txt'));
+ $this->assertFalse($this->instance->fileExists('lorem.txt/'));
$this->assertEqual(file_get_contents($textFile),$this->instance->getFile('lorem.txt'));
$this->instance->addFile('lorem.txt','foobar');
@@ -94,4 +95,39 @@ abstract class Test_Archive extends UnitTestCase {
$this->assertTrue($this->instance->fileExists('lorem.txt'));
$this->assertEqual(file_get_contents($dir.'/lorem.txt'),$this->instance->getFile('lorem.txt'));
}
+ public function testFolder(){
+ $this->instance=$this->getNew();
+ $this->assertFalse($this->instance->fileExists('/test'));
+ $this->assertFalse($this->instance->fileExists('/test/'));
+ $this->instance->addFolder('/test');
+ $this->assertTrue($this->instance->fileExists('/test'));
+ $this->assertTrue($this->instance->fileExists('/test/'));
+ $this->instance->remove('/test');
+ $this->assertFalse($this->instance->fileExists('/test'));
+ $this->assertFalse($this->instance->fileExists('/test/'));
+ }
+ public function testExtract(){
+ $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+ $this->instance=$this->getExisting();
+ $tmpDir=OC_Helper::tmpFolder();
+ $this->instance->extract($tmpDir);
+ $this->assertEqual(true,file_exists($tmpDir.'lorem.txt'));
+ $this->assertEqual(true,file_exists($tmpDir.'dir/lorem.txt'));
+ $this->assertEqual(true,file_exists($tmpDir.'logo-wide.png'));
+ $this->assertEqual(file_get_contents($dir.'/lorem.txt'),file_get_contents($tmpDir.'lorem.txt'));
+ OC_Helper::rmdirr($tmpDir);
+ }
+ public function testMoveRemove(){
+ $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+ $textFile=$dir.'/lorem.txt';
+ $this->instance=$this->getNew();
+ $this->instance->addFile('lorem.txt',$textFile);
+ $this->assertFalse($this->instance->fileExists('target.txt'));
+ $this->instance->rename('lorem.txt','target.txt');
+ $this->assertTrue($this->instance->fileExists('target.txt'));
+ $this->assertFalse($this->instance->fileExists('lorem.txt'));
+ $this->assertEqual(file_get_contents($textFile),$this->instance->getFile('target.txt'));
+ $this->instance->remove('target.txt');
+ $this->assertFalse($this->instance->fileExists('target.txt'));
+ }
}
diff --git a/apps/files_archive/tests/tar.php b/apps/files_archive/tests/tar.php
new file mode 100644
index 00000000000..193a65b550a
--- /dev/null
+++ b/apps/files_archive/tests/tar.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+require_once('archive.php');
+
+class Test_Archive_TAR extends Test_Archive{
+ protected function getExisting(){
+ $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+ return new OC_Archive_TAR($dir.'/data.tar.gz');
+ }
+
+ protected function getNew(){
+ return new OC_Archive_TAR(OC_Helper::tmpFile('.tar.gz'));
+ }
+}
diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml
index 053044aaed2..691b265bf60 100644
--- a/apps/files_encryption/appinfo/info.xml
+++ b/apps/files_encryption/appinfo/info.xml
@@ -7,4 +7,7 @@
<licence>AGPL</licence>
<author>Robin Appelman</author>
<require>3</require>
+ <types>
+ <filesystem/>
+ </types>
</info>
diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php
new file mode 100644
index 00000000000..95770b44b75
--- /dev/null
+++ b/apps/files_external/appinfo/app.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+OC::$CLASSPATH['OC_Filestorage_FTP']='apps/files_external/lib/ftp.php';
+OC::$CLASSPATH['OC_Filestorage_DAV']='apps/files_external/lib/webdav.php';
+OC::$CLASSPATH['OC_Filestorage_Google']='apps/files_external/lib/google.php';
diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml
new file mode 100644
index 00000000000..fb58297ff17
--- /dev/null
+++ b/apps/files_external/appinfo/info.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<info>
+ <id>files_external</id>
+ <name>External storage support</name>
+ <description>Mount external storage sources</description>
+ <version>0.1</version>
+ <licence>AGPL</licence>
+ <author>Robin Appelman</author>
+ <require>3</require>
+ <types>
+ <filesystem/>
+ </types>
+</info>
diff --git a/apps/files_remote/lib/ftp.php b/apps/files_external/lib/ftp.php
index 802446b4fd8..802446b4fd8 100644
--- a/apps/files_remote/lib/ftp.php
+++ b/apps/files_external/lib/ftp.php
diff --git a/lib/filestorage/google.php b/apps/files_external/lib/google.php
index 49985548382..0d6db1987f8 100644
--- a/lib/filestorage/google.php
+++ b/apps/files_external/lib/google.php
@@ -24,14 +24,12 @@ require_once 'common.inc.php';
class OC_Filestorage_Google extends OC_Filestorage_Common {
- private $datadir;
private $consumer;
private $oauth_token;
private $sig_method;
private $entries;
public function __construct($arguments) {
- $this->datadir = $arguments['datadir'];
$consumer_key = isset($arguments['consumer_key']) ? $arguments['consumer_key'] : 'anonymous';
$consumer_secret = isset($arguments['consumer_secret']) ? $arguments['consumer_secret'] : 'anonymous';
$this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);
@@ -40,7 +38,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
$this->entries = array();
}
- private function sendRequest($feedUri, $http_method, $postData = null) {
+ private function sendRequest($feedUri, $http_method, $isDownload = false, $postData = null) {
$feedUri = trim($feedUri);
// create an associative array from each key/value url query param pair.
$params = array();
@@ -54,30 +52,71 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
$tempStr .= '&' . urlencode($key) . '=' . urlencode($value);
}
$feedUri = preg_replace('/&/', '?', $tempStr, 1);
- $req = OAuthRequest::from_consumer_and_token($this->consumer, $this->oauth_token, $http_method, $feedUri, $params);
- $req->sign_request($this->sig_method, $this->consumer, $this->oauth_token);
- $auth_header = $req->to_header();
- $result = send_signed_request($http_method, $feedUri, array($auth_header, 'Content-Type: application/atom+xml', 'GData-Version: 3.0'), $postData);
- // TODO Return false if error is received
- if (!$result) {
- return false;
+ $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->oauth_token, $http_method, $feedUri, $params);
+ $request->sign_request($this->sig_method, $this->consumer, $this->oauth_token);
+ $auth_header = $request->to_header();
+ $headers = array($auth_header, 'Content-Type: application/atom+xml', 'GData-Version: 3.0');
+ $curl = curl_init($feedUri);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_FAILONERROR, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ switch ($http_method) {
+ case 'GET':
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+ break;
+ case 'POST':
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
+ break;
+ case 'PUT':
+ $headers[] = 'If-Match: *';
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_method);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
+ break;
+ case 'DELETE':
+ $headers[] = 'If-Match: *';
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_method);
+ break;
+ default:
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+ }
+ if ($isDownload) {
+ $tmpFile = OC_Helper::tmpFile();
+ $fp = fopen($tmpFile, 'w');
+ curl_setopt($curl, CURLOPT_FILE, $fp);
+ curl_exec($curl);
+ curl_close($curl);
+ return $tmpFile;
}
- $result = explode('<', $result, 2);
- $result = isset($result[1]) ? '<'.$result[1] : $result[0];
+ $result = curl_exec($curl);
+ curl_close($curl);
$dom = new DOMDocument();
$dom->loadXML($result);
return $dom;
}
private function getResource($path) {
- if (array_key_exists($path, $this->entries)) {
- return $this->entries[$path];
+ $file = basename($path);
+ if (array_key_exists($file, $this->entries)) {
+ return $this->entries[$file];
} else {
- $title = basename($path);
- $dom = $this->sendRequest('https://docs.google.com/feeds/default/private/full?showfolders=true&title='.$title, 'GET');
+ // Strip the file extension; file could be a native Google Docs resource
+ if ($pos = strpos($file, '.')) {
+ $title = substr($file, 0, $pos);
+ $dom = $this->sendRequest('https://docs.google.com/feeds/default/private/full?showfolders=true&title='.$title, 'GET');
+ // Check if request was successful and entry exists
+ if ($dom && $entry = $dom->getElementsByTagName('entry')->item(0)) {
+ $this->entries[$file] = $entry;
+ return $entry;
+ }
+ }
+ $dom = $this->sendRequest('https://docs.google.com/feeds/default/private/full?showfolders=true&title='.$file, 'GET');
// Check if request was successful and entry exists
if ($dom && $entry = $dom->getElementsByTagName('entry')->item(0)) {
- $this->entries[$path] = $entry;
+ $this->entries[$file] = $entry;
return $entry;
}
return false;
@@ -86,7 +125,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
private function getExtension($entry) {
$mimetype = $this->getMimeType('', $entry);
- switch($mimetype) {
+ switch ($mimetype) {
case 'httpd/unix-directory':
return '';
case 'application/vnd.oasis.opendocument.text':
@@ -158,7 +197,10 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
$name = $entry->getElementsByTagName('title')->item(0)->nodeValue;
// Google Docs resources don't always include extensions in title
if (!strpos($name, '.')) {
- $name .= '.'.$this->getExtension($entry);
+ $extension = $this->getExtension($entry);
+ if ($extension != '') {
+ $name .= '.'.$extension;
+ }
}
$files[] = $name;
// Cache entry for future use
@@ -178,11 +220,15 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
} else if ($entry = $this->getResource($path)) {
// NOTE: Native resources don't have a file size
$stat['size'] = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesUsed')->item(0)->nodeValue;
- $stat['atime'] = strtotime($entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'lastViewed')->item(0)->nodeValue);
+// if (isset($atime = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'lastViewed')->item(0)->nodeValue))
+// $stat['atime'] = strtotime($entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'lastViewed')->item(0)->nodeValue);
$stat['mtime'] = strtotime($entry->getElementsByTagName('updated')->item(0)->nodeValue);
$stat['ctime'] = strtotime($entry->getElementsByTagName('published')->item(0)->nodeValue);
}
- return $stat;
+ if (isset($stat)) {
+ return $stat;
+ }
+ return false;
}
public function filetype($path) {
@@ -278,14 +324,36 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
public function fopen($path, $mode) {
if ($entry = $this->getResource($path)) {
- $extension = $this->getExtension($path);
- $downloadUri = $entry->getElementsByTagName('content')->item(0)->getAttribute('src');
- // TODO Non-native documents don't need these additional parameters
- $downloadUri .= '&exportFormat='.$extension.'&format='.$extension;
+ switch ($mode) {
+ case 'r':
+ case 'rb':
+ $extension = $this->getExtension($entry);
+ $downloadUri = $entry->getElementsByTagName('content')->item(0)->getAttribute('src');
+ // TODO Non-native documents don't need these additional parameters
+ $downloadUri .= '&exportFormat='.$extension.'&format='.$extension;
+ $tmpFile = $this->sendRequest($downloadUri, 'GET', true);
+ return fopen($tmpFile, 'r');
+ case 'w':
+ case 'wb':
+ case 'a':
+ case 'ab':
+ case 'r+':
+ case 'w+':
+ case 'wb+':
+ case 'a+':
+ case 'x':
+ case 'x+':
+ case 'c':
+ case 'c+':
+ // TODO Edit documents
+ }
+
}
+ return false;
}
public function getMimeType($path, $entry = null) {
+ // Entry can be passed, because extension is required for opendir and the entry can't be cached without the extension
if ($entry == null) {
if ($path == '' || $path == '/') {
return 'httpd/unix-directory';
@@ -297,7 +365,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
$mimetype = $entry->getElementsByTagName('content')->item(0)->getAttribute('type');
// Native Google Docs resources often default to text/html, but it may be more useful to default to a corresponding ODF mimetype
// Collections get reported as application/atom+xml, make sure it actually is a folder and fix the mimetype
- if ($mimetype == 'text/html' || $mimetype == 'application/atom+xml') {
+ if ($mimetype == 'text/html' || $mimetype == 'application/atom+xml;type=feed') {
$categories = $entry->getElementsByTagName('category');
foreach ($categories as $category) {
if ($category->getAttribute('scheme') == 'http://schemas.google.com/g/2005#kind') {
@@ -334,8 +402,8 @@ class OC_Filestorage_Google extends OC_Filestorage_Common {
return false;
}
- public function search($query) {
-
+ public function touch($path, $mtime = null) {
+
}
} \ No newline at end of file
diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php
new file mode 100644
index 00000000000..7a2da5c8ec0
--- /dev/null
+++ b/apps/files_external/lib/webdav.php
@@ -0,0 +1,293 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_FileStorage_DAV extends OC_Filestorage_Common{
+ private $password;
+ private $user;
+ private $host;
+ private $secure;
+ private $root;
+ /**
+ * @var Sabre_DAV_Client
+ */
+ private $client;
+
+ private static $tempFiles=array();
+
+ public function __construct($params){
+ $this->host=$params['host'];
+ $this->user=$params['user'];
+ $this->password=$params['password'];
+ $this->secure=isset($params['secure'])?(bool)$params['secure']:false;
+ $this->root=isset($params['root'])?$params['root']:'/';
+ if(substr($this->root,0,1)!='/'){
+ $this->root='/'.$this->root;
+ }
+ if(substr($this->root,-1,1)!='/'){
+ $this->root.='/';
+ }
+
+ $settings = array(
+ 'baseUri' => $this->createBaseUri(),
+ 'userName' => $this->user,
+ 'password' => $this->password,
+ );
+ $this->client = new Sabre_DAV_Client($settings);
+
+ //create the root folder if necesary
+ $this->mkdir('');
+ }
+
+ private function createBaseUri(){
+ $baseUri='http';
+ if($this->secure){
+ $baseUri.'s';
+ }
+ $baseUri.='://'.$this->host.$this->root;
+ return $baseUri;
+ }
+
+ public function mkdir($path){
+ $path=$this->cleanPath($path);
+ return $this->simpleResponse('MKCOL',$path,null,201);
+ }
+
+ public function rmdir($path){
+ $path=$this->cleanPath($path);
+ return $this->simpleResponse('DELETE',$path,null,204);
+ }
+
+ public function opendir($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array(),1);
+ $stripLength=strlen($this->root)+strlen($path);
+ $id=md5('webdav'.$this->root.$path);
+ OC_FakeDirStream::$dirs[$id]=array();
+ foreach($response as $file=>$data){
+ //strip root and path
+ $file=trim(substr($file,$stripLength));
+ $file=trim($file,'/');
+ if($file){
+ OC_FakeDirStream::$dirs[$id][]=$file;
+ }
+ }
+ return opendir('fakedir://'.$id);
+ }catch(Exception $e){
+ return false;
+ }
+ }
+
+ public function filetype($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array('{DAV:}resourcetype'));
+ $responseType=$response["{DAV:}resourcetype"]->resourceType;
+ return (count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file';
+ }catch(Exception $e){
+ return false;
+ }
+ }
+
+ public function is_readable($path){
+ return true;//not properly supported
+ }
+
+ public function is_writable($path){
+ return true;//not properly supported
+ }
+
+ public function file_exists($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array('{DAV:}resourcetype'));
+ return true;//no 404 exception
+ }catch(Exception $e){
+ return false;
+ }
+ }
+
+ public function unlink($path){
+ return $this->simpleResponse('DELETE',$path,null,204);
+ }
+
+ public function fopen($path,$mode){
+ $path=$this->cleanPath($path);
+ switch($mode){
+ case 'r':
+ case 'rb':
+ //straight up curl instead of sabredav here, sabredav put's the entire get result in memory
+ $curl = curl_init();
+ $fp = fopen('php://temp', 'r+');
+ curl_setopt($curl,CURLOPT_USERPWD,$this->user.':'.$this->password);
+ curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().$path);
+ curl_setopt($curl, CURLOPT_FILE, $fp);
+
+ curl_exec ($curl);
+ curl_close ($curl);
+ rewind($fp);
+ return $fp;
+ case 'w':
+ case 'wb':
+ case 'a':
+ case 'ab':
+ case 'r+':
+ case 'w+':
+ case 'wb+':
+ case 'a+':
+ case 'x':
+ case 'x+':
+ case 'c':
+ case 'c+':
+ //emulate these
+ if(strrpos($path,'.')!==false){
+ $ext=substr($path,strrpos($path,'.'));
+ }else{
+ $ext='';
+ }
+ $tmpFile=OC_Helper::tmpFile($ext);
+ OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack');
+ if($this->file_exists($path)){
+ $this->getFile($path,$tmpFile);
+ }
+ self::$tempFiles[$tmpFile]=$path;
+ return fopen('close://'.$tmpFile,$mode);
+ }
+ }
+
+ public function writeBack($tmpFile){
+ if(isset(self::$tempFiles[$tmpFile])){
+ $this->uploadFile($tmpFile,self::$tempFiles[$tmpFile]);
+ unlink($tmpFile);
+ }
+ }
+
+ public function free_space($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array('{DAV:}quota-available-bytes'));
+ if(isset($response['{DAV:}quota-available-bytes'])){
+ return (int)$response['{DAV:}quota-available-bytes'];
+ }else{
+ return 0;
+ }
+ }catch(Exception $e){
+ return 0;
+ }
+ }
+
+ public function touch($path,$mtime=null){
+ if(is_null($mtime)){
+ $mtime=time();
+ }
+ $path=$this->cleanPath($path);
+ $this->client->proppatch($path, array('{DAV:}lastmodified' => $mtime,));
+ }
+
+ public function getFile($path,$target){
+ $source=$this->fopen($path,'r');
+ file_put_contents($target,$source);
+ }
+
+ public function uploadFile($path,$target){
+ $source=fopen($path,'r');
+
+ $curl = curl_init();
+ curl_setopt($curl,CURLOPT_USERPWD,$this->user.':'.$this->password);
+ curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().$target);
+ curl_setopt($curl, CURLOPT_BINARYTRANSFER, true);
+ curl_setopt($curl, CURLOPT_INFILE, $source); // file pointer
+ curl_setopt($curl, CURLOPT_INFILESIZE, filesize($path));
+ curl_setopt($curl, CURLOPT_PUT, true);
+ curl_exec ($curl);
+ curl_close ($curl);
+ }
+
+ public function rename($path1,$path2){
+ $path1=$this->cleanPath($path1);
+ $path2=$this->root.$this->cleanPath($path2);
+ try{
+ $response=$this->client->request('MOVE',$path1,null,array('Destination'=>$path2));
+ return true;
+ }catch(Exception $e){
+ echo $e;
+ echo 'fail';
+ var_dump($response);
+ return false;
+ }
+ }
+
+ public function copy($path1,$path2){
+ $path1=$this->cleanPath($path1);
+ $path2=$this->root.$this->cleanPath($path2);
+ try{
+ $response=$this->client->request('COPY',$path1,null,array('Destination'=>$path2));
+ return true;
+ }catch(Exception $e){
+ echo $e;
+ echo 'fail';
+ var_dump($response);
+ return false;
+ }
+ }
+
+ public function stat($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array('{DAV:}getlastmodified','{DAV:}getcontentlength'));
+ if(isset($response['{DAV:}getlastmodified']) and isset($response['{DAV:}getcontentlength'])){
+ return array(
+ 'mtime'=>strtotime($response['{DAV:}getlastmodified']),
+ 'size'=>(int)$response['{DAV:}getcontentlength'],
+ 'ctime'=>-1,
+ );
+ }else{
+ return array();
+ }
+ }catch(Exception $e){
+ return array();
+ }
+ }
+
+ public function getMimeType($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array('{DAV:}getcontenttype','{DAV:}resourcetype'));
+ $responseType=$response["{DAV:}resourcetype"]->resourceType;
+ $type=(count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file';
+ if($type=='dir'){
+ return 'httpd/unix-directory';
+ }elseif(isset($response['{DAV:}getcontenttype'])){
+ return $response['{DAV:}getcontenttype'];
+ }else{
+ return false;
+ }
+ }catch(Exception $e){
+ return false;
+ }
+ }
+
+ private function cleanPath($path){
+ if(substr($path,0,1)=='/'){
+ return substr($path,1);
+ }else{
+ return $path;
+ }
+ }
+
+ private function simpleResponse($method,$path,$body,$expected){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->request($method,$path,$body);
+ return $response['statusCode']==$expected;
+ }catch(Exception $e){
+ return false;
+ }
+ }
+}
+
diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php
new file mode 100644
index 00000000000..9b40d2b98cf
--- /dev/null
+++ b/apps/files_external/tests/config.php
@@ -0,0 +1,22 @@
+<?php
+return array(
+ 'ftp'=>array(
+ 'host'=>'localhost',
+ 'user'=>'test',
+ 'password'=>'test',
+ 'root'=>'/test',
+ ),
+ 'webdav'=>array(
+ 'host'=>'localhost',
+ 'user'=>'test',
+ 'password'=>'test',
+ 'root'=>'/owncloud/files/webdav.php',
+ ),
+ 'google'=>array(
+ 'consumer_key'=>'anonymous',
+ 'consumer_secret'=>'anonymous',
+ 'token'=>'test',
+ 'token_secret'=>'test',
+ 'root'=>'/google',
+ )
+);
diff --git a/apps/files_remote/tests/ftp.php b/apps/files_external/tests/ftp.php
index 2d5405ccda6..aa565751ba3 100644
--- a/apps/files_remote/tests/ftp.php
+++ b/apps/files_external/tests/ftp.php
@@ -7,15 +7,12 @@
*/
class Test_Filestorage_FTP extends Test_FileStorage {
- /**
- * @var string tmpDir
- */
private $config;
private $id;
public function setUp(){
$id=uniqid();
- $this->config=include('apps/files_remote/tests/config.php');
+ $this->config=include('apps/files_external/tests/config.php');
$this->config['ftp']['root'].='/'.$id;//make sure we have an new empty folder to work in
$this->instance=new OC_Filestorage_FTP($this->config['ftp']);
}
@@ -24,5 +21,3 @@ class Test_Filestorage_FTP extends Test_FileStorage {
OC_Helper::rmdirr($this->instance->constructUrl(''));
}
}
-
-?> \ No newline at end of file
diff --git a/apps/files_external/tests/google.php b/apps/files_external/tests/google.php
new file mode 100644
index 00000000000..1c028945228
--- /dev/null
+++ b/apps/files_external/tests/google.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Michael Gapczynski
+* @copyright 2012 Michael Gapczynski mtgap@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/>.
+*/
+
+class Test_Filestorage_Google extends Test_FileStorage {
+
+ private $config;
+ private $id;
+
+ public function setUp(){
+ $id=uniqid();
+ $this->config=include('apps/files_external/tests/config.php');
+ $this->config['google']['root'].='/'.$id;//make sure we have an new empty folder to work in
+ $this->instance=new OC_Filestorage_Google($this->config['google']);
+ }
+
+ public function tearDown(){
+ $this->instance->rmdir('/');
+ }
+}
diff --git a/apps/files_external/tests/webdav.php b/apps/files_external/tests/webdav.php
new file mode 100644
index 00000000000..51799290540
--- /dev/null
+++ b/apps/files_external/tests/webdav.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class Test_Filestorage_DAV extends Test_FileStorage {
+ private $config;
+ private $id;
+
+ public function setUp(){
+ $id=uniqid();
+ $this->config=include('apps/files_external/tests/config.php');
+ $this->config['webdav']['root'].='/'.$id;//make sure we have an new empty folder to work in
+ $this->instance=new OC_Filestorage_DAV($this->config['webdav']);
+ }
+
+ public function tearDown(){
+ $this->instance->rmdir('/');
+ }
+}
diff --git a/apps/files_remote/appinfo/app.php b/apps/files_remote/appinfo/app.php
deleted file mode 100644
index f94e813ea5f..00000000000
--- a/apps/files_remote/appinfo/app.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-OC::$CLASSPATH['OC_Filestorage_FTP']='apps/files_remote/lib/ftp.php';
diff --git a/apps/files_remote/appinfo/info.xml b/apps/files_remote/appinfo/info.xml
deleted file mode 100644
index 0720b6095b9..00000000000
--- a/apps/files_remote/appinfo/info.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0"?>
-<info>
- <id>files_remote</id>
- <name>Remote storage support</name>
- <description>Mount remote storage sources</description>
- <version>0.1</version>
- <licence>AGPL</licence>
- <author>Robin Appelman</author>
- <require>3</require>
-</info>
diff --git a/apps/files_remote/tests/config.php b/apps/files_remote/tests/config.php
deleted file mode 100644
index 54105782919..00000000000
--- a/apps/files_remote/tests/config.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-return array(
- 'ftp'=>array(
- 'host'=>'localhost',
- 'user'=>'test',
- 'password'=>'test',
- 'root'=>'/test',
- )
-);
diff --git a/apps/files_sharing/ajax/userautocomplete.php b/apps/files_sharing/ajax/userautocomplete.php
index 9d971fb62af..38b673ee51b 100644
--- a/apps/files_sharing/ajax/userautocomplete.php
+++ b/apps/files_sharing/ajax/userautocomplete.php
@@ -7,21 +7,23 @@ OC_JSON::checkLoggedIn();
OC_JSON::checkAppEnabled('files_sharing');
$users = array();
-$ocusers = OC_User::getUsers();
+$groups = array();
$self = OC_User::getUser();
-$groups = OC_Group::getUserGroups($self);
+$userGroups = OC_Group::getUserGroups($self);
$users[] = "<optgroup label='Users'>";
-foreach ($ocusers as $user) {
- if ($user != $self) {
- $users[] = "<option value='".$user."'>".$user."</option>";
+$groups[] = "<optgroup label='Groups'>";
+foreach ($userGroups as $group) {
+ $groupUsers = OC_Group::usersInGroup($group);
+ foreach ($groupUsers as $user) {
+ if ($user != $self) {
+ $users[] = "<option value='".$user."'>".$user."</option>";
+ }
}
+ $groups[] = "<option value='".$group."'>".$group."</option>";
}
$users[] = "</optgroup>";
-$users[] = "<optgroup label='Groups'>";
-foreach ($groups as $group) {
- $users[] = "<option value='".$group."'>".$group."</option>";
-}
-$users[] = "</optgroup>";
+$groups[] = "</optgroup>";
+$users = array_merge($users, $groups);
OC_JSON::encodedPrint($users);
?>
diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml
index abf847b4483..8fda775520b 100644
--- a/apps/files_sharing/appinfo/info.xml
+++ b/apps/files_sharing/appinfo/info.xml
@@ -8,4 +8,7 @@
<author>Michael Gapczynski</author>
<require>2</require>
<default_enable/>
+ <types>
+ <filesystem/>
+ </types>
</info>
diff --git a/apps/files_sharing/lib_share.php b/apps/files_sharing/lib_share.php
index 42739bdfba9..673984f393b 100644
--- a/apps/files_sharing/lib_share.php
+++ b/apps/files_sharing/lib_share.php
@@ -52,8 +52,18 @@ class OC_Share {
// Remove the owner from the list of users in the group
$uid_shared_with = array_diff($uid_shared_with, array($uid_owner));
} else if (OC_User::userExists($uid_shared_with)) {
- $gid = null;
- $uid_shared_with = array($uid_shared_with);
+ $userGroups = OC_Group::getUserGroups($uid_owner);
+ // Check if the user is in one of the owner's groups
+ foreach ($userGroups as $group) {
+ if ($inGroup = OC_Group::inGroup($uid_shared_with, $group)) {
+ $gid = null;
+ $uid_shared_with = array($uid_shared_with);
+ break;
+ }
+ }
+ if (!$inGroup) {
+ throw new Exception("You can't share with ".$uid_shared_with);
+ }
} else {
throw new Exception($uid_shared_with." is not a user");
}
diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js
index 02d39b98430..1e136fe68e5 100644
--- a/apps/files_texteditor/js/editor.js
+++ b/apps/files_texteditor/js/editor.js
@@ -76,7 +76,7 @@ function showControls(filename,writeperms){
function bindControlEvents(){
$("#editor_save").die('click',doFileSave).live('click',doFileSave);
- $('#editor_close').die('click',hideFileEditor).live('click',hideFileEditor);
+ $('#editor_close').die('click',closeBtnClick).live('click',closeBtnClick);
$('#gotolineval').die('keyup', goToLine).live('keyup', goToLine);
$('#editorsearchval').die('keyup', doSearch).live('keyup', doSearch);
$('#clearsearchbtn').die('click', resetSearch).live('click', resetSearch);
@@ -141,31 +141,38 @@ 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');
+ }
}
};
@@ -192,10 +199,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 +215,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
}
@@ -220,6 +235,19 @@ function showFileEditor(dir,filename){
}
}
+function closeBtnClick(){
+ if($('#editor').attr('data-edited')=='true'){
+ // Show confirm
+ OC.dialogs.confirm(t('files_texteditor','You have unsaved changes that will be lost! Do you still want to close?'),t('files_texteditor','Really close?'),function(close){
+ if(close){
+ hideFileEditor();
+ }
+ });
+ } else {
+ hideFileEditor();
+ }
+}
+
// Fades out the editor.
function hideFileEditor(){
// Fades out editor controls
@@ -287,4 +315,5 @@ $(document).ready(function(){
$('#editor').remove();
// Binds the save keyboard shortcut events
//$(document).unbind('keydown').bind('keydown',checkForSaveKeyPress);
+
});
diff --git a/apps/gallery/ajax/galleryOp.php b/apps/gallery/ajax/galleryOp.php
index 459c30f6ac6..1b3ad48f561 100644
--- a/apps/gallery/ajax/galleryOp.php
+++ b/apps/gallery/ajax/galleryOp.php
@@ -111,9 +111,51 @@ function handleGetGallery($path) {
$p[] = utf8_encode($r['file_path']);
}
- OC_JSON::success(array('albums'=>$a, 'photos'=>$p));
+ $r = OC_Gallery_Sharing::getEntryByAlbumId($album_details['album_id']);
+ $shared = false;
+ $recursive = false;
+ $token = '';
+ if ($row = $r->fetchRow()) {
+ $shared = true;
+ $recursive = ($row['recursive'] == 1)? true : false;
+ $token = $row['token'];
+ }
+
+ OC_JSON::success(array('albums'=>$a, 'photos'=>$p, 'shared' => $shared, 'recursive' => $recursive, 'token' => $token));
}
+function handleShare($path, $share, $recursive) {
+ $recursive = $recursive == 'true' ? 1 : 0;
+ $owner = OC_User::getUser();
+ $root = OC_Preferences::getValue(OC_User::getUser(),'gallery', 'root', '/');
+ $path = utf8_decode(rtrim($root.$path,'/'));
+ if($path == '') $path = '/';
+ $r = OC_Gallery_Album::find($owner, null, $path);
+ if ($row = $r->fetchRow()) {
+ $albumId = $row['album_id'];
+ } else {
+ OC_JSON::error(array('cause' => 'Couldn\'t find requested gallery'));
+ exit;
+ }
+
+ if ($share == false) {
+ OC_Gallery_Sharing::remove($albumId);
+ OC_JSON::success(array('sharing' => false));
+ } else { // share, yeah \o/
+ $r = OC_Gallery_Sharing::getEntryByAlbumId($albumId);
+ if (($row = $r->fetchRow())) { // update entry
+ OC_Gallery_Sharing::updateSharingByToken($row['token'], $recursive);
+ OC_JSON::success(array('sharing' => true, 'token' => $row['token'], 'recursive' => $recursive == 1 ? true : false ));
+ } else { // and new sharing entry
+ $date = new DateTime();
+ $token = md5($owner . $date->getTimestamp());
+ OC_Gallery_Sharing::addShared($token, intval($albumId), $recursive);
+ OC_JSON::success(array('sharing' => true, 'token' => $token, 'recursive' => $recursive == 1 ? true : false ));
+ }
+ }
+}
+
+
if ($_GET['operation']) {
switch($_GET['operation']) {
case 'rename':
@@ -136,6 +178,9 @@ if ($_GET['operation']) {
case 'get_gallery':
handleGetGallery($_GET['path']);
break;
+ case 'share':
+ handleShare($_GET['path'], $_GET['share'] == 'true' ? true : false, $_GET['recursive']);
+ break;
default:
OC_JSON::error(array('cause' => 'Unknown operation'));
}
diff --git a/apps/gallery/ajax/sharing.php b/apps/gallery/ajax/sharing.php
new file mode 100644
index 00000000000..fba85fa34ee
--- /dev/null
+++ b/apps/gallery/ajax/sharing.php
@@ -0,0 +1,115 @@
+<?php
+
+/**
+* ownCloud - gallery application
+*
+* @author Bartek Przybylski
+* @copyright 2012 Bartek Przybylski bartek@alefzero.eu
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+require_once('../../../lib/base.php');
+
+if (!isset($_GET['token']) || !isset($_GET['operation'])) {
+ OC_JSON::error(array('cause' => 'Not enought arguments'));
+ exit;
+}
+
+$operation = $_GET['operation'];
+$token = $_GET['token'];
+
+if (!OC_Gallery_Sharing::isTokenValid($token)) {
+ OC_JSON::error(array('cause' => 'Given token is not valid'));
+ exit;
+}
+
+function handleGetGallery($token, $path) {
+ $owner = OC_Gallery_Sharing::getTokenOwner($token);
+ $apath = OC_Gallery_Sharing::getPath($token);
+
+ if ($path == false)
+ $root = $apath;
+ else
+ $root = rtrim($apath,'/').$path;
+
+ $r = OC_Gallery_Album::find($owner, null, $root);
+ $albums = array();
+ $photos = array();
+ $albumId = -1;
+ if ($row = $r->fetchRow()) {
+ $albumId = $row['album_id'];
+ }
+ if ($albumId != -1) {
+
+ if (OC_Gallery_Sharing::isRecursive($token)) {
+ $r = OC_Gallery_Album::find($owner, null, null, $root);
+ while ($row = $r->fetchRow())
+ $albums[] = $row['album_name'];
+ }
+
+ $r = OC_Gallery_Photo::find($albumId);
+ while ($row = $r->fetchRow())
+ $photos[] = $row['file_path'];
+ }
+
+ OC_JSON::success(array('albums' => $albums, 'photos' => $photos));
+}
+
+function handleGetThumbnail($token, $imgpath) {
+ $owner = OC_Gallery_Sharing::getTokenOwner($token);
+ $image = OC_Gallery_Photo::getThumbnail($imgpath, $owner);
+ if ($image) {
+ OC_Response::enableCaching(3600 * 24); // 24 hour
+ $image->show();
+ }
+}
+
+function handleGetAlbumThumbnail($token, $albumname)
+{
+ $owner = OC_Gallery_Sharing::getTokenOwner($token);
+ $file = OC_Config::getValue("datadirectory").'/'. $owner .'/gallery/'.$albumname.'.png';
+ $image = new OC_Image($file);
+ if ($image->valid()) {
+ $image->centerCrop();
+ $image->resize(200);
+ $image->fixOrientation();
+ OC_Response::enableCaching(3600 * 24); // 24 hour
+ $image->show();
+ }
+}
+
+function handleGetPhoto($token, $photo) {
+ $owner = OC_Gallery_Sharing::getTokenOwner($token);
+ $file = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ).'/'.$owner.'/files'.urldecode($photo);
+ header('Content-Type: '.OC_Image::getMimeTypeForFile($file));
+ OC_Response::sendFile($file);
+}
+
+switch ($operation) {
+ case 'get_gallery':
+ handleGetGallery($token, isset($_GET['path'])? $_GET['path'] : false);
+ break;
+ case 'get_thumbnail':
+ handleGetThumbnail($token, urldecode($_GET['img']));
+ break;
+ case 'get_album_thumbnail':
+ handleGetAlbumThumbnail($token, urldecode($_GET['albumname']));
+ break;
+ case 'get_photo':
+ handleGetPhoto($token, urldecode($_GET['photo']));
+ break;
+}
+
diff --git a/apps/gallery/appinfo/app.php b/apps/gallery/appinfo/app.php
index 1e5e27d408f..f7e0651275e 100644
--- a/apps/gallery/appinfo/app.php
+++ b/apps/gallery/appinfo/app.php
@@ -24,6 +24,7 @@
OC::$CLASSPATH['OC_Gallery_Album'] = 'apps/gallery/lib/album.php';
OC::$CLASSPATH['OC_Gallery_Photo'] = 'apps/gallery/lib/photo.php';
OC::$CLASSPATH['OC_Gallery_Scanner'] = 'apps/gallery/lib/scanner.php';
+OC::$CLASSPATH['OC_Gallery_Sharing'] = 'apps/gallery/lib/sharing.php';
OC::$CLASSPATH['OC_Gallery_Hooks_Handlers'] = 'apps/gallery/lib/hooks_handlers.php';
$l = new OC_L10N('gallery');
@@ -31,14 +32,14 @@ $l = new OC_L10N('gallery');
OC_App::register(array(
'order' => 20,
'id' => 'gallery',
- 'name' => 'Gallery'));
+ 'name' => 'Pictures'));
OC_App::addNavigationEntry( array(
'id' => 'gallery_index',
'order' => 20,
'href' => OC_Helper::linkTo('gallery', 'index.php'),
'icon' => OC_Helper::imagePath('core', 'places/picture.svg'),
- 'name' => $l->t('Gallery')));
+ 'name' => $l->t('Pictures')));
class OC_GallerySearchProvider implements OC_Search_Provider{
static function search($query){
diff --git a/apps/gallery/appinfo/database.xml b/apps/gallery/appinfo/database.xml
index 62fdbee9cd8..e3b13f7e93c 100644
--- a/apps/gallery/appinfo/database.xml
+++ b/apps/gallery/appinfo/database.xml
@@ -67,4 +67,28 @@
</field>
</declaration>
</table>
+ <table>
+ <name>*dbprefix*gallery_sharing</name>
+ <declaration>
+ <field>
+ <name>token</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+ <field>
+ <name>gallery_id</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <length>4</length>
+ </field>
+ <field>
+ <name>recursive</name>
+ <type>integer</type>
+ <notnull>true</notnull>
+ <length>1</length>
+ </field>
+ </declaration>
+ </table>
</database>
diff --git a/apps/gallery/appinfo/info.xml b/apps/gallery/appinfo/info.xml
index 19c5dc8b25e..4c8c1cee242 100644
--- a/apps/gallery/appinfo/info.xml
+++ b/apps/gallery/appinfo/info.xml
@@ -1,11 +1,11 @@
<?xml version="1.0"?>
<info>
<id>gallery</id>
- <name>Gallery</name>
+ <name>Pictures</name>
<version>0.4</version>
<licence>AGPL</licence>
<author>Bartek Przybylski</author>
<require>2</require>
- <description>Gallery application for ownCloud</description>
+ <description>Dedicated pictures application</description>
<default_enable/>
</info>
diff --git a/apps/gallery/css/sharing.css b/apps/gallery/css/sharing.css
new file mode 100644
index 00000000000..eaac82ebd60
--- /dev/null
+++ b/apps/gallery/css/sharing.css
@@ -0,0 +1,8 @@
+body { background-color: #eee; margin: 0; padding: 0;}
+#gallery_list { height: 100%; width: 80%; background-color: white; margin: 0 auto; box-shadow: 0 0 8px #888; }
+div.gallery_box { width: 200px; position:relative; text-align: center; border: 0; display: inline-block; margin: 5pt; vertical-align: top; padding: 5px 5px 5px 5px; position: relative; cursor: pointer; -webkit-transition: color 0.5s ease-in-out; -o-transition: color 0.5s ease-in-out; -moz-transition: color 0.5s ease-in-out;color: #BBB;}
+div.gallery_box:hover { color: black; }
+div.gallery_box h1 {font-size: 17px; font-weight: normal;}
+div#breadcrumb { border: 0; width: 70%; margin: 0 auto; padding: 25px 0; font-family: Verdana; text-align: center;}
+span.breadcrumbelement { margin: 10px; margin-right: 0; cursor: pointer;}
+span.inside { background-image: url('../img/breadcrumb.png'); padding-left: 20px; background-position: left; background-repeat: no-repeat;}
diff --git a/apps/gallery/css/styles.css b/apps/gallery/css/styles.css
index 013cd1b262d..fbf54e43db2 100644
--- a/apps/gallery/css/styles.css
+++ b/apps/gallery/css/styles.css
@@ -16,3 +16,5 @@ div.gallery_control_overlay a { color:white; }
#g-settings {position: absolute; left 13.5em; top: 0;}
input[type=button] { -webkit-transition: opacity 0.5s ease-in-out; -moz-transition: opacity 0.5s ease-in-out; -o-transition: opacity 0.5s ease-in-out; opacity: 1}
input[type=button]:disabled { opacity: 0.5 }
+.ui-dialog tr {background-color: #eee;}
+.ui-dialog input {width: 90%;}
diff --git a/apps/gallery/img/breadcrumb.png b/apps/gallery/img/breadcrumb.png
new file mode 100644
index 00000000000..a252a751554
--- /dev/null
+++ b/apps/gallery/img/breadcrumb.png
Binary files differ
diff --git a/apps/gallery/index.php b/apps/gallery/index.php
index 822a5b8e143..7de7c094142 100644
--- a/apps/gallery/index.php
+++ b/apps/gallery/index.php
@@ -4,7 +4,7 @@
* ownCloud - gallery application
*
* @author Bartek Przybylski
-* @copyright 2012 Bartek Przybylski bart.p.pl@gmail.com
+* @copyright 2012 Bartek Przybylski bartek@alefzero.eu
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -29,9 +29,6 @@ OC_App::setActiveNavigationEntry( 'gallery_index' );
if (!file_exists(OC_Config::getValue("datadirectory").'/'. OC_User::getUser() .'/gallery')) {
mkdir(OC_Config::getValue("datadirectory").'/'. OC_User::getUser() .'/gallery');
- $f = fopen(OC_Config::getValue("datadirectory").'/'. OC_User::getUser() .'/gallery/.htaccess', 'w');
- fwrite($f, "allow from all");
- fclose($f);
}
if (!isset($_GET['view'])) {
diff --git a/apps/gallery/js/album_cover.js b/apps/gallery/js/album_cover.js
index 8cafce35f98..d44e7f83d1f 100644
--- a/apps/gallery/js/album_cover.js
+++ b/apps/gallery/js/album_cover.js
@@ -30,17 +30,62 @@ function albumClick(title) {
});
}
+function constructSharingPath() {
+ return document.location.protocol + '//' + document.location.host + OC.linkTo('gallery', 'sharing.php') + '?token=' + Albums.token;
+}
+
+function shareGallery() {
+ var existing_token = '';
+ if (Albums.token)
+ existing_token = constructSharingPath();
+ var form_fields = [{text: 'Share', name: 'share', type: 'checkbox', value: Albums.shared},
+ {text: 'Share recursive', name: 'recursive', type: 'checkbox', value: Albums.recursive},
+ {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]+'/';
+ 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;
+ if (Albums.shared) {
+ Albums.token = r.token;
+ Albums.recursive = r.recursive;
+ } else {
+ Albums.token = '';
+ Albums.recursive = false;
+ }
+ var actual_addr = '';
+ if (Albums.token)
+ actual_addr = constructSharingPath();
+ $('input[name="address"]').val(actual_addr);
+ } else {
+ OC.dialogs.alert(t('gallery', 'Error: ') + r.cause, t('gallery', 'Internal error'));
+ }
+ });
+ });
+}
+
function albumClickHandler(r) {
Albums.photos = [];
Albums.albums = [];
if (r.status == 'success') {
for (var i in r.albums) {
var a = r.albums[i];
- Albums.add(a.name, a.numOfItems,a.path);
+ Albums.add(a.name, a.numOfItems, a.path, a.shared, a.recursive, a.token);
}
for (var i in r.photos) {
- Albums.photos.push(r.photos[i]);
+ Albums.photos.push(r.photos[i]);
}
+ Albums.shared = r.shared;
+ if (Albums.shared) {
+ Albums.recursive = r.recursive;
+ Albums.token = r.token;
+ } else {
+ Albums.recursive = false;
+ Albums.token = '';
+ }
var targetDiv = document.getElementById('gallery_list');
if (targetDiv) {
$(targetDiv).html('');
@@ -54,7 +99,7 @@ function albumClickHandler(r) {
OC.dialogs.alert(t('gallery', 'Error: no such layer `gallery_list`'), t('gallery', 'Internal error'));
}
} else {
- OC.dialogs.alert(t('gallery', 'Error: ') + r.message, t('gallery', 'Internal error'));
+ OC.dialogs.alert(t('gallery', 'Error: ') + r.cause, t('gallery', 'Internal error'));
}
$('#g-album-loading').hide();
}
@@ -68,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');
- }
- }
- ],
- });
+ });
}
diff --git a/apps/gallery/js/albums.js b/apps/gallery/js/albums.js
index d3326841cc7..1fb38a5546c 100644
--- a/apps/gallery/js/albums.js
+++ b/apps/gallery/js/albums.js
@@ -8,12 +8,15 @@ Albums={
// the album cover
albums:new Array(),
photos:new Array(),
+ shared: false,
+ recursive: false,
+ token: '',
// add simply adds new album to internal structure
// however albums names must be unique so other
// album with the same name wont be insered,
// and false will be returned
// true on success
- add: function(album_name, num,path) {
+ add: function(album_name, num, path) {
if (Albums.albums[album_name] != undefined) return false;
Albums.albums[album_name] = {name: album_name, numOfCovers: num, path:path};
return true;
@@ -64,14 +67,14 @@ Albums={
$(".gallery_album_cover", local).css('background-repeat', 'no-repeat');
$(".gallery_album_cover", local).css('background-position', '0');
$(".gallery_album_cover", local).css('background-image','url("'+OC.filePath('gallery','ajax','galleryOp.php')+'?operation=get_covers&albumname='+escape(a.name)+'")');
- $(".gallery_album_cover", local).mousemove(function(e) {
+ $(".gallery_album_cover", local).mousemove(function(event) {
var albumMetadata = Albums.find(this.title);
if (albumMetadata == undefined) {
return;
}
- var x = Math.floor((e.layerX - this.offsetLeft)/(this.offsetWidth/albumMetadata.numOfCovers));
+ var x = Math.floor(event.offsetX/(this.offsetWidth/albumMetadata.numOfCovers));
x *= this.offsetWidth;
- if (x < 0) x=0;
+ if (x < 0 || isNaN(x)) x=0;
$(this).css('background-position', -x+'px 0');
});
$(element).append(local);
diff --git a/apps/gallery/js/sharing.js b/apps/gallery/js/sharing.js
new file mode 100644
index 00000000000..340d1b9b274
--- /dev/null
+++ b/apps/gallery/js/sharing.js
@@ -0,0 +1,57 @@
+$(document).ready(function() {
+ $.getJSON('ajax/sharing.php', {operation: 'get_gallery', token: TOKEN}, albumClickHandler);
+});
+
+var paths = [];
+var counter = 0;
+
+function returnTo(num) {
+ while (num != counter) {
+ paths.pop();
+ $('.breadcrumbelement:last').remove();
+ counter--;
+ }
+ path = '';
+ for (var e in paths) path += '/' + paths[e];
+ $.getJSON('ajax/sharing.php', {operation: 'get_gallery', token: TOKEN, path: path}, function(r) {
+ albumClickHandler(r);
+ });
+}
+
+function albumClickHandler(r) {
+ var element = $('div#gallery_list');
+ element.html('');
+ var album_template = '<div class="gallery_box"><div><a rel="images"><img src="ajax/sharing.php?token='+TOKEN+'&operation=get_album_thumbnail&albumname=IMGPATH"></a></div><h1></h1></div>';
+
+ for (var i in r.albums) {
+ var a = r.albums[i];
+ var local = $(album_template.replace('IMGPATH', encodeURIComponent(a)));
+ local.attr('title', a);
+ $('h1', local).html(a);
+ element.append(local);
+ }
+
+ $('div.gallery_box').each(function(i, element) {
+ $(element).click(function() {
+ paths.push($(this).attr('title'));
+ path = '';
+ for (var e in paths) path += '/' + paths[e];
+ $.getJSON('ajax/sharing.php', {operation: 'get_gallery', token: TOKEN, path: path}, function(r) {
+ var name = paths[paths.length-1];
+ counter++;
+ var d = '<span class="breadcrumbelement" onclick="javascript:returnTo('+counter+');return false;">'+name+'</span>';
+ d = $(d).addClass('inside');
+ $('#breadcrumb').append(d);
+ albumClickHandler(r);
+ });
+ });
+ });
+
+ var pat = '';
+ for (var a in paths) pat += '/'+paths[a];
+ var photo_template = '<div class="gallery_box"><div><a rel="images" href="*HREF*" target="_blank"><img src="ajax/sharing.php?token='+TOKEN+'&operation=get_thumbnail&img=IMGPATH"></a></div></div>';
+ for (var a in r.photos) {
+ var local = photo_template.replace('IMGPATH', encodeURIComponent(r.photos[a])).replace('*HREF*', 'ajax/sharing.php?token='+TOKEN+'&operation=get_photo&photo='+encodeURIComponent(r.photos[a]));
+ element.append(local);
+ }
+}
diff --git a/apps/gallery/lib/album.php b/apps/gallery/lib/album.php
index 070afdd6cd6..ef361a37913 100644
--- a/apps/gallery/lib/album.php
+++ b/apps/gallery/lib/album.php
@@ -79,7 +79,7 @@ class OC_Gallery_Album {
$sql .= ' AND parent_path = ?';
$args[] = $parent;
}
- $order = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'order', 'ASC');
+ $order = OC_Preferences::getValue($owner, 'gallery', 'order', 'ASC');
$sql .= ' ORDER BY album_name ' . $order;
$stmt = OC_DB::prepare($sql);
diff --git a/apps/gallery/lib/photo.php b/apps/gallery/lib/photo.php
index 5e6df8069b5..3bb6f9129fa 100644
--- a/apps/gallery/lib/photo.php
+++ b/apps/gallery/lib/photo.php
@@ -66,11 +66,15 @@ class OC_Gallery_Photo {
$stmt->execute(array($newpath, $newAlbumId, $oldAlbumId, $oldpath));
}
- public static function getThumbnail($image_name) {
- $save_dir = OC_Config::getValue("datadirectory").'/'. OC_User::getUser() .'/gallery/';
+ public static function getThumbnail($image_name, $owner = null) {
+ if (!$owner) $owner = OC_User::getUser();
+ $save_dir = OC_Config::getValue("datadirectory").'/'. $owner .'/gallery/';
$save_dir .= dirname($image_name). '/';
$image_path = $image_name;
$thumb_file = $save_dir . basename($image_name);
+ if (!is_dir($save_dir)) {
+ mkdir($save_dir, 0777, true);
+ }
if (file_exists($thumb_file)) {
$image = new OC_Image($thumb_file);
} else {
@@ -80,17 +84,15 @@ class OC_Gallery_Photo {
}
$image = new OC_Image($image_path);
if ($image->valid()) {
- $image->centerCrop();
- $image->resize(200);
+ $image->centerCrop(200);
$image->fixOrientation();
- if (!is_dir($save_dir)) {
- mkdir($save_dir, 0777, true);
- }
$image->save($thumb_file);
}
}
if ($image->valid()) {
return $image;
+ }else{
+ $image->destroy();
}
return null;
}
diff --git a/apps/gallery/lib/scanner.php b/apps/gallery/lib/scanner.php
index c8825c267eb..0317f943e5d 100644
--- a/apps/gallery/lib/scanner.php
+++ b/apps/gallery/lib/scanner.php
@@ -78,9 +78,11 @@ class OC_Gallery_Scanner {
$image = OC_Gallery_Photo::getThumbnail($files[$i]);
if ($image && $image->valid()) {
imagecopyresampled($thumbnail, $image->resource(), $i*200, 0, 0, 0, 200, 200, 200, 200);
+ $image->destroy();
}
}
imagepng($thumbnail, OC_Config::getValue("datadirectory").'/'. OC_User::getUser() .'/gallery/' . $albumName.'.png');
+ imagedestroy($thumbnail);
}
public static function createIntermediateAlbums() {
diff --git a/apps/gallery/lib/sharing.php b/apps/gallery/lib/sharing.php
new file mode 100644
index 00000000000..60f108bd6c6
--- /dev/null
+++ b/apps/gallery/lib/sharing.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+* ownCloud - gallery application
+*
+* @author Bartek Przybylski
+* @copyright 2012 Bartek Przybylski bartek@alefzero.eu
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+class OC_Gallery_Sharing {
+ private static function getEntries($token) {
+ $sql = 'SELECT * FROM *PREFIX*gallery_sharing WHERE token = ?';
+ $stmt = OC_DB::prepare($sql);
+ return $stmt->execute(array($token));
+ }
+
+ public static function isTokenValid($token) {
+ $r = self::getEntries($token);
+ $row = $r->fetchRow();
+ return $row != null;
+ }
+
+ public static function isRecursive($token) {
+ $r = self::getEntries($token);
+ if ($row = $r->fetchRow()) return $row['recursive'] == 1;
+ return false;
+ }
+
+ public static function getTokenOwner($token) {
+ $r = self::getEntries($token);
+ if ($row = $r->fetchRow()) {
+ $galleryId = $row['gallery_id'];
+ $sql = 'SELECT * FROM *PREFIX*gallery_albums WHERE album_id = ?';
+ $stmt = OC_DB::prepare($sql);
+ $r = $stmt->execute(array($galleryId));
+ if ($row = $r->fetchRow())
+ return $row['uid_owner'];
+ }
+ return false;
+ }
+
+ public static function getPath($token) {
+ $r = self::getEntries($token);
+ if ($row = $r->fetchRow()) {
+ $galleryId = $row['gallery_id'];
+ $sql = 'SELECT * FROM *PREFIX*gallery_albums WHERE album_id = ?';
+ $stmt = OC_DB::prepare($sql);
+ $r = $stmt->execute(array($galleryId));
+ if ($row = $r->fetchRow())
+ return $row['album_path'];
+ }
+ }
+
+ public static function updateSharingByToken($token, $recursive) {
+ $stmt = OC_DB::prepare('UPDATE *PREFIX*gallery_sharing SET recursive = ? WHERE token = ?');
+ $stmt->execute(array($recursive, $token));
+ }
+
+ public static function getEntryByAlbumId($album_id) {
+ $stmt = OC_DB::prepare('SELECT * FROM *PREFIX*gallery_sharing WHERE gallery_id = ?');
+ return $stmt->execute(array($album_id));
+ }
+
+ public static function addShared($token, $albumId, $recursive) {
+ $sql = 'INSERT INTO *PREFIX*gallery_sharing (token, gallery_id, recursive) VALUES (?, ?, ?)';
+ $stmt = OC_DB::prepare($sql);
+ $stmt->execute(array($token, $albumId, $recursive));
+ }
+
+ public static function remove($albumId) {
+ $stmt = OC_DB::prepare('DELETE FROM *PREFIX*gallery_sharing WHERE gallery_id = ?');
+ $stmt->execute(array($albumId));
+ }
+}
+
diff --git a/apps/gallery/sharing.php b/apps/gallery/sharing.php
new file mode 100644
index 00000000000..d7430becf43
--- /dev/null
+++ b/apps/gallery/sharing.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+* ownCloud - gallery application
+*
+* @author Bartek Przybylski
+* @copyright 2012 Bartek Przybylski bartek@alefzero.eu
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+if (!isset($_GET['token']) || empty($_GET['token'])) {
+ exit;
+}
+
+require_once('../../lib/base.php');
+
+OC_Util::checkAppEnabled('gallery');
+
+?>
+<!doctype html>
+<html>
+ <head>
+ <link rel="stylesheet" href="css/sharing.css" type="text/css"/>
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
+ <script src="js/sharing.js" type="text/javascript"></script>
+ <script>
+ var TOKEN = '<?php echo $_GET['token']; ?>';
+ </script>
+ </head>
+ <body>
+ <div id="breadcrumb"><span class="breadcrumbelement" onclick="javascript:returnTo(0);return false;">Shared gallery</span></div>
+ <div id="gallery_list"></div>
+ </body>
+</html>
diff --git a/apps/gallery/templates/index.php b/apps/gallery/templates/index.php
index dc5852733bd..9bec5db1b91 100644
--- a/apps/gallery/templates/index.php
+++ b/apps/gallery/templates/index.php
@@ -9,12 +9,13 @@ 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>
<input type="button" class="start" value="<?php echo $l->t('Rescan');?>" onclick="javascript:scanForAlbums();" />
- <input type="button" class="stop" style="display:none" value="<?php echo $l->t('Stop');?>" onclick="javascript:Scanner.stop();" />
+ <input type="button" class="stop" style="display:none" value="<?php echo $l->t('Stop');?>" onclick="javascript:Scanner.stop();" />
+ <input type="button" id="g-share-button" value="<?php echo $l->t('Share'); ?>" onclick="javascript:shareGallery();" />
<input type="button" id="g-settings-button" value="<?php echo $l->t('Settings');?>" onclick="javascript:settings();"/>
</div>
<div id="g-album-navigation">
@@ -28,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>
-
diff --git a/apps/media/css/music.css b/apps/media/css/music.css
index 07173624018..164a6c62ae6 100644
--- a/apps/media/css/music.css
+++ b/apps/media/css/music.css
@@ -22,6 +22,7 @@ div.jp-volume-bar-value { background:#ccc; width:0; height:0.4em; }
#leftcontent img.remove { display:none; float:right; cursor:pointer; opacity: 0; }
#leftcontent li:hover img.remove { display:inline; opacity: .3; }
#leftcontent li div.label { float: left; width: 200px; overflow: hidden; text-overflow: ellipsis; }
+#rightcontent { overflow: auto; }
#playlist li { list-style-type:none; }
.template { display:none; }
.collection_playing { background:#eee; font-weight: bold; }
diff --git a/apps/remoteStorage/appinfo/info.xml b/apps/remoteStorage/appinfo/info.xml
index 121587795db..1f9618a3334 100644
--- a/apps/remoteStorage/appinfo/info.xml
+++ b/apps/remoteStorage/appinfo/info.xml
@@ -2,8 +2,8 @@
<info>
<id>remoteStorage</id>
<name>remoteStorage compatibility</name>
- <description>Enables your users to use ownCloud as their remote storage for unhosted applications.</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>
diff --git a/apps/remoteStorage/appinfo/webfinger.php b/apps/remoteStorage/appinfo/webfinger.php
new file mode 100644
index 00000000000..7c0ab846057
--- /dev/null
+++ b/apps/remoteStorage/appinfo/webfinger.php
@@ -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>
diff --git a/apps/user_ldap/appinfo/info.xml b/apps/user_ldap/appinfo/info.xml
index 9a6ee1436fc..99830dd1ffd 100644
--- a/apps/user_ldap/appinfo/info.xml
+++ b/apps/user_ldap/appinfo/info.xml
@@ -7,4 +7,7 @@
<licence>AGPL</licence>
<author>Dominik Schmidt</author>
<require>2</require>
+ <types>
+ <authentication/>
+ </types>
</info>
diff --git a/apps/user_openid/appinfo/info.xml b/apps/user_openid/appinfo/info.xml
index 37be15abfda..721db1877e3 100644
--- a/apps/user_openid/appinfo/info.xml
+++ b/apps/user_openid/appinfo/info.xml
@@ -7,4 +7,7 @@
<licence>AGPL</licence>
<author>Robin Appelman</author>
<require>2</require>
+ <types>
+ <authentication/>
+ </types>
</info>
diff --git a/apps/user_webfinger/.htaccess b/apps/user_webfinger/.htaccess
deleted file mode 100644
index 4d4d2e9c58f..00000000000
--- a/apps/user_webfinger/.htaccess
+++ /dev/null
@@ -1,3 +0,0 @@
-RewriteEngine On
-RewriteBase /
-RewriteRule host-meta$ \/\.well-known\/host-meta\.php [L]
diff --git a/apps/user_webfinger/appinfo/info.xml b/apps/user_webfinger/appinfo/info.xml
index 55cf2cf2201..d47fb723a3a 100644
--- a/apps/user_webfinger/appinfo/info.xml
+++ b/apps/user_webfinger/appinfo/info.xml
@@ -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>
diff --git a/apps/user_webfinger/appinfo/install.php b/apps/user_webfinger/appinfo/install.php
index f570a3a249b..c8d9a427425 100644
--- a/apps/user_webfinger/appinfo/install.php
+++ b/apps/user_webfinger/appinfo/install.php
@@ -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);
diff --git a/apps/user_webfinger/host-meta b/apps/user_webfinger/host-meta
deleted file mode 100644
index dfaf3636145..00000000000
--- a/apps/user_webfinger/host-meta
+++ /dev/null
@@ -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'
diff --git a/apps/user_webfinger/host-meta.php b/apps/user_webfinger/host-meta.php
deleted file mode 100644
index ac577cf9a0c..00000000000
--- a/apps/user_webfinger/host-meta.php
+++ /dev/null
@@ -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>
-
diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php
index 5c2a24aa070..9ada473ca87 100644
--- a/apps/user_webfinger/webfinger.php
+++ b/apps/user_webfinger/webfinger.php
@@ -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($userName == "") {
+ $id = "";
+} else {
+ $id = $userName . '@' . $hostName;
+}
if(isset($_SERVER['HTTPS'])) {
- $baseAddress = 'https://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/';
+ $baseAddress = 'https://';
} else {
- $baseAddress = 'http://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/';
+ $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>
diff --git a/config/config.sample.php b/config/config.sample.php
index 52067374555..199c9248c51 100755
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -15,6 +15,10 @@ $CONFIG = array(
"theme" => "",
"3rdpartyroot" => "",
"3rdpartyurl" => "",
+"knowledgebaseenabled" => true,
+"knowledgebaseurl" => "",
+"appstoreenabled" => true,
+"appstoreurl" => "",
// "datadirectory" => ""
);
?>
diff --git a/core/css/styles.css b/core/css/styles.css
index f5a181c4529..1c50df9e58b 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -131,3 +131,9 @@ 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;}
diff --git a/core/js/eventsource.js b/core/js/eventsource.js
index dece1a69d04..08259e02cae 100644
--- a/core/js/eventsource.js
+++ b/core/js/eventsource.js
@@ -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:[],
diff --git a/core/js/js.js b/core/js/js.js
index df1b5c6ce76..44b4f503b8c 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -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;
diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js
index 9ce2bae1642..7ae9bc9d2ad 100644
--- a/core/js/oc-dialogs.js
+++ b/core/js/oc-dialogs.js
@@ -2,7 +2,7 @@
* ownCloud
*
* @author Bartek Przybylski
- * @copyright 2012 Bartek Przybylski bart.p.pl@gmail.com
+ * @copyright 2012 Bartek Przybylski bartek@alefzero.eu
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -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,33 +59,87 @@ 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
- * fields should be passed in following format: [{text:'prompt text', name:'return name', type:'input type'},...]
+ * fields should be passed in following format: [{text:'prompt text', name:'return name', type:'input type', value: 'dafault value'},...]
* @param fields to display
* @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>';
var type=fields[a].type;
- if (type == 'text' || type == 'checkbox' || type == 'password')
- content += '<input type="'+type+'" name="'+fields[a].name+'">';
- content += "</td></tr>"
+ if (type == 'text' || type == 'checkbox' || type == 'password') {
+ content += '<input type="'+type+'" name="'+fields[a].name+'"';
+ if (type == 'checkbox') {
+ if (fields[a].value != undefined && fields[a].value == true) {
+ content += ' checked="checked">';
+ } 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 += "</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>';
+ 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', function(r){OC.dialogs.fillFilePicker(r, c_id, callback)});
+ }).data('multiselect', multiselect);
+ // 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) {
@@ -100,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)};
@@ -112,7 +165,8 @@ OCdialogs = {
b[0] = {text: t('dialogs', 'Ok'), click: f};
break;
}
- $(c_id).dialog({width: 4*$(document).width()/9, height: $(d).height() + 150, modal: false, buttons: b});
+ var possible_height = ($('tr', d).size()+1)*30;
+ $(c_id).dialog({width: 4*$(document).width()/9, height: possible_height + 120, modal: modal, buttons: b});
OCdialogs.dialogs_counter++;
},
// dialogs buttons types
@@ -127,7 +181,7 @@ OCdialogs = {
dialogs_counter: 0,
determineValue: function(element) {
switch ($(element).attr('type')) {
- case 'checkbox': return $(element).attr('checked') != undefined;
+ case 'checkbox': return element.checked;
}
return $(element).val();
},
@@ -136,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++;
});
@@ -145,5 +199,44 @@ 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);
+ },
+ 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);
+ $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: path}, 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>');
+ $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: p}, function(r){OC.dialogs.fillFilePicker(r, dcid)});
}
};
diff --git a/core/js/oc-vcategories.txt b/core/js/oc-vcategories.txt
index 76d4245f5eb..31216f80bd3 100644
--- a/core/js/oc-vcategories.txt
+++ b/core/js/oc-vcategories.txt
@@ -17,7 +17,7 @@ Set the app specific values in your javascript file. This is what I've used for
OCCategories.app = 'contacts';
OCCategories.changed = Contacts.UI.Card.categoriesChanged;
-If OCCategories.changed point is set that function will be called each time the categories have been changed
+If OCCategories.changed is set that function will be called each time the categories have been changed
in the editor (add/delete/rescan) to allow the app to update the UI accordingly. The only argument to the function
is an array of the updated categories e.g.:
@@ -25,4 +25,9 @@ OCCategories.changed = function(categories) {
for(var category in categories) {
console.log(categories[category]);
}
-} \ No newline at end of file
+}
+
+To show the categories editor call:
+
+ OCCategories.edit()
+
diff --git a/core/lostpassword/index.php b/core/lostpassword/index.php
index da0428e3ced..a9b7d10804f 100644
--- a/core/lostpassword/index.php
+++ b/core/lostpassword/index.php
@@ -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)) {
diff --git a/core/templates/403.php b/core/templates/403.php
new file mode 100644
index 00000000000..cdfef08ac76
--- /dev/null
+++ b/core/templates/403.php
@@ -0,0 +1,15 @@
+<?php
+if(!isset($_)){//also provide standalone error page
+ require_once '../../lib/base.php';
+
+ $tmpl = new OC_Template( '', '403', 'guest' );
+ $tmpl->printPage();
+ exit;
+}
+?>
+<ul>
+ <li class='error'>
+ <?php echo $l->t( 'Access forbidden' ); ?><br/>
+ <p class='hint'><?php if(isset($_['file'])) echo $_['file']?></p>
+ </li>
+</ul>
diff --git a/core/templates/login.php b/core/templates/login.php
index 6c0a7a12227..4ba92221a7d 100644
--- a/core/templates/login.php
+++ b/core/templates/login.php
@@ -1,3 +1,4 @@
+<!--[if IE 8]><style>input[type="checkbox"]{padding:0;}</style><![endif]-->
<form action="index.php" method="post">
<fieldset>
<?php if(!empty($_['redirect'])) { echo '<input type="hidden" name="redirect_url" value="'.$_['redirect'].'" />'; } ?>
@@ -6,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>
diff --git a/db_structure.xml b/db_structure.xml
index 5eef44d8e84..2df218d359c 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -65,6 +65,14 @@
</field>
<field>
+ <name>path_hash</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>32</length>
+ </field>
+
+ <field>
<name>parent</name>
<type>integer</type>
<default>
@@ -79,7 +87,7 @@
<default>
</default>
<notnull>true</notnull>
- <length>512</length>
+ <length>300</length>
</field>
<field>
@@ -159,14 +167,13 @@
<length>1</length>
</field>
- <!--<index>
- <name>fscache_path_index</name>
- <unique>true</unique>
+ <index>
+ <name>fscache_path_hash_index</name>
<field>
- <name>path</name>
+ <name>path_hash</name>
<sorting>ascending</sorting>
</field>
- </index>-->
+ </index>
<index>
<name>parent_index</name>
@@ -177,6 +184,14 @@
</index>
<index>
+ <name>name_index</name>
+ <field>
+ <name>name</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+
+ <index>
<name>parent_name_index</name>
<field>
<name>parent</name>
diff --git a/files/ajax/download.php b/files/ajax/download.php
index 198069f3fa1..39852613ab9 100644
--- a/files/ajax/download.php
+++ b/files/ajax/download.php
@@ -21,6 +21,9 @@
*
*/
+// only need filesystem apps
+$RUNTIME_APPTYPES=array('filesystem');
+
// Init owncloud
require_once('../../lib/base.php');
diff --git a/files/ajax/list.php b/files/ajax/list.php
index 8a414827e1c..ec9ab7342dd 100644
--- a/files/ajax/list.php
+++ b/files/ajax/list.php
@@ -1,5 +1,8 @@
<?php
+// only need filesystem apps
+$RUNTIME_APPTYPES=array('filesystem');
+
// Init owncloud
require_once('../../lib/base.php');
diff --git a/files/ajax/mimeicon.php b/files/ajax/mimeicon.php
index 8724016b3a1..ff72ba0f5b7 100644
--- a/files/ajax/mimeicon.php
+++ b/files/ajax/mimeicon.php
@@ -1,5 +1,8 @@
<?php
+// no need for apps
+$RUNTIME_NOAPPS=false;
+
// Init owncloud
require_once('../../lib/base.php');
diff --git a/files/ajax/rawlist.php b/files/ajax/rawlist.php
new file mode 100644
index 00000000000..88ba48a6c48
--- /dev/null
+++ b/files/ajax/rawlist.php
@@ -0,0 +1,25 @@
+<?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'] : '';
+
+// make filelist
+$files = array();
+foreach( OC_Files::getdirectorycontent( $dir ) 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));
+
+?>
diff --git a/files/ajax/scan.php b/files/ajax/scan.php
index 565275911b4..db09b7d5c64 100644
--- a/files/ajax/scan.php
+++ b/files/ajax/scan.php
@@ -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{
diff --git a/files/css/files.css b/files/css/files.css
index 512e462cb6f..9e950517b82 100644
--- a/files/css/files.css
+++ b/files/css/files.css
@@ -64,8 +64,8 @@ table td.filename .nametext { width:60%; }
table td.filename form { float:left; font-size:.85em; }
table thead.fixed tr{ position:fixed; top:6.5em; z-index:49; -moz-box-shadow:0 -3px 7px #ddd; -webkit-box-shadow:0 -3px 7px #ddd; box-shadow:0 -3px 7px #ddd; }
table thead.fixed { height:2em; }
-#fileList tr td.filename>input[type=checkbox]:first-child { opacity:0; float:left; margin:.7em 0 0 1em; /* bigger clickable area doesn’t work in FF width:2.8em; height:2.4em;*/ -webkit-transition:opacity 500ms; -moz-transition:opacity 500ms; -o-transition:opacity 500ms; transition:opacity 500ms; }
-#fileList tr td.filename>input[type="checkbox"]:hover:first-child { opacity:.8; }
+#fileList tr td.filename>input[type=checkbox]:first-child { opacity:0; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; float:left; margin:.7em 0 0 1em; /* bigger clickable area doesn’t work in FF width:2.8em; height:2.4em;*/ -webkit-transition:opacity 500ms; -moz-transition:opacity 500ms; -o-transition:opacity 500ms; transition:opacity 500ms; }
+#fileList tr td.filename>input[type="checkbox"]:hover:first-child { opacity:.8; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; }
#fileList tr td.filename>input[type="checkbox"]:checked:first-child { opacity:1; }
#fileList tr td.filename { -webkit-transition:background-image 500ms; -moz-transition:background-image 500ms; -o-transition:background-image 500ms; transition:background-image 500ms; }
#select_all { float:left; margin:.3em 0.6em 0 .5em; }
diff --git a/files/js/files.js b/files/js/files.js
index 539d5598899..df9f45a7af4 100644
--- a/files/js/files.js
+++ b/files/js/files.js
@@ -98,7 +98,7 @@ $(document).ready(function() {
procesSelection();
});
- $('td.filename input:checkbox').live('click',function(event) {
+ $('td.filename input:checkbox').live('change',function(event) {
if (event.shiftKey) {
var last = $(lastChecked).parent().parent().prevAll().length;
var first = $(this).parent().parent().prevAll().length;
diff --git a/files/templates/index.php b/files/templates/index.php
index da1e58ce137..f591d066d8c 100644
--- a/files/templates/index.php
+++ b/files/templates/index.php
@@ -1,3 +1,4 @@
+<!--[if IE 8]><style>input[type="checkbox"]{padding:0;}table td{position:static !important;}</style><![endif]-->
<div id="controls">
<?php echo($_['breadcrumb']); ?>
<?php if (!isset($_['readonly']) || !$_['readonly']):?>
@@ -62,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']; ?>" /> \ No newline at end of file
+<input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php echo $_['allowZipDownload']; ?>" />
diff --git a/files/webdav.php b/files/webdav.php
index 6fae33a8f71..25e33024470 100644
--- a/files/webdav.php
+++ b/files/webdav.php
@@ -26,6 +26,9 @@
// Do not load FS ...
$RUNTIME_NOSETUPFS = true;
+// only need filesystem apps
+$RUNTIME_APPTYPES=array('filesystem','authentication');
+
require_once('../lib/base.php');
// Backends
diff --git a/lib/app.php b/lib/app.php
index 64cbe8894ea..7d5e8fa91c6 100644..100755
--- a/lib/app.php
+++ b/lib/app.php
@@ -34,16 +34,20 @@ class OC_App{
static private $settingsForms = array();
static private $adminForms = array();
static private $personalForms = array();
+ static private $appInfo = array();
/**
* @brief loads all apps
+ * @param array $types
* @returns true/false
*
* This function walks through the owncloud directory and loads all apps
* it can find. A directory contains an app if the file /appinfo/app.php
* exists.
+ *
+ * if $types is set, only apps of those types will be loaded
*/
- public static function loadApps(){
+ public static function loadApps($types=null){
// Did we allready load everything?
if( self::$init ){
return true;
@@ -51,13 +55,15 @@ class OC_App{
// Our very own core apps are hardcoded
foreach( array('files', 'settings') as $app ){
- require( $app.'/appinfo/app.php' );
+ if(is_null($types)){
+ require( $app.'/appinfo/app.php' );
+ }
}
// The rest comes here
- $apps = OC_Appconfig::getApps();
+ $apps = self::getEnabledApps();
foreach( $apps as $app ){
- if( self::isEnabled( $app )){
+ if(is_null($types) or self::isType($app,$types)){
if(is_file(OC::$APPSROOT.'/apps/'.$app.'/appinfo/app.php')){
require( $app.'/appinfo/app.php' );
}
@@ -71,6 +77,41 @@ class OC_App{
}
/**
+ * check if an app is of a sepcific type
+ * @param string $app
+ * @param string/array $types
+ */
+ public static function isType($app,$types){
+ if(is_string($types)){
+ $types=array($types);
+ }
+ $appData=self::getAppInfo($app);
+ if(!isset($appData['types'])){
+ return false;
+ }
+ $appTypes=$appData['types'];
+ foreach($types as $type){
+ if(array_search($type,$appTypes)!==false){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * get all enabled apps
+ */
+ public static function getEnabledApps(){
+ $apps=array();
+ $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;
+ }
+
+ /**
* @brief checks whether or not an app is enabled
* @param $app app
* @returns true/false
@@ -210,10 +251,13 @@ class OC_App{
public static function getSettingsNavigation(){
$l=new OC_L10N('core');
+ $settings = array();
// by default, settings only contain the help menu
- $settings = array(
- array( "id" => "help", "order" => 1000, "href" => OC_Helper::linkTo( "settings", "help.php" ), "name" => $l->t("Help"), "icon" => OC_Helper::imagePath( "settings", "help.svg" ))
- );
+ if(OC_Config::getValue('knowledgebaseenabled', true)==true){
+ $settings = array(
+ array( "id" => "help", "order" => 1000, "href" => OC_Helper::linkTo( "settings", "help.php" ), "name" => $l->t("Help"), "icon" => OC_Helper::imagePath( "settings", "help.svg" ))
+ );
+ }
// if the user is logged-in
if (OC_User::isLoggedIn()) {
@@ -262,24 +306,36 @@ class OC_App{
/**
* @brief Read app metadata from the info.xml file
* @param string $appid id of the app or the path of the info.xml file
+ * @param boolean path (optional)
* @returns array
*/
- public static function getAppInfo($appid){
- if(is_file($appid)){
+ public static function getAppInfo($appid,$path=false){
+ if($path){
$file=$appid;
}else{
- $file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/info.xml';
- if(!is_file($file)){
- return array();
+ if(isset(self::$appInfo[$appid])){
+ return self::$appInfo[$appid];
}
+ $file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/info.xml';
}
$data=array();
$content=file_get_contents($file);
+ if(!$content){
+ return;
+ }
$xml = new SimpleXMLElement($content);
$data['info']=array();
foreach($xml->children() as $child){
- $data[$child->getName()]=(string)$child;
+ if($child->getName()=='types'){
+ $data['types']=array();
+ foreach($child->children() as $type){
+ $data['types'][]=$type->getName();
+ }
+ }else{
+ $data[$child->getName()]=(string)$child;
+ }
}
+ self::$appInfo[$appid]=$data;
return $data;
}
@@ -378,9 +434,8 @@ class OC_App{
*/
public static function updateApps(){
// The rest comes here
- $apps = OC_Appconfig::getApps();
- foreach( $apps as $app ){
- $installedVersion=OC_Appconfig::getValue($app,'installed_version');
+ $versions = self::getAppVersions();
+ foreach( $versions as $app=>$installedVersion ){
$appInfo=OC_App::getAppInfo($app);
if (isset($appInfo['version'])) {
$currentVersion=$appInfo['version'];
@@ -393,6 +448,19 @@ class OC_App{
}
/**
+ * get the installed version of all papps
+ */
+ public static function getAppVersions(){
+ $versions=array();
+ $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'];
+ }
+ return $versions;
+ }
+
+ /**
* update the database for the app and call the update script
* @param string appid
*/
diff --git a/lib/base.php b/lib/base.php
index 54cc9f2c731..83dd0c98f45 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -139,6 +139,12 @@ class OC{
OC::$WEBROOT='/'.OC::$WEBROOT;
}
+ // ensure we can find OC_Config
+ set_include_path(
+ OC::$SERVERROOT.'/lib'.PATH_SEPARATOR.
+ get_include_path()
+ );
+
// search the 3rdparty folder
if(OC_Config::getValue('3rdpartyroot', '')<>'' and OC_Config::getValue('3rdpartyurl', '')<>''){
OC::$THIRDPARTYROOT=OC_Config::getValue('3rdpartyroot', '');
@@ -223,10 +229,44 @@ 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'));
+
// set some stuff
//ob_start();
error_reporting(E_ALL | E_STRICT);
@@ -237,6 +277,24 @@ class OC{
date_default_timezone_set('Europe/Berlin');
ini_set('arg_separator.output','&amp;');
+ //try to configure php to enable big file uploads.
+ //this doesn´t work always depending on the webserver and php configuration.
+ //Let´s try to overwrite some defaults anyways
+
+ //try to set the maximum execution time to 60min
+ @set_time_limit(3600);
+ @ini_set('max_execution_time',3600);
+ @ini_set('max_input_time',3600);
+
+ //try to set the maximum filesize to 10G
+ @ini_set('upload_max_filesize','10G');
+ @ini_set('post_max_size','10G');
+ @ini_set('file_uploads','50');
+
+ //try to set the session lifetime to 60min
+ @ini_set('gc_maxlifetime','3600');
+
+
//set http auth headers for apache+php-cgi work around
if (isset($_SERVER['HTTP_AUTHORIZATION']) && preg_match('/Basic\s+(.*)$/i', $_SERVER['HTTP_AUTHORIZATION'], $matches))
{
@@ -252,6 +310,8 @@ class OC{
$_SERVER['PHP_AUTH_USER'] = strip_tags($name);
$_SERVER['PHP_AUTH_PW'] = strip_tags($password);
}
+
+ self::initPaths();
// register the stream wrappers
require_once('streamwrappers.php');
@@ -259,40 +319,12 @@ class OC{
stream_wrapper_register('static', 'OC_StaticStreamWrapper');
stream_wrapper_register('close', 'OC_CloseStreamWrapper');
- self::initPaths();
self::checkInstalled();
self::checkSSL();
- 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" );
+ self::initSession();
+ self::initTemplateEngine();
+ self::checkUpgrade();
$errors=OC_Util::checkServer();
if(count($errors)>0) {
@@ -325,9 +357,17 @@ class OC{
// Load Apps
// This includes plugins for users and filesystems as well
global $RUNTIME_NOAPPS;
+ global $RUNTIME_APPTYPES;
if(!$RUNTIME_NOAPPS ){
- OC_App::loadApps();
+ if($RUNTIME_APPTYPES){
+ OC_App::loadApps($RUNTIME_APPTYPES);
+ }else{
+ 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'));
diff --git a/lib/db.php b/lib/db.php
index 9fab51edfcd..a0fb6c385d8 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -318,9 +318,6 @@ class OC_DB {
// Make changes and save them to an in-memory file
$file2 = 'static://db_scheme';
- if($file2 == ''){
- die('could not create tempfile in get_temp_dir() - aborting');
- }
$content = str_replace( '*dbname*', $CONFIG_DBNAME, $content );
$content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content );
if( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite doesn't
diff --git a/lib/eventsource.php b/lib/eventsource.php
index 523f72403c3..cf10660b94c 100644
--- a/lib/eventsource.php
+++ b/lib/eventsource.php
@@ -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){
diff --git a/lib/filecache.php b/lib/filecache.php
index 280a9929db0..4a4183cbdb5 100644
--- a/lib/filecache.php
+++ b/lib/filecache.php
@@ -59,8 +59,8 @@ class OC_FileCache{
$root='';
}
$path=$root.$path;
- $query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path=?');
- $result=$query->execute(array($path))->fetchRow();
+ $query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path_hash=?');
+ $result=$query->execute(array(md5($path)))->fetchRow();
if(is_array($result)){
return $result;
}else{
@@ -111,8 +111,8 @@ class OC_FileCache{
}
$mimePart=dirname($data['mimetype']);
$user=OC_User::getUser();
- $query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, size, mtime, ctime, mimetype, mimepart,user,writable,encrypted,versioned) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)');
- $result=$query->execute(array($parent,basename($path),$path,$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'],$data['encrypted'],$data['versioned']));
+ $query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, path_hash, size, mtime, ctime, mimetype, mimepart,user,writable,encrypted,versioned) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)');
+ $result=$query->execute(array($parent,basename($path),$path,md5($path),$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'],$data['encrypted'],$data['versioned']));
if(OC_DB::isError($result)){
OC_Log::write('files','error while writing file('.$path.') to cache',OC_Log::ERROR);
}
@@ -162,8 +162,8 @@ class OC_FileCache{
$oldPath=$root.$oldPath;
$newPath=$root.$newPath;
$newParent=self::getParentId($newPath);
- $query=OC_DB::prepare('UPDATE *PREFIX*fscache SET parent=? ,name=?, path=? WHERE path=?');
- $query->execute(array($newParent,basename($newPath),$newPath,$oldPath));
+ $query=OC_DB::prepare('UPDATE *PREFIX*fscache SET parent=? ,name=?, path=?, path_hash=? WHERE path_hash=?');
+ $query->execute(array($newParent,basename($newPath),$newPath,md5($newPath),md5($oldPath)));
}
/**
@@ -285,12 +285,12 @@ class OC_FileCache{
* @return int
*/
private static function getFileId($path){
- $query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE path=?');
+ $query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE path_hash=?');
if(OC_DB::isError($query)){
OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR);
return -1;
}
- $result=$query->execute(array($path));
+ $result=$query->execute(array(md5($path)));
if(OC_DB::isError($result)){
OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR);
return -1;
@@ -367,8 +367,8 @@ class OC_FileCache{
}
}
$path=$root.$path;
- $query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path=?');
- $result=$query->execute(array($path))->fetchRow();
+ $query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path_hash=?');
+ $result=$query->execute(array(md5($path)))->fetchRow();
if(is_array($result)){
if(isset(self::$savedData[$path])){
$result=array_merge($result,self::$savedData[$path]);
@@ -389,8 +389,8 @@ class OC_FileCache{
}
}
$path=$root.$path;
- $query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE path=?');
- $result=$query->execute(array($path));
+ $query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE path_hash=?');
+ $result=$query->execute(array(md5($path)));
if($row=$result->fetchRow()){
return $row['size'];
}else{//file not in cache
@@ -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));
+ }
}
}
}
@@ -579,8 +584,8 @@ class OC_FileCache{
$mtime=$view->filemtime($path);
$isDir=$view->is_dir($path);
$path=$root.$path;
- $query=OC_DB::prepare('SELECT mtime FROM *PREFIX*fscache WHERE path=?');
- $result=$query->execute(array($path));
+ $query=OC_DB::prepare('SELECT mtime FROM *PREFIX*fscache WHERE path_hash=?');
+ $result=$query->execute(array(md5($path)));
if($row=$result->fetchRow()){
$cachedMTime=$row['mtime'];
return ($mtime>$cachedMTime);
@@ -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
diff --git a/lib/files.php b/lib/files.php
index 57ebb9005ad..e7bfbbc19bb 100644
--- a/lib/files.php
+++ b/lib/files.php
@@ -63,8 +63,8 @@ class OC_Files {
$executionTime = intval(ini_get('max_execution_time'));
set_time_limit(0);
$zip = new ZipArchive();
- $filename = get_temp_dir()."/ownCloud.zip";
- if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
+ $filename = get_temp_dir().'/ownCloud_'.mt_rand(10000,99999).'.zip';
+ if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==TRUE) {
exit("cannot open <$filename>\n");
}
foreach($files as $file){
@@ -84,8 +84,8 @@ class OC_Files {
$executionTime = intval(ini_get('max_execution_time'));
set_time_limit(0);
$zip = new ZipArchive();
- $filename = get_temp_dir()."/ownCloud.zip";
- if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
+ $filename = get_temp_dir().'/ownCloud_'.mt_rand(10000,99999).'.zip';
+ if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==TRUE) {
exit("cannot open <$filename>\n");
}
$file=$dir.'/'.$files;
diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php
index 688501aee90..bd757f52ce7 100644
--- a/lib/filestorage/local.php
+++ b/lib/filestorage/local.php
@@ -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;
diff --git a/lib/filesystem.php b/lib/filesystem.php
index 12905d189f9..b6909f5acdf 100644
--- a/lib/filesystem.php
+++ b/lib/filesystem.php
@@ -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.
*/
diff --git a/lib/filesystemview.php b/lib/filesystemview.php
index 89e0385fe9c..9d530c7ad63 100644
--- a/lib/filesystemview.php
+++ b/lib/filesystemview.php
@@ -23,11 +23,11 @@
class OC_FilesystemView {
private $fakeRoot='';
-
+
public function __construct($root){
$this->fakeRoot=$root;
}
-
+
public function getAbsolutePath($path){
if(!$path){
$path='/';
@@ -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){
@@ -282,7 +285,8 @@ class OC_FilesystemView {
if($source){
$extention=substr($path,strrpos($path,'.'));
$tmpFile=OC_Helper::tmpFile($extention);
- return file_put_contents($tmpFile,$source);
+ file_put_contents($tmpFile,$source);
+ return $tmpFile;
}
}
}
diff --git a/lib/helper.php b/lib/helper.php
index 75942f092cf..efff00c2fe6 100644..100755
--- a/lib/helper.php
+++ b/lib/helper.php
@@ -220,7 +220,7 @@ class OC_Helper {
$fullpath = $path.'/'.$file;
if(is_link($fullpath))
return FALSE;
- elseif(!is_dir($fullpath) && !chmod($fullpath, $filemode))
+ elseif(!is_dir($fullpath) && !@chmod($fullpath, $filemode))
return FALSE;
elseif(!self::chmodr($fullpath, $filemode))
return FALSE;
@@ -432,6 +432,19 @@ class OC_Helper {
self::$tmpFiles[]=$file;
return $file;
}
+
+ /**
+ * create a temporary folder with an unique filename
+ * @return string
+ *
+ * temporary files are automatically cleaned up after the script is finished
+ */
+ public static function tmpFolder(){
+ $path=get_temp_dir().'/'.md5(time().rand());
+ mkdir($path);
+ self::$tmpFiles[]=$path;
+ return $path.'/';
+ }
/**
* remove all files created by self::tmpFile
@@ -439,7 +452,7 @@ class OC_Helper {
public static function cleanTmp(){
foreach(self::$tmpFiles as $file){
if(file_exists($file)){
- unlink($file);
+ self::rmdirr($file);
}
}
}
diff --git a/lib/image.php b/lib/image.php
index 60a714880d0..4c53dc32f58 100644
--- a/lib/image.php
+++ b/lib/image.php
@@ -216,7 +216,7 @@ class OC_Image {
OC_Log::write('core','OC_Image->fixOrientation() No readable file path set.', OC_Log::DEBUG);
return false;
}
- $exif = @exif_read_data($this->filepath, 'IFD0');
+ $exif = @exif_read_data($this->filepath, 'IFD0');
if(!$exif) {
return false;
}
@@ -267,6 +267,7 @@ class OC_Image {
if($res) {
if(imagealphablending($res, true)) {
if(imagesavealpha($res, true)) {
+ imagedestroy($this->resource);
$this->resource = $res;
return true;
} else {
@@ -317,10 +318,7 @@ class OC_Image {
*/
public function loadFromFileHandle($handle) {
OC_Log::write('core',__METHOD__.'(): Trying', OC_Log::DEBUG);
- $contents = '';
- while (!feof($handle)) {
- $contents .= fread($handle, 8192);
- }
+ $contents = stream_get_contents($handle);
if($this->loadFromData($contents)) {
return $this->resource;
}
@@ -486,22 +484,24 @@ class OC_Image {
imagedestroy($process);
return false;
}
+ imagedestroy($this->resource);
$this->resource = $process;
return true;
}
/**
* @brief Crops the image to the middle square. If the image is already square it just returns.
+ * @param int maximum size for the result (optional)
* @returns bool for success or failure
*/
- public function centerCrop() {
+ public function centerCrop($size=0) {
if(!$this->valid()) {
OC_Log::write('core','OC_Image->centerCrop, No image loaded', OC_Log::ERROR);
return false;
}
$width_orig=imageSX($this->resource);
$height_orig=imageSY($this->resource);
- if($width_orig === $height_orig) {
+ if($width_orig === $height_orig and $size==0) {
return true;
}
$ratio_orig = $width_orig/$height_orig;
@@ -514,18 +514,26 @@ class OC_Image {
$y = ($height_orig/2) - ($height/2);
$x = 0;
}
- $process = imagecreatetruecolor($width, $height);
+ if($size>0){
+ $targetWidth=$size;
+ $targetHeight=$size;
+ }else{
+ $targetWidth=$width;
+ $targetHeight=$height;
+ }
+ $process = imagecreatetruecolor($targetWidth, $targetHeight);
if ($process == false) {
OC_Log::write('core','OC_Image->centerCrop. Error creating true color image',OC_Log::ERROR);
imagedestroy($process);
return false;
}
- imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $width, $height, $width, $height);
+ imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
if ($process == false) {
OC_Log::write('core','OC_Image->centerCrop. Error resampling process image '.$width.'x'.$height,OC_Log::ERROR);
imagedestroy($process);
return false;
}
+ imagedestroy($this->resource);
$this->resource = $process;
return true;
}
@@ -558,7 +566,19 @@ class OC_Image {
imagedestroy($process);
return false;
}
+ imagedestroy($this->resource);
$this->resource = $process;
return true;
}
+
+ public function destroy(){
+ if($this->valid()){
+ imagedestroy($this->resource);
+ }
+ $this->resource=null;
+ }
+
+ public function __destruct(){
+ $this->destroy();
+ }
}
diff --git a/lib/installer.php b/lib/installer.php
index 2a9676998f6..38e17130e3c 100644
--- a/lib/installer.php
+++ b/lib/installer.php
@@ -62,7 +62,7 @@ class OC_Installer{
//download the file if necesary
if($data['source']=='http'){
- $path=OC_Helper::tmpFile('.zip');
+ $path=OC_Helper::tmpFile();
if(!isset($data['href'])){
OC_Log::write('core','No href specified when installing app from http',OC_Log::ERROR);
return false;
@@ -76,14 +76,24 @@ class OC_Installer{
$path=$data['path'];
}
+ //detect the archive type
+ $mime=OC_Helper::getMimeType($path);
+ if($mime=='application/zip'){
+ rename($path,$path.'.zip');
+ $path.='.zip';
+ }elseif($mime=='application/x-gzip'){
+ rename($path,$path.'.tgz');
+ $path.='.tgz';
+ }else{
+ OC_Log::write('core','Archives of type '.$mime.' are not supported',OC_Log::ERROR);
+ return false;
+ }
+
//extract the archive in a temporary folder
- $extractDir=tempnam(get_temp_dir(),'oc_installer_uncompressed_');
- unlink($extractDir);
+ $extractDir=OC_Helper::tmpFolder();
mkdir($extractDir);
- $zip = new ZipArchive;
- if($zip->open($path)===true){
- $zip->extractTo($extractDir);
- $zip->close();
+ if($archive=OC_Archive::open($path)){
+ $archive->extract($extractDir);
} else {
OC_Log::write('core','Failed to open archive when installing app',OC_Log::ERROR);
OC_Helper::rmdirr($extractDir);
@@ -95,6 +105,17 @@ class OC_Installer{
//load the info.xml file of the app
if(!is_file($extractDir.'/appinfo/info.xml')){
+ //try to find it in a subdir
+ $dh=opendir($extractDir);
+ while($folder=readdir($dh)){
+ if(substr($folder,0,1)!='.' and is_dir($extractDir.'/'.$folder)){
+ if(is_file($extractDir.'/'.$folder.'/appinfo/info.xml')){
+ $extractDir.='/'.$folder;
+ }
+ }
+ }
+ }
+ if(!is_file($extractDir.'/appinfo/info.xml')){
OC_Log::write('core','App does not provide an info.xml file',OC_Log::ERROR);
OC_Helper::rmdirr($extractDir);
if($data['source']=='http'){
@@ -102,7 +123,7 @@ class OC_Installer{
}
return false;
}
- $info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml');
+ $info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml',true);
$basedir=OC::$APPSROOT.'/apps/'.$info['id'];
//check if an app with the same id is already installed
@@ -275,7 +296,7 @@ class OC_Installer{
if(is_file(OC::$APPSROOT."/apps/$app/appinfo/install.php")){
include(OC::$APPSROOT."/apps/$app/appinfo/install.php");
}
- $info=OC_App::getAppInfo(OC::$APPSROOT."/apps/$app/appinfo/info.xml");
+ $info=OC_App::getAppInfo($app);
OC_Appconfig::setValue($app,'installed_version',$info['version']);
return $info;
}
diff --git a/lib/log.php b/lib/log.php
index 4e450a027f5..8bb2839be66 100644
--- a/lib/log.php
+++ b/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);
- }
- }
-
- /**
- * 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]);
+ 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'));
}
- return $entries;
+ $log_class=self::$class;
+ $log_class::write($app, $message, $level);
}
}
diff --git a/lib/log/owncloud.php b/lib/log/owncloud.php
new file mode 100644
index 00000000000..0ed30510134
--- /dev/null
+++ b/lib/log/owncloud.php
@@ -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;
+ }
+}
diff --git a/lib/log/syslog.php b/lib/log/syslog.php
new file mode 100644
index 00000000000..d1fb28d8b0a
--- /dev/null
+++ b/lib/log/syslog.php
@@ -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);
+ }
+}
diff --git a/lib/ocsclient.php b/lib/ocsclient.php
index 9d5932fb720..d830a4f3e7e 100644..100755
--- a/lib/ocsclient.php
+++ b/lib/ocsclient.php
@@ -29,13 +29,45 @@
class OC_OCSClient{
/**
+ * @brief Get the url of the OCS AppStore server.
+ * @returns string of the AppStore server
+ *
+ * This function returns the url of the OCS AppStore server. It´s possible to set it in the config file or it will fallback to the default
+ */
+ private static function getAppStoreURL(){
+ $configurl=OC_Config::getValue('appstoreurl', '');
+ if($configurl<>'') {
+ $url=$configurl;
+ }else{
+ $url='http://api.apps.owncloud.com/v1';
+ }
+ return($url);
+ }
+
+ /**
+ * @brief Get the url of the OCS KB server.
+ * @returns string of the KB server
+ * This function returns the url of the OCS knowledge base server. It´s possible to set it in the config file or it will fallback to the default
+ */
+ private static function getKBURL(){
+ $configurl=OC_Config::getValue('knowledgebaseurl', '');
+ if($configurl<>'') {
+ $url=$configurl;
+ }else{
+ $url='http://api.apps.owncloud.com/v1';
+ }
+ return($url);
+ }
+
+
+ /**
* @brief Get all the categories from the OCS server
* @returns array with category ids
*
* This function returns a list of all the application categories on the OCS server
*/
public static function getCategories(){
- $url='http://api.apps.owncloud.com/v1/content/categories';
+ $url=OC_OCSClient::getAppStoreURL().'/content/categories';
$xml=@file_get_contents($url);
if($xml==FALSE){
@@ -64,12 +96,16 @@ class OC_OCSClient{
* This function returns a list of all the applications on the OCS server
*/
public static function getApplications($categories){
+ if(OC_Config::getValue('appstoreenabled', true)==false){
+ return(array());
+ }
+
if(is_array($categories)) {
$categoriesstring=implode('x',$categories);
}else{
$categoriesstring=$categories;
}
- $url='http://api.apps.owncloud.com/v1/content/data?categories='.urlencode($categoriesstring).'&sortmode=new&page=0&pagesize=10';
+ $url=OC_OCSClient::getAppStoreURL().'/content/data?categories='.urlencode($categoriesstring).'&sortmode=new&page=0&pagesize=10';
$apps=array();
$xml=@file_get_contents($url);
if($xml==FALSE){
@@ -104,7 +140,7 @@ class OC_OCSClient{
* This function returns an applications from the OCS server
*/
public static function getApplication($id){
- $url='http://api.apps.owncloud.com/v1/content/data/'.urlencode($id);
+ $url=OC_OCSClient::getAppStoreURL().'/content/data/'.urlencode($id);
$xml=@file_get_contents($url);
if($xml==FALSE){
@@ -137,7 +173,7 @@ class OC_OCSClient{
* This function returns an download url for an applications from the OCS server
*/
public static function getApplicationDownload($id,$item){
- $url='http://api.apps.owncloud.com/v1/content/download/'.urlencode($id).'/'.urlencode($item);
+ $url=OC_OCSClient::getAppStoreURL().'/content/download/'.urlencode($id).'/'.urlencode($item);
$xml=@file_get_contents($url);
if($xml==FALSE){
@@ -164,9 +200,15 @@ class OC_OCSClient{
* This function returns a list of all the knowledgebase entries from the OCS server
*/
public static function getKnownledgebaseEntries($page,$pagesize){
+ if(OC_Config::getValue('knowledgebaseenabled', true)==false){
+ $kbe=array();
+ $kbe['totalitems']=0;
+ return $kbe;
+ }
+
$p= (int) $page;
$s= (int) $pagesize;
- $url='http://api.apps.owncloud.com/v1/knowledgebase/data?type=150&page='.$p.'&pagesize='.$s;
+ $url=OC_OCSClient::getKBURL().'/knowledgebase/data?type=150&page='.$p.'&pagesize='.$s;
$kbe=array();
$xml=@file_get_contents($url);
diff --git a/lib/updater.php b/lib/updater.php
index 57623797ae5..196822ac35d 100644
--- a/lib/updater.php
+++ b/lib/updater.php
@@ -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
diff --git a/lib/util.php b/lib/util.php
index fa5b3daaab6..722b7404d0c 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -66,7 +66,7 @@ class OC_Util {
* @return array
*/
public static function getVersion(){
- return array(3,00,3);
+ return array(3,00,4);
}
/**
@@ -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
*
diff --git a/settings/ajax/getlog.php b/settings/ajax/getlog.php
index 600ebefcece..ed48b2cae1a 100644
--- a/settings/ajax/getlog.php
+++ b/settings/ajax/getlog.php
@@ -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));
diff --git a/settings/js/log.js b/settings/js/log.js
index 3814d9c10bf..ae83f0a6283 100644
--- a/settings/js/log.js
+++ b/settings/js/log.js
@@ -32,7 +32,7 @@ OC.Log={
row.append(messageTd);
var timeTd=$('<td/>');
- timeTd.text(formatDate(entry.time));
+ timeTd.text(formatDate(entry.time*1000));
row.append(timeTd);
$('#log').append(row);
}
diff --git a/settings/log.php b/settings/log.php
index 946f2b6f8e5..ddbf72c4433 100644
--- a/settings/log.php
+++ b/settings/log.php
@@ -28,7 +28,7 @@ OC_Util::addStyle( "settings", "settings" );
OC_Util::addScript( "settings", "apps" );
OC_App::setActiveNavigationEntry( "core_log" );
-$entries=OC_Log::getEntries();
+$entries=OC_Log_Owncloud::getEntries();
OC_Util::addScript('settings','log');
OC_Util::addStyle('settings','settings');
diff --git a/settings/templates/personal.php b/settings/templates/personal.php
index 57731d979d9..d40da7eb773 100644
--- a/settings/templates/personal.php
+++ b/settings/templates/personal.php
@@ -50,7 +50,7 @@
};?>
<p class="personalblock">
- <strong>ownCloud</strong> <?php echo(OC_Util::getVersionString()); ?><br />
+ <strong>ownCloud</strong> <?php echo(OC_Util::getVersionString()); ?> <?php echo(OC_Util::getEditionString()); ?><br />
developed by the <a href="http://ownCloud.org/credits" target="_blank">ownCloud community</a><br />
<?php echo(OC_Updater::ShowUpdatingHint()); ?><br />
<a href="http://gitorious.org/owncloud" target="_blank">source code</a> licensed freely under <a href="http://www.gnu.org/licenses/agpl-3.0.html" target="_blank">AGPL</a>
diff --git a/status.php b/status.php
index 94c8cfce842..81f339fa53f 100644
--- a/status.php
+++ b/status.php
@@ -26,7 +26,7 @@ $RUNTIME_NOAPPS = TRUE; //no apps, yet
require_once('lib/base.php');
if(OC_Config::getValue('installed')==1) $installed='true'; else $installed='false';
-$values=array('installed'=>$installed,'version'=>implode('.',OC_Util::getVersion()),'versionstring'=>OC_Util::getVersionString());
+$values=array('installed'=>$installed,'version'=>implode('.',OC_Util::getVersion()),'versionstring'=>OC_Util::getVersionString(),'edition'=>OC_Util::getEditionString());
echo(json_encode($values));
diff --git a/tests/index.php b/tests/index.php
index 2e86366740b..a6f678b3bc2 100644
--- a/tests/index.php
+++ b/tests/index.php
@@ -38,6 +38,7 @@ foreach($apps as $app){
}
function loadTests($dir=''){
+ $test=isset($_GET['test'])?$_GET['test']:false;
if($dh=opendir($dir)){
while($name=readdir($dh)){
if(substr($name,0,1)!='.'){//no hidden files, '.' or '..'
@@ -45,10 +46,13 @@ function loadTests($dir=''){
if(is_dir($file)){
loadTests($file);
}elseif(substr($file,-4)=='.php' and $file!=__FILE__){
- $testCase=new TestSuite(getTestName($file));
- $testCase->addFile($file);
- if($testCase->getSize()>0){
- $testCase->run(new HtmlReporter());
+ $name=getTestName($file);
+ if($test===false or $test==$name or substr($name,0,strlen($test))==$test){
+ $testCase=new TestSuite($name);
+ $testCase->addFile($file);
+ if($testCase->getSize()>0){
+ $testCase->run(new HtmlReporter());
+ }
}
}
}