Merge remote-tracking branch 'upstream/dev' into website-import

This commit is contained in:
Andrew Manning 2016-07-30 06:30:46 -04:00
commit f17f51a9c1
38 changed files with 14521 additions and 13995 deletions

View file

@ -8,6 +8,9 @@ namespace Zotlabs\Lib;
class Cache {
public static function get($key) {
$key = substr($key,0,254);
$r = q("SELECT v FROM cache WHERE k = '%s' limit 1",
dbesc($key)
);
@ -19,6 +22,8 @@ class Cache {
public static function set($key,$value) {
$key = substr($key,0,254);
$r = q("SELECT * FROM cache WHERE k = '%s' limit 1",
dbesc($key)
);

View file

@ -17,12 +17,20 @@ class PConfig {
*/
static public function Load($uid) {
if($uid === false)
if(is_null($uid) || $uid === false)
return false;
if(! array_key_exists($uid, \App::$config))
\App::$config[$uid] = array();
if(! is_array(\App::$config)) {
btlogger('App::$config not an array: ' . $uid);
}
if(! is_array(\App::$config[$uid])) {
btlogger('App::$config[$uid] not an array: ' . $uid);
}
$r = q("SELECT * FROM pconfig WHERE uid = %d",
intval($uid)
);
@ -61,7 +69,7 @@ class PConfig {
static public function Get($uid,$family,$key,$instore = false) {
if($uid === false)
if(is_null($uid) || $uid === false)
return false;
if(! array_key_exists($uid, \App::$config))
@ -102,7 +110,7 @@ class PConfig {
// we provide a function backtrace in the logs so that we can find
// and fix the calling function.
if($uid === false) {
if(is_null($uid) || $uid === false) {
btlogger('UID is FALSE!', LOGGER_NORMAL, LOG_ERR);
return;
}
@ -172,6 +180,9 @@ class PConfig {
static public function Delete($uid, $family, $key) {
if(is_null($uid) || $uid === false)
return false;
$ret = false;
if(array_key_exists($key, \App::$config[$uid][$family]))

View file

@ -0,0 +1,40 @@
<?php
namespace Zotlabs\Module;
require_once('include/attach.php');
require_once('include/channel.php');
require_once('include/photos.php');
class File_upload extends \Zotlabs\Web\Controller {
function post() {
// logger('file upload: ' . print_r($_REQUEST,true));
$channel = (($_REQUEST['channick']) ? get_channel_by_nick($_REQUEST['channick']) : null);
if(! $channel) {
logger('channel not found');
killme();
}
$_REQUEST['source'] = 'file_upload';
if($channel['channel_id'] != local_channel()) {
$_REQUEST['contact_allow'] = expand_acl($channel['channel_allow_cid']);
$_REQUEST['group_allow'] = expand_acl($channel['channel_allow_gid']);
$_REQUEST['contact_deny'] = expand_acl($channel['channel_deny_cid']);
$_REQUEST['group_deny'] = expand_acl($channel['channel_deny_gid']);
}
if($_REQUEST['directory_name'])
$r = attach_mkdir($channel,get_observer_hash(),$_REQUEST);
else
$r = attach_store($channel,get_observer_hash(), '', $_REQUEST);
goaway(z_root() . '/' . $_REQUEST['return_url']);
}
}

View file

@ -1,319 +0,0 @@
<?php
namespace Zotlabs\Module;
/**
* @file mod/id.php
* @brief OpenID implementation
*/
require 'library/openid/provider/provider.php';
$attrMap = array(
'namePerson/first' => t('First Name'),
'namePerson/last' => t('Last Name'),
'namePerson/friendly' => t('Nickname'),
'namePerson' => t('Full Name'),
'contact/internet/email' => t('Email'),
'contact/email' => t('Email'),
'media/image/aspect11' => t('Profile Photo'),
'media/image' => t('Profile Photo'),
'media/image/default' => t('Profile Photo'),
'media/image/16x16' => t('Profile Photo 16px'),
'media/image/32x32' => t('Profile Photo 32px'),
'media/image/48x48' => t('Profile Photo 48px'),
'media/image/64x64' => t('Profile Photo 64px'),
'media/image/80x80' => t('Profile Photo 80px'),
'media/image/128x128' => t('Profile Photo 128px'),
'timezone' => t('Timezone'),
'contact/web/default' => t('Homepage URL'),
'language/pref' => t('Language'),
'birthDate/birthYear' => t('Birth Year'),
'birthDate/birthMonth' => t('Birth Month'),
'birthDate/birthday' => t('Birth Day'),
'birthDate' => t('Birthdate'),
'gender' => t('Gender'),
);
/**
* @brief Entrypoint for the OpenID implementation.
*
* @param App &$a
*/
class Id extends \Zotlabs\Web\Controller {
function init() {
logger('id: ' . print_r($_REQUEST, true));
if(argc() > 1) {
$which = argv(1);
} else {
\App::$error = 404;
return;
}
$profile = '';
$channel = \App::get_channel();
profile_load($which,$profile);
$op = new MysqlProvider;
$op->server();
}
/**
* @brief Returns user data needed for OpenID.
*
* If no $handle is provided we will use local_channel() by default.
*
* @param string $handle (default null)
* @return boolean|array
*/
static public function getUserData($handle = null) {
if (! local_channel()) {
notice( t('Permission denied.') . EOL);
\App::$page['content'] = login();
return false;
}
// logger('handle: ' . $handle);
if ($handle) {
$r = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_address = '%s' limit 1",
dbesc($handle)
);
} else {
$r = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d",
intval(local_channel())
);
}
if (! r)
return false;
$x = q("select * from account where account_id = %d limit 1",
intval($r[0]['channel_account_id'])
);
if ($x)
$r[0]['email'] = $x[0]['account_email'];
$p = q("select * from profile where is_default = 1 and uid = %d limit 1",
intval($r[0]['channel_account_id'])
);
$gender = '';
if ($p[0]['gender'] == t('Male'))
$gender = 'M';
if ($p[0]['gender'] == t('Female'))
$gender = 'F';
$r[0]['firstName'] = ((strpos($r[0]['channel_name'],' ')) ? substr($r[0]['channel_name'],0,strpos($r[0]['channel_name'],' ')) : $r[0]['channel_name']);
$r[0]['lastName'] = ((strpos($r[0]['channel_name'],' ')) ? substr($r[0]['channel_name'],strpos($r[0]['channel_name'],' ')+1) : '');
$r[0]['namePerson'] = $r[0]['channel_name'];
$r[0]['pphoto'] = $r[0]['xchan_photo_l'];
$r[0]['pphoto16'] = z_root() . '/photo/profile/16/' . $r[0]['channel_id'] . '.jpg';
$r[0]['pphoto32'] = z_root() . '/photo/profile/32/' . $r[0]['channel_id'] . '.jpg';
$r[0]['pphoto48'] = z_root() . '/photo/profile/48/' . $r[0]['channel_id'] . '.jpg';
$r[0]['pphoto64'] = z_root() . '/photo/profile/64/' . $r[0]['channel_id'] . '.jpg';
$r[0]['pphoto80'] = z_root() . '/photo/profile/80/' . $r[0]['channel_id'] . '.jpg';
$r[0]['pphoto128'] = z_root() . '/photo/profile/128/' . $r[0]['channel_id'] . '.jpg';
$r[0]['timezone'] = $r[0]['channel_timezone'];
$r[0]['url'] = $r[0]['xchan_url'];
$r[0]['language'] = (($x[0]['account_language']) ? $x[0]['account_language'] : 'en');
$r[0]['birthyear'] = ((intval(substr($p[0]['dob'],0,4))) ? intval(substr($p[0]['dob'],0,4)) : '');
$r[0]['birthmonth'] = ((intval(substr($p[0]['dob'],5,2))) ? intval(substr($p[0]['dob'],5,2)) : '');
$r[0]['birthday'] = ((intval(substr($p[0]['dob'],8,2))) ? intval(substr($p[0]['dob'],8,2)) : '');
$r[0]['birthdate'] = (($r[0]['birthyear'] && $r[0]['birthmonth'] && $r[0]['birthday']) ? $p[0]['dob'] : '');
$r[0]['gender'] = $gender;
return $r[0];
/*
* if(isset($_POST['login'],$_POST['password'])) {
* $login = mysql_real_escape_string($_POST['login']);
* $password = sha1($_POST['password']);
* $q = mysql_query("SELECT * FROM Users WHERE login = '$login' AND password = '$password'");
* if($data = mysql_fetch_assoc($q)) {
* return $data;
* }
* if($handle) {
* echo 'Wrong login/password.';
* }
* }
* if($handle) {
* ?>
* <form action="" method="post">
* <input type="hidden" name="openid.assoc_handle" value="<?php
namespace Zotlabs\Module; echo $handle?>">
* Login: <input type="text" name="login"><br>
* Password: <input type="password" name="password"><br>
* <button>Submit</button>
* </form>
* <?php
namespace Zotlabs\Module;
* die();
* }
*/
}
}
/**
* @brief MySQL provider for OpenID implementation.
*
*/
class MysqlProvider extends \LightOpenIDProvider {
// See http://openid.net/specs/openid-attribute-properties-list-1_0-01.html
// This list contains a few variations of these attributes to maintain
// compatibility with legacy clients
private $attrFieldMap = array(
'namePerson/first' => 'firstName',
'namePerson/last' => 'lastName',
'namePerson/friendly' => 'channel_address',
'namePerson' => 'namePerson',
'contact/internet/email' => 'email',
'contact/email' => 'email',
'media/image/aspect11' => 'pphoto',
'media/image' => 'pphoto',
'media/image/default' => 'pphoto',
'media/image/16x16' => 'pphoto16',
'media/image/32x32' => 'pphoto32',
'media/image/48x48' => 'pphoto48',
'media/image/64x64' => 'pphoto64',
'media/image/80x80' => 'pphoto80',
'media/image/128x128' => 'pphoto128',
'timezone' => 'timezone',
'contact/web/default' => 'url',
'language/pref' => 'language',
'birthDate/birthYear' => 'birthyear',
'birthDate/birthMonth' => 'birthmonth',
'birthDate/birthday' => 'birthday',
'birthDate' => 'birthdate',
'gender' => 'gender',
);
function setup($identity, $realm, $assoc_handle, $attributes) {
global $attrMap;
// logger('identity: ' . $identity);
// logger('realm: ' . $realm);
// logger('assoc_handle: ' . $assoc_handle);
// logger('attributes: ' . print_r($attributes,true));
$data = \Zotlabs\Module\Id::getUserData($assoc_handle);
/** @FIXME this needs to be a template with localised strings */
$o .= '<form action="" method="post">'
. '<input type="hidden" name="openid.assoc_handle" value="' . $assoc_handle . '">'
. '<input type="hidden" name="login" value="' . $_POST['login'] .'">'
. '<input type="hidden" name="password" value="' . $_POST['password'] .'">'
. "<b>$realm</b> wishes to authenticate you.";
if($attributes['required'] || $attributes['optional']) {
$o .= " It also requests following information (required fields marked with *):"
. '<ul>';
foreach($attributes['required'] as $attr) {
if(isset($this->attrMap[$attr])) {
$o .= '<li>'
. '<input type="checkbox" name="attributes[' . $attr . ']"> '
. $this->attrMap[$attr] . ' <span class="required">*</span></li>';
}
}
foreach($attributes['optional'] as $attr) {
if(isset($this->attrMap[$attr])) {
$o .= '<li>'
. '<input type="checkbox" name="attributes[' . $attr . ']"> '
. $this->attrMap[$attr] . '</li>';
}
}
$o .= '</ul>';
}
$o .= '<br>'
. '<button name="once">Allow once</button> '
. '<button name="always">Always allow</button> '
. '<button name="cancel">cancel</button> '
. '</form>';
\App::$page['content'] .= $o;
}
function checkid($realm, &$attributes) {
logger('checkid: ' . $realm);
logger('checkid attrs: ' . print_r($attributes,true));
if(isset($_POST['cancel'])) {
$this->cancel();
}
$data = \Zotlabs\Module\Id::getUserData();
if(! $data) {
return false;
}
$q = get_pconfig(local_channel(), 'openid', $realm);
$attrs = array();
if($q) {
$attrs = $q;
} elseif(isset($_POST['attributes'])) {
$attrs = array_keys($_POST['attributes']);
} elseif(!isset($_POST['once']) && !isset($_POST['always'])) {
return false;
}
$attributes = array();
foreach($attrs as $attr) {
if(isset($this->attrFieldMap[$attr])) {
$attributes[$attr] = $data[$this->attrFieldMap[$attr]];
}
}
if(isset($_POST['always'])) {
set_pconfig(local_channel(),'openid',$realm,array_keys($attributes));
}
return z_root() . '/id/' . $data['channel_address'];
}
function assoc_handle() {
logger('assoc_handle');
$channel = \App::get_channel();
return z_root() . '/channel/' . $channel['channel_address'];
}
function setAssoc($handle, $data) {
logger('setAssoc');
$channel = channelx_by_nick(basename($handle));
if($channel)
set_pconfig($channel['channel_id'],'openid','associate',$data);
}
function getAssoc($handle) {
logger('getAssoc: ' . $handle);
$channel = channelx_by_nick(basename($handle));
if($channel)
return get_pconfig($channel['channel_id'], 'openid', 'associate');
return false;
}
function delAssoc($handle) {
logger('delAssoc');
$channel = channelx_by_nick(basename($handle));
if($channel)
return del_pconfig($channel['channel_id'], 'openid', 'associate');
}
}

View file

@ -264,23 +264,22 @@ class Like extends \Zotlabs\Web\Controller {
logger('like: no item ' . $item_id);
killme();
}
xchan_query($r,true,(($r[0]['uid'] == local_channel()) ? 0 : local_channel()));
$item = $r[0];
$owner_uid = $item['uid'];
$owner_aid = $item['aid'];
$sys = get_sys_channel();
// if this is a "discover" item, (item['uid'] is the sys channel),
// fallback to the item comment policy, which should've been
// respected when generating the conversation thread.
// Even if the activity is rejected by the item owner, it should still get attached
// to the local discover conversation on this site.
if(($owner_uid != $sys['channel_id']) && (! perm_is_allowed($owner_uid,$observer['xchan_hash'],'post_comments'))) {
$owner_uid = $r[0]['uid'];
$owner_aid = $r[0]['aid'];
$can_comment = false;
if((array_key_exists('owner',$item)) && intval($item['owner']['abook_self']))
$can_comment = perm_is_allowed($item['uid'],$observer['xchan_hash'],'post_comments');
else
$can_comment = can_comment_on_post($observer['xchan_hash'],$item);
if(! $can_comment) {
notice( t('Permission denied') . EOL);
killme();
}

View file

@ -1,198 +0,0 @@
<?php
namespace Zotlabs\Module;
require_once('library/openid/openid.php');
require_once('include/auth.php');
class Openid extends \Zotlabs\Web\Controller {
function get() {
$noid = get_config('system','disable_openid');
if($noid)
goaway(z_root());
logger('mod_openid ' . print_r($_REQUEST,true), LOGGER_DATA);
if(x($_REQUEST,'openid_mode')) {
$openid = new LightOpenID(z_root());
if($openid->validate()) {
logger('openid: validate');
$authid = normalise_openid($_REQUEST['openid_identity']);
if(! strlen($authid)) {
logger( t('OpenID protocol error. No ID returned.') . EOL);
goaway(z_root());
}
$x = match_openid($authid);
if($x) {
$r = q("select * from channel where channel_id = %d limit 1",
intval($x)
);
if($r) {
$y = q("select * from account where account_id = %d limit 1",
intval($r[0]['channel_account_id'])
);
if($y) {
foreach($y as $record) {
if(($record['account_flags'] == ACCOUNT_OK) || ($record['account_flags'] == ACCOUNT_UNVERIFIED)) {
logger('mod_openid: openid success for ' . $x[0]['channel_name']);
$_SESSION['uid'] = $r[0]['channel_id'];
$_SESSION['account_id'] = $r[0]['channel_account_id'];
$_SESSION['authenticated'] = true;
authenticate_success($record,$r[0],true,true,true,true);
goaway(z_root());
}
}
}
}
}
// Successful OpenID login - but we can't match it to an existing account.
// See if they've got an xchan
$r = q("select * from xconfig left join xchan on xchan_hash = xconfig.xchan where cat = 'system' and k = 'openid' and v = '%s' limit 1",
dbesc($authid)
);
if($r) {
$_SESSION['authenticated'] = 1;
$_SESSION['visitor_id'] = $r[0]['xchan_hash'];
$_SESSION['my_url'] = $r[0]['xchan_url'];
$_SESSION['my_address'] = $r[0]['xchan_addr'];
$arr = array('xchan' => $r[0], 'session' => $_SESSION);
call_hooks('magic_auth_openid_success',$arr);
\App::set_observer($r[0]);
require_once('include/security.php');
\App::set_groups(init_groups_visitor($_SESSION['visitor_id']));
info(sprintf( t('Welcome %s. Remote authentication successful.'),$r[0]['xchan_name']));
logger('mod_openid: remote auth success from ' . $r[0]['xchan_addr']);
if($_SESSION['return_url'])
goaway($_SESSION['return_url']);
goaway(z_root());
}
// no xchan...
// create one.
// We should probably probe the openid url and figure out if they have any kind of social presence we might be able to
// scrape some identifying info from.
$name = $authid;
$url = trim($_REQUEST['openid_identity'],'/');
if(strpos($url,'http') === false)
$url = 'https://' . $url;
$pphoto = z_root() . '/' . get_default_profile_photo();
$parsed = @parse_url($url);
if($parsed) {
$host = $parsed['host'];
}
$attr = $openid->getAttributes();
if(is_array($attr) && count($attr)) {
foreach($attr as $k => $v) {
if($k === 'namePerson/friendly')
$nick = notags(trim($v));
if($k === 'namePerson/first')
$first = notags(trim($v));
if($k === 'namePerson')
$name = notags(trim($v));
if($k === 'contact/email')
$addr = notags(trim($v));
if($k === 'media/image/aspect11')
$photosq = trim($v);
if($k === 'media/image/default')
$photo_other = trim($v);
}
}
if(! $nick) {
if($first)
$nick = $first;
else
$nick = $name;
}
require_once('library/urlify/URLify.php');
$x = strtolower(\URLify::transliterate($nick));
if($nick & $host)
$addr = $nick . '@' . $host;
$network = 'unknown';
if($photosq)
$pphoto = $photosq;
elseif($photo_other)
$pphoto = $photo_other;
$mimetype = guess_image_type($pphoto);
$x = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_mimetype,
xchan_photo_l, xchan_addr, xchan_url, xchan_connurl, xchan_follow, xchan_connpage, xchan_name, xchan_network, xchan_photo_date,
xchan_name_date, xchan_hidden)
values ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', 1) ",
dbesc($url),
dbesc(''),
dbesc(''),
dbesc(''),
dbesc($mimetype),
dbesc($pphoto),
dbesc($addr),
dbesc($url),
dbesc(''),
dbesc(''),
dbesc(''),
dbesc($name),
dbesc($network),
dbesc(datetime_convert()),
dbesc(datetime_convert())
);
if($x) {
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($url)
);
if($r) {
$photos = import_xchan_photo($pphoto,$url);
if($photos) {
$z = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s',
xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'",
dbesc(datetime_convert()),
dbesc($photos[0]),
dbesc($photos[1]),
dbesc($photos[2]),
dbesc($photos[3]),
dbesc($url)
);
}
set_xconfig($url,'system','openid',$authid);
$_SESSION['authenticated'] = 1;
$_SESSION['visitor_id'] = $r[0]['xchan_hash'];
$_SESSION['my_url'] = $r[0]['xchan_url'];
$_SESSION['my_address'] = $r[0]['xchan_addr'];
$arr = array('xchan' => $r[0], 'session' => $_SESSION);
call_hooks('magic_auth_openid_success',$arr);
\App::set_observer($r[0]);
info(sprintf( t('Welcome %s. Remote authentication successful.'),$r[0]['xchan_name']));
logger('mod_openid: remote auth success from ' . $r[0]['xchan_addr']);
if($_SESSION['return_url'])
goaway($_SESSION['return_url']);
goaway(z_root());
}
}
}
}
notice( t('Login failed.') . EOL);
goaway(z_root());
// NOTREACHED
}
}

View file

@ -58,7 +58,9 @@ class Ratingsearch extends \Zotlabs\Web\Controller {
$ret['success'] = true;
$r = q("select * from xlink left join xchan on xlink_xchan = xchan_hash
where xlink_link = '%s' and xlink_rating != 0 and xlink_static = 1 order by xchan_name asc",
where xlink_link = '%s' and xlink_rating != 0 and xlink_static = 1
and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0
order by xchan_name asc",
dbesc($target)
);

View file

@ -2,7 +2,6 @@
namespace Zotlabs\Module;
class Rmagic extends \Zotlabs\Web\Controller {
function init() {
@ -32,18 +31,6 @@ class Rmagic extends \Zotlabs\Web\Controller {
$arr = array('address' => $address);
call_hooks('reverse_magic_auth', $arr);
try {
require_once('library/openid/openid.php');
$openid = new \LightOpenID(z_root());
$openid->identity = $address;
$openid->returnUrl = z_root() . '/openid';
$openid->required = array('namePerson/friendly', 'namePerson');
$openid->optional = array('namePerson/first','media/image/aspect11','media/image/default');
goaway($openid->authUrl());
} catch (\Exception $e) {
notice( t('We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID.').'<br /><br >'. t('The error message was:').' '.$e->getMessage());
}
// if they're still here...
notice( t('Authentication failed.') . EOL);
return;

View file

@ -274,6 +274,22 @@ class Browser extends DAV\Browser\Plugin {
// SimpleCollection, we won't need to show the panel either.
if (get_class($node) === 'Sabre\\DAV\\SimpleCollection')
return;
require_once('include/acl_selectors.php');
$aclselect = null;
$lockstate = '';
if($this->auth-owner_id) {
$channel = channelx_by_n($this->auth->owner_id);
if($channel) {
$acl = new \Zotlabs\Access\AccessList($channel);
$channel_acl = $acl->get();
$lockstate = (($acl->is_private()) ? 'lock' : 'unlock');
$aclselect = ((local_channel() == $this->auth->owner_id) ? populate_acl($channel_acl,false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_storage')) : '');
}
}
// Storage and quota for the account (all channels of the owner of this directory)!
$limit = engr_units_to_bytes(service_class_fetch($owner, 'attach_upload_limit'));
@ -293,7 +309,6 @@ class Browser extends DAV\Browser\Plugin {
userReadableSize($limit),
round($used / $limit, 1) * 100);
}
// prepare quota for template
$quota = array();
$quota['used'] = $used;
@ -306,7 +321,12 @@ class Browser extends DAV\Browser\Plugin {
'$folder_submit' => t('Create'),
'$upload_header' => t('Upload file'),
'$upload_submit' => t('Upload'),
'$quota' => $quota
'$quota' => $quota,
'$channick' => $this->auth->owner_nick,
'$aclselect' => $aclselect,
'$lockstate' => $lockstate,
'$return_url' => \App::$cmd,
'$dragdroptext' => t('Drop files here to immediately upload')
));
}

View file

@ -3,6 +3,7 @@
namespace Zotlabs\Storage;
use Sabre\DAV;
use Sabre\HTTP;
/**
* @brief RedDirectory class.
@ -159,7 +160,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
throw new DAV\Exception\Forbidden('Permission denied.');
}
list($parent_path, ) = DAV\URLUtil::splitPath($this->red_path);
list($parent_path, ) = HTTP\URLUtil::splitPath($this->red_path);
$new_path = $parent_path . '/' . $name;
$r = q("UPDATE attach SET filename = '%s' WHERE hash = '%s' AND uid = %d",

View file

@ -152,6 +152,7 @@ class Router {
// pretend this is a module so it will initialise the theme
\App::$module = '404';
\App::$module_loaded = true;
\App::$error = true;
}
}
}

View file

@ -124,7 +124,7 @@ class WebServer {
// now that we've been through the module content, see if the page reported
// a permission problem and if so, a 403 response would seem to be in order.
if(stristr(implode("", $_SESSION['sysmsg']), t('Permission denied'))) {
if(is_array($_SESSION['sysmsg']) && stristr(implode("", $_SESSION['sysmsg']), t('Permission denied'))) {
header($_SERVER['SERVER_PROTOCOL'] . ' 403 ' . t('Permission denied.'));
}

View file

@ -765,6 +765,7 @@ class App {
public static $pdl = null; // Comanche page description
private static $perms = null; // observer permissions
private static $widgets = array(); // widgets for this page
public static $config = array(); // config cache
public static $session = null;
public static $groups;
@ -774,7 +775,6 @@ class App {
public static $plugins_admin;
public static $module_loaded = false;
public static $query_string;
public static $config; // config cache
public static $page;
public static $profile;
public static $user;
@ -1551,6 +1551,9 @@ function check_config(&$a) {
load_hooks();
check_for_new_perms();
check_cron_broken();
}
@ -2440,6 +2443,67 @@ function cert_bad_email() {
}
function check_for_new_perms() {
$pregistered = get_config('system','perms');
$pcurrent = array_keys(\Zotlabs\Access\Permissions::Perms());
if(! $pregistered) {
set_config('system','perms',$pcurrent);
return;
}
$found_new_perm = false;
foreach($pcurrent as $p) {
if(! in_array($p,$pregistered)) {
$found_new_perm = true;
// for all channels
$c = q("select channel_id from channel where true");
if($c) {
foreach($c as $cc) {
// get the permission role
$r = q("select v from pconfig where uid = %d and cat = 'system' and k = 'permissions_role'",
intval($cc['uid'])
);
if($r) {
// get a list of connections
$x = q("select abook_xchan from abook where abook_channel = %d and abook_self = 0",
intval($cc['uid'])
);
// get the permissions role details
$rp = \Zotlabs\Access\PermissionRoles::role_perms($r[0]['v']);
if($rp) {
// set the channel limits if appropriate or 0
if(array_key_exists('limits',$rp) && array_key_exists($p,$rp['limits'])) {
\Zotlabs\Access\PermissionLimits::Set($cc['uid'],$p,$rp['limits'][$p]);
}
else {
\Zotlabs\Access\PermissionLimits::Set($cc['uid'],$p,0);
}
$set = ((array_key_exists('perms_connect',$rp) && array_key_exists($p,$rp['perms_connect'])) ? true : false);
// foreach connection set to the perms_connect value
if($x) {
foreach($x as $xx) {
set_abconfig($cc['uid'],$xx['abook_xchan'],'my_perms',$p,intval($set));
}
}
}
}
}
}
}
}
// We should probably call perms_refresh here, but this should get pushed in 24 hours and there is no urgency
if($found_new_perm)
set_config('system','perms',$pcurrent);
}
/**
* @brief Send warnings every 3-5 days if cron is not running.
*/

View file

@ -72,7 +72,7 @@ require_once('include/api_auth.php');
* MAIN API ENTRY POINT *
**************************/
function api_call(&$a){
function api_call($a){
GLOBAL $API, $called_api;
// preset
@ -166,7 +166,7 @@ require_once('include/api_auth.php');
/**
* RSS extra info
*/
function api_rss_extra(&$a, $arr, $user_info){
function api_rss_extra($a, $arr, $user_info){
if (is_null($user_info)) $user_info = api_get_user($a);
$arr['$user'] = $user_info;
$arr['$rss'] = array(
@ -186,7 +186,7 @@ require_once('include/api_auth.php');
* Returns user info array.
*/
function api_get_user(&$a, $contact_id = null, $contact_xchan = null){
function api_get_user($a, $contact_id = null, $contact_xchan = null){
global $called_api;
$user = null;
$extra_query = "";
@ -356,7 +356,7 @@ require_once('include/api_auth.php');
}
function api_client_register(&$a,$type) {
function api_client_register($a,$type) {
$ret = array();
$key = random_string(16);
@ -389,7 +389,7 @@ require_once('include/api_auth.php');
function api_item_get_user(&$a, $item) {
function api_item_get_user($a, $item) {
// The author is our direct contact, in a conversation with us.
@ -473,7 +473,7 @@ require_once('include/api_auth.php');
* returns a 401 status code and an error message if not.
* http://developer.twitter.com/doc/get/account/verify_credentials
*/
function api_account_verify_credentials(&$a, $type){
function api_account_verify_credentials($a, $type){
if (api_user()===false) return false;
$user_info = api_get_user($a);
@ -483,7 +483,7 @@ require_once('include/api_auth.php');
api_register_func('api/account/verify_credentials','api_account_verify_credentials', true);
function api_account_logout(&$a, $type){
function api_account_logout($a, $type){
require_once('include/auth.php');
App::$session->nuke();
return api_apply_template("user", $type, array('$user' => null));
@ -507,7 +507,7 @@ require_once('include/api_auth.php');
* Red basic channel export
*/
function api_export_basic(&$a, $type) {
function api_export_basic($a, $type) {
if(api_user() === false) {
logger('api_export_basic: no user');
return false;
@ -521,7 +521,7 @@ require_once('include/api_auth.php');
api_register_func('api/red/channel/export/basic','api_export_basic', true);
function api_channel_stream(&$a, $type) {
function api_channel_stream($a, $type) {
if(api_user() === false) {
logger('api_channel_stream: no user');
return false;
@ -537,7 +537,7 @@ require_once('include/api_auth.php');
}
api_register_func('api/red/channel/stream','api_channel_stream', true);
function api_attach_list(&$a,$type) {
function api_attach_list($a,$type) {
logger('api_user: ' . api_user());
json_return_and_die(attach_list_files(api_user(),get_observer_hash(),'','','','created asc'));
}
@ -547,7 +547,7 @@ require_once('include/api_auth.php');
function api_file_meta(&$a,$type) {
function api_file_meta($a,$type) {
if (api_user()===false) return false;
if(! $_REQUEST['file_id']) return false;
$r = q("select * from attach where uid = %d and hash = '%s' limit 1",
@ -565,7 +565,7 @@ require_once('include/api_auth.php');
api_register_func('api/red/filemeta', 'api_file_meta', true);
function api_file_data(&$a,$type) {
function api_file_data($a,$type) {
if (api_user()===false) return false;
if(! $_REQUEST['file_id']) return false;
$start = (($_REQUEST['start']) ? intval($_REQUEST['start']) : 0);
@ -609,7 +609,7 @@ require_once('include/api_auth.php');
function api_file_detail(&$a,$type) {
function api_file_detail($a,$type) {
if (api_user()===false) return false;
if(! $_REQUEST['file_id']) return false;
$r = q("select * from attach where uid = %d and hash = '%s' limit 1",
@ -633,18 +633,18 @@ require_once('include/api_auth.php');
api_register_func('api/red/file', 'api_file_detail', true);
function api_albums(&$a,$type) {
function api_albums($a,$type) {
json_return_and_die(photos_albums_list(App::get_channel(),App::get_observer()));
}
api_register_func('api/red/albums','api_albums', true);
function api_photos(&$a,$type) {
function api_photos($a,$type) {
$album = $_REQUEST['album'];
json_return_and_die(photos_list_photos(App::get_channel(),App::get_observer(),$album));
}
api_register_func('api/red/photos','api_photos', true);
function api_photo_detail(&$a,$type) {
function api_photo_detail($a,$type) {
if (api_user()===false) return false;
if(! $_REQUEST['photo_id']) return false;
$scale = ((array_key_exists('scale',$_REQUEST)) ? intval($_REQUEST['scale']) : 0);
@ -686,7 +686,7 @@ require_once('include/api_auth.php');
api_register_func('api/red/photo', 'api_photo_detail', true);
function api_group_members(&$a,$type) {
function api_group_members($a,$type) {
if(api_user() === false)
return false;
@ -710,7 +710,7 @@ require_once('include/api_auth.php');
function api_group(&$a,$type) {
function api_group($a,$type) {
if(api_user() === false)
return false;
@ -722,7 +722,7 @@ require_once('include/api_auth.php');
api_register_func('api/red/group','api_group', true);
function api_red_xchan(&$a,$type) {
function api_red_xchan($a,$type) {
logger('api_xchan');
if(api_user() === false)
@ -740,7 +740,7 @@ require_once('include/api_auth.php');
api_register_func('api/red/xchan','api_red_xchan',true);
function api_statuses_mediap(&$a, $type) {
function api_statuses_mediap($a, $type) {
if (api_user() === false) {
logger('api_statuses_update: no user');
return false;
@ -786,7 +786,7 @@ require_once('include/api_auth.php');
}
api_register_func('api/statuses/mediap','api_statuses_mediap', true);
function api_statuses_update(&$a, $type) {
function api_statuses_update($a, $type) {
if (api_user() === false) {
logger('api_statuses_update: no user');
return false;
@ -907,7 +907,7 @@ require_once('include/api_auth.php');
api_register_func('api/statuses/update','api_statuses_update', true);
function red_item_new(&$a, $type) {
function red_item_new($a, $type) {
if (api_user() === false) {
logger('api_red_item_new: no user');
@ -941,7 +941,7 @@ require_once('include/api_auth.php');
api_register_func('api/red/item/new','red_item_new', true);
function red_item(&$a, $type) {
function red_item($a, $type) {
if (api_user() === false) {
logger('api_red_item_full: no user');
@ -1042,7 +1042,7 @@ require_once('include/api_auth.php');
return $status_info;
}
function api_status_show(&$a, $type){
function api_status_show($a, $type){
$user_info = api_get_user($a);
// get last public message
@ -1120,7 +1120,7 @@ require_once('include/api_auth.php');
// FIXME - this is essentially the same as api_status_show except for the template formatting at the end. Consolidate.
function api_users_show(&$a, $type){
function api_users_show($a, $type){
$user_info = api_get_user($a);
require_once('include/security.php');
@ -1192,7 +1192,7 @@ require_once('include/api_auth.php');
* TODO: Add reply info
*/
function api_statuses_home_timeline(&$a, $type){
function api_statuses_home_timeline($a, $type){
if (api_user() === false)
return false;
@ -1274,7 +1274,7 @@ require_once('include/api_auth.php');
api_register_func('api/statuses/home_timeline','api_statuses_home_timeline', true);
api_register_func('api/statuses/friends_timeline','api_statuses_home_timeline', true);
function api_statuses_public_timeline(&$a, $type){
function api_statuses_public_timeline($a, $type){
if (api_user()===false) return false;
$user_info = api_get_user($a);
@ -1338,7 +1338,7 @@ require_once('include/api_auth.php');
*
*/
function api_statuses_show(&$a, $type){
function api_statuses_show($a, $type){
if (api_user()===false) return false;
$user_info = api_get_user($a);
@ -1388,7 +1388,7 @@ require_once('include/api_auth.php');
/**
*
*/
function api_statuses_repeat(&$a, $type){
function api_statuses_repeat($a, $type){
if (api_user()===false) return false;
$user_info = api_get_user($a);
@ -1434,7 +1434,7 @@ require_once('include/api_auth.php');
*
*/
function api_statuses_destroy(&$a, $type){
function api_statuses_destroy($a, $type){
if (api_user()===false) return false;
$user_info = api_get_user($a);
@ -1498,7 +1498,7 @@ require_once('include/api_auth.php');
*/
function api_statuses_mentions(&$a, $type){
function api_statuses_mentions($a, $type){
if (api_user()===false) return false;
$user_info = api_get_user($a);
@ -1565,7 +1565,7 @@ require_once('include/api_auth.php');
api_register_func('api/statuses/replies','api_statuses_mentions', true);
function api_statuses_user_timeline(&$a, $type){
function api_statuses_user_timeline($a, $type){
if (api_user()===false) return false;
$user_info = api_get_user($a);
@ -1649,7 +1649,7 @@ require_once('include/api_auth.php');
*
* api v1 : https://web.archive.org/web/20131019055350/https://dev.twitter.com/docs/api/1/post/favorites/create/%3Aid
*/
function api_favorites_create_destroy(&$a, $type){
function api_favorites_create_destroy($a, $type){
logger('favorites_create_destroy');
@ -1717,7 +1717,7 @@ require_once('include/api_auth.php');
function api_favorites(&$a, $type){
function api_favorites($a, $type){
if (api_user()===false)
return false;
@ -1986,7 +1986,7 @@ require_once('include/api_auth.php');
}
function api_account_rate_limit_status(&$a,$type) {
function api_account_rate_limit_status($a,$type) {
$hash = array(
'reset_time_in_seconds' => strtotime('now + 1 hour'),
@ -2002,7 +2002,7 @@ require_once('include/api_auth.php');
}
api_register_func('api/account/rate_limit_status','api_account_rate_limit_status',true);
function api_help_test(&$a,$type) {
function api_help_test($a,$type) {
if ($type == 'xml')
$ok = "true";
@ -2019,7 +2019,7 @@ require_once('include/api_auth.php');
* This function is deprecated by Twitter
* returns: json, xml
**/
function api_statuses_f(&$a, $type, $qtype) {
function api_statuses_f($a, $type, $qtype) {
if (api_user()===false) return false;
$user_info = api_get_user($a);
@ -2040,6 +2040,7 @@ require_once('include/api_auth.php');
return false;
}
// @fixme - update for hubzilla extensible perms using abconfig or find a better way to do it
// For Red, the closest thing we can do to figure out if you're friends is if both of you are sending each other your streams.
// This won't work if either of you send your stream to everybody on the network
if($qtype == 'friends')
@ -2060,12 +2061,12 @@ require_once('include/api_auth.php');
return array('$users' => $ret);
}
function api_statuses_friends(&$a, $type){
function api_statuses_friends($a, $type){
$data = api_statuses_f($a,$type,"friends");
if ($data===false) return false;
return api_apply_template("friends", $type, $data);
}
function api_statuses_followers(&$a, $type){
function api_statuses_followers($a, $type){
$data = api_statuses_f($a,$type,"followers");
if ($data===false) return false;
return api_apply_template("friends", $type, $data);
@ -2078,7 +2079,7 @@ require_once('include/api_auth.php');
function api_statusnet_config(&$a,$type) {
function api_statusnet_config($a,$type) {
load_config('system');
@ -2115,7 +2116,7 @@ require_once('include/api_auth.php');
api_register_func('api/friendica/config','api_statusnet_config',false);
api_register_func('api/red/config','api_statusnet_config',false);
function api_statusnet_version(&$a,$type) {
function api_statusnet_version($a,$type) {
// liar
@ -2133,7 +2134,7 @@ require_once('include/api_auth.php');
api_register_func('api/statusnet/version','api_statusnet_version',false);
function api_friendica_version(&$a,$type) {
function api_friendica_version($a,$type) {
if($type === 'xml') {
header("Content-type: application/xml");
@ -2150,7 +2151,7 @@ require_once('include/api_auth.php');
api_register_func('api/red/version','api_friendica_version',false);
function api_ff_ids(&$a,$type,$qtype) {
function api_ff_ids($a,$type,$qtype) {
if(! api_user())
return false;
@ -2186,17 +2187,17 @@ require_once('include/api_auth.php');
}
}
function api_friends_ids(&$a,$type) {
function api_friends_ids($a,$type) {
api_ff_ids($a,$type,'friends');
}
function api_followers_ids(&$a,$type) {
function api_followers_ids($a,$type) {
api_ff_ids($a,$type,'followers');
}
api_register_func('api/friends/ids','api_friends_ids',true);
api_register_func('api/followers/ids','api_followers_ids',true);
function api_direct_messages_new(&$a, $type) {
function api_direct_messages_new($a, $type) {
if (api_user()===false) return false;
if (!x($_POST, "text") || !x($_POST,"screen_name")) return;
@ -2254,7 +2255,7 @@ require_once('include/api_auth.php');
}
api_register_func('api/direct_messages/new','api_direct_messages_new',true);
function api_direct_messages_box(&$a, $type, $box) {
function api_direct_messages_box($a, $type, $box) {
if (api_user()===false) return false;
$user_info = api_get_user($a);
@ -2314,16 +2315,16 @@ require_once('include/api_auth.php');
}
function api_direct_messages_sentbox(&$a, $type){
function api_direct_messages_sentbox($a, $type){
return api_direct_messages_box($a, $type, "sentbox");
}
function api_direct_messages_inbox(&$a, $type){
function api_direct_messages_inbox($a, $type){
return api_direct_messages_box($a, $type, "inbox");
}
function api_direct_messages_all(&$a, $type){
function api_direct_messages_all($a, $type){
return api_direct_messages_box($a, $type, "all");
}
function api_direct_messages_conversation(&$a, $type){
function api_direct_messages_conversation($a, $type){
return api_direct_messages_box($a, $type, "conversation");
}
api_register_func('api/direct_messages/conversation','api_direct_messages_conversation',true);
@ -2332,7 +2333,7 @@ require_once('include/api_auth.php');
api_register_func('api/direct_messages','api_direct_messages_inbox',true);
function api_oauth_request_token(&$a, $type){
function api_oauth_request_token($a, $type){
try{
$oauth = new ZotOAuth1();
$req = OAuth1Request::from_request();
@ -2347,7 +2348,7 @@ require_once('include/api_auth.php');
killme();
}
function api_oauth_access_token(&$a, $type){
function api_oauth_access_token($a, $type){
try{
$oauth = new ZotOAuth1();
$req = OAuth1Request::from_request();

View file

@ -64,8 +64,10 @@ function api_login(&$a){
}
}
if($record['account']) {
authenticate_success($record);
authenticate_success($record['account']);
if($channel_login)
change_channel($channel_login);

View file

@ -74,6 +74,7 @@ function z_mime_content_type($filename) {
// 'webm' => 'audio/webm',
'mp4' => 'video/mp4',
// 'mp4' => 'audio/mp4',
'mkv' => 'video/x-matroska',
// adobe
'pdf' => 'application/pdf',

View file

@ -16,16 +16,24 @@ require_once('include/security.php');
/**
* @brief Verify login credentials.
*
* If system <i>authlog</i> is set a log entry will be added for failed login
* If system.authlog is set a log entry will be added for failed login
* attempts.
*
* @param string $email
* @param string $login
* The login to verify (channel address, account email or guest login token).
* @param string $pass
* The provided password to verify.
* @return array|null
* Returns account record on success, null on failure.
* The return array is dependent on the login mechanism.
* $ret['account'] will be set if either an email or channel address validation was successful (local login).
* $ret['channel'] will be set if a channel address validation was successful.
* $ret['xchan'] will be set if a guest access token validation was successful.
* Keys will exist for invalid return arrays but will be set to null.
* This function does not perform a login. It merely validates systems passwords and tokens.
*
*/
function account_verify_password($login, $pass) {
$ret = [ 'account' => null, 'channel' => null, 'xchan' => null ];

View file

@ -1371,7 +1371,8 @@ function zat_init() {
dbesc($_REQUEST['zat'])
);
if($r) {
atoken_login($r[0]);
$xchan = atoken_xchan($r[0]);
atoken_login($xchan);
}
}
@ -1567,7 +1568,7 @@ function is_public_profile() {
return false;
$channel = App::get_channel();
if($channel) {
$perm = \Zotlabs\Access\PermissionLimit::Get($channel['channel_id'],'view_profile');
$perm = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile');
if($perm == PERMS_PUBLIC)
return true;
}

View file

@ -556,13 +556,13 @@ function update_birthdays() {
$ev['uid'] = $rr['abook_channel'];
$ev['account'] = $rr['abook_account'];
$ev['event_xchan'] = $rr['xchan_hash'];
$ev['start'] = datetime_convert('UTC', 'UTC', $rr['abook_dob']);
$ev['finish'] = datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day ');
$ev['dtstart'] = datetime_convert('UTC', 'UTC', $rr['abook_dob']);
$ev['dtend'] = datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day ');
$ev['adjust'] = intval(feature_enabled($rr['abook_channel'],'smart_birthdays'));
$ev['summary'] = sprintf( t('%1$s\'s birthday'), $rr['xchan_name']);
$ev['description'] = sprintf( t('Happy Birthday %1$s'),
'[zrl=' . $rr['xchan_url'] . ']' . $rr['xchan_name'] . '[/zrl]') ;
$ev['type'] = 'birthday';
$ev['etype'] = 'birthday';
$z = event_store_event($ev);
if ($z) {

View file

@ -2639,32 +2639,33 @@ function getIconFromType($type) {
'application/octet-stream' => 'fa-file-o',
//Text
'text/plain' => 'fa-file-text-o',
'application/msword' => 'fa-file-text-o',
'application/pdf' => 'fa-file-text-o',
'application/vnd.oasis.opendocument.text' => 'fa-file-text-o',
'application/msword' => 'fa-file-word-o',
'application/pdf' => 'fa-file-pdf-o',
'application/vnd.oasis.opendocument.text' => 'fa-file-word-o',
'application/epub+zip' => 'fa-book',
//Spreadsheet
'application/vnd.oasis.opendocument.spreadsheet' => 'fa-table',
'application/vnd.ms-excel' => 'fa-table',
'application/vnd.oasis.opendocument.spreadsheet' => 'fa-file-excel-o',
'application/vnd.ms-excel' => 'fa-file-excel-o',
//Image
'image/jpeg' => 'fa-picture-o',
'image/png' => 'fa-picture-o',
'image/gif' => 'fa-picture-o',
'image/svg+xml' => 'fa-picture-o',
//Archive
'application/zip' => 'fa-archive',
'application/x-rar-compressed' => 'fa-archive',
'application/zip' => 'fa-file-archive-o',
'application/x-rar-compressed' => 'fa-file-archive-o',
//Audio
'audio/mpeg' => 'fa-music',
'audio/wav' => 'fa-music',
'application/ogg' => 'fa-music',
'audio/ogg' => 'fa-music',
'audio/webm' => 'fa-music',
'audio/mp4' => 'fa-music',
'audio/mpeg' => 'fa-file-audio-o',
'audio/wav' => 'fa-file-audio-o',
'application/ogg' => 'fa-file-audio-o',
'audio/ogg' => 'fa-file-audio-o',
'audio/webm' => 'fa-file-audio-o',
'audio/mp4' => 'fa-file-audio-o',
//Video
'video/quicktime' => 'fa-film',
'video/webm' => 'fa-film',
'video/mp4' => 'fa-film'
'video/quicktime' => 'fa-file-video-o',
'video/webm' => 'fa-file-video-o',
'video/mp4' => 'fa-file-video-o',
'video/x-matroska' => 'fa-file-video-o'
);
$iconFromType = 'fa-file-o';

View file

@ -9,16 +9,22 @@ Readmore.js is tested with—and supported on—all versions of jQuery greater t
## Install
Install Readmore.js with Bower:
Install Readmore.js with npm:
```
$ bower install readmore
$ npm install readmore-js
```
Then include it in your HTML:
```html
<script src="/bower_components/readmore/readmore.min.js"></script>
<script src="/node_modules/readmore-js/readmore.min.js"></script>
```
Or, using Webpack or Browserify:
```javascript
require('readmore-js');
```
@ -49,17 +55,23 @@ $('article').readmore({
* `startOpen: false` do not immediately truncate, start in the fully opened position
* `beforeToggle: function() {}` called after a more or less link is clicked, but *before* the block is collapsed or expanded
* `afterToggle: function() {}` called *after* the block is collapsed or expanded
* `blockProcessed: function() {}` called once per block during initilization after Readmore.js has processed the block.
If the element has a `max-height` CSS property, Readmore.js will use that value rather than the value of the `collapsedHeight` option.
### The callbacks:
The callback functions, `beforeToggle` and `afterToggle`, both receive the same arguments: `trigger`, `element`, and `expanded`.
The `beforeToggle` and `afterToggle` callbacks both receive the same arguments: `trigger`, `element`, and `expanded`.
* `trigger`: the "Read more" or "Close" element that was clicked
* `element`: the block that is being collapsed or expanded
* `expanded`: Boolean; `true` means the block is expanded
The `blockProcessed` callback receives `element` and `collapsable`.
* `element`: the block that has just been processed
* `collapsable`: Boolean; `false` means the block was shorter than the specified minimum `collapsedHeight`--the block will not have a "Read more" link
#### Callback example:
Here's an example of how you could use the `afterToggle` callback to scroll back to the top of a block when the "Close" link is clicked.
@ -166,6 +178,6 @@ $ npm install
Which will install the necessary development dependencies. Then, to build the minified script:
```
$ gulp compress
$ npm run build
```

View file

@ -0,0 +1,13 @@
diff --git a/library/readmore.js/readmore.js b/library/readmore.js/readmore.js
index 34a624e..51222ce 100644
--- a/library/readmore.js/readmore.js
+++ b/library/readmore.js/readmore.js
@@ -246,7 +246,7 @@
collapsedHeight = $element.data('collapsedHeight');
if ($element.height() <= collapsedHeight) {
- newHeight = $element.data('expandedHeight') + 'px';
+ newHeight = 100 + '%';
newLink = 'lessLink';
expanded = true;
}

View file

@ -37,8 +37,9 @@
startOpen: false,
// callbacks
beforeToggle: function(){},
afterToggle: function(){}
blockProcessed: function() {},
beforeToggle: function() {},
afterToggle: function() {}
},
cssEmbedded = {},
uniqueIdCounter = 0;
@ -187,6 +188,9 @@
if (current.outerHeight(true) <= collapsedHeight + heightMargin) {
// The block is shorter than the limit, so there's no need to truncate it.
if (this.options.blockProcessed && typeof this.options.blockProcessed === 'function') {
this.options.blockProcessed(current, false);
}
return true;
}
else {
@ -206,7 +210,7 @@
};
})(this))
.attr({
'data-readmore-toggle': '',
'data-readmore-toggle': id,
'aria-controls': id
}));
@ -215,6 +219,10 @@
height: collapsedHeight
});
}
if (this.options.blockProcessed && typeof this.options.blockProcessed === 'function') {
this.options.blockProcessed(current, true);
}
}
},
@ -224,11 +232,11 @@
}
if (! trigger) {
trigger = $('[aria-controls="' + _this.element.id + '"]')[0];
trigger = $('[aria-controls="' + this.element.id + '"]')[0];
}
if (! element) {
element = _this.element;
element = this.element;
}
var $element = $(element),
@ -250,14 +258,18 @@
// Fire beforeToggle callback
// Since we determined the new "expanded" state above we're now out of sync
// with our true current state, so we need to flip the value of `expanded`
this.options.beforeToggle(trigger, $element, ! expanded);
if (this.options.beforeToggle && typeof this.options.beforeToggle === 'function') {
this.options.beforeToggle(trigger, $element, ! expanded);
}
$element.css({'height': newHeight});
// Fire afterToggle callback
$element.on('transitionend', (function(_this) {
return function() {
_this.options.afterToggle(trigger, $element, expanded);
if (_this.options.afterToggle && typeof _this.options.afterToggle === 'function') {
_this.options.afterToggle(trigger, $element, expanded);
}
$(this).attr({
'aria-expanded': expanded
@ -272,7 +284,7 @@
};
})(this))
.attr({
'data-readmore-toggle': '',
'data-readmore-toggle': $element.attr('id'),
'aria-controls': $element.attr('id')
}));
},

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
<?php
function po2php_run($argc,$argv) {
if ($argc < 2) {
@ -59,8 +58,9 @@ function po2php_run($argc,$argv) {
$out .= 'function string_plural_select_' . $lang . '($n){'."\n";
$out .= ' return '.$cond.';'."\n";
$out .= '}}'."\n";
$out .= 'App::$rtl = ' . intval($rtl) ;
}
$out .= 'App::$rtl = ' . intval($rtl) . ';';
if ($k!="" && substr($l,0,7)=="msgstr "){
if ($ink) { $ink = False; $out .= 'App::$strings["'.$k.'"] = '; }

View file

@ -163,7 +163,7 @@ class Plugin extends DAV\ServerPlugin {
* @return bool
*/
function httpPOST(RequestInterface $request, ResponseInterface $response) {
$contentType = $request->getHeader('Content-Type');
list($contentType) = explode(';', $contentType);
if ($contentType !== 'application/x-www-form-urlencoded' &&
@ -179,7 +179,7 @@ class Plugin extends DAV\ServerPlugin {
if ($this->server->emit('onBrowserPostAction', [$uri, $postVars['sabreAction'], $postVars])) {
switch ($postVars['sabreAction']) {
switch ($postVars['sabreAction']) {
case 'mkcol' :
if (isset($postVars['name']) && trim($postVars['name'])) {
@ -221,7 +221,7 @@ class Plugin extends DAV\ServerPlugin {
if ($_FILES) $file = current($_FILES);
else break;
list(, $newName) = URLUtil::splitPath(trim($file['name']));
if (isset($postVars['name']) && trim($postVars['name']))
$newName = trim($postVars['name']);

View file

@ -40,6 +40,12 @@
resize: vertical;
}
#profile-jot-text.hover {
background-color: aliceblue;
opacity: 0.5;
box-shadow: inset 0 0px 7px #5cb85c;
}
.jot-attachment {
border: 0px;
padding: 10px;

View file

@ -41,3 +41,16 @@
padding: 7px 10px;
width: 100%;
}
#cloud-drag-area.hover {
background-color: aliceblue;
opacity: 0.5;
box-shadow: inset 0 0px 7px #5cb85c;
}
.upload-progress-bar {
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOM2RFTDwAE2QHxFMHIIwAAAABJRU5ErkJggg==') repeat-y;
background-size: 0px;
padding: 0px !important;
height: 3px;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -659,7 +659,7 @@ function collapseHeight() {
var position = $(window).scrollTop();
$(".wall-item-content, .directory-collapse").each(function() {
var orgHeight = parseInt($(this).css('height'));
var orgHeight = $(this).outerHeight(true);
if(orgHeight > divmore_height) {
if(! $(this).hasClass('divmore')) {
@ -679,7 +679,7 @@ function collapseHeight() {
beforeToggle: function(trigger, element, expanded) {
if(expanded) {
if((($(element).offset().top + divmore_height) - $(window).scrollTop()) < 65 ) {
$(window).scrollTop($(window).scrollTop() - (orgHeight - divmore_height));
$(window).scrollTop($(window).scrollTop() - ($(element).outerHeight(true) - divmore_height));
}
}
}

216
view/js/mod_cloud.js Normal file
View file

@ -0,0 +1,216 @@
/**
* JavaScript for mod/cloud
*/
$(document).ready(function () {
// call initialization file
if (window.File && window.FileList && window.FileReader) {
UploadInit();
}
});
//
// initialize
function UploadInit() {
var fileselect = $("#files-upload");
var filedrag = $("#cloud-drag-area");
var submit = $("#upload-submit");
// is XHR2 available?
var xhr = new XMLHttpRequest();
if (xhr.upload) {
// file select
fileselect.attr("multiple", 'multiple');
fileselect.on("change", UploadFileSelectHandler);
// file submit
submit.on("click", fileselect, UploadFileSelectHandler);
// file drop
filedrag.on("dragover", DragDropUploadFileHover);
filedrag.on("dragleave", DragDropUploadFileHover);
filedrag.on("drop", DragDropUploadFileSelectHandler);
}
window.filesToUpload = 0;
window.fileUploadsCompleted = 0;
}
// file drag hover
function DragDropUploadFileHover(e) {
e.stopPropagation();
e.preventDefault();
e.currentTarget.className = (e.type == "dragover" ? "hover" : "");
}
// file selection via drag/drop
function DragDropUploadFileSelectHandler(e) {
// cancel event and hover styling
DragDropUploadFileHover(e);
// fetch FileList object
var files = e.target.files || e.originalEvent.dataTransfer.files;
$('.new-upload').remove();
// process all File objects
for (var i = 0, f; f = files[i]; i++) {
prepareHtml(f, i);
UploadFile(f, i);
}
}
// file selection via input
function UploadFileSelectHandler(e) {
// fetch FileList object
if(e.target.id === 'upload-submit') {
e.preventDefault();
var files = e.data[0].files;
}
if(e.target.id === 'files-upload') {
$('.new-upload').remove();
var files = e.target.files;
}
// process all File objects
for (var i = 0, f; f = files[i]; i++) {
if(e.target.id === 'files-upload')
prepareHtml(f, i);
if(e.target.id === 'upload-submit') {
UploadFile(f, i);
}
}
}
function prepareHtml(f, i) {
var num = i - 1;
$('#cloud-index #new-upload-progress-bar-' + num.toString()).after(
'<tr id="new-upload-' + i + '" class="new-upload">' +
'<td><i class="fa ' + getIconFromType(f.type) + '" title="' + f.type + '"></i></td>' +
'<td>' + f.name + '</td>' +
'<td id="upload-progress-' + i + '"></td><td></td><td></td><td></td><td></td>' +
'<td class="hidden-xs">' + formatSizeUnits(f.size) + '</td><td class="hidden-xs"></td>' +
'</tr>' +
'<tr id="new-upload-progress-bar-' + i + '" class="new-upload">' +
'<td id="upload-progress-bar-' + i + '" colspan="9" class="upload-progress-bar"></td>' +
'</tr>'
);
}
function formatSizeUnits(bytes){
if (bytes>=1000000000) {bytes=(bytes/1000000000).toFixed(2)+' GB';}
else if (bytes>=1000000) {bytes=(bytes/1000000).toFixed(2)+' MB';}
else if (bytes>=1000) {bytes=(bytes/1000).toFixed(2)+' KB';}
else if (bytes>1) {bytes=bytes+' bytes';}
else if (bytes==1) {bytes=bytes+' byte';}
else {bytes='0 byte';}
return bytes;
}
// this is basically a js port of include/text.php getIconFromType() function
function getIconFromType(type) {
var map = {
//Common file
'application/octet-stream': 'fa-file-o',
//Text
'text/plain': 'fa-file-text-o',
'application/msword': 'fa-file-word-o',
'application/pdf': 'fa-file-pdf-o',
'application/vnd.oasis.opendocument.text': 'fa-file-word-o',
'application/epub+zip': 'fa-book',
//Spreadsheet
'application/vnd.oasis.opendocument.spreadsheet': 'fa-file-excel-o',
'application/vnd.ms-excel': 'fa-file-excel-o',
//Image
'image/jpeg': 'fa-picture-o',
'image/png': 'fa-picture-o',
'image/gif': 'fa-picture-o',
'image/svg+xml': 'fa-picture-o',
//Archive
'application/zip': 'fa-file-archive-o',
'application/x-rar-compressed': 'fa-file-archive-o',
//Audio
'audio/mpeg': 'fa-file-audio-o',
'audio/mp3': 'fa-file-audio-o', //webkit browsers need that
'audio/wav': 'fa-file-audio-o',
'application/ogg': 'fa-file-audio-o',
'audio/ogg': 'fa-file-audio-o',
'audio/webm': 'fa-file-audio-o',
'audio/mp4': 'fa-file-audio-o',
//Video
'video/quicktime': 'fa-file-video-o',
'video/webm': 'fa-file-video-o',
'video/mp4': 'fa-file-video-o',
'video/x-matroska': 'fa-file-video-o'
};
var iconFromType = 'fa-file-o';
if (type in map) {
iconFromType = map[type];
}
return iconFromType;
}
// upload files
function UploadFile(file, idx) {
window.filesToUpload = window.filesToUpload + 1;
var xhr = new XMLHttpRequest();
xhr.withCredentials = true; // Include the SESSION cookie info for authentication
(xhr.upload || xhr).addEventListener('progress', function (e) {
var done = e.position || e.loaded;
var total = e.totalSize || e.total;
// Dynamically update the percentage complete displayed in the file upload list
$('#upload-progress-' + idx).html(Math.round(done / total * 100) + '%');
$('#upload-progress-bar-' + idx).css('background-size', Math.round(done / total * 100) + '%');
if(done == total) {
$('#upload-progress-' + idx).html('Processing...');
}
});
xhr.addEventListener('load', function (e) {
//we could possibly turn the filenames to real links here and add the delete and edit buttons to avoid page reload...
$('#upload-progress-' + idx).html('Ready!');
//console.log('xhr upload complete', e);
window.fileUploadsCompleted = window.fileUploadsCompleted + 1;
// When all the uploads have completed, refresh the page
if (window.filesToUpload > 0 && window.fileUploadsCompleted === window.filesToUpload) {
window.fileUploadsCompleted = window.filesToUpload = 0;
// After uploads complete, refresh browser window to display new files
window.location.href = window.location.href;
}
});
xhr.addEventListener('error', function (e) {
$('#upload-progress-' + idx).html('<span style="color: red;">ERROR</span>');
});
// POST to the entire cloud path
xhr.open('post', window.location.pathname, true);
var formfields = $("#ajax-upload-files").serializeArray();
var data = new FormData();
$.each(formfields, function(i, field) {
data.append(field.name, field.value);
});
data.append('file', file);
xhr.send(data);
}

View file

@ -106,6 +106,7 @@ input[type="submit"] {
input, optgroup, select, textarea {
color: #333;
resize: vertical;
}
pre code {
@ -2040,19 +2041,4 @@ dl.bb-dl > dd > li {
#wiki-preview img {
max-width: 100%;
.atoken-list {
margin-right: 5px;
list-style-type: none;
}
.atoken-list li {
margin-bottom: 10px;
}
.atoken-text {
margin: 5px 10px 5px 10px;
}
.atoken-example {
margin-left: 20px;
}
.zat-example {
color: red;
}

View file

@ -1,20 +1,20 @@
<div id="files-mkdir-tools" class="section-content-tools-wrapper">
<label for="files-mkdir">{{$folder_header}}</label>
<form method="post" action="">
<input type="hidden" name="sabreAction" value="mkcol">
<input id="files-mkdir" type="text" name="name" class="form-control form-group">
<button class="btn btn-primary btn-sm pull-right" type="submit" value="{{$folder_submit}}">{{$folder_submit}}</button>
</form>
<div class="clear"></div>
<label for="files-mkdir">{{$folder_header}}</label>
<form method="post" action="">
<input type="hidden" name="sabreAction" value="mkcol">
<input id="files-mkdir" type="text" name="name" class="form-control form-group">
<button class="btn btn-primary btn-sm pull-right" type="submit" value="{{$folder_submit}}">{{$folder_submit}}</button>
</form>
<div class="clear"></div>
</div>
<div id="files-upload-tools" class="section-content-tools-wrapper">
{{if $quota.limit || $quota.used}}<div class="{{if $quota.warning}}section-content-danger-wrapper{{else}}section-content-info-wrapper{{/if}}">{{if $quota.warning}}<strong>{{$quota.warning}} </strong>{{/if}}{{$quota.desc}}</div>{{/if}}
<label for="files-upload">{{$upload_header}}</label>
<form method="post" action="" enctype="multipart/form-data">
<input type="hidden" name="sabreAction" value="put">
<input class="form-group" id="files-upload" type="file" name="file">
<button class="btn btn-primary btn-sm pull-right" type="submit" value="{{$upload_submit}}">{{$upload_submit}}</button>
<!-- Name (optional): <input type="text" name="name"> we should rather provide a rename action in edit form-->
</form>
<div class="clear"></div>
{{if $quota.limit || $quota.used}}<div class="{{if $quota.warning}}section-content-danger-wrapper{{else}}section-content-info-wrapper{{/if}}">{{if $quota.warning}}<strong>{{$quota.warning}} </strong>{{/if}}{{$quota.desc}}</div>{{/if}}
<form id="ajax-upload-files" method="post" action="" enctype="multipart/form-data">
<input type="hidden" name="sabreAction" value="put">
<label for="files-upload">{{$upload_header}}</label>
<div class="clear"></div>
<input class="form-group pull-left" id="files-upload" type="file" name="file">
<button id="upload-submit" class="btn btn-primary btn-sm pull-right" type="submit" value="{{$upload_submit}}">{{$upload_submit}}</button>
</form>
<div class="clear"></div>
</div>

View file

@ -1,4 +1,4 @@
<div class="section-content-wrapper-np">
<div id="cloud-drag-area" class="section-content-wrapper-np">
<table id="cloud-index">
<tr>
<th width="1%"></th>
@ -18,6 +18,7 @@
<td class="hidden-xs"></td>
</tr>
{{/if}}
<tr id="new-upload-progress-bar--1"></tr> {{* this is needed to append the upload files in the right order *}}
{{foreach $entries as $item}}
<tr id="cloud-index-{{$item.attachId}}">
<td><i class="fa {{$item.iconFromType}}" title="{{$item.type}}"></i></td>
@ -38,6 +39,7 @@
<tr id="cloud-tools-{{$item.attachId}}">
<td id="perms-panel-{{$item.attachId}}" colspan="9"></td>
</tr>
{{/foreach}}
</table>
</div>

View file

@ -164,6 +164,12 @@ function enableOnUser(){
});
} catch(e) {
}
// call initialization file
if (window.File && window.FileList && window.FileReader) {
DragDropUploadInit();
}
});
function deleteCheckedItems() {
@ -446,7 +452,81 @@ function enableOnUser(){
},
'json');
};
//
// initialize
function DragDropUploadInit() {
var filedrag = $("#profile-jot-text");
// is XHR2 available?
var xhr = new XMLHttpRequest();
if (xhr.upload) {
// file drop
filedrag.on("dragover", DragDropUploadFileHover);
filedrag.on("dragleave", DragDropUploadFileHover);
filedrag.on("drop", DragDropUploadFileSelectHandler);
}
window.filesToUpload = 0;
window.fileUploadsCompleted = 0;
}
// file drag hover
function DragDropUploadFileHover(e) {
e.stopPropagation();
e.preventDefault();
e.target.className = (e.type == "dragover" ? "hover" : "");
}
// file selection
function DragDropUploadFileSelectHandler(e) {
// cancel event and hover styling
DragDropUploadFileHover(e);
// fetch FileList object
var files = e.target.files || e.originalEvent.dataTransfer.files;
// process all File objects
for (var i = 0, f; f = files[i]; i++) {
DragDropUploadFile(f, i);
}
}
// upload files
function DragDropUploadFile(file, idx) {
window.filesToUpload = window.filesToUpload + 1;
var xhr = new XMLHttpRequest();
xhr.withCredentials = true; // Include the SESSION cookie info for authentication
(xhr.upload || xhr).addEventListener('progress', function (e) {
$('#profile-rotator').spin('tiny');
});
xhr.addEventListener('load', function (e) {
//console.log('xhr upload complete', e);
window.fileUploadsCompleted = window.fileUploadsCompleted + 1;
addeditortext(xhr.responseText);
$('#jot-media').val($('#jot-media').val() + xhr.responseText);
// When all the uploads have completed, refresh the page
if (window.filesToUpload > 0 && window.fileUploadsCompleted === window.filesToUpload) {
$('#profile-rotator').spin(false);
window.fileUploadsCompleted = window.filesToUpload = 0;
}
});
// POST to the wall_upload endpoint
xhr.open('post', '{{$baseurl}}/wall_attach/{{$nickname}}', true);
var data = new FormData();
data.append('userfile', file);
xhr.send(data);
}
</script>
<script>