diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index 59e836a4d..9bc43316d 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -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(); } diff --git a/Zotlabs/Lib/System.php b/Zotlabs/Lib/System.php index b124c7a99..5cac1e2b3 100644 --- a/Zotlabs/Lib/System.php +++ b/Zotlabs/Lib/System.php @@ -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(); } diff --git a/Zotlabs/Module/Admin/Cover_photo.php b/Zotlabs/Module/Admin/Cover_photo.php new file mode 100644 index 000000000..42aa238e6 --- /dev/null +++ b/Zotlabs/Module/Admin/Cover_photo.php @@ -0,0 +1,499 @@ + 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; + } + + +} diff --git a/Zotlabs/Module/Admin/Profile_photo.php b/Zotlabs/Module/Admin/Profile_photo.php new file mode 100644 index 000000000..bc7160832 --- /dev/null +++ b/Zotlabs/Module/Admin/Profile_photo.php @@ -0,0 +1,534 @@ +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; + } +} diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php index eb34e5b8a..c3797e33f 100644 --- a/Zotlabs/Module/Admin/Site.php +++ b/Zotlabs/Module/Admin/Site.php @@ -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 ], diff --git a/Zotlabs/Widget/Admin.php b/Zotlabs/Widget/Admin.php index 2bf62570b..95e64244e 100644 --- a/Zotlabs/Widget/Admin.php +++ b/Zotlabs/Widget/Admin.php @@ -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 */ diff --git a/include/nav.php b/include/nav.php index 2cc8501dd..7d7e7b252 100644 --- a/include/nav.php +++ b/include/nav.php @@ -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, diff --git a/view/theme/redbasic/css/style.css b/view/theme/redbasic/css/style.css index 5a16dba4e..4b5ec53c9 100644 --- a/view/theme/redbasic/css/style.css +++ b/view/theme/redbasic/css/style.css @@ -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; diff --git a/view/tpl/admin_cropbody.tpl b/view/tpl/admin_cropbody.tpl new file mode 100644 index 000000000..a5954576e --- /dev/null +++ b/view/tpl/admin_cropbody.tpl @@ -0,0 +1,48 @@ +

{{$title}}

+

+{{$desc}} +

+
+{{$title}} +
+
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +
diff --git a/view/tpl/admin_profile_photo.tpl b/view/tpl/admin_profile_photo.tpl new file mode 100644 index 000000000..547d4e7d6 --- /dev/null +++ b/view/tpl/admin_profile_photo.tpl @@ -0,0 +1,195 @@ + + + + + + + + + + + + +
+
+

{{$title}}

+
+
+ +
+ + +
+ {{if $info}} +
{{$info}}
+ {{/if}} + {{if $importfile}} + + {{else}} +
+
+
+ + {{/if}} +
+ + + {{if $single}} + + {{else}} + + + + + +
+
+
+ {{/if}} + +
+ +
+
+ +
+
+ +
+
+ + + + + diff --git a/view/tpl/admin_site.tpl b/view/tpl/admin_site.tpl index 634bdf898..0d7819ee8 100755 --- a/view/tpl/admin_site.tpl +++ b/view/tpl/admin_site.tpl @@ -55,7 +55,6 @@
{{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}} diff --git a/view/tpl/navbar_default.tpl b/view/tpl/navbar_default.tpl index d91a06f34..28e26ba80 100755 --- a/view/tpl/navbar_default.tpl +++ b/view/tpl/navbar_default.tpl @@ -1,4 +1,4 @@ -
{{$project_title}}
+
{{$project_title}}
{{if $nav.login && !$userinfo}}
{{if $nav.loginmenu.1.4}}