refactor site customisation

This commit is contained in:
nobody 2020-09-09 17:29:46 -07:00
parent 1c62f7f5ad
commit fa09727217
12 changed files with 1300 additions and 50 deletions

View file

@ -3286,19 +3286,15 @@ class Libzot {
}
}
}
$f = 'images/' . strtolower(System::get_platform_name()) . '.png';
if (file_exists($f)) {
$ret['site']['logo'] = z_root() . '/' . $f;
}
$ret['site']['about'] = bbcode(get_config('system','siteinfo'), [ 'export' => true ]);
$ret['site']['plugins'] = $visible_plugins;
$ret['site']['sitehash'] = get_config('system','location_hash');
$ret['site']['sitename'] = get_config('system','sitename');
$ret['site']['sellpage'] = get_config('system','sellpage');
$ret['site']['location'] = get_config('system','site_location');
$ret['site']['realm'] = get_directory_realm();
$ret['site']['sitename'] = System::get_site_name();
$ret['site']['logo'] = System::get_site_icon();
$ret['site']['project'] = System::get_platform_name();
$ret['site']['version'] = System::get_project_version();
}

View file

@ -20,11 +20,10 @@ class System {
static public function get_banner() {
if(is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('banner',App::$config['system']))
if(is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('banner',App::$config['system']) && App::$config['system']['banner']) {
return App::$config['system']['banner'];
return EMPTY_STR;
}
return self::get_site_name();
}
static public function get_project_icon() {
@ -32,7 +31,6 @@ class System {
return App::$config['system']['icon'];
}
return z_root() . '/images/' . PLATFORM_NAME . '-64.png';
}
@ -55,13 +53,13 @@ class System {
static public function get_notify_icon() {
if(is_array(App::$config) && is_array(App::$config['system']) && App::$config['system']['email_notify_icon_url'])
return App::$config['system']['email_notify_icon_url'];
return z_root() . DEFAULT_NOTIFY_ICON;
return self::get_project_icon();
}
static public function get_site_icon() {
if(is_array(App::$config) && is_array(App::$config['system']) && App::$config['system']['site_icon_url'])
return App::$config['system']['site_icon_url'];
return z_root() . DEFAULT_PLATFORM_ICON ;
return self::get_project_icon();
}

View file

@ -0,0 +1,499 @@
<?php
namespace Zotlabs\Module\Admin;
use App;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\Libprofile;
use Zotlabs\Access\AccessControl;
use Zotlabs\Web\Controller;
/*
@file cover_photo.php
@brief Module-file with functions for handling of cover-photos
*/
require_once('include/attach.php');
require_once('include/photo_factory.php');
require_once('include/photos.php');
/* @brief Initalize the cover-photo edit view
*
* @param $a Current application
* @return void
*
*/
class Cover_photo {
function init() {
if (! local_channel()) {
return;
}
$channel = App::get_channel();
Libprofile::load($channel['channel_address']);
}
/**
* @brief Evaluate posted values
*
* @return void
*
*/
function post() {
if (! local_channel()) {
return;
}
$channel = App::get_channel();
check_form_security_token_redirectOnErr('/cover_photo', 'cover_photo');
if ((array_key_exists('cropfinal',$_POST)) && ($_POST['cropfinal'] == 1)) {
// phase 2 - we have finished cropping
if (argc() != 2) {
notice( t('Image uploaded but image cropping failed.') . EOL );
return;
}
$image_id = argv(1);
if (substr($image_id,-2,1) == '-') {
$scale = substr($image_id,-1,1);
$image_id = substr($image_id,0,-2);
}
$srcX = intval($_POST['xstart']);
$srcY = intval($_POST['ystart']);
$srcW = intval($_POST['xfinal']) - $srcX;
$srcH = intval($_POST['yfinal']) - $srcY;
$r = q("select gender from profile where uid = %d and is_default = 1 limit 1",
intval(local_channel())
);
if ($r) {
$profile = array_shift($r);
}
$r = q("SELECT * FROM photo WHERE resource_id = '%s' AND uid = %d AND imgscale > 0 order by imgscale asc LIMIT 1",
dbesc($image_id),
intval(local_channel())
);
if ($r) {
$max_thumb = intval(get_config('system','max_thumbnail',1600));
$iscaled = false;
if (intval($r[0]['height']) > $max_thumb || intval($r[0]['width']) > $max_thumb) {
$imagick_path = get_config('system','imagick_convert_path');
if ($imagick_path && @file_exists($imagick_path) && intval($r[0]['os_storage'])) {
$fname = dbunescbin($r[0]['content']);
$tmp_name = $fname . '-001';
$newsize = photo_calculate_scale(array_merge(getimagesize($fname),['max' => $max_thumb]));
$cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $fname) . ' -resize ' . $newsize . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmp_name);
// logger('imagick thumbnail command: ' . $cmd);
for ($x = 0; $x < 4; $x ++) {
exec($cmd);
if (file_exists($tmp_name)) {
break;
}
}
if (file_exists($tmp_name)) {
$base_image = $r[0];
$gis = getimagesize($tmp_name);
logger('gis: ' . print_r($gis,true));
$base_image['width'] = $gis[0];
$base_image['height'] = $gis[1];
$base_image['content'] = @file_get_contents($tmp_name);
$iscaled = true;
@unlink($tmp_name);
}
}
}
if (! $iscaled) {
$base_image = $r[0];
$base_image['content'] = (($base_image['os_storage']) ? @file_get_contents(dbunescbin($base_image['content'])) : dbunescbin($base_image['content']));
}
$im = photo_factory($base_image['content'], $base_image['mimetype']);
if ($im->is_valid()) {
// We are scaling and cropping the relative pixel locations to the original photo instead of the
// scaled photo we operated on.
// First load the scaled photo to check its size. (Should probably pass this in the post form and save
// a query.)
$g = q("select width, height from photo where resource_id = '%s' and uid = %d and imgscale = 3",
dbesc($image_id),
intval(local_channel())
);
$scaled_width = $g[0]['width'];
$scaled_height = $g[0]['height'];
if ((! $scaled_width) || (! $scaled_height)) {
logger('potential divide by zero scaling cover photo');
return;
}
// unset all other cover photos
q("update photo set photo_usage = %d where photo_usage = %d and uid = %d",
intval(PHOTO_NORMAL),
intval(PHOTO_COVER),
intval(local_channel())
);
$orig_srcx = ( $base_image['width'] / $scaled_width ) * $srcX;
$orig_srcy = ( $base_image['height'] / $scaled_height ) * $srcY;
$orig_srcw = ( $srcW / $scaled_width ) * $base_image['width'];
$orig_srch = ( $srcH / $scaled_height ) * $base_image['height'];
$im->cropImageRect(1200,435,$orig_srcx, $orig_srcy, $orig_srcw, $orig_srch);
$aid = get_account_id();
$p = [
'aid' => $aid,
'uid' => local_channel(),
'resource_id' => $base_image['resource_id'],
'filename' => $base_image['filename'],
'album' => t('Cover Photos'),
'os_path' => $base_image['os_path'],
'display_path' => $base_image['display_path'],
'created' => $base_image['created'],
'edited' => $base_image['edited']
];
$p['imgscale'] = 7;
$p['photo_usage'] = PHOTO_COVER;
$r1 = $im->storeThumbnail($p, PHOTO_RES_COVER_1200);
$im->doScaleImage(850,310);
$p['imgscale'] = 8;
$r2 = $im->storeThumbnail($p, PHOTO_RES_COVER_850);
$im->doScaleImage(425,160);
$p['imgscale'] = 9;
$r3 = $im->storeThumbnail($p, PHOTO_RES_COVER_425);
if ($r1 === false || $r2 === false || $r3 === false) {
// if one failed, delete them all so we can start over.
notice( t('Image resize failed.') . EOL );
$x = q("delete from photo where resource_id = '%s' and uid = %d and imgscale >= 7 ",
dbesc($base_image['resource_id']),
local_channel()
);
return;
}
$channel = App::get_channel();
$this->send_cover_photo_activity($channel,$base_image,$profile);
}
else
notice( t('Unable to process image') . EOL);
}
goaway(z_root() . '/channel/' . $channel['channel_address']);
}
$hash = photo_new_resource();
$smallest = 0;
$matches = [];
$partial = false;
if (array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) {
$pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches);
if ($pm) {
logger('Content-Range: ' . print_r($matches,true));
$partial = true;
}
}
if ($partial) {
$x = save_chunk($channel,$matches[1],$matches[2],$matches[3]);
if ($x['partial']) {
header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0));
json_return_and_die($result);
}
else {
header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0));
$_FILES['userfile'] = [
'name' => $x['name'],
'type' => $x['type'],
'tmp_name' => $x['tmp_name'],
'error' => $x['error'],
'size' => $x['size']
];
}
}
else {
if (! array_key_exists('userfile',$_FILES)) {
$_FILES['userfile'] = [
'name' => $_FILES['files']['name'],
'type' => $_FILES['files']['type'],
'tmp_name' => $_FILES['files']['tmp_name'],
'error' => $_FILES['files']['error'],
'size' => $_FILES['files']['size']
];
}
}
$res = attach_store(App::get_channel(), get_observer_hash(), '', array('album' => t('Cover Photos'), 'hash' => $hash));
logger('attach_store: ' . print_r($res,true),LOGGER_DEBUG);
json_return_and_die([ 'message' => $hash ]);
}
function send_cover_photo_activity($channel,$photo,$profile) {
$arr = [];
$arr['item_thread_top'] = 1;
$arr['item_origin'] = 1;
$arr['item_wall'] = 1;
$arr['uuid'] = new_uuid();
$arr['mid'] = z_root() . '/item/' . $arr['uuid'];
$arr['obj_type'] = ACTIVITY_OBJ_NOTE;
$arr['verb'] = ACTIVITY_CREATE;
if ($profile && stripos($profile['gender'],t('female')) !== false) {
$t = t('%1$s updated her %2$s');
}
elseif ($profile && stripos($profile['gender'],t('male')) !== false) {
$t = t('%1$s updated his %2$s');
}
else {
$t = t('%1$s updated their %2$s');
}
$ptext = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' . t('cover photo') . '[/zrl]';
$ltext = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-8[/zmg][/zrl]';
$arr['body'] = sprintf($t,$channel['channel_name'],$ptext) . "\n\n" . $ltext;
$arr['obj'] = [
'type' => ACTIVITY_OBJ_NOTE,
'published' => datetime_convert('UTC','UTC',$photo['created'],ATOM_TIME),
'updated' => datetime_convert('UTC','UTC',$photo['edited'],ATOM_TIME),
'id' => $arr['mid'],
'url' => [ 'type' => 'Link', 'mediaType' => $photo['mimetype'], 'href' => z_root() . '/photo/' . $photo['resource_id'] . '-7' ],
'source' => [ 'content' => $arr['body'], 'mediaType' => 'text/bbcode' ],
'content' => bbcode($arr['body']),
'actor' => Activity::encode_person($channel,false),
];
$acl = new AccessControl($channel);
$x = $acl->get();
$arr['allow_cid'] = $x['allow_cid'];
$arr['allow_gid'] = $x['allow_gid'];
$arr['deny_cid'] = $x['deny_cid'];
$arr['deny_gid'] = $x['deny_gid'];
$arr['uid'] = $channel['channel_id'];
$arr['aid'] = $channel['channel_account_id'];
$arr['owner_xchan'] = $channel['channel_hash'];
$arr['author_xchan'] = $channel['channel_hash'];
post_activity_item($arr);
}
/**
* @brief Generate content of profile-photo view
*
* @return string
*
*/
function get() {
if (! local_channel()) {
notice( t('Permission denied.') . EOL );
return;
}
$channel = App::get_channel();
$newuser = false;
if (argc() == 2 && argv(1) === 'new')
$newuser = true;
if (argv(1) === 'use') {
if (argc() < 3) {
notice( t('Permission denied.') . EOL );
return;
};
// check_form_security_token_redirectOnErr('/cover_photo', 'cover_photo');
$resource_id = argv(2);
$r = q("SELECT id, album, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' and imgscale > 0 ORDER BY imgscale ASC",
intval(local_channel()),
dbesc($resource_id)
);
if (! $r) {
notice( t('Photo not available.') . EOL );
return;
}
$havescale = false;
foreach ($r as $rr) {
if ($rr['imgscale'] == 7) {
$havescale = true;
}
}
$r = q("SELECT content, mimetype, resource_id, os_storage FROM photo WHERE id = %d and uid = %d limit 1",
intval($r[0]['id']),
intval(local_channel())
);
if (! $r) {
notice( t('Photo not available.') . EOL );
return;
}
if (intval($r[0]['os_storage'])) {
$data = @file_get_contents(dbunescbin($r[0]['content']));
}
else {
$data = dbunescbin($r[0]['content']);
}
$ph = photo_factory($data, $r[0]['mimetype']);
$smallest = 0;
if ($ph->is_valid()) {
// go ahead as if we have just uploaded a new photo to crop
$i = q("select resource_id, imgscale from photo where resource_id = '%s' and uid = %d and imgscale = 0",
dbesc($r[0]['resource_id']),
intval(local_channel())
);
if ($i) {
$hash = $i[0]['resource_id'];
foreach ($i as $ii) {
$smallest = intval($ii['imgscale']);
}
}
}
$this->cover_photo_crop_ui_head($ph, $hash, $smallest);
}
if(! array_key_exists('imagecrop',App::$data)) {
$o .= replace_macros(get_markup_template('cover_photo.tpl'), [
'$user' => App::$channel['channel_address'],
'$info' => t('Your cover photo may be visible to anybody on the internet'),
'$existing' => get_cover_photo(local_channel(),'array',PHOTO_RES_COVER_850),
'$lbl_upfile' => t('Upload File:'),
'$lbl_profiles' => t('Select a profile:'),
'$title' => t('Change Cover Photo'),
'$submit' => t('Upload'),
'$profiles' => $profiles,
'$embedPhotos' => t('Use a photo from your albums'),
'$embedPhotosModalTitle' => t('Use a photo from your albums'),
'$embedPhotosModalCancel' => t('Cancel'),
'$embedPhotosModalOK' => t('OK'),
'$modalchooseimages' => t('Choose images to embed'),
'$modalchoosealbum' => t('Choose an album'),
'$modaldiffalbum' => t('Choose a different album'),
'$modalerrorlist' => t('Error getting album list'),
'$modalerrorlink' => t('Error getting photo link'),
'$modalerroralbum' => t('Error getting album'),
'$form_security_token' => get_form_security_token("cover_photo"),
'$select' => t('Select previously uploaded photo'),
]);
call_hooks('cover_photo_content_end', $o);
return $o;
}
else {
$filename = App::$data['imagecrop'] . '-3';
$resolution = 3;
$o .= replace_macros(get_markup_template('cropcover.tpl'), [
'$filename' => $filename,
'$profile' => intval($_REQUEST['profile']),
'$resource' => \App::$data['imagecrop'] . '-3',
'$image_url' => z_root() . '/photo/' . $filename,
'$title' => t('Crop Image'),
'$desc' => t('Please adjust the image cropping for optimum viewing.'),
'$form_security_token' => get_form_security_token("cover_photo"),
'$done' => t('Done Editing')
]);
return $o;
}
}
/* @brief Generate the UI for photo-cropping
*
* @param $a Current application
* @param $ph Photo-Factory
* @return void
*
*/
function cover_photo_crop_ui_head($ph, $hash, $smallest){
$max_length = get_config('system','max_image_length');
if (! $max_length) {
$max_length = MAX_IMAGE_LENGTH;
}
if ($max_length > 0) {
$ph->scaleImage($max_length);
}
$width = $ph->getWidth();
$height = $ph->getHeight();
if ($width < 300 || $height < 300) {
$ph->scaleImageUp(240);
$width = $ph->getWidth();
$height = $ph->getHeight();
}
App::$data['imagecrop'] = $hash;
App::$data['imagecrop_resolution'] = $smallest;
App::$page['htmlhead'] .= replace_macros(get_markup_template('crophead.tpl'), []);
return;
}
}

