Merge branch 'dev' into perms

This commit is contained in:
redmatrix 2016-07-17 21:40:04 -07:00
commit de4f9d68bd
43 changed files with 2703 additions and 2707 deletions

View File

@ -1,3 +1,44 @@
Hubzilla 1.10
Wiki:
Lots of enhanced functionality, usability improvements, and bugfixes from v1.8
Turned into an optional feature (default on) but disabled in UNO
Sync:
Items are now relocated (links patched) when syncing to clones
Access Tokens:
New feature - allows members to create access controlled guest logins and create/share 'dropbox' style links to protected resources.
UI:
Use icons instead of iconic text constructs
Only request geolocation permission when creating a post, not on page load
provide 'redeliver' option on Delivery Report page for when things really stuff up
CalDAV/CardDAV management pages with heaps of functionality
Lib:
z_fetch_url() updated to accept different request methods and request bodies
item_store(), item_store_update() now return the stored items
vcard microformat changes to remain spec compliant
microformat meta tags added to post/comments
AbConfig API changed to use channel_id rather than channel_hash, which was overly complicated to use
SuperCurl class added to provide a framework for re-use of obscure CURL options
Allow absolute links to CSS/JS files on CDN
Add Let'sEncrypt intermediate cert to lib in case you forget to install it on the server
Update fullcalendar and jquery (3.1) libs
Update sabre/dav to 3.2.0
Change content export from a month/year system to begin/end
Use streaming I/O for delivering large photos
Allow multiple App description files in a single plugin directory
optimise a couple of troublesome/inefficient SQL queries
avoid sending clone sync packets to dead sites
Resolved Issues:
channel home page not providing content to clients with javascript disabled
Replace '@' obfuscation with html entity rather than the unicode look-alike
xchan_query() failing to detect duplicates, resulting in inefficient queries
issues with 'use existing photo' for profile photo
layout editor "list all layouts" returned empty
oembed - better detect video file URLs so they aren't loaded into memory.
handcrafted bbcode tables could end up with way too much whitespace due to CRLF translation
refresh permissions whitescreen in 1.8
force immediate profile photo update on local site
regression: 'save bookmarks' post action missing
Hubzilla 1.8
Administration:
Cleanup and resolve some edge cases with addon repository manager

View File

@ -47,4 +47,16 @@ Possible website applications include
<em><a href="https://github.com/redmatrix/hubzilla/blob/master/install/INSTALL.txt">Installing Hubzilla</a></em>
</p>
**Who Are We and What Are Our Principles?**
The Hubzilla community is powered by passionate volunteers creating an open source **commons** of decentralised services which are highly integrated and can rival the feature set of centralised providers. We are open to sponsorship and donations to cover expenses and compensate for our time and energy, however the project core is basically non-profit and is not designed for the purpose of commercial gain or exploitation.
Some sites may include monetisation strategies such as subscriptions and *freemium* models where members pay for resources they consume beyond a basic level. The project community supports such monetisation initiatives (nobody should be forced to pay "out of pocket" to provide a service to others), but we maintain the **commons** to provide open and free access of the software to all.
The software is not designed for data collection of its members or providing advertising. We don't have a need or desire for these things and feel that software built around these goals is poorly designed and represents compromised principles and ethics.
As a project, we are inclusive of all beliefs and cultures and do what we are able to provide an environment that is free from hostility and harrassment. Whether or not we succeed in this endaevour requires constant vigilance and help from all members of the community, working together to build an inter-networking tool with amazing potential.
[![Build Status](https://travis-ci.org/redmatrix/hubzilla.svg)](https://travis-ci.org/redmatrix/hubzilla)

View File

@ -62,6 +62,15 @@ class Cron {
}
// delete expired access tokens
q("delete from atoken where atoken_expires != '%s' && atoken_expires < %s",
dbesc(NULL_DATE),
dbutcnow()
);
// Ensure that every channel pings a directory server once a month. This way we can discover
// channels and sites that quietly vanished and prevent the directory from accumulating stale
// or dead entries.

View File

@ -146,13 +146,40 @@ class Acl extends \Zotlabs\Web\Controller {
if(local_channel()) {
if($extra_channels_sql != '')
$extra_channels_sql = " OR (abook_channel IN ($extra_channels_sql)) and abook_hidden = 0 ";
$r2 = null;
$r1 = q("select * from atoken where atoken_uid = %d",
intval(local_channel())
);
if($r1) {
require_once('include/security.php');
$r2 = array();
foreach($r1 as $rr) {
$x = atoken_xchan($rr);
$r2[] = [
'id' => 'a' . $rr['atoken_id'] ,
'hash' => $x['xchan_hash'],
'name' => $x['xchan_name'],
'micro' => $x['xchan_photo_m'],
'url' => z_root(),
'nick' => $x['xchan_addr'],
'abook_their_perms' => 0,
'abook_flags' => 0,
'abook_self' => 0
];
}
}
$r = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, xchan_pubforum, abook_flags, abook_self
FROM abook left join xchan on abook_xchan = xchan_hash
WHERE (abook_channel = %d $extra_channels_sql) AND abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc" ,
intval(local_channel())
);
if($r2)
$r = array_merge($r2,$r);
}
else { // Visitors
$r = q("SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self

View File

@ -13,6 +13,9 @@ use \Zotlabs\Storage;
// composer autoloader for SabreDAV
require_once('vendor/autoload.php');
require_once('include/attach.php');
/**
* @brief Fires up the SabreDAV server.
*

View File

@ -14,6 +14,7 @@ use \Zotlabs\Storage;
// composer autoloader for SabreDAV
require_once('vendor/autoload.php');
require_once('include/attach.php');
/**
* @brief Fires up the SabreDAV server.

View File

@ -159,7 +159,7 @@ function embedphotos_widget_album($args) {
'$upload' => array(t('Upload'), z_root() . '/photos/' . \App::$profile['channel_address'] . '/upload/' . bin2hex($album)),
'$order' => false,
'$upload_form' => $upload_form,
'$usage' => $usage_message
'$no_fullscreen_btn' => true
));
return $o;

View File

@ -28,6 +28,19 @@ class Home extends \Zotlabs\Web\Controller {
goaway($dest);
}
if(remote_channel() && (! $splash) && $_SESSION['atoken']) {
$r = q("select * from atoken where atoken_id = %d",
intval($_SESSION['atoken'])
);
if($r) {
$x = channelx_by_n($r[0]['atoken_uid']);
if($x) {
goaway(z_root() . '/channel/' . $x['channel_address']);
}
}
}
if(get_account_id() && ! $splash) {
goaway(z_root() . '/new_channel');

View File

@ -1,17 +1,31 @@
<?php
namespace Zotlabs\Module;
require_once('include/security.php');
class Lockview extends \Zotlabs\Web\Controller {
function get() {
$atokens = array();
if(local_channel()) {
$at = q("select * from atoken where atoken_uid = %d",
intval(local_channel())
);
if($at) {
foreach($at as $t) {
$atokens[] = atoken_xchan($t);
}
}
}
$type = ((argc() > 1) ? argv(1) : 0);
if (is_numeric($type)) {
$item_id = intval($type);
$type='item';
} else {
}
else {
$item_id = ((argc() > 2) ? intval(argv(2)) : 0);
}
@ -98,6 +112,13 @@ class Lockview extends \Zotlabs\Web\Controller {
if($r)
foreach($r as $rr)
$l[] = '<li>' . $rr['xchan_name'] . '</li>';
if($atokens) {
foreach($atokens as $at) {
if(in_array("'" . $at['xchan_hash'] . "'",$allowed_users)) {
$l[] = '<li>' . $at['xchan_name'] . '</li>';
}
}
}
}
if(count($deny_groups)) {
$r = q("SELECT gname FROM `groups` WHERE hash IN ( " . implode(', ', $deny_groups) . " )");
@ -110,6 +131,16 @@ class Lockview extends \Zotlabs\Web\Controller {
if($r)
foreach($r as $rr)
$l[] = '<li><strike>' . $rr['xchan_name'] . '</strike></li>';
if($atokens) {
foreach($atokens as $at) {
if(in_array("'" . $at['xchan_hash'] . "'",$deny_users)) {
$l[] = '<li><strike>' . $at['xchan_name'] . '</strike></li>';
}
}
}
}
echo $o . implode($l);

View File

@ -117,6 +117,60 @@ class Settings extends \Zotlabs\Web\Controller {
build_sync_packet();
return;
}
if((argc() > 1) && (argv(1) == 'tokens')) {
check_form_security_token_redirectOnErr('/settings/tokens', 'settings_tokens');
$token_errs = 0;
if(array_key_exists('token',$_POST)) {
$atoken_id = (($_POST['atoken_id']) ? intval($_POST['atoken_id']) : 0);
$name = trim(escape_tags($_POST['name']));
$token = trim($_POST['token']);
if((! $name) || (! $token))
$token_errs ++;
if(trim($_POST['expires']))
$expires = datetime_convert(date_default_timezone_get(),'UTC',$_POST['expires']);
else
$expires = NULL_DATE;
$max_atokens = service_class_fetch(local_channel(),'access_tokens');
if($max_atokens) {
$r = q("select count(atoken_id) as total where atoken_uid = %d",
intval(local_channel())
);
if($r && intval($r[0]['total']) >= $max_tokens) {
notice( sprintf( t('This channel is limited to %d tokens'), $max_tokens) . EOL);
return;
}
}
}
if($token_errs) {
notice( t('Name and Password are required.') . EOL);
return;
}
if($atoken_id) {
$r = q("update atoken set atoken_name = '%s', atoken_token = '%s' atoken_expires = '%s'
where atoken_id = %d and atoken_uid = %d",
dbesc($name),
dbesc($token),
dbesc($expires),
intval($atoken_id),
intval($channel['channel_id'])
);
}
else {
$r = q("insert into atoken ( atoken_aid, atoken_uid, atoken_name, atoken_token, atoken_expires )
values ( %d, %d, '%s', '%s', '%s' ) ",
intval($channel['channel_account_id']),
intval($channel['channel_id']),
dbesc($name),
dbesc($token),
dbesc($expires)
);
}
info( t('Token saved.') . EOL);
return;
}
@ -706,6 +760,53 @@ class Settings extends \Zotlabs\Web\Controller {
));
return $o;
}
if((argc() > 1) && (argv(1) === 'tokens')) {
$atoken = null;
if(argc() > 2) {
$id = argv(2);
$atoken = q("select * from atoken where atoken_id = %d and atoken_uid = %d",
intval($id),
intval(local_channel())
);
if($atoken)
$atoken = $atoken[0];
if($atoken && argc() > 3 && argv(3) === 'drop') {
$r = q("delete from atoken where atoken_id = %d",
intval($id)
);
}
}
$t = q("select * from atoken where atoken_uid = %d",
intval(local_channel())
);
$desc = t('Use this form to create temporary access identifiers to share things with non-members. These identities may be used in Access Control Lists and visitors may login using these credentials to access the private content.');
$desc2 = t('You may also provide <em>dropbox</em> style access links to friends and associates by adding the Login Password to any specific site URL as shown. Examples:');
$tpl = get_markup_template("settings_tokens.tpl");
$o .= replace_macros($tpl, array(
'$form_security_token' => get_form_security_token("settings_tokens"),
'$title' => t('Guest Access Tokens'),
'$desc' => $desc,
'$desc2' => $desc2,
'$tokens' => $t,
'$atoken' => $atoken,
'$url1' => z_root() . '/channel/' . $channel['channel_address'],
'$url2' => z_root() . '/photos/' . $channel['channel_address'],
'$name' => array('name', t('Login Name') . ' <span class="required">*</span>', (($atoken) ? $atoken['atoken_name'] : ''),''),
'$token'=> array('token', t('Login Password') . ' <span class="required">*</span>',(($atoken) ? $atoken['atoken_token'] : autoname(8)), ''),
'$expires'=> array('expires', t('Expires (yyyy-mm-dd)'), (($atoken['atoken_expires'] && $atoken['atoken_expires'] != NULL_DATE) ? datetime_convert('UTC',date_default_timezone_get(),$atoken['atoken_expires']) : ''), ''),
'$submit' => t('Submit')
));
return $o;
}

View File

@ -206,7 +206,6 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
throw new DAV\Exception\Forbidden('Permission denied.');
}
require_once('include/attach.php');
$mimetype = z_mime_content_type($name);

View File

@ -337,6 +337,10 @@ class File extends DAV\Node implements DAV\IFile {
}
}
if(get_pconfig($this->auth->owner_id,'system','os_delete_prohibit') && \App::$module == 'dav') {
throw new DAV\Exception\Forbidden('Permission denied.');
}
attach_delete($this->auth->owner_id, $this->data['hash']);
$ch = channelx_by_n($this->auth->owner_id);

View File

@ -59,7 +59,14 @@ class WebServer {
\App::$query_string = strip_zids(\App::$query_string);
if(! local_channel()) {
$_SESSION['my_address'] = $_GET['zid'];
zid_init($a);
zid_init();
}
}
if((x($_GET,'zat')) && (! \App::$install)) {
\App::$query_string = strip_zats(\App::$query_string);
if(! local_channel()) {
zat_init();
}
}

View File

@ -44,10 +44,10 @@ require_once('include/account.php');
define ( 'PLATFORM_NAME', 'hubzilla' );
define ( 'STD_VERSION', '1.9' );
define ( 'STD_VERSION', '1.11' );
define ( 'ZOT_REVISION', '1.1' );
define ( 'DB_UPDATE_VERSION', 1180 );
define ( 'DB_UPDATE_VERSION', 1181 );
/**
@ -1703,7 +1703,7 @@ function login($register = false, $form_id = 'main-login', $hiddens=false) {
'$logout' => t('Logout'),
'$login' => t('Login'),
'$form_id' => $form_id,
'$lname' => array('username', t('Email') , '', ''),
'$lname' => array('username', t('Login/Email') , '', ''),
'$lpassword' => array('password', t('Password'), '', ''),
'$remember_me' => array('remember_me', t('Remember me'), '', '',array(t('No'),t('Yes'))),
'$hiddens' => $hiddens,

View File

@ -35,3 +35,4 @@ attach_upload_limit - maximum file upload storage (bytes)
minimum_feedcheck_minutes - lowest setting allowed for polling rss feeds
chatrooms - maximum chatrooms
chatters_inroom - maximum chatters per room
access_tokens - maximum number of Guest Access Tokens per channel

View File

@ -36,22 +36,33 @@ function account_verify_password($email, $pass) {
// you have to verify the email and then go through the account approval workflow before
// letting them login.
if(($email_verify) && ($register_policy == REGISTER_OPEN) && ($record['account_flags'] & ACCOUNT_UNVERIFIED))
return null;
// @bug there is no record here
//if(($email_verify) && ($register_policy == REGISTER_OPEN) && ($record['account_flags'] & ACCOUNT_UNVERIFIED))
// return null;
$r = q("select * from account where account_email = '%s'",
dbesc($email)
);
if(! ($r && count($r)))
return null;
if($r) {
foreach($r as $record) {
if(($record['account_flags'] == ACCOUNT_OK)
&& (hash('whirlpool', $record['account_salt'] . $pass) === $record['account_password'])) {
logger('password verified for ' . $email);
return $record;
foreach($r as $record) {
if(($record['account_flags'] == ACCOUNT_OK)
&& (hash('whirlpool', $record['account_salt'] . $pass) === $record['account_password'])) {
logger('password verified for ' . $email);
return $record;
}
}
}
$x = q("select * from atoken where atoken_name = '%s' and atoken_token = '%s' limit 1",
dbesc($email),
dbesc($pass)
);
if($x) {
atoken_login($x[0]);
return $x[0];
}
$error = 'password failed for ' . $email;
logger($error);
@ -123,10 +134,18 @@ if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) &&
authenticate_success($x[0], true, true);
}
}
$r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s' limit 1",
dbesc($_SESSION['visitor_id'])
);
if(array_key_exists('atoken',$_SESSION)) {
$y = q("select * from atoken where atoken_id = %d limit 1",
intval($_SESSION['atoken'])
);
if($y)
$r = array(atoken_xchan($y[0]));
}
else {
$r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s' limit 1",
dbesc($_SESSION['visitor_id'])
);
}
if($r) {
App::set_observer($r[0]);
}
@ -199,20 +218,27 @@ else {
call_hooks('authenticate', $addon_auth);
$atoken = false;
if(($addon_auth['authenticated']) && (count($addon_auth['user_record']))) {
$record = $addon_auth['user_record'];
}
else {
$record = App::$account = account_verify_password($_POST['username'], $_POST['password']);
$x = account_verify_password($_POST['username'], $_POST['password']);
if(array_key_exists('atoken',$x))
$atoken = true;
if(! $atoken) {
$record = App::$account = $x;
if(App::$account) {
$_SESSION['account_id'] = App::$account['account_id'];
}
else {
notice( t('Failed authentication') . EOL);
}
if(App::$account) {
$_SESSION['account_id'] = App::$account['account_id'];
}
else {
notice( t('Failed authentication') . EOL);
}
logger('authenticate: ' . print_r(App::$account, true), LOGGER_ALL);
logger('authenticate: ' . print_r(App::$account, true), LOGGER_ALL);
}
}
if((! $record) || (! count($record))) {
@ -252,7 +278,8 @@ else {
// if we haven't failed up this point, log them in.
$_SESSION['last_login_date'] = datetime_convert();
authenticate_success($record, true, true);
if(! $atoken)
authenticate_success($record, true, true);
}
}
@ -270,6 +297,7 @@ else {
* @return int|bool
* Return channel_id from pconfig or false.
*/
function match_openid($authid) {
// Query the uid/channel_id from pconfig for a given value.
$r = q("SELECT uid FROM pconfig WHERE cat = 'system' AND k = 'openid' AND v = '%s' LIMIT 1",

View File

@ -1310,13 +1310,12 @@ function get_my_address() {
* If somebody arrives at our site using a zid, add their xchan to our DB if we don't have it already.
* And if they aren't already authenticated here, attempt reverse magic auth.
*
* @param App &$a
*
* @hooks 'zid_init'
* string 'zid' - their zid
* string 'url' - the destination url
*/
function zid_init(&$a) {
function zid_init() {
$tmp_str = get_my_address();
if(validate_email($tmp_str)) {
Zotlabs\Daemon\Master::Summon(array('Gprobe',bin2hex($tmp_str)));
@ -1342,6 +1341,28 @@ function zid_init(&$a) {
}
}
/**
* @brief
*
* If somebody arrives at our site using a zat, authenticate them
*
*/
function zat_init() {
if(local_channel() || remote_channel())
return;
$r = q("select * from atoken where atoken_token = '%s' limit 1",
dbesc($_REQUEST['zat'])
);
if($r) {
atoken_login($r[0]);
}
}
/**
* @brief Adds a zid parameter to a url.
*

View File

@ -20,8 +20,6 @@ function perm_limits_upgrade($channel) {
}
function perm_abook_upgrade($abook) {
set_abconfig($abook['abook_channel'],$abook['abook_xchan'],'their_perms','view_stream',intval(($abook['abook_their_perms'] & PERMS_R_STREAM)? 1 : 0));
set_abconfig($abook['abook_channel'],$abook['abook_xchan'],'their_perms','view_profile',intval(($abook['abook_their_perms'] & PERMS_R_PROFILE)? 1 : 0));
@ -60,4 +58,7 @@ function perm_abook_upgrade($abook) {
set_abconfig($abook['abook_channel'],$abook['abook_xchan'],'my_perms','delegate',intval(($abook['abook_my_perms'] & PERMS_A_DELEGATE)? 1 : 0));
}
}

View File

@ -82,6 +82,44 @@ function authenticate_success($user_record, $login_initial = false, $interactive
/* else just return */
}
function atoken_login($atoken) {
if(! $atoken)
return false;
$xchan = atoken_xchan($atoken);
$_SESSION['authenticated'] = 1;
$_SESSION['visitor_id'] = $xchan['xchan_hash'];
$_SESSION['atoken'] = $atoken['atoken_id'];
\App::set_observer($xchan);
return [ 'atoken' => true ];
}
function atoken_xchan($atoken) {
$c = channelx_by_n($atoken['atoken_uid']);
if($c) {
return [
'xchan_hash' => substr($c['channel_hash'],0,16) . '.' . $atoken['atoken_name'],
'xchan_name' => $atoken['atoken_name'],
'xchan_addr' => t('guest:') . $atoken['atoken_name'] . '@' . \App::get_hostname(),
'xchan_network' => 'unknown',
'xchan_hidden' => 1,
'xchan_photo_mimetype' => 'image/jpeg',
'xchan_photo_l' => get_default_profile_photo(300),
'xchan_photo_m' => get_default_profile_photo(80),
'xchan_photo_s' => get_default_profile_photo(48)
];
}
}
/**
* @brief Change to another channel with current logged-in account.
*

View File

@ -774,6 +774,10 @@ function strip_zids($s) {
return preg_replace('/[\?&]zid=(.*?)(&|$)/ism','$2',$s);
}
function strip_zats($s) {
return preg_replace('/[\?&]zat=(.*?)(&|$)/ism','$2',$s);
}
// quick and dirty quoted_printable encoding

View File

@ -609,6 +609,15 @@ function widget_settings_menu($arr) {
'selected' => ((argv(1) === 'oauth') ? 'active' : ''),
);
if(! UNO) {
$tabs[] = array(
'label' => t('Guest Access Tokens'),
'url' => z_root() . '/settings/tokens',
'selected' => ((argv(1) === 'tokens') ? 'active' : ''),
);
}
if($role === false || $role === 'custom') {
$tabs[] = array(
'label' => t('Connection Default Permissions'),

View File

@ -141,6 +141,23 @@ CREATE TABLE IF NOT EXISTS `app` (
KEY `app_edited` (`app_edited`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `atoken` (
`atoken_id` int(11) NOT NULL AUTO_INCREMENT,
`atoken_aid` int(11) NOT NULL DEFAULT 0,
`atoken_uid` int(11) NOT NULL DEFAULT 0,
`atoken_name` char(255) NOT NULL DEFAULT '',
`atoken_token` char(255) NOT NULL DEFAULT '',
`atoken_expires` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`atoken_id`),
KEY `atoken_aid` (`atoken_aid`),
KEY `atoken_uid` (`atoken_uid`),
KEY `atoken_uid_2` (`atoken_uid`),
KEY `atoken_name` (`atoken_name`),
KEY `atoken_token` (`atoken_token`),
KEY `atoken_expires` (`atoken_expires`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `attach` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`aid` int(10) unsigned NOT NULL DEFAULT '0',

View File

@ -137,6 +137,21 @@ create index "app_created" on app ("app_created");
create index "app_edited" on app ("app_edited");
create index "app_deleted" on app ("app_deleted");
create index "app_system" on app ("app_system");
CREATE TABLE "atoken" (
"atoken_id" serial NOT NULL,
"atoken_aid" bigint NOT NULL DEFAULT 0,
"atoken_uid" bigint NOT NULL DEFAULT 0,
"atoken_name" varchar(255) NOT NULL DEFAULT '',
"atoken_token" varchar(255) NOT NULL DEFAULT '',
"atoken_expires" timestamp NOT NULL DEFAULT '0001-01-01 00:00:00',
PRIMARY KEY ("atoken_id"));
create index atoken_aid on atoken (atoken_aid);
create index atoken_uid on atoken (atoken_uid);
create index atoken_name on atoken (atoken_name);
create index atoken_token on atoken (atoken_token);
create index atoken_expires on atoken (atoken_expires);
CREATE TABLE "attach" (
"id" serial NOT NULL,
"aid" bigint NOT NULL DEFAULT '0',

View File

@ -1,6 +1,6 @@
<?php
define( 'UPDATE_VERSION' , 1180 );
define( 'UPDATE_VERSION' , 1181 );
/**
*
@ -2364,7 +2364,49 @@ function update_r1178() {
function update_r1179() {
require_once('install/perm_upgrade.php');
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
$r1 = q("CREATE TABLE atoken (
atoken_id serial NOT NULL,
atoken_aid bigint NOT NULL DEFAULT 0,
atoken_uid bigint NOT NULL DEFAULT 0,
atoken_name varchar(255) NOT NULL DEFAULT '',
atoken_token varchar(255) NOT NULL DEFAULT '',
atoken_expires timestamp NOT NULL DEFAULT '0001-01-01 00:00:00',
PRIMARY KEY (atoken_id)) ");
$r2 = q("create index atoken_aid on atoken (atoken_aid)");
$r3 = q("create index atoken_uid on atoken (atoken_uid)");
$r4 = q("create index atoken_name on atoken (atoken_name)");
$r5 = q("create index atoken_token on atoken (atoken_token)");
$r6 = q("create index atoken_expires on atoken (atoken_expires)");
$r = $r1 && $r2 && $r3 && $r4 && $r5 && $r6;
}
else {
$r = q("CREATE TABLE IF NOT EXISTS `atoken` (
`atoken_id` int(11) NOT NULL AUTO_INCREMENT,
`atoken_aid` int(11) NOT NULL DEFAULT 0,
`atoken_uid` int(11) NOT NULL DEFAULT 0,
`atoken_name` char(255) NOT NULL DEFAULT '',
`atoken_token` char(255) NOT NULL DEFAULT '',
`atoken_expires` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`atoken_id`),
KEY `atoken_aid` (`atoken_aid`),
KEY `atoken_uid` (`atoken_uid`),
KEY `atoken_name` (`atoken_name`),
KEY `atoken_token` (`atoken_token`),
KEY `atoken_expires` (`atoken_expires`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ");
}
if($r)
return UPDATE_SUCCESS;
return UPDATE_FAILED;
}
function update_r1180() {
require_once('include/perm_upgrade.php');
$r1 = q("select * from channel where true");
if($r1) {
@ -2384,6 +2426,5 @@ function update_r1179() {
if($r)
return UPDATE_SUCCESS;
return UPDATE_FAILED;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -56,7 +56,7 @@ if($argc == 3) {
echo "service_class $oclass\t\t\033[1m" . $argv[2] . "\033[0m\n";
$new = get_config('service_class', $argv[2]);
foreach(array('photo_upload_limit','total_items','total_pages','total_identities','total_channels','total_feeds','attach_upload_limit','minimum_feedcheck_minutes','chatrooms','chatters_inroom') as $prop) {
foreach(array('photo_upload_limit','total_items','total_pages','total_identities','total_channels','total_feeds','attach_upload_limit','minimum_feedcheck_minutes','chatrooms','chatters_inroom','access_tokens') as $prop) {
echo $prop . str_repeat(' ',26 - strlen($prop)) . (($old && $old[$prop]) ? $old[$prop] : 'unlimited') . "\t\t\033[1m" . (($new && $new[$prop]) ? $new[$prop] : 'unlimited') . "\033[0m\n";
}
$r = '';

View File

@ -5,6 +5,7 @@
nav .badge {
position: relative;
top: -49px;
left: 2px;
float: left;
font-size: 10px;
line-height: 20px;

View File

@ -121,7 +121,7 @@ a.wall-item-name-link {
}
.wall-item-content {
overflow: auto;
overflow: hidden;
}
.wall-item-content h1,
@ -316,4 +316,4 @@ code.inline-code {
img.smiley.emoji:hover {
width: 32px;
height: 32px;
}
}

View File

@ -30,5 +30,5 @@
}
.directory-collapse {
overflow: auto;
overflow: hidden;
}

View File

@ -7,5 +7,22 @@
}
.channel-menu {
margin-top: 24px;
margin-top: 24px;
}
.zat-example {
color: red;
}
#atoken-index {
width: 100%;
}
#atoken-index td:nth-child(1){
padding: 7px 3px 7px 10px;
white-space: nowrap;
}
.atoken-index-tool {
padding: 7px 10px;
}

View File

@ -1,511 +0,0 @@
/*!
* jQuery Migrate - v1.1.1 - 2013-02-16
* https://github.com/jquery/jquery-migrate
* Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors; Licensed MIT
*/
(function( jQuery, window, undefined ) {
// See http://bugs.jquery.com/ticket/13335
// "use strict";
var warnedAbout = {};
// List of warnings already given; public read only
jQuery.migrateWarnings = [];
// Set to true to prevent console output; migrateWarnings still maintained
// jQuery.migrateMute = false;
// Show a message on the console so devs know we're active
if ( !jQuery.migrateMute && window.console && console.log ) {
console.log("JQMIGRATE: Logging is active");
}
// Set to false to disable traces that appear with warnings
if ( jQuery.migrateTrace === undefined ) {
jQuery.migrateTrace = true;
}
// Forget any warnings we've already given; public
jQuery.migrateReset = function() {
warnedAbout = {};
jQuery.migrateWarnings.length = 0;
};
function migrateWarn( msg) {
if ( !warnedAbout[ msg ] ) {
warnedAbout[ msg ] = true;
jQuery.migrateWarnings.push( msg );
if ( window.console && console.warn && !jQuery.migrateMute ) {
console.warn( "JQMIGRATE: " + msg );
if ( jQuery.migrateTrace && console.trace ) {
console.trace();
}
}
}
}
function migrateWarnProp( obj, prop, value, msg ) {
if ( Object.defineProperty ) {
// On ES5 browsers (non-oldIE), warn if the code tries to get prop;
// allow property to be overwritten in case some other plugin wants it
try {
Object.defineProperty( obj, prop, {
configurable: true,
enumerable: true,
get: function() {
migrateWarn( msg );
return value;
},
set: function( newValue ) {
migrateWarn( msg );
value = newValue;
}
});
return;
} catch( err ) {
// IE8 is a dope about Object.defineProperty, can't warn there
}
}
// Non-ES5 (or broken) browser; just set the property
jQuery._definePropertyBroken = true;
obj[ prop ] = value;
}
if ( document.compatMode === "BackCompat" ) {
// jQuery has never supported or tested Quirks Mode
migrateWarn( "jQuery is not compatible with Quirks Mode" );
}
var attrFn = jQuery( "<input/>", { size: 1 } ).attr("size") && jQuery.attrFn,
oldAttr = jQuery.attr,
valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get ||
function() { return null; },
valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set ||
function() { return undefined; },
rnoType = /^(?:input|button)$/i,
rnoAttrNodeType = /^[238]$/,
rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
ruseDefault = /^(?:checked|selected)$/i;
// jQuery.attrFn
migrateWarnProp( jQuery, "attrFn", attrFn || {}, "jQuery.attrFn is deprecated" );
jQuery.attr = function( elem, name, value, pass ) {
var lowerName = name.toLowerCase(),
nType = elem && elem.nodeType;
if ( pass ) {
// Since pass is used internally, we only warn for new jQuery
// versions where there isn't a pass arg in the formal params
if ( oldAttr.length < 4 ) {
migrateWarn("jQuery.fn.attr( props, pass ) is deprecated");
}
if ( elem && !rnoAttrNodeType.test( nType ) &&
(attrFn ? name in attrFn : jQuery.isFunction(jQuery.fn[name])) ) {
return jQuery( elem )[ name ]( value );
}
}
// Warn if user tries to set `type`, since it breaks on IE 6/7/8; by checking
// for disconnected elements we don't warn on $( "<button>", { type: "button" } ).
if ( name === "type" && value !== undefined && rnoType.test( elem.nodeName ) && elem.parentNode ) {
migrateWarn("Can't change the 'type' of an input or button in IE 6/7/8");
}
// Restore boolHook for boolean property/attribute synchronization
if ( !jQuery.attrHooks[ lowerName ] && rboolean.test( lowerName ) ) {
jQuery.attrHooks[ lowerName ] = {
get: function( elem, name ) {
// Align boolean attributes with corresponding properties
// Fall back to attribute presence where some booleans are not supported
var attrNode,
property = jQuery.prop( elem, name );
return property === true || typeof property !== "boolean" &&
( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
name.toLowerCase() :
undefined;
},
set: function( elem, value, name ) {
var propName;
if ( value === false ) {
// Remove boolean attributes when set to false
jQuery.removeAttr( elem, name );
} else {
// value is true since we know at this point it's type boolean and not false
// Set boolean attributes to the same name and set the DOM property
propName = jQuery.propFix[ name ] || name;
if ( propName in elem ) {
// Only set the IDL specifically if it already exists on the element
elem[ propName ] = true;
}
elem.setAttribute( name, name.toLowerCase() );
}
return name;
}
};
// Warn only for attributes that can remain distinct from their properties post-1.9
if ( ruseDefault.test( lowerName ) ) {
migrateWarn( "jQuery.fn.attr('" + lowerName + "') may use property instead of attribute" );
}
}
return oldAttr.call( jQuery, elem, name, value );
};
// attrHooks: value
jQuery.attrHooks.value = {
get: function( elem, name ) {
var nodeName = ( elem.nodeName || "" ).toLowerCase();
if ( nodeName === "button" ) {
return valueAttrGet.apply( this, arguments );
}
if ( nodeName !== "input" && nodeName !== "option" ) {
migrateWarn("jQuery.fn.attr('value') no longer gets properties");
}
return name in elem ?
elem.value :
null;
},
set: function( elem, value ) {
var nodeName = ( elem.nodeName || "" ).toLowerCase();
if ( nodeName === "button" ) {
return valueAttrSet.apply( this, arguments );
}
if ( nodeName !== "input" && nodeName !== "option" ) {
migrateWarn("jQuery.fn.attr('value', val) no longer sets properties");
}
// Does not return so that setAttribute is also used
elem.value = value;
}
};
var matched, browser,
oldInit = jQuery.fn.init,
oldParseJSON = jQuery.parseJSON,
// Note this does NOT include the #9521 XSS fix from 1.7!
rquickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*|#([\w\-]*))$/;
// $(html) "looks like html" rule change
jQuery.fn.init = function( selector, context, rootjQuery ) {
var match;
if ( selector && typeof selector === "string" && !jQuery.isPlainObject( context ) &&
(match = rquickExpr.exec( selector )) && match[1] ) {
// This is an HTML string according to the "old" rules; is it still?
if ( selector.charAt( 0 ) !== "<" ) {
migrateWarn("$(html) HTML strings must start with '<' character");
}
// Now process using loose rules; let pre-1.8 play too
if ( context && context.context ) {
// jQuery object as context; parseHTML expects a DOM object
context = context.context;
}
if ( jQuery.parseHTML ) {
return oldInit.call( this, jQuery.parseHTML( jQuery.trim(selector), context, true ),
context, rootjQuery );
}
}
return oldInit.apply( this, arguments );
};
jQuery.fn.init.prototype = jQuery.fn;
// Let $.parseJSON(falsy_value) return null
jQuery.parseJSON = function( json ) {
if ( !json && json !== null ) {
migrateWarn("jQuery.parseJSON requires a valid JSON string");
return null;
}
return oldParseJSON.apply( this, arguments );
};
jQuery.uaMatch = function( ua ) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
/(msie) ([\w.]+)/.exec( ua ) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
// Don't clobber any existing jQuery.browser in case it's different
if ( !jQuery.browser ) {
matched = jQuery.uaMatch( navigator.userAgent );
browser = {};
if ( matched.browser ) {
browser[ matched.browser ] = true;
browser.version = matched.version;
}
// Chrome is Webkit, but Webkit is also Safari.
if ( browser.chrome ) {
browser.webkit = true;
} else if ( browser.webkit ) {
browser.safari = true;
}
jQuery.browser = browser;
}
// Warn if the code tries to get jQuery.browser
migrateWarnProp( jQuery, "browser", jQuery.browser, "jQuery.browser is deprecated" );
jQuery.sub = function() {
function jQuerySub( selector, context ) {
return new jQuerySub.fn.init( selector, context );
}
jQuery.extend( true, jQuerySub, this );
jQuerySub.superclass = this;
jQuerySub.fn = jQuerySub.prototype = this();
jQuerySub.fn.constructor = jQuerySub;
jQuerySub.sub = this.sub;
jQuerySub.fn.init = function init( selector, context ) {
if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
context = jQuerySub( context );
}
return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
};
jQuerySub.fn.init.prototype = jQuerySub.fn;
var rootjQuerySub = jQuerySub(document);
migrateWarn( "jQuery.sub() is deprecated" );
return jQuerySub;
};
// Ensure that $.ajax gets the new parseJSON defined in core.js
jQuery.ajaxSetup({
converters: {
"text json": jQuery.parseJSON
}
});
var oldFnData = jQuery.fn.data;
jQuery.fn.data = function( name ) {
var ret, evt,
elem = this[0];
// Handles 1.7 which has this behavior and 1.8 which doesn't
if ( elem && name === "events" && arguments.length === 1 ) {
ret = jQuery.data( elem, name );
evt = jQuery._data( elem, name );
if ( ( ret === undefined || ret === evt ) && evt !== undefined ) {
migrateWarn("Use of jQuery.fn.data('events') is deprecated");
return evt;
}
}
return oldFnData.apply( this, arguments );
};
var rscriptType = /\/(java|ecma)script/i,
oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack;
jQuery.fn.andSelf = function() {
migrateWarn("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()");
return oldSelf.apply( this, arguments );
};
// Since jQuery.clean is used internally on older versions, we only shim if it's missing
if ( !jQuery.clean ) {
jQuery.clean = function( elems, context, fragment, scripts ) {
// Set context per 1.8 logic
context = context || document;
context = !context.nodeType && context[0] || context;
context = context.ownerDocument || context;
migrateWarn("jQuery.clean() is deprecated");
var i, elem, handleScript, jsTags,
ret = [];
jQuery.merge( ret, jQuery.buildFragment( elems, context ).childNodes );
// Complex logic lifted directly from jQuery 1.8
if ( fragment ) {
// Special handling of each script element
handleScript = function( elem ) {
// Check if we consider it executable
if ( !elem.type || rscriptType.test( elem.type ) ) {
// Detach the script and store it in the scripts array (if provided) or the fragment
// Return truthy to indicate that it has been handled
return scripts ?
scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
fragment.appendChild( elem );
}
};
for ( i = 0; (elem = ret[i]) != null; i++ ) {
// Check if we're done after handling an executable script
if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
// Append to fragment and handle embedded scripts
fragment.appendChild( elem );
if ( typeof elem.getElementsByTagName !== "undefined" ) {
// handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );
// Splice the scripts into ret after their former ancestor and advance our index beyond them
ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
i += jsTags.length;
}
}
}
}
return ret;
};
}
var eventAdd = jQuery.event.add,
eventRemove = jQuery.event.remove,
eventTrigger = jQuery.event.trigger,
oldToggle = jQuery.fn.toggle,
oldLive = jQuery.fn.live,
oldDie = jQuery.fn.die,
ajaxEvents = "ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",
rajaxEvent = new RegExp( "\\b(?:" + ajaxEvents + ")\\b" ),
rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
hoverHack = function( events ) {
if ( typeof( events ) !== "string" || jQuery.event.special.hover ) {
return events;
}
if ( rhoverHack.test( events ) ) {
migrateWarn("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'");
}
return events && events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
};
// Event props removed in 1.9, put them back if needed; no practical way to warn them
if ( jQuery.event.props && jQuery.event.props[ 0 ] !== "attrChange" ) {
jQuery.event.props.unshift( "attrChange", "attrName", "relatedNode", "srcElement" );
}
// Undocumented jQuery.event.handle was "deprecated" in jQuery 1.7
if ( jQuery.event.dispatch ) {
migrateWarnProp( jQuery.event, "handle", jQuery.event.dispatch, "jQuery.event.handle is undocumented and deprecated" );
}
// Support for 'hover' pseudo-event and ajax event warnings
jQuery.event.add = function( elem, types, handler, data, selector ){
if ( elem !== document && rajaxEvent.test( types ) ) {
migrateWarn( "AJAX events should be attached to document: " + types );
}
eventAdd.call( this, elem, hoverHack( types || "" ), handler, data, selector );
};
jQuery.event.remove = function( elem, types, handler, selector, mappedTypes ){
eventRemove.call( this, elem, hoverHack( types ) || "", handler, selector, mappedTypes );
};
jQuery.fn.error = function() {
var args = Array.prototype.slice.call( arguments, 0);
migrateWarn("jQuery.fn.error() is deprecated");
args.splice( 0, 0, "error" );
if ( arguments.length ) {
return this.bind.apply( this, args );
}
// error event should not bubble to window, although it does pre-1.7
this.triggerHandler.apply( this, args );
return this;
};
jQuery.fn.toggle = function( fn, fn2 ) {
// Don't mess with animation or css toggles
if ( !jQuery.isFunction( fn ) || !jQuery.isFunction( fn2 ) ) {
return oldToggle.apply( this, arguments );
}
migrateWarn("jQuery.fn.toggle(handler, handler...) is deprecated");
// Save reference to arguments for access in closure
var args = arguments,
guid = fn.guid || jQuery.guid++,
i = 0,
toggler = function( event ) {
// Figure out which function to execute
var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
// Make sure that clicks stop
event.preventDefault();
// and execute the function
return args[ lastToggle ].apply( this, arguments ) || false;
};
// link all the functions, so any of them can unbind this click handler
toggler.guid = guid;
while ( i < args.length ) {
args[ i++ ].guid = guid;
}
return this.click( toggler );
};
jQuery.fn.live = function( types, data, fn ) {
migrateWarn("jQuery.fn.live() is deprecated");
if ( oldLive ) {
return oldLive.apply( this, arguments );
}
jQuery( this.context ).on( types, this.selector, data, fn );
return this;
};
jQuery.fn.die = function( types, fn ) {
migrateWarn("jQuery.fn.die() is deprecated");
if ( oldDie ) {
return oldDie.apply( this, arguments );
}
jQuery( this.context ).off( types, this.selector || "**", fn );
return this;
};
// Turn global events into document-triggered events
jQuery.event.trigger = function( event, data, elem, onlyHandlers ){
if ( !elem && !rajaxEvent.test( event ) ) {
migrateWarn( "Global events are undocumented and deprecated" );
}
return eventTrigger.call( this, event, data, elem || document, onlyHandlers );
};
jQuery.each( ajaxEvents.split("|"),
function( _, name ) {
jQuery.event.special[ name ] = {
setup: function() {
var elem = this;
// The document needs no shimming; must be !== for oldIE
if ( elem !== document ) {
jQuery.event.add( document, name + "." + jQuery.guid, function() {
jQuery.event.trigger( name, null, elem, true );
});
jQuery._data( this, name, jQuery.guid++ );
}
return false;
},
teardown: function() {
if ( this !== document ) {
jQuery.event.remove( document, name + "." + jQuery._data( this, name ) );
}
return false;
}
};
}
);
})( jQuery, window );

8
view/js/jquery.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -283,12 +283,13 @@ $(function() {
/* Turn elements with one of our special rel tags into popup menus */
/* CHANGES: let bootstrap handle popups and only do the loading here */
$('a[rel^=#]').click(function(e){
$('a[rel^="#"]').click(function(e){
manage_popup_menu(this, e);
return;
});
$('span[rel^=#]').click(function(e){
$('span[rel^="#"]').click(function(e){
manage_popup_menu(this, e);
return;
});
@ -639,7 +640,7 @@ function updateConvItems(mode,data) {
var bimgcount = bimgs.length;
if (bimgcount) {
bimgs.load(function() {
bimgs.on('load',function() {
bimgcount--;
if (! bimgcount) {
collapseHeight();
@ -652,7 +653,7 @@ function updateConvItems(mode,data) {
}
function collapseHeight() {
var origContentHeight = parseInt($("#region_2").height());
var origContentHeight = Math.ceil($("#region_2").height());
var cDiff = 0;
var i = 0;
var position = $(window).scrollTop();
@ -662,25 +663,19 @@ function collapseHeight() {
if(orgHeight > divmore_height) {
if(! $(this).hasClass('divmore')) {
//var trigger = $(window).scrollTop() < $(this).offset().top ? true : false;
//console.log($(this).offset().top + divmore_height - $(window).scrollTop() + cDiff - ($(".divgrow-showmore").outerHeight() * i));
// check if we will collapse some content above the visible content and compensate the diff later
if($(this).offset().top + divmore_height - $(window).scrollTop() + cDiff - ($(".divgrow-showmore").outerHeight() * i) < 65) {
//$(this).css('color', 'red');
//console.log($(this).offset().top + divmore_height + ' / ' + $(window).scrollTop());
diff = orgHeight - divmore_height;
cDiff = cDiff + diff;
i++;
}
//if(trigger) {
$(this).readmore({
speed: 0,
heightMargin: 50,
collapsedHeight: divmore_height,
moreLink: '<a href="#" class="divgrow-showmore">' + aStr.divgrowmore + '</a>',
lessLink: '<a href="#" class="divgrow-showmore">' + aStr.divgrowless + '</a>',
moreLink: '<a href="#" class="divgrow-showmore fakelink">' + aStr.divgrowmore + '</a>',
lessLink: '<a href="#" class="divgrow-showmore fakelink">' + aStr.divgrowless + '</a>',
beforeToggle: function(trigger, element, expanded) {
if(expanded) {
if((($(element).offset().top + divmore_height) - $(window).scrollTop()) < 65 ) {
@ -690,12 +685,11 @@ function collapseHeight() {
}
});
$(this).addClass('divmore');
//}
}
}
});
var collapsedContentHeight = parseInt($("#region_2").height());
var collapsedContentHeight = Math.ceil($("#region_2").height());
contentHeightDiff = origContentHeight - collapsedContentHeight;
console.log('collapseHeight() - contentHeightDiff: ' + contentHeightDiff + 'px');

View File

@ -5,6 +5,9 @@
$(document).ready(function() {
$('form').areYouSure({'addRemoveFieldsMarksDirty':true, 'message': aStr['leavethispage'] }); // Warn user about unsaved settings
$('.token-mirror').html($('#id_token').val());
$('#id_token').keyup( function() { $('.token-mirror').html($('#id_token').val()); });
$("#id_permissions_role").change(function() {
var role = $("#id_permissions_role").val();
if(role == 'custom')

View File

@ -13,7 +13,7 @@ head_add_css('library/justifiedGallery/justifiedGallery.min.css');
head_add_css('library/Text_Highlighter/sample.css');
head_add_js('jquery.js');
//head_add_js('jquery-migrate-1.1.1.js');
//head_add_js('jquery.migrate-3.0.0.js');
head_add_js('library/justifiedGallery/jquery.justifiedGallery.min.js');
head_add_js('library/sprintf.js/dist/sprintf.min.js');

View File

@ -1416,9 +1416,6 @@ img.mail-conv-sender-photo {
display: block;
border-top: 1px dashed #ccc;
text-align: center;
font-size: $body_font_size;
color: $link_colour;
cursor: pointer;
}
.divgrow-showmore:hover {
@ -1658,6 +1655,7 @@ main.fullscreen .section-content-wrapper-np {
display: none;
}
.atoken-index-row:hover td,
.chatroom-index-row:hover td,
.locs-index-row:hover td,
[id^="cloud-index-"]:hover td,

View File

@ -56,16 +56,15 @@ $(document).ready(function() {
function makeFullScreen(full) {
if(typeof full=='undefined' || full == true) {
$('main').css({'transition': 'none'}).addClass('fullscreen');
$('header, nav, aside, #tabs-collapse-1').css({'visibility': 'hidden'});
$('#fullscreen-btn').hide();
$('header, nav, aside, #fullscreen-btn').hide();
$('#tabs-collapse-1').css({'visibility': 'hidden'});
$('#inline-btn').show();
}
else {
$('main').removeClass('fullscreen');
$('header, nav, aside, #tabs-collapse-1').css({'visibility': ''});
$('header, nav, aside, #fullscreen-btn').show();
$('#tabs-collapse-1').css({'visibility': ''});
$('#inline-btn').hide();
$('#fullscreen-btn').show();
$('main').css({'transition': ''});
}
}

View File

@ -153,6 +153,18 @@ if(file_exists('view/theme/redbasic/css/style.css')) {
$x = file_get_contents('view/theme/redbasic/css/style.css');
if($narrow_navbar && file_exists('view/theme/redbasic/css/narrow_navbar.css')) {
$x .= file_get_contents('view/theme/redbasic/css/narrow_navbar.css');
}
if($align_left && file_exists('view/theme/redbasic/css/align_left.css')) {
$x .= file_get_contents('view/theme/redbasic/css/align_left.css');
}
if($schemecss) {
$x .= $schemecss;
}
$aside_width = 287;
// left aside and right aside are 285px + converse width
@ -204,18 +216,6 @@ if(file_exists('view/theme/redbasic/css/style.css')) {
}
if($narrow_navbar && file_exists('view/theme/redbasic/css/narrow_navbar.css')) {
echo file_get_contents('view/theme/redbasic/css/narrow_navbar.css');
}
if($align_left && file_exists('view/theme/redbasic/css/align_left.css')) {
echo file_get_contents('view/theme/redbasic/css/align_left.css');
}
if($schemecss) {
echo $schemecss;
}
// Set the schema to the default schema in derived themes. See the documentation for creating derived themes how to override this.
if(local_channel() && App::$channel && App::$channel['channel_theme'] != 'redbasic')

View File

@ -11,4 +11,3 @@
</div>
{{/if}}
</div>
<div class="clear"></div>

View File

@ -1,8 +1,17 @@
<script>
var aside_padding_top;
var section_padding_top;
$(document).ready(function() {
aside_padding_top = parseInt($('aside').css('padding-top'));
section_padding_top = parseInt($('section').css('padding-top'));
if($('#cover-photo').length && $(window).width() > 755) {
$('.navbar-fixed-top').css('position', 'relative');
$('aside, section').css('padding-top', 0 + 'px');
$('main').css('margin-top', - $('nav').outerHeight(true) + 'px');
$('aside').css('padding-top', aside_padding_top - $('nav').outerHeight() + 'px');
$('section').css('padding-top', section_padding_top - $('nav').outerHeight() + 'px');
$('main').css('opacity', 0);
$('header').hide();
}
@ -15,9 +24,11 @@
if($('#cover-photo').length && $(window).width() > 755 && $(window).scrollTop() >= $('#cover-photo').height()) {
$('header').fadeIn();
$('main').css('opacity', 1);
$('aside, section').css('padding-top', $('nav').outerHeight(true) + 'px');
$('aside').css('padding-top', aside_padding_top + 'px');
$('section').css('padding-top', section_padding_top + 'px');
$(window).scrollTop($(window).scrollTop() - $('#cover-photo').height())
$('.navbar-fixed-top').css('position', 'fixed');
$('main').css('margin-top', '');
$('#cover-photo').remove();
}
if($('#cover-photo').length) {
@ -28,7 +39,8 @@
$(window).resize(function () {
if($('#cover-photo').length && $(window).width() < 755) {
$('main').css('opacity', 1);
$('aside, section').css('padding-top', $('nav').outerHeight(true) + 'px');
$('aside').css('padding-top', aside_padding_top + 'px');
$('section').css('padding-top', section_padding_top + 'px');
$('.navbar-fixed-top').css('position', 'fixed');
$('#cover-photo').remove();
}
@ -36,7 +48,7 @@
});
function slideUpCover() {
$('html, body').animate({scrollTop: $('#cover-photo').height() + 'px'});
$('html, body').animate({scrollTop: Math.ceil($('#cover-photo').height()) + 'px' });
}
</script>

View File

@ -12,8 +12,10 @@
<button class="btn btn-xs btn-success btn-xs" title="{{$usage}}" onclick="openClose('photo-upload-form'); closeMenu('photo-album-edit-wrapper');"><i class="fa fa-arrow-circle-o-up"></i>&nbsp;{{$upload.0}}</button>
{{/if}}
</div>
{{if !$no_fullscreen_btn}}
<button id="fullscreen-btn" type="button" class="btn btn-default btn-xs" onclick="makeFullScreen();"><i class="fa fa-expand"></i></button>
<button id="inline-btn" type="button" class="btn btn-default btn-xs" onclick="makeFullScreen(false);"><i class="fa fa-compress"></i></button>
{{/if}}
</div>
<h2>{{$album}}</h2>
<div class="clear"></div>

View File

@ -0,0 +1,40 @@
<div class="generic-content-wrapper">
<div class="section-title-wrapper">
<h2>{{$title}}</h2>
<div class="clear"></div>
</div>
<div class="section-content-tools-wrapper">
<div class="section-content-info-wrapper">
{{$desc}}
</div>
<form action="settings/tokens" id="settings-account-form" method="post" autocomplete="off" >
<input type='hidden' name='form_security_token' value='{{$form_security_token}}'>
{{if $atoken}}<input type="hidden" name="atoken_id" value="{{$atoken.atoken_id}}" />{{/if}}
{{include file="field_input.tpl" field=$name}}
{{include file="field_input.tpl" field=$token}}
{{include file="field_input.tpl" field=$expires}}
<div class="settings-submit-wrapper form-group">
<button type="submit" name="submit" class="btn btn-primary">{{$submit}}</button>
</div>
</form>
<div class="descriptive-text">{{$desc2}}</div>
<ul>
<li>{{$url1}}<span class="zat-example">?f=&zat=<span class="token-mirror"></span></span></li>
<li>{{$url2}}<span class="zat-example">?f=&zat=<span class="token-mirror"></span></span></li>
</ul>
</div>
{{if $tokens}}
<div class="section-content-wrapper-np">
<table id="atoken-index">
{{foreach $tokens as $t}}
<tr id="atoken-index-{{$t.atoken_id}}" class="atoken-index-row">
<td width="99%"><a href="settings/tokens/{{$t.atoken_id}}">{{$t.atoken_name}}</a></td>
<td width="1%" class="atoken-index-tool"><i class="fa fa-trash-o drop-icons" onClick="dropItem('/settings/tokens/{{$t.atoken_id}}/drop', '#atoken-index-{{$t.atoken_id}}')"></i></td>
</tr>
{{/foreach}}
</table>
</div>
{{/if}}
</div>