diff options
author | Chris Case <kahotep@bunda.dreamhost.com> | 2011-05-21 21:40:16 -0700 |
---|---|---|
committer | Chris Case <kahotep@bunda.dreamhost.com> | 2011-05-21 21:40:16 -0700 |
commit | 4cff911939b263993eb41682ca558c975e2db01f (patch) | |
tree | 78f58e08d04413827744689d0f5df7660bee6caa /include | |
parent | 2cf696d0b5d647e1741d2f94ee379aa19b25ae1b (diff) | |
parent | f3f063c0dd7fd8b706987b856d79c7b58924acbb (diff) | |
download | volse-hubzilla-4cff911939b263993eb41682ca558c975e2db01f.tar.gz volse-hubzilla-4cff911939b263993eb41682ca558c975e2db01f.tar.bz2 volse-hubzilla-4cff911939b263993eb41682ca558c975e2db01f.zip |
merged multipart email changes
Diffstat (limited to 'include')
-rw-r--r-- | include/Contact.php | 5 | ||||
-rw-r--r-- | include/Photo.php | 12 | ||||
-rw-r--r-- | include/Scrape.php | 288 | ||||
-rw-r--r-- | include/acl_selectors.php | 13 | ||||
-rw-r--r-- | include/api.php | 431 | ||||
-rw-r--r-- | include/auth.php | 21 | ||||
-rw-r--r-- | include/bbcode.php | 17 | ||||
-rw-r--r-- | include/conversation.php | 755 | ||||
-rw-r--r-- | include/dba.php | 19 | ||||
-rw-r--r-- | include/email.php | 201 | ||||
-rw-r--r-- | include/expire.php | 44 | ||||
-rw-r--r-- | include/fcontact.php | 41 | ||||
-rw-r--r-- | include/group.php | 24 | ||||
-rw-r--r-- | include/hostxrd.php | 4 | ||||
-rw-r--r-- | include/items.php | 288 | ||||
-rw-r--r-- | include/main.js | 103 | ||||
-rw-r--r-- | include/nav.php | 68 | ||||
-rw-r--r-- | include/notifier.php | 137 | ||||
-rw-r--r-- | include/pgettext.php | 46 | ||||
-rw-r--r-- | include/poller.php | 217 | ||||
-rw-r--r-- | include/profile_advanced.php | 258 | ||||
-rw-r--r-- | include/salmon.php | 2 | ||||
-rw-r--r-- | include/security.php | 2 | ||||
-rw-r--r-- | include/template_processor.php | 137 |
24 files changed, 2973 insertions, 160 deletions
diff --git a/include/Contact.php b/include/Contact.php index 7cac3c0e0..4ca77d065 100644 --- a/include/Contact.php +++ b/include/Contact.php @@ -14,11 +14,13 @@ function user_remove($uid) { q("DELETE FROM `group` WHERE `uid` = %d", intval($uid)); q("DELETE FROM `group_member` WHERE `uid` = %d", intval($uid)); q("DELETE FROM `intro` WHERE `uid` = %d", intval($uid)); + q("DELETE FROM `event` WHERE `uid` = %d", intval($uid)); q("DELETE FROM `item` WHERE `uid` = %d", intval($uid)); q("DELETE FROM `mail` WHERE `uid` = %d", intval($uid)); q("DELETE FROM `photo` WHERE `uid` = %d", intval($uid)); q("DELETE FROM `profile` WHERE `uid` = %d", intval($uid)); q("DELETE FROM `profile_check` WHERE `uid` = %d", intval($uid)); + q("DELETE FROM `pconfig` WHERE `uid` = %d", intval($uid)); q("DELETE FROM `user` WHERE `uid` = %d", intval($uid)); if($uid == local_user()) { unset($_SESSION['authenticated']); @@ -41,6 +43,9 @@ function contact_remove($id) { q("DELETE FROM `mail` WHERE `contact-id` = %d ", intval($id) ); + q("DELETE FROM `event` WHERE `cid` = %d ", + intval($id) + ); } diff --git a/include/Photo.php b/include/Photo.php index 9934b9a39..707b0de5d 100644 --- a/include/Photo.php +++ b/include/Photo.php @@ -162,12 +162,20 @@ class Photo { } public function saveImage($path) { - imagejpeg($this->image,$path,100); + $quality = get_config('system','jpeg_quality'); + if((! $quality) || ($quality > 100)) + $quality = JPEG_QUALITY; + imagejpeg($this->image,$path,$quality); } public function imageString() { ob_start(); - imagejpeg($this->image,NULL,100); + + $quality = get_config('system','jpeg_quality'); + if((! $quality) || ($quality > 100)) + $quality = JPEG_QUALITY; + + imagejpeg($this->image,NULL,$quality); $s = ob_get_contents(); ob_end_clean(); return $s; diff --git a/include/Scrape.php b/include/Scrape.php index ff9899252..7fc0c964b 100644 --- a/include/Scrape.php +++ b/include/Scrape.php @@ -216,7 +216,7 @@ function scrape_feed($url) { } if(stristr($line,'application/rss+xml') || stristr($s,'<rss')) { $ret['feed_rss'] = $url; - return ret; + return $ret; } } } @@ -227,17 +227,287 @@ function scrape_feed($url) { if(! $dom) return $ret; + + $items = $dom->getElementsByTagName('img'); + + // get img elements (twitter) + + if($items) { + foreach($items as $item) { + $x = $item->getAttribute('id'); + if($x === 'profile-image') { + $ret['photo'] = $item->getAttribute('src'); + } + } + } + + + $head = $dom->getElementsByTagName('base'); + if($head) { + foreach($head as $head0) { + $basename = $head0->getAttribute('href'); + break; + } + } + if(! $basename) + $basename = substr($url,0,strrpos($url,'/')) . '/'; + $items = $dom->getElementsByTagName('link'); - // get Atom link elements + // get Atom/RSS link elements, take the first one of either. - foreach($items as $item) { - $x = $item->getAttribute('rel'); - if(($x === 'alternate') && ($item->getAttribute('type') === 'application/atom+xml')) - $ret['feed_atom'] = $item->getAttribute('href'); - if(($x === 'alternate') && ($item->getAttribute('type') === 'application/rss+xml')) - $ret['feed_rss'] = $item->getAttribute('href'); + if($items) { + foreach($items as $item) { + $x = $item->getAttribute('rel'); + if(($x === 'alternate') && ($item->getAttribute('type') === 'application/atom+xml')) { + if(! x($ret,'feed_atom')) + $ret['feed_atom'] = $item->getAttribute('href'); + } + if(($x === 'alternate') && ($item->getAttribute('type') === 'application/rss+xml')) { + if(! x($ret,'feed_rss')) + $ret['feed_rss'] = $item->getAttribute('href'); + } + } } + // Drupal and perhaps others only provide relative URL's. Turn them into absolute. + + if(x($ret,'feed_atom') && (! strstr($ret['feed_atom'],'://'))) + $ret['feed_atom'] = $basename . $ret['feed_atom']; + if(x($ret,'feed_rss') && (! strstr($ret['feed_rss'],'://'))) + $ret['feed_rss'] = $basename . $ret['feed_rss']; + return $ret; -}}
\ No newline at end of file +}} + + +function probe_url($url) { + require_once('include/email.php'); + + $result = array(); + + if(! $url) + return $result; + + $diaspora = false; + $email_conversant = false; + + if($url) { + $links = lrdd($url); + + if(count($links)) { + logger('probe_url: found lrdd links: ' . print_r($links,true), LOGGER_DATA); + foreach($links as $link) { + if($link['@attributes']['rel'] === NAMESPACE_DFRN) + $dfrn = unamp($link['@attributes']['href']); + if($link['@attributes']['rel'] === 'salmon') + $notify = unamp($link['@attributes']['href']); + if($link['@attributes']['rel'] === NAMESPACE_FEED) + $poll = unamp($link['@attributes']['href']); + if($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') + $hcard = unamp($link['@attributes']['href']); + if($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') + $profile = unamp($link['@attributes']['href']); + if($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') + $diaspora = true; + } + + // Status.Net can have more than one profile URL. We need to match the profile URL + // to a contact on incoming messages to prevent spam, and we won't know which one + // to match. So in case of two, one of them is stored as an alias. Only store URL's + // and not webfinger user@host aliases. If they've got more than two non-email style + // aliases, let's hope we're lucky and get one that matches the feed author-uri because + // otherwise we're screwed. + + foreach($links as $link) { + if($link['@attributes']['rel'] === 'alias') { + if(strpos($link['@attributes']['href'],'@') === false) { + if(isset($profile)) { + if($link['@attributes']['href'] !== $profile) + $alias = unamp($link['@attributes']['href']); + } + else + $profile = unamp($link['@attributes']['href']); + } + } + } + } + else { + + // Check email + + $orig_url = $url; + if((strpos($orig_url,'@')) && validate_email($orig_url)) { + $x = q("SELECT `prvkey` FROM `user` WHERE `uid` = %d LIMIT 1", + intval(local_user()) + ); + $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d AND `server` != '' LIMIT 1", + intval(local_user()) + ); + if(count($x) && count($r)) { + $mailbox = construct_mailbox_name($r[0]); + $password = ''; + openssl_private_decrypt(hex2bin($r[0]['pass']),$password,$x[0]['prvkey']); + $mbox = email_connect($mailbox,$r[0]['user'],$password); + unset($password); + } + if($mbox) { + $msgs = email_poll($mbox,$orig_url); + if(count($msgs)) { + $addr = $orig_url; + $network = NETWORK_MAIL; + $name = substr($url,0,strpos($url,'@')); + $profile = 'http://' . substr($url,strpos($url,'@')+1); + // fix nick character range + $vcard = array('fn' => $name, 'nick' => $name, 'photo' => gravatar_img($url)); + $notify = 'smtp ' . random_string(); + $poll = 'email ' . random_string(); + $priority = 0; + $x = email_msg_meta($mbox,$msgs[0]); + if(stristr($x->from,$orig_url)) + $adr = imap_rfc822_parse_adrlist($x->from,''); + elseif(stristr($x->to,$orig_url)) + $adr = imap_rfc822_parse_adrlist($x->to,''); + if(isset($adr) && strlen($adr[0]->personal)) + $vcard['fn'] = notags($adr[0]->personal); + } + imap_close($mbox); + } + } + } + } + + if(strlen($dfrn)) { + $ret = scrape_dfrn($dfrn); + if(is_array($ret) && x($ret,'dfrn-request')) { + $network = NETWORK_DFRN; + $request = $ret['dfrn-request']; + $confirm = $ret['dfrn-confirm']; + $notify = $ret['dfrn-notify']; + $poll = $ret['dfrn-poll']; + } + } + + if($network !== NETWORK_DFRN && $network !== NETWORK_MAIL) { + $network = NETWORK_OSTATUS; + $priority = 0; + + if($hcard) { + $vcard = scrape_vcard($hcard); + + // Google doesn't use absolute url in profile photos + + if((x($vcard,'photo')) && substr($vcard['photo'],0,1) == '/') { + $h = @parse_url($hcard); + if($h) + $vcard['photo'] = $h['scheme'] . '://' . $h['host'] . $vcard['photo']; + } + + logger('probe_url: scrape_vcard: ' . print_r($vcard,true), LOGGER_DATA); + } + + if(! $profile) { + if($diaspora) + $profile = $hcard; + else + $profile = $url; + } + + if(! x($vcard,'fn')) + if(x($vcard,'nick')) + $vcard['fn'] = $vcard['nick']; + + if((! isset($vcard)) && (! $poll)) { + + $ret = scrape_feed($url); + logger('probe_url: scrape_feed returns: ' . print_r($ret,true), LOGGER_DATA); + if(count($ret) && ($ret['feed_atom'] || $ret['feed_rss'])) { + $poll = ((x($ret,'feed_atom')) ? unamp($ret['feed_atom']) : unamp($ret['feed_rss'])); + $vcard = array(); + if(x($ret,'photo')) + $vcard['photo'] = $ret['photo']; + require_once('simplepie/simplepie.inc'); + $feed = new SimplePie(); + $xml = fetch_url($poll); + + $feed->set_raw_data($xml); + + $feed->init(); + + if(! x($vcard,'photo')) + $vcard['photo'] = $feed->get_image_url(); + $author = $feed->get_author(); + if($author) { + $vcard['fn'] = unxmlify(trim($author->get_name())); + if(! $vcard['fn']) + $vcard['fn'] = trim(unxmlify($author->get_email())); + if(strpos($vcard['fn'],'@') !== false) + $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@')); + $vcard['nick'] = strtolower(notags(unxmlify($vcard['fn']))); + if(strpos($vcard['nick'],' ')) + $vcard['nick'] = trim(substr($vcard['nick'],0,strpos($vcard['nick'],' '))); + $email = unxmlify($author->get_email()); + } + else { + $item = $feed->get_item(0); + if($item) { + $author = $item->get_author(); + if($author) { + $vcard['fn'] = trim(unxmlify($author->get_name())); + if(! $vcard['fn']) + $vcard['fn'] = trim(unxmlify($author->get_email())); + if(strpos($vcard['fn'],'@') !== false) + $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@')); + $vcard['nick'] = strtolower(unxmlify($vcard['fn'])); + if(strpos($vcard['nick'],' ')) + $vcard['nick'] = trim(substr($vcard['nick'],0,strpos($vcard['nick'],' '))); + $email = unxmlify($author->get_email()); + } + if(! $vcard['photo']) { + $rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/','thumbnail'); + if($rawmedia && $rawmedia[0]['attribs']['']['url']) + $vcard['photo'] = unxmlify($rawmedia[0]['attribs']['']['url']); + } + } + } + if((! $vcard['photo']) && strlen($email)) + $vcard['photo'] = gravatar_img($email); + if($poll === $profile) + $lnk = $feed->get_permalink(); + if(isset($lnk) && strlen($lnk)) + $profile = $lnk; + if(! (x($vcard,'fn'))) + $vcard['fn'] = notags($feed->get_title()); + if(! (x($vcard,'fn'))) + $vcard['fn'] = notags($feed->get_description()); + $network = 'feed'; + $priority = 2; + } + } + } + + if(! x($vcard,'photo')) { + $a = get_app(); + $vcard['photo'] = $a->get_baseurl() . '/images/default-profile.jpg' ; + } + $vcard['fn'] = notags($vcard['fn']); + $vcard['nick'] = notags($vcard['nick']); + + + $result['name'] = $vcard['fn']; + $result['nick'] = $vcard['nick']; + $result['url'] = $profile; + $result['addr'] = $addr; + $result['notify'] = $notify; + $result['poll'] = $poll; + $result['request'] = $request; + $result['confirm'] = $confirm; + $result['photo'] = $vcard['photo']; + $result['priority'] = $priority; + $result['network'] = $network; + $result['alias'] = $alias; + + logger('probe_url: ' . print_r($result,true), LOGGER_DEBUG); + + return $result; +} diff --git a/include/acl_selectors.php b/include/acl_selectors.php index fa700818f..b1bcf5108 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -48,7 +48,7 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p $o = ''; - // When used for private messages, we limit correspondence to mutual friends and the selector + // When used for private messages, we limit correspondence to mutual DFRN/Friendika friends and the selector // to one recipient. By default our selector allows multiple selects amongst all contacts. $sql_extra = ''; @@ -57,9 +57,12 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p $sql_extra .= sprintf(" AND `rel` = %d ", intval(REL_BUD)); } - if($privmail || $privatenet) { + if($privmail) { $sql_extra .= " AND `network` IN ( 'dfrn' ) "; - } + } + elseif($privatenet) { + $sql_extra .= " AND `network` IN ( 'dfrn', 'mail', 'face' ) "; + } if($privmail) $o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"$size\" >\r\n"; @@ -80,8 +83,6 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p call_hooks($a->module . '_pre_' . $selname, $arr); - - if(count($r)) { foreach($r as $rr) { if((is_array($preselected)) && in_array($rr['id'], $preselected)) @@ -129,7 +130,7 @@ function populate_acl($user = null,$celeb = false) { $o = ''; $o .= '<div id="acl-wrapper">'; $o .= '<div id="acl-permit-outer-wrapper">'; - $o .= '<div id="acl-permit-text">' . t('Visible To:') . '</div>'; + $o .= '<div id="acl-permit-text">' . t('Visible To:') . '</div><div id="jot-public">' . t('everybody') . '</div>'; $o .= '<div id="acl-permit-text-end"></div>'; $o .= '<div id="acl-permit-wrapper">'; $o .= '<div id="group_allow_wrapper">'; diff --git a/include/api.php b/include/api.php new file mode 100644 index 000000000..84cb7b38f --- /dev/null +++ b/include/api.php @@ -0,0 +1,431 @@ +<?php + require_once("bbcode.php"); + require_once("datetime.php"); + + /* + * Twitter-Like API + * + */ + + $API = Array(); + + + + function api_date($str){ + //Wed May 23 06:01:13 +0000 2007 + return datetime_convert('UTC', 'UTC', $str, "D M d h:i:s +0000 Y" ); + } + + + function api_register_func($path, $func, $auth=false){ + global $API; + $API[$path] = array('func'=>$func, + 'auth'=>$auth); + } + + /** + * Simple HTTP Login + */ + function api_login(&$a){ + if (!isset($_SERVER['PHP_AUTH_USER'])) { + header('WWW-Authenticate: Basic realm="Friendika"'); + header('HTTP/1.0 401 Unauthorized'); + die('This api require login'); + } + + $user = $_SERVER['PHP_AUTH_USER']; + $encrypted = hash('whirlpool',trim($_SERVER['PHP_AUTH_PW'])); + + + /** + * next code from mod/auth.php. needs better solution + */ + + // process normal login request + + $r = q("SELECT * FROM `user` WHERE ( `email` = '%s' OR `nickname` = '%s' ) + AND `password` = '%s' AND `blocked` = 0 AND `verified` = 1 LIMIT 1", + dbesc(trim($user)), + dbesc(trim($user)), + dbesc($encrypted) + ); + if(count($r)){ + $record = $r[0]; + } else { + header('WWW-Authenticate: Basic realm="Friendika"'); + header('HTTP/1.0 401 Unauthorized'); + die('This api require login'); + } + $_SESSION['uid'] = $record['uid']; + $_SESSION['theme'] = $record['theme']; + $_SESSION['authenticated'] = 1; + $_SESSION['page_flags'] = $record['page-flags']; + $_SESSION['my_url'] = $a->get_baseurl() . '/profile/' . $record['nickname']; + $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; + + //notice( t("Welcome back ") . $record['username'] . EOL); + $a->user = $record; + + if(strlen($a->user['timezone'])) { + date_default_timezone_set($a->user['timezone']); + $a->timezone = $a->user['timezone']; + } + + $r = q("SELECT * FROM `contact` WHERE `uid` = %s AND `self` = 1 LIMIT 1", + intval($_SESSION['uid'])); + if(count($r)) { + $a->contact = $r[0]; + $a->cid = $r[0]['id']; + $_SESSION['cid'] = $a->cid; + } + q("UPDATE `user` SET `login_date` = '%s' WHERE `uid` = %d LIMIT 1", + dbesc(datetime_convert()), + intval($_SESSION['uid']) + ); + + call_hooks('logged_in', $a->user); + + header('X-Account-Management-Status: active; name="' . $a->user['username'] . '"; id="' . $a->user['nickname'] .'"'); + } + + /************************** + * MAIN API ENTRY POINT * + **************************/ + function api_call(&$a){ + GLOBAL $API; + foreach ($API as $p=>$info){ + if (strpos($a->query_string, $p)===0){ + #unset($_SERVER['PHP_AUTH_USER']); + if ($info['auth']===true && local_user()===false) { + api_login($a); + } + + $type="json"; + if (strpos($a->query_string, ".xml")>0) $type="xml"; + if (strpos($a->query_string, ".json")>0) $type="json"; + if (strpos($a->query_string, ".rss")>0) $type="rss"; + if (strpos($a->query_string, ".atom")>0) $type="atom"; + + $r = call_user_func($info['func'], $a, $type); + if ($r===false) return; + + switch($type){ + case "xml": + $r = mb_convert_encoding($r, "UTF-8",mb_detect_encoding($r)); + header ("Content-Type: text/xml"); + return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; + break; + case "json": + header ("Content-Type: application/json"); + return json_encode($r); + break; + case "rss": + header ("Content-Type: application/rss+xml"); + return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; + break; + case "atom": + #header ("Content-Type: application/atom+xml"); + return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; + break; + + } + //echo "<pre>"; var_dump($r); die(); + } + } + return false; + } + + /** + * RSS extra info + */ + function api_rss_extra(&$a, $arr, $user_info){ + if (is_null($user_info)) $user_info = api_get_user($a); + $arr['$rss'] = array( + 'alternate' => $user_info['url'], + 'self' => $a->get_baseurl(). "/". $a->query_string, + 'updated' => api_date(null), + 'language' => $user_info['language'], + 'logo' => $a->get_baseurl()."/images/friendika-32.png", + ); + + return $arr; + } + + /** + * Returns user info array. + */ + function api_get_user(&$a){ + $user = null; + $extra_query = ""; + if(x($_GET, 'user_id')) { + $user = intval($_GET['user_id']); + $extra_query = "AND `contact`.`id` = %d "; + } + if(x($_GET, 'screen_name')) { + $user = dbesc($_GET['screen_name']); + $extra_query = "AND `contact`.`nick` = '%s' "; + } + + if ($user===null){ + list($user, $null) = explode(".",$a->argv[3]); + if(is_numeric($user)){ + $user = intval($user); + $extra_query = "AND `contact`.`id` = %d "; + } else { + $user = dbesc($user); + $extra_query = "AND `contact`.`nick` = '%s' "; + } + } + + if ($user==='') { + if (local_user()===false) { + api_login($a); return False; + } else { + $user = $_SESSION['uid']; + $extra_query = "AND `user`.`uid` = %d "; + } + + } + + + // user info + $uinfo = q("SELECT *, `contact`.`id` as `cid` FROM `user`, `contact` + WHERE `user`.`uid`=`contact`.`uid` AND `contact`.`self`=1 + $extra_query", + $user + ); + if (count($uinfo)==0) { + return False; + } + + // count public wall messages + $r = q("SELECT COUNT(`id`) as `count` FROM `item` + WHERE `uid` = %d + AND `type`='wall' + AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`=''", + intval($uinfo[0]['uid']) + ); + $countitms = $r[0]['count']; + + // count friends + $r = q("SELECT COUNT(`id`) as `count` FROM `contact` + WHERE `uid` = %d + AND `self`=0 AND `blocked`=0", + intval($uinfo[0]['uid']) + ); + $countfriends = $r[0]['count']; + + + $ret = Array( + 'id' => $uinfo[0]['cid'], + 'name' => $uinfo[0]['username'], + 'screen_name' => $uinfo[0]['nickname'], + 'location' => $uinfo[0]['default-location'], + 'profile_image_url' => $uinfo[0]['micro'], + 'url' => $uinfo[0]['url'], + 'protected' => false, # + 'friends_count' => $countfriends, + 'created_at' => api_date($uinfo[0]['created']), + 'utc_offset' => 0, #XXX: fix me + 'time_zone' => $uinfo[0]['timezone'], + 'geo_enabled' => false, + 'statuses_count' => $countitms, #XXX: fix me + 'lang' => 'en', #XXX: fix me + 'description' => '', + 'followers_count' => $countfriends, #XXX: fix me + 'lang' => 'en', #XXX: fix me + 'favourites_count' => 0, + 'contributors_enabled' => false, + 'follow_request_sent' => false, + 'profile_background_color' => 'cfe8f6', + 'profile_text_color' => '000000', + 'profile_link_color' => 'FF8500', + 'profile_sidebar_fill_color' =>'AD0066', + 'profile_sidebar_border_color' => 'AD0066', + 'profile_background_image_url' => '', + 'profile_background_tile' => false, + 'profile_use_background_image' => false, + 'notifications' => false, + 'verified' => true, #XXX: fix me + 'followers' => '', #XXX: fix me + #'status' => null + ); + + return $ret; + + } + + /** + * apply xmlify() to all values of array $val, recursively + */ + function api_xmlify($val){ + if (is_bool($val)) return $val?"true":"false"; + if (is_array($val)) return array_map('api_xmlify', $val); + return xmlify($val); + } + + /** + * load api $templatename for $type and replace $data array + */ + function api_apply_template($templatename, $type, $data){ + switch($type){ + case "rss": + case "atom": + case "xml": + $data = api_xmlify($data); + $tpl = get_markup_template("api_".$templatename."_".$type.".tpl"); + $ret = replace_macros($tpl, $data); + break; + case "json": + $ret = $data; + break; + } + return $ret; + } + + /** + ** TWITTER API + */ + + /** + * Returns an HTTP 200 OK response code and a representation of the requesting user if authentication was successful; + * returns a 401 status code and an error message if not. + * http://developer.twitter.com/doc/get/account/verify_credentials + */ + function api_account_verify_credentials(&$a, $type){ + if (local_user()===false) return false; + $user_info = api_get_user($a); + + return api_apply_template("user", $type, array('$user' => $user_info)); + + } + api_register_func('api/account/verify_credentials','api_account_verify_credentials', true); + + + + /** + * Returns extended information of a given user, specified by ID or screen name as per the required id parameter. + * The author's most recent status will be returned inline. + * http://developer.twitter.com/doc/get/users/show + */ + function api_users_show(&$a, $type){ + $user_info = api_get_user($a); + // get last public wall message + $lastwall = q("SELECT `item`.*, `i`.`contact-id` as `reply_uid`, `i`.`nick` as `reply_author` + FROM `item`, `contact`, + (SELECT `item`.`id`, `item`.`contact-id`, `contact`.`nick` FROM `item`,`contact` WHERE `contact`.`id`=`item`.`contact-id`) as `i` + WHERE `item`.`contact-id` = %d + AND `i`.`id` = `item`.`parent` + AND `contact`.`id`=`item`.`contact-id` AND `contact`.`self`=1 + AND `type`!='activity' + AND `item`.`allow_cid`='' AND `item`.`allow_gid`='' AND `item`.`deny_cid`='' AND `item`.`deny_gid`='' + ORDER BY `created` DESC + LIMIT 1", + intval($user_info['id']) + ); + + if (count($lastwall)>0){ + $lastwall = $lastwall[0]; + + $in_reply_to_status_id = ''; + $in_reply_to_user_id = ''; + $in_reply_to_screen_name = ''; + if ($lastwall['parent']!=$lastwall['id']) { + $in_reply_to_status_id=$lastwall['parent']; + $in_reply_to_user_id = $lastwall['reply_uid']; + $in_reply_to_screen_name = $lastwall['reply_author']; + } + $user_info['status'] = array( + 'created_at' => api_date($lastwall['created']), + 'id' => $lastwall['contact-id'], + 'text' => strip_tags(bbcode($lastwall['body'])), + 'source' => 'web', + 'truncated' => false, + 'in_reply_to_status_id' => $in_reply_to_status_id, + 'in_reply_to_user_id' => $in_reply_to_user_id, + 'favorited' => false, + 'in_reply_to_screen_name' => $in_reply_to_screen_name, + 'geo' => '', + 'coordinates' => $lastwall['coord'], + 'place' => $lastwall['location'], + 'contributors' => '' + ); + } + return api_apply_template("user", $type, array('$user' => $user_info)); + + } + api_register_func('api/users/show','api_users_show'); + + /** + * + * http://developer.twitter.com/doc/get/statuses/home_timeline + * + * TODO: Optional parameters + * TODO: Add reply info + */ + function api_statuses_home_timeline(&$a, $type){ + if (local_user()===false) return false; + + $user_info = api_get_user($a); + + // get last newtork messages + $sql_extra = " AND `item`.`parent` IN ( SELECT `parent` FROM `item` WHERE `id` = `parent` ) "; + + $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, + `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, + `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, + `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` + FROM `item`, `contact`, `user` + WHERE `item`.`contact-id` = %d AND `user`.`uid` = `item`.`uid` + AND `item`.`visible` = 1 AND `item`.`deleted` = 0 + AND `contact`.`id` = `item`.`contact-id` + AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 + $sql_extra + ORDER BY `item`.`created` DESC LIMIT %d ,%d ", + intval($user_info['id']), + 0,20 + ); + $ret = Array(); + + foreach($r as $item) { + $status = array( + 'created_at'=> api_date($item['created']), + 'id' => $item['id'], + 'text' => strip_tags(bbcode($item['body'])), + 'html' => bbcode($item['body']), + 'source' => 'web', + 'url' => ($item['plink']!=''?$item['plink']:$item['author-link']), + 'truncated' => False, + 'in_reply_to_status_id' => ($item['parent']!=$item['id']?$item['id']:''), + 'in_reply_to_user_id' => '', + 'favorited' => false, + 'in_reply_to_screen_name' => '', + 'geo' => '', + 'coordinates' => $item['coord'], + 'place' => $item['location'], + 'contributors' => '', + 'annotations' => '', + 'entities' => '', + 'user' => $user_info, + 'objecttype' => $item['object-type'], + 'verb' => $item['verb'], + 'self' => $a->get_baseurl()."/api/statuses/show/".$ite['id'].".".$type, + 'edit' => $a->get_baseurl()."/api/statuses/show/".$ite['id'].".".$type, + ); + $ret[]=$status; + }; + + $data = array('$statuses' => $ret); + switch($type){ + case "atom": + case "rss": + $data = api_rss_extra($a, $data, $user_info); + } + + return api_apply_template("timeline", $type, $data); + } + api_register_func('api/statuses/home_timeline','api_statuses_home_timeline', true); + api_register_func('api/statuses/friends_timeline','api_statuses_home_timeline', true); + api_register_func('api/statuses/user_timeline','api_statuses_home_timeline', true); + # TODO: user_timeline should be profile view + diff --git a/include/auth.php b/include/auth.php index e8cee3918..1c430406e 100644 --- a/include/auth.php +++ b/include/auth.php @@ -28,6 +28,15 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p goaway($a->get_baseurl()); } + if(x($_SESSION,'visitor_id') && (! x($_SESSION,'uid'))) { + $r = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1", + intval($_SESSION['visitor_id']) + ); + if(count($r)) { + $a->contact = $r[0]; + } + } + if(x($_SESSION,'uid')) { // already logged in user returning @@ -191,9 +200,17 @@ else { $_SESSION['my_url'] = $a->get_baseurl() . '/profile/' . $record['nickname']; $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; - notice( t("Welcome back ") . $record['username'] . EOL); $a->user = $record; + if($a->user['login_date'] === '0000-00-00 00:00:00') { + $_SESSION['return_url'] = 'profile_photo/new'; + $a->module = 'profile_photo'; + notice( t("Welcome ") . $a->user['username'] . EOL); + notice( t('Please upload a profile photo.') . EOL); + } + else + notice( t("Welcome back ") . $a->user['username'] . EOL); + if(strlen($a->user['timezone'])) { date_default_timezone_set($a->user['timezone']); $a->timezone = $a->user['timezone']; @@ -214,6 +231,8 @@ else { $a->cid = $r[0]['id']; $_SESSION['cid'] = $a->cid; } + + q("UPDATE `user` SET `login_date` = '%s' WHERE `uid` = %d LIMIT 1", dbesc(datetime_convert()), intval($_SESSION['uid']) diff --git a/include/bbcode.php b/include/bbcode.php index 978b4af69..195fc9181 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -3,7 +3,7 @@ require_once("include/oembed.php"); // BBcode 2 HTML was written by WAY2WEB.net // extended to work with Mistpark/Friendika - Mike Macgirvin -function bbcode($Text) { +function bbcode($Text,$preserve_nl = false) { // Replace any html brackets with HTML Entities to prevent executing HTML or script // Don't use strip_tags here because it breaks [url] search by replacing & with amp @@ -12,7 +12,10 @@ function bbcode($Text) { $Text = str_replace(">", ">", $Text); // Convert new line chars to html <br /> tags + $Text = nl2br($Text); + if($preserve_nl) + $Text = str_replace(array("\n","\r"), array('',''),$Text); // Set up the parameters for a URL search string $URLSearchString = "^\[\]"; @@ -22,7 +25,7 @@ function bbcode($Text) { // Perform URL Search - $Text = preg_replace("/([^\]\=]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\.\=\_\~\#\'\%\$\!\+]+)/", ' <a href="$2" target="external-link">$2</a>', $Text); + $Text = preg_replace("/([^\]\=]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\.\=\_\~\#\'\%\$\!\+\,]+)/", ' <a href="$2" target="external-link">$2</a>', $Text); $Text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/", '<a href="$1" target="external-link">$1</a>', $Text); $Text = preg_replace("(\[url\=([$URLSearchString]*)\](.+?)\[/url\])", '<a href="$1" target="external-link">$2</a>', $Text); @@ -77,7 +80,7 @@ function bbcode($Text) { // Images // [img]pathtoimage[/img] - $Text = preg_replace("/\[img\](.+?)\[\/img\]/", '<img src="$1">', $Text); + $Text = preg_replace("/\[img\](.+?)\[\/img\]/", '<img src="$1" alt="' . t('Image/photo') . '" />', $Text); // html5 video and audio @@ -90,8 +93,12 @@ function bbcode($Text) { $Text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.+?)\[\/img\]/", '<img src="$3" height="$2" width="$1">', $Text); // Youtube extensions - $Text = preg_replace("/\[youtube\]http:\/\/www.youtube.com\/watch\?v\=(.+?)\[\/youtube\]/",'[youtube]$1[/youtube]',$Text); - $Text = preg_replace("/\[youtube\](.+?)\[\/youtube\]/", '<object width="425" height="350" type="application/x-shockwave-flash" data="http://www.youtube.com/v/$1" ><param name="movie" value="http://www.youtube.com/v/$1"></param><!--[if IE]><embed src="http://www.youtube.com/v/$1" type="application/x-shockwave-flash" width="425" height="350" /><![endif]--></object>', $Text); + $Text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/watch\?v\=(.+?)\[\/youtube\]/",'[youtube]$1[/youtube]',$Text); + $Text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.+?)\[\/youtube\]/",'[youtube]$1[/youtube]',$Text); + + $Text = preg_replace("/\[youtube\](.+?)\[\/youtube\]/", '<iframe width="425" height="349" src="http://www.youtube.com/embed/$1" frameborder="0" allowfullscreen></iframe>', $Text); + +// $Text = preg_replace("/\[youtube\](.+?)\[\/youtube\]/", '<object width="425" height="350" type="application/x-shockwave-flash" data="http://www.youtube.com/v/$1" ><param name="movie" value="http://www.youtube.com/v/$1"></param><!--[if IE]><embed src="http://www.youtube.com/v/$1" type="application/x-shockwave-flash" width="425" height="350" /><![endif]--></object>', $Text); // oembed tag $Text = oembed_bbcode2html($Text); diff --git a/include/conversation.php b/include/conversation.php new file mode 100644 index 000000000..5f761ccf0 --- /dev/null +++ b/include/conversation.php @@ -0,0 +1,755 @@ +<?php + +/** + * Render actions localized + */ +function localize_item(&$item){ + + if ($item['verb']=="http://activitystrea.ms/schema/1.0/like" || + $item['verb']=="http://activitystrea.ms/schema/1.0/dislike"){ + + $r = q("SELECT * from `item`,`contact` WHERE + `item`.`contact-id`=`contact`.`id` AND `item`.`uri`='%s';", + dbesc($item['parent-uri'])); + if(count($r)==0) return; + $obj=$r[0]; + + $author = '[url=' . $item['author-link'] . ']' . $item['author-name'] . '[/url]'; + $objauthor = '[url=' . $obj['author-link'] . ']' . $obj['author-name'] . '[/url]'; + + $post_type = (($obj['resource-id']) ? t('photo') : t('status')); + $plink = '[url=' . $obj['plink'] . ']' . $post_type . '[/url]'; + + switch($item['verb']){ + case "http://activitystrea.ms/schema/1.0/like": + $bodyverb = t('%1$s likes %2$s\'s %3$s'); + break; + case "http://activitystrea.ms/schema/1.0/dislike": + $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); + break; + } + $item['body'] = sprintf($bodyverb, $author, $objauthor, $plink); + + } + if ($item['verb']=='http://activitystrea.ms/schema/1.0/make-friend'){ + + if ($item['object-type']=="" || $item['object-type']!='http://activitystrea.ms/schema/1.0/person') return; + + $Aname = $item['author-name']; + $Alink = $item['author-link']; + + $xmlhead="<"."?xml version='1.0' encoding='UTF-8' ?".">"; + + $obj = parse_xml_string($xmlhead.$item['object']); + $links = parse_xml_string($xmlhead."<links>".unxmlify($obj->link)."</links>"); + + $Bname = $obj->title; + $Blink = ""; $Bphoto = ""; + foreach ($links->link as $l){ + $atts = $l->attributes(); + switch($atts['rel']){ + case "alternate": $Blink = $atts['href']; + case "photo": $Bphoto = $atts['href']; + } + + } + + $A = '[url=' . $Alink . ']' . $Aname . '[/url]'; + $B = '[url=' . $Blink . ']' . $Bname . '[/url]'; + if ($Bphoto!="") $Bphoto = '[url=' . $Blink . '][img]' . $Bphoto . '[/img][/url]'; + + $item['body'] = sprintf( t('%1$s is now friends with %2$s'), $A, $B)."\n\n\n".$Bphoto; + + } + +} + +/** + * "Render" a conversation or list of items for HTML display. + * There are two major forms of display: + * - Sequential or unthreaded ("New Item View" or search results) + * - conversation view + * The $mode parameter decides between the various renderings and also + * figures out how to determine page owner and other contextual items + * that are based on unique features of the calling module. + * + */ +function conversation(&$a, $items, $mode, $update) { + + require_once('bbcode.php'); + + $profile_owner = 0; + $page_writeable = false; + + if($mode === 'network') { + $profile_owner = local_user(); + $page_writeable = true; + } + + if($mode === 'profile') { + $profile_owner = $a->profile['profile_uid']; + $page_writeable = can_write_wall($a,$profile_owner); + } + + if($mode === 'notes') { + $profile_owner = $a->profile['profile_uid']; + $page_writeable = true; + } + + if($mode === 'display') { + $profile_owner = $a->profile['uid']; + $page_writeable = can_write_wall($a,$profile_owner); + } + + if($update) + $return_url = $_SESSION['return_url']; + else + $return_url = $_SESSION['return_url'] = $a->cmd; + + load_contact_links(local_user()); + + + $cmnt_tpl = get_markup_template('comment_item.tpl'); + $like_tpl = get_markup_template('like.tpl'); + $noshare_tpl = get_markup_template('like_noshare.tpl'); + $tpl = get_markup_template('wall_item.tpl'); + $wallwall = get_markup_template('wallwall_item.tpl'); + + $alike = array(); + $dlike = array(); + + if(count($items)) { + + if($mode === 'network-new' || $mode === 'search') { + + // "New Item View" on network page or search page results + // - just loop through the items and format them minimally for display + + $tpl = get_markup_template('search_item.tpl'); + $droptpl = get_markup_template('wall_fake_drop.tpl'); + + foreach($items as $item) { + + $comment = ''; + $owner_url = ''; + $owner_photo = ''; + $owner_name = ''; + $sparkle = ''; + + if($mode === 'search') { + if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) + && ($item['id'] != $item['parent'])) + continue; + $nickname = $item['nickname']; + } + else + $nickname = $a->user['nickname']; + + $profile_name = ((strlen($item['author-name'])) ? $item['author-name'] : $item['name']); + + $sp = false; + $profile_link = best_link_url($item,$sp); + if($sp) + $sparkle = ' sparkle'; + if($profile_link === 'mailbox') + $profile_link = ''; + + + $normalised = normalise_link((strlen($item['author-link'])) ? $item['author-link'] : $item['url']); + if(($normalised != 'mailbox') && (x($a->contacts[$normalised]))) + $profile_avatar = $a->contacts[$normalised]['thumb']; + else + $profile_avatar = ((strlen($item['author-avatar'])) ? $item['author-avatar'] : $item['thumb']); + + $location = (($item['location']) ? '<a target="map" title="' . $item['location'] . '" href="http://maps.google.com/?q=' . urlencode($item['location']) . '">' . $item['location'] . '</a>' : ''); + $coord = (($item['coord']) ? '<a target="map" title="' . $item['coord'] . '" href="http://maps.google.com/?q=' . urlencode($item['coord']) . '">' . $item['coord'] . '</a>' : ''); + if($coord) { + if($location) + $location .= '<br /><span class="smalltext">(' . $coord . ')</span>'; + else + $location = '<span class="smalltext">' . $coord . '</span>'; + } + + $drop = ''; + $dropping = false; + + if((intval($item['contact-id']) && $item['contact-id'] == remote_user()) || ($item['uid'] == local_user())) + $dropping = true; + + $drop = replace_macros((($dropping)? $droptpl : $fakedrop), array('$id' => $item['id'], '$delete' => t('Delete'))); + + // + localize_item($item); + + $drop = replace_macros($droptpl,array('$id' => $item['id'])); + $lock = '<div class="wall-item-lock"></div>'; + + $o .= replace_macros($tpl,array( + '$id' => $item['item_id'], + '$linktitle' => sprintf( t('View %s\'s profile'), $profile_name), + '$profile_url' => $profile_link, + '$item_photo_menu' => item_photo_menu($item), + '$name' => $profile_name, + '$sparkle' => $sparkle, + '$lock' => $lock, + '$thumb' => $profile_avatar, + '$title' => $item['title'], + '$body' => smilies(bbcode($item['body'])), + '$ago' => relative_date($item['created']), + '$location' => $location, + '$indent' => '', + '$owner_url' => $owner_url, + '$owner_photo' => $owner_photo, + '$owner_name' => $owner_name, + '$drop' => $drop, + '$conv' => '<a href="' . $a->get_baseurl() . '/display/' . $nickname . '/' . $item['id'] . '">' . t('View in context') . '</a>' + )); + + } + + return $o; + } + + + + + // Normal View + + + // Figure out how many comments each parent has + // (Comments all have gravity of 6) + // Store the result in the $comments array + + $comments = array(); + foreach($items as $item) { + if((intval($item['gravity']) == 6) && ($item['id'] != $item['parent'])) { + if(! x($comments,$item['parent'])) + $comments[$item['parent']] = 1; + else + $comments[$item['parent']] += 1; + } + } + + // map all the like/dislike activities for each parent item + // Store these in the $alike and $dlike arrays + + foreach($items as $item) { + like_puller($a,$item,$alike,'like'); + like_puller($a,$item,$dlike,'dislike'); + } + + $comments_collapsed = false; + $blowhard = 0; + $blowhard_count = 0; + + foreach($items as $item) { + + $comment = ''; + $template = $tpl; + $commentww = ''; + $sparkle = ''; + $owner_url = $owner_photo = $owner_name = ''; + + // We've already parsed out like/dislike for special treatment. We can ignore them now + + if(((activity_match($item['verb'],ACTIVITY_LIKE)) + || (activity_match($item['verb'],ACTIVITY_DISLIKE))) + && ($item['id'] != $item['parent'])) + continue; + + $toplevelpost = (($item['id'] == $item['parent']) ? true : false); + + + // Take care of author collapsing and comment collapsing + // If a single author has more than 3 consecutive top-level posts, squash the remaining ones. + // If there are more than two comments, squash all but the last 2. + + if($toplevelpost) { + + $item_writeable = (($item['writable'] || $item['self']) ? true : false); + + if($blowhard == $item['cid'] && (! $item['self']) && ($mode != 'profile') && ($mode != 'notes')) { + $blowhard_count ++; + if($blowhard_count == 3) { + $o .= '<div class="icollapse-wrapper fakelink" id="icollapse-wrapper-' . $item['parent'] + . '" onclick="openClose(' . '\'icollapse-' . $item['parent'] . '\'); $(\'#icollapse-wrapper-' . $item['parent'] . '\').hide();" >' + . t('See more posts like this') . '</div>' . '<div class="icollapse" id="icollapse-' + . $item['parent'] . '" style="display: none;" >'; + } + } + else { + $blowhard = $item['cid']; + if($blowhard_count >= 3) + $o .= '</div>'; + $blowhard_count = 0; + } + + $comments_seen = 0; + $comments_collapsed = false; + } + else + $comments_seen ++; + + + $override_comment_box = ((($page_writeable) && ($item_writeable)) ? true : false); + $show_comment_box = ((($page_writeable) && ($item_writeable) && ($comments_seen == $comments[$item['parent']])) ? true : false); + + if(($comments[$item['parent']] > 2) && ($comments_seen <= ($comments[$item['parent']] - 2)) && ($item['gravity'] == 6)) { + if(! $comments_collapsed) { + $o .= '<div class="ccollapse-wrapper fakelink" id="ccollapse-wrapper-' . $item['parent'] + . '" onclick="openClose(' . '\'ccollapse-' . $item['parent'] . '\'); $(\'#ccollapse-wrapper-' . $item['parent'] . '\').hide();" >' + . sprintf( t('See all %d comments'), $comments[$item['parent']]) . '</div>' + . '<div class="ccollapse" id="ccollapse-' . $item['parent'] . '" style="display: none;" >'; + $comments_collapsed = true; + } + } + if(($comments[$item['parent']] > 2) && ($comments_seen == ($comments[$item['parent']] - 1))) { + $o .= '</div>'; + } + + $redirect_url = $a->get_baseurl() . '/redir/' . $item['cid'] ; + + $lock = ((($item['private']) || (($item['uid'] == local_user()) && (strlen($item['allow_cid']) || strlen($item['allow_gid']) + || strlen($item['deny_cid']) || strlen($item['deny_gid'])))) + ? '<div class="wall-item-lock"><img src="images/lock_icon.gif" class="lockview" alt="' . t('Private Message') . '" onclick="lockview(event,' . $item['id'] . ');" /></div>' + : '<div class="wall-item-lock"></div>'); + + + // Top-level wall post not written by the wall owner (wall-to-wall) + // First figure out who owns it. + + $osparkle = ''; + + if(($toplevelpost) && (! $item['self']) && ($mode !== 'profile')) { + + if($item['type'] === 'wall') { + + // On the network page, I am the owner. On the display page it will be the profile owner. + // This will have been stored in $a->page_contact by our calling page. + // Put this person on the left of the wall-to-wall notice. + + $owner_url = $a->page_contact['url']; + $owner_photo = $a->page_contact['thumb']; + $owner_name = $a->page_contact['name']; + $template = $wallwall; + $commentww = 'ww'; + } + if(($item['type'] === 'remote') && (strlen($item['owner-link'])) && ($item['owner-link'] != $item['author-link'])) { + + // Could be anybody. + + $owner_url = $item['owner-link']; + $owner_photo = $item['owner-avatar']; + $owner_name = $item['owner-name']; + $template = $wallwall; + $commentww = 'ww'; + // If it is our contact, use a friendly redirect link + if((link_compare($item['owner-link'],$item['url'])) + && ($item['network'] === 'dfrn')) { + $owner_url = $redirect_url; + $osparkle = ' sparkle'; + } + } + } + + + $likebuttons = ''; + + if($page_writeable) { + if($toplevelpost) { + $likebuttons = replace_macros((($item['private']) ? $noshare_tpl : $like_tpl),array( + '$id' => $item['id'], + '$likethis' => t("I like this \x28toggle\x29"), + '$nolike' => t("I don't like this \x28toggle\x29"), + '$share' => t('Share'), + '$wait' => t('Please wait') + )); + } + + if(($show_comment_box) || (($show_comment_box == false) && ($override_comment_box == false) && ($item['last-child']))) { + $comment = replace_macros($cmnt_tpl,array( + '$return_path' => '', + '$jsreload' => (($mode === 'display') ? $_SESSION['return_url'] : ''), + '$type' => (($mode === 'profile') ? 'wall-comment' : 'net-comment'), + '$id' => $item['item_id'], + '$parent' => $item['parent'], + '$profile_uid' => $profile_owner, + '$mylink' => $a->contact['url'], + '$mytitle' => t('This is you'), + '$myphoto' => $a->contact['thumb'], + '$comment' => t('Comment'), + '$submit' => t('Submit'), + '$ww' => (($mode === 'network') ? $commentww : '') + )); + } + } + + $edpost = (((($profile_owner == local_user()) && ($toplevelpost) && (intval($item['wall']) == 1)) || ($mode === 'notes')) + ? '<a class="editpost" href="' . $a->get_baseurl() . '/editpost/' . $item['id'] + . '" title="' . t('Edit') . '"><img src="images/pencil.gif" /></a>' + : ''); + $drop = replace_macros(get_markup_template('wall_item_drop.tpl'), array('$id' => $item['id'], '$delete' => t('Delete'))); + + $photo = $item['photo']; + $thumb = $item['thumb']; + + // Post was remotely authored. + + $diff_author = ((link_compare($item['url'],$item['author-link'])) ? false : true); + + $profile_name = (((strlen($item['author-name'])) && $diff_author) ? $item['author-name'] : $item['name']); + + $sp = false; + $profile_link = best_link_url($item,$sp); + if($sp) + $sparkle = ' sparkle'; + + if($profile_link === 'mailbox') + $profile_link = ''; + + $normalised = normalise_link((strlen($item['author-link'])) ? $item['author-link'] : $item['url']); + if(($normalised != 'mailbox') && (x($a->contacts[$normalised]))) + $profile_avatar = $a->contacts[$normalised]['thumb']; + else + $profile_avatar = (((strlen($item['author-avatar'])) && $diff_author) ? $item['author-avatar'] : $thumb); + + + + + + $like = ((x($alike,$item['id'])) ? format_like($alike[$item['id']],$alike[$item['id'] . '-l'],'like',$item['id']) : ''); + $dislike = ((x($dlike,$item['id'])) ? format_like($dlike[$item['id']],$dlike[$item['id'] . '-l'],'dislike',$item['id']) : ''); + + $location = (($item['location']) ? '<a target="map" title="' . $item['location'] + . '" href="http://maps.google.com/?q=' . urlencode($item['location']) . '">' . $item['location'] . '</a>' : ''); + $coord = (($item['coord']) ? '<a target="map" title="' . $item['coord'] + . '" href="http://maps.google.com/?q=' . urlencode($item['coord']) . '">' . $item['coord'] . '</a>' : ''); + if($coord) { + if($location) + $location .= '<br /><span class="smalltext">(' . $coord . ')</span>'; + else + $location = '<span class="smalltext">' . $coord . '</span>'; + } + + $indent = (($toplevelpost) ? '' : ' comment'); + + if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0) + $indent .= ' shiny'; + + // + localize_item($item); + + // Build the HTML + + $tmp_item = replace_macros($template,array( + '$id' => $item['item_id'], + '$linktitle' => sprintf( t('View %s\'s profile'), $profile_name), + '$olinktitle' => sprintf( t('View %s\'s profile'), $owner_name), + '$to' => t('to'), + '$wall' => t('Wall-to-Wall'), + '$vwall' => t('via Wall-To-Wall:'), + '$profile_url' => $profile_link, + '$item_photo_menu' => item_photo_menu($item), + '$name' => $profile_name, + '$thumb' => $profile_avatar, + '$osparkle' => $osparkle, + '$sparkle' => $sparkle, + '$title' => $item['title'], + '$body' => smilies(bbcode($item['body'])), + '$ago' => relative_date($item['created']), + '$lock' => $lock, + '$location' => $location, + '$indent' => $indent, + '$owner_url' => $owner_url, + '$owner_photo' => $owner_photo, + '$owner_name' => $owner_name, + '$plink' => get_plink($item), + '$edpost' => $edpost, + '$drop' => $drop, + '$vote' => $likebuttons, + '$like' => $like, + '$dislike' => $dislike, + '$comment' => $comment + )); + + $arr = array('item' => $item, 'output' => $tmp_item); + call_hooks('display_item', $arr); + + $o .= $arr['output']; + + } + } + + + // if author collapsing is in force but didn't get closed, close it off now. + + if($blowhard_count >= 3) + $o .= '</div>'; + + return $o; +} + + +if(! function_exists('load_contact_links')) { +function load_contact_links($uid) { + + $a = get_app(); + + $ret = array(); + + if(! $uid || x($a->contacts,'empty')) + return; + + $r = q("SELECT `id`,`network`,`url`,`thumb` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 ", + intval($uid) + ); + if(count($r)) { + foreach($r as $rr){ + $url = normalise_link($rr['url']); + $ret[$url] = $rr; + } + } + else + $ret['empty'] = true; + $a->contacts = $ret; + return; +}} + + +function best_link_url($item,&$sparkle) { + + $a = get_app(); + + $best_url = ''; + $sparkle = false; + + $clean_url = normalise_link($item['author-link']); + + if((local_user()) && (local_user() == $item['uid'])) { + if(isset($a->contacts) && x($a->contacts,$clean_url)) { + if($a->contacts[$clean_url]['network'] === NETWORK_DFRN) { + $best_url = $a->get_baseurl() . '/redir/' . $a->contacts[$clean_url]['id']; + $sparkle = true; + } + else + $best_url = $a->contacts[$clean_url]['url']; + } + } + if(! $best_url) { + if(strlen($item['author-link'])) + $best_url = $item['author-link']; + else + $best_url = $item['url']; + } + + return $best_url; +} + + +if(! function_exists('item_photo_menu')){ +function item_photo_menu($item){ + $a = get_app(); + + if (local_user() && (! count($a->contacts))) + load_contact_links(local_user()); + + $contact_url=""; + $pm_url=""; + $status_link=""; + $photos_link=""; + $posts_link=""; + + $sparkle = false; + $profile_link = best_link_url($item,$sparkle); + if($profile_link === 'mailbox') + $profile_link = ''; + + if($sparkle) { + $cid = intval(basename($profile_link)); + $status_link = $profile_link . "?url=status"; + $photos_link = $profile_link . "?url=photos"; + $profile_link = $profile_link . "?url=profile"; + $pm_url = $a->get_baseurl() . '/message/new/' . $cid; + } + else { + if(local_user() && local_user() == $item['uid'] && link_compare($item['url'],$item['author-link'])) { + $cid = $item['contact-id']; + } + else { + $cid = 0; + } + } + if(($cid) && (! $item['self'])) { + $contact_url = $a->get_baseurl() . '/contacts/' . $cid; + $posts_link = $a->get_baseurl() . '/network/?cid=' . $cid; + } + + $menu = Array( + t("View status") => $status_link, + t("View profile") => $profile_link, + t("View photos") => $photos_link, + t("View recent") => $posts_link, + t("Edit contact") => $contact_url, + t("Send PM") => $pm_url, + ); + + + $args = array($item, &$menu); + + call_hooks('item_photo_menu', $args); + + $o = ""; + foreach($menu as $k=>$v){ + if ($v!="") $o .= "<li><a href='$v'>$k</a></li>\n"; + } + return $o; +}} + +if(! function_exists('like_puller')) { +function like_puller($a,$item,&$arr,$mode) { + + $url = ''; + $sparkle = ''; + $verb = (($mode === 'like') ? ACTIVITY_LIKE : ACTIVITY_DISLIKE); + + if((activity_match($item['verb'],$verb)) && ($item['id'] != $item['parent'])) { + $url = $item['author-link']; + if((local_user()) && (local_user() == $item['uid']) && ($item['network'] === 'dfrn') && (! $item['self']) && (link_compare($item['author-link'],$item['url']))) { + $url = $a->get_baseurl() . '/redir/' . $item['contact-id']; + $sparkle = ' class="sparkle" '; + } + if(! ((isset($arr[$item['parent'] . '-l'])) && (is_array($arr[$item['parent'] . '-l'])))) + $arr[$item['parent'] . '-l'] = array(); + if(! isset($arr[$item['parent']])) + $arr[$item['parent']] = 1; + else + $arr[$item['parent']] ++; + $arr[$item['parent'] . '-l'][] = '<a href="'. $url . '"'. $sparkle .'>' . $item['author-name'] . '</a>'; + } + return; +}} + +// Format the like/dislike text for a profile item +// $cnt = number of people who like/dislike the item +// $arr = array of pre-linked names of likers/dislikers +// $type = one of 'like, 'dislike' +// $id = item id +// returns formatted text + +if(! function_exists('format_like')) { +function format_like($cnt,$arr,$type,$id) { + $o = ''; + if($cnt == 1) + $o .= (($type === 'like') ? sprintf( t('%s likes this.'), $arr[0]) : sprintf( t('%s doesn\'t like this.'), $arr[0])) . EOL ; + else { + $spanatts = 'class="fakelink" onclick="openClose(\'' . $type . 'list-' . $id . '\');"'; + $o .= (($type === 'like') ? + sprintf( t('<span %1$s>%2$d people</span> like this.'), $spanatts, $cnt) + : + sprintf( t('<span %1$s>%2$d people</span> don\'t like this.'), $spanatts, $cnt) ); + $o .= EOL ; + $total = count($arr); + if($total >= MAX_LIKERS) + $arr = array_slice($arr, 0, MAX_LIKERS - 1); + if($total < MAX_LIKERS) + $arr[count($arr)-1] = t('and') . ' ' . $arr[count($arr)-1]; + $str = implode(', ', $arr); + if($total >= MAX_LIKERS) + $str .= sprintf( t(', and %d other people'), $total - MAX_LIKERS ); + $str = (($type === 'like') ? sprintf( t('%s like this.'), $str) : sprintf( t('%s don\'t like this.'), $str)); + $o .= "\t" . '<div id="' . $type . 'list-' . $id . '" style="display: none;" >' . $str . '</div>'; + } + return $o; +}} + + +function status_editor($a,$x, $notes_cid = 0) { + + $o = ''; + + $geotag = (($x['allow_location']) ? get_markup_template('jot_geotag.tpl') : ''); + + $tpl = get_markup_template('jot-header.tpl'); + + $a->page['htmlhead'] .= replace_macros($tpl, array( + '$baseurl' => $a->get_baseurl(), + '$geotag' => $geotag, + '$nickname' => $x['nickname'], + '$ispublic' => t('Visible to <strong>everybody</strong>'), + '$linkurl' => t('Please enter a link URL:'), + '$utubeurl' => t('Please enter a YouTube link:'), + '$vidurl' => t("Please enter a video\x28.ogg\x29 link/URL:"), + '$audurl' => t("Please enter an audio\x28.ogg\x29 link/URL:"), + '$whereareu' => t('Where are you right now?'), + '$title' => t('Enter a title for this item') + )); + + + $tpl = get_markup_template("jot.tpl"); + + $jotplugins = ''; + $jotnets = ''; + + $mail_disabled = ((function_exists('imap_open') && (! get_config('system','imap_disabled'))) ? 0 : 1); + + $mail_enabled = false; + $pubmail_enabled = false; + + if(($x['is_owner']) && (! $mail_disabled)) { + $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d AND `server` != '' LIMIT 1", + intval(local_user()) + ); + if(count($r)) { + $mail_enabled = true; + if(intval($r[0]['pubmail'])) + $pubmail_enabled = true; + } + } + + if($mail_enabled) { + $selected = (($pubmail_enabled) ? ' checked="checked" ' : ''); + $jotnets .= '<div class="profile-jot-net"><input type="checkbox" name="pubmail_enable"' . $selected . 'value="1" /> ' + . t("Post to Email") . '</div>'; + } + + call_hooks('jot_tool', $jotplugins); + call_hooks('jot_networks', $jotnets); + + if($notes_cid) + $jotnets .= '<input type="hidden" name="contact_allow[]" value="' . $notes_cid .'" />'; + + $tpl = replace_macros($tpl,array('$jotplugins' => $jotplugins)); + + $o .= replace_macros($tpl,array( + '$return_path' => $a->cmd, + '$action' => 'item', + '$share' => (($x['button']) ? $x['button'] : t('Share')), + '$upload' => t('Upload photo'), + '$weblink' => t('Insert web link'), + '$youtube' => t('Insert YouTube video'), + '$video' => t('Insert Vorbis [.ogg] video'), + '$audio' => t('Insert Vorbis [.ogg] audio'), + '$setloc' => t('Set your location'), + '$noloc' => t('Clear browser location'), + '$title' => t('Set title'), + '$wait' => t('Please wait'), + '$permset' => t('Permission settings'), + '$ptyp' => (($notes_cid) ? 'note' : 'wall'), + '$content' => '', + '$post_id' => '', + '$baseurl' => $a->get_baseurl(), + '$defloc' => $x['default_location'], + '$visitor' => $x['visitor'], + '$pvisit' => (($notes_cid) ? 'none' : $x['visitor']), + '$emailcc' => t('CC: email addresses'), + '$public' => t('Public post'), + '$jotnets' => $jotnets, + '$emtitle' => t('Example: bob@example.com, mary@example.com'), + '$lockstate' => $x['lockstate'], + '$acl' => $x['acl'], + '$bang' => $x['bang'], + '$profile_uid' => $x['profile_uid'], + )); + + return $o; +}
\ No newline at end of file diff --git a/include/dba.php b/include/dba.php index d75ed560a..49b325cf7 100644 --- a/include/dba.php +++ b/include/dba.php @@ -19,6 +19,23 @@ class dba { public $connected = false; function __construct($server,$user,$pass,$db,$install = false) { + + $server = trim($server); + $user = trim($user); + $pass = trim($pass); + $db = trim($db); + + if($install) { + if(strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1')) { + if(! dns_get_record($server, DNS_A + DNS_CNAME + DNS_PTR)) { + notice( sprintf( t('Cannot locate DNS info for database server \'%s\''), $server)); + $this->connected = false; + $this->db = null; + return; + } + } + } + $this->db = @new mysqli($server,$user,$pass,$db); if(! mysqli_connect_errno()) { $this->connected = true; @@ -61,7 +78,7 @@ class dba { } else { - /* + /** * If dbfail.out exists, we will write any failed calls directly to it, * regardless of any logging that may or may nor be in effect. * These usually indicate SQL syntax errors that need to be resolved. diff --git a/include/email.php b/include/email.php new file mode 100644 index 000000000..7e0351e94 --- /dev/null +++ b/include/email.php @@ -0,0 +1,201 @@ +<?php + +function email_connect($mailbox,$username,$password) { + if(! function_exists('imap_open')) + return false; + + $mbox = imap_open($mailbox,$username,$password); + + return $mbox; +} + +function email_poll($mbox,$email_addr) { + + if(! ($mbox && $email_addr)) + return array();; + + $search = imap_search($mbox,'FROM "' . $email_addr . '"', SE_UID); + + $search2 = imap_search($mbox,'TO "' . $email_addr . '"', SE_UID); + + if($search && $search2) + $res = array_merge($search,$search2); + elseif($search) + $res = $search; + else + $res = $search2; + + return (($res) ? $res : array()); +} + + +function construct_mailbox_name($mailacct) { + $ret = '{' . $mailacct['server'] . ((intval($mailacct['port'])) ? ':' . $mailacct['port'] : ''); + $ret .= (($mailacct['ssltype']) ? '/' . $mailacct['ssltype'] . '/novalidate-cert' : ''); + $ret .= '}' . $mailacct['mailbox']; + return $ret; +} + + +function email_msg_meta($mbox,$uid) { + $ret = (($mbox && $uid) ? imap_fetch_overview($mbox,$uid,FT_UID) : array(array())); + return ((count($ret)) ? $ret[0] : array()); +} + +function email_msg_headers($mbox,$uid) { + $raw_header = (($mbox && $uid) ? imap_fetchheader($mbox,$uid,FT_UID) : ''); + $raw_header = str_replace("\r",'',$raw_header); + $ret = array(); + $h = split("\n",$raw_header); + if(count($h)) + foreach($h as $line ) { + if (preg_match("/^[a-zA-Z]/", $line)) { + $key = substr($line,0,strpos($line,':')); + $value = substr($line,strpos($line,':')+1); + + $last_entry = strtolower($key); + $ret[$last_entry] = trim($value); + } + else { + $ret[$last_entry] .= ' ' . trim($line); + } + } + return $ret; +} + + +function email_get_msg($mbox,$uid) { + $ret = array(); + + $struc = (($mbox && $uid) ? imap_fetchstructure($mbox,$uid,FT_UID) : null); + + if(! $struc) + return $ret; + + if(! $struc->parts) { + $ret['body'] = email_get_part($mbox,$uid,$struc,0); + } + else { + foreach($struc->parts as $ptop => $p) { + $x = email_get_part($mbox,$uid,$p,$ptop + 1); + if($x) + $ret['body'] = $x; + } + } + return $ret; +} + +// At the moment - only return plain/text. +// Later we'll repackage inline images as data url's and make the HTML safe + +function email_get_part($mbox,$uid,$p,$partno) { + // $partno = '1', '2', '2.1', '2.1.3', etc for multipart, 0 if simple + global $htmlmsg,$plainmsg,$charset,$attachments; + + echo $partno; + + // DECODE DATA + $data = ($partno) + ? imap_fetchbody($mbox,$uid,$partno, FT_UID|FT_PEEK) + : imap_body($mbox,$uid,FT_UID|FT_PEEK); + + // Any part may be encoded, even plain text messages, so check everything. + if ($p->encoding==4) + $data = quoted_printable_decode($data); + elseif ($p->encoding==3) + $data = base64_decode($data); + + // PARAMETERS + // get all parameters, like charset, filenames of attachments, etc. + $params = array(); + if ($p->parameters) + foreach ($p->parameters as $x) + $params[strtolower($x->attribute)] = $x->value; + if ($p->dparameters) + foreach ($p->dparameters as $x) + $params[strtolower($x->attribute)] = $x->value; + + // ATTACHMENT + // Any part with a filename is an attachment, + // so an attached text file (type 0) is not mistaken as the message. + + if ($params['filename'] || $params['name']) { + // filename may be given as 'Filename' or 'Name' or both + $filename = ($params['filename'])? $params['filename'] : $params['name']; + // filename may be encoded, so see imap_mime_header_decode() + $attachments[$filename] = $data; // this is a problem if two files have same name + } + + // TEXT + if ($p->type == 0 && $data) { + // Messages may be split in different parts because of inline attachments, + // so append parts together with blank row. + if (strtolower($p->subtype)=='plain') + return (trim($data) ."\n\n"); + else + $data = ''; + + // $htmlmsg .= $data ."<br><br>"; + $charset = $params['charset']; // assume all parts are same charset + } + + // EMBEDDED MESSAGE + // Many bounce notifications embed the original message as type 2, + // but AOL uses type 1 (multipart), which is not handled here. + // There are no PHP functions to parse embedded messages, + // so this just appends the raw source to the main message. +// elseif ($p->type==2 && $data) { +// $plainmsg .= $data."\n\n"; +// } + + // SUBPART RECURSION + if ($p->parts) { + foreach ($p->parts as $partno0=>$p2) { + $x = email_get_part($mbox,$uid,$p2,$partno . '.' . ($partno0+1)); // 1.2, 1.2.1, etc. + if($x) + return $x; + } + } +} + + + +function email_header_encode($in_str, $charset) { + $out_str = $in_str; + if ($out_str && $charset) { + + // define start delimimter, end delimiter and spacer + $end = "?="; + $start = "=?" . $charset . "?B?"; + $spacer = $end . "\r\n " . $start; + + // determine length of encoded text within chunks + // and ensure length is even + $length = 75 - strlen($start) - strlen($end); + + /* + [EDIT BY danbrown AT php DOT net: The following + is a bugfix provided by (gardan AT gmx DOT de) + on 31-MAR-2005 with the following note: + "This means: $length should not be even, + but divisible by 4. The reason is that in + base64-encoding 3 8-bit-chars are represented + by 4 6-bit-chars. These 4 chars must not be + split between two encoded words, according + to RFC-2047. + */ + $length = $length - ($length % 4); + + // encode the string and split it into chunks + // with spacers after each chunk + $out_str = base64_encode($out_str); + $out_str = chunk_split($out_str, $length, $spacer); + + // remove trailing spacer and + // add start and end delimiters + $spacer = preg_quote($spacer); + $out_str = preg_replace("/" . $spacer . "$/", "", $out_str); + $out_str = $start . $out_str . $end; + } + return $out_str; +}
\ No newline at end of file diff --git a/include/expire.php b/include/expire.php new file mode 100644 index 000000000..3c30e01c1 --- /dev/null +++ b/include/expire.php @@ -0,0 +1,44 @@ +<?php + +require_once("boot.php"); + +function expire_run($argv, $argc){ + global $a, $db; + + if(is_null($a)) { + $a = new App; + } + + if(is_null($db)) { + @include(".htconfig.php"); + require_once("dba.php"); + $db = new dba($db_host, $db_user, $db_pass, $db_data); + unset($db_host, $db_user, $db_pass, $db_data); + }; + + require_once('session.php'); + require_once('datetime.php'); + require_once('simplepie/simplepie.inc'); + require_once('include/items.php'); + require_once('include/Contact.php'); + + $a->set_baseurl(get_config('system','url')); + + + logger('expire: start'); + + $r = q("SELECT `uid`,`username`,`expire` FROM `user` WHERE `expire` != 0"); + if(count($r)) { + foreach($r as $rr) { + logger('Expire: ' . $rr['username'] . ' interval: ' . $rr['expire'], LOGGER_DEBUG); + item_expire($rr['uid'],$rr['expire']); + } + } + + return; +} + +if (array_search(__file__,get_included_files())===0){ + expire_run($argv,$argc); + killme(); +} diff --git a/include/fcontact.php b/include/fcontact.php new file mode 100644 index 000000000..8821a985f --- /dev/null +++ b/include/fcontact.php @@ -0,0 +1,41 @@ +<?php + + + +function fcontact_store($url,$name,$photo) { + + $nurl = str_replace(array('https:','//www.'), array('http:','//'), $url); + + $r = q("SELECT `id` FROM `fcontact` WHERE `url` = '%s' LIMIT 1", + dbesc($nurl) + ); + + if(count($r)) + return $r[0]['id']; + + $r = q("INSERT INTO `fcontact` ( `url`, `name`, `photo` ) VALUES ( '%s', '%s', '%s' ) ", + dbesc($nurl), + dbesc($name), + dbesc($photo) + ); + + if($r) { + $r = q("SELECT `id` FROM `fcontact` WHERE `url` = '%s' LIMIT 1", + dbesc($nurl) + ); + if(count($r)) + return $r[0]['id']; + } + + return 0; +} + +function ffinder_store($uid,$cid,$fid) { + $r = q("INSERT INTO `ffinder` ( `uid`, `cid`, `fid` ) VALUES ( %d, %d, %d ) ", + intval($uid), + intval($cid), + intval($fid) + ); + return $r; +} + diff --git a/include/group.php b/include/group.php index 793e854be..8866104fc 100644 --- a/include/group.php +++ b/include/group.php @@ -108,9 +108,9 @@ function group_get_members($gid) { if(intval($gid)) { $r = q("SELECT `group_member`.`contact-id`, `contact`.* FROM `group_member` LEFT JOIN `contact` ON `contact`.`id` = `group_member`.`contact-id` - WHERE `gid` = %d AND `group_member`.`uid` = %d", + WHERE `gid` = %d AND `group_member`.`uid` = %d ORDER BY `contact`.`name` ASC ", intval($gid), - intval($_SESSION['uid']) + intval(local_user()) ); if(count($r)) $ret = $r; @@ -118,9 +118,25 @@ function group_get_members($gid) { return $ret; } +function group_public_members($gid) { + $ret = 0; + if(intval($gid)) { + $r = q("SELECT `contact`.`id` AS `contact-id` FROM `group_member` + LEFT JOIN `contact` ON `contact`.`id` = `group_member`.`contact-id` + WHERE `gid` = %d AND `group_member`.`uid` = %d + AND `contact`.`network` != 'dfrn' AND `contact`.`network` != 'mail' AND `contact`.`network` != 'face' ", + intval($gid), + intval(local_user()) + ); + if(count($r)) + $ret = count($r); + } + return $ret; +} + -function group_side($every="contacts",$each="group") { +function group_side($every="contacts",$each="group",$edit = false) { $o = ''; @@ -150,7 +166,7 @@ EOT; ); if(count($r)) { foreach($r as $rr) - $o .= " <li class=\"sidebar-group-li\"><a href=\"$each/{$rr['id']}\">{$rr['name']}</a></li>\r\n"; + $o .= ' <li class="sidebar-group-li">' . (($edit) ? "<a href=\"group/{$rr['id']}\" title=\"" . t('Edit') . "\" ><img src=\"images/spencil.gif\" alt=\"" . t('Edit') . "\"></a> " : "") . "<a href=\"$each/{$rr['id']}\">{$rr['name']}</a></li>\r\n"; } $o .= " </ul>\r\n </div>\r\n</div>"; diff --git a/include/hostxrd.php b/include/hostxrd.php index 9161b265c..987175c33 100644 --- a/include/hostxrd.php +++ b/include/hostxrd.php @@ -1,10 +1,10 @@ <?php -function hostxrd($hostname) { +function hostxrd($baseurl) { header("Content-type: text/xml"); $tpl = file_get_contents('view/xrd_host.tpl'); - echo str_replace('$domain',$hostname,$tpl); + echo str_replace('$domain',$baseurl,$tpl); session_write_close(); exit(); diff --git a/include/items.php b/include/items.php index e930ab5d2..55b17158a 100644 --- a/include/items.php +++ b/include/items.php @@ -113,7 +113,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) $items = $r; - $feed_template = load_view_file('view/atom_feed.tpl'); + $feed_template = get_markup_template('atom_feed.tpl'); $atom = ''; @@ -180,7 +180,7 @@ function construct_activity_object($item) { if($item['object']) { $o = '<as:object>' . "\r\n"; - $r = @simplexml_load_string($item['object']); + $r = parse_xml_string($item['object']); if($r->type) $o .= '<as:object-type>' . xmlify($r->type) . '</as:object-type>' . "\r\n"; if($r->id) @@ -206,7 +206,7 @@ function construct_activity_target($item) { if($item['target']) { $o = '<as:target>' . "\r\n"; - $r = @simplexml_load_string($item['target']); + $r = parse_xml_string($item['target']); if($r->type) $o .= '<as:object-type>' . xmlify($r->type) . '</as:object-type>' . "\r\n"; if($r->id) @@ -241,8 +241,14 @@ function get_atom_elements($feed,$item) { $res = array(); $author = $item->get_author(); - $res['author-name'] = unxmlify($author->get_name()); - $res['author-link'] = unxmlify($author->get_link()); + if($author) { + $res['author-name'] = unxmlify($author->get_name()); + $res['author-link'] = unxmlify($author->get_link()); + } + else { + $res['author-name'] = unxmlify($feed->get_title()); + $res['author-link'] = unxmlify($feed->get_permalink()); + } $res['uri'] = unxmlify($item->get_id()); $res['title'] = unxmlify($item->get_title()); $res['body'] = unxmlify($item->get_content()); @@ -343,12 +349,14 @@ function get_atom_elements($feed,$item) { // the wild, by sanitising it and converting supported tags to bbcode before we rip out any remaining // html. - if((strpos($res['body'],'<') !== false) || (strpos($res['body'],'>') !== false)) { $res['body'] = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s', '[youtube]$1[/youtube]', $res['body']); + $res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s', + '[youtube]$1[/youtube]', $res['body']); + $res['body'] = oembed_html2bbcode($res['body']); $config = HTMLPurifier_Config::createDefault(); @@ -401,6 +409,17 @@ function get_atom_elements($feed,$item) { $res['edited'] = $item->get_date('c'); + // Disallow time travelling posts + + $d1 = strtotime($res['created']); + $d2 = strtotime($res['edited']); + $d3 = strtotime('now'); + + if($d1 > $d3) + $res['created'] = datetime_convert(); + if($d2 > $d3) + $res['edited'] = datetime_convert(); + $rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner'); if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']) $res['owner-name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']); @@ -440,7 +459,45 @@ function get_atom_elements($feed,$item) { if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow')) $res['verb'] = ACTIVITY_UNFOLLOW; - + + $cats = $item->get_categories(); + if($cats) { + $tag_arr = array(); + foreach($cats as $cat) { + $term = $cat->get_term(); + if(! $term) + $term = $cat->get_label(); + $scheme = $cat->get_scheme(); + if($scheme && $term && stristr($scheme,'X-DFRN:')) + $tag_arr[] = substr($scheme,7,1) . '[url=' . unxmlify(substr($scheme,9)) . ']' . unxmlify($term) . '[/url]'; + elseif($term) + $tag_arr[] = notags(trim($term)); + } + $res['tag'] = implode(',', $tag_arr); + } + + $attach = $item->get_enclosures(); + if($attach) { + $att_arr = array(); + foreach($attach as $att) { + $len = intval($att->get_length()); + $link = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_link())))); + $title = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_title())))); + $type = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_type())))); + if(strpos($type,';')) + $type = substr($type,0,strpos($type,';')); + if((! $link) || (strpos($link,'http') !== 0)) + continue; + + if(! $title) + $title = ' '; + if(! $type) + $type = 'application/octet-stream'; + + $att_arr[] = '[attach]href="' . $link . '" size="' . $len . '" type="' . $type . '" title="' . $title . '"[/attach]'; + } + $res['attach'] = implode(',', $att_arr); + } $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object'); @@ -467,6 +524,10 @@ function get_atom_elements($feed,$item) { $body = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s', '[youtube]$1[/youtube]', $body); + $res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s', + '[youtube]$1[/youtube]', $res['body']); + + $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.DefinitionImpl', null); @@ -506,6 +567,9 @@ function get_atom_elements($feed,$item) { $body = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s', '[youtube]$1[/youtube]', $body); + $res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s', + '[youtube]$1[/youtube]', $res['body']); + $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.DefinitionImpl', null); @@ -598,6 +662,8 @@ function item_store($arr,$force_parent = false) { $arr['deny_gid'] = ((x($arr,'deny_gid')) ? trim($arr['deny_gid']) : ''); $arr['private'] = ((x($arr,'private')) ? intval($arr['private']) : 0 ); $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : ''); + $arr['tag'] = ((x($arr,'tag')) ? notags(trim($arr['tag'])) : ''); + $arr['attach'] = ((x($arr,'attach')) ? notags(trim($arr['attach'])) : ''); if($arr['parent-uri'] === $arr['uri']) { $parent_id = 0; @@ -645,6 +711,7 @@ function item_store($arr,$force_parent = false) { $parent_id = 0; $arr['thr-parent'] = $arr['parent-uri']; $arr['parent-uri'] = $arr['uri']; + $arr['gravity'] = 0; } else { logger('item_store: item parent was not found - ignoring item'); @@ -702,6 +769,18 @@ function item_store($arr,$force_parent = false) { intval($current_post) ); + /** + * If this is now the last-child, force all _other_ children of this parent to *not* be last-child + */ + + if($arr['last-child']) { + $r = q("UPDATE `item` SET `last-child` = 0 WHERE `parent-uri` = '%s' AND `uid` = %d AND `id` != %d", + dbesc($arr['uri']), + intval($arr['uid']), + intval($current_post) + ); + } + return $current_post; } @@ -739,7 +818,7 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { if(! $rino_enable) $rino = 0; - $url = $contact['notify'] . '?dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino=1' : ''); + $url = $contact['notify'] . '&dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino=1' : ''); logger('dfrn_deliver: ' . $url); @@ -760,7 +839,7 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { return 3; } - $res = simplexml_load_string($xml); + $res = parse_xml_string($xml); if((intval($res->status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id))) return (($res->status) ? $res->status : 3); @@ -799,14 +878,14 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { if($dissolve) $postvars['dissolve'] = '1'; - if(($contact['rel']) && ($contact['rel'] != REL_FAN) && (! $contact['blocked']) && (! $contact['readonly'])) { - $postvars['data'] = $atom; - } - elseif($owner['page-flags'] == PAGE_COMMUNITY) { + + if((($contact['rel']) && ($contact['rel'] != REL_FAN) && (! $contact['blocked'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) { $postvars['data'] = $atom; + $postvars['perm'] = 'rw'; } else { $postvars['data'] = str_replace('<dfrn:comment-allow>1','<dfrn:comment-allow>0',$atom); + $postvars['perm'] = 'r'; } if($rino && $rino_allowed && (! $dissolve)) { @@ -848,17 +927,15 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { if((! $curl_stat) || (! strlen($xml))) return(-1); // timed out - if(strpos($xml,'<?xml') === false) { logger('dfrn_deliver: phase 2: no valid XML returned'); logger('dfrn_deliver: phase 2: returned XML: ' . $xml, LOGGER_DATA); return 3; } - $res = simplexml_load_string($xml); + $res = parse_xml_string($xml); - return $res->status; - + return $res->status; } @@ -878,7 +955,7 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { * */ -function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { +function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $secure_feed = false) { require_once('simplepie/simplepie.inc'); @@ -893,6 +970,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { if($feed->error()) logger('consume_feed: Error parsing XML: ' . $feed->error()); + $permalink = $feed->get_permalink(); // Check at the feed level for updated contact name and/or photo @@ -1137,6 +1215,13 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { $item_id = $item->get_id(); $datarray = get_atom_elements($feed,$item); + if(! x($datarray,'author-name')) + $datarray['author-name'] = $contact['name']; + if(! x($datarray,'author-link')) + $datarray['author-link'] = $contact['url']; + if(! x($datarray,'author-avatar')) + $datarray['author-avatar'] = $contact['thumb']; + $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']) @@ -1207,8 +1292,18 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { // Head post of a conversation. Have we seen it? If not, import it. $item_id = $item->get_id(); + $datarray = get_atom_elements($feed,$item); + if(is_array($contact)) { + if(! x($datarray,'author-name')) + $datarray['author-name'] = $contact['name']; + if(! x($datarray,'author-link')) + $datarray['author-link'] = $contact['url']; + if(! x($datarray,'author-avatar')) + $datarray['author-avatar'] = $contact['thumb']; + } + $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']) @@ -1252,7 +1347,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { if(! is_array($contact)) return; - if($contact['network'] === 'stat') { + if($contact['network'] === 'stat' || stristr($permalink,'twitter.com')) { if(strlen($datarray['title'])) unset($datarray['title']); $datarray['last-child'] = 1; @@ -1299,8 +1394,8 @@ function new_follower($importer,$contact,$datarray,$item) { // create contact record - set to readonly $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `name`, `nick`, `photo`, `network`, `rel`, - `blocked`, `readonly`, `pending` ) - VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, 0, 1, 1 ) ", + `blocked`, `readonly`, `pending`, `writable` ) + VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, 0, 1, 1, 1 ) ", intval($importer['uid']), dbesc(datetime_convert()), dbesc($url), @@ -1336,7 +1431,7 @@ function new_follower($importer,$contact,$datarray,$item) { $a = get_app(); if(count($r)) { if(($r[0]['notify-flags'] & NOTIFY_INTRO) && ($r[0]['page-flags'] == PAGE_NORMAL)) { - $email_tpl = load_view_file('view/follow_notify_eml.tpl'); + $email_tpl = get_intltext_template('follow_notify_eml.tpl'); $email = replace_macros($email_tpl, array( '$requestor' => ((strlen($name)) ? $name : t('[Name Withheld]')), '$url' => $url, @@ -1347,7 +1442,9 @@ function new_follower($importer,$contact,$datarray,$item) { $res = mail($r[0]['email'], t("You have a new follower at ") . $a->config['sitename'], $email, - 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] ); + 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n" + . 'Content-type: text/plain; charset=UTF-8' . "\n" + . 'Content-transfer-encoding: 8bit' ); } } @@ -1426,17 +1523,24 @@ function atom_author($tag,$name,$uri,$h,$w,$photo) { function atom_entry($item,$type,$author,$owner,$comment = false) { + $a = get_app(); + if($item['deleted']) return '<at:deleted-entry ref="' . xmlify($item['uri']) . '" when="' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '" />' . "\r\n"; - $a = get_app(); + + if($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid']) + $body = fix_private_photos($item['body'],$owner['uid']); + else + $body = $item['body']; + $o = "\r\n\r\n<entry>\r\n"; if(is_array($author)) $o .= atom_author('author',$author['name'],$author['url'],80,80,$author['thumb']); else - $o .= atom_author('author',$item['name'],$item['url'],80,80,$item['thumb']); + $o .= atom_author('author',(($item['author-name']) ? $item['author-name'] : $item['name']),(($item['author-link']) ? $item['author-link'] : $item['url']),80,80,(($item['author-avatar']) ? $item['author-avatar'] : $item['thumb'])); if(strlen($item['owner-name'])) $o .= atom_author('dfrn:owner',$item['owner-name'],$item['owner-link'],80,80,$item['owner-avatar']); @@ -1447,8 +1551,8 @@ function atom_entry($item,$type,$author,$owner,$comment = false) { $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; $o .= '<published>' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '</published>' . "\r\n"; $o .= '<updated>' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '</updated>' . "\r\n"; - $o .= '<dfrn:env>' . base64url_encode($item['body'], true) . '</dfrn:env>' . "\r\n"; - $o .= '<content type="' . $type . '" >' . xmlify(($type === 'html') ? bbcode($item['body']) : $item['body']) . '</content>' . "\r\n"; + $o .= '<dfrn:env>' . base64url_encode($body, true) . '</dfrn:env>' . "\r\n"; + $o .= '<content type="' . $type . '" >' . xmlify(($type === 'html') ? bbcode($body) : $body) . '</content>' . "\r\n"; $o .= '<link rel="alternate" type="text/html" href="' . xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['id']) . '" />' . "\r\n"; if($comment) $o .= '<dfrn:comment-allow>' . intval($item['last-child']) . '</dfrn:comment-allow>' . "\r\n"; @@ -1473,6 +1577,15 @@ function atom_entry($item,$type,$author,$owner,$comment = false) { if(strlen($actarg)) $o .= $actarg; + $tags = item_getfeedtags($item); + if(count($tags)) { + foreach($tags as $t) { + $o .= '<category scheme="X-DFRN:' . xmlify($t[0]) . ':' . xmlify($t[1]) . '" term="' . xmlify($t[2]) . '" />' . "\r\n"; + } + } + + $o .= item_getfeedattach($item); + $mentioned = get_mentions($item); if($mentioned) $o .= $mentioned; @@ -1483,4 +1596,125 @@ function atom_entry($item,$type,$author,$owner,$comment = false) { return $o; } + +function fix_private_photos($s,$uid) { + $a = get_app(); + logger('fix_private_photos'); + + if(preg_match("/\[img\](.+?)\[\/img\]/is",$s,$matches)) { + $image = $matches[1]; + logger('fix_private_photos: found photo ' . $image); + if(stristr($image ,$a->get_baseurl() . '/photo/')) { + $i = basename($image); + $i = str_replace('.jpg','',$i); + $x = strpos($i,'-'); + if($x) { + $res = substr($i,$x+1); + $i = substr($i,0,$x); + $r = q("SELECT * FROM `photo` WHERE `resource-id` = '%s' AND `scale` = %d AND `uid` = %d", + dbesc($i), + intval($res), + intval($uid) + ); + if(count($r)) { + logger('replacing photo'); + $s = str_replace($image, 'data:image/jpg;base64,' . base64_encode($r[0]['data']), $s); + } + } + logger('fix_private_photos: replaced: ' . $s, LOGGER_DATA); + } + } + return($s); +} + + + +function item_getfeedtags($item) { + $ret = array(); + $matches = false; + $cnt = preg_match_all('|\#\[url\=(.+?)\](.+?)\[\/url\]|',$item['tag'],$matches); + if($cnt) { + for($x = 0; $x < count($matches); $x ++) { + if($matches[1][$x]) + $ret[] = array('#',$matches[1][$x], $matches[2][$x]); + } + } + $matches = false; + $cnt = preg_match_all('|\@\[url\=(.+?)\](.+?)\[\/url\]|',$item['tag'],$matches); + if($cnt) { + for($x = 0; $x < count($matches); $x ++) { + if($matches[1][$x]) + $ret[] = array('#',$matches[1][$x], $matches[2][$x]); + } + } + return $ret; +} + +function item_getfeedattach($item) { + $ret = ''; + $arr = explode(',',$item['attach']); + if(count($arr)) { + foreach($arr as $r) { + $matches = false; + $cnt = preg_match('|\[attach\]href=\"(.+?)\" size=\"(.+?)\" type=\"(.+?)\" title=\"(.+?)\"\[\/attach\]|',$r,$matches); + if($cnt) { + $ret .= '<link rel="enclosure" href="' . xmlify($matches[1]) . '" type="' . xmlify($matches[3]) . '" '; + if(intval($matches[2])) + $ret .= 'size="' . intval($matches[2]) . '" '; + if($matches[4] !== ' ') + $ret .= 'title="' . xmlify(trim($matches[4])) . '" '; + $ret .= ' />' . "\r\n"; + } + } + } + return $ret; +} + + +function item_expire($uid,$days) { + + if((! $uid) || (! $days)) + return; + + $r = q("SELECT * FROM `item` + WHERE `uid` = %d + AND `created` < UTC_TIMESTAMP() - INTERVAL %d DAY + AND `id` = `parent` + AND `deleted` = 0", + intval($uid), + intval($days) + ); + + if(! count($r)) + return; + + logger('expire: # items=' . count($r) ); + + foreach($r as $item) { + + // Only expire posts, not photos and photo comments + + if(strlen($item['resource-id'])) + continue; + + $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s' WHERE `id` = %d LIMIT 1", + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($item['id']) + ); + + // kill the kids + + $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ", + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc($item['parent-uri']), + intval($item['uid']) + ); + + } + + proc_run('php',"include/notifier.php","expire","$uid"); + +}
\ No newline at end of file diff --git a/include/main.js b/include/main.js index 824f2dfdf..0d68c2b2b 100644 --- a/include/main.js +++ b/include/main.js @@ -26,6 +26,8 @@ var pr = 0; var liking = 0; var in_progress = false; + var langSelect = false; + var commentBusy = false; $(document).ready(function() { $.ajaxSetup({cache: false}); @@ -45,6 +47,18 @@ $('#pause').html(''); } } + // F8 - show/hide language selector + if(event.keyCode == '119') { + if(langSelect) { + langSelect = false; + $('#language-selector').hide(); + } + else { + langSelect = true; + $('#language-selector').show(); + } + } + // this is shift-home on FF, but $ on IE, disabling until I figure out why the diff. // update: incompatible usage of onKeyDown vs onKeyPress // if(event.keyCode == '36' && event.shiftKey == true) { @@ -77,26 +91,26 @@ $.get("ping",function(data) { $(data).find('result').each(function() { var net = $(this).find('net').text(); - if(net == 0) { net = ''; } + if(net == 0) { net = ''; $('#net-update').hide() } else { $('#net-update').show() } $('#net-update').html(net); var home = $(this).find('home').text(); - if(home == 0) { home = ''; } + if(home == 0) { home = ''; $('#home-update').hide() } else { $('#home-update').show() } $('#home-update').html(home); var mail = $(this).find('mail').text(); - if(mail == 0) { mail = ''; } + if(mail == 0) { mail = ''; $('#mail-update').hide() } else { $('#mail-update').show() } $('#mail-update').html(mail); var intro = $(this).find('intro').text(); var register = $(this).find('register').text(); if(intro == 0) { intro = ''; } if(register != 0 && intro != '') { intro = intro+'/'+register; } if(register != 0 && intro == '') { intro = '0/'+register; } + if (intro == '') { $('#notify-update').hide() } else { $('#notify-update').show() } $('#notify-update').html(intro); }); }) ; } timer = setTimeout(NavUpdate,30000); - } function liveUpdate() { @@ -113,12 +127,21 @@ $.get(update_url,function(data) { in_progress = false; + $('.ccollapse-wrapper',data).each(function() { + var ident = $(this).attr('id'); + var is_hidden = $('#' + ident).is(':hidden'); + if($('#' + ident).length) { + $('#' + ident).replaceWith($(this)); + if(is_hidden) + $('#' + ident).hide(); + } + }); $('.wall-item-outside-wrapper',data).each(function() { var ident = $(this).attr('id'); - if($('#' + ident).length == 0) { - $('img',this).each(function() { - $(this).attr('src',$(this).attr('dst')); - }); + if($('#' + ident).length == 0) { + $('img',this).each(function() { + $(this).attr('src',$(this).attr('dst')); + }); $('#' + prev).after($(this)); } else { @@ -127,29 +150,26 @@ $('#' + ident + ' ' + '.wall-item-comment-wrapper').replaceWith($(this).find('.wall-item-comment-wrapper')); $('#' + ident + ' ' + '.wall-item-like').replaceWith($(this).find('.wall-item-like')); $('#' + ident + ' ' + '.wall-item-dislike').replaceWith($(this).find('.wall-item-dislike')); - $('#' + ident + ' ' + '.my-comment-photo').each(function() { - $(this).attr('src',$(this).attr('dst')); - }); - - + $('#' + ident + ' ' + '.my-comment-photo').each(function() { + $(this).attr('src',$(this).attr('dst')); + }); } prev = ident; }); $('.like-rotator').hide(); + if(commentBusy) { + commentBusy = false; + $('body').css('cursor', 'auto'); + } }); - } function imgbright(node) { - $(node).attr("src",$(node).attr("src").replace('hide','show')); - $(node).css('width',24); - $(node).css('height',24); + $(node).removeClass("drophide").addClass("drop"); } function imgdull(node) { - $(node).attr("src",$(node).attr("src").replace('show','hide')); - $(node).css('width',16); - $(node).css('height',16); + $(node).removeClass("drop").addClass("drophide"); } // Since our ajax calls are asynchronous, we will give a few @@ -215,6 +235,8 @@ } function post_comment(id) { + commentBusy = true; + $('body').css('cursor', 'wait'); $.post( "item", $("#comment-edit-form-" + id).serialize(), @@ -231,10 +253,49 @@ if(data.reload) { window.location.href=data.reload; } - }, "json" ); return false; } + + function bin2hex(s){ + // Converts the binary representation of data to hex + // + // version: 812.316 + // discuss at: http://phpjs.org/functions/bin2hex + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Onno Marsman + // + bugfixed by: Linuxworld + // * example 1: bin2hex('Kev'); + // * returns 1: '4b6576' + // * example 2: bin2hex(String.fromCharCode(0x00)); + // * returns 2: '00' + var v,i, f = 0, a = []; + s += ''; + f = s.length; + + for (i = 0; i<f; i++) { + a[i] = s.charCodeAt(i).toString(16).replace(/^([\da-f])$/,"0$1"); + } + + return a.join(''); + } + + function groupChangeMember(gid,cid) { + $('body .fakelink').css('cursor', 'wait'); + $.get('group/' + gid + '/' + cid, function(data) { + $('#group-update-wrapper').html(data); + $('body .fakelink').css('cursor', 'auto'); + }); + } + + function profChangeMember(gid,cid) { + $('body .fakelink').css('cursor', 'wait'); + $.get('profperm/' + gid + '/' + cid, function(data) { + $('#prof-update-wrapper').html(data); + $('body .fakelink').css('cursor', 'auto'); + }); + } + diff --git a/include/nav.php b/include/nav.php index 4104cf3cf..66fdbc49b 100644 --- a/include/nav.php +++ b/include/nav.php @@ -27,24 +27,23 @@ function nav(&$a) { $myident = ((is_array($a->user) && isset($a->user['nickname'])) ? $a->user['nickname'] . '@' : ''); - $a->page['nav'] .= '<div id="site-location">' . $myident . substr($a->get_baseurl(),strpos($a->get_baseurl(),'//') + 2 ) . '</div>'; + $sitelocation = $myident . substr($a->get_baseurl(),strpos($a->get_baseurl(),'//') + 2 ); + // nav links: array of array('href', 'text', 'extra css classes') + $nav = Array(); + /** * Display login or logout */ if(local_user()) { - $a->page['nav'] .= '<a id="nav-logout-link" class="nav-link" href="logout">' . t('Logout') . "</a>\r\n"; + $nav['logout'] = Array('logout',t('Logout'), ""); } else { - $a->page['nav'] .= '<a id="nav-login-link" class="nav-login-link'; - if ($a->module == 'login') { $a->page['nav'] .= ' nav-selected'; } - - $a->page['nav'] .= '" href="login">' . t('Login') . "</a>\r\n"; + $nav['login'] = Array('login',t('Login'), ($a->module == 'login'?'nav-selected':'')); } - $a->page['nav'] .= "<span id=\"nav-link-wrapper\" >\r\n"; /** * "Home" should also take you home from an authenticated remote profile connection @@ -53,18 +52,22 @@ function nav(&$a) { $homelink = ((x($_SESSION,'visitor_home')) ? $_SESSION['visitor_home'] : ''); if(($a->module != 'home') && (! (local_user()))) - $a->page['nav'] .= '<a id="nav-home-link" class="nav-commlink" href="' . $homelink . '">' . t('Home') . "</a>\r\n"; + $nav['home'] = array($homelink, t('Home'), ""); if(($a->config['register_policy'] == REGISTER_OPEN) && (! local_user()) && (! remote_user())) - $a->page['nav'] .= '<a id="nav-register-link" class="nav-commlink" href="register" >' - . t('Register') . "</a>\r\n"; + $nav['register'] = array('register',t('Register'), ""); + + $help_url = $a->get_baseurl() . '/help'; + + if(! get_config('system','hide-help')) + $nav['help'] = array($help_url, t('Help'), ""); + - if(strlen($a->apps)) { - $a->page['nav'] .= '<a id="nav-apps-link" class="nav-link" href="apps">' . t('Apps') . "</a>\r\n"; - } - $a->page['nav'] .= '<a id="nav-search-link" class="nav-link" href="search">' . t('Search') . "</a>\r\n"; + $nav['apps'] = array('apps', t('Apps'), ""); + + $nav['search'] = array('search', t('Search'), ""); $gdirpath = 'directory'; @@ -74,7 +77,7 @@ function nav(&$a) { $gdirpath = $gdir; } - $a->page['nav'] .= '<a id="nav-directory-link" class="nav-link" href="' . $gdirpath . '">' . t('Directory') . "</a>\r\n"; + $nav['directory'] = array($gdirpath, t('Directory'), ""); /** * @@ -84,36 +87,30 @@ function nav(&$a) { if(local_user()) { - $a->page['nav'] .= '<a id="nav-network-link" class="nav-commlink" href="network">' . t('Network') - . '</a><span id="net-update" class="nav-ajax-left"></span>' . "\r\n"; + $nav['network'] = array('network', t('Network'), ""); + + $nav['home'] = array('profile/' . $a->user['nickname'], t('Home'), ""); - $a->page['nav'] .= '<a id="nav-home-link" class="nav-commlink" href="profile/' . $a->user['nickname'] . '">' - . t('Home') . '</a><span id="home-update" class="nav-ajax-left"></span>' . "\r\n"; /* only show friend requests for normal pages. Other page types have automatic friendship. */ if($_SESSION['page_flags'] == PAGE_NORMAL) { - $a->page['nav'] .= '<a id="nav-notify-link" class="nav-commlink" href="notifications">' . t('Notifications') - . '</a><span id="notify-update" class="nav-ajax-left"></span>' . "\r\n"; + $nav['notifications'] = array('notifications', t('Notifications'), ""); } - $a->page['nav'] .= '<a id="nav-messages-link" class="nav-commlink" href="message">' . t('Messages') - . '</a><span id="mail-update" class="nav-ajax-left"></span>' . "\r\n"; + $nav['messages'] = array('message', t('Messages'), ""); if(is_array($a->identities) && count($a->identities) > 1) { - $a->page['nav'] .= '<a id="nav-manage-link" class="nav-commlink" href="manage">' . t('Manage') . '</a>' . "\r\n"; + $nav['manage'] = array('manage', t('Manage'), ""); } - $a->page['nav'] .= '<a id="nav-settings-link" class="nav-link" href="settings">' . t('Settings') . "</a>\r\n"; - - $a->page['nav'] .= '<a id="nav-profiles-link" class="nav-link" href="profiles">' . t('Profiles') . "</a>\r\n"; - - $a->page['nav'] .= '<a id="nav-contacts-link" class="nav-link" href="contacts">' . t('Contacts') . "</a>\r\n"; + $nav['settings'] = array('settings', t('Settings'),""); + $nav['profiles'] = array('profiles', t('Profiles'),""); + $nav['contacts'] = array('contacts', t('Contacts'),""); } - $a->page['nav'] .= "</span>\r\n<span id=\"nav-end\"></span>\r\n"; /** * @@ -124,10 +121,17 @@ function nav(&$a) { $banner = get_config('system','banner'); if($banner === false) - $banner .= '<a href="http://friendika.com"><img id="logo-img" src="images/friendika-32.png" alt="logo" /></a><span id="logo-text"><a href="http://friendika.com">Friendika</a></span>'; + $banner .= '<a href="http://project.friendika.com"><img id="logo-img" src="images/friendika-32.png" alt="logo" /></a><span id="logo-text"><a href="http://project.friendika.com">Friendika</a></span>'; + + $tpl = get_markup_template('nav.tpl'); - $a->page['nav'] .= '<span id="banner">' . $banner . '</span>'; + $a->page['nav'] .= replace_macros($tpl, array( + '$langselector' => lang_selector(), + '$sitelocation' => $sitelocation, + '$nav' => $nav, + '$banner' => $banner, + )); call_hooks('page_header', $a->page['nav']); diff --git a/include/notifier.php b/include/notifier.php index 648a07062..db2542849 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -20,6 +20,8 @@ function notifier_run($argv, $argc){ require_once('include/items.php'); require_once('include/bbcode.php'); + load_hooks(); + if($argc < 3) return; @@ -40,6 +42,7 @@ function notifier_run($argv, $argc){ break; } + $expire = false; $top_level = false; $recipients = array(); $url_recipients = array(); @@ -57,10 +60,21 @@ function notifier_run($argv, $argc){ $item = $message[0]; } + elseif($cmd === 'expire') { + $expire = true; + $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1 + AND `deleted` = 1 AND `changed` > UTC_TIMESTAMP - INTERVAL 10 MINUTE", + intval($item_id) + ); + $uid = $item_id; + $item_id = 0; + if(! count($items)) + return; + } else { // find ancestors - $r = q("SELECT `parent`, `uid`, `edited` FROM `item` WHERE `id` = %d LIMIT 1", + $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1", intval($item_id) ); @@ -68,6 +82,7 @@ function notifier_run($argv, $argc){ return; } + $parent_item = $r[0]; $parent_id = intval($r[0]['parent']); $uid = $r[0]['uid']; $updated = $r[0]['edited']; @@ -76,11 +91,10 @@ function notifier_run($argv, $argc){ intval($parent_id) ); - if(! count($items)){ + if(! count($items)) { return; } - // avoid race condition with deleting entries if($items[0]['deleted']) { @@ -98,11 +112,11 @@ function notifier_run($argv, $argc){ intval($uid) ); - if(count($r)) - $owner = $r[0]; - else { + if(! count($r)) return; - } + + $owner = $r[0]; + $hub = get_config('system','huburl'); // If this is a public conversation, notify the feed hub @@ -117,7 +131,7 @@ function notifier_run($argv, $argc){ $parent = $items[0]; - if($parent['type'] === 'remote') { + if($parent['type'] === 'remote' && (! $expire)) { // local followup to remote post $followup = true; $notify_hub = false; // not public @@ -177,8 +191,8 @@ function notifier_run($argv, $argc){ $contacts = $r; } - $feed_template = load_view_file('view/atom_feed.tpl'); - $mail_template = load_view_file('view/atom_mail.tpl'); + $feed_template = get_markup_template('atom_feed.tpl'); + $mail_template = get_markup_template('atom_mail.tpl'); $atom = ''; $slaps = array(); @@ -235,6 +249,7 @@ function notifier_run($argv, $argc){ } else { foreach($items as $item) { + if(! $item['parent']) continue; @@ -242,9 +257,9 @@ function notifier_run($argv, $argc){ if(! $contact) continue; - $atom .= atom_entry($item,'text',$contact,$owner,true); + $atom .= atom_entry($item,'text',$contact,$owner,true); - if(($top_level) && ($notify_hub) && ($item['author-link'] === $item['owner-link'])) + if(($top_level) && ($notify_hub) && ($item['author-link'] === $item['owner-link']) && (! $expire)) $slaps[] = atom_entry($item,'html',$contact,$owner,true); } } @@ -255,12 +270,30 @@ function notifier_run($argv, $argc){ logger('notifier: slaps: ' . print_r($slaps,true), LOGGER_DATA); + // If this is a public message and pubmail is set on the parent, include all your email contacts + + $mail_disabled = ((function_exists('imap_open') && (! get_config('system','imap_disabled'))) ? 0 : 1); + + if(! $mail_disabled) { + if((! strlen($parent_item['allow_cid'])) && (! strlen($parent_item['allow_gid'])) + && (! strlen($parent_item['deny_cid'])) && (! strlen($parent_item['deny_gid'])) + && (intval($parent_item['pubmail']))) { + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `network` = '%s'", + intval($uid), + dbesc(NETWORK_MAIL) + ); + if(count($r)) { + foreach($r as $rr) + $recipients[] = $rr['id']; + } + } + } + if($followup) $recip_str = $parent['contact-id']; else $recip_str = implode(', ', $recipients); - $r = q("SELECT * FROM `contact` WHERE `id` IN ( %s ) AND `blocked` = 0 AND `pending` = 0 ", dbesc($recip_str) ); @@ -319,7 +352,7 @@ function notifier_run($argv, $argc){ // only send salmon if public - e.g. if it's ok to notify // a public hub, it's ok to send a salmon - if(count($slaps) && $notify_hub) { + if((count($slaps)) && ($notify_hub) && (! $expire)) { logger('notifier: slapdelivery: ' . $contact['name']); foreach($slaps as $slappy) { if($contact['notify']) { @@ -340,8 +373,82 @@ function notifier_run($argv, $argc){ } break; case 'mail': + + // WARNING: does not currently convert to RFC2047 header encodings, etc. + + $addr = $contact['addr']; + if(! strlen($addr)) + break; + + if($cmd === 'wall-new' || $cmd === 'comment-new') { + + $it = null; + if($cmd === 'wall-new') + $it = $items[0]; + else { + $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", + intval($argv[2]), + intval($uid) + ); + if(count($r)) + $it = $r[0]; + } + if(! $it) + break; + + + + $local_user = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", + intval($uid) + ); + if(! count($local_user)) + break; + + $reply_to = ''; + $r1 = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1", + intval($uid) + ); + if($r1 && $r1[0]['reply_to']) + $reply_to = $r1[0]['reply_to']; + + $subject = (($it['title']) ? $it['title'] : t("\x28no subject\x29")) ; + $headers = 'From: ' . $local_user[0]['username'] . ' <' . $local_user[0]['email'] . '>' . "\n"; + + if($reply_to) + $headers .= 'Reply-to: ' . $reply_to . "\n"; + + $headers .= 'Message-id: <' . $it['uri'] . '>' . "\n"; + + if($it['uri'] !== $it['parent-uri']) { + $header .= 'References: <' . $it['parent-uri'] . '>' . "\n"; + if(! strlen($it['title'])) { + $r = q("SELECT `title` FROM `item` WHERE `parent-uri` = '%s' LIMIT 1", + dbesc($it['parent-uri']) + ); + if(count($r)) { + $subtitle = $r[0]['title']; + if($subtitle) { + if(strncasecmp($subtitle,'RE:',3)) + $subject = $subtitle; + else + $subject = 'Re: ' . $subtitle; + } + } + } + } + + $headers .= 'MIME-Version: 1.0' . "\n"; + $headers .= 'Content-Type: text/html; charset=UTF-8' . "\n"; + $headers .= 'Content-Transfer-Encoding: 8bit' . "\n\n"; + $html = prepare_body($it); + $message = '<html><body>' . $html . '</body></html>'; + logger('notifier: email delivery to ' . $addr); + mail($addr, $subject, $message, $headers); + } + break; case 'dspr': case 'feed': + case 'face': default: break; } @@ -350,7 +457,7 @@ function notifier_run($argv, $argc){ // send additional slaps to mentioned remote tags (@foo@example.com) - if($slap && count($url_recipients) && $followup && $notify_hub) { + if($slap && count($url_recipients) && $followup && $notify_hub && (! $expire)) { foreach($url_recipients as $url) { if($url) { logger('notifier: urldelivery: ' . $url); diff --git a/include/pgettext.php b/include/pgettext.php new file mode 100644 index 000000000..2ffee70bc --- /dev/null +++ b/include/pgettext.php @@ -0,0 +1,46 @@ +<?php +/** + * translation support + */ + +// load string translation table for alternate language + +if(! function_exists('load_translation_table')) { +function load_translation_table($lang) { + global $a; + + if(file_exists("view/$lang/strings.php")) + include("view/$lang/strings.php"); +}} + +// translate string if translation exists + +if(! function_exists('t')) { +function t($s) { + + $a = get_app(); + + if(x($a->strings,$s)) { + $t = $a->strings[$s]; + return is_array($t)?$t[0]:$t; + } + return $s; +}} + +if(! function_exists('tt')){ +function tt($singular, $plural, $count){ + + $a = get_app(); + + if(x($a->strings,$singular)) { + $t = $a->strings[$singular]; + $k = string_plural_select($count); + return is_array($t)?$t[$k]:$t; + } + + if ($count!=1){ + return $plural; + } else { + return $singular; + } +}}
\ No newline at end of file diff --git a/include/poller.php b/include/poller.php index a093190a6..7490bfa81 100644 --- a/include/poller.php +++ b/include/poller.php @@ -1,4 +1,5 @@ <?php + require_once("boot.php"); function poller_run($argv, $argc){ @@ -15,20 +16,35 @@ function poller_run($argv, $argc){ unset($db_host, $db_user, $db_pass, $db_data); }; + require_once('session.php'); require_once('datetime.php'); require_once('simplepie/simplepie.inc'); require_once('include/items.php'); require_once('include/Contact.php'); + require_once('include/email.php'); $a->set_baseurl(get_config('system','url')); + load_hooks(); + logger('poller: start'); // run queue delivery process in the background proc_run('php',"include/queue.php"); + // once daily run expire in background + + $d1 = get_config('system','last_expire_day'); + $d2 = intval(datetime_convert('UTC','UTC','now','d')); + + if($d2 != intval($d1)) { + set_config('system','last_expire_day',$d2); + proc_run('php','include/expire.php'); + } + + // clear old cache q("DELETE FROM `cache` WHERE `updated` < '%s'", dbesc(datetime_convert('UTC','UTC',"now - 30 days"))); @@ -47,10 +63,12 @@ function poller_run($argv, $argc){ $sql_extra = (($manual_id) ? " AND `id` = $manual_id " : ""); + reload_plugins(); + $d = datetime_convert(); + call_hooks('cron', $d); - reload_plugins(); $contacts = q("SELECT `id` FROM `contact` WHERE ( `rel` = %d OR `rel` = %d ) AND `poll` != '' @@ -74,6 +92,9 @@ function poller_run($argv, $argc){ continue; foreach($res as $contact) { + + $xml = false; + if($manual_id) $contact['last-update'] = '0000-00-00 00:00:00'; @@ -147,25 +168,29 @@ function poller_run($argv, $argc){ : datetime_convert('UTC','UTC',$contact['last-update'], ATOM_TIME) ); - if($contact['network'] === 'dfrn') { + if($contact['network'] === NETWORK_DFRN) { $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']); if(intval($contact['duplex']) && $contact['dfrn-id']) $idtosend = '0:' . $orig_id; if(intval($contact['duplex']) && $contact['issued-id']) - $idtosend = '1:' . $orig_id; + $idtosend = '1:' . $orig_id; + + // they have permission to write to us. We already filtered this in the contact query. + $perm = 'rw'; $url = $contact['poll'] . '?dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION - . '&type=data&last_update=' . $last_update ; + . '&type=data&last_update=' . $last_update + . '&perm=' . $perm ; - $xml = fetch_url($url); + $handshake_xml = fetch_url($url); - logger('poller: handshake with url ' . $url . ' returns xml: ' . $xml, LOGGER_DATA); + logger('poller: handshake with url ' . $url . ' returns xml: ' . $handshake_xml, LOGGER_DATA); - if(! $xml) { + if(! $handshake_xml) { logger("poller: $url appears to be dead - marking for death "); // dead connection - might be a transient event, or this might // mean the software was uninstalled or the domain expired. @@ -182,7 +207,7 @@ function poller_run($argv, $argc){ continue; } - if(! strstr($xml,'<?xml')) { + if(! strstr($handshake_xml,'<?xml')) { logger('poller: response from ' . $url . ' did not contain XML.'); $r = q("UPDATE `contact` SET `last-update` = '%s' WHERE `id` = %d LIMIT 1", dbesc(datetime_convert()), @@ -192,7 +217,7 @@ function poller_run($argv, $argc){ } - $res = simplexml_load_string($xml); + $res = parse_xml_string($handshake_xml); if(intval($res->status) == 1) { logger("poller: $url replied status 1 - marking for death "); @@ -239,56 +264,181 @@ function poller_run($argv, $argc){ $final_dfrn_id = substr($final_dfrn_id,2); if($final_dfrn_id != $orig_id) { - + logger('poller: ID did not decode: ' . $contact['id'] . ' orig: ' . $orig_id . ' final: ' . $final_dfrn_id); // did not decode properly - cannot trust this site continue; } $postvars['dfrn_id'] = $idtosend; $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION; + $postvars['perm'] = 'rw'; $xml = post_url($contact['poll'],$postvars); } - else { + elseif(($contact['network'] === NETWORK_OSTATUS) + || ($contact['network'] === NETWORK_DIASPORA) + || ($contact['network'] === NETWORK_FEED) ) { + + // Upgrading DB fields from an older Friendika version + // Will only do this once per notify-enabled OStatus contact - // $contact['network'] !== 'dfrn' + if(($contact['notify']) && (! $contact['writable'])) { + q("UPDATE `contact` SET `writable` = 1 WHERE `id` = %d LIMIT 1", + intval($contact['id']) + ); + } $xml = fetch_url($contact['poll']); } + elseif($contact['network'] === NETWORK_MAIL) { - logger('poller: received xml : ' . $xml, LOGGER_DATA); + $mail_disabled = ((function_exists('imap_open') && (! get_config('system','imap_disabled'))) ? 0 : 1); + if($mail_disabled) + continue; - if(! strstr($xml,'<?xml')) { - logger('poller: post_handshake: response from ' . $url . ' did not contain XML.'); - $r = q("UPDATE `contact` SET `last-update` = '%s' WHERE `id` = %d LIMIT 1", - dbesc(datetime_convert()), - intval($contact['id']) + $mbox = null; + $x = q("SELECT `prvkey` FROM `user` WHERE `uid` = %d LIMIT 1", + intval($importer_uid) ); - continue; + $mailconf = q("SELECT * FROM `mailacct` WHERE `server` != '' AND `uid` = %d LIMIT 1", + intval($importer_uid) + ); + if(count($x) && count($mailconf)) { + $mailbox = construct_mailbox_name($mailconf[0]); + $password = ''; + openssl_private_decrypt(hex2bin($mailconf[0]['pass']),$password,$x[0]['prvkey']); + $mbox = email_connect($mailbox,$mailconf[0]['user'],$password); + unset($password); + if($mbox) { + q("UPDATE `mailacct` SET `last_check` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1", + dbesc(datetime_convert()), + intval($mailconf[0]['id']), + intval($importer_uid) + ); + } + } + if($mbox) { + + $msgs = email_poll($mbox,$contact['addr']); + + if(count($msgs)) { + foreach($msgs as $msg_uid) { + $datarray = array(); + $meta = email_msg_meta($mbox,$msg_uid); + $headers = email_msg_headers($mbox,$msg_uid); + + // look for a 'references' header and try and match with a parent item we have locally. + + $raw_refs = ((x($headers,'references')) ? str_replace("\t",'',$headers['references']) : ''); + $datarray['uri'] = trim($meta->message_id,'<>'); + + if($raw_refs) { + $refs_arr = explode(' ', $raw_refs); + if(count($refs_arr)) { + for($x = 0; $x < count($refs_arr); $x ++) + $refs_arr[$x] = "'" . str_replace(array('<','>',' '),array('','',''),dbesc($refs_arr[$x])) . "'"; + } + $qstr = implode(',',$refs_arr); + $r = q("SELECT `uri` , `parent-uri` FROM `item` WHERE `uri` IN ( $qstr ) AND `uid` = %d LIMIT 1", + intval($importer_uid) + ); + if(count($r)) + $datarray['parent-uri'] = $r[0]['uri']; + } + + + if(! x($datarray,'parent-uri')) + $datarray['parent-uri'] = $datarray['uri']; + + // Have we seen it before? + $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `uri` = '%s' LIMIT 1", + intval($importer_uid), + dbesc($datarray['uri']) + ); + + if(count($r)) { + if($meta->deleted && ! $r[0]['deleted']) { + q("UPDATE `item` SET `deleted` = 1, `changed` = '%s' WHERE `id` = %d LIMIT 1", + dbesc(datetime_convert()), + intval($r[0]['id']) + ); + } + continue; + } + $datarray['title'] = notags(trim($meta->subject)); + $datarray['created'] = datetime_convert('UTC','UTC',$meta->date); + + $r = email_get_msg($mbox,$msg_uid); + if(! $r) + continue; + $datarray['body'] = escape_tags($r['body']); + + // some mailing lists have the original author as 'from' - add this sender info to msg body. + // todo: adding a gravatar for the original author would be cool + + if(! stristr($meta->from,$contact['addr'])) + $datarray['body'] = t('From: ') . escape_tags($meta->from) . "\n\n" . $datarray['body']; + + $datarray['uid'] = $importer_uid; + $datarray['contact-id'] = $contact['id']; + if($datarray['parent-uri'] === $datarray['uri']) + $datarray['private'] = 1; + $datarray['author-name'] = $contact['name']; + $datarray['author-link'] = 'mailbox'; + $datarray['author-avatar'] = $contact['photo']; + + $stored_item = item_store($datarray); + q("UPDATE `item` SET `last-child` = 0 WHERE `parent-uri` = '%s' AND `uid` = %d", + dbesc($datarray['parent-uri']), + intval($importer_uid) + ); + q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d LIMIT 1", + intval($stored_item) + ); + } + } + + imap_close($mbox); + } + } + elseif($contact['network'] === NETWORK_FACEBOOK) { + // TODO: work in progress } - consume_feed($xml,$importer,$contact,$hub,1); + if($xml) { + logger('poller: received xml : ' . $xml, LOGGER_DATA); - // do it twice. Ensures that children of parents which may be later in the stream aren't tossed + if(! strstr($xml,'<?xml')) { + logger('poller: post_handshake: response from ' . $url . ' did not contain XML.'); + $r = q("UPDATE `contact` SET `last-update` = '%s' WHERE `id` = %d LIMIT 1", + dbesc(datetime_convert()), + intval($contact['id']) + ); + continue; + } - consume_feed($xml,$importer,$contact,$hub,1); + consume_feed($xml,$importer,$contact,$hub,1, true); - if((strlen($hub)) && ($hub_update) - && (($contact['rel'] == REL_BUD) || (($contact['network'] === 'stat') && (! $contact['readonly'])))) { - logger('poller: subscribing to hub(s) : ' . $hub . ' contact name : ' . $contact['name'] . ' local user : ' . $importer['name']); - $hubs = explode(',', $hub); - if(count($hubs)) { - foreach($hubs as $h) { - $h = trim($h); - if(! strlen($h)) - continue; - subscribe_to_hub($h,$importer,$contact); + // do it twice. Ensures that children of parents which may be later in the stream aren't tossed + + consume_feed($xml,$importer,$contact,$hub,1); + + + if((strlen($hub)) && ($hub_update) && (($contact['rel'] == REL_BUD) || (($contact['network'] === NETWORK_OSTATUS) && (! $contact['readonly'])))) { + logger('poller: subscribing to hub(s) : ' . $hub . ' contact name : ' . $contact['name'] . ' local user : ' . $importer['name']); + $hubs = explode(',', $hub); + if(count($hubs)) { + foreach($hubs as $h) { + $h = trim($h); + if(! strlen($h)) + continue; + subscribe_to_hub($h,$importer,$contact); + } } } } - $updated = datetime_convert(); $r = q("UPDATE `contact` SET `last-update` = '%s', `success_update` = '%s' WHERE `id` = %d LIMIT 1", @@ -300,6 +450,7 @@ function poller_run($argv, $argc){ // loop - next contact } } + return; } diff --git a/include/profile_advanced.php b/include/profile_advanced.php new file mode 100644 index 000000000..c1dfad66f --- /dev/null +++ b/include/profile_advanced.php @@ -0,0 +1,258 @@ +<?php + +function advanced_profile(&$a) { + +$o .= ''; + +$o .= '<h2>' . t('Profile') . '</h2>'; + +if($a->profile['name']) { + $lbl_fullname = t('Full Name:'); + $fullname = $a->profile['name']; + +$o .= <<< EOT +<div id="advanced-profile-name-wrapper" > +<div id="advanced-profile-name-text">$lbl_fullname</div> +<div id="advanced-profile-name">$fullname</div> +</div> +<div id="advanced-profile-name-end"></div> +EOT; +} + +if($a->profile['gender']) { + $lbl_gender = t('Gender:'); + $gender = $a->profile['gender']; + +$o .= <<< EOT +<div id="advanced-profile-gender-wrapper" > +<div id="advanced-profile-gender-text">$lbl_gender</div> +<div id="advanced-profile-gender">$gender</div> +</div> +<div id="advanced-profile-gender-end"></div> +EOT; +} + +if(($a->profile['dob']) && ($a->profile['dob'] != '0000-00-00')) { + $lbl_birthday = t('Birthday:'); + +$o .= <<< EOT +<div id="advanced-profile-dob-wrapper" > +<div id="advanced-profile-dob-text">$lbl_birthday</div> +EOT; + +// If no year, add an arbitrary one so just we can parse the month and day. + +$year_bd_format = t('j F, Y'); +$short_bd_format = t('j F'); + +$o .= '<div id="advanced-profile-dob">' + . ((intval($a->profile['dob'])) + ? day_translate(datetime_convert('UTC','UTC',$a->profile['dob'] . ' 00:00 +00:00',$year_bd_format)) + : day_translate(datetime_convert('UTC','UTC','2001-' . substr($a->profile['dob'],6) . ' 00:00 +00:00',$short_bd_format))) + . "</div>\r\n</div>"; + +$o .= '<div id="advanced-profile-dob-end"></div>'; + +} + +if($age = age($a->profile['dob'],$a->profile['timezone'],'')) { + $lbl_age = t('Age:'); +$o .= <<< EOT +<div id="advanced-profile-age-wrapper" > +<div id="advanced-profile-age-text">$lbl_age</div> +<div id="advanced-profile-age">$age</div> +</div> +<div id="advanced-profile-age-end"></div> +EOT; +} + +if($a->profile['marital']) { + $lbl_marital = t('<span class="heart">♥</span> Status:'); + $marital = $a->profile['marital']; + +$o .= <<< EOT +<div id="advanced-profile-marital-wrapper" > +<div id="advanced-profile-marital-text">$lbl_marital</div> +<div id="advanced-profile-marital">$marital</div> +EOT; + +if($a->profile['with']) { + $with = $a->profile['with']; + $o .= "<div id=\"advanced-profile-with\">($with)</div>"; +} +$o .= <<< EOT +</div> +<div id="advanced-profile-marital-end"></div> +EOT; +} + +if($a->profile['sexual']) { + $lbl_sexual = t('Sexual Preference:'); + $sexual = $a->profile['sexual']; + +$o .= <<< EOT +<div id="advanced-profile-sexual-wrapper" > +<div id="advanced-profile-sexual-text">$lbl_sexual</div> +<div id="advanced-profile-sexual">$sexual</div> +</div> +<div id="advanced-profile-sexual-end"></div> +EOT; +} + +if($a->profile['homepage']) { + $lbl_homepage = t('Homepage:'); + $homepage = linkify($a->profile['homepage']); +$o .= <<< EOT +<div id="advanced-profile-homepage-wrapper" > +<div id="advanced-profile-homepage-text">$lbl_homepage</div> +<div id="advanced-profile-homepage">$homepage</div> +</div> +<div id="advanced-profile-homepage-end"></div> +EOT; +} + +if($a->profile['politic']) { + $lbl_politic = t('Political Views:'); + $politic = $a->profile['politic']; +$o .= <<< EOT +<div id="advanced-profile-politic-wrapper" > +<div id="advanced-profile-politic-text">$lbl_politic</div> +<div id="advanced-profile-politic">$politic</div> +</div> +<div id="advanced-profile-politic-end"></div> +EOT; +} + +if($a->profile['religion']) { + $lbl_religion = t('Religion:'); + $religion = $a->profile['religion']; +$o .= <<< EOT +<div id="advanced-profile-religion-wrapper" > +<div id="advanced-profile-religion-text">$lbl_religion</div> +<div id="advanced-profile-religion">$religion</div> +</div> +<div id="advanced-profile-religion-end"></div> +EOT; +} +if($txt = prepare_text($a->profile['about'])) { + $lbl_about = t('About:'); +$o .= <<< EOT +<div id="advanced-profile-about-wrapper" > +<div id="advanced-profile-about-text">$lbl_about</div> +<br /> +<div id="advanced-profile-about">$txt</div> +</div> +<div id="advanced-profile-about-end"></div> +EOT; +} + +if($txt = prepare_text($a->profile['interest'])) { + $lbl_interests = t('Hobbies/Interests:'); +$o .= <<< EOT +<div id="advanced-profile-interest-wrapper" > +<div id="advanced-profile-interest-text">$lbl_interests</div> +<br /> +<div id="advanced-profile-interest">$txt</div> +</div> +<div id="advanced-profile-interest-end"></div> +EOT; +} + +if($txt = prepare_text($a->profile['contact'])) { + $lbl_contact = t('Contact information and Social Networks:'); +$o .= <<< EOT +<div id="advanced-profile-contact-wrapper" > +<div id="advanced-profile-contact-text">$lbl_contact</div> +<br /> +<div id="advanced-profile-contact">$txt</div> +</div> +<div id="advanced-profile-contact-end"></div> +EOT; +} + +if($txt = prepare_text($a->profile['music'])) { + $lbl_music = t('Musical interests:'); +$o .= <<< EOT +<div id="advanced-profile-music-wrapper" > +<div id="advanced-profile-music-text">$lbl_music</div> +<br /> +<div id="advanced-profile-music">$txt</div> +</div> +<div id="advanced-profile-music-end"></div> +EOT; +} + +if($txt = prepare_text($a->profile['book'])) { + $lbl_book = t('Books, literature:'); +$o .= <<< EOT +<div id="advanced-profile-book-wrapper" > +<div id="advanced-profile-book-text">$lbl_book</div> +<br /> +<div id="advanced-profile-book">$txt</div> +</div> +<div id="advanced-profile-book-end"></div> +EOT; +} + +if($txt = prepare_text($a->profile['tv'])) { + $lbl_tv = t('Television:'); +$o .= <<< EOT +<div id="advanced-profile-tv-wrapper" > +<div id="advanced-profile-tv-text">$lbl_tv</div> +<br /> +<div id="advanced-profile-tv">$txt</div> +</div> +<div id="advanced-profile-tv-end"></div> +EOT; +} + +if($txt = prepare_text($a->profile['film'])) { + $lbl_film = t('Film/dance/culture/entertainment:'); +$o .= <<< EOT +<div id="advanced-profile-film-wrapper" > +<div id="advanced-profile-film-text">$lbl_film</div> +<br /> +<div id="advanced-profile-film">$txt</div> +</div> +<div id="advanced-profile-film-end"></div> +EOT; +} + +if($txt = prepare_text($a->profile['romance'])) { + $lbl_romance = t('Love/Romance:'); +$o .= <<< EOT +<div id="advanced-profile-romance-wrapper" > +<div id="advanced-profile-romance-text">$lbl_romance</div> +<br /> +<div id="advanced-profile-romance">$txt</div> +</div> +<div id="advanced-profile-romance-end"></div> +EOT; +} + +if($txt = prepare_text($a->profile['work'])) { + $lbl_work = t('Work/employment:'); +$o .= <<< EOT +<div id="advanced-profile-work-wrapper" > +<div id="advanced-profile-work-text">$lbl_work</div> +<br /> +<div id="advanced-profile-work">$txt</div> +</div> +<div id="advanced-profile-work-end"></div> +EOT; +} + +if($txt = prepare_text($a->profile['education'])) { + $lbl_education = t('School/education:'); +$o .= <<< EOT +<div id="advanced-profile-education-wrapper" > +<div id="advanced-profile-education-text">$lbl_education</div> +<br /> +<div id="advanced-profile-education">$txt</div> +</div> +<div id="advanced-profile-education-end"></div> +EOT; +} + +return $o; +} diff --git a/include/salmon.php b/include/salmon.php index b5c21a87b..d7060b0f7 100644 --- a/include/salmon.php +++ b/include/salmon.php @@ -161,7 +161,7 @@ EOT; $signature2 = base64url_encode($rsa->sign($data)); - $salmon_tpl = load_view_file('view/magicsig.tpl'); + $salmon_tpl = get_markup_template('magicsig.tpl'); $salmon = replace_macros($salmon_tpl,array( '$data' => $data, '$encoding' => $encoding, diff --git a/include/security.php b/include/security.php index 5e79e1edd..c74a9b4a3 100644 --- a/include/security.php +++ b/include/security.php @@ -25,7 +25,7 @@ function can_write_wall(&$a,$owner) { else { $r = q("SELECT `contact`.*, `user`.`page-flags` FROM `contact` LEFT JOIN `user` on `user`.`uid` = `contact`.`uid` WHERE `contact`.`uid` = %d AND `contact`.`id` = %d AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - AND `readonly` = 0 AND ( `contact`.`rel` IN ( %d , %d ) OR `user`.`page-flags` = %d ) LIMIT 1", + AND `user`.`blockwall` = 0 AND `readonly` = 0 AND ( `contact`.`rel` IN ( %d , %d ) OR `user`.`page-flags` = %d ) LIMIT 1", intval($owner), intval(remote_user()), intval(REL_VIP), diff --git a/include/template_processor.php b/include/template_processor.php new file mode 100644 index 000000000..efdfbaecb --- /dev/null +++ b/include/template_processor.php @@ -0,0 +1,137 @@ +<?php + + class Template { + var $r; + var $search; + var $replace; + var $stack = array(); + var $nodes = array(); + var $done = false; + + private function _build_replace($r, $prefix){ + + if(is_array($r) && count($r)) { + foreach ($r as $k => $v ) { + if (is_array($v)) + $this->_build_replace($v, "$prefix$k."); + + $this->search[] = $prefix . $k; + $this->replace[] = $v; + } + } + } + + private function _push_stack(){ + $this->stack[] = array($this->r, $this->search, $this->replace, $this->nodes); + } + private function _pop_stack(){ + list($this->r, $this->search, $this->replace, $this->nodes) = array_pop($this->stack); + } + + private function _get_var($name){ + $keys = array_map('trim',explode(".",$name)); + $val = $this->r; + foreach($keys as $k) { + $val = $val[$k]; + } + return $val; + } + + /** + * IF node + * + * {{ if <$var> }}...{{ endif }} + */ + private function _replcb_if($args){ + $val = $this->_get_var($args[2]); + return ($val?$args[3]:""); + } + + /** + * FOR node + * + * {{ for <$var> as $name }}...{{ endfor }} + * {{ for <$var> as $key=>$name }}...{{ endfor }} + */ + private function _replcb_for($args){ + $m = array_map('trim', explode(" as ", $args[2])); + list($keyname, $varname) = explode("=>",$m[1]); + if (is_null($varname)) { $varname=$keyname; $keyname=""; } + if ($m[0]=="" || $varname=="" || is_null($varname)) die("template error: 'for ".$m[0]." as ".$varname."'") ; + $vals = $this->r[$m[0]]; + $ret=""; + if (!is_array($vals)) return $ret; + foreach ($vals as $k=>$v){ + $this->_push_stack(); + $r = $this->r; + $r[$varname] = $v; + if ($keyname!='') $r[$keyname] = $k; + $ret .= $this->replace($args[3], $r); + $this->_pop_stack(); + } + return $ret; + } + + /** + * INC node + * + * {{ inc <templatefile> [with $var1=$var2] }}{{ endinc }} + */ + private function _replcb_inc($args){ + list($tplfile, $newctx) = array_map('trim', explode("with",$args[2])); + $this->_push_stack(); + $r = $this->r; + if (!is_null($newctx)) { + list($a,$b) = array_map('trim', explode("=",$newctx)); + $r[$a] = $this->_get_var($b); + } + $this->nodes = Array(); + $tpl = get_markup_template($tplfile); + $ret = $this->replace($tpl, $r); + $this->_pop_stack(); + return $ret; + + } + + private function _replcb_node($m) { + $node = $this->nodes[$m[1]]; + if (method_exists($this, "_replcb_".$node[1])){ + return call_user_func(array($this, "_replcb_".$node[1]), $node); + } else { + return ""; + } + } + + private function _replcb($m){ + $this->done = false; + $this->nodes[] = (array) $m; + return "||". (count($this->nodes)-1) ."||"; + } + + private function _build_nodes($s){ + $this->done = false; + while (!$this->done){ + $this->done=true; + $s = preg_replace_callback('|{{ *([a-z]*) *([^}]*)}}([^{]*){{ *end\1 *}}|', array($this, "_replcb"), $s); + } + krsort($this->nodes); + return $s; + } + + public function replace($s, $r) { + $this->r = $r; + $this->search = array(); + $this->replace = array(); + + $this->_build_replace($r, ""); + + #$s = str_replace(array("\n","\r"),array("§n§","§r§"),$s); + $s = $this->_build_nodes($s); + $s = preg_replace_callback('/\|\|([0-9]+)\|\|/', array($this, "_replcb_node"), $s); + $s = str_replace($this->search,$this->replace, (string) $s); + + return $s; + } + } + + $t = new Template; |