diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e1850648 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +favicon.* +.htconfig.php +\#* +*.log +*.out +*.version* +favicon.* +*~ + +#ignore reports, should be generted with every build +report/ + +#ignore config files from eclipse, we don't want IDE files in our repository +.project +.buildpath +.externalToolBuilders +.settings +#ignore OSX .DS_Store files +.DS_Store + +/nbproject/private/ + +#ignore smarty cache +/view/smarty3/compiled/ \ No newline at end of file diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 00000000..ec9372e8 --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,18 @@ +********************* +* Install Using Git * +********************* + +To install all addons using git, cd into your top level Friendica directory and + +git clone https://github.com/friendica/friendica-addons.git addon + +This will clone the entire repository in a directory called addon. They can now be activated in the plugins section of your admin panel. + +******************** +* Install Manually * +******************** + +1. Download the archive (AddonName.tgz) containing the addon you want to install. +2. Unzip the contents of the archive to your harddrive. +3. Upload the extracted directory and all it's contents to /path/to/friendica/addon. You will need to create the addon directory if this is the first addon you have installed. +4. Activate the addon in the plugins section of your admin panel. diff --git a/altpager.tgz b/altpager.tgz new file mode 100644 index 00000000..fbfd5594 Binary files /dev/null and b/altpager.tgz differ diff --git a/altpager/altpager.php b/altpager/altpager.php index 65f9c0d8..c6f537bd 100755 --- a/altpager/altpager.php +++ b/altpager/altpager.php @@ -61,6 +61,10 @@ function altpager_settings(&$a,&$s) { if(! local_user()) return; + $global = get_config("alt_pager", "global"); + if($global == 1) + return; + /* Add our stylesheet to the page so we can make our settings look nice */ $a->page['htmlhead'] .= '' . "\r\n"; @@ -87,3 +91,19 @@ function altpager_settings(&$a,&$s) { $s .= '
'; } + +function altpager_plugin_admin(&$a, &$o){ + $t = get_markup_template( "admin.tpl", "addon/altpager/" ); + $o = replace_macros($t, array( + '$submit' => t('Submit'), + '$global' => array('altpagerchoice', t('Global'), 1, t('Force global use of the alternate pager'), get_config('alt_pager', 'global') == 1), + '$individual' => array('altpagerchoice', t('Individual'), 2, t('Each user chooses whether to use the alternate pager'), get_config('alt_pager', 'global') == 0) + )); +} + +function altpager_plugin_admin_post(&$a){ + $choice = ((x($_POST,'altpagerchoice')) ? notags(trim($_POST['altpagerchoice'])) : ''); + set_config('alt_pager','global',($choice == 1 ? 1 : 0)); + info( t('Settings updated.'). EOL ); +} + diff --git a/altpager/view/admin.tpl b/altpager/view/admin.tpl new file mode 100644 index 00000000..60558808 --- /dev/null +++ b/altpager/view/admin.tpl @@ -0,0 +1,3 @@ +{{ inc field_radio.tpl with $field=$global }}{{ endinc }} +{{ inc field_radio.tpl with $field=$individual }}{{ endinc }} +
diff --git a/altpager/view/admin.tpl.old b/altpager/view/admin.tpl.old new file mode 100755 index 00000000..60558808 --- /dev/null +++ b/altpager/view/admin.tpl.old @@ -0,0 +1,3 @@ +{{ inc field_radio.tpl with $field=$global }}{{ endinc }} +{{ inc field_radio.tpl with $field=$individual }}{{ endinc }} +
diff --git a/altpager/view/smarty3/admin.tpl b/altpager/view/smarty3/admin.tpl new file mode 100644 index 00000000..e67afd73 --- /dev/null +++ b/altpager/view/smarty3/admin.tpl @@ -0,0 +1,3 @@ +{{include file="field_radio.tpl" field=$global}} +{{include file="field_radio.tpl" field=$individual}} +
diff --git a/blackout.tgz b/blackout.tgz index 4b5a5d31..6d081dc4 100644 Binary files a/blackout.tgz and b/blackout.tgz differ diff --git a/blackout/blackout.php b/blackout/blackout.php index 2cb7c041..83495611 100644 --- a/blackout/blackout.php +++ b/blackout/blackout.php @@ -93,8 +93,9 @@ function blackout_plugin_admin(&$a, &$o) { if (! is_string($myend)) { $myend = "YYYY-MM-DD:hhmm"; } $myurl = get_config('blackout','url'); if (! is_string($myurl)) { $myurl = "http://www.example.com"; } - $t = file_get_contents( dirname(__file__)."/admin.tpl" ); - $o = replace_macros($t, array( + $t = get_markup_template( "admin.tpl", "addon/blackout/" ); + + $o = replace_macros($t, array( '$submit' => t('Submit'), '$rurl' => array("rurl", "Redirect URL", $myurl, "all your visitors from the web will be redirected to this URL"), '$startdate' => array("startdate", "Begin of the Blackout
(YYYY-MM-DD hh:mm)", $mystart, "format is YYYY year, MM month, DD day, hh hour and mm minute"), diff --git a/blackout/admin.tpl b/blackout/view/admin.tpl similarity index 100% rename from blackout/admin.tpl rename to blackout/view/admin.tpl diff --git a/blackout/view/smarty3/admin.tpl b/blackout/view/smarty3/admin.tpl new file mode 100644 index 00000000..11a4d914 --- /dev/null +++ b/blackout/view/smarty3/admin.tpl @@ -0,0 +1,11 @@ +{{include file="field_input.tpl" field=$startdate}} +{{include file="field_input.tpl" field=$enddate}} +{{include file="field_input.tpl" field=$rurl}} + +
Note: The redirect will be active from the moment you +press the submit button. Users currently logged in will not be +thrown out but can't login again after logging out should the blackout is +still in place.
+ +
diff --git a/buglink.tgz b/buglink.tgz index 171a17e9..f66558f2 100755 Binary files a/buglink.tgz and b/buglink.tgz differ diff --git a/buglink/buglink.php b/buglink/buglink.php index faa97a0c..9cd35ccd 100755 --- a/buglink/buglink.php +++ b/buglink/buglink.php @@ -12,4 +12,4 @@ function buglink_install() { register_hook('page_end', 'addon/buglink/buglink.ph function buglink_uninstall() { unregister_hook('page_end', 'addon/buglink/buglink.php', 'buglink_active'); } -function buglink_active(&$a,&$b) { $b .= '
' . t('Report Bug') . '
'; } +function buglink_active(&$a,&$b) { $b .= '
' . t('Report Bug') . '
'; } diff --git a/communityhome.tgz b/communityhome.tgz index 129081b6..ab251425 100755 Binary files a/communityhome.tgz and b/communityhome.tgz differ diff --git a/communityhome/README.md b/communityhome/README.md index 3cf610ec..21f2a946 100755 --- a/communityhome/README.md +++ b/communityhome/README.md @@ -9,3 +9,26 @@ choosed to be in site directory), last ten public photos and last ten In main content is shown the community stream. This plugin doesn't honour your community page visibility site setting: the community stream is shown also if you have choose to not show the community page. + +If 'home.html' is found in your friendica root, its content is inserted +before community stream + +Each elements can be show or not. At the moment, there is no admin page +for settings, so this settings must be added to yout .htconfig.php + + + $a->config['communityhome']['showcommunitystream'] = true; + $a->config['communityhome']['showlastlike'] = true; + $a->config['communityhome']['showlastphotos'] = true; + $a->config['communityhome']['showactiveusers'] = true; + $a->config['communityhome']['showlastusers'] = true; + +If you don't want to show something, set it to false. + +Note: +----- + +- Default is "false". With no settings in .htconfig.php, nothing is +shown, except login form and content of 'home.html' + +- Active users query can be heavy for db, and on some system don't work diff --git a/communityhome/communityhome.css b/communityhome/communityhome.css index 2efb6ebd..45a65537 100755 --- a/communityhome/communityhome.css +++ b/communityhome/communityhome.css @@ -39,4 +39,5 @@ aside .directory-photo-img { max-width: 48px; max-height: 48px; } aside #likes { margin: 0px; padding: 0px; list-style: none; } -aside #login-extra-links { overflow: auto; width: 100%; padding-top:120px;} +aside #div_id_remember { overflow: auto; width: 100%; padding-top:120px;} +#login_openid input { width: 160px; } diff --git a/communityhome/communityhome.php b/communityhome/communityhome.php index 2b14fd33..ba2af6de 100755 --- a/communityhome/communityhome.php +++ b/communityhome/communityhome.php @@ -2,7 +2,7 @@ /** * Name: Community home * Description: Show last community activity in homepage - * Version: 1.0 + * Version: 2.0 * Author: Fabio Comuni */ @@ -35,151 +35,166 @@ function communityhome_home(&$a, &$o){ $aside['$login_form'] = login(($a->config['register_policy'] == REGISTER_CLOSED) ? false : true); // last 12 users - $aside['$lastusers_title'] = t('Latest users'); - $aside['$lastusers_items'] = array(); - $sql_extra = ""; - $publish = (get_config('system','publish_all') ? '' : " AND `publish` = 1 " ); - $order = " ORDER BY `register_date` DESC "; + if (get_config('communityhome','showlastusers')===true){ + $aside['$lastusers_title'] = t('Latest users'); + $aside['$lastusers_items'] = array(); + $sql_extra = ""; + $publish = (get_config('system','publish_all') ? '' : " AND `publish` = 1 " ); + $order = " ORDER BY `register_date` DESC "; - $r = q("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname` - FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` - WHERE `is-default` = 1 $publish AND `user`.`blocked` = 0 $sql_extra $order LIMIT %d , %d ", - 0, - 12 - ); - $tpl = file_get_contents( dirname(__file__).'/directory_item.tpl'); - if(count($r)) { - $photo = 'thumb'; - foreach($r as $rr) { - $profile_link = $a->get_baseurl() . '/profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']); - $entry = replace_macros($tpl,array( - '$id' => $rr['id'], - '$profile-link' => $profile_link, - '$photo' => $a->get_cached_avatar_image($rr[$photo]), - '$alt-text' => $rr['name'], - )); - $aside['$lastusers_items'][] = $entry; + $r = q("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname` + FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` + WHERE `is-default` = 1 $publish AND `user`.`blocked` = 0 $sql_extra $order LIMIT %d , %d ", + 0, + 12 + ); + # $tpl = file_get_contents( dirname(__file__).'/directory_item.tpl'); + $tpl = get_markup_template( 'directory_item.tpl', 'addon/communityhome/' ); + if(count($r)) { + $photo = 'thumb'; + foreach($r as $rr) { + $profile_link = $a->get_baseurl() . '/profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']); + $entry = replace_macros($tpl,array( + '$id' => $rr['id'], + '$profile_link' => $profile_link, + '$photo' => $a->get_cached_avatar_image($rr[$photo]), + '$alt_text' => $rr['name'], + )); + $aside['$lastusers_items'][] = $entry; + } } } - // 12 most active users (by posts and contacts) // this query don't work on some mysql versions - $r = q("SELECT `uni`.`contacts`,`uni`.`items`, `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname` FROM - (SELECT COUNT(`id`) as `contacts`, `uid` FROM `contact` WHERE `self`=0 GROUP BY `uid`) AS `con`, - (SELECT COUNT(`id`) as `items`, `uid` FROM `item` WHERE `item`.`changed` > DATE(NOW() - INTERVAL 1 MONTH) AND `item`.`wall` = 1 GROUP BY `uid`) AS `ite`, - ( - SELECT `contacts`,`items`,`ite`.`uid` FROM `con` RIGHT OUTER JOIN `ite` ON `con`.`uid`=`ite`.`uid` - UNION ALL - SELECT `contacts`,`items`,`con`.`uid` FROM `con` LEFT OUTER JOIN `ite` ON `con`.`uid`=`ite`.`uid` - ) AS `uni`, `user`, `profile` - WHERE `uni`.`uid`=`user`.`uid` - AND `uni`.`uid`=`profile`.`uid` AND `profile`.`publish`=1 - GROUP BY `uid` - ORDER BY `items` DESC,`contacts` DESC - LIMIT 0,10"); - if($r && count($r)) { - $aside['$activeusers_title'] = t('Most active users'); - $aside['$activeusers_items'] = array(); - - $photo = 'thumb'; - foreach($r as $rr) { - $profile_link = $a->get_baseurl() . '/profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']); - $entry = replace_macros($tpl,array( - '$id' => $rr['id'], - '$profile-link' => $profile_link, - '$photo' => $rr[$photo], - '$alt-text' => sprintf("%s (%s posts, %s contacts)",$rr['name'], ($rr['items']?$rr['items']:'0'), ($rr['contacts']?$rr['contacts']:'0')) - )); - $aside['$activeusers_items'][] = $entry; + if (get_config('communityhome','showactiveusers')===true){ + $r = q("SELECT `uni`.`contacts`,`uni`.`items`, `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname` FROM + (SELECT COUNT(`id`) as `contacts`, `uid` FROM `contact` WHERE `self`=0 GROUP BY `uid`) AS `con`, + (SELECT COUNT(`id`) as `items`, `uid` FROM `item` WHERE `item`.`changed` > DATE(NOW() - INTERVAL 1 MONTH) AND `item`.`wall` = 1 GROUP BY `uid`) AS `ite`, + ( + SELECT `contacts`,`items`,`ite`.`uid` FROM `con` RIGHT OUTER JOIN `ite` ON `con`.`uid`=`ite`.`uid` + UNION ALL + SELECT `contacts`,`items`,`con`.`uid` FROM `con` LEFT OUTER JOIN `ite` ON `con`.`uid`=`ite`.`uid` + ) AS `uni`, `user`, `profile` + WHERE `uni`.`uid`=`user`.`uid` + AND `uni`.`uid`=`profile`.`uid` AND `profile`.`publish`=1 + GROUP BY `uid` + ORDER BY `items` DESC,`contacts` DESC + LIMIT 0,10"); + if($r && count($r)) { + $aside['$activeusers_title'] = t('Most active users'); + $aside['$activeusers_items'] = array(); + + $photo = 'thumb'; + foreach($r as $rr) { + $profile_link = $a->get_baseurl() . '/profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']); + $entry = replace_macros($tpl,array( + '$id' => $rr['id'], + '$profile_link' => $profile_link, + '$photo' => $rr[$photo], + '$alt_text' => sprintf("%s (%s posts, %s contacts)",$rr['name'], ($rr['items']?$rr['items']:'0'), ($rr['contacts']?$rr['contacts']:'0')) + )); + $aside['$activeusers_items'][] = $entry; + } } } - // last 12 photos - $aside['$photos_title'] = t('Latest photos'); - $aside['$photos_items'] = array(); - $r = q("SELECT `photo`.`id`, `photo`.`resource-id`, `photo`.`scale`, `photo`.`desc`, `user`.`nickname`, `user`.`username` FROM - (SELECT `resource-id`, MAX(`scale`) as maxscale FROM `photo` - WHERE `profile`=0 AND `contact-id`=0 AND `album` NOT IN ('Contact Photos', '%s', 'Profile Photos', '%s') - AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`='' GROUP BY `resource-id`) AS `t1` - INNER JOIN `photo` ON `photo`.`resource-id`=`t1`.`resource-id` AND `photo`.`scale` = `t1`.`maxscale`, - `user` - WHERE `user`.`uid` = `photo`.`uid` - AND `user`.`blockwall`=0 - ORDER BY `photo`.`edited` DESC - LIMIT 0, 12", - dbesc(t('Contact Photos')), - dbesc(t('Profile Photos')) - ); + if (get_config('communityhome','showlastphotos')===true){ + $aside['$photos_title'] = t('Latest photos'); + $aside['$photos_items'] = array(); + $r = q("SELECT `photo`.`id`, `photo`.`resource-id`, `photo`.`scale`, `photo`.`desc`, `user`.`nickname`, `user`.`username` FROM + (SELECT `resource-id`, MAX(`scale`) as maxscale FROM `photo` + WHERE `profile`=0 AND `contact-id`=0 AND `album` NOT IN ('Contact Photos', '%s', 'Profile Photos', '%s') + AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`='' GROUP BY `resource-id`) AS `t1` + INNER JOIN `photo` ON `photo`.`resource-id`=`t1`.`resource-id` AND `photo`.`scale` = `t1`.`maxscale`, + `user` + WHERE `user`.`uid` = `photo`.`uid` + AND `user`.`blockwall`=0 + AND `user`.`hidewall` = 0 + ORDER BY `photo`.`edited` DESC + LIMIT 0, 12", + dbesc(t('Contact Photos')), + dbesc(t('Profile Photos')) + ); - - if(count($r)) { - $tpl = file_get_contents( dirname(__file__).'/directory_item.tpl'); - foreach($r as $rr) { - $photo_page = $a->get_baseurl() . '/photos/' . $rr['nickname'] . '/image/' . $rr['resource-id']; - $photo_url = $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['scale'] .'.jpg'; - - $entry = replace_macros($tpl,array( - '$id' => $rr['id'], - '$profile-link' => $photo_page, - '$photo' => $photo_url, - '$alt-text' => $rr['username']." : ".$rr['desc'], - )); + + if(count($r)) { + # $tpl = file_get_contents( dirname(__file__).'/directory_item.tpl'); + $tpl = get_markup_template( 'directory_item.tpl', 'addon/communityhome/' ); + foreach($r as $rr) { + $photo_page = $a->get_baseurl() . '/photos/' . $rr['nickname'] . '/image/' . $rr['resource-id']; + $photo_url = $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['scale'] .'.jpg'; + + $entry = replace_macros($tpl,array( + '$id' => $rr['id'], + '$profile_link' => $photo_page, + '$photo' => $photo_url, + '$alt_text' => $rr['username']." : ".$rr['desc'], + )); - $aside['$photos_items'][] = $entry; + $aside['$photos_items'][] = $entry; + } } } // last 10 liked items - $aside['$like_title'] = t('Latest likes'); - $aside['$like_items'] = array(); - $r = q("SELECT `T1`.`created`, `T1`.`liker`, `T1`.`liker-link`, `item`.* FROM - (SELECT `parent-uri`, `created`, `author-name` AS `liker`,`author-link` AS `liker-link` - FROM `item` WHERE `verb`='http://activitystrea.ms/schema/1.0/like' GROUP BY `parent-uri` ORDER BY `created` DESC) AS T1 - INNER JOIN `item` ON `item`.`uri`=`T1`.`parent-uri` - WHERE `T1`.`liker-link` LIKE '%s%%' OR `item`.`author-link` LIKE '%s%%' - GROUP BY `uri` - ORDER BY `T1`.`created` DESC - LIMIT 0,10", - $a->get_baseurl(),$a->get_baseurl() - ); + if (get_config('communityhome','showlastlike')===true){ + $aside['$like_title'] = t('Latest likes'); + $aside['$like_items'] = array(); + $r = q("SELECT `T1`.`created`, `T1`.`liker`, `T1`.`liker-link`, `item`.* FROM + (SELECT `parent-uri`, `created`, `author-name` AS `liker`,`author-link` AS `liker-link` + FROM `item` WHERE `verb`='http://activitystrea.ms/schema/1.0/like' GROUP BY `parent-uri` ORDER BY `created` DESC) AS T1 + INNER JOIN `item` ON `item`.`uri`=`T1`.`parent-uri` + WHERE `T1`.`liker-link` LIKE '%s%%' OR `item`.`author-link` LIKE '%s%%' + GROUP BY `uri` + ORDER BY `T1`.`created` DESC + LIMIT 0,10", + $a->get_baseurl(),$a->get_baseurl() + ); - foreach ($r as $rr) { - $author = '' . $rr['liker'] . ''; - $objauthor = '' . $rr['author-name'] . ''; - - //var_dump($rr['verb'],$rr['object-type']); killme(); - switch($rr['verb']){ - case 'http://activitystrea.ms/schema/1.0/post': - switch ($rr['object-type']){ - case 'http://activitystrea.ms/schema/1.0/event': - $post_type = t('event'); - break; - default: + foreach ($r as $rr) { + $author = '' . $rr['liker'] . ''; + $objauthor = '' . $rr['author-name'] . ''; + + //var_dump($rr['verb'],$rr['object-type']); killme(); + switch($rr['verb']){ + case 'http://activitystrea.ms/schema/1.0/post': + switch ($rr['object-type']){ + case 'http://activitystrea.ms/schema/1.0/event': + $post_type = t('event'); + break; + default: + $post_type = t('status'); + } + break; + default: + if ($rr['resource-id']){ + $post_type = t('photo'); + $m=array(); preg_match("/\[url=([^]]*)\]/", $rr['body'], $m); + $rr['plink'] = $m[1]; + } else { $post_type = t('status'); - } - break; - default: - if ($rr['resource-id']){ - $post_type = t('photo'); - $m=array(); preg_match("/\[url=([^]]*)\]/", $rr['body'], $m); - $rr['plink'] = $m[1]; - } else { - $post_type = t('status'); - } - } - $plink = '' . $post_type . ''; + } + } + $plink = '' . $post_type . ''; - $aside['$like_items'][] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink); - + $aside['$like_items'][] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink); + + } } - $tpl = file_get_contents(dirname(__file__).'/communityhome.tpl'); +# $tpl = file_get_contents(dirname(__file__).'/communityhome.tpl'); + $tpl = get_markup_template('communityhome.tpl', 'addon/communityhome/'); $a->page['aside'] = replace_macros($tpl, $aside); $o = '

' . ((x($a->config,'sitename')) ? sprintf( t("Welcome to %s") ,$a->config['sitename']) : "" ) . '

'; - $oldset = get_config('system','no_community_page'); - set_config('system','no_community_page', false); - $o .= community_content($a,1); - set_config('system','no_community_page', $oldset); + if(file_exists('home.html')) + $o = file_get_contents('home.html'); + + if (get_config('communityhome','showcommunitystream')===true){ + $oldset = get_config('system','no_community_page'); + set_config('system','no_community_page', false); + $o .= community_content($a,1); + set_config('system','no_community_page', $oldset); + } } diff --git a/communityhome/twillingham/README b/communityhome/twillingham/README deleted file mode 100644 index dbbe1419..00000000 --- a/communityhome/twillingham/README +++ /dev/null @@ -1,3 +0,0 @@ -This is a variant of the community home. Instead of displaying the community tab in the front page, we still use home.html, but we also add the latest users to the sidebar. - -Simply replace addon/communityhome/communityhome.php with this version then enable community home in your admin panel as usual. \ No newline at end of file diff --git a/communityhome/twillingham/communityhome.php b/communityhome/twillingham/communityhome.php deleted file mode 100644 index 102732ac..00000000 --- a/communityhome/twillingham/communityhome.php +++ /dev/null @@ -1,107 +0,0 @@ - - */ - - -require_once('mod/community.php'); - - -function communityhome_install() { - register_hook('home_content', 'addon/communityhome/communityhome.php', 'communityhome_home'); - logger("installed communityhome"); -} - -function communityhome_uninstall() { - unregister_hook('home_content', 'addon/communityhome/communityhome.php', 'communityhome_home'); - logger("removed communityhome"); -} - -function communityhome_home(&$a, &$o){ - // custom css - $a->page['htmlhead'] .= ''; - - $aside = array( - '$tab_1' => t('Login'), - '$tab_2' => t('OpenID'), - '$noOid' => get_config('system','no_openid'), - ); - - // login form - $aside['$login_title'] = t('Login'); - $aside['$login_form'] = login(($a->config['register_policy'] == REGISTER_CLOSED) ? false : true); - - // last 12 users - $aside['$lastusers_title'] = t('Latest users'); - $aside['$lastusers_items'] = array(); - $sql_extra = ""; - $publish = (get_config('system','publish_all') ? '' : " AND `publish` = 1 " ); - $order = " ORDER BY `register_date` DESC "; - - $r = q("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname` - FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` - WHERE `is-default` = 1 $publish AND `user`.`blocked` = 0 $sql_extra $order LIMIT %d , %d ", - 0, - 12 - ); - $tpl = file_get_contents( dirname(__file__).'/directory_item.tpl'); - if(count($r)) { - $photo = 'thumb'; - foreach($r as $rr) { - $profile_link = $a->get_baseurl() . '/profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']); - $entry = replace_macros($tpl,array( - '$id' => $rr['id'], - '$profile-link' => $profile_link, - '$photo' => $rr[$photo], - '$alt-text' => $rr['name'], - )); - $aside['$lastusers_items'][] = $entry; - } - } - - // 12 most active users (by posts and contacts) - // this query don't work on some mysql versions - $r = q("SELECT `uni`.`contacts`,`uni`.`items`, `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname` FROM - (SELECT COUNT(`id`) as `contacts`, `uid` FROM `contact` WHERE `self`=0 GROUP BY `uid`) AS `con`, - (SELECT COUNT(`id`) as `items`, `uid` FROM `item` WHERE `item`.`changed` > DATE(NOW() - INTERVAL 1 MONTH) AND `item`.`wall` = 1 GROUP BY `uid`) AS `ite`, - ( - SELECT `contacts`,`items`,`ite`.`uid` FROM `con` RIGHT OUTER JOIN `ite` ON `con`.`uid`=`ite`.`uid` - UNION ALL - SELECT `contacts`,`items`,`con`.`uid` FROM `con` LEFT OUTER JOIN `ite` ON `con`.`uid`=`ite`.`uid` - ) AS `uni`, `user`, `profile` - WHERE `uni`.`uid`=`user`.`uid` - AND `uni`.`uid`=`profile`.`uid` AND `profile`.`publish`=1 - GROUP BY `uid` - ORDER BY `items` DESC,`contacts` DESC - LIMIT 0,10"); - if($r && count($r)) { - $aside['$activeusers_title'] = t('Most active users'); - $aside['$activeusers_items'] = array(); - - $photo = 'thumb'; - foreach($r as $rr) { - $profile_link = $a->get_baseurl() . '/profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']); - $entry = replace_macros($tpl,array( - '$id' => $rr['id'], - '$profile-link' => $profile_link, - '$photo' => $rr[$photo], - '$alt-text' => sprintf("%s (%s posts, %s contacts)",$rr['name'], ($rr['items']?$rr['items']:'0'), ($rr['contacts']?$rr['contacts']:'0')) - )); - $aside['$activeusers_items'][] = $entry; - } - } - - - - - $tpl = file_get_contents(dirname(__file__).'/communityhome.tpl'); - $a->page['aside'] = replace_macros($tpl, $aside); - $o = ''; - if(file_exists('home.html')) - - $o .= file_get_contents('home.html'); - -} diff --git a/communityhome/communityhome.tpl b/communityhome/view/communityhome.tpl similarity index 100% rename from communityhome/communityhome.tpl rename to communityhome/view/communityhome.tpl diff --git a/communityhome/directory_item.tpl b/communityhome/view/directory_item.tpl similarity index 62% rename from communityhome/directory_item.tpl rename to communityhome/view/directory_item.tpl index db1936e4..f32f5a4f 100755 --- a/communityhome/directory_item.tpl +++ b/communityhome/view/directory_item.tpl @@ -2,8 +2,8 @@
diff --git a/communityhome/view/smarty3/communityhome.tpl b/communityhome/view/smarty3/communityhome.tpl new file mode 100644 index 00000000..b8f80381 --- /dev/null +++ b/communityhome/view/smarty3/communityhome.tpl @@ -0,0 +1,70 @@ + +{{if $noOid}} +

{{$login_title}}

+{{else}} + +{{/if}} +{{$login_form}} + + +{{if $lastusers_title}} +

{{$lastusers_title}}

+
+{{foreach $lastusers_items as $i}} + {{$i}} +{{/foreach}} +
+{{/if}} + + +{{if $activeusers_title}} +

{{$activeusers_title}}

+
+{{foreach $activeusers_items as $i}} + {{$i}} +{{/foreach}} +
+{{/if}} + +{{if $photos_title}} +

{{$photos_title}}

+
+{{foreach $photos_items as $i}} + {{$i}} +{{/foreach}} +
+{{/if}} + + +{{if $like_title}} +

{{$like_title}}

+ +{{/if}} diff --git a/communityhome/view/smarty3/directory_item.tpl b/communityhome/view/smarty3/directory_item.tpl new file mode 100644 index 00000000..5fb11986 --- /dev/null +++ b/communityhome/view/smarty3/directory_item.tpl @@ -0,0 +1,10 @@ + +
+
+
+ + {{$alt}}-text + +
+
+
diff --git a/convert.tgz b/convert.tgz index 56e0080c..766eaf7a 100755 Binary files a/convert.tgz and b/convert.tgz differ diff --git a/curweather.tgz b/curweather.tgz new file mode 100644 index 00000000..76e0646c Binary files /dev/null and b/curweather.tgz differ diff --git a/curweather/curweather.css b/curweather/curweather.css new file mode 100644 index 00000000..6c127963 --- /dev/null +++ b/curweather/curweather.css @@ -0,0 +1,10 @@ + +#curtemp-settings-label, #curtemp-location-label, #curtemp-enable-label { + float: left; + width: 200px; + margin-bottom: 25px; +} +#curtemp-network { + float: left; +} + diff --git a/curweather/curweather.php b/curweather/curweather.php new file mode 100644 index 00000000..c935eb57 --- /dev/null +++ b/curweather/curweather.php @@ -0,0 +1,108 @@ + Find the location code for the station or airport nearest you here. + * Version: 1.0 + * Author: Tony Baldwin + * Author: Fabio Comuni + * + */ +require_once('addon/curweather/getweather.php'); + +function curweather_install() { + register_hook('network_mod_init', 'addon/curweather/curweather.php', 'curweather_network_mod_init'); + register_hook('plugin_settings', 'addon/curweather/curweather.php', 'curweather_plugin_settings'); + register_hook('plugin_settings_post', 'addon/curweather/curweather.php', 'curweather_plugin_settings_post'); + +} + +function curweather_uninstall() { + unregister_hook('network_mod_init', 'addon/curweather/curweather.php', 'curweather_network_mod_init'); + unregister_hook('plugin_settings', 'addon/curweather/curweather.php', 'curweather_plugin_settings'); + unregister_hook('plugin_settings_post', 'addon/curweather/curweather.php', 'curweather_plugin_settings_post'); + +} + + +function curweather_network_mod_init(&$fk_app,&$b) { + + if(! intval(get_pconfig(local_user(),'curweather','curweather_enable'))) + return; + + $fk_app->page['htmlhead'] .= '' . "\r\n"; + + // the getweather file does all the work here + // the $rpt value is needed for location + // which getweather uses to fetch the weather data for weather and temp + $rpt = get_pconfig(local_user(), 'curweather', 'curweather_loc'); + $wxdata = GetWeather::get($rpt); + $temp = $wxdata['TEMPERATURE_STRING']; + $weather = $wxdata['WEATHER']; + $rhumid = $wxdata['RELATIVE_HUMIDITY']; + $pressure = $wxdata['PRESSURE_STRING']; + $wind = $wxdata['WIND_STRING']; + $curweather = '
+
+

'.t("Current Weather").'

'; + + $curweather .= "Weather: $weather
+ Temperature: $temp
+ Relative Humidity: $rhumid
+ Pressure: $pressure
+ Wind: $wind"; + + $curweather .= '
'; + + $fk_app->page['aside'] = $curweather.$fk_app->page['aside']; + +} + + +function curweather_plugin_settings_post($a,$post) { + if(! local_user() || (! x($_POST,'curweather-settings-submit'))) + return; + set_pconfig(local_user(),'curweather','curweather_loc',trim($_POST['curweather_loc'])); + set_pconfig(local_user(),'curweather','curweather_enable',intval($_POST['curweather_enable'])); + + info( t('Current Weather settings updated.') . EOL); +} + + +function curweather_plugin_settings(&$a,&$s) { + + if(! local_user()) + return; + + /* Add our stylesheet to the curweather so we can make our settings look nice */ + + $a->page['htmlhead'] .= '' . "\r\n"; + + /* Get the current state of our config variable */ + + $curweather_loc = get_pconfig(local_user(), 'curweather', 'curweather_loc'); + $enable = intval(get_pconfig(local_user(),'curweather','curweather_enable')); + $enable_checked = (($enable) ? ' checked="checked" ' : ''); + + + /* Add some HTML to the existing form */ + + $s .= '
'; + $s .= '

' . t('Current Weather Settings') . '

'; + $s .= '
'; + $s .= '

Find the location code for the airport/weather station nearest you here.

'; + $s .= ''; + $s .= ''; + $s .= '
'; + $s .= ''; + $s .= ''; + $s .= '
'; + + $s .= '
'; + + /* provide a submit button */ + + $s .= '
'; + +} + + diff --git a/curweather/getweather.php b/curweather/getweather.php new file mode 100644 index 00000000..b4660b9e --- /dev/null +++ b/curweather/getweather.php @@ -0,0 +1,230 @@ +$i){ + $a=explode(" | ",$i); + if(is_numeric(array_search($wx,$a))){ + self::$wxdata['ICON']="$imgpath/$k.jpg"; + break; + } + } + + // Replace any null elements with "Not available" + foreach(array_keys(self::$wxdata) as $key){ + self::$wxdata[$key]=self::$wxdata[$key]=="NULL"?"Not available":self::$wxdata[$key]; + } + + // If we got humidity + if(is_numeric(self::$wxdata['RELATIVE_HUMIDITY'])) + // Append a percent sign + self::$wxdata['RELATIVE_HUMIDITY'].="%"; + + // Do some formatting to make the output a little friendlier + if(self::$wxdata['VISIBILITY_MI']=="NA") + self::$wxdata['VISIBILITY']="Not available"; + if(self::$wxdata['VISIBILITY']!="Not available") + self::$wxdata['VISIBILITY']=(1*self::$wxdata['VISIBILITY_MI'])." miles"; + + // If we got wind data + if(is_numeric(self::$wxdata['WIND_MPH'])){ + // We're going to output wind data as both MPH from a cardinal direction + // and as Knots from a direction in degrees + + // Calculate the value for Knots + self::$wxdata['WIND_KNOTS']=self::$wxdata['WIND_MPH']/1.15; + + // Format the output + $wind=sprintf("From the %s at %d mph (%03.0f° at %d knots)",self::$wxdata['WIND_DIR'],self::$wxdata['WIND_MPH'],self::$wxdata['WIND_DEGREES'],self::$wxdata['WIND_KNOTS']); + + // If we got a value for wind gusts + if(is_numeric(self::$wxdata['WIND_GUST_MPH']) && self::$wxdata['WIND_GUST_MPH']>0){ + // add it into the wind string + $wind=str_replace("mph","gusting to ".self::$wxdata['WIND_GUST_MPH']." mph
", $wind); + $knots=sprintf("%d",self::$wxdata['WIND_GUST_MPH']/1.15); + $wind=str_replace("knots","gusting to $knots knots",$wind); + } + } else { + // Otherwise, if wind is zero, we'll show "Calm" + $wind=self::$wxdata['WIND_MPH']=="Not available"?"Not available":"Calm"; + } // Done with wind + self::$wxdata['WIND_STRING']=$wind; + + } // Done getting and formatting the data + return self::$wxdata; + } + + function startElement($parser, $name, $attrs) { + self::$itemname=$name; + self::$itemdata=""; + } + + function endElement($parser, $name) { + self::$wxdata[self::$itemname]=self::$itemdata; + self::$itemdata=""; + } + + function characterData($parser, $data) { + self::$itemdata.=$data; + } + + function defineIcons(){ + // See http://weather.gov/data/current_obs/weather.php for source data for this function + $retVal['bkn']="Mostly Cloudy | Mostly Cloudy with Haze | Mostly Cloudy and Breezy"; + $retVal['skc']="Fair | Clear | Fair with Haze | Clear with Haze | Fair and Breezy | Clear and Breezy"; + $retVal['few']="A Few Clouds | A Few Clouds with Haze | A Few Clouds and Breezy"; + $retVal['sct']="Partly Cloudy | Party Cloudy with Haze | Partly Cloudy and Breezy"; + $retVal['ovc']="Overcast | Overcast with Haze | Overcast and Breezy"; + $retVal['nfg']="Fog/Mist | Fog | Freezing Fog | Shallow Fog | Partial Fog | Patches of Fog | Fog in Vicinity | Freezing Fog in Vicinity | Shallow Fog in Vicinity | Partial Fog in Vicinity | Patches of Fog in Vicinity | Showers in Vicinity Fog | Light Freezing Fog | Heavy Freezing Fog"; + $retVal['smoke']="Smoke"; + $retVal['fzra']="Freezing Rain | Freezing Drizzle | Light Freezing Rain | Light Freezing Drizzle | Heavy Freezing Rain | Heavy Freezing Drizzle | Freezing Rain in Vicinity | Freezing Drizzle in Vicinity"; + $retVal['ip']="Ice Pellets | Light Ice Pellets | Heavy Ice Pellets | Ice Pellets in Vicinity | Showers Ice Pellets | Thunderstorm Ice Pellets | Ice Crystals | Hail | Small Hail/Snow Pellets | Light Small Hail/Snow Pellets | Heavy Small Hail/Snow Pellets | Showers Hail | Hail Showers"; + $retVal['mix']="Freezing Rain Snow | Light Freezing Rain Snow | Heavy Freezing Rain Snow | Freezing Drizzle Snow | Light Freezing Drizzle Snow | Heavy Freezing Drizzle Snow | Snow Freezing Rain| Light Snow Freezing Rain | Heavy Snow Freezing Rain | Snow Freezing Drizzle | Light Snow Freezing Drizzle | Heavy Snow Freezing Drizzle"; + $retVal['raip']="Rain Ice Pellets | Light Rain Ice Pellets | Heavy Rain Ice Pellets | Drizzle Ice Pellets | Light Drizzle Ice Pellets | Heavy Drizzle Ice Pellets | Ice Pellets Rain | Light Ice Pellets Rain | Heavy Ice Pellets Rain | Ice Pellets Drizzle | Light Ice Pellets Drizzle | Heavy Ice Pellets Drizzle"; + $retVal['rasn']="Rain Snow | Light Rain Snow | Heavy Rain Snow | Snow Rain | Light Snow Rain | Heavy Snow Rain | Drizzle Snow | Light Drizzle Snow | Heavy Drizzle Snow | Snow Drizzle | Light Snow Drizzle | Heavy Snow Drizzle"; + $retVal['shra']="Rain Showers | Light Rain Showers | Heavy Rain Showers | Rain Showers in Vicinity | Light Showers Rain | Heavy Showers Rain | Showers Rain | Showers Rain in Vicinity | Rain Showers Fog/Mist | Light Rain Showers Fog/Mist | Heavy Rain Showers Fog/Mist | Rain Showers in Vicinity Fog/Mist | Light Showers Rain Fog/Mist | Heavy Showers Rain Fog/Mist | Showers Rain Fog/Mist | Showers Rain in Vicinity Fog/Mist"; + $retVal['tsra']="Thunderstorm | Light Thunderstorm Rain | Heavy Thunderstorm Rain | Thunderstorm Rain Fog/Mist | Light Thunderstorm Rain Fog/Mist | Heavy Thunderstorm Rain Fog/Mist | Thunderstorm Showers in Vicinity | | Light Thunderstorm Rain Haze | Heavy Thunderstorm Rain Haze | Thunderstorm Fog | Light Thunderstorm Rain Fog | Heavy Thunderstorm Rain Fog | Thunderstorm Light Rain | Thunderstorm Heavy Rain | Thunderstorm Rain Fog/Mist | Thunderstorm Light Rain Fog/Mist | Thunderstorm Heavy Rain Fog/Mist | Thunderstorm in Vicinity Fog/Mist | Thunderstorm Showers in Vicinity | Thunderstorm in Vicinity | Thunderstorm in Vicinity Haze | Thunderstorm Haze in Vicinity | Thunderstorm Light Rain Haze | Thunderstorm Heavy Rain Haze | Thunderstorm Fog | Thunderstorm Light Rain Fog | Thunderstorm Heavy Rain Fog | Thunderstorm Hail | Light Thunderstorm Rain Hail | Heavy Thunderstorm Rain Hail | Thunderstorm Rain Hail Fog/Mist | Light Thunderstorm Rain Hail Fog/Mist | Heavy Thunderstorm Rain Hail Fog/Mist | Thunderstorm Showers in Vicinity Hail | | Light Thunderstorm Rain Hail Haze | Heavy Thunderstorm Rain Hail Haze | Thunderstorm Hail Fog | Light Thunderstorm Rain Hail Fog | Heavy Thunderstorm Rain Hail Fog | Thunderstorm Light Rain Hail | Thunderstorm Heavy Rain Hail | Thunderstorm Rain Hail Fog/Mist | Thunderstorm Light Rain Hail Fog/Mist | Thunderstorm Heavy Rain Hail Fog/Mist | Thunderstorm in Vicinity Hail Fog/Mist | Thunderstorm Showers in Vicinity Hail | Thunderstorm in Vicinity Hail | Thunderstorm in Vicinity Hail Haze | Thunderstorm Haze in Vicinity Hail | Thunderstorm Light Rain Hail Haze | Thunderstorm Heavy Rain Hail Haze | Thunderstorm Hail Fog | Thunderstorm Light Rain Hail Fog | Thunderstorm Heavy Rain Hail Fog | Thunderstorm Small Hail/Snow Pellets | Thunderstorm Rain Small Hail/Snow Pellets | Light Thunderstorm Rain Small Hail/Snow Pellets | Heavy Thunderstorm Rain Small Hail/Snow Pellets"; + $retVal['sn']="Snow | Light Snow | Heavy Snow | Snow Showers | Light Snow Showers | Heavy Snow Showers | Showers Snow | Light Showers Snow | Heavy Showers Snow | Snow Fog/Mist | Light Snow Fog/Mist | Heavy Snow Fog/Mist | Snow Showers Fog/Mist | Light Snow Showers Fog/Mist | Heavy Snow Showers Fog/Mist | Showers Snow Fog/Mist | Light Showers Snow Fog/Mist | Heavy Showers Snow Fog/Mist | Snow Fog | Light Snow Fog | Heavy Snow Fog | Snow Showers Fog | Light Snow Showers Fog | Heavy Snow Showers Fog | Showers Snow Fog | Light Showers Snow Fog | Heavy Showers Snow Fog | Showers in Vicinity Snow | Snow Showers in Vicinity | Snow Showers in Vicinity Fog/Mist | Snow Showers in Vicinity Fog | Low Drifting Snow | Blowing Snow | Snow Low Drifting Snow | Snow Blowing Snow | Light Snow Low Drifting Snow | Light Snow Blowing Snow | Heavy Snow Low Drifting Snow | Heavy Snow Blowing Snow | Thunderstorm Snow | Light Thunderstorm Snow | Heavy Thunderstorm Snow | Snow Grains | Light Snow Grains | Heavy Snow Grains | Heavy Blowing Snow | Blowing Snow in Vicinity"; + $retVal['wind']="Windy | Fair and Windy | A Few Clouds and Windy | Partly Cloudy and Windy | Mostly Cloudy and Windy | Overcast and Windy"; + $retVal['hi_shwrs']="Showers in Vicinity | Showers in Vicinity Fog/Mist | Showers in Vicinity Fog | Showers in Vicinity Haze"; + $retVal['fzrara']="Freezing Rain Rain | Light Freezing Rain Rain | Heavy Freezing Rain Rain | Rain Freezing Rain | Light Rain Freezing Rain | Heavy Rain Freezing Rain | Freezing Drizzle Rain | Light Freezing Drizzle Rain | Heavy Freezing Drizzle Rain | Rain Freezing Drizzle | Light Rain Freezing Drizzle | Heavy Rain Freezing Drizzle"; + $retVal['hi_tsra']="Thunderstorm in Vicinity | Thunderstorm in Vicinity Fog/Mist | Thunderstorm in Vicinity Fog | Thunderstorm Haze in Vicinity | Thunderstorm in Vicinity Haze"; + $retVal['ra1']="Light Rain | Drizzle | Light Drizzle | Heavy Drizzle | Light Rain Fog/Mist | Drizzle Fog/Mist | Light Drizzle Fog/Mist | Heavy Drizzle Fog/Mist | Light Rain Fog | Drizzle Fog | Light Drizzle Fog | Heavy Drizzle Fog"; + $retVal['ra']="Rain | Heavy Rain | Rain Fog/Mist | Heavy Rain Fog/Mist | Rain Fog | Heavy Rain Fog"; + $retVal['nsvrtsra']="Funnel Cloud | Funnel Cloud in Vicinity | Tornado/Water Spout"; + $retVal['dust']="Dust | Low Drifting Dust | Blowing Dust | Sand | Blowing Sand | Low Drifting Sand | Dust/Sand Whirls | Dust/Sand Whirls in Vicinity | Dust Storm | Heavy Dust Storm | Dust Storm in Vicinity | Sand Storm | Heavy Sand Storm | Sand Storm in Vicinity"; + $retVal['mist']="Haze"; + return $retVal; + } +// end CLASS +} +?> diff --git a/curweather/test.php b/curweather/test.php new file mode 100644 index 00000000..cd51c23c --- /dev/null +++ b/curweather/test.php @@ -0,0 +1,5 @@ + * diff --git a/extcron.tgz b/extcron.tgz index 0e5b2892..dabe1d09 100755 Binary files a/extcron.tgz and b/extcron.tgz differ diff --git a/extcron/extcron.php b/extcron/extcron.php index e3c21209..3eb34cdc 100755 --- a/extcron/extcron.php +++ b/extcron/extcron.php @@ -5,7 +5,7 @@ * Name: external cron * Description: Use external server or service to run poller regularly * Version: 1.0 - * Author: Mike Macgirvin + * Author: Mike Macgirvin * * Notes: External service needs to make a web request to http(s)://yoursite/extcron */ diff --git a/facebook.tgz b/facebook.tgz index ff920dc6..2dbd4efd 100644 Binary files a/facebook.tgz and b/facebook.tgz differ diff --git a/facebook/facebook.php b/facebook/facebook.php index 6786febb..4c1c0a14 100644 --- a/facebook/facebook.php +++ b/facebook/facebook.php @@ -383,10 +383,12 @@ function fb_get_friends_sync_full($uid, $access_token, $persons) { if($s) { $results = json_decode($s); logger('fb_get_friends: info: ' . print_r($results,true), LOGGER_DATA); - foreach ($results as $contact) { - if ($contact->code != 200) logger('fb_get_friends: not found: ' . print_r($contact,true), LOGGER_DEBUG); - else fb_get_friends_sync_parsecontact($uid, json_decode($contact->body)); - } + if(count($results)) { + foreach ($results as $contact) { + if ($contact->code != 200) logger('fb_get_friends: not found: ' . print_r($contact,true), LOGGER_DEBUG); + else fb_get_friends_sync_parsecontact($uid, json_decode($contact->body)); + } + } } } } @@ -1242,8 +1244,12 @@ function facebook_post_local(&$a,&$b) { $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0); // if API is used, default to the chosen settings - if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default'))) - $fb_enable = 1; + // but allow a specific override + + if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default'))) { + if(! x($_REQUEST,'facebook_enable')) + $fb_enable = 1; + } if(! $fb_enable) return; diff --git a/fbpost.tgz b/fbpost.tgz new file mode 100644 index 00000000..b118c6d7 Binary files /dev/null and b/fbpost.tgz differ diff --git a/fbpost/README.md b/fbpost/README.md new file mode 100644 index 00000000..c214360a --- /dev/null +++ b/fbpost/README.md @@ -0,0 +1,18 @@ +Installing the Friendica/Facebook connector + +Detailed instructions how to use this plugin can be found at +the [How to: Friendica's Facebook Connector](https://github.com/friendica/friendica/wiki/How-to:-Friendica%E2%80%99s-Facebook-connector) page. + +Vidoes and embeds will not be posted if there is no other content. Links +and images will be converted to a format suitable for the Facebook API and +long posts truncated - with a link to view the full post. + +Facebook contacts will not be able to view private photos, as they are not able to +authenticate to your site to establish identity. We will address this +in a future release. + +This addon will only post your entries to your Facebook account but won't fetch +content from there. + +Info: please make sure that you understand all aspects due to Friendica's +default licence which is: [MIT License](https://github.com/friendica/friendica/blob/master/LICENSE) diff --git a/fbpost/fbpost.css b/fbpost/fbpost.css new file mode 100644 index 00000000..224d27bd --- /dev/null +++ b/fbpost/fbpost.css @@ -0,0 +1,13 @@ + +#fbpost-enable-wrapper { + margin-top: 20px; +} + +#fbpost-disable-wrapper { + margin-top: 20px; +} + +#fbpost-post-default-form input { + margin-top: 20px; + margin-right: 20px; +} \ No newline at end of file diff --git a/fbpost/fbpost.php b/fbpost/fbpost.php new file mode 100644 index 00000000..ba60f4e6 --- /dev/null +++ b/fbpost/fbpost.php @@ -0,0 +1,1201 @@ + + * Author: Tobias Hößl + * + */ + +/** + * Installing the Friendica/Facebook connector + * + * Detailed instructions how to use this plugin can be found at + * https://github.com/friendica/friendica/wiki/How-to:-Friendica%E2%80%99s-Facebook-connector + * + * Vidoes and embeds will not be posted if there is no other content. Links + * and images will be converted to a format suitable for the Facebook API and + * long posts truncated - with a link to view the full post. + * + * Facebook contacts will not be able to view private photos, as they are not able to + * authenticate to your site to establish identity. We will address this + * in a future release. + */ + +define('FACEBOOK_DEFAULT_POLL_INTERVAL', 5); // given in minutes + +require_once('include/security.php'); + +function fbpost_install() { + register_hook('post_local', 'addon/fbpost/fbpost.php', 'fbpost_post_local'); + register_hook('notifier_normal', 'addon/fbpost/fbpost.php', 'fbpost_post_hook'); + register_hook('jot_networks', 'addon/fbpost/fbpost.php', 'fbpost_jot_nets'); + register_hook('connector_settings', 'addon/fbpost/fbpost.php', 'fbpost_plugin_settings'); + register_hook('enotify', 'addon/fbpost/fbpost.php', 'fbpost_enotify'); + register_hook('queue_predeliver', 'addon/fbpost/fbpost.php', 'fbpost_queue_hook'); + register_hook('cron', 'addon/fbpost/fbpost.php', 'fbpost_cron'); +} + + +function fbpost_uninstall() { + unregister_hook('post_local', 'addon/fbpost/fbpost.php', 'fbpost_post_local'); + unregister_hook('notifier_normal', 'addon/fbpost/fbpost.php', 'fbpost_post_hook'); + unregister_hook('jot_networks', 'addon/fbpost/fbpost.php', 'fbpost_jot_nets'); + unregister_hook('connector_settings', 'addon/fbpost/fbpost.php', 'fbpost_plugin_settings'); + unregister_hook('enotify', 'addon/fbpost/fbpost.php', 'fbpost_enotify'); + unregister_hook('queue_predeliver', 'addon/fbpost/fbpost.php', 'fbpost_queue_hook'); + unregister_hook('cron', 'addon/fbpost/fbpost.php', 'fbpost_cron'); +} + + +/* declare the fbpost_module function so that /fbpost url requests will land here */ + +function fbpost_module() {} + + + +// If a->argv[1] is a nickname, this is a callback from Facebook oauth requests. +// If $_REQUEST["realtime_cb"] is set, this is a callback from the Real-Time Updates API + +/** + * @param App $a + */ +function fbpost_init(&$a) { + + if($a->argc != 2) + return; + + $nick = $a->argv[1]; + + if(strlen($nick)) + $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1", + dbesc($nick) + ); + if(!(isset($r) && count($r))) + return; + + $uid = $r[0]['uid']; + $auth_code = (x($_GET, 'code') ? $_GET['code'] : ''); + $error = (x($_GET, 'error_description') ? $_GET['error_description'] : ''); + + + if($error) + logger('fbpost_init: Error: ' . $error); + + if($auth_code && $uid) { + + $appid = get_config('facebook','appid'); + $appsecret = get_config('facebook', 'appsecret'); + + $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id=' + . $appid . '&client_secret=' . $appsecret . '&redirect_uri=' + . urlencode($a->get_baseurl() . '/fbpost/' . $nick) + . '&code=' . $auth_code); + + logger('fbpost_init: returned access token: ' . $x, LOGGER_DATA); + + if(strpos($x,'access_token=') !== false) { + $token = str_replace('access_token=', '', $x); + if(strpos($token,'&') !== false) + $token = substr($token,0,strpos($token,'&')); + set_pconfig($uid,'facebook','access_token',$token); + set_pconfig($uid,'facebook','post','1'); + fbpost_get_self($uid); + } + + } + +} + + +/** + * @param int $uid + */ +function fbpost_get_self($uid) { + $access_token = get_pconfig($uid,'facebook','access_token'); + if(! $access_token) + return; + $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token); + if($s) { + $j = json_decode($s); + set_pconfig($uid,'facebook','self_id',(string) $j->id); + } +} + + +// This is the POST method to the facebook settings page +// Content is posted to Facebook in the function facebook_post_hook() + +/** + * @param App $a + */ +function fbpost_post(&$a) { + + $uid = local_user(); + if($uid){ + + + $fb_limited = get_config('facebook','crestrict'); + + + $value = ((x($_POST,'post_by_default')) ? intval($_POST['post_by_default']) : 0); + set_pconfig($uid,'facebook','post_by_default', $value); + + $value = ((x($_POST,'mirror_posts')) ? intval($_POST['mirror_posts']) : 0); + set_pconfig($uid,'facebook','mirror_posts', $value); + + $value = ((x($_POST,'suppress_view_on_friendica')) ? intval($_POST['suppress_view_on_friendica']) : 0); + set_pconfig($uid,'facebook','suppress_view_on_friendica', $value); + + $value = ((x($_POST,'post_to_page')) ? $_POST['post_to_page'] : "0-0"); + $values = explode("-", $value); + set_pconfig($uid,'facebook','post_to_page', $values[0]); + set_pconfig($uid,'facebook','page_access_token', $values[1]); + + info( t('Settings updated.') . EOL); + } + + return; +} + +// Facebook settings form + +/** + * @param App $a + * @return string + */ +function fbpost_content(&$a) { + + if(! local_user()) { + notice( t('Permission denied.') . EOL); + return ''; + } + + + if(! service_class_allows(local_user(),'facebook_connect')) { + notice( t('Permission denied.') . EOL); + return upgrade_bool_message(); + } + + + if($a->argc > 1 && $a->argv[1] === 'remove') { + del_pconfig(local_user(),'facebook','post'); + info( t('Facebook Post disabled') . EOL); + } + + $o = ''; + + $fb_installed = false; + if (get_pconfig(local_user(),'facebook','post')) { + $access_token = get_pconfig(local_user(),'facebook','access_token'); + if ($access_token) { + $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token); + if($s) { + $j = json_decode($s); + if (isset($j->data)) $fb_installed = true; + } + } + } + + $appid = get_config('facebook','appid'); + + if(! $appid) { + notice( t('Facebook API key is missing.') . EOL); + return ''; + } + + $a->page['htmlhead'] .= '' . "\r\n"; + + $o .= '

' . t('Facebook Post') . '

'; + + if(! $fb_installed) { + $o .= ''; + } + + if($fb_installed) { + $o .= ''; + + $o .= ''; + + $o .= '
'; + $o .= '
'; + $post_by_default = get_pconfig(local_user(),'facebook','post_by_default'); + $checked = (($post_by_default) ? ' checked="checked" ' : ''); + $o .= '' . ' ' . t('Post to Facebook by default') . EOL; + + $suppress_view_on_friendica = get_pconfig(local_user(),'facebook','suppress_view_on_friendica'); + $checked = (($suppress_view_on_friendica) ? ' checked="checked" ' : ''); + $o .= '' . ' ' . t('Suppress "View on friendica"') . EOL; + + $mirror_posts = get_pconfig(local_user(),'facebook','mirror_posts'); + $checked = (($mirror_posts) ? ' checked="checked" ' : ''); + $o .= '' . ' ' . t('Mirror wall posts from facebook to friendica.') . EOL; + + // List all pages + $post_to_page = get_pconfig(local_user(),'facebook','post_to_page'); + $page_access_token = get_pconfig(local_user(),'facebook','page_access_token'); + $fb_token = get_pconfig($a->user['uid'],'facebook','access_token'); + $url = 'https://graph.facebook.com/me/accounts'; + $x = fetch_url($url."?access_token=".$fb_token); + $accounts = json_decode($x); + + $o .= t("Post to page/group:").""; + + $o .= '

'; + + } + + return $o; +} + +/** + * @param App $a + * @param null|object $b + */ +function fbpost_plugin_settings(&$a,&$b) { + + $b .= '
'; + $b .= '

' . t('Facebook') . '

'; + $b .= '' . t('Facebook Post Settings') . '
'; + $b .= '
'; + +} + + +/** + * @param App $a + * @param null|object $o + */ +function fbpost_plugin_admin(&$a, &$o){ + + + $o = ''; + + $o .= '

' . t('Facebook API Key') . '

'; + + $appid = get_config('facebook', 'appid' ); + $appsecret = get_config('facebook', 'appsecret' ); + + $ret1 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appid' LIMIT 1"); + $ret2 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appsecret' LIMIT 1"); + if ((count($ret1) > 0 && $ret1[0]['v'] != $appid) || (count($ret2) > 0 && $ret2[0]['v'] != $appsecret)) $o .= t('Error: it appears that you have specified the App-ID and -Secret in your .htconfig.php file. As long as they are specified there, they cannot be set using this form.

'); + + $o .= '
'; + $o .= '
'; + + $o .= ''; + +} + +/** + * @param App $a + */ + +function fbpost_plugin_admin_post(&$a){ + check_form_security_token_redirectOnErr('/admin/plugins/fbpost', 'fbsave'); + + if (x($_REQUEST,'fb_save_keys')) { + set_config('facebook', 'appid', $_REQUEST['appid']); + set_config('facebook', 'appsecret', $_REQUEST['appsecret']); + + info(t('The new values have been saved.')); + } + +} + +/** + * @param App $a + * @param object $b + * @return mixed + */ +function fbpost_jot_nets(&$a,&$b) { + if(! local_user()) + return; + + $fb_post = get_pconfig(local_user(),'facebook','post'); + if(intval($fb_post) == 1) { + $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default'); + $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : ''); + $b .= '
' + . t('Post to Facebook') . '
'; + } +} + +function fbpost_ShareAttributes($match) { + + $attributes = $match[1]; + + $author = ""; + preg_match("/author='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $author = $matches[1]; + + preg_match('/author="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $author = $matches[1]; + + $headline = '
'; + + $headline .= sprintf(t('%s:'), $author); + + $headline .= "
"; + + //$text = "
".$headline."
".$match[2]."
"; + $text = "\n\t".$match[2].":\t"; + + return($text); +} + + +/** + * @param App $a + * @param object $b + * @return mixed + */ +function fbpost_post_hook(&$a,&$b) { + + + if($b['deleted'] || ($b['created'] !== $b['edited'])) + return; + + // Don't transmit answers (have to be cleaned up in the following code) + if($b['parent'] != $b['id']) + return; + + // if post comes from facebook don't send it back + if($b['app'] == "Facebook") + return; + + /** + * Post to Facebook stream + */ + + require_once('include/group.php'); + require_once('include/html2plain.php'); + + logger('Facebook post'); + + $reply = false; + $likes = false; + + $deny_arr = array(); + $allow_arr = array(); + + $toplevel = (($b['id'] == $b['parent']) ? true : false); + + + $linking = ((get_pconfig($b['uid'],'facebook','no_linking')) ? 0 : 1); + + if((! $toplevel) && ($linking)) { + $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", + intval($b['parent']), + intval($b['uid']) + ); + if(count($r) && substr($r[0]['uri'],0,4) === 'fb::') + $reply = substr($r[0]['uri'],4); + elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::') + $reply = substr($r[0]['extid'],4); + else + return; + + $u = q("SELECT * FROM user where uid = %d limit 1", + intval($b['uid']) + ); + if(! count($u)) + return; + + // only accept comments from the item owner. Other contacts are unknown to FB. + + if(! link_compare($b['author-link'], $a->get_baseurl() . '/profile/' . $u[0]['nickname'])) + return; + + + logger('facebook reply id=' . $reply); + } + + if(strstr($b['postopts'],'facebook') || ($b['private']) || ($reply)) { + + if($b['private'] && $reply === false) { + $allow_people = expand_acl($b['allow_cid']); + $allow_groups = expand_groups(expand_acl($b['allow_gid'])); + $deny_people = expand_acl($b['deny_cid']); + $deny_groups = expand_groups(expand_acl($b['deny_gid'])); + + $recipients = array_unique(array_merge($allow_people,$allow_groups)); + $deny = array_unique(array_merge($deny_people,$deny_groups)); + + $allow_str = dbesc(implode(', ',$recipients)); + if($allow_str) { + $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'"); + if(count($r)) + foreach($r as $rr) + $allow_arr[] = $rr['notify']; + } + + $deny_str = dbesc(implode(', ',$deny)); + if($deny_str) { + $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'"); + if(count($r)) + foreach($r as $rr) + $deny_arr[] = $rr['notify']; + } + + if(count($deny_arr) && (! count($allow_arr))) { + + // One or more FB folks were denied access but nobody on FB was specifically allowed access. + // This might cause the post to be open to public on Facebook, but only to selected members + // on another network. Since this could potentially leak a post to somebody who was denied, + // we will skip posting it to Facebook with a slightly vague but relevant message that will + // hopefully lead somebody to this code comment for a better explanation of what went wrong. + + notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL); + return; + } + + + // if it's a private message but no Facebook members are allowed or denied, skip Facebook post + + if((! count($allow_arr)) && (! count($deny_arr))) + return; + } + + if($b['verb'] == ACTIVITY_LIKE) + $likes = true; + + + $appid = get_config('facebook', 'appid' ); + $secret = get_config('facebook', 'appsecret' ); + + if($appid && $secret) { + + logger('facebook: have appid+secret'); + + $fb_token = get_pconfig($b['uid'],'facebook','access_token'); + + + // post to facebook if it's a public post and we've ticked the 'post to Facebook' box, + // or it's a private message with facebook participants + // or it's a reply or likes action to an existing facebook post + + if($fb_token && ($toplevel || $b['private'] || $reply)) { + logger('facebook: able to post'); + require_once('library/facebook.php'); + require_once('include/bbcode.php'); + + $msg = $b['body']; + + logger('Facebook post: original msg=' . $msg, LOGGER_DATA); + + // make links readable before we strip the code + + // unless it's a dislike - just send the text as a comment + + // if($b['verb'] == ACTIVITY_DISLIKE) + // $msg = trim(strip_tags(bbcode($msg))); + + // Old code + /*$search_str = $a->get_baseurl() . '/search'; + + if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) { + + // don't use hashtags for message link + + if(strpos($matches[2],$search_str) === false) { + $link = $matches[1]; + if(substr($matches[2],0,5) != '[img]') + $linkname = $matches[2]; + } + } + + // strip tag links to avoid link clutter, this really should be + // configurable because we're losing information + + $msg = preg_replace("/\#\[url=(.*?)\](.*?)\[\/url\]/is",'#$2',$msg); + + // provide the link separately for normal links + $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg); + + if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches)) + $image = $matches[1]; + + $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg); + + if((strpos($link,z_root()) !== false) && (! $image)) + $image = $a->get_baseurl() . '/images/friendica-64.jpg'; + + $msg = trim(strip_tags(bbcode($msg)));*/ + + // New code + + // Looking for the first image + $image = ''; + if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$b['body'],$matches)) + $image = $matches[3]; + + if ($image == '') + if(preg_match("/\[img\](.*?)\[\/img\]/is",$b['body'],$matches)) + $image = $matches[1]; + + // When saved into the database the content is sent through htmlspecialchars + // That means that we have to decode all image-urls + $image = htmlspecialchars_decode($image); + + // Checking for a bookmark element + $body = $b['body']; + if (strpos($body, "[bookmark") !== false) { + // splitting the text in two parts: + // before and after the bookmark + $pos = strpos($body, "[bookmark"); + $body1 = substr($body, 0, $pos); + $body2 = substr($body, $pos); + + // Removing the bookmark and all quotes after the bookmark + // they are mostly only the content after the bookmark. + $body2 = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism",'',$body2); + $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2); + $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2); + + $body = $body1.$body2; + } + + // Convert recycle signs + $body = str_replace("\t", " ", $body); + // recycle 1 + $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8'); + $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n\t$2:\t", $body); + // recycle 2 (Test) + $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8'); + $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n\t$2:\t", $body); + + // share element + $body = preg_replace_callback("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]/ism","fbpost_ShareAttributes", $body); + + $bodyparts = explode("\t", $body); + // Doesn't help with multiple repeats - the problem has to be solved later + if (sizeof($bodyparts) == 3) { + $html = bbcode($bodyparts[2], false, false); + $test = trim(html2plain($html, 0, true)); + + if (trim($bodyparts[0]) == "") + $body = trim($bodyparts[2]); + else if (trim($test) == "") + $body = trim($bodyparts[0]); + else + $body = trim($bodyparts[0])."\n\n".trim($bodyparts[1])."[quote]".trim($bodyparts[2])."[/quote]"; + } else + $body = str_replace("\t", "", $body); + + // At first convert the text to html + $html = bbcode($body, false, false); + + // Then convert it to plain text + $msg = trim($b['title']." \n\n".html2plain($html, 0, true)); + + // Removing useless spaces + if (substr($msg, -2) == "«") + $msg = trim(substr($msg, 0, -2))."«"; + + $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8'); + + // Removing multiple newlines + while (strpos($msg, "\n\n\n") !== false) + $msg = str_replace("\n\n\n", "\n\n", $msg); + + // add any attachments as text urls + $arr = explode(',',$b['attach']); + + if(count($arr)) { + $msg .= "\n"; + foreach($arr as $r) { + $matches = false; + $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches); + if($cnt) { + $msg .= "\n".$matches[1]; + } + } + } + + $link = ''; + $linkname = ''; + // look for bookmark-bbcode and handle it with priority + if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches)) { + $link = $matches[1]; + $linkname = $matches[2]; + } + + // If there is no bookmark element then take the first link + if ($link == '') { + $links = collecturls($html); + if (sizeof($links) > 0) { + reset($links); + $link = current($links); + } + } + + // Remove trailing and leading spaces + $msg = trim($msg); + + + // Fallback - if message is empty + if(!strlen($msg)) + $msg = $linkname; + + if(!strlen($msg)) + $msg = $link; + + if(!strlen($msg)) + $msg = $image; + + // If there is nothing to post then exit + if(!strlen($msg)) + return; + + logger('Facebook post: msg=' . $msg, LOGGER_DATA); + + $video = ""; + + if($likes) { + $postvars = array('access_token' => $fb_token); + } else { + // message, picture, link, name, caption, description, source, place, tags + if(trim($link) != "") + if (@exif_imagetype($link) != 0) { + $image = $link; + $link = ""; + } + + $postvars = array( + 'access_token' => $fb_token, + 'message' => $msg + ); + if(trim($image) != "") + $postvars['picture'] = $image; + + if(trim($link) != "") { + $postvars['link'] = $link; + + if ((stristr($link,'youtube')) || (stristr($link,'youtu.be')) || (stristr($link,'vimeo'))) { + $video = $link; + } + } + if(trim($linkname) != "") + $postvars['name'] = $linkname; + } + + if(($b['private']) && ($toplevel)) { + $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"'; + if(count($allow_arr)) + $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"'; + if(count($deny_arr)) + $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"'; + $postvars['privacy'] .= '}'; + + } + + $post_to_page = get_pconfig($b['uid'],'facebook','post_to_page'); + $page_access_token = get_pconfig($b['uid'],'facebook','page_access_token'); + if ((intval($post_to_page) != 0) and ($page_access_token != "")) + $target = $post_to_page; + else + $target = "me"; + + if($reply) { + $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments'); + } else if (($video != "") or (($image == "") and ($link != ""))) { + // If it is a link to a video or a link without a preview picture then post it as a link + if ($video != "") + $link = $video; + + $postvars = array( + 'access_token' => $fb_token, + 'link' => $link, + ); + if ($msg != $video) + $postvars['message'] = $msg; + + $url = 'https://graph.facebook.com/'.$target.'/links'; + } else if (($link == "") and ($image != "")) { + // If it is only an image without a page link then post this image as a photo + $postvars = array( + 'access_token' => $fb_token, + 'url' => $image, + ); + if ($msg != $image) + $postvars['message'] = $msg; + + $url = 'https://graph.facebook.com/'.$target.'/photos'; + } else if (($link != "") or ($image != "") or ($b['title'] == '') or (strlen($msg) < 500)) { + $url = 'https://graph.facebook.com/'.$target.'/feed'; + if (!get_pconfig($b['uid'],'facebook','suppress_view_on_friendica') and $b['plink']) + $postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' . $b['plink'] . '"}'; + } else { + // if its only a message and a subject and the message is larger than 500 characters then post it as note + $postvars = array( + 'access_token' => $fb_token, + 'message' => bbcode($b['body'], false, false), + 'subject' => $b['title'], + ); + $url = 'https://graph.facebook.com/'.$target.'/notes'; + } + + // Post to page? + if (!$reply and ($target != "me") and $page_access_token) + $postvars['access_token'] = $page_access_token; + + logger('facebook: post to ' . $url); + logger('facebook: postvars: ' . print_r($postvars,true)); + + // "test_mode" prevents anything from actually being posted. + // Otherwise, let's do it. + + if(! get_config('facebook','test_mode')) { + $x = post_url($url, $postvars); + logger('Facebook post returns: ' . $x, LOGGER_DEBUG); + + $retj = json_decode($x); + if($retj->id) { + q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1", + dbesc('fb::' . $retj->id), + intval($b['id']) + ); + } + else { + if(! $likes) { + $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars)); + require_once('include/queue_fn.php'); + add_to_queue($a->contact,NETWORK_FACEBOOK,$s); + notice( t('Facebook post failed. Queued for retry.') . EOL); + } + + if (isset($retj->error) && $retj->error->type == "OAuthException" && $retj->error->code == 190) { + logger('Facebook session has expired due to changed password.', LOGGER_DEBUG); + + $last_notification = get_pconfig($b['uid'], 'facebook', 'session_expired_mailsent'); + if (!$last_notification || $last_notification < (time() - FACEBOOK_SESSION_ERR_NOTIFICATION_INTERVAL)) { + require_once('include/enotify.php'); + + $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($b['uid']) ); + notification(array( + 'uid' => $b['uid'], + 'type' => NOTIFY_SYSTEM, + 'system_type' => 'facebook_connection_invalid', + 'language' => $r[0]['language'], + 'to_name' => $r[0]['username'], + 'to_email' => $r[0]['email'], + 'source_name' => t('Administrator'), + 'source_link' => $a->config["system"]["url"], + 'source_photo' => $a->config["system"]["url"] . '/images/person-80.jpg', + )); + + set_pconfig($b['uid'], 'facebook', 'session_expired_mailsent', time()); + } else logger('Facebook: No notification, as the last one was sent on ' . $last_notification, LOGGER_DEBUG); + } + } + } + } + } + } +} + +/** + * @param App $app + * @param object $data + */ +function fbpost_enotify(&$app, &$data) { + if (x($data, 'params') && $data['params']['type'] == NOTIFY_SYSTEM && x($data['params'], 'system_type') && $data['params']['system_type'] == 'facebook_connection_invalid') { + $data['itemlink'] = '/facebook'; + $data['epreamble'] = $data['preamble'] = t('Your Facebook connection became invalid. Please Re-authenticate.'); + $data['subject'] = t('Facebook connection became invalid'); + $data['body'] = sprintf( t("Hi %1\$s,\n\nThe connection between your accounts on %2\$s and Facebook became invalid. This usually happens after you change your Facebook-password. To enable the connection again, you have to %3\$sre-authenticate the Facebook-connector%4\$s."), $data['params']['to_name'], "[url=" . $app->config["system"]["url"] . "]" . $app->config["sitename"] . "[/url]", "[url=" . $app->config["system"]["url"] . "/facebook]", "[/url]"); + } +} + +/** + * @param App $a + * @param object $b + */ +function fbpost_post_local(&$a,&$b) { + + // Figure out if Facebook posting is enabled for this post and file it in 'postopts' + // where we will discover it during background delivery. + + // This can only be triggered by a local user posting to their own wall. + + if((local_user()) && (local_user() == $b['uid'])) { + + $fb_post = intval(get_pconfig(local_user(),'facebook','post')); + $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0); + + // if API is used, default to the chosen settings + // but allow a specific override + + if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default'))) { + if(! x($_REQUEST,'facebook_enable')) + $fb_enable = 1; + } + + if(! $fb_enable) + return; + + if(strlen($b['postopts'])) + $b['postopts'] .= ','; + $b['postopts'] .= 'facebook'; + } +} + + +/** + * @param App $a + * @param object $b + */ +function fbpost_queue_hook(&$a,&$b) { + + $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'", + dbesc(NETWORK_FACEBOOK) + ); + if(! count($qi)) + return; + + require_once('include/queue_fn.php'); + + foreach($qi as $x) { + if($x['network'] !== NETWORK_FACEBOOK) + continue; + + logger('facebook_queue: run'); + + $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` + WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1", + intval($x['cid']) + ); + if(! count($r)) + continue; + + $user = $r[0]; + + $appid = get_config('facebook', 'appid' ); + $secret = get_config('facebook', 'appsecret' ); + + if($appid && $secret) { + $fb_post = intval(get_pconfig($user['uid'],'facebook','post')); + $fb_token = get_pconfig($user['uid'],'facebook','access_token'); + + if($fb_post && $fb_token) { + logger('facebook_queue: able to post'); + require_once('library/facebook.php'); + + $z = unserialize($x['content']); + $item = $z['item']; + $j = post_url($z['url'],$z['post']); + + $retj = json_decode($j); + if($retj->id) { + q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1", + dbesc('fb::' . $retj->id), + intval($item) + ); + logger('facebook_queue: success: ' . $j); + remove_queue_item($x['id']); + } + else { + logger('facebook_queue: failed: ' . $j); + update_queue_time($x['id']); + } + } + } + } +} + + +/** + * @return bool|string + */ +function fbpost_get_app_access_token() { + + $acc_token = get_config('facebook','app_access_token'); + + if ($acc_token !== false) return $acc_token; + + $appid = get_config('facebook','appid'); + $appsecret = get_config('facebook', 'appsecret'); + + if ($appid === false || $appsecret === false) { + logger('fb_get_app_access_token: appid and/or appsecret not set', LOGGER_DEBUG); + return false; + } + logger('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials', LOGGER_DATA); + $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials'); + + if(strpos($x,'access_token=') !== false) { + logger('fb_get_app_access_token: returned access token: ' . $x, LOGGER_DATA); + + $token = str_replace('access_token=', '', $x); + if(strpos($token,'&') !== false) + $token = substr($token,0,strpos($token,'&')); + + if ($token == "") { + logger('fb_get_app_access_token: empty token: ' . $x, LOGGER_DEBUG); + return false; + } + set_config('facebook','app_access_token',$token); + return $token; + } else { + logger('fb_get_app_access_token: response did not contain an access_token: ' . $x, LOGGER_DATA); + return false; + } +} + +function fbpost_cron($a,$b) { + $last = get_config('facebook','last_poll'); + + $poll_interval = intval(get_config('facebook','poll_interval')); + if(! $poll_interval) + $poll_interval = FACEBOOK_DEFAULT_POLL_INTERVAL; + + if($last) { + $next = $last + ($poll_interval * 60); + if($next > time()) { + logger('facebook: poll intervall not reached'); + return; + } + } + logger('facebook: cron_start'); + + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND() "); + if(count($r)) { + foreach($r as $rr) { + logger('facebook: fetching for user '.$rr['uid']); + fbpost_fetchwall($a, $rr['uid']); + } + } + + logger('facebook: cron_end'); + + set_config('facebook','last_poll', time()); +} + +function fbpost_fetchwall($a, $uid) { + $access_token = get_pconfig($uid,'facebook','access_token'); + $post_to_page = get_pconfig($uid,'facebook','post_to_page'); + $lastcreated = get_pconfig($uid,'facebook','last_created'); + + if ((int)$post_to_page == 0) + $post_to_page = "me"; + + $url = "https://graph.facebook.com/".$post_to_page."/feed?access_token=".$access_token; + + $first_time = ($lastcreated == ""); + + if ($lastcreated != "") + $url .= "&since=".urlencode($lastcreated); + + $feed = fetch_url($url); + $data = json_decode($feed); + + if (!is_array($data->data)) + return; + + $items = array_reverse($data->data); + + foreach ($items as $item) { + if ($item->created_time > $lastcreated) + $lastcreated = $item->created_time; + + if ($first_time) + continue; + + if ($item->application->id == get_config('facebook','appid')) + continue; + + if(isset($item->privacy) && ($item->privacy->value !== 'EVERYONE') && ($item->privacy->value !== '')) + continue; + + if (($post_to_page != $item->from->id) AND ((int)$post_to_page != 0)) + continue; + + $_SESSION["authenticated"] = true; + $_SESSION["uid"] = $uid; + + $_REQUEST["type"] = "wall"; + $_REQUEST["api_source"] = true; + $_REQUEST["profile_uid"] = $uid; + $_REQUEST["source"] = "Facebook"; + + $_REQUEST["body"] = (isset($item->message) ? escape_tags($item->message) : ''); + + if(isset($item->name) and isset($item->link)) + $_REQUEST["body"] .= "\n\n[bookmark=".$item->link."]".$item->name."[/bookmark]"; + elseif (isset($item->name)) + $_REQUEST["body"] .= "\n\n[b]" . $item->name."[/b]"; + + /*if(isset($item->caption)) { + if(!isset($item->name) and isset($item->link)) + $_REQUEST["body"] .= "\n\n[bookmark=".$item->link."]".$item->caption."[/bookmark]"; + //else + // $_REQUEST["body"] .= "[i]" . $item->caption."[/i]\n"; + } + + if(!isset($item->caption) and !isset($item->name)) { + if (isset($item->link)) + $_REQUEST["body"] .= "\n[url]".$item->link."[/url]\n"; + else + $_REQUEST["body"] .= "\n"; + }*/ + + $quote = ""; + if(isset($item->description) and ($item->type != "photo")) + $quote = $item->description; + + if(isset($item->caption) and ($item->type == "photo")) + $quote = $item->caption; + + //if (isset($item->properties)) + // foreach ($item->properties as $property) + // $quote .= "\n".$property->name.": [url=".$property->href."]".$property->text."[/url]"; + + if ($quote) + $_REQUEST["body"] .= "\n[quote]".$quote."[/quote]"; + + // Only import the picture when the message is no video + // oembed display a picture of the video as well + if ($item->type != "video") { + //if (($item->type != "video") and ($item->type != "photo")) { + if(isset($item->picture) && isset($item->link)) + $_REQUEST["body"] .= "\n".'[url='.$item->link.'][img]'.fpost_cleanpicture($item->picture).'[/img][/url]'; + else { + if (isset($item->picture)) + $_REQUEST["body"] .= "\n".'[img]'.fpost_cleanpicture($item->picture).'[/img]'; + // if just a link, it may be a wall photo - check + if(isset($item->link)) + $_REQUEST["body"] .= fbpost_get_photo($uid,$item->link); + } + } + + /*if (($datarray['app'] == "Events") and isset($item->actions)) + foreach ($item->actions as $action) + if ($action->name == "View") + $_REQUEST["body"] .= " [url=".$action->link."]".$item->story."[/url]"; + */ + + if(trim($_REQUEST["body"]) == '') { + logger('facebook: empty body '.$item->id.' '.print_r($item, true)); + continue; + } + + $_REQUEST["body"] = trim($_REQUEST["body"]); + + if (isset($item->place)) { + if ($item->place->name or $item->place->location->street or + $item->place->location->city or $item->place->location->country) { + $_REQUEST["location"] = ''; + if ($item->place->name) + $_REQUEST["location"] .= $item->place->name; + if ($item->place->location->street) + $_REQUEST["location"] .= " ".$item->place->location->street; + if ($item->place->location->city) + $_REQUEST["location"] .= " ".$item->place->location->city; + if ($item->place->location->country) + $_REQUEST["location"] .= " ".$item->place->location->country; + + $_REQUEST["location"] = trim($_REQUEST["location"]); + } + if ($item->place->location->latitude and $item->place->location->longitude) + $_REQUEST["coord"] = substr($item->place->location->latitude, 0, 8) + .' '.substr($item->place->location->longitude, 0, 8); + } + + //print_r($_REQUEST); + logger('facebook: posting for user '.$uid); + + require_once('mod/item.php'); + item_post($a); + } + + set_pconfig($uid,'facebook','last_created', $lastcreated); +} + +function fbpost_get_photo($uid,$link) { + $access_token = get_pconfig($uid,'facebook','access_token'); + if(! $access_token || (! stristr($link,'facebook.com/photo.php'))) + return ""; + + $ret = preg_match('/fbid=([0-9]*)/',$link,$match); + if($ret) + $photo_id = $match[1]; + else + return ""; + + $x = fetch_url('https://graph.facebook.com/'.$photo_id.'?access_token='.$access_token); + $j = json_decode($x); + if($j->picture) + return "\n\n".'[url='.$link.'][img]'.fpost_cleanpicture($j->picture).'[/img][/url]'; + + return ""; +} + +function fpost_cleanpicture($image) { + + if (strpos($image, ".fbcdn.net/") and (substr($image, -6) == "_s.jpg")) + $image = substr($image, 0, -6)."_n.jpg"; + + $queryvar = fbpost_parse_query($image); + if ($queryvar['url'] != "") + $image = urldecode($queryvar['url']); + + return $image; +} + +function fbpost_parse_query($var) { + /** + * Use this function to parse out the query array element from + * the output of parse_url(). + */ + $var = parse_url($var, PHP_URL_QUERY); + $var = html_entity_decode($var); + $var = explode('&', $var); + $arr = array(); + + foreach($var as $val) { + $x = explode('=', $val); + $arr[$x[0]] = $x[1]; + } + + unset($val, $x, $var); + return $arr; +} diff --git a/fortunate.tgz b/fortunate.tgz new file mode 100644 index 00000000..a2dd39d9 Binary files /dev/null and b/fortunate.tgz differ diff --git a/fortunate/README b/fortunate/README new file mode 100644 index 00000000..8297cf41 --- /dev/null +++ b/fortunate/README @@ -0,0 +1,7 @@ +This addon requires a fortune server. You may use the DB supplied here to create one. + +gunzip the fortunate.sql.gz and import into your database. +Copy cookie.php to the top level Friendica directory. +Edit fortunate.php and change FORTUNATE_SERVER definition to your hostname. Change the http in that file to https if your server doesn't support http. + +Many additional options are available if you examine cookie.php - a clever developer can provide a settings page to tailor this to one's liking. Also several languages are supported, and it would be convenient to set this to the current Friendica language if that is amongst those supported. \ No newline at end of file diff --git a/fortunate/cookie.php b/fortunate/cookie.php new file mode 100644 index 00000000..0acfa230 --- /dev/null +++ b/fortunate/cookie.php @@ -0,0 +1,349 @@ +real_escape_string($_GET['lang']); + +if(strlen($_GET['pattern'])) + $pattern = @$db->real_escape_string(urldecode($_GET['pattern'])); + +if(strlen($_GET['regex'])) + $regex = @$db->real_escape_string(urldecode($_GET['regex'])); + +if(strlen($_GET['db'])) + $table = @$db->real_escape_string(urldecode($_GET['db'])); +else + $table = ''; + +if($length < 0) + $length = 0; +if($numlines < 0) + $numlines = 0; + +function do_query($table,$length,$numlines,$adult,$cat,$limit,$lang,$pattern,$regex,$equal) { + global $db; + $rnd = mt_rand(); + $r = array(); + + $typesql = (($table) ? " WHERE `category` = '$table' " : " WHERE 1 "); + $lengthsql = (($length) ? " AND LENGTH(`text`) < $length " : "" ); + + if($adult == 2) + $adultsql = " AND offensive = 1 "; + elseif($adult == 1) + $adultsql = ""; + else + $adultsql = " AND offensive = 0 "; + + + if($numlines) + $lengthsql .= + " AND (LENGTH(`text`) - LENGTH(REPLACE(`text`,\"\n\",\"\"))) <= $numlines "; + + $langsql = " AND lang = '$lang' "; + + $patsql = ''; + if(strlen($pattern)) + $patsql = " AND MATCH text AGAINST ('$pattern' IN BOOLEAN MODE) "; + + $regexsql = ''; + if(strlen($regex)) + $regexsql = " AND text REGEXP '$regex' "; + + $eqsql = ''; + + if($equal) { + $catsavail = array(); + $res = @$db->query("SELECT DISTINCT ( `category` ) FROM `fortune` + $typesql + $adultsql + $lengthsql + $langsql + $patsql + $regexsql "); + if($res->num_rows) { + while($x = $res->fetch_array(MYSQL_ASSOC)) + $catsavail[] = $x['category']; + + $eqsql = " AND `category` = '" + . $catsavail[mt_rand(0,$res->num_rows - 1)] . "' "; + } + } + + $result = @$db->query("SELECT `text`, `category` FROM `fortune` + $typesql + $adultsql + $lengthsql + $langsql + $patsql + $regexsql + $eqsql + ORDER BY RAND($rnd) + LIMIT $limit"); + + if($result->num_rows) { + while($x = $result->fetch_array(MYSQL_ASSOC)) + $r[] = fortune_to_html($x['text']) + .(($cat) ? "
[{$x['category']}]
" : ""); + } + return $r; +} + + +function do_stats($table,$length,$numlines,$adult,$cat,$limit,$lang,$pattern,$regex,$equal) { + global $db; + $rnd = mt_rand(); + $r = array(); + + $typesql = (($table) ? " WHERE `category` = '$table' " : " WHERE 1 "); + $lengthsql = (($length) ? " AND LENGTH(`text`) < $length " : "" ); + + if($adult == 2) + $adultsql = " AND offensive = 1 "; + elseif($adult == 1) + $adultsql = ""; + else + $adultsql = " AND offensive = 0 "; + + + if($numlines) + $lengthsql .= + " AND (LENGTH(`text`) - LENGTH(REPLACE(`text`,\"\n\",\"\"))) <= $numlines "; + + $langsql = " AND lang = '$lang' "; + + $patsql = ''; + if(strlen($pattern)) + $patsql = " AND MATCH text AGAINST ('$pattern' IN BOOLEAN MODE) "; + + $regexsql = ''; + if(strlen($regex)) + $regexsql = " AND text REGEXP '$regex' "; + + $eqsql = ''; + + $result = @$db->query("SELECT `text`, `category` FROM `fortune` + $typesql + $adultsql + $lengthsql + $langsql + $patsql + $regexsql + $eqsql"); + + + echo '
' . $result->num_rows . ' matching quotations.
'; + + + $res = @$db->query("SELECT DISTINCT ( `category` ) FROM `fortune` + $typesql + $adultsql + $lengthsql + $langsql + $patsql + $regexsql "); + if($res->num_rows) { + echo '
Matching Databases:
'; + while($x = $res->fetch_array(MYSQL_ASSOC)) + echo $x['category'].'
'; + + } + else + echo '
No matching databases using those search parameters - please refine your options.
'; + + +} + + +function fortune_to_html($s) { + + // First pass - escape all the HTML entities, and while we're at it + // get rid of any MS-DOS end-of-line characters and expand tabs to + // 8 non-breaking spaces, and translate linefeeds to
. + // We also get rid of ^G which used to sound the terminal beep or bell + // on ASCII terminals and were humourous in some fortunes. + // We could map these to autoplay a short sound file but browser support + // is still sketchy and then there's the issue of where to locate the + // URL, and a lot of people find autoplay sounds downright annoying. + // So for now, just remove them. + + $s = str_replace( + array("&", + "<", + ">", + '"', + "\007", + "\t", + "\r", + "\n"), + + array("&", + "<", + ">", + """, + "", + "        ", + "", + "
"), + $s); + // Replace pseudo diacritics + // These were used to produce accented characters. For instance an accented + // e would have been encoded by '^He - the backspace moving the cursor + // backward so both the single quote and the e would appear in the same + // character position. Umlauts were quite clever - they used a double quote + // as the accent mark over a normal character. + + $s = preg_replace("/'\010([a-zA-Z])/","&\\1acute;",$s); + $s = preg_replace("/\"\010([a-zA-Z])/","&\\1uml;",$s); + $s = preg_replace("/\`\010([a-zA-Z])/","&\\1grave;",$s); + $s = preg_replace("/\^\010([a-zA-Z])/","&\\1circ;",$s); + $s = preg_replace("/\~\010([a-zA-Z])/","&\\1tilde;",$s); + + // Ignore multiple underlines for the same character. These were + // most useful when sent to a line printer back in the day as it + // would type over the same character a number of times making it + // much darker (e.g. bold). I think there are only one or two + // instances of this in the current (2008) fortune cookie database. + + $s = preg_replace("/(_\010)+/","_\010",$s); + // Map the characters which sit underneath a backspace. + // If you can come up with a regex to do all of the following + // madness - be my guest. + // It's not as simple as you think. We need to take something + // that has been backspaced over an arbitrary number of times + // and wrap a forward looking matching number of characters in + // HTML, whilst deciding if it's intended as an underline or + // strikeout sequence. + + // Essentially we produce a string of '1' and '0' characters + // the same length as the source text. + // Any position which is marked '1' has been backspaced over. + + $cursor = 0; + $dst = $s; + $bs_found = false; + for($x = 0; $x < strlen($s); $x ++) { + if($s[$x] == "\010" && $cursor) { + $bs_found = true; + $cursor --; + $dst[$cursor] = '1'; + $dst[$x] = '0'; + $continue; + } + else { + if($bs_found) { + $bs_found = false; + $cursor = $x; + } + $dst[$cursor] = '0'; + $cursor ++; + } + + } + + $out = ''; + $strike = false; + $bold = false; + + // Underline sequence, convert to bold to avoid confusion with links. + // These were generally used for emphasis so it's a reasonable choice. + // Please note that this logic will fail if there is an underline sequence + // and also a strikeout sequence in the same fortune. + + if(strstr($s,"_\010")) { + $len = 0; + for($x = 0; $x < strlen($s); $x ++) { + if($dst[$x] == '1') { + $len ++; + $bold = true; + } + else { + if($bold) { + $out .= ''; + while($s[$x] == "\010") + $x ++; + $out .= substr($s,$x,$len); + $out .= ''; + $x = $x + $len - 1; + $len = 0; + $bold = false; + } + else + $out .= $s[$x]; + } + } + } + + // These aren't seen very often these days - simulation of + // backspace/replace. You could occasionally see the original text + // on slower terminals before it got replaced. Once modems reached + // 4800/9600 baud in the late 70's and early 80's the effect was + // mostly lost - but if you find a really old fortune file you might + // encounter a few of these. + + else { + for($x = 0; $x < strlen($s); $x ++) { + if($dst[$x] == '1') { + if($strike) + $out .= $s[$x]; + else + $out .= ''.$s[$x]; + $strike = true; + } + else { + if($strike) + $out .= ''; + $strike = false; + $out .= $s[$x]; + } + } + } + + // Many of the underline sequences are also wrapped in asterisks, + // which was yet another way of marking ASCII as 'bold'. + // So if it's an underline sequence, and there are asterisks + // on both ends, strip the asterisks as we've already emboldened the text. + + $out = preg_replace('/\*([^<]*<\/strong>)\*/',"\\1",$out); + + // Finally, remove the backspace characters which we don't need anymore. + + return str_replace("\010","",$out); +} + +$result1 = do_query($table,$length,$numlines,$adult,$cat,1,$lang,$pattern,$regex,$equal); + +if(count($result1)) + echo $result1[0]; + +if($stats) + do_stats($table,$length,$numlines,$adult,$cat,1,$lang,$pattern,$regex,$equal); + + diff --git a/fortunate/fortunate.css b/fortunate/fortunate.css new file mode 100644 index 00000000..61813b7d --- /dev/null +++ b/fortunate/fortunate.css @@ -0,0 +1,7 @@ +.fortunate { + margin-top: 25px; + margin-left: 100px; + margin-bottom: 25px; + color: #000088; + font-size: 14px; +} \ No newline at end of file diff --git a/fortunate/fortunate.php b/fortunate/fortunate.php new file mode 100644 index 00000000..427d620c --- /dev/null +++ b/fortunate/fortunate.php @@ -0,0 +1,35 @@ + + */ + +// IMPORTANT: SET THIS to your fortunate server + +define ('FORTUNATE_SERVER', 'hostname.com'); + +function fortunate_install() { + register_hook('page_end', 'addon/fortunate/fortunate.php', 'fortunate_fetch'); + if(FORTUNATE_SERVER == 'hostname.com' && is_site_admin()) { + notice('Fortunate plugin requires configuration. See README'); + } +} + +function fortunate_uninstall() { + unregister_hook('page_end', 'addon/fortunate/fortunate.php', 'fortunate_fetch'); +} + + +function fortunate_fetch(&$a,&$b) { + + $a->page['htmlhead'] .= '' . "\r\n"; + + if(FORTUNATE_SERVER != 'hostname.com') { + $s = fetch_url('http://' . FORTUNATE_SERVER . '/cookie.php?numlines=2&equal=1&rand=' . mt_rand()); + $b .= '
' . $s . '
'; + } +} + diff --git a/fortunate/fortunemod.sql.gz b/fortunate/fortunemod.sql.gz new file mode 100644 index 00000000..2ce0e557 Binary files /dev/null and b/fortunate/fortunemod.sql.gz differ diff --git a/forumdirectory.tgz b/forumdirectory.tgz new file mode 100644 index 00000000..9f163dea Binary files /dev/null and b/forumdirectory.tgz differ diff --git a/forumdirectory/forumdirectory.css b/forumdirectory/forumdirectory.css new file mode 100644 index 00000000..c3c84856 --- /dev/null +++ b/forumdirectory/forumdirectory.css @@ -0,0 +1,67 @@ + +section .forumdirectory-item dl { + height: auto; + overflow: auto; +} + +section .forumdirectory-item dt { + float: left; + margin-left: 0px; + text-align: right; + color: #999; +} +section .forumdirectory-item dd { + float: left; + margin-left: 5px; +} +.forumdirectory-profile-wrapper { + float: left; + /*max-height: 178px; */ + overflow: hidden; + width: 500px; + margin: 0px 20px; +} +.forumdirectory-copy-wrapper { + float: left; + overflow: hidden; +} + +.forumdirectory-item { + float: left; + width: 800px; + /* height: 200px; */ + box-shadow: 8px 8px 4px #000; + margin-top: 30px; + border: solid 1px #222; +} +section .forumdirectory-photo-wrapper { + float: left; + height: 200px; + width: 165px; +} + +.forumcontact-name { + font-size: 18px; + font-weight: bold; + margin-bottom: -3px; + text-align: left; +} + +.page-type { + font-size: 10px; + font-style: italic; +} +.directory-detailscolumn-wrapper { + float: left; + width: 305px; + margin-right: 10px; +} +.directory-profile-wrapper dl { + margin-top: 3px; + margin-bottom: 3px; +} +.directory-profile-title { + font-weight: bold; + margin-bottom: 3px; + font-size: 14px; +} diff --git a/forumdirectory/forumdirectory.php b/forumdirectory/forumdirectory.php new file mode 100644 index 00000000..f4292c61 --- /dev/null +++ b/forumdirectory/forumdirectory.php @@ -0,0 +1,204 @@ + +*/ + +function forumdirectory_install() { +register_hook('app_menu', 'addon/forumdirectory/forumdirectory.php', 'forumdirectory_app_menu'); +} + +function forumdirectory_uninstall() { +unregister_hook('app_menu', 'addon/forumdirectory/forumdirectory.php', 'forumdirectory_app_menu'); +} + +function forumdirectory_module() { +return; +} + +function forumdirectory_app_menu($a,&$b) { +$b['app_menu'][] = ''; +} + +function forumdirectory_init(&$a) { + $a->page['htmlhead'] .= ''; + + $a->set_pager_itemspage(60); + + if(local_user()) { + require_once('include/contact_widgets.php'); + + $a->page['aside'] .= findpeople_widget(); + + } + else + unset($_SESSION['theme']); + + +} + + +function forumdirectory_post(&$a) { + if(x($_POST,'search')) + $a->data['search'] = $_POST['search']; +} + + + +function forumdirectory_content(&$a) { + + if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) { + notice( t('Public access denied.') . EOL); + return; + } + + $o = ''; + nav_set_selected('directory'); + + if(x($a->data,'search')) + $search = notags(trim($a->data['search'])); + else + $search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : ''); + + $tpl = get_markup_template('directory_header.tpl'); + + $globaldir = ''; + $gdirpath = dirname(get_config('system','directory_submit_url')); + if(strlen($gdirpath)) { + $globaldir = ''; + } + + $admin = ''; + + $o .= replace_macros($tpl, array( + '$search' => $search, + '$globaldir' => $globaldir, + '$desc' => t('Find on this site'), + '$admin' => $admin, + '$finding' => (strlen($search) ? '

' . t('Finding: ') . "'" . $search . "'" . '

' : ""), + '$sitedir' => t('Site Directory'), + '$submit' => t('Find') + )); + + if($search) + $search = dbesc($search); + $sql_extra = ((strlen($search)) ? " AND MATCH (`profile`.`name`, `user`.`nickname`, `pdesc`, `locality`,`region`,`country-name`,`gender`,`marital`,`sexual`,`about`,`romance`,`work`,`education`,`pub_keywords`,`prv_keywords` ) AGAINST ('$search' IN BOOLEAN MODE) " : ""); + + $publish = ((get_config('system','publish_all')) ? '' : " AND `publish` = 1 " ); + + + $r = q("SELECT COUNT(*) AS `total` FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` WHERE `is-default` = 1 $publish AND `user`.`blocked` = 0 AND `page-flags` = 2 $sql_extra "); + if(count($r)) + $a->set_pager_total($r[0]['total']); + + $order = " ORDER BY `name` ASC "; + + + $r = q("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname`, `user`.`timezone` , `user`.`page-flags` FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` WHERE `is-default` = 1 $publish AND `user`.`blocked` = 0 AND `page-flags` = 2 $sql_extra $order LIMIT %d , %d ", + intval($a->pager['start']), + intval($a->pager['itemspage']) + ); + if(count($r)) { + + if(in_array('small', $a->argv)) + $photo = 'thumb'; + else + $photo = 'photo'; + + foreach($r as $rr) { + + + $profile_link = $a->get_baseurl() . '/profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']); + + $pdesc = (($rr['pdesc']) ? $rr['pdesc'] . '
' : ''); + + $details = ''; + if(strlen($rr['locality'])) + $details .= $rr['locality']; + if(strlen($rr['region'])) { + if(strlen($rr['locality'])) + $details .= ', '; + $details .= $rr['region']; + } + if(strlen($rr['country-name'])) { + if(strlen($details)) + $details .= ', '; + $details .= $rr['country-name']; + } + if(strlen($rr['dob'])) { + if(($years = age($rr['dob'],$rr['timezone'],'')) != 0) + $details .= '
' . t('Age: ') . $years ; + } + if(strlen($rr['gender'])) + $details .= '
' . t('Gender: ') . $rr['gender']; + + if($rr['page-flags'] == PAGE_NORMAL) + $page_type = "Personal Profile"; + if($rr['page-flags'] == PAGE_SOAPBOX) + $page_type = "Fan Page"; + if($rr['page-flags'] == PAGE_COMMUNITY) + $page_type = "Community Forum"; + if($rr['page-flags'] == PAGE_FREELOVE) + $page_type = "Open Forum"; + if($rr['page-flags'] == PAGE_PRVGROUP) + $page_type = "Private Group"; + + $profile = $rr; + + if((x($profile,'address') == 1) + || (x($profile,'locality') == 1) + || (x($profile,'region') == 1) + || (x($profile,'postal-code') == 1) + || (x($profile,'country-name') == 1)) + $location = t('Location:'); + + $gender = ((x($profile,'gender') == 1) ? t('Gender:') : False); + + $marital = ((x($profile,'marital') == 1) ? t('Status:') : False); + + $homepage = ((x($profile,'homepage') == 1) ? t('Homepage:') : False); + + $about = ((x($profile,'about') == 1) ? t('About:') : False); + +# $tpl = file_get_contents( dirname(__file__).'/forumdirectory_item.tpl'); + $tpl = get_markup_template( 'forumdirectory_item.tpl', 'addon/forumdirectory/' ); + + $entry = replace_macros($tpl,array( + '$id' => $rr['id'], + '$profile_link' => $profile_link, + '$photo' => $a->get_cached_avatar_image($rr[$photo]), + '$alt_text' => $rr['name'], + '$name' => $rr['name'], + '$details' => $pdesc . $details, + '$page_type' => $page_type, + '$profile' => $profile, + '$location' => template_escape($location), + '$gender' => $gender, + '$pdesc' => $pdesc, + '$marital' => $marital, + '$homepage' => $homepage, + '$about' => $about, + + )); + + $arr = array('contact' => $rr, 'entry' => $entry); + + unset($profile); + unset($location); + + $o .= $entry; + + } + + $o .= "
\r\n"; + $o .= paginate($a); + + } + else + info( t("No entries \x28some entries may be hidden\x29.") . EOL); + + return $o; +} diff --git a/forumdirectory/view/forumdirectory_item.tpl b/forumdirectory/view/forumdirectory_item.tpl new file mode 100755 index 00000000..e1bbffec --- /dev/null +++ b/forumdirectory/view/forumdirectory_item.tpl @@ -0,0 +1,42 @@ + +
+
+
+ + $alt_text + +
+
+
+
$name
+
$page_type
+ {{ if $pdesc }}
$profile.pdesc
{{ endif }} +
+
+ {{ if $location }} +
$location
+
+ {{ if $profile.address }}
$profile.address
{{ endif }} + + $profile.locality{{ if $profile.locality }}, {{ endif }} + $profile.region + $profile.postal-code + + {{ if $profile.country-name }}$profile.country-name{{ endif }} +
+
+ {{ endif }} + + {{ if $gender }}
$gender
$profile.gender
{{ endif }} +
+
+ {{ if $marital }}
$marital
$profile.marital
{{ endif }} + + {{ if $homepage }}
$homepage
$profile.homepage
{{ endif }} +
+
+
+ {{ if $about }}
$about
$profile.about
{{ endif }} +
+
+
diff --git a/forumdirectory/view/smarty3/forumdirectory_item.tpl b/forumdirectory/view/smarty3/forumdirectory_item.tpl new file mode 100644 index 00000000..66410efe --- /dev/null +++ b/forumdirectory/view/smarty3/forumdirectory_item.tpl @@ -0,0 +1,42 @@ + +
+
+
+ + {{$alt_text}} + +
+
+
+
{{$name}}
+
{{$page_type}}
+ {{if $pdesc}}
{{$profile.pdesc}}
{{/if}} +
+
+ {{if $location}} +
{{$location}}
+
+ {{if $profile.address}}
{{$profile.address}}
{{/if}} + + {{$profile.locality}}{{if $profile.locality}}, {{/if}} + {{$profile.region}} + {{$profile.postal-code}} + + {{if $profile.country-name}}{{$profile.country-name}}{{/if}} +
+
+ {{/if}} + + {{if $gender}}
{{$gender}}
{{$profile.gender}}
{{/if}} +
+
+ {{if $marital}}
{{$marital}}
{{$profile.marital}}
{{/if}} + + {{if $homepage}}
{{$homepage}}
{{$profile.homepage}}
{{/if}} +
+
+
+ {{if $about}}
{{$about}}
{{$profile.about}}
{{/if}} +
+
+
diff --git a/forumlist.tgz b/forumlist.tgz new file mode 100644 index 00000000..8356d443 Binary files /dev/null and b/forumlist.tgz differ diff --git a/forumlist/forumlist.css b/forumlist/forumlist.css new file mode 100644 index 00000000..18c8e168 --- /dev/null +++ b/forumlist/forumlist.css @@ -0,0 +1,22 @@ + +#hide-forum-list { + opacity: 0.3; + filter:alpha(opacity=30); +} + +#hide-forum-list:hover { + opacity: 1.0; + filter:alpha(opacity=100); +} + + +#forumlist-settings-label, #forumlist-random-label, #forumlist-profile-label, #forumlist-network-label { + float: left; + width: 200px; + margin-bottom: 25px; +} + +#forumlist-max-forumlists, #forumlist-random, #forumlist-profile, #forumlist-network { + float: left; +} + diff --git a/forumlist/forumlist.php b/forumlist/forumlist.php new file mode 100644 index 00000000..95ae9890 --- /dev/null +++ b/forumlist/forumlist.php @@ -0,0 +1,182 @@ + + * based on pages plugin by + * Author: Michael Vogel + * + */ + +function forumlist_install() { + register_hook('network_mod_init', 'addon/forumlist/forumlist.php', 'forumlist_network_mod_init'); + register_hook('plugin_settings', 'addon/forumlist/forumlist.php', 'forumlist_plugin_settings'); + register_hook('plugin_settings_post', 'addon/forumlist/forumlist.php', 'forumlist_plugin_settings_post'); + register_hook('profile_advanced', 'addon/forumlist/forumlist.php', 'forumlist_profile_advanced'); + +} + +function forumlist_uninstall() { + unregister_hook('network_mod_init', 'addon/forumlist/forumlist.php', 'forumlist_network_mod_init'); + unregister_hook('plugin_settings', 'addon/forumlist/forumlist.php', 'forumlist_plugin_settings'); + unregister_hook('plugin_settings_post', 'addon/forumlist/forumlist.php', 'forumlist_plugin_settings_post'); + unregister_hook('profile_advanced', 'addon/forumlist/forumlist.php', 'forumlist_profile_advanced'); + +} + + +function forumlist_getpage($uid,$showhidden = true,$randomise = false, $showprivate = false) { + + + $forumlist = array(); + + $order = (($showhidden) ? '' : " and hidden = 0 "); + $order .= (($randomise) ? ' order by rand() ' : ' order by name asc '); + $select = "`forum` = 1"; + if ($showprivate) { + $select = "( `forum` = 1 OR `prv` = 1 )"; + } + + $contacts = q("SELECT `contact`.`id`, `contact`.`url`, `contact`.`name`, `contact`.`micro` from contact + WHERE `network`= 'dfrn' AND $select AND `uid` = %d + and blocked = 0 and hidden = 0 and pending = 0 and archive = 0 + $order ", + intval($uid) + ); + + // Look if the profile is a community page + foreach($contacts as $contact) { + $forumlist[] = array("url"=>$contact["url"], "name"=>$contact["name"], "id"=>$contact["id"], "micro"=>$contact['micro']); + } + return($forumlist); +} + +function forumlist_network_mod_init($a,$b) { + + if(! intval(get_pconfig(local_user(),'forumlist','show_on_network'))) + return; + + $a->page['htmlhead'] .= '' . "\r\n"; + + $forumlist = '
+
+

'.t("Forums").'

'; + + $forumlist .= '' + . '
"; + if (sizeof($contacts) > 0) + $a->page['aside'] = $forumlist . $a->page['aside']; +} + + +function forumlist_profile_advanced($a,&$b) { + $a->page['htmlhead'] .= '' . "\r\n"; + + $profile = intval(get_pconfig($a->profile['profile_uid'],'forumlist','show_on_profile')); + if(! $profile) + return; + + $forumlist = '
+
'.t("Forums:").'
+
'; + + // place holder in case somebody wants configurability + $show_total = 9999; + + $randomise = true; + + $contacts = forumlist_getpage($a->user['uid'],false,$randomise,false); + + $total_shown = 0; + $more = false; + + foreach($contacts as $contact) { + $forumlist .= micropro($contact,false,'forumlist-profile-advanced'); + $total_shown ++; + if($total_shown == $show_total) + break; + } + $forumlist .= '
'; + + if(count($contacts) > 0) + $b .= $forumlist; + +} + + + +function forumlist_plugin_settings_post($a,$post) { + if(! local_user() || (! x($_POST,'forumlist-settings-submit'))) + return; +// set_pconfig(local_user(),'forumlist','max_forumlists',intval($_POST['forumlist_max_forumlists'])); + set_pconfig(local_user(),'forumlist','randomise',intval($_POST['forumlist_random'])); + set_pconfig(local_user(),'forumlist','show_on_profile',intval($_POST['forumlist_profile'])); + set_pconfig(local_user(),'forumlist','show_on_network',intval($_POST['forumlist_network'])); + + info( t('Forumlist settings updated.') . EOL); +} + + +function forumlist_plugin_settings(&$a,&$s) { + + if(! local_user()) + return; + + /* Add our stylesheet to the forumlist so we can make our settings look nice */ + + $a->page['htmlhead'] .= '' . "\r\n"; + + /* Get the current state of our config variable */ + + $randomise = intval(get_pconfig(local_user(),'forumlist','randomise')); + $randomise_checked = (($randomise) ? ' checked="checked" ' : ''); + + $profile = intval(get_pconfig(local_user(),'forumlist','show_on_profile')); + $profile_checked = (($profile) ? ' checked="checked" ' : ''); + + $network = intval(get_pconfig(local_user(),'forumlist','show_on_network')); + $network_checked = (($network) ? ' checked="checked" ' : ''); + + + /* Add some HTML to the existing form */ + + $s .= '
'; + $s .= '

' . t('Forumlist Settings') . '

'; + $s .= '
'; + $s .= ''; + $s .= ''; + $s .= '
'; + $s .= ''; + $s .= ''; + $s .= '
'; + $s .= ''; + $s .= ''; + $s .= '
'; + + $s .= '
'; + + /* provide a submit button */ + + $s .= '
'; + +} + + diff --git a/fromapp.tgz b/fromapp.tgz new file mode 100644 index 00000000..30d84e90 Binary files /dev/null and b/fromapp.tgz differ diff --git a/fromapp/fromapp.css b/fromapp/fromapp.css new file mode 100644 index 00000000..422624a1 --- /dev/null +++ b/fromapp/fromapp.css @@ -0,0 +1,14 @@ + + + +#fromapp-label, #fromapp-force-label { + float: left; + width: 200px; + margin-bottom: 25px; +} + +#fromapp-input, #fromapp-force { + float: left; +} + + diff --git a/fromapp/fromapp.php b/fromapp/fromapp.php new file mode 100644 index 00000000..9a9fa12b --- /dev/null +++ b/fromapp/fromapp.php @@ -0,0 +1,101 @@ +page['htmlhead'] .= '' . "\r\n"; + + /* Get the current state of our config variable */ + + $fromapp = get_pconfig(local_user(),'fromapp','app'); + if($fromapp === false) + $fromapp = ''; + + $force = intval(get_pconfig(local_user(),'fromapp','force')); + + $force_enabled = (($force) ? ' checked="checked" ' : ''); + + + /* Add some HTML to the existing form */ + + $s .= '
'; + $s .= '

' . t('FromApp Settings') . '

'; + $s .= '
'; + $s .= ''; + $s .= ''; + $s .= '
'; + + $s .= ''; + $s .= ''; + + $s .= '
'; + + /* provide a submit button */ + + $s .= '
'; + +} + +function fromapp_post_hook(&$a,&$item) { + if(! local_user()) + return; + + if(local_user() != $item['uid']) + return; + + $app = get_pconfig(local_user(), 'fromapp', 'app'); + $force = intval(get_pconfig(local_user(), 'fromapp','force')); + + if(($app === false) || (! strlen($app))) + return; + + if(strlen(trim($item['app'])) && (! $force)) + return; + + $apps = explode(',',$app); + $item['app'] = trim($apps[mt_rand(0,count($apps)-1)]); + return; + +} \ No newline at end of file diff --git a/fromgplus.tgz b/fromgplus.tgz new file mode 100644 index 00000000..0a402b76 Binary files /dev/null and b/fromgplus.tgz differ diff --git a/fromgplus/README b/fromgplus/README index cecbb2b9..82a29f66 100644 --- a/fromgplus/README +++ b/fromgplus/README @@ -1 +1,15 @@ -This extension is a preparation of the upcoming import of items via Google+ +This extension fetches messages from a Google+ account and reshares it. + +You have to place the following config values in your .htconfig.php: + +$a->config['fromgplus']['key'] = "your key"; +$a->config['fromgplus']['poll_interval'] = 10; + +You need an API key for "Simple API Access". + +- You go to https://code.google.com/apis/console/ +- Then you go to "Services" and activate "Google+ API". +- After that you go to "API Access". +- At the bottom of the page you see "Simple API Access". + +The value after "API key:" is the key that you need. diff --git a/fromgplus/fromgplus.php b/fromgplus/fromgplus.php index 88d2622f..5bd82ed8 100644 --- a/fromgplus/fromgplus.php +++ b/fromgplus/fromgplus.php @@ -1,20 +1,24 @@ * */ +define('FROMGPLUS_DEFAULT_POLL_INTERVAL', 30); // given in minutes + function fromgplus_install() { register_hook('plugin_settings', 'addon/fromgplus/fromgplus.php', 'fromgplus_addon_settings'); register_hook('plugin_settings_post', 'addon/fromgplus/fromgplus.php', 'fromgplus_addon_settings_post'); + register_hook('cron', 'addon/fromgplus/fromgplus.php', 'fromgplus_cron'); } function fromgplus_uninstall() { unregister_hook('plugin_settings', 'addon/fromgplus/fromgplus.php', 'fromgplus_addon_settings'); unregister_hook('plugin_settings_post', 'addon/fromgplus/fromgplus.php', 'fromgplus_addon_settings_post'); + unregister_hook('cron', 'addon/fromgplus/fromgplus.php', 'fromgplus_cron'); } function fromgplus_addon_settings(&$a,&$s) { @@ -55,129 +59,330 @@ function fromgplus_addon_settings_post(&$a,&$b) { info( t('Google+ Import Settings saved.') . EOL); } } -/* -function html2bbcode($html) { + +function fromgplus_cron($a,$b) { + $last = get_config('fromgplus','last_poll'); + + $poll_interval = intval(get_config('fromgplus','poll_interval')); + if(! $poll_interval) + $poll_interval = FROMGPLUS_DEFAULT_POLL_INTERVAL; + + if($last) { + $next = $last + ($poll_interval * 60); + if($next > time()) { + logger('fromgplus: poll intervall not reached'); + return; + } + } + + logger('fromgplus: cron_start'); + + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'fromgplus' AND `k` = 'enable' AND `v` = '1' ORDER BY RAND() "); + if(count($r)) { + foreach($r as $rr) { + $account = get_pconfig($rr['uid'],'fromgplus','account'); + if ($account) { + logger('fromgplus: fetching for user '.$rr['uid']); + fromgplus_fetch($a, $rr['uid']); + } + } + } + + logger('fromgplus: cron_end'); + + set_config('fromgplus','last_poll', time()); +} + +function fromgplus_post($a, $uid, $source, $body, $location) { + + //$uid = 2; + + $body = trim($body); + + if (substr($body, 0, 3) == "[b]") { + $pos = strpos($body, "[/b]"); + $title = substr($body, 3, $pos-3); + $body = trim(substr($body, $pos+4)); + } else + $title = ""; + + $_SESSION['authenticated'] = true; + $_SESSION['uid'] = $uid; + + $_REQUEST['type'] = 'wall'; + $_REQUEST['api_source'] = true; + + $_REQUEST['profile_uid'] = $uid; + $_REQUEST['source'] = $source; + + // $_REQUEST['verb'] + // $_REQUEST['parent'] + // $_REQUEST['parent_uri'] + + $_REQUEST['title'] = $title; + $_REQUEST['body'] = $body; + $_REQUEST['location'] = $location; + + logger('fromgplus: posting for user '.$uid); + + require_once('mod/item.php'); + //print_r($_REQUEST); + item_post($a); +} + +function fromgplus_html2bbcode($html) { $bbcode = html_entity_decode($html, ENT_QUOTES, 'UTF-8'); - $bbcode = str_replace(array("\n"), array(""), $bbcode); - $bbcode = str_replace(array("", ""), array("[b]", "[/b]"), $bbcode); - $bbcode = str_replace(array("", ""), array("[i]", "[/i]"), $bbcode); - $bbcode = str_replace(array("", ""), array("[s]", "[/s]"), $bbcode); - $bbcode = str_replace(array("
"), array("\n"), $bbcode); + $bbcode = str_ireplace(array("\n"), array(""), $bbcode); + $bbcode = str_ireplace(array("", ""), array("[b]", "[/b]"), $bbcode); + $bbcode = str_ireplace(array("", ""), array("[i]", "[/i]"), $bbcode); + $bbcode = str_ireplace(array("", ""), array("[s]", "[/s]"), $bbcode); + $bbcode = str_ireplace(array("
"), array("\n"), $bbcode); + $bbcode = str_ireplace(array("
"), array("\n"), $bbcode); + $bbcode = str_ireplace(array("
"), array("\n"), $bbcode); $bbcode = trim(strip_tags($bbcode)); return($bbcode); } -function friendicapost($post) { - global $friendica; +function fromgplus_parse_query($var) + { + /** + * Use this function to parse out the query array element from + * the output of parse_url(). + */ + $var = parse_url($var, PHP_URL_QUERY); + $var = html_entity_decode($var); + $var = explode('&', $var); + $arr = array(); - $api = new Statusnet($friendica["user"], $friendica["pw"], "GooglePlus", $friendica["server"]); - $ret = $api->updateStatus($post); - $api->endSession(); + foreach($var as $val) { + $x = explode('=', $val); + $arr[$x[0]] = $x[1]; + } + unset($val, $x, $var); + return $arr; } -function handleattachments($item) { +function fromgplus_cleanupgoogleproxy($fullImage, $image) { + + $preview = "/w".$fullImage->width."-h".$fullImage->height."/"; + $preview2 = "/w".$fullImage->width."-h".$fullImage->height."-p/"; + $fullImage = str_replace(array($preview, $preview2), array("/", "/"), $fullImage->url); + + $preview = "/w".$image->width."-h".$image->height."/"; + $preview2 = "/w".$image->width."-h".$image->height."-p/"; + $image = str_replace(array($preview, $preview2), array("/", "/"), $image->url); + + $cleaned = array(); + + $queryvar = fromgplus_parse_query($fullImage); + if ($queryvar['url'] != "") + $cleaned["full"] = urldecode($queryvar['url']); + else + $cleaned["full"] = $fullImage; + if (@exif_imagetype($cleaned["full"]) == 0) + $cleaned["full"] = ""; + + $queryvar = fromgplus_parse_query($image); + if ($queryvar['url'] != "") + $cleaned["preview"] = urldecode($queryvar['url']); + else + $cleaned["preview"] = $image; + if (@exif_imagetype($cleaned["preview"]) == 0) + $cleaned["preview"] = ""; + + if ($cleaned["full"] == "") { + $cleaned["full"] = $cleaned["preview"]; + $cleaned["preview"] = ""; + } + + if ($cleaned["full"] == $cleaned["preview"]) + $cleaned["preview"] = ""; + + if ($cleaned["full"] == "") + if (@exif_imagetype($fullImage) != 0) + $cleaned["full"] = $fullImage; + + if ($cleaned["full"] == "") + if (@exif_imagetype($image) != 0) + $cleaned["full"] = $fullImage; + + return($cleaned); +} + +function fromgplus_handleattachments($item) { $post = ""; + $quote = ""; foreach ($item->object->attachments as $attachment) { switch($attachment->objectType) { case "video": - //$post .= "\n\n[url=".$attachment->url."]". - // "[size=large][b]".html2bbcode($attachment->displayName)."[/b][/size][/url]\n"; - $post .= "\n\n[bookmark=".$attachment->url."]".html2bbcode($attachment->displayName)."[/bookmark]\n"; + $post .= "\n\n[bookmark=".$attachment->url."]".fromgplus_html2bbcode($attachment->displayName)."[/bookmark]\n"; - //if (strpos($attachment->embed->url, "youtube.com")) - // $post .= "[youtube]".$attachment->url."[/youtube]\n"; - //else - /// $post .= "[url=".$attachment->url."][img]".$attachment->image->url."[/img][/url]\n"; + /*$images = cleanupgoogleproxy($attachment->fullImage, $attachment->image); + if ($images["preview"] != "") + $post .= "\n[url=".$images["full"]."][img]".$images["preview"]."[/img][/url]\n"; + elseif ($images["full"] != "") + $post .= "\n[img]".$images["full"]."[/img]\n";*/ - ///$post .= "[quote]".trim(html2bbcode($attachment->content))."[/quote]"; break; case "article": - //$post .= "\n\n[url=".$attachment->url."]". - // "[size=large][b]".html2bbcode($attachment->displayName)."[/b][/size][/url]\n"; - $post .= "\n\n[bookmark=".$attachment->url."]".html2bbcode($attachment->displayName)."[/bookmark]\n"; - $post .= "[quote]".trim(html2bbcode($attachment->content))."[/quote]"; + $post .= "\n\n[bookmark=".$attachment->url."]".fromgplus_html2bbcode($attachment->displayName)."[/bookmark]\n"; + + $images = fromgplus_cleanupgoogleproxy($attachment->fullImage, $attachment->image); + if ($images["preview"] != "") + $post .= "\n[url=".$images["full"]."][img]".$images["preview"]."[/img][/url]\n"; + elseif ($images["full"] != "") + $post .= "\n[img]".$images["full"]."[/img]\n"; + + //$post .= "[quote]".trim(fromgplus_html2bbcode($attachment->content))."[/quote]"; + $quote = trim(fromgplus_html2bbcode($attachment->content)); + if ($quote != "") + $quote = "\n[quote]".$quote."[/quote]"; break; case "photo": - //$post .= "\n\n[url=".$attachment->fullImage->url."]". - // "[img]".$attachment->fullImage->url."[/img][/url]\n"; - $post .= "\n\n[img]".$attachment->fullImage->url."[/img]\n"; + $images = fromgplus_cleanupgoogleproxy($attachment->fullImage, $attachment->image); + if ($images["preview"] != "") + $post .= "\n[url=".$images["full"]."][img]".$images["preview"]."[/img][/url]\n"; + elseif ($images["full"] != "") + $post .= "\n[img]".$images["full"]."[/img]\n"; + if ($attachment->displayName != "") - $post .= html2bbcode($attachment->displayName)."\n"; + $post .= fromgplus_html2bbcode($attachment->displayName)."\n"; break; case "photo-album": - $post .= "\n\n[url=".$attachment->url."]". - "[size=large][b]".html2bbcode($attachment->displayName)."[/b][/size][/url]\n"; + $post .= "\n\n[bookmark=".$attachment->url."]".fromgplus_html2bbcode($attachment->displayName)."[/bookmark]\n"; + + $images = fromgplus_cleanupgoogleproxy($attachment->fullImage, $attachment->image); + if ($images["preview"] != "") + $post .= "\n[url=".$images["full"]."][img]".$images["preview"]."[/img][/url]\n"; + elseif ($images["full"] != "") + $post .= "\n[img]".$images["full"]."[/img]\n"; + break; - default: - print_r($attachment); - die(); + case "album": + foreach($attachment->thumbnails as $thumb) { + $preview = "/w".$thumb->image->width."-h".$thumb->image->height."/"; + $preview2 = "/w".$thumb->image->width."-h".$thumb->image->height."-p/"; + $image = str_replace(array($preview, $preview2), array("/", "/"), $thumb->image->url); + + $post .= "\n[url=".$thumb->url."][img]".$image."[/img][/url]\n"; + } break; + case "audio": + $post .= "\n\n[bookmark=".$attachment->url."]".fromgplus_html2bbcode($attachment->displayName)."[/bookmark]\n"; + break; + //default: + // die($attachment->objectType); } } - return($post); + return($post.$quote); } -$result = -file_get_contents("https://www.googleapis.com/plus/v1/people/".$google["id"]."/activities/public?alt=json&pp=1&key=".$google["key"]."&maxResults=".$google["maxfetch"]); -$activities = json_decode($result); +function fromgplus_fetch($a, $uid) { + $maxfetch = 20; -$state = array("lastid"=>''); -if (file_exists($statefile)) - $state = unserialize(file_get_contents($statefile)); + $account = get_pconfig($uid,'fromgplus','account'); + $key = get_config('fromgplus','key'); -$lastid = ""; + $result = fetch_url("https://www.googleapis.com/plus/v1/people/".$account."/activities/public?alt=json&pp=1&key=".$key."&maxResults=".$maxfetch); + //$result = file_get_contents("google.txt"); + //file_put_contents("google.txt", $result); -foreach($activities->items as $item) { - if ($item->id == $state["lastid"]) - break; + $activities = json_decode($result); - if ($lastid == "") - $lastid = $item->id; + $initiallastdate = get_pconfig($uid,'fromgplus','lastdate'); - switch($item->object->objectType) { - case "note": - $post = html2bbcode($item->object->content); + $lastdate = 0; - if (is_array($item->object->attachments)) - $post .= handleattachments($item); - friendicapost($post); - break; + if (!is_array($activities->items)) + return; - case "activity": - $post = html2bbcode($item->annotation)."\n"; - //$post .= html2bbcode("♲ "); - $post .= html2bbcode("♻ "); - $post .= "[url=".$item->object->actor->url."]".$item->object->actor->displayName."[/url]"; - $post .= " \n"; - //$post .= "[quote]"; + $reversed = array_reverse($activities->items); - $post .= html2bbcode($item->object->content); + foreach($reversed as $item) { + if (strtotime($item->published) <= $initiallastdate) + continue; - if (is_array($item->object->attachments)) - $post .= "\n".trim(handleattachments($item)); + if ($lastdate < strtotime($item->published)) + $lastdate = strtotime($item->published); - //$post .= "[/quote]"; + if ($item->access->description == "Public") + switch($item->object->objectType) { + case "note": + $post = fromgplus_html2bbcode($item->object->content); - friendicapost($post); - break; + if (is_array($item->object->attachments)) + $post .= fromgplus_handleattachments($item); - default: - print_r($item); - die(); - break; + // geocode, placeName + if (isset($item->address)) + $location = $item->address; + else + $location = ""; + + fromgplus_post($a, $uid, $item->provider->title, $post, $location); + + break; + + case "activity": + $post = fromgplus_html2bbcode($item->annotation)."\n"; + + if (intval(get_config('system','new_share'))) { + $post .= "[share author='".str_replace("'", "'",$item->object->actor->displayName). + "' profile='".$item->object->actor->url. + "' avatar='".$item->object->actor->image->url. + "' link='".$item->object->url."']"; + + $post .= fromgplus_html2bbcode($item->object->content); + + if (is_array($item->object->attachments)) + $post .= "\n".trim(fromgplus_handleattachments($item)); + + $post .= "[/share]"; + } else { + $post .= fromgplus_html2bbcode("♲"); + $post .= " [url=".$item->object->actor->url."]".$item->object->actor->displayName."[/url] \n"; + $post .= fromgplus_html2bbcode($item->object->content); + + if (is_array($item->object->attachments)) + $post .= "\n".trim(fromgplus_handleattachments($item)); + } + + if (isset($item->address)) + $location = $item->address; + else + $location = ""; + + fromgplus_post($a, $uid, $item->provider->title, $post, $location); + break; + } } + if ($lastdate != 0) + set_pconfig($uid,'fromgplus','lastdate', $lastdate); } -if ($lastid != "") { - $state['lastid'] = $lastid; - file_put_contents($statefile, serialize($state)); +/* +// Test +require_once("boot.php"); + +if(@is_null($a)) { + $a = new App; } + +if(@is_null($db)) { + @include(".htconfig.php"); + require_once("include/dba.php"); + $db = new dba($db_host, $db_user, $db_pass, $db_data); + unset($db_host, $db_user, $db_pass, $db_data); +}; + +$test = array(); +fromgplus_cron($a, $test); */ diff --git a/geonames.tgz b/geonames.tgz index 952a7602..68b78861 100644 Binary files a/geonames.tgz and b/geonames.tgz differ diff --git a/geonames/geonames.php b/geonames/geonames.php index 8226fc0b..19725bef 100755 --- a/geonames/geonames.php +++ b/geonames/geonames.php @@ -40,8 +40,8 @@ function geonames_install() { * */ - register_hook('plugin_settings', 'addon/geonames/geonames.php', 'geonames_settings'); - register_hook('plugin_settings_post', 'addon/geonames/geonames.php', 'geonames_settings_post'); + register_hook('plugin_settings', 'addon/geonames/geonames.php', 'geonames_plugin_admin'); + register_hook('plugin_settings_post', 'addon/geonames/geonames.php', 'geonames_plugin_admin_post'); logger("installed geonames"); } @@ -58,8 +58,8 @@ function geonames_uninstall() { */ unregister_hook('post_local', 'addon/geonames/geonames.php', 'geonames_post_hook'); - unregister_hook('plugin_settings', 'addon/geonames/geonames.php', 'geonames_settings'); - unregister_hook('plugin_settings_post', 'addon/geonames/geonames.php', 'geonames_settings_post'); + unregister_hook('plugin_settings', 'addon/geonames/geonames.php', 'geonames_plugin_admin'); + unregister_hook('plugin_settings_post', 'addon/geonames/geonames.php', 'geonames_plugin_admin_post'); logger("removed geonames"); @@ -135,7 +135,7 @@ function geonames_post_hook($a, &$item) { * */ -function geonames_settings_post($a,$post) { +function geonames_plugin_admin_post($a,$post) { if(! local_user() || (! x($_POST,'geonames-submit'))) return; set_pconfig(local_user(),'geonames','enable',intval($_POST['geonames'])); @@ -153,7 +153,7 @@ function geonames_settings_post($a,$post) { -function geonames_settings(&$a,&$s) { +function geonames_plugin_admin(&$a,&$s) { if(! local_user()) return; diff --git a/gravatar.tgz b/gravatar.tgz index 25106f26..561c4aae 100644 Binary files a/gravatar.tgz and b/gravatar.tgz differ diff --git a/gravatar/gravatar.php b/gravatar/gravatar.php index fc5358eb..12a8e44f 100644 --- a/gravatar/gravatar.php +++ b/gravatar/gravatar.php @@ -55,7 +55,7 @@ function gravatar_lookup($a, &$b) { * Display admin settings for this addon */ function gravatar_plugin_admin (&$a, &$o) { - $t = file_get_contents( dirname(__file__)."/admin.tpl"); + $t = get_markup_template( "admin.tpl", "addon/gravatar/" ); $default_avatar = get_config('gravatar', 'default_img'); $rating = get_config('gravatar', 'rating'); diff --git a/gravatar/admin.tpl b/gravatar/view/admin.tpl similarity index 100% rename from gravatar/admin.tpl rename to gravatar/view/admin.tpl diff --git a/gravatar/view/smarty3/admin.tpl b/gravatar/view/smarty3/admin.tpl new file mode 100644 index 00000000..5dfd4488 --- /dev/null +++ b/gravatar/view/smarty3/admin.tpl @@ -0,0 +1,3 @@ +{{include file="field_select.tpl" field=$default_avatar}} +{{include file="field_select.tpl" field=$rating}} +
diff --git a/group_text.tgz b/group_text.tgz new file mode 100644 index 00000000..5dca3140 Binary files /dev/null and b/group_text.tgz differ diff --git a/group_text/group_text.php b/group_text/group_text.php index 151ff0ae..5ec5c9c2 100755 --- a/group_text/group_text.php +++ b/group_text/group_text.php @@ -43,7 +43,7 @@ function group_text_settings_post($a,$post) { return; set_pconfig(local_user(),'system','groupedit_image_limit',intval($_POST['group_text'])); - info( t('Editplain settings updated.') . EOL); + info( t('Group Text settings updated.') . EOL); } diff --git a/impressum.tgz b/impressum.tgz index d87f17f7..b47b625c 100755 Binary files a/impressum.tgz and b/impressum.tgz differ diff --git a/impressum/impressum.php b/impressum/impressum.php index 9d038178..3c1106c9 100755 --- a/impressum/impressum.php +++ b/impressum/impressum.php @@ -78,7 +78,7 @@ function impressum_plugin_admin_post (&$a) { info( t('Settings updated.'). EOL ); } function impressum_plugin_admin (&$a, &$o) { - $t = file_get_contents( dirname(__file__). "/admin.tpl" ); + $t = get_markup_template( "admin.tpl", "addon/impressum/" ); $o = replace_macros($t, array( '$submit' => t('Submit'), '$owner' => array('owner', t('Site Owner'), get_config('impressum','owner'), t('The page operators name.')), diff --git a/impressum/admin.tpl b/impressum/view/admin.tpl old mode 100755 new mode 100644 similarity index 100% rename from impressum/admin.tpl rename to impressum/view/admin.tpl diff --git a/impressum/view/smarty3/admin.tpl b/impressum/view/smarty3/admin.tpl new file mode 100644 index 00000000..80b67824 --- /dev/null +++ b/impressum/view/smarty3/admin.tpl @@ -0,0 +1,7 @@ +{{include file="field_input.tpl" field=$owner}} +{{include file="field_input.tpl" field=$ownerprofile}} +{{include file="field_textarea.tpl" field=$postal}} +{{include file="field_textarea.tpl" field=$notes}} +{{include file="field_input.tpl" field=$email}} +{{include file="field_textarea.tpl" field=$footer_text}} +
diff --git a/jappixmini.tgz b/jappixmini.tgz index 212bb25e..56d6dfa1 100644 Binary files a/jappixmini.tgz and b/jappixmini.tgz differ diff --git a/js_upload.tgz b/js_upload.tgz index 7b684103..5a415de7 100755 Binary files a/js_upload.tgz and b/js_upload.tgz differ diff --git a/js_upload/js_upload.php b/js_upload/js_upload.php index 3ba5f9c4..148fde31 100755 --- a/js_upload/js_upload.php +++ b/js_upload/js_upload.php @@ -196,7 +196,13 @@ class qqUploadedFileXhr { */ function save() { $input = fopen("php://input", "r"); - $this->pathnm = tempnam(sys_get_temp_dir(),'frn'); + + $upload_dir = get_config('system','tempdir'); + if(! $upload_dir) + $upload_dir = sys_get_temp_dir(); + + $this->pathnm = tempnam($upload_dir,'frn'); + $temp = fopen($this->pathnm,"w"); $realSize = stream_copy_to_stream($input, $temp); diff --git a/libravatar.tgz b/libravatar.tgz index 0405af9b..d61f63ba 100644 Binary files a/libravatar.tgz and b/libravatar.tgz differ diff --git a/libravatar/libravatar.php b/libravatar/libravatar.php index 08ed6d00..8cbf1e98 100644 --- a/libravatar/libravatar.php +++ b/libravatar/libravatar.php @@ -60,7 +60,7 @@ function libravatar_lookup($a, &$b) { * Display admin settings for this addon */ function libravatar_plugin_admin (&$a, &$o) { - $t = file_get_contents( dirname(__file__)."/admin.tpl"); + $t = get_markup_template( "admin.tpl", "addon/libravatar" ); $default_avatar = get_config('libravatar', 'default_img'); diff --git a/libravatar/admin.tpl b/libravatar/view/admin.tpl similarity index 100% rename from libravatar/admin.tpl rename to libravatar/view/admin.tpl diff --git a/libravatar/view/smarty3/admin.tpl b/libravatar/view/smarty3/admin.tpl new file mode 100644 index 00000000..ee958287 --- /dev/null +++ b/libravatar/view/smarty3/admin.tpl @@ -0,0 +1,2 @@ +{{include file="field_select.tpl" field=$default_avatar}} +
diff --git a/ljpost.tgz b/ljpost.tgz index 840e2ee2..db016585 100644 Binary files a/ljpost.tgz and b/ljpost.tgz differ diff --git a/ljpost/ljpost.css b/ljpost/ljpost.css index 2087d3f0..e0494c5c 100755 --- a/ljpost/ljpost.css +++ b/ljpost/ljpost.css @@ -1,6 +1,5 @@ #ljpost-enable-label, #ljpost-username-label, #ljpost-password-label, #ljpost-bydefault-label { -<<<<<<< HEAD float: left; width: 200px; margin-top: 10px; @@ -15,18 +14,3 @@ margin-top: 15px; } -======= -float: left; -width: 200px; -margin-top: 10px; -} - -#ljpost-checkbox, #ljpost-username, #ljpost-password, #ljpost-bydefault { -float: left; -margin-top: 10px; -} - -#ljpost-submit { -margin-top: 15px; -} ->>>>>>> 99d9fddb6af9e872266666038447771e42ce13b4 diff --git a/mahjongg.tar b/mahjongg.tar new file mode 100644 index 00000000..56d70515 Binary files /dev/null and b/mahjongg.tar differ diff --git a/mahjongg/mahjongg.php b/mahjongg/mahjongg.php new file mode 100755 index 00000000..977c693d --- /dev/null +++ b/mahjongg/mahjongg.php @@ -0,0 +1,42 @@ +Mahjongg
'; +} + + +function mahjongg_module() {} + +function mahjongg_content(&$a) { + +$baseurl = $a->get_baseurl() . '/addon/mahjongg'; + +$o .= <<< EOT +

+

+ +

+Simply locate the matching tiles and find a way to clear them from the board as quickly as possible. +A timer at the top of the screen keeps track of how you are doing.
+

+EOT; + +return $o; +} diff --git a/mahjongg/mahjongg.swf b/mahjongg/mahjongg.swf new file mode 100755 index 00000000..79f8c9e3 Binary files /dev/null and b/mahjongg/mahjongg.swf differ diff --git a/mathjax.tgz b/mathjax.tgz index bb591e1d..a340aee4 100644 Binary files a/mathjax.tgz and b/mathjax.tgz differ diff --git a/mathjax/mathjax.php b/mathjax/mathjax.php index 7105772b..e57d69bf 100644 --- a/mathjax/mathjax.php +++ b/mathjax/mathjax.php @@ -66,11 +66,12 @@ function mathjax_plugin_admin_post (&$a) { info( t('Settings updated.'). EOL); } function mathjax_plugin_admin (&$a, &$o) { - $t = file_get_contents( dirname(__file__)."/admin.tpl"); - if (get_config('mathjax','baseurl','') == '') { - set_config('mathjax','baseurl','http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'); - } - $o = replace_macros( $t, array( + $t = get_markup_template( "admin.tpl", "addon/mathjax/" ); + if (get_config('mathjax','baseurl','') == '') { + set_config('mathjax','baseurl','http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'); + } + + $o = replace_macros( $t, array( '$baseurl' => array('baseurl', t('MathJax Base URL'), get_config('mathjax','baseurl' ), t('The URL for the javascript file that should be included to use MathJax. Can be either the MathJax CDN or another installation of MathJax.')), - )); + )); } diff --git a/mathjax/admin.tpl b/mathjax/view/admin.tpl similarity index 100% rename from mathjax/admin.tpl rename to mathjax/view/admin.tpl diff --git a/mathjax/view/smarty3/admin.tpl b/mathjax/view/smarty3/admin.tpl new file mode 100644 index 00000000..f6ec03e0 --- /dev/null +++ b/mathjax/view/smarty3/admin.tpl @@ -0,0 +1,2 @@ +{{include file="field_input.tpl" field=$baseurl}} +
diff --git a/morepokes.tgz b/morepokes.tgz new file mode 100644 index 00000000..7999b719 Binary files /dev/null and b/morepokes.tgz differ diff --git a/morepokes/morepokes.php b/morepokes/morepokes.php index bdbd7dcf..717643ec 100644 --- a/morepokes/morepokes.php +++ b/morepokes/morepokes.php @@ -19,9 +19,8 @@ function morepokes_poke_verbs($a,&$b) { $b['bitchslap'] = array('bitchslapped', t('bitchslap'), t('bitchslapped')); $b['shag'] = array('shag', t('shag'), t('shagged')); $b['somethingobscenelybiological'] = array('something obscenely biological', t('do something obscenely biological to'), t('did something obscenely biological to')); - $b['newpokefeature'] = array('pointed out the new poke feature to', t('point out the new poke feature to'), t('pointed out the new poke feature to')); + $b['newpokefeature'] = array('pointed out the poke feature to', t('point out the poke feature to'), t('pointed out the poke feature to')); $b['declareundyinglove'] = array('declared undying love for', t('declare undying love for'), t('declared undying love for')); - $b['setfireto'] = array('set fire to', t('set fire to'), t('set fire to')); $b['patent'] = array('patented', t('patent'), t('patented')); $b['strokebeard'] = array('stroked their beard at', t('stroke beard'), t('stroked their beard at')); $b['bemoan'] = array('bemoaned the declining standards of modern secondary and tertiary education to', t('bemoan the declining standards of modern secondary and tertiary education to'), t('bemoans the declining standards of modern secondary and tertiary education to')); @@ -35,4 +34,4 @@ function morepokes_poke_verbs($a,&$b) { $b['giggleandfawn'] = array('giggled and fawned at', t('giggle and fawn at'), t('giggled and fawned at')); $b['doubt'] = array('doubted', t('doubt'), t('doubted')); $b['glare'] = array('glared at', t('glare'), t('glared at')); -;} \ No newline at end of file +;} diff --git a/nsfw.tgz b/nsfw.tgz index 73870dc3..a35ec568 100755 Binary files a/nsfw.tgz and b/nsfw.tgz differ diff --git a/nsfw/nsfw.php b/nsfw/nsfw.php index 60ab4581..96e1a9bf 100755 --- a/nsfw/nsfw.php +++ b/nsfw/nsfw.php @@ -24,6 +24,37 @@ function nsfw_uninstall() { } +// This function isn't perfect and isn't trying to preserve the html structure - it's just a +// quick and dirty filter to pull out embedded photo blobs because 'nsfw' seems to come up +// inside them quite often. We don't need anything fancy, just pull out the data blob so we can +// check against the rest of the body. + +function nsfw_extract_photos($body) { + + $new_body = ''; + + $img_start = strpos($body,'src="data:'); + $img_end = (($img_start !== false) ? strpos(substr($body,$img_start),'>') : false); + + $cnt = 0; + + while($img_end !== false) { + $img_end += $img_start; + $new_body = $new_body . substr($body,0,$img_start); + + $cnt ++; + $body = substr($body,0,$img_end); + + $img_start = strpos($body,'src="data:'); + $img_end = (($img_start !== false) ? strpos(substr($body,$img_start),'>') : false); + + } + + if(! $cnt) + return $body; + + return $new_body; +} @@ -77,6 +108,7 @@ function nsfw_addon_settings_post(&$a,&$b) { function nsfw_prepare_body(&$a,&$b) { + $words = null; if(get_pconfig(local_user(),'nsfw','disable')) return; @@ -93,19 +125,22 @@ function nsfw_prepare_body(&$a,&$b) { $found = false; if(count($arr)) { + + $body = nsfw_extract_photos($b['html']); + foreach($arr as $word) { $word = trim($word); if(! strlen($word)) { continue; } if(strpos($word,'/') === 0) { - if(preg_match($word,$b['html'])) { + if(preg_match($word,$body)) { $found = true; break; } } else { - if(stristr($b['html'],$word)) { + if(stristr($body,$word)) { $found = true; break; } @@ -115,6 +150,7 @@ function nsfw_prepare_body(&$a,&$b) { } } } + } if($found) { $rnd = random_string(8); diff --git a/openstreetmap.tgz b/openstreetmap.tgz index 34f4bd6b..562115c7 100644 Binary files a/openstreetmap.tgz and b/openstreetmap.tgz differ diff --git a/openstreetmap/openstreetmap.js b/openstreetmap/openstreetmap.js new file mode 100644 index 00000000..479e769c --- /dev/null +++ b/openstreetmap/openstreetmap.js @@ -0,0 +1,41 @@ +var toolserver = 'http://toolserver.org/~kolossos/openlayers/kml-on-ol.php'; +var startTag = ''; + +jQuery(document).ready(function($) { + + $('.wall-item-content-wrapper').each(function(index) { + var link = $(this).find('.wall-item-location .OSMMapLink'); + link.toggle(addIframe, removeIframe); + }); +}); + +function addIframe(ev) { + var coordinate = $(ev.target).attr('title'); + + var newTag = startTag + convertCoordinateString(coordinate) + endTag; + $(ev.target).parents('.wall-item-content-wrapper').append(newTag); +} + +function removeIframe(ev) { + $(ev.target).parents('.wall-item-content-wrapper').find('iframe').remove(); + +} + +function convertCoordinateString(coordinate) { + var locstring = coordinate.split(' '); + var northSouth; + var westEast; + + if (locstring[0] < 0) { + northSouth = '_S_'; + }else{ + northSouth = '_N_'; + } + if (locstring[1] < 0) { + westEast = '_W'; + }else{ + westEast = '_E'; + } + return Math.abs(locstring[0]) + northSouth + Math.abs(locstring[1]) + westEast; +} \ No newline at end of file diff --git a/openstreetmap/openstreetmap.php b/openstreetmap/openstreetmap.php index fda29905..2c5975eb 100755 --- a/openstreetmap/openstreetmap.php +++ b/openstreetmap/openstreetmap.php @@ -10,24 +10,33 @@ function openstreetmap_install() { register_hook('render_location', 'addon/openstreetmap/openstreetmap.php', 'openstreetmap_location'); + register_hook('page_header', 'addon/openstreetmap/openstreetmap.php', 'openstreetmap_alterheader'); logger("installed openstreetmap"); } function openstreetmap_uninstall() { unregister_hook('render_location', 'addon/openstreetmap/openstreetmap.php', 'openstreetmap_location'); + unregister_hook('page_header', 'addon/openstreetmap/openstreetmap.php', 'openstreetmap_alterheader'); logger("removed openstreetmap"); } +function openstreetmap_alterheader($a, &$navHtml) { + $addScriptTag='' . "\r\n"; + $a->page['htmlhead'] .= $addScriptTag; +} function openstreetmap_location($a, &$item) { + + // + if(! (strlen($item['location']) || strlen($item['coord']))) - return; + return; /* * Get the configuration variables from the .htconfig file. - */ + */ $tmsserver = get_config('openstreetmap','tmsserver'); if(! $tmsserver) $tmsserver = 'http://openstreetmap.org'; @@ -38,12 +47,27 @@ function openstreetmap_location($a, &$item) { $location = ''; $coord = ''; + + if($item['location'] && !$item['coord'] && true){ //if only a location is given, find the lat-lon + $geo_account='demo'; + + $s = fetch_url('http://api.geonames.org/search?maxRows=1&fuzzy=0.8&q=' . $item['location'] . '&username=' . $geo_account); + + if($s){ + $xml = parse_xml_string($s); + + if($xml->geoname->lat && $xml->geoname->lng){ + $item['coord'] = $xml->geoname->lat . ' ' . $xml->geoname->lng; + } + } + } + $location = (($item['location']) ? '' . $item['location'] . '' : ''); if($item['coord']) { $coords = explode(' ', $item['coord']); if(count($coords) > 1) { - $coord = '' . $item['coord'] . '' ; + $coord = ' Map ' ; } } if(strlen($coord)) { @@ -58,7 +82,7 @@ function openstreetmap_location($a, &$item) { function openstreetmap_plugin_admin (&$a, &$o) { - $t = file_get_contents( dirname(__file__)."/admin.tpl"); + $t = get_markup_template( "admin.tpl", "addon/openstreetmap/" ); $tmsserver = get_config('openstreetmap','tmsserver'); if(! $tmsserver) $tmsserver = 'http://openstreetmap.org'; @@ -67,9 +91,9 @@ function openstreetmap_plugin_admin (&$a, &$o) { $zoom = 17; $o = replace_macros( $t, array( - '$submit' => t('Submit'), - '$tmsserver' => array('tmsserver', t('Tile Server URL'), $tmsserver, t('A list of public tile servers')), - '$zoom' => array('zoom', t('Default zoom'), $zoom, t('The default zoom level. (1:world, 18:highest)')), + '$submit' => t('Submit'), + '$tmsserver' => array('tmsserver', t('Tile Server URL'), $tmsserver, t('A list of public tile servers')), + '$zoom' => array('zoom', t('Default zoom'), $zoom, t('The default zoom level. (1:world, 18:highest)')), )); } function openstreetmap_plugin_admin_post (&$a) { diff --git a/openstreetmap/admin.tpl b/openstreetmap/view/admin.tpl similarity index 100% rename from openstreetmap/admin.tpl rename to openstreetmap/view/admin.tpl diff --git a/openstreetmap/view/smarty3/admin.tpl b/openstreetmap/view/smarty3/admin.tpl new file mode 100644 index 00000000..6ee07174 --- /dev/null +++ b/openstreetmap/view/smarty3/admin.tpl @@ -0,0 +1,3 @@ +{{include file="field_input.tpl" field=$tmsserver}} +{{include file="field_input.tpl" field=$zoom}} +
diff --git a/page.tgz b/page.tgz index d300a3e2..c9610e28 100644 Binary files a/page.tgz and b/page.tgz differ diff --git a/page/page.php b/page/page.php index 3f0f0419..21a83e16 100755 --- a/page/page.php +++ b/page/page.php @@ -1,7 +1,7 @@ * based on pages plugin by @@ -38,6 +38,7 @@ function page_getpage($uid,$showhidden = true,$randomise = false) { $contacts = q("SELECT `id`, `url`, `name`, `micro` FROM `contact` WHERE `network`= 'dfrn' AND `forum` = 1 AND `uid` = %d + and blocked = 0 and hidden = 0 and pending = 0 and archive = 0 $order ", intval($uid) ); diff --git a/piwik.tgz b/piwik.tgz index 080dd9e3..a5bd437a 100755 Binary files a/piwik.tgz and b/piwik.tgz differ diff --git a/piwik/piwik.php b/piwik/piwik.php index 9ba15db6..3501b2c8 100755 --- a/piwik/piwik.php +++ b/piwik/piwik.php @@ -84,7 +84,7 @@ function piwik_analytics($a,&$b) { } } function piwik_plugin_admin (&$a, &$o) { - $t = file_get_contents( dirname(__file__)."/admin.tpl"); + $t = get_markup_template( "admin.tpl", "addon/piwik/" ); $o = replace_macros( $t, array( '$submit' => t('Submit'), '$baseurl' => array('baseurl', t('Piwik Base URL'), get_config('piwik','baseurl' ), t('Absolute path to your Piwik installation. (without protocol (http/s), with trailing slash)')), diff --git a/piwik/admin.tpl b/piwik/view/admin.tpl old mode 100755 new mode 100644 similarity index 100% rename from piwik/admin.tpl rename to piwik/view/admin.tpl diff --git a/piwik/view/smarty3/admin.tpl b/piwik/view/smarty3/admin.tpl new file mode 100644 index 00000000..bfe04aa3 --- /dev/null +++ b/piwik/view/smarty3/admin.tpl @@ -0,0 +1,5 @@ +{{include file="field_input.tpl" field=$baseurl}} +{{include file="field_input.tpl" field=$siteid}} +{{include file="field_checkbox.tpl" field=$optout}} +{{include file="field_checkbox.tpl" field=$async}} +
diff --git a/pledgie/pledgie.php b/pledgie/pledgie.php new file mode 100644 index 00000000..8f6228f6 --- /dev/null +++ b/pledgie/pledgie.php @@ -0,0 +1,16 @@ + + * */ + + +function pledgie_install() { register_hook('page_end', 'addon/pledgie/pledgie.php', 'pledgie_active'); } + + +function pledgie_uninstall() { unregister_hook('page_end', 'addon/pledgie/pledgie.php', 'pledgie_active'); } + +function pledgie_active(&$a,&$b) { $b .= '
\'Click
'; } + diff --git a/privacy_image_cache.tgz b/privacy_image_cache.tgz index b4c10080..45444a68 100644 Binary files a/privacy_image_cache.tgz and b/privacy_image_cache.tgz differ diff --git a/privacy_image_cache/privacy_image_cache.php b/privacy_image_cache/privacy_image_cache.php index f4ada61e..b0909e81 100644 --- a/privacy_image_cache/privacy_image_cache.php +++ b/privacy_image_cache/privacy_image_cache.php @@ -9,6 +9,7 @@ define("PRIVACY_IMAGE_CACHE_DEFAULT_TIME", 86400); // 1 Day require_once('include/security.php'); +require_once("include/Photo.php"); function privacy_image_cache_install() { register_hook('prepare_body', 'addon/privacy_image_cache/privacy_image_cache.php', 'privacy_image_cache_prepare_body_hook'); @@ -30,48 +31,85 @@ function privacy_image_cache_uninstall() { function privacy_image_cache_module() {} - function privacy_image_cache_init() { - global $a; + global $a, $_SERVER; - if ($a->config["system"]["db_log"] != "") - $stamp1 = microtime(true); + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { + header('HTTP/1.1 304 Not Modified'); + header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT"); + header('Etag: '.$_SERVER['HTTP_IF_NONE_MATCH']); + header("Expires: " . gmdate("D, d M Y H:i:s", time() + (31536000)) . " GMT"); + header("Cache-Control: max-age=31536000"); + if(function_exists('header_remove')) { + header_remove('Last-Modified'); + header_remove('Expires'); + header_remove('Cache-Control'); + } + exit; + } if(function_exists('header_remove')) { header_remove('Pragma'); header_remove('pragma'); } + $thumb = false; + + // Look for filename in the arguments + if (isset($a->argv[1]) OR isset($a->argv[2]) OR isset($a->argv[3])) { + if (isset($a->argv[3])) + $url = $a->argv[3]; + elseif (isset($a->argv[2])) + $url = $a->argv[2]; + else + $url = $a->argv[1]; + + $pos = strrpos($url, "=."); + if ($pos) + $url = substr($url, 0, $pos+1); + + $url = str_replace(array(".jpg", ".jpeg", ".gif", ".png"), array("","","",""), $url); + + $url = base64_decode(strtr($url, '-_', '+/'), true); + + if ($url) + $_REQUEST['url'] = $url; + $thumb = (isset($a->argv[3]) and ($a->argv[3] == "thumb")); + } + $urlhash = 'pic:' . sha1($_REQUEST['url']); // Double encoded url - happens with Diaspora $urlhash2 = 'pic:' . sha1(urldecode($_REQUEST['url'])); - $cache = get_config('system','itemcache'); - if (($cache != '') and is_dir($cache)) { - $cachefile = $cache."/".hash("md5", $_REQUEST['url']); + $cachefile = get_cachefile(hash("md5", $_REQUEST['url'])); + if ($cachefile != '') { if (file_exists($cachefile)) { $img_str = file_get_contents($cachefile); - $mime = image_type_to_mime_type(exif_imagetype($cachefile)); header("Content-type: $mime"); - header("Expires: " . gmdate("D, d M Y H:i:s", time() + (3600*24)) . " GMT"); - header("Cache-Control: max-age=" . (3600*24)); + header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT"); + header('Etag: "'.md5($img_str).'"'); + header("Expires: " . gmdate("D, d M Y H:i:s", time() + (31536000)) . " GMT"); + header("Cache-Control: max-age=31536000"); + + // reduce quality - if it isn't a GIF + if ($mime != "image/gif") { + $img = new Photo($img_str, $mime); + if($img->is_valid()) + $img_str = $img->imageString(); + } echo $img_str; - if ($a->config["system"]["db_log"] != "") { - $stamp2 = microtime(true); - $duration = round($stamp2-$stamp1, 3); - if ($duration > $a->config["system"]["db_loglimit"]) - @file_put_contents($a->config["system"]["db_log"], $duration."\t".strlen($img_str)."\t".$_REQUEST['url']."\n", FILE_APPEND); - } + if (is_dir($_SERVER["DOCUMENT_ROOT"]."/privacy_image_cache")) + file_put_contents($_SERVER["DOCUMENT_ROOT"]."/privacy_image_cache/".privacy_image_cache_cachename($_REQUEST['url'], true), $img_str); killme(); } } - require_once("Photo.php"); + $valid = true; $r = q("SELECT * FROM `photo` WHERE `resource-id` in ('%s', '%s') LIMIT 1", $urlhash, $urlhash2); if (count($r)) { @@ -79,21 +117,14 @@ function privacy_image_cache_init() { $mime = $r[0]["desc"]; if ($mime == "") $mime = "image/jpeg"; - // Test - //if ($mime == "image/jpeg") { - // $img = new Photo($img_str); - // if($img->is_valid()) { - // $img->scaleImage(1000); - // $img_str = $img->imageString(); - // } - //} } else { // It shouldn't happen but it does - spaces in URL $_REQUEST['url'] = str_replace(" ", "+", $_REQUEST['url']); - $img_str = fetch_url($_REQUEST['url'],true); + $redirects = 0; + $img_str = fetch_url($_REQUEST['url'],true, $redirects, 10); - $tempfile = tempnam("", "cache"); + $tempfile = tempnam(get_config("system","temppath"), "cache"); file_put_contents($tempfile, $img_str); $mime = image_type_to_mime_type(exif_imagetype($tempfile)); unlink($tempfile); @@ -102,7 +133,13 @@ function privacy_image_cache_init() { if ((substr($a->get_curl_code(), 0, 1) == "4") or (!$img_str)) { $img_str = file_get_contents("images/blank.png"); $mime = "image/png"; - //} else if (substr($img_str, 0, 6) == "GIF89a") { + $cachefile = ""; // Clear the cachefile so that the dummy isn't stored + $valid = false; + $img = new Photo($img_str, "image/png"); + if($img->is_valid()) { + $img->scaleImage(10); + $img_str = $img->imageString(); + } } else if ($mime != "image/jpeg") { $image = @imagecreatefromstring($img_str); @@ -126,34 +163,75 @@ function privacy_image_cache_init() { ); } else { - $img = new Photo($img_str); + $img = new Photo($img_str, $mime); if($img->is_valid()) { $img->store(0, 0, $urlhash, $_REQUEST['url'], '', 100); - //$img->scaleImage(1000); // Test + if ($thumb) + $img->scaleImage(200); // Test $img_str = $img->imageString(); } - $mime = "image/jpeg"; + //$mime = "image/jpeg"; } } + // reduce quality - if it isn't a GIF + if ($mime != "image/gif") { + $img = new Photo($img_str, $mime); + if($img->is_valid()) + $img_str = $img->imageString(); + } - // Writing in cachefile - if (isset($cachefile) && ($cachefile != '') and (file_exists($cachefile)) and (exif_imagetype($cachefile) > 0)) + // If there is a real existing directory then put the cache file there + // advantage: real file access is really fast + // Otherwise write in cachefile + if ($valid AND is_dir($_SERVER["DOCUMENT_ROOT"]."/privacy_image_cache")) + file_put_contents($_SERVER["DOCUMENT_ROOT"]."/privacy_image_cache/".privacy_image_cache_cachename($_REQUEST['url'], true), $img_str); + elseif ($cachefile != '') file_put_contents($cachefile, $img_str); header("Content-type: $mime"); - header("Expires: " . gmdate("D, d M Y H:i:s", time() + (3600*24)) . " GMT"); - header("Cache-Control: max-age=" . (3600*24)); + + // Only output the cache headers when the file is valid + if ($valid) { + header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT"); + header('Etag: "'.md5($img_str).'"'); + header("Expires: " . gmdate("D, d M Y H:i:s", time() + (31536000)) . " GMT"); + header("Cache-Control: max-age=31536000"); + } echo $img_str; - if ($a->config["system"]["db_log"] != "") { - $stamp2 = microtime(true); - $duration = round($stamp2-$stamp1, 3); - if ($duration > $a->config["system"]["db_loglimit"]) - @file_put_contents($a->config["system"]["db_log"], $duration."\t".strlen($img_str)."\t".$_REQUEST['url']."\n", FILE_APPEND); + killme(); +} + +function privacy_image_cache_cachename($url, $writemode = false) { + global $_SERVER; + + $pos = strrpos($url, "."); + if ($pos) { + $extension = strtolower(substr($url, $pos+1)); + $pos = strpos($extension, "?"); + if ($pos) + $extension = substr($extension, 0, $pos); } - killme(); + $basepath = $_SERVER["DOCUMENT_ROOT"]."/privacy_image_cache"; + + $path = substr(hash("md5", $url), 0, 2); + + if (is_dir($basepath) and $writemode) + if (!is_dir($basepath."/".$path)) { + mkdir($basepath."/".$path); + chmod($basepath."/".$path, 0777); + } + + $path .= "/".strtr(base64_encode($url), '+/', '-_'); + + $extensions = array("jpg", "jpeg", "gif", "png"); + + if (in_array($extension, $extensions)) + $path .= ".".$extension; + + return($path); } /** @@ -161,13 +239,21 @@ function privacy_image_cache_init() { * @return boolean */ function privacy_image_cache_is_local_image($url) { - if ($url[0] == '/') return true; + if ($url[0] == '/') return true; + if (strtolower(substr($url, 0, 5)) == "data:") return true; + // Check if the cached path would be longer than 255 characters - apache doesn't like it + if (is_dir($_SERVER["DOCUMENT_ROOT"]."/privacy_image_cache")) { + $cachedurl = get_app()->get_baseurl()."/privacy_image_cache/". privacy_image_cache_cachename($url); + if (strlen($url) > 150) + return true; + } + // links normalised - bug #431 - $baseurl = normalise_link(get_app()->get_baseurl()); + $baseurl = normalise_link(get_app()->get_baseurl()); $url = normalise_link($url); - return (substr($url, 0, strlen($baseurl)) == $baseurl); + return (substr($url, 0, strlen($baseurl)) == $baseurl); } /** @@ -175,11 +261,23 @@ function privacy_image_cache_is_local_image($url) { * @return string */ function privacy_image_cache_img_cb($matches) { + + // if the picture seems to be from another picture cache then take the original source + $queryvar = privacy_image_cache_parse_query($matches[2]); + if ($queryvar['url'] != "") + $matches[2] = urldecode($queryvar['url']); + + // if fetching facebook pictures don't fetch the thumbnail but the big one + if (strpos($matches[2], ".fbcdn.net/") and (substr($matches[2], -6) == "_s.jpg")) + $matches[2] = substr($matches[2], 0, -6)."_n.jpg"; + // following line changed per bug #431 if (privacy_image_cache_is_local_image($matches[2])) return $matches[1] . $matches[2] . $matches[3]; - return $matches[1] . get_app()->get_baseurl() . "/privacy_image_cache/?url=" . addslashes(rawurlencode(htmlspecialchars_decode($matches[2]))) . $matches[3]; + //return $matches[1] . get_app()->get_baseurl() . "/privacy_image_cache/?url=" . addslashes(rawurlencode(htmlspecialchars_decode($matches[2]))) . $matches[3]; + + return $matches[1].get_app()->get_baseurl()."/privacy_image_cache/". privacy_image_cache_cachename(htmlspecialchars_decode($matches[2])).$matches[3]; } /** @@ -207,9 +305,14 @@ function privacy_image_cache_bbcode_hook(&$a, &$o) { function privacy_image_cache_display_item_hook(&$a, &$o) { if (isset($o["output"])) { if (isset($o["output"]["thumb"]) && !privacy_image_cache_is_local_image($o["output"]["thumb"])) - $o["output"]["thumb"] = $a->get_baseurl() . "/privacy_image_cache/?url=" . escape_tags(addslashes(rawurlencode($o["output"]["thumb"]))); + $o["output"]["thumb"] = $a->get_baseurl() . "/privacy_image_cache/".privacy_image_cache_cachename($o["output"]["thumb"]); + //$o["output"]["thumb"] = $a->get_baseurl() . "/privacy_image_cache/?url=" . escape_tags(addslashes(rawurlencode($o["output"]["thumb"]))); if (isset($o["output"]["author-avatar"]) && !privacy_image_cache_is_local_image($o["output"]["author-avatar"])) - $o["output"]["author-avatar"] = $a->get_baseurl() . "/privacy_image_cache/?url=" . escape_tags(addslashes(rawurlencode($o["output"]["author-avatar"]))); + $o["output"]["author-avatar"] = $a->get_baseurl() . "/privacy_image_cache/".privacy_image_cache_cachename($o["output"]["author-avatar"]); + //$o["output"]["author-avatar"] = $a->get_baseurl() . "/privacy_image_cache/?url=" . escape_tags(addslashes(rawurlencode($o["output"]["author-avatar"]))); + if (isset($o["output"]["owner-avatar"]) && !privacy_image_cache_is_local_image($o["output"]["owner-avatar"])) + $o["output"]["owner-avatar"] = $a->get_baseurl() . "/privacy_image_cache/".privacy_image_cache_cachename($o["output"]["owner-avatar"]); + //$o["output"]["owner-avatar"] = $a->get_baseurl() . "/privacy_image_cache/?url=" . escape_tags(addslashes(rawurlencode($o["output"]["owner-avatar"]))); } } @@ -220,7 +323,8 @@ function privacy_image_cache_display_item_hook(&$a, &$o) { */ function privacy_image_cache_ping_xmlize_hook(&$a, &$o) { if ($o["photo"] != "" && !privacy_image_cache_is_local_image($o["photo"])) - $o["photo"] = $a->get_baseurl() . "/privacy_image_cache/?url=" . escape_tags(addslashes(rawurlencode($o["photo"]))); + $o["photo"] = $a->get_baseurl() . "/privacy_image_cache/".privacy_image_cache_cachename($o["photo"]); + //$o["photo"] = $a->get_baseurl() . "/privacy_image_cache/?url=" . escape_tags(addslashes(rawurlencode($o["photo"]))); } @@ -238,12 +342,12 @@ function privacy_image_cache_cron(&$a = null, &$b = null) { logger("Purging old Cache of the Privacy Image Cache", LOGGER_DEBUG); q('DELETE FROM `photo` WHERE `uid` = 0 AND `resource-id` LIKE "pic:%%" AND `created` < NOW() - INTERVAL %d SECOND', $cachetime); + + clear_cache($a->get_basepath(), $a->get_basepath()."/privacy_image_cache"); + set_config('pi_cache', 'last_delete', $time); } - - - /** * @param App $a * @param null|object $o @@ -289,3 +393,22 @@ function privacy_image_cache_plugin_admin_post(&$a = null, &$o = null){ q('DELETE FROM `photo` WHERE `uid` = 0 AND `resource-id` LIKE "pic:%%"'); } } + +function privacy_image_cache_parse_query($var) { + /** + * Use this function to parse out the query array element from + * the output of parse_url(). + */ + $var = parse_url($var, PHP_URL_QUERY); + $var = html_entity_decode($var); + $var = explode('&', $var); + $arr = array(); + + foreach($var as $val) { + $x = explode('=', $val); + $arr[$x[0]] = $x[1]; + } + + unset($val, $x, $var); + return $arr; +} diff --git a/procrunner.tgz b/procrunner.tgz new file mode 100644 index 00000000..fec9dfa7 Binary files /dev/null and b/procrunner.tgz differ diff --git a/procrunner/procrunner.php b/procrunner/procrunner.php new file mode 100755 index 00000000..4c6f64b6 --- /dev/null +++ b/procrunner/procrunner.php @@ -0,0 +1,53 @@ + + * Author: Mike Macgirvin + */ + +function procrunner_install() { + + $addons = get_config('system','addon'); + if(strstr('poormancron',$addons)) { + logger('procrunner incompatible with poormancron. Not installing procrunner.'); + return; + } + + // check for command line php + $a = get_app(); + $ex = Array(); + $ex[0] = ((x($a->config,'php_path')) && (strlen($a->config['php_path'])) ? $a->config['php_path'] : 'php'); + $ex[1] = dirname(dirname(dirname(__file__)))."/testargs.php"; + $ex[2] = "test"; + $out = exec(implode(" ", $ex)); + if ($out==="test") { + logger('procrunner not required on this system. Not installing.'); + return; + } else { + register_hook('proc_run', 'addon/procrunner/procrunner.php','procrunner_procrun'); + logger("installed procrunner"); + } + +} + +function procrunner_uninstall() { + unregister_hook('proc_run', 'addon/procrunner/procrunner.php','procrunner_procrun'); + logger("removed procrunner"); +} + + + +function procrunner_procrun(&$a, &$arr) { + + $argv = $arr['args']; + $arr['run_cmd'] = false; + logger("procrunner procrun ".implode(", ",$argv)); + array_shift($argv); + $argc = count($argv); + logger("procrunner procrun require_once ".basename($argv[0])); + require_once(basename($argv[0])); + $funcname=str_replace(".php", "", basename($argv[0]))."_run"; + $funcname($argv, $argc); +} diff --git a/randplace.tgz b/randplace.tgz index 7ab5f0db..bdad8baf 100755 Binary files a/randplace.tgz and b/randplace.tgz differ diff --git a/remote_permissions.tgz b/remote_permissions.tgz new file mode 100644 index 00000000..fef4f39b Binary files /dev/null and b/remote_permissions.tgz differ diff --git a/remote_permissions/README.md b/remote_permissions/README.md new file mode 100644 index 00000000..b9e38582 --- /dev/null +++ b/remote_permissions/README.md @@ -0,0 +1,8 @@ +The Remote Permissions plugin enables recipients of private posts to see who else has received the post. This can be beneficial on community servers where people may want to modify the way they speak depending on who can see their comments to the post. + +Note that since Friendica is federated, the local hub may have posts that originated elsewhere. In that case, the plugin has no way of knowing all the recipients of the post, and it must settle for finding out who else can see it on the local hub. + +The hub admin can specify one of two behaviors for this plugin: + +* **Global:** every private post on the local hub will show all recipients (or at least the ones it can discover) of the post to any other users on the local hub +* **Individual:** only private posts from those users on the local hub who "opt-in" will show the post recipients. None of the private posts that originated elsewhere will show even partial lists of post recipients diff --git a/remote_permissions/remote_permissions.php b/remote_permissions/remote_permissions.php new file mode 100644 index 00000000..8955130f --- /dev/null +++ b/remote_permissions/remote_permissions.php @@ -0,0 +1,207 @@ + + * + */ + + +function remote_permissions_install() { + register_hook('lockview_content', 'addon/remote_permissions/remote_permissions.php', 'remote_permissions_content'); + register_hook('plugin_settings', 'addon/remote_permissions/remote_permissions.php', 'remote_permissions_settings'); + register_hook('plugin_settings_post', 'addon/remote_permissions/remote_permissions.php', 'remote_permissions_settings_post'); +} + +function remote_permissions_uninstall() { + unregister_hook('lockview_content', 'addon/remote_permissions/remote_permissions.php', 'remote_permissions_content'); + unregister_hook('plugin_settings', 'addon/remote_permissions/remote_permissions.php', 'remote_permissions_settings'); + unregister_hook('plugin_settings_post', 'addon/remote_permissions/remote_permissions.php', 'remote_permissions_settings_post'); +} + +function remote_permissions_settings(&$a,&$o) { + + if(! local_user()) + return; + + $global = get_config("remote_perms", "global"); + if($global == 1) + return; + + /* Add our stylesheet to the page so we can make our settings look nice */ + + $a->page['htmlhead'] .= '' . "\r\n"; + + /* Get the current state of our config variable */ + + $remote_perms = get_pconfig(local_user(),'remote_perms','show'); + + /* Add some HTML to the existing form */ + +// $t = file_get_contents("addon/remote_permissions/settings.tpl" ); + $t = get_markup_template("settings.tpl", "addon/remote_permissions/" ); + $o .= replace_macros($t, array( + '$remote_perms_title' => t('Remote Permissions Settings'), + '$remote_perms_label' => t('Allow recipients of your private posts to see the other recipients of the posts'), + '$checked' => (($remote_perms == 1) ? 'checked="checked"' : ''), + '$submit' => t('Submit') + )); + +} + +function remote_permissions_settings_post($a,$post) { + if(! local_user() || (! x($_POST,'remote-perms-submit'))) + return; + + set_pconfig(local_user(),'remote_perms','show',intval($_POST['remote-perms'])); + info( t('Remote Permissions settings updated.') . EOL); +} + +function remote_permissions_content($a, $item_copy) { + + if($item_copy['uid'] != local_user()) + return; + + if(get_config('remote_perms','global') == 0) { + // Admin has set Individual choice. We need to find + // the original poster. First, get the contact's info + $r = q("SELECT nick, url FROM contact WHERE id = %d LIMIT 1", + intval($item_copy['contact-id']) + ); + if(! $r) + return; + + // Find out if the contact lives here + $baseurl = $a->get_baseurl(); + $baseurl = substr($baseurl, strpos($baseurl, '://') + 3); + if(strpos($r[0]['url'], $baseurl) === false) + return; + + // The contact lives here. Get his/her user info + $nick = $r[0]['nick']; + $r = q("SELECT uid FROM user WHERE nickname = '%s' LIMIT 1", + dbesc($nick) + ); + if(! $r) + return; + + if(get_pconfig($r[0]['uid'],'remote_perms','show') == 0) + return; + } + + if(($item_copy['private'] == 1) && (! strlen($item_copy['allow_cid'])) && (! strlen($item_copy['allow_gid'])) + && (! strlen($item_copy['deny_cid'])) && (! strlen($item_copy['deny_gid']))) { + + $allow_names = array(); + + // Check for the original post here -- that's the only way + // to definitely get all of the recipients + + if($item_copy['uri'] === $item_copy['parent-uri']) { + // Lockview for a top-level post + $r = q("SELECT allow_cid, allow_gid, deny_cid, deny_gid FROM item WHERE uri = '%s' AND type = 'wall' LIMIT 1", + dbesc($item_copy['uri']) + ); + } + else { + // Lockview for a comment + $r = q("SELECT allow_cid, allow_gid, deny_cid, deny_gid FROM item WHERE uri = '%s' + AND parent = ( SELECT id FROM item WHERE uri = '%s' AND type = 'wall' ) LIMIT 1", + dbesc($item_copy['uri']), + dbesc($item_copy['parent-uri']) + ); + } + if($r) { + + $item = $r[0]; + + $allowed_users = expand_acl($item['allow_cid']); + $allowed_groups = expand_acl($item['allow_gid']); + $deny_users = expand_acl($item['deny_cid']); + $deny_groups = expand_acl($item['deny_gid']); + + $o = t('Visible to:') . '
'; + $allow = array(); + $deny = array(); + + if(count($allowed_groups)) { + $r = q("SELECT DISTINCT `contact-id` FROM group_member WHERE gid IN ( %s )", + dbesc(implode(', ', $allowed_groups)) + ); + foreach($r as $rr) + $allow[] = $rr['contact-id']; + } + $allow = array_unique($allow + $allowed_users); + + if(count($deny_groups)) { + $r = q("SELECT DISTINCT `contact-id` FROM group_member WHERE gid IN ( %s )", + dbesc(implode(', ', $deny_groups)) + ); + foreach($r as $rr) + $deny[] = $rr['contact-id']; + } + $deny = $deny + $deny_users; + + if($allow) + { + $r = q("SELECT name FROM contact WHERE id IN ( %s )", + dbesc(implode(', ', array_diff($allow, $deny))) + ); + foreach($r as $rr) + $allow_names[] = $rr['name']; + } + } + else { + // We don't have the original post. Let's try for the next best thing: + // checking who else has the post on our own server. Note that comments + // that were sent to Diaspora and were relayed to others on our server + // will have different URIs than the original. We can match the GUID for + // those + $r = q("SELECT `uid` FROM item WHERE uri = '%s' OR guid = '%s'", + dbesc($item_copy['uri']), + dbesc($item_copy['guid']) + ); + if(! $r) + return; + + $allow = array(); + foreach($r as $rr) + $allow[] = $rr['uid']; + + $r = q("SELECT username FROM user WHERE uid IN ( %s )", + dbesc(implode(', ', $allow)) + ); + if(! $r) + return; + + $o = t('Visible to') . ' (' . t('may only be a partial list') . '):
'; + + foreach($r as $rr) + $allow_names[] = $rr['username']; + } + + // Sort the names alphabetically, case-insensitive + natcasesort($allow_names); + echo $o . implode(', ', $allow_names); + killme(); + } + + return; +} + +function remote_permissions_plugin_admin(&$a, &$o){ + $t = get_markup_template( "admin.tpl", "addon/remote_permissions/" ); + $o = replace_macros($t, array( + '$submit' => t('Submit'), + '$global' => array('remotepermschoice', t('Global'), 1, t('The posts of every user on this server show the post recipients'), get_config('remote_perms', 'global') == 1), + '$individual' => array('remotepermschoice', t('Individual'), 2, t('Each user chooses whether his/her posts show the post recipients'), get_config('remote_perms', 'global') == 0) + )); +} + +function remote_permissions_plugin_admin_post(&$a){ + $choice = ((x($_POST,'remotepermschoice')) ? notags(trim($_POST['remotepermschoice'])) : ''); + set_config('remote_perms','global',($choice == 1 ? 1 : 0)); + info( t('Settings updated.'). EOL ); +} + diff --git a/remote_permissions/settings.css b/remote_permissions/settings.css new file mode 100644 index 00000000..ef6051d2 --- /dev/null +++ b/remote_permissions/settings.css @@ -0,0 +1,16 @@ + + + +#remote-perms-label { + float: left; + width: 200px; + margin-bottom: 25px; + margin-right: 20px; + text-align: justify; +} + +#remote-perms { + float: left; +} + + diff --git a/remote_permissions/view/admin.tpl b/remote_permissions/view/admin.tpl new file mode 100644 index 00000000..60558808 --- /dev/null +++ b/remote_permissions/view/admin.tpl @@ -0,0 +1,3 @@ +{{ inc field_radio.tpl with $field=$global }}{{ endinc }} +{{ inc field_radio.tpl with $field=$individual }}{{ endinc }} +
diff --git a/remote_permissions/view/settings.tpl b/remote_permissions/view/settings.tpl new file mode 100644 index 00000000..9fd98957 --- /dev/null +++ b/remote_permissions/view/settings.tpl @@ -0,0 +1,8 @@ +
+

$remote_perms_title

+
+ + +
+
+ diff --git a/remote_permissions/view/smarty3/admin.tpl b/remote_permissions/view/smarty3/admin.tpl new file mode 100644 index 00000000..e67afd73 --- /dev/null +++ b/remote_permissions/view/smarty3/admin.tpl @@ -0,0 +1,3 @@ +{{include file="field_radio.tpl" field=$global}} +{{include file="field_radio.tpl" field=$individual}} +
diff --git a/remote_permissions/view/smarty3/settings.tpl b/remote_permissions/view/smarty3/settings.tpl new file mode 100644 index 00000000..df89a32e --- /dev/null +++ b/remote_permissions/view/smarty3/settings.tpl @@ -0,0 +1,8 @@ +
+

{{$remote_perms_title}}

+
+ + +
+
+ diff --git a/rendertime/rendertime.php b/rendertime/rendertime.php new file mode 100755 index 00000000..dcbfb411 --- /dev/null +++ b/rendertime/rendertime.php @@ -0,0 +1,43 @@ + + * + */ + +function rendertime_install() { + register_hook('page_end', 'addon/rendertime/rendertime.php', 'rendertime_page_end'); +} + + +function rendertime_uninstall() { + unregister_hook('init_1', 'addon/rendertime/rendertime.php', 'rendertime_init_1'); + unregister_hook('page_end', 'addon/rendertime/rendertime.php', 'rendertime_page_end'); +} + +function rendertime_init_1(&$a) { +} + +function rendertime_page_end(&$a, &$o) { + + $duration = microtime(true)-$a->performance["start"]; + + $o = $o.'
'.sprintf(t("Performance: Database: %s, Network: %s, Rendering: %s, Parser: %s, I/O: %s, Other: %s, Total: %s"), + round($a->performance["database"], 3), + round($a->performance["network"], 3), + round($a->performance["rendering"], 3), + round($a->performance["parser"], 3), + round($a->performance["file"], 3), + round($duration - $a->performance["database"] - $a->performance["network"] + - $a->performance["rendering"] - $a->performance["parser"] + - $a->performance["file"], 3), + round($duration, 3) + //round($a->performance["markstart"], 3) + //round($a->performance["plugin"], 3) + )."
"; + +} diff --git a/smiley_pack.tgz b/smiley_pack.tgz index 8c184414..2efe6332 100644 Binary files a/smiley_pack.tgz and b/smiley_pack.tgz differ diff --git a/smiley_pack/icons/food/birthdaycake.gif b/smiley_pack/icons/food/birthdaycake.gif new file mode 100644 index 00000000..30ebceb3 Binary files /dev/null and b/smiley_pack/icons/food/birthdaycake.gif differ diff --git a/smiley_pack/smiley_pack.php b/smiley_pack/smiley_pack.php index 4623ded5..4d49b7c0 100644 --- a/smiley_pack/smiley_pack.php +++ b/smiley_pack/smiley_pack.php @@ -370,6 +370,9 @@ function smiley_pack_smilies(&$a,&$b) { $b['texts'][] = ':fryegg'; $b['icons'][] = '' . ':fryegg' . ''; + $b['texts'][] = ':birthdaycake'; + $b['icons'][] = '' . ':birthdaycake' . ''; + #Happy smileys $b['texts'][] = ':cloud9'; diff --git a/sniper.tgz b/sniper.tgz index fa75b3e4..fba75c15 100755 Binary files a/sniper.tgz and b/sniper.tgz differ diff --git a/statusnet.tgz b/statusnet.tgz index c171f2c3..da873b17 100755 Binary files a/statusnet.tgz and b/statusnet.tgz differ diff --git a/statusnet/statusnet.css b/statusnet/statusnet.css index a5594cb7..81c38d98 100755 --- a/statusnet/statusnet.css +++ b/statusnet/statusnet.css @@ -15,19 +15,16 @@ width: 250px; margin-bottom: 25px; } -#statusnet-default-label { - float: left; - width: 250px; -} -#statusnet-sendtaglinks-label { - float: left; - width: 250px; - margin-bottom: 25px; -} #statusnet-disconnect { float: left; } +#statusnet-default-label, +#statusnet-applicationname-label, +#statusnet-sendtaglinks-label, +#statusnet-shortening-label, +#statusnet-mirror-label, +#statusnet-pin-label, #statusnet-enable-label { float: left; width: 250px; @@ -38,11 +35,6 @@ float: left; } -#statusnet-pin-label { - float: left; - width: 250px; - margin-bottom: 25px; -} #statusnet-pin { float: left; } diff --git a/statusnet/statusnet.php b/statusnet/statusnet.php index 46b3f03f..77bff7a4 100755 --- a/statusnet/statusnet.php +++ b/statusnet/statusnet.php @@ -30,6 +30,8 @@ * Thank you guys for the Twitter compatible API! */ +define('STATUSNET_DEFAULT_POLL_INTERVAL', 5); // given in minutes + require_once('library/twitteroauth.php'); class StatusNetOAuth extends TwitterOAuth { @@ -104,6 +106,7 @@ function statusnet_install() { register_hook('notifier_normal', 'addon/statusnet/statusnet.php', 'statusnet_post_hook'); register_hook('post_local', 'addon/statusnet/statusnet.php', 'statusnet_post_local'); register_hook('jot_networks', 'addon/statusnet/statusnet.php', 'statusnet_jot_nets'); + register_hook('cron', 'addon/statusnet/statusnet.php', 'statusnet_cron'); logger("installed statusnet"); } @@ -114,6 +117,7 @@ function statusnet_uninstall() { unregister_hook('notifier_normal', 'addon/statusnet/statusnet.php', 'statusnet_post_hook'); unregister_hook('post_local', 'addon/statusnet/statusnet.php', 'statusnet_post_local'); unregister_hook('jot_networks', 'addon/statusnet/statusnet.php', 'statusnet_jot_nets'); + unregister_hook('cron', 'addon/statusnet/statusnet.php', 'statusnet_cron'); // old setting - remove only unregister_hook('post_local_end', 'addon/statusnet/statusnet.php', 'statusnet_post_hook'); @@ -131,13 +135,10 @@ function statusnet_jot_nets(&$a,&$b) { $statusnet_defpost = get_pconfig(local_user(),'statusnet','post_by_default'); $selected = ((intval($statusnet_defpost) == 1) ? ' checked="checked" ' : ''); $b .= '
' - . t('Post to StatusNet') . '
'; + . t('Post to StatusNet') . ''; } } - - - function statusnet_settings_post ($a,$post) { if(! local_user()) return; @@ -148,14 +149,17 @@ function statusnet_settings_post ($a,$post) { /*** * if the statusnet-disconnect checkbox is set, clear the statusnet configuration */ - del_pconfig( local_user(), 'statusnet', 'consumerkey' ); - del_pconfig( local_user(), 'statusnet', 'consumersecret' ); - del_pconfig( local_user(), 'statusnet', 'post' ); - del_pconfig( local_user(), 'statusnet', 'post_by_default' ); - del_pconfig( local_user(), 'statusnet', 'oauthtoken' ); - del_pconfig( local_user(), 'statusnet', 'oauthsecret' ); - del_pconfig( local_user(), 'statusnet', 'baseapi' ); - del_pconfig( local_user(), 'statusnet', 'post_taglinks'); + del_pconfig(local_user(), 'statusnet', 'consumerkey'); + del_pconfig(local_user(), 'statusnet', 'consumersecret'); + del_pconfig(local_user(), 'statusnet', 'post'); + del_pconfig(local_user(), 'statusnet', 'post_by_default'); + del_pconfig(local_user(), 'statusnet', 'oauthtoken'); + del_pconfig(local_user(), 'statusnet', 'oauthsecret'); + del_pconfig(local_user(), 'statusnet', 'baseapi'); + del_pconfig(local_user(), 'statusnet', 'post_taglinks'); + del_pconfig(local_user(), 'statusnet', 'lastid'); + del_pconfig(local_user(), 'statusnet', 'mirror_posts'); + del_pconfig(local_user(), 'statusnet', 'intelligent_shortening'); } else { if (isset($_POST['statusnet-preconf-apiurl'])) { /*** @@ -172,6 +176,7 @@ function statusnet_settings_post ($a,$post) { set_pconfig(local_user(), 'statusnet', 'consumerkey', $asn['consumerkey'] ); set_pconfig(local_user(), 'statusnet', 'consumersecret', $asn['consumersecret'] ); set_pconfig(local_user(), 'statusnet', 'baseapi', $asn['apiurl'] ); + set_pconfig(local_user(), 'statusnet', 'application_name', $asn['applicationname'] ); } else { notice( t('Please contact your site administrator.
The provided API URL is not valid.').EOL.$asn['apiurl'].EOL ); } @@ -190,6 +195,7 @@ function statusnet_settings_post ($a,$post) { set_pconfig(local_user(), 'statusnet', 'consumerkey', $_POST['statusnet-consumerkey']); set_pconfig(local_user(), 'statusnet', 'consumersecret', $_POST['statusnet-consumersecret']); set_pconfig(local_user(), 'statusnet', 'baseapi', $apibase ); + set_pconfig(local_user(), 'statusnet', 'application_name', $_POST['statusnet-applicationname'] ); } else { // the API path is not correct, maybe missing trailing / ? $apibase = $apibase . '/'; @@ -207,7 +213,7 @@ function statusnet_settings_post ($a,$post) { goaway($a->get_baseurl().'/settings/connectors'); } else { if (isset($_POST['statusnet-pin'])) { - // if the user supplied us with a PIN from Twitter, let the magic of OAuth happen + // if the user supplied us with a PIN from StatusNet, let the magic of OAuth happen $api = get_pconfig(local_user(), 'statusnet', 'baseapi'); $ckey = get_pconfig(local_user(), 'statusnet', 'consumerkey' ); $csecret = get_pconfig(local_user(), 'statusnet', 'consumersecret' ); @@ -229,6 +235,8 @@ function statusnet_settings_post ($a,$post) { set_pconfig(local_user(),'statusnet','post',intval($_POST['statusnet-enable'])); set_pconfig(local_user(),'statusnet','post_by_default',intval($_POST['statusnet-default'])); set_pconfig(local_user(),'statusnet','post_taglinks',intval($_POST['statusnet-sendtaglinks'])); + set_pconfig(local_user(), 'statusnet', 'mirror_posts', intval($_POST['statusnet-mirror'])); + set_pconfig(local_user(), 'statusnet', 'intelligent_shortening', intval($_POST['statusnet-shortening'])); info( t('StatusNet settings updated.') . EOL); }}}} } @@ -253,6 +261,12 @@ function statusnet_settings(&$a,&$s) { $defchecked = (($defenabled) ? ' checked="checked" ' : ''); $linksenabled = get_pconfig(local_user(),'statusnet','post_taglinks'); $linkschecked = (($linksenabled) ? ' checked="checked" ' : ''); + + $mirrorenabled = get_pconfig(local_user(),'statusnet','mirror_posts'); + $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : ''); + $shorteningenabled = get_pconfig(local_user(),'statusnet','intelligent_shortening'); + $shorteningchecked = (($shorteningenabled) ? ' checked="checked" ' : ''); + $s .= '
'; $s .= '

'. t('StatusNet Posting Settings').'

'; @@ -289,6 +303,9 @@ function statusnet_settings(&$a,&$s) { $s .= ''; $s .= '
'; $s .= '

'; + $s .= ''; + $s .= '
'; + $s .= '

'; $s .= '
'; } else { /*** @@ -342,6 +359,15 @@ function statusnet_settings(&$a,&$s) { $s .= ''; $s .= ''; $s .= '
'; + + $s .= ''; + $s .= ''; + $s .= '
'; + + $s .= ''; + $s .= ''; + $s .= '
'; + $s .= ''; $s .= ''; $s .= '
'; @@ -404,6 +430,131 @@ function short_link($url) { return $slinky->short(); } }; +function statusnet_shortenmsg($b, $max_char) { + require_once("include/bbcode.php"); + require_once("include/html2plain.php"); + + // Looking for the first image + $image = ''; + if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$b['body'],$matches)) + $image = $matches[3]; + + if ($image == '') + if(preg_match("/\[img\](.*?)\[\/img\]/is",$b['body'],$matches)) + $image = $matches[1]; + + $multipleimages = (strpos($b['body'], "[img") != strrpos($b['body'], "[img")); + + // When saved into the database the content is sent through htmlspecialchars + // That means that we have to decode all image-urls + $image = htmlspecialchars_decode($image); + + $body = $b["body"]; + if ($b["title"] != "") + $body = $b["title"]."\n\n".$body; + + if (strpos($body, "[bookmark") !== false) { + // splitting the text in two parts: + // before and after the bookmark + $pos = strpos($body, "[bookmark"); + $body1 = substr($body, 0, $pos); + $body2 = substr($body, $pos); + + // Removing all quotes after the bookmark + // they are mostly only the content after the bookmark. + $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2); + $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2); + $body = $body1.$body2; + } + + // Add some newlines so that the message could be cut better + $body = str_replace(array("[quote", "[bookmark", "[/bookmark]", "[/quote]"), + array("\n[quote", "\n[bookmark", "[/bookmark]\n", "[/quote]\n"), $body); + + // remove the recycle signs and the names since they aren't helpful on twitter + // recycle 1 + $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8'); + $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body); + // recycle 2 (Test) + $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8'); + $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body); + + // remove the share element + $body = preg_replace("/\[share(.*?)\](.*?)\[\/share\]/ism","\n\n$2\n\n",$body); + + // At first convert the text to html + $html = bbcode($body, false, false); + + // Then convert it to plain text + //$msg = trim($b['title']." \n\n".html2plain($html, 0, true)); + $msg = trim(html2plain($html, 0, true)); + $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8'); + + // Removing multiple newlines + while (strpos($msg, "\n\n\n") !== false) + $msg = str_replace("\n\n\n", "\n\n", $msg); + + // Removing multiple spaces + while (strpos($msg, " ") !== false) + $msg = str_replace(" ", " ", $msg); + + // Removing URLs + $msg = preg_replace('/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', "", $msg); + + $msg = trim($msg); + + $link = ''; + // look for bookmark-bbcode and handle it with priority + if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches)) + $link = $matches[1]; + + $multiplelinks = (strpos($b['body'], "[bookmark") != strrpos($b['body'], "[bookmark")); + + // If there is no bookmark element then take the first link + if ($link == '') { + $links = collecturls($html); + if (sizeof($links) > 0) { + reset($links); + $link = current($links); + } + $multiplelinks = (sizeof($links) > 1); + } + + $msglink = ""; + if ($multiplelinks) + $msglink = $b["plink"]; + else if ($link != "") + $msglink = $link; + else if ($multipleimages) + $msglink = $b["plink"]; + else if ($image != "") + $msglink = $image; + + if (($msglink == "") and strlen($msg) > $max_char) + $msglink = $b["plink"]; + + if (strlen($msglink) > 20) + $msglink = short_link($msglink); + + if (strlen(trim($msg." ".$msglink)) > $max_char) { + $msg = substr($msg, 0, $max_char - (strlen($msglink))); + $lastchar = substr($msg, -1); + $msg = substr($msg, 0, -1); + $pos = strrpos($msg, "\n"); + if ($pos > 0) + $msg = substr($msg, 0, $pos); + else if ($lastchar != "\n") + $msg = substr($msg, 0, -3)."..."; + } + $msg = str_replace("\n", " ", $msg); + + // Removing multiple spaces - again + while (strpos($msg, " ") !== false) + $msg = str_replace(" ", " ", $msg); + + return(array("msg"=>trim($msg." ".$msglink), "image"=>$image)); +} + function statusnet_post_hook(&$a,&$b) { /** @@ -416,105 +567,136 @@ function statusnet_post_hook(&$a,&$b) { if(! strstr($b['postopts'],'statusnet')) return; + // if posts comes from statusnet don't send it back + if($b['app'] == "StatusNet") + return; + + logger('statusnet post invoked'); + load_pconfig($b['uid'], 'statusnet'); - + $api = get_pconfig($b['uid'], 'statusnet', 'baseapi'); - $ckey = get_pconfig($b['uid'], 'statusnet', 'consumerkey' ); - $csecret = get_pconfig($b['uid'], 'statusnet', 'consumersecret' ); - $otoken = get_pconfig($b['uid'], 'statusnet', 'oauthtoken' ); - $osecret = get_pconfig($b['uid'], 'statusnet', 'oauthsecret' ); + $ckey = get_pconfig($b['uid'], 'statusnet', 'consumerkey'); + $csecret = get_pconfig($b['uid'], 'statusnet', 'consumersecret'); + $otoken = get_pconfig($b['uid'], 'statusnet', 'oauthtoken'); + $osecret = get_pconfig($b['uid'], 'statusnet', 'oauthsecret'); + $intelligent_shortening = get_pconfig($b['uid'], 'statusnet', 'intelligent_shortening'); + + // Global setting overrides this + if (get_config('statusnet','intelligent_shortening')) + $intelligent_shortening = get_config('statusnet','intelligent_shortening'); if($ckey && $csecret && $otoken && $osecret) { require_once('include/bbcode.php'); $dent = new StatusNetOAuth($api,$ckey,$csecret,$otoken,$osecret); $max_char = $dent->get_maxlength(); // max. length for a dent - // we will only work with up to two times the length of the dent + // we will only work with up to two times the length of the dent // we can later send to StatusNet. This way we can "gain" some // information during shortening of potential links but do not // shorten all the links in a 200000 character long essay. - if (! $b['title']=='') { - $tmp = $b['title'].": \n".$b['body']; -// $tmp = substr($tmp, 0, 4*$max_char); - } else { - $tmp = $b['body']; // substr($b['body'], 0, 3*$max_char); - } - // if [url=bla][img]blub.png[/img][/url] get blub.png - $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\]\[img\](\\w+.*?)\\[\\/img\]\\[\\/url\]/i', '$2', $tmp); - // preserve links to images, videos and audios - $tmp = preg_replace( '/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism', '$3', $tmp); - $tmp = preg_replace( '/\[\\/?img(\\s+.*?\]|\])/i', '', $tmp); - $tmp = preg_replace( '/\[\\/?video(\\s+.*?\]|\])/i', '', $tmp); - $tmp = preg_replace( '/\[\\/?youtube(\\s+.*?\]|\])/i', '', $tmp); - $tmp = preg_replace( '/\[\\/?vimeo(\\s+.*?\]|\])/i', '', $tmp); - $tmp = preg_replace( '/\[\\/?audio(\\s+.*?\]|\])/i', '', $tmp); - $linksenabled = get_pconfig($b['uid'],'statusnet','post_taglinks'); - // if a #tag is linked, don't send the [url] over to SN - // that is, don't send if the option is not set in the - // connector settings - if ($linksenabled=='0') { - // #-tags - $tmp = preg_replace( '/#\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '#$2', $tmp); - // @-mentions - $tmp = preg_replace( '/@\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '@$2', $tmp); - // recycle 1 - $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8'); - $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp); - // recycle 2 (test) - $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8'); - $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp); - } - // preserve links to webpages - $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/url\]/i', '$2 $1', $tmp); - $tmp = preg_replace( '/\[bookmark\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/bookmark\]/i', '$2 $1', $tmp); - // find all http or https links in the body of the entry and - // apply the shortener if the link is longer then 20 characters - if (( strlen($tmp)>$max_char ) && ( $max_char > 0 )) { - preg_match_all ( '/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', $tmp, $allurls ); - foreach ($allurls as $url) { - foreach ($url as $u) { - if (strlen($u)>20) { - $sl = short_link($u); - $tmp = str_replace( $u, $sl, $tmp ); - } - } - } - } - // ok, all the links we want to send out are save, now strip - // away the remaining bbcode - //$msg = strip_tags(bbcode($tmp, false, false)); - $msg = bbcode($tmp, false, false); - $msg = str_replace(array('
','
'),"\n",$msg); - $msg = strip_tags($msg); - // quotes not working - let's try this - $msg = html_entity_decode($msg); + $tempfile = ""; + $intelligent_shortening = get_config('statusnet','intelligent_shortening'); + if (!$intelligent_shortening) { + if (! $b['title']=='') { + $tmp = $b['title'].": \n".$b['body']; + // $tmp = substr($tmp, 0, 4*$max_char); + } else { + $tmp = $b['body']; // substr($b['body'], 0, 3*$max_char); + } + // if [url=bla][img]blub.png[/img][/url] get blub.png + $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\]\[img\](\\w+.*?)\\[\\/img\]\\[\\/url\]/i', '$2', $tmp); + // preserve links to images, videos and audios + $tmp = preg_replace( '/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism', '$3', $tmp); + $tmp = preg_replace( '/\[\\/?img(\\s+.*?\]|\])/i', '', $tmp); + $tmp = preg_replace( '/\[\\/?video(\\s+.*?\]|\])/i', '', $tmp); + $tmp = preg_replace( '/\[\\/?youtube(\\s+.*?\]|\])/i', '', $tmp); + $tmp = preg_replace( '/\[\\/?vimeo(\\s+.*?\]|\])/i', '', $tmp); + $tmp = preg_replace( '/\[\\/?audio(\\s+.*?\]|\])/i', '', $tmp); + $linksenabled = get_pconfig($b['uid'],'statusnet','post_taglinks'); + // if a #tag is linked, don't send the [url] over to SN + // that is, don't send if the option is not set in the + // connector settings + if ($linksenabled=='0') { + // #-tags + $tmp = preg_replace( '/#\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '#$2', $tmp); + // @-mentions + $tmp = preg_replace( '/@\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '@$2', $tmp); + // recycle 1 + $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8'); + $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp); + // recycle 2 (test) + $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8'); + $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp); + } + // preserve links to webpages + $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/url\]/i', '$2 $1', $tmp); + $tmp = preg_replace( '/\[bookmark\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/bookmark\]/i', '$2 $1', $tmp); + // find all http or https links in the body of the entry and + // apply the shortener if the link is longer then 20 characters + if (( strlen($tmp)>$max_char ) && ( $max_char > 0 )) { + preg_match_all ( '/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', $tmp, $allurls ); + foreach ($allurls as $url) { + foreach ($url as $u) { + if (strlen($u)>20) { + $sl = short_link($u); + $tmp = str_replace( $u, $sl, $tmp ); + } + } + } + } + // ok, all the links we want to send out are save, now strip + // away the remaining bbcode + //$msg = strip_tags(bbcode($tmp, false, false)); + $msg = bbcode($tmp, false, false); + $msg = str_replace(array('
','
'),"\n",$msg); + $msg = strip_tags($msg); - if (( strlen($msg) > $max_char) && $max_char > 0) { - $shortlink = short_link( $b['plink'] ); - // the new message will be shortened such that "... $shortlink" - // will fit into the character limit - $msg = nl2br(substr($msg, 0, $max_char-strlen($shortlink)-4)); - $msg = str_replace(array('
','
'),' ',$msg); - $e = explode(' ', $msg); - // remove the last word from the cut down message to - // avoid sending cut words to the MicroBlog - array_pop($e); - $msg = implode(' ', $e); - $msg .= '... ' . $shortlink; + // quotes not working - let's try this + $msg = html_entity_decode($msg); + + if (( strlen($msg) > $max_char) && $max_char > 0) { + $shortlink = short_link( $b['plink'] ); + // the new message will be shortened such that "... $shortlink" + // will fit into the character limit + $msg = nl2br(substr($msg, 0, $max_char-strlen($shortlink)-4)); + $msg = str_replace(array('
','
'),' ',$msg); + $e = explode(' ', $msg); + // remove the last word from the cut down message to + // avoid sending cut words to the MicroBlog + array_pop($e); + $msg = implode(' ', $e); + $msg .= '... ' . $shortlink; + } + + $msg = trim($msg); + $postdata = array('status' => $msg); + } else { + $msgarr = statusnet_shortenmsg($b, $max_char); + $msg = $msgarr["msg"]; + $image = $msgarr["image"]; + if ($image != "") { + $imagedata = file_get_contents($image); + $tempfile = tempnam(get_config("system","temppath"), "upload"); + file_put_contents($tempfile, $imagedata); + $postdata = array("status"=>$msg, "media"=>"@".$tempfile); + } else + $postdata = array("status"=>$msg); } - $msg = trim($msg); - // and now dent it :-) if(strlen($msg)) { - $result = $dent->post('statuses/update', array('status' => $msg)); + //$result = $dent->post('statuses/update', array('status' => $msg)); + $result = $dent->post('statuses/update', $postdata); logger('statusnet_post send, result: ' . print_r($result, true). - "\nmessage: ".$msg, LOGGER_DEBUG."\nOriginal post: ".print_r($b)); + "\nmessage: ".$msg, LOGGER_DEBUG."\nOriginal post: ".print_r($b, true)."\nPost Data: ".print_r($postdata, true)); if ($result->error) { logger('Send to StatusNet failed: "' . $result->error . '"'); } } + if ($tempfile != "") + unlink($tempfile); } } @@ -527,6 +709,7 @@ function statusnet_plugin_admin_post(&$a){ $apiurl=trim($_POST['apiurl'][$id]); $secret=trim($_POST['secret'][$id]); $key=trim($_POST['key'][$id]); + $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'][$id])):''); if ($sitename!="" && $apiurl!="" && $secret!="" && @@ -537,7 +720,8 @@ function statusnet_plugin_admin_post(&$a){ 'sitename' => $sitename, 'apiurl' => $apiurl, 'consumersecret' => $secret, - 'consumerkey' => $key + 'consumerkey' => $key, + 'applicationname' => $applicationname ); } } @@ -557,6 +741,7 @@ function statusnet_plugin_admin(&$a, &$o){ 'apiurl' => Array("apiurl[$id]", "Api url", $s['apiurl'], ""), 'secret' => Array("secret[$id]", "Secret", $s['consumersecret'], ""), 'key' => Array("key[$id]", "Key", $s['consumerkey'], ""), + 'applicationname' => Array("applicationname[$id]", "Application name", $s['applicationname'], ""), 'delete' => Array("delete[$id]", "Delete", False , "Check to delete this preset"), ); } @@ -568,16 +753,126 @@ function statusnet_plugin_admin(&$a, &$o){ 'apiurl' => Array("apiurl[$id]", t("API URL"), "", ""), 'secret' => Array("secret[$id]", t("Consumer Secret"), "", ""), 'key' => Array("key[$id]", t("Consumer Key"), "", ""), + 'applicationname' => Array("applicationname[$id]", t("Application name"), "", ""), ); - - $t = file_get_contents( dirname(__file__). "/admin.tpl" ); + $t = get_markup_template( "admin.tpl", "addon/statusnet/" ); $o = replace_macros($t, array( '$submit' => t('Submit'), - '$sites' => $sitesform, - )); - - } + +function statusnet_cron($a,$b) { + $last = get_config('statusnet','last_poll'); + + $poll_interval = intval(get_config('statusnet','poll_interval')); + if(! $poll_interval) + $poll_interval = STATUSNET_DEFAULT_POLL_INTERVAL; + + if($last) { + $next = $last + ($poll_interval * 60); + if($next > time()) { + logger('statusnet: poll intervall not reached'); + return; + } + } + logger('statusnet: cron_start'); + + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'statusnet' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND() "); + if(count($r)) { + foreach($r as $rr) { + logger('statusnet: fetching for user '.$rr['uid']); + statusnet_fetchtimeline($a, $rr['uid']); + } + } + + logger('statusnet: cron_end'); + + set_config('statusnet','last_poll', time()); +} + +function statusnet_fetchtimeline($a, $uid) { + $ckey = get_pconfig($uid, 'statusnet', 'consumerkey'); + $csecret = get_pconfig($uid, 'statusnet', 'consumersecret'); + $api = get_pconfig($uid, 'statusnet', 'baseapi'); + $otoken = get_pconfig($uid, 'statusnet', 'oauthtoken'); + $osecret = get_pconfig($uid, 'statusnet', 'oauthsecret'); + $lastid = get_pconfig($uid, 'statusnet', 'lastid'); + + // get the application name for the SN app + // 1st try personal config, then system config and fallback to the + // hostname of the node if neither one is set. + $application_name = get_pconfig( $uid, 'statusnet', 'application_name'); + if ($application_name == "") + $application_name = get_config('statusnet', 'application_name'); + if ($application_name == "") + $application_name = $a->get_hostname(); + + $connection = new StatusNetOAuth($api, $ckey,$csecret,$otoken,$osecret); + + $parameters = array("exclude_replies" => true, "trim_user" => true, "contributor_details" => false, "include_rts" => false); + + $first_time = ($lastid == ""); + + if ($lastid <> "") + $parameters["since_id"] = $lastid; + + $items = $connection->get('statuses/user_timeline', $parameters); + + if (!is_array($items)) + return; + + $posts = array_reverse($items); + + if (count($posts)) { + foreach ($posts as $post) { + if ($post->id > $lastid) + $lastid = $post->id; + + if ($first_time) + continue; + + if (is_object($post->retweeted_status)) + continue; + + if ($post->in_reply_to_status_id != "") + continue; + + if (!strpos($post->source, $application_name)) { + $_SESSION["authenticated"] = true; + $_SESSION["uid"] = $uid; + + $_REQUEST["type"] = "wall"; + $_REQUEST["api_source"] = true; + $_REQUEST["profile_uid"] = $uid; + $_REQUEST["source"] = "StatusNet"; + + //$_REQUEST["date"] = $post->created_at; + + $_REQUEST["body"] = $post->text; + if (is_string($post->place->name)) + $_REQUEST["location"] = $post->place->name; + + if (is_string($post->place->full_name)) + $_REQUEST["location"] = $post->place->full_name; + + if (is_array($post->geo->coordinates)) + $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1]; + + if (is_array($post->coordinates->coordinates)) + $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0]; + + //print_r($_REQUEST); + if ($_REQUEST["body"] != "") { + logger('statusnet: posting for user '.$uid); + + require_once('mod/item.php'); + item_post($a); + } + } + } + } + set_pconfig($uid, 'statusnet', 'lastid', $lastid); +} + diff --git a/statusnet/admin.tpl b/statusnet/view/admin.tpl old mode 100755 new mode 100644 similarity index 88% rename from statusnet/admin.tpl rename to statusnet/view/admin.tpl index b40adf35..686a4c79 --- a/statusnet/admin.tpl +++ b/statusnet/view/admin.tpl @@ -3,6 +3,7 @@ {{ inc field_input.tpl with $field=$s.apiurl }}{{ endinc }} {{ inc field_input.tpl with $field=$s.secret }}{{ endinc }} {{ inc field_input.tpl with $field=$s.key }}{{ endinc }} + {{ inc field_input.tpl with $field=$s.applicationname }}{{ endinc }} {{ if $s.delete }} {{ inc field_checkbox.tpl with $field=$s.delete }}{{ endinc }}
diff --git a/statusnet/view/smarty3/admin.tpl b/statusnet/view/smarty3/admin.tpl new file mode 100644 index 00000000..f8d14c69 --- /dev/null +++ b/statusnet/view/smarty3/admin.tpl @@ -0,0 +1,17 @@ +{{foreach $sites as $s}} + {{include file="field_input.tpl" field=$s.sitename}} + {{include file="field_input.tpl" field=$s.apiurl}} + {{include file="field_input.tpl" field=$s.secret}} + {{include file="field_input.tpl" field=$s.key}} + {{include file="field_input.tpl" field=$s.applicationname}} + {{if $s.delete}} + {{include file="field_checkbox.tpl" field=$s.delete}} +
+ {{else}} +

Fill this form to add a new site

+ {{/if}} + +{{/foreach}} + + +
diff --git a/tumblr.tgz b/tumblr.tgz index fe038955..b4f6ff9b 100755 Binary files a/tumblr.tgz and b/tumblr.tgz differ diff --git a/tumblr/README b/tumblr/README new file mode 100644 index 00000000..62d7fd04 --- /dev/null +++ b/tumblr/README @@ -0,0 +1,9 @@ +Define in your .htconfig.php: +$a->config['tumblr']['consumer_key'] = "your-consumer-key"; +$a->config['tumblr']['consumer_secret'] = "your-consumer-secret"; + +You can get it here: +http://www.tumblr.com/oauth/apps + +Tumblr-OAuth-Library: +https://groups.google.com/d/msg/tumblr-api/g6SeIBWvsnE/gnWqT9jFSlEJ diff --git a/tumblr/tumblr.php b/tumblr/tumblr.php index eeb51348..4bbae8e6 100755 --- a/tumblr/tumblr.php +++ b/tumblr/tumblr.php @@ -7,6 +7,9 @@ * Author: Mike Macgirvin */ +require_once('library/OAuth1.php'); +require_once('addon/tumblr/tumblroauth/tumblroauth.php'); + function tumblr_install() { register_hook('post_local', 'addon/tumblr/tumblr.php', 'tumblr_post_local'); register_hook('notifier_normal', 'addon/tumblr/tumblr.php', 'tumblr_send'); @@ -23,6 +26,125 @@ function tumblr_uninstall() { unregister_hook('connector_settings_post', 'addon/tumblr/tumblr.php', 'tumblr_settings_post'); } +function tumblr_module() {} + +function tumblr_content(&$a) { + + if(! local_user()) { + notice( t('Permission denied.') . EOL); + return ''; + } + + if (isset($a->argv[1])) + switch ($a->argv[1]) { + case "connect": + $o = tumblr_connect($a); + break; + case "callback": + $o = tumblr_callback($a); + break; + default: + $o = print_r($a->argv, true); + break; + } + else + $o = tumblr_connect($a); + + return $o; +} + +function tumblr_connect($a) { + // Start a session. This is necessary to hold on to a few keys the callback script will also need + session_start(); + + // Include the TumblrOAuth library + //require_once('addon/tumblr/tumblroauth/tumblroauth.php'); + + // Define the needed keys + $consumer_key = get_config('tumblr','consumer_key'); + $consumer_secret = get_config('tumblr','consumer_secret'); + + // The callback URL is the script that gets called after the user authenticates with tumblr + // In this example, it would be the included callback.php + $callback_url = $a->get_baseurl()."/tumblr/callback"; + + // Let's begin. First we need a Request Token. The request token is required to send the user + // to Tumblr's login page. + + // Create a new instance of the TumblrOAuth library. For this step, all we need to give the library is our + // Consumer Key and Consumer Secret + $tum_oauth = new TumblrOAuth($consumer_key, $consumer_secret); + + // Ask Tumblr for a Request Token. Specify the Callback URL here too (although this should be optional) + $request_token = $tum_oauth->getRequestToken($callback_url); + + // Store the request token and Request Token Secret as out callback.php script will need this + $_SESSION['request_token'] = $token = $request_token['oauth_token']; + $_SESSION['request_token_secret'] = $request_token['oauth_token_secret']; + + // Check the HTTP Code. It should be a 200 (OK), if it's anything else then something didn't work. + switch ($tum_oauth->http_code) { + case 200: + // Ask Tumblr to give us a special address to their login page + $url = $tum_oauth->getAuthorizeURL($token); + + // Redirect the user to the login URL given to us by Tumblr + header('Location: ' . $url); + + // That's it for our side. The user is sent to a Tumblr Login page and + // asked to authroize our app. After that, Tumblr sends the user back to + // our Callback URL (callback.php) along with some information we need to get + // an access token. + + break; + default: + // Give an error message + $o = 'Could not connect to Tumblr. Refresh the page or try again later.'; + } + return($o); +} + +function tumblr_callback($a) { + + // Start a session, load the library + session_start(); + //require_once('addon/tumblr/tumblroauth/tumblroauth.php'); + + // Define the needed keys + $consumer_key = get_config('tumblr','consumer_key'); + $consumer_secret = get_config('tumblr','consumer_secret'); + + // Once the user approves your app at Tumblr, they are sent back to this script. + // This script is passed two parameters in the URL, oauth_token (our Request Token) + // and oauth_verifier (Key that we need to get Access Token). + // We'll also need out Request Token Secret, which we stored in a session. + + // Create instance of TumblrOAuth. + // It'll need our Consumer Key and Secret as well as our Request Token and Secret + $tum_oauth = new TumblrOAuth($consumer_key, $consumer_secret, $_SESSION['request_token'], $_SESSION['request_token_secret']); + + // Ok, let's get an Access Token. We'll need to pass along our oauth_verifier which was given to us in the URL. + $access_token = $tum_oauth->getAccessToken($_REQUEST['oauth_verifier']); + + // We're done with the Request Token and Secret so let's remove those. + unset($_SESSION['request_token']); + unset($_SESSION['request_token_secret']); + + // Make sure nothing went wrong. + if (200 == $tum_oauth->http_code) { + // good to go + } else { + return('Unable to authenticate'); + } + + // What's next? Now that we have an Access Token and Secret, we can make an API call. + set_pconfig(local_user(), "tumblr", "oauth_token", $access_token['oauth_token']); + set_pconfig(local_user(), "tumblr", "oauth_token_secret", $access_token['oauth_token_secret']); + + $o = t("You are now authenticated to tumblr."); + $o .= '
'.t("return to the connector page").''; + return($o); +} function tumblr_jot_nets(&$a,&$b) { if(! local_user()) @@ -57,34 +179,55 @@ function tumblr_settings(&$a,&$s) { $def_checked = (($def_enabled) ? ' checked="checked" ' : ''); - $tmbl_username = get_pconfig(local_user(), 'tumblr', 'tumblr_username'); - $tmbl_password = get_pconfig(local_user(), 'tumblr', 'tumblr_password'); - - /* Add some HTML to the existing form */ $s .= '
'; $s .= '

' . t('Tumblr Post Settings') . '

'; + + $s .= '
'; + $s .= '
'; $s .= ''; $s .= ''; $s .= '
'; - $s .= '
'; - $s .= ''; - $s .= ''; - $s .= '
'; - - $s .= '
'; - $s .= ''; - $s .= ''; - $s .= '
'; - $s .= '
'; $s .= ''; $s .= ''; $s .= '
'; + $oauth_token = get_pconfig(local_user(), "tumblr", "oauth_token"); + $oauth_token_secret = get_pconfig(local_user(), "tumblr", "oauth_token_secret"); + + $s .= '
'; + if (($oauth_token != "") and ($oauth_token_secret != "")) { + + $page = get_pconfig(local_user(),'tumblr','page'); + $consumer_key = get_config('tumblr','consumer_key'); + $consumer_secret = get_config('tumblr','consumer_secret'); + + $tum_oauth = new TumblrOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret); + + $userinfo = $tum_oauth->get('user/info'); + + $blogs = array(); + + $s .= t("Post to page:").""; + } else + $s .= t("You are not authenticated to tumblr"); + $s .= '
'; + /* provide a submit button */ $s .= '
'; @@ -97,9 +240,8 @@ function tumblr_settings_post(&$a,&$b) { if(x($_POST,'tumblr-submit')) { set_pconfig(local_user(),'tumblr','post',intval($_POST['tumblr'])); + set_pconfig(local_user(),'tumblr','page',$_POST['tumblr_page']); set_pconfig(local_user(),'tumblr','post_by_default',intval($_POST['tumblr_bydefault'])); - set_pconfig(local_user(),'tumblr','tumblr_username',trim($_POST['tumblr_username'])); - set_pconfig(local_user(),'tumblr','tumblr_password',trim($_POST['tumblr_password'])); } @@ -147,12 +289,12 @@ function tumblr_send(&$a,&$b) { if($b['parent'] != $b['id']) return; + $oauth_token = get_pconfig($b['uid'], "tumblr", "oauth_token"); + $oauth_token_secret = get_pconfig($b['uid'], "tumblr", "oauth_token_secret"); + $page = get_pconfig($b['uid'], "tumblr", "page"); + $tmbl_blog = 'blog/'.$page.'/post'; - $tmbl_username = get_pconfig($b['uid'],'tumblr','tumblr_username'); - $tmbl_password = get_pconfig($b['uid'],'tumblr','tumblr_password'); - $tmbl_blog = 'http://www.tumblr.com/api/write'; - - if($tmbl_username && $tmbl_password && $tmbl_blog) { + if($oauth_token && $oauth_token_secret && $tmbl_blog) { require_once('include/bbcode.php'); @@ -193,10 +335,8 @@ function tumblr_send(&$a,&$b) { } $params = array( - 'email' => $tmbl_username, - 'password' => $tmbl_password, 'format' => 'html', - 'generator' => 'Friendica', + 'tweet' => 'off', 'tags' => $tags); if (($link != '') and $video) { @@ -209,17 +349,25 @@ function tumblr_send(&$a,&$b) { $params['caption'] = bbcode($body, false, false); } else if (($link != '') and !$video) { $params['type'] = "link"; - $params['name'] = $title; + $params['title'] = $title; $params['url'] = $link; $params['description'] = bbcode($b["body"], false, false); } else { - $params['type'] = "regular"; + $params['type'] = "text"; $params['title'] = $title; $params['body'] = bbcode($b['body'], false, false); } - $x = post_url($tmbl_blog,$params); - $ret_code = $a->get_curl_code(); + $consumer_key = get_config('tumblr','consumer_key'); + $consumer_secret = get_config('tumblr','consumer_secret'); + + $tum_oauth = new TumblrOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret); + + // Make an API call with the TumblrOAuth instance. + $x = $tum_oauth->post($tmbl_blog,$params); + + $ret_code = $tum_oauth->http_code; + if($ret_code == 201) logger('tumblr_send: success'); elseif($ret_code == 403) diff --git a/tumblr/tumblroauth/OAuth.php b/tumblr/tumblroauth/OAuth.php new file mode 100644 index 00000000..982aaa5d --- /dev/null +++ b/tumblr/tumblroauth/OAuth.php @@ -0,0 +1,874 @@ +key = $key; + $this->secret = $secret; + $this->callback_url = $callback_url; + } + + function __toString() { + return "OAuthConsumer[key=$this->key,secret=$this->secret]"; + } +} + +class OAuthToken { + // access tokens and request tokens + public $key; + public $secret; + + /** + * key = the token + * secret = the token secret + */ + function __construct($key, $secret) { + $this->key = $key; + $this->secret = $secret; + } + + /** + * generates the basic string serialization of a token that a server + * would respond to request_token and access_token calls with + */ + function to_string() { + return "oauth_token=" . + OAuthUtil::urlencode_rfc3986($this->key) . + "&oauth_token_secret=" . + OAuthUtil::urlencode_rfc3986($this->secret); + } + + function __toString() { + return $this->to_string(); + } +} + +/** + * A class for implementing a Signature Method + * See section 9 ("Signing Requests") in the spec + */ +abstract class OAuthSignatureMethod { + /** + * Needs to return the name of the Signature Method (ie HMAC-SHA1) + * @return string + */ + abstract public function get_name(); + + /** + * Build up the signature + * NOTE: The output of this function MUST NOT be urlencoded. + * the encoding is handled in OAuthRequest when the final + * request is serialized + * @param OAuthRequest $request + * @param OAuthConsumer $consumer + * @param OAuthToken $token + * @return string + */ + abstract public function build_signature($request, $consumer, $token); + + /** + * Verifies that a given signature is correct + * @param OAuthRequest $request + * @param OAuthConsumer $consumer + * @param OAuthToken $token + * @param string $signature + * @return bool + */ + public function check_signature($request, $consumer, $token, $signature) { + $built = $this->build_signature($request, $consumer, $token); + return $built == $signature; + } +} + +/** + * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104] + * where the Signature Base String is the text and the key is the concatenated values (each first + * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&' + * character (ASCII code 38) even if empty. + * - Chapter 9.2 ("HMAC-SHA1") + */ +class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { + function get_name() { + return "HMAC-SHA1"; + } + + public function build_signature($request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + + return base64_encode(hash_hmac('sha1', $base_string, $key, true)); + } +} + +/** + * The PLAINTEXT method does not provide any security protection and SHOULD only be used + * over a secure channel such as HTTPS. It does not use the Signature Base String. + * - Chapter 9.4 ("PLAINTEXT") + */ +class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { + public function get_name() { + return "PLAINTEXT"; + } + + /** + * oauth_signature is set to the concatenated encoded values of the Consumer Secret and + * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is + * empty. The result MUST be encoded again. + * - Chapter 9.4.1 ("Generating Signatures") + * + * Please note that the second encoding MUST NOT happen in the SignatureMethod, as + * OAuthRequest handles this! + */ + public function build_signature($request, $consumer, $token) { + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + $request->base_string = $key; + + return $key; + } +} + +/** + * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in + * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for + * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a + * verified way to the Service Provider, in a manner which is beyond the scope of this + * specification. + * - Chapter 9.3 ("RSA-SHA1") + */ +abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { + public function get_name() { + return "RSA-SHA1"; + } + + // Up to the SP to implement this lookup of keys. Possible ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // (2) fetch via http using a url provided by the requester + // (3) some sort of specific discovery code based on request + // + // Either way should return a string representation of the certificate + protected abstract function fetch_public_cert(&$request); + + // Up to the SP to implement this lookup of keys. Possible ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // + // Either way should return a string representation of the certificate + protected abstract function fetch_private_cert(&$request); + + public function build_signature($request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + // Fetch the private key cert based on the request + $cert = $this->fetch_private_cert($request); + + // Pull the private key ID from the certificate + $privatekeyid = openssl_get_privatekey($cert); + + // Sign using the key + $ok = openssl_sign($base_string, $signature, $privatekeyid); + + // Release the key resource + openssl_free_key($privatekeyid); + + return base64_encode($signature); + } + + public function check_signature($request, $consumer, $token, $signature) { + $decoded_sig = base64_decode($signature); + + $base_string = $request->get_signature_base_string(); + + // Fetch the public key cert based on the request + $cert = $this->fetch_public_cert($request); + + // Pull the public key ID from the certificate + $publickeyid = openssl_get_publickey($cert); + + // Check the computed signature against the one passed in the query + $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); + + // Release the key resource + openssl_free_key($publickeyid); + + return $ok == 1; + } +} + +class OAuthRequest { + private $parameters; + private $http_method; + private $http_url; + // for debug purposes + public $base_string; + public static $version = '1.0'; + public static $POST_INPUT = 'php://input'; + + function __construct($http_method, $http_url, $parameters=NULL) { + @$parameters or $parameters = array(); + $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters); + $this->parameters = $parameters; + $this->http_method = $http_method; + $this->http_url = $http_url; + } + + + /** + * attempt to build up a request from what was passed to the server + */ + public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) { + $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") + ? 'http' + : 'https'; + @$http_url or $http_url = $scheme . + '://' . $_SERVER['HTTP_HOST'] . + ':' . + $_SERVER['SERVER_PORT'] . + $_SERVER['REQUEST_URI']; + @$http_method or $http_method = $_SERVER['REQUEST_METHOD']; + + // We weren't handed any parameters, so let's find the ones relevant to + // this request. + // If you run XML-RPC or similar you should use this to provide your own + // parsed parameter-list + if (!$parameters) { + // Find request headers + $request_headers = OAuthUtil::get_headers(); + + // Parse the query-string to find GET parameters + $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']); + + // It's a POST request of the proper content-type, so parse POST + // parameters and add those overriding any duplicates from GET + if ($http_method == "POST" + && @strstr($request_headers["Content-Type"], + "application/x-www-form-urlencoded") + ) { + $post_data = OAuthUtil::parse_parameters( + file_get_contents(self::$POST_INPUT) + ); + $parameters = array_merge($parameters, $post_data); + } + + // We have a Authorization-header with OAuth data. Parse the header + // and add those overriding any duplicates from GET or POST + if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") { + $header_parameters = OAuthUtil::split_header( + $request_headers['Authorization'] + ); + $parameters = array_merge($parameters, $header_parameters); + } + + } + + return new OAuthRequest($http_method, $http_url, $parameters); + } + + /** + * pretty much a helper function to set up the request + */ + public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) { + @$parameters or $parameters = array(); + $defaults = array("oauth_version" => OAuthRequest::$version, + "oauth_nonce" => OAuthRequest::generate_nonce(), + "oauth_timestamp" => OAuthRequest::generate_timestamp(), + "oauth_consumer_key" => $consumer->key); + if ($token) + $defaults['oauth_token'] = $token->key; + + $parameters = array_merge($defaults, $parameters); + + return new OAuthRequest($http_method, $http_url, $parameters); + } + + public function set_parameter($name, $value, $allow_duplicates = true) { + if ($allow_duplicates && isset($this->parameters[$name])) { + // We have already added parameter(s) with this name, so add to the list + if (is_scalar($this->parameters[$name])) { + // This is the first duplicate, so transform scalar (string) + // into an array so we can add the duplicates + $this->parameters[$name] = array($this->parameters[$name]); + } + + $this->parameters[$name][] = $value; + } else { + $this->parameters[$name] = $value; + } + } + + public function get_parameter($name) { + return isset($this->parameters[$name]) ? $this->parameters[$name] : null; + } + + public function get_parameters() { + return $this->parameters; + } + + public function unset_parameter($name) { + unset($this->parameters[$name]); + } + + /** + * The request parameters, sorted and concatenated into a normalized string. + * @return string + */ + public function get_signable_parameters() { + // Grab all parameters + $params = $this->parameters; + + // Remove oauth_signature if present + // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") + if (isset($params['oauth_signature'])) { + unset($params['oauth_signature']); + } + + return OAuthUtil::build_http_query($params); + } + + /** + * Returns the base string of this request + * + * The base string defined as the method, the url + * and the parameters (normalized), each urlencoded + * and the concated with &. + */ + public function get_signature_base_string() { + $parts = array( + $this->get_normalized_http_method(), + $this->get_normalized_http_url(), + $this->get_signable_parameters() + ); + + $parts = OAuthUtil::urlencode_rfc3986($parts); + + return implode('&', $parts); + } + + /** + * just uppercases the http method + */ + public function get_normalized_http_method() { + return strtoupper($this->http_method); + } + + /** + * parses the url and rebuilds it to be + * scheme://host/path + */ + public function get_normalized_http_url() { + $parts = parse_url($this->http_url); + + $port = @$parts['port']; + $scheme = $parts['scheme']; + $host = $parts['host']; + $path = @$parts['path']; + + $port or $port = ($scheme == 'https') ? '443' : '80'; + + if (($scheme == 'https' && $port != '443') + || ($scheme == 'http' && $port != '80')) { + $host = "$host:$port"; + } + return "$scheme://$host$path"; + } + + /** + * builds a url usable for a GET request + */ + public function to_url() { + $post_data = $this->to_postdata(); + $out = $this->get_normalized_http_url(); + if ($post_data) { + $out .= '?'.$post_data; + } + return $out; + } + + /** + * builds the data one would send in a POST request + */ + public function to_postdata() { + return OAuthUtil::build_http_query($this->parameters); + } + + /** + * builds the Authorization: header + */ + public function to_header($realm=null) { + $first = true; + if($realm) { + $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"'; + $first = false; + } else + $out = 'Authorization: OAuth'; + + $total = array(); + foreach ($this->parameters as $k => $v) { + if (substr($k, 0, 5) != "oauth") continue; + if (is_array($v)) { + throw new OAuthException('Arrays not supported in headers'); + } + $out .= ($first) ? ' ' : ','; + $out .= OAuthUtil::urlencode_rfc3986($k) . + '="' . + OAuthUtil::urlencode_rfc3986($v) . + '"'; + $first = false; + } + return $out; + } + + public function __toString() { + return $this->to_url(); + } + + + public function sign_request($signature_method, $consumer, $token) { + $this->set_parameter( + "oauth_signature_method", + $signature_method->get_name(), + false + ); + $signature = $this->build_signature($signature_method, $consumer, $token); + $this->set_parameter("oauth_signature", $signature, false); + } + + public function build_signature($signature_method, $consumer, $token) { + $signature = $signature_method->build_signature($this, $consumer, $token); + return $signature; + } + + /** + * util function: current timestamp + */ + private static function generate_timestamp() { + return time(); + } + + /** + * util function: current nonce + */ + private static function generate_nonce() { + $mt = microtime(); + $rand = mt_rand(); + + return md5($mt . $rand); // md5s look nicer than numbers + } +} + +class OAuthServer { + protected $timestamp_threshold = 300; // in seconds, five minutes + protected $version = '1.0'; // hi blaine + protected $signature_methods = array(); + + protected $data_store; + + function __construct($data_store) { + $this->data_store = $data_store; + } + + public function add_signature_method($signature_method) { + $this->signature_methods[$signature_method->get_name()] = + $signature_method; + } + + // high level functions + + /** + * process a request_token request + * returns the request token on success + */ + public function fetch_request_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // no token required for the initial token request + $token = NULL; + + $this->check_signature($request, $consumer, $token); + + // Rev A change + $callback = $request->get_parameter('oauth_callback'); + $new_token = $this->data_store->new_request_token($consumer, $callback); + + return $new_token; + } + + /** + * process an access_token request + * returns the access token on success + */ + public function fetch_access_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // requires authorized request token + $token = $this->get_token($request, $consumer, "request"); + + $this->check_signature($request, $consumer, $token); + + // Rev A change + $verifier = $request->get_parameter('oauth_verifier'); + $new_token = $this->data_store->new_access_token($token, $consumer, $verifier); + + return $new_token; + } + + /** + * verify an api call, checks all the parameters + */ + public function verify_request(&$request) { + $this->get_version($request); + $consumer = $this->get_consumer($request); + $token = $this->get_token($request, $consumer, "access"); + $this->check_signature($request, $consumer, $token); + return array($consumer, $token); + } + + // Internals from here + /** + * version 1 + */ + private function get_version(&$request) { + $version = $request->get_parameter("oauth_version"); + if (!$version) { + // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. + // Chapter 7.0 ("Accessing Protected Ressources") + $version = '1.0'; + } + if ($version !== $this->version) { + throw new OAuthException("OAuth version '$version' not supported"); + } + return $version; + } + + /** + * figure out the signature with some defaults + */ + private function get_signature_method(&$request) { + $signature_method = + @$request->get_parameter("oauth_signature_method"); + + if (!$signature_method) { + // According to chapter 7 ("Accessing Protected Ressources") the signature-method + // parameter is required, and we can't just fallback to PLAINTEXT + throw new OAuthException('No signature method parameter. This parameter is required'); + } + + if (!in_array($signature_method, + array_keys($this->signature_methods))) { + throw new OAuthException( + "Signature method '$signature_method' not supported " . + "try one of the following: " . + implode(", ", array_keys($this->signature_methods)) + ); + } + return $this->signature_methods[$signature_method]; + } + + /** + * try to find the consumer for the provided request's consumer key + */ + private function get_consumer(&$request) { + $consumer_key = @$request->get_parameter("oauth_consumer_key"); + if (!$consumer_key) { + throw new OAuthException("Invalid consumer key"); + } + + $consumer = $this->data_store->lookup_consumer($consumer_key); + if (!$consumer) { + throw new OAuthException("Invalid consumer"); + } + + return $consumer; + } + + /** + * try to find the token for the provided request's token key + */ + private function get_token(&$request, $consumer, $token_type="access") { + $token_field = @$request->get_parameter('oauth_token'); + $token = $this->data_store->lookup_token( + $consumer, $token_type, $token_field + ); + if (!$token) { + throw new OAuthException("Invalid $token_type token: $token_field"); + } + return $token; + } + + /** + * all-in-one function to check the signature on a request + * should guess the signature method appropriately + */ + private function check_signature(&$request, $consumer, $token) { + // this should probably be in a different method + $timestamp = @$request->get_parameter('oauth_timestamp'); + $nonce = @$request->get_parameter('oauth_nonce'); + + $this->check_timestamp($timestamp); + $this->check_nonce($consumer, $token, $nonce, $timestamp); + + $signature_method = $this->get_signature_method($request); + + $signature = $request->get_parameter('oauth_signature'); + $valid_sig = $signature_method->check_signature( + $request, + $consumer, + $token, + $signature + ); + + if (!$valid_sig) { + throw new OAuthException("Invalid signature"); + } + } + + /** + * check that the timestamp is new enough + */ + private function check_timestamp($timestamp) { + if( ! $timestamp ) + throw new OAuthException( + 'Missing timestamp parameter. The parameter is required' + ); + + // verify that timestamp is recentish + $now = time(); + if (abs($now - $timestamp) > $this->timestamp_threshold) { + throw new OAuthException( + "Expired timestamp, yours $timestamp, ours $now" + ); + } + } + + /** + * check that the nonce is not repeated + */ + private function check_nonce($consumer, $token, $nonce, $timestamp) { + if( ! $nonce ) + throw new OAuthException( + 'Missing nonce parameter. The parameter is required' + ); + + // verify that the nonce is uniqueish + $found = $this->data_store->lookup_nonce( + $consumer, + $token, + $nonce, + $timestamp + ); + if ($found) { + throw new OAuthException("Nonce already used: $nonce"); + } + } + +} + +class OAuthDataStore { + function lookup_consumer($consumer_key) { + // implement me + } + + function lookup_token($consumer, $token_type, $token) { + // implement me + } + + function lookup_nonce($consumer, $token, $nonce, $timestamp) { + // implement me + } + + function new_request_token($consumer, $callback = null) { + // return a new token attached to this consumer + } + + function new_access_token($token, $consumer, $verifier = null) { + // return a new access token attached to this consumer + // for the user associated with this token if the request token + // is authorized + // should also invalidate the request token + } + +} + +class OAuthUtil { + public static function urlencode_rfc3986($input) { + if (is_array($input)) { + return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input); + } else if (is_scalar($input)) { + return str_replace( + '+', + ' ', + str_replace('%7E', '~', rawurlencode($input)) + ); + } else { + return ''; + } +} + + + // This decode function isn't taking into consideration the above + // modifications to the encoding process. However, this method doesn't + // seem to be used anywhere so leaving it as is. + public static function urldecode_rfc3986($string) { + return urldecode($string); + } + + // Utility function for turning the Authorization: header into + // parameters, has to do some unescaping + // Can filter out any non-oauth parameters if needed (default behaviour) + public static function split_header($header, $only_allow_oauth_parameters = true) { + $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/'; + $offset = 0; + $params = array(); + while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { + $match = $matches[0]; + $header_name = $matches[2][0]; + $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0]; + if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) { + $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content); + } + $offset = $match[1] + strlen($match[0]); + } + + if (isset($params['realm'])) { + unset($params['realm']); + } + + return $params; + } + + // helper to try to sort out headers for people who aren't running apache + public static function get_headers() { + if (function_exists('apache_request_headers')) { + // we need this to get the actual Authorization: header + // because apache tends to tell us it doesn't exist + $headers = apache_request_headers(); + + // sanitize the output of apache_request_headers because + // we always want the keys to be Cased-Like-This and arh() + // returns the headers in the same case as they are in the + // request + $out = array(); + foreach( $headers AS $key => $value ) { + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace("-", " ", $key))) + ); + $out[$key] = $value; + } + } else { + // otherwise we don't have apache and are just going to have to hope + // that $_SERVER actually contains what we need + $out = array(); + if( isset($_SERVER['CONTENT_TYPE']) ) + $out['Content-Type'] = $_SERVER['CONTENT_TYPE']; + if( isset($_ENV['CONTENT_TYPE']) ) + $out['Content-Type'] = $_ENV['CONTENT_TYPE']; + + foreach ($_SERVER as $key => $value) { + if (substr($key, 0, 5) == "HTTP_") { + // this is chaos, basically it is just there to capitalize the first + // letter of every word that is not an initial HTTP and strip HTTP + // code from przemek + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace("_", " ", substr($key, 5)))) + ); + $out[$key] = $value; + } + } + } + return $out; + } + + // This function takes a input like a=b&a=c&d=e and returns the parsed + // parameters like this + // array('a' => array('b','c'), 'd' => 'e') + public static function parse_parameters( $input ) { + if (!isset($input) || !$input) return array(); + + $pairs = explode('&', $input); + + $parsed_parameters = array(); + foreach ($pairs as $pair) { + $split = explode('=', $pair, 2); + $parameter = OAuthUtil::urldecode_rfc3986($split[0]); + $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : ''; + + if (isset($parsed_parameters[$parameter])) { + // We have already recieved parameter(s) with this name, so add to the list + // of parameters with this name + + if (is_scalar($parsed_parameters[$parameter])) { + // This is the first duplicate, so transform scalar (string) into an array + // so we can add the duplicates + $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]); + } + + $parsed_parameters[$parameter][] = $value; + } else { + $parsed_parameters[$parameter] = $value; + } + } + return $parsed_parameters; + } + + public static function build_http_query($params) { + if (!$params) return ''; + + // Urlencode both keys and values + $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); + $values = OAuthUtil::urlencode_rfc3986(array_values($params)); + $params = array_combine($keys, $values); + + // Parameters are sorted by name, using lexicographical byte value ordering. + // Ref: Spec: 9.1.1 (1) + uksort($params, 'strcmp'); + + $pairs = array(); + foreach ($params as $parameter => $value) { + if (is_array($value)) { + // If two or more parameters share the same name, they are sorted by their value + // Ref: Spec: 9.1.1 (1) + natsort($value); + foreach ($value as $duplicate_value) { + $pairs[] = $parameter . '=' . $duplicate_value; + } + } else { + $pairs[] = $parameter . '=' . $value; + } + } + // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61) + // Each name-value pair is separated by an '&' character (ASCII code 38) + return implode('&', $pairs); + } +} + +?> diff --git a/tumblr/tumblroauth/tumblroauth.php b/tumblr/tumblroauth/tumblroauth.php new file mode 100644 index 00000000..3c6f13c1 --- /dev/null +++ b/tumblr/tumblroauth/tumblroauth.php @@ -0,0 +1,245 @@ +http_status; } + function lastAPICall() { return $this->last_api_call; } + + /** + * construct TumblrOAuth object + */ + function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) { + $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1(); + $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); + if (!empty($oauth_token) && !empty($oauth_token_secret)) { + $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret); + } else { + $this->token = NULL; + } + } + + + /** + * Get a request_token from Tumblr + * + * @returns a key/value array containing oauth_token and oauth_token_secret + */ + function getRequestToken($oauth_callback = NULL) { + $parameters = array(); + if (!empty($oauth_callback)) { + $parameters['oauth_callback'] = $oauth_callback; + } + $request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters); + $token = OAuthUtil::parse_parameters($request); + $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); + return $token; + } + + /** + * Get the authorize URL + * + * @returns a string + */ + function getAuthorizeURL($token, $sign_in_with_tumblr = TRUE) { + if (is_array($token)) { + $token = $token['oauth_token']; + } + if (empty($sign_in_with_tumblr)) { + return $this->authorizeURL() . "?oauth_token={$token}"; + } else { + return $this->authenticateURL() . "?oauth_token={$token}"; + } + } + + /** + * Exchange request token and secret for an access token and + * secret, to sign API calls. + * + * @returns array("oauth_token" => "the-access-token", + * "oauth_token_secret" => "the-access-secret", + * "user_id" => "9436992", + * "screen_name" => "abraham") + */ + function getAccessToken($oauth_verifier = FALSE) { + $parameters = array(); + if (!empty($oauth_verifier)) { + $parameters['oauth_verifier'] = $oauth_verifier; + } + $request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters); + $token = OAuthUtil::parse_parameters($request); + $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); + return $token; + } + + /** + * One time exchange of username and password for access token and secret. + * + * @returns array("oauth_token" => "the-access-token", + * "oauth_token_secret" => "the-access-secret", + * "user_id" => "9436992", + * "screen_name" => "abraham", + * "x_auth_expires" => "0") + */ + function getXAuthToken($username, $password) { + $parameters = array(); + $parameters['x_auth_username'] = $username; + $parameters['x_auth_password'] = $password; + $parameters['x_auth_mode'] = 'client_auth'; + $request = $this->oAuthRequest($this->accessTokenURL(), 'POST', $parameters); + $token = OAuthUtil::parse_parameters($request); + $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); + return $token; + } + + /** + * GET wrapper for oAuthRequest. + */ + function get($url, $parameters = array()) { + $response = $this->oAuthRequest($url, 'GET', $parameters); + if ($this->format === 'json' && $this->decode_json) { + return json_decode($response); + } + return $response; + } + + /** + * POST wrapper for oAuthRequest. + */ + function post($url, $parameters = array()) { + $response = $this->oAuthRequest($url, 'POST', $parameters); + if ($this->format === 'json' && $this->decode_json) { + return json_decode($response); + } + return $response; + } + + /** + * DELETE wrapper for oAuthReqeust. + */ + function delete($url, $parameters = array()) { + $response = $this->oAuthRequest($url, 'DELETE', $parameters); + if ($this->format === 'json' && $this->decode_json) { + return json_decode($response); + } + return $response; + } + + /** + * Format and sign an OAuth / API request + */ + function oAuthRequest($url, $method, $parameters) { + if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) { + $url = "{$this->host}{$url}"; + } + $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters); + $request->sign_request($this->sha1_method, $this->consumer, $this->token); + switch ($method) { + case 'GET': + return $this->http($request->to_url(), 'GET'); + default: + return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata()); + } + } + + /** + * Make an HTTP request + * + * @return API results + */ + function http($url, $method, $postfields = NULL) { + $this->http_info = array(); + $ci = curl_init(); + /* Curl settings */ + curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent); + curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout); + curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout); + curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:')); + curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer); + curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader')); + curl_setopt($ci, CURLOPT_HEADER, FALSE); + + switch ($method) { + case 'POST': + curl_setopt($ci, CURLOPT_POST, TRUE); + if (!empty($postfields)) { + curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields); + } + break; + case 'DELETE': + curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE'); + if (!empty($postfields)) { + $url = "{$url}?{$postfields}"; + } + } + + curl_setopt($ci, CURLOPT_URL, $url); + $response = curl_exec($ci); + $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE); + $this->http_info = array_merge($this->http_info, curl_getinfo($ci)); + $this->url = $url; + curl_close ($ci); + return $response; + } + + /** + * Get the header info to store. + */ + function getHeader($ch, $header) { + $i = strpos($header, ':'); + if (!empty($i)) { + $key = str_replace('-', '_', strtolower(substr($header, 0, $i))); + $value = trim(substr($header, $i + 2)); + $this->http_header[$key] = $value; + } + return strlen($header); + } +} diff --git a/twitter.tgz b/twitter.tgz index beeea2be..7558bca9 100755 Binary files a/twitter.tgz and b/twitter.tgz differ diff --git a/twitter/README b/twitter/README index ff08976c..8041f317 100755 --- a/twitter/README +++ b/twitter/README @@ -3,17 +3,14 @@ By Tobias Diekershoff http://diekershoff.homeunix.net/friendika/profile/tobias tobias.diekershoff(at)gmx.net -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! This addon is currently under development. If you have any problem !! -!! with it, please contact the Author. !! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - With this addon to Friendica you can give your user the possibility to post their *public* messages to Twitter. The messages will be strapped their rich context and shortened to 140 characters length if necessary. If shortening of the message was performed a link will be added to the Tweet pointing to the original message on your server. +The addon can also mirror a users Tweets into the ~friendica wall. + There is a similar addon for forwarding public messages to "StatusNet":http://status.net [[StatusNet Plugin]]. diff --git a/twitter/twitter.css b/twitter/twitter.css index 75747979..3ff37cda 100755 --- a/twitter/twitter.css +++ b/twitter/twitter.css @@ -19,29 +19,20 @@ #twitter-disconnect { float: left; } -#twitter-enable-label { - float: left; - width: 250px; - margin-bottom: 5px; -} -#twitter-default-label { - float: left; - width: 250px; -} -#twitter-sendtaglinks-label { +#twitter-default-label, +#twitter-sendtaglinks-label, +#twitter-enable-label, +#twitter-shortening-label, +#twitter-mirror-label, +#twitter-pin-label { float: left; width: 250px; - margin-bottom: 25px; + margin-bottom: 10px; } #twitter-checkbox { float: left; } -#twitter-pin-label { - float: left; - width: 250px; - margin-bottom: 25px; -} #twitter-pin { float: left; diff --git a/twitter/twitter.php b/twitter/twitter.php index 5fd053fa..e4848f9d 100755 --- a/twitter/twitter.php +++ b/twitter/twitter.php @@ -36,6 +36,8 @@ * Documentation: http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/Twitter_Plugin */ +define('TWITTER_DEFAULT_POLL_INTERVAL', 5); // given in minutes + function twitter_install() { // we need some hooks, for the configuration and for sending tweets register_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); @@ -43,6 +45,7 @@ function twitter_install() { register_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local'); register_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook'); register_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets'); + register_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron'); logger("installed twitter"); } @@ -53,6 +56,7 @@ function twitter_uninstall() { unregister_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local'); unregister_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook'); unregister_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets'); + unregister_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron'); // old setting - remove only unregister_hook('post_local_end', 'addon/twitter/twitter.php', 'twitter_post_hook'); @@ -70,10 +74,8 @@ function twitter_jot_nets(&$a,&$b) { $tw_defpost = get_pconfig(local_user(),'twitter','post_by_default'); $selected = ((intval($tw_defpost) == 1) ? ' checked="checked" ' : ''); $b .= '
' - . t('Post to Twitter') . '
'; + . t('Post to Twitter') . ''; } - - } function twitter_settings_post ($a,$post) { @@ -87,20 +89,23 @@ function twitter_settings_post ($a,$post) { * if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair * from the user configuration */ - del_pconfig( local_user(), 'twitter', 'consumerkey' ); - del_pconfig( local_user(), 'twitter', 'consumersecret' ); - del_pconfig( local_user(), 'twitter', 'oauthtoken' ); - del_pconfig( local_user(), 'twitter', 'oauthsecret' ); - del_pconfig( local_user(), 'twitter', 'post' ); - del_pconfig( local_user(), 'twitter', 'post_by_default' ); - del_pconfig( local_user(), 'twitter', 'post_taglinks'); + del_pconfig(local_user(), 'twitter', 'consumerkey'); + del_pconfig(local_user(), 'twitter', 'consumersecret'); + del_pconfig(local_user(), 'twitter', 'oauthtoken'); + del_pconfig(local_user(), 'twitter', 'oauthsecret'); + del_pconfig(local_user(), 'twitter', 'post'); + del_pconfig(local_user(), 'twitter', 'post_by_default'); + del_pconfig(local_user(), 'twitter', 'post_taglinks'); + del_pconfig(local_user(), 'twitter', 'lastid'); + del_pconfig(local_user(), 'twitter', 'mirror_posts'); + del_pconfig(local_user(), 'twitter', 'intelligent_shortening'); } else { if (isset($_POST['twitter-pin'])) { // if the user supplied us with a PIN from Twitter, let the magic of OAuth happen logger('got a Twitter PIN'); require_once('library/twitteroauth.php'); - $ckey = get_config('twitter', 'consumerkey' ); - $csecret = get_config('twitter', 'consumersecret' ); + $ckey = get_config('twitter', 'consumerkey'); + $csecret = get_config('twitter', 'consumersecret'); // the token and secret for which the PIN was generated were hidden in the settings // form as token and token2, we need a new connection to Twitter using these token // and secret to request a Access Token with the PIN @@ -119,6 +124,8 @@ function twitter_settings_post ($a,$post) { set_pconfig(local_user(),'twitter','post',intval($_POST['twitter-enable'])); set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default'])); set_pconfig(local_user(),'twitter','post_taglinks',intval($_POST['twitter-sendtaglinks'])); + set_pconfig(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror'])); + set_pconfig(local_user(), 'twitter', 'intelligent_shortening', intval($_POST['twitter-shortening'])); info( t('Twitter settings updated.') . EOL); }} } @@ -141,6 +148,10 @@ function twitter_settings(&$a,&$s) { $defchecked = (($defenabled) ? ' checked="checked" ' : ''); $linksenabled = get_pconfig(local_user(),'twitter','post_taglinks'); $linkschecked = (($linksenabled) ? ' checked="checked" ' : ''); + $mirrorenabled = get_pconfig(local_user(),'twitter','mirror_posts'); + $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : ''); + $shorteningenabled = get_pconfig(local_user(),'twitter','intelligent_shortening'); + $shorteningchecked = (($shorteningenabled) ? ' checked="checked" ' : ''); $s .= '
'; $s .= '

'. t('Twitter Posting Settings') .'

'; @@ -198,6 +209,15 @@ function twitter_settings(&$a,&$s) { $s .= ''; $s .= ''; $s .= '
'; + + $s .= ''; + $s .= ''; + $s .= '
'; + + $s .= ''; + $s .= ''; + $s .= '
'; + $s .= ''; $s .= ''; $s .= '
'; @@ -261,6 +281,133 @@ function short_link ($url) { return $slinky->short(); } }; +function twitter_shortenmsg($b) { + require_once("include/bbcode.php"); + require_once("include/html2plain.php"); + + $max_char = 140; + + // Looking for the first image + $image = ''; + if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$b['body'],$matches)) + $image = $matches[3]; + + if ($image == '') + if(preg_match("/\[img\](.*?)\[\/img\]/is",$b['body'],$matches)) + $image = $matches[1]; + + $multipleimages = (strpos($b['body'], "[img") != strrpos($b['body'], "[img")); + + // When saved into the database the content is sent through htmlspecialchars + // That means that we have to decode all image-urls + $image = htmlspecialchars_decode($image); + + $body = $b["body"]; + if ($b["title"] != "") + $body = $b["title"]."\n\n".$body; + + if (strpos($body, "[bookmark") !== false) { + // splitting the text in two parts: + // before and after the bookmark + $pos = strpos($body, "[bookmark"); + $body1 = substr($body, 0, $pos); + $body2 = substr($body, $pos); + + // Removing all quotes after the bookmark + // they are mostly only the content after the bookmark. + $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2); + $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2); + $body = $body1.$body2; + } + + // Add some newlines so that the message could be cut better + $body = str_replace(array("[quote", "[bookmark", "[/bookmark]", "[/quote]"), + array("\n[quote", "\n[bookmark", "[/bookmark]\n", "[/quote]\n"), $body); + + // remove the recycle signs and the names since they aren't helpful on twitter + // recycle 1 + $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8'); + $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body); + // recycle 2 (Test) + $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8'); + $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body); + + // remove the share element + $body = preg_replace("/\[share(.*?)\](.*?)\[\/share\]/ism","\n\n$2\n\n",$body); + + // At first convert the text to html + $html = bbcode($body, false, false); + + // Then convert it to plain text + //$msg = trim($b['title']." \n\n".html2plain($html, 0, true)); + $msg = trim(html2plain($html, 0, true)); + $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8'); + + // Removing multiple newlines + while (strpos($msg, "\n\n\n") !== false) + $msg = str_replace("\n\n\n", "\n\n", $msg); + + // Removing multiple spaces + while (strpos($msg, " ") !== false) + $msg = str_replace(" ", " ", $msg); + + // Removing URLs + $msg = preg_replace('/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', "", $msg); + + $msg = trim($msg); + + $link = ''; + // look for bookmark-bbcode and handle it with priority + if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches)) + $link = $matches[1]; + + $multiplelinks = (strpos($b['body'], "[bookmark") != strrpos($b['body'], "[bookmark")); + + // If there is no bookmark element then take the first link + if ($link == '') { + $links = collecturls($html); + if (sizeof($links) > 0) { + reset($links); + $link = current($links); + } + $multiplelinks = (sizeof($links) > 1); + } + + $msglink = ""; + if ($multiplelinks) + $msglink = $b["plink"]; + else if ($link != "") + $msglink = $link; + else if ($multipleimages) + $msglink = $b["plink"]; + else if ($image != "") + $msglink = $image; + + if (($msglink == "") and strlen($msg) > $max_char) + $msglink = $b["plink"]; + + if (strlen($msglink) > 20) + $msglink = short_link($msglink); + + if (strlen(trim($msg." ".$msglink)) > $max_char) { + $msg = substr($msg, 0, $max_char - (strlen($msglink))); + $lastchar = substr($msg, -1); + $msg = substr($msg, 0, -1); + $pos = strrpos($msg, "\n"); + if ($pos > 0) + $msg = substr($msg, 0, $pos); + else if ($lastchar != "\n") + $msg = substr($msg, 0, -3)."..."; + } + $msg = str_replace("\n", " ", $msg); + + // Removing multiple spaces - again + while (strpos($msg, " ") !== false) + $msg = str_replace(" ", " ", $msg); + + return(trim($msg." ".$msglink)); +} + function twitter_post_hook(&$a,&$b) { /** @@ -276,15 +423,24 @@ function twitter_post_hook(&$a,&$b) { if($b['parent'] != $b['id']) return; + // if post comes from twitter don't send it back + if($b['app'] == "Twitter") + return; + logger('twitter post invoked'); load_pconfig($b['uid'], 'twitter'); - $ckey = get_config('twitter', 'consumerkey' ); - $csecret = get_config('twitter', 'consumersecret' ); - $otoken = get_pconfig($b['uid'], 'twitter', 'oauthtoken' ); - $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret' ); + $ckey = get_config('twitter', 'consumerkey'); + $csecret = get_config('twitter', 'consumersecret'); + $otoken = get_pconfig($b['uid'], 'twitter', 'oauthtoken'); + $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret'); + $intelligent_shortening = get_pconfig($b['uid'], 'twitter', 'intelligent_shortening'); + + // Global setting overrides this + if (get_config('twitter','intelligent_shortening')) + $intelligent_shortening = get_config('twitter','intelligent_shortening'); if($ckey && $csecret && $otoken && $osecret) { logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG); @@ -294,81 +450,84 @@ function twitter_post_hook(&$a,&$b) { $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret); // in theory max char is 140 but T. uses t.co to make links // longer so we give them 10 characters extra - $max_char = 130; // max. length for a tweet - // we will only work with up to two times the length of the dent - // we can later send to Twitter. This way we can "gain" some - // information during shortening of potential links but do not - // shorten all the links in a 200000 character long essay. - if (! $b['title']=='') { - $tmp = $b['title'] . ' : '. $b['body']; -// $tmp = substr($tmp, 0, 4*$max_char); - } else { - $tmp = $b['body']; // substr($b['body'], 0, 3*$max_char); - } - // if [url=bla][img]blub.png[/img][/url] get blub.png - $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\]\[img\](\\w+.*?)\\[\\/img\]\\[\\/url\]/i', '$2', $tmp); - // preserve links to images, videos and audios - $tmp = preg_replace( '/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism', '$3', $tmp); - $tmp = preg_replace( '/\[\\/?img(\\s+.*?\]|\])/i', '', $tmp); - $tmp = preg_replace( '/\[\\/?video(\\s+.*?\]|\])/i', '', $tmp); - $tmp = preg_replace( '/\[\\/?youtube(\\s+.*?\]|\])/i', '', $tmp); - $tmp = preg_replace( '/\[\\/?vimeo(\\s+.*?\]|\])/i', '', $tmp); - $tmp = preg_replace( '/\[\\/?audio(\\s+.*?\]|\])/i', '', $tmp); - $linksenabled = get_pconfig($b['uid'],'twitter','post_taglinks'); - // if a #tag is linked, don't send the [url] over to SN - // that is, don't send if the option is not set in the - // connector settings - if ($linksenabled=='0') { - // #-tags - $tmp = preg_replace( '/#\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '#$2', $tmp); - // @-mentions - $tmp = preg_replace( '/@\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '@$2', $tmp); - // recycle 1 - $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8'); - $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp); - // recycle 2 (Test) - $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8'); - $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp); - } - $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/url\]/i', '$2 $1', $tmp); - $tmp = preg_replace( '/\[bookmark\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/bookmark\]/i', '$2 $1', $tmp); - // find all http or https links in the body of the entry and - // apply the shortener if the link is longer then 20 characters - if (( strlen($tmp)>$max_char ) && ( $max_char > 0 )) { - preg_match_all ( '/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', $tmp, $allurls ); - foreach ($allurls as $url) { - foreach ($url as $u) { - if (strlen($u)>20) { - $sl = short_link($u); - $tmp = str_replace( $u, $sl, $tmp ); - } - } - } - } - // ok, all the links we want to send out are save, now strip - // away the remaining bbcode - //$msg = strip_tags(bbcode($tmp, false, false)); - $msg = bbcode($tmp, false, false); - $msg = str_replace(array('
','
'),"\n",$msg); - $msg = strip_tags($msg); + if (!$intelligent_shortening) { + $max_char = 130; // max. length for a tweet + // we will only work with up to two times the length of the dent + // we can later send to Twitter. This way we can "gain" some + // information during shortening of potential links but do not + // shorten all the links in a 200000 character long essay. + if (! $b['title']=='') { + $tmp = $b['title'] . ' : '. $b['body']; + // $tmp = substr($tmp, 0, 4*$max_char); + } else { + $tmp = $b['body']; // substr($b['body'], 0, 3*$max_char); + } + // if [url=bla][img]blub.png[/img][/url] get blub.png + $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\]\[img\](\\w+.*?)\\[\\/img\]\\[\\/url\]/i', '$2', $tmp); + // preserve links to images, videos and audios + $tmp = preg_replace( '/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism', '$3', $tmp); + $tmp = preg_replace( '/\[\\/?img(\\s+.*?\]|\])/i', '', $tmp); + $tmp = preg_replace( '/\[\\/?video(\\s+.*?\]|\])/i', '', $tmp); + $tmp = preg_replace( '/\[\\/?youtube(\\s+.*?\]|\])/i', '', $tmp); + $tmp = preg_replace( '/\[\\/?vimeo(\\s+.*?\]|\])/i', '', $tmp); + $tmp = preg_replace( '/\[\\/?audio(\\s+.*?\]|\])/i', '', $tmp); + $linksenabled = get_pconfig($b['uid'],'twitter','post_taglinks'); + // if a #tag is linked, don't send the [url] over to SN + // that is, don't send if the option is not set in the + // connector settings + if ($linksenabled=='0') { + // #-tags + $tmp = preg_replace( '/#\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '#$2', $tmp); + // @-mentions + $tmp = preg_replace( '/@\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '@$2', $tmp); + // recycle 1 + $recycle = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8'); + $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp); + // recycle 2 (Test) + $recycle = html_entity_decode("◌ ", ENT_QUOTES, 'UTF-8'); + $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp); + } + $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/url\]/i', '$2 $1', $tmp); + $tmp = preg_replace( '/\[bookmark\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/bookmark\]/i', '$2 $1', $tmp); + // find all http or https links in the body of the entry and + // apply the shortener if the link is longer then 20 characters + if (( strlen($tmp)>$max_char ) && ( $max_char > 0 )) { + preg_match_all ( '/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', $tmp, $allurls ); + foreach ($allurls as $url) { + foreach ($url as $u) { + if (strlen($u)>20) { + $sl = short_link($u); + $tmp = str_replace( $u, $sl, $tmp ); + } + } + } + } + // ok, all the links we want to send out are save, now strip + // away the remaining bbcode + //$msg = strip_tags(bbcode($tmp, false, false)); + $msg = bbcode($tmp, false, false); + $msg = str_replace(array('
','
'),"\n",$msg); + $msg = strip_tags($msg); - // quotes not working - let's try this - $msg = html_entity_decode($msg); - if (( strlen($msg) > $max_char) && $max_char > 0) { - $shortlink = short_link( $b['plink'] ); - // the new message will be shortened such that "... $shortlink" - // will fit into the character limit - $msg = nl2br(substr($msg, 0, $max_char-strlen($shortlink)-4)); - $msg = str_replace(array('
','
'),' ',$msg); - $e = explode(' ', $msg); - // remove the last word from the cut down message to - // avoid sending cut words to the MicroBlog - array_pop($e); - $msg = implode(' ', $e); - $msg .= '... ' . $shortlink; - } + // quotes not working - let's try this + $msg = html_entity_decode($msg); + if (( strlen($msg) > $max_char) && $max_char > 0) { + $shortlink = short_link( $b['plink'] ); + // the new message will be shortened such that "... $shortlink" + // will fit into the character limit + $msg = nl2br(substr($msg, 0, $max_char-strlen($shortlink)-4)); + $msg = str_replace(array('
','
'),' ',$msg); + $e = explode(' ', $msg); + // remove the last word from the cut down message to + // avoid sending cut words to the MicroBlog + array_pop($e); + $msg = implode(' ', $e); + $msg .= '... ' . $shortlink; + } - $msg = trim($msg); + $msg = trim($msg); + } else + $msg = twitter_shortenmsg($b); // and now tweet it :-) if(strlen($msg)) { @@ -384,16 +543,122 @@ function twitter_post_hook(&$a,&$b) { function twitter_plugin_admin_post(&$a){ $consumerkey = ((x($_POST,'consumerkey')) ? notags(trim($_POST['consumerkey'])) : ''); $consumersecret = ((x($_POST,'consumersecret')) ? notags(trim($_POST['consumersecret'])): ''); + $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):''); set_config('twitter','consumerkey',$consumerkey); set_config('twitter','consumersecret',$consumersecret); + set_config('twitter','application_name',$applicationname); info( t('Settings updated.'). EOL ); } function twitter_plugin_admin(&$a, &$o){ - $t = file_get_contents( dirname(__file__). "/admin.tpl" ); + $t = get_markup_template( "admin.tpl", "addon/twitter/" ); + $o = replace_macros($t, array( '$submit' => t('Submit'), // name, label, value, help, [extra values] '$consumerkey' => array('consumerkey', t('Consumer key'), get_config('twitter', 'consumerkey' ), ''), - '$consumersecret' => array('consumersecret', t('Consumer secret'), get_config('twitter', 'consumersecret' ), '') + '$consumersecret' => array('consumersecret', t('Consumer secret'), get_config('twitter', 'consumersecret' ), ''), + '$applicationname' => array('applicationname', t('Name of the Twitter Application'), get_config('twitter','application_name'),t('set this to avoid mirroring postings from ~friendica back to ~friendica')) )); } + +function twitter_cron($a,$b) { + $last = get_config('twitter','last_poll'); + + $poll_interval = intval(get_config('twitter','poll_interval')); + if(! $poll_interval) + $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL; + + if($last) { + $next = $last + ($poll_interval * 60); + if($next > time()) { + logger('twitter: poll intervall not reached'); + return; + } + } + logger('twitter: cron_start'); + + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND() "); + if(count($r)) { + foreach($r as $rr) { + logger('twitter: fetching for user '.$rr['uid']); + twitter_fetchtimeline($a, $rr['uid']); + } + } + + logger('twitter: cron_end'); + + set_config('twitter','last_poll', time()); +} + +function twitter_fetchtimeline($a, $uid) { + $ckey = get_config('twitter', 'consumerkey'); + $csecret = get_config('twitter', 'consumersecret'); + $otoken = get_pconfig($uid, 'twitter', 'oauthtoken'); + $osecret = get_pconfig($uid, 'twitter', 'oauthsecret'); + $lastid = get_pconfig($uid, 'twitter', 'lastid'); + + $application_name = get_config('twitter', 'application_name'); + + if ($application_name == "") + $application_name = $a->get_hostname(); + + require_once('library/twitteroauth.php'); + $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret); + + $parameters = array("exclude_replies" => true, "trim_user" => true, "contributor_details" => false, "include_rts" => false); + + $first_time = ($lastid == ""); + + if ($lastid <> "") + $parameters["since_id"] = $lastid; + + $items = $connection->get('statuses/user_timeline', $parameters); + + if (!is_array($items)) + return; + + $posts = array_reverse($items); + + if (count($posts)) { + foreach ($posts as $post) { + if ($post->id_str > $lastid) + $lastid = $post->id_str; + + if ($first_time) + continue; + + if (!strpos($post->source, $application_name)) { + $_SESSION["authenticated"] = true; + $_SESSION["uid"] = $uid; + + $_REQUEST["type"] = "wall"; + $_REQUEST["api_source"] = true; + $_REQUEST["profile_uid"] = $uid; + $_REQUEST["source"] = "Twitter"; + + //$_REQUEST["date"] = $post->created_at; + + $_REQUEST["body"] = $post->text; + if (is_string($post->place->name)) + $_REQUEST["location"] = $post->place->name; + + if (is_string($post->place->full_name)) + $_REQUEST["location"] = $post->place->full_name; + + if (is_array($post->geo->coordinates)) + $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1]; + + if (is_array($post->coordinates->coordinates)) + $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0]; + + //print_r($_REQUEST); + logger('twitter: posting for user '.$uid); + + require_once('mod/item.php'); + item_post($a); + + } + } + } + set_pconfig($uid, 'twitter', 'lastid', $lastid); +} diff --git a/twitter/admin.tpl b/twitter/view/admin.tpl old mode 100755 new mode 100644 similarity index 75% rename from twitter/admin.tpl rename to twitter/view/admin.tpl index a83eb07a..b89f51b6 --- a/twitter/admin.tpl +++ b/twitter/view/admin.tpl @@ -1,3 +1,4 @@ {{ inc field_input.tpl with $field=$consumerkey }}{{ endinc }} {{ inc field_input.tpl with $field=$consumersecret }}{{ endinc }} +{{ inc field_input.tpl with $field=$applicationname }}{{ endinc }}
diff --git a/twitter/view/smarty3/admin.tpl b/twitter/view/smarty3/admin.tpl new file mode 100644 index 00000000..554ed5a0 --- /dev/null +++ b/twitter/view/smarty3/admin.tpl @@ -0,0 +1,4 @@ +{{include file="field_input.tpl" field=$consumerkey}} +{{include file="field_input.tpl" field=$consumersecret}} +{{include file="field_input.tpl" field=$applicationname}} +
diff --git a/uhremotestorage.tgz b/uhremotestorage.tgz index 4aa58096..7a758b00 100755 Binary files a/uhremotestorage.tgz and b/uhremotestorage.tgz differ diff --git a/uhremotestorage/uhremotestorage.php b/uhremotestorage/uhremotestorage.php index a2a8cc3d..85d6b13e 100755 --- a/uhremotestorage/uhremotestorage.php +++ b/uhremotestorage/uhremotestorage.php @@ -78,7 +78,7 @@ function uhremotestorage_settings($a, &$s){ 'Dropbox' => 'Dropbox', ); */ - $tpl = file_get_contents(dirname(__file__)."/settings.tpl"); + $tpl = get_markup_template("settings.tpl", "addon/uhremotestorage/"); $s .= replace_macros($tpl, array( '$title' => 'Unhosted remote storage', '$desc' => sprintf( t('Allow to use your friendica id (%s) to connecto to external unhosted-enabled storage (like ownCloud). See RemoteStorage WebFinger'), $uid ), diff --git a/uhremotestorage/settings.tpl b/uhremotestorage/view/settings.tpl old mode 100755 new mode 100644 similarity index 100% rename from uhremotestorage/settings.tpl rename to uhremotestorage/view/settings.tpl diff --git a/uhremotestorage/view/smarty3/settings.tpl b/uhremotestorage/view/smarty3/settings.tpl new file mode 100644 index 00000000..9a0a55f3 --- /dev/null +++ b/uhremotestorage/view/smarty3/settings.tpl @@ -0,0 +1,9 @@ +
+

{{$title}}

+

{{$desc}}

+ {{include file="field_input.tpl" field=$url}} + {{include file="field_input.tpl" field=$auth}} + {{include file="field_select.tpl" field=$api}} +
+ +
diff --git a/viewsrc.tgz b/viewsrc.tgz index 2ace240a..16d587a7 100644 Binary files a/viewsrc.tgz and b/viewsrc.tgz differ diff --git a/viewsrc/viewsrc.php b/viewsrc/viewsrc.php index f165e9c5..9366930f 100644 --- a/viewsrc/viewsrc.php +++ b/viewsrc/viewsrc.php @@ -25,7 +25,9 @@ function viewsrc_page_end(&$a, &$o){ $a->page['htmlhead'] .= <<< EOS EOS; diff --git a/widgets.tgz b/widgets.tgz index f3b18578..d89204f5 100755 Binary files a/widgets.tgz and b/widgets.tgz differ diff --git a/widgets/settings.tpl b/widgets/view/settings.tpl similarity index 100% rename from widgets/settings.tpl rename to widgets/view/settings.tpl diff --git a/widgets/view/smarty3/settings.tpl b/widgets/view/smarty3/settings.tpl new file mode 100644 index 00000000..017fa120 --- /dev/null +++ b/widgets/view/smarty3/settings.tpl @@ -0,0 +1,19 @@ +
+

{{$title}}

+
+ + {{$key}} +
+ +
+ +
+ +

{{$widgets_h}}

+
    + {{foreach $widgets as $w}} +
  • {{$w.1}}
  • + {{/foreach}} +
+ +
diff --git a/widgets/view/smarty3/widget_like.tpl b/widgets/view/smarty3/widget_like.tpl new file mode 100644 index 00000000..ad529575 --- /dev/null +++ b/widgets/view/smarty3/widget_like.tpl @@ -0,0 +1,3 @@ + +{{$like}} like +{{$dislike}} dislike diff --git a/widgets/widget_like.tpl b/widgets/view/widget_like.tpl similarity index 100% rename from widgets/widget_like.tpl rename to widgets/view/widget_like.tpl diff --git a/widgets/widget_like.php b/widgets/widget_like.php index 649d4a76..8f356da9 100755 --- a/widgets/widget_like.php +++ b/widgets/widget_like.php @@ -52,7 +52,8 @@ function like_widget_content(&$a, $conf){ $o = ""; - $t = file_get_contents( dirname(__file__). "/widget_like.tpl" ); +# $t = file_get_contents( dirname(__file__). "/widget_like.tpl" ); + $t = get_markup_template("widget_like.tpl", "addon/widgets/"); $o .= replace_macros($t, array( '$like' => $likes, '$strlike' => sprintf( tt("%d person likes this", "%d people like this", $likes), $likes), diff --git a/widgets/widgets.php b/widgets/widgets.php index 47a6e48a..72534ce3 100755 --- a/widgets/widgets.php +++ b/widgets/widgets.php @@ -51,7 +51,8 @@ function widgets_settings(&$a,&$o) { - $t = file_get_contents( dirname(__file__). "/settings.tpl" ); +# $t = file_get_contents( dirname(__file__). "/settings.tpl" ); + $t = get_markup_template("settings.tpl", "addon/widgets/"); $o .= replace_macros($t, array( '$submit' => t('Generate new key'), '$baseurl' => $a->get_baseurl(),