diff --git a/README.md b/README.md index b6750480e..645065948 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ Hubzilla - Community Server Help us redefine the web - using integrated and united community websites. -------------------------------------------------------------------------- -[![Build Status](https://travis-ci.org/redmatrix/hubzilla.svg)](https://travis-ci.org/redmatrix/hubzilla) +
**What are Hubs?** @@ -38,3 +40,8 @@ Possible website applications include * dating websites * pretty much anything you can do on a traditional blog or community website, but that you could do better if you could easily connect it with other websites or privately share things across website boundaries. + + +[![Build Status](https://travis-ci.org/redmatrix/hubzilla.svg)](https://travis-ci.org/redmatrix/hubzilla) diff --git a/boot.php b/boot.php index f58709384..599514d20 100755 --- a/boot.php +++ b/boot.php @@ -48,7 +48,7 @@ require_once('include/AccessList.php'); define ( 'PLATFORM_NAME', 'hubzilla' ); define ( 'RED_VERSION', trim(file_get_contents('version.inc'))); -define ( 'STD_VERSION', '1.1' ); +define ( 'STD_VERSION', '1.1.1' ); define ( 'ZOT_REVISION', 1 ); define ( 'DB_UPDATE_VERSION', 1161 ); diff --git a/include/account.php b/include/account.php index e448bdcc6..dae0f4895 100644 --- a/include/account.php +++ b/include/account.php @@ -11,6 +11,7 @@ require_once('include/text.php'); require_once('include/language.php'); require_once('include/datetime.php'); require_once('include/crypto.php'); +require_once('include/identity.php'); function check_account_email($email) { @@ -329,7 +330,7 @@ function send_reg_approval_email($arr) { return($delivered ? true : false); } -function send_verification_email($email,$password) { +function send_register_success_email($email,$password) { $email_msg = replace_macros(get_intltext_template('register_open_eml.tpl'), array( '$sitename' => get_config('system','sitename'), @@ -353,7 +354,7 @@ function send_verification_email($email,$password) { * @param string $hash * @return array|boolean */ -function user_allow($hash) { +function account_allow($hash) { $ret = array('success' => false); @@ -406,6 +407,9 @@ function user_allow($hash) { pop_lang(); + if(get_config('system','auto_channel_create')) + auto_channel_create($register[0]['uid']); + if ($res) { info( t('Account approved.') . EOL ); return true; @@ -414,7 +418,7 @@ function user_allow($hash) { /** - * @brief Denies a user registration. + * @brief Denies an account registration. * * This does not have to go through user_remove() and save the nickname * permanently against re-registration, as the person was not yet @@ -423,7 +427,8 @@ function user_allow($hash) { * @param string $hash * @return boolean */ -function user_deny($hash) { + +function account_deny($hash) { $register = q("SELECT * FROM register WHERE hash = '%s' LIMIT 1", dbesc($hash) @@ -452,11 +457,14 @@ function user_deny($hash) { } +// called from regver to activate an account from the email verification link -function user_approve($hash) { +function account_approve($hash) { $ret = array('success' => false); + // Note: when the password in the register table is 'verify', the uid actually contains the account_id + $register = q("SELECT * FROM `register` WHERE `hash` = '%s' and password = 'verify' LIMIT 1", dbesc($hash) ); @@ -491,6 +499,10 @@ function user_approve($hash) { intval($register[0]['uid']) ); + + if(get_config('system','auto_channel_create')) + auto_channel_create($register[0]['uid']); + info( t('Account verified. Please login.') . EOL ); return true; diff --git a/include/config.php b/include/config.php index c94d25eb8..f65e4a470 100644 --- a/include/config.php +++ b/include/config.php @@ -531,3 +531,22 @@ function del_xconfig($xchan, $family, $key) { ); return $ret; } + + +// account configuration storage is built on top of the under-utilised xconfig + +function load_aconfig($account_id) { + load_xconfig('a_' . $account_id); +} + +function get_aconfig($account_id, $family, $key) { + return get_xconfig('a_' . $account_id, $family, $key); +} + +function set_aconfig($account_id, $family, $key, $value) { + return set_xconfig('a_' . $account_id, $family, $key, $value); +} + +function del_aconfig($account_id, $family, $key) { + return del_xconfig('a_' . $account_id, $family, $key); +} \ No newline at end of file diff --git a/include/identity.php b/include/identity.php index cfedd243a..1d908056f 100644 --- a/include/identity.php +++ b/include/identity.php @@ -1695,3 +1695,45 @@ function profiles_build_sync($channel_id) { build_sync_packet($channel_id,array('profile' => $r)); } } + + +function auto_channel_create($account_id) { + + if(! $account_id) + return false; + + $arr = array(); + $arr['account_id'] = $account_id; + $arr['name'] = get_aconfig($account_id,'register','channel_name'); + $arr['nickname'] = legal_webbie(get_aconfig($account_id,'register','channel_address')); + $arr['permissions_role'] = get_aconfig($account_id,'register','permissions_role'); + + del_aconfig($account_id,'register','channel_name'); + del_aconfig($account_id,'register','channel_address'); + del_aconfig($account_id,'register','permissions_role'); + + if((! $arr['name']) || (! $arr['nickname'])) { + $x = q("select * from account where account_id = %d limit 1", + intval($account_id) + ); + if($x) { + if(! $arr['name']) + $arr['name'] = substr($x[0]['account_email'],0,strpos($x[0]['account_email'],'@')); + if(! $arr['nickname']) + $arr['nickname'] = legal_webbie(substr($x[0]['account_email'],0,strpos($x[0]['account_email'],'@'))); + } + } + if(! $arr['permissions_role']) + $arr['permissions_role'] = 'social'; + + if(validate_channelname($arr['name'])) + return false; + if($arr['nickname'] === 'sys') + $arr['nickname'] = $arr['nickname'] . mt_rand(1000,9999); + + $arr['nickname'] = check_webbie(array($arr['nickname'], $arr['nickname'] . mt_rand(1000,9999))); + + return create_identity($arr); + +} + diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index 0de3b9c97..dce92d8da 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -69,6 +69,8 @@ abstract class photo_driver { abstract function cropImage($max,$x,$y,$w,$h); + abstract function cropImageRect($maxx,$maxy,$x,$y,$w,$h); + abstract function imageString(); @@ -229,6 +231,7 @@ abstract class photo_driver { $this->doScaleImage($dest_width,$dest_height); } + public function scaleImageSquare($dim) { if(!$this->is_valid()) return FALSE; diff --git a/include/photo/photo_gd.php b/include/photo/photo_gd.php index 2ac7287e4..24bdc204f 100644 --- a/include/photo/photo_gd.php +++ b/include/photo/photo_gd.php @@ -108,6 +108,23 @@ class photo_gd extends photo_driver { $this->setDimensions(); } + public function cropImageRect($maxx,$maxy,$x,$y,$w,$h) { + if(!$this->is_valid()) + return FALSE; + + $dest = imagecreatetruecolor( $maxx, $maxy ); + imagealphablending($dest, false); + imagesavealpha($dest, true); + if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha + imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h); + if($this->image) + imagedestroy($this->image); + $this->image = $dest; + $this->setDimensions(); + } + + + public function imageString() { if(!$this->is_valid()) return FALSE; diff --git a/include/photo/photo_imagick.php b/include/photo/photo_imagick.php index 3f84fd06c..32bb61342 100644 --- a/include/photo/photo_imagick.php +++ b/include/photo/photo_imagick.php @@ -163,6 +163,24 @@ class photo_imagick extends photo_driver { $this->doScaleImage($max,$max); } + public function cropImageRect($maxx,$maxy,$x,$y,$w,$h) { + if(!$this->is_valid()) + return FALSE; + + $this->image->setFirstIterator(); + do { + $this->image->cropImage($w, $h, $x, $y); + /** + * We need to remove the canvas, + * or the image is not resized to the crop: + * http://php.net/manual/en/imagick.cropimage.php#97232 + */ + $this->image->setImagePage(0, 0, 0, 0); + } while ($this->image->nextImage()); + + $this->doScaleImage($maxx,$maxy); + } + public function imageString() { if(!$this->is_valid()) return FALSE; diff --git a/include/plugin.php b/include/plugin.php index 4da73dfd8..2bc363556 100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -348,19 +348,46 @@ function get_plugin_info($plugin){ } else { $info[$k][] = array('name' => $v); } - } else { -// if (array_key_exists($k, $info)){ - $info[$k] = $v; -// } + } + else { + $info[$k] = $v; } } } } - return $info; } +function check_plugin_versions($info) { + + if(! is_array($info)) + return true; + + if(array_key_exists('minversion',$info)) { + if(version_compare(trim($info['minversion']),STD_VERSION, '>=')) { + logger('minversion limit: ' . $info['name'],LOGGER_NORMAL,LOG_WARNING); + return false; + } + } + if(array_key_exists('maxversion',$info)) { + if(version_compare(STD_VERSION,trim($info['maxversion']), '>')) { + logger('maxversion limit: ' . $info['name'],LOGGER_NORMAL,LOG_WARNING); + return false; + } + } + if(array_key_exists('minphpversion',$info)) { + if(version_compare(trim($info['minphpversion']),PHP_VERSION, '>=')) { + logger('minphpversion limit: ' . $info['name'],LOGGER_NORMAL,LOG_WARNING); + return false; + } + } + + return true; +} + + + /** * @brief Parse theme comment in search of theme infos. diff --git a/include/widgets.php b/include/widgets.php index a7d06d39e..033ba44fe 100644 --- a/include/widgets.php +++ b/include/widgets.php @@ -170,8 +170,8 @@ function widget_follow($args) { } return replace_macros(get_markup_template('follow.tpl'),array( '$connect' => t('Add New Connection'), - '$desc' => t('Enter the channel address'), - '$hint' => t('Example: bob@example.com, http://example.com/barbara'), + '$desc' => t('Enter channel address'), + '$hint' => t('Examples: bob@example.com, https://example.com/barbara'), '$follow' => t('Connect'), '$abook_usage_message' => $abook_usage_message )); diff --git a/index.php b/index.php index 6ed7eeb11..c864a7b37 100755 --- a/index.php +++ b/index.php @@ -336,11 +336,13 @@ if($a->module_loaded) { } if((! $a->error) && (function_exists($a->module . '_content'))) { - $arr = array('content' => $a->page['content']); + $arr = array('content' => $a->page['content'], 'replace' => false); call_hooks($a->module . '_mod_content', $arr); $a->page['content'] = $arr['content']; - $func = $a->module . '_content'; - $arr = array('content' => $func($a)); + if(! $arr['replace']) { + $func = $a->module . '_content'; + $arr = array('content' => $func($a)); + } call_hooks($a->module . '_mod_aftercontent', $arr); $a->page['content'] .= $arr['content']; } diff --git a/install/INSTALL.txt b/install/INSTALL.txt index 8ca74c23b..78cea638d 100644 --- a/install/INSTALL.txt +++ b/install/INSTALL.txt @@ -1,6 +1,6 @@ Hubzilla Installation -We've tried very hard to ensure that the Hubzilla will run on commodity +We've tried very hard to ensure that Hubzilla will run on commodity hosting platforms - such as those used to host Wordpress blogs and Drupal websites. It will run on most any Linux VPS system. Windows LAMP platforms such as XAMPP and WAMP are not officially supported at this time - however @@ -29,7 +29,7 @@ issues. Before you begin: Choose a domain name or subdomain name for your server. -The Hubzilla can only be installed into the root of a domain or +Hubzilla can only be installed into the root of a domain or sub-domain, and can not be installed using alternate TCP ports. Decide if you will use SSL and obtain an SSL certificate before software @@ -53,13 +53,14 @@ advice. This is disruptive to the community. That said, we recognise the issues surrounding the current certificate infrastructure and agree there are many problems, but that doesn't change the requirement. -Free "browser-valid" certificates are available from providers such as StartSSL. +Free "browser-valid" certificates are available from providers such as StartSSL +and LetsEncrypt. If you do NOT use SSL, there may be a delay of up to a minute for the initial install script - while we check the SSL port to see if anything responds there. When communicating with new sites, Hubzilla always attempts connection on the SSL port first, before falling back to a less secure connection. If you do not -use SSL, your webserver must not listen on port 443 at all. +use SSL, your webserver MUST NOT listen on port 443 at all. 1. Requirements - Apache with mod-rewrite enabled and "AllowOverride All" so you can use a @@ -137,7 +138,7 @@ use SSL, your webserver must not listen on port 443 at all. cd mywebsite util/update_addon_repo matrix - - Create searchable represenations of the online documentation. You may do this any time + - Create searchable representations of the online documentation. You may do this any time that the documentation is updated. cd mywebsite @@ -177,7 +178,16 @@ Registration errors should all be recoverable automatically. If you get any *critical* failure at this point, it generally indicates the database was not installed correctly. You might wish to move/rename .htconfig.php to another name and empty (called 'dropping') the database -tables, so that you can start fresh. +tables, so that you can start fresh. + +In order for your account to be given administrator access, it should be the +first account created, and the email address provided during registration +must match the "administrator email" address you provided during +installation. Otherwise to give an account administrator access, +add 4096 to the account_roles for that account in the database. + +For your site security there is no way to provide administrator access +using web forms. **************************************************************************** **************************************************************************** @@ -185,7 +195,7 @@ tables, so that you can start fresh. **************************************************************************** **************************************************************************** -8. Set up a cron job or scheduled task to run the poller once every 5-10 +8. Set up a cron job or scheduled task to run the poller once every 10-15 minutes to pick up the recent "public" postings of your friends. Example: cd /base/directory; /path/to/php include/poller.php @@ -201,10 +211,11 @@ You can generally find the location of PHP by executing "which php". If you have troubles with this section please contact your hosting provider for assistance. Hubzilla will not work correctly if you cannot perform this step. -You should also be sure that $a->config['system']['php_path'] is set correctly, -it should look like (changing it to the correct PHP location) +You should also be sure that $a->config['system']['php_path'] is set correctly +in your .htconfig.php file, it should look like (changing it to the correct +PHP location): -$a->config['system']['php_path'] = '/usr/local/php53/bin/php'; +$a->config['system']['php_path'] = '/usr/local/php55/bin/php'; ##################################################################### @@ -221,7 +232,7 @@ $a->config['system']['php_path'] = '/usr/local/php53/bin/php'; Check your database settings. It usually means your database could not be opened or accessed. If the database resides on the same machine, check that -the database server name is the word "localhost". +the database server name is "127.0.0.1" or the word "localhost". ##################################################################### - 500 Internal Error @@ -296,7 +307,7 @@ Retry the installation. As soon as the database has been created, ##################################################################### - Some configurations with "suhosin" security are configured without -an ability to run external processes. The Hubzilla requires this ability. +an ability to run external processes. Hubzilla requires this ability. Following are some notes provided by one of our members. ##################################################################### @@ -386,4 +397,5 @@ MaxRequestWorkers to 70. Here you can read more about Apache performance tuning: https://httpd.apache.org/docs/2.4/misc/perf-tuning.html -There are tons of scripts to help you with fine-tuning your Apache installation. Just search with your favorite search engine 'apache fine-tuning script'. +There are tons of scripts to help you with fine-tuning your Apache installation. +Just search with your favorite search engine 'apache fine-tuning script'. diff --git a/mod/admin.php b/mod/admin.php index c175a4a61..2d859399b 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -1092,6 +1092,23 @@ function admin_page_plugins(&$a){ return ''; } + $enabled = in_array($plugin,$a->plugins); + $info = get_plugin_info($plugin); + $x = check_plugin_versions($info); + + // disable plugins which are installed but incompatible versions + + if($enabled && ! $x) { + $enabled = false; + $idz = array_search($plugin, $a->plugins); + if ($idz !== false) { + unset($a->plugins[$idz]); + uninstall_plugin($plugin); + set_config("system","addon", implode(", ",$a->plugins)); + } + } + $info['disabled'] = 1-intval($x); + if (x($_GET,"a") && $_GET['a']=="t"){ check_form_security_token_redirectOnErr('/admin/plugins', 'admin_plugins', 't'); @@ -1142,6 +1159,7 @@ function admin_page_plugins(&$a){ } } + $t = get_markup_template('admin_plugins_details.tpl'); return replace_macros($t, array( '$title' => t('Administration'), @@ -1153,9 +1171,14 @@ function admin_page_plugins(&$a){ '$plugin' => $plugin, '$status' => $status, '$action' => $action, - '$info' => get_plugin_info($plugin), + '$info' => $info, '$str_author' => t('Author: '), '$str_maintainer' => t('Maintainer: '), + '$str_minversion' => t('Minimum project version: '), + '$str_maxversion' => t('Maximum project version: '), + '$str_minphpversion' => t('Minimum PHP version: '), + + '$disabled' => t('Disabled - version incompatibility'), '$admin_form' => $admin_form, '$function' => 'plugins', @@ -1177,7 +1200,23 @@ function admin_page_plugins(&$a){ if (is_dir($file)){ list($tmp, $id) = array_map('trim', explode('/', $file)); $info = get_plugin_info($id); - $plugins[] = array( $id, (in_array($id, $a->plugins)?"on":"off") , $info); + $enabled = in_array($id,$a->plugins); + $x = check_plugin_versions($info); + + // disable plugins which are installed but incompatible versions + + if($enabled && ! $x) { + $enabled = false; + $idz = array_search($id, $a->plugins); + if ($idz !== false) { + unset($a->plugins[$idz]); + uninstall_plugin($id); + set_config("system","addon", implode(", ",$a->plugins)); + } + } + $info['disabled'] = 1-intval($x); + + $plugins[] = array( $id, (($enabled)?"on":"off") , $info); } } } @@ -1190,6 +1229,7 @@ function admin_page_plugins(&$a){ '$baseurl' => $a->get_baseurl(true), '$function' => 'plugins', '$plugins' => $plugins, + '$disabled' => t('Disabled - version incompatibility'), '$form_security_token' => get_form_security_token('admin_plugins'), )); } diff --git a/mod/connections.php b/mod/connections.php index 2060ca85e..915d14b77 100644 --- a/mod/connections.php +++ b/mod/connections.php @@ -121,53 +121,60 @@ function connections_content(&$a) { $search = ((x($_REQUEST,'search')) ? notags(trim($_REQUEST['search'])) : ''); $tabs = array( + /* array( 'label' => t('Suggestions'), 'url' => z_root() . '/suggest', 'sel' => '', 'title' => t('Suggest new connections'), ), - array( + */ + + 'pending' => array( 'label' => t('New Connections'), 'url' => z_root() . '/connections/pending', 'sel' => ($pending) ? 'active' : '', 'title' => t('Show pending (new) connections'), ), - array( + + 'all' => array( 'label' => t('All Connections'), 'url' => z_root() . '/connections/all', 'sel' => ($all) ? 'active' : '', 'title' => t('Show all connections'), ), + + /* array( 'label' => t('Unblocked'), 'url' => z_root() . '/connections', 'sel' => (($unblocked) && (! $search) && (! $nets)) ? 'active' : '', 'title' => t('Only show unblocked connections'), ), + */ - array( + 'blocked' => array( 'label' => t('Blocked'), 'url' => z_root() . '/connections/blocked', 'sel' => ($blocked) ? 'active' : '', 'title' => t('Only show blocked connections'), ), - array( + 'ignored' => array( 'label' => t('Ignored'), 'url' => z_root() . '/connections/ignored', 'sel' => ($ignored) ? 'active' : '', 'title' => t('Only show ignored connections'), ), - array( + 'archived' => array( 'label' => t('Archived'), 'url' => z_root() . '/connections/archived', 'sel' => ($archived) ? 'active' : '', 'title' => t('Only show archived connections'), ), - array( + 'hidden' => array( 'label' => t('Hidden'), 'url' => z_root() . '/connections/hidden', 'sel' => ($hidden) ? 'active' : '', @@ -184,8 +191,8 @@ function connections_content(&$a) { ); - $tab_tpl = get_markup_template('common_tabs.tpl'); - $t = replace_macros($tab_tpl, array('$tabs'=>$tabs)); + //$tab_tpl = get_markup_template('common_tabs.tpl'); + //$t = replace_macros($tab_tpl, array('$tabs'=>$tabs)); $searching = false; if($search) { @@ -224,6 +231,7 @@ function connections_content(&$a) { $contacts[] = array( 'img_hover' => sprintf( t('%1$s [%2$s]'),$rr['xchan_name'],$rr['xchan_url']), 'edit_hover' => t('Edit connection'), + 'delete_hover' => t('Delete connection'), 'id' => $rr['abook_id'], 'alt_text' => $alt_text, 'dir_icon' => $dir_icon, @@ -232,7 +240,9 @@ function connections_content(&$a) { 'username' => $rr['xchan_name'], 'classes' => (intval($rr['abook_archived']) ? 'archived' : ''), 'link' => z_root() . '/connedit/' . $rr['abook_id'], + 'deletelink' => z_root() . '/connedit/' . $rr['abook_id'] . '/drop', 'edit' => t('Edit'), + 'delete' => t('Delete'), 'url' => chanlink_url($rr['xchan_url']), 'network' => network_to_name($rr['network']), ); @@ -257,12 +267,13 @@ function connections_content(&$a) { else { $o .= ""; $o .= replace_macros(get_markup_template('connections.tpl'),array( - '$header' => t('Connections') . (($head) ? ' - ' . $head : ''), - '$tabs' => $t, + '$header' => t('Connections') . (($head) ? ': ' . $head : ''), + '$tabs' => $tabs, '$total' => $total, '$search' => $search_hdr, + '$label' => t('Search'), '$desc' => t('Search your connections'), - '$finding' => (($searching) ? t('Finding: ') . "'" . $search . "'" : ""), + '$finding' => (($searching) ? t('Connections search') . ": '" . $search . "'" : ""), '$submit' => t('Find'), '$edit' => t('Edit'), '$cmd' => $a->cmd, diff --git a/mod/cover_photo.php b/mod/cover_photo.php new file mode 100644 index 000000000..fd7d794f5 --- /dev/null +++ b/mod/cover_photo.php @@ -0,0 +1,391 @@ +get_channel(); + profile_load($a,$channel['channel_address']); + +} + +/* @brief Evaluate posted values + * + * @param $a Current application + * @return void + * + */ + +function cover_photo_post(&$a) { + + if(! local_channel()) { + return; + } + + check_form_security_token_redirectOnErr('/cover_photo', 'cover_photo'); + + if((x($_POST,'cropfinal')) && ($_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 = $_POST['xstart']; + $srcY = $_POST['ystart']; + $srcW = $_POST['xfinal'] - $srcX; + $srcH = $_POST['yfinal'] - $srcY; + + $r = q("SELECT * FROM photo WHERE resource_id = '%s' AND uid = %d AND scale = 0 LIMIT 1", + dbesc($image_id), + intval(local_channel()) + ); + + if($r) { + + $base_image = $r[0]; + $base_image['data'] = (($r[0]['os_storage']) ? @file_get_contents($base_image['data']) : dbunescbin($base_image['data'])); + + $im = photo_factory($base_image['data'], $base_image['type']); + if($im->is_valid()) { + + $g = q("select width, height from photo where resource_id = '%s' and uid = %d and scale = 3", + dbesc($image_id), + intval(local_channel()) + ); + + // scale these numbers to the original photo instead of the scaled photo we operated on + + $scaled_width = $g[0]['width']; + $scaled_height = $g[0]['height']; + + if((! $scaled_width) || (! $scaled_height)) { + logger('potential divide by zero scaling cover photo'); + return; + } + + $orig_srcx = ( $r[0]['width'] / $scaled_width ) * $srcX; + $orig_srcy = ( $r[0]['height'] / $scaled_height ) * $srcY; + $orig_srcw = ( $srcW / $scaled_width ) * $r[0]['width']; + $orig_srch = ( $srcH / $scaled_height ) * $r[0]['height']; + + $im->cropImageRect(1200,435,$orig_srcx, $orig_srcy, $orig_srcw, $orig_srch); + + $aid = get_account_id(); + + $p = array('aid' => $aid, 'uid' => local_channel(), 'resource_id' => $base_image['resource_id'], + 'filename' => $base_image['filename'], 'album' => t('Profile Photos')); + + $p['scale'] = 7; + $p['photo_usage'] = PHOTO_COVER; + + $r1 = $im->save($p); + + $im->doScaleImage(850,310); + $p['scale'] = 8; + + $r2 = $im->save($p); + + if($r1 === false || $r2 === 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 scale >= 7 ", + dbesc($base_image['resource_id']), + local_channel() + ); + return; + } + + $channel = $a->get_channel(); + + + } + else + notice( t('Unable to process image') . EOL); + } + + goaway($a->get_baseurl() . '/profiles'); + return; // NOTREACHED + } + + + $hash = photo_new_resource(); + $smallest = 0; + + require_once('include/attach.php'); + + $res = attach_store($a->get_channel(), get_observer_hash(), '', array('album' => t('Profile Photos'), 'hash' => $hash)); + + logger('attach_store: ' . print_r($res,true)); + + if($res && intval($res['data']['is_photo'])) { + $i = q("select * from photo where resource_id = '%s' and uid = %d and scale = 0", + dbesc($hash), + intval(local_channel()) + ); + + if(! $i) { + notice( t('Image upload failed.') . EOL ); + return; + } + $os_storage = false; + + foreach($i as $ii) { + $smallest = intval($ii['scale']); + $os_storage = intval($ii['os_storage']); + $imagedata = $ii['data']; + $filetype = $ii['type']; + + } + } + + $imagedata = (($os_storage) ? @file_get_contents($imagedata) : $imagedata); + $ph = photo_factory($imagedata, $filetype); + + if(! $ph->is_valid()) { + notice( t('Unable to process image.') . EOL ); + return; + } + + return cover_photo_crop_ui_head($a, $ph, $hash, $smallest); + +} + +function send_cover_photo_activity($channel,$photo,$profile) { + + // for now only create activities for the default profile + + if(! intval($profile['is_default'])) + return; + + $arr = array(); + $arr['item_thread_top'] = 1; + $arr['item_origin'] = 1; + $arr['item_wall'] = 1; + $arr['obj_type'] = ACTIVITY_OBJ_PHOTO; + $arr['verb'] = ACTIVITY_UPDATE; + + $arr['object'] = json_encode(array( + 'type' => $arr['obj_type'], + 'id' => z_root() . '/photo/profile/l/' . $channel['channel_id'], + 'link' => array('rel' => 'photo', 'type' => $photo['type'], 'href' => z_root() . '/photo/profile/l/' . $channel['channel_id']) + )); + + if(stripos($profile['gender'],t('female')) !== false) + $t = t('%1$s updated her %2$s'); + elseif(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('profile photo') . '[/zrl]'; + + $ltext = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . '[zmg=150x150]' . z_root() . '/photo/' . $photo['resource_id'] . '-4[/zmg][/zrl]'; + + $arr['body'] = sprintf($t,$channel['channel_name'],$ptext) . "\n\n" . $ltext; + + $acl = new AccessList($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 + * + * @param $a Current application + * @return void + * + */ + + +function cover_photo_content(&$a) { + + if(! local_channel()) { + notice( t('Permission denied.') . EOL ); + return; + } + + $channel = $a->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, scale FROM photo WHERE uid = %d AND resource_id = '%s' ORDER BY scale ASC", + intval(local_channel()), + dbesc($resource_id) + ); + if(! $r) { + notice( t('Photo not available.') . EOL ); + return; + } + $havescale = false; + foreach($r as $rr) { + if($rr['scale'] == 7) + $havescale = true; + } + + $r = q("SELECT `data`, `type`, 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($r[0]['data']); + else + $data = dbunescbin($r[0]['data']); + + $ph = photo_factory($data, $r[0]['type']); + $smallest = 0; + if($ph->is_valid()) { + // go ahead as if we have just uploaded a new photo to crop + $i = q("select resource_id, scale from photo where resource_id = '%s' and uid = %d and scale = 0", + dbesc($r[0]['resource_id']), + intval(local_channel()) + ); + + if($i) { + $hash = $i[0]['resource_id']; + foreach($i as $ii) { + $smallest = intval($ii['scale']); + } + } + } + + cover_photo_crop_ui_head($a, $ph, $hash, $smallest); + } + + + if(! x($a->data,'imagecrop')) { + + $tpl = get_markup_template('cover_photo.tpl'); + + $o .= replace_macros($tpl,array( + '$user' => $a->channel['channel_address'], + '$lbl_upfile' => t('Upload File:'), + '$lbl_profiles' => t('Select a profile:'), + '$title' => t('Upload Cover Photo'), + '$submit' => t('Upload'), + '$profiles' => $profiles, + '$form_security_token' => get_form_security_token("cover_photo"), +// FIXME - yuk + '$select' => sprintf('%s %s', t('or'), ($newuser) ? '' . t('skip this step') . '' : '' . t('select a photo from your photo albums') . '') + )); + + call_hooks('cover_photo_content_end', $o); + + return $o; + } + else { + $filename = $a->data['imagecrop'] . '-3'; + $resolution = 3; + $tpl = get_markup_template("cropcover.tpl"); + $o .= replace_macros($tpl,array( + '$filename' => $filename, + '$profile' => intval($_REQUEST['profile']), + '$resource' => $a->data['imagecrop'] . '-3', + '$image_url' => $a->get_baseurl() . '/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; + } + + return; // NOTREACHED +} + +/* @brief Generate the UI for photo-cropping + * + * @param $a Current application + * @param $ph Photo-Factory + * @return void + * + */ + + + +function cover_photo_crop_ui_head(&$a, $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(); + } + + + $a->data['imagecrop'] = $hash; + $a->data['imagecrop_resolution'] = $smallest; + $a->page['htmlhead'] .= replace_macros(get_markup_template("crophead.tpl"), array()); + return; +} + diff --git a/mod/profile_photo.php b/mod/profile_photo.php index 2884505f0..0091d0585 100644 --- a/mod/profile_photo.php +++ b/mod/profile_photo.php @@ -502,8 +502,8 @@ function profile_photo_crop_ui_head(&$a, $ph, $hash, $smallest){ $width = $ph->getWidth(); $height = $ph->getHeight(); - if($width < 300 || $height < 300) { - $ph->scaleImageUp(200); + if($width < 500 || $height < 500) { + $ph->scaleImageUp(400); $width = $ph->getWidth(); $height = $ph->getHeight(); } diff --git a/mod/register.php b/mod/register.php index 2e9967c60..853728e10 100644 --- a/mod/register.php +++ b/mod/register.php @@ -1,5 +1,6 @@ t('Membership on this site is by invitation only.'), '$label_invite' => t('Please enter your invitation code'), '$invite_code' => $invite_code, - + '$auto_create' => $auto_create, + '$label_name' => t('Channel Name'), + '$help_name' => t('Enter your name'), + '$label_nick' => t('Choose a short nickname'), + '$nick_desc' => t('Your nickname will be used to create an easily remembered channel address (like an email address) which you can share with others.'), + '$name' => $name, + '$help_role' => t('Please choose a channel type (such as social networking or community forum) and privacy requirements so we can select the best permissions for you'), + '$role' => array('permissions_role' , t('Channel Type'), ($privacy_role) ? $privacy_role : 'social', ''.t('Read more about roles').'',get_roles()), + '$default_role' => $default_role, + '$nickname' => $nickname, + '$submit' => t('Create'), '$label_email' => t('Your email address'), '$label_pass1' => t('Choose a password'), '$label_pass2' => t('Please re-enter your password'), diff --git a/mod/regmod.php b/mod/regmod.php index c0a75ef48..a1d300e56 100644 --- a/mod/regmod.php +++ b/mod/regmod.php @@ -25,10 +25,10 @@ function regmod_content(&$a) { $hash = argv(2); if($cmd === 'deny') { - if (!user_deny($hash)) killme(); + if (! account_deny($hash)) killme(); } if($cmd === 'allow') { - if (!user_allow($hash)) killme(); + if (! account_allow($hash)) killme(); } } diff --git a/mod/regver.php b/mod/regver.php index c3ade2ee1..78f146ec5 100644 --- a/mod/regver.php +++ b/mod/regver.php @@ -13,10 +13,10 @@ function regver_content(&$a) { $hash = argv(2); if($cmd === 'deny') { - if (!user_deny($hash)) killme(); + if (! account_deny($hash)) killme(); } if($cmd === 'allow') { - if (!user_approve($hash)) killme(); + if (! account_approve($hash)) killme(); } } diff --git a/mod/share.php b/mod/share.php index 7ed6cf9a6..198a6b210 100644 --- a/mod/share.php +++ b/mod/share.php @@ -45,7 +45,7 @@ function share_init(&$a) { $is_photo = (($r[0]['obj_type'] === ACTIVITY_OBJ_PHOTO) ? true : false); if($is_photo) { $object = json_decode($r[0]['object'],true); - $photo_bb = $object['bbcode']; + $photo_bb = $object['body']; } if (strpos($r[0]['body'], "[/share]") !== false) { diff --git a/util/addons b/util/addons new file mode 100755 index 000000000..2c128c50e --- /dev/null +++ b/util/addons @@ -0,0 +1,132 @@ +#!/usr/bin/env php +plugins = $plugins_arr; + + $plugins = array(); + $files = glob('addon/*/'); + if($files) { + foreach($files as $file) { + if(is_dir($file)){ + list($tmp, $id) = array_map('trim', explode('/', $file)); + $info = get_plugin_info($id); + $enabled = in_array($id,$a->plugins); + $x = check_plugin_versions($info); + if($enabled && ! $x) { + $enabled = false; + $idz = array_search($id, $a->plugins); + if ($idz !== false) { + unset($a->plugins[$idz]); + uninstall_plugin($id); + set_config("system","addon", implode(", ",$a->plugins)); + } + } + $info['disabled'] = 1-intval($x); + + $plugins[] = array( $id, (($enabled)? '*' : '') , $info); + } + } + } + +if($argc == 1) { + usage(); + killme(); +} + + +if($argc == 2 && $argv[1] === 'list') { + if($plugins) { + foreach($plugins as $p) { + if($p[1]) { + echo $p[0] . "\n"; + } + } + } + killme(); +} + +if($argc == 3 && $argv[1] === 'list' && $argv[2] === 'all') { + + if($plugins) { + foreach($plugins as $p) { + echo $p[0] . (($p[1]) ? $p[1] : (($p[2]['disabled']) ? '!' : '')) . "\n"; + } + } + + killme(); +} + + +if($argc == 3 && $argv[1] === 'install') { + + if($plugins) { + foreach($plugins as $p) { + if($p[0] === $argv[2]) { + if($p[1]) + echo $p[0] . ' already installed.' . "\n"; + elseif($p[2]['disabled']) + echo $p[0] . ' disabled (version compatibility).' . "\n"; + else { + $a->plugins[] = $p[0]; + install_plugin($p[0]); + set_config("system","addon", implode(", ",$a->plugins)); + echo $p[0] . ' installed.' . "\n"; + } + } + } + } + + killme(); +} + + + +if($argc == 3 && $argv[1] === 'uninstall') { + + if($plugins) { + foreach($plugins as $p) { + if($p[0] === $argv[2]) { + if(! $p[1]) + echo $p[0] . ' not installed.' . "\n"; + elseif($p[2]['disabled']) + echo $p[0] . ' disabled (version compatibility).' . "\n"; + else { + $idx = array_search($p[0], $a->plugins); + if ($idx !== false) + unset($a->plugins[$idx]); + uninstall_plugin($p[0]); + set_config("system","addon", implode(", ",$a->plugins)); + echo $p[0] . ' uninstalled.' . "\n"; + } + } + } + } + + killme(); +} + + diff --git a/util/hz b/util/hz new file mode 100755 index 000000000..baa5bfb55 --- /dev/null +++ b/util/hz @@ -0,0 +1,36 @@ +#!/bin/bash + +# Simple, minimalist command line tool to post status to hubzilla via the API. Requires curl. +# Put it in your path, and sneeze your statuses to the zot network from your shell. + +CONF=${HOME}/.hubzilla + +usage () { +echo "usage: hz [conffile]" +echo "Create a conf file, either in .hubzilla in your home directory, or supplied as an arg" +echo " USER=youruserame " +echo " PASS=yourpass" +echo " HUB=your.hub.domain.org" +echo +echo "Type \"hz\" (with or without a conf file as an arg), then enter your message. Hit ENTER to send." + +} + +CUR=`which curl` + +[ "$CUR" ] || { echo "curl is not installed or on your path"; usage; exit 1; } + +[ "$1" ] && CONF="$1" + + +. ${CONF} + +[ "$USER" ] || { echo "no USER"; usage; exit 1; } +[ "$PASS" ] || { echo "no PASS"; usage; exit 1; } +[ "$HUB" ] || { echo "no HUB"; usage; exit 1; } + +echo "enter your message to be posted as $USER @ $HUB, then hit ENTER to send:" + +(read MSG; curl -ssl -u${USER}:${PASS} --data-urlencode "status=${MSG}" https://${HUB}/api/statuses/update ) + + diff --git a/version.inc b/version.inc index 3641bf5e9..a2d8d6da6 100644 --- a/version.inc +++ b/version.inc @@ -1 +1 @@ -2016-01-10.1274H +2016-01-13.1277H diff --git a/view/css/mod_connections.css b/view/css/mod_connections.css index d5c9b3d4f..0f35c5dbf 100644 --- a/view/css/mod_connections.css +++ b/view/css/mod_connections.css @@ -1,151 +1,18 @@ -.search-input { - padding: 4px 12px; - margin: 3px; +#contacts-search-form { + display: none; } -.field_abook_help { - color: #000; -} -.abook-them { - margin-left: 375px; - margin-bottom: 15px; -} -.abook-me { - margin-left: 36px; - margin-bottom: 15px; -} -.acheckbox { - margin-bottom: 5px !important; +.section-subtitle-wrapper .btn-xs { + margin-top: -2px; } -.abook-pending-contact { - background: orange; - font-weight: bold; - margin: 10px; - padding: 20px 5px 10px; +.contact-search { + height: 22px; + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; } -#contact-slider { - width: 600px !important; -} - -.abook-edit-them, .abook-edit-me { - float: left; - width: 100px !important; -} -.field_abook_help { - float: left; -} - -#contacts-main { - margin-top: 20px; - margin-bottom: 20px; -} - - - -#contact-edit-wrapper { - margin-top: 10px; -} - -#contact-edit-banner-name { - font-size: 1.4em; - font-weight: bold; -} - -#contact-edit-poll-wrapper { - margin-top: 15px; -} - -#contact-edit-poll-text { - margin-top: 15px; - margin-bottom: 5px; -} - -#contact-edit-update-now { - margin-top: 15px; -} - -#contact-edit-links{ - clear: both; -} - -#contact-edit-links ul { - list-style: none; - list-style-type: none; - margin-left: 0px; - padding-left: 0px; -} - -#contact-edit-links li { - margin-top: 5px; -} - -#contact-edit-drop-link { - float: right; - margin-right: 20px; -} - -#contact-edit-nav-end { - clear: both; -} - -#contact-edit-wrapper { - width: 100%; -} - -#contact-edit-end { - clear: both; - margin-top: 15px; -} - -#contact-profile-selector { - width: 175px; - margin-left: 175px; -} - -.contact-edit-submit { - margin-top: 20px; -} - -.contact-entry-wrapper { - float: left; - width: auto; - height: auto; - padding: 10px; - margin: 8px 10px 0 0; - border-top: 1px solid #eee; - border-left: 2px solid #eee; -} - -#contacts-search { - font-size: 1em; - width: 300px; -} - -#contacts-search-end { - margin-bottom: 10px; -} - -.contact-entry-photo-end { - clear: both; -} - -.contact-entry-name { - float: left; - margin-left: 0px; - margin-right: 10px; - margin-bottom: 10px; - width: 147px; - height: auto; - overflow: hidden; - white-space: nowrap; -} - -.contact-entry-name-end { - clear:both; -} - -.contact-entry-end { - clear: both; +.directory-photo-img.archived { + opacity: 0.3; + filter:alpha(opacity=30); } diff --git a/view/css/mod_mail.css b/view/css/mod_mail.css index 843d1274c..c1874edac 100644 --- a/view/css/mod_mail.css +++ b/view/css/mod_mail.css @@ -26,7 +26,7 @@ } #prvmail-text { - height: 20.0em; + height: 15.0em; } .mail-conv-outside-wrapper { diff --git a/view/css/mod_register.css b/view/css/mod_register.css index 618b7ce95..890a3f76b 100644 --- a/view/css/mod_register.css +++ b/view/css/mod_register.css @@ -42,3 +42,40 @@ h2 { margin-bottom: 20px; } +#newchannel-form { + font-size: 1.4em; + margin-left: 15%; + margin-top: 20px; + width: 50%; +} + +#newchannel-form .descriptive-paragraph { + color: #888; + margin-left: 20px; + margin-bottom: 25px; +} + +.newchannel-label { + float: left; + width: 275px; +} + +.newchannel-role-morehelp { + float: left; + width: 32px; +} +.newchannel-input { + float: left; + width: 275px; + padding: 5px; +} + +.newchannel-feedback { + float: left; + margin-left: 5px; +} + +.newchannel-field-end { + clear: both; + margin-bottom: 20px; +} diff --git a/view/css/widgets.css b/view/css/widgets.css index f1c1436c7..a1801ac77 100644 --- a/view/css/widgets.css +++ b/view/css/widgets.css @@ -23,17 +23,6 @@ margin-top: 10px; } -/* follow */ - -#side-follow-url { - margin-top: 5px; - width: 100%; -} - -#side-follow-submit { - margin-top: 15px; -} - /* notes */ #note-text { diff --git a/view/it/hmessages.po b/view/it/hmessages.po index 5ce6260bf..0a8b1a2fe 100644 --- a/view/it/hmessages.po +++ b/view/it/hmessages.po @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: Redmatrix\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-01-01 00:03-0800\n" -"PO-Revision-Date: 2016-01-03 12:40+0000\n" +"POT-Creation-Date: 2016-01-08 00:03-0800\n" +"PO-Revision-Date: 2016-01-10 21:09+0000\n" "Last-Translator: Paolo Wave{{$info.name}} - {{$info.version}} : {{$action}}
+ +{{if ! $info.disabled}}{{else}}{{/if}} {{$info.name}} - {{$info.version}}{{if ! $info.disabled}} : {{$action}}{{/if}}
+ + {{if $info.disabled}} +{{$disabled}}
+ {{/if}} +{{$info.description}}
{{foreach $info.author as $a}} @@ -10,6 +15,16 @@ {{/foreach}} + {{if $info.minversion}} +{{$str_minversion}}{{$info.minversion}}
+ {{/if}} + {{if $info.maxversion}} +{{$str_maxversion}}{{$info.maxversion}}
+ {{/if}} + {{if $info.minphpversion}} +{{$str_minphpversion}}{{$info.minphpversion}}
+ {{/if}} + {{foreach $info.maintainer as $a}}{{$str_maintainer}} diff --git a/view/tpl/connection_template.tpl b/view/tpl/connection_template.tpl index 35e97f3e9..aca6aa991 100755 --- a/view/tpl/connection_template.tpl +++ b/view/tpl/connection_template.tpl @@ -1,10 +1,15 @@ -
+{{$desc}} +
+