View file

@ -0,0 +1,534 @@
<?php
namespace Zotlabs\Module\Admin;
/*
* @file Profile_photo.php
* @brief Module-file with functions for uploading and scaling of profile-photos
*
*/
use App;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Libsync;
use Zotlabs\Lib\Libprofile;
use Zotlabs\Lib\Config;
use Zotlabs\Daemon\Run;
require_once('include/photo_factory.php');
require_once('include/photos.php');
class Profile_photo {
/* @brief Initalize the profile-photo edit view
*
* @return void
*
*/
function init() {
if(! is_site_admin()) {
return;
}
$channel = get_sys_channel();
Libprofile::load($channel['channel_address']);
}
/* @brief Evaluate posted values
*
* @param $a Current application
* @return void
*
*/
function post() {
if (! is_site_admin()) {
return;
}
$channel = get_sys_channel();
check_form_security_token_redirectOnErr('/profile_photo', 'profile_photo');
if ((array_key_exists('cropfinal',$_POST)) && (intval($_POST['cropfinal']) == 1)) {
// logger('crop: ' . print_r($_POST,true));
// phase 2 - we have finished cropping
if (argc() != 3) {
notice( t('Image uploaded but image cropping failed.') . EOL );
return;
}
$image_id = argv(2);
if (substr($image_id,-2,1) == '-') {
$scale = substr($image_id,-1,1);
$image_id = substr($image_id,0,-2);
}
// unless proven otherwise
$is_default_profile = 1;
if ($_REQUEST['profile']) {
$r = q("select id, profile_guid, is_default, gender from profile where id = %d and uid = %d limit 1",
intval($_REQUEST['profile']),
intval($channel['channel_id'])
);
if ($r) {
$profile = array_shift($r);
if (! intval($profile['is_default'])) {
$is_default_profile = 0;
}
}
}
$srcX = intval($_POST['xstart']);
$srcY = intval($_POST['ystart']);
$srcW = intval($_POST['xfinal']) - $srcX;
$srcH = intval($_POST['yfinal']) - $srcY;
$r = q("SELECT * FROM photo WHERE resource_id = '%s' AND uid = %d AND imgscale = %d LIMIT 1",
dbesc($image_id),
dbesc($channel['channel_id']),
intval($scale));
if ($r) {
$base_image = array_shift($r);
$base_image['content'] = (($base_image['os_storage']) ? @file_get_contents(dbunescbin($base_image['content'])) : dbunescbin($base_image['content']));
$im = photo_factory($base_image['content'], $base_image['mimetype']);
if ($im->is_valid()) {
$im->cropImage(300,$srcX,$srcY,$srcW,$srcH);
$aid = 0;
$p = [
'aid' => $aid,
'uid' => $channel['channel_id'],
'resource_id' => $base_image['resource_id'],
'filename' => $base_image['filename'],
'album' => t('Profile Photos'),
'os_path' => $base_image['os_path'],
'display_path' => $base_image['display_path'],
'created' => $base_image['created'],
'edited' => $base_image['edited']
];
$p['imgscale'] = PHOTO_RES_PROFILE_300;
$p['photo_usage'] = (($is_default_profile) ? PHOTO_PROFILE : PHOTO_NORMAL);
$r1 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_300);
$im->scaleImage(80);
$p['imgscale'] = PHOTO_RES_PROFILE_80;
$r2 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_80);
$im->scaleImage(48);
$p['imgscale'] = PHOTO_RES_PROFILE_48;
$r3 = $im->storeThumbnail($p, PHOTO_RES_PROFILE_48);
if ($r1 === false || $r2 === false || $r3 === false) {
// if one failed, delete them all so we can start over.
notice( t('Image resize failed.') . EOL );
$x = q("delete from photo where resource_id = '%s' and uid = %d and imgscale in ( %d, %d, %d ) ",
dbesc($base_image['resource_id']),
$channel['channel_id'],
intval(PHOTO_RES_PROFILE_300),
intval(PHOTO_RES_PROFILE_80),
intval(PHOTO_RES_PROFILE_48)
);
return;
}
$r = q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d
AND resource_id != '%s' AND uid = %d",
intval(PHOTO_NORMAL),
intval(PHOTO_PROFILE),
dbesc($base_image['resource_id']),
intval($channel['channel_id'])
);
// We'll set the updated profile-photo timestamp even if it isn't the default profile,
// so that browsers will do a cache update unconditionally
// Also set links back to site-specific profile photo url in case it was
// changed to a generic URL by a clone operation. Otherwise the new photo may
// not get pushed to other sites correctly.
$r = q("UPDATE xchan set xchan_photo_mimetype = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s'
where xchan_hash = '%s'",
dbesc($im->getType()),
dbesc(datetime_convert()),
dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']),
dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']),
dbesc(z_root() . '/photo/profile/s/' . $channel['channel_id']),
dbesc($channel['xchan_hash'])
);
// Similarly, tell the nav bar to bypass the cache and update the avatar image.
$_SESSION['reload_avatar'] = true;
Config::Set('system','site_icon_url',z_root() . '/photo/profile/m/' . $channel['channel_id']);
info( t('Shift-reload the page or clear browser cache if the new photo does not display immediately.') . EOL);
}
else {
notice( t('Unable to process image') . EOL);
}
}
goaway(z_root() . '/admin');
}
// A new photo was uploaded. Store it and save some important details
// in App::$data for use in the cropping function
$hash = photo_new_resource();
$importing = false;
$smallest = 0;
if ($_REQUEST['importfile']) {
$hash = $_REQUEST['importfile'];
$importing = true;
}
else {
$matches = [];
$partial = false;
if (array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) {
$pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches);
if ($pm) {
logger('Content-Range: ' . print_r($matches,true), LOGGER_DEBUG);
$partial = true;
}
}
if ($partial) {
$x = save_chunk($channel,$matches[1],$matches[2],$matches[3]);
if ($x['partial']) {
header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0));
json_return_and_die($result);
}
else {
header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0));
$_FILES['userfile'] = [
'name' => $x['name'],
'type' => $x['type'],
'tmp_name' => $x['tmp_name'],
'error' => $x['error'],
'size' => $x['size']
];
}
}
else {
if (! array_key_exists('userfile',$_FILES)) {
$_FILES['userfile'] = [
'name' => $_FILES['files']['name'],
'type' => $_FILES['files']['type'],
'tmp_name' => $_FILES['files']['tmp_name'],
'error' => $_FILES['files']['error'],
'size' => $_FILES['files']['size']
];
}
}
$res = attach_store($channel, $channel['channel_hash'], '', array('album' => t('Profile Photos'), 'hash' => $hash));
logger('attach_store: ' . print_r($res,true), LOGGER_DEBUG);
json_return_and_die([ 'message' => $hash ]);
}
if (($res && intval($res['data']['is_photo'])) || $importing) {
$i = q("select * from photo where resource_id = '%s' and uid = %d order by imgscale",
dbesc($hash),
intval($channel['channel_hash'])
);
if (! $i) {
notice( t('Image upload failed.') . EOL );
return;
}
$os_storage = false;
foreach ($i as $ii) {
if (intval($ii['imgscale']) < PHOTO_RES_640) {
$smallest = intval($ii['imgscale']);
$os_storage = intval($ii['os_storage']);
$imagedata = $ii['content'];
$filetype = $ii['mimetype'];
}
}
}
$imagedata = (($os_storage) ? @file_get_contents(dbunescbin($imagedata)) : dbunescbin($imagedata));
$ph = photo_factory($imagedata, $filetype);
if (! $ph->is_valid()) {
notice( t('Unable to process image.') . EOL );
return;
}
return $this->profile_photo_crop_ui_head($ph, $hash, $smallest);
// This will "fall through" to the get() method, and since
// App::$data['imagecrop'] is set, it will proceed to cropping
// rather than present the upload form
}
/* @brief Generate content of profile-photo view
*
* @return void
*
*/
function get() {
if (! is_site_admin()) {
notice( t('Permission denied.') . EOL );
return;
}
$channel = get_sys_channel();
$pf = 0;
$newuser = false;
if (argc() == 3 && argv(2) === 'new') {
$newuser = true;
}
if (argv(2) === 'reset') {
Config::Delete('system','site_icon_url');
}
if (argv(2) === 'use') {
if (argc() < 4) {
notice( t('Permission denied.') . EOL );
return;
};
$resource_id = argv(3);
$pf = (($_REQUEST['pf']) ? intval($_REQUEST['pf']) : 0);
$c = q("select id, is_default from profile where uid = %d",
intval($channel['channel_id'])
);
$multi_profiles = true;
if (($c) && (count($c) === 1) && (intval($c[0]['is_default']))) {
$_REQUEST['profile'] = $c[0]['id'];
$multi_profiles = false;
}
else {
$_REQUEST['profile'] = $pf;
}
$r = q("SELECT id, album, imgscale FROM photo WHERE uid = %d AND resource_id = '%s' ORDER BY imgscale ASC",
intval($channel['channel_id']),
dbesc($resource_id)
);
if (! $r) {
notice( t('Photo not available.') . EOL );
return;
}
$havescale = false;
foreach ($r as $rr) {
if ($rr['imgscale'] == PHOTO_RES_PROFILE_80) {
$havescale = true;
}
}
// set an already loaded and cropped photo as profile photo
if ($havescale) {
// unset any existing profile photos
$r = q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND uid = %d",
intval(PHOTO_NORMAL),
intval(PHOTO_PROFILE),
intval($channel['channel_id'])
);
$r = q("UPDATE photo SET photo_usage = %d WHERE uid = %d AND resource_id = '%s'",
intval(PHOTO_PROFILE),
intval($channel['channel_id']),
dbesc($resource_id)
);
$r = q("UPDATE xchan set xchan_photo_date = '%s' where xchan_hash = '%s'",
dbesc(datetime_convert()),
dbesc($channel['xchan_hash'])
);
goaway(z_root() . '/admin');
}
$r = q("SELECT content, mimetype, resource_id, os_storage FROM photo WHERE id = %d and uid = %d limit 1",
intval($r[0]['id']),
intval($channel['channel_id'])
);
if (! $r) {
notice( t('Photo not available.') . EOL );
return;
}
if (intval($r[0]['os_storage'])) {
$data = @file_get_contents(dbunescbin($r[0]['content']));
}
else {
$data = dbunescbin($r[0]['content']);
}
$ph = photo_factory($data, $r[0]['mimetype']);
$smallest = 0;
if ($ph->is_valid()) {
// go ahead as if we have just uploaded a new photo to crop
$i = q("select resource_id, imgscale from photo where resource_id = '%s' and uid = %d order by imgscale",
dbesc($r[0]['resource_id']),
intval($channel['channel_id'])
);
if ($i) {
$hash = $i[0]['resource_id'];
foreach ($i as $ii) {
if (intval($ii['imgscale']) < PHOTO_RES_640) {
$smallest = intval($ii['imgscale']);
}
}
}
}
if ($multi_profiles) {
App::$data['importfile'] = $resource_id;
}
else {
$this->profile_photo_crop_ui_head($ph, $hash, $smallest);
}
// falls through with App::$data['imagecrop'] set so we go straight to the cropping section
}
// present an upload form
$profiles = q("select id, profile_name as name, is_default from profile where uid = %d order by id asc",
intval($channel['channel_id'])
);
if ($profiles) {
for ($x = 0; $x < count($profiles); $x ++) {
$profiles[$x]['selected'] = false;
if ($pf && $profiles[$x]['id'] == $pf) {
$profiles[$x]['selected'] = true;
}
if ((! $pf) && $profiles[$x]['is_default']) {
$profiles[$x]['selected'] = true;
}
}
}
$importing = ((array_key_exists('importfile',App::$data)) ? true : false);
if (! array_key_exists('imagecrop', App::$data)) {
$tpl = get_markup_template('admin_profile_photo.tpl');
$o .= replace_macros($tpl, [
'$user' => $channel['channel_address'],
'$info' => ((count($profiles) > 1) ? t('Your default profile photo is visible to anybody on the internet. Profile photos for alternate profiles will inherit the permissions of the profile') : t('Your site photo is visible to anybody on the internet and may be distributed to other websites.')),
'$importfile' => (($importing) ? App::$data['importfile'] : ''),
'$lbl_upfile' => t('Upload File:'),
'$lbl_profiles' => t('Select a profile:'),
'$title' => (($importing) ? t('Use Photo for Site Logo') : t('Change Site Logo')),
'$submit' => (($importing) ? t('Use') : t('Upload')),
'$profiles' => $profiles,
'$single' => ((count($profiles) == 1) ? true : false),
'$profile0' => $profiles[0],
'$embedPhotos' => t('Use a photo from your albums'),
'$embedPhotosModalTitle' => t('Use a photo from your albums'),
'$embedPhotosModalCancel' => t('Cancel'),
'$embedPhotosModalOK' => t('OK'),
'$modalchooseimages' => t('Choose images to embed'),
'$modalchoosealbum' => t('Choose an album'),
'$modaldiffalbum' => t('Choose a different album'),
'$modalerrorlist' => t('Error getting album list'),
'$modalerrorlink' => t('Error getting photo link'),
'$modalerroralbum' => t('Error getting album'),
'$form_security_token' => get_form_security_token("profile_photo"),
'$select' => t('Select previously uploaded photo'),
]);
call_hooks('profile_photo_content_end', $o);
return $o;
}
else {
// present a cropping form
$filename = App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution'];
$resolution = App::$data['imagecrop_resolution'];
$o .= replace_macros(get_markup_template('admin_cropbody.tpl'), [
'$filename' => $filename,
'$profile' => intval($_REQUEST['profile']),
'$resource' => App::$data['imagecrop'] . '-' . App::$data['imagecrop_resolution'],
'$image_url' => z_root() . '/photo/' . $filename,
'$title' => t('Crop Image'),
'$desc' => t('Please adjust the image cropping for optimum viewing.'),
'$form_security_token' => get_form_security_token("profile_photo"),
'$done' => t('Done Editing')
]);
return $o;
}
}
/* @brief Generate the UI for photo-cropping
*
* @param $ph Photo-Factory
* @return void
*
*/
function profile_photo_crop_ui_head($ph, $hash, $smallest) {
$max_length = get_config('system','max_image_length');
if (! $max_length) {
$max_length = MAX_IMAGE_LENGTH;
}
if ($max_length > 0) {
$ph->scaleImage($max_length);
}
App::$data['width'] = $ph->getWidth();
App::$data['height'] = $ph->getHeight();
if (App::$data['width'] < 500 || App::$data['height'] < 500) {
$ph->scaleImageUp(400);
App::$data['width'] = $ph->getWidth();
App::$data['height'] = $ph->getHeight();
}
App::$data['imagecrop'] = $hash;
App::$data['imagecrop_resolution'] = $smallest;
App::$page['htmlhead'] .= replace_macros(get_markup_template('crophead.tpl'), []);
return;
}
}

View file

@ -25,8 +25,6 @@ class Site {
$sitename = ((x($_POST,'sitename')) ? notags(trim($_POST['sitename'])) : '');
$banner = ((x($_POST,'banner')) ? trim($_POST['banner']) : false);
$admininfo = ((x($_POST,'admininfo')) ? trim($_POST['admininfo']) : false);
$siteinfo = ((x($_POST,'siteinfo')) ? trim($_POST['siteinfo']) : '');
$language = ((x($_POST,'language')) ? notags(trim($_POST['language'])) : 'en');
@ -123,13 +121,6 @@ class Site {
set_config('system','directory_server',$directory_server);
}
if ($banner == '') {
del_config('system', 'banner');
}
else {
set_config('system', 'banner', $banner);
}
if ($admininfo == '') {
del_config('system', 'admininfo');
}
@ -250,11 +241,6 @@ class Site {
}
}
/* Banner */
$banner = System::get_banner();
$banner = htmlspecialchars($banner);
/* Admin Info */
@ -304,7 +290,6 @@ class Site {
'$advanced' => t('Advanced'),
'$baseurl' => z_root(),
'$sitename' => [ 'sitename', t("Site name"), htmlspecialchars(get_config('system','sitename'), ENT_QUOTES, 'UTF-8'),'' ],
'$banner' => [ 'banner', t("Banner/Logo"), $banner, t('Unfiltered HTML/CSS/JS is allowed') ],
'$admininfo' => [ 'admininfo', t("Administrator Information"), $admininfo, t("Contact information for site administrators. Displayed on siteinfo page. BBCode may be used here.") ],
'$siteinfo' => [ 'siteinfo', t('Site Information'), get_config('system','siteinfo'), t("Publicly visible description of this site. Displayed on siteinfo page. BBCode may be used here.") ],
'$language' => [ 'language', t("System language"), get_config('system','language','en'), "", $lang_choices ],

View file

@ -19,16 +19,17 @@ class Admin {
// array( url, name, extra css classes )
$aside = [
'site' => array(z_root() . '/admin/site/', t('Site'), 'site'),
'accounts' => array(z_root() . '/admin/accounts/', t('Accounts'), 'accounts', 'pending-update', t('Member registrations waiting for confirmation')),
'channels' => array(z_root() . '/admin/channels/', t('Channels'), 'channels'),
'security' => array(z_root() . '/admin/security/', t('Security'), 'security'),
// 'features' => array(z_root() . '/admin/features/', t('Features'), 'features'),
'addons' => array(z_root() . '/admin/addons/', t('Addons'), 'addons'),
'themes' => array(z_root() . '/admin/themes/', t('Themes'), 'themes'),
'queue' => array(z_root() . '/admin/queue', t('Inspect queue'), 'queue'),
// 'profs' => array(z_root() . '/admin/profs', t('Profile Fields'), 'profs'),
'dbsync' => array(z_root() . '/admin/dbsync/', t('DB updates'), 'dbsync')
'site' => array(z_root() . '/admin/site/', t('Site'), 'site'),
'profile_photo' => array(z_root() . '/admin/profile_photo', t('Site icon/logo'), 'profile_photo'),
'accounts' => array(z_root() . '/admin/accounts/', t('Accounts'), 'accounts', 'pending-update', t('Member registrations waiting for confirmation')),
'channels' => array(z_root() . '/admin/channels/', t('Channels'), 'channels'),
'security' => array(z_root() . '/admin/security/', t('Security'), 'security'),
// 'features' => array(z_root() . '/admin/features/', t('Features'), 'features'),
'addons' => array(z_root() . '/admin/addons/', t('Addons'), 'addons'),
'themes' => array(z_root() . '/admin/themes/', t('Themes'), 'themes'),
'queue' => array(z_root() . '/admin/queue', t('Inspect queue'), 'queue'),
// 'profs' => array(z_root() . '/admin/profs', t('Profile Fields'), 'profs'),
'dbsync' => array(z_root() . '/admin/dbsync/', t('DB updates'), 'dbsync')
];
/* get plugins admin page */

View file

@ -47,9 +47,9 @@ function nav($template = 'default') {
$channel_apps[] = channel_apps($is_owner, App::$profile['channel_address']);
$project_icon = System::get_project_icon();
$site_icon = System::get_site_icon();
$banner = System::get_banner();
$banner = System::get_site_name();
App::$page['header'] .= replace_macros(get_markup_template('hdr.tpl'), array(
//we could additionally use this to display important system notifications e.g. for updates
@ -284,7 +284,7 @@ function nav($template = 'default') {
App::$page['nav'] .= replace_macros($tpl, array(
'$baseurl' => z_root(),
'$project_icon' => $project_icon,
'$project_icon' => $site_icon,
'$project_title' => t('Powered by $Projectname'),
'$fulldocs' => t('Help'),
'$sitelocation' => $sitelocation,

View file

@ -15,11 +15,6 @@ html {
margin-right: 1.5rem;
}
.project-banner img {
height: 32px;
width: 32px;
}
.project-banner a {
color: $banner_colour;
}
@ -1301,7 +1296,7 @@ img.mail-conv-sender-photo {
width: 3.75rem;
}
#avatar {
#avatar, .nav-avatar {
width: 2.35rem;
height: 2.35rem;
border-radius: $radius;

View file

@ -0,0 +1,48 @@
<h1>{{$title}}</h1>
<p id="cropimage-desc">
{{$desc}}
</p>
<div id="cropimage-wrapper">
<img src="{{$image_url}}" id="croppa" class="imgCrop" alt="{{$title}}" />
</div>
<div id="cropimage-preview-wrapper" >
<div id="previewWrap" class="crop-preview d-lg-none" style="height: 300px; width: 300px; max-width: 300px; max-height: 300px; overflow: hidden;"></div>
</div>
<script type="text/javascript" language="javascript">
var image = document.getElementById('croppa');
var cropper = new Cropper(image, {
aspectRatio: 1 / 1,
viewMode: 1,
preview: '#profile-photo-wrapper, .crop-preview',
crop: function(e) {
$( '#x1' ).val(e.detail.x);
$( '#y1' ).val(e.detail.y);
$( '#x2' ).val(e.detail.x + e.detail.width);
$( '#y2' ).val(e.detail.y + e.detail.height);
$( '#width' ).val(e.detail.scaleX);
$( '#height' ).val(e.detail.scaleY);
}
});
</script>
<form action="admin/profile_photo/{{$resource}}" id="crop-image-form" method="post" />
<input type='hidden' name='form_security_token' value='{{$form_security_token}}'>
<input type='hidden' name='profile' value='{{$profile}}'>
<input type="hidden" name="cropfinal" value="1" />
<input type="hidden" name="xstart" id="x1" />
<input type="hidden" name="ystart" id="y1" />
<input type="hidden" name="xfinal" id="x2" />
<input type="hidden" name="yfinal" id="y2" />
<input type="hidden" name="height" id="height" />
<input type="hidden" name="width" id="width" />
<div id="crop-image-submit-wrapper" >
<input type="submit" name="submit" value="{{$done}}" />
</div>
</form>

View file

@ -0,0 +1,195 @@
<script src="vendor/blueimp/jquery-file-upload/js/vendor/jquery.ui.widget.js"></script>
<script src="vendor/blueimp/jquery-file-upload/js/jquery.iframe-transport.js"></script>
<script src="vendor/blueimp/jquery-file-upload/js/jquery.fileupload.js"></script>
<script>
var initializeEmbedPhotoDialog = function () {
$('.embed-photo-selected-photo').each(function (index) {
$(this).removeClass('embed-photo-selected-photo');
});
getPhotoAlbumList();
$('#embedPhotoModalBodyAlbumDialog').off('click');
$('#embedPhotoModal').modal('show');
};
var choosePhotoFromAlbum = function (album) {
$.post("embedphotos/album", {name: album},
function(data) {
if (data['status']) {
$('#embedPhotoModalLabel').html("{{$modalchooseimages}}");
$('#embedPhotoModalBodyAlbumDialog').html('\
<div><div class="nav nav-pills flex-column">\n\
<li class="nav-item"><a class="nav-link" href="#" onclick="initializeEmbedPhotoDialog();return false;">\n\
<i class="fa fa-chevron-left"></i>&nbsp\n\
{{$modaldiffalbum}}\n\
</a>\n\
</li>\n\
</div><br></div>')
$('#embedPhotoModalBodyAlbumDialog').append(data['content']);
$('#embedPhotoModalBodyAlbumDialog').click(function (evt) {
evt.preventDefault();
var image = document.getElementById(evt.target.id);
if (typeof($(image).parent()[0]) !== 'undefined') {
var imageparent = document.getElementById($(image).parent()[0].id);
$(imageparent).toggleClass('embed-photo-selected-photo');
var href = $(imageparent).attr('href');
$.post("embedphotos/photolink", {href: href},
function(ddata) {
if (ddata['status']) {
var pf = $('#profile-photo-profiles').val();
var prof = ((typeof pf !== 'undefined') ? '?f=&pf=' + pf : '');
window.location.href = 'admin/profile_photo/use/' + ddata['resource_id'] + prof;
} else {
window.console.log("{{$modalerrorlink}}" + ':' + ddata['errormsg']);
}
return false;
},
'json');
$('#embedPhotoModalBodyAlbumDialog').html('');
$('#embedPhotoModalBodyAlbumDialog').off('click');
$('#embedPhotoModal').modal('hide');
}
});
$('#embedPhotoModalBodyAlbumListDialog').addClass('d-none');
$('#embedPhotoModalBodyAlbumDialog').removeClass('d-none');
} else {
window.console.log("{{$modalerroralbum}} " + JSON.stringify(album) + ':' + data['errormsg']);
}
return false;
},
'json');
};
var getPhotoAlbumList = function () {
$.post("embedphotos/albumlist", {},
function(data) {
if (data['status']) {
var albums = data['albumlist']; //JSON.parse(data['albumlist']);
$('#embedPhotoModalLabel').html("{{$modalchoosealbum}}");
$('#embedPhotoModalBodyAlbumList').html('<ul class="nav nav-pills flex-column"></ul>');
for(var i=0; i<albums.length; i++) {
var albumName = albums[i].text;
var jsAlbumName = albums[i].jstext;
var albumLink = '<li class="nav-item">';
albumLink += '<a class="nav-link" href="#" onclick="choosePhotoFromAlbum(\'' + jsAlbumName + '\'); return false;">' + albumName + '</a>';
albumLink += '</li>';
$('#embedPhotoModalBodyAlbumList').find('ul').append(albumLink);
}
$('#embedPhotoModalBodyAlbumDialog').addClass('d-none');
$('#embedPhotoModalBodyAlbumListDialog').removeClass('d-none');
} else {
window.console.log("{{$modalerrorlist}}" + ':' + data['errormsg']);
}
return false;
},
'json');
};
</script>
<input id="invisible-photos-file-upload" type="file" name="files" style="visibility:hidden;position:absolute;top:-50;left:-50;width:0;height:0;" >
<div id="profile-photo-content" class="generic-content-wrapper">
<div class="section-title-wrapper">
<h2>{{$title}}</h2>
</div>
<div class="section-content-wrapper">
<form enctype="multipart/form-data" id="profile-photo-form" action="admin/profile_photo" method="post">
<input type='hidden' name='form_security_token' value='{{$form_security_token}}'>
<div id="profile-photo-upload-wrapper">
{{if $info}}
<div class="section-content-warning-wrapper">{{$info}}</div>
{{/if}}
{{if $importfile}}
<input type="hidden" name="importfile" value="{{$importfile}}">
{{else}}
<div id="profile-photo-upload-spinner" class="spinner-wrapper">
<div class="spinner m"></div>
</div>
<!--label id="profile-photo-upload-label" class="form-label" for="profile-photo-upload">{{$lbl_upfile}}</label>
<input name="userfile" class="form-input" type="file" id="profile-photo-upload" size="48" / -->
{{/if}}
<div class="clear"></div>
{{if $single}}
<input type="hidden" name="profile" value="{{$profile0.id}}" />
{{else}}
<label id="profile-photo-profiles-label" class="form-label" for="profile-photo-profiles">{{$lbl_profiles}}</label>
<select name="profile" id="profile-photo-profiles" class="form-control" >
{{foreach $profiles as $p}}
<option value="{{$p.id}}" {{if $p.selected}}selected="selected"{{/if}}>{{$p.name}}</option>
{{/foreach}}
</select>
<div class="clear"></div>
<br>
<br>
{{/if}}
<div id="profile-photo-submit-wrapper">
<input type="submit" name="submit" id="profile-photo-submit" value="{{$submit}}">
</div>
</div>
</form>
<br>
<div id="profile-photo-link-select-wrapper">
<button id="embed-photo-wrapper" class="btn btn-default btn-primary" title="{{$embedPhotos}}" onclick="initializeEmbedPhotoDialog();return false;">
<i id="embed-photo" class="fa fa-file-image-o"></i> {{$select}}
</button>
</div>
</div>
</div>
<script>
$('#invisible-photos-file-upload').fileupload({
url: 'admin/profile_photo',
dataType: 'json',
dropZone: $('#profile-photo-content'),
maxChunkSize: 2 * 1024 * 1024,
add: function(e,data) {
data.formData = $('#profile-photo-form').serializeArray();
data.submit();
$('#profile-photo-upload-spinner').show();
},
done: function(e,data) {
$('#profile-photo-upload-spinner').hide();
window.location.href = window.location.href + '/use/' + data.result.message;
}
});
$('#profile-photo-submit').click(function(event) { event.preventDefault(); $('#invisible-photos-file-upload').trigger('click'); return false;});
</script>
<div class="modal" id="embedPhotoModal" tabindex="-1" role="dialog" aria-labelledby="embedPhotoLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="embedPhotoModalLabel">{{$embedPhotosModalTitle}}</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body" id="embedPhotoModalBody" >
<div id="embedPhotoModalBodyAlbumListDialog" class="d-none">
<div id="embedPhotoModalBodyAlbumList"></div>
</div>
<div id="embedPhotoModalBodyAlbumDialog" class="d-none"></div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View file

@ -55,7 +55,6 @@
<div id="basic-settings-collapse" class="collapse show" role="tabpanel" aria-labelledby="basic-settings" data-parent="#settings">
<div class="section-content-tools-wrapper">
{{include file="field_input.tpl" field=$sitename}}
{{include file="field_textarea.tpl" field=$banner}}
{{include file="field_textarea.tpl" field=$siteinfo}}
{{include file="field_textarea.tpl" field=$admininfo}}
{{include file="field_input.tpl" field=$reply_address}}

View file

@ -1,4 +1,4 @@
<div class="project-banner" title="{{$project_title}}"><a href="{{$baseurl}}/siteinfo"><img src="{{$project_icon}}" alt="{{$project_title}}"></a></div>
<div class="project-banner" title="{{$project_title}}"><a href="{{$baseurl}}/siteinfo"><img src="{{$project_icon}}" alt="{{$project_title}}" class="nav-avatar"></a></div>
{{if $nav.login && !$userinfo}}
<div class="d-lg-none pt-1 pb-1">
{{if $nav.loginmenu.1.4}}