2012-06-16 00:52:56 +00:00
< ? 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 />.
*/
namespace OCP ;
/**
* This class provides the ability for apps to share their content between users .
* Apps must create a backend class that extends OCP\Share_Backend and register it with this class .
*/
class Share {
2012-06-21 16:07:11 +00:00
const SHARE_TYPE_USER = 0 ;
const SHARE_TYPE_GROUP = 1 ;
2012-06-24 23:16:50 +00:00
const SHARE_TYPE_PRIVATE_LINK = 3 ;
2012-06-16 00:52:56 +00:00
2012-07-14 13:35:39 +00:00
/** CRUDS permissions ( Create , Read , Update , Delete , Share ) using a bitmask
* Construct permissions for share () and setPermissions with Or ( | ) e . g . Give user read and update permissions : PERMISSION_READ | PERMISSION_UPDATE
* Check if permission is granted with And ( & ) e . g . Check if delete is granted : if ( $permissions & PERMISSION_DELETE )
* Remove permissions with And ( & ) and Not ( ~ ) e . g . Remove the update permission : $permissions &= ~ PERMISSION_UPDATE
* Apps are required to handle permissions on their own , this class only stores and manages the permissions of shares
*/
const PERMISSION_CREATE = 4 ;
const PERMISSION_READ = 1 ;
const PERMISSION_UPDATE = 2 ;
const PERMISSION_DELETE = 8 ;
const PERMISSION_SHARE = 16 ;
2012-06-21 16:07:11 +00:00
2012-06-22 21:43:04 +00:00
const FORMAT_NONE = - 1 ;
2012-06-24 23:16:50 +00:00
const FORMAT_STATUSES = - 2 ;
2012-06-22 21:43:04 +00:00
2012-06-21 16:07:11 +00:00
private static $shareTypeUserAndGroups = - 1 ;
2012-06-24 23:16:50 +00:00
private static $shareTypeGroupUserUnique = 2 ;
2012-06-16 00:52:56 +00:00
private static $backends = array ();
2012-06-30 21:25:21 +00:00
private static $backendTypes = array ();
2012-06-16 00:52:56 +00:00
/**
* @ brief Register a sharing backend class that extends OCP\Share_Backend for an item type
* @ param string Item type
2012-06-30 21:25:21 +00:00
* @ param string Backend class
2012-06-16 00:52:56 +00:00
* @ param string ( optional ) Depends on item type
* @ param array ( optional ) List of supported file extensions if this item type depends on files
* @ return Returns true if backend is registered or false if error
*/
2012-07-10 22:56:22 +00:00
public static function registerBackend ( $itemType , $class , $collectionOf = null , $supportedFileExtensions = null ) {
2012-06-30 21:25:21 +00:00
if ( ! isset ( self :: $backendTypes [ $itemType ])) {
2012-07-10 22:56:22 +00:00
self :: $backendTypes [ $itemType ] = array ( 'class' => $class , 'collectionOf' => $collectionOf , 'supportedFileExtensions' => $supportedFileExtensions );
2012-06-30 21:25:21 +00:00
return true ;
2012-06-16 00:52:56 +00:00
}
2012-06-30 21:25:21 +00:00
\OC_Log :: write ( 'OCP\Share' , 'Sharing backend ' . $class . ' not registered, ' . self :: $backendTypes [ $itemType ][ 'class' ] . ' is already registered for ' . $itemType , \OC_Log :: WARN );
2012-06-16 00:52:56 +00:00
return false ;
}
/**
* @ brief Get the items of item type shared with the current user
* @ param string Item type
2012-06-22 21:43:04 +00:00
* @ param int Format ( optional ) Format type must be defined by the backend
* @ param int Number of items to return ( optional ) Returns all by default
* @ return Return depends on format
2012-06-16 00:52:56 +00:00
*/
2012-07-10 22:56:22 +00:00
public static function getItemsSharedWith ( $itemType , $format = self :: FORMAT_NONE , $parameters = null , $limit = - 1 ) {
return self :: getItems ( $itemType , null , self :: $shareTypeUserAndGroups , \OC_User :: getUser (), null , $format , $parameters , $limit );
2012-06-16 00:52:56 +00:00
}
/**
* @ brief Get the item of item type shared with the current user
* @ param string Item type
2012-06-22 21:43:04 +00:00
* @ param string Item target
* @ param int Format ( optional ) Format type must be defined by the backend
* @ return Return depends on format
2012-06-16 00:52:56 +00:00
*/
2012-07-10 22:56:22 +00:00
public static function getItemSharedWith ( $itemType , $itemTarget , $format = self :: FORMAT_NONE , $parameters = null ) {
return self :: getItems ( $itemType , $itemTarget , self :: $shareTypeUserAndGroups , \OC_User :: getUser (), null , $format , $parameters , 1 );
2012-06-16 00:52:56 +00:00
}
2012-07-03 15:11:01 +00:00
/**
* @ brief Get the item of item type shared with the current user by source
* @ param string Item type
* @ param string Item source
* @ param int Format ( optional ) Format type must be defined by the backend
* @ return Return depends on format
*/
2012-07-10 22:56:22 +00:00
public static function getItemSharedWithBySource ( $itemType , $itemSource , $format = self :: FORMAT_NONE , $parameters = null ) {
return self :: getItems ( $itemType , $itemSource , self :: $shareTypeUserAndGroups , \OC_User :: getUser (), null , $format , $parameters , 1 , true );
2012-07-03 15:11:01 +00:00
}
2012-06-16 00:52:56 +00:00
/**
* @ brief Get the shared items of item type owned by the current user
* @ param string Item type
2012-06-22 21:43:04 +00:00
* @ param int Format ( optional ) Format type must be defined by the backend
* @ param int Number of items to return ( optional ) Returns all by default
* @ return Return depends on format
2012-06-16 00:52:56 +00:00
*/
2012-07-10 22:56:22 +00:00
public static function getItemsShared ( $itemType , $format = self :: FORMAT_NONE , $parameters = null , $limit = - 1 ) {
return self :: getItems ( $itemType , null , null , null , \OC_User :: getUser (), $format , $parameters , $limit );
2012-06-16 00:52:56 +00:00
}
/**
* @ brief Get the shared item of item type owned by the current user
* @ param string Item type
2012-06-22 21:43:04 +00:00
* @ param string Item
* @ param int Format ( optional ) Format type must be defined by the backend
* @ return Return depends on format
2012-06-16 00:52:56 +00:00
*/
2012-07-10 22:56:22 +00:00
public static function getItemShared ( $itemType , $item , $format = self :: FORMAT_NONE , $parameters = null ) {
return self :: getItems ( $itemType , $item , null , null , \OC_User :: getUser (), $format , $parameters );
2012-06-16 00:52:56 +00:00
}
2012-07-03 15:11:01 +00:00
/**
* @ brief Get the shared item of item type owned by the current user by source
* @ param string Item type
* @ param string Item source
* @ param int Format ( optional ) Format type must be defined by the backend
* @ return Return depends on format
*/
2012-07-10 22:56:22 +00:00
public static function getItemSharedBySource ( $itemType , $item , $format = self :: FORMAT_NONE , $parameters = null ) {
return self :: getItems ( $itemType , $item , null , null , \OC_User :: getUser (), $format , $parameters , - 1 , true );
2012-07-03 15:11:01 +00:00
}
2012-06-16 00:52:56 +00:00
/**
* @ brief Share an item with a user , group , or via private link
* @ param string Item type
* @ param string Item
2012-07-14 14:19:18 +00:00
* @ param int SHARE_TYPE_USER , SHARE_TYPE_GROUP , or SHARE_TYPE_PRIVATE_LINK
2012-06-16 00:52:56 +00:00
* @ param string User or group the item is being shared with
2012-07-14 13:35:39 +00:00
* @ param int CRUDS permissions
2012-07-14 14:19:18 +00:00
* @ return bool Returns true on success or false on failure
2012-06-16 00:52:56 +00:00
*/
public static function share ( $itemType , $item , $shareType , $shareWith , $permissions ) {
$uidOwner = \OC_User :: getUser ();
// Verify share type and sharing conditions are met
switch ( $shareType ) {
2012-06-21 16:07:11 +00:00
case self :: SHARE_TYPE_USER :
2012-06-16 00:52:56 +00:00
if ( $shareWith == $uidOwner ) {
\OC_Log :: write ( 'OCP\Share' , 'Sharing ' . $item . ' failed, because the user ' . $shareWith . ' is the item owner' , \OC_Log :: ERROR );
return false ;
}
if ( ! \OC_User :: userExists ( $shareWith )) {
\OC_Log :: write ( 'OCP\Share' , 'Sharing ' . $item . ' failed, because the user ' . $shareWith . ' does not exist' , \OC_Log :: ERROR );
return false ;
} else {
$inGroup = array_intersect ( \OC_Group :: getUserGroups ( $uidOwner ), \OC_Group :: getUserGroups ( $shareWith ));
if ( empty ( $inGroup )) {
\OC_Log :: write ( 'OCP\Share' , 'Sharing ' . $item . ' failed, because the user ' . $shareWith . ' is not a member of any groups that ' . $uidOwner . ' is a member of' , \OC_Log :: ERROR );
return false ;
}
}
break ;
2012-06-21 16:07:11 +00:00
case self :: SHARE_TYPE_GROUP :
2012-06-16 00:52:56 +00:00
if ( ! \OC_Group :: groupExists ( $shareWith )) {
\OC_Log :: write ( 'OCP\Share' , 'Sharing ' . $item . ' failed, because the group ' . $shareWith . ' does not exist' , \OC_Log :: ERROR );
return false ;
} else if ( ! \OC_Group :: inGroup ( $uidOwner , $shareWith )) {
\OC_Log :: write ( 'OCP\Share' , 'Sharing ' . $item . ' failed, because ' . $uidOwner . ' is not a member of the group ' . $shareWith , \OC_Log :: ERROR );
return false ;
}
2012-06-21 16:07:11 +00:00
// Convert share with into an array with the keys group and users
$group = $shareWith ;
$shareWith = array ();
$shareWith [ 'group' ] = $group ;
2012-06-25 17:21:01 +00:00
$shareWith [ 'users' ] = array_diff ( \OC_Group :: usersInGroup ( $group ), array ( $uidOwner ));
2012-06-16 00:52:56 +00:00
break ;
2012-07-11 16:18:59 +00:00
case self :: SHARE_TYPE_PRIVATE_LINK :
$shareWith = md5 ( uniqid ( $item , true ));
return self :: put ( $itemType , $item , $shareType , $shareWith , $uidOwner , $permissions );
2012-06-22 22:00:10 +00:00
// Future share types need to include their own conditions
2012-06-16 00:52:56 +00:00
default :
\OC_Log :: write ( 'OCP\Share' , 'Share type ' . $shareType . ' is not valid for ' . $item , \OC_Log :: ERROR );
return false ;
}
2012-07-12 15:59:48 +00:00
if ( self :: getItems ( $itemType , $item , $shareType , $shareWith , $uidOwner , self :: FORMAT_NONE , null , 1 )) {
\OC_Log :: write ( 'OCP\Share' , 'Sharing ' . $item . ' failed, because this item is already shared with ' . $shareWith , \OC_Log :: ERROR );
return false ;
}
if ( $collectionTypes = self :: getCollectionItemTypes ( $itemType )) {
foreach ( $collectionTypes as $collectionType ) {
$collections = self :: getItems ( $collectionType , null , self :: SHARE_TYPE_USER , $shareWith , $uidOwner );
if ( $backend = self :: getBackend ( $collectionType )) {
if ( $backend -> inCollection ( $collections , $item )) {
\OC_Log :: write ( 'OCP\Share' , 'Sharing ' . $item . ' failed, because this item is already shared with ' . $shareWith . ' inside a collection' , \OC_Log :: ERROR );
return false ;
}
}
}
}
2012-06-16 00:52:56 +00:00
// If the item is a folder, scan through the folder looking for equivalent item types
if ( $itemType == 'folder' ) {
2012-06-21 16:07:11 +00:00
$parentFolder = self :: put ( 'folder' , $item , $shareType , $shareWith , $uidOwner , $permissions , true );
2012-06-16 00:52:56 +00:00
if ( $parentFolder && $files = \OC_Files :: getDirectoryContent ( $item )) {
for ( $i = 0 ; $i < count ( $files ); $i ++ ) {
$name = substr ( $files [ $i ][ 'name' ], strpos ( $files [ $i ][ 'name' ], $item ) - strlen ( $item ));
2012-07-10 22:56:22 +00:00
if ( $files [ $i ][ 'mimetype' ] == 'httpd/unix-directory' && $children = \OC_Files :: getDirectoryContent ( $name , '/' )) {
2012-06-16 00:52:56 +00:00
// Continue scanning into child folders
array_push ( $files , $children );
} else {
// Pass on to put() to check if this item should be converted, the item won't be inserted into the database unless it can be converted
2012-06-21 16:07:11 +00:00
self :: put ( 'file' , $name , $shareType , $shareWith , $uidOwner , $permissions , $parentFolder );
2012-06-16 00:52:56 +00:00
}
}
return $return ;
}
return false ;
} else {
// Put the item into the database
2012-06-21 16:07:11 +00:00
return self :: put ( $itemType , $item , $shareType , $shareWith , $uidOwner , $permissions );
2012-06-16 00:52:56 +00:00
}
}
/**
* @ brief Unshare an item from a user , group , or delete a private link
* @ param string Item type
* @ param string Item
2012-07-14 14:19:18 +00:00
* @ param int SHARE_TYPE_USER , SHARE_TYPE_GROUP , or SHARE_TYPE_PRIVATE_LINK
2012-06-16 00:52:56 +00:00
* @ param string User or group the item is being shared with
* @ return Returns true on success or false on failure
*/
public static function unshare ( $itemType , $item , $shareType , $shareWith ) {
2012-07-11 01:29:34 +00:00
if ( $item = self :: getItems ( $itemType , $item , $shareType , $shareWith , \OC_User :: getUser (), self :: FORMAT_NONE , null , 1 )) {
2012-06-18 19:18:30 +00:00
self :: delete ( $item [ 'id' ]);
2012-06-16 00:52:56 +00:00
return true ;
}
return false ;
}
/**
* @ brief
* @ param
* @ param
* @ return
*/
public static function unshareFromSelf ( $itemType , $itemTarget ) {
$uidSharedWith = \OC_User :: getUser ();
if ( $item = self :: getItems ( $itemType , $itemTarget , $uidSharedWith , true , null , false , 1 )) {
2012-06-19 18:21:44 +00:00
// Check if item is inside a shared folder and was converted
2012-06-16 00:52:56 +00:00
if ( $item [ 'parent' ]) {
2012-06-25 00:12:33 +00:00
$query = \OC_DB :: prepare ( 'SELECT item_type FROM *PREFIX*share WHERE id = ? LIMIT 1' );
2012-06-16 00:52:56 +00:00
$result = $query -> execute ( array ( $item [ 'parent' ])) -> fetchRow ();
2012-06-21 16:07:11 +00:00
if ( isset ( $result [ 'item_type' ]) && $result [ 'item_type' ] == 'folder' ) {
2012-06-16 00:52:56 +00:00
return false ;
}
}
// Check if this is a group share, if it is a group share a new entry needs to be created marked as unshared from self
if ( $item [ 'uid_shared_with' ] == null ) {
2012-06-25 00:12:33 +00:00
$query = \OC_DB :: prepare ( 'INSERT INTO *PREFIX*share VALUES(?,?,?,?,?,?,?,?,?,?)' );
2012-06-16 00:52:56 +00:00
$result = $query -> execute ( array ( $item [ 'item_type' ], $item [ 'item_source' ], $item [ 'item_target' ], $uidSharedWith , $item [ 'gid_shared_with' ], $item [ 'uid_owner' ], self :: UNSHARED_FROM_SELF , $item [ 'stime' ], $item [ 'file_source' ], $item [ 'file_target' ]));
if ( \OC_DB :: isError ( $result )) {
// \OC_Log::write('OCP\Share', 'Share type '.$shareType.' is not valid for item '.$item, \OC_Log::ERROR);
return false ;
}
}
2012-06-18 19:18:30 +00:00
return self :: delete ( $item [ 'id' ], true );
2012-06-16 00:52:56 +00:00
}
return false ;
}
/**
* @ brief Set the target name of the item for the current user
* @ param string Item type
* @ param string Old item name
* @ param string New item name
* @ return Returns true on success or false on failure
*/
public static function setTarget ( $itemType , $oldTarget , $newTarget ) {
if ( $backend = self :: getBackend ( $itemType )) {
$uidSharedWith = \OC_User :: getUser ();
2012-06-22 22:00:10 +00:00
// TODO Check permissions for setting target?
2012-07-03 18:03:04 +00:00
if ( $item = self :: getItems ( $itemType , $oldTarget , self :: SHARE_TYPE_USER , $uidSharedWith , null , self :: FORMAT_NONE , 1 )) {
2012-06-16 00:52:56 +00:00
// Check if this is a group share
if ( $item [ 'uid_shared_with' ] == null ) {
// A new entry needs to be created exclusively for the user
2012-06-25 00:12:33 +00:00
$query = \OC_DB :: prepare ( 'INSERT INTO *PREFIX*share VALUES(?,?,?,?,?,?,?,?,?,?)' );
2012-06-16 00:52:56 +00:00
if ( isset ( $item [ 'file_target' ])) {
$fileTarget = $newTarget ;
} else {
$fileTarget = null ;
}
$query -> execute ( array ( $itemType , $item [ 'item_source' ], $newTarget , $uidSharedWith , $item [ 'gid_shared_with' ], $item [ 'uid_owner' ], $item [ 'permissions' ], $item [ 'stime' ], $item [ 'file_source' ], $fileTarget ));
return true ;
} else {
// Check if this item is a file or folder, update the file_target as well if this is the case
if ( $itemType == 'file' || $itemType == 'folder' ) {
2012-06-25 00:12:33 +00:00
$query = \OC_DB :: prepare ( 'UPDATE *PREFIX*share SET item_target = ?, file_target = REPLACE(file_target, ?, ?) WHERE uid_shared_with = ?' );
2012-06-16 00:52:56 +00:00
$query -> execute ( array ( $newTarget , $oldTarget , $newTarget , $uidSharedWith ));
} else {
2012-06-25 00:12:33 +00:00
$query = \OC_DB :: prepare ( 'UPDATE *PREFIX*share SET item_target = ? WHERE item_type = ? AND item_target = ? AND uid_shared_with = ?' );
2012-06-16 00:52:56 +00:00
$query -> execute ( array ( $newTarget , $itemType , $oldTarget , $uidSharedWith ));
}
return true ;
}
}
}
return false ;
}
/**
* @ brief Set the permissions of an item for a specific user or group
* @ param string Item type
* @ param string Item
2012-07-14 14:19:18 +00:00
* @ param int SHARE_TYPE_USER , SHARE_TYPE_GROUP , or SHARE_TYPE_PRIVATE_LINK
2012-06-16 00:52:56 +00:00
* @ param string User or group the item is being shared with
2012-07-14 13:35:39 +00:00
* @ param int CRUDS permissions
2012-06-16 00:52:56 +00:00
* @ return Returns true on success or false on failure
*/
public static function setPermissions ( $itemType , $item , $shareType , $shareWith , $permissions ) {
2012-07-11 01:29:34 +00:00
if ( $item = self :: getItems ( $itemType , $item , $shareType , $shareWith , \OC_User :: getUser (), self :: FORMAT_NONE , null , 1 )) {
2012-06-16 00:52:56 +00:00
// Check if this item is a reshare and verify that the permissions granted don't exceed the parent shared item
if ( isset ( $item [ 'parent' ])) {
2012-06-25 00:12:33 +00:00
$query = \OC_DB :: prepare ( 'SELECT permissions FROM *PREFIX*share WHERE id = ? LIMIT 1' );
2012-06-16 00:52:56 +00:00
$result = $query -> execute ( array ( $item [ 'parent' ])) -> fetchRow ();
2012-07-14 13:35:39 +00:00
if ( ~ ( int ) $result [ 'permissions' ] & $permissions ) {
\OC_Log :: write ( 'OCP\Share' , 'Setting permissions for ' . $item . ' failed, because the permissions exceed permissions granted to the parent item' , \OC_Log :: ERROR );
2012-06-16 00:52:56 +00:00
return false ;
}
}
2012-06-25 00:12:33 +00:00
$query = \OC_DB :: prepare ( 'UPDATE *PREFIX*share SET permissions = ? WHERE id = ?' );
2012-06-16 00:52:56 +00:00
$query -> execute ( array ( $permissions , $item [ 'id' ]));
2012-07-14 13:35:39 +00:00
// Check if permissions were removed
if ( $item [ 'permissions' ] & ~ $permissions ) {
// If share permission is removed all reshares must be deleted
if (( $item [ 'permissions' ] & self :: PERMISSION_SHARE ) && ( ~ $permissions & self :: PERMISSION_SHARE )) {
self :: delete ( $item [ 'id' ], true );
} else {
$ids = array ();
$parents = array ( $item [ 'id' ]);
while ( ! empty ( $parents )) {
$parents = " ' " . implode ( " ',' " , $parents ) . " ' " ;
$query = \OC_DB :: prepare ( 'SELECT id, permissions FROM *PREFIX*share WHERE parent IN (' . $parents . ')' );
$result = $query -> execute ();
// Reset parents array, only go through loop again if items are found that need permissions removed
$parents = array ();
while ( $item = $result -> fetchRow ()) {
// Check if permissions need to be removed
if ( $item [ 'permissions' ] & ~ $permissions ) {
// Add to list of items that need permissions removed
$ids [] = $item [ 'id' ];
$parents [] = $item [ 'id' ];
}
}
}
// Remove the permissions for all reshares of this item
if ( ! empty ( $ids )) {
$ids = " ' " . implode ( " ',' " , $ids ) . " ' " ;
$query = \OC_DB :: prepare ( 'UPDATE *PREFIX*share SET permissions = permissions & ? WHERE id IN (' . $ids . ')' );
$query -> execute ( array ( $permissions ));
2012-06-16 00:52:56 +00:00
}
}
}
return true ;
}
2012-07-14 13:35:39 +00:00
\OC_Log :: write ( 'OCP\Share' , 'Setting permissions for ' . $item . ' failed, because the item was not found' , \OC_Log :: ERROR );
2012-06-16 00:52:56 +00:00
return false ;
}
2012-06-19 18:21:44 +00:00
/**
2012-06-16 00:52:56 +00:00
* @ brief Get the backend class for the specified item type
* @ param string Item type
* @ return Sharing backend object
*/
private static function getBackend ( $itemType ) {
2012-06-25 02:17:54 +00:00
if ( isset ( self :: $backends [ $itemType ])) {
return self :: $backends [ $itemType ];
2012-06-30 21:25:21 +00:00
} else if ( isset ( self :: $backendTypes [ $itemType ][ 'class' ])) {
$class = self :: $backendTypes [ $itemType ][ 'class' ];
if ( class_exists ( $class )) {
self :: $backends [ $itemType ] = new $class ;
if ( ! is_subclass_of ( self :: $backends [ $itemType ], 'OCP\Share_Backend' )) {
\OC_Log :: write ( 'OCP\Share' , 'Sharing backend ' . $class . ' must extend abstract class OC_Share_Backend' , \OC_Log :: ERROR );
return false ;
}
return self :: $backends [ $itemType ];
} else {
\OC_Log :: write ( 'OCP\Share' , 'Sharing backend ' . $class . ' not found' , \OC_Log :: ERROR );
return false ;
}
2012-06-16 00:52:56 +00:00
}
\OC_Log :: write ( 'OCP\Share' , 'Sharing backend for ' . $itemType . ' not found' , \OC_Log :: ERROR );
return false ;
}
/**
2012-07-10 22:56:22 +00:00
* @ brief Get a list of collection item types for the specified item type
2012-06-16 00:52:56 +00:00
* @ param string Item type
* @ return array
*/
2012-07-10 22:56:22 +00:00
private static function getCollectionItemTypes ( $itemType ) {
2012-07-12 14:52:30 +00:00
$collectionTypes = array ( $itemType );
2012-07-10 22:56:22 +00:00
foreach ( self :: $backendTypes as $type => $backend ) {
2012-07-12 14:52:30 +00:00
if ( in_array ( $backend [ 'collectionOf' ], $collectionTypes )) {
$collectionTypes [] = $type ;
2012-06-16 00:52:56 +00:00
}
}
2012-07-12 14:52:30 +00:00
if ( count ( $collectionTypes ) > 1 ) {
unset ( $collectionTypes [ 0 ]);
return $collectionTypes ;
2012-06-16 00:52:56 +00:00
}
return false ;
}
/**
* @ brief Get shared items from the database
* @ param string Item type
2012-06-22 21:43:04 +00:00
* @ param string Item or item target ( optional )
2012-07-14 14:19:18 +00:00
* @ param int SHARE_TYPE_USER , SHARE_TYPE_GROUP , SHARE_TYPE_PRIVATE_LINK , $shareTypeUserAndGroups , or $shareTypeGroupUserUnique
* @ param string User or group the item is being shared with
2012-06-16 00:52:56 +00:00
* @ param string User that is the owner of shared items ( optional )
2012-07-14 14:19:18 +00:00
* @ param int Format to convert items to with formatItems ()
* @ param mixed Parameters to pass to formatItems ()
2012-06-16 00:52:56 +00:00
* @ param int Number of items to return , - 1 to return all matches ( optional )
2012-07-14 14:19:18 +00:00
* @ param bool Is item the source ( optional )
* @ return mixed
2012-06-16 00:52:56 +00:00
*
* See public functions getItem ( s ) ... for parameter usage
*
*/
2012-07-10 22:56:22 +00:00
private static function getItems ( $itemType , $item = null , $shareType = null , $shareWith = null , $uidOwner = null , $format = self :: FORMAT_NONE , $parameters = null , $limit = - 1 , $isSource = false ) {
2012-06-16 00:52:56 +00:00
if ( $backend = self :: getBackend ( $itemType )) {
// Check if there are any parent types that include this type of items, e.g. a music album contains songs
2012-07-12 14:52:30 +00:00
if ( $collectionTypes = self :: getCollectionItemTypes ( $itemType )) {
$collectionTypes = array_merge ( array ( $itemType ), $collectionTypes );
$where = " WHERE item_type IN (' " . implode ( " ',' " , $collectionTypes ) . " ') " ;
2012-07-10 22:56:22 +00:00
} else {
$where = " WHERE item_type = ' " . $itemType . " ' " ;
2012-06-16 00:52:56 +00:00
}
2012-06-21 16:07:11 +00:00
if ( isset ( $shareType ) && isset ( $shareWith )) {
// Include all user and group items
if ( $shareType == self :: $shareTypeUserAndGroups ) {
2012-06-25 03:12:20 +00:00
$where .= " AND share_type IN ( " . self :: SHARE_TYPE_USER . " , " . self :: SHARE_TYPE_GROUP . " , " . self :: $shareTypeGroupUserUnique . " ) " ;
2012-06-21 16:07:11 +00:00
$groups = \OC_Group :: getUserGroups ( $shareWith );
$userAndGroups = array_merge ( array ( $shareWith ), $groups );
2012-06-25 03:12:20 +00:00
$where .= " AND share_with IN (' " . implode ( " ',' " , $userAndGroups ) . " ') " ;
2012-06-16 00:52:56 +00:00
} else {
2012-06-25 03:00:29 +00:00
$where .= " AND share_type = " . $shareType . " AND share_with = ' " . $shareWith . " ' " ;
2012-06-16 00:52:56 +00:00
}
}
if ( isset ( $uidOwner )) {
$where .= " AND uid_owner = ' " . $uidOwner . " ' " ;
2012-06-24 20:42:51 +00:00
if ( ! isset ( $shareType )) {
// Prevent unique user targets for group shares from being selected
$where .= " AND share_type != ' " . self :: $shareTypeGroupUserUnique . " ' " ;
}
2012-06-16 00:52:56 +00:00
}
if ( isset ( $item )) {
// If looking for own shared items, check item_source else check item_target
if ( isset ( $uidOwner )) {
$source = $backend -> getSource ( $item , $uidOwner );
2012-06-25 03:00:29 +00:00
// If item type is a file, file source needs to be checked in case the item was converted
2012-07-12 18:29:51 +00:00
if ( $itemType == 'file' || $itemType == 'folder' ) {
2012-06-25 03:00:29 +00:00
$where .= " AND file_source = " . \OC_FileCache :: getId ( $source [ 'file' ]);
2012-06-16 00:52:56 +00:00
} else {
2012-06-25 03:00:29 +00:00
// Check if this item depends on a file and getSource() returned an array
if ( is_array ( $source )) {
$itemSource = $source [ 'item' ];
} else {
$itemSource = $source ;
}
$where .= " AND item_source = ' " . $itemSource . " ' " ;
2012-06-16 00:52:56 +00:00
}
} else {
2012-07-03 15:11:01 +00:00
if ( $isSource ) {
2012-07-10 22:56:22 +00:00
if ( $itemType == 'file' || $itemType == 'folder' ) {
2012-07-03 15:11:01 +00:00
$where .= " AND file_source = ' " . $item . " ' " ;
} else {
$where .= " AND item_source = ' " . $item . " ' " ;
}
2012-06-21 16:07:11 +00:00
} else {
2012-07-10 22:56:22 +00:00
if ( $itemType == 'file' || $itemType == 'folder' ) {
$where .= " AND file_target = ' " . $item . " ' " ;
2012-07-03 15:11:01 +00:00
} else {
$where .= " AND item_target = ' " . $item . " ' " ;
}
2012-06-21 16:07:11 +00:00
}
2012-06-16 00:52:56 +00:00
}
2012-07-10 22:56:22 +00:00
} else if ( $itemType == 'file' ) {
// TODO Exclude converted items inside shared folders
2012-06-16 00:52:56 +00:00
}
if ( $limit != - 1 ) {
2012-06-24 20:42:51 +00:00
if ( $limit == 1 && $shareType == self :: $shareTypeUserAndGroups ) {
// Make sure the unique user target is returned if it exists, unique targets should follow the group share in the database
// If the limit is not 1, the filtering can be done later
$where .= ' ORDER BY id DESC' ;
}
2012-06-16 00:52:56 +00:00
$where .= ' LIMIT ' . $limit ;
}
2012-07-01 01:15:10 +00:00
if ( $format == self :: FORMAT_STATUSES ) {
2012-07-02 19:29:34 +00:00
$select = 'id, item_type, item, item_source, share_type' ;
2012-07-01 01:15:10 +00:00
} else {
2012-07-02 19:29:34 +00:00
if ( isset ( $uidOwner )) {
2012-07-14 13:35:39 +00:00
$select = 'id, item_type, item, item_source, parent, share_type, share_with, permissions, stime, file_source' ;
2012-07-02 19:29:34 +00:00
} else {
2012-07-03 18:00:09 +00:00
$select = '*' ;
2012-07-02 19:29:34 +00:00
}
2012-07-01 01:15:10 +00:00
}
$query = \OC_DB :: prepare ( 'SELECT ' . $select . ' FROM *PREFIX*share ' . $where );
2012-06-16 00:52:56 +00:00
$result = $query -> execute ();
2012-06-22 21:43:04 +00:00
$items = array ();
while ( $item = $result -> fetchRow ()) {
2012-07-11 01:30:16 +00:00
// Return only the item instead of a 2-dimensional array
if ( $limit == 1 && $format == self :: FORMAT_NONE ) {
return $item ;
}
2012-06-24 20:42:51 +00:00
// Filter out duplicate group shares for users with unique targets
2012-06-22 21:43:04 +00:00
if ( $item [ 'share_type' ] == self :: $shareTypeGroupUserUnique ) {
2012-07-03 15:11:01 +00:00
// Remove the parent group share
2012-07-10 22:56:22 +00:00
unset ( $items [ $item [ 'parent' ]]);
2012-06-21 16:07:11 +00:00
}
2012-07-10 22:56:22 +00:00
$items [ $item [ 'id' ]] = $item ;
2012-06-22 21:43:04 +00:00
// TODO Add in parent item types children?
2012-07-12 14:52:30 +00:00
if ( $collectionTypes && in_array ( $item [ 'item_type' ], $collectionTypes )) {
2012-06-22 21:43:04 +00:00
$children [] = $item ;
2012-06-21 16:07:11 +00:00
}
2012-06-22 21:43:04 +00:00
}
2012-06-25 00:12:33 +00:00
if ( ! empty ( $items )) {
if ( $format == self :: FORMAT_NONE ) {
return $items ;
} else if ( $format == self :: FORMAT_STATUSES ) {
$statuses = array ();
2012-07-10 22:56:22 +00:00
foreach ( $items as $item ) {
if ( $item [ 'share_type' ] == self :: SHARE_TYPE_PRIVATE_LINK ) {
$statuses [ $item [ 'item' ]] = true ;
break ;
} else if ( ! isset ( $statuses [ $item [ 'item' ]])) {
$statuses [ $item [ 'item' ]] = false ;
2012-06-25 00:12:33 +00:00
}
2012-06-24 23:16:50 +00:00
}
2012-06-25 00:12:33 +00:00
return $statuses ;
} else {
2012-07-10 22:56:22 +00:00
return $backend -> formatItems ( $items , $format , $parameters );
2012-06-24 23:16:50 +00:00
}
2012-07-03 18:03:04 +00:00
} else if ( $limit == 1 || ( isset ( $uidOwner ) && isset ( $item ))) {
2012-07-03 15:11:01 +00:00
return false ;
2012-06-16 00:52:56 +00:00
}
}
2012-06-30 22:00:01 +00:00
return array ();
2012-06-16 00:52:56 +00:00
}
/**
* @ brief Put shared item into the database
* @ param string Item type
* @ param string Item
2012-07-14 14:19:18 +00:00
* @ param int SHARE_TYPE_USER , SHARE_TYPE_GROUP , or SHARE_TYPE_PRIVATE_LINK
* @ param string User or group the item is being shared with
* @ param int CRUDS permissions
2012-06-18 19:18:30 +00:00
* @ param bool | array Parent folder target ( optional )
2012-07-14 14:19:18 +00:00
* @ return bool Returns true on success or false on failure
2012-06-16 00:52:56 +00:00
*/
2012-06-25 00:12:33 +00:00
private static function put ( $itemType , $item , $shareType , $shareWith , $uidOwner , $permissions , $parentFolder = null ) {
2012-06-16 00:52:56 +00:00
// Check file extension for an equivalent item type to convert to
if ( $itemType == 'file' ) {
$extension = strtolower ( substr ( $item , strrpos ( $item , '.' ) + 1 ));
2012-06-25 02:17:54 +00:00
foreach ( self :: $backends as $type => $backend ) {
if ( isset ( $backend -> dependsOn ) && $backend -> dependsOn == 'file' && isset ( $backend -> supportedFileExtensions ) && in_array ( $extension , $backend -> supportedFileExtensions )) {
2012-06-16 00:52:56 +00:00
$itemType = $type ;
break ;
}
}
// Exit if this is being called for a file inside a folder, and no equivalent item type is found
if ( isset ( $parentFolder ) && $itemType == 'file' ) {
return false ;
}
}
if ( $backend = self :: getBackend ( $itemType )) {
// Check if this is a reshare
2012-07-03 18:03:04 +00:00
if ( $checkReshare = self :: getItemSharedWith ( $itemType , $item )) {
2012-07-14 13:35:39 +00:00
if ( $checkReshare [ 'permissions' ] & self :: PERMISSION_SHARE ) {
// TODO Don't check if inside folder
$parent = $checkReshare [ 'id' ];
$itemSource = $checkReshare [ 'item_source' ];
$fileSource = $checkReshare [ 'file_source' ];
$fileTarget = $checkReshare [ 'file_target' ];
} else {
\OC_Log :: write ( 'OCP\Share' , 'Sharing ' . $item . ' failed, because resharing is not allowed' , \OC_Log :: ERROR );
return false ;
}
2012-06-16 00:52:56 +00:00
} else {
$parent = null ;
$source = $backend -> getSource ( $item , $uidOwner );
if ( ! $source ) {
\OC_Log :: write ( 'OCP\Share' , 'Sharing ' . $item . ' failed, because the sharing backend for ' . $itemType . ' could not find its source' , \OC_Log :: ERROR );
return false ;
} else if ( is_array ( $source )) {
$itemSource = $source [ 'item' ];
2012-06-25 03:00:29 +00:00
$fileSource = \OC_FileCache :: getId ( $source [ 'file' ]);
2012-07-12 00:43:48 +00:00
if ( $fileSource == - 1 ) {
\OC_Log :: write ( 'OCP\Share' , 'Sharing ' . $item . ' failed, because the file could not be found in the file cache' , \OC_Log :: ERROR );
return false ;
}
2012-06-16 00:52:56 +00:00
} else {
$itemSource = $source ;
$fileSource = null ;
}
}
2012-06-25 00:12:33 +00:00
$query = \OC_DB :: prepare ( 'INSERT INTO *PREFIX*share (item_type, item, item_source, item_target, parent, share_type, share_with, uid_owner, permissions, stime, file_source, file_target) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)' );
2012-06-18 19:18:30 +00:00
// Share with a group
2012-06-21 16:07:11 +00:00
if ( $shareType == self :: SHARE_TYPE_GROUP ) {
2012-06-16 00:52:56 +00:00
if ( isset ( $fileSource )) {
2012-06-18 19:18:30 +00:00
if ( $parentFolder ) {
if ( $parentFolder === true ) {
$groupFileTarget = self :: getBackend ( 'file' ) -> generateTarget ( $source [ 'file' ], false );
// Set group default file target for future use
$parentFolders [ 0 ][ 'folder' ] = $groupFileTarget ;
} else {
// Get group default file target
$groupFileTarget = $parentFolder [ 0 ][ 'folder' ] . $item ;
$parent = $parentFolder [ 0 ][ 'id' ];
unset ( $parentFolder [ 0 ]);
// Only loop through users we know have different file target paths
$uidSharedWith = array_keys ( $parentFolder );
}
2012-06-16 00:52:56 +00:00
} else {
$groupFileTarget = self :: getBackend ( 'file' ) -> generateTarget ( $source [ 'file' ], false );
}
} else {
$groupFileTarget = null ;
}
$groupItemTarget = $backend -> generateTarget ( $item , false );
2012-06-22 21:43:04 +00:00
$query -> execute ( array ( $itemType , $item , $itemSource , $groupItemTarget , $parent , $shareType , $shareWith [ 'group' ], $uidOwner , $permissions , time (), $fileSource , $groupFileTarget ));
2012-06-16 00:52:56 +00:00
// Save this id, any extra rows for this group share will need to reference it
2012-06-25 00:12:33 +00:00
$parent = \OC_DB :: insertid ( '*PREFIX*share' );
2012-06-18 19:18:30 +00:00
// Loop through all users of this group in case we need to add an extra row
2012-06-21 16:07:11 +00:00
foreach ( $shareWith [ 'users' ] as $uid ) {
2012-06-18 19:18:30 +00:00
$itemTarget = $backend -> generateTarget ( $item , $uid );
if ( isset ( $fileSource )) {
if ( $parentFolder ) {
if ( $parentFolder === true ) {
2012-06-25 17:21:01 +00:00
$fileTarget = self :: getBackend ( 'file' ) -> generateTarget ( $source [ 'file' ], $uid );
2012-06-18 19:18:30 +00:00
if ( $fileTarget != $groupFileTarget ) {
$parentFolders [ $uid ][ 'folder' ] = $fileTarget ;
}
} else if ( isset ( $parentFolder [ $uid ])) {
$fileTarget = $parentFolder [ $uid ][ 'folder' ] . $item ;
$parent = $parentFolder [ $uid ][ 'id' ];
}
} else {
2012-06-25 17:21:01 +00:00
$fileTarget = self :: getBackend ( 'file' ) -> generateTarget ( $source [ 'file' ], $uid );
2012-06-18 19:18:30 +00:00
}
2012-06-16 00:52:56 +00:00
} else {
2012-06-18 19:18:30 +00:00
$fileTarget = null ;
2012-06-16 00:52:56 +00:00
}
// Insert an extra row for the group share if the item or file target is unique for this user
if ( $itemTarget != $groupItemTarget || ( isset ( $fileSource ) && $fileTarget != $groupFileTarget )) {
2012-06-22 21:43:04 +00:00
$query -> execute ( array ( $itemType , $item , $itemSource , $itemTarget , $parent , self :: $shareTypeGroupUserUnique , $uid , $uidOwner , $permissions , time (), $fileSource , $fileTarget ));
2012-06-25 00:12:33 +00:00
$id = \OC_DB :: insertid ( '*PREFIX*share' );
2012-06-18 19:18:30 +00:00
}
if ( $parentFolder === true ) {
$parentFolders [ 'id' ] = $id ;
}
}
if ( $parentFolder === true ) {
// Return parent folders to preserve file target paths for potential children
return $parentFolders ;
}
} else {
2012-07-11 16:18:59 +00:00
if ( $shareType == self :: SHARE_TYPE_PRIVATE_LINK ) {
$itemTarget = null ;
} else {
$itemTarget = $backend -> generateTarget ( $item , $shareWith );
}
2012-06-18 19:18:30 +00:00
if ( isset ( $fileSource )) {
if ( $parentFolder ) {
if ( $parentFolder === true ) {
2012-06-25 00:12:33 +00:00
$fileTarget = self :: getBackend ( 'file' ) -> generateTarget ( $source [ 'file' ], $shareWith );
2012-06-18 19:18:30 +00:00
$parentFolders [ 'folder' ] = $fileTarget ;
} else {
$fileTarget = $parentFolder [ 'folder' ] . $item ;
$parent = $parentFolder [ 'id' ];
}
} else {
2012-07-11 16:18:59 +00:00
if ( $shareType == self :: SHARE_TYPE_PRIVATE_LINK ) {
$fileTarget = basename ( $source [ 'file' ]);
} else {
$fileTarget = self :: getBackend ( 'file' ) -> generateTarget ( $source [ 'file' ], $shareWith );
}
2012-06-16 00:52:56 +00:00
}
} else {
2012-06-18 19:18:30 +00:00
$fileTarget = null ;
}
2012-06-22 21:43:04 +00:00
$query -> execute ( array ( $itemType , $item , $itemSource , $itemTarget , $parent , $shareType , $shareWith , $uidOwner , $permissions , time (), $fileSource , $fileTarget ));
2012-06-25 00:12:33 +00:00
$id = \OC_DB :: insertid ( '*PREFIX*share' );
2012-06-18 19:18:30 +00:00
if ( $parentFolder === true ) {
$parentFolders [ 'id' ] = $id ;
// Return parent folder to preserve file target paths for potential children
return $parentFolders ;
2012-06-16 00:52:56 +00:00
}
}
return true ;
}
return false ;
}
/**
* @ brief Delete all reshares of an item
* @ param int Id of item to delete
2012-07-14 15:53:02 +00:00
* @ param bool If true , exclude the parent from the delete ( optional )
* @ param string The user that the parent was shared with ( optinal )
2012-06-16 00:52:56 +00:00
*/
2012-07-14 15:53:02 +00:00
private static function delete ( $parent , $excludeParent = false , $uidOwner = null ) {
2012-06-16 00:52:56 +00:00
$ids = array ( $parent );
2012-07-14 14:52:31 +00:00
$parents = array ( $parent );
while ( ! empty ( $parents )) {
$parents = " ' " . implode ( " ',' " , $parents ) . " ' " ;
2012-07-14 15:53:02 +00:00
// Check the owner on the first search of reshares, useful for finding and deleting the reshares by a single user of a group share
if ( count ( $ids ) == 1 && isset ( $uidOwner )) {
$query = \OC_DB :: prepare ( 'SELECT id FROM *PREFIX*share WHERE parent IN (' . $parents . ') AND uid_owner = ?' );
$result = $query -> execute ( array ( $uidOwner ));
} else {
$query = \OC_DB :: prepare ( 'SELECT id FROM *PREFIX*share WHERE parent IN (' . $parents . ')' );
$result = $query -> execute ();
}
2012-07-14 14:52:31 +00:00
// Reset parents array, only go through loop again if items are found
$parents = array ();
while ( $item = $result -> fetchRow ()) {
$ids [] = $item [ 'id' ];
$parents [] = $item [ 'id' ];
}
2012-06-16 00:52:56 +00:00
}
if ( $excludeParent ) {
unset ( $ids [ 0 ]);
}
if ( ! empty ( $ids )) {
2012-07-14 14:52:31 +00:00
$ids = " ' " . implode ( " ',' " , $ids ) . " ' " ;
$query = \OC_DB :: prepare ( 'DELETE FROM *PREFIX*share WHERE id IN (' . $ids . ')' );
$query -> execute ();
2012-06-16 00:52:56 +00:00
}
}
/**
* Hook Listeners
*/
public static function post_deleteUser ( $arguments ) {
// Delete any items shared with the deleted user
2012-07-14 15:02:16 +00:00
$query = \OC_DB :: prepare ( 'DELETE FROM *PREFIX*share WHERE share_with = ?' );
2012-06-16 00:52:56 +00:00
$result = $query -> execute ( array ( $arguments [ 'uid' ]));
// Delete any items the deleted user shared
2012-07-14 15:02:16 +00:00
$query = \OC_DB :: prepare ( 'SELECT id FROM *PREFIX*share WHERE uid_owner = ?' );
2012-06-16 00:52:56 +00:00
$result = $query -> execute ( array ( $arguments [ 'uid' ]));
while ( $item = $result -> fetchRow ()) {
2012-06-18 19:18:30 +00:00
self :: delete ( $item [ 'id' ]);
2012-06-16 00:52:56 +00:00
}
}
public static function post_addToGroup ( $arguments ) {
// TODO
}
public static function post_removeFromGroup ( $arguments ) {
2012-07-14 15:55:22 +00:00
$query = \OC_DB :: prepare ( 'SELECT id, share_type FROM *PREFIX*share WHERE (share_type = ? AND share_with = ?) OR (share_type = ? AND share_with = ?)' );
$result = $query -> execute ( array ( self :: SHARE_TYPE_GROUP , $arguments [ 'gid' ], self :: $shareTypeGroupUserUnique , $arguments [ 'uid' ]));
while ( $item = $result -> fetchRow ()) {
if ( $item [ 'share_type' ] == self :: SHARE_TYPE_GROUP ) {
// Delete all reshares by this user of the group share
self :: delete ( $item [ 'id' ], true , $arguments [ 'uid' ]);
} else {
self :: delete ( $item [ 'id' ]);
}
}
2012-06-16 00:52:56 +00:00
}
}
/**
* Abstract backend class that apps must extend to share content .
*/
abstract class Share_Backend {
2012-06-25 02:17:54 +00:00
public $dependsOn ;
public $supportedFileExtensions = array ();
2012-06-21 16:07:11 +00:00
2012-06-16 00:52:56 +00:00
/**
* @ brief Get the source of the item to be stored in the database
* @ param string Item
* @ param string Owner of the item
* @ return mixed | array | false Source
*
* Return an array if the item is file dependent , the array needs two keys : 'item' and 'file'
* Return false if the item does not exist for the user
*
2012-06-25 20:51:05 +00:00
* The formatItems () function will translate the source returned back into the item
2012-06-16 00:52:56 +00:00
*/
public abstract function getSource ( $item , $uid );
2012-06-21 16:07:11 +00:00
2012-06-16 00:52:56 +00:00
/**
* @ brief Get a unique name of the item for the specified user
* @ param string Item
* @ param string | false User the item is being shared with
* @ param array | null List of similar item names already existing as shared items
* @ return string Target name
*
* This function needs to verify that the user does not already have an item with this name .
* If it does generate a new name e . g . name_ #
*/
public abstract function generateTarget ( $item , $uid , $exclude = null );
2012-06-21 16:07:11 +00:00
/**
2012-06-22 21:43:04 +00:00
* @ brief Converts the shared item sources back into the item in the specified format
2012-07-02 19:29:34 +00:00
* @ param array Shared items
2012-06-22 21:43:04 +00:00
* @ param int Format
* @ return ?
*
2012-07-02 19:29:34 +00:00
* The items array is a 3 - dimensional array with the item_source as the first key and the share id as the second key to an array with the share info .
* The key / value pairs included in the share info depend on the function originally called :
* If called by getItem ( s ) Shared : id , item_type , item , item_source , share_type , share_with , permissions , stime , file_source
* If called by getItem ( s ) SharedWith : id , item_type , item , item_source , item_target , share_type , share_with , permissions , stime , file_source , file_target
2012-06-22 21:43:04 +00:00
* This function allows the backend to control the output of shared items with custom formats .
2012-07-02 19:29:34 +00:00
* It is only called through calls to the public getItem ( s ) Shared ( With ) functions .
2012-06-21 16:07:11 +00:00
*/
2012-07-10 22:56:22 +00:00
public abstract function formatItems ( $items , $format , $parameters = null );
2012-06-21 16:07:11 +00:00
2012-06-16 00:52:56 +00:00
}
2012-07-10 22:56:22 +00:00
abstract class Share_Backend_Collection extends Share_Backend {
public abstract function inCollection ( $collections , $item );
2012-06-16 00:52:56 +00:00
public abstract function getChildren ( $item );
}
2012-06-25 20:51:05 +00:00
?>