Merge branch 'master' into filesystem
This commit is contained in:
commit
4b616764e8
|
@ -26,6 +26,9 @@ namespace OCA\user_ldap\lib;
|
|||
abstract class Access {
|
||||
protected $connection;
|
||||
|
||||
//never ever check this var directly, always use getPagedSearchResultState
|
||||
protected $pagedSearchedSuccessful;
|
||||
|
||||
public function setConnector(Connection &$connection) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
@ -441,12 +444,12 @@ abstract class Access {
|
|||
return true;
|
||||
}
|
||||
|
||||
public function fetchListOfUsers($filter, $attr) {
|
||||
return $this->fetchList($this->searchUsers($filter, $attr), (count($attr) > 1));
|
||||
public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null) {
|
||||
return $this->fetchList($this->searchUsers($filter, $attr, $limit, $offset), (count($attr) > 1));
|
||||
}
|
||||
|
||||
public function fetchListOfGroups($filter, $attr) {
|
||||
return $this->fetchList($this->searchGroups($filter, $attr), (count($attr) > 1));
|
||||
public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
|
||||
return $this->fetchList($this->searchGroups($filter, $attr, $limit, $offset), (count($attr) > 1));
|
||||
}
|
||||
|
||||
private function fetchList($list, $manyAttributes) {
|
||||
|
@ -470,8 +473,8 @@ abstract class Access {
|
|||
*
|
||||
* Executes an LDAP search
|
||||
*/
|
||||
public function searchUsers($filter, $attr = null) {
|
||||
return $this->search($filter, $this->connection->ldapBaseUsers, $attr);
|
||||
public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
|
||||
return $this->search($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -482,8 +485,8 @@ abstract class Access {
|
|||
*
|
||||
* Executes an LDAP search
|
||||
*/
|
||||
public function searchGroups($filter, $attr = null) {
|
||||
return $this->search($filter, $this->connection->ldapBaseGroups, $attr);
|
||||
public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
|
||||
return $this->search($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -495,29 +498,73 @@ abstract class Access {
|
|||
*
|
||||
* Executes an LDAP search
|
||||
*/
|
||||
private function search($filter, $base, $attr = null) {
|
||||
private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
|
||||
if(!is_null($attr) && !is_array($attr)) {
|
||||
$attr = array(mb_strtolower($attr, 'UTF-8'));
|
||||
}
|
||||
|
||||
// See if we have a resource
|
||||
// See if we have a resource, in case not cancel with message
|
||||
$link_resource = $this->connection->getConnectionResource();
|
||||
if(is_resource($link_resource)) {
|
||||
$sr = ldap_search($link_resource, $base, $filter, $attr);
|
||||
$findings = ldap_get_entries($link_resource, $sr );
|
||||
|
||||
// if we're here, probably no connection resource is returned.
|
||||
// to make ownCloud behave nicely, we simply give back an empty array.
|
||||
if(is_null($findings)) {
|
||||
return array();
|
||||
}
|
||||
} else {
|
||||
if(!is_resource($link_resource)) {
|
||||
// Seems like we didn't find any resource.
|
||||
// Return an empty array just like before.
|
||||
\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
|
||||
return array();
|
||||
}
|
||||
|
||||
//TODO: lines 516:540 into a function of its own. $pagedSearchOK as return
|
||||
//check wether paged query should be attempted
|
||||
$pagedSearchOK = false;
|
||||
if($this->connection->hasPagedResultSupport && !is_null($limit)) {
|
||||
$offset = intval($offset); //can be null
|
||||
//get the cookie from the search for the previous search, required by LDAP
|
||||
$cookie = $this->getPagedResultCookie($filter, $limit, $offset);
|
||||
if(empty($cookie) && ($offset > 0)) {
|
||||
//no cookie known, although the offset is not 0. Maybe cache run out. We need to start all over *sigh* (btw, Dear Reader, did you need LDAP paged searching was designed by MSFT?)
|
||||
$reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
|
||||
//a bit recursive, $offset of 0 is the exit
|
||||
$this->search($filter, $base, $attr, $limit, $reOffset, true);
|
||||
$cookie = $this->getPagedResultCookie($filter, $limit, $offset);
|
||||
//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
|
||||
//TODO: remember this, probably does not change in the next request...
|
||||
if(empty($cookie)) {
|
||||
$cookie = null;
|
||||
}
|
||||
}
|
||||
if(!is_null($cookie)) {
|
||||
$pagedSearchOK = ldap_control_paged_result($link_resource, $limit, false, $cookie);
|
||||
\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
|
||||
} else {
|
||||
\OCP\Util::writeLog('user_ldap', 'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset, \OCP\Util::DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
$sr = ldap_search($link_resource, $base, $filter, $attr);
|
||||
$findings = ldap_get_entries($link_resource, $sr );
|
||||
if($pagedSearchOK) {
|
||||
\OCP\Util::writeLog('user_ldap', 'Paged search successful', \OCP\Util::INFO);
|
||||
ldap_control_paged_result_response($link_resource, $sr, $cookie);
|
||||
\OCP\Util::writeLog('user_ldap', 'Set paged search cookie '.$cookie, \OCP\Util::INFO);
|
||||
$this->setPagedResultCookie($filter, $limit, $offset, $cookie);
|
||||
//browsing through prior pages to get the cookie for the new one
|
||||
if($skipHandling) {
|
||||
return;
|
||||
}
|
||||
//if count is bigger, then the server does not support paged search. Instead, he did a normal search. We set a flag here, so the callee knows how to deal with it.
|
||||
//TODO: Not used, just make a count on the returned values in the callee
|
||||
if($findings['count'] <= $limit) {
|
||||
$this->pagedSearchedSuccessful = true;
|
||||
}
|
||||
} else {
|
||||
\OCP\Util::writeLog('user_ldap', 'Paged search failed :(', \OCP\Util::INFO);
|
||||
}
|
||||
|
||||
// if we're here, probably no connection resource is returned.
|
||||
// to make ownCloud behave nicely, we simply give back an empty array.
|
||||
if(is_null($findings)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if(!is_null($attr)) {
|
||||
$selection = array();
|
||||
$multiarray = false;
|
||||
|
@ -557,6 +604,7 @@ abstract class Access {
|
|||
}
|
||||
}
|
||||
}
|
||||
// die(var_dump($selection));
|
||||
return $selection;
|
||||
}
|
||||
return $findings;
|
||||
|
@ -680,4 +728,51 @@ abstract class Access {
|
|||
}
|
||||
return $uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get a cookie for the next LDAP paged search
|
||||
* @param $filter the search filter to identify the correct search
|
||||
* @param $limit the limit (or 'pageSize'), to identify the correct search well
|
||||
* @param $offset the offset for the new search to identify the correct search really good
|
||||
* @returns string containing the key or empty if none is cached
|
||||
*/
|
||||
private function getPagedResultCookie($filter, $limit, $offset) {
|
||||
if($offset == 0) {
|
||||
return '';
|
||||
}
|
||||
$offset -= $limit;
|
||||
//we work with cache here
|
||||
$cachekey = 'lc' . dechex(crc32($filter)) . '-' . $limit . '-' . $offset;
|
||||
$cookie = $this->connection->getFromCache($cachekey);
|
||||
if(is_null($cookie)) {
|
||||
$cookie = '';
|
||||
}
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set a cookie for LDAP paged search run
|
||||
* @param $filter the search filter to identify the correct search
|
||||
* @param $limit the limit (or 'pageSize'), to identify the correct search well
|
||||
* @param $offset the offset for the run search to identify the correct search really good
|
||||
* @param $cookie string containing the cookie returned by ldap_control_paged_result_response
|
||||
* @return void
|
||||
*/
|
||||
private function setPagedResultCookie($filter, $limit, $offset) {
|
||||
if(!empty($cookie)) {
|
||||
$cachekey = 'lc' . dechex(crc32($filter)) . '-' . $limit . '-' . $offset;
|
||||
$cookie = $this->connection->writeToCache($cachekey, $cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief check wether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
|
||||
* @return true on success, null or false otherwise
|
||||
*/
|
||||
public function getPagedSearchResultState() {
|
||||
$result = $this->pagedSearchedSuccessful;
|
||||
$this->pagedSearchedSuccessful = null;
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
|
@ -56,15 +56,20 @@ class Connection {
|
|||
'ldapUuidAttribute' => null,
|
||||
'ldapOverrideUuidAttribute' => null,
|
||||
'homeFolderNamingRule' => null,
|
||||
'hasPagedResultSupport' => false,
|
||||
);
|
||||
|
||||
public function __construct($configID = 'user_ldap') {
|
||||
$this->configID = $configID;
|
||||
$this->cache = \OC_Cache::getGlobalCache();
|
||||
$this->config['hasPagedResultSupport'] = (function_exists('ldap_control_paged_result') && function_exists('ldap_control_paged_result_response'));
|
||||
\OCP\Util::writeLog('user_ldap', 'PHP supports paged results? '.print_r($this->config['hasPagedResultSupport'], true), \OCP\Util::INFO);
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
@ldap_unbind($this->ldapConnectionRes);
|
||||
if(is_resource($this->ldapConnectionRes)) {
|
||||
@ldap_unbind($this->ldapConnectionRes);
|
||||
};
|
||||
}
|
||||
|
||||
public function __get($name) {
|
||||
|
|
|
@ -104,24 +104,38 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
|
|||
* Get a list of all users.
|
||||
*/
|
||||
public function getUsers($search = '', $limit = 10, $offset = 0) {
|
||||
$ldap_users = $this->connection->getFromCache('getUsers');
|
||||
if(is_null($ldap_users)) {
|
||||
$ldap_users = $this->fetchListOfUsers($this->connection->ldapUserFilter, array($this->connection->ldapUserDisplayName, 'dn'));
|
||||
$ldap_users = $this->ownCloudUserNames($ldap_users);
|
||||
$this->connection->writeToCache('getUsers', $ldap_users);
|
||||
}
|
||||
$this->userSearch = $search;
|
||||
if(!empty($this->userSearch)) {
|
||||
$ldap_users = array_filter($ldap_users, array($this, 'userMatchesFilter'));
|
||||
}
|
||||
if($limit == -1) {
|
||||
$limit = null;
|
||||
}
|
||||
return array_slice($ldap_users, $offset, $limit);
|
||||
}
|
||||
$cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
|
||||
|
||||
public function userMatchesFilter($user) {
|
||||
return (strripos($user, $this->userSearch) !== false);
|
||||
//check if users are cached, if so return
|
||||
$ldap_users = $this->connection->getFromCache($cachekey);
|
||||
if(!is_null($ldap_users)) {
|
||||
return $ldap_users;
|
||||
}
|
||||
|
||||
//prepare search filter
|
||||
$search = empty($search) ? '*' : '*'.$search.'*';
|
||||
$filter = $this->combineFilterWithAnd(array(
|
||||
$this->connection->ldapUserFilter,
|
||||
$this->connection->ldapGroupDisplayName.'='.$search
|
||||
));
|
||||
|
||||
\OCP\Util::writeLog('user_ldap', 'getUsers: Get users filter '.$filter, \OCP\Util::DEBUG);
|
||||
//do the search and translate results to owncloud names
|
||||
$ldap_users = $this->fetchListOfUsers($filter, array($this->connection->ldapUserDisplayName, 'dn'), $limit, $offset);
|
||||
$ldap_users = $this->ownCloudUserNames($ldap_users);
|
||||
|
||||
if(!$this->getPagedSearchResultState()) {
|
||||
\OCP\Util::writeLog('user_ldap', 'getUsers: We got old-style results', \OCP\Util::DEBUG);
|
||||
//if not supported, a 'normal' search has run automatically, we just need to get our slice of the cake. And we cache the general search, too
|
||||
$this->connection->writeToCache('getUsers-'.$search, $ldap_users);
|
||||
$ldap_users = array_slice($ldap_users, $offset, $limit);
|
||||
} else {
|
||||
//debug message only
|
||||
\OCP\Util::writeLog('user_ldap', 'getUsers: We got paged results', \OCP\Util::DEBUG);
|
||||
}
|
||||
|
||||
$this->connection->writeToCache($cachekey, $ldap_users);
|
||||
return $ldap_users;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
$RUNTIME_NOAPPS = true;
|
||||
|
||||
require_once '../../lib/base.php';
|
||||
|
||||
OC_Util::checkAdminUser();
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
$app = $_GET['app'];
|
||||
|
||||
//load the one app and see what it adds to the navigation
|
||||
OC_App::loadApp($app);
|
||||
|
||||
$navigation = OC_App::getNavigation();
|
||||
|
||||
$navIds = array();
|
||||
foreach ($navigation as $nav) {
|
||||
$navIds[] = $nav['id'];
|
||||
}
|
||||
|
||||
OCP\JSON::success(array('nav_ids' => array_values($navIds), 'nav_entries' => $navigation));
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* In-Field Label jQuery Plugin
|
||||
* http://fuelyourcoding.com/scripts/infield.html
|
||||
*
|
||||
* Copyright (c) 2009 Doug Neiner
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* Uses the same license as jQuery, see:
|
||||
* http://docs.jquery.com/License
|
||||
*
|
||||
* @version 0.1
|
||||
*/
|
||||
(function($){
|
||||
|
||||
$.InFieldLabels = function(label,field, options){
|
||||
// To avoid scope issues, use 'base' instead of 'this'
|
||||
// to reference this class from internal events and functions.
|
||||
var base = this;
|
||||
|
||||
// Access to jQuery and DOM versions of each element
|
||||
base.$label = $(label);
|
||||
base.label = label;
|
||||
|
||||
base.$field = $(field);
|
||||
base.field = field;
|
||||
|
||||
base.$label.data("InFieldLabels", base);
|
||||
base.showing = true;
|
||||
|
||||
base.init = function(){
|
||||
// Merge supplied options with default options
|
||||
base.options = $.extend({},$.InFieldLabels.defaultOptions, options);
|
||||
|
||||
// Check if the field is already filled in
|
||||
if(base.$field.val() != ""){
|
||||
base.$label.hide();
|
||||
base.showing = false;
|
||||
};
|
||||
|
||||
base.$field.focus(function(){
|
||||
base.fadeOnFocus();
|
||||
}).blur(function(){
|
||||
base.checkForEmpty(true);
|
||||
}).bind('keydown.infieldlabel',function(e){
|
||||
// Use of a namespace (.infieldlabel) allows us to
|
||||
// unbind just this method later
|
||||
base.hideOnChange(e);
|
||||
}).change(function(e){
|
||||
base.checkForEmpty();
|
||||
}).bind('onPropertyChange', function(){
|
||||
base.checkForEmpty();
|
||||
});
|
||||
};
|
||||
|
||||
// If the label is currently showing
|
||||
// then fade it down to the amount
|
||||
// specified in the settings
|
||||
base.fadeOnFocus = function(){
|
||||
if(base.showing){
|
||||
base.setOpacity(base.options.fadeOpacity);
|
||||
};
|
||||
};
|
||||
|
||||
base.setOpacity = function(opacity){
|
||||
base.$label.stop().animate({ opacity: opacity }, base.options.fadeDuration);
|
||||
base.showing = (opacity > 0.0);
|
||||
};
|
||||
|
||||
// Checks for empty as a fail safe
|
||||
// set blur to true when passing from
|
||||
// the blur event
|
||||
base.checkForEmpty = function(blur){
|
||||
if(base.$field.val() == ""){
|
||||
base.prepForShow();
|
||||
base.setOpacity( blur ? 1.0 : base.options.fadeOpacity );
|
||||
} else {
|
||||
base.setOpacity(0.0);
|
||||
};
|
||||
};
|
||||
|
||||
base.prepForShow = function(e){
|
||||
if(!base.showing) {
|
||||
// Prepare for a animate in...
|
||||
base.$label.css({opacity: 0.0}).show();
|
||||
|
||||
// Reattach the keydown event
|
||||
base.$field.bind('keydown.infieldlabel',function(e){
|
||||
base.hideOnChange(e);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
base.hideOnChange = function(e){
|
||||
if(
|
||||
(e.keyCode == 16) || // Skip Shift
|
||||
(e.keyCode == 9) // Skip Tab
|
||||
) return;
|
||||
|
||||
if(base.showing){
|
||||
base.$label.hide();
|
||||
base.showing = false;
|
||||
};
|
||||
|
||||
// Remove keydown event to save on CPU processing
|
||||
base.$field.unbind('keydown.infieldlabel');
|
||||
};
|
||||
|
||||
// Run the initialization method
|
||||
base.init();
|
||||
};
|
||||
|
||||
$.InFieldLabels.defaultOptions = {
|
||||
fadeOpacity: 0.5, // Once a field has focus, how transparent should the label be
|
||||
fadeDuration: 300 // How long should it take to animate from 1.0 opacity to the fadeOpacity
|
||||
};
|
||||
|
||||
|
||||
$.fn.inFieldLabels = function(options){
|
||||
return this.each(function(){
|
||||
// Find input or textarea based on for= attribute
|
||||
// The for attribute on the label must contain the ID
|
||||
// of the input or textarea element
|
||||
var for_attr = $(this).attr('for');
|
||||
if( !for_attr ) return; // Nothing to attach, since the for field wasn't used
|
||||
|
||||
|
||||
// Find the referenced input or textarea element
|
||||
var $field = $(
|
||||
"input#" + for_attr + "[type='text']," +
|
||||
"input#" + for_attr + "[type='password']," +
|
||||
"textarea#" + for_attr
|
||||
);
|
||||
|
||||
if( $field.length == 0) return; // Again, nothing to attach
|
||||
|
||||
// Only create object for input[text], input[password], or textarea
|
||||
(new $.InFieldLabels(this, $field[0], options));
|
||||
});
|
||||
};
|
||||
|
||||
})(jQuery);
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* In-Field Label jQuery Plugin
|
||||
* http://fuelyourcoding.com/scripts/infield.html
|
||||
*
|
||||
* Copyright (c) 2009 Doug Neiner
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* Uses the same license as jQuery, see:
|
||||
* http://docs.jquery.com/License
|
||||
*
|
||||
* @version 0.1
|
||||
*/
|
||||
(function($){$.InFieldLabels=function(b,c,d){var f=this;f.$label=$(b);f.label=b;f.$field=$(c);f.field=c;f.$label.data("InFieldLabels",f);f.showing=true;f.init=function(){f.options=$.extend({},$.InFieldLabels.defaultOptions,d);if(f.$field.val()!=""){f.$label.hide();f.showing=false};f.$field.focus(function(){f.fadeOnFocus()}).blur(function(){f.checkForEmpty(true)}).bind('keydown.infieldlabel',function(e){f.hideOnChange(e)}).change(function(e){f.checkForEmpty()}).bind('onPropertyChange',function(){f.checkForEmpty()})};f.fadeOnFocus=function(){if(f.showing){f.setOpacity(f.options.fadeOpacity)}};f.setOpacity=function(a){f.$label.stop().animate({opacity:a},f.options.fadeDuration);f.showing=(a>0.0)};f.checkForEmpty=function(a){if(f.$field.val()==""){f.prepForShow();f.setOpacity(a?1.0:f.options.fadeOpacity)}else{f.setOpacity(0.0)}};f.prepForShow=function(e){if(!f.showing){f.$label.css({opacity:0.0}).show();f.$field.bind('keydown.infieldlabel',function(e){f.hideOnChange(e)})}};f.hideOnChange=function(e){if((e.keyCode==16)||(e.keyCode==9))return;if(f.showing){f.$label.hide();f.showing=false};f.$field.unbind('keydown.infieldlabel')};f.init()};$.InFieldLabels.defaultOptions={fadeOpacity:0.5,fadeDuration:300};$.fn.inFieldLabels=function(c){return this.each(function(){var a=$(this).attr('for');if(!a)return;var b=$("input#"+a+"[type='text'],"+"input#"+a+"[type='password'],"+"textarea#"+a);if(b.length==0)return;(new $.InFieldLabels(this,b[0],c))})}})(jQuery);
|
||||
In-Field Label jQuery Plugin
|
||||
http://fuelyourcoding.com/scripts/infield.html
|
||||
|
||||
Copyright (c) 2009-2010 Doug Neiner
|
||||
Dual licensed under the MIT and GPL licenses.
|
||||
Uses the same license as jQuery, see:
|
||||
http://docs.jquery.com/License
|
||||
|
||||
@version 0.1.5
|
||||
*/
|
||||
(function($){$.InFieldLabels=function(label,field,options){var base=this;base.$label=$(label);base.label=label;base.$field=$(field);base.field=field;base.$label.data("InFieldLabels",base);base.showing=true;base.init=function(){base.options=$.extend({},$.InFieldLabels.defaultOptions,options);setTimeout(function(){if(base.$field.val()!==""){base.$label.hide();base.showing=false}},200);base.$field.focus(function(){base.fadeOnFocus()}).blur(function(){base.checkForEmpty(true)}).bind('keydown.infieldlabel',function(e){base.hideOnChange(e)}).bind('paste',function(e){base.setOpacity(0.0)}).change(function(e){base.checkForEmpty()}).bind('onPropertyChange',function(){base.checkForEmpty()}).bind('keyup.infieldlabel',function(){base.checkForEmpty()})};base.fadeOnFocus=function(){if(base.showing){base.setOpacity(base.options.fadeOpacity)}};base.setOpacity=function(opacity){base.$label.stop().animate({opacity:opacity},base.options.fadeDuration);base.showing=(opacity>0.0)};base.checkForEmpty=function(blur){if(base.$field.val()===""){base.prepForShow();base.setOpacity(blur?1.0:base.options.fadeOpacity)}else{base.setOpacity(0.0)}};base.prepForShow=function(e){if(!base.showing){base.$label.css({opacity:0.0}).show();base.$field.bind('keydown.infieldlabel',function(e){base.hideOnChange(e)})}};base.hideOnChange=function(e){if((e.keyCode===16)||(e.keyCode===9)){return}if(base.showing){base.$label.hide();base.showing=false}base.$field.unbind('keydown.infieldlabel')};base.init()};$.InFieldLabels.defaultOptions={fadeOpacity:0.5,fadeDuration:300};$.fn.inFieldLabels=function(options){return this.each(function(){var for_attr=$(this).attr('for'),$field;if(!for_attr){return}$field=$("input#"+for_attr+"[type='text'],"+"input#"+for_attr+"[type='search'],"+"input#"+for_attr+"[type='tel'],"+"input#"+for_attr+"[type='url'],"+"input#"+for_attr+"[type='email'],"+"input#"+for_attr+"[type='password'],"+"textarea#"+for_attr);if($field.length===0){return}(new $.InFieldLabels(this,$field[0],options))})}}(jQuery));
|
||||
|
|
|
@ -373,20 +373,15 @@ class OC_Helper {
|
|||
}
|
||||
if (!$isWrapped and $mimeType=='application/octet-stream' && OC_Helper::canExecute("file")) {
|
||||
// it looks like we have a 'file' command,
|
||||
// lets see it it does have mime support
|
||||
// lets see if it does have mime support
|
||||
$path=escapeshellarg($path);
|
||||
$fp = popen("file -i -b $path 2>/dev/null", "r");
|
||||
$reply = fgets($fp);
|
||||
pclose($fp);
|
||||
|
||||
//trim the character set from the end of the response
|
||||
$mimeType=substr($reply,0, strrpos($reply,' '));
|
||||
$mimeType=substr($mimeType,0, strrpos($mimeType,"\n"));
|
||||
|
||||
//trim ;
|
||||
if (strpos($mimeType, ';') !== false) {
|
||||
$mimeType = strstr($mimeType, ';', true);
|
||||
}
|
||||
// we have smth like 'text/x-c++; charset=us-ascii\n'
|
||||
// and need to eliminate everything starting with semicolon including trailing LF
|
||||
$mimeType = preg_replace('/;.*/ms', '', trim($reply));
|
||||
|
||||
}
|
||||
return $mimeType;
|
||||
|
|
|
@ -28,6 +28,9 @@ namespace OCP;
|
|||
/**
|
||||
* This class provides the ability for apps to share their content between users.
|
||||
* Apps must create a backend class that implements OCP\Share_Backend and register it with this class.
|
||||
*
|
||||
* It provides the following hooks:
|
||||
* - post_shared
|
||||
*/
|
||||
class Share {
|
||||
|
||||
|
@ -937,7 +940,20 @@ class Share {
|
|||
// 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)) {
|
||||
$query->execute(array($itemType, $itemSource, $itemTarget, $parent, self::$shareTypeGroupUserUnique, $uid, $uidOwner, $permissions, time(), $fileSource, $fileTarget));
|
||||
\OC_DB::insertid('*PREFIX*share');
|
||||
$id = \OC_DB::insertid('*PREFIX*share');
|
||||
\OC_Hook::emit('OCP\Share', 'post_shared', array(
|
||||
'itemType' => $itemType,
|
||||
'itemSource' => $itemSource,
|
||||
'itemTarget' => $itemTarget,
|
||||
'parent' => $parent,
|
||||
'shareType' => self::$shareTypeGroupUserUnique,
|
||||
'shareWith' => $uid,
|
||||
'uidOwner' => $uidOwner,
|
||||
'permissions' => $permissions,
|
||||
'fileSource' => $fileSource,
|
||||
'fileTarget' => $fileTarget,
|
||||
'id' => $id
|
||||
));
|
||||
}
|
||||
}
|
||||
if ($parentFolder === true) {
|
||||
|
@ -963,6 +979,19 @@ class Share {
|
|||
}
|
||||
$query->execute(array($itemType, $itemSource, $itemTarget, $parent, $shareType, $shareWith, $uidOwner, $permissions, time(), $fileSource, $fileTarget));
|
||||
$id = \OC_DB::insertid('*PREFIX*share');
|
||||
\OC_Hook::emit('OCP\Share', 'post_shared', array(
|
||||
'itemType' => $itemType,
|
||||
'itemSource' => $itemSource,
|
||||
'itemTarget' => $itemTarget,
|
||||
'parent' => $parent,
|
||||
'shareType' => $shareType,
|
||||
'shareWith' => $shareWith,
|
||||
'uidOwner' => $uidOwner,
|
||||
'permissions' => $permissions,
|
||||
'fileSource' => $fileSource,
|
||||
'fileTarget' => $fileTarget,
|
||||
'id' => $id
|
||||
));
|
||||
if ($parentFolder === true) {
|
||||
$parentFolders['id'] = $id;
|
||||
// Return parent folder to preserve file target paths for potential children
|
||||
|
|
|
@ -51,6 +51,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
}
|
||||
else {
|
||||
element.data('active',false);
|
||||
OC.Settings.Apps.removeNavigation(appid);
|
||||
element.val(t('settings','Enable'));
|
||||
}
|
||||
},'json');
|
||||
|
@ -61,6 +62,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
OC.dialogs.alert('Error while enabling app','Error');
|
||||
}
|
||||
else {
|
||||
OC.Settings.Apps.addNavigation(appid);
|
||||
element.data('active',true);
|
||||
element.val(t('settings','Disable'));
|
||||
}
|
||||
|
@ -87,6 +89,38 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
applist.last().after(app);
|
||||
}
|
||||
return app;
|
||||
},
|
||||
removeNavigation: function(appid){
|
||||
$.getJSON(OC.filePath('core','ajax','navigationdetect.php'), {app: appid}).done(function(response){
|
||||
if(response.status === 'success'){
|
||||
var navIds=response.nav_ids;
|
||||
for(var i=0; i< navIds.length; i++){
|
||||
$('#apps').children('li[data-id="'+navIds[i]+'"]').remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
addNavigation: function(appid){
|
||||
$.getJSON(OC.filePath('core','ajax','navigationdetect.php'), {app: appid}).done(function(response){
|
||||
if(response.status === 'success'){
|
||||
var navEntries=response.nav_entries;
|
||||
for(var i=0; i< navEntries.length; i++){
|
||||
var entry = navEntries[i];
|
||||
var container = $('#apps');
|
||||
|
||||
if(container.children('li[data-id="'+entry.id+'"]').length === 0){
|
||||
var li=$('<li></li>');
|
||||
li.attr('data-id', entry.id);
|
||||
var a=$('<a></a>');
|
||||
a.attr('style', 'background-image: url('+entry.icon+')');
|
||||
a.text(entry.name);
|
||||
a.attr('href', entry.href);
|
||||
li.append(a);
|
||||
container.append(li);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue