diff --git a/CHANGELOG b/CHANGELOG index 0aab0fe96..5b51a5511 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,100 @@ +Version 3.4.2 + + Updates to the documentation (tobias, silke, annando) + Updates to the translations (tobiasd & translation teams) + Updates to themes frost-mobile, vier, duepuntozero, quattro (annando, tobiasd) + Enancements of the communications via OStatus and Diaspora protocols (annando) + Option to automatically follow OStatus contacts was moved from addon to the core (annando) + Add tool to import OStatus contacts from an old account (annando) + SALMON slaps with OStatus were reworked (annando) + Fix for saving searches (rabuzarus) + Fix separation of list items in contact editor (issue #1747) (tobiasd) + When a picture is uploaded, "don't send a note about this new picture" is now the default behaviour (tobiasd) + Show profile url in contact-edit overview listing (issue #1745) (tobiasd) + The vagrant VM usage was changed so that the "installation" is now done automatically on the first run. Example users are automatically put into the database (silke) + Buttons to insert images or attachment to a post use a popup browser to select a previously uploaded item or upload a new one (fabrixxm, rabuzarus) + Improvements in contact handling (annando) + Friendica node can now query other nodes about their users and the contact lists (annando) + Contact recommendation is done only for recently active users (annando) + Admins can opt for search the local DB for contacts instead of the global directory (annando) + The global directory is queried in the background to update local DB and improve similar searches in the future. (annando) + By communication over the Diaspora protocol, red#matrix sources are now correctly identified, hubzilla is detected (annando) + Adopt limitation of usage of "-" in username to avoid conflicts with GNU Social and Diaspora (annando) + The [url] tag now also suppots ftp, mailto, gopher links (annando) + An "inspect queue" module was added to the admin panel (tobiasd) + Fix some missing SQL data escapes (fabrixxm) + Improved the accessibility of the web UI for better screen reader compatibility (annando) + Added access keys (annando) + Support for the public relay server of Diaspora (annando) + Support for the new nodeinfo protocol (successor of current statistics.json), addon deprecated as functionality has been moved into the core (annando) + Fix issue with moved Friendica profiles and Diaspora communication (issue #1491) (annando) + Show more information on contact request page (issue #1739) (annando) + Support for newer versions of the Twidere client was enhanced (annando) + Support for inline [code] tag usage (fabrixxm) + Fix login form in aside (issue #1348) (annando) + Show both url-style and webfinger-style identity address in profile (issue #1621) (tobiasd) + Add button to reload all active plugins in admin plugins page to ensure new hooks are used (fabrixxm) + Make the hardcoded path to global directory configurable (annando) + Change default directory to dir.friendi.ca (annando) + Improve cache system with granular expire time (annando) + Remove oohembed code (issue #1855) (annando) + Checks for mcrypt availability before enable or use RINO2 (fabrixm) + Fix following email contacts (issue #1896) (annando) + Parse BBCode in contact request notification email (annando) + + +Version 3.4.1 + + Implement server-to-server encryption (RINO) using php-encryption library as "RINO 2", deprecate "RINO 1" (issue #1655) (fabrixxm) + Fix connection with Diaspora "freelove" account (issue #1572) (annando) + Various SQL speedups (annando) + Port of Javascript DatePicker input from RedMatrix (rabuzarus) + Port of RedMatrix archive widget (rabuzarus) + Load profile owner settings for theme on profile page (rabuzarus) + Move HTML code from php into templates (rabuzarus) + Theme "frost": add event with doubleclick, event preview (rabuzarus) + Delete attachments on item deletion, delete videos from video tab (issue #1574) (fabrixxm) + Improvements with reshared Diaspora items (annando) + Improvements in OStatus communications: (annando) + improve duplicate handling + publish comments to post to all PuSH subscribers + use correct contact when automatically add @-replies + add attachment links as enclosures + send salmon notifications to every mentioned person + better thread completition + support for bookmarks + support for events and questions + link to items using GUID + Fix warning in mod/photo (issue #1638) (rabuzarus) + New option to block public access to local directory and poco + Fix parsing bbcode [url] tag with fragment identifier (issue #1514) (fabrixxm) + Fix HTML for oembeds (issue #1612) (fabrixxm) + Add fake fields to API response for better Twitter API compatibility (annando) + Fix search in local directory (issue #1657) (annando) + Improve OEmbed (issue #1640) (annando) + Fix double html encodig in site administration page for sitename and register text (issue #1628) (annando) + Fix remote subscription from GNU Social (annando) + Fix "{0}" in notifications (issue #1642) (annando) + Fix desktop notification (fabrixxm) + Fix rewrite test in install wizard with self-signed certificate (annando) + Better support for non standard installations of GNU Social (annando) + Fix emoticons alt text (tobias) + Improve threaded display in Vier theme (annando) + Use field templates in photo edit form (fabrixxm) + Alllow deletion of any user but yourself (issue #1625) (fabrixxm) + Install wizard load htconfig template from template/ folder, remove localized htconfig templates (fabrixxm) + Add contact detail to non-js contact drop confirm dialog (issue #1629) (fabrixxm) + Return geo coord in API (annando) + Improve events reminder: use title, show in colorbox, link using event ID (rabuzarus) + Fix spelling in accepted connection notification email (strk) + Show image size warning in a human readable format (rabuzarus) + Move ACL window in template (rabuzarus) + New option "-s" in util/run_xgettext.sh (fabrixxm) + Support, but ignore at the moment, delete message from Quitter (annando) + Remove google maps from core. Functionality moved to addon "googlemap" alongside "openstreetmap" (issue #1705) (annando) + Update to German documentation (Frank Dieckmann, tobias) + Updated translations (translation teams, tobias) + Version 3.4 Optionally, "like" and "dislike" activities don't update thread timestamp (annando) diff --git a/INSTALL.txt b/INSTALL.txt index 294c6c9dd..7726bdb0d 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -64,6 +64,8 @@ you wish to communicate with the Diaspora network. 3. Create an empty database and note the access details (hostname, username, password, database name). + - Friendica needs the permission to create and delete fields and tables in its own database. + 4. If you know in advance that it will be impossible for the web server to write or create files in your web directory, create an empty file called diff --git a/README.translate.md b/README.translate.md index 5575d5176..f43406361 100644 --- a/README.translate.md +++ b/README.translate.md @@ -18,7 +18,7 @@ others can use them. We do not include every translation from transifex in the source tree to avoid a scattered and disturbed overall experience. As an uneducated guess we have a lower limit of 50% translated strings before we include the language (for the -core message.po file, addont translation will be included once all strings of +core message.po file, addon translation will be included once all strings of an addon are translated. This limit is judging only by the amount of translated strings under the assumption that the most prominent strings for the UI will be translated first by a translation team. If you feel your translation useable @@ -47,10 +47,10 @@ view/de/message.po you would do the following. 2. Execute the po2php script, which will place the translation in the strings.php file that is used by friendica. - $> php util/po2php.php view/de/message.po + $> php util/po2php.php view/de/messages.po The output of the script will be placed at view/de/strings.php where - froemdoca os expecting it, so you can test your translation mmediately. + friendica is expecting it, so you can test your translation immediately. 3. Visit your friendica page to check if it still works in the language you just translated. If not try to find the error, most likely PHP will give @@ -71,7 +71,7 @@ Utilities Additional to the po2php script there are some more utilities for translation in the "util" directory of the friendica source tree. If you only want to -translate friendica into another language you wont need any of these tools most +translate friendica into another language you won't need any of these tools most likely but it gives you an idea how the translation process of friendica works. diff --git a/boot.php b/boot.php index 6da885117..dcf6c65b1 100644 --- a/boot.php +++ b/boot.php @@ -10,15 +10,16 @@ require_once('include/nav.php'); require_once('include/cache.php'); require_once('library/Mobile_Detect/Mobile_Detect.php'); require_once('include/features.php'); +require_once('include/identity.php'); require_once('update.php'); require_once('include/dbstructure.php'); define ( 'FRIENDICA_PLATFORM', 'Friendica'); define ( 'FRIENDICA_CODENAME', 'Lily of the valley'); -define ( 'FRIENDICA_VERSION', '3.4.0' ); +define ( 'FRIENDICA_VERSION', '3.4.2' ); define ( 'DFRN_PROTOCOL_VERSION', '2.23' ); -define ( 'DB_UPDATE_VERSION', 1182 ); +define ( 'DB_UPDATE_VERSION', 1188 ); define ( 'EOL', "
\r\n" ); define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' ); @@ -83,6 +84,15 @@ define ( 'LOGGER_DEBUG', 2 ); define ( 'LOGGER_DATA', 3 ); define ( 'LOGGER_ALL', 4 ); +/** + * cache levels + */ + +define ( 'CACHE_MONTH', 0 ); +define ( 'CACHE_WEEK', 1 ); +define ( 'CACHE_DAY', 2 ); +define ( 'CACHE_HOUR', 3 ); + /** * registration policies */ @@ -273,6 +283,7 @@ define ( 'ACTIVITY_POST', NAMESPACE_ACTIVITY_SCHEMA . 'post' ); define ( 'ACTIVITY_UPDATE', NAMESPACE_ACTIVITY_SCHEMA . 'update' ); define ( 'ACTIVITY_TAG', NAMESPACE_ACTIVITY_SCHEMA . 'tag' ); define ( 'ACTIVITY_FAVORITE', NAMESPACE_ACTIVITY_SCHEMA . 'favorite' ); +define ( 'ACTIVITY_SHARE', NAMESPACE_ACTIVITY_SCHEMA . 'share' ); define ( 'ACTIVITY_POKE', NAMESPACE_ZOT . '/activity/poke' ); define ( 'ACTIVITY_MOOD', NAMESPACE_ZOT . '/activity/mood' ); @@ -290,6 +301,7 @@ define ( 'ACTIVITY_OBJ_EVENT', NAMESPACE_ACTIVITY_SCHEMA . 'event' ); define ( 'ACTIVITY_OBJ_GROUP', NAMESPACE_ACTIVITY_SCHEMA . 'group' ); define ( 'ACTIVITY_OBJ_TAGTERM', NAMESPACE_DFRN . '/tagterm' ); define ( 'ACTIVITY_OBJ_PROFILE', NAMESPACE_DFRN . '/profile' ); +define ( 'ACTIVITY_OBJ_QUESTION', 'http://activityschema.org/object/question' ); /** * item weight for query ordering @@ -356,6 +368,7 @@ if(! class_exists('App')) { public $config; public $page; public $profile; + public $profile_uid; public $user; public $cid; public $contact; @@ -379,6 +392,7 @@ if(! class_exists('App')) { public $identities; public $is_mobile; public $is_tablet; + public $is_friendica_app; public $performance = array(); public $nav_sel; @@ -414,9 +428,6 @@ if(! class_exists('App')) { // array of instanced template engines ('name'=>'instance') public $template_engine_instance = array(); - // Used for reducing load to the ostatus completion - public $last_ostatus_conversation_url; - private $ldelim = array( 'internal' => '', 'smarty3' => '{{' @@ -534,14 +545,14 @@ if(! class_exists('App')) { $this->cmd = trim($_GET['q'],'/\\'); - // fix query_string - $this->query_string = str_replace($this->cmd."&",$this->cmd."?", $this->query_string); + // fix query_string + $this->query_string = str_replace($this->cmd."&",$this->cmd."?", $this->query_string); // unix style "homedir" if(substr($this->cmd,0,1) === '~') - $this->cmd = 'profile/' . substr($this->cmd,1); + $this->cmd = 'profile/' . substr($this->cmd,1); // Diaspora style profile url @@ -596,6 +607,9 @@ if(! class_exists('App')) { $this->is_mobile = $mobile_detect->isMobile(); $this->is_tablet = $mobile_detect->isTablet(); + // Friendica-Client + $this->is_friendica_app = ($_SERVER['HTTP_USER_AGENT'] == "Apache-HttpClient/UNAVAILABLE (java 1.4)"); + /** * register template engines */ @@ -824,10 +838,10 @@ if(! class_exists('App')) { $v = get_class_vars( $class ); if(x($v,"name")) $name = $v['name']; } - if ($name===""){ - echo "template engine $class cannot be registered without a name.\n"; + if ($name===""){ + echo "template engine $class cannot be registered without a name.\n"; killme(); - } + } $this->template_engines[$name] = $class; } @@ -906,6 +920,10 @@ if(! class_exists('App')) { return(FRIENDICA_PLATFORM." '".FRIENDICA_CODENAME."' ".FRIENDICA_VERSION."-".DB_UPDATE_VERSION."; ".$this->get_baseurl()); } + function is_friendica_app() { + return($this->is_friendica_app); + } + } } @@ -1369,530 +1387,6 @@ if(! function_exists('get_max_import_size')) { } } - - -/** - * - * Function : profile_load - * @parameter App $a - * @parameter string $nickname - * @parameter int $profile - * - * Summary: Loads a profile into the page sidebar. - * The function requires a writeable copy of the main App structure, and the nickname - * of a registered local account. - * - * If the viewer is an authenticated remote viewer, the profile displayed is the - * one that has been configured for his/her viewing in the Contact manager. - * Passing a non-zero profile ID can also allow a preview of a selected profile - * by the owner. - * - * Profile information is placed in the App structure for later retrieval. - * Honours the owner's chosen theme for display. - * - * IMPORTANT: Should only be run in the _init() functions of a module. That ensures that - * the theme is chosen before the _init() function of a theme is run, which will usually - * load a lot of theme-specific content - * - */ - -if(! function_exists('profile_load')) { - function profile_load(&$a, $nickname, $profile = 0, $profiledata = array()) { - - $user = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1", - dbesc($nickname) - ); - - if(!$user && count($user) && !count($profiledata)) { - logger('profile error: ' . $a->query_string, LOGGER_DEBUG); - notice( t('Requested account is not available.') . EOL ); - $a->error = 404; - return; - } - - if(remote_user() && count($_SESSION['remote'])) { - foreach($_SESSION['remote'] as $visitor) { - if($visitor['uid'] == $user[0]['uid']) { - $r = q("SELECT `profile-id` FROM `contact` WHERE `id` = %d LIMIT 1", - intval($visitor['cid']) - ); - if(count($r)) - $profile = $r[0]['profile-id']; - break; - } - } - } - - $r = null; - - if($profile) { - $profile_int = intval($profile); - $r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `contact`.`avatar-date` AS picdate, `user`.* FROM `profile` - INNER JOIN `contact` on `contact`.`uid` = `profile`.`uid` INNER JOIN `user` ON `profile`.`uid` = `user`.`uid` - WHERE `user`.`nickname` = '%s' AND `profile`.`id` = %d and `contact`.`self` = 1 LIMIT 1", - dbesc($nickname), - intval($profile_int) - ); - } - if((!$r) && (!count($r))) { - $r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `contact`.`avatar-date` AS picdate, `user`.* FROM `profile` - INNER JOIN `contact` on `contact`.`uid` = `profile`.`uid` INNER JOIN `user` ON `profile`.`uid` = `user`.`uid` - WHERE `user`.`nickname` = '%s' AND `profile`.`is-default` = 1 and `contact`.`self` = 1 LIMIT 1", - dbesc($nickname) - ); - } - - if(($r === false) || (!count($r)) && !count($profiledata)) { - logger('profile error: ' . $a->query_string, LOGGER_DEBUG); - notice( t('Requested profile is not available.') . EOL ); - $a->error = 404; - return; - } - - // fetch user tags if this isn't the default profile - - if(!$r[0]['is-default']) { - $x = q("select `pub_keywords` from `profile` where uid = %d and `is-default` = 1 limit 1", - intval($r[0]['profile_uid']) - ); - if($x && count($x)) - $r[0]['pub_keywords'] = $x[0]['pub_keywords']; - } - - $a->profile = $r[0]; - - $a->profile['mobile-theme'] = get_pconfig($a->profile['profile_uid'], 'system', 'mobile_theme'); - $a->profile['network'] = NETWORK_DFRN; - - $a->page['title'] = $a->profile['name'] . " @ " . $a->config['sitename']; - - if (!$profiledata) - $_SESSION['theme'] = $a->profile['theme']; - - $_SESSION['mobile-theme'] = $a->profile['mobile-theme']; - - /** - * load/reload current theme info - */ - - $a->set_template_engine(); // reset the template engine to the default in case the user's theme doesn't specify one - - $theme_info_file = "view/theme/".current_theme()."/theme.php"; - if (file_exists($theme_info_file)){ - require_once($theme_info_file); - } - - if(! (x($a->page,'aside'))) - $a->page['aside'] = ''; - - if(local_user() && local_user() == $a->profile['uid'] && $profiledata) { - $a->page['aside'] .= replace_macros(get_markup_template('profile_edlink.tpl'),array( - '$editprofile' => t('Edit profile'), - '$profid' => $a->profile['id'] - )); - } - - $block = (((get_config('system','block_public')) && (! local_user()) && (! remote_user())) ? true : false); - - // To-Do: - // By now, the contact block isn't shown, when a different profile is given - // But: When this profile was on the same server, then we could display the contacts - if ($profiledata) - $a->page['aside'] .= profile_sidebar($profiledata, true); - else - $a->page['aside'] .= profile_sidebar($a->profile, $block); - - - /*if(! $block) - $a->page['aside'] .= contact_block();*/ - - return; - } -} - - -/** - * - * Function: profile_sidebar - * - * Formats a profile for display in the sidebar. - * It is very difficult to templatise the HTML completely - * because of all the conditional logic. - * - * @parameter: array $profile - * - * Returns HTML string stuitable for sidebar inclusion - * Exceptions: Returns empty string if passed $profile is wrong type or not populated - * - */ - - -if(! function_exists('profile_sidebar')) { - function profile_sidebar($profile, $block = 0) { - $a = get_app(); - - $o = ''; - $location = false; - $address = false; - $pdesc = true; - - if((! is_array($profile)) && (! count($profile))) - return $o; - - $profile['picdate'] = urlencode($profile['picdate']); - - if (($profile['network'] != "") AND ($profile['network'] != NETWORK_DFRN)) { - require_once('include/contact_selectors.php'); - if ($profile['url'] != "") - $profile['network_name'] = ''.network_to_name($profile['network']).""; - else - $profile['network_name'] = network_to_name($profile['network']); - } else - $profile['network_name'] = ""; - - call_hooks('profile_sidebar_enter', $profile); - - - // don't show connect link to yourself - $connect = (($profile['uid'] != local_user()) ? t('Connect') : False); - - // don't show connect link to authenticated visitors either - if(remote_user() && count($_SESSION['remote'])) { - foreach($_SESSION['remote'] as $visitor) { - if($visitor['uid'] == $profile['uid']) { - $connect = false; - break; - } - } - } - - // Is the local user already connected to that user? - if ($connect AND local_user()) { - if (isset($profile["url"])) - $profile_url = normalise_link($profile["url"]); - else - $profile_url = normalise_link($a->get_baseurl()."/profile/".$profile["nickname"]); - - $r = q("SELECT * FROM `contact` WHERE NOT `pending` AND `uid` = %d AND `nurl` = '%s'", - local_user(), $profile_url); - if (count($r)) - $connect = false; - } - - if ($connect AND ($profile['network'] != NETWORK_DFRN) AND !isset($profile['remoteconnect'])) - $connect = false; - - if (isset($profile['remoteconnect'])) - $remoteconnect = $profile['remoteconnect']; - - if( get_my_url() && $profile['unkmail'] && ($profile['uid'] != local_user()) ) - $wallmessage = t('Message'); - else - $wallmessage = false; - - // show edit profile to yourself - if ($profile['uid'] == local_user() && feature_enabled(local_user(),'multi_profiles')) { - $profile['edit'] = array($a->get_baseurl(). '/profiles', t('Profiles'),"", t('Manage/edit profiles')); - $r = q("SELECT * FROM `profile` WHERE `uid` = %d", - local_user()); - - $profile['menu'] = array( - 'chg_photo' => t('Change profile photo'), - 'cr_new' => t('Create New Profile'), - 'entries' => array(), - ); - - if(count($r)) { - - foreach($r as $rr) { - $profile['menu']['entries'][] = array( - 'photo' => $rr['thumb'], - 'id' => $rr['id'], - 'alt' => t('Profile Image'), - 'profile_name' => $rr['profile-name'], - 'isdefault' => $rr['is-default'], - 'visibile_to_everybody' => t('visible to everybody'), - 'edit_visibility' => t('Edit visibility'), - - ); - } - - - } - } - if ($profile['uid'] == local_user() && !feature_enabled(local_user(),'multi_profiles')) { - $profile['edit'] = array($a->get_baseurl(). '/profiles/'.$profile['id'], t('Edit profile'),"", t('Edit profile')); - $profile['menu'] = array( - 'chg_photo' => t('Change profile photo'), - 'cr_new' => null, - 'entries' => array(), - ); - } - - 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); - - if(($profile['hidewall'] || $block) && (! local_user()) && (! remote_user())) { - $location = $pdesc = $gender = $marital = $homepage = $about = False; - } - - $firstname = ((strpos($profile['name'],' ')) - ? trim(substr($profile['name'],0,strpos($profile['name'],' '))) : $profile['name']); - $lastname = (($firstname === $profile['name']) ? '' : trim(substr($profile['name'],strlen($firstname)))); - - $diaspora = array( - 'podloc' => $a->get_baseurl(), - 'searchable' => (($profile['publish'] && $profile['net-publish']) ? 'true' : 'false' ), - 'nickname' => $profile['nickname'], - 'fullname' => $profile['name'], - 'firstname' => $firstname, - 'lastname' => $lastname, - 'photo300' => $a->get_cached_avatar_image($a->get_baseurl() . '/photo/custom/300/' . $profile['uid'] . '.jpg'), - 'photo100' => $a->get_cached_avatar_image($a->get_baseurl() . '/photo/custom/100/' . $profile['uid'] . '.jpg'), - 'photo50' => $a->get_cached_avatar_image($a->get_baseurl() . '/photo/custom/50/' . $profile['uid'] . '.jpg'), - ); - - if (!$block){ - $contact_block = contact_block(); - - if(is_array($a->profile) AND !$a->profile['hide-friends']) { - $r = q("SELECT `gcontact`.`updated` FROM `contact` INNER JOIN `gcontact` WHERE `gcontact`.`nurl` = `contact`.`nurl` AND `self` AND `uid` = %d LIMIT 1", - intval($a->profile['uid'])); - if(count($r)) - $updated = date("c", strtotime($r[0]['updated'])); - - $r = q("SELECT COUNT(*) AS `total` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0 AND `hidden` = 0 AND `archive` = 0 - AND `network` IN ('%s', '%s', '%s', '')", - intval($profile['uid']), - dbesc(NETWORK_DFRN), - dbesc(NETWORK_DIASPORA), - dbesc(NETWORK_OSTATUS) - ); - if(count($r)) - $contacts = intval($r[0]['total']); - } - } - - $p = array(); - foreach($profile as $k => $v) { - $k = str_replace('-','_',$k); - $p[$k] = $v; - } - - if($a->theme['template_engine'] === 'internal') - $location = template_escape($location); - - - $tpl = get_markup_template('profile_vcard.tpl'); - $o .= replace_macros($tpl, array( - '$profile' => $p, - '$connect' => $connect, - '$remoteconnect' => $remoteconnect, - '$wallmessage' => $wallmessage, - '$location' => $location, - '$gender' => $gender, - '$pdesc' => $pdesc, - '$marital' => $marital, - '$homepage' => $homepage, - '$about' => $about, - '$network' => t('Network:'), - '$contacts' => $contacts, - '$updated' => $updated, - '$diaspora' => $diaspora, - '$contact_block' => $contact_block, - )); - - - $arr = array('profile' => &$profile, 'entry' => &$o); - - call_hooks('profile_sidebar', $arr); - - return $o; - } -} - - -if(! function_exists('get_birthdays')) { - function get_birthdays() { - - $a = get_app(); - $o = ''; - - if(! local_user() || $a->is_mobile || $a->is_tablet) - return $o; - -// $mobile_detect = new Mobile_Detect(); -// $is_mobile = $mobile_detect->isMobile() || $mobile_detect->isTablet(); - -// if($is_mobile) -// return $o; - - $bd_format = t('g A l F d') ; // 8 AM Friday January 18 - $bd_short = t('F d'); - - $r = q("SELECT `event`.*, `event`.`id` AS `eid`, `contact`.* FROM `event` - INNER JOIN `contact` ON `contact`.`id` = `event`.`cid` - WHERE `event`.`uid` = %d AND `type` = 'birthday' AND `start` < '%s' AND `finish` > '%s' - ORDER BY `start` ASC ", - intval(local_user()), - dbesc(datetime_convert('UTC','UTC','now + 6 days')), - dbesc(datetime_convert('UTC','UTC','now')) - ); - - if($r && count($r)) { - $total = 0; - $now = strtotime('now'); - $cids = array(); - - $istoday = false; - foreach($r as $rr) { - if(strlen($rr['name'])) - $total ++; - if((strtotime($rr['start'] . ' +00:00') < $now) && (strtotime($rr['finish'] . ' +00:00') > $now)) - $istoday = true; - } - $classtoday = $istoday ? ' birthday-today ' : ''; - if($total) { - foreach($r as &$rr) { - if(! strlen($rr['name'])) - continue; - - // avoid duplicates - - if(in_array($rr['cid'],$cids)) - continue; - $cids[] = $rr['cid']; - - $today = (((strtotime($rr['start'] . ' +00:00') < $now) && (strtotime($rr['finish'] . ' +00:00') > $now)) ? true : false); - $sparkle = ''; - $url = $rr['url']; - if($rr['network'] === NETWORK_DFRN) { - $sparkle = " sparkle"; - $url = $a->get_baseurl() . '/redir/' . $rr['cid']; - } - - $rr['link'] = $url; - $rr['title'] = $rr['name']; - $rr['date'] = day_translate(datetime_convert('UTC', $a->timezone, $rr['start'], $rr['adjust'] ? $bd_format : $bd_short)) . (($today) ? ' ' . t('[today]') : ''); - $rr['startime'] = Null; - $rr['today'] = $today; - - } - } - } - $tpl = get_markup_template("birthdays_reminder.tpl"); - return replace_macros($tpl, array( - '$baseurl' => $a->get_baseurl(), - '$classtoday' => $classtoday, - '$count' => $total, - '$event_reminders' => t('Birthday Reminders'), - '$event_title' => t('Birthdays this week:'), - '$events' => $r, - '$lbr' => '{', // raw brackets mess up if/endif macro processing - '$rbr' => '}' - - )); - } -} - - -if(! function_exists('get_events')) { - function get_events() { - - require_once('include/bbcode.php'); - - $a = get_app(); - - if(! local_user() || $a->is_mobile || $a->is_tablet) - return $o; - - -// $mobile_detect = new Mobile_Detect(); -// $is_mobile = $mobile_detect->isMobile() || $mobile_detect->isTablet(); - -// if($is_mobile) -// return $o; - - $bd_format = t('g A l F d') ; // 8 AM Friday January 18 - $bd_short = t('F d'); - - $r = q("SELECT `event`.* FROM `event` - WHERE `event`.`uid` = %d AND `type` != 'birthday' AND `start` < '%s' AND `start` >= '%s' - ORDER BY `start` ASC ", - intval(local_user()), - dbesc(datetime_convert('UTC','UTC','now + 7 days')), - dbesc(datetime_convert('UTC','UTC','now - 1 days')) - ); - - if($r && count($r)) { - $now = strtotime('now'); - $istoday = false; - foreach($r as $rr) { - if(strlen($rr['name'])) - $total ++; - - $strt = datetime_convert('UTC',$rr['convert'] ? $a->timezone : 'UTC',$rr['start'],'Y-m-d'); - if($strt === datetime_convert('UTC',$a->timezone,'now','Y-m-d')) - $istoday = true; - } - $classtoday = (($istoday) ? 'event-today' : ''); - - $skip = 0; - - foreach($r as &$rr) { - if($rr['adjust']) - $md = datetime_convert('UTC',$a->timezone,$rr['start'],'Y/m'); - else - $md = datetime_convert('UTC','UTC',$rr['start'],'Y/m'); - $md .= "/#link-".$rr['id']; - - $title = substr(strip_tags(bbcode($rr['desc'])),0,32) . '... '; - if(! $title) - $title = t('[No description]'); - - $strt = datetime_convert('UTC',$rr['convert'] ? $a->timezone : 'UTC',$rr['start']); - - if(substr($strt,0,10) < datetime_convert('UTC',$a->timezone,'now','Y-m-d')) { - $skip++; - continue; - } - - $today = ((substr($strt,0,10) === datetime_convert('UTC',$a->timezone,'now','Y-m-d')) ? true : false); - - $rr['link'] = $md; - $rr['title'] = $title; - $rr['date'] = day_translate(datetime_convert('UTC', $rr['adjust'] ? $a->timezone : 'UTC', $rr['start'], $bd_format)) . (($today) ? ' ' . t('[today]') : ''); - $rr['startime'] = $strt; - $rr['today'] = $today; - } - } - - $tpl = get_markup_template("events_reminder.tpl"); - return replace_macros($tpl, array( - '$baseurl' => $a->get_baseurl(), - '$classtoday' => $classtoday, - '$count' => count($r) - $skip, - '$event_reminders' => t('Event Reminders'), - '$event_title' => t('Events this week:'), - '$events' => $r, - )); - } -} - - /** * * Wrap calls to proc_close(proc_open()) and call hook @@ -1941,10 +1435,10 @@ if(! function_exists('proc_run')) { if(count($args) && $args[0] === 'php') $args[0] = ((x($a->config,'php_path')) && (strlen($a->config['php_path'])) ? $a->config['php_path'] : 'php'); - // add baseurl to args. cli scripts can't construct it - $args[] = $a->get_baseurl(); + // add baseurl to args. cli scripts can't construct it + $args[] = $a->get_baseurl(); - for($x = 0; $x < count($args); $x ++) + for($x = 0; $x < count($args); $x ++) $args[$x] = escapeshellarg($args[$x]); @@ -1963,29 +1457,58 @@ if(! function_exists('current_theme')) { $a = get_app(); + $page_theme = null; + + // Find the theme that belongs to the user whose stuff we are looking at + + if($a->profile_uid && ($a->profile_uid != local_user())) { + $r = q("select theme from user where uid = %d limit 1", + intval($a->profile_uid) + ); + if($r) + $page_theme = $r[0]['theme']; + } + + // Allow folks to over-rule user themes and always use their own on their own site. + // This works only if the user is on the same server + + if($page_theme && local_user() && (local_user() != $a->profile_uid)) { + if(get_pconfig(local_user(),'system','always_my_theme')) + $page_theme = null; + } + // $mobile_detect = new Mobile_Detect(); // $is_mobile = $mobile_detect->isMobile() || $mobile_detect->isTablet(); $is_mobile = $a->is_mobile || $a->is_tablet; + $standard_system_theme = ((isset($a->config['system']['theme'])) ? $a->config['system']['theme'] : ''); + $standard_theme_name = ((isset($_SESSION) && x($_SESSION,'theme')) ? $_SESSION['theme'] : $standard_system_theme); + if($is_mobile) { if(isset($_SESSION['show-mobile']) && !$_SESSION['show-mobile']) { - $system_theme = ''; - $theme_name = ''; + $system_theme = $standard_system_theme; + $theme_name = $standard_theme_name; } else { - $system_theme = ((isset($a->config['system']['mobile-theme'])) ? $a->config['system']['mobile-theme'] : ''); + $system_theme = ((isset($a->config['system']['mobile-theme'])) ? $a->config['system']['mobile-theme'] : $standard_system_theme); $theme_name = ((isset($_SESSION) && x($_SESSION,'mobile-theme')) ? $_SESSION['mobile-theme'] : $system_theme); if($theme_name === '---') { // user has selected to have the mobile theme be the same as the normal one - $system_theme = ''; - $theme_name = ''; + $system_theme = $standard_system_theme; + $theme_name = $standard_theme_name; + + if($page_theme) + $theme_name = $page_theme; } } } - if(!$is_mobile || ($system_theme === '' && $theme_name === '')) { - $system_theme = ((isset($a->config['system']['theme'])) ? $a->config['system']['theme'] : ''); - $theme_name = ((isset($_SESSION) && x($_SESSION,'theme')) ? $_SESSION['theme'] : $system_theme); + else { + $system_theme = $standard_system_theme; + $theme_name = $standard_theme_name; + + if($page_theme) + $theme_name = $page_theme; } if($theme_name && @@ -2013,9 +1536,13 @@ if(! function_exists('current_theme')) { if(! function_exists('current_theme_url')) { function current_theme_url() { global $a; + $t = current_theme(); + + $opts = (($a->profile_uid) ? '?f=&puid=' . $a->profile_uid : ''); if (file_exists('view/theme/' . $t . '/style.php')) - return($a->get_baseurl() . '/view/theme/' . $t . '/style.pcss'); + return($a->get_baseurl() . '/view/theme/' . $t . '/style.pcss' . $opts); + return($a->get_baseurl() . '/view/theme/' . $t . '/style.css'); } } @@ -2101,127 +1628,14 @@ if(! function_exists('load_contact_links')) { $url = normalise_link($rr['url']); $ret[$url] = $rr; } - } - else + } else $ret['empty'] = true; + $a->contacts = $ret; return; } } -if(! function_exists('profile_tabs')){ - function profile_tabs($a, $is_owner=False, $nickname=Null){ - //echo "
"; var_dump($a->user); killme();
-
-		if (is_null($nickname))
-			$nickname  = $a->user['nickname'];
-
-		if(x($_GET,'tab'))
-			$tab = notags(trim($_GET['tab']));
-
-		$url = $a->get_baseurl() . '/profile/' . $nickname;
-
-		$tabs = array(
-			array(
-				'label'=>t('Status'),
-				'url' => $url,
-				'sel' => ((!isset($tab)&&$a->argv[0]=='profile')?'active':''),
-				'title' => t('Status Messages and Posts'),
-				'id' => 'status-tab',
-			),
-			array(
-				'label' => t('Profile'),
-				'url' 	=> $url.'/?tab=profile',
-				'sel'	=> ((isset($tab) && $tab=='profile')?'active':''),
-				'title' => t('Profile Details'),
-				'id' => 'profile-tab',
-			),
-			array(
-				'label' => t('Photos'),
-				'url'	=> $a->get_baseurl() . '/photos/' . $nickname,
-				'sel'	=> ((!isset($tab)&&$a->argv[0]=='photos')?'active':''),
-				'title' => t('Photo Albums'),
-				'id' => 'photo-tab',
-			),
-			array(
-				'label' => t('Videos'),
-				'url'	=> $a->get_baseurl() . '/videos/' . $nickname,
-				'sel'	=> ((!isset($tab)&&$a->argv[0]=='videos')?'active':''),
-				'title' => t('Videos'),
-				'id' => 'video-tab',
-			),
-		);
-
-		if ($is_owner){
-			$tabs[] = array(
-				'label' => t('Events'),
-				'url'	=> $a->get_baseurl() . '/events',
-				'sel' 	=>((!isset($tab)&&$a->argv[0]=='events')?'active':''),
-				'title' => t('Events and Calendar'),
-				'id' => 'events-tab',
-			);
-			$tabs[] = array(
-				'label' => t('Personal Notes'),
-				'url'	=> $a->get_baseurl() . '/notes',
-				'sel' 	=>((!isset($tab)&&$a->argv[0]=='notes')?'active':''),
-				'title' => t('Only You Can See This'),
-				'id' => 'notes-tab',
-			);
-		}
-
-
-		$arr = array('is_owner' => $is_owner, 'nickname' => $nickname, 'tab' => (($tab) ? $tab : false), 'tabs' => $tabs);
-		call_hooks('profile_tabs', $arr);
-
-		$tpl = get_markup_template('common_tabs.tpl');
-
-		return replace_macros($tpl,array('$tabs' => $arr['tabs']));
-	}
-}
-
-function get_my_url() {
-	if(x($_SESSION,'my_url'))
-		return $_SESSION['my_url'];
-	return false;
-}
-
-function zrl_init(&$a) {
-	$tmp_str = get_my_url();
-	if(validate_url($tmp_str)) {
-
-		// Is it a DDoS attempt?
-		// The check fetches the cached value from gprobe to reduce the load for this system
-		$urlparts = parse_url($tmp_str);
-
-		$result = Cache::get("gprobe:".$urlparts["host"]);
-		if (!is_null($result)) {
-			$result = unserialize($result);
-			if ($result["network"] == NETWORK_FEED) {
-				logger("DDoS attempt detected for ".$urlparts["host"]." by ".$_SERVER["REMOTE_ADDR"].". server data: ".print_r($_SERVER, true), LOGGER_DEBUG);
-				return;
-			}
-		}
-
-		proc_run('php','include/gprobe.php',bin2hex($tmp_str));
-		$arr = array('zrl' => $tmp_str, 'url' => $a->cmd);
-		call_hooks('zrl_init',$arr);
-	}
-}
-
-function zrl($s,$force = false) {
-	if(! strlen($s))
-		return $s;
-	if((! strpos($s,'/profile/')) && (! $force))
-		return $s;
-	if($force && substr($s,-1,1) !== '/')
-		$s = $s . '/';
-	$achar = strpos($s,'?') ? '&' : '?';
-	$mine = get_my_url();
-	if($mine and ! link_compare($mine,$s))
-		return $s . $achar . 'zrl=' . urlencode($mine);
-	return $s;
-}
-
 /**
 * returns querystring as string from a mapped array
 *
@@ -2229,24 +1643,24 @@ function zrl($s,$force = false) {
 * @return string
 */
 function build_querystring($params, $name=null) {
-    $ret = "";
-    foreach($params as $key=>$val) {
-        if(is_array($val)) {
-            if($name==null) {
-                $ret .= build_querystring($val, $key);
-            } else {
-                $ret .= build_querystring($val, $name."[$key]");
-            }
-        } else {
-            $val = urlencode($val);
-            if($name!=null) {
-                $ret.=$name."[$key]"."=$val&";
-            } else {
-                $ret.= "$key=$val&";
-            }
-        }
-    }
-    return $ret;
+	$ret = "";
+	foreach($params as $key=>$val) {
+		if(is_array($val)) {
+			if($name==null) {
+				$ret .= build_querystring($val, $key);
+			} else {
+				$ret .= build_querystring($val, $name."[$key]");
+			}
+		} else {
+			$val = urlencode($val);
+			if($name!=null) {
+				$ret.=$name."[$key]"."=$val&";
+			} else {
+				$ret.= "$key=$val&";
+			}
+		}
+	}
+	return $ret;
 }
 
 function explode_querystring($query) {
@@ -2254,8 +1668,7 @@ function explode_querystring($query) {
 	if($arg_st !== false) {
 		$base = substr($query, 0, $arg_st);
 		$arg_st += 1;
-	}
-	else {
+	} else {
 		$base = '';
 		$arg_st = 0;
 	}
@@ -2304,6 +1717,15 @@ function random_digits($digits) {
 	return $rn;
 }
 
+function get_server() {
+	$server = get_config("system", "directory");
+
+	if ($server == "")
+		$server = "http://dir.friendi.ca";
+
+	return($server);
+}
+
 function get_cachefile($file, $writemode = true) {
 	$cache = get_itemcachepath();
 
@@ -2341,16 +1763,16 @@ function clear_cache($basepath = "", $path = "") {
 		$cachetime = 86400;
 
 	if (is_writable($path)){
-	if ($dh = opendir($path)) {
-		while (($file = readdir($dh)) !== false) {
-			$fullpath = $path."/".$file;
-			if ((filetype($fullpath) == "dir") and ($file != ".") and ($file != ".."))
-				clear_cache($basepath, $fullpath);
-			if ((filetype($fullpath) == "file") and (filectime($fullpath) < (time() - $cachetime)))
-				unlink($fullpath);
+		if ($dh = opendir($path)) {
+			while (($file = readdir($dh)) !== false) {
+				$fullpath = $path."/".$file;
+				if ((filetype($fullpath) == "dir") and ($file != ".") and ($file != ".."))
+					clear_cache($basepath, $fullpath);
+				if ((filetype($fullpath) == "file") and (filectime($fullpath) < (time() - $cachetime)))
+					unlink($fullpath);
+			}
+			closedir($dh);
 		}
-		closedir($dh);
-	}
 	}
 }
 
@@ -2389,7 +1811,11 @@ function get_lockpath() {
 
 	if ($temppath != "") {
 		$lockpath = $temppath."/lock";
-		mkdir($lockpath);
+
+		if (!is_dir($lockpath))
+			mkdir($lockpath);
+		elseif (!is_writable($lockpath))
+			$lockpath = $temppath;
 
 		if (is_dir($lockpath) AND is_writable($lockpath)) {
 			set_config("system", "lockpath", $lockpath);
@@ -2400,14 +1826,22 @@ function get_lockpath() {
 }
 
 function get_temppath() {
+	$a = get_app();
+
 	$temppath = get_config("system","temppath");
 	if (($temppath != "") AND is_dir($temppath) AND is_writable($temppath))
 		return($temppath);
 
 	$temppath = sys_get_temp_dir();
 	if (($temppath != "") AND is_dir($temppath) AND is_writable($temppath)) {
-		set_config("system", "temppath", $temppath);
-		return($temppath);
+		$temppath .= "/".$a->get_hostname();
+		if (!is_dir($temppath))
+			mkdir($temppath);
+
+		if (is_dir($temppath) AND is_writable($temppath)) {
+			set_config("system", "temppath", $temppath);
+			return($temppath);
+		}
 	}
 
 	return("");
@@ -2421,8 +1855,8 @@ function set_template_engine(&$a, $engine = 'internal') {
 }
 
 if(!function_exists('exif_imagetype')) {
-        function exif_imagetype($file) {
-                $size = getimagesize($file);
-                return($size[2]);
-        }
+	function exif_imagetype($file) {
+		$size = getimagesize($file);
+		return($size[2]);
+	}
 }
diff --git a/database.sql b/database.sql
index 8babeb4d4..76df6aec1 100644
--- a/database.sql
+++ b/database.sql
@@ -1,6 +1,6 @@
 -- ------------------------------------------
--- Friendica 3.4.0 (Ginger)
--- DB_UPDATE_VERSION 1182
+-- Friendica 3.4.1 (Lily of the valley)
+-- DB_UPDATE_VERSION 1188
 -- ------------------------------------------
 
 
@@ -53,6 +53,7 @@ CREATE TABLE IF NOT EXISTS `auth_codes` (
 CREATE TABLE IF NOT EXISTS `cache` (
 	`k` varchar(255) NOT NULL PRIMARY KEY,
 	`v` text NOT NULL,
+	`expire_mode` int(11) NOT NULL DEFAULT 0,
 	`updated` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
 	 INDEX `updated` (`updated`)
 ) DEFAULT CHARSET=utf8;
@@ -136,10 +137,12 @@ CREATE TABLE IF NOT EXISTS `contact` (
 	`hub-verify` varchar(255) NOT NULL DEFAULT '',
 	`last-update` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
 	`success_update` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+	`failure_update` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
 	`name-date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
 	`uri-date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
 	`avatar-date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
 	`term-date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+	`last-item` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
 	`priority` tinyint(3) NOT NULL DEFAULT 0,
 	`blocked` tinyint(1) NOT NULL DEFAULT 1,
 	`readonly` tinyint(1) NOT NULL DEFAULT 0,
@@ -299,18 +302,25 @@ CREATE TABLE IF NOT EXISTS `gcign` (
 CREATE TABLE IF NOT EXISTS `gcontact` (
 	`id` int(10) unsigned NOT NULL auto_increment PRIMARY KEY,
 	`name` varchar(255) NOT NULL DEFAULT '',
+	`nick` varchar(255) NOT NULL DEFAULT '',
 	`url` varchar(255) NOT NULL DEFAULT '',
 	`nurl` varchar(255) NOT NULL DEFAULT '',
 	`photo` varchar(255) NOT NULL DEFAULT '',
 	`connect` varchar(255) NOT NULL DEFAULT '',
+	`created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
 	`updated` datetime DEFAULT '0000-00-00 00:00:00',
+	`last_contact` datetime DEFAULT '0000-00-00 00:00:00',
+	`last_failure` datetime DEFAULT '0000-00-00 00:00:00',
 	`location` varchar(255) NOT NULL DEFAULT '',
 	`about` text NOT NULL,
 	`keywords` text NOT NULL,
 	`gender` varchar(32) NOT NULL DEFAULT '',
+	`community` tinyint(1) NOT NULL DEFAULT 0,
 	`network` varchar(255) NOT NULL DEFAULT '',
 	`generation` tinyint(3) NOT NULL DEFAULT 0,
-	 INDEX `nurl` (`nurl`)
+	`server_url` varchar(255) NOT NULL DEFAULT '',
+	 INDEX `nurl` (`nurl`),
+	 INDEX `updated` (`updated`)
 ) DEFAULT CHARSET=utf8;
 
 --
@@ -351,13 +361,40 @@ CREATE TABLE IF NOT EXISTS `group_member` (
 	 INDEX `uid_gid_contactid` (`uid`,`gid`,`contact-id`)
 ) DEFAULT CHARSET=utf8;
 
+--
+-- TABLE gserver
+--
+CREATE TABLE IF NOT EXISTS `gserver` (
+	`id` int(10) unsigned NOT NULL auto_increment PRIMARY KEY,
+	`url` varchar(255) NOT NULL DEFAULT '',
+	`nurl` varchar(255) NOT NULL DEFAULT '',
+	`version` varchar(255) NOT NULL DEFAULT '',
+	`site_name` varchar(255) NOT NULL DEFAULT '',
+	`info` text NOT NULL,
+	`register_policy` tinyint(1) NOT NULL DEFAULT 0,
+	`poco` varchar(255) NOT NULL DEFAULT '',
+	`noscrape` varchar(255) NOT NULL DEFAULT '',
+	`network` varchar(32) NOT NULL DEFAULT '',
+	`platform` varchar(255) NOT NULL DEFAULT '',
+	`created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+	`last_poco_query` datetime DEFAULT '0000-00-00 00:00:00',
+	`last_contact` datetime DEFAULT '0000-00-00 00:00:00',
+	`last_failure` datetime DEFAULT '0000-00-00 00:00:00',
+	 INDEX `nurl` (`nurl`)
+) DEFAULT CHARSET=utf8;
+
 --
 -- TABLE guid
 --
 CREATE TABLE IF NOT EXISTS `guid` (
 	`id` int(10) unsigned NOT NULL auto_increment PRIMARY KEY,
 	`guid` varchar(255) NOT NULL DEFAULT '',
-	 INDEX `guid` (`guid`)
+	`plink` varchar(255) NOT NULL DEFAULT '',
+	`uri` varchar(255) NOT NULL DEFAULT '',
+	`network` varchar(32) NOT NULL DEFAULT '',
+	 INDEX `guid` (`guid`),
+	 INDEX `plink` (`plink`),
+	 INDEX `uri` (`uri`)
 ) DEFAULT CHARSET=utf8;
 
 --
@@ -586,6 +623,7 @@ CREATE TABLE IF NOT EXISTS `notify` (
 	`msg` mediumtext NOT NULL,
 	`uid` int(11) NOT NULL DEFAULT 0,
 	`link` varchar(255) NOT NULL DEFAULT '',
+	`iid` int(11) NOT NULL DEFAULT 0,
 	`parent` int(11) NOT NULL DEFAULT 0,
 	`seen` tinyint(1) NOT NULL DEFAULT 0,
 	`verb` varchar(255) NOT NULL DEFAULT '',
diff --git a/doc/Accesskeys.md b/doc/Accesskeys.md
new file mode 100644
index 000000000..0c00c2547
--- /dev/null
+++ b/doc/Accesskeys.md
@@ -0,0 +1,70 @@
+Accesskeys in Friendica
+=======================
+
+General
+-------
+* p: profile
+* n: network
+* c: community
+* s: search
+* a: admin
+* f: notifications
+* u: user menu (in themes "vier" and "quattro")
+
+/profile
+--------
+* m: Status Messages and Posts
+* r: Profile Details
+* h: Photo Albums
+* v: Videos
+* e: Events and Calendar
+* t: Personal Notes
+
+/contacts (contact list)
+---------
+* g: Suggestions
+* l: Show all Contacts
+* o: Only show unblocked contacts
+* b: Only show blocked contacts
+* i: Only show ignored contacts
+* y: Only show archived contacts
+* h: Only show hidden contacts
+
+/contacts (single contact view)
+-------------------------------
+* b: Toggle Blocked status
+* i: Toggle Ignored status
+* v: Toggle Archive status
+* r: Repair
+
+/message
+--------
+* m: New message
+
+/network
+--------
+* e: Sort by Comment Date
+* t: Sort by Post Date
+* r: Conversation (Posts that mention or involve you)
+* w: New posts
+* b: Bookmarks
+* m: Favourite Posts
+
+/notifications
+--------------
+* y: System
+* w: Network
+* r: Personal
+* h: Home
+* i: Introductions
+
+/settings
+---------
+* o: Account
+* t: Additional features
+* w: Social Networks
+* l: Plugins
+* d: Delegations
+* b: Connected apps
+* e: Export personal data
+* r: Remove account
diff --git a/doc/Account-Basics.md b/doc/Account-Basics.md
index 91e19b649..ef6a13f22 100644
--- a/doc/Account-Basics.md
+++ b/doc/Account-Basics.md
@@ -3,71 +3,96 @@ Account Basics
 
 * [Home](help)
 
+Registration
+---
 
-**Registration**
+Not all Friendica sites allow open registration.
+If registration is allowed, you will see a "Register" link immediately below the login prompt on the site home page.
+Following this link will take you to the site registration page.
+The strength of our network is that lots of different sites are all completely compatible with each other.
+If the site you're visting doesn't allow registration, or you think you might prefer another one, you can find a [list of public servers here](http://dir.friendica.com/siteinfo), and find one that meets your needs.
 
-Not all Friendica sites allow open registration. If registration is allowed, you will see a "Register" link immediately below the login prompts on the site home page. Following this link will take you to the site registration page.  The strength of our network is that lots of different sites are all completely compatible with each other.  If the site you're visting doesn't allow registration, or you think you might prefer another one, you can find a list of public servers here, and find one that meets your needs.  
+If you'd like to have your own server, you can do that too.
+Visit [the Friendica website](http://friendica.com/download) to download the code with setup instructions.
+It's a very simple installation process that anybody experienced in hosting websites, or with basic Linux experience can handle easily.
 
-If you'd like to have your own server, you can do that too.  Visit the Friendica website to download the code with setup instructions.  It's a very simple install process that anybody experienced in hosting websites, or with basic Linux experience can handle easily.
+###OpenID
+
+The first field on the Registration page is for an OpenID address.
+If you do not have an OpenID address or do not wish to use OpenID, leave this field blank.
+If you have an OpenID account elsewhere and wish to use it, enter the address into this field and click 'Register'.
+Friendica will attempt to extract as much information as possible from your OpenID provider and return to this page with those items already filled in.
+
+###Your Full Name
+
+Please provide your full name **as you would like it to be displayed on this system**.
+Most people use their real name for this, but you're under no obligation to do so yourself.
+
+###Email Address
+
+Please provide a valid email address.
+Your email address is **never** published.
+We need this to send you account information and your login details.
+You may also occasionally receive notifications of incoming messages or items requiring your attention, but you have the possibility to completely disable these from your Settings page once you have logged in.
+This doesn't have to be your primary email address, but it does need to be a real email address.
+You can't get your initial password, or reset a lost password later without it.
+This is the only bit of personal information that has to be accurate.
+
+###Nickname
+
+A nickname is used to generate web addresses for many of your personal pages, and is also treated like an email address when establishing communications with others.
+Due to the way that the nickname is used, it has some limitations. 
+It must contain only US-ASCII text characters and numbers, and must also start with a text character.
+It also must be unique on this system. 
+This is used in many places to identify your account, and once set - cannot be changed.
 
 
-*OpenID*
+###Directory Publishing
 
-The first field on the Registration page is for an OpenID address. If you do not have an OpenID address or do not wish to use OpenID, leave this field blank. If you have an OpenID account elsewhere and wish to use it, enter the address into this field and click 'Register'. Friendica will attempt to extract as much information as possible from your OpenID provider and return to this page with those items already filled in.
+The registration form also allows you to choose whether or not to list your account in the online directory.
+This is like a "phone book" and you may choose to be unlisted.
+We recommend that you select 'Yes' so that other people (friends, family, etc.) will be able to find you.
+If you choose 'No', you will essentially be invisible and have few opportunities for interaction.
+Whichever you choose, this can be changed any time from your Settings page after you login. 
 
+###Register
 
-*Your Full Name*
+Once you have provided the necessary details, click the 'Register' button.
+An email will be sent to you providing your account login details.
+Please watch your email (including spam folders) for your registration details and initial password. 
 
-Please provide your full name **as you would like it to be displayed on this system**.  Most people use their real name for this, but you're under no obligation to do so yourself.
+Login Page
+---
 
+On the 'Login' page, please enter your login information that was provided during registration.
+You may use either your nickname or email address as a Login Name. 
 
-*Email Address*
+If you use your account to manage multiple '[Pages](help/Pages)' and these all have the same email address, please enter the nickname for the account you wish to manage.
 
-Please provide a valid email address. Your email address is **never** published. We need this to send you account information and your login details. You may also occasionally receive notifications of incoming messages or items requiring your attention, but you have the ability to completely disable these from your Settings page once you have logged in.  This doesn't have to be your primary email address, but it does need to be a real email address.  You can't get your initial password, or reset a lost password later without it.  This is the only bit of personal information that has to be accurate.
+*If* your account has been OpenID enabled, you may use your OpenID address as a login name and leave the password blank.
+You will be redirected to your OpenID provider to complete your authorisation. 
 
+Otherwise, enter your password.
+This will have been initially provided in your registration email message.
+Your password is case-sensitive, so please check your 'Caps Lock' key if you are having difficulty logging in. 
 
-*Nickname*
-
-A nickname is used to generate web addresses for many of your personal pages, and is also treated like an email address when establishing communications with others. Due to the way that the nickname is used, it has some limitations. It must contain only US-ASCII text characters and numbers, and must also start with a text character. It also must be unique on this system. This is used in many places to identify your account, and once set - cannot be changed.
-
-
-*Directory Publishing*
-
-The Registration form also allows you to choose whether or not to list your account in the online directory. This is like a "phone book" and you may choose to be unlisted. We recommend that you select 'Yes' so that other people (friends, family, etc.) will be able to find you. If you choose 'No', you will essentially be invisible and have few opportunities for interaction. Whichever you choose, this can be changed any time from your Settings page after you login. 
-
-
-*Register*
-
-Once you have provided the necessary details, click the 'Register' button. An email will be sent to you providing your account login details. Please watch your email (including spam folders) for your registration details and initial password. 
-
-
-**Login Page**
-
-On the 'Login' page, please enter your login information that was provided during registration. You may use either your nickname or email address as a Login Name. 
-
-If you use your account to manage multiple '[Pages](help/Pages)' and these all have the same email address, please enter the nickname for the account you wish to manage.  
-
-*If* your account has been OpenID enabled, you may use your OpenID address as a login name and leave the password blank. You will be redirected to your OpenID provider to complete your authorisation. 
-
-Otherwise, enter your password. This will have been initially provided in your registration email message. Your password is case-sensitive, so please check your 'Caps Lock' key if you are having difficulty logging in. 
-
-
-**Changing Your Password**
+Changing Your Password
+---
 
 After your first login, please visit the 'Settings' page from the top menu bar and change your password to something that you will remember.
 
-
-**Getting Started**
+Getting Started
+---
 
 A ['Tips for New Members'](newmember) link will show up on your network and home pages for two weeks to provide some important Getting Started information.
 
+Retrieving Personal Data
+---
 
-**Retrieving Personal Data**
+You can export a copy of your personal data in XML format from the "Export personal data" link at the top of your settings page.
 
-You can export a copy of your personal data in XML format from the "Export personal data" link at the top of your settings  page.
-
-
-**See Also**
+See Also
+---
 
 * [Profiles](help/Profiles)
 
diff --git a/doc/BBCode.md b/doc/BBCode.md
index c84308e5b..fe7c1481f 100644
--- a/doc/BBCode.md
+++ b/doc/BBCode.md
@@ -1,138 +1,154 @@
-Friendica BBCode tags reference
-========================
-
-* [Home](help)
-
-Inline
------
-
-
+Friendica BBCode tags reference
+========================
+
+* [Home](help)
+
+Inline
+-----
+
+
 
[b]bold[/b]
: bold - +
[i]italic[/i]
: italic - +
[u]underlined[/u]
: underlined - +
[s]strike[/s]
: strike - +
[color=red]red[/color]
: red - +
[url=http://www.friendica.com]Friendica[/url]
: Friendica - +
[img]http://friendica.com/sites/default/files/friendika-32.png[/img]
: Immagine/foto - +
[size=xx-small]small text[/size]
: small text - +
[size=xx-large]big text[/size]
: big text - +
[size=20]exact size[/size] (size can be any number, in pixel)
: exact size - - - - - - - -Block ------ - -
[code]code[/code]
- -code - -

 

- -
[quote]quote[/quote]
- -
quote
- -

 

- -
[quote=Author]Author? Me? No, no, no...[/quote]
- -Author wrote:
Author? Me? No, no, no...
- -

 

- -
[center]centered text[/center]
- -
centered text
- -

 

- -**Table** -
[table border=1]
- [tr] 
-   [th]Tables now[/th]
- [/tr]
- [tr]
-   [td]Have headers[/td]
- [/tr]
-[/table]
- -
Tables now
Have headers
- -

 

- -**List** - -
[list]
- [*] First list element
- [*] Second list element
-[/list]
- - -[list] is equivalent to [ul] (unordered list). - -[ol] can be used instead of [list] to show an ordered list: - -
[ol]
- [*] First list element
- [*] Second list element
-[/ol]
- - -For more options on ordered lists, you can define the style of numeration on [list] argument: -
[list=1]
: decimal - -
[list=i]
: lover case roman - -
[list=I]
: upper case roman - -
[list=a]
: lover case alphabetic - -
[list=A] 
: upper case alphabetic - - - - -Embed ------- - -You can embed video, audio and more in a message. - -
[video]url[/video]
-
[audio]url[/audio]
- -Where *url* can be an url to youtube, vimeo, soundcloud, or other sites wich supports oembed or opengraph specifications. -*url* can be also full url to an ogg file. HTML5 tag will be used to show it. - -
[url]*url*[/url]
- -If *url* supports oembed or opengraph specifications the embedded object will be shown (eg, documents from scribd). -Page title with a link to *url* will be shown. - - - -Special -------- - -If you need to put literal bbcode in a message, [noparse], [nobb] or [pre] are used to escape bbcode: - -
[noparse][b]bold[/b][/noparse]
: [b]bold[/b] - - + + + + + + + +Block +----- + +
[code]code[/code]
+ +code + +

 

+ +
[quote]quote[/quote]
+ +
quote
+ +

 

+ +
[quote=Author]Author? Me? No, no, no...[/quote]
+ +Author wrote:
Author? Me? No, no, no...
+ +

 

+ +
[center]centered text[/center]
+ +
centered text
+ +

 

+ +
You should not read any further if you want to be surprised.[spoiler]There is a happy end.[/spoiler]
+ +You should not read any further if you want to be surprised.
*click to open/close* + +(The text between thhe opening and the closing of the spoiler tag will be visible once the link is clicked. So *"There is a happy end."* wont be visible until the spoiler is uncovered.) + +

 

+ +**Table** +
[table border=1]
+ [tr] 
+   [th]Tables now[/th]
+ [/tr]
+ [tr]
+   [td]Have headers[/td]
+ [/tr]
+[/table]
+ +
Tables now
Have headers
+ +

 

+ +**List** + +
[list]
+ [*] First list element
+ [*] Second list element
+[/list]
+ + +[list] is equivalent to [ul] (unordered list). + +[ol] can be used instead of [list] to show an ordered list: + +
[ol]
+ [*] First list element
+ [*] Second list element
+[/ol]
+ + +For more options on ordered lists, you can define the style of numeration on [list] argument: +
[list=1]
: decimal + +
[list=i]
: lover case roman + +
[list=I]
: upper case roman + +
[list=a]
: lover case alphabetic + +
[list=A] 
: upper case alphabetic + + + + +Embed +------ + +You can embed video, audio and more in a message. + +
[video]url[/video]
+
[audio]url[/audio]
+ +Where *url* can be an url to youtube, vimeo, soundcloud, or other sites wich supports oembed or opengraph specifications. +*url* can be also full url to an ogg file. HTML5 tag will be used to show it. + +
[url]*url*[/url]
+ +If *url* supports oembed or opengraph specifications the embedded object will be shown (eg, documents from scribd). +Page title with a link to *url* will be shown. + +Map +--- + +
[map]address[/map]
+
[map=lat,long]
+ +You can embed maps from coordinates or addresses. +This require "openstreetmap" addon version 1.3 or newer. + + +Special +------- + +If you need to put literal bbcode in a message, [noparse], [nobb] or [pre] are used to escape bbcode: + +
[noparse][b]bold[/b][/noparse]
: [b]bold[/b] + + diff --git a/doc/Bugs-and-Issues.md b/doc/Bugs-and-Issues.md index c9f845c00..366b2ed66 100644 --- a/doc/Bugs-and-Issues.md +++ b/doc/Bugs-and-Issues.md @@ -3,11 +3,16 @@ Bugs and Issues * [Home](help) +If your server has a support page, you should report any bugs/issues you encounter there first. +Reporting to your support page before reporting to the developers makes their job easier, as they don't have to deal with bug reports that might not have anything to do with them. +This helps us get new features faster. -If your server has a support page, you should report any bugs/issues you encounter there first. Reporting to your support page before reporting to the developers makes their job easier, as they don't have to deal with bug reports that might not have anything to do with them, and that helps us get new features faster. +If you're a technical user, or your site doesn't have a support page, you'll need to use the [Bug Tracker](http://bugs.friendica.com/). +Please perform a search to see if there's already an open bug that matches yours before submitting anything. -If you're a technical user, or your site doesn't have a support page, you'll need to use the Bug Tracker. Please perform a search to see if there's already an open bug that matches yours before submitting anything. +Try to provide as much information as you can about the bug, including the **full** text of any error messages or notices, and any steps required to replicate the problem in as much detail as possible. +It's generally better to provide too much information than not enough. -Try to provide as much information as you can about the bug, including the **full** text of any error messages or notices, and any steps required to replicate the problem in as much detail as possible. It's generally better to provide too much information than not enough. +See [this article](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) to learn more about submitting **good** bug reports. -See this article to learn more about submitting **good** bug reports. +And last but not least: Better report an issue you encountered even if you don't write the perfect bug report! diff --git a/doc/Chats.md b/doc/Chats.md index ba5a64bd8..77b21833a 100644 --- a/doc/Chats.md +++ b/doc/Chats.md @@ -8,45 +8,64 @@ There are two possibilities to use a chat on your friendica site * IRC Chat * Jappix -##IRC-Chat Plugin +IRC-Chat Plugin +--- -After activating the plugin, you can find the chat at [yoursite.com/irc](../irc). Note: you can use this chat without any login at your site so that everyone could use it. +After activating the plugin, you can find the chat at [yoursite.com/irc](../irc). +Note: you can use this chat without any login at your site so that everyone could use it. -If you follow the link, you will see the login page of the IR chat. Now choose a nickname and a chatroom. You can choose every name for the room, even something like #superchatwhosenameisonlyknownbyme. At last, solve the captchas and click the connect button. +If you follow the link, you will see the login page of the IR chat. +Now choose a nickname and a chatroom. +You can choose every name for the room, even something like #superchatwhosenameisonlyknownbyme. +At last, solve the captchas and click the connect button. -The following window shows some text while connecting. This text isn't importend for you, just wait for the next window. The first line shows your name and your current IP address. The right part of the window shows all user. The lower part of the window contains an input field. +The following window shows some text while connecting. +This text isn't importend for you, just wait for the next window. +The first line shows your name and your current IP address. +The right part of the window shows all users. +The lower part of the window contains an input field. -##Jappix Mini +Jappix Mini +--- -The Jappix Mini Plugin creates a chatbox for jabber- and XMPP-contacts. You should already have a jabber/XMPP-account before setting up the plugin. You can find more information at http://www.jabber.org/ +The Jappix Mini Plugin creates a chatbox for jabber- and XMPP-contacts. +You should already have a jabber/XMPP-account before setting up the plugin. +You can find more information at [jabber.org](http://www.jabber.org/). -You can use several server to create an account: +You can use several servers to create an account: * [https://jappix.com](https://jappix.com) * [http://xmpp.net](http://xmpp.net) -**1. Basics** +###1. Basics -At first you have to get the current version (via github): +At first you have to get the current version. You can either pull it from [Github](https://github.com) like so: -cd /var/www/virtual/YOURSPACE/html/addon; git pull + $> cd /var/www/virtual/YOURSPACE/html/addon; git pull -or as a normal download via: https://github.com/friendica/friendica-addons/blob/master/jappixmini.tgz (click at „view raw“). +Or you can download a tar archive here: [jappixmini.tgz](https://github.com/friendica/friendica-addons/blob/master/jappixmini.tgz) (click at „view raw“). -Just unpack the file and rename the directory to „jappixmini“. Next, upload this directory and the .tgz-file into your addon directory of your friendica installation. +Just unpack the file and rename the directory to „jappixmini“. +Next, upload this directory and the .tgz-file into your addon directory of your friendica installation. -Now you can activate the plugin at the admin pages. Now you can find an entry of jappix at the plugin sidebar (where you can also find twitter, statusnet and other ones). The following page shows the settings of this plugin. +Now you can activate the plugin globally on the admin pages. +In the plugin sidebar, you will find an entry of jappix now (where you can also find twitter, GNU Social and others). +The following page shows the settings of this plugin. -Now you can activate the BOSH proxy. -Next, go to the setting page of your account. +Activate the BOSH proxy. -**2. Settings** +###2. Settings -Go to the account settings and choose the plugin page. Scroll down until you find the Jappix Mini addon settings +Go to your user account settings next and choose the plugin page. +Scroll down until you find the Jappix Mini addon settings. At first you have to activate the addon. -Now add your Jabber/XMPP name, the domain/server (without "http"; just "jappix.com"). For „Jabber BOSH Host“ you could use "https://bind.jappix.com/". You can find further information in the „Configuration Help“-section below this fields. -At last you have enter your password (there are some more optional options, you can choose). Finish these steps with "send" to save the entries. Now, you should find the chatbox at the lower right corner of your browser window. +Now add your Jabber/XMPP name, the domain/server (without "http"; just "jappix.com"). +For „Jabber BOSH Host“ you could use "https://bind.jappix.com/". +You can find further information in the „Configuration Help“-section below this fields. +At last you have enter your password (there are some more optional options, you can choose). +Finish these steps with "send" to save the entries. +Now, you should find the chatbox at the lower right corner of your browser window. -If you want to add contacts manually, you can click "add contact". \ No newline at end of file +If you want to add contacts manually, you can click "add contact". diff --git a/doc/Connectors.md b/doc/Connectors.md index 08b4c135e..cd4b643f1 100644 --- a/doc/Connectors.md +++ b/doc/Connectors.md @@ -3,65 +3,68 @@ Connectors * [Home](help) -Connectors allow you to connect with external social networks and services. Connectors are only required for posting to existing accounts on Facebook, Twitter, and StatusNet. There is also a connector for accessing your email INBOX. +Connectors allow you to connect with external social networks and services. +They are only required for posting to existing accounts on Twitter or GNU Social. +There is also a connector for accessing your email INBOX. If the following network connectors are installed on your system, select the following links to visit the appropriate settings page and configure them for your account: -* [Facebook](/settings/addon) * [Twitter](/settings/addon) -* [StatusNet](/settings/addon) +* [GNU Social](/settings/addon) * [Email](/settings) Instructions For Connecting To People On Specific Services ========================================================== -**Friendica** +Friendica +--- + +You can either connect to others by providing your Identity Address on the 'Connect' page of any Friendica member. +Or you can put their Identity Address into the Connect box on your [Contacts](contacts) page. -You may connect by providing your Identity Address on the 'Connect' page of any Friendica member. You may also put their Identity Address into the Connect box on your [Contacts](contacts) page. - - -**Diaspora** +Diaspora +--- Add the Diaspora 'handle' to the 'Connect/Follow' text box on your [Contacts](contacts) page. -**Identi.ca/StatusNet/GNU-Social** +GNU Social +--- -These are described as the "federated social web" or OStatus contacts. +This is described as the "federated social web" or OStatus contacts. -Please note that there are **no** privacy provisions on the OStatus network. Any message which is delivered to **any** OStatus member is visible to anybody in the world and will negate any privacy settings that you have in effect. These messages will also turn up in public searches. +Please note that there are **no** privacy provisions on the OStatus network. +Any message which is delivered to **any** OStatus member is visible to anybody in the world and will negate any privacy settings that you have in effect. +These messages will also turn up in public searches. Since OStatus communications do not use authentication, if you select the profile privacy option to hide your profile and messages from unknown viewers, OStatus members will **not** be able to receive your communications. To connect with an OStatus member insert their profile URL or Identity address into the Connect box on your [Contacts](contacts) page. -The StatusNet connector may be used if you wish posts to appear on an OStatus site using an existing OStatus account. - +The GNU Social connector may be used if you wish posts to appear on an OStatus site using an existing OStatus account. It is not necessary to do this, as you may 'follow' OStatus members from Friendica and they may follow you (by placing their own Identity Address into your 'Connect' page). -**Blogger, Wordpress, RSS feeds, arbitrary web pages** +Blogger, Wordpress, RSS feeds, arbitrary web pages +--- -Put the URL into the Connect box on your [Contacts](contacts) page. You will not be able to reply to these contacts. +Put the URL into the Connect box on your [Contacts](contacts) page. +PLease note that you will not be able to reply to these contacts. -This will allow you to _connect_ with millions of pages on the internet. All that we require to do this is that the page use a discoverable feed using either the RSS or Atom syndication format, and which provides an author name and a site image in a form which we can extract. +This feed reader feature will allow you to _connect_ with millions of pages on the internet. +All that the pages need to have is a discoverable feed using either the RSS or Atom syndication format, and which provides an author name and a site image in a form which we can extract. +Twitter +--- -**Twitter** +To follow a Twitter member, put the URL of the Twitter member's main page into the Connect box on your [Contacts](contacts) page. +To reply, you must have the Twitter connector installed, and reply using your own status editor. +Begin the message with @twitterperson replacing with the Twitter username. -To follow a Twitter member, put the URL of the Twitter member's main page into the Connect box on your [Contacts](contacts) page. To reply, you must have the Twitter connector installed, and reply using your own status editor. Begin the message with @twitterperson replacing with the Twitter username. - -**Email** - -Configure the email connector from your [Settings](settings) page. Once this has been done, you may enter an email addres to connect with using the Connect box on your [Contacts](contacts) page. They must be the sender of a message which is currently in your INBOX for the connect to succeed. You may include email contacts in private conversations. - -**Facebook** - -The Facebook connector is a plugin/addon which allows you to interact with friends on Facebook from within Friendica. If enabled, your Facebook friend list will be imported, and you will see and be able to respond to Facebook posts. Facebook members may also be added to private conversation groups. You will not be able to connect with individual Facebook accounts - but will have your entire friend list imported and updated if new friends are added. - -Assuming the Facebook plugin/addon has been installed on your system, it can be enabled by going to your [Plugin Settings](settings/addon) page, and then select "Facebook Connector Settings" on that page. This will only appear if the Facebook plugin/addon has been installed. Follow the instruction to install or remove the Facebook connector. - -You may also choose whether your public postings are posted to Facebook by default. You may toggle this setting at any time from the Permissions settings of the status post editor (click the lock icon). This setting has no effect on private conversations - which will always be delivered to Facebook friends who are included in the permissions. - -(Note: At this time, Facebook contacts will not be able to view any private photos. This will be resolved in a future release. Facebook contacts may however see any public photos you have uploaded.) +Email +--- +Configure the email connector from your [Settings](settings) page. +Once this has been done, you may enter an email address to connect with using the Connect box on your [Contacts](contacts) page. +They must be the sender of a message which is currently in your INBOX for the connection to succeed. +You may include email contacts in private conversations. diff --git a/doc/Developers-Intro.md b/doc/Developers-Intro.md index 23aff4f01..ff8c3c54c 100644 --- a/doc/Developers-Intro.md +++ b/doc/Developers-Intro.md @@ -3,47 +3,76 @@ Where to get started to help improve Friendica? * [Home](help) -Do you want to help us improve Friendica? Here we have compiled some hints on how to get started and some tasks to help you choose. A project like Friendica is the sum of many different contributions. **Very different skills are required to make good software. Some of them involve coding, others do not.** We are looking for helpers in all areas, whether you write text or code, whether you spread the word to convince people or design new icons. Whether you feel like an expert or like a newbie - join us with your ideas! +Do you want to help us improve Friendica? +Here we have compiled some hints on how to get started and some tasks to help you choose. +A project like Friendica is the sum of many different contributions. +**Very different skills are required to make good software. +Some of them involve coding, others do not.** +We are looking for helpers in all areas, whether you write text or code, whether you spread the word to convince people or design new icons. +Whether you feel like an expert or like a newbie - join us with your ideas! -**Contact us** +Contact us +--- The discussion of Friendica development takes place in the following Friendica forums: * The main [forum for Friendica development](https://friendika.openmindspace.org/profile/friendicadevelopers) * The [forum for Friendica theme development](https://friendica.eu/profile/ftdevs) -**Help other users** +Help other users +--- -Remember the questions you had when you first tried Friendica? A good place to start can be to help new people find their way around Friendica in the [general support forum](https://helpers.pyxis.uberspace.de/profile/helpers). Welcome them, answer their questions, point them to documentation or ping other helpers directly if you can't help but think you know who can. +Remember the questions you had when you first tried Friendica? +A good place to start can be to help new people find their way around Friendica in the [general support forum](https://helpers.pyxis.uberspace.de/profile/helpers). +Welcome them, answer their questions, point them to documentation or ping other helpers directly if you can't help but think you know who can. -**Translations** +Translation +--- The documentation contains help on how to translate Friendica in the [at Transifex](/help/translations) where the UI is translated. If you don't want to translate the UI, or it is already done to your satisfaction, you might want to work on the translation of the /help files? -**Design** +Design +--- -Are you good at designing things? If you have seen Friendica you probably have ideas to improve it, haven't you? +Are you good at designing things? +If you have seen Friendica you probably have ideas to improve it, haven't you? * If you would like to work with us on enhancing the user interface, please join the [UX Watchdogs forum](https://fc.oscp.info/profile/ux-watchdogs) * Make plans for a better Friendica interface design and share them with us. -* Tell us if you are able to realize your ideas or what kind of help you need. We can't promise we have the right skills in the group but we'll try. +* Tell us if you are able to realize your ideas or what kind of help you need. +We can't promise we have the right skills in the group but we'll try. * Choose a thing to start with, e.g. work on the icon set of your favourite theme -**Programming** +Programming +--- -* **Issues:** Have a look at our [issue tracker](https://github.com/friendica/friendica) on gihub! - * Try to reproduce a bug that needs more inquries and write down what you find out. - * If a bug looks fixed, ask the bug reporters for feedback to find out if the bug can be closed. - * Fix a bug if you can. Please make the pull request against the *develop* branch of the repository. +###Issues -* **Web interface:** The thing many people want most is a better interface, preferably a responsive Friendica theme. This is a piece of work! If you want to get involved here: - * Look at the first steps that were made (e.g. the clean theme). Ask us to find out whom to talk to about their experiences. - * Talk to design people if you know any. - * Let us know about your plans [in the dev forum](https://friendika.openmindspace.org/profile/friendicadevelopers) and the [theme developer forum](https://friendica.eu/profile/ftdevs). Do not worry about cross-posting. +Have a look at our [issue tracker](https://github.com/friendica/friendica) on github! -* **Client software:** There are free software clients that do somehow work with Friendica but most of them need love and maintenance. Also, they were mostly made for other platforms using the StatusNet API. This means they lack:w - the features that are really specific to Friendica. Popular clients you might want to have a look at are: - * [Hotot (Linux)](http://hotot.org/) - abandoned - * [Friendica for Android](https://github.com/max-weller/friendica-for-android) - abandoned - * You can find more working client software in [Wikipedia](https://en.wikipedia.org/wiki/Friendica). + * Try to reproduce a bug that needs more inquries and write down what you find out. + * If a bug looks fixed, ask the bug reporters for feedback to find out if the bug can be closed. + * Fix a bug if you can. Please make the pull request against the *develop* branch of the repository. + +###Web interface + +The thing many people want most is a better interface, preferably a responsive Friendica theme. +This is a piece of work! +If you want to get involved here: + +* Look at the first steps that were made (e.g. the clean theme). +Ask us to find out whom to talk to about their experiences. +* Talk to design people if you know any. +* Let us know about your plans [in the dev forum](https://friendika.openmindspace.org/profile/friendicadevelopers) and the [theme developer forum](https://friendica.eu/profile/ftdevs). +Do not worry about cross-posting. + +###Client software +There are free software clients that do somehow work with Friendica but most of them need love and maintenance. +Also, they were mostly made for other platforms using the GNU Social API. +This means they lack the features that are really specific to Friendica. +Popular clients you might want to have a look at are: + +* [Hotot (Linux)](http://hotot.org/) - abandoned +* [Friendica for Android](https://github.com/max-weller/friendica-for-android) - abandoned +* You can find more working client software in [Wikipedia](https://en.wikipedia.org/wiki/Friendica). diff --git a/doc/FAQ.md b/doc/FAQ.md index 6fa852e0b..9197c068c 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -19,98 +19,117 @@ Admins * **[Can I configure multiple domains with the same code instance?](help/FAQ#multiple)** * **[Where can I find the source code of friendica, addons and themes?](help/FAQ#sources)** +* **[I've changed the my email address now the admin panel is gone?](help/FAQ#adminaccount1)** +* **[Can there be more then just one admin for a node?](help/FAQ#adminaccount2)** User -------- ***** -**Why do I getting warnings about certificates?** +###Why do I get warnings about SSL certificates? -Sometimes you get a browser warning about a missing certificate. These warnings can have three reasons: +SSL (Secure Socket Layer) is a technology to encrypt data transfer between computers. +Sometimes your browser warns you about a missing or invalid certificate. +These warnings can have three reasons: -1. the server you are connected to doesn't have SSL; +1. The server you are connected to doesn't offer SSL encryption. +2. The server has a self-signed certificate (not recommended). +3. The certificate is expired. -2. the server has a self-signed certificate (not recommended) - -3. the certificate is expired. - -*(SSL (Secure Socket Layer) is a technology to encrypt data as it passes between two computers).* - -If you dont have a SSL cert yet, there are three ways to get one: buy one, get a free one (eg. via StartSSL) or create your own (not recommended). [You can find more information about setting up SSL and why it's a bad idea to use self-signed SSL here.](help/SSL) - -Be aware that a browser warning about security issues is something that can make new users feel insecure about the whole friendica project. - -Also you can have problems with the connection to diaspora because some pods require a SSL-certificated connection. - -If you are just using friendica for a specified group of people on a single server without a connection to the rest of the friendica network, there is no need to use SSL. If you exclusively use public posts, there is also no need for it. - -If you havn't set up a server yet, it's wise to compare the different provider and their SSL conditions. Some allow the usage of free certificates or give you the access to their certificate for free. Other ones only allow bought certificates from themselves or other providers. +We recommend to talk to the admin(s) of the affected friendica server. (Admins, please see the respective section of the [admin manual](help/SSL).) -**How can I upload images, files, links, videos and sound files to posts?** +###How can I upload images, files, links, videos and sound files to posts? -You can upload images from your computer by using the [editor](help/Text_editor). An overview of all uploaded images is listed at yourpage.com/photos/profilename. There you could also upload images directly and choose, if your contacts shall receive a message about this upload. +You can upload images from your computer by using the [editor](help/Text_editor). +An overview of all uploaded images is listed at *yourpage.com/photos/profilename*. +On that page, you can also upload images directly and choose, if your contacts shall receive a message about this upload. -Generally, you could attach every kind of file to a post. This is possible by using the "paper-clip"-symbol at the editor. These files will be linked to your post and can be downloaded by your contacts. But it's not possible to get a preview for these ones. Because of this, this upload method is recommended for office or zipped files. -If you want to use Dropbox, owncloud at your own server or any other [filehoster](http://en.wikipedia.org/wiki/Comparison_of_file_hosting_services), you'll have use the "link"-button (chain-symbol). +Generally, you could attach every kind of file to a post. +This is possible by using the "paper-clip"-symbol in the editor. +These files will be linked to your post and can be downloaded by your contacts. +But it's not possible to get a preview for these ones. +Because of this, this upload method is recommended for office or zipped files. +If you want share content from Dropbox, Owncloud or any other [filehoster](http://en.wikipedia.org/wiki/Comparison_of_file_hosting_services), use the "link"-button (chain-symbol). -When you're adding URLs of other webpages with the "link"-button, Friendica tries to create a small preview. If this doesn't work, try to add the link by typing: [url=http://example.com]self-chosen name[/url]. +When you're adding URLs of other webpages with the "link"-button, Friendica tries to create a small preview. +If this doesn't work, try to add the link by typing: [url=http://example.com]*self-chosen name*[/url]. -You can also add video and audio files to posts. But instead of a direct upload you have to use one of the following methods: +You can also add video and audio files to posts. +But instead of a direct upload you have to use one of the following methods: 1. Add the video or audio link of a hoster (Youtube, Vimeo, Soundcloud and everyone else with oembed/opengraph-support). Videos will be shown with a preview image you can click on to start it. SoundCloud directly inserts a player to your post. -2. If you have an own server, you can upload your multimedia files via FTP and insert the URL. Your video or sound file will be shown with their own player at your post. +2. If you have your own server, you can upload multimedia files via FTP and insert the URL. -Friendica is using HTML5 for embedding content. Therefore, the supported files are depending on your browser and operating system (OS). Some filetypes are WebM, MP4, MP3 and OGG. There are more examples at wikipedia ([video](http://en.wikipedia.org/wiki/HTML5_video), [audio](http://en.wikipedia.org/wiki/HTML5_audio)). +Friendica is using HTML5 for embedding content. +Therefore, the supported files are depending on your browser and operating system. +Some supported filetypes are WebM, MP4, MP3 and OGG. +See Wikipedia for more of them ([video](http://en.wikipedia.org/wiki/HTML5_video), [audio](http://en.wikipedia.org/wiki/HTML5_audio)). -**Is it possible to have different avatars per profile?** +###Is it possible to have different avatars per profile? -Yes. On your Edit/Manage Profiles page, you will find a "change profile photo" link. Clicking this will take you to a page where you can upload a photograph and select which profile it will be associated with. To avoid privacy leakage, we only display the photograph associated with your default profile as the avatar in your posts. +Yes. On your Edit/Manage Profiles page, you will find a "change profile photo" link. +Clicking this will take you to a page where you can upload a photograph and select which profile it will be associated with. +To avoid privacy leakage, we only display the photograph associated with your default profile as the avatar in your posts. -**What is the difference between blocked|ignored|archived|hidden contacts?** +###What is the difference between blocked|ignored|archived|hidden contacts? -We prevent direct communication with blocked contacts. They are not included in delivery, and their own posts to you are not imported; however their conversations with your friends will still be visible in your stream. If you remove a contact completely, they can send you another friend request. Blocked contacts cannot do this. They cannot communicate with you directly, only through friends. +We prevent direct communication with **blocked contacts**. +They are not included in delivery, and their own posts to you are not imported. +However their conversations with your friends will still be visible in your stream. +If you remove a contact completely, they can send you another friend request. +Blocked contacts cannot do this. They cannot communicate with you directly, only through friends. -Ignored contacts are included in delivery - they will receive your posts. However we do not import their posts to you. Like blocking, you will still see this person's comments to posts made by your friends. +**Ignored contacts** are included in delivery - they will receive your posts. +However we do not import their posts to you. +Like blocking, you will still see this person's comments to posts made by your friends. -[A plugin called "blockem" can be installed to collapse/hide all posts from a particular person in your stream if you desire complete blocking of an individual, including his/her conversations with your other friends.] +A plugin called "blockem" can be installed to collapse/hide all posts from a particular person in your stream if you desire complete blocking of an individual, including his/her conversations with your other friends. -An archived contact means that communication is not possible and will not be attempted (perhaps the person moved to a new site and removed the old profile); however unlike blocking, existing posts this person made before being archived will be visible in your stream. +An **archived contact** means that communication is not possible and will not be attempted. +(Perhaps the person moved to a new site and removed the old profile.) +However unlike blocking, existing posts this person made before being archived will be visible in your stream. -A hidden contact will not be displayed in any "friend list" (except to you). However a hidden contact will appear normally in conversations and this may expose his/her hidden status to anybody who can see the conversation. +A **hidden contact** will not be displayed in any "friend list" (except to you). +However a hidden contact will appear normally in conversations and this may expose his/her hidden status to anybody who can see the conversation. -**What happens when an account is removed? Is it truly deleted?** +###What happens when an account is removed? Is it truly deleted? -If you delete your account, we will immediately remove all your content on your server, and then issue requests to all your contacts to remove you. This will also remove you from the global directory. Doing this requires that your account and profile still be "partially" available for up to 24 hours in order to establish contact with all your friends. We can block it in several ways so that it appears empty and all profile information erased, but will then wait for 24 hours (or after all of your contacts have been notified) before we can physically remove it. +If you delete your account, we will immediately remove all your content on **your** server. + +Then Friendica issues requests to all your contacts to remove you. +This will also remove you from the global directory. +Doing this requires your account and profile still to be "partially" available for up to 24 hours in order to establish contact with all your friends. +We can block it in several ways so that it appears empty and all profile information erased, but will then wait for 24 hours (or after all of your contacts have been notified) before we can physically remove it. + +After that, your account is deleted. -**Can I follow a hashtag?** +###Can I follow a hashtag? No. The act of 'following' a hashtags is an interesting technology, but presents a few issues. -1.) Posts which have to be copied to all sites on the network that are "listening" to that tag, which increases the storage demands to the detriment of small sites, and making the use of shared hosting practically impossible, and +1. Posts would have to be copied to all sites on the network that are "listening" to that hashtag. This would increase the storage demands to the detriment of small sites. It would make the use of shared hosting practically impossible. -2.) Making spam easy (tag spam is quite a serious issue on identi.ca for instance) +2. Making spam easy (tag spam is a serious issue on Twitter for instance) -but mostly - -3.) It creates a natural bias towards large sites which hold more tagged content - if your network uses tagging instead of other conversation federation mechanisms such as groups/forums. +3. It creates a natural bias towards large sites which hold more tagged content - if your network uses tagging instead of other conversation federation mechanisms such as groups/forums. Instead, we offer other mechanisms for wide-area conversations while retaining a 'level playing ground' for both large and small sites, such as forums and community pages and shared tags. -**How to create a RSS feed of the stream?** +###How to create a RSS feed of the stream? If you want to share your public page via rss you can use one of the following links: @@ -132,9 +151,10 @@ RSS feed of the conversations at your site -**Are there any clients for friendica I can use?** +###Are there any clients for friendica I can use? -Friendica is using a [Twitter/StatusNet compatible API](help/api), which means you can use any Twitter/StatusNet/GNU Social client for your plattform as long as you can change the API path in its settings. Here is a list of known working clients +Friendica is using a [Twitter/GNU Social compatible API](help/api), which means you can use any Twitter/GNU Social client for your plattform as long as you can change the API path in its settings. +Here is a list of known working clients: * Android * Friendica Client for Android @@ -153,9 +173,11 @@ Depending on the features of the client you might encounter some glitches in usa -**Where I can find help?** +###Where I can find help? -If you have problems with your Friendica page, you can ask the community at the [Friendica Support Group](https://helpers.pyxis.uberspace.de/profile/helpers). If you can't use your default profile you can either use a test account [test server](http://friendica.com/node/31) respectively an account at a public site [list](http://dir.friendica.com/siteinfo) or you can use the Librelist mailing list. If you want to use the mailing list, please just send a mail to friendica AT librelist DOT com. +If you have problems with your Friendica page, you can ask the community at the [Friendica Support Group](https://helpers.pyxis.uberspace.de/profile/helpers). +If you can't use your default profile you can either use a test account [test server](http://friendica.com/node/31) respectively an account at a public site [list](http://dir.friendica.com/siteinfo) or you can use the Librelist mailing list. +If you want to use the mailing list, please just send a mail to friendica AT librelist DOT com. If you are a theme developer, you will find help at this forum: [Friendica Theme Developers](https://friendica.eu/profile/ftdevs). @@ -164,16 +186,28 @@ Admin ***** -**Can I configure multiple domains with the same code instance?** +###Can I configure multiple domains with the same code instance? -This function is not supported anymore starting from Friendica 3.3 +No, this function is not supported anymore starting from Friendica 3.3. -**Where can I find the source code of friendica, addons and themes?** +###Where can I find the source code of friendica, addons and themes? -You can find the main respository [here](https://github.com/friendica/friendica). There you will always find the current stable version of friendica. +You can find the main respository [here](https://github.com/friendica/friendica). +There you will always find the current stable version of friendica. Addons are listed at [this page](https://github.com/friendica/friendica-addons). If you are searching for new themes, you can find them at [Friendica-Themes.com](http://friendica-themes.com/) + + +###I've changed the my email address now the admin panel is gone? + +Have a look into your .htconfig.php and fix your email address there. + + +###Can there be more then just one admin for a node? + +Yes. You just have to list more then one email address in the +.htconfig.php file. The listed emails need to be separated by a comma. diff --git a/doc/Forums.md b/doc/Forums.md index 56f47b350..2eac81a72 100644 --- a/doc/Forums.md +++ b/doc/Forums.md @@ -6,31 +6,56 @@ Forums Friendica also lets you create forums and/or celebrity accounts. -Every page in Friendica has a nickname and these must all be unique. This applies to all forums, whether they are normal profiles or forum profiles. +Every page in Friendica has a nickname and these must all be unique. +This applies to all forums, whether they are normal profiles or forum profiles. -Therefore the first thing you need to do to create a new forum is to register a new account for the forum. Please note that the site administrator can restrict and/or regulate the registration of new accounts. +Therefore the first thing you need to do to create a new forum is to register a new account for the forum. +Please note that the site administrator can restrict and/or regulate the registration of new accounts. -If you create a second account on a system and use the same email address or OpenID account as an existing account, you will no longer be able to use the email address (or OpenID) to login to the account. You should login using the account nickname instead. +If you create a second account on a system and use the same email address or OpenID account as an existing account, you will no longer be able to use the email address (or OpenID) to log in to the account. +You should log in using the account nickname instead. -On the new account, visit the 'Settings' page. Towards the end of the page are "Advanced Account/Page Type Settings". Typically you would use "Normal Account" for a normal personal account. This is the default selection. Community Forum/Celebrity Accounts provide the ability for people to become friends/fans of the forum without requiring approval. +On the new account, visit the 'Settings' page. +Towards the end of the page are "Advanced Account/Page Type Settings". +Typically you would use "Normal Account" for a normal personal account. +This is the default selection. +Community Forum/Celebrity Accounts provide the ability for people to become friends/fans of the forum without requiring approval. -The exact setting you would use depends on how you wish to interact with people who join the page. The "Soapbox" setting let's the page owner control all communications. Everything you post will go out to the forum members, but there will be no opportunity for interaction. This setting would typically be used for announcements or corporate communications. +The exact setting you would use depends on how you wish to interact with people who join the page. +The "Soapbox" setting let's the page owner control all communications. +Everything you post will go out to the forum members, but there will be no opportunity for interaction. +This setting would typically be used for announcements or corporate communications. -The most common setting is the "Community Forum". This creates a forum page where all members can freely interact. +The most common setting is the "Community Forum". +This creates a forum page where all members can freely interact. The "Automatic Friend Account" is typically used for personal profile forums where you wish to automatically approve any friendship/connection requests. -**Managing Multiple forums** +Managing Multiple forums +--- -We recommend that you create group forums with the same email address and password as your normal account. If you do this, you will find a new "Manage" tab on the menu bar which lets you toggle identities easily and manage your forums. You are not required to do this, but the alternative is to logout and log back into the other account to manage alternate forums - and this could get cumbersome if you manage several different forums/identities. +We recommend that you create group forums with the same email address and password as your normal account. +If you do this, you will find a new "Manage" tab on the menu bar which lets you toggle identities easily and manage your forums. +You are not required to do this, but the alternative is to log out and log back into the other account to manage alternate forums. +This could get cumbersome if you manage several different forums/identities. -You may also appoint a delegate to manage your forum. Do this by visiting the [Delegation Setup Page](delegate). This will provide you with a list of contacts on this system under "Potential Delegates". Selecting one or more persons will give them access to manage your forum. They will be able to edit contacts, profiles, and all content for this account/page. Please use this facility wisely. Delegated managers will not be able to alter basic account settings such as passwords or page types and/or remove the account. +You may also appoint a delegate to manage your forum. +Do this by visiting the [Delegation Setup Page](delegate). +This will provide you with a list of contacts on this system under "Potential Delegates". +Selecting one or more persons will give them access to manage your forum. +They will be able to edit contacts, profiles, and all content for this account/page. +Please use this facility wisely. +Delegated managers will not be able to alter basic account settings such as passwords or page types and/or remove the account. -**Posting to Community forums** +Posting to Community forums +--- -If you are a member of a community forum, you may post to the forum by including an @-tag in the post mentioning the forum. For example @bicycle would send my post to all members of the group "bicycle" in addition to the normal recipients. If your post is private you must also explicitly include the group in the post permissions (to allow the forum "contact" to see the post) **and** mention it in a tag (which redistributes the post to the forum members). +If you are a member of a community forum, you may post to the forum by including an @-tag in the post mentioning the forum. +For example @bicycle would send my post to all members of the group "bicycle" in addition to the normal recipients. +If your post is private you must also explicitly include the group in the post permissions (to allow the forum "contact" to see the post) **and** mention it in a tag (which redistributes the post to the forum members). You may also post to a community forum by posting a "wall-to-wall" post using secure cross-site authentication. -Comments which are relayed to community forums will be relayed back to the original post creator. Mentioning the forum with an @-tag in a comment does not relay the message, as distribution is controlled entirely by the original post creator. \ No newline at end of file +Comments which are relayed to community forums will be relayed back to the original post creator. +Mentioning the forum with an @-tag in a comment does not relay the message, as distribution is controlled entirely by the original post creator. diff --git a/doc/Github.md b/doc/Github.md index efee32d5b..cfdb01476 100644 --- a/doc/Github.md +++ b/doc/Github.md @@ -3,26 +3,68 @@ Friendica on Github * [Home](help) -**Here is how you can work on the code with us** +Here is how you can work on the code with us. If you have any questions please write to the Friendica developers' forum. + +Introduction to the workflow with our Github repository +------------------------------------------------------- 1. Install git on the system you will be developing on. 2. Create your own [github](https://github.com) account. 3. Fork the Friendica repository from [https://github.com/friendica/friendica.git](https://github.com/friendica/friendica.git). -4. Clone your fork from your Github account to your machine. Follow the instructions provided here: [http://help.github.com/fork-a-repo/](http://help.github.com/fork-a-repo/) to create and use your own tracking fork on github -5. Commit your changes to your fork. Then go to your github page and create a "Pull request" to notify us to merge your work. +4. Clone your fork from your Github account to your machine. +Follow the instructions provided here: [http://help.github.com/fork-a-repo/](http://help.github.com/fork-a-repo/) to create and use your own tracking fork on github +5. Commit your changes to your fork. +Then go to your github page and create a "Pull request" to notify us to merge your work. -**Branches** +Our Git Branches +---------------- -There are two branches in the main repo on Github: +There are two relevant branches in the main repo on Github: 1. master: This branch contains stable releases only. -2. develop: This branch contains the latest code. This is what you want to work with. +2. develop: This branch contains the latest code. +This is what you want to work with. -**Important** +Fast-forwarding +--------------- -Please pull in any changes from the project repository and merge them with your work **before** issuing a pull request. We reserve the right to reject any patch which results in a large number of merge conflicts. This is especially true in the case of language translations - where we may not be able to understand the subtle differences between conflicting versions. +Fast forwarding is enabled by default in git. +When you merge with fast-forwarding it does not add a new commit to mark when you've performed the merge and how. +This means in your commit history you can't know exactly what happened in terms of merges. +**It's best to turn off fast-forwarding.** +This is done by running "git merge --no-ff". +[Here](https://stackoverflow.com/questions/5519007/how-do-i-make-git-merges-default-be-no-ff-no-commit) is an explanation on how to configure git to turn off fast-forwarding by default. +You can find some more background reading [here](http://nvie.com/posts/a-successful-git-branching-model/). -Also - **test your changes**. Don't assume that a simple fix won't break something else. If possible get an experienced Friendica developer to review the code. +Release branches +---------------- + +A release branch is created when the develop branch contains all features it should have. +A release branch is used for a few things. + +1. It allows last-minute bug fixing before the release goes to master branch. +2. It allows meta-data changes (README, CHANGELOG, etc.) for version bumps and documentation changes. +3. It makes sure the develop branch can receive new features that are **not** part of this release. + +That last point is important because... +**The moment a release branch is created, develop is now intended for the version after this release**. +So please don't ever merge develop into a release! +An example: If a release branch "release-3.4" is created, "develop" becomes either 3.5 or 4.0. +If you were to merge develop into release-3.4 at this point, features and bug-fixes intended for 3.5 or 4.0 might leak into this release branch. +This might introduce new bugs, too. +Which defeats the purpose of the release branch. + +Some important reminders +------------------------ + +1. Please pull in any changes from the project repository and merge them with your work **before** issuing a pull request. +We reserve the right to reject any patch which results in a large number of merge conflicts. +This is especially true in the case of language translations - where we may not be able to understand the subtle differences between conflicting versions. + +2. **Test your changes**. +Don't assume that a simple fix won't break anything else. +If possible get an experienced Friendica developer to review the code. +Don't hesitate to ask us in case of doubt. Check out how to work with [our Vagrant](help/Vagrant) to save a lot of setup time! diff --git a/doc/Groups-and-Privacy.md b/doc/Groups-and-Privacy.md index 6e860b20d..ddde9eeb2 100644 --- a/doc/Groups-and-Privacy.md +++ b/doc/Groups-and-Privacy.md @@ -40,7 +40,7 @@ These private conversations work best when your friends are Friendica members. W This is a trust issue you need to be aware of. No software in the world can prevent your friends from leaking your confidential and trusted communications. Only a wise choice of friends. -But it isn't as clear cut when dealing with status.net, identi.ca and other network providers. You are encouraged to be **very** cautious when other network members are in a group because it's entirely possible for your private messages to end up in a public newsfeed. If you look at the Contact Edit page for any person, we will tell you whether or not they are members of an insecure network where you should exercise caution. +But it isn't as clear cut when dealing with GNU Social and other network providers. You are encouraged to be **very** cautious when other network members are in a group because it's entirely possible for your private messages to end up in a public newsfeed. If you look at the Contact Edit page for any person, we will tell you whether or not they are members of an insecure network where you should exercise caution. Once you have created a post, you can not change the permissions assigned. Within seconds it has been delivered to lots of people - and perhaps everybody it was addressed to. If you mistakenly created a message and wish you could take it back, the best you can do is to delete it. We will send out a delete notification to everybody who received the message - and this should wipe out the message with the same speed it was initially propagated. In most cases it will be completely wiped from the Internet - in under a minute. Again, this applies to Friendica networks. Once a message spreads to other networks, it may not be removed quickly and in some cases it may not be removed at all. @@ -62,8 +62,8 @@ Our developers are working on solutions to allow access to your friends - no mat Your profile and "wall" may also be visited by your friends from other networks, and you can block access to these by web visitors that Friendica doesn't know. Be aware that this could include some of your friends on other networks. -This may produce undesired results when posting a long status message to (for instance) Twitter and even Facebook. When Friendica sends a post to these networks which exceeds the service length limit, we truncate it and provide a link to the original. The original is a link back to your Friendica profile. As Friendica cannot prove who they are, it may not be possible for these people to view your post in full. +This may produce undesired results when posting a long status message to (for instance) Twitter or App.net. When Friendica sends a post to these networks which exceeds the service length limit, we truncate it and provide a link to the original. The original is a link back to your Friendica profile. As Friendica cannot prove who they are, it may not be possible for these people to view your post in full. For people in this situation we would recommend providing a "Twitter-length" summary, with more detail for friends that can see the post in full. -Blocking your profile or entire Friendica site from unknown web visitors also has serious implications for communicating with StatusNet/identi.ca members. These networks communicate with others via public protocols that are not authenticated. In order to view your posts, these networks have to access them as an "unknown web visitor". If we allowed this, it would mean anybody could in fact see your posts, and you've instructed Friendica not to allow this. So be aware that the act of blocking your profile to unknown visitors also has the effect of blocking outbound communication with public networks (such as identi.ca) and feed readers such as Google Reader. +Blocking your profile or entire Friendica site from unknown web visitors also has serious implications for communicating with GNU Social members. These networks communicate with others via public protocols that are not authenticated. In order to view your posts, these networks have to access them as an "unknown web visitor". If we allowed this, it would mean anybody could in fact see your posts, and you've instructed Friendica not to allow this. So be aware that the act of blocking your profile to unknown visitors also has the effect of blocking outbound communication with public networks (such as GNU Social) and feed readers such as Google Reader. diff --git a/doc/Home.md b/doc/Home.md index 592ee977b..ff991e3d2 100644 --- a/doc/Home.md +++ b/doc/Home.md @@ -10,6 +10,8 @@ Friendica Documentation and Resources * [BBCode tag reference](help/BBCode) * [Comment, sort and delete posts](help/Text_comment) * [Profiles](help/Profiles) + * [Accesskey reference](help/Accesskeys + * [Events](help/events) * You and other users * [Connectors](help/Connectors) * [Making Friends](help/Making-Friends) @@ -27,11 +29,10 @@ Friendica Documentation and Resources * [Install](help/Install) * [Settings](help/Settings) -* [Plugins](help/Plugins) -* [Installing Connectors (Facebook/Twitter/StatusNet)](help/Installing-Connectors) +* [Installing Connectors (Twitter/GNU Social)](help/Installing-Connectors) * [Message Flow](help/Message-Flow) * [Using SSL with Friendica](help/SSL) -* [Twitter/StatusNet API Functions](help/api) +* [Twitter/GNU Social API Functions](help/api) **Developer Manual** @@ -40,6 +41,8 @@ Friendica Documentation and Resources * [Help on Vagrant](help/Vagrant) * [How to translate Friendica](help/translations) * [Bugs and Issues](help/Bugs-and-Issues) +* [Plugin Development](help/Plugins) +* [Theme Development](help/themes) * [Smarty 3 Templates](help/smarty3-templates) **External Resources** diff --git a/doc/Improve-Performance.md b/doc/Improve-Performance.md index d4c94d2d1..1128e41b1 100644 --- a/doc/Improve-Performance.md +++ b/doc/Improve-Performance.md @@ -1,13 +1,9 @@ -How to: improve performance +How to improve the performance of a Friendica site ============== * [Home](help) -A little guide to increase the performance of a Friendica site - -**At first** - -Feel free to ask at Friendica support at https://helpers.pyxis.uberspace.de/profile/helpers if you need some clarification about the following instructions or if you need help in any other way. +Feel free to ask in the [Friendica support forum](https://helpers.pyxis.uberspace.de/profile/helpers) if you need some clarification about the following instructions or if you need help in any other way. System configuration -------- @@ -20,61 +16,26 @@ This value reduces the data that is send from the server to the client. 50 is a Set "OStatus conversation completion interval" to "never". -If you have many OStatus contacts then completing of conversations can be very time wasting. The downside: You won't see every comment in OStatus threads. - - Set "Path for lock file" to an empty folder outside your web root. - -Lock files help avoid the possibility of several background processes running at the same time. - -For example: It can happen that the poller.php takes longer than expected. When there is no lock file, it is possible for several instances of poller.php to run at the same time - which would slow down the system and affect the maximum numbers of processes and database connections. - -Please define a full file path that is writeable by the web server process. If your site is located at "/var/www/sitename/htdocs/" you could maybe create a folder "/var/www/sitename/temp/". +If you have many OStatus contacts then completing of conversations can take some time. Since you will miss several comments in OStatus threads, you maybe should consider the option "At post arrival" instead. Enable "Use MySQL full text engine" -When using MyISAM (default) this speeds up search. - - Set "Path to item cache" to an empty value outside your web root. - -Parsed BBCode and some external images will be put there. Parsing BBCode is a time wasting process that also makes heave use of the CPU. - -You can use the same folder you used for the lock file. - -**Warning!** - -The folder for item cache is cleaned up regularly. Every file that exceeds the cache duration is deleted. **If you accidentally point the cache path to your web root then you will delete your web root!** - -So double check that the folder only contains temporary content that can be deleted at any time. - -You have been warned. - -P.S. It happened to me :) +When using MyISAM (default) or InnoDB on MariaDB 10 this speeds up search. Plugins -------- Active the following plugins: - Alternate Pagination - Privacy Image Cache rendertime -###Alternate Pagination - - -**Description** - -This plugin reduces the database load massively. Downside: You can't see the total number of pages available at each module, and have this replaced with "older" and "newer" links. - -**Administration** - -Go to the admin settings of "altpager" and set it to "global". - ###rendertime -This plugin doesn't speed up your system. It helps analyzing your bottlenecks. +This plugin doesn't speed up your system. +It helps to analyze your bottlenecks. -When enabled you see some values like the following at the bottom of every page: +When enabled you see some values at the bottom of every page. +They show your performance problems. Performance: Database: 0.244, Network: 0.002, Rendering: 0.044, Parser: 0.001, I/O: 0.021, Other: 0.237, Total: 0.548 @@ -86,50 +47,40 @@ When enabled you see some values like the following at the bottom of every page: Others: Everything else :) Total: The sum of all above values -These values show your performance problems. - -Webserver +Apache Webserver -------- -If you are using Apache please enable the following modules. +The following Apache modules are recommended: -**Cache-Control** +###Cache-Control This module tells the client to cache the content of static files so that they aren't fetched with every request. - Enable the module "mod_expires" by typing in "a2enmod expires" as root. - Please add the following lines to your site configuration in the "directory" context. -ExpiresActive on ExpiresDefault "access plus 1 week" + ExpiresActive on ExpiresDefault "access plus 1 week" -See also: http://httpd.apache.org/docs/2.2/mod/mod_expires.html +Also see the Apache [2.2](http://httpd.apache.org/docs/2.2/mod/mod_expires.html) / [2.4](https://httpd.apache.org/docs/2.4/mod/mod_expires.html) documentation. -**Compress content** +###Compress content This module compresses the traffic between the web server and the client. - Enable the module "mod_deflate" by typing in "a2enmod deflate" as root. -See also: http://httpd.apache.org/docs/2.2/mod/mod_deflate.html +Also see the Apache [2.2](http://httpd.apache.org/docs/2.2/mod/mod_deflate.html) / [2.4](https://httpd.apache.org/docs/2.4/mod/mod_deflate.html) documentation. PHP -------- -**FCGI** +###FCGI -When using apache think about using FCGI. When using a Debian based distribution you will need the packages named "php5-cgi" and "libapache2-mod-fcgid". +When using Apache think about using FCGI. +In a Debian-based distribution you will need to install the packages named "php5-cgi" and "libapache2-mod-fcgid". -Please refer to external documentations for a more detailed explanation how to set up a system based upon FCGI. +Please refer to external documentation for a more detailed explanation how to set up a system based upon FCGI. -**APC** +###Database -APC is an opcode cache. It speeds up the processing of PHP code. +There are scripts like [tuning-primer.sh](http://www.day32.com/MySQL/) and [mysqltuner.pl](http://mysqltuner.pl) that analyze your database server and give hints on values that could be changed. -When APC is enabled, Friendica uses it to store configuration data between different requests. This helps speeding up the page creation time. - -**Database** - -There are scripts like [tuning-primer.sh](http://www.day32.com/MySQL/) and [mysqltuner.pl](http://mysqltuner.pl) that analyzes your database server and give hints on values that could be changed. - -Please enable the slow query log. This helps being aware of performance problems. +Please enable the slow query log. This helps to find performance problems. diff --git a/doc/Install.md b/doc/Install.md index 28b8ba9f7..bd15f10b5 100644 --- a/doc/Install.md +++ b/doc/Install.md @@ -1,118 +1,128 @@ Friendica Installation =============== -We've tried very hard to ensure that Friendica will run on commodity hosting platforms - such as those used to host Wordpress blogs and Drupal websites. But be aware that Friendica is more than a simple web application. It is a complex communications system which more closely resembles an email server than a web server. For reliability and performance, messages are delivered in the background and are queued for later delivery when sites are down. This kind of functionality requires a bit more of the host system than the typical blog. Not every PHP/MySQL hosting provider will be able to support Friendica. Many will. But **please** review the requirements and confirm these with your hosting provider prior to installation. +We've tried very hard to ensure that Friendica will run on commodity hosting platforms - such as those used to host Wordpress blogs and Drupal websites. +But be aware that Friendica is more than a simple web application. +It is a complex communications system which more closely resembles an email server than a web server. +For reliability and performance, messages are delivered in the background and are queued for later delivery when sites are down. +This kind of functionality requires a bit more of the host system than the typical blog. +Not every PHP/MySQL hosting provider will be able to support Friendica. +Many will. +But **please** review the requirements and confirm these with your hosting provider prior to installation. -Also if you encounter installation issues, please let us know via the forums at http://groups.google.com/group/friendica or file an issue at http://bugs.friendica.com . Please be as clear as you can about your operating environment and provide as much detail as possible about any error messages you may see, so that we can prevent it from happening in the future. Due to the large variety of operating systems and PHP platforms in existence we may have only limited ability to debug your PHP installation or acquire any missing modules - but we will do our best to solve any general code issues. +Also if you encounter installation issues, please let us know via the [helper]() or the [developer]() forum or [file an issue](https://github.com/friendica/friendica/issues). +Please be as clear as you can about your operating environment and provide as much detail as possible about any error messages you may see, so that we can prevent it from happening in the future. +Due to the large variety of operating systems and PHP platforms in existence we may have only limited ability to debug your PHP installation or acquire any missing modules - but we will do our best to solve any general code issues. -Before you begin: Choose a domain name or subdomain name for your server. Put some thought into this - because changing it after installation is currently not-supported. Things will break, and some of your friends may have difficulty communicating with you. We plan to address this limitation in a future release. +Before you begin: Choose a domain name or subdomain name for your server. +Put some thought into this. Changing it after installation is currently not supported. +Things will break, and some of your friends may have difficulty communicating with you. +We plan to address this limitation in a future release. -1. Requirements - - Apache with mod-rewrite enabled and "Options All" so you can use a -local .htaccess file +Requirements +--- - - PHP 5.2+. The later the better. You'll need 5.3 for encryption of key exchange conversations. On a Windows environment, 5.2+ might not work as the function dns_get_record() is only available with version 5.3. - - PHP *command line* access with register_argc_argv set to true in the -php.ini file - - curl, gd, mysql, hash and openssl extensions - - some form of email server or email gateway such that PHP mail() works - - mcrypt (optional; used for server-to-server message encryption) +* Apache with mod-rewrite enabled and "Options All" so you can use a local .htaccess file +* PHP 5.2+. The later the better. You'll need 5.3 for encryption of key exchange conversations. On a Windows environment, 5.2+ might not work as the function dns_get_record() is only available with version 5.3. +* PHP *command line* access with register_argc_argv set to true in the php.ini file +* curl, gd, mysql, hash and openssl extensions +* some form of email server or email gateway such that PHP mail() works +* mcrypt (optional; used for server-to-server message encryption) +* Mysql 5.x +* the ability to schedule jobs with cron (Linux/Mac) or Scheduled Tasks (Windows) (Note: other options are presented in Section 7 of this document.) +* Installation into a top-level domain or sub-domain (without a directory/path component in the URL) is preferred. Directory paths will not be as convenient to use and have not been thoroughly tested. +* If your hosting provider doesn't allow Unix shell access, you might have trouble getting everything to work. - - Mysql 5.x +Installation procedure +--- - - ability to schedule jobs with cron (Linux/Mac) or Scheduled Tasks -(Windows) [Note: other options are presented in Section 7 of this document] +###Get Friendica - - Installation into a top-level domain or sub-domain (without a -directory/path component in the URL) is preferred. Directory paths will -not be as convenient to use and have not been thoroughly tested. +Unpack the Friendica files into the root of your web server document area. +If you are able to do so, we recommend using git to clone the source repository rather than to use a packaged tar or zip file. +This makes the software much easier to update. +The Linux command to clone the repository into a directory "mywebsite" would be - - [Dreamhost.com offers all of the necessary hosting features at a -reasonable price. If your hosting provider doesn't allow Unix shell access, -you might have trouble getting everything to work.] - -2. Unpack the Friendica files into the root of your web server document area. - - - If you are able to do so, we recommend using git to clone the source repository rather than to use a packaged tar or zip file. This makes the software much easier to update. The Linux command to clone the repository into a directory "mywebsite" would be - - `git clone https://github.com/friendica/friendica.git mywebsite` - - - and then you can pick up the latest changes at any time with - - `git pull` + git clone https://github.com/friendica/friendica.git mywebsite - - make sure folder *view/smarty3* exists and is writable by webserver +Make sure the folder *view/smarty3* exists and is writable by the webserver user - `mkdir view/smarty3` - - `chmod 777 view/smarty3` + mkdir view/smarty3 + chmod 777 view/smarty3 - - For installing addons - - - First you should be **on** your website folder +Get the addons by going into your website folder. - `cd mywebsite` + cd mywebsite - - Then you should clone the addon repository (separtely) +Clone the addon repository (separately): - `git clone https://github.com/friendica/friendica-addons.git addon` + git clone https://github.com/friendica/friendica-addons.git addon - - For keeping the addon tree updated, you should be on you addon tree and issue a git pull - - `cd mywebsite/addon` - - `git pull` - - - If you copy the directory tree to your webserver, make sure - that you also copy .htaccess - as "dot" files are often hidden - and aren't normally copied. +If you copy the directory tree to your webserver, make sure that you also copy .htaccess - as "dot" files are often hidden and aren't normally copied. +###Create a database -3. Create an empty database and note the access details (hostname, username, password, database name). +Create an empty database and note the access details (hostname, username, password, database name). -4. Visit your website with a web browser and follow the instructions. Please note any error messages and correct these before continuing. +Friendica needs the permission to create and delete fields and tables in its own database. -5. *If* the automated installation fails for any reason, check the following: +###Run the installer - - ".htconfig.php" exists ... If not, edit htconfig.php and change system settings. Rename -to .htconfig.php - - Database is populated. ... If not, import the contents of "database.sql" with phpmyadmin -or mysql command line +Point your web browser to the new site and follow the instructions. +Please note any error messages and correct these before continuing. -6. At this point visit your website again, and register your personal account. +*If* the automated installation fails for any reason, check the following: + +* Does ".htconfig.php" exist? If not, edit htconfig.php and change the system settings. Rename to .htconfig.php +* Is the database is populated? If not, import the contents of "database.sql" with phpmyadmin or mysql command line. + +At this point visit your website again, and register your personal account. Registration errors should all be recoverable automatically. -If you get any *critical* failure at this point, it generally indicates the -database was not installed correctly. You might wish to move/rename -.htconfig.php to another name and empty (called 'dropping') the database -tables, so that you can start fresh. +If you get any *critical* failure at this point, it generally indicates the database was not installed correctly. +You might wish to move/rename .htconfig.php to another name and empty (called 'dropping') the database tables, so that you can start fresh. -7. Set up a cron job or scheduled task to run the poller once every 5-10 -minutes in order to perform background processing. Example: +###Set up the poller - `cd /base/directory; /path/to/php include/poller.php` +Set up a cron job or scheduled task to run the poller once every 5-10 minutes in order to perform background processing. +Example: + + cd /base/directory; /path/to/php include/poller.php Change "/base/directory", and "/path/to/php" as appropriate for your situation. If you are using a Linux server, run "crontab -e" and add a line like the one shown, substituting for your unique paths and settings: -`*/10 * * * * cd /home/myname/mywebsite; /usr/bin/php include/poller.php` + */10 * * * * cd /home/myname/mywebsite; /usr/bin/php include/poller.php -You can generally find the location of PHP by executing "which php". If you -have troubles with this section please contact your hosting provider for -assistance. Friendica will not work correctly if you cannot perform this step. +You can generally find the location of PHP by executing "which php". +If you run into trouble with this section please contact your hosting provider for assistance. +Friendica will not work correctly if you cannot perform this step. -Alternative: You may be able to use the 'poormancron' plugin to perform this step -if you are using a recent Friendica release. To do this, edit the file ".htconfig.php" -and look for a line describing your plugins. On a fresh installation, it will look like +Alternative: You may be able to use the 'poormancron' plugin to perform this step. +To do this, edit the file ".htconfig.php" and look for a line describing your plugins. +On a fresh installation, it will look like this: -`$a->config['system']['addon'] = 'js_upload';` + $a->config['system']['addon'] = 'js_upload'; -This indicates the "js_upload" addon module is enabled. You may add additional -addons/plugins using this same line in the configuration file. Change it to read +It indicates the "js_upload" addon module is enabled. +You may add additional addons/plugins using this same line in the configuration file. +Change it to read -`$a->config['system']['addon'] = 'js_upload,poormancron';` + $a->config['system']['addon'] = 'js_upload,poormancron'; and save your changes. + +Updating your installation with git +--- + +You can get the latest changes at any time with + + cd mywebsite + git pull + +The addon tree has to be updated separately like so: + + cd mywebsite/addon + git pull diff --git a/doc/Installing-Connectors.md b/doc/Installing-Connectors.md index 83d6954e7..4cacc1c45 100644 --- a/doc/Installing-Connectors.md +++ b/doc/Installing-Connectors.md @@ -1,132 +1,92 @@ -Installing Connectors (Facebook/Twitter/StatusNet) +Installing Connectors (Twitter/GNU Social) ================================================== * [Home](help) -Friendica uses plugins to provide connectivity to some networks, such as Facebook and Twitter. +Friendica uses plugins to provide connectivity to some networks, such as Twitter or App.net. -There is also a plugin to post through to an existing account on a Status.Net service. You do not require this to communicate with Status.Net members from Friendica - only if you wish to post to an existing account. +There is also a plugin to post through to an existing account on a GNU Social service. +You only need this to post to an already existing GNU Social account, but not to communicate with GNU Social members in general. -All three of these plugins require an account on the target network. In addition you (or typically the server administrator) will need to obtain an API key to provide authenticated access to your Friendica server. +All three plugins require an account on the target network. +In addition you (or typically the server administrator) will need to obtain an API key to provide authenticated access to your Friendica server. -**Site Configuration** +Site Configuration +--- -Plugins must be installed by the site administrator before they can be used. This is accomplished through the site administration panel. +Plugins must be installed by the site administrator before they can be used. +This is accomplished through the site administration panel. +Each of the connectors also requires an "API key" from the service you wish to connect with. +Some plugins allow you to enter this information in the site administration pages, while others may require you to edit your configuration file (.htconfig.php). +The ways to obtain these keys vary between the services, but they all require an existing account on the target service. +Once installed, these API keys can usually be shared by all site members. -Each of the connectors also requires an "API key" from the service you wish to connect with. Some plugins allow you to enter this information in the site administration pages, while others may require you to edit your configuration file (.htconfig.php). The method for obtaining these keys varies greatly - but almost always requires an existing account on the target service. Once installed, these API keys can usually be shared by all site members. +The details of configuring each service follow (much of this information comes directly from the plugin source files): - -The details of configuring each service follows (much of this information comes directly from the plugin source files): - -**Twitter Plugin for Friendica** +Twitter Plugin for Friendica +--- * Author: Tobias Diekershoff * tobias.diekershoff@gmx.net +* License: 3-clause BSD license -* License:3-clause BSD license - -Configuration: -To use this plugin you need a OAuth Consumer key pair (key & secret) -you can get it from Twitter at https://twitter.com/apps +###Configuration +To use this plugin you need a OAuth Consumer key pair (key & secret). +You can get it from [Twitter](https://twitter.com/apps). Register your Friendica site as "Client" application with "Read & Write" access. -We do not need "Twitter as login". When you've registered the app you get the -OAuth Consumer key and secret pair for your application/site. +We do not need "Twitter as login". +When you've registered the app you get a key pair with an OAuth Consumer key and a secret key for your application/site. +Add this key pair to your global .htconfig.php: -Add this key pair to your global .htconfig.php + $a->config['twitter']['consumerkey'] = 'your consumer_key here'; + $a->config['twitter']['consumersecret'] = 'your consumer_secret here'; -``` -$a->config['twitter']['consumerkey'] = 'your consumer_key here'; -$a->config['twitter']['consumersecret'] = 'your consumer_secret here'; -``` +After this, your users can configure their Twitter account settings from "Settings -> Connector Settings". -After this, your user can configure their Twitter account settings -from "Settings -> Connector Settings". +###More documentation -Documentation: http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/Twitter_Plugin +Find the author's documentation here: [http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/Twitter_Plugin](http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/Twitter_Plugin) -**StatusNet Plugin for Friendica** +GNU Social Plugin for Friendica +--- * Author: Tobias Diekershoff * tobias.diekershoff@gmx.net +* License: 3-clause BSD license -* License:3-clause BSD license +###Configuration -Configuration +When the addon is activated the user has to aquire the following in order to connect to the GNU Social account of choice. -When the addon is activated the user has to aquire the following in order to connect to the StatusNet account of choice. - -* The base URL for the StatusNet API, for identi.ca this is https://identi.ca/api/ +* The base URL for the GNU Social API, for quitter.se this is https://quitter.se/api/ * OAuth Consumer key & secret -To get the OAuth Consumer key pair the user has to +To get the OAuth Consumer key pair the user has to -(a) ask her Friendica admin if a pair already exists or -(b) has to register the Friendica server as a client application on the StatusNet server. +1 ask her Friendica admin if a pair already exists or +2 has to register the Friendica server as a client application on the GNU Social server. -This can be done from the account settings under "Settings -> Connections -> Register an OAuth client application -> Register a new application" on the StatusNet server. +This can be done from the account settings under "Settings -> Connections -> Register an OAuth client application -> Register a new application" on the GNU Social server. During the registration of the OAuth client remember the following: -* Application names must be unique on the StatusNet site, so we recommend a Name of 'friendica-nnnn', replace 'nnnn' with a random number or your website name. +* Application names must be unique on the GNU Social site, so we recommend a Name of 'friendica-nnnn', replace 'nnnn' with a random number or your website name. * there is no callback url * register a desktop client * with read & write access * the Source URL should be the URL of your Friendica server -After the required credentials for the application are stored in the configuration you have to actually connect your Friendica account with StatusNet. This is done from the Settings -> Connector Settings page. Follow the Sign in with StatusNet button, allow access and then copy the security code into the box provided. Friendica will then try to acquire the final OAuth credentials from the API. +After the required credentials for the application are stored in the configuration you have to actually connect your Friendica account with GNU Social. +This is done from the Settings -> Connector Settings page. +Follow the Sign in with GNU Social button, allow access and then copy the security code into the box provided. +Friendica will then try to acquire the final OAuth credentials from the API. -If successful the addon settings will allow you to select to post your public messages to your StatusNet account (have a look behind the little lock symbol beneath the status "editor" on your Home or Network pages). - -Documentation: http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/StatusNet_Plugin - - - -**Installing the Friendica/Facebook connector** - -* register an API key for your site from developer.facebook.com - -This requires a Facebook account, and may require additional authentication in the form of credit card or mobile phone verification. - -a. We'd be very happy if you include "Friendica" in the application name -to increase name recognition. The Friendica icons are also present -in the images directory and may be uploaded as a Facebook app icon. -Use images/friendica-16.jpg for the Icon and images/friendica-128.jpg for the Logo. - -b. The url should be your site URL with a trailing slash. - -You **may** be required to provide a privacy and/or terms of service URL. - -c. Navigate to Set Web->Site URL & Domain -> Website Settings. Set Site URL -to yoursubdomain.yourdomain.com. Set Site Domain to your yourdomain.com. - -d. Install the Facebook plugin on your Friendica site at 'admin/plugins'. You should then see a link for Facebook under 'Plugin Features' on the sidebar of the admin panel. Select that. - -e. Enter the App-ID and App Secret that Facebook gave you. Change any other settings as desired. - - -On Friendica, each member who wishes to use the Facebook connector should visit the Facebook Settings section of their "Settings->Connector Settings" page, and click 'Install Facebook Connector'. - -Choose the appropriate settings for your usage and privacy requirements. - -This will ask you to login to Facebook and grant permission to the -plugin to do its stuff. Allow it to do so. - -You're done. To turn it off visit the Connector Settings page again and -'Remove Facebook posting'. - -Videos 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 also 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. - - - - - +If successful, the addon settings will allow you to select to post your public messages to your GNU Social account (have a look behind the little lock symbol beneath the status "editor" on your Home or Network pages). +###More documentation +Find the author's documentation here: [http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/StatusNet_Plugin](http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/StatusNet_Plugin) diff --git a/doc/Making-Friends.md b/doc/Making-Friends.md index e0b1a254e..8f2985463 100644 --- a/doc/Making-Friends.md +++ b/doc/Making-Friends.md @@ -3,55 +3,101 @@ Making Friends * [Home](help) -Friendship in Friendica can take on a great many different meanings. But let's keep it simple, you want to be friends with somebody. How do you do it? +Friendship in Friendica can take on a great many different meanings. +But let's keep it simple, you want to be friends with somebody. +How do you do it? -The first thing you can do is look at the Directory. The directory is split up into two parts. If you click the directory button, you will be presented with a list of all members (who chose to be listed) on your server. You'll also see a link to the Global Directory. If you click through to the global directory, you will be presented with a list of everybody who chose to be listed across all instances of Friendica. You will also see a "Show Community Forums" link, which will direct you to Groups, Forums and Fanpages. You connect to people, groups and forums in the same way, except groups and forums will automatically accept your introduction request, whereas a human will approve you manually. +The Directories +--- +Friendica has two different kinds of "addressbook": +The directory of the Friendica server you are registered on and the global directory that collects account information across all Friendica instances. -To connect with other Friendica users: +The first thing you can do is look at the **Directory**. +The directory is split up into two parts. +If you click the directory button, you will be presented with a list of all members (who chose to be listed) on your server. -Visit their profile. Just beneath their profile picture will be the word 'Connect' (we're assuming this is an English language profile). +You'll also see a link to the **Global Directory**. +If you click through to the global directory, you will be presented with a list of everybody who chose to be listed across all instances of Friendica. +You will also see a "Show Community Forums" link, which will direct you to Groups, Forums and Fanpages. +You connect to people, groups and forums in the same way, except groups and forums will automatically accept your introduction request, whereas a human will approve you manually. -Click that. It will take you to a "Connect" form. +Connect to other Friendica users +--- -This is going to ask you for your Identity Address. This is necessary so that this person's website can find yours. +Visit their profile. +Just beneath their profile picture will be the word 'Connect' (we're assuming this is an English language profile). +Click that 'Connect' button. +It will take you to a 'Connect' form. + +The form is going to ask you for your Identity Address. +This is necessary so that this person's website can find yours. What do you put in the box? If your Friendica site is called "demo.friendica.com" and your username/nickname on that site is "bob", you would put in "bob@demo.friendica.com". -Notice this looks just like an email address. It was meant to be that way. It's easy for people to remember. +Notice this looks just like an email address. +It was meant to be that way. +It's easy for people to remember. You *could* also put in the URL of your "home" page, such as "http://demo.friendica.com/profile/bob", but the email-style address is certainly easier. -When you've submitted the connection page, it will take you back to your own site where you must then login (if necessary) and verify the connection request on *your* site. Once you've done this, the two websites can communicate with each other to complete the process (after your new friend has approved the request). +When you've submitted the connection page, it will take you back to your own site where you must then login (if necessary) and verify the connection request on *your* site. +Once you've done this, the two websites can communicate with each other to complete the process (after your new friend has approved the request). -If you already know somebody's Identity Address, you can enter it in the "connect" box on your "Contacts" page. This will take you through a similar process. +If you already know somebody's Identity Address, you can enter it in the "connect" box on your "Contacts" page. +This will take you through a similar process. -**Alternate Networks** +Connect to users of alternate networks +--- +###GNU Social, Twitter, Diaspora +You can also use your Identity Address or other people's Identity Addresses to become friends across networks. +The list of possible networks is growing all the time. +If you know (for instance) "bob" on gnusocial.de (a GNU Social site) you could put bob@gnusocial.de into your Contact page and become friends across networks. +(Or you can put in the URL to Bob's gnusocial.de page if you wish). -You can also use your Identity Address or other people's Identity Addresses to become friends across networks. The list of possible networks is growing all the time. If you know (for instance) "bob" on identi.ca (a Status.Net site) you could put bob@identi.ca into your Contact page and become friends across networks. (Or you can put in the URL to Bob's identi.ca page if you wish). You can also be "partial" friends with somebody on Google Buzz by putting in their gmail address. Google Buzz does not yet support all the protocols we need for direct messaging, but you should be able to follow status updates from within Friendica. You can do the same for Twitter accounts and Diaspora accounts. In fact you can "follow" most anybody or any website that produces a syndication feed (RSS/Atom,etc.). If we can find an information stream and a name to attach to the contact, we'll try to connect with them. +You can do the same for Twitter accounts and Diaspora accounts. -If you have supplied your mailbox connection information on your Settings page, you can enter the email address of anybody that has sent you a message recently and have their email messages show up in your social stream. You can also reply to them from within Friendica. +In fact, you can "follow" almost anybody or any website that produces a syndication feed (RSS/Atom,etc.). +If we can find an information stream and a name to attach to the contact, we'll try to connect with them. -People can also become friends with you from other networks. If a friend of yours has an identi.ca account, they can become friends with you by putting your Friendica Identity Address into their identi.ca subscription dialog box. A similar mechanism is available for Diaspora members, by putting your iendtity address into their search bar. +###Email +If you have supplied your mailbox connection information on your Settings page, you can enter the email address of anybody that has sent you a message recently and have their email messages show up in your social stream. +You can also reply to them from within Friendica. -Note: Some versions of StatusNet software may require the full URL to your profile and may not work with the identity address. +People can also become friends with you from other networks. +If a friend of yours has an GNU Social account, they can become friends with you by putting your Friendica Identity Address into their GNU Social subscription dialog box. +A similar mechanism is available for Diaspora members, by putting your identity address into their search bar. -When somebody requests friendship you will receive a notification. You will need to approve this before the friendship is complete. +Note: Some versions of GNU Social software may require the full URL to your profile and may not work with the identity address. -Some networks allow people to send you messages without being friends and without your approval. Friendica does not allow this by default, as it would open a gateway for spam. +Notification +--- +When somebody requests friendship you will receive a notification. +You will need to approve this before the friendship is complete. -When you receive a friendship notification from another Friendica member, you will have the option of allowing them as a "fan" or as a "friend". If they are a fan, they can see what you have to say, including private communications that you send to them, but not vice versa. As a friend, you can both communicate with each other. +Approval +--- +Some networks allow people to send you messages without being friends and without your approval. +Friendica does not allow this by default, as it would open a gateway for spam. -Diaspora uses a different terminology, and you are given the option of allowing them to "share with you", or being full friends. +Unilateral or bilateral friendships +--- +When you receive a friendship notification from another Friendica member, you will have the option of allowing them as a "fan" or as a "friend". +If they are a fan, they can see what you have to say, including private communications that you send to them, but not vice versa. +As a friend, you can both communicate with each other. -Once you have become friends, if you find the person constantly sends you spam or worthless information, you can "Ignore" them - without breaking off the friendship or even alerting them to the fact that you aren't interested in anything they are saying. In many ways they are like a "fan" - but they don't know this. They think they are a friend. +Diaspora uses a different terminology, and you are given the option of allowing them to "share with you", or being full friends. -You can also "block" a person. This completely blocks communications with that person. They may still be able to see your public posts, as can anybody in the world, but they cannot communicate with you directly. +Ignoring, blocking and deleting contacts +--- +Once you have become friends, if you find the person constantly sends you spam or worthless information, you can "Ignore" them - without breaking off the friendship or even alerting them to the fact that you aren't interested in anything they are saying. +In many ways they are like a "fan" - but they don't know this. +They think they are a friend. -You can also delete a friend no matter what the friendship status - which complete removes everything relating to that person from your website. - - - +You can also "block" a person. +This completely blocks communications with that person. +They may still be able to see your public posts, as can anybody in the world, but they cannot communicate with you directly. +You can also delete a friend no matter what the friendship status - which completely removes everything relating to that person from your website. diff --git a/doc/Message-Flow.md b/doc/Message-Flow.md index bb1c063d3..ce0a4248a 100644 --- a/doc/Message-Flow.md +++ b/doc/Message-Flow.md @@ -1,13 +1,15 @@ Friendica Message Flow +=== -This page attempts to document some of the details of how messages get from one person to another in the Friendica network. There are multiple paths, using multiple protocols and message formats. - -Those attempting to understand these message flows should become familiar with (at the minimum) the DFRN protocol document (http://dfrn.org/dfrn.pdf) and the message passing elements of the OStatus stack (salmon and Pubsubhubbub). +This page documents some of the details of how messages get from one person to another in the Friendica network. +There are multiple paths, using multiple protocols and message formats. +Those attempting to understand these message flows should become familiar with (at the minimum) the [DFRN protocol document](http://dfrn.org/dfrn.pdf) and the message passing elements of the OStatus stack (salmon and Pubsubhubbub). Most message passing involves the file include/items.php, which has functions for several feed-related import/export activities. -When a message is posted, all immediate deliveries to all networks are made using include/notifier.php, which chooses how (and to whom) to deliver the message. This file also invokes the local side of all deliveries including DFRN-notify. +When a message is posted, all immediate deliveries to all networks are made using include/notifier.php, which chooses how (and to whom) to deliver the message. +This file also invokes the local side of all deliveries including DFRN-notify. mod/dfrn_notify.php handles the remote side of DFRN-notify. @@ -19,36 +21,42 @@ Push (pubsubhubbub) feeds arrive via mod/pubsub.php DFRN-poll feed imports arrive via include/poller.php as a scheduled task, this implements the local side of the DFRN-poll protocol. - - - Scenario #1. Bob posts a public status message - -This is a public message with no conversation members so no private transport is used. There are two paths it can take - as a bbcode path to DFRN clients, and converted to HTML with the server's PuSH (pubsubhubbub) hubs notified. When a PuSH hub is operational, dfrn-poll clients prefer to receive their information through the PuSH channel. They will fall back on a daily poll in case the hub has delivery issues (this is quite common when using the default Google reference hub). If there is no specified hub or hubs, DFRN clients will poll at a configurable (per-contact) rate at up to 5-minute intervals. Feeds retrieved via dfrn-poll are bbcode and may also contain private conversations which the poller has permissions to see. +--- +This is a public message with no conversation members so no private transport is used. +There are two paths it can take - as a bbcode path to DFRN clients, and converted to HTML with the server's PuSH (pubsubhubbub) hubs notified. +When a PuSH hub is operational, dfrn-poll clients prefer to receive their information through the PuSH channel. +They will fall back on a daily poll in case the hub has delivery issues (this is quite common when using the default Google reference hub). +If there is no specified hub or hubs, DFRN clients will poll at a configurable (per-contact) rate at up to 5-minute intervals. +Feeds retrieved via dfrn-poll are bbcode and may also contain private conversations which the poller has permissions to see. Scenario #2. Jack replies to Bob's public message. Jack is on the Friendica/DFRN network. - -Jack uses dfrn-notify to send a direct reply to Bob. Bob then creates a feed of the conversation and sends it to everybody involved in the conversation using dfrn-notify. PuSH hubs are notified that new content is available. The hub or hubs will then retrieve the latest feed and transmit it to all hub subscribers (which may be on different networks). +--- +Jack uses dfrn-notify to send a direct reply to Bob. +Bob then creates a feed of the conversation and sends it to everybody involved in the conversation using dfrn-notify. +PuSH hubs are notified that new content is available. +The hub or hubs will then retrieve the latest feed and transmit it to all hub subscribers (which may be on different networks). Scenario #3. Mary replies to Bob's public message. Mary is on the Friendica/DFRN network. - -Mary uses dfrn-notify to send a direct reply to Bob. Bob then creates a feed of the conversation and sends it to everybody involved in the conversation (excluding himself, the conversation is now sent to both Jack and Mary). Messages are sent using dfrn-notify. Push hubs are also notified that new content is available. The hub or hubs will then retrieve the latest feed and transmit it to all hub subscribers (which may be on different networks). +--- +Mary uses dfrn-notify to send a direct reply to Bob. +Bob then creates a feed of the conversation and sends it to everybody involved in the conversation (excluding himself, the conversation is now sent to both Jack and Mary). +Messages are sent using dfrn-notify. +Push hubs are also notified that new content is available. +The hub or hubs will then retrieve the latest feed and transmit it to all hub subscribers (which may be on different networks). Scenario #4. William replies to Bob's public message. William is on the OStatus network. - -William uses salmon to notify Bob of the reply. Content is html embedded in salmon magic envelope. Bob then creates a feed of the conversation and sends it to all Friendica participants involved in the conversation using dfrn-notify (excluding himself, the conversation is sent to both Jack and Mary). Push hubs are notified that new content is available. The hub or hubs will then retrieve the latest feed and transmit it to all hub subscribers (which may be on different networks). +--- +William uses salmon to notify Bob of the reply. +Content is html embedded in salmon magic envelope. +Bob then creates a feed of the conversation and sends it to all Friendica participants involved in the conversation using dfrn-notify (excluding himself, the conversation is sent to both Jack and Mary). +Push hubs are notified that new content is available. +The hub or hubs will then retrieve the latest feed and transmit it to all hub subscribers (which may be on different networks). Scenario #5. Bob posts a private message to Mary and Jack. - -Message is delivered immediately to Mary and Jack using dfrn_notify. Public hubs are not notified. Requeueing is attempted in case of timeout. Replies follow the same flow as the public replies except that hubs are not notified and message is never made available in the public feed. The entire conversation is also made available to Mary and Jack (and nobody else) through their dfrn-poll personalised feed. - - - - - - - - - - - +--- +Message is delivered immediately to Mary and Jack using dfrn_notify. +Public hubs are not notified. +Requeueing is attempted in case of timeout. +Replies follow the same flow as the public replies except that hubs are not notified and message is never made available in the public feed. +The entire conversation is also made available to Mary and Jack (and nobody else) through their dfrn-poll personalised feed. diff --git a/doc/Move-Account.md b/doc/Move-Account.md index 35c50259e..19e6f1155 100644 --- a/doc/Move-Account.md +++ b/doc/Move-Account.md @@ -1,35 +1,27 @@ -Move Account +How to move your account between servers ============ * [Home](help) -! **this is an experimental feature** +! **This is an experimental feature** -** How to move an account between servers ** +* Go to "Settings" -> "[Export personal data](uexport)" +* Click on "Export account" to save your account data. +* **Save the file in a secure place!** It contains your details, your contacts, groups, and personal settings. It also contains your secret keys to authenticate yourself to your contacts. +* Go to your new server, and open *http://newserver.com/uimport* (there is not a direct link to this page at the moment). +* Do NOT create a new account prior to importing your old settings - uimport should be used *instead* of register. +* Load your saved account file and click "Import". +* After the move, the account on the old server will not work reliably anymore, and should be not used. -Go to "Settings" -> "[Export personal data](uexport)" -Click on "Export account" to save your account data. -This file contains your details, your contacts, groups, and personal settings. -It also contains your secret keys to authenticate yourself to your contacts: -**save this file in a secure place**! - -Go to your new server, and open *http://newserver.com/uimport* (there is not a -direct link to this page at the moment). - -Do NOT create a new account prior to importing your old settings - uimport should be used *instead* of register. - -Load your saved account file and click "Import". +Friendica contacts +--- Friendica will recreate your account on the new server, with your contacts and groups. -A message is sent to Friendica contacts, to inform them about your move: if your -contacts are runnning on an updated server, your details on their -side will be automatically updated. -Contacts on Statusnet/Identi.ca or Diaspora will be archived, as we can't inform -them about your move. -You should ask them to remove your contact from their lists and re-add you, and you -should do the same with their contact. - -After the move, the account on the old server will not work reliably anymore, and -should be not used. +A message is sent to Friendica contacts, to inform them about your move: +If your contacts are runnning on an updated server, your details on their side will be automatically updated. +GNU Social/Diaspora contacts +--- +Contacts on GNU Social or Diaspora will be archived, as we can't inform them about your move. +You should ask them to remove your contact from their lists and re-add you, and you should do the same with their contact. diff --git a/doc/Plugins.md b/doc/Plugins.md index 833c1d200..dcd6e3b05 100644 --- a/doc/Plugins.md +++ b/doc/Plugins.md @@ -1,10 +1,18 @@ Friendica Addon/Plugin development ========================== -Please see the sample addon 'randplace' for a working example of using some of these features. The facebook addon provides an example of integrating both "addon" and "module" functionality. Addons work by intercepting event hooks - which must be registered. Modules work by intercepting specific page requests (by URL path). +Please see the sample addon 'randplace' for a working example of using some of these features. +Addons work by intercepting event hooks - which must be registered. +Modules work by intercepting specific page requests (by URL path). - -Plugin names cannot contain spaces or other punctuation and are used as filenames and function names. You may supply a "friendly" name within the comment block. Each addon must contain both an install and an uninstall function based on the addon/plugin name. For instance "plugin1name_install()". These two functions take no arguments and are usually responsible for registering (and unregistering) event hooks that your plugin will require. The install and uninstall functions will also be called (i.e. re-installed) if the plugin changes after installation - therefore your uninstall should not destroy data and install should consider that data may already exist. Future extensions may provide for "setup" amd "remove". +Plugin names cannot contain spaces or other punctuation and are used as filenames and function names. +You may supply a "friendly" name within the comment block. +Each addon must contain both an install and an uninstall function based on the addon/plugin name. +For instance "plugin1name_install()". +These two functions take no arguments and are usually responsible for registering (and unregistering) event hooks that your plugin will require. +The install and uninstall functions will also be called (i.e. re-installed) if the plugin changes after installation. +Therefore your uninstall should not destroy data and install should consider that data may already exist. +Future extensions may provide for "setup" amd "remove". Plugins should contain a comment block with the four following parameters: @@ -15,63 +23,71 @@ Plugins should contain a comment block with the four following parameters: * Author: John Q. Public */ - - - Register your plugin hooks during installation. register_hook($hookname, $file, $function); $hookname is a string and corresponds to a known Friendica hook. -$file is a pathname relative to the top-level Friendica directory. This *should* be 'addon/plugin_name/plugin_name.php' in most cases. +$file is a pathname relative to the top-level Friendica directory. +This *should* be 'addon/plugin_name/plugin_name.php' in most cases. $function is a string and is the name of the function which will be executed when the hook is called. - +Arguments +--- Your hook callback functions will be called with at least one and possibly two arguments - function myhook_function(&$a, &$b) { - } -If you wish to make changes to the calling data, you must declare them as -reference variables (with '&') during function declaration. +If you wish to make changes to the calling data, you must declare them as reference variables (with '&') during function declaration. -$a is the Friendica 'App' class - which contains a wealth of information -about the current state of Friendica, such as which module has been called, -configuration info, the page contents at the point the hook was invoked, profile -and user information, etc. It is recommeded you call this '$a' to match its usage -elsewhere. +###$a +$a is the Friendica 'App' class. +It contains a wealth of information about the current state of Friendica: -$b can be called anything you like. This is information which is specific to the hook -currently being processed, and generally contains information that is being immediately -processed or acted on that you can use, display, or alter. Remember to declare it with -'&' if you wish to alter it. +* which module has been called, +* configuration information, +* the page contents at the point the hook was invoked, +* profile and user information, etc. + +It is recommeded you call this '$a' to match its usage elsewhere. + +###$b +$b can be called anything you like. +This is information specific to the hook currently being processed, and generally contains information that is being immediately processed or acted on that you can use, display, or alter. +Remember to declare it with '&' if you wish to alter it. Modules -------- -Plugins/addons may also act as "modules" and intercept all page requests for a given URL path. In order for a plugin to act as a module it needs to define a function "plugin_name_module()" which takes no arguments and need not do anything. +Plugins/addons may also act as "modules" and intercept all page requests for a given URL path. +In order for a plugin to act as a module it needs to define a function "plugin_name_module()" which takes no arguments and needs not do anything. + +If this function exists, you will now receive all page requests for "http://my.web.site/plugin_name" - with any number of URL components as additional arguments. +These are parsed into an array $a->argv, with a corresponding $a->argc indicating the number of URL components. +So http://my.web.site/plugin/arg1/arg2 would look for a module named "plugin" and pass its module functions the $a App structure (which is available to many components). +This will include: -If this function exists, you will now receive all page requests for "http://my.web.site/plugin_name" - with any number of URL components as additional arguments. These are parsed into an array $a->argv, with a corresponding $a->argc indicating the number of URL components. So http://my.web.site/plugin/arg1/arg2 would look for a module named "plugin" and pass its module functions the $a App structure (which is available to many components). This will include: $a->argc = 3 $a->argv = array(0 => 'plugin', 1 => 'arg1', 2 => 'arg2'); -Your module functions will often contain the function plugin_name_content(&$a), which defines and returns the page body content. They may also contain plugin_name_post(&$a) which is called before the _content function and typically handles the results of POST forms. You may also have plugin_name_init(&$a) which is called very early on and often does module initialisation. - +Your module functions will often contain the function plugin_name_content(&$a), which defines and returns the page body content. +They may also contain plugin_name_post(&$a) which is called before the _content function and typically handles the results of POST forms. +You may also have plugin_name_init(&$a) which is called very early on and often does module initialisation. Templates ---------- -If your plugin need some template, you can use Friendica template system. Friendica use [smarty3](http://www.smarty.net/) as template engine. +If your plugin needs some template, you can use the Friendica template system. +Friendica uses [smarty3](http://www.smarty.net/) as a template engine. -Put your tpl files in *templates/* subfolder of your plugin. +Put your tpl files in the *templates/* subfolder of your plugin. -In your code, like in function plugin_name_content(), load template file and execute it passing needed values: +In your code, like in the function plugin_name_content(), load the template file and execute it passing needed values: # load template file. first argument is the template name, # second is the plugin path relative to friendica top folder @@ -80,143 +96,185 @@ In your code, like in function plugin_name_content(), load template file and exe # apply template. first argument is the loaded template, # second an array of 'name'=>'values' to pass to template $output = replace_macros($tpl,array( - 'title' => 'My beautifull plugin', + 'title' => 'My beautiful plugin', )); -See also wiki page [Quick Template Guide](https://github.com/friendica/friendica/wiki/Quick-Template-Guide) +See also the wiki page [Quick Template Guide](https://github.com/friendica/friendica/wiki/Quick-Template-Guide). -Current hooks: --------------- +Current hooks +------------- -**'authenticate'** - called when a user attempts to login. - $b is an array - 'username' => the supplied username - 'password' => the supplied password - 'authenticated' => set this to non-zero to authenticate the user. - 'user_record' => successful authentication must also return a valid user record from the database +###'authenticate' +'authenticate' is called when a user attempts to login. +$b is an array containing: -**'logged_in'** - called after a user has successfully logged in. - $b contains the $a->user array + 'username' => the supplied username + 'password' => the supplied password + 'authenticated' => set this to non-zero to authenticate the user. + 'user_record' => successful authentication must also return a valid user record from the database +###'logged_in' +'logged_in' is called after a user has successfully logged in. +$b contains the $a->user array. -**'display_item'** - called when formatting a post for display. - $b is an array - 'item' => The item (array) details pulled from the database - 'output' => the (string) HTML representation of this item prior to adding it to the page +###'display_item' +'display_item' is called when formatting a post for display. +$b is an array: -**'post_local'** - called when a status post or comment is entered on the local system - $b is the item array of the information to be stored in the database - {Please note: body contents are bbcode - not HTML) + 'item' => The item (array) details pulled from the database + 'output' => the (string) HTML representation of this item prior to adding it to the page -**'post_local_end'** - called when a local status post or comment has been stored on the local system - $b is the item array of the information which has just been stored in the database - {Please note: body contents are bbcode - not HTML) +###'post_local' +* called when a status post or comment is entered on the local system +* $b is the item array of the information to be stored in the database +* Please note: body contents are bbcode - not HTML -**'post_remote'** - called when receiving a post from another source. This may also be used to post local activity or system generated messages. - $b is the item array of information to be stored in the database and the item - body is bbcode. +###'post_local_end' +* called when a local status post or comment has been stored on the local system +* $b is the item array of the information which has just been stored in the database +* Please note: body contents are bbcode - not HTML -**'settings_form'** - called when generating the HTML for the user Settings page - $b is the (string) HTML of the settings page before the final '' tag. +###'post_remote' +* called when receiving a post from another source. This may also be used to post local activity or system generated messages. +* $b is the item array of information to be stored in the database and the item body is bbcode. -**'settings_post'** - called when the Settings pages are submitted. - $b is the $_POST array +###'settings_form' +* called when generating the HTML for the user Settings page +* $b is the (string) HTML of the settings page before the final '' tag. -**'plugin_settings'** - called when generating the HTML for the addon settings page - $b is the (string) HTML of the addon settings page before the final '' tag. +###'settings_post' +* called when the Settings pages are submitted +* $b is the $_POST array -**'plugin_settings_post'** - called when the Addon Settings pages are submitted. - $b is the $_POST array +###'plugin_settings' +* called when generating the HTML for the addon settings page +* $b is the (string) HTML of the addon settings page before the final '' tag. -**'profile_post'** - called when posting a profile page. - $b is the $_POST array +###'plugin_settings_post' +* called when the Addon Settings pages are submitted +* $b is the $_POST array -**'profile_edit'** - called prior to output of profile edit page - $b is array - 'profile' => profile (array) record from the database - 'entry' => the (string) HTML of the generated entry +###'profile_post' +* called when posting a profile page +* $b is the $_POST array +###'profile_edit' +'profile_edit' is called prior to output of profile edit page. +$b is an array containing: -**'profile_advanced'** - called when the HTML is generated for the 'Advanced profile', corresponding to the 'Profile' tab within a person's profile page. - $b is the (string) HTML representation of the generated profile - (The profile array details are in $a->profile) + 'profile' => profile (array) record from the database + 'entry' => the (string) HTML of the generated entry -**'directory_item'** - called from the Directory page when formatting an item for display - $b is an array - 'contact' => contact (array) record for the person from the database - 'entry' => the (string) HTML of the generated entry +###'profile_advanced' +* called when the HTML is generated for the 'Advanced profile', corresponding to the 'Profile' tab within a person's profile page +* $b is the (string) HTML representation of the generated profile +* The profile array details are in $a->profile. -**'profile_sidebar_enter'** - called prior to generating the sidebar "short" profile for a page - $b is (array) the person's profile array +###'directory_item' +'directory_item' is called from the Directory page when formatting an item for display. +$b is an array: -**'profile_sidebar'** - called when generating the sidebar "short" profile for a page - $b is an array - 'profile' => profile (array) record for the person from the database - 'entry' => the (string) HTML of the generated entry + 'contact' => contact (array) record for the person from the database + 'entry' => the (string) HTML of the generated entry -**'contact_block_end'** - called when formatting the block of contacts/friends on a profile sidebar has completed - $b is an array - 'contacts' => array of contacts - 'output' => the (string) generated HTML of the contact block +###'profile_sidebar_enter' +* called prior to generating the sidebar "short" profile for a page +* $b is the person's profile array -**'bbcode'** - called during conversion of bbcode to html - $b is (string) converted text +###'profile_sidebar' +'profile_sidebar is called when generating the sidebar "short" profile for a page. +$b is an array: -**'html2bbcode'** - called during conversion of html to bbcode (e.g. remote message posting) - $b is (string) converted text + 'profile' => profile (array) record for the person from the database + 'entry' => the (string) HTML of the generated entry -**'page_header'** - called after building the page navigation section - $b is (string) HTML of nav region +###'contact_block_end' +is called when formatting the block of contacts/friends on a profile sidebar has completed. +$b is an array: -**'personal_xrd'** - called prior to output of personal XRD file. - $b is an array - 'user' => the user record for the person - 'xml' => the complete XML to be output + 'contacts' => array of contacts + 'output' => the (string) generated HTML of the contact block -**'home_content'** - called prior to output home page content, shown to unlogged users - $b is (string) HTML of section region +###'bbcode' +* called during conversion of bbcode to html +* $b is a string converted text -**'contact_edit'** - called when editing contact details on an individual from the Contacts page - $b is (array) - 'contact' => contact record (array) of target contact - 'output' => the (string) generated HTML of the contact edit page +###'html2bbcode' +* called during conversion of html to bbcode (e.g. remote message posting) +* $b is a string converted text -**'contact_edit_post'** - called when posting the contact edit page - $b is the $_POST array +###'page_header' +* called after building the page navigation section +* $b is a string HTML of nav region -**'init_1'** - called just after DB has been opened and before session start - $b is not used or passed +###'personal_xrd' +'personal_xrd' is called prior to output of personal XRD file. +$b is an array: -**'page_end'** - called after HTML content functions have completed - $b is (string) HTML of content div + 'user' => the user record for the person + 'xml' => the complete XML to be output -**'avatar_lookup'** - called when looking up the avatar - $b is (array) - 'size' => the size of the avatar that will be looked up - 'email' => email to look up the avatar for - 'url' => the (string) generated URL of the avatar +###'home_content' +* called prior to output home page content, shown to unlogged users +* $b is (string) HTML of section region -**'emailer_send_prepare'** - called from Emailer::send() before building the mime message - $b is (array) , params to Emailer::send() - 'fromName' => name of the sender - 'fromEmail' => email fo the sender - 'replyTo' => replyTo address to direct responses - 'toEmail' => destination email address - 'messageSubject' => subject of the message - 'htmlVersion' => html version of the message - 'textVersion' => text only version of the message - 'additionalMailHeader' => additions to the smtp mail header +###'contact_edit' +is called when editing contact details on an individual from the Contacts page. +$b is an array: -**'emailer_send'** - called before calling PHP's mail() - $b is (array) , params to mail() - 'to' - 'subject' - 'body' - 'headers' + 'contact' => contact record (array) of target contact + 'output' => the (string) generated HTML of the contact edit page +###'contact_edit_post' +* called when posting the contact edit page. +* $b is the $_POST array -A complete list of all hook callbacks with file locations (generated 14-Feb-2012): Please see the source for details of any hooks not documented above. +###'init_1' +* called just after DB has been opened and before session start +* $b is not used or passed +###'page_end' +* called after HTML content functions have completed +* $b is (string) HTML of content div + +###'avatar_lookup' +'avatar_lookup' is called when looking up the avatar. +$b is an array: + + 'size' => the size of the avatar that will be looked up + 'email' => email to look up the avatar for + 'url' => the (string) generated URL of the avatar + +###'emailer_send_prepare' +'emailer_send_prepare' called from Emailer::send() before building the mime message. +$b is an array, params to Emailer::send() + + 'fromName' => name of the sender + 'fromEmail' => email fo the sender + 'replyTo' => replyTo address to direct responses + 'toEmail' => destination email address + 'messageSubject' => subject of the message + 'htmlVersion' => html version of the message + 'textVersion' => text only version of the message + 'additionalMailHeader' => additions to the smtp mail header + +###'emailer_send' +is called before calling PHP's mail(). +$b is an array, params to mail() + + 'to' + 'subject' + 'body' + 'headers' + +###'nav_info' +is called after the navigational menu is build in include/nav.php. +$b is an array containing $nav from nav.php. + +Complete list of hook callbacks +--- + +Here is a complete list of all hook callbacks with file locations (as of 14-Feb-2012). Please see the source for details of any hooks not documented above. boot.php: call_hooks('login_hook',$o); diff --git a/doc/Profiles.md b/doc/Profiles.md index 716db908e..715d27d95 100644 --- a/doc/Profiles.md +++ b/doc/Profiles.md @@ -3,49 +3,83 @@ Profiles * [Home](help) -Friendica has unlimited profiles. You may use different profiles to show different "sides of yourself" to different audiences. +Friendica has unlimited profiles. +You may use different profiles to show different "sides of yourself" to different audiences. -You always have a profile known as your "default" or "public" profile. This profile is always available to the general public and cannot be hidden (there may be rare exceptions on privately run or disconnected sites). You may, and probably should restrict the information you make available on your public profile. +Default / public profile +--- +You always have a profile known as your "default" or "public" profile. +This profile is always available to the general public and cannot be hidden (there may be rare exceptions on privately run or disconnected sites). +You may, and probably should restrict the information you make available on your public profile. -That said, if you want other friends to be able to find you, it helps to have the following information in your public profile... +That said, if you want other friends to be able to find you, it helps to have the following information in your public profile: * Your real name * A photo of **you** * Your location on the planet, at least to a country level. -Without this basic information, you could get very lonely here. Most people (even your best friends) will not try and connect with somebody that has a fake name or doesn't contain a real photo. +Without this basic information, you could get very lonely here. +Most people (even your best friends) will not try and connect with somebody that has a fake name or doesn't contain a real photo. -In addition, if you'd like to meet people that share some general interests with you, please take a moment and add some "Public Keywords" to your profile. Such as "music, linux, photography" or whatever. You can add as many keywords as you like. +In addition, if you'd like to meet people that share some general interests with you, please take a moment and add some "Public Keywords" to your profile. +Such as "music, linux, photography" or whatever. +You can add as many keywords as you like. +Your default or public profile is also shown to contacts on other networks, since they do not have the ability to view your private profiles. +Only members of the Friendica network can see alternate/private profiles. -Your default or public profile is also shown to contacts on other networks, since they do not have the ability to view your private profiles. Only members of the Friendica network can see alternate/private profiles. +Alternate profiles +--- +To create an alternate profile, select "Profiles" from the menu of your Friendica site. +You may edit an existing profile, change the profile photo, or create a new profile. +You may also create a "clone" of an existing profile if you only wish to change a few items but don't wish to enter all the information again. +To assign a profile to specific persons, select the person from your "Contacts" page and click the pencil "Edit" icon. +You will find a dropdown box listing the various profiles available. +If this box is not selectable, the person is not in a supported network and cannot be assigned a specific profile. -To create an alternate profile, select "Profiles" from the menu of your Friendica site. You may edit an existing profile, change the profile photo, or create a new profile. You may also create a "clone" of an existing profile if you only wish to change a few items but don't wish to enter all the information again. +Once a profile has been selected, when the person views your profile from one of the "magic profile links" on their site, they will see the private profile you have assigned. +If they are not logged into their site or view your profile from elsewhere, they will see your public profile. -To assign a profile to specific persons, select the person from your "Contacts" page and click the pencil "Edit" icon. You will find a dropdown box listing the various profiles available. If this box is not selectable, the person is not in a supported network and cannot be assigned a specific profile. +A magic profile link is indicated by a special cursor when hovering over a contact's name or photo. +Currently this cursor is a hand next to a small padlock. +These magic cursors indicate that by following the link, you are able to access special areas of the other person's pages which are only available to friends and may not be available to the general public. -Once a profile has been selected, when the person views your profile from one of the "magic profile links" on their site, they will see the private profile you have assigned. If they are not logged into their site or view your profile from elsewhere, they will see your public profile. +You may also discover that (assuming you have the proper permissions) you may be able to post directly on the other person's profile (often called a "wall-to-wall" post). +You may also be able to comment directly on posts from while visiting the other person's profile page. -A magic profile link is indicated by a special cursor when hovering over a contact's name or photo. Currently this cursor is a hand next to a small padlock. These magic cursors indicate that by following the link, you are able to access special areas of the other person's pages which are only available to friends and may not be available to the general public. - -You may also discover that (assuming you have the proper permissions) you may be able to post directly on the other person's profile (often called a "wall-to-wall" post). You may also be able to comment directly on posts from while visiting the other person's profile page. - -There are two settings which allow you to publish your profile to a directory and ensure that it can be found by others. You can change these through settings on the "Settings" page. One setting allows you to publish your profile in the site directory of this Friendica server. Another option (this may have been disabled by the site creator) allows you to publish your profile in the "Global Directory". This is a mega directory which contains people from many other Friendica installations world-wide. +There are two settings which allow you to publish your profile to a directory and ensure that it can be found by others. +You can change these through settings on the "Settings" page. +One setting allows you to publish your profile in the site directory of this Friendica server. +Another option (this may have been disabled by the site creator) allows you to publish your profile in the "Global Directory". +This is a mega directory which contains people from many other Friendica installations world-wide. If you do not wish to be visible to any of these sites, you may leave your profile unpublished. -Although you may have multiple profiles, you only have one profile photo. This is intentional. In early tests we experimented with different photos for each profile and found it was very confusing for people. They might see a different picture depending on what website they visited or what conversation they were in, and often alerted them to the fact that other people might be able to see different profiles of you than they could see. +Although you may have multiple profiles, you only have one profile photo. +This is intentional. +In early tests we experimented with different photos for each profile and found it was very confusing for people. +They might see a different picture depending on what website they visited or what conversation they were in, and often alerted them to the fact that other people might be able to see different profiles of you than they could see. (But you can use the rich-text information boxes within a profile such as "Tell us about yourself" and link other photos onto the page.) -**Keywords and Directory Search** +Keywords and Directory Search +--- +On the site Directory page, you may search for people with published profiles who are on this site. +The search is typically for your nickname or part of your full name. +However this search will also match against other profile fields - such as gender, location, "about", work, and education. +You may also include "Keywords" in your default profile - which may be used to search for common interests with other members. +You have two sets of keywords available - public and private. +Private keywords are *not* visible to anybody. +You could use these keywords to locate people who share membership in secret societies, or that share a love of fishing (for example) - without making this information visible on your public profile. +Public keywords are used in the friend suggestion tool and although they aren't readily visible, they may be seen by viewing the HTML of your profile page. -On the site Directory page, you may search for people with published profiles who are on this site. The search is typically for your nickname or part of your full name. However this search will also match against other profile fields - such as gender, location, "about", work, and education. You may also include "Keywords" in your default profile - which may be used to search for common interests with other members. You have two sets of keywords available - public and private. Private keywords are *not* visible to anybody. You could use these keywords to locate people who share membership in secret societies, or that share a love of fishing (for example) - without making this information visible on your public profile. Public keywords are used in the friend suggestion tool and although they aren't readily visible, they may be seen by viewing the HTML of your profile page. +Directory searches are also able to use "boolean" logic so that you can search for "+lesbian +Florida" and find those who's sexual preference (or keywords) contain the world "lesbian" and that live in Florida. +See the section on "Topical Tags" on the [Tags-and-Mentions](help/Tags-and-Mentions) page for more information on performing boolean searches. -Directory searches are also able to use "boolean" logic so that you can search for "+lesbian +Florida" and find those who's sexual preference (or keywords) contain the world "lesbian" and that live in Florida. See the section on "Topical Tags" on the [Tags-and-Mentions](help/Tags-and-Mentions) page for more information on performing boolean searches. - -On your Contacts page is a link to "Find People with Shared Interests" (unless your site administrator has disabled the global directory). This will combine both your public and private keywords, and find people in the global directory who have matching and/or similar keywords. (Your private keywords are not identified or stored on the global directory). The more keywords you provide, the more relevant the search results that are returned. These are sorted by relevance. You may discover that you are the first person on the list - because you are very likely the most relevant match for your keywords in the directory. - - - \ No newline at end of file +On your Contacts page is a link to "Find People with Shared Interests" (unless your site administrator has disabled the global directory). +This will combine both your public and private keywords, and find people in the global directory who have matching and/or similar keywords. +(Your private keywords are not identified or stored on the global directory). +The more keywords you provide, the more relevant the search results that are returned. +These are sorted by relevance. +You may discover that you are the first person on the list - because you are very likely the most relevant match for your keywords in the directory. diff --git a/doc/Remove-Account.md b/doc/Remove-Account.md index e33c2b06e..4b4853b6b 100644 --- a/doc/Remove-Account.md +++ b/doc/Remove-Account.md @@ -7,8 +7,16 @@ We don't like to see people leave Friendica, but if you need to remove your acco http://sitename/removeme -with your web browser. You will need to be logged in at the time. +with your web browser. +You will need to be logged in at the time. -You will be asked for your password to confirm the request. If this matches your stored password, your account will immediately be blocked to all probing. Unlike some social networks we do **not** hold onto it for a grace period in case you change your mind. All your content and user data, etc is instantly removed. For all intents and purposes, the account is gone in moments. +You will be asked for your password to confirm the request. +If this matches your stored password, your account will immediately be blocked to all probing. +Unlike some social networks we do **not** hold onto it for a grace period in case you change your mind. +All your content and user data, etc is instantly removed. For all intents and purposes, the account is gone in moments. -We then send out an "unfriend" signal to all of your contacts. This signal deletes all content on those networks. Unfortunately, due to limitations of the other networks, this only works well with Friendica contacts. We allow four days for this, in case some servers were down and the unfriend signal was queued. After this, we finish off deleting the account. +We then send out an "unfriend" signal to all of your contacts. +This signal deletes all content on those networks. +Unfortunately, due to limitations of the other networks, this only works well with Friendica contacts. +We allow four days for this, in case some servers were down and the unfriend signal was queued. +After this, we finish off deleting the account. diff --git a/doc/Settings.md b/doc/Settings.md index 574ce8dcc..60fff847a 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -1,227 +1,240 @@ -Here are some of the built-in features which don't have an exposed interface or are otherwise undocumented. Configuration settings are stored in the file ".htconfig.php". Edit this file with a text editor to make the desired changes. Several system settings are already documented in that file and will not be covered here. +Settings +=== +Here are some of the built-in features which don't have an exposed interface or are otherwise undocumented. +Configuration settings are stored in the file ".htconfig.php". +Edit this file with a text editor to make the desired changes. +Several system settings are already documented in that file and will not be covered here. -**Hot Keys** +Hot Keys +--- Friendica traps the following keyboard events: -* [Pause] - Pauses "Ajax" update activity. This is the process that provides updates without reloading the page. You may wish to pause it to reduce network usage and/or as a debugging aid for javascript developers. A pause indicator will appear at the lower right hand corner of the page. Hit the [pause] key once again to resume. - +* [Pause] - Pauses "Ajax" update activity. This is the process that provides updates without reloading the page. You may wish to pause it to reduce network usage and/or as a debugging aid for javascript developers. A pause indicator will appear at the lower right hand corner of the page. Hit the [pause] key once again to resume. * [F8] - Displays a language selector -**Birthday Notifications** +Birthday Notifications +--- -Birthday events are published on your Home page for any friends having a birthday in the coming 6 days. In order for your birthday to be discoverable by all of your friends, you must set your birthday (at least the month and day) in your default profile. You are not required to provide the year. +Birthday events are published on your Home page for any friends having a birthday in the coming 6 days. +In order for your birthday to be discoverable by all of your friends, you must set your birthday (at least the month and day) in your default profile. +You are not required to provide the year. -**Configuration settings** +System settings +--- - -**Language** - -System Setting +###Language Please see util/README for information on creating language translations. Config: -``` -$a->config['system']['language'] = 'name'; -``` + $a->config['system']['language'] = 'name'; -**System Theme** +###System Theme -System Setting - -Choose a named theme to be the default system theme (which may be over-ridden by user profiles). Default theme is "default". +Choose a theme to be the default system theme. This can be over-ridden by user profiles. +Default theme is "default". Config: -``` -$a->config['system']['theme'] = 'theme-name'; -``` + $a->config['system']['theme'] = 'theme-name'; -**Verify SSL Certitificates** +###Proxy Configuration Settings -Security setting - -By default Friendica allows SSL communication between websites that have "self-signed" SSL certificates. For the widest compatibility with browsers and other networks we do not recommend using self-signed certificates, but we will not prevent you from using them. SSL encrypts all the data transmitted between sites (and to your browser) and this allows you to have completely encrypted communications, and also protect your login session from hijacking. Self-signed certificates can be generated for free, without paying top-dollar for a website SSL certificate - however these aren't looked upon favourably in the security community because they can be subject to so-called "man-in-the-middle" attacks. If you wish, you can turn on strict certificate checking. This will mean you cannot connect (at all) to self-signed SSL sites. +If your site uses a proxy to connect to the internet, you may use these settings to communicate with the outside world. +The outside world still needs to be able to see your website, or this will not be very useful. Config: -``` -$a->config['system']['verifyssl'] = true; -``` + $a->config['system']['proxy'] = "http://proxyserver.domain:port"; + $a->config['system']['proxyuser'] = "username:password"; -**Allowed Friend Domains** +###Network Timeout -Corporate/Edu enhancement - -Comma separated list of domains which are allowed to establish friendships with this site. Wildcards are accepted. (Wildcard support on Windows platforms requires PHP5.3). By default, any (valid) domain may establish friendships with this site. +How long to wait on a network communication before timing out. +Value is in seconds. +Default is 60 seconds. +Set to 0 for unlimited (not recommended). Config: -``` -$a->config['system']['allowed_sites'] = "sitea.com, *siteb.com"; -``` + $a->config['system']['curl_timeout'] = 60; -**Allowed Email Domains** +###Banner/Logo -Corporate/Edu enhancement - -Comma separated list of domains which are allowed in email addresses for registrations to this site. This can lockout those who are not part of this organisation from registering here. Wildcards are accepted. (Wildcard support on Windows platforms requires PHP5.3). By default, any (valid) email address is allowed in registrations. +Set the content for the site banner. +The default logo is the Friendica logo and name. +You may wish to provide HTML/CSS to style and/or position this content, as it may not be themed by default. Config: -``` -$a->config['system']['allowed_email'] = "sitea.com, *siteb.com"; -``` -**Block Public** + $a->config['system']['banner'] = 'My Great Website'; -Corporate/Edu enhancement +###Maximum Image Size -Set to true to block public access to all otherwise public personal pages on this site unless you are currently logged in. This blocks the viewing of profiles, friends, photos, the site directory and search pages to unauthorised persons. A side effect is that entries from this site will not appear in the global directory. We recommend specifically disabling that also (setting is described elsewhere on this page). Note: this is specifically for sites that desire to be "standalone" and do not wish to be connected to any other Friendica sites. Unauthorised persons will also not be able to request friendship with site members. Default is false. Available in version 2.2 or greater. +Maximum size in bytes of uploaded images. +The default is set to 0, which means no limits. + +Config: + + $a->config['system']['maximagesize'] = 1000000; + +###UTF-8 Regular Expressions + +During registrations, full names are checked using UTF-8 regular expressions. +This requires PHP to have been compiled with a special setting to allow UTF-8 expressions. +If you are completely unable to register accounts, set no_utf to true. +The default is set to false (meaning UTF8 regular expressions are supported and working). Config: -``` -$a->config['system']['block_public'] = true; -``` + $a->config['system']['no_utf'] = true; -**Force Publish** +###Check Full Names -Corporate/Edu enhancement - -By default, each user can choose on their Settings page whether or not to have their profile published in the site directory. This setting forces all -profiles on this site to be listed in the site directory and there is no option provided to the user to change it. Default is false. +You may find a lot of spammers trying to register on your site. +During testing we discovered that since these registrations were automatic, the "Full Name" field was often set to just an account name with no space between first and last name. +If you would like to support people with only one name as their full name, you may change this setting to true. +Default is false. Config: -``` -$a->config['system']['publish_all'] = true; -``` + $a->config['system']['no_regfullname'] = true; -**Global Directory** +###OpenID -Corporate/Edu enhancement - -This configures the URL to update the global directory, and is supplied in the default configuration. The undocumented part is that if this is not set, the global directory is completely unavailable to the application. This allows a private community to be completely isolated from the global mistpark network. - -``` -$a->config['system']['directory_submit_url'] = 'http://dir.friendica.com/submit'; -``` - - -**Proxy Configuration Settings** - -If your site uses a proxy to connect to the internet, you may use these settings to communicate with the outside world (the outside world still needs to be able to see your website, or this will not be very useful). +By default, OpenID may be used for both registration and logins. +If you do not wish to make OpenID facilities available on your system (at all), set 'no_openid' to true. +Default is false. Config: -``` -$a->config['system']['proxy'] = "http://proxyserver.domain:port"; -$a->config['system']['proxyuser'] = "username:password"; -``` + $a->config['system']['no_openid'] = true; -**Network Timeout** +###Multiple Registrations -How long to wait on a network communication before timing out. Value is in seconds. Default is 60 seconds. Set to 0 for unlimited (not recommended). - -Config: -``` -$a->config['system']['curl_timeout'] = 60; -``` - - -**Banner/Logo** - -Set the content for the site banner. Default is the Friendica logo and name. You may wish to provide HTML/CSS to style and/or position this content, as it may not be themed by default. - -Config: -``` -$a->config['system']['banner'] = 'My Great Website'; -``` - - -**Maximum Image Size** - -Maximum size in bytes of uploaded images. Default is 0, which means no limits. - -Config: -``` -$a->config['system']['maximagesize'] = 1000000; -``` - - -**UTF-8 Regular Expressions** - -During registrations, full names are checked using UTF-8 regular expressions. This requires PHP to have been compiled with a special setting to allow UTF-8 expressions. If you are completely unable to register accounts, set no_utf to true. Default is false (meaning UTF8 regular expressions are supported and working). +The ability to create "Pages" requires a person to register more than once. +Your site configuration can block registration (or require approval to register). +By default, logged in users can register additional accounts for use as pages. +These will still require approval if REGISTER_APPROVE is selected. +You may prohibit logged in users from creating additional accounts by setting 'block_extended_register' to true. +Default is false. Config: -``` -$a->config['system']['no_utf'] = true; -``` + $a->config['system']['block_extended_register'] = true; -**Check Full Names** +Security settings +--- -You may find a lot of spammers trying to register on your site. During testing we discovered that since these registrations were automatic, the "Full Name" field was often set to just an account name with no space between first and last name. If you would like to support people with only one name as their full name, you may change this setting to true. Default is false. +###Verify SSL Certitificates + +By default Friendica allows SSL communication between websites that have "self-signed" SSL certificates. +For the widest compatibility with browsers and other networks we do not recommend using self-signed certificates, but we will not prevent you from using them. +SSL encrypts all the data transmitted between sites (and to your browser). This allows you to have completely encrypted communications, and also protect your login session from hijacking. +Self-signed certificates can be generated for free, without paying top-dollar for a website SSL certificate. +However these aren't looked upon favourably in the security community because they can be subject to so-called "man-in-the-middle" attacks. +If you wish, you can turn on strict certificate checking. +This will mean you cannot connect (at all) to self-signed SSL sites. + +Config: + + $a->config['system']['verifyssl'] = true; + +Corporate/Edu enhancements +--- + +###Allowed Friend Domains + +Comma separated list of domains which are allowed to establish friendships with this site. +Wildcards are accepted. +(Wildcard support on Windows platforms requires PHP5.3). +By default, any (valid) domain may establish friendships with this site. + +Config: + + $a->config['system']['allowed_sites'] = "sitea.com, *siteb.com"; + +###Allowed Email Domains + +Comma separated list of domains which are allowed in email addresses for registrations to this site. +This can lockout those who are not part of this organisation from registering here. +Wildcards are accepted. +(Wildcard support on Windows platforms requires PHP5.3). +By default, any (valid) email address is allowed in registrations. + +Config: + + $a->config['system']['allowed_email'] = "sitea.com, *siteb.com"; + +###Block Public + +Set to true to block public access to all otherwise public personal pages on this site unless you are currently logged in. +This blocks the viewing of profiles, friends, photos, the site directory and search pages to unauthorised persons. +A side effect is that entries from this site will not appear in the global directory. +We recommend specifically disabling that also (setting is described elsewhere on this page). +Note: this is specifically for sites that desire to be "standalone" and do not wish to be connected to any other Friendica sites. +Unauthorised persons will also not be able to request friendship with site members. +Default is false. +Available in version 2.2 or greater. Config: -``` -$a->config['system']['no_regfullname'] = true; -``` + $a->config['system']['block_public'] = true; -**OpenID** +###Force Publish -By default, OpenID may be used for both registration and logins. If you do not wish to make OpenID facilities available on your system (at all), set 'no_openid' to true. Default is false. - -Config: -``` -$a->config['system']['no_openid'] = true; -``` - - -**Multiple Registrations** - -The ability to create "Pages" requires a person to register more than once. Your site configuration can block registration (or require approval to register). By default logged in users can register additional accounts for use as pages. These will still require approval if REGISTER_APPROVE is selected. You may prohibit logged in users from creating additional accounts by setting 'block_extended_register' to true. Default is false. +By default, each user can choose on their Settings page whether or not to have their profile published in the site directory. +This setting forces all profiles on this site to be listed in the site directory and there is no option provided to the user to change it. +Default is false. Config: -``` -$a->config['system']['block_extended_register'] = true; -``` + $a->config['system']['publish_all'] = true; -**Developer Settings** +###Global Directory +This configures the URL to update the global directory, and is supplied in the default configuration. +The undocumented part is that if this is not set, the global directory is completely unavailable to the application. +This allows a private community to be completely isolated from the global mistpark network. + + $a->config['system']['directory'] = 'http://dir.friendi.ca'; + +Developer Settings +--- + +### Debugging Most useful when debugging protocol exchanges and tracking down other communications issues. Config: -``` -$a->config['system']['debugging'] = true; -$a->config['system']['logfile'] = 'logfile.out'; -$a->config['system']['loglevel'] = LOGGER_DEBUG; -``` -Turns on detailed debugging logs which will be stored in 'logfile.out' (which must be writeable by the webserver). LOGGER_DEBUG will show a good deal of information about system activity but will not include detailed data. You may also select LOGGER_ALL but due to the volume of information we recommend only enabling this when you are tracking down a specific problem. Other log levels are possible but are not being used at the present time. + $a->config['system']['debugging'] = true; + $a->config['system']['logfile'] = 'logfile.out'; + $a->config['system']['loglevel'] = LOGGER_DEBUG; + +Turns on detailed debugging logs which will be stored in 'logfile.out' (which must be writeable by the webserver). +LOGGER_DEBUG will show a good deal of information about system activity but will not include detailed data. +You may also select LOGGER_ALL but due to the volume of information we recommend only enabling this when you are tracking down a specific problem. +Other log levels are possible but are not being used at the present time. -**PHP error logging** +###PHP error logging Use the following settings to redirect PHP errors to a file. Config: -``` -error_reporting(E_ERROR | E_WARNING | E_PARSE ); -ini_set('error_log','php.out'); -ini_set('log_errors','1'); -ini_set('display_errors', '0'); -``` + error_reporting(E_ERROR | E_WARNING | E_PARSE ); + ini_set('error_log','php.out'); + ini_set('log_errors','1'); + ini_set('display_errors', '0'); -This will put all PHP errors in the file php.out (which must be writeable by the webserver). Undeclared variables are occasionally referenced in the program and therefore we do not recommend using E_NOTICE or E_ALL. The vast majority of issues reported at these levels are completely harmless. Please report to the developers any errors you encounter in the logs using the recommended settings above. They generally indicate issues which need to be resolved. +This will put all PHP errors in the file php.out (which must be writeable by the webserver). +Undeclared variables are occasionally referenced in the program and therefore we do not recommend using E_NOTICE or E_ALL. +The vast majority of issues reported at these levels are completely harmless. +Please report to the developers any errors you encounter in the logs using the recommended settings above. +They generally indicate issues which need to be resolved. If you encounter a blank (white) page when using the application, view the PHP logs - as this almost always indicates an error has occurred. - - - diff --git a/doc/Tags-and-Mentions.md b/doc/Tags-and-Mentions.md index 337525d88..5501d0d58 100644 --- a/doc/Tags-and-Mentions.md +++ b/doc/Tags-and-Mentions.md @@ -23,7 +23,7 @@ You can tag a person on a different network or one that is **not in your social Unless their system blocks unsolicited "mentions", the person tagged will likely receive a "Mention" post/activity or become a direct participant in the conversation in the case of public posts. Please note that Friendica blocks incoming "mentions" from people with no relationship to you. This is a spam prevention measure. -Remote mentions are delivered using the OStatus protocol. This protocol is used by Friendica and StatusNet and several other systems, but is not currently implemented in Diaspora. +Remote mentions are delivered using the OStatus protocol. This protocol is used by Friendica and GNU Social and several other systems, but is not currently implemented in Diaspora. Friendica makes no distinction between people and groups for the purpose of tagging. (Some other networks use !group to indicate a group.) diff --git a/doc/Vagrant.md b/doc/Vagrant.md index 136583c4d..1d23ace13 100644 --- a/doc/Vagrant.md +++ b/doc/Vagrant.md @@ -3,17 +3,25 @@ Vagrant for Friendica Developers * [Home](help) -**Getting started** +Getting started +--------------- -[Vagrant](https://www.vagrantup.com/) is a virtualization solution for developers. No need to setup up a webserver, database etc. before actually starting. Vagrant creates a virtual machine (an Ubuntu 12.04) for you that you can just run inside VirtualBox and start to work directly on Friendica. What you need to do: +[Vagrant](https://www.vagrantup.com/) is a virtualization solution for developers. +No need to setup up a webserver, database etc. before actually starting. +Vagrant creates a virtual machine (an Ubuntu 12.04) for you that you can just run inside VirtualBox and start to work directly on Friendica. +What you need to do: 1. Install VirtualBox and vagrant. -2. Git clone your Friendica repository. Inside, you'll find a "Vagrantfile" and some scripts in the utils folder. -3. Run "vagrant up" from inside the friendica clone. Be patient: When it runs for the first time, it downloads an Ubuntu Server image. +2. Git clone your Friendica repository. +Inside, you'll find a "Vagrantfile" and some scripts in the utils folder. +3. Run "vagrant up" from inside the friendica clone. +Be patient: When it runs for the first time, it downloads an Ubuntu Server image. 4. Run "vagrant ssh" to log into the virtual machine to log in to the VM. -5. Open 192.168.22.10 in a browser to finish the Friendica installation. The mysql database is called "friendica", the mysql user and password both are "root". +5. Open 192.168.22.10 in a browser. +The mysql database is called "friendica", the mysql user and password both are "root". 6. Work on Friendica's code in your git clone on your machine (not in the VM). -7. Check the changes in your browser in the VM. Debug via the "vagrant ssh" login. +7. Check the changes in your browser in the VM. +Debug via the "vagrant ssh" login. 8. Commit and push your changes directly back to Github. If you want to stop vagrant after finishing your work, run the following command @@ -22,16 +30,11 @@ If you want to stop vagrant after finishing your work, run the following command in the development directory. -**Import test data** - -If you want some test data in your vagrant Friendica instance import the database dump friendica_test_data.sql like so (inside the VM): - - $> mysql -u root -p friendica < /vagrant/friendica_test_data.sql - +The vagrant Friendica instance contains a test database. You will then have the following accounts to login: * admin, password admin - * friendica1, password friendica + * friendica1, password friendica1 * friendica2, password friendica2 and so on until friendica5 * friendica1 is connected to all others. friendica1 has two groups: group1 with friendica2 and friendica4, group2 with friendica3 and friendica5. * friendica2 and friendica3 are conntected. friendica4 and friendica5 are connected. diff --git a/doc/api.md b/doc/api.md index f36a79a5e..147c8b751 100644 --- a/doc/api.md +++ b/doc/api.md @@ -1,4 +1,6 @@ -The friendica API aims to be compatible to the [StatusNet API](http://status.net/wiki/Twitter-compatible_API) which aims to be compatible to the [Twitter API 1.0](https://dev.twitter.com/docs/api/1). +Implemented API calls +=== +The Friendica API aims to be compatible to the [GNU Social API](http://skilledtests.com/wiki/Twitter-compatible_API) and the [Twitter API](https://dev.twitter.com/rest/public). Please refer to the linked documentation for further information. @@ -6,13 +8,13 @@ Please refer to the linked documentation for further information. ### General #### Unsupported parameters -* cursor: Not implemented in StatusNet -* trim_user: Not implemented in StatusNet -* contributor_details: Not implemented in StatusNet -* place_id: Not implemented in StatusNet -* display_coordinates: Not implemented in StatusNet +* cursor: Not implemented in GNU Social +* trim_user: Not implemented in GNU Social +* contributor_details: Not implemented in GNU Social +* place_id: Not implemented in GNU Social +* display_coordinates: Not implemented in GNU Social * include_rts: To-Do -* include_my_retweet: Retweets in friendica are implemented in a different way +* include_my_retweet: Retweets in Friendica are implemented in a different way #### Different behaviour * screen_name: The nick name in friendica is only unique in each network but not for all networks. The users are searched in the following priority: Friendica, StatusNet/GNU Social, Diaspora, pump.io, Twitter. If no contact was found by this way, then the first contact is taken. @@ -22,48 +24,183 @@ Please refer to the linked documentation for further information. * cid: Contact id of the user (important for "contact_allow" and "contact_deny") * network: network of the user +### account/rate_limit_status + ### account/verify_credentials #### Parameters * skip_status: Don't show the "status" field. (Default: false) * include_entities: "true" shows entities for pictures and links (Default: false) -### statuses/update, statuses/update_with_media +### conversation/show +Unofficial Twitter command. It shows all direct answers (excluding the original post) to a given id. + #### Parameters -* title: Title of the status -* status: Status in text format -* htmlstatus: Status in HTML format -* in_reply_to_status_id -* lat: latitude -* long: longitude -* media: image data -* source: Application name -* group_allow -* contact_allow -* group_deny -* contact_deny -* network +* id: id of the post +* count: Items per page (default: 20) +* page: page number +* since_id: minimal id +* max_id: maximum id * include_entities: "true" shows entities for pictures and links (Default: false) #### Unsupported parameters -* trim_user -* place_id -* display_coordinates +* include_rts +* trim_user +* contributor_details -### users/search +### direct_messages #### Parameters -* q: name of the user +* count: Items per page (default: 20) +* page: page number +* since_id: minimal id +* max_id: maximum id +* getText: Defines the format of the status field. Can be "html" or "plain" +* include_entities: "true" shows entities for pictures and links (Default: false) #### Unsupported parameters -* page -* count -* include_entities +* skip_status -### users/show +### direct_messages/all +#### Parameters +* count: Items per page (default: 20) +* page: page number +* since_id: minimal id +* max_id: maximum id +* getText: Defines the format of the status field. Can be "html" or "plain" + +### direct_messages/conversation +Shows all direct messages of a conversation +#### Parameters +* count: Items per page (default: 20) +* page: page number +* since_id: minimal id +* max_id: maximum id +* getText: Defines the format of the status field. Can be "html" or "plain" +* uri: URI of the conversation + +### direct_messages/new #### Parameters * user_id: id of the user * screen_name: screen name (for technical reasons, this value is not unique!) +* text: The message +* replyto: ID of the replied direct message +* title: Title of the direct message + +### direct_messages/sent +#### Parameters +* count: Items per page (default: 20) +* page: page number +* since_id: minimal id +* max_id: maximum id +* getText: Defines the format of the status field. Can be "html" or "plain" * include_entities: "true" shows entities for pictures and links (Default: false) +### favorites +#### Parameters +* count: Items per page (default: 20) +* page: page number +* since_id: minimal id +* max_id: maximum id +* include_entities: "true" shows entities for pictures and links (Default: false) + +#### Unsupported parameters +* user_id +* screen_name + +Favorites aren't displayed to other users, so "user_id" and "screen_name". So setting this value will result in an empty array. + +### favorites/create +#### Parameters +* id +* include_entities: "true" shows entities for pictures and links (Default: false) + +### favorites/destroy +#### Parameters +* id +* include_entities: "true" shows entities for pictures and links (Default: false) + +### followers/ids +#### Parameters +* stringify_ids: Should the id numbers be sent as text (true) or number (false)? (default: false) + +#### Unsupported parameters +* user_id +* screen_name +* cursor + +Friendica doesn't allow showing followers of other users. + +### friendica/photo +#### Parameters +* photo_id: Resource id of a photo. + +Returns data of a picture with the given resource. + +### friendica/photos/list + +Returns a list of all photo resources of the logged in user. + +### friends/ids +#### Parameters +* stringify_ids: Should the id numbers be sent as text (true) or number (false)? (default: false) + +#### Unsupported parameters +* user_id +* screen_name +* cursor + +Friendica doesn't allow showing friends of other users. + +### help/test + +### media/upload +#### Parameters +* media: image data + +### oauth/request_token +#### Parameters +* oauth_callback + +#### Unsupported parameters +* x_auth_access_type + +### oauth/access_token +#### Parameters +* oauth_verifier + +#### Unsupported parameters +* x_auth_password +* x_auth_username +* x_auth_mode + +### statuses/destroy +#### Parameters +* id: message number +* include_entities: "true" shows entities for pictures and links (Default: false) + +#### Unsupported parameters +* trim_user + +### statuses/followers +* include_entities: "true" shows entities for pictures and links (Default: false) + +### statuses/friends +* include_entities: "true" shows entities for pictures and links (Default: false) + +### statuses/friends_timeline +#### Parameters +* count: Items per page (default: 20) +* page: page number +* since_id: minimal id +* max_id: maximum id +* exclude_replies: don't show replies (default: false) +* conversation_id: Shows all statuses of a given conversation. +* include_entities: "true" shows entities for pictures and links (Default: false) + +#### Unsupported parameters +* include_rts +* trim_user +* contributor_details + ### statuses/home_timeline #### Parameters * count: Items per page (default: 20) @@ -79,14 +216,12 @@ Please refer to the linked documentation for further information. * trim_user * contributor_details -### statuses/friends_timeline +### statuses/mentions #### Parameters * count: Items per page (default: 20) * page: page number * since_id: minimal id * max_id: maximum id -* exclude_replies: don't show replies (default: false) -* conversation_id: Shows all statuses of a given conversation. * include_entities: "true" shows entities for pictures and links (Default: false) #### Unsupported parameters @@ -107,45 +242,6 @@ Please refer to the linked documentation for further information. #### Unsupported parameters * trim_user -### statuses/show -#### Parameters -* id: message number -* conversation: if set to "1" show all messages of the conversation with the given id -* include_entities: "true" shows entities for pictures and links (Default: false) - -#### Unsupported parameters -* include_my_retweet -* trim_user - -### statuses/retweet -#### Parameters -* id: message number -* include_entities: "true" shows entities for pictures and links (Default: false) - -#### Unsupported parameters -* trim_user - -### statuses/destroy -#### Parameters -* id: message number -* include_entities: "true" shows entities for pictures and links (Default: false) - -#### Unsupported parameters -* trim_user - -### statuses/mentions -#### Parameters -* count: Items per page (default: 20) -* page: page number -* since_id: minimal id -* max_id: maximum id -* include_entities: "true" shows entities for pictures and links (Default: false) - -#### Unsupported parameters -* include_rts -* trim_user -* contributor_details - ### statuses/replies #### Parameters * count: Items per page (default: 20) @@ -159,6 +255,47 @@ Please refer to the linked documentation for further information. * trim_user * contributor_details +### statuses/retweet +#### Parameters +* id: message number +* include_entities: "true" shows entities for pictures and links (Default: false) + +#### Unsupported parameters +* trim_user + +### statuses/show +#### Parameters +* id: message number +* conversation: if set to "1" show all messages of the conversation with the given id +* include_entities: "true" shows entities for pictures and links (Default: false) + +#### Unsupported parameters +* include_my_retweet +* trim_user + +### statuses/update, statuses/update_with_media +#### Parameters +* title: Title of the status +* status: Status in text format +* htmlstatus: Status in HTML format +* in_reply_to_status_id +* lat: latitude +* long: longitude +* media: image data +* source: Application name +* group_allow +* contact_allow +* group_deny +* contact_deny +* network +* include_entities: "true" shows entities for pictures and links (Default: false) +* media_ids: (By now only a single value, no array) + +#### Unsupported parameters +* trim_user +* place_id +* display_coordinates + ### statuses/user_timeline #### Parameters * user_id: id of the user @@ -176,67 +313,31 @@ Please refer to the linked documentation for further information. * trim_user * contributor_details -### conversation/show -Unofficial Twitter command. It shows all direct answers (excluding the original post) to a given id. - -#### Parameters -* id: id of the post -* count: Items per page (default: 20) -* page: page number -* since_id: minimal id -* max_id: maximum id -* include_entities: "true" shows entities for pictures and links (Default: false) - -#### Unsupported parameters -* include_rts -* trim_user -* contributor_details - -### favorites -#### Parameters -* count: Items per page (default: 20) -* page: page number -* since_id: minimal id -* max_id: maximum id -* include_entities: "true" shows entities for pictures and links (Default: false) - -#### Unsupported parameters -* user_id -* screen_name - -Favorites aren't displayed to other users, so "user_id" and "screen_name". So setting this value will result in an empty array. - -### account/rate_limit_status - -### help/test - -### statuses/friends -* include_entities: "true" shows entities for pictures and links (Default: false) - -#### Unsupported parameters -* user_id -* screen_name -* cursor - -Friendica doesn't allow showing friends of other users. - -### statuses/followers -* include_entities: "true" shows entities for pictures and links (Default: false) - -#### Unsupported parameters -* user_id -* screen_name -* cursor - -Friendica doesn't allow showing followers of other users. - ### statusnet/config ### statusnet/version -### friends/ids +#### Unsupported parameters +* user_id +* screen_name +* cursor + +Friendica doesn't allow showing followers of other users. + +### users/search #### Parameters -* stringify_ids: Should the id numbers be sent as text (true) or number (false)? (default: false) +* q: name of the user + +#### Unsupported parameters +* page +* count +* include_entities + +### users/show +#### Parameters +* user_id: id of the user +* screen_name: screen name (for technical reasons, this value is not unique!) +* include_entities: "true" shows entities for pictures and links (Default: false) #### Unsupported parameters * user_id @@ -245,106 +346,91 @@ Friendica doesn't allow showing followers of other users. Friendica doesn't allow showing friends of other users. -### followers/ids -#### Parameters -* stringify_ids: Should the id numbers be sent as text (true) or number (false)? (default: false) - -#### Unsupported parameters -* user_id -* screen_name -* cursor - -Friendica doesn't allow showing followers of other users. - -### direct_messages/new -#### Parameters -* user_id: id of the user -* screen_name: screen name (for technical reasons, this value is not unique!) -* text: The message -* replyto: ID of the replied direct message -* title: Title of the direct message - -### direct_messages/conversation -Shows all direct messages of a conversation -#### Parameters -* count: Items per page (default: 20) -* page: page number -* since_id: minimal id -* max_id: maximum id -* getText: Defines the format of the status field. Can be "html" or "plain" -* uri: URI of the conversation - -### direct_messages/all -#### Parameters -* count: Items per page (default: 20) -* page: page number -* since_id: minimal id -* max_id: maximum id -* getText: Defines the format of the status field. Can be "html" or "plain" - -### direct_messages/sent -#### Parameters -* count: Items per page (default: 20) -* page: page number -* since_id: minimal id -* max_id: maximum id -* getText: Defines the format of the status field. Can be "html" or "plain" -* include_entities: "true" shows entities for pictures and links (Default: false) - -### direct_messages -#### Parameters -* count: Items per page (default: 20) -* page: page number -* since_id: minimal id -* max_id: maximum id -* getText: Defines the format of the status field. Can be "html" or "plain" -* include_entities: "true" shows entities for pictures and links (Default: false) - -#### Unsupported parameters -* skip_status - -### oauth/request_token -#### Parameters -* oauth_callback - -#### Unsupported parameters -* x_auth_access_type - -### oauth/access_token -#### Parameters -* oauth_verifier - -#### Unsupported parameters -* x_auth_password -* x_auth_username -* x_auth_mode - ## Not Implemented API calls -The following list is extracted from the [API source file](https://github.com/friendica/friendica/blob/master/include/api.php) (at the very bottom): -* favorites/create -* favorites/destroy +The following API calls are implemented in GNU Social but not in Friendica: (incomplete) + * statuses/retweets_of_me * friendships/create * friendships/destroy * friendships/exists * friendships/show -* account/update_location * account/update_profile_background_image * account/update_profile_image * blocks/create * blocks/destroy -The following are things from the Twitter API also not implemented in StatusNet: -* statuses/retweeted_to_me -* statuses/retweeted_by_me +The following API calls from the Twitter API aren't implemented neither in Friendica nor in GNU Social: + +* statuses/mentions_timeline +* statuses/retweets/:id +* statuses/oembed +* statuses/retweeters/ids +* statuses/lookup +* direct_messages/show +* search/tweets * direct_messages/destroy -* account/end_session +* friendships/no_retweets/ids +* friendships/incoming +* friendships/outgoing +* friendships/update +* friends/list +* friendships/lookup +* account/settings * account/update_delivery_device -* notifications/follow -* notifications/leave -* blocks/exists -* blocks/blocking -* lists +* account/update_profile +* account/update_profile_background_image +* account/update_profile_image +* blocks/list +* blocks/ids +* users/lookup +* users/show +* users/search +* account/remove_profile_banner +* account/update_profile_banner +* users/profile_banner +* mutes/users/create +* mutes/users/destroy +* mutes/users/ids +* mutes/users/list +* users/suggestions/:slug +* users/suggestions +* users/suggestions/:slug/members +* favorites/list +* lists/list +* lists/statuses +* lists/members/destroy +* lists/memberships +* lists/subscribers +* lists/subscribers/create +* lists/subscribers/show +* lists/subscribers/destroy +* lists/members/create_all +* lists/members/show +* lists/members +* lists/members/create +* lists/destroy +* lists/update +* lists/create +* lists/show +* lists/subscriptions +* lists/members/destroy_all +* lists/ownerships +* saved_searches/list +* saved_searches/show/:id +* saved_searches/create +* saved_searches/destroy/:id +* geo/id/:place_id +* geo/reverse_geocode +* geo/search +* geo/place +* trends/place +* trends/available +* help/configuration +* help/languages +* help/privacy +* help/tos +* trends/closest +* users/report_spam ## Usage Examples ### BASH / cURL @@ -359,4 +445,4 @@ The [RSStoFriedika](https://github.com/pafcu/RSStoFriendika) code can be used as url = server + '/api/statuses/update' urllib2.urlopen(url, urllib.urlencode({'status': message,'group_allow[]':group_allow}, doseq=True)) -There is also a [module for python 3](https://bitbucket.org/tobiasd/python-friendica) for using the API. \ No newline at end of file +There is also a [module for python 3](https://bitbucket.org/tobiasd/python-friendica) for using the API. diff --git a/doc/de/Account-Basics.md b/doc/de/Account-Basics.md index 3319560e7..ddd3d9ef8 100644 --- a/doc/de/Account-Basics.md +++ b/doc/de/Account-Basics.md @@ -6,68 +6,100 @@ Account - Basics **Registrierung** -Nicht alle Friendica-Seiten bieten eine freie Registrierung. Wenn die Registrierung erlaubt ist, zeigt sich sofort ein "Registrieren"-Link unter dem Login-Feld auf der Startseite. Ein Klick auf diesen Link führt zur Registrierungsseite. Die Stärke unseres Netzwerks ist, dass viele verschiedene Seiten komplett kompatible zueinander sind. Wenn die Seite, die du besuchst, eine Registrierung nicht erlaubt oder wenn du glaubst, dass dir eine andere Seite möglicherweise besser gefällt, dann kannst du hier eine Liste von öffentlichen Servern finden und die Seite suchen, die zu deinen Anforderungen passt. +Nicht alle Friendica-Knoten bieten die Möglichkeit zur Registrierung. +Wenn die Registrierung möglich ist, wird ein "Registrieren"-Link unter dem Login-Feld auf der Startseite angezeigt, der zur Registrierungsseite führt. +Die Stärke unseres Netzwerks ist, dass die verschiedenen Knoten komplett kompatibel zueinander sind. +Wenn der Knoten, den Du besuchst, keine Registrierung anbietet, oder wenn Du glaubst, dass Dir eine andere Seite möglicherweise besser gefällt, dann kannst Du hier eine Liste von öffentlichen Servern (Knoten) finden und den Knoten heraus suchen, der am Besten zu Deinen Anforderungen passt. -Wenn du deinen eigenen Server aufsetzen willst, kannst du das ebenfalls machen. Besuche die Friendica-Webseite, um den Code mit den Installationsanleitungen herunterzuladen. Es ist ein einfacher Installationsprozess, den jeder mit ein wenig Erfahrungen im Webseiten-Hosting oder mit grundlegenden Linux-Erfahrungen einfach handhaben kann. +Wenn Du Deinen eigenen Server aufsetzen willst, kannst Du das ebenfalls machen. +Besuche die Friendica-Webseite, um den Code mit den Installationsanleitungen herunterzuladen. +Es ist ein einfacher Installationsprozess, den jeder mit ein wenig Erfahrungen im Webseiten-Hosting oder mit grundlegenden Linux-Erfahrungen einfach handhaben kann. *OpenID* -Das erste Feld auf der Registrierungsseite ist für eine OpenID-Adresse. Wenn du keine OpenID-Adresse hast oder nicht wünschst, diese zu nutzen, dann lasse das Feld frei. Wenn du einen OpenID-Account hast und diesen nutzen willst, gib die Adresse in das Feld ein und klicke auf "Registrieren". Friendica wird versuchen, so viele Informationen wie möglich von deinem OpenID-Provider zu übernehmen, um diese in dein Profil auf dieser Seite einzutragen. +Das erste Feld auf der Registrierungsseite ist für eine OpenID-Adresse. +Wenn Du keine OpenID-Adresse hast oder nicht wünschst, diese zu nutzen, dann lasse das Feld frei. +Wenn Du einen OpenID-Account hast und diesen nutzen willst, gib die Adresse in das Feld ein und klicke auf "Registrieren". +Friendica wird versuchen, so viele Informationen wie möglich von Deinem OpenID-Provider zu übernehmen, um diese in Dein Profil auf dieser Seite einzutragen. *Dein vollständiger Name* -Bitte trage deinen vollständigen Namen **so ein, wie du ihn im System anzeigen lassen willst**. Viele Leute nutzen ihren richtigen Namen hierfür, allerdings besteht für dich keine Pflicht, das auch so zu machen. +Bitte trage Deinen vollständigen Namen **so ein, wie Du ihn im System anzeigen lassen willst**. +Viele Leute nutzen ihren richtigen Namen hierfür, allerdings besteht für dich keine Pflicht, das auch so zu machen. *Email-Adresse* -Bitte trage eine richtige Email-Adresse ein. Deine Email-Adresse wird **niemals** veröffentlicht. Wir benötigen diese, um dir Account-Informationen und die Login-Daten zu schicken. Du erhältst zudem von Zeit zu Zeit Benachrichtigungen über eingegangene Nachrichten oder Punkte, die deine Aufmerksamkeit benötigen. Du hast aber auch die Möglichkeit, diese Nachrichten in deinen Account-Einstellungen komplett abzuschalten. Du musst nicht deine Haupt-Email-Adresse sein, jedoch wird eine funktionierende Adresse benötigt. Ohne dieses kannst du weder dein Initialpasswort erhalten, noch dein Passwort zurücksetzen. Dies ist die einzige persönliche Information, die korrekt sein muss. +Bitte trage eine richtige Email-Adresse ein. +Deine Email-Adresse wird **niemals** veröffentlicht. +Wir benötigen diese, um Dir Account-Informationen und die Login-Daten zu schicken. +Du erhältst zudem von Zeit zu Zeit Benachrichtigungen über eingegangene Nachrichten oder Punkte, die Deine Aufmerksamkeit benötigen. +Du hast aber auch die Möglichkeit, diese Nachrichten in Deinen Account-Einstellungen komplett abzuschalten. + +Du musst nicht Deine Haupt-Email-Adresse sein, jedoch wird eine funktionierende Adresse benötigt. +Ohne dieses kannst Du weder Dein Initialpasswort erhalten, noch Dein Passwort zurücksetzen. +Dies ist die einzige persönliche Information, die korrekt sein muss. *Spitzname/Nickname* -Der Spitzname wird benötigt, um eine Webadresse für viele deiner persönlichen Seiten zu erstellen. Auch wird dieser wie eine Email-Adresse genutzt, wenn eine Verbindung zu anderen Personen hergestellt werden soll. Durch die Art, wie der Spitzname genutzt wird, gibt es bestimmte Einschränkungen. Er darf nur US-ASCII-Textzeichen und Nummern enthalten und er muss zudem mit einem Buchstaben beginnen. Er muss außerdem einzigartig im System sein. Dieser Spitzname wird an vielen Stellen genutzt, um deinen Account zu identifizieren, und kann daher später nicht mehr geändert werden. +Der Spitzname wird benötigt, um eine Webadresse für viele Deiner persönlichen Seiten zu erstellen. +Auch wird dieser wie eine Email-Adresse genutzt, wenn eine Verbindung zu anderen Personen hergestellt werden soll. +Durch die Art, wie der Spitzname genutzt wird, gibt es bestimmte Einschränkungen. Er darf nur US-ASCII-Textzeichen und Nummern enthalten und er muss zudem mit einem Buchstaben beginnen. +Er muss außerdem einzigartig im System sein. +Dieser Spitzname wird an vielen Stellen genutzt, um Deinen Account zu identifizieren, und kann daher später nicht mehr geändert werden. *Verzeichnis-Eintrag* -Das Registrierungsformular erlaubt es dir, direkt auszuwählen, ob du im Onlineverzeichnis aufgelistet wirst oder nicht. Das ist wie ein Telefonbuch und du kannst entscheiden, nicht aufgeführt zu werden. Wir bitten dich, "Ja" zu wählen, so dass dich andere Leute (Freunde, Familie etc.) finden können. Wenn du "Nein" wählst, wirst du hauptsächlich unsichtbar sein und nur wenige Möglichkeiten zur Interaktion haben. Was auch immer du wählst, kann jederzeit nach dem Login in deinen Account-Einstellungen geändert werden. +Das Registrierungsformular erlaubt es dir, direkt auszuwählen, ob Du im Onlineverzeichnis aufgelistet wirst oder nicht. +Das ist wie ein Telefonbuch und Du kannst entscheiden, nicht aufgeführt zu werden. +Wir bitten dich, "Ja" zu wählen, so dass dich andere Leute (Freunde, Familie etc.) finden können. +Wenn Du "Nein" wählst, wirst Du hauptsächlich unsichtbar sein und nur wenige Möglichkeiten zur Interaktion haben. +Was auch immer Du wählst, kann jederzeit nach dem Login in Deinen Account-Einstellungen geändert werden. *Registrierung* -Sobald du die nötigen Informationen eingegeben hast, klicke auf "Registrieren". Eine Email wird an die hinterlegte Email-Adresse geschickt. Bitte prüfe den Posteingang (inkl. dem Spam-Ordner) für die Registrierungsdetails und dein Initialpasswort. +Sobald Du die nötigen Informationen eingegeben hast, klicke auf "Registrieren". +Eine Email mit den Registrierungsdetails und Deinem Initialpasswort wird an die hinterlegte Email-Adresse geschickt. +Bitte prüfe den Posteingang (inkl. dem Spam-Ordner). **Login-Seite** -Gib auf der "Login"-Seite die Informationen ein, die du während der Registrierung erhalten hast. Du kannst entweder deinen Spitznamen oder die Email-Adresse als Login-Namen nutzen. +Gib auf der "Login"-Seite die Informationen ein, die Du mit der oben genannten Email erhalten hast. +Du kannst entweder Deinen Spitznamen oder die Email-Adresse als Login-Namen nutzen. -Wenn du deinen Account nutzt, um mehrfache '[Seiten](help/Pages)' zu verwalten, die die gleiche Email-Adresse benutzen, dann nutze bitte den Spitznamen des Accounts, der verwaltet werden soll. +Wenn Du Deinen Account nutzt, um unterschiedliche '[Seiten](help/Pages)' zu verwalten, die die gleiche Email-Adresse verwenden, dann nutze bitte den Spitznamen des Accounts, der verwaltet werden soll. -*Wenn* dein Account OpenID nutzt, dann kannst du deine OpenID-Adresse als Login-Name nutzen und das Passwort-Feld frei lassen. Du wirst zu deinem OpenID-Anbieter weitergeleitet, wo du deine Anmeldung abschließt. +*Wenn* Dein Account OpenID nutzt, dann kannst Du Deine OpenID-Adresse als Login-Name nutzen und das Passwort-Feld frei lassen. +Du wirst zu Deinem OpenID-Anbieter weitergeleitet, wo Du Deine Anmeldung abschließt. -Wenn OpenID nicht genutzt wird, gib dein Passwort ein. Dieses hast du zu Beginn in der Registrierungsmail erhalten. Dein Passwort ist zeichengenau; Groß- und Kleinschreibung wird beachtet. Prüfe bitte, ob deine Feststelltaste aktiv ist, falls du Schwierigkeiten beim Login hast. +Wenn Du OpenID nicht nutzt, dann gib Dein Passwort ein, das Du mit der Registrierungsmail erhalten hast. +Das Passwort muss genau so geschrieben werden, wie es in der Email steht; Groß- und Kleinschreibung wird beachtet. +Falls Du Schwierigkeiten beim Login hast, prüfe bitte, ob z. B. Deine Feststelltaste aktiv ist. **Passwort ändern** -Besuche nach deinem ersten Login bitte die Einstellungsseite und wechsle das Passwort in eines, dass du dir merken kannst. +Besuche nach Deinem ersten Login bitte die Einstellungsseite und wechsle das Passwort in eines, dass Du Dir merken kannst. **Der Anfang** -Ein ['Tipp für neue Mitglieder'](newmember)-Link zeigt sich in den ersten beiden Wochen auf deiner Startseite, um dir erste Informationen zum Start zu bieten. +Ein ['Tipp für neue Mitglieder'](newmember)-Link zeigt sich in den ersten beiden Wochen auf Deiner Startseite, um Dir erste Informationen zum Start zu bieten. **Persönliche Daten exportieren** -Du kannst eine Kopie deiner persönlichen Daten in einer XML-Datei exportieren. Gehe hierzu in deinen Einstellungen auf "Persönliche Daten exportieren". +Du kannst eine Kopie Deiner persönlichen Daten in einer XML-Datei exportieren. +Gehe hierzu in Deinen Einstellungen auf "Persönliche Daten exportieren". -**Schau dir ebenfalls folgende Seiten an** +**Schau Dir ebenfalls folgende Seiten an** * [Profile](help/Profiles) diff --git a/doc/de/BBCode.md b/doc/de/BBCode.md index 5c0518ce8..d3e205f0f 100644 --- a/doc/de/BBCode.md +++ b/doc/de/BBCode.md @@ -1,138 +1,155 @@ Referenz der Friendica BBCode Tags -======================== - -* [Zur Startseite der Hilfe](help) - -Inline Tags ------ - - +======================== + +* [Zur Startseite der Hilfe](help) + +Inline Tags +----- + +
[b]fett[/b]
: fett - +
[i]kursiv[/i]
: kursiv - +
[u]unterstrichen[/u]
: unterstrichen - +
[s]durchgestrichen[/s]
: durchgestrichen - +
[color=red]rot[/color]
: rot - +
[url=http://www.friendica.com]Friendica[/url]
: Friendica - +
[img]http://friendica.com/sites/default/files/friendika-32.png[/img]
: Immagine/foto - +
[size=xx-small]kleiner Text[/size]
: kleiner Text - +
[size=xx-large]groß Text[/size]
: großer Text - +
[size=20]exakte Textgröße[/size] (Textgröße kann jede Zahl sein, in Pixeln)
: exakte Größe - - - - - - - -Block Tags ------ - -
[code]Code[/code]
- -Code - -

 

- -
[quote]Zitat[/quote]
- -
Zitat
- -

 

- -
[quote=Autor]Der Autor? Ich? Nein, nein, nein...[/quote]
- -Autor hat geschrieben:
Der Autor? Ich? Nein, nein, nein...
- -

 

- -
[center]zentrierter Text[/center]
- -
zentrierter Text
- -

 

- -**Tabelle** -
[table border=1]
- [tr] 
-   [th]Tabellenzeile[/th]
- [/tr]
- [tr]
-   [td]haben Überschriften[/td]
- [/tr]
-[/table]
- -
Tabellenzeile
haben Überschriften
- -

 

- -**Listen** - -
[list]
- [*] Erstes Listenelement
- [*] Zweites Listenelement
-[/list]
- - -[list] ist Equivalent zu [ul] (unsortierte Liste). - -[ol] kann anstelle von [list] verwendet werden um eine sortierte Liste zu erzeugen: - -
[ol]
- [*] Erstes Listenelement
- [*] Zweites Listenelement
-[/ol]
- - -Für weitere Optionen von sortierten Listen kann man den Stil der Numerierung der Liste definieren: -
[list=1]
: dezimal - -
[list=i]
: römisch, Kleinbuchstaben - -
[list=I]
: römisch, Großbuchstaben - -
[list=a]
: alphabetisch, Kleinbuchstaben - -
[list=A] 
: alphabethisch, Großbuchstaben - - - - -Einbettung von Inhalten ------- - -Man kann viele Dinge, z.B. Video und Audio Dateine, in Nachrichten einbetten. - -
[video]url[/video]
-
[audio]url[/audio]
- -Wobei die *url* von youtube, vimeo, soundcloud oder einer anderen Seite stammen kann die die oembed oder opengraph Spezifikationen unterstützt. Außerdem kann *url* die genaue url zu einer ogg Datei sein, die dann per HTML5 eingebunden wird. - -
[url]*url*[/url]
- + + + + + + + +Block Tags +----- + +
[code]Code[/code]
+ +Code + +

 

+ +
[quote]Zitat[/quote]
+ +
Zitat
+ +

 

+ +
[quote=Autor]Der Autor? Ich? Nein, nein, nein...[/quote]
+ +Autor hat geschrieben:
Der Autor? Ich? Nein, nein, nein...
+ +

 

+ +
[center]zentrierter Text[/center]
+ +
zentrierter Text
+ +

 

+ +
Wer überrascht werden möchte sollte nicht weiter lesen.[spoiler]Es gibt ein Happy End.[/spoiler]
+ +Wer überrascht werden möchte sollte nicht weiter lesen.
*klicken zum öffnen/schließen* + +(Der Text zweischen dem öffnenden und dem schließenden Teil des spoiler Tags wird nicht angezeigt, bis der Link angeklickt wurde. In dem Fall wird *"Es gibt ein Happy End."* also erst angezeigt, wenn der Spoiler verraten wird.) + +

 

+ +**Tabelle** +
[table border=1]
+ [tr] 
+   [th]Tabellenzeile[/th]
+ [/tr]
+ [tr]
+   [td]haben Überschriften[/td]
+ [/tr]
+[/table]
+ +
Tabellenzeile
haben Überschriften
+ +

 

+ +**Listen** + +
[list]
+ [*] Erstes Listenelement
+ [*] Zweites Listenelement
+[/list]
+ + +[list] ist Equivalent zu [ul] (unsortierte Liste). + +[ol] kann anstelle von [list] verwendet werden um eine sortierte Liste zu erzeugen: + +
[ol]
+ [*] Erstes Listenelement
+ [*] Zweites Listenelement
+[/ol]
+ + +Für weitere Optionen von sortierten Listen kann man den Stil der Numerierung der Liste definieren: +
[list=1]
: dezimal + +
[list=i]
: römisch, Kleinbuchstaben + +
[list=I]
: römisch, Großbuchstaben + +
[list=a]
: alphabetisch, Kleinbuchstaben + +
[list=A] 
: alphabethisch, Großbuchstaben + + + + +Einbettung von Inhalten +------ + +Man kann viele Dinge, z.B. Video und Audio Dateine, in Nachrichten einbetten. + +
[video]url[/video]
+
[audio]url[/audio]
+ +Wobei die *url* von youtube, vimeo, soundcloud oder einer anderen Seite stammen kann die die oembed oder opengraph Spezifikationen unterstützt. +Außerdem kann *url* die genaue url zu einer ogg Datei sein, die dann per HTML5 eingebunden wird. + +
[url]*url*[/url]
+ Wenn *url* entweder oembed oder opengraph unterstützt wird das eingebettete Objekt (z.B. ein Dokument von scribd) eingebunden. Der Titel der Seite mit einem Link zur *url* wird ebenfalls angezeigt. - - - -Spezielle Tags -------- - -Wenn du über BBCode Tags in einer Nachricht schreiben möchtest, kannst du [noparse], [nobb] oder [pre] verwenden um den BBCode Tags vor der Evaluierung zu schützen: - -
[noparse][b]fett[/b][/noparse]
: [b]fett[/b] - - + +Um eine Karte in einen Beitrag einzubinden, muss das *openstreetmap* Addon aktiviert werden. Ist dies der Fall, kann mit + +
[map]Broadway 26, New York[/map]
+ +eine Karte von [OpenStreetmap](http://openstreetmap.org) eingebettet werden. Zur Identifikation des Ortes können entweder seine Koordinaten in der Form + +
[map=lat,long]
+ +oder eine Adresse in obiger Form verwendet werden. + +Spezielle Tags +------- + +Wenn Du über BBCode Tags in einer Nachricht schreiben möchtest, kannst Du [noparse], [nobb] oder [pre] verwenden um den BBCode Tags vor der Evaluierung zu schützen: + +
[noparse][b]fett[/b][/noparse]
: [b]fett[/b] + + diff --git a/doc/de/Bugs-and-Issues.md b/doc/de/Bugs-and-Issues.md index 7d8ea9348..1323b4b9d 100644 --- a/doc/de/Bugs-and-Issues.md +++ b/doc/de/Bugs-and-Issues.md @@ -3,26 +3,14 @@ Bugs und Probleme * [Zur Startseite der Hilfe](help) -Wenn dein Server eine Supportseite hat, solltest du jeden Bug und jedes Problem, den/das du findest, zunächst dort melden. Die Fehler zunächst dort zu melden, statt auf der allgemeinen Bug-Seite, erleichtert es den Entwicklern, neue Features zu entwickeln, wenn sie sich nicht mit Fehlern beschäftigen müssen, mit denen sie nichts zu tun haben. +Du solltest jeden Bug und jedes Problem, den/das Du findest, zunächst dem Administrator (oder gegebenenfalls der Support-Seite) Deines Servers melden, statt auf der allgemeinen Bug-Seite. +Das erleichtert den Entwicklern ihre Arbeit (z. B. neue Features zu entwickeln), da sie sich nicht mit Fehlern beschäftigen müssen, mit denen sie nichts zu tun haben. -Wenn du ein technisch verantwortlicher Nutzer bist oder wenn deine Seite keine Support-Seite hat, dann kannst du den Bug Tracker nutzen. Bitte nutze zunächst die Suche, ob es bereits einen offenen Bug gibt, der deiner Anfrage entspricht. +Wenn Du technisch versiert bist oder Dein Knoten keine Support-Seite hat, dann kannst Du den Bug Tracker nutzen. +Bitte durchsuche zunächst die Seite, ob es bereits einen offenen Bug gibt, der Deiner Anfrage entspricht. -Versuche, so viele Informationen wie möglich zum Bug zu bieten. Hierzu gehört auch die **komplette** Fehlermeldung oder Notiz und alle Schritte, die zu dem Fehler geführt haben. Es ist generell besser, zu viele Informationen zu liefern, als zu wenige. +Liefere so viele Informationen wie möglich zu dem Bug. +Hierzu gehört auch die **komplette** Fehlermeldung oder Notiz und alle Schritte, die zu dem Fehler geführt haben. +Es ist generell besser, zu viele Informationen zu liefern, als zu wenige. -Lies dir diesen Artikel (mehrsprachig) durch, um mehr über **gute** Bug-Reports zu erfahren. - -**Bug-Bearbeitung sponsern** - -Wenn du einen Bug findest, der seine Ursache im Hauptsystem hat (also wenn es sich nicht nur um deine Seite handelt), dann kannst du diesen sponsern. - -Die Bug/Fehler-Datenbank erlaubt es dir, Fehler zu sponsern. Das schafft einen Anreiz für die Entwickler, deinen Fehler zu bearbeiten. Das ist nicht zwingend notwendig, da wir keine Bugs mögen und versuchen, diese zu beheben. Wichtiger ist dieses für die zukünftige Projektentwicklung und für Feature-Anfragen. - -Bug-Sponsoring arbeitet nach dem System der Anerkennung. Wenn du 10€ spendest, um einen Bug zu beheben, dann sende die Zahlung per PayPal an den Entwickler, der den Bug behoben hat. Und denke nie daran, für geleistete Arbeit nicht zu bezahlen. Einige dieser Leute könnten deine Kreditkarte hacken, falls du sie verärgern solltest. - -Zur Zeit können nur Personen gesponserte Bugs bearbeiten, die als Entwickler bestätigt wurden. Hierfür muss der Entwickler bereits einige Friendica-Bugs bearbeitet haben. Das dient zur Absicherung, damit der behobene Bug auch gut mit Friendica läuft. Wenn du wünschst, als Entwickler bestätigt zu werden, dann arbeite dich ein und übernimm einige nicht-gesponserte Probleme oder dein eigenes Projekt und du wirst auf der Leiter nach oben klettern. - -Wenn du sicher glaubst, dass du einen gesponserten Bug beheben kannst, aber nicht als Entwickler bestätigt bist, kann es passieren, dass ein gesponserter Entwickler den Bug bearbeiten, bevor du ihn dir sichern kannst. Wenn das nicht der Fall ist, dann trage einen kleinen Vermerk in die Anfrage ein. Wenn du unsere Code-Standards erfüllst, versuchen wir, dir einen Bonus zukommen zu lassen. - -Wenn du ein Projekt mit mehr als 50€ sponserst, dann fragen dich die Entwickler gegebenenfalls, ob sie einen Teil der Zahlung vorab erhalten (normalerweise die Hälfte). Nochmals: es handelt sich um ein Anerkennungssystem - und hauptsächlich dient es dazu, Zahlungsprobleme und Streitigkeiten zu verhindern. Du solltest nach 1-2 Wochen einen gewissen Fortschritt oder Demonstrationen erwarten können. Wenn die Arbeit nicht in einer für dich annehmbaren Zeit gelöst ist, hast du das Recht, das Geld zurückzufordern. - -Friendica ist nicht in diese Transaktionen involviert. Es handelt sich ausschließlich um ein persönliches Abkommen zwischen dem Sponsor und dem Entwickler. Wenn es irgendwelche Probleme gibt, müssen die Parteien es untereinander klären. Wir erstellen gerade einige Richtlinien, um potentielle Probleme zu vermeiden. +Lies Dir diesen Artikel (mehrsprachig) durch, um mehr über **gute** Bug-Reports zu erfahren. diff --git a/doc/de/Chats.md b/doc/de/Chats.md index e538285d6..a2d06f3c4 100644 --- a/doc/de/Chats.md +++ b/doc/de/Chats.md @@ -3,23 +3,36 @@ Chats * [Zur Startseite der Hilfe](help) -Du hast derzeit zwei Möglichkeiten, einen Chat auf deiner Friendica-Seite zu betreiben +Du hast derzeit zwei Möglichkeiten, einen Chat auf Deiner Friendica-Seite zu betreiben * IRC - Internet Relay Chat * Jappix ##IRC Plugin -Sobald das Plugin aktiviert ist, kannst du den Chat unter [deineSeite.de/irc](../irc) finden. Beachte aber, dass dieser Chat auch ohne Anmeldung auf deiner Seite zugänglich ist und somit auch Fremde diesen Chat mitnutzen können. +Sobald das Plugin aktiviert ist, kannst Du den Chat unter [deineSeite.de/irc](../irc) finden. +Beachte aber, dass dieser Chat auch ohne Anmeldung auf Deiner Seite zugänglich ist und somit auch Fremde diesen Chat mitnutzen können. -Wenn du dem Link folgst, dann kommst du zum Anmeldefenster des IR-Chats. Wähle nun einen Spitznamen (Nickname) aus und wähle einen Raum aus, in dem du chatten willst. Hier kannst du jeden Namen eingeben. Es ist also auch #tollerChatdessenNamenurichkenne sein. Gib als nächstes nur noch die Captchas ein, um zu zeigen, dass es sich bei dir um einen Menschen handelt. Gehe nun auf "Connect". +Wenn Du dem Link folgst, dann kommst Du zum Anmeldefenster des IR-Chats. +Wähle nun einen Spitznamen (Nickname) und wähle einen Raum aus, in dem Du chatten willst. +Hier kannst Du jeden Namen eingeben. +Es kann also auch #tollerChatdessenNamenurichkenne sein. +Gib als nächstes noch die Captchas ein, um zu zeigen, dass es sich bei Dir um einen Menschen handelt und klicke auf "Connect". -Im nächsten Fenster siehst du zunächst viel Text beim Verbindungsaufbau, der allerdings für dich nicht weiter von Bedeutung ist. Anschließend öffnet sich das Chat-Fenster. In den ersten Zeilen wird dir dein Name und deine aktuelle IP-Adresse angezeigt. Rechts im Fenster siehst du alle Teilnehmer des Chats. Unten hast du ein Eingabefeld, um Beiträge zu schreiben. +Im nächsten Fenster siehst Du zunächst viel Text beim Verbindungsaufbau, der allerdings für Dich nicht weiter von Bedeutung ist. +Anschließend öffnet sich das Chat-Fenster. +In den ersten Zeilen wird Dir Dein Name und Deine aktuelle IP-Adresse angezeigt. +Rechts im Fenster siehst Du alle Teilnehmer des Chats. +Unten hast Du ein Eingabefeld, um Beiträge zu schreiben. + +Weiter Informationen zu IRC findest Du zum Beispiel auf ubuntuusers.de, in Wikipedia oder bei icrhelp.org (in Englisch). ##Jappix Mini -Das Jappix Mini Plugin erlaubt das Erstellen einer Chatbox für Jabber/XMPP-Kontakte. Ein Jabber/XMPP Account sollte vor der Installation bereits vorhanden sein. -Eine ausführliche Anleitung dazu und eine Kontrolle, ob man nicht sogar schon über seinen E-Mail Anbieter einen Jabber-Account hat, findet man unter http://einfachjabber.de. +Das Jappix Mini Plugin erlaubt das Erstellen einer Chatbox für Jabber/XMPP-Kontakte. +Ein Jabber/XMPP Account sollte vor der Installation bereits vorhanden sein. +Die ausführliche Anleitung dazu und eine Kontrolle, ob Du nicht sogar schon über Deinen E-Mail Anbieter einen Jabber-Account hast, findest Du unter einfachjabber.de. + Einige Server zum Anmelden eines neuen Accounts: * [https://jappix.com](https://jappix.com) @@ -29,25 +42,40 @@ Einige Server zum Anmelden eines neuen Accounts: **1. Grundsätzliches** -Als erstes muss die aktuellste Version heruntergeladen werden, per Git: +Als erstes musst Du die aktuellste Version herunterladen: -cd /var/www/virtual/DEINUBERSPACE/html/addon; git pull +Per Git: +

+cd /var/www/<Pfad zu Deiner friendica-Installation>/addon; git pull +

oder als normaler Download von hier: https://github.com/friendica/friendica-addons/blob/master/jappixmini.tgz (auf „view raw“ klicken) -Diese Datei wird entpackt, ggf. den entpackten Ordner in „jappixmini“ umbenennen und sowohl den kompletten entpackten Ordner als auch die .tgz Datei in den Addon Ordner deiner Friendica Installation hochladen. +Entpacke diese Datei (ggf. den entpackten Ordner in „jappixmini“ umbenennen) und lade sowohl den entpackten Ordner komplett als auch die .tgz Datei in den Addon Ordner Deiner Friendica Installation hoch. -Nach dem Upload gehts in den Friendica Adminbereich, dort zu den Plugins. Das Jappixmini Addon aktivieren und anschließend über die Plugins Seitenleiste (dort wo auch die Twitter-, Impressums-, StatusNet-, usw Einstellungen gemacht werden) zu den Jappix Grundeinstellungen gehen. +Nach dem Upload gehts in den Friendica Adminbereich und dort zu den Plugins. +Aktiviere das Jappixmini Addon und gehe anschließend über die Plugins Seitenleiste (dort wo auch die Twitter-, Impressums-, GNU Social-, usw. Einstellungen gemacht werden) zu den Jappix Grundeinstellungen. -Hier den Haken zur Aktivierung des BOSH Proxys setzen. -Weiter gehts in den Einstellungen deines Friendica Account. +Setze hier den Haken zur Aktivierung des BOSH Proxys. +Weiter gehts in den Einstellungen Deines Friendica Accounts. -**2. Einstellungen** +2. Einstellungen -In deinen Einstellungen (Account Settings), gehe bitte zu den Plugin-Einstellungen. Etwas scrollen bis zu Jappix Mini addon settings +Gehe bitte zu den Plugin-Einstellungen in Deinen Konto-Einstellungen (Account Settings). +Scrolle ein Stück hinunter bis zu den Jappix Mini Addon settings. -Hier zuerst das Addon aktvieren. +Aktiviere hier zuerst das Addon. -Trage nun deinen Jabber/XMPP Namen ein, ebenfalls die entsprechende Domain bzw. den Server (ohne http, also zb einfach so: jappix.com). Bei „Jabber BOSH Host“ kannst du erstmal “https://bind.jappix.com/ “ eintragen. Siehe dazu auch die „Configuration Help“ weiter unten. Danach noch dein Passwort und damit ist eigentlich schon fast alles geschafft. Die weiteren Einstellmöglichkeiten bleiben dir überlassen, sind also optional. Jetzt noch auf „senden“ klicken und fertig. +Trage nun Deinen Jabber/XMPP Namen ein, ebenfalls die entsprechende Domain bzw. den Server (ohne http, also zb einfach so: jappix.com). +Um das JavaScript Applet zum Chatten im Browser verwenden zu können, benötigst du einen BOSH Proxy. +Entweder betreibst du deinen eigenen (s. Dokumentation deines XMPP Servers) oder du verwendest einen öffentlichen BOSH Proxy. +Beachte aber, dass der Betreiber dieses Proxies den kompletten Datenverkehr über den Proxy mitlesen kann. +Siehe dazu auch die „Configuration Help“ weiter unten. +Gebe danach noch Dein Passwort an, und damit ist eigentlich schon fast alles geschafft. +Die weiteren Einstellmöglichkeiten bleiben Dir überlassen, sind also optional. +Jetzt noch auf „senden“ klicken und fertig. -Falls du manuell Kontakte hinzufügen möchtest, einfach den „Add Contact“ Knopf nutzen. Deine Chatbox sollte jetzt irgendwo unten rechts im Browserfenster „kleben“. Viel Spass beim Chatten! +Deine Chatbox sollte jetzt irgendwo unten rechts im Browserfenster „kleben“. +Falls Du manuell Kontakte hinzufügen möchtest, einfach den „Add Contact“-Knopf nutzen. + +Viel Spass beim Chatten! diff --git a/doc/de/Connectors.md b/doc/de/Connectors.md index 52f93c883..7247124ef 100644 --- a/doc/de/Connectors.md +++ b/doc/de/Connectors.md @@ -3,65 +3,67 @@ Konnektoren (Connectors) * [Zur Startseite der Hilfe](help) -Konnektoren erlauben es dir, dich mit anderen sozialen Netzwerken zu verbinden. Konnektoren werden nur bei bestehenden Facebook-, Twitter und StatusNet-Accounts benötigt. Außerdem gibt es einen Konnektor, um deinen Email-Posteingang zu nutzen. +Konnektoren erlauben es Dir, Dich mit anderen sozialen Netzwerken zu verbinden. +Konnektoren werden nur bei bestehenden Twitter und GNU Social-Accounts benötigt. +Außerdem gibt es einen Konnektor, um Deinen Email-Posteingang zu nutzen. +Wenn Du keinen eigenen Knoten betreibst und wissen willst, ob der server Deiner Wahl diese Konnektoren installiert hat, kannst Du Dich darüber auf der Seite '<domain_des_friendica-servers>/friendica' informieren. -Wenn die folgenden Netzwerk-Konnektoren auf deinem System installiert sind, kannst du mit den folgenden Links die Einstellungsseiten besuchen und für deinen Account konfigurieren: +Sind die Netzwerk-Konnektoren auf Deinem System installiert sind, kannst Du mit den folgenden Links die Einstellungsseiten besuchen und für Deinen Account konfigurieren: -* [Facebook](/settings/addon) -* [Twitter](/settings/addon) -* [StatusNet](/settings/addon) -* [Email](/settings) +* [Twitter](/settings/connectors) +* [GNU Social](/settings/connectors) +* [Email](/settings/connectors) Anleitung, um sich mit Personen in bestimmten Netzwerken zu verbinden ========================================================== **Friendica** -Du kannst dich verbinden, indem du deine Identitäts-Adresse auf der "Verbinden"-Seite des Friendica-Nutzers eingibst. Ebenso kannst du deren Identitäts-Adresse in der "Verbinden"-Box auf deiner ["Kontakt"-Seite](contacts) eingeben. +Du kannst Dich verbinden, indem Du die Adresse Deiner Identität (<dein_nick>@<dein_friendica-host>) auf der "Verbinden"-Seite des Friendica-Nutzers eingibst. +Ebenso kannst Du deren Identitäts-Adresse in der "Verbinden"-Box auf Deiner ["Kontakt"-Seite](contacts) eingeben. **Diaspora** -Füge die Diaspore-Identitäts-Adresse (z.B. name@diasporapod.com)auf deiner ["Kontakte"-Seite](contacts) in das Feld "Neuen Kontakt hinzufügen" ein. +Füge die Diaspora-Identitäts-Adresse (z.B. name@diasporapod.com)auf Deiner ["Kontakte"-Seite](contacts) in das Feld "Neuen Kontakt hinzufügen" ein. -**Identi.ca/StatusNet/GNU-Social** +**GNU Social** -Diese Netzwerke werden als "federated social web" bzw. "OStatus"-Kontakte bezeichnet. +Dieses Netzwerk wird als "federated social web" bzw. "OStatus"-Kontakte bezeichnet. -Bitte beachte, dass es **keine** Einstellungen zur Privatssphäre im OStatus-Netzwerk gibt. **Jede** Nachricht, die an eines dieser OStatus-Mitglieder verschickt wird, ist für jeden auf der Welt sichtbar; alle Privatssphäreneinstellungen verlieren ihre Wirkung. Diese Nachrichten erscheinen ebenfalls in öffentlichen Suchergebnissen. +Bitte beachte, dass es **keine** Einstellungen zur Privatssphäre im OStatus-Netzwerk gibt. +**Jede** Nachricht, die an eines dieser OStatus-Mitglieder verschickt wird, ist für jeden auf der Welt sichtbar; alle Privatssphäreneinstellungen verlieren ihre Wirkung. +Diese Nachrichten erscheinen ebenfalls in öffentlichen Suchergebnissen. -Da die OStatus-Kommunikation keine Authentifizierung benutzt, können OStatus-Nutzer *keine* Nachrichten empfangen, wenn du in deinen Privatssphäreneinstellungen "Profil und Nachrichten vor Unbekannten verbergen" wählst. +Da die OStatus-Kommunikation keine Authentifizierung benutzt, können OStatus-Nutzer *keine* Nachrichten empfangen, wenn Du in Deinen Privatssphäreneinstellungen "Profil und Nachrichten vor Unbekannten verbergen" wählst. -Um dich mit einem OStatus-Mitglied zu verbinden, trage deren Profil-URL oder Identitäts-Adresse auf deiner ["Kontakte"-Seite](contacts) in das Feld "Neuen Kontakt hinzufügen" ein. +Um Dich mit einem OStatus-Mitglied zu verbinden, trage deren Profil-URL oder Identitäts-Adresse auf Deiner ["Kontakte"-Seite](contacts) in das Feld "Neuen Kontakt hinzufügen" ein. -Der StatusNet-Konnektor kann genutzt werden, wenn du Beiträge schreiben willst, die auf einer OStatus-Seite über einen existierenden OStatus-Account erscheinen sollen. +Der GNU Social-Konnektor kann genutzt werden, wenn Du Beiträge schreiben willst, die auf einer OStatus-Seite über einen existierenden OStatus-Account erscheinen sollen. -Das ist nicht notwendig, wenn du OStatus-Mitgliedern von Friendica aus folgst und diese dir auch folgen, indem sie auf deiner Kontaktseite ihre eigene Identitäts-Adresse eingeben. +Das ist nicht notwendig, wenn Du OStatus-Mitgliedern von Friendica aus folgst und diese Dir auch folgen, indem sie auf Deiner Kontaktseite ihre eigene Identitäts-Adresse eingeben. **Blogger, Wordpress, RSS feeds, andere Webseiten** -Trage die URL auf deiner ["Kontakte"-Seite](contacts) in das Feld "Neuen Kontakt hinzufügen" ein. Du hast keine Möglichkeit, diesen Kontakten zu antworten. +Trage die URL auf Deiner ["Kontakte"-Seite](contacts) in das Feld "Neuen Kontakt hinzufügen" ein. +Du hast keine Möglichkeit, diesen Kontakten zu antworten. -Das erlaubt dir, dich mit Millionen von Seiten im Internet zu _verbinden_. Alles, was dafür nötig ist, ist dass die Seite einen Feed im RSS- oder Atom Syndication-Format nutzt und welches einen Autoren und ein Bild zur Seite liefert. +Das erlaubt Dir, Dich mit Millionen von Seiten im Internet zu _verbinden_. +Alles, was dafür nötig ist, ist dass die Seite einen Feed im RSS- oder Atom Syndication-Format nutzt und welches einen Autoren und ein Bild zur Seite liefert. **Twitter** -Um einem Twitter-Nutzer zu folgen, trage die URL der Hauptseite des Twitter-Accounts auf deiner ["Kontakte"-Seite](contacts) in das Feld "Neuen Kontakt hinzufügen" ein. Um zu antworten, musst du den Twitter-Konnektor installieren und über deinen eigenen Status-Editor antworten. Beginne deine Nachricht mit @twitternutzer, ersetze das aber durch den richtigen Twitter-Namen. +Um einem Twitter-Nutzer zu folgen, trage die URL der Hauptseite des Twitter-Accounts auf Deiner ["Kontakte"-Seite](contacts) in das Feld "Neuen Kontakt hinzufügen" ein. +Um zu antworten, musst Du den Twitter-Konnektor installieren und über Deinen eigenen Status-Editor antworten. +Beginne Deine Nachricht mit @twitternutzer, ersetze das aber durch den richtigen Twitter-Namen. **Email** -Konfiguriere den Email-Konnektor auf deiner [Einstellungsseite](settings). Wenn du das gemacht hast, kannst du auf deiner ["Kontakte"-Seite](contacts) die Email-Adresse in das Feld "Neuen Kontakt hinzufügen" eintragen. Diese Email-Adresse muss jedoch bereits mit einer Nachricht in deinem Email-Posteingang auf dem Server liegen. Du hast die Möglichkeit, Email-Kontakte in deine privaten Unterhaltungen einzubeziehen. - -**Facebook** - -Der Facebook-Konnektor ist ein Plugin/Addon, dass es dir erlaubt, von Friendica aus mit Freunden auf Facebook zu interagieren. Wenn er aktiviert ist, wird deine Facebook-Freundesliste importiert und du wirst Facebook-Beiträge sehen und kommentieren können. Facebook-Freunde können außerdem zu privaten Gesprächen hinzugefügt werden. Du hast nicht die Möglichkeit, einzelne Facebook-Accounts hinzuzufügen, sondern nur deine gesamte Freundesliste, die aktualisiert wird, wenn neue Freunde hinzugefügt werden. - -Wenn das Facebook-Plugin/Addon installiert ist, kannst du diesen auf deiner Einstellungsseite unter ["Facebook Connector Settings"](settings/addon) einstellen. Dieser Eintrag erscheint nur, wenn das Plugin/Addon installiert ist. Folge den Vorgaben, um den Facebook-Konnektor zu installieren oder löschen. - -Du kannst ebenfalls auswählen, ob deine öffentlichen Posts auch standardmäßig bei Facebook veröffentlicht werden sollen. Du kannst diese Einstellung jederzeit im aktuellen Beitrag beeinflussen, indem du auf das "Schloss"-Icon unter dem Beitragseditor gehst. Diese Einstellung hat keine Auswirkung auf private Unterhaltungen. Diese werden immer an Facebook-Freunde mit den entsprechenden Genehmigungen geschickt. - -(Beachte: Aktuell können Facebook-Kontakte keine privaten Fotos sehen. Das wird zukünftig gelöst. Facebook-Kontakte können aber trotzdem öffentliche Fotos sehen, die du hochgeladen hast.) +Konfiguriere den Email-Konnektor auf Deiner [Einstellungsseite](settings). +Wenn Du das gemacht hast, kannst Du auf Deiner ["Kontakte"-Seite](contacts) die Email-Adresse in das Feld "Neuen Kontakt hinzufügen" eintragen. +Diese Email-Adresse muss jedoch bereits mit einer Nachricht in Deinem Email-Posteingang auf dem Server liegen. +Du hast die Möglichkeit, Email-Kontakte in Deine privaten Unterhaltungen einzubeziehen. diff --git a/doc/de/Developers.md b/doc/de/Developers.md index f5c90c489..b1d118fd5 100644 --- a/doc/de/Developers.md +++ b/doc/de/Developers.md @@ -3,20 +3,22 @@ Friendica - Entwickler-Guide * [Zur Startseite der Hilfe](help) -Hier erfährst du, wie du bei uns mitmachen kannst +Hier erfährst Du, wie Du bei uns mitmachen kannst: -Zunächst erstelle dir ein funktionierendes Git-Paket auf deinem System, auf dem du die Entwicklung durchführst. +Zunächst erstelle Dir per 'git clone https://github.com/friendica/friendica.git' ein funktionierendes Git-Paket auf Deinem System, auf dem Du die Entwicklung durchführst, und einen eigenen Github-Account. -Erstelle deinen eigenen Github-Account. +Erstelle Deine eigene Kopie (fork) der Ursprungsdaten auf Github, an der Du dann entspannt arbeiten kannst. +Deine Arbeiten sollten mit einem neuen Arbeitszweig (branch) beginnen, den du vom develop Zweig des Repositories beginnst. +Die Anleitung unter [http://help.github.com/fork-a-repo/](http://help.github.com/fork-a-repo/) erklärt Dir genau, wie Du das tun musst. -Du hast die Möglichkeit, die Friendica-Daten direkt über Github von der folgenden Seite laden: [https://github.com/friendica/friendica.git](https://github.com/friendica/friendica.git) - -Befolge die Anleitung unter diesem Link [http://help.github.com/fork-a-repo/](http://help.github.com/fork-a-repo/), um deine eigene Kopie (fork) der Ursprungsdaten auf Github zu erstellen und bearbeiten. - -Gehe nun zu deiner Github-Seite und erstelle eine "Pull request", wenn du soweit bist, dein Projekt wieder in das Hauptprojekt einzugliedern. +Gehe dann nach getaner Arbeit zu Deiner Github-Seite und erstelle eine "Pull request", um Deine Änderungen in das Hauptprojekt einzugliedern (merge). **Wichtig** -Bitte hole dir alle Änderungen aus dem Projektverzeichnis und führe sie mit deiner Arbeit zusammen, **bevor** du deine "pull request" stellt. Wir behalten es uns vor, Patches abzulehnen, die eine große Anzahl an Fehlern hervorrufen. Dies gilt vor allem für Übersetzungen, da wir hier möglicherweise nicht alle feinen Unterschiede in konfliktären Versionen erkennen können. +Bitte hole Dir alle Änderungen aus dem Projektverzeichnis und führe sie mit Deiner Arbeit zusammen, **bevor** Du Deine "pull request" erstellst. Wir behalten es uns vor, Patches abzulehnen, die eine große Anzahl an Fehlern hervorrufen. +Dies gilt vor allem für Übersetzungen, da wir hier möglicherweise nicht alle feinen Unterschiede in konfliktären Versionen erkennen können. -Außerdem: **teste deine Änderungen!** Vergiss nicht, dass eine simple Fehlerlösung einen anderen Fehler auslösen kann. Lass deine Änderungen von einem erfahrenen Friendica-Entwickler gegenprüfen. +Außerdem: **teste Deine Änderungen!** Vergiss nicht, dass eine simple Fehlerlösung einen anderen Fehler auslösen kann. +Lass Deine Änderungen von einem erfahrenen Friendica-Entwickler gegenprüfen. + +Eine ausführliche Anleitung zu Git findest Du unter https://git-scm.com/book/de/v1. diff --git a/doc/de/FAQ.md b/doc/de/FAQ.md index a1c5d0114..d5aa19e6c 100644 --- a/doc/de/FAQ.md +++ b/doc/de/FAQ.md @@ -6,8 +6,7 @@ Häufig gestellte Fragen - FAQ Nutzer * **[Warum erhalte ich Warnungen über fehlende Zertifikate?](help/FAQ#ssl)** -* **[Wie kann ich Bilder, Dateien, Links, Video und Audio in Beiträge einfügen? -](help/FAQ#upload)** +* **[Wie kann ich Bilder, Dateien, Links, Video und Audio in Beiträge einfügen?](help/FAQ#upload)** * **[Ist es möglich, bei mehreren Profilen verschiedene Avatare (Nutzerbilder) zu haben?](help/FAQ#avatars)** * **[Was ist der Unterschied zwischen blockierten|ignorierten|archivierten|versteckten Kontakten?](help/FAQ#contacts)** * **[Was passiert, wenn ein Account gelöscht ist? Ist dieser richtig gelöscht?](help/FAQ#removed)** @@ -27,9 +26,10 @@ Nutzer **Warum erhalte ich Warnungen über fehlende Zertifikate?** -Manchmal erhältst du eine Browser-Warnung über fehlende Zertifikate. Diese Warnungen können drei Gründe haben: +Manchmal erhältst Du eine Browser-Warnung über fehlende Zertifikate. +Diese Warnungen können drei Gründe haben: -1. der Server, mit dem du verbunden bist, nutzt kein SSL; +1. der Server, mit dem Du verbunden bist, nutzt kein SSL; 2. der Server hat ein selbst-signiertes Zertifikat (nicht empfohlen) @@ -37,31 +37,45 @@ Manchmal erhältst du eine Browser-Warnung über fehlende Zertifikate. Diese War *(SSL (Secure Socket Layer) ist eine Technologie, die Daten auf ihrem Weg zwischen zwei Computern verschlüsselt.)* -Wenn du noch kein SSL-Zertifikat hast, dann gibt es drei Wege, eines zu erhalten: kauf dir eines, hole dir ein kostenloses (z.B. bei StartSSL) oder kreiere dein eigenes (nicht empfohlen). [Weitere Informationen über die Einrichtung von SSL und warum es schlecht ist, selbst-signierte Zertifikate zu nutzen, findest du hier.](help/SSL) +Wenn Du noch kein SSL-Zertifikat hast, dann gibt es drei Wege, eines zu erhalten: kauf Dir eines, hole Dir ein kostenloses (z.B. bei StartSSL, WoSign, hoffentlich bald auch letsencrypt) oder kreiere Dein eigenes (nicht empfohlen). +[Weitere Informationen über die Einrichtung von SSL und warum es schlecht ist, selbst-signierte Zertifikate zu nutzen, findest Du hier.](help/SSL) -Sei dir bewusst, dass Browser-Warnungen über Sicherheitslücken etwas sind, wodurch neue Nutzer schnell das Vertrauen in das gesamte Friendica-Projekt verlieren können. Aus diesem Grund wird Friendica Red nur SSL-Zertifikate eines anerkannten Anbieters (CA, certificate authority) akzeptieren und nicht zu Seiten verbinden, die kein SSL nutzen. Unabhängig von den negativen Aspekten von SSL handelt es sich hierbei um eine notwendige Lösung, solange keine etablierte Alternative existiert. +Sei Dir bewusst, dass Browser-Warnungen über Sicherheitslücken etwas sind, wodurch neue Nutzer schnell das Vertrauen in das gesamte Friendica-Projekt verlieren können. +Aus diesem Grund wird Friendica Red nur SSL-Zertifikate eines anerkannten Anbieters (CA, certificate authority) akzeptieren und nicht zu Seiten verbinden, die kein SSL nutzen. +Unabhängig von den negativen Aspekten von SSL handelt es sich hierbei um eine notwendige Lösung, solange keine etablierte Alternative existiert. Abgesehen davon kann es ohne SSL auch Probleme mit der Verbindung zu Diaspora geben, da einige Diaspora-Pods eine zertifizierte Verbindung benötigen. -Wenn du Friendica nur für eine bestimmte Gruppe von Leuten auf einem einzelnen Server nutzt, bei dem keine Verbindung zum restlichen Netzwerk besteht, dann benötigst du kein SSL. Ebenso benötigst du SSL nicht, wenn du ausschließlich öffentliche Beiträge auf deiner Seite veröffentlichst bzw. empfängst. +Wenn Du Friendica nur für eine bestimmte Gruppe von Leuten auf einem einzelnen Server nutzt, bei dem keine Verbindung zum restlichen Netzwerk besteht, dann benötigst Du kein SSL. +Ebenso benötigst Du SSL nicht, wenn Du ausschließlich öffentliche Beiträge auf Deiner Seite veröffentlichst bzw. empfängst. -Wenn du zum jetzigen Zeitpunkt noch keinen Server aufgesetzt hast, ist es sinnvoll, die verschiedenen Anbieter in Bezug auf SSL zu vergleichen. Einige erlauben die Nutzung von freien Zertifikaten oder lassen dich ihre eigenen Zertifikate mitnutzen. Andere erlauben nur kostenpflichtige Zertifikate als eigenes Angebot bzw. von anderen Anbietern. +Wenn Du zum jetzigen Zeitpunkt noch keinen Server aufgesetzt hast, ist es sinnvoll, die verschiedenen Anbieter in Bezug auf SSL zu vergleichen. +Einige erlauben die Nutzung von freien Zertifikaten oder lassen Dich ihre eigenen Zertifikate mitnutzen. +Andere erlauben nur kostenpflichtige Zertifikate als eigenes Angebot bzw. von anderen Anbietern. **Wie kann ich Bilder, Dateien, Links, Video und Audio in Beiträge einfügen?** -Bilder können direkt im [Beitragseditor](help/Text_editor) vom Computer hochgeladen werden. Eine Übersicht aller Bilder, die auf deinem Server liegen, findest du unter deineSeite.de/photos/profilname. Dort kannst du auch direkt Bilder hochladen und festlegen, ob deine Kontakte eine Nachricht über das neue Bild bekommen. +Bilder können direkt im [Beitragseditor](help/Text_editor) vom Computer hochgeladen werden. +Eine Übersicht aller Bilder, die auf Deinem Server liegen, findest Du unter deineSeite.de/photos/profilname. +Dort kannst Du auch direkt Bilder hochladen und festlegen, ob Deine Kontakte eine Nachricht über das neue Bild bekommen. -Alle Arten von Dateien können grundsätzlich als Anhang in Friendica hochgeladen werden. Dafür verwendest du das Büroklammersymbol im Editor. Sie sind dann direkt an den Beitrag geknüpft, können von den Betrachtern heruntergeladen werden, aber werden nicht als Vorschau angezeigt. Deshalb eignet sich diese Methode vor allem für Office-Dateien oder gepackte Dateien wie ZIPs, aber weniger für Multimediadateien. Wer hingegen Dateien über Dropbox, über eine auf dem eigenen Server installierte Owncloud oder über einen anderen [Filehoster](http://en.wikipedia.org/wiki/Comparison_of_file_hosting_services) einfügen will, verwendet den Link-Button. +Alle Arten von Dateien können grundsätzlich als Anhang in Friendica hochgeladen werden. +Dafür verwendest Du das Büroklammersymbol im Editor. +Sie sind dann direkt an den Beitrag geknüpft, können von den Betrachtern heruntergeladen werden, aber werden nicht als Vorschau angezeigt. +Deshalb eignet sich diese Methode vor allem für Office-Dateien oder gepackte Dateien wie ZIPs, aber weniger für Multimediadateien. +Wer hingegen Dateien über Dropbox, über eine auf dem eigenen Server installierte Owncloud oder über einen anderen [Filehoster](http://en.wikipedia.org/wiki/Comparison_of_file_hosting_services) einfügen will, verwendet den Link-Button. -Wenn du mit dem Link-Button (Ketten-Symbol) URLs zu anderen Seiten einfügst, versucht Friendica eine kurze Zusammenfassung als Vorschau abzurufen. Manchmal klappts das nicht ... dann verlinke den Beitrag einfach per [url=http://example.com]freigewählter Name[/url] im Editor. +Wenn Du mit dem Link-Button (Ketten-Symbol) URLs zu anderen Seiten einfügst, versucht Friendica eine kurze Zusammenfassung als Vorschau abzurufen. +Manchmal klappts das nicht ... dann verlinke den Beitrag einfach per [url=http://example.com]freigewählter Name[/url] im Editor. -Video- und Audiodateien können zwar in Beiträge eingebunden werden, allerdings geht das nicht über einen direkten Upload im Editor wie bei Fotos. Du hast zwei Möglichkeiten: +Video- und Audiodateien können zwar in Beiträge eingebunden werden, allerdings geht das nicht über einen direkten Upload im Editor wie bei Fotos. +Du hast zwei Möglichkeiten: -1. Du kannst bei dem Video- oder Audiobutton die URL von einem Hoster eingeben (Youtube, Vimeo, Soundcloud und alle anderen mit oembed/opengraph-Unterstützung). Bei Videos zeigt Friendica dann ein Vorschaubild in deinem Beitrag an, nach einem Klick öffnet sich ein eingebetter Player. Bei Soundcloud wird der Player direkt eingebunden. -2. Wenn du Zugang zu einem eigenen Server hast, kannst deine Multimediadatei per FTP dort hochladen und beim Video-/Audiobutton diese URL angeben. Dann wird das Video oder die Audiodatei direkt mit einem Player in deinem Beitrag angezeigt. -Friendica verwendet zur Einbettung HTML5. Das bedeutet, dass je nach Browser und Betriebssystem andere Formate unterstützt werden, darunter WebM, MP4, MP3 und Ogg. Eine Tabelle findest du bei Wikipedia ([Video](http://en.wikipedia.org/wiki/HTML5_video), [Audio](http://en.wikipedia.org/wiki/HTML5_audio)). +1. Du kannst bei dem Video- oder Audiobutton die URL von einem Hoster eingeben (Youtube, Vimeo, Soundcloud und alle anderen mit oembed/opengraph-Unterstützung). Bei Videos zeigt Friendica dann ein Vorschaubild in Deinem Beitrag an, nach einem Klick öffnet sich ein eingebetter Player. Bei Soundcloud wird der Player direkt eingebunden. +2. Wenn Du Zugang zu einem eigenen Server hast, kannst Deine Multimediadatei per FTP dort hochladen und beim Video-/Audiobutton diese URL angeben. Dann wird das Video oder die Audiodatei direkt mit einem Player in Deinem Beitrag angezeigt. +Friendica verwendet zur Einbettung HTML5. Das bedeutet, dass je nach Browser und Betriebssystem andere Formate unterstützt werden, darunter WebM, MP4, MP3 und Ogg. Eine Tabelle findest Du bei Wikipedia ([Video](http://en.wikipedia.org/wiki/HTML5_video), [Audio](http://en.wikipedia.org/wiki/HTML5_audio)). Zum Konvertieren von Videos in das lizenfreie Videoformat WebM gibt es unter Windows das kostenlose Programm [Xmedia-Recode](http://www.xmedia-recode.de/). @@ -69,49 +83,68 @@ Zum Konvertieren von Videos in das lizenfreie Videoformat WebM gibt es unter Win **Ist es möglich, bei mehreren Profilen verschiedene Avatare (Nutzerbilder) zu haben?** -Ja. Auf deiner ["Profile verwalten/editieren"-Seite](../profiles) wählst du zunächst das gewünschte Profil aus. Anschließend siehst du eine Seite mit allen Infos zu diesem Profil. Klicke nun oben auf den Link "Profilbild ändern" und lade im nächsten Fenster ein Bild von deinem PC hoch. Um deine privaten Daten zu schützen, wird in Beiträgen nur das Bild aus deinem öffentlichen Profil angezeigt. +Ja. +Auf Deiner ["Profile verwalten/editieren"-Seite](../profiles) wählst Du zunächst das gewünschte Profil aus. +Anschließend siehst Du eine Seite mit allen Infos zu diesem Profil. +Klicke nun oben auf den Link "Profilbild ändern" und lade im nächsten Fenster ein Bild von Deinem PC hoch. +Um Deine privaten Daten zu schützen, wird in Beiträgen nur das Bild aus Deinem öffentlichen Profil angezeigt. **Was ist der Unterschied zwischen blockierten|ignorierten|archivierten|versteckten Kontakten?** -Wir verhindern direkte Kommunikation mit blockierten Kontakten. Sie gehören nicht zu den Empfängern beim Versand von Beiträgen und deren Beiträge werden auch nicht importiert. Trotzdem werden deren Unterhaltungen mit deinen Freunden trotzdem in deinem Stream sichtbar sein. Wenn du einen Kontakt komplett löschst, können sie dir eine neue Freundschaftsanfrage schicken. Blockierte Kontakte können das nicht machen. Sie können nicht mit dir direkt kommunizieren, nur über Freunde. +Wir verhindern direkte Kommunikation mit blockierten Kontakten. +Sie gehören nicht zu den Empfängern beim Versand von Beiträgen und deren Beiträge werden auch nicht importiert. +Trotzdem werden deren Unterhaltungen mit Deinen Freunden in Deinem Stream sichtbar sein. +Wenn Du einen Kontakt komplett löschst, können sie Dir eine neue Freundschaftsanfrage schicken. +Blockierte Kontakte können das nicht machen. +Sie können nicht mit Dir direkt kommunizieren, nur über Freunde. -Ignorierte Kontakte können weiterhin Beiträge von dir erhalten. Deren Beiträge werden allerdings nicht importiert. Wie bei blockierten Beiträgen siehst du auch hier weiterhin die Kommentare dieser Person zu anderen Beiträgen deiner Freunde. +Ignorierte Kontakte können weiterhin Beiträge von Dir erhalten. +Deren Beiträge werden allerdings nicht importiert. W +ie bei blockierten Beiträgen siehst Du auch hier weiterhin die Kommentare dieser Person zu anderen Beiträgen Deiner Freunde. -[Ein Plugin namens "blockem" kann installiert werden, um alle Beiträge einer bestimmten Person in deinem Stream zu verstecken bzw. zu verkürzen. Dabei werden auch Kommentare dieser Person in Beiträgen deiner Freunde blockiert.] +[Ein Plugin namens "blockem" kann installiert werden, um alle Beiträge einer bestimmten Person in Deinem Stream zu verstecken bzw. zu verkürzen. +Dabei werden auch Kommentare dieser Person in Beiträgen Deiner Freunde blockiert.] -Ein archivierter Kontakt bedeutet, dass Kommunikation nicht möglich ist und auch nicht versucht wird (das ist z.B. sinnvoll, wenn eine Person zu einer neuen Seite gewechselt ist und das alte Profil gelöscht hat). Anders als beim Blockieren werden existierende Beiträge, die vor der Archivierung erstellt wurden, weiterhin angezeigt. +Ein archivierter Kontakt bedeutet, dass Kommunikation nicht möglich ist und auch nicht versucht wird (das ist z.B. sinnvoll, wenn eine Person zu einem neuen Server gewechselt ist und das alte Profil gelöscht hat). +Anders als beim Blockieren werden existierende Beiträge, die vor der Archivierung erstellt wurden, weiterhin angezeigt. -Ein versteckter Kontakt wird in keiner "Freundeliste" erscheinen (außer für dich). Trotzdem wird ein versteckter Kontakt trotzdem normal in Unterhaltungen angezeigt, was jedoch für andere Kontakte ein Hinweis sein kann, dass diese Person als versteckter Kontakt in deiner Liste ist. +Ein versteckter Kontakt wird in keiner "Freundeliste" erscheinen (außer für dich). +Trotzdem wird ein versteckter Kontakt normal in Unterhaltungen angezeigt - was für andere Kontakte ein Hinweis sein kann, dass diese Person als versteckter Kontakt in Deiner Liste ist. **Was passiert, wenn ein Account gelöscht ist? Ist dieser richtig gelöscht?** -Wenn du deinen Account löschst, wird sofort der gesamte Inhalt auf deinem Server gelöscht und ein Löschbefehl an alle deine Kontakte verschickt. Dadurch wirst du ebenfalls aus dem globalen Verzeichnis gelöscht. Dieses Vorgehen setzt voraus, dass dein Profil für 24 Stunden weiterhin "teilweise" verfügbar sein wird, um eine Verbindung zu allen deinen Kontakten ermöglicht. Wir können also dein Profil blockieren und es so erscheinen lassen, als wären alle Daten sofort gelöscht, allerdings warten wir 24 Stunden (bzw. bis alle deine Kontakte informiert wurden), bevor wir die Daten auch physikalisch löschen. +Wenn Du Deinen Account löschst, wird sofort der gesamte Inhalt auf Deinem Server gelöscht und ein Löschbefehl an alle Deine Kontakte verschickt. +Dadurch wirst Du ebenfalls aus dem globalen Verzeichnis gelöscht. +Dieses Vorgehen setzt voraus, dass Dein Profil für 24 Stunden weiterhin "teilweise" verfügbar sein wird, um eine Verbindung zu allen Deinen Kontakten ermöglicht. +Wir können also Dein Profil blockieren und es so erscheinen lassen, als wären alle Daten sofort gelöscht, allerdings warten wir 24 Stunden (bzw. bis alle Deine Kontakte informiert wurden), bevor wir die Daten auch physikalisch löschen. **Kann ich einem hashtag folgen?** -Nein. Die Möglichkeit, einem hashtag zu folgen, ist eine interessante Technik, führt aber zu einigen Schwierigkeiten. +Nein. +Die Möglichkeit, einem hashtag zu folgen, ist eine interessante Technik, führt aber zu einigen Schwierigkeiten. 1.) Alle Beiträge, die diesen tag nutzen, müssten zu allen Seiten im Netzwerk kopiert werden. Das erhöht den Speicherbedarf und beeinträchtigt kleine Seiten. Die Nutzung von geteilten Hosting-Angeboten (Shared Hosting) wäre praktisch unmöglich. -2.) Die Verbreitung von Spam wäre vereinfacht (tag-Spam ist z.B. bei identi.ca ein schwerwiegendes Problem) +2.) Die Verbreitung von Spam wäre vereinfacht (tag-Spam ist z.B. bei Twitter ein schwerwiegendes Problem) -3.) Der wichtigste Grund der gegen diese Technik spricht ist, dass sie eine natürliche Ausrichtung auf größere Seiten mit mehr getaggten Inhalten zur Folge hat. Dies kann z.B. aufkommen, wenn dein Netzwerk tags anstelle von anderen Kommunikationsmitteln wie Gruppen oder Foren nutzt. +3.) Der wichtigste Grund der gegen diese Technik spricht ist, dass sie eine natürliche Ausrichtung auf größere Seiten mit mehr getaggten Inhalten zur Folge hat. Dies kann z.B. aufkommen, wenn Dein Netzwerk tags anstelle von anderen Kommunikationsmitteln wie Gruppen oder Foren nutzt. -Stattdessen bieten wir andere Mechanismen, um globale Unterhaltungen zu erreichen, dabei aber eine angemesse Basis für kleine und große Seiten zu bieten. Hierzu gehören Foren, Gruppen und geteilte tags. +Stattdessen bieten wir andere Mechanismen, um globale Unterhaltungen zu erreichen, dabei aber eine angemesse Basis für kleine und große Seiten zu bieten. +Hierzu gehören Foren, Gruppen und geteilte tags. **Wie kann ich einen RSS-Feed meiner Netzwerkseite (Stream) erstellen?** -Wenn du die Beiträge deines Accounts mit RSS teilen willst, dann kannst du einen der folgenden Links nutzen: +Wenn Du die Beiträge Deines Accounts mit RSS teilen willst, dann kannst Du einen der folgenden Links nutzen: -RSS-Feed deiner Beiträge +RSS-Feed Deiner Beiträge deineSeite.de/**dfrn_poll/profilname @@ -119,7 +152,7 @@ RSS-Feed deiner Beiträge https://helpers.pyxis.uberspace.de/dfrn_poll/helpers -RSS-Feed aller Unterhaltungen auf deiner Seite +RSS-Feed aller Unterhaltungen auf Deiner Seite deineSeite.de/dfrn_poll/profilname/converse @@ -131,11 +164,11 @@ RSS-Feed aller Unterhaltungen auf deiner Seite **Wo finde ich Hilfe?** -Wenn du Probleme mit deiner Friendica-Seite hast, dann kannst du die Community in der [Friendica-Support-Gruppe](https://helpers.pyxis.uberspace.de/profile/helpers) oder im [deutschen Friendica-Support-Forum](http://toktan.org/profile/wiki) fragen oder dir das [deutsche Wiki](http://wiki.toktan.org/doku.php) anschauen. Wenn du deinen Account nicht nutzen kannst, kannst du entweder einen [Testaccount](http://friendica.com/node/31) bzw. einen Account auf einer öffentlichen Seite ([Liste](http://dir.friendica.com/siteinfo)) nutzen, oder du wählst die Librelist-mailing-Liste. Wenn du die Mailing-Liste nutzen willst, schicke eine Mail an friendica AT librelist PUNKT com. +Wenn Du Probleme mit Deiner Friendica-Seite hast, dann kannst Du die Community in der [Friendica-Support-Gruppe](https://helpers.pyxis.uberspace.de/profile/helpers) oder im [deutschen Friendica-Support-Forum](http://toktan.org/profile/wiki) fragen oder Dir das [deutsche Wiki](http://wiki.toktan.org/doku.php) anschauen. +Wenn Du Deinen Account nicht nutzen kannst, kannst Du entweder einen [Testaccount](http://friendica.com/node/31) bzw. einen Account auf einer öffentlichen Seite ([Liste](http://dir.friendica.com/siteinfo)) nutzen, oder Du wählst die Librelist-mailing-Liste. +Wenn Du die Mailing-Liste nutzen willst, schicke eine Mail an friendica AT librelist PUNKT com. -Wenn du Friendica Red nutzt, findest du außerdem in diesem Forum Hilfe: [Friendica Red Development](https://myfriendica.net/profile/friendicared). - -Wenn du ein Theme-Entwickler bist, wirst du in diesem Forum Hilfe finden: [Friendica Theme Developers](https://friendica.eu/profile/ftdevs). +Wenn Du ein Theme-Entwickler bist, wirst Du in diesem Forum Hilfe finden: [Friendica Theme Developers](https://friendica.eu/profile/ftdevs). Admin -------- @@ -144,14 +177,19 @@ Admin **Kann ich mehrere Domains mit den selben Dateien aufsetzen?** -Ja, das ist möglich. Es ist allerdings nicht möglich, eine Datenbank durch zwei Domains zu nutzen. Solange du deine .htconfig.php allerdings so einrichtest, dass das System nicht versucht, eine Installation durchzuführen, kannst du die richtige Config-Datei in include/$hostname/.htconfig.php hinterlegen. Alle Cache-Aspekte und der Zugriffsschutz können pro Instanz konfiguriert werden. +Ja, das ist möglich. +Es ist allerdings nicht möglich, eine Datenbank durch zwei Domains zu nutzen. +Solange Du Deine .htconfig.php allerdings so einrichtest, dass das System nicht versucht, eine Installation durchzuführen, kannst Du die richtige Config-Datei in include/$hostname/.htconfig.php hinterlegen. +Alle Cache-Aspekte und der Zugriffsschutz können pro Instanz konfiguriert werden. **Wo kann ich den Quellcode von Friendica, Addons und Themes finden?** -Du kannst den Friendica-Quellcode [hier](https://github.com/friendica/friendica) finden. Dort findest du immer die aktuellste stabile Version von Friendica. Der Quellcode von Friendica Red ist [hier](https://github.com/friendica/red) zu finden. +Du kannst den Friendica-Quellcode [hier](https://github.com/friendica/friendica) finden. +Dort findest Du immer die aktuellste stabile Version von Friendica. +Der Quellcode von Friendica Red ist [hier](https://github.com/friendica/red) zu finden. -Addons findest du auf [dieser Seite](https://github.com/friendica/friendica-addons). +Addons findest Du auf [dieser Seite](https://github.com/friendica/friendica-addons). -Wenn du neue Themen suchst, findest du sie auf [Friendica-Themes.com](http://friendica-themes.com/) \ No newline at end of file +Wenn Du neue Themen suchst, findest Du sie auf [Friendica-Themes.com](http://friendica-themes.com/). diff --git a/doc/de/Forums.md b/doc/de/Forums.md index 6a8fd5bd7..1d7c5cec8 100644 --- a/doc/de/Forums.md +++ b/doc/de/Forums.md @@ -4,34 +4,55 @@ Foren * [Zur Startseite der Hilfe](help) -Friendica lässt dich auch Foren und/oder Prominenten-Seiten erstellen. +In Friendica kannst Du auch Foren und/oder Prominenten-Seiten erstellen. -Jede Seite in Friendica hat einen einzigartigen Spitznamen. Das gilt für alle Seiten, unabhängig davon, ob es sich um normale Profile oder Forenseite handelt. +Jede Seite in Friendica hat einen einmaligen Spitznamen. +Das gilt für alle Seiten, unabhängig davon, ob es sich um normale Profile oder Forenseiten handelt. -Das Erste, was du machen musst, um ein neues Forum zu kreieren ist, einen neuen Account zu erstellen. Bitte beachte, dass der Seitenadministrator die Registrierung neuer Accounts sperren oder an Bedingungen knüpfen kann. +Das Erste, was Du machen musst, um ein neues Forum zu kreieren, ist einen neuen Account zu erstellen. +Bitte beachte, dass der Seitenadministrator die Registrierung neuer Accounts sperren oder an Bedingungen knüpfen kann. -Wenn du einen zweiten Account in einem System erstellst und die gleiche Email-Adresse oder den gleichen OpenID-Account nutzt, kannst du dich zukünftig nur noch mit deinem Spitznamen anmelden. +Wenn Du einen zweiten Account in einem System erstellst und die gleiche Email-Adresse oder den gleichen OpenID-Account nutzt, kannst Du Dich zukünftig nur noch mit Deinem Spitznamen anmelden. -Gehe im neuen Account auf die "Einstellungs"-Seite und dort am Ende der Seite auf "Erweiterte Konto-/Seitentyp-Einstellungen". Normalerweise nutzt du "Normales Konto" für einen normalen, persönlichen Account. Das ist die Standardeinstellung. Gr‬uppenseiten bieten die Möglichkeit, Leute als Freund/Fan ohne Kontaktbestätigung zuzulassen. +Gehe im neuen Account auf die "Einstellungs"-Seite und dort am Ende der Seite auf "Erweiterte Konto-/Seitentyp-Einstellungen". +Normalerweise nutzt Du "Normales Konto" für einen normalen, persönlichen Account. +Das ist die Standardeinstellung. +Gr‬uppenseiten bieten die Möglichkeit, Leute als Freund/Fan ohne Kontaktbestätigung zuzulassen. -Die Auswahl der Einstellung, die du wählst, hängt davon ab, wie du mit anderen Leuten auf deiner Seite interagieren willst. Die "Marktschreier"-Einstellung (Soapbox) lässt den Seitenbesitzer die gesamte Kommunikation kontrollieren. Alles was du schreibst, geht an alle Seitennutzer, aber es gibt keine Möglichkeit, zu interagieren. Diese Seite wird normalerweise für Ankündigungen oder die Kommunikation von Gemeinschaften genutzt. +Die Auswahl der Einstellung, die Du wählst, hängt davon ab, wie Du mit anderen Leuten auf Deiner Seite interagieren willst. +Die "Marktschreier"-Einstellung (Soapbox) lässt den Seitenbesitzer die gesamte Kommunikation kontrollieren. +Alles was Du schreibst, geht an alle Seitennutzer, aber es gibt keine Möglichkeit, zu interagieren. +Diese Seite wird normalerweise für Ankündigungen oder die Kommunikation von Gemeinschaften genutzt. -Die normalste Einstellung ist das "Forum-/Promi-Konto". Diese erstellt eine Gruppenseite, in der alle Mitglieder frei miteinander interagieren können. +Die normalste Einstellung ist das "Forum-/Promi-Konto". +Diese erstellt eine Gruppenseite, in der alle Mitglieder frei miteinander interagieren können. -Der "Automatische Freunde Seite"-Account ist typischerweise für persönliche Profile, bei denen du alle Freundschaftsanfragen automatisch bestätigen willst. +Der "Automatische Freunde Seite"-Account ist typischerweise für persönliche Profile, bei denen Du alle Freundschaftsanfragen automatisch bestätigen willst. **Multiple Foren verwalten** -Wir schlagen vor, dass du ein Gruppenforum mit der gleichen Email-Adresse und dem gleichen Passwort wie bei deinem normalen Account nutzt. Wenn du das machst, findest du einen neuen "Verwalten"-Link im Hauptmenü, das dir hilft, einfach zwischen den Identitäten zu wechseln. Du musst das nicht machen, die Alternative ist allerdings, dich immer wieder aus- und wieder einzuloggen. Und das kann umständlich sein, wenn du mehrere verschiedene Foren/Identitäten verwaltest. +Wir schlagen vor, dass Du ein Gruppenforum mit der gleichen Email-Adresse und dem gleichen Passwort wie bei Deinem normalen Account nutzt. +Wenn Du das machst, findest Du einen neuen "Verwalten"-Link in der Menüleiste, über den Du einfach zwischen den Identitäten wechseln kannst. +Du musst das nicht machen, die Alternative ist allerdings, Dich immer wieder aus- und wieder einzuloggen. +Und das kann umständlich sein, wenn Du mehrere verschiedene Foren/Identitäten verwaltest. -Du kannst ebenso jemanden wählen, der dein Forum verwaltet. Mach das, indem du die [Delegierungs-Setup-Seite](delegate) besuchst. Dort wird dir eine Liste an "Potentiellen Bevollmächtigen" angezeigt. Die Auswahl einer oder mehrerer Personen gibt diesen die Möglichkeit, dein Forum zu verwalten. Sie können Kontakte, Profile und alle Inhalte deines Accounts/deiner Seite bearbeiten. Bitte nutze diese Einstellung mit Vorsicht. Delegierte haben jedoch keine Möglichkeit, grundlegende Account-Einstellungen wie das Passwort oder den Seitentypen zu ändern bzw. den Account zu löschen. +Du kannst ebenso jemanden wählen, der Dein Forum verwaltet. +Mach das, indem Du die [Delegations-Setup-Seite](/delegate) besuchst. +Dort wird Dir eine Liste an "Potentiellen Bevollmächtigen" angezeigt. +Die Auswahl einer oder mehrerer Personen gibt diesen die Möglichkeit, Dein Forum zu verwalten. +Sie können Kontakte, Profile und alle Inhalte Deines Accounts/deiner Seite bearbeiten. +Bitte nutze diese Einstellung mit Vorsicht. +Delegierte haben allerdings keine Möglichkeit, grundlegende Account-Einstellungen wie das Passwort oder den Seitentypen zu ändern bzw. den Account zu löschen. **Beiträge auf Community-Foren** -Wenn du Mitglied eines Community-Forums bist, kannst du das Forum in einem Beitrag hinzufügen/erwähnen, wenn du den @-Tag nutzt. Zum Beispiel würde @Fahrrad deinen Beitrag neben den sonst ausgewählten Nutzern an alle Nutzer schicken, die in der Gruppe "Fahrrad" sind. Wenn dein Beitrag privat ist, musst du diese Gruppe explizit in den Zugriffsrechten des Beitrags auswählen **und** sie mit dem @-Tag erwähnen (was den Beitrag auf die Gruppenmitglieder erweitert). +Wenn Du Mitglied eines Community-Forums bist, kannst Du das Forum in einem Beitrag hinzufügen/erwähnen, wenn Du den @-Tag nutzt. +Zum Beispiel würde @Fahrrad Deinen Beitrag neben den sonst ausgewählten Nutzern an alle Nutzer schicken, die in der Gruppe "Fahrrad" sind. +Wenn Dein Beitrag privat ist, musst Du diese Gruppe explizit in den Zugriffsrechten des Beitrags auswählen **und** sie mit dem @-Tag erwähnen (was den Beitrag auf die Gruppenmitglieder erweitert). Du kannst außerdem via "Wall zu Wall" einen Beitrag auf der Community-Seite bzw. in dem Community-Forum erstellen. -Kommentare, die du an ein Community-Forum schickst, werden an den Originalbeitrag weitergeleitet. Das Forum mit dem @-Tag zu erwähnen, leitet den Beitrag nicht weiter, da die Verteilung des Beitrages komplett vom Original-Beitragsschreiber kontrolliert wird. +Kommentare, die Du an ein Community-Forum schickst, werden dem Originalbeitrag hinzugefügt. +Ein weiteres Forum mit dem @-Tag zu erwähnen, leitet den Beitrag nicht an dieses weiter, da die Verteilung der Kommentare komplett vom Originalbeitrag bestimmt wird. diff --git a/doc/de/Groups-and-Privacy.md b/doc/de/Groups-and-Privacy.md index 90487e898..4ce4d28e1 100644 --- a/doc/de/Groups-and-Privacy.md +++ b/doc/de/Groups-and-Privacy.md @@ -3,66 +3,110 @@ Gruppen und Privatsphäre * [Zur Startseite der Hilfe](help) -Gruppen sind nur eine Ansammlung von Freunden. Aber Friendica nutzt diese, um sehr mächtige Features zur Verfügung zu stellen. +Gruppen sind nur eine Ansammlung von Freunden. +Aber Friendica nutzt diese, um sehr mächtige Features zur Verfügung zu stellen. **Gruppen erstellen** -Um eine Gruppe zu erstellen, besuche deine "Kontakte"-Seite und wähle "Neue Gruppe erstellen" (je nach Design nur als Pluszeichen angezeigt). Gib deiner Gruppe einen Namen. +Um eine Gruppe zu erstellen, besuche deine "Kontakte"-Seite und wähle "Neue Gruppe erstellen" (je nach Design nur als Pluszeichen angezeigt). +Gib deiner Gruppe einen Namen. Das führt dich zu einer Seite, auf der du die Gruppenmitglieder auswählen kannst. -Du hast zwei Boxen auf der Seite. Die obere Box ist die Übersicht der aktuellen Mitglieder. Die untere beinhaltet alle Freunde, die *nicht* Mitglied dieser Gruppe sind. +Du hast zwei Boxen auf der Seite. +Die obere Box ist die Übersicht der aktuellen Mitglieder. +Die untere beinhaltet alle Freunde, die *nicht* Mitglied dieser Gruppe sind. -Wenn du auf das Foto einer Person klickst, die nicht in der Gruppe ist, wird diese in die Gruppe verschoben. Wenn du auf das Foto einer Person klickst, die bereits in der Gruppe ist, dann wird diese Person daraus entfernt. +Wenn du auf das Foto einer Person klickst, die nicht in der Gruppe ist, wird diese in die Gruppe verschoben. +Wenn du auf das Foto einer Person klickst, die bereits in der Gruppe ist, dann wird diese Person daraus entfernt. **Zugriffskontrolle** -Sobald du eine Gruppe erstellt hast, kannst du diese auf jeder Zugriffsrechteliste nutzen. Damit ist das kleine Schloss neben deinem Statuseditor auf deiner Startseite gemeint. Wenn du darauf klickst, kannst du auswählen, wer deinen Beitrag sehen kann und wer *nicht*. Dabei kann es sich um eine einzelne Person oder eine ganze Gruppe handeln. +Sobald du eine Gruppe erstellt hast, kannst du diese auf jeder Zugriffsrechteliste nutzen. +Damit ist das kleine Schloss neben deinem Statuseditor auf deiner Startseite gemeint. +Wenn du darauf klickst, kannst du auswählen, wer deinen Beitrag sehen kann und wer *nicht*. +Dabei kann es sich um eine einzelne Person oder eine ganze Gruppe handeln. -Auf deiner "Netzwerk"-Seite ("Unterhaltungen deiner Kontakte") findest du Beiträge und Gespräche aller deiner Kontakte in deinem Netzwerk. Du kannst aber auch eine einzelne Gruppe auswählen und nur Beiträge dieser Gruppenmitglieder anzeigen lassen. +Auf deiner "Netzwerk"-Seite ("Unterhaltungen deiner Kontakte") findest du Beiträge und Gespräche aller deiner Kontakte in deinem Netzwerk. +Du kannst aber auch eine einzelne Gruppe auswählen und nur Beiträge dieser Gruppenmitglieder anzeigen lassen. Aber stopp, es gibt noch mehr... -Wenn du auf deiner "Netzwerk"-Seite eine bestimmte Gruppe ausgewählt hast, dann findest du im Statuseditor neben dem Schloss ein Ausrufezeichen. Dies dient dazu, deine Aufmerksamkeit auf das Schloss zu richten. Klicke auf das Schloss. Dort siehst du, dass dein Status-Update in dieser Ansicht standardmäßig nur für diese Gruppe freigegeben ist. Das hilft dir, deinen zukünftigen Mitarbeitern nicht das Gleiche zu schreiben wie deinen Trinkfreunden. Du kannst diese Einstellung natürlich auch überschreiben. +Wenn du auf deiner "Netzwerk"-Seite eine bestimmte Gruppe ausgewählt hast, dann findest du im Statuseditor neben dem Schloss ein Ausrufezeichen. +Dies dient dazu, deine Aufmerksamkeit auf das Schloss zu richten. +Klicke auf das Schloss. +Dort siehst du, dass dein Status-Update in dieser Ansicht standardmäßig nur für diese Gruppe freigegeben ist. +Das hilft dir, deinen zukünftigen Mitarbeitern nicht das Gleiche zu schreiben wie deinen Trinkfreunden. +Du kannst diese Einstellung natürlich auch überschreiben. **Standardmäßige Zugriffsrechte von Beiträgen** -Standardmäßig geht Friendica davon aus, dass alle deine Beiträge privat sein sollen. Aus diesem Grund erstellt Friendica nach der Anmeldung eine Gruppe, in die automatisch alle deine Kontakte hinzugefügt werden. Alle deine Beiträge sind nur auf diese Gruppe beschränkt. +Standardmäßig geht Friendica davon aus, dass alle deine Beiträge privat sein sollen. +Aus diesem Grund erstellt Friendica nach der Anmeldung eine Gruppe, in die automatisch alle deine Kontakte hinzugefügt werden. +Alle deine Beiträge sind nur auf diese Gruppe beschränkt. Beachte, dass diese Einstellung von deinem Seiten-Administrator überschrieben werden kann, was bedeutet, dass alle deine Beiträge standardmäßig "öffentlich" sind (bspw. für das gesamte Internet). -Wenn du deine Beiträge standardmäßig "öffentlich" haben willst, dann kannst du deine Standardzugriffsrechte auf deiner Einstellungseite ändern. Dort kannst du außerdem festlegen, welchen Gruppen standardmäßig deine Beiträge erhalten oder in welche Gruppe deine neuen Kontakte standardmäßig eingeordnet werden. +Wenn du deine Beiträge standardmäßig "öffentlich" haben willst, dann kannst du deine Standardzugriffsrechte auf deiner Einstellungseite ändern. +Dort kannst du außerdem festlegen, welchen Gruppen standardmäßig deine Beiträge erhalten oder in welche Gruppe deine neuen Kontakte standardmäßig eingeordnet werden. **Fragen der Privatssphäre, die zu beachten sind** -Diese privaten Gespräche funktionieren am besten, wenn deine Freunde Friendica-Mitglieder sind. So wissen wir, wer sonst noch deine Gespräche sehen kann - niemand, *solange* deine Freunde deine Nachrichten nicht kopieren und an andere verschicken. +Diese privaten Gespräche funktionieren am besten, wenn deine Freunde Friendica-Mitglieder sind. +So wissen wir, wer sonst noch deine Gespräche sehen kann - niemand, *solange* deine Freunde deine Nachrichten nicht kopieren und an andere verschicken. -Dies ist eine Vertrauensfrage, die du beachten musst. Keine Software der Welt kann deine Freunde davon abhalten, die privaten Unterhaltungen zu veröffentlichen. Nur eine gute Auswahl deiner Freunde. +Dies ist eine Vertrauensfrage, die du beachten musst. +Keine Software der Welt kann deine Freunde davon abhalten, die privaten Unterhaltungen zu veröffentlichen. +Nur eine gute Auswahl deiner Freunde. -Bei status.net, identi.ca und anderen Netzwerk-Anbietern ist es nicht so gesichert. Du musst **sehr** vorsichtig sein, wenn du Mitglieder anderer Netzwerke in einer deiner Gruppen hast, da es möglich ist, dass deine privaten Nachrichten in einem öffentlichen Stream enden. Wenn du auf die "Kontakt bearbeiten"-Seite einer Person gehst, zeigen wir dir, ob sie Mitglied eines unsicheren Netzwerks ist oder nicht. +Bei GNu Social und anderen Netzwerk-Anbietern ist es nicht so gesichert. +Du musst **sehr** vorsichtig sein, wenn du Mitglieder anderer Netzwerke in einer deiner Gruppen hast, da es möglich ist, dass deine privaten Nachrichten in einem öffentlichen Stream enden. +Wenn du auf die "Kontakt bearbeiten"-Seite einer Person gehst, zeigen wir dir, ob sie Mitglied eines unsicheren Netzwerks ist oder nicht. -Sobald du einen Post erstellt hast, kannst du die Zugriffsrechte nicht mehr ändern. Innerhalb von Sekunden ist dieser an viele verschiedene Personen verschickt worden - möglicherweise bereits an alle Addressierten. Wenn du versehentlich eine Nachricht erstellt hast und sie zurücknehmen willst, dann ist es das beste, diese zu löschen. Wir senden eine Löschmitteilung an jeden, der deine Nachricht erhalten hat - und das sollte die Nachricht genauso schnell löschen, wie sie zunächst erstellt wurde. In vielen Fällen wird sie in weniger als einer Minute aus dem Internet gelöscht. Nochmals: das gilt für Friendica-Netzwerke. Sobald eine Nachricht an ein anderes Netzwerk geschickt wurde, kann es nicht mehr so schnell gelöscht werden und in manchen Fällen auch gar nicht mehr. +Sobald du einen Post erstellt hast, kannst du die Zugriffsrechte nicht mehr ändern. +Innerhalb von Sekunden ist dieser an viele verschiedene Personen verschickt worden - möglicherweise bereits an alle Addressierten. +Wenn du versehentlich eine Nachricht erstellt hast und sie zurücknehmen willst, dann ist es das beste, diese zu löschen. +Wir senden eine Löschmitteilung an jeden, der deine Nachricht erhalten hat - und das sollte die Nachricht genauso schnell löschen, wie sie zunächst erstellt wurde. +In vielen Fällen wird sie in weniger als einer Minute aus dem Internet gelöscht. +Nochmals: das gilt für Friendica-Netzwerke. +Sobald eine Nachricht an ein anderes Netzwerk geschickt wurde, kann es nicht mehr so schnell gelöscht werden und in manchen Fällen auch gar nicht mehr. -Wenn du das bisher noch nicht wusstest, dann empfehlen wir dir, deine Freunde dazu zu ermutigen, auch Friendica zu nutzen, da alle diese Privatsphären-Einstellungen innerhalb eines privatsphärenbewussten Netzwerk viel besser funktionieren. Viele andere Netzwerke, mit denen sich Friendica verbinden kann, bieten keine Kontrolle über die Privatsphäre. +Wenn du das bisher noch nicht wusstest, dann empfehlen wir dir, deine Freunde dazu zu ermutigen, auch Friendica zu nutzen, da alle diese Privatsphären-Einstellungen innerhalb eines privatsphärenbewussten Netzwerk viel besser funktionieren. +Viele andere Netzwerke, mit denen sich Friendica verbinden kann, bieten keine Kontrolle über die Privatsphäre. Profile, Fotos und die Privatsphäre ============================= -Die dezentralisierte Natur von Friendica (statt eine Webseite zu haben, die alles kontrolliert, gibt es viele Webseiten, die Information austauschen) hat in der Kommunikation mit anderen Seiten einige Konsequenzen. Du solltest dir über einige Dinge bewusst sein, um am besten entscheiden zu können, wie du mit deiner Privatsphäre umgehst. +Die dezentralisierte Natur von Friendica (statt eine Webseite zu haben, die alles kontrolliert, gibt es viele Webseiten, die Information austauschen) hat in der Kommunikation mit anderen Seiten einige Konsequenzen. +Du solltest dir über einige Dinge bewusst sein, um am besten entscheiden zu können, wie du mit deiner Privatsphäre umgehst. **Fotos** -Fotos privat zu verteilen ist ein Problem. Wir können Fotos nur mit Friendica-Nutzern __privat__ austauschen. Um mit anderen Leuten Fotos zu teilen, müssen wir erkennen, wer sie sind. Wir können die Identität von Friendica-Nutzern prüfen, da es hierfür einen Mechanismus gibt. Deine Freunde anderer Netzwerke werden deine privaten Fotos nicht sehen können, da wir deren Identität nicht überprüfen können. +Fotos privat zu verteilen ist ein Problem. +Wir können Fotos nur mit Friendica-Nutzern __privat__ austauschen. +Um mit anderen Leuten Fotos zu teilen, müssen wir erkennen, wer sie sind. +Wir können die Identität von Friendica-Nutzern prüfen, da es hierfür einen Mechanismus gibt. +Deine Freunde anderer Netzwerke werden deine privaten Fotos nicht sehen können, da wir deren Identität nicht überprüfen können. -Unsere Entwickler arbeiten an einer Lösung, um deinen Freunden den Zugriff zu ermöglichen - unabhängig, zu welchem Netzwerk sie gehören. Wir nehmen hingegen Privatsphäre ernst und agieren nicht wie andere Netzwerke, die __nur so tun__ als ob deine Fotos privat sind, sie aber trotzdem anderen ohne Identitätsprüfung zeigen. +Unsere Entwickler arbeiten an einer Lösung, um deinen Freunden den Zugriff zu ermöglichen - unabhängig, zu welchem Netzwerk sie gehören. +Wir nehmen hingegen Privatsphäre ernst und agieren nicht wie andere Netzwerke, die __nur so tun__ als ob deine Fotos privat sind, sie aber trotzdem anderen ohne Identitätsprüfung zeigen. **Profile** -Dein Profil und deine "Wall" sollen vielleicht auch von Freunden anderer Netzwerke besucht werden können. Wenn du diese Seiten allerdings für Webbesucher sperrst, die Friendica nicht kennt, kann das auch Freunde anderer Netzwerke blockieren. +Dein Profil und deine "Wall" sollen vielleicht auch von Freunden anderer Netzwerke besucht werden können. +Wenn du diese Seiten allerdings für Webbesucher sperrst, die Friendica nicht kennt, kann das auch Freunde anderer Netzwerke blockieren. -Das kann möglicherweise ungewollte Ergebnisse produzieren, wenn du lange Statusbeiträge z.B. für Twitter oder Facebook schreibst. Wenn Friendica einen Beitrag an diese Netzwerke schickt und nur eine bestimmte Nachrichtenlänge erlaubt ist, dann verkürzen wir diesen und erstellen einen Link, der zum Originalbeitrag führt. Der Originallink führt zurück zu deinem Friendica-Profil. Da Friendica nicht bestätigen kann, um wen es sich handelt, kann es passieren, dass diese Leute den Beitrag nicht komplett lesen können. +Das kann möglicherweise ungewollte Ergebnisse produzieren, wenn du lange Statusbeiträge z.B. für Twitter oder Facebook schreibst. +Wenn Friendica einen Beitrag an diese Netzwerke schickt und nur eine bestimmte Nachrichtenlänge erlaubt ist, dann verkürzen wir diesen und erstellen einen Link, der zum Originalbeitrag führt. +Der Originallink führt zurück zu deinem Friendica-Profil. +Da Friendica nicht bestätigen kann, um wen es sich handelt, kann es passieren, dass diese Leute den Beitrag nicht komplett lesen können. Für Leute, die davon betroffen sind, schlagen wir vor, eine Zusammenfassung in Twitter-Länge zu erstellen mit mehr Details für Freunde, die den ganzen Beitrag sehen können. -Dein Profil oder deine gesamte Friendica-Seite zu blockieren, hat außerdem ernsthafte Einflüsse auf deine Kommunikation mit StatusNet/identi.ca-Nutzern. Diese Netzwerke kommunizieren mit anderen über öffentliche Protokolle, die nicht authentifiziert werden. Um deine Beiträge zu sehen, müssen diese Netzwerke deine Beiträge als "unbekannte Webbesucher" ansehen. Wenn wir das erlauben, würde es dazu führen, das absolut jeder deine Beiträge sehen. Und du hast Friendica so eingestellt, das nicht zuzulassen. Beachte also, dass das Blockieren von unbekannten Besuchern auch dazu führen kann, dass öffentliche Netzwerke (wie identi.ca) und Newsfeed-Reader wie Google Reader auch geblockt werden. +Dein Profil oder deine gesamte Friendica-Seite zu blockieren, hat außerdem ernsthafte Einflüsse auf deine Kommunikation mit GNU Social-Nutzern. +Diese Netzwerke kommunizieren mit anderen über öffentliche Protokolle, die nicht authentifiziert werden. +Um deine Beiträge zu sehen, müssen diese Netzwerke deine Beiträge als "unbekannte Webbesucher" ansehen. +Wenn wir das erlauben, würde es dazu führen, das absolut jeder deine Beiträge sehen. +Und du hast Friendica so eingestellt, das nicht zuzulassen. +Beachte also, dass das Blockieren von unbekannten Besuchern auch dazu führen kann, dass öffentliche Netzwerke (wie GNU Social) und Newsfeed-Reader auch geblockt werden. diff --git a/doc/de/Home.md b/doc/de/Home.md index 0fd5634cc..561809d6c 100644 --- a/doc/de/Home.md +++ b/doc/de/Home.md @@ -10,6 +10,8 @@ Friendica - Dokumentation und Ressourcen * [Referenz der BBCode Elemente](help/BBCode) * [Beiträge kommentieren, einordnen und löschen](help/Text_comment) * [Profile](help/Profiles) + * [Referenz der Accesskeys](help/Accesskeys) + * [Veranstaltungen](help/events) (EN) * Du und andere Nutzer * [Konnektoren (Connectors)](help/Connectors) * [Freunde finden](help/Making-Friends) @@ -29,13 +31,23 @@ Friendica - Dokumentation und Ressourcen * [Installation](help/Install) * [Konfigurationen](help/Settings) * [Plugins](help/Plugins) -* [Konnektoren (Connectors) installieren (Facebook/Twitter/StatusNet)](help/Installing-Connectors) +* [Konnektoren (Connectors) installieren (Twitter/GNU Social)](help/Installing-Connectors) * [Nachrichtenfluss](help/Message-Flow) * [Betreibe deine Seite mit einem SSL-Zertifikat](help/SSL) * [Entwickler](help/Developers) -* [Twitter/StatusNet API Functions](help/api) (EN) +* [Twitter/GNU Social API Functions](help/api) (EN) * [Translation of Friendica](help/translations) (EN) +**Entwickler Dokumentation** + +* [Where to get started?](help/Developers-Intro) +* [Help on Github](help/Github) +* [Help on Vagrant](help/Vagrant) +* [How to translate Friendica](help/translations) +* [Bugs and Issues](help/Bugs-and-Issues) +* [Plugin Development](help/Plugins) +* [Theme Development](help/themes) +* [Smarty 3 Templates](help/smarty3-templates) **Externe Ressourcen** diff --git a/doc/de/Improve-Performance.md b/doc/de/Improve-Performance.md index 346c76fc3..674927396 100644 --- a/doc/de/Improve-Performance.md +++ b/doc/de/Improve-Performance.md @@ -16,55 +16,25 @@ Geh auf /admin/site in deinem System und ändere die folgenden Werte: setze "Qualität des JPEG Bildes" auf 50. -Dieser Wert reduziert die Daten, die vom Server an den Client geschickt werden. 50 ist ein Wert, der die Bildqualität nicht zu stark beeinflusst. +Dieser Wert reduziert die Daten, die vom Server an den Client geschickt werden. +50 ist ein Wert, der die Bildqualität nicht zu stark beeinflusst. setze "Intervall zum Vervollständigen von OStatus Unterhaltungen" auf "niemals" -Wenn du viele OStatus-Kontakte hast, dann kann die Vervollständigung von Unterhaltungen sehr zeitraubend sein. Der Nachteil: Du siehst nicht jede Antwort einer OStatus-Unterhaltung. - - setze "Pfad für die Sperrdatei" auf einen Ordner außerhalb deines Stammverzeichnisses deines Servers. - -Sperrdateien sorgen dafür, dass Hintergrundprozesse nicht parallel ablaufen. - -Als Beispiel: Es kann passieren, dass die poller.php länger als erwartet läuft. Ohne Sperrdatei kann es passieren, dass mehrere Instanzen der poller.php zur gleichen Zeit laufen. Dies würde das System verlangsamen und Einfluss auf die maximale Anzahl an Prozessen und Datenbankverbindungen nehmen. - -Bitte definiere einen kompletten Pfad, auf den der Server einen Schreibzugriff hat. Wenn deine Seite unter "/var/www/namederseite/htdocs/" liegt, dann kannst du z.B. einen Ordner unter "/var/www/sitename/temp/" erstellen. +Wenn du viele OStatus-Kontakte hast, dann kann die Vervollständigung von Unterhaltungen sehr zeitraubend sein. +Der Nachteil: Du siehst nicht jede Antwort einer OStatus-Unterhaltung. Aus diesem Grund ist die Option "Beim Empfang von Nachrichten" in der Regel ein guter Kompromiss. setze "Nutze MySQL full text engine". -Wenn du MyISAM (Standardeinstellung) nutzt, dann beschleunigt dies die Suche. - - setze "Pfad zum Eintrag Cache" auf einen leeren Ordner außerhalb deines Stammverzeichnisses. - -Verarbeiteter BBCode und einige externe Bilder werden hier gespeichert. BBCode verarbeiten ist ein zeitintensiver Prozess, der zudem eine hohe CPU-Leistung erfordert. - -Du kannst den gleichen Ordner nutzen, den du für die Sperrdatei genutzt hast. - -**Warnung!** - -Der Ordner für den Eintrag-Cache wird regelmäßig geleert. Jede Datei, die die Cache-Dauer überschreitet, wird gelöscht. **Wenn du versehentlich den Cache-Pfad auf dein Stammverzeichnis legst, dann würde dir dies das gesamte Stammverzeichnis löschen.** - -Prüfe also doppelt, dass der gewählte Ordner nur temporäre Dateien enthält, die jederzeit gelöscht werden können. +Wenn du MyISAM (Standardeinstellung) oder InnoDB mit MariaDB 10 nutzt, dann beschleunigt dies die Suche. Plugins -------- Aktiviere die folgenden Plugins: - Alternate Pagination - Privacy Image Cache rendertime -###Alternate Pagination - -**Beschreibung** - -Dieses Plugin reduziert die Ladezeit der Datenbank massiv. Nachteil: Du kannst nicht mehr die Anzahl aller Seiten sehen. - -**Einrichtung** - -Gehe auf admin/plugins/altpager und wähle "global". - ###rendertime **Beschreibung** @@ -119,15 +89,10 @@ Weitere Informationen findest du hier: http://httpd.apache.org/docs/2.2/mod/mod_ **FCGI** -Wenn du Apache nutzt, dann denk darüber nach, FCGI zu nutzen. Wenn du eine Debian-basierte Distribution nutzt, dann wirst du die Pakete "php5-cgi" und "libapache2-mod-fcgid" benötigen. +Wenn du Apache nutzt, dann denk darüber nach, FCGI zu nutzen. +Wenn du eine Debian-basierte Distribution nutzt, dann wirst du die Pakete "php5-cgi" und "libapache2-mod-fcgid" benötigen. Nutze externe Dokumente, um eine detailiertere Erklärung für die Einrichtung eines Systems auf FCGI-Basis zu erhalten. -**APC** - -APC ist ein Zwischenspeicher für die Verarbeitung des Befehlscodes. Es beschleunigt die Verarbeitung des PHP-Codes. - -Wenn APC aktiviert ist, dann nutzt Friendica dies, um Konfigurationseinstellungen für verschiedene Anfragen zwischenzuspeichern. Dies beschleunigt die Reaktionszeit der Seite. - ###Database Es gibt Skripte wie [tuning-primer.sh](http://www.day32.com/MySQL/) und [mysqltuner.pl](http://mysqltuner.pl), die den Datenbankserver analysieren und Hinweise darauf geben, welche Werte verändert werden könnten. diff --git a/doc/de/Install.md b/doc/de/Install.md index 8edebc5c4..df95735f3 100644 --- a/doc/de/Install.md +++ b/doc/de/Install.md @@ -3,11 +3,21 @@ Friendica Installation * [Zur Startseite der Hilfe](help) -Wir haben hart daran gearbeitet, um Friendica auf vorgefertigten Hosting-Plattformen zum Laufen zu bringen - solche, auf denen auch Wordpress Blogs und Drupal-Installationen laufen. Aber bedenke, dass Friendica mehr als eine einfache Webanwendung ist. Es handelt sich um ein komplexes Kommunikationssystem, das eher an einen Email-Server erinnert als an einen Webserver. Um die Verfügbarkeit und Performance zu gewährleisten, werden Nachrichten im Hintergrund verschickt und gespeichert, um sie später zu verschicken, wenn eine Webseite gerade nicht erreichbar ist. Diese Funktionalität benötigt ein wenig mehr als die normalen Blogs. Nicht jeder PHP/MySQL-Hosting-Anbieter kann Friendica unterstützen. Viele hingegen können es. Aber **bitte** prüfe die Voraussetzungen deines Servers vor der Installation. +Wir haben hart daran gearbeitet, um Friendica auf vorgefertigten Hosting-Plattformen zum Laufen zu bringen - solche, auf denen auch Wordpress Blogs und Drupal-Installationen laufen. +Aber bedenke, dass Friendica mehr als eine einfache Webanwendung ist. +Es handelt sich um ein komplexes Kommunikationssystem, das eher an einen Email-Server erinnert als an einen Webserver. +Um die Verfügbarkeit und Performance zu gewährleisten, werden Nachrichten im Hintergrund verschickt und gespeichert, um sie später zu verschicken, wenn eine Webseite gerade nicht erreichbar ist. +Diese Funktionalität benötigt ein wenig mehr als die normalen Blogs. +Nicht jeder PHP/MySQL-Hosting-Anbieter kann Friendica unterstützen. +Viele hingegen können es. Aber **bitte** prüfe die Voraussetzungen deines Servers vor der Installation. -Wenn dir Fehler während der Installation auffallen, sag uns bitte über das Forum auf http://groups.google.com/group/friendica oder über http://bugs.friendica.com Bescheid. Gib uns bitte so viele Infos zu deinem System, wie du kannst, und beschreibe den Fehler mit allen Details und Fehlermeldungen, so dass wir den Fehler zukünftig verhindern können. Aufgrund der großen Anzahl an verschiedenen Betriebssystemen und PHP-Plattformen haben wir nur geringe Kapazitäten, um deine PHP-Installation zu debuggen oder fehlende Module zu ersetzen, aber wir tun unser Bestes, um allgemeine Code-Fehler zu beheben. +Wenn dir Fehler während der Installation auffallen, sag uns bitte über http://bugs.friendica.com Bescheid. +Gib uns bitte so viele Infos zu deinem System, wie du kannst, und beschreibe den Fehler mit allen Details und Fehlermeldungen, so dass wir den Fehler zukünftig verhindern können. +Aufgrund der großen Anzahl an verschiedenen Betriebssystemen und PHP-Plattformen haben wir nur geringe Kapazitäten, um deine PHP-Installation zu debuggen oder fehlende Module zu ersetzen, aber wir tun unser Bestes, um allgemeine Code-Fehler zu beheben. -Bevor du anfängst: suche dir einen Domain- oder Subdomainnamen für deinen Server. Denke ausreichend darüber nach, da ein Wechsel nach der Friendica-Installation derzeit nicht unterstützt wird. Dinge verändern sich und einige deiner Freunde haben möglicherweise Probleme, mit dir zu kommunizieren. Wir planen, diese Einschränkung in einer zukünftigen Version zu beheben. +Bevor du anfängst: suche dir einen Domain- oder Subdomainnamen für deinen Server. +Dinge verändern sich und einige deiner Freunde haben möglicherweise Probleme, mit dir zu kommunizieren. +Wir planen, diese Einschränkung in einer zukünftigen Version zu beheben. 1. Voraussetzungen @@ -60,6 +70,8 @@ Bevor du anfängst: suche dir einen Domain- oder Subdomainnamen für deinen Serv 3. Erstelle eine leere Datenbank und notiere alle Zugangsdaten (Adresse der Datenbank, Nutzername, Passwort, Datenbankname). +Friendica benötigt die Berechtigungen um neue Felder in dieser Datenbank zu ertellen (create) und zu löschen (delete). + 4. Besuche deine Webseite mit deinem Browser und befolge die Anleitung. Bitte beachte jeden Fehler und korrigiere diese, bevor du fortfährst. 5. *Wenn* die automatisierte Installation aus irgendeinem Grund fehlschlägt, dann prüfe das Folgende: @@ -81,13 +93,18 @@ Wenn du einen Linux-Server nutzt, benutze den Befehl "crontab -e" und ergänze e `*/10 * * * * cd /home/myname/mywebsite; /usr/bin/php include/poller.php` -Du kannst den PHP-Pfad finden, indem du den Befehl „which php“ ausführst. Wenn du Schwierigkeiten mit diesem Schritt hast, kannst du deinen Hosting-Anbieter kontaktieren. Friendica wird nicht korrekt laufen, wenn dieser Schritt nicht erfolgreich abgeschlossen werden kann. +Du kannst den PHP-Pfad finden, indem du den Befehl „which php“ ausführst. +Wenn du Schwierigkeiten mit diesem Schritt hast, kannst du deinen Hosting-Anbieter kontaktieren. +Friendica wird nicht korrekt laufen, wenn dieser Schritt nicht erfolgreich abgeschlossen werden kann. -Alternativ kannst du das Plugin 'poormancron' nutzen, um diesen Schritt durchzuführen, wenn du eine aktuelle Friendica-Version nutzt. Um dies zu machen, musst du die ".htconfig.php" an der Stelle anpassen, die dein Plugin beschreibt. In einer frischen Installation sieht es aus wie: +Alternativ kannst du das Plugin 'poormancron' nutzen, um diesen Schritt durchzuführen, wenn du eine aktuelle Friendica-Version nutzt. +Um dies zu machen, musst du die ".htconfig.php" an der Stelle anpassen, die dein Plugin beschreibt. +In einer frischen Installation sieht es aus wie: `$a->config['system']['addon'] = 'js_upload';` -Dies setzt voraus, dass das Addon-Modul "js_upload" aktiviert ist. Du kannst auch weitere Addons/Plugins ergänzen. Ändere den Eintrag folgendermaßen ab: +Dies setzt voraus, dass das Addon-Modul "js_upload" aktiviert ist. +Du kannst auch weitere Addons/Plugins ergänzen. Ändere den Eintrag folgendermaßen ab: `$a->config['system']['addon'] = 'js_upload,poormancron';` diff --git a/doc/de/Installing-Connectors.md b/doc/de/Installing-Connectors.md index 80739ace7..76daa685f 100644 --- a/doc/de/Installing-Connectors.md +++ b/doc/de/Installing-Connectors.md @@ -1,20 +1,26 @@ -Konnektoren installieren (Facebook/Twitter/StatusNet) +Konnektoren installieren (Twitter/GNU Social) ================================================== * [Zur Startseite der Hilfe](help) -Friendica nutzt Plugins, um die Verbindung zu anderen Netzwerken wie Facebook und Twitter zu gewährleisten. +Friendica nutzt Plugins, um die Verbindung zu anderen Netzwerken wie Twitter oder App.net zu gewährleisten. -Es gibt außerdem ein Plugin, um über einen bestehenden Status.Net-Account diesen Service zu nutzen. Du brauchst dieses Plugin aber nicht, um mit Status.Net-Mitgliedern von Friendica aus zu kommunizieren - es sei denn, du wünschst es, über einen existierenden Account einen Beitrag zu schreiben. +Es gibt außerdem ein Plugin, um über einen bestehenden GNU Social-Account diesen Service zu nutzen. +Du brauchst dieses Plugin aber nicht, um mit GNU Social-Mitgliedern von Friendica aus zu kommunizieren - es sei denn, du wünschst es, über einen existierenden Account einen Beitrag zu schreiben. -Alle drei Plugins benötigen einen Account im gewünschten Netzwerk. Zusätzlich musst du (bzw. der Administrator der Seite) einen API-Schlüssel holen, um einen authentifizierten Zugriff zu deinem Friendica-Server herstellen zu lassen. +Alle drei Plugins benötigen einen Account im gewünschten Netzwerk. +Zusätzlich musst du (bzw. der Administrator der Seite) einen API-Schlüssel holen, um einen authentifizierten Zugriff zu deinem Friendica-Server herstellen zu lassen. **Seitenkonfiguration** -Plugins müssen vom Administrator installiert werden, bevor sie genutzt werden können. Dieses kann über das Administrationsmenü erstellt werden. +Plugins müssen vom Administrator installiert werden, bevor sie genutzt werden können. +Dieses kann über das Administrationsmenü erstellt werden. -Jeder der Konnektoren benötigt zudem einen API-Schlüssel vom Service, der verbunden werden soll. Einige Plugins erlaube es, diese Informationen auf den Administrationsseiten einzustellen, wohingegen andere eine direkte Bearbeitung der Konfigurationsdatei ".htconfig.php" erfordern. Der Weg, um diese Schlüssel zu erhalten, variiert stark, jedoch brauchen fast alle einen bestehenden Account im gewünschten Service. Einmal installiert, können diese Schlüssel von allen Seitennutzern genutzt werden. +Jeder der Konnektoren benötigt zudem einen API-Schlüssel vom Service, der verbunden werden soll. +Einige Plugins erlaube es, diese Informationen auf den Administrationsseiten einzustellen, wohingegen andere eine direkte Bearbeitung der Konfigurationsdatei ".htconfig.php" erfordern. +Der Weg, um diese Schlüssel zu erhalten, variiert stark, jedoch brauchen fast alle einen bestehenden Account im gewünschten Service. +Einmal installiert, können diese Schlüssel von allen Seitennutzern genutzt werden. Im Folgenden findest du die Einstellungen für die verschiedenen Services (viele dieser Informationen kommen direkt aus den Quelldateien der Plugins): @@ -40,10 +46,8 @@ $a->config['twitter']['consumersecret'] = 'your consumer_secret here'; Anschließend kann der Nutzer deiner Seite die Twitter-Einstellungen selbst eintragen: "Einstellungen -> Connector Einstellungen". -Dokumentation: http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/Twitter_Plugin - -**StatusNet Plugin für Friendica** +**GNU Social Plugin für Friendica** * Author: Tobias Diekershoff * tobias.diekershoff@gmx.net @@ -52,59 +56,29 @@ Dokumentation: http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/Twit Konfiguration -Wenn das Addon aktiv ist, muss der Nutzer die folgenden Einstellungen vornehmen, um sich mit dem StatusNet-Account seiner Wahl zu verbinden. +Wenn das Addon aktiv ist, muss der Nutzer die folgenden Einstellungen vornehmen, um sich mit dem GNU Social-Account seiner Wahl zu verbinden. -* Die Basis-URL des StatusNet-API; für identi.ca ist es https://identi.ca/api/ +* Die Basis-URL des GNU Social-API; für quitter.se ist es https://quitter.se/api/ * OAuth Consumer key & Geheimnis Um das OAuth-Schlüsselpaar zu erhalten, muss der Nutzer (a) seinen Friendica-Admin fragen, ob bereits ein Schlüsselpaar existiert oder -(b) einen Friendica-Server als Anwendung auf dem StatusNet-Server anmelden. +(b) einen Friendica-Server als Anwendung auf dem GNU Social-Server anmelden. -Dies kann über Einstellungen --> Connections --> "Register an OAuth client application" -> "Register a new application" auf dem StatusNet-Server durchgeführt werden. +Dies kann über Einstellungen --> Connections --> "Register an OAuth client application" -> "Register a new application" auf dem GNU Social-Server durchgeführt werden. Während der Registrierung des OAuth-Clients ist Folgendes zu beachten: -* Der Anwendungsname muss auf der StatusNet-Seite einzigartig sein, daher empfehlen wir einen Namen wie "friendica-nnnn", ersetze dabei "nnnn" mit einer frei gewählten Nummer oder deinem Webseitennamen. +* Der Anwendungsname muss auf der GNU Social-Seite einzigartig sein, daher empfehlen wir einen Namen wie "friendica-nnnn", ersetze dabei "nnnn" mit einer frei gewählten Nummer oder deinem Webseitennamen. * es gibt keine Callback-URL * Registriere einen Desktop-Client * stelle Lese- und Schreibrechte ein * die Quell-URL sollte die URL deines Friendica-Servers sein -Sobald die benötigten Daten gespeichert sind, musst du deinen Friendica-Account mit StatusNet verbinden. Das kannst du über Einstellungen --> Connector-Einstellungen durchführen. Folge dem "Einloggen mit StatusNet"-Button, erlaube den Zugriff und kopiere den Sicherheitscode in die entsprechende Box. Friendica wird dann versuchen, die abschließende OAuth-Einstellungen über die API zu beziehen. +Sobald die benötigten Daten gespeichert sind, musst du deinen Friendica-Account mit GNU Social verbinden. +Das kannst du über Einstellungen --> Connector-Einstellungen durchführen. +Folge dem "Einloggen mit GNU Social"-Button, erlaube den Zugriff und kopiere den Sicherheitscode in die entsprechende Box. +Friendica wird dann versuchen, die abschließende OAuth-Einstellungen über die API zu beziehen. -Wenn es geklappt hat, kannst du in den Einstellungen festlegen, ob deine öffentlichen Nachrichten automatisch in deinem StatusNet-Account erscheinen soll (achte hierbei auf das kleine Schloss-Symbol im Status-Editor) - -Dokumentation: http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/StatusNet_Plugin - - -**Installiere den Friendica/Facebook-Konnektor** - -* Registriere einen API-Schlüssel für deine Seite auf [developer.facebook.com](Facebook). - -Hierfür benötigst du einen Facebook-Account und ggf. weitere Authentifizierungen über eine Kreditkarten- oder Mobilfunknummer. - -a. Wir würden uns sehr darüber freuen, wenn du "Friendica" in dem Anwendungsnamen eintragen würdest, um die Bekanntheit des Namens zu erhöhen. Das Friendica-Icon ist im Bildverzeichnis enthalten und kann als Anwendungs-Icon für die Facebook-App genutzt werden. Nutze [images/friendica-16.jpg](images/friendica-16.jpg) für das Icon und [images/friendica-128.jpg](images/friendica-128.jpg) für das Logo. - -b. Die URL sollte deine Seite mit dem abschließenden Schrägstrich sein - -Es **kann** notwendig sein, dass du eine "Privacy"- oder "Terms of service"-URL angeben musst. - -c. Setze nun noch unter "App Domains" die URL auf deineSubdomain.deineDomain.de und bei "Website with Facebook Login" die URL zu deineDomain.de. - -d. Installiere nun das Facebook-Plugin auf deiner Friendica-Seite über "admin/plugins". Du solltest links in der Sidebar einen Facebook-Link unter "Plugin Features" finden. Klicke diesen an. - -e. Gib nun die App-ID und das App-Secret ein, die Facebook dir gegeben hat. Ändere die anderen Daten, wie es gewünscht ist. - -Auf Friendica kann nun jeder Nutzer, der eine Verbindung zu Facebook wünscht, die Seite "Einstellungen -> Connector-Einstellungen" aufrufen und dort "Installiere Facebook-Connector" auswählen. - -Wähle die gewünschten Einstellungen für deine Nutzungs- und Privatsphäreansprüche. - -Hier meldest du dich bei Facebook an und gibst dem Plugin die nötigen Zugriffsrechte, um richtig zu funktionieren. Erlaube dieses. - -Und fertig. Um es abzustellen, gehe wieder auf die Einstellungsseite und auf "Remove Facebook posting". - -Videos und eingebetteter Code werden nicht gepostet, wenn sonst kein anderer Inhalt enthalten ist. Links und Bilder werden in ein Format übertragen, das von der Facebook-API verstanden wird. Lange Texte werden verkürzt und mit einem Link zum Originalbeitrag versehen. - -Facebook-Kontakte können außerdem keine privaten Fotos sehen, da diese nicht richtig authentifiziert werden können, wenn sie deine Seite besuchen. Dieser Fehler wird zukünftig bearbeitet. +Wenn es geklappt hat, kannst du in den Einstellungen festlegen, ob deine öffentlichen Nachrichten automatisch in deinem GNU Social-Account erscheinen soll (achte hierbei auf das kleine Schloss-Symbol im Status-Editor) diff --git a/doc/de/Making-Friends.md b/doc/de/Making-Friends.md index 74f1f62b3..b9ff78761 100644 --- a/doc/de/Making-Friends.md +++ b/doc/de/Making-Friends.md @@ -3,43 +3,75 @@ Freunde finden * [Zur Startseite der Hilfe](help) -Freundschaft kann in Friendica viele verschiedene Bedeutungen annehmen. Aber lasst es uns einfach halten, du willst einfach mit jemandem befreundet sein. Wie machst du das? +Freundschaft kann in Friendica viele verschiedene Bedeutungen annehmen. +Aber lasst es uns einfach halten, du willst einfach mit jemandem befreundet sein. +Wie machst du das? -Schau dir das Verzeichnis an. Das Verzeichnis ist in zwei Teile aufgeteilt. Wenn du auf den "Verzeichnis"-Button klickst, wirst du zunächst alle Mitglieder deines Servers sehen, die sich dazu entschlossen haben, angezeigt zu werden. Außerdem siehst du dort einen Link zum globalen Verzeichnis. Wenn du dich durch das globale Verzeichnis klickst, siehst du alle Nutzer weltweit auf allen Servern, die sich entschlossen haben, im Verzeichnis zu erscheinen. Du wirst außerdem den Link "Show Community Forums" sehen, welcher dich zu Gruppen, Foren und Fan-Seiten führt. Du verbindest dich mit Personen, Gruppen und Foren auf die gleiche Art, wobei Gruppen und Foren deine Anfrage automatisch annehmen, wohingegen ein Mensch dich erst manuell bestätigen muss. +Schau dir das Verzeichnis an. +Das Verzeichnis ist in zwei Teile aufgeteilt. +Wenn du auf den "Verzeichnis"-Button klickst, wirst du zunächst alle Mitglieder deines Servers sehen, die sich dazu entschlossen haben, angezeigt zu werden. +Außerdem siehst du dort einen Link zum globalen Verzeichnis. +Wenn du dich durch das globale Verzeichnis klickst, siehst du alle Nutzer weltweit auf allen Servern, die sich entschlossen haben, im Verzeichnis zu erscheinen. +Du wirst außerdem den Link "Show Community Forums" sehen, welcher dich zu Gruppen, Foren und Fan-Seiten führt. +Du verbindest dich mit Personen, Gruppen und Foren auf die gleiche Art, wobei Gruppen und Foren deine Anfrage automatisch annehmen, wohingegen ein Mensch dich erst manuell bestätigen muss. *Mit anderen Friendica-Nutzern verbinden* -Bes‪uche ihr Profil. Direkt unter dem Profilfoto ist das Wort "Verbinden" (bzw. "Connect" in einem englischsprachigem Profil). Klicke drauf und du gelangst zur "Verbinden"-Seite. Dort wirst du nach deiner Identitätsadresse gefragt. Das ist nötig, damit die Seite dein Profil finden kann. +Bes‪uche ihr Profil. +Direkt unter dem Profilfoto ist das Wort "Verbinden" (bzw. "Connect" in einem englischsprachigem Profil). +Klicke drauf und du gelangst zur "Verbinden"-Seite. +Dort wirst du nach deiner Identitätsadresse gefragt. +Das ist nötig, damit die Seite dein Profil finden kann. *Was kommt in die Box?* -Wenn deine Friendica-Seite "demo.friendica.com" heißt und dein Nutzername/Spitzname auf der Seite "bob" ist, dann wäre es "bob@demo.friendica.com". Wie du siehst, sieht es wie eine Email-Adresse aus. Das ist beabsichtigt, da sich die Leute das so leichter merken können. Du *kannst* auch die URL deiner Startseite eintragen, wie z.B. "http://demo.friendica.com/profile/bob", aber der Email-Adressen-Stil ist einfacher. +Wenn deine Friendica-Seite "demo.friendica.com" heißt und dein Nutzername/Spitzname auf der Seite "bob" ist, dann wäre es "bob@demo.friendica.com". +Wie du siehst, sieht es wie eine Email-Adresse aus. +Das ist beabsichtigt, da sich die Leute das so leichter merken können. +Du *kannst* auch die URL deiner Startseite eintragen, wie z.B. "http://demo.friendica.com/profile/bob", aber der Email-Adressen-Stil ist einfacher. -Wenn du die "Verbinden"-Seite bestätigt hast, kommst du zurück zu deiner Seite, um dort die Anfrage zu bestätigen. Wenn du das gemacht hast, können beide Seiten miteinander kommunizieren, um den Prozess abzuschließen (sobald dein neuer Freund die Anfrage bestätigt hat). +Wenn du die "Verbinden"-Seite bestätigt hast, kommst du zurück zu deiner Seite, um dort die Anfrage zu bestätigen. +Wenn du das gemacht hast, können beide Seiten miteinander kommunizieren, um den Prozess abzuschließen (sobald dein neuer Freund die Anfrage bestätigt hat). -Wenn du bereits die Identitäts-Adresse einer Person kennst, kannst du diese auch direkt in das "Verbinden"-Feld auf deiner "Kontakte"-Seite eintragen. Dies wird dich durch einen ähnlichen Prozess leiten. +Wenn du bereits die Identitäts-Adresse einer Person kennst, kannst du diese auch direkt in das "Verbinden"-Feld auf deiner "Kontakte"-Seite eintragen. +Dies wird dich durch einen ähnlichen Prozess leiten. **Alternative Netzwerke** -Du kannst deine oder andere Identitäts-Adressen ebenfalls nutzen, um über verschiedene Netzwerke hinweg Freundschaften aufzubauen. Die Liste möglicher Netzwerke steigt immer weiter. Wenn du z.B. "bob" auf identi.ca (eine Status.Net-Seite) kennst, dann kannst du bob@identi.ca auf deiner "Kontakt"-Seite verbinden. (Oder du kannst die URL von Bobs identi.ca-Seite eintragen, wenn du es wünscht). Du kannst auch "teilweise" mit Leuten auf Google Buzz befreundet sein, wenn du deren GMail-Adresse einträgst. Google Buzz unterstützt bisher noch nicht alle Protokolle, die wir für die direkte Kommunikation benötigen, aber es sollte möglich sein, Statusupdates von Friendica zu folgen. Das Gleiche gilt für Twitter- und Diaspora-Accounts. Tatsächlich kannst du jedem und jeder Website folgen, der/die einen Syndication-Feed (RSS/Atom etc.) zur Verfügung stellt. Wenn wir einen Informationsstrom und einen Namen dazu finden, können wir auch versuchen, uns damit zu verbinden. +Du kannst deine oder andere Identitäts-Adressen ebenfalls nutzen, um über verschiedene Netzwerke hinweg Freundschaften aufzubauen. +Die Liste möglicher Netzwerke steigt immer weiter. +Wenn du z.B. "bob" auf quitter.se (eine GNU Social-Seite) kennst, dann kannst du bob@quitter.se auf deiner "Kontakt"-Seite verbinden. (Oder du kannst die URL von Bobs quitter.se-Seite eintragen, wenn du es wünscht). +Tatsächlich kannst du jedem und jeder Website folgen, der/die einen Syndication-Feed (RSS/Atom etc.) zur Verfügung stellt. +Wenn wir einen Informationsstrom und einen Namen dazu finden, können wir auch versuchen, uns damit zu verbinden. -Wenn du deine Email-Postfachverbindung auf deiner Einstellungsseite konfiguriert hast, dann kannst du die Email-Adresse jeder Person eintragen, die dir schon eine Nachricht an dein Postfach geschickt hat und bereits in deinem sozialen Stream erscheint. Du kannst diesen Personen außerdem von Friendica aus antworten. +Wenn du deine Email-Postfachverbindung auf deiner Einstellungsseite konfiguriert hast, dann kannst du die Email-Adresse jeder Person eintragen, die dir schon eine Nachricht an dein Postfach geschickt hat und bereits in deinem sozialen Stream erscheint. +Du kannst diesen Personen außerdem von Friendica aus antworten. -Leute können sich ebenfalls von anderen Netzwerken aus mit dir befreunden. Ein Freund von dir hat einen identi.ca-Account und kann sich mit dir befreunden, indem er deine Identitäts-Adresse in seine identi.ca-Verbinden-Dialogbox einträgt. Ein ähnlicher Mechanismus ist für Diaspora-Nutzer vorhanden, indem deine Identitäts-Adresse in ihre Suchleiste eingegeben wird. +Leute können sich ebenfalls von anderen Netzwerken aus mit dir befreunden. +Ein Freund von dir hat einen GNU Social-Account und kann sich mit dir befreunden, indem er deine Identitäts-Adresse in seine GNU Social-Verbinden-Dialogbox einträgt. +Ein ähnlicher Mechanismus ist für Diaspora-Nutzer vorhanden, indem deine Identitäts-Adresse in ihre Suchleiste eingegeben wird. -Beachte: Manche StatusNet-Versionen benötigen die volle URL deines Profils und funktionieren möglicherweise nicht mit der Identitäts-Adresse. +Beachte: Manche GNU Social-Versionen benötigen die volle URL deines Profils und funktionieren möglicherweise nicht mit der Identitäts-Adresse. -Wenn jemand eine Freundschaftsanfrage schickt, erhältst du eine Benachrichtigung. Du musst dann diese Anfrage bestätigen, um die Freundschaftsanfrage abzuschließen. +Wenn jemand eine Freundschaftsanfrage schickt, erhältst du eine Benachrichtigung. +Du musst dann diese Anfrage bestätigen, um die Freundschaftsanfrage abzuschließen. -Einige Netzwerke erlauben es, Nachrichten zu schicken, ohne befreundet zu sein oder deine Bestätigung zu benötigen. Friendica erlaubt dies in der Standardeinstellung nicht, da es zu Spam führen kann. +Einige Netzwerke erlauben es, Nachrichten zu schicken, ohne befreundet zu sein oder deine Bestätigung zu benötigen. +Friendica erlaubt dies in der Standardeinstellung nicht, da es zu Spam führen kann. -Wenn du eine Freundschaftsanfrage von einem anderen Friendica-Nutzer erhältst, dann hast du die Möglichkeit, diesen als "Fan" oder "Freund" einzutragen. Ein Fan kann sehen, was du schreibst und auch private Kommunikation sehen, die du zu diesen sendest, aber nicht umgekehrt. Als Freund kannst du in beide Richtungen kommunizieren. +Wenn du eine Freundschaftsanfrage von einem anderen Friendica-Nutzer erhältst, dann hast du die Möglichkeit, diesen als "Fan" oder "Freund" einzutragen. +Ein Fan kann sehen, was du schreibst und auch private Kommunikation sehen, die du zu diesen sendest, aber nicht umgekehrt. +Als Freund kannst du in beide Richtungen kommunizieren. Diaspora nutzt eine andere Terminologie mit der Unterteilung in "mit dir teilen" und "Freund". -Sobald ihr Freunde geworden seid, dir die Person aber permanent Spam oder sinnlose Informationen schickt, dann kannst du diese "ignorieren" - ohne die Freundschaft komplett zu beenden oder denjenigen zu zeigen, dass du nicht daran interessiert bist, was diese Person sagt. In verschiedener Hinsicht sind diese Personen wie "Fans", aber sie wissen es nicht. Sie denken, sie sind als Freunde eingetragen. +Sobald ihr Freunde geworden seid, dir die Person aber permanent Spam oder sinnlose Informationen schickt, dann kannst du diese "ignorieren" - ohne die Freundschaft komplett zu beenden oder denjenigen zu zeigen, dass du nicht daran interessiert bist, was diese Person sagt. +In verschiedener Hinsicht sind diese Personen wie "Fans", aber sie wissen es nicht. +Sie denken, sie sind als Freunde eingetragen. -Du kannst auch eine Person "blocken". Das blockt die komplette Kommunikation mit dieser Person. Sie können zwar weiterhin öffentliche Beiträge sehen, wie auch jeder andere in der Welt, allerdings können sie nicht direkt mit dir kommunizieren. +Du kannst auch eine Person "blocken". +Das blockt die komplette Kommunikation mit dieser Person. +Sie können zwar weiterhin öffentliche Beiträge sehen, wie auch jeder andere in der Welt, allerdings können sie nicht direkt mit dir kommunizieren. Du kannst Freunde löschen, egal wie der Freundschaftsstatus ist, was dazu führt, dass alles, was mit dieser Person verbunden ist, von deiner Webseite gelöscht wird. diff --git a/doc/de/Message-Flow.md b/doc/de/Message-Flow.md index add6507ea..0694db134 100644 --- a/doc/de/Message-Flow.md +++ b/doc/de/Message-Flow.md @@ -3,13 +3,15 @@ Friendica Nachrichtenfluss * [Zur Startseite der Hilfe](help) -Diese Seite soll einige Infos darüber dokumentieren, wie Nachrichten innerhalb von Friendica von einer Person zur anderen übertragen werden. Es gibt verschiedene Pfade, die verschiedene Protokolle und Nachrichtenformate nutzen. +Diese Seite soll einige Infos darüber dokumentieren, wie Nachrichten innerhalb von Friendica von einer Person zur anderen übertragen werden. +Es gibt verschiedene Pfade, die verschiedene Protokolle und Nachrichtenformate nutzen. Diejenigen, die den Nachrichtenfluss genauer verstehen wollen, sollten sich mindestens mit dem DFRN-Protokoll (http://dfrn.org/dfrn.pdf) und den Elementen zur Nachrichtenverarbeitung des OStatus Stack informieren (salmon und Pubsubhubbub). Der Großteil der Nachrichtenverarbeitung nutzt die Datei include/items.php, welche Funktionen für verschiedene Feed-bezogene Import-/Exportaktivitäten liefert. -Wenn eine Nachricht veröffentlicht wird, werden alle Übermittlungen an alle Netzwerke mit include/notifier.php durchgeführt, welche entscheidet, wie und an wen die Nachricht geliefert wird. Diese Datei bindet dabei die lokale Bearbeitung aller Übertragungen ein inkl. dfrn-notify. +Wenn eine Nachricht veröffentlicht wird, werden alle Übermittlungen an alle Netzwerke mit include/notifier.php durchgeführt, welche entscheidet, wie und an wen die Nachricht geliefert wird. +Diese Datei bindet dabei die lokale Bearbeitung aller Übertragungen ein inkl. dfrn-notify. mod/dfrn_notify.php handhabt die Rückmeldung (remote side) von dfrn-notify. @@ -24,20 +26,38 @@ DFRN-poll Feed-Imports kommen via include/poller.php als geplanter Task an, das Szenario #1. Bob schreibt eine öffentliche Statusnachricht -Dies ist eine öffentliche Nachricht ohne begrenzte Nutzerfreigabe, so dass keine private Übertragung notwendig ist. Es gibt zwei Wege, die genutzt werden können - als bbcode an DFRN-Clients oder als durch den Server konvertierten HTML-Code (mit PuSH; pubsubhubbub). Wenn ein PuSH-Hub einsatzfähig ist, nutzen DFRN-Poll-Clients vorrangig die Informationen, die durch den PuSH-Kanal kommen. Sie fallen zurück auf eine tägliche Abfrage, wenn der Hub Übertragungsschwierigkeiten hat (das kann vorkommen, wenn der standardmäßige Google-Referenzhub genutzt wird). Wenn kein spezifizierter Hub oder Hubs ausgewählt sind, werden DFRN-Clients in einer pro Kontakt konfigurierbaren Rate mit bis zu 5-Minuten-Intervallen abfragen. Feeds, die via DFRN-Poll abgerufen werden, sind bbcode und können auch private Unterhaltungen enthalten, die vom Poller auf ihre Zugriffsrechte hin geprüft werden. +Dies ist eine öffentliche Nachricht ohne begrenzte Nutzerfreigabe, so dass keine private Übertragung notwendig ist. +Es gibt zwei Wege, die genutzt werden können - als bbcode an DFRN-Clients oder als durch den Server konvertierten HTML-Code (mit PuSH; pubsubhubbub). +Wenn ein PuSH-Hub einsatzfähig ist, nutzen DFRN-Poll-Clients vorrangig die Informationen, die durch den PuSH-Kanal kommen. +Sie fallen zurück auf eine tägliche Abfrage, wenn der Hub Übertragungsschwierigkeiten hat (das kann vorkommen, wenn der standardmäßige Google-Referenzhub genutzt wird). +Wenn kein spezifizierter Hub oder Hubs ausgewählt sind, werden DFRN-Clients in einer pro Kontakt konfigurierbaren Rate mit bis zu 5-Minuten-Intervallen abfragen. +Feeds, die via DFRN-Poll abgerufen werden, sind bbcode und können auch private Unterhaltungen enthalten, die vom Poller auf ihre Zugriffsrechte hin geprüft werden. Szenario #2. Jack antwortet auf Bobs öffentliche Nachricht. Jack ist im Friendica/DFRN-Netzwerk. -Jack nutzt dfrn-notify, um eine direkte Antwort an Bob zu schicken. Bob erstellt dann einen Feed der Unterhaltung und sendet diesen an jeden, der an der Unterhaltung beteiligt ist und dfrn-notify nutzt. Die PuSH-Hubs werden darüber informiert, dass neuer Inhalt verfügbar ist. Der/die Hub/s erhalten dann die neuesten Feeds und übertragen diese an alle Hub-Teilnehmer (die auch zu verschiedenen Netzwerken gehören können). +Jack nutzt dfrn-notify, um eine direkte Antwort an Bob zu schicken. +Bob erstellt dann einen Feed der Unterhaltung und sendet diesen an jeden, der an der Unterhaltung beteiligt ist und dfrn-notify nutzt. +Die PuSH-Hubs werden darüber informiert, dass neuer Inhalt verfügbar ist. Der/die Hub/s erhalten dann die neuesten Feeds und übertragen diese an alle Hub-Teilnehmer (die auch zu verschiedenen Netzwerken gehören können). Szenario #3. Mary antwortet auf Bobs öffentliche Nachricht. Mary ist im Friendica/DFRN-Netzwerk. -Mary nutzt dfrn-notify, um eine direkte Antwort an Bob zu schicken. Bob erstellt dann einen Feed der Unterhaltung und sendet diesen an jeden, der an der Unterhaltung beteiligt ist (mit Ausnahme von Bob selbst; die Unterhaltung wird nun an Jack und Mary geschickt). Die Nachrichten werden mit dfrn-notify übertragen. PuSH-Hubs werden darüber informiert, dass neuer Inhalt verfügbar ist. Der/die Hub/s erhalten dann die neuesten Feeds und übertragen sie an alle Hub-Teilnehmer (die auch zu verschiedenen Netzwerken gehören können). +Mary nutzt dfrn-notify, um eine direkte Antwort an Bob zu schicken. +Bob erstellt dann einen Feed der Unterhaltung und sendet diesen an jeden, der an der Unterhaltung beteiligt ist (mit Ausnahme von Bob selbst; die Unterhaltung wird nun an Jack und Mary geschickt). +Die Nachrichten werden mit dfrn-notify übertragen. +PuSH-Hubs werden darüber informiert, dass neuer Inhalt verfügbar ist. +Der/die Hub/s erhalten dann die neuesten Feeds und übertragen sie an alle Hub-Teilnehmer (die auch zu verschiedenen Netzwerken gehören können). Szenario #4. William antwortet auf Bobs öffentliche Nachricht. William ist in einem OStatus-Netzwerk. -William nutzt salmon, um Bob über seine Antwort zu benachrichtigen. Der Inhalt ist HTML-Code, der in das Salmon Magic Envelope eingebettet ist. Bob erstellt dann einen Feed der Unterhaltung und sendet es an alle Friendica-Nutzer, die an der Unterhaltung beteiligt sind und dfrn-notify nutzen (mit Ausnahme von William selbst; die Unterhaltung wird an Jack und Mary weitergeleitet). PuSH-Hubs werden darüber informiert, dass neuer Inhalt verfügbar ist. Der/die Hub/s erhalten dann die neuesten Feeds und übertragen sie an alle Hub-Teilnehmer (die auch zu verschiedenen Netzwerken gehören können). +William nutzt salmon, um Bob über seine Antwort zu benachrichtigen. +Der Inhalt ist HTML-Code, der in das Salmon Magic Envelope eingebettet ist. +Bob erstellt dann einen Feed der Unterhaltung und sendet es an alle Friendica-Nutzer, die an der Unterhaltung beteiligt sind und dfrn-notify nutzen (mit Ausnahme von William selbst; die Unterhaltung wird an Jack und Mary weitergeleitet). +PuSH-Hubs werden darüber informiert, dass neuer Inhalt verfügbar ist. Der/die Hub/s erhalten dann die neuesten Feeds und übertragen sie an alle Hub-Teilnehmer (die auch zu verschiedenen Netzwerken gehören können). Szenario #5. Bob schreibt eine private Nachricht an Mary und Jack. -Die Nachricht wird sofort an Mary und Jack mit Hilfe von dfrn_notify geschickt. Öffentliche Hubs werden nicht benachrichtigt. Im Falle eines Timeouts wird eine erneute Verarbeitung angestoßen. Antworten folgen dem gleichen Nachrichtenfluss wie öffentliche Antworten, allerdings werden die Hubs nicht darüber informiert, wodurch die Nachrichten niemals in öffentliche Feeds gelangen. Die komplette Unterhaltung ist nur für Mary und Jack in ihren durch dfrn-poll personalisierten Feeds verfügbar (und für niemanden sonst). +Die Nachricht wird sofort an Mary und Jack mit Hilfe von dfrn_notify geschickt. +Öffentliche Hubs werden nicht benachrichtigt. +Im Falle eines Timeouts wird eine erneute Verarbeitung angestoßen. +Antworten folgen dem gleichen Nachrichtenfluss wie öffentliche Antworten, allerdings werden die Hubs nicht darüber informiert, wodurch die Nachrichten niemals in öffentliche Feeds gelangen. +Die komplette Unterhaltung ist nur für Mary und Jack in ihren durch dfrn-poll personalisierten Feeds verfügbar (und für niemanden sonst). diff --git a/doc/de/Move-Account.md b/doc/de/Move-Account.md index 8861fe7ef..14de05438 100644 --- a/doc/de/Move-Account.md +++ b/doc/de/Move-Account.md @@ -10,32 +10,23 @@ Accounts Umziehen Unter "Einstellungen" -> "[Persönliche Daten exportieren](uexport)" aufrufen. "Account exportieren" anklicken und die Daten speichern. -Diese Datei enthält Details über dich, deine Kontakte, Gruppen und persönliche -Einstellungen. Außerdem enthält sie deinen geheimen Schlüssel mit dem du dich -deinen Kontakten gegenüber ausweist. +Diese Datei enthält Details über dich, deine Kontakte, Gruppen und persönliche Einstellungen. +Außerdem enthält sie deinen geheimen Schlüssel mit dem du dich deinen Kontakten gegenüber ausweist. **Speichere diese Datei an einem sicheren Ort**! -Rufe nun dem neuen Server die Seite *http://newserver.com/uimport* auf -(es gibt derzeit keinen direkten Link auf diese Seite). +Rufe nun dem neuen Server die Seite *http://newserver.com/uimport* auf (es gibt derzeit keinen direkten Link auf diese Seite). Lege auf dem neuen Server auf keinen Fall einen gleichnamigen Account an! uimport muss anstelle des Registrierens verwendet werden. Wähle die gesicherte Account Datei aus und klicke "Importieren". -Friendica wird nun deinen Account auf dem neuen Server wiederherstellen, mit -all deinen Friendica Kontakten und Gruppen. An deine Friendica Kontakte wird -außerdem eine Nachricht gesendet um sie über deine neue Adresse zu informieren. -Wenn deine Kontakte ihren Account auf einem aktuellen Server haben werden deine -Kontaktdetails automatisch aktualisiert. +Friendica wird nun deinen Account auf dem neuen Server wiederherstellen, mit all deinen Friendica Kontakten und Gruppen. +An deine Friendica Kontakte wird außerdem eine Nachricht gesendet um sie über deine neue Adresse zu informieren. +Wenn deine Kontakte ihren Account auf einem aktuellen Server haben werden deine Kontaktdetails automatisch aktualisiert. -Kontakte auf StatusNet/idendi.ca oder Diaspora werden archiviert, da wir ihnen -keine Information über deinen Umzug zukommen lassen können. -Du solltest sie persönlich anschreiben deinen Eintrag aus ihren Kontaktlisten -zu entfernen und dich neu hinzuzufügen, anschließend solltest du da gleiche mit -ihren Accounts tun.. - -Nach dem Umzug wird dein Account auf dem alten Server nicht mehr zuverlässig -funktionieren und sollte deshalb gelöscht werden. +Kontakte auf GNU Social oder Diaspora werden archiviert, da wir ihnen keine Information über deinen Umzug zukommen lassen können. +Du solltest sie persönlich anschreiben deinen Eintrag aus ihren Kontaktlisten zu entfernen und dich neu hinzuzufügen, anschließend solltest du da gleiche mit ihren Accounts tun. +Nach dem Umzug wird dein Account auf dem alten Server nicht mehr zuverlässig funktionieren und sollte deshalb gelöscht werden. diff --git a/doc/de/Plugins.md b/doc/de/Plugins.md index 9fa67bd7b..dcff41a4b 100644 --- a/doc/de/Plugins.md +++ b/doc/de/Plugins.md @@ -3,9 +3,16 @@ * [Zur Startseite der Hilfe](help) -Bitte schau dir das Beispiel-Addon "randplace" für ein funktionierendes Beispiel für manche der hier aufgeführten Funktionen an. Das Facebook-Addon bietet ein Beispiel dafür, die "addon"- und "module"-Funktion gemeinsam zu integrieren. Addons arbeiten, indem sie Event Hooks abfangen. Module arbeiten, indem bestimmte Seitenanfragen (durch den URL-Pfad) abgefangen werden +Bitte schau dir das Beispiel-Addon "randplace" für ein funktionierendes Beispiel für manche der hier aufgeführten Funktionen an. +Das Facebook-Addon bietet ein Beispiel dafür, die "addon"- und "module"-Funktion gemeinsam zu integrieren. +Addons arbeiten, indem sie Event Hooks abfangen. Module arbeiten, indem bestimmte Seitenanfragen (durch den URL-Pfad) abgefangen werden -Plugin-Namen können keine Leerstellen oder andere Interpunktionen enthalten und werden als Datei- und Funktionsnamen genutzt. Du kannst einen lesbaren Namen im Kommentarblock eintragen. Jedes Addon muss beides beinhalten - eine Installations- und eine Deinstallationsfunktion, die auf dem Addon-/Plugin-Namen basieren; z.B. "plugin1name_install()". Diese beiden Funktionen haben keine Argumente und sind dafür verantwortlich, Event Hooks zu registrieren und abzumelden (unregistering), die dein Plugin benötigt. Die Installations- und Deinstallationsfunktionfunktionen werden auch ausgeführt (z.B. neu installiert), wenn sich das Plugin nach der Installation ändert - somit sollte deine Deinstallationsfunktion keine Daten zerstört und deine Installationsfunktion sollte bestehende Daten berücksichtigen. Zukünftige Extensions werden möglicherweise "Setup" und "Entfernen" anbieten. +Plugin-Namen können keine Leerstellen oder andere Interpunktionen enthalten und werden als Datei- und Funktionsnamen genutzt. +Du kannst einen lesbaren Namen im Kommentarblock eintragen. +Jedes Addon muss beides beinhalten - eine Installations- und eine Deinstallationsfunktion, die auf dem Addon-/Plugin-Namen basieren; z.B. "plugin1name_install()". +Diese beiden Funktionen haben keine Argumente und sind dafür verantwortlich, Event Hooks zu registrieren und abzumelden (unregistering), die dein Plugin benötigt. +Die Installations- und Deinstallationsfunktionfunktionen werden auch ausgeführt (z.B. neu installiert), wenn sich das Plugin nach der Installation ändert - somit sollte deine Deinstallationsfunktion keine Daten zerstört und deine Installationsfunktion sollte bestehende Daten berücksichtigen. +Zukünftige Extensions werden möglicherweise "Setup" und "Entfernen" anbieten. Plugins sollten einen Kommentarblock mit den folgenden vier Parametern enthalten: @@ -22,7 +29,8 @@ Registriere deine Plugin-Hooks während der Installation. $hookname ist ein String und entspricht einem bekannten Friendica-Hook. -$file steht für den Pfadnamen, der relativ zum Top-Level-Friendicaverzeichnis liegt. Das *sollte* "addon/plugin_name/plugin_name.php' sein. +$file steht für den Pfadnamen, der relativ zum Top-Level-Friendicaverzeichnis liegt. +Das *sollte* "addon/plugin_name/plugin_name.php' sein. $function ist ein String und der Name der Funktion, die ausgeführt wird, wenn der Hook aufgerufen wird. @@ -34,21 +42,29 @@ Deine Hook-Callback-Funktion wird mit mindestens einem und bis zu zwei Argumente Wenn du Änderungen an den aufgerufenen Daten vornehmen willst, musst du diese als Referenzvariable (mit "&") während der Funktionsdeklaration deklarieren. -$a ist die Friendica "App"-Klasse, die eine Menge an Informationen über den aktuellen Friendica-Status beinhaltet, u.a. welche Module genutzt werden, Konfigurationsinformationen, Inhalte der Seite zum Zeitpunkt des Hook-Aufrufs. Es ist empfohlen, diese Funktion "$a" zu nennen, um seine Nutzung an den Gebrauch an anderer Stelle anzugleichen. +$a ist die Friendica "App"-Klasse, die eine Menge an Informationen über den aktuellen Friendica-Status beinhaltet, u.a. welche Module genutzt werden, Konfigurationsinformationen, Inhalte der Seite zum Zeitpunkt des Hook-Aufrufs. +Es ist empfohlen, diese Funktion "$a" zu nennen, um seine Nutzung an den Gebrauch an anderer Stelle anzugleichen. -$b kann frei benannt werden. Diese Information ist speziell auf den Hook bezogen, der aktuell bearbeitet wird, und beinhaltet normalerweise Daten, die du sofort nutzen, anzeigen oder bearbeiten kannst. Achte darauf, diese mit "&" zu deklarieren, wenn du sie bearbeiten willst. +$b kann frei benannt werden. +Diese Information ist speziell auf den Hook bezogen, der aktuell bearbeitet wird, und beinhaltet normalerweise Daten, die du sofort nutzen, anzeigen oder bearbeiten kannst. +Achte darauf, diese mit "&" zu deklarieren, wenn du sie bearbeiten willst. **Module** -Plugins/Addons können auch als "Module" agieren und alle Seitenanfragen für eine bestimte URL abfangen. Um ein Plugin als Modul zu nutzen, ist es nötig, die Funktion "plugin_name_module()" zu definieren, die keine Argumente benötigt und nichts weiter machen muss. +Plugins/Addons können auch als "Module" agieren und alle Seitenanfragen für eine bestimte URL abfangen. +Um ein Plugin als Modul zu nutzen, ist es nötig, die Funktion "plugin_name_module()" zu definieren, die keine Argumente benötigt und nichts weiter machen muss. -Wenn diese Funktion existiert, wirst du nun alle Seitenanfragen für "http://my.web.site/plugin_name" erhalten - mit allen URL-Komponenten als zusätzliche Argumente. Diese werden in ein Array $a->argv geparst und stimmen mit $a->argc überein, wobei sie die Anzahl der URL-Komponenten abbilden. So würde http://my.web.site/plugin/arg1/arg2 nach einem Modul "plugin" suchen und seiner Modulfunktion die $a-App-Strukur übergeben (dies ist für viele Komponenten verfügbar). Das umfasst: +Wenn diese Funktion existiert, wirst du nun alle Seitenanfragen für "http://my.web.site/plugin_name" erhalten - mit allen URL-Komponenten als zusätzliche Argumente. +Diese werden in ein Array $a->argv geparst und stimmen mit $a->argc überein, wobei sie die Anzahl der URL-Komponenten abbilden. +So würde http://my.web.site/plugin/arg1/arg2 nach einem Modul "plugin" suchen und seiner Modulfunktion die $a-App-Strukur übergeben (dies ist für viele Komponenten verfügbar). Das umfasst: $a->argc = 3 $a->argv = array(0 => 'plugin', 1 => 'arg1', 2 => 'arg2'); -Deine Modulfunktionen umfassen oft die Funktion plugin_name_content(&$a), welche den Seiteninhalt definiert und zurückgibt. Sie können auch plugin_name_post(&$a) umfassen, welches vor der content-Funktion aufgerufen wird und normalerweise die Resultate der POST-Formulare handhabt. Du kannst ebenso plugin_name_init(&$a) nutzen, was oft frühzeitig aufgerufen wird und das Modul initialisert. +Deine Modulfunktionen umfassen oft die Funktion plugin_name_content(&$a), welche den Seiteninhalt definiert und zurückgibt. +Sie können auch plugin_name_post(&$a) umfassen, welches vor der content-Funktion aufgerufen wird und normalerweise die Resultate der POST-Formulare handhabt. +Du kannst ebenso plugin_name_init(&$a) nutzen, was oft frühzeitig aufgerufen wird und das Modul initialisert. **Derzeitige Hooks:** @@ -160,6 +176,9 @@ Deine Modulfunktionen umfassen oft die Funktion plugin_name_content(&$a), welche 'email' => Email-Adresse, um nach dem Avatar zu suchen 'url' => generierte URL (String) des Avatars +**'nav_info'** + - wird aufgerufen nachdem in include/nav,php der Inhalt des Navigations Menüs erzeugt wurde. + - $b ist ein Array, das $nav wiederspiegelt. Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 14-Feb-2012 generiert): Bitte schau in die Quellcodes für Details zu Hooks, die oben nicht dokumentiert sind. diff --git a/doc/de/Profiles.md b/doc/de/Profiles.md index 0da6476d8..311f1c792 100644 --- a/doc/de/Profiles.md +++ b/doc/de/Profiles.md @@ -3,9 +3,12 @@ Profile * [Zur Startseite der Hilfe](help) -Mit Friendica kann eine unbegrenzte Anzahl an Profilen angelegt werden. Du kannst verschiedene Profile nutzen, um verschiedenen Gruppen verschiedene Seiten von dir zu zeigen. +Mit Friendica kann eine unbegrenzte Anzahl an Profilen angelegt werden. +Du kannst verschiedene Profile nutzen, um verschiedenen Gruppen verschiedene Seiten von dir zu zeigen. -Du hast immer ein Profil, das als dein "Standard"- (default) oder "öffentliches" (public) Profil angelegt ist. Dieses Profil ist immer für die Öffentlichkeit zugänglich und kann nicht versteckt werden (hier mag es einige wenige Ausnahmen auf privaten oder getrennten Seiten geben). Du kannst und solltest die Informationen, die du in deinem öffentlichen Profil veröffentlichst, begrenzen. +Du hast immer ein Profil, das als dein "Standard"- (default) oder "öffentliches" (public) Profil angelegt ist. +Dieses Profil ist immer für die Öffentlichkeit zugänglich und kann nicht versteckt werden (hier mag es einige wenige Ausnahmen auf privaten oder getrennten Seiten geben). +Du kannst und solltest die Informationen, die du in deinem öffentlichen Profil veröffentlichst, begrenzen. Das bedeutet, dass du folgende Informationen in dein öffentlichen Profil eintragen solltest, wenn du willst, dass Freunde dich finden können ... @@ -13,35 +16,64 @@ Das bedeutet, dass du folgende Informationen in dein öffentlichen Profil eintra * Ein Foto von **dir** * Dein geographischer Standort; zumindest das Land, in dem du lebst. -Ohne diese Basisinformationen kannst du hier sehr einsam sein. Die meisten Leute, auch deine besten Freunde, werden nicht versuchen, einen Account mit Spitznamen und ohne Foto zu verbinden. +Ohne diese Basisinformationen kannst du hier sehr einsam sein. +Die meisten Leute, auch deine besten Freunde, werden nicht versuchen, einen Account mit Spitznamen und ohne Foto zu verbinden. -Wenn du außerdem Leute mit gleichen Interessen treffen willst, dann nimm dir etwas Zeit und trage einige Stichworte ein. Zum Beispiel etwas wie "Musik, Linux, Photographie" oder andere Dinge. Du kannst so viele Stichworte eintragen, wie du willst. +Wenn du außerdem Leute mit gleichen Interessen treffen willst, dann nimm dir etwas Zeit und trage einige Stichworte ein. +Zum Beispiel etwas wie "Musik, Linux, Photographie" oder andere Dinge. +Du kannst so viele Stichworte eintragen, wie du willst. -Dein "Standard-" oder "öffentliches" Profil wird außerdem Kontakten in anderen Netzwerken gezeigt, auch wenn sie nicht die Möglichkeit haben, die privaten Profile einzusehen. Nur Mitglieder des Friendica-Netzwerks können alternative oder private Profile sehen. +Dein "Standard-" oder "öffentliches" Profil wird außerdem Kontakten in anderen Netzwerken gezeigt, auch wenn sie nicht die Möglichkeit haben, die privaten Profile einzusehen. +Nur Mitglieder des Friendica-Netzwerks können alternative oder private Profile sehen. -Um ein alternatives Profil zu erstellen, gehe auf "Profil verwalten/editieren". Du kannst entweder ein bestehendes Profil bearbeiten, das Foto ändern, oder ein neues Profil erstellen. Du kannst ebenfalls einen Klon eines bestehenden Profils erstellen, falls du nur einige wenige Einstellungen ändern, aber nicht alle Daten noch mal eingeben willst. +Um ein alternatives Profil zu erstellen, gehe auf "Profil verwalten/editieren". +Du kannst entweder ein bestehendes Profil bearbeiten, das Foto ändern, oder ein neues Profil erstellen. +Du kannst ebenfalls einen Klon eines bestehenden Profils erstellen, falls du nur einige wenige Einstellungen ändern, aber nicht alle Daten noch mal eingeben willst. -Um bestimmten Personen ein Profil zuzuweisen, wähle die Person über "Kontakte" und klicke auf das Bearbeiten-Symbol (Stift). Du wirst ein Auswahlmenü mit verschiedenen vorhandenen Profilen angezeigt bekommen. Wenn diese Auswahl nicht angezeigt wird, dann ist die Person in einem nicht unterstützten Netzwerk und kann dadurch auch kein Profil zugewiesen bekommen. +Um bestimmten Personen ein Profil zuzuweisen, wähle die Person über "Kontakte" und klicke auf das Bearbeiten-Symbol (Stift). +Du wirst ein Auswahlmenü mit verschiedenen vorhandenen Profilen angezeigt bekommen. +Wenn diese Auswahl nicht angezeigt wird, dann ist die Person in einem nicht unterstützten Netzwerk und kann dadurch auch kein Profil zugewiesen bekommen. -Wenn eine befreundete Person auf den "magischen Profillink" klickt, sieht sie das private Profil, das du dieser Person zugewiesen hast. Wenn sie nicht eingeloggt ist oder das Profil von woanders angeschaut wird, wird nur das öffentliche Profil angezeigt. +Wenn eine befreundete Person auf den "magischen Profillink" klickt, sieht sie das private Profil, das du dieser Person zugewiesen hast. +Wenn sie nicht eingeloggt ist oder das Profil von woanders angeschaut wird, wird nur das öffentliche Profil angezeigt. -Ein "magischer Profillink" erscheint, wenn man mit der Maus über den Kontaktnamen oder das Foto geht. Der Cursor wird zur Hand und auf dem Bild erscheint ein Pfeil, der nach unten zeigt. Dieser "magische Cursor" zeigt an, dass du ein spezielles Profil angezeigt bekommst, das nur für Freunde, aber nicht für die Öffentlichkeit sichtbar ist. +Ein "magischer Profillink" erscheint, wenn man mit der Maus über den Kontaktnamen oder das Foto geht. +Der Cursor wird zur Hand und auf dem Bild erscheint ein Pfeil, der nach unten zeigt. +Dieser "magische Cursor" zeigt an, dass du ein spezielles Profil angezeigt bekommst, das nur für Freunde, aber nicht für die Öffentlichkeit sichtbar ist. -Du wirst außerdem möglicherweise entdecken (vorausgesetzt, du hast die nötigen Zugriffsrechte), dass du direkt auf die Seite einer anderen Person schreiben kannst (oft wird diese Beitragsart "wall-to-wall" genannt). Ebenso kannst du die Möglichkeit haben, direkt Beiträge zu kommentieren, während du die Seite der anderen Person besuchst. +Du wirst außerdem möglicherweise entdecken (vorausgesetzt, du hast die nötigen Zugriffsrechte), dass du direkt auf die Seite einer anderen Person schreiben kannst (oft wird diese Beitragsart "wall-to-wall" genannt). +Ebenso kannst du die Möglichkeit haben, direkt Beiträge zu kommentieren, während du die Seite der anderen Person besuchst. -Es gibt zwei Einstellungen, welche erlauben, dein Profil ins Verzeichnis einzutragen, so dass du von anderen Personen gefunden werden kannst. Du kannst diese Einstellungen auf deiner "Einstellungen"-Seite ändern. Die eine Einstellung erlaubt dir, dein Profil im Verzeichnis dieses Servers zu veröffentlichen. Die zweite Option erlaubt es dir, dich in das globale Friendica-Verzeichnis einzutragen. Dies ist ein riesiges Verzeichnis, dass alle Personen von vielen Friendica-Installationen weltweit umfasst. +Es gibt zwei Einstellungen, welche erlauben, dein Profil ins Verzeichnis einzutragen, so dass du von anderen Personen gefunden werden kannst. +Du kannst diese Einstellungen auf deiner "Einstellungen"-Seite ändern. +Die eine Einstellung erlaubt dir, dein Profil im Verzeichnis dieses Servers zu veröffentlichen. +Die zweite Option erlaubt es dir, dich in das globale Friendica-Verzeichnis einzutragen. +Dies ist ein riesiges Verzeichnis, dass alle Personen von vielen Friendica-Installationen weltweit umfasst. Wenn du für andere nicht sichtbar sein willst, dann kannst du dein Profil einfach unveröffentlicht lassen. -Außerdem hast du möglicherweise mehrere Profile, aber nur ein Profilfoto. Dies ist beabsichtigt. In frühen Tests haben wir mit verschiedenen Fotos für jedes Profil experimentiert und herausgefunden, dass es sehr verwirrend für die Nutzer ist. Sie sehen möglicherweise je nach Profil, Seite oder Unterhaltung verschiedene Fotos und merken, dass es unterschiedliche Profile gibt, die sie nicht einsehen können. +Außerdem hast du möglicherweise mehrere Profile, aber nur ein Profilfoto. Dies ist beabsichtigt. +In frühen Tests haben wir mit verschiedenen Fotos für jedes Profil experimentiert und herausgefunden, dass es sehr verwirrend für die Nutzer ist. +Sie sehen möglicherweise je nach Profil, Seite oder Unterhaltung verschiedene Fotos und merken, dass es unterschiedliche Profile gibt, die sie nicht einsehen können. (Du kannst aber die Rich-Text-Infoboxen in deinem Profil nutzen und dort weitere Bilder in das Feld "Erzähle uns ein bisschen von dir …" einfügen.) **Schlüsselwörter und Verzeichnissuche** -Auf der Verzeichnisseite willst du vielleicht nach Personen deines Servers suchen, die ihre Profile veröffentlicht haben. Die Suche richtet sich normalerweise nach deinem Spitznamen oder Teilen deines richtigen Namens. Darüber hinaus wird dieses Feld auch andere Felder deines Profils wie Geschlecht, Ort, "über mich", Arbeit und Bildung finden. Du kannst zudem auch "Schlüsselwörter" in dein Standardprofil eintragen, so dass dich andere Personen über deine Interessen finden können. Du hast zwei Schlüsselwortarten zur Auswahl - öffentlich und privat. Private Schlüsselwörter werden *nicht* jedem angezeigt. Du kannst diese Schlüsselwörter nutzen, um andere Personen zu finden, die ebenfalls in einer bestimmten Gruppe sind oder z.B. das Fischen mögen, ohne dass es jeder in einem öffentlichen Profil sieht. Öffentliche Schlüsselwörter werden auf der "Kontaktvorschläge"-Seite genutzt. Auch wenn die Schlüsselwörter hier nicht direkt angezeigt werden, kann es trotzdem sein, dass diese im HTML-Code der Seite gesehen werden könnten. +Auf der Verzeichnisseite willst du vielleicht nach Personen deines Servers suchen, die ihre Profile veröffentlicht haben. +Die Suche richtet sich normalerweise nach deinem Spitznamen oder Teilen deines richtigen Namens. +Darüber hinaus wird dieses Feld auch andere Felder deines Profils wie Geschlecht, Ort, "über mich", Arbeit und Bildung finden. +Du kannst zudem auch "Schlüsselwörter" in dein Standardprofil eintragen, so dass dich andere Personen über deine Interessen finden können. +Du hast zwei Schlüsselwortarten zur Auswahl - öffentlich und privat. Private Schlüsselwörter werden *nicht* jedem angezeigt. +Du kannst diese Schlüsselwörter nutzen, um andere Personen zu finden, die ebenfalls in einer bestimmten Gruppe sind oder z.B. das Fischen mögen, ohne dass es jeder in einem öffentlichen Profil sieht. +Öffentliche Schlüsselwörter werden auf der "Kontaktvorschläge"-Seite genutzt. +Auch wenn die Schlüsselwörter hier nicht direkt angezeigt werden, kann es trotzdem sein, dass diese im HTML-Code der Seite gesehen werden könnten. -In der Verzeichnis-Suche kannst du ebenfalls die "booleasche"-Logik zu nutzen. Mit "+lesbisch +Florida" kannst du Leute finden, deren sexuelle Einstellung (oder andere Schlüsselwörter) das Wort "lesbisch" enthält und die in Florida leben. Schau dir den Bereich über "Thematische Tags" auf der "[Tags und Erwähnungen-Seite](help/Tags-and-Mentions) für weitere Informationen, um booleansche Suchen durchzuführen. +In der Verzeichnis-Suche kannst du ebenfalls die "booleasche"-Logik zu nutzen. Mit "+lesbisch +Florida" kannst du Leute finden, deren sexuelle Einstellung (oder andere Schlüsselwörter) das Wort "lesbisch" enthält und die in Florida leben. +Schau dir den Bereich über "Thematische Tags" auf der "[Tags und Erwähnungen-Seite](help/Tags-and-Mentions) für weitere Informationen, um booleansche Suchen durchzuführen. -Auf deiner Kontaktseite ist der Link "Ähnliche Interessen", um damit andere Leute zu finden (falls dein Seitenadministrator das globale Verzeichnis nicht ausgeschaltet hat). Hierfür werden die Schlüsselwörter aus deinen öffentlichen und privaten Profilen genutzt, um Personen im globalen Verzeichnis zu finden, die gleiche oder ähnliche Schlüsselwörter haben (deine privaten Schlüsselwörter werden nicht in das globale Verzeichnis übertragen oder gespeichert). Je mehr Schlüsselwörter du einträgst, umso genauer ist die Suche. Das Suchergebnis ist nach Relevanz sortiert. Gegebenenfalls stehst du ganz oben auf der Liste - schließlich bist du die Person, die am besten zu deinen Schlüsselwörtern passt. +Auf deiner Kontaktseite ist der Link "Ähnliche Interessen", um damit andere Leute zu finden (falls dein Seitenadministrator das globale Verzeichnis nicht ausgeschaltet hat). +Hierfür werden die Schlüsselwörter aus deinen öffentlichen und privaten Profilen genutzt, um Personen im globalen Verzeichnis zu finden, die gleiche oder ähnliche Schlüsselwörter haben (deine privaten Schlüsselwörter werden nicht in das globale Verzeichnis übertragen oder gespeichert). +Je mehr Schlüsselwörter du einträgst, umso genauer ist die Suche. Das Suchergebnis ist nach Relevanz sortiert. +Gegebenenfalls stehst du ganz oben auf der Liste - schließlich bist du die Person, die am besten zu deinen Schlüsselwörtern passt. diff --git a/doc/de/Quick-Start-andfinally.md b/doc/de/Quick-Start-andfinally.md index b6e492ae6..2e91b08d0 100644 --- a/doc/de/Quick-Start-andfinally.md +++ b/doc/de/Quick-Start-andfinally.md @@ -3,18 +3,13 @@ Und damit sind wir auch schon am Ende der Schnellstartanleitung. -Hier sind noch einige weitere Dinge, die dir den Start vereinfachen können. +Hier sind noch einige weitere Dinge, die Dir den Start vereinfachen können. **Gruppen** - Friendica Support - Probleme? Dann ist das der Platz, um zu fragen! -- Let's Talk eine Gruppe, um Leute und Gruppen mit gleichen Interessen zu finden - -- Local Friendica eine Seite für lokale Friendica-Gruppen - - **Dokumentation** - Zu weiteren Netzwerken verbinden diff --git a/doc/de/Quick-Start-groupsandpages.md b/doc/de/Quick-Start-groupsandpages.md index f0547667f..3170bee05 100644 --- a/doc/de/Quick-Start-groupsandpages.md +++ b/doc/de/Quick-Start-groupsandpages.md @@ -3,13 +3,23 @@ Gruppen und Seiten * [Zur Startseite der Hilfe](help) -Hier siehst du das globale Verzeichnis. Wenn du dich mal verirrt hast, kannst du diesen Link klicken und wieder hierher kommen. +Hier siehst Du das globale Verzeichnis. +Wenn Du Dich mal verirrt hast, kannst Du diesen Link klicken und wieder hierher kommen. -Auf dieser Seite findest du eine Zusammenstellung von Gruppen, Foren und bekannten Seiten. Gruppen sind keine realen Personen. Sich mit diesen zu verbinden ist, als wenn man jemanden auf Facebook "liked" ("gefällt mir") oder wenn man sich in einem Forum anmeldet. Habe keine Sorge, falls du dich unbehaglich fühlst, wenn du dich einer neuen Person vorstellen sollst, da es sich nicht um Personen handelt. +Auf dieser Seite findest Du eine Zusammenstellung von Gruppen, Foren und Promi-Seiten. +Gruppen sind keine realen Personen. +Sich mit diesen zu verbinden ist, als wenn man jemanden auf Facebook "liked" ("gefällt mir") oder wenn man sich in einem Forum anmeldet. +Du musst nicht unsicher sein, ob Du jemandem zu nahe trittst, wenn Du Dich so ohne weiteres mit einer Gruppe verbindest; es handelt sich eben nicht um reale Personen. -Wenn du dich mit einer Gruppe verbindest, erscheinen alle Nachrichten der Gruppe in deinem "Netzwerk"-Tab. Du kannst diese Beiträge kommentieren oder selbst in der Gruppe schreiben, ohne eine der Gruppenmitglieder persönlich hinzuzufügen. Das ist ein großartiger Weg, dynamisch neue Freunde zu gewinnen. Du findest Personen, die du magst, anstatt Fremde hinzuzufügen. Suche dir einfach eine Gruppe und füge sie so hinzu, wie du auch normale Freunde hinzufügst. Es gibt eine Menge Gruppen und möglicherweise findest du nicht wieder zu dieser Seite zurück. In diesem Fall nutze einfach den Link oben auf dieser Seite. +Wenn Du Dich mit einer Gruppe verbindest, erscheinen alle Nachrichten der Gruppe in Deinem "Netzwerk"-Tab. +Du kannst diese Beiträge kommentieren oder selbst in der Gruppe schreiben, ohne eines der Gruppenmitglieder persönlich hinzuzufügen. +Das ist ein großartiger Weg, dynamisch neue Freunde zu gewinnen. +Du findest Personen Deines Interesses, anstatt Fremde hinzuzufügen. +Suche Dir einfach eine Gruppe und füge sie so hinzu, wie Du auch normale Freunde hinzufügst. +Es gibt eine Menge Gruppen. +Solltest Du beim Stöbern durch die vielen Gruppen nicht wieder hierher zurück finden, so nutze einfach den Link oben auf dieser Seite. -Wenn du einige Gruppen hinzugefügt hast, gehe weiter zum nächsten Schritt. +Wenn Du einige Gruppen hinzugefügt hast, gehe weiter zum nächsten Schritt. diff --git a/doc/de/Quick-Start-guide.md b/doc/de/Quick-Start-guide.md index eb0fd0539..6260bf97f 100644 --- a/doc/de/Quick-Start-guide.md +++ b/doc/de/Quick-Start-guide.md @@ -3,15 +3,34 @@ Erste Schritte... * [Zur Startseite der Hilfe](help) -Das Erste zum Anfang: geh sicher, dass du schon eingeloggt bist. Wenn du noch nicht eingeloggt bist, kannst du das in dem Fenster unten machen. +Als Erstes: Gehe sicher, dass Du eingeloggt bist. +Wenn Du noch nicht eingeloggt bist, kannst Du das in dem Fenster unten machen. -Sobald du eingeloggt bist (oder wenn du bereits eingeloggt bist), kannst du unten nun auf deine Profilseite schauen. +Sobald dies geschehen ist, schaust Du auf die Netzwerkseite Deines Profils. +Klicke auf den Reiter "Pinnwand". -Hier sieht es ein wenig wie auf deiner Facebook-Seite aus. Hier findest du alle deine Statusmeldungen und Nachrichten deiner Freunde, die direkt auf deine Seite ("Wall") geschrieben haben. Um deinen Status einzutragen, klicke einfach auf die Box oben, in der "Teilen" steht. Wenn du das machst, vergrößert sich die Box. Nun kannst du einige Formatierungsoptionen wie Fett, kursiv, unterstrichen auswählen und ebenfalls Bilder und Links hinzufügen. Unten findest du in diesem Feld weitere Links, mit denen du Bilder und Dateien von deinem Computer hochladen, Webseiten mit einem Kurztext teilen und Video- und Audiodateien aus dem Internet einfügen kannst. Außerdem kannst du hier eintragen, wo du gerade bist. +Hier sieht es ein wenig wie auf (D)einer Facebook-Seite aus. +Du findest hier alle Deine Statusmeldungen und Nachrichten Deiner Freunde, die direkt auf Deine "Pinnwand" ("Wall") geschrieben haben. +Um Deinen Status einzutragen, klicke einfach auf die Box oben, in der "Teilen" steht. +Wenn Du das machst, vergrößert sich die Box, und Du kannst nun Deinen Text eintragen, mit Hilfe einiger Formatierungsoptionen wie fett, kursiv, unterstrichen formatieren und ebenfalls Bilder und Links hinzufügen. +Unten findest Du in diesem Feld weitere Knöpfe, mit denen Du Bilder und Dateien von Deinem Computer hochladen, Webseiten mit einem Kurztext teilen und Video- und Audiodateien aus dem Internet einfügen kannst. +Außerdem kannst Du hier eintragen, wo Du gerade bist. -Wenn du deinen Beitrag ("Post") geschrieben hast, kannst du auf das "Schloss"-Symbol klicken und festlegen, wer deinen Beitrag sehen kann. Wenn du dieses Symbol nicht anklickst, ist dein Beitrag öffentlich. Das bedeutet, dass jeder, der dein Profil ansieht, der auf dem "Community"-Tab deines Servers oder auf dem "Netzwerk"-Tab ("Beiträge deiner Kontakte") eines befreundeten Kontakts ist, den Beitrag sehen kann. +Wenn Du Deinen Beitrag ("Post") geschrieben hast, kannst Du auf das "Schloss"-Symbol klicken und festlegen, wer Deinen Beitrag sehen kann. +Wenn Du dieses Symbol nicht anklickst, ist Dein Beitrag öffentlich, bzw. es werden deine Grundeinstellungen verwendet, wenn diese nicht öffentlich sind. -Probiere es doch einfach mal aus. Wenn du fertig bist, schauen wir uns den "Netzwerk"-Tab an. +Ein öffentlicher Beitrag ist sichbar für + +Auch wenn Du Deinen Server so konfiguriert hast, dass der Zugriff von außerhalb des Friendica-Netzwerks theoretisch nicht möglich ist, so ist Dein Beitrag über die Profile Deiner Kontakte sichtbar, wenn deren Knoten solche Zugriffe zulassen. + + + +Probiere es doch einfach mal aus. Wenn Du fertig bist, schauen wir uns den "Netzwerk"-Tab an. diff --git a/doc/de/Quick-Start-makingnewfriends.md b/doc/de/Quick-Start-makingnewfriends.md index 24d9ee418..b7381cddf 100644 --- a/doc/de/Quick-Start-makingnewfriends.md +++ b/doc/de/Quick-Start-makingnewfriends.md @@ -3,13 +3,25 @@ Neue Freunde finden * [Zur Startseite der Hilfe](help) -Hier siehst du die Kontaktvorschläge. Wenn du dich mal verirrt hast, kannst du diesen Link klicken und wieder hierher kommen. +Hier siehst Du die Kontaktvorschläge. +Wenn Du Dich mal verirrt hast, kannst Du diesen Link klicken und wieder hierher kommen. -Diese Seite ist ein wenig wie die Kontaktvorschläge in Facebook. Jeder auf dieser Liste hat zugestimmt, als Kontaktvorschlag zu erscheinen. Das bedeutet, das sie Anfragen meist nicht ablehnen, da sie neue Leute kennenlernen wollen. +Diese Seite funktioniert in etwa wie die Seite für Kontaktvorschläge in Facebook. +Jeder auf dieser Liste hat zugestimmt, als Kontaktvorschlag zu erscheinen. +Das bedeutet, das sie Anfragen meist nicht ablehnen, da sie neue Leute kennenlernen wollen. -Siehst du jemanden, dessen Aussehen du magst? Klicke auf den "Verbinden"-Button beim Foto. Als nächstes kommst du zur Seite "Freundschafts-/Kontaktanfrage". Fülle das Formular wie vorgegeben aus und trage optional eine kleine Notiz ein. Nun musst du nur noch auf die Bestätigung warten. Beachte dabei, dass es sich um reale Personen handelt und es somit etwas dauern kann. Jetzt, nachdem du jemanden hinzugefügt hast, weißt du vielleicht nicht mehr, wie du zurückkommst. Klicke einfach auf den Link oben auf dieser Seite und du kommst zurück zur Seite mit den Kontaktvorschlägen, um weitere Personen hinzuzufügen. +Siehst Du jemanden, der Dir interessant erscheint? +Klicke auf den "Verbinden"-Knopf beim Foto. +Als nächstes kommst Du zur Seite "Freundschafts-/Kontaktanfrage". +Fülle das Formular wie vorgegeben aus und trage optional eine kleine Notiz ein. +Nun musst Du nur noch auf die Bestätigung warten. +Beachte dabei, dass es sich um reale Personen handelt und es somit etwas dauern kann. -Du willst nicht einfach Personen hinzufügen, die du nicht kennst? Kein Problem - an dieser Stelle kommen wir zu den Gruppen und Seiten. +Jetzt, nachdem Du jemanden hinzugefügt hast, weißt Du vielleicht nicht mehr, wie Du zurückkommst. +Klicke einfach auf den Link oben auf dieser Seite und Du gelangst zur Seite mit den Kontaktvorschlägen zurück, um weitere Personen hinzuzufügen. + +Du willst nicht einfach Personen hinzufügen, die du nicht kennst? +Kein Problem - an dieser Stelle kommen wir zu den Gruppen und Seiten. diff --git a/doc/de/Quick-Start-network.md b/doc/de/Quick-Start-network.md index 6132cd57f..0c6f82835 100644 --- a/doc/de/Quick-Start-network.md +++ b/doc/de/Quick-Start-network.md @@ -3,11 +3,17 @@ Deine "Netzwerk"-Seite * [Zur Startseite der Hilfe](help) -Das ist dein "Netzwerk"-Tab. Wenn du dich mal verirrt hast, kannst du diesen Link klicken, um wieder hierher zu kommen. +Dies ist Dein "Netzwerk"-Tab. +Wenn Du Dich mal verirrt hast, kannst Du diesen Link klicken, um wieder hierher zu kommen. -Diese Seite ist ein wenig wie die News-Seite in Facebook oder der Stream in Diaspora. Hier findest du alle Beiträge deiner Kontakte, Gruppen und Feeds, die du eingetragen hast. Wenn du neu bist, siehst du hier noch nichts, falls du deinen Status im letzten Schritt noch nicht eingetragen hast. Wenn du bereits ein paar Freunde eingetragen hast, findest du hier ihre Beiträge. Hier kannst du Beiträge kommentieren, eintragen, dass du den Beitrag magst oder ablehnst oder die Profile durch einen Klick auf deren Namen anschauen und auf deren Seite ("Wall") Nachrichten schreiben. +Diese Seite ist ein wenig wie die News-Seite in Facebook oder der Stream in Diaspora. +Hier findest Du alle Beiträge Deiner Kontakte, Gruppen und Feeds, die Du eingetragen hast. +Wenn Du neu bist, siehst Du hier noch nichts, falls Du an Deinem Status im letzten Schritt noch nichts geändert haben solltest. +Wenn Du bereits ein paar Freunde gefunden hast, so findest Du hier ihre Beiträge. +Du kannst ihre Beiträge von hier aus kommentieren, mitteilen, dass Du den Beitrag magst oder ablehnst (Daumen hoch, Daumen runter) oder die Profile durch einen Klick auf deren Namen besuchen und dort auf deren "Pinnwand" ("Wall") Nachrichten schreiben. -Nun wollen wir diese Seite mit Inhalt füllen. Der erste Schritt ist es, Leute zu deinem Account hinzuzufügen. +Nun wollen wir diese Seite mit Inhalt füllen. +Der erste Schritt ist es, Leute zu Deinem Account hinzuzufügen. diff --git a/doc/de/README.md b/doc/de/README.md index 5dc32ee76..a4e2bdec7 100644 --- a/doc/de/README.md +++ b/doc/de/README.md @@ -3,6 +3,7 @@ Friendica-doc-german Friendica - doc - german -Hier findest du die deutsche Version der Friendica-Hilfedateien. Es handelt sich um eine selbst erstellte, öffentlich freigegebene Arbeit mit dem Ziel, Friendica durch deutsche Hilfedateien für weitere Personen zugänglich zu machen, die dem Englischen nicht ausreichend mächtig sind. +Hier findest du die deutsche Version der Friendica-Hilfedateien. +Es handelt sich um eine selbst erstellte, öffentlich freigegebene Arbeit mit dem Ziel, Friendica durch deutsche Hilfedateien für weitere Personen zugänglich zu machen, die dem Englischen nicht ausreichend mächtig sind. Die Daten basieren auf dem offiziellen Friendica-Github https://github.com/friendica/friendica (Stand: 03.11.12) diff --git a/doc/de/Remove-Account.md b/doc/de/Remove-Account.md index 7762ec9d0..9c6e0403b 100644 --- a/doc/de/Remove-Account.md +++ b/doc/de/Remove-Account.md @@ -9,16 +9,25 @@ Wir freuen uns nicht, wenn Leute Friendica verlassen, aber wenn du deinen Accoun in deinem Webbrowser. Du musst dabei eingeloggt sein. -Du wirst nach deinem Passwort gefragt, um die Anfrage zu bestätigen. Wenn dieses mit deinem gespeichertem Passwort übereinstimmt, dann wird dein Account sofort gelöscht. Anders als andere Netzwerke, behalten wir die Daten **nicht** für eine gewisse Zeit, falls du deine Meinung noch änderst. Deine Nutzerdetails, deine Unterhaltungen, deine Photos, deine Freunde - alles; wird sofort gelöscht und du wirst ausgeloggt. +Du wirst nach deinem Passwort gefragt, um die Anfrage zu bestätigen. +Wenn dieses mit deinem gespeichertem Passwort übereinstimmt, dann wird dein Account sofort gelöscht. +Anders als andere Netzwerke, behalten wir die Daten **nicht** für eine gewisse Zeit, falls du deine Meinung noch änderst. +Deine Nutzerdetails, deine Unterhaltungen, deine Photos, deine Freunde - alles; wird sofort gelöscht und du wirst ausgeloggt. -Wenn Beiträge ablaufen, schicken wir Mitteilungen an Friendica, um diese zu löschen. Diaspora hat keine automatische Löschfunktion, so dass diese Funktion in dem Netzwerk deaktiviert ist. Und hoffentlich ist klar, dass das Löschen auch in anderen Netzwerken nicht funktioniert. Wenn du manuell einen Beitrag bzw. eine Reihe von Beiträgen löschst, dann senden wir individuelle Mitteilungen zu Friendica und Diaspora für jeden gelöschten Post. +Wenn Beiträge ablaufen, schicken wir Mitteilungen an Friendica, um diese zu löschen. +Diaspora hat keine automatische Löschfunktion, so dass diese Funktion in dem Netzwerk deaktiviert ist. +Und hoffentlich ist klar, dass das Löschen auch in anderen Netzwerken nicht funktioniert. +Wenn du manuell einen Beitrag bzw. eine Reihe von Beiträgen löschst, dann senden wir individuelle Mitteilungen zu Friendica und Diaspora für jeden gelöschten Post. Diaspora versäumt dieses oft. -Wenn du einen Beitrag löscht, aber jemand diesem Beitrag folgt, wird es trotzdem gelöscht. Dein Wunsch hat Priorität. +Wenn du einen Beitrag löscht, aber jemand diesem Beitrag folgt, wird es trotzdem gelöscht. +Dein Wunsch hat Priorität. Wenn du deinen Account löscht, dann löschen wir alle Beiträge, dein Profil, die Nutzerdaten etc. sofort. -Um einen Gesamtlöschauftrag zu versenden, bräuchten wir zunächst noch deinen Account; auch, um deinen Freunden zu zeigen, wer diese Anfrage stellt. Das können wir nicht tun, wenn du keinen Account mehr hast. +Um einen Gesamtlöschauftrag zu versenden, bräuchten wir zunächst noch deinen Account; auch, um deinen Freunden zu zeigen, wer diese Anfrage stellt. +Das können wir nicht tun, wenn du keinen Account mehr hast. -Deine Freunde können möglicherweise noch deine Beiträge sehen, wenn dein Account gelöscht wurde, aber es gibt keinen öffentlichen Ort in Friendica mehr, wo diese angeschaut werden können. Wenn du Freunde bei Diaspora hast, kann es sein, dass deine Beiträge weiterhin vorhanden und für andere aus diesem Netzwerk sichtbar sind. +Deine Freunde können möglicherweise noch deine Beiträge sehen, wenn dein Account gelöscht wurde, aber es gibt keinen öffentlichen Ort in Friendica mehr, wo diese angeschaut werden können. +Wenn du Freunde bei Diaspora hast, kann es sein, dass deine Beiträge weiterhin vorhanden und für andere aus diesem Netzwerk sichtbar sind. diff --git a/doc/de/SSL.md b/doc/de/SSL.md index ecb10d9d3..dd9b42676 100644 --- a/doc/de/SSL.md +++ b/doc/de/SSL.md @@ -7,13 +7,19 @@ Wenn du deine eigene Friendica-Seite betreibst, willst du vielleicht SSL (https) Wenn du das auf deiner eigenen Domain machen willst, musst du ein Zertifikat von einer anerkannten Organisation beschaffen (sogenannte selbst-signierte Zertifikate, die unter Computerfreaks beliebt sind, arbeiten nicht sehr gut mit Friendica, weil sie Warnungen im Browser hervorrufen können). -Wenn du dieses Dokument liest, bevor du Friendica installierst, kannst du eine sehr einfache Option in Betracht ziehen: suche dir ein geteiltes Hosting-Angebot (shared hosting) ohne eigene Domain. Dadurch wirst du eine Adresse in der Form deinName.deinAnbietername.de erhalten, was nicht so schön wie deinName.de ist. Aber es wird trotzdem deine ganz persönliche Seite sein und du wirst unter Umständen die Möglichkeit haben, das SSL-Zertifikat deines Anbieters mitzubenutzen. Das bedeutet, dass du SSL überhaupt nicht konfigurieren musst - es wird einfach sofort funktionieren, wenn die Besucher deiner Seite https statt http eingeben. +Wenn du dieses Dokument liest, bevor du Friendica installierst, kannst du eine sehr einfache Option in Betracht ziehen: suche dir ein geteiltes Hosting-Angebot (shared hosting) ohne eigene Domain. +Dadurch wirst du eine Adresse in der Form deinName.deinAnbietername.de erhalten, was nicht so schön wie deinName.de ist. +Aber es wird trotzdem deine ganz persönliche Seite sein und du wirst unter Umständen die Möglichkeit haben, das SSL-Zertifikat deines Anbieters mitzubenutzen. +Das bedeutet, dass du SSL überhaupt nicht konfigurieren musst - es wird einfach sofort funktionieren, wenn die Besucher deiner Seite https statt http eingeben. Wenn dir diese Lösung nicht gefällt, lies weiter... **Geteilte Hosting-Angebote/Shared hosts** -Wenn du ein geteiltes Hosting-Angebot mit einer eigenen Domain nutzt, dann wird dir dein Anbieter ggf. anbieten, dir das Zertifikat zu besorgen und zu installieren. Du musst es nur beantragen und bezahlen und alles wird eingerichtet. Wenn das die Lösung für dich ist, musst du das weitere Dokument nicht lesen. Gehe nur sicher, dass das Zertifikat auch für die Domain gilt, die du für Friendica nutzt: z.B. meinfriendica.de oder friendica.meinserver.de. +Wenn du ein geteiltes Hosting-Angebot mit einer eigenen Domain nutzt, dann wird dir dein Anbieter ggf. anbieten, dir das Zertifikat zu besorgen und zu installieren. +Du musst es nur beantragen und bezahlen und alles wird eingerichtet. +Wenn das die Lösung für dich ist, musst du das weitere Dokument nicht lesen. +Gehe nur sicher, dass das Zertifikat auch für die Domain gilt, die du für Friendica nutzt: z.B. meinfriendica.de oder friendica.meinserver.de. Das Vorangehende wird die häufigste Art sein, eine Friendica-Seite zu betreiben, so dass der Rest des Artikels für die meisten Leute nicht von Bedeutung ist. @@ -21,29 +27,50 @@ Das Vorangehende wird die häufigste Art sein, eine Friendica-Seite zu betreiben Alternativ kannst du dir auch selbst ein Zertifikat besorgen und hochladen, falls dein Anbieter das unterstützt. -Der nächste Abschnitt beschreibt den Ablauf, um ein Zertifikat von StartSSL zu erhalten. Das Gute an StartSSL ist, dass du kostenlos ein einfaches, aber perfekt ausreichendes Zertifikat erhältst. Das ist bei vielen anderen Anbietern nicht so, weshalb wir uns in diesem Dokument auf StartSSL konzentrieren werden. Wenn du ein Zertifikat eines anderen Anbieters nutzen willst, musst du die Vorgaben dieser Organisation befolgen. Wir können hier nicht jede Möglichkeit abdecken. +Der nächste Abschnitt beschreibt den Ablauf, um ein Zertifikat von StartSSL zu erhalten. +Das Gute an StartSSL ist, dass du kostenlos ein einfaches, aber perfekt ausreichendes Zertifikat erhältst. +Das ist bei vielen anderen Anbietern nicht so, weshalb wir uns in diesem Dokument auf StartSSL konzentrieren werden. +Wenn du ein Zertifikat eines anderen Anbieters nutzen willst, musst du die Vorgaben dieser Organisation befolgen. +Wir können hier nicht jede Möglichkeit abdecken. -Die Installation deines erhaltenen Zertifikats hängt von den Vorgaben deines Anbieters ab. Aber generell nutzen solche Anbieter ein einfaches Web-Tool, um die Einrichtung zu unterstützen. +Die Installation deines erhaltenen Zertifikats hängt von den Vorgaben deines Anbieters ab. +Aber generell nutzen solche Anbieter ein einfaches Web-Tool, um die Einrichtung zu unterstützen. -Beachte: dein Zertifikat gilt gewöhnlich nur für eine Subdomain. Wenn du dein Zertifikat beantragst, sorge dafür, dass es für die Domain und die Subdomain gilt, die du für Friendica nutzt: z.B. meinfriendica.de oder friendica.meinserver.de. +Beachte: dein Zertifikat gilt gewöhnlich nur für eine Subdomain. +Wenn du dein Zertifikat beantragst, sorge dafür, dass es für die Domain und die Subdomain gilt, die du für Friendica nutzt: z.B. meinfriendica.de oder friendica.meinserver.de. **Erhalte ein kostenloses StartSSL-Zertifikat** -Die Webseite von StartSSL führt dich durch den Erstellungsprozess, aber manche Leute haben hier trotzdem Probleme. Wir empfehlen dir ausdrücklich, die Installationsanleitung Schritt für Schritt langsam und sorgfältig zu befolgen. Lese dir jedes Wort durch und schließe deinen Browser erst, wenn alles läuft. Es heißt, dass es drei Schritte gibt, die den Nutzer verwirren können: +Die Webseite von StartSSL führt dich durch den Erstellungsprozess, aber manche Leute haben hier trotzdem Probleme. +Wir empfehlen dir ausdrücklich, die Installationsanleitung Schritt für Schritt langsam und sorgfältig zu befolgen. +Lese dir jedes Wort durch und schließe deinen Browser erst, wenn alles läuft. +Es heißt, dass es drei Schritte gibt, die den Nutzer verwirren können: -Wenn du dich erstmals bei StartSSL anmeldest, erhältst du ein erstes Zertifikat, dass sich einfach in deinem Browser installiert. Dieses Zertifikat solltest du zur Sicherheit irgendwo speichern, so dass du es für einen neuen Browser neu installieren kannst, wenn du z.B. etwas erneuern musst. Dieses Authentifizierungszertifikat wird nur für das Login benötigt und hat nichts mit dem Zertifikat zu tun, dass du später für deinen Server benötigst. Als Anfänger mit StartSSL kannst du [hier starten](https://www.startssl.com/?lang=de) und die "Express Lane" nutzen, um dein Browser-Zertifikiat zu erhalten. Im nächsten Schritt kannst du die Einrichtung deines Zertifikats fortsetzen. +Wenn du dich erstmals bei StartSSL anmeldest, erhältst du ein erstes Zertifikat, dass sich einfach in deinem Browser installiert. +Dieses Zertifikat solltest du zur Sicherheit irgendwo speichern, so dass du es für einen neuen Browser neu installieren kannst, wenn du z.B. etwas erneuern musst. +Dieses Authentifizierungszertifikat wird nur für das Login benötigt und hat nichts mit dem Zertifikat zu tun, dass du später für deinen Server benötigst. +Als Anfänger mit StartSSL kannst du [hier starten](https://www.startssl.com/?lang=de) und die "Express Lane" nutzen, um dein Browser-Zertifikiat zu erhalten. +Im nächsten Schritt kannst du die Einrichtung deines Zertifikats fortsetzen. -Wenn du zuerst nach einer Domain für dein Zertifikat gefragt wirst, musst du die Top-Level-Domain angeben, nicht die Subdomain, die Friendica nutzt. Im nächsten Schritt kannst du dann die Subdomain spezifizieren. Wenn du also friendica.deinName.de auf deinem Server hast, musst du zuerst deinName.de angeben. +Wenn du zuerst nach einer Domain für dein Zertifikat gefragt wirst, musst du die Top-Level-Domain angeben, nicht die Subdomain, die Friendica nutzt. +Im nächsten Schritt kannst du dann die Subdomain spezifizieren. +Wenn du also friendica.deinName.de auf deinem Server hast, musst du zuerst deinName.de angeben. -Höre nicht zu früh auf, wenn du am Ende der Einrichtung dein persönliches Server-Zertifikat erhalten hast. Abhängig von deiner Server-Software benötigst du ein oder zwei generische Dateien, die du mit deinem kostenlosen StartSSL-Zertifikat nutzen musst. Diese Dateien sind sub.class1.server.ca.pem und ca.pem. Wenn du diesen Schritt bereits übersprungen hast, kannst du die Dateien hier finden: [http://www.startssl.com/?app=21](http://www.startssl.com/?app=21). Aber am besten funktioniert es, wenn du StartSSL nicht beendest, bevor du den Vorgang komplett abgeschlossen hast und dein https-Zertifikat hochgeladen ist und funktioniert. +Höre nicht zu früh auf, wenn du am Ende der Einrichtung dein persönliches Server-Zertifikat erhalten hast. +Abhängig von deiner Server-Software benötigst du ein oder zwei generische Dateien, die du mit deinem kostenlosen StartSSL-Zertifikat nutzen musst. +Diese Dateien sind sub.class1.server.ca.pem und ca.pem. +Wenn du diesen Schritt bereits übersprungen hast, kannst du die Dateien hier finden: [http://www.startssl.com/?app=21](http://www.startssl.com/?app=21). +Aber am besten funktioniert es, wenn du StartSSL nicht beendest, bevor du den Vorgang komplett abgeschlossen hast und dein https-Zertifikat hochgeladen ist und funktioniert. **Virtuelle private und dedizierte Server (mit StartSSL free)** -Der Rest dieses Dokuments ist etwas komplizierter, aber es ist auch nur für Personen, die Friendica auf einem virtuellen oder dedizierten Server nutzen. Jeder andere kann an dieser Stelle mit dem Lesen aufhören. +Der Rest dieses Dokuments ist etwas komplizierter, aber es ist auch nur für Personen, die Friendica auf einem virtuellen oder dedizierten Server nutzen. +Jeder andere kann an dieser Stelle mit dem Lesen aufhören. Folge den weiteren Anleitungen [hier](http://www.startssl.com/?app=20), um den Webserver, den du benutzt (z.B. Apache), für dein Zertifikat einzurichten. -Um die nötigen Schritte zu verdeutlichen, setzen wir nun voraus, dass Apache aktiv ist. Im Wesentlichen kannst du einfach einen zweiten httpd.conf-Eintrag für Friendica erstellen. +Um die nötigen Schritte zu verdeutlichen, setzen wir nun voraus, dass Apache aktiv ist. +Im Wesentlichen kannst du einfach einen zweiten httpd.conf-Eintrag für Friendica erstellen. Um das zu machen, kopiere den existierenden Eintrag und ändere das Ende der ersten Zeile auf "lesen" :443> anstelle von :80> und trage dann die folgenden Zeilen ein, wie du es auch in der Anleitung von StartSSL finden kannst: @@ -59,17 +86,27 @@ Um das zu machen, kopiere den existierenden Eintrag und ändere das Ende der ers CustomLog /usr/local/apache/logs/ssl_request_log \ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" -(Beachte, dass das Verzeichnis /usr/local/apache/conf/ möglicherweise nicht in deinem System existiert. In Debian ist der Pfad bspw. /etc/apache2/, in dem du ein SSL-Unterverzeichnis erstellen kannst, wenn dieses noch nicht vorhanden ist. Dann hast du /etc/apache2/ssl/… statt /usr/local/apache/conf/…) +(Beachte, dass das Verzeichnis /usr/local/apache/conf/ möglicherweise nicht in deinem System existiert. +In Debian ist der Pfad bspw. /etc/apache2/, in dem du ein SSL-Unterverzeichnis erstellen kannst, wenn dieses noch nicht vorhanden ist. +Dann hast du /etc/apache2/ssl/… statt /usr/local/apache/conf/…) Du solltest nun zwei Einträgen für deine Friendica-Seite haben - einen für einfaches http und eines für https. -Ein Hinweis für diejenigen, die SSL steuern wollen: setze keine Weiterleitung deines SSL in deine Apache-Einstellung. Friendicas Admin-Panel hat eine spezielle Einstellung für die SSL-Methode. Bitte nutze diese Einstellungen. +Ein Hinweis für diejenigen, die SSL steuern wollen: setze keine Weiterleitung deines SSL in deine Apache-Einstellung. Friendicas Admin-Panel hat eine spezielle Einstellung für die SSL-Methode. +Bitte nutze diese Einstellungen. **Vermische Zertifikate in Apache – StartSSL und andere (selbst-signierte)** -Viele Leute nutzen einen virtuellen privaten oder einen dedizierten Server, um mehr als Friendica darauf laufen zu lassen. Sie wollen möglicherweise SSL auch für andere Seiten nutzen, die auf dem Server liegen. Um das zu erreichen, wollen sie mehrere Zertifikate für eine IP nutzen, z.B. ein Zertifikat eines anerkannten Anbieters für Friendica und ein selbst-signiertes für eine persönliche Inhalte (möglw. ein Wildcard-Zertifikat für mehrere Subdomains). +Viele Leute nutzen einen virtuellen privaten oder einen dedizierten Server, um mehr als Friendica darauf laufen zu lassen. +Sie wollen möglicherweise SSL auch für andere Seiten nutzen, die auf dem Server liegen. +Um das zu erreichen, wollen sie mehrere Zertifikate für eine IP nutzen, z.B. ein Zertifikat eines anerkannten Anbieters für Friendica und ein selbst-signiertes für eine persönliche Inhalte (möglw. ein Wildcard-Zertifikat für mehrere Subdomains). -Um das zum Laufen zu bringen, bietet Apache eine NameVirtualHost-Direktive. Du findest Informationen zur Nutzung in httpd.conf in den folgenden Ausschnitten. Beachte, dass Wildcards (*) in httpd.conf dazu führen, dass die NameVirtualHost-Methode nicht funktioniert; du kannst diese in dieser neuen Konfiguration nicht nutzen. Das bedeutet, dass *80> oder *443> nicht funktionieren. Und du musst unbedingt die IP definieren, selbst wenn du nur eine hast. Beachte außerdem, dass du bald zwei Zeilen zu Beginn der Datei hinzufügen musst, um NameVirtualHost für IPv6 vorzubereiten. +Um das zum Laufen zu bringen, bietet Apache eine NameVirtualHost-Direktive. +Du findest Informationen zur Nutzung in httpd.conf in den folgenden Ausschnitten. +Beachte, dass Wildcards (*) in httpd.conf dazu führen, dass die NameVirtualHost-Methode nicht funktioniert; du kannst diese in dieser neuen Konfiguration nicht nutzen. +Das bedeutet, dass *80> oder *443> nicht funktionieren. +Und du musst unbedingt die IP definieren, selbst wenn du nur eine hast. +Beachte außerdem, dass du bald zwei Zeilen zu Beginn der Datei hinzufügen musst, um NameVirtualHost für IPv6 vorzubereiten. NameVirtualHost 12.123.456.1:443 NameVirtualHost 12.123.456.1:80 @@ -102,17 +139,25 @@ Um das zum Laufen zu bringen, bietet Apache eine NameVirtualHost-Direktive. Du f -Natürlich kannst du auch andere Verzeichnisse auf deinem Server nutzen, um Apache zu konfigurieren. In diesem Fall müssen nur einige Zeilen in httpd.conf oder ports.conf angepasst werden - vor allem die NameVirtualHost-Zeilen. Aber wenn du sicher im Umgang mit solchen Alternativen bist, wirst du sicherlich die nötigen Anpassungen herausfinden. +Natürlich kannst du auch andere Verzeichnisse auf deinem Server nutzen, um Apache zu konfigurieren. +In diesem Fall müssen nur einige Zeilen in httpd.conf oder ports.conf angepasst werden - vor allem die NameVirtualHost-Zeilen. +Aber wenn du sicher im Umgang mit solchen Alternativen bist, wirst du sicherlich die nötigen Anpassungen herausfinden. Starte dein Apache abschließend neu. **StartSSL auf Nginx** -Führe zunächst ein Update auf den neuesten Friendica-Code durch. Folge dann der Anleitung oben, um dein kostenloses Zertifikat zu erhalten. Aber statt der Apache-Installationsanleitung zu folgen, mache das Folgende: +Führe zunächst ein Update auf den neuesten Friendica-Code durch. +Folge dann der Anleitung oben, um dein kostenloses Zertifikat zu erhalten. +Aber statt der Apache-Installationsanleitung zu folgen, mache das Folgende: -Lade dein Zertifikat hoch. Es ist nicht wichtig, wohin du es lädst, solange Nginx es finden kann. Einige Leute nutzen /home/verschiedeneNummernundBuchstaben, du kannst aber auch z.B. etwas wie /foo/bar nutzen. +Lade dein Zertifikat hoch. +Es ist nicht wichtig, wohin du es lädst, solange Nginx es finden kann. +Einige Leute nutzen /home/verschiedeneNummernundBuchstaben, du kannst aber auch z.B. etwas wie /foo/bar nutzen. -Du kannst das Passwort entfernen, wenn du willst. Es ist zwar möglicherweise nicht die beste Wahl, aber wenn du es nicht machst, wirst du das Passwort immer wieder eingeben müssen, wenn du Ngingx neustartest. Um es zu entfernen, gebe Folgendes ein: +Du kannst das Passwort entfernen, wenn du willst. +Es ist zwar möglicherweise nicht die beste Wahl, aber wenn du es nicht machst, wirst du das Passwort immer wieder eingeben müssen, wenn du Ngingx neustartest. +Um es zu entfernen, gebe Folgendes ein: openssl rsa -in ssl.key-pass -out ssl.key @@ -124,7 +169,8 @@ Nun vereinige die Dateien: cat ssl.crt sub.class1.server.ca.pem > ssl.crt -In manchen Konfigurationen ist ein Bug enthalten, weshalb diese Schritte nicht ordentlich arbeiten. Du musst daher ggf. ssl.crt bearbeiten: +In manchen Konfigurationen ist ein Bug enthalten, weshalb diese Schritte nicht ordentlich arbeiten. +Du musst daher ggf. ssl.crt bearbeiten: nano /foo/bar/ssl.crt @@ -138,7 +184,9 @@ Das ist schlecht. Du brauchst die folgenden Einträge: -----BEGIN CERTIFICATE----- -Du kannst den Zeilenumbruch manuell eingeben, falls dein System vom Bug betroffen ist. Beachte, dass nach -----BEGIN CERTIFICATE----- nur ein Zeilenumbruch ist. Es gibt keine leere Zeile zwischen beiden Einträgen. +Du kannst den Zeilenumbruch manuell eingeben, falls dein System vom Bug betroffen ist. +Beachte, dass nach -----BEGIN CERTIFICATE----- nur ein Zeilenumbruch ist. +Es gibt keine leere Zeile zwischen beiden Einträgen. Nun musst du Nginx über die Zertifikate informieren. @@ -166,4 +214,5 @@ Nun starte Nginx neu: Und das war es schon. -Für multiple Domains ist es mit Nginx einfacher als mit Apache. Du musst du oben genannten Schritte nur für jedes Zertifikat wiederholen und die spezifischen Informationen im eigenen {server...}-Bereich spezifizieren. \ No newline at end of file +Für multiple Domains ist es mit Nginx einfacher als mit Apache. +Du musst du oben genannten Schritte nur für jedes Zertifikat wiederholen und die spezifischen Informationen im eigenen {server...}-Bereich spezifizieren. diff --git a/doc/de/Settings.md b/doc/de/Settings.md index 68a6c89f8..988b3657c 100644 --- a/doc/de/Settings.md +++ b/doc/de/Settings.md @@ -3,7 +3,10 @@ Konfigurationen * [Zur Startseite der Hilfe](help) -Hier findest du einige eingebaute Features, welche kein graphisches Interface haben oder nicht dokumentiert sind. Konfigurationseinstellungen sind in der Datei ".htconfig.php" gespeichert. Bearbeite diese Datei, indem du sie z.B. mit einem Texteditor öffnest. Verschiedene Systemeinstellungen sind bereits in dieser Datei dokumentiert und werden hier nicht weiter erklärt. +Hier findest du einige eingebaute Features, welche kein graphisches Interface haben oder nicht dokumentiert sind. +Konfigurationseinstellungen sind in der Datei ".htconfig.php" gespeichert. +Bearbeite diese Datei, indem du sie z.B. mit einem Texteditor öffnest. +Verschiedene Systemeinstellungen sind bereits in dieser Datei dokumentiert und werden hier nicht weiter erklärt. **Tastaturbefehle** @@ -16,7 +19,9 @@ Friendica erfasst die folgenden Tastaturbefehle: **Geburtstagsbenachrichtigung** -Geburtstage erscheinen auf deiner Startseite für alle Freunde, die in den nächsten 6 Tagen Geburtstag haben. Um deinen Geburtstag für alle sichtbar zu machen, musst du deinen Geburtstag (zumindest Tag und Monat) in dein Standardprofil eintragen. Es ist nicht notwendig, das Jahr einzutragen. +Geburtstage erscheinen auf deiner Startseite für alle Freunde, die in den nächsten 6 Tagen Geburtstag haben. +Um deinen Geburtstag für alle sichtbar zu machen, musst du deinen Geburtstag (zumindest Tag und Monat) in dein Standardprofil eintragen. +Es ist nicht notwendig, das Jahr einzutragen. **Konfigurationseinstellungen** @@ -49,7 +54,13 @@ $a->config['system']['theme'] = 'theme-name'; Sicherheitseinstellungen -Standardmäßig erlaubt Friendica SSL-Kommunikation von Seiten, die "selbstunterzeichnete" SSL-Zertifikate nutzen. Um eine weitreichende Kompatibilität mit anderen Netzwerken und Browsern zu gewährleisten, empfehlen wir, selbstunterzeichnete Zertifikate **nicht** zu nutzen. Aber wir halten dich nicht davon ab, solche zu nutzen. SSL verschlüsselt alle Daten zwischen den Webseiten (und für deinen Browser), was dir eine komplett verschlüsselte Kommunikation erlaubt. Auch schützt es deine Login-Daten vor Datendiebstahl. Selbstunterzeichnete Zertifikate können kostenlos erstellt werden. Diese Zertifikate können allerdings Opfer eines sogenannten ["man-in-the-middle"-Angriffs](http://de.wikipedia.org/wiki/Man-in-the-middle-Angriff) werden, und sind daher weniger bevorzugt. Wenn du es wünscht, kannst du eine strikte Zertifikatabfrage einstellen. Das führt dazu, dass du keinerlei Verbindung zu einer selbstunterzeichneten SSL-Seite erstellen kannst +Standardmäßig erlaubt Friendica SSL-Kommunikation von Seiten, die "selbstunterzeichnete" SSL-Zertifikate nutzen. +Um eine weitreichende Kompatibilität mit anderen Netzwerken und Browsern zu gewährleisten, empfehlen wir, selbstunterzeichnete Zertifikate **nicht** zu nutzen. +Aber wir halten dich nicht davon ab, solche zu nutzen. SSL verschlüsselt alle Daten zwischen den Webseiten (und für deinen Browser), was dir eine komplett verschlüsselte Kommunikation erlaubt. +Auch schützt es deine Login-Daten vor Datendiebstahl. Selbstunterzeichnete Zertifikate können kostenlos erstellt werden. +Diese Zertifikate können allerdings Opfer eines sogenannten ["man-in-the-middle"-Angriffs](http://de.wikipedia.org/wiki/Man-in-the-middle-Angriff) werden, und sind daher weniger bevorzugt. +Wenn du es wünscht, kannst du eine strikte Zertifikatabfrage einstellen. +Das führt dazu, dass du keinerlei Verbindung zu einer selbstunterzeichneten SSL-Seite erstellen kannst Konfiguriere: ``` @@ -61,7 +72,8 @@ $a->config['system']['verifyssl'] = true; Kooperationen/Gemeinschaften/Bildung Erweiterung -Kommagetrennte Liste von Domains, welche eine Freundschaft mit dieser Seite eingehen dürfen. Wildcards werden akzeptiert (Wildcard-Unterstützung unter Windows benötigt PHP5.3) Standardmäßig sind alle gültigen Domains erlaubt. +Kommagetrennte Liste von Domains, welche eine Freundschaft mit dieser Seite eingehen dürfen. +Wildcards werden akzeptiert (Wildcard-Unterstützung unter Windows benötigt PHP5.3) Standardmäßig sind alle gültigen Domains erlaubt. Konfiguriere: ``` @@ -73,7 +85,9 @@ $a->config['system']['allowed_sites'] = "sitea.com, *siteb.com"; Kooperationen/Gemeinschaften/Bildung Erweiterung -Kommagetrennte Liste von Domains, welche bei der Registrierung als Part der Email-Adresse erlaubt sind. Das grenzt Leute aus, die nicht Teil der Gruppe oder Organisation sind. Wildcards werden akzeptiert (Wildcard-Unterstützung unter Windows benötigt PHP5.3) Standardmäßig sind alle gültigen Email-Adressen erlaubt. +Kommagetrennte Liste von Domains, welche bei der Registrierung als Part der Email-Adresse erlaubt sind. +Das grenzt Leute aus, die nicht Teil der Gruppe oder Organisation sind. +Wildcards werden akzeptiert (Wildcard-Unterstützung unter Windows benötigt PHP5.3) Standardmäßig sind alle gültigen Email-Adressen erlaubt. Konfiguriere: ``` @@ -84,7 +98,14 @@ $a->config['system']['allowed_email'] = "sitea.com, *siteb.com"; Kooperationen/Gemeinschaften/Bildung Erweiterung -Setze diese Einstellung auf "true" und sperre den öffentlichen Zugriff auf alle Seiten, solange man nicht eingeloggt ist. Das blockiert die Ansicht von Profilen, Freunden, Fotos, vom Verzeichnis und den Suchseiten. Ein Nebeneffekt ist, dass Einträge dieser Seite nicht im globalen Verzeichnis erscheinen. Wir empfehlen, speziell diese Einstellung auszuschalten (die Einstellung ist an anderer Stelle auf dieser Seite erklärt). Beachte: das ist speziell für Seiten, die beabsichtigen, von anderen Friendica-Netzwerken abgeschottet zu sein. Unautorisierte Personen haben ebenfalls nicht die Möglichkeit, Freundschaftsanfragen von Seitennutzern zu beantworten. Die Standardeinstellung steht auf "false". Verfügbar in Version 2.2 und höher. +Setze diese Einstellung auf "true" und sperre den öffentlichen Zugriff auf alle Seiten, solange man nicht eingeloggt ist. +Das blockiert die Ansicht von Profilen, Freunden, Fotos, vom Verzeichnis und den Suchseiten. +Ein Nebeneffekt ist, dass Einträge dieser Seite nicht im globalen Verzeichnis erscheinen. +Wir empfehlen, speziell diese Einstellung auszuschalten (die Einstellung ist an anderer Stelle auf dieser Seite erklärt). +Beachte: das ist speziell für Seiten, die beabsichtigen, von anderen Friendica-Netzwerken abgeschottet zu sein. +Unautorisierte Personen haben ebenfalls nicht die Möglichkeit, Freundschaftsanfragen von Seitennutzern zu beantworten. +Die Standardeinstellung steht auf "false". +Verfügbar in Version 2.2 und höher. Konfiguriere: ``` @@ -96,7 +117,9 @@ $a->config['system']['block_public'] = true; Kooperationen/Gemeinschaften/Bildung Erweiterung -Standardmäßig können Nutzer selbst auswählen, ob ihr Profil im Seitenverzeichnis erscheint. Diese Einstellung zwingt alle Nutzer dazu, im Verzeichnis zu erscheinen. Diese Einstellung kann vom Nutzer nicht deaktiviert werden. Die Standardeinstellung steht auf "false". +Standardmäßig können Nutzer selbst auswählen, ob ihr Profil im Seitenverzeichnis erscheint. +Diese Einstellung zwingt alle Nutzer dazu, im Verzeichnis zu erscheinen. +Diese Einstellung kann vom Nutzer nicht deaktiviert werden. Die Standardeinstellung steht auf "false". Konfiguriere: ``` @@ -108,11 +131,14 @@ $a->config['system']['publish_all'] = true; Kooperationen/Gemeinschaften/Bildung Erweiterung -Mit diesem Befehl wird die URL eingestellt, die zum Update des globalen Verzeichnisses genutzt wird. Dieser Befehl ist in der Standardkonfiguration enthalten. Der nichtdokumentierte Teil dieser Einstellung ist, dass das globale Verzeichnis gar nicht verfügbar ist, wenn diese Einstellung nicht gesetzt wird. Dies erlaubt eine private Kommunikation, die komplett vom globalen Verzeichnis isoliert ist. +Mit diesem Befehl wird die URL eingestellt, die zum Update des globalen Verzeichnisses genutzt wird. +Dieser Befehl ist in der Standardkonfiguration enthalten. +Der nichtdokumentierte Teil dieser Einstellung ist, dass das globale Verzeichnis gar nicht verfügbar ist, wenn diese Einstellung nicht gesetzt wird. +Dies erlaubt eine private Kommunikation, die komplett vom globalen Verzeichnis isoliert ist. Konfiguriere: ``` -$a->config['system']['directory_submit_url'] = 'http://dir.friendica.com/submit'; +$a->config['system']['directory'] = 'http://dir.friendi.ca'; ``` @@ -129,9 +155,11 @@ $a->config['system']['proxyuser'] = "username:password"; **Netzwerk-Timeout** -Legt fest, wie lange das Netzwerk warten soll, bevor ein Timeout eintritt. Der Wert wird in Sekunden angegeben. Standardmäßig ist 60 eingestellt; 0 steht für "unbegrenzt" (nicht empfohlen). +Legt fest, wie lange das Netzwerk warten soll, bevor ein Timeout eintritt. +Der Wert wird in Sekunden angegeben. Standardmäßig ist 60 eingestellt; 0 steht für "unbegrenzt" (nicht empfohlen). Konfiguriere: + ``` $a->config['system']['curl_timeout'] = 60; ``` @@ -139,9 +167,11 @@ $a->config['system']['curl_timeout'] = 60; **Banner/Logo** -Hiermit legst du das Banner der Seite fest. Standardmäßig ist das Friendica-Logo und der Name festgelegt. Du kannst hierfür HTML/CSS nutzen, um den Inhalt zu gestalten und/oder die Position zu ändern, wenn es nicht bereits voreingestellt ist. +Hiermit legst du das Banner der Seite fest. Standardmäßig ist das Friendica-Logo und der Name festgelegt. +Du kannst hierfür HTML/CSS nutzen, um den Inhalt zu gestalten und/oder die Position zu ändern, wenn es nicht bereits voreingestellt ist. Konfiguriere: + ``` $a->config['system']['banner'] = 'Meine tolle Webseite'; ``` @@ -152,6 +182,7 @@ $a->config['system']['banner'] = 'Meine tolle Webseiteconfig['system']['maximagesize'] = 1000000; ``` @@ -159,9 +190,13 @@ $a->config['system']['maximagesize'] = 1000000; **UTF-8 Reguläre Ausdrücke** -Während der Registrierung werden die Namen daraufhin geprüft, ob sie reguläre UTF-8-Ausdrücke nutzen. Hierfür wird PHP benötigt, um mit einer speziellen Einstellung kompiliert zu werden, die UTF-8-Ausdrücke benutzt. Wenn du absolut keine Möglichkeit hast, Accounts zu registrieren, setze den Wert von "no_utf" auf "true". Standardmäßig ist "false" eingestellt (das bedeutet, dass UTF-8-Ausdrücke unterstützt werden und funktionieren). +Während der Registrierung werden die Namen daraufhin geprüft, ob sie reguläre UTF-8-Ausdrücke nutzen. +Hierfür wird PHP benötigt, um mit einer speziellen Einstellung kompiliert zu werden, die UTF-8-Ausdrücke benutzt. +Wenn du absolut keine Möglichkeit hast, Accounts zu registrieren, setze den Wert von "no_utf" auf "true". +Standardmäßig ist "false" eingestellt (das bedeutet, dass UTF-8-Ausdrücke unterstützt werden und funktionieren). Konfiguriere: + ``` $a->config['system']['no_utf'] = true; ``` @@ -169,9 +204,13 @@ $a->config['system']['no_utf'] = true; **Prüfe vollständigen Namen** -Es kann vorkommen, dass viele Spammer versuchen, sich auf deiner Seite zu registrieren. In Testphasen haben wir festgestellt, dass diese automatischen Registrierungen das Feld "Vollständiger Name" oft nur mit Namen ausfüllen, die kein Leerzeichen beinhalten. Wenn du Leuten erlauben willst, sich nur mit einem Namen anzumelden, dann setze die Einstellung auf "true". Die Standardeinstellung ist auf "false" gesetzt. +Es kann vorkommen, dass viele Spammer versuchen, sich auf deiner Seite zu registrieren. +In Testphasen haben wir festgestellt, dass diese automatischen Registrierungen das Feld "Vollständiger Name" oft nur mit Namen ausfüllen, die kein Leerzeichen beinhalten. +Wenn du Leuten erlauben willst, sich nur mit einem Namen anzumelden, dann setze die Einstellung auf "true". +Die Standardeinstellung ist auf "false" gesetzt. Konfiguriere: + ``` $a->config['system']['no_regfullname'] = true; ``` @@ -179,7 +218,9 @@ $a->config['system']['no_regfullname'] = true; **OpenID** -Standardmäßig wird OpenID für die Registrierung und für Logins genutzt. Wenn du nicht willst, dass OpenID-Strukturen für dein System übernommen werden, dann setze "no_openid" auf "true". Standardmäßig ist hier "false" gesetzt. +Standardmäßig wird OpenID für die Registrierung und für Logins genutzt. +Wenn du nicht willst, dass OpenID-Strukturen für dein System übernommen werden, dann setze "no_openid" auf "true". +Standardmäßig ist hier "false" gesetzt. Konfiguriere: ``` @@ -189,7 +230,12 @@ $a->config['system']['no_openid'] = true; **Multiple Registrierungen** -Um mehrfache Seiten zu erstellen, muss sich eine Person mehrfach registrieren können. Deine Seiteneinstellung kann Registrierungen komplett blockieren oder an Bedingungen knüpfen. Standardmäßig können eingeloggte Nutzer weitere Accounts für die Seitenerstellung registrieren. Hier ist weiterhin eine Bestätigung notwendig, wenn "REGISTER_APPROVE" ausgewählt ist. Wenn du die Erstellung weiterer Accounts blockieren willst, dann setze die Einstellung "block_extended_register" auf "true". Standardmäßig ist hier "false" gesetzt. +Um mehrfache Seiten zu erstellen, muss sich eine Person mehrfach registrieren können. +Deine Seiteneinstellung kann Registrierungen komplett blockieren oder an Bedingungen knüpfen. +Standardmäßig können eingeloggte Nutzer weitere Accounts für die Seitenerstellung registrieren. +Hier ist weiterhin eine Bestätigung notwendig, wenn "REGISTER_APPROVE" ausgewählt ist. +Wenn du die Erstellung weiterer Accounts blockieren willst, dann setze die Einstellung "block_extended_register" auf "true". +Standardmäßig ist hier "false" gesetzt. Konfiguriere: ``` @@ -207,7 +253,9 @@ $a->config['system']['debugging'] = true; $a->config['system']['logfile'] = 'logfile.out'; $a->config['system']['loglevel'] = LOGGER_DEBUG; ``` -Erstellt detaillierte Debugging-Logfiles, die in der Datei "logfile.out" gespeichert werden (Datei muss auf dem Server mit Schreibrechten versehen sein). "LOGGER_DEBUG" zeigt eine Menge an Systeminformationen, enthält aber keine detaillierten Daten. Du kannst ebenfalls "LOGGER_ALL" auswählen, allerdings empfehlen wir dieses nur, wenn ein spezifisches Problem eingegrenzt werden soll. Andere Log-Level sind möglich, werden aber derzeit noch nicht genutzt. +Erstellt detaillierte Debugging-Logfiles, die in der Datei "logfile.out" gespeichert werden (Datei muss auf dem Server mit Schreibrechten versehen sein). "LOGGER_DEBUG" zeigt eine Menge an Systeminformationen, enthält aber keine detaillierten Daten. +Du kannst ebenfalls "LOGGER_ALL" auswählen, allerdings empfehlen wir dieses nur, wenn ein spezifisches Problem eingegrenzt werden soll. +Andere Log-Level sind möglich, werden aber derzeit noch nicht genutzt. **PHP-Fehler-Logging** @@ -222,5 +270,9 @@ ini_set('log_errors','1'); ini_set('display_errors', '0'); ``` -Diese Befehle erfassen alle PHP-Fehler in der Datei "php.out" (Datei muss auf dem Server mit Schreibrechten versehen sein). Nicht deklarierte Variablen werden manchmal mit einem Verweis versehen, weshalb wir empfehlen, "E_NOTICE" und "E_ALL" nicht zu nutzen. Die Menge an Fehlern, die auf diesem Level gemeldet werden, ist komplett harmlos. Bitte informiere die Entwickler über alle Fehler, die du in deinen Log-Dateien mit den oben genannten Einstellungen erhältst. Sie weisen generell auf Fehler in, die bearbeitet werden müssen. +Diese Befehle erfassen alle PHP-Fehler in der Datei "php.out" (Datei muss auf dem Server mit Schreibrechten versehen sein). +Nicht deklarierte Variablen werden manchmal mit einem Verweis versehen, weshalb wir empfehlen, "E_NOTICE" und "E_ALL" nicht zu nutzen. +Die Menge an Fehlern, die auf diesem Level gemeldet werden, ist komplett harmlos. +Bitte informiere die Entwickler über alle Fehler, die du in deinen Log-Dateien mit den oben genannten Einstellungen erhältst. +Sie weisen generell auf Fehler in, die bearbeitet werden müssen. Wenn du eine leere (weiße) Seite erhältst, schau in die PHP-Log-Datei - dies deutet fast immer darauf hin, dass ein Fehler aufgetreten ist. diff --git a/doc/de/Tags-and-Mentions.md b/doc/de/Tags-and-Mentions.md index 0aef0beae..e99d758db 100644 --- a/doc/de/Tags-and-Mentions.md +++ b/doc/de/Tags-and-Mentions.md @@ -20,15 +20,24 @@ Personen, die in einem anderen Netzwerk sind oder die sich **NICHT in deiner Kon * @mike@macgirvin.com - diese Schreibweise wird "Fernerwähnung" (remote mention)genannt und kann nur im Email-Stil geschrieben werden, nicht als Internetadresse/URL. -Wenn das System ungewollte Erwähnungen nicht blockiert, erhält diese Person eine Mitteilung oder nimmt direkt an der Diskussion teil, wenn es sich um einen öffentlichen Beitrag handelt. Bitte beachte, dass Friendica eingehende "Erwähnungs"-Nachrichten von Personen blockt, die du nicht zu deinem Profil hinzugefügt hast. Diese Maßnahme dient dazu, Spam zu vermeiden. +Wenn das System ungewollte Erwähnungen nicht blockiert, erhält diese Person eine Mitteilung oder nimmt direkt an der Diskussion teil, wenn es sich um einen öffentlichen Beitrag handelt. +Bitte beachte, dass Friendica eingehende "Erwähnungs"-Nachrichten von Personen blockt, die du nicht zu deinem Profil hinzugefügt hast. +Diese Maßnahme dient dazu, Spam zu vermeiden. -"Fernerwähnungen" werden durch das OStatus-Protokoll übermittelt. Dieses Protokoll wird von Friendica, StatusNet und anderen Systemen genutzt, ist allerdings derzeit nicht in Diaspora eingebaut. +"Fernerwähnungen" werden durch das OStatus-Protokoll übermittelt. +Dieses Protokoll wird von Friendica, GNU Social und anderen Systemen genutzt, ist allerdings derzeit nicht in Diaspora eingebaut. Friendica unterscheidet bei Tags nicht zwischen Personen und Gruppen (einige andere Netzwerke nutzen "!gruppe", um solche zu markieren). **Thematische Tags** -Thematische Tags werden durch eine "#" gekennzeichnet. Dieses Zeichen erstellen einen Link zur allgemeinen Seitensuche mit dem ausgewählten Begriff. So wird z.B. #Autos zu einer Suche führen, die alle Beiträge deiner Seite umfasst, die dieses Wort erwähnen. Thematische Tags haben generell eine Mindestlänge von 3 Stellen. Kürzere Suchbegriffe finden meist keine Suchergebnisse, wobei dieses abhängig von der Datenbankeinstellung ist. Tags mit einem Leerzeichen werden, wie es auch bei Namen der Fall ist, durch einen Unterstrich gekennzeichnet. Es ist hingegen nicht möglich, Tags zu erstellen, deren gesuchtes Wort einen Unterstrich enthält. +Thematische Tags werden durch eine "#" gekennzeichnet. +Dieses Zeichen erstellen einen Link zur allgemeinen Seitensuche mit dem ausgewählten Begriff. +So wird z.B. #Autos zu einer Suche führen, die alle Beiträge deiner Seite umfasst, die dieses Wort erwähnen. +Thematische Tags haben generell eine Mindestlänge von 3 Stellen. +Kürzere Suchbegriffe finden meist keine Suchergebnisse, wobei dieses abhängig von der Datenbankeinstellung ist. +Tags mit einem Leerzeichen werden, wie es auch bei Namen der Fall ist, durch einen Unterstrich gekennzeichnet. +Es ist hingegen nicht möglich, Tags zu erstellen, deren gesuchtes Wort einen Unterstrich enthält. Thematische Tags werden auch dann nicht verlinkt, wenn sie nur aus Nummern bestehen, wie z.B. #1. Wenn du einen numerischen Tag nutzen willst, füge bitte einen Beschreibungstext hinzu wie z.B. #2012_Wahl. diff --git a/doc/de/Text_comment.md b/doc/de/Text_comment.md index 5c3d3d4e4..88e5e0b11 100644 --- a/doc/de/Text_comment.md +++ b/doc/de/Text_comment.md @@ -3,35 +3,49 @@ Beiträge kommentieren, einordnen und löschen * [Zur Startseite der Hilfe](help) -Hier findest du eine Übersicht über die verschiedenen Möglichkeiten, bestehende Beiträge einzuordnen und zu kommentieren. Achtung: für dieses Beispiel wurde das Thema "Diabook" genutzt. Wenn du ein anderes Design benutzt, wirst du manche dieser Symbole gar nicht oder in anderer Form vorfinden. +Hier findest du eine Übersicht über die verschiedenen Möglichkeiten, bestehende Beiträge einzuordnen und zu kommentieren. + +Achtung: für dieses Beispiel wurde das Thema "Diabook" genutzt. +Wenn du ein anderes Design benutzt, wirst du manche dieser Symbole gar nicht oder in anderer Form vorfinden. diabook Die einzelnen Symbole -post_thumbs_up.png Mit diesem Symbol kannst du zeigen, dass dir ein Beitrag gefällt. Falls du diese Eingabe zurücknehmen willst, klicke einfach ein zweites Mal auf das Symbol. +post_thumbs_up.png Mit diesem Symbol kannst du zeigen, dass dir ein Beitrag gefällt. +Falls du diese Eingabe zurücknehmen willst, klicke einfach ein zweites Mal auf das Symbol.

-post_thumbs_down.png Mit diesem Symbol kannst du zeigen, dass dir ein Beitrag nicht gefällt. Falls du diese Eingabe zurücknehmen willst, klicke einfach ein zweites Mal auf das Symbol. +post_thumbs_down.png Mit diesem Symbol kannst du zeigen, dass dir ein Beitrag nicht gefällt. +Falls du diese Eingabe zurücknehmen willst, klicke einfach ein zweites Mal auf das Symbol.

-post_share.png Mit diesem Symbol kannst du einen Beitrag weiter verteilen. Einfach anklicken und sofort erscheint der Beitrag in deinem Beitragseditor. Am Ende des eingefügten Beitrags erscheint ein Link zum Originalbeitrag. +post_share.png Mit diesem Symbol kannst du einen Beitrag weiter verteilen. +Einfach anklicken und sofort erscheint der Beitrag in deinem Beitragseditor. +Am Ende des eingefügten Beitrags erscheint ein Link zum Originalbeitrag.

-post_mark.png Mit diesem Symbol kannst du einen Beitrag für dich markieren. Markierte Beiträge erscheinen in deiner Netzwerk-Seite unter "Markierte". Wenn du die Markierung entfernen willst, klicke einfach ein zweites Mal auf das Symbol. +post_mark.png Mit diesem Symbol kannst du einen Beitrag für dich markieren. +Markierte Beiträge erscheinen in deiner Netzwerk-Seite unter "Markierte". +Wenn du die Markierung entfernen willst, klicke einfach ein zweites Mal auf das Symbol.

-post_tag.png Mit diesem Symbol kannst du einen tag zum Beitrag hinzufügen und diesen so einem bestimmten Schlagwort zuzuordnen. Anschließend kannst du auf diesen tag klicken und alle Beiträge mit diesem tag ansehen. ACHTUNG: tags können nicht mehr entfernt werden. +post_tag.png Mit diesem Symbol kannst du einen tag zum Beitrag hinzufügen und diesen so einem bestimmten Schlagwort zuzuordnen. +Anschließend kannst du auf diesen tag klicken und alle Beiträge mit diesem tag ansehen. +ACHTUNG: tags können nicht mehr entfernt werden.

-post_categorize.png Mit diesem Symbol ist es möglich, die Beiträge in bestimmte Gruppen einzuordnen. Dies dient dazu, gewählte Beiträge nach eigenen Vorstellungen zu sortieren und wieder zu finden. Wähle eine vorhandene Gruppe oder gib einen neuen Namen ein. Die erstellten Gruppen findest du unter "Gespeicherte Ordner" in der Netzwerk-Ansicht. +post_categorize.png Mit diesem Symbol ist es möglich, die Beiträge in bestimmte Gruppen einzuordnen. +Dies dient dazu, gewählte Beiträge nach eigenen Vorstellungen zu sortieren und wieder zu finden. +Wähle eine vorhandene Gruppe oder gib einen neuen Namen ein. Die erstellten Gruppen findest du unter "Gespeicherte Ordner" in der Netzwerk-Ansicht.

post_delete.png Mit diesem Symbol löschst du deinen eigenen Beitrag bzw. entfernst einen Beitrag einer anderen Person aus deinem Stream.

-post_choose.png Mit diesem Symbol kannst du mehrere Beiträge auswählen und gesammelt löschen. Hierfür gehst du nach dem Markieren aller gewünschten Beiträge auf "Lösche die markierten Beiträge" am Ende der Seite mit allen Beiträgen. +post_choose.png Mit diesem Symbol kannst du mehrere Beiträge auswählen und gesammelt löschen. +Hierfür gehst du nach dem Markieren aller gewünschten Beiträge auf "Lösche die markierten Beiträge" am Ende der Seite mit allen Beiträgen.

**Im Folgenden findest du Symbole weiterer Themen** diff --git a/doc/de/Text_editor.md b/doc/de/Text_editor.md index 7425ba1ae..0d9fbb5c7 100644 --- a/doc/de/Text_editor.md +++ b/doc/de/Text_editor.md @@ -3,29 +3,43 @@ Beiträge erstellen * [Zur Startseite der Hilfe](help) -Hier findest du eine Übersicht über die verschiedenen Möglichkeiten, deinen Beitrag zu bearbeiten. Achtung: für dieses Beispiel wurde das Thema "Diabook" genutzt. Wenn du ein anderes Design benutzt, wirst du manche dieser Symbole gar nicht oder in anderer Form vorfinden. +Hier findest du eine Übersicht über die verschiedenen Möglichkeiten, deinen Beitrag zu bearbeiten. + +Achtung: für dieses Beispiel wurde das Thema "Diabook" genutzt. +Wenn du ein anderes Design benutzt, wirst du manche dieser Symbole gar nicht oder in anderer Form vorfinden. editor Die einzelnen Symbole -editor Wenn du auf dieses Symbol klickst, dann kannst du ein Bild von deinem Computer hinzufügen. Wenn du eine Internetadresse (URL) eingeben willst, dann kannst du das "Baum"-Symbol im oberen Teil des Editors nutzen. Wenn du ein Bild ausgewählt hast, dann erscheint eine Miniaturdarstellung des Bildes im Editor.* +editor Wenn du auf dieses Symbol klickst, dann kannst du ein Bild von deinem Computer hinzufügen. +Wenn du eine Internetadresse (URL) eingeben willst, dann kannst du das "Baum"-Symbol im oberen Teil des Editors nutzen. +Wenn du ein Bild ausgewählt hast, dann erscheint eine Miniaturdarstellung des Bildes im Editor.*

paper_clip Wenn du dieses Symbol anklickst, dann kannst du weitere Dateien von deinem Computer einfügen. Eine Vorschau des Dateiinhalts erfolgt nicht.*

-chain Wenn du die Kette anklickst, dann kannst du eine Internetadresse (URL) einfügen. Im Editor erscheint automatisch eine kurze Information zum eingefügten Link.* +chain Wenn du die Kette anklickst, dann kannst du eine Internetadresse (URL) einfügen. +Im Editor erscheint automatisch eine kurze Information zum eingefügten Link.*

-video Mit dieser Funktion kannst du die Internetadresse (URL) einer Videodatei einfügen. Das Video erscheint dann mit einem Player in deinem Beitrag. Da Friendica zur Einbindung [HTML5](http://en.wikipedia.org/wiki/HTML5_video) verwendet, werden je nach Browser verschiedene Videoformate unterstützt (z.B. WebM oder MP4). Außerdem kannst du hier die URLs von Videos auf Youtube, Vimeo und manchen anderen Videohostern eingeben. Die Videos werden dann mit Vorschaubild angezeigt, nach einem Klick öffnet sich ein eingebetteter Player.* +video Mit dieser Funktion kannst du die Internetadresse (URL) einer Videodatei einfügen. +Das Video erscheint dann mit einem Player in deinem Beitrag. +Da Friendica zur Einbindung [HTML5](http://en.wikipedia.org/wiki/HTML5_video) verwendet, werden je nach Browser verschiedene Videoformate unterstützt (z.B. WebM oder MP4). +Außerdem kannst du hier die URLs von Videos auf Youtube, Vimeo und manchen anderen Videohostern eingeben. +Die Videos werden dann mit Vorschaubild angezeigt, nach einem Klick öffnet sich ein eingebetteter Player.*

-mic Mit dieser Funktion kannst du die Internetadresse (URL) einer Sound-Datei einfügen. Da Friendica zur Einbindung [HTML5](http://en.wikipedia.org/wiki/HTML5_video) verwendet, werden je nach Browser und Betriebssystem MP3, Ogg oder AAC unterstützt. Außerdem kannst du hier auch URLs von manchen Audiohostern wie Soundcloud eingeben, um eine dort gespeicherte Audiodatei mit Player in deinem Beitrag anzuzeigen.* +mic Mit dieser Funktion kannst du die Internetadresse (URL) einer Sound-Datei einfügen. +Da Friendica zur Einbindung [HTML5](http://en.wikipedia.org/wiki/HTML5_video) verwendet, werden je nach Browser und Betriebssystem MP3, Ogg oder AAC unterstützt. +Außerdem kannst du hier auch URLs von manchen Audiohostern wie Soundcloud eingeben, um eine dort gespeicherte Audiodatei mit Player in deinem Beitrag anzuzeigen.*

-globe Wenn du dieses Symbol wählst, dann kannst du deinen Standort festlegen. Hier reicht schon eine Angabe wie "Berlin" oder "10775". Dieser Eintrag führt anschließend zu einer Suchanfrage bei Google Maps. +globe Wenn du dieses Symbol wählst, dann kannst du deinen Standort festlegen. +Hier reicht schon eine Angabe wie "Berlin" oder "10775". +Dieser Eintrag führt anschließend zu einer Suchanfrage bei Google Maps.

* wie du Dateien hochladen kannst, erfährst du [hier](help/FAQ#upload) diff --git a/doc/de/andfinally.md b/doc/de/andfinally.md index b6e492ae6..a873b046d 100644 --- a/doc/de/andfinally.md +++ b/doc/de/andfinally.md @@ -3,17 +3,13 @@ Und damit sind wir auch schon am Ende der Schnellstartanleitung. -Hier sind noch einige weitere Dinge, die dir den Start vereinfachen können. +Hier sind noch einige weitere Dinge, die Dir den Start vereinfachen können. **Gruppen** - Friendica Support - Probleme? Dann ist das der Platz, um zu fragen! -- Let's Talk eine Gruppe, um Leute und Gruppen mit gleichen Interessen zu finden - -- Local Friendica eine Seite für lokale Friendica-Gruppen - **Dokumentation** diff --git a/doc/de/groupsandpages.md b/doc/de/groupsandpages.md index 0b5f1fced..d5b8e7f69 100644 --- a/doc/de/groupsandpages.md +++ b/doc/de/groupsandpages.md @@ -3,11 +3,21 @@ Gruppen und Seiten * [Zur Startseite der Hilfe](help) -Hier siehst du das globale Verzeichnis. Wenn du dich mal verirrt hast, kannst du diesen Link klicken und wieder hierher kommen. +Hier siehst du das globale Verzeichnis. +Wenn du dich mal verirrt hast, kannst du diesen Link klicken und wieder hierher kommen. -Auf dieser Seite findest du eine Zusammenstellung von Gruppen, Foren und bekannten Seiten. Gruppen sind keine realen Personen. Sich mit diesen zu verbinden ist, als wenn man jemanden auf Facebook "liked" ("gefällt mir") oder wenn man sich in einem Forum anmeldet. Habe keine Sorge, falls du dich unbehaglich fühlst, wenn du dich einer neuen Person vorstellen sollst, da es sich nicht um Personen handelt. +Auf dieser Seite findest du eine Zusammenstellung von Gruppen, Foren und bekannten Seiten. +Gruppen sind keine realen Personen. +Sich mit diesen zu verbinden ist, als wenn man jemanden auf Facebook "liked" ("gefällt mir") oder wenn man sich in einem Forum anmeldet. +Habe keine Sorge, falls du dich unbehaglich fühlst, wenn du dich einer neuen Person vorstellen sollst, da es sich nicht um Personen handelt. -Wenn du dich mit einer Gruppe verbindest, erscheinen alle Nachrichten der Gruppe in deinem "Netzwerk"-Tab. Du kannst diese Beiträge kommentieren oder selbst in der Gruppe schreiben, ohne eine der Gruppenmitglieder persönlich hinzuzufügen. Das ist ein großartiger Weg, dynamisch neue Freunde zu gewinnen. Du findest Personen, die du magst, anstatt Fremde hinzuzufügen. Suche dir einfach eine Gruppe und füge sie so hinzu, wie du auch normale Freunde hinzufügst. Es gibt eine Menge Gruppen und möglicherweise findest du nicht wieder zu dieser Seite zurück. In diesem Fall nutze einfach den Link oben auf dieser Seite. +Wenn du dich mit einer Gruppe verbindest, erscheinen alle Nachrichten der Gruppe in deinem "Netzwerk"-Tab. +Du kannst diese Beiträge kommentieren oder selbst in der Gruppe schreiben, ohne eine der Gruppenmitglieder persönlich hinzuzufügen. +Das ist ein großartiger Weg, dynamisch neue Freunde zu gewinnen. +Du findest Personen, die du magst, anstatt Fremde hinzuzufügen. +Suche dir einfach eine Gruppe und füge sie so hinzu, wie du auch normale Freunde hinzufügst. +Es gibt eine Menge Gruppen und möglicherweise findest du nicht wieder zu dieser Seite zurück. +In diesem Fall nutze einfach den Link oben auf dieser Seite. Wenn du einige Gruppen hinzugefügt hast, gehe weiter zum nächsten Schritt. diff --git a/doc/de/guide.md b/doc/de/guide.md index a5ce54a1d..fde9cb8ea 100644 --- a/doc/de/guide.md +++ b/doc/de/guide.md @@ -3,13 +3,22 @@ Erste Schritte... * [Zur Startseite der Hilfe](help) -Das Erste zum Anfang: geh sicher, dass du schon eingeloggt bist. Wenn du noch nicht eingeloggt bist, kannst du das in dem Fenster unten machen. +Das Erste zum Anfang: geh sicher, dass du schon eingeloggt bist. +Wenn du noch nicht eingeloggt bist, kannst du das in dem Fenster unten machen. Sobald du eingeloggt bist (oder wenn du bereits eingeloggt bist), kannst du unten nun auf deine Profilseite schauen. -Hier sieht es ein wenig wie auf deiner Facebook-Seite aus. Hier findest du alle deine Statusmeldungen und Nachrichten deiner Freunde, die direkt auf deine Seite ("Wall") geschrieben haben. Um deinen Status einzutragen, klicke einfach auf die Box oben, in der "Teilen" steht. Wenn du das machst, vergrößert sich die Box. Nun kannst du einige Formatierungsoptionen wie Fett, kursiv, unterstrichen auswählen und ebenfalls Bilder und Links hinzufügen. Unten findest du in diesem Feld weitere Links, mit denen du Bilder und Dateien von deinem Computer hochladen, Webseiten mit einem Kurztext teilen und Video- und Audiodateien aus dem Internet einfügen kannst. Außerdem kannst du hier eintragen, wo du gerade bist. +Hier sieht es ein wenig wie auf deiner Facebook-Seite aus. +Hier findest du alle deine Statusmeldungen und Nachrichten deiner Freunde, die direkt auf deine Seite ("Wall") geschrieben haben. +Um deinen Status einzutragen, klicke einfach auf die Box oben, in der "Teilen" steht. +Wenn du das machst, vergrößert sich die Box. +Nun kannst du einige Formatierungsoptionen wie Fett, kursiv, unterstrichen auswählen und ebenfalls Bilder und Links hinzufügen. +Unten findest du in diesem Feld weitere Links, mit denen du Bilder und Dateien von deinem Computer hochladen, Webseiten mit einem Kurztext teilen und Video- und Audiodateien aus dem Internet einfügen kannst. +Außerdem kannst du hier eintragen, wo du gerade bist. -Wenn du deinen Beitrag ("Post") geschrieben hast, kannst du auf das "Schloss"-Symbol klicken und festlegen, wer deinen Beitrag sehen kann. Wenn du dieses Symbol nicht anklickst, ist dein Beitrag öffentlich. Das bedeutet, dass jeder, der dein Profil ansieht, der auf dem "Community"-Tab deines Servers oder auf dem "Netzwerk"-Tab ("Beiträge deiner Kontakte") eines befreundeten Kontakts ist, den Beitrag sehen kann. +Wenn du deinen Beitrag ("Post") geschrieben hast, kannst du auf das "Schloss"-Symbol klicken und festlegen, wer deinen Beitrag sehen kann. +Wenn du dieses Symbol nicht anklickst, ist dein Beitrag öffentlich. +Das bedeutet, dass jeder, der dein Profil ansieht, der auf dem "Community"-Tab deines Servers oder auf dem "Netzwerk"-Tab ("Beiträge deiner Kontakte") eines befreundeten Kontakts ist, den Beitrag sehen kann. Probiere es doch einfach mal aus. Wenn du fertg bist, schauen wir uns den "Netzwerk"-Tab an. diff --git a/doc/de/makingnewfriends.md b/doc/de/makingnewfriends.md index 911b7f4a8..8b15cec90 100644 --- a/doc/de/makingnewfriends.md +++ b/doc/de/makingnewfriends.md @@ -3,13 +3,24 @@ Neue Freunde finden * [Zur Startseite der Hilfe](help) -Hier siehst du die Kontaktvorschläge. Wenn du dich mal verirrt hast, kannst du diesen Link klicken und wieder hierher kommen. +Hier siehst du die Kontaktvorschläge. +Wenn du dich mal verirrt hast, kannst du diesen Link klicken und wieder hierher kommen. -Diese Seite ist ein wenig wie die Kontaktvorschläge in Facebook. Jeder auf dieser Liste hat zugestimmt, als Kontaktvorschlag zu erscheinen. Das bedeutet, das sie Anfragen meist nicht ablehnen, da sie neue Leute kennenlernen wollen. +Diese Seite ist ein wenig wie die Kontaktvorschläge in Facebook. +Jeder auf dieser Liste hat zugestimmt, als Kontaktvorschlag zu erscheinen. +Das bedeutet, das sie Anfragen meist nicht ablehnen, da sie neue Leute kennenlernen wollen. -Siehst du jemanden, dessen Aussehen du magst? Klicke auf den "Verbinden"-Button beim Foto. Als nächstes kommst du zur Seite "Freundschafts-/Kontaktanfrage". Fülle das Formular wie vorgegeben aus und trage optional eine kleine Notiz ein. Nun musst du nur noch auf die Bestätigung warten. Beachte dabei, dass es sich um reale Personen handelt und es somit etwas dauern kann. Jetzt, nachdem du jemanden hinzugefügt hast, weißt du vielleicht nicht mehr, wie du zurückkommst. Klicke einfach auf den Link oben auf dieser Seite und du kommst zurück zur Seite mit den Kontaktvorschlägen, um weitere Personen hinzuzufügen. +Siehst du jemanden, dessen Aussehen du magst? +Klicke auf den "Verbinden"-Button beim Foto. +Als nächstes kommst du zur Seite "Freundschafts-/Kontaktanfrage". +Fülle das Formular wie vorgegeben aus und trage optional eine kleine Notiz ein. +Nun musst du nur noch auf die Bestätigung warten. +Beachte dabei, dass es sich um reale Personen handelt und es somit etwas dauern kann. +Jetzt, nachdem du jemanden hinzugefügt hast, weißt du vielleicht nicht mehr, wie du zurückkommst. +Klicke einfach auf den Link oben auf dieser Seite und du kommst zurück zur Seite mit den Kontaktvorschlägen, um weitere Personen hinzuzufügen. -Du willst nicht einfach Personen hinzufügen, die du nicht kennst? Kein Problem - an dieser Stelle kommen wir zu den Gruppen und Seiten. +Du willst nicht einfach Personen hinzufügen, die du nicht kennst? +Kein Problem - an dieser Stelle kommen wir zu den Gruppen und Seiten. diff --git a/doc/de/network.md b/doc/de/network.md index 37eeec869..154455319 100644 --- a/doc/de/network.md +++ b/doc/de/network.md @@ -3,11 +3,17 @@ Deine "Netzwerk"-Seite * [Zur Startseite der Hilfe](help) -Das ist dein "Netzwerk"-Tab. Wenn du dich mal verirrt hast, kannst du diesen Link klicken, um wieder hierher zu kommen. +Das ist dein "Netzwerk"-Tab. +Wenn du dich mal verirrt hast, kannst du diesen Link klicken, um wieder hierher zu kommen. -Diese Seite ist ein wenig wie die News-Seite in Facebook oder der Stream in Diaspora. Hier findest du alle Beiträge deiner Kontakte, Gruppen und Feeds, die du eingetragen hast. Wenn du neu bist, siehst du hier noch nichts, falls du deinen Status im letzten Schritt noch nicht eingetragen hast. Wenn du bereits ein paar Freunde eingetragen hast, findest du hier ihre Beiträge. Hier kannst du Beiträge kommentieren, eintragen, dass du den Beitrag magst oder ablehnst oder die Profile durch einen Klick auf deren Namen anschauen und auf deren Seite ("Wall") Nachrichten schreiben. +Diese Seite ist ein wenig wie die News-Seite in Facebook oder der Stream in Diaspora. +Hier findest du alle Beiträge deiner Kontakte, Gruppen und Feeds, die du eingetragen hast. +Wenn du neu bist, siehst du hier noch nichts, falls du deinen Status im letzten Schritt noch nicht eingetragen hast. +Wenn du bereits ein paar Freunde eingetragen hast, findest du hier ihre Beiträge. +Hier kannst du Beiträge kommentieren, eintragen, dass du den Beitrag magst oder ablehnst oder die Profile durch einen Klick auf deren Namen anschauen und auf deren Seite ("Wall") Nachrichten schreiben. -Nun wollen wir diese Seite mit Inhalt füllen. Der erste Schritt ist es, Leute zu deinem Account hinzuzufügen. +Nun wollen wir diese Seite mit Inhalt füllen. +Der erste Schritt ist es, Leute zu deinem Account hinzuzufügen. diff --git a/doc/events.md b/doc/events.md new file mode 100644 index 000000000..abb457687 --- /dev/null +++ b/doc/events.md @@ -0,0 +1,61 @@ +# Events + +* [Home](help) + +A special form of postings are events. +The events you and your contacts share can be found at [/events](/events) on your node. +To get there go to your wall and follow the tab "events" +Depending on the theme you are using, there might be an additional link from the navigation menu to this page. + +## Event Overview + +The overview page shows the calendar of the current month, plus eventually some days in the beginning and the end. +Listed are all events for this month you created, or your contacts have shared with you. +This includes birthday reminders for contacts who share their birthday with you. + +From the controls, you can switch between month/week/day view. +Flip through the view forwards and backwards. +And return to *today*. + +To create a new event, you can either follow the link "Create New Event" or make a double click on the desired box in the calendarium for when the event should take place. + +With a click on an existing event a pop-up box will be opened which shows you the event. +From there you can edit the event or view the event at the source link, if you are the one who created the event. + +## Create a new Event + +Following one of the methods mentioned above you reach a form to enter the event data. +Fields marked with a *** have to be filled. + +* **Event Starts**: enter the date/time of the start of the event here +* **Event Finishes**: enter the finishing date/time for the event here + +When you click in one of these fields a pop-up will be opened that allows you to pick the day and the time. +If you double clicked on the day box in the calendarium these fields will be pre-filled for you. +The finishing date/time has to be after the beginning date/time of the event. +But you don't have to specify it. +If the event is open-end or the finishing date/time does not matter, just select the box below the two first fields. + +* **Adjust for viewer timezone**: If you check this box, the beginning and finisching times will automatically converted to the local time according to the timezone setting + +This might prevent too early birthday wishes, or the panic attac that you have forgotten the birthday from your buddy at the other end of the world. +And similar events. + +* **Title**: a title for the event +* **Description**: a longer description for the event +* **Location**: the location the event will took place + +These three fields describe your events. +In the descirption and location field you can use BBCode to format the text. + +* **Share this event**: when this box is checked the ACL will be shown to let you select with whom you wish to share the event. This works just like the controls of any other posting. + +When you *Share* the event it will be posted to your wall with the access permissions you've selected. +But before you do, you can also *preview* the event in a pop-up box. + +### Addons + +#### OpenStreetMap + +If this addon is activated on you friendica node, the content of the location field will be mathced with the identification service of OSM when you submit the event. +Should OSM find anything matching, a map for the location will be embedded automatically at the end of the events view. diff --git a/doc/network.md b/doc/network.md index 08ddca290..36be159ea 100644 --- a/doc/network.md +++ b/doc/network.md @@ -1,6 +1,11 @@ -This is your Network Tab. If you get lost, you can click this link to bring yourself back here. +This is your Network Tab. +If you get lost, you can click this link to bring yourself back here. -This is a bit like the Newsfeed at Facebook or the Stream at Diaspora. It's where all the posts from your contacts, groups, and feeds will appear. If you're new, you won't see anything in this page, unless you posted your status in the last step. If you've already added a few friends, you'll be able to see their posts. Here, you can comment, like, or dislike posts, or click on somebody's name to visit their profile page where you can write on their wall. +This is a bit like the Newsfeed at Facebook or the Stream at Diaspora. +It's where all the posts from your contacts, groups, and feeds will appear. +If you're new, you won't see anything in this page, unless you posted your status in the last step. +If you've already added a few friends, you'll be able to see their posts. +Here, you can comment, like, or dislike posts, or click on somebody's name to visit their profile page where you can write on their wall. Now we need to fill it up, the first step, is to make some new friends. diff --git a/doc/readme.md b/doc/readme.md index 6020f2c01..a5eeb5451 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -29,11 +29,11 @@ Friendica Documentation and Resources * [Install](help/Install) * [Settings](help/Settings) * [Plugins](help/Plugins) -* [Installing Connectors (Facebook/Twitter/StatusNet)](help/Installing-Connectors) +* [Installing Connectors (Twitter/GNU Social)](help/Installing-Connectors) * [Message Flow](help/Message-Flow) * [Using SSL with Friendica](help/SSL) * [Developers](help/Developers) -* [Twitter/StatusNet API Functions](help/api) +* [Twitter/GNU Social API Functions](help/api) * [Translation of Friendica](help/translations) diff --git a/doc/themes.md b/doc/themes.md new file mode 100644 index 000000000..add44c776 --- /dev/null +++ b/doc/themes.md @@ -0,0 +1,291 @@ +# Themes + +* [Home](help) + +To change the look of friendica you have to touch the themes. +The current default theme is [duepunto zero](https://github.com/friendica/friendica/tree/master/view/theme/duepuntozero) but there are numerous others. +Have a look at [friendica-themes.com](http://friendica-themes.com) for an overview of the existing themes. +In case none of them suits your needs, there are several ways to change a theme. +If you need help theming, there is a forum @[ftdevs@friendica.eu](https://friendica.eu/profile/ftdevs) where you can ask theme specific questions and present your themes. + +So, how to work on the UI of friendica. + +You can either directly edit an existing theme. +But you might loose your changes when the theme is updated by the friendica team. + +If you are almost happy with an existing theme, the easiest way to cover your needs is to create a new theme, inheritating most of the properties of the parent theme and change just minor stuff. +The below for a more detailed description of theme heritage. + +Some themes also allow users to select *variants* of the theme. +Those theme variants most often contain an additional [CSS](https://en.wikipedia.org/wiki/CSS) file to override some styling of the default theme values. +From the themes in the main repository *duepunto zero* and *vier* are using this methods for variations. +Quattro is using a slightly different approach. + +Third you can start your theme from scratch. +Which is the most complex way to change friendicas look. +But it leaves you the most freedom. +So below for a *detailed* description and the meaning of some special files. + +### Styling + +If you want to change the styling of a theme, have a look at the themes CSS file. +In most cases, you can found these in + + /view/theme/**your-theme-name**/style.css + +sometimes, there is also a file called style.php in the theme directory. +This is only needed if the theme allowes the user to change certain things of the theme dynamically. +Say the font size or set a background image. + +### Templates + +If you want to change the structure of the theme, you need to change the templates used by the theme. +Friendica themes are using [SMARTY3](http://www.smarty.net/) for templating. +The default template can be found in + + /view/templates + +if you want to override any template within your theme create your version of the template in + + /view/theme/**your-theme-name**/templates + +any template that exists there will be used instead of the default one. + +### Javascript + +The same rule applies to the JavaScript files found in + + /js + +they will be overwritten by files in + + /view/theme/**your-theme-name**/js. + +## Expand an existing Theme + +### Theme Variations + +Many themes are more *theme families* then only one theme. +*duepunto zero* and *vier* allow easily to add new theme variation. +We will go through the process of creating a new variation for *duepunto zero*. +The same (well almost, some names change) procedure applies to the *vier* theme. +And similar steps are needed for *quattro* but this theme is using [lessc](http://lesscss.org/#docs) to maintaine the CSS files.. + +In + + /view/theme/duepuntozero/deriv + +you find a couple of CSS files that define color derivations from the duepunto theme. +These resemble some of the now as unsupported marked themes, that were inherited by the duepunto theme. +Darkzero and Easter Bunny for example. + +The selection of the colorset is done in a combination of a template for a new form in the settings and aome functions in the theme.php file. +The template (theme_settings.tpl) + + {{include file="field_select.tpl" field=$colorset}} +
+ +
+ +defines a formular consisting of a [select](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) pull-down which contains all aviable variants and s submit button. +See the documentation about [SMARTY3 templates](/help/snarty3-templates.md) for a summary of friendica specific blocks other then the select element. +But we don't really need to change anything at the template itself. + +The template alone wont work though. +You make friendica aware of its existance and tell it how to use the template file, by defining a config.php file. +It needs to define at lest the following functions + +* theme_content +* theme_post + +and may also define functions for the admin interface + +* theme_admin +* theme_admin_post. + +theme_content and theme_admin are used to make the form available in the settings, repectively the admin panel. +The _post functions handle the processing of the send form, in this case they save to selected variand in friendicas database. + +To make your own variation appear in the menu, all you need to do is to create a new CSS file in the deriv directoy and include it in the array in the config.php: + + $colorset = array( + 'default'=>t('default'), + 'greenzero'=>t('greenzero'), + 'purplezero'=>t('purplezero'), + 'easterbunny'=>t('easterbunny'), + 'darkzero'=>t('darkzero'), + 'comix'=>t('comix'), + 'slackr'=>t('slackr'), + ); + +the 1st part of the line is the name of the CSS file (without the .css) the 2nd part is the common name of the variant. +Calling the t() function with the common name makes the string translateable. +The selected 1st part will be saved in the database by the theme_post function. + + function theme_post(&$a){ + // non local users shall not pass + if(! local_user()) + return; + // if the one specific submit button was pressed then proceed + if (isset($_POST['duepuntozero-settings-submit'])){ + // and save the selection key into the personal config of the user + set_pconfig(local_user(), 'duepuntozero', 'colorset', $_POST['duepuntozero_colorset']); + } + } + +Now that this information is set in the database, what should friendica do with it? +For this, have a look at the theme.php file of the *duepunto zero*. +There you'll find somethink alike + + $colorset = get_pconfig( local_user(), 'duepuntozero','colorset'); + if (!$colorset) + $colorset = get_config('duepuntozero', 'colorset'); + if ($colorset) { + if ($colorset == 'greenzero') + $a->page['htmlhead'] .= ''."\n"; + /* some more variants */ + } + +which tells friendica to get the personal config of a user. +Check if it is set and if not look for the global config. +And finally if a config for the colorset was found, apply it by adding a link to the CSS file into the HTML header of the page. +So you'll just need to add a if selection, fitting your variant keyword and link to the CSS file of it. + +Done. +Now you can use the variant on your system. +But remember once the theme.php or the config.php you have to readd your variant to them. +If you think your color variation could be benifical for other friendica users as well, feel free to generate a pull request at github so we can include your work into the repository. + +### Inheritation + +Say, you like the duepuntozero but you want to have the content of the outer columns left and right exchanged. +That would be not a color variation as shown above. +Instead we will create a new theme, duepuntozero_lr, inherit the properties of duepuntozero and make small changes to the underlying php files. + +So create a directory called duepunto_lr and create a file called theme.php with your favorite text editor. +The content of this file should be something like + + theme_info = array( + 'extends' => 'duepuntozero'. + ); + set_template_engine($a, 'smarty3'); + /* and more stuff e.g. the JavaScript function for the header */ + } + +Next take the default.php file found in the /view direcotry and exchange the aside and right_aside elements. +So the central part of the file now looks like this: + + + + +
+ +
+ +
+ + +Finally we need a style.css file, inheriting the definitions from the parent theme and containing out changes for the new theme. +***Note***:You need to create the style.css and at lest import the base CSS file from the parent theme. + + @import url('../duepuntozero/style.css'); + +Done. +But I agree it is not really useful at this state. +Nevertheless, to use it, you just need to activate in the admin panel. +That done, you can select it in the settings like any other activated theme. + +## Creating a Theme from Scratch + +Keep patient. +Basically what you have to do is identifying which template you have to change so it looks more like what you want. +Adopt the CSS of the theme accordingly. +And iterate the process until you have the theme the way you want it. + +*Use the source Luke.* and don't hesitate to ask in @[ftdevs](https://friendica.eu/profile/ftdevs) or @[helpers](https://helpers.pyxis.uberspace.de/profile/helpers). + +## Special Files + +### unsupported + +If a file with this name (which might be empty) exists in the theme directory, the theme is marked as *unsupported*. +An unsupported theme may not be selected by a user in the settings. +Users who are already using it wont notice anything. + +### README(.md) + +The contents of this file, with or without the .md which indicates [Markdown](https://daringfireball.net/projects/markdown/) syntax, will be displayed at most repository hosting services and in the theme page within the admin panel of friendica. + +This file should contain information you want to let others know about your theme. + +### screenshot.[png|jpg] + +If you want to have a preview image of your theme displayed in the settings you should take a screenshot and save it with this name. +Supported formats are PNG and JPEG. + +### theme.php + +This is the main definition file of the theme. +In the header of that file, some meta information are stored. +For example, have a look at the theme.php of the *quattro* theme: + + + * Maintainer: Fabio + * Maintainer: Tobias + */ + +You see the definition of the themes name, it's version and the initial author of the theme. +These three information should be listed. +If the original author is not anymore working on the theme, but a maintainer has taken over, the maintainer should be listed as well. +The information from the theme header will be displayed in the admin panelö. + +Next crucial part of the theme.php file is a definition of an init function. +The name of the function is _init. +So in the case of quattro it is + + function quattro_init(&$a) { + $a->theme_info = array(); + set_template_engine($a, 'smarty3'); + } + +Here we have set the basic theme information, in this case they are empthy. +But the array needs to be set. +And we have set the template engine that should be used by friendica for this theme. +At the moment you should use the *smarty3* engine. +There once was a friendica specific templating engine as well but that is not used anymore. +If you like to use another templating engine, please implement it. + +When you want to inherit stuff from another theme you have to *announce* this in the theme_info: + + $a->theme_info = array( + 'extends' => 'duepuntozero', + ); + +which declares *duepuntozero* as parent of the theme. + +If you want to add something to the HTML header of the theme, one way to do so is by adding it to the theme.php file. +To do so, add something alike + + $a->page['htmlhead'] .= <<< EOT + /* stuff you want to add to the header */ + EOT; + +The $a variable holds the friendica application. +So you can access the properties of this friendica session from the theme.php file as well. + +### default.php + +This file covers the structure of the underlying HTML layout. +The default file is in + + /view/default.php + +if you want to change it, say adding a 4th column for banners of your favourite FLOSS projects, place a new default.php file in your theme directory. +As with the theme.php file, you can use the properties of the $a variable with holds the friendica application to decide what content is displayed. \ No newline at end of file diff --git a/htconfig.php b/htconfig.php index 4208924cf..508de9a32 100644 --- a/htconfig.php +++ b/htconfig.php @@ -1,12 +1,12 @@ config['sitename'] = "Friendica Social Network"; // Your choices are REGISTER_OPEN, REGISTER_APPROVE, or REGISTER_CLOSED. -// Be certain to create your own personal account before setting -// REGISTER_CLOSED. 'register_text' (if set) will be displayed prominently on +// Be certain to create your own personal account before setting +// REGISTER_CLOSED. 'register_text' (if set) will be displayed prominently on // the registration page. REGISTER_APPROVE requires you set 'admin_email' // to the email address of an already registered person who can authorise -// and/or approve/deny the request. +// and/or approve/deny the request. // In order to perform system administration via the admin panel, admin_email // must precisely match the email address of the person logged in. @@ -51,22 +51,10 @@ $a->config['system']['maximagesize'] = 800000; $a->config['php_path'] = 'php'; -// You shouldn't need to change anything else. -// Location of global directory submission page. - -$a->config['system']['directory_submit_url'] = 'http://dir.friendica.com/submit'; -$a->config['system']['directory_search_url'] = 'http://dir.friendica.com/directory?search='; - // PuSH - aka pubsubhubbub URL. This makes delivery of public posts as fast as private posts $a->config['system']['huburl'] = '[internal]'; -// Server-to-server private message encryption (RINO) is allowed by default. -// Encryption will only be provided if this setting is true and the -// PHP mcrypt extension is installed on both systems - -$a->config['system']['rino_encrypt'] = true; - // allowed themes (change this from admin panel after installation) $a->config['system']['allowed_themes'] = 'dispy,quattro,vier,darkzero,duepuntozero,greenzero,purplezero,slackr,diabook'; @@ -80,17 +68,8 @@ $a->config['system']['theme'] = 'duepuntozero'; $a->config['system']['no_regfullname'] = true; -// If set to true the priority settings of ostatus contacts are used -$a->config['system']['ostatus_use_priority'] = false; +//Deny public access to the local directory +//$a->config['system']['block_local_dir'] = false; -// If enabled, all items are cached in the given directory -$a->config['system']['itemcache'] = ""; - -// If enabled, the lockpath is used for a lockfile to check if the poller is running -$a->config['system']['lockpath'] = ""; - -// If enabled, the MyBB fulltext engine is used -// $a->config['system']['use_fulltext_engine'] = true; - -// Use the old style "share" -// $a->config['system']['old_share'] = false; +// Location of the global directory +$a->config['system']['directory'] = 'http://dir.friendi.ca'; diff --git a/include/Contact.php b/include/Contact.php index 633936025..920ec3c2b 100644 --- a/include/Contact.php +++ b/include/Contact.php @@ -113,7 +113,7 @@ function terminate_friendship($user,$self,$contact) { '$photo' => $self['photo'], '$thumb' => $self['thumb'], '$published' => datetime_convert('UTC','UTC', 'now', ATOM_TIME), - '$item_id' => 'urn:X-dfrn:' . $a->get_hostname() . ':unfollow:' . random_string(), + '$item_id' => 'urn:X-dfrn:' . $a->get_hostname() . ':unfollow:' . get_guid(32), '$title' => '', '$type' => 'text', '$content' => t('stopped following'), @@ -267,7 +267,12 @@ function contact_photo_menu($contact) { function random_profile() { - $r = q("select url from gcontact where url like '%%://%%/profile/%%' order by rand() limit 1"); + $r = q("SELECT `url` FROM `gcontact` WHERE `network` = '%s' + AND `last_contact` >= `last_failure` + AND `updated` > UTC_TIMESTAMP - INTERVAL 1 MONTH + ORDER BY rand() LIMIT 1", + dbesc(NETWORK_DFRN)); + if(count($r)) return dirname($r[0]['url']); return ''; @@ -330,7 +335,8 @@ function get_contact($url, $uid = 0) { if (!$update_photo) return($contactid); - } + } elseif ($uid != 0) + return 0; if (!count($data)) $data = probe_url($url); diff --git a/include/Photo.php b/include/Photo.php index ccb6af29e..785601c7e 100644 --- a/include/Photo.php +++ b/include/Photo.php @@ -345,6 +345,24 @@ class Photo { } public function orient($filename) { + if ($this->is_imagick()) { + // based off comment on http://php.net/manual/en/imagick.getimageorientation.php + $orientation = $this->image->getImageOrientation(); + switch ($orientation) { + case imagick::ORIENTATION_BOTTOMRIGHT: + $this->image->rotateimage("#000", 180); + break; + case imagick::ORIENTATION_RIGHTTOP: + $this->image->rotateimage("#000", 90); + break; + case imagick::ORIENTATION_LEFTBOTTOM: + $this->image->rotateimage("#000", -90); + break; + } + + $this->image->setImageOrientation(imagick::ORIENTATION_TOPLEFT); + return TRUE; + } // based off comment on http://php.net/manual/en/function.imagerotate.php if(!$this->is_valid()) @@ -995,3 +1013,4 @@ function store_photo($a, $uid, $imagedata = "", $url = "") { return($image); } + diff --git a/include/Scrape.php b/include/Scrape.php index ce18bb103..93d68be22 100644 --- a/include/Scrape.php +++ b/include/Scrape.php @@ -249,7 +249,7 @@ function scrape_feed($url) { $ret['feed_atom'] = $url; return $ret; } - if(stristr($line,'application/rss+xml') || stristr($s,'loadHTML($s); + $xpath = new DomXPath($doc); + + $base = $xpath->query("//base"); + foreach ($base as $node) { + $attr = array(); + + if ($node->attributes->length) + foreach ($node->attributes as $attribute) + $attr[$attribute->name] = $attribute->value; + + if ($attr["href"] != "") + $basename = $attr["href"] ; } - if(! $dom) { - logger('scrape_feed: failed to parse.'); - return $ret; + $list = $xpath->query("//link"); + foreach ($list as $node) { + $attr = array(); + + if ($node->attributes->length) + foreach ($node->attributes as $attribute) + $attr[$attribute->name] = $attribute->value; + + if (($attr["rel"] == "alternate") AND ($attr["type"] == "application/atom+xml")) + $ret["feed_atom"] = $attr["href"]; + + if (($attr["rel"] == "alternate") AND ($attr["type"] == "application/rss+xml")) + $ret["feed_rss"] = $attr["href"]; } - - $head = $dom->getElementsByTagName('base'); - if($head) { - foreach($head as $head0) { - $basename = $head0->getAttribute('href'); - break; - } - } - if(! $basename) - $basename = implode('/', array_slice(explode('/',$url),0,3)) . '/'; - - $items = $dom->getElementsByTagName('link'); - - // get Atom/RSS link elements, take the first one of either. - - if($items) { - foreach($items as $item) { - $x = $item->getAttribute('rel'); - if(($x === 'alternate') && ($item->getAttribute('type') === 'application/atom+xml')) { - if(! x($ret,'feed_atom')) - $ret['feed_atom'] = $item->getAttribute('href'); - } - if(($x === 'alternate') && ($item->getAttribute('type') === 'application/rss+xml')) { - if(! x($ret,'feed_rss')) - $ret['feed_rss'] = $item->getAttribute('href'); - } - } - } - - // Drupal and perhaps others only provide relative URL's. Turn them into absolute. + // Drupal and perhaps others only provide relative URLs. Turn them into absolute. if(x($ret,'feed_atom') && (! strstr($ret['feed_atom'],'://'))) $ret['feed_atom'] = $basename . $ret['feed_atom']; @@ -335,7 +328,7 @@ function scrape_feed($url) { define ( 'PROBE_NORMAL', 0); define ( 'PROBE_DIASPORA', 1); -function probe_url($url, $mode = PROBE_NORMAL) { +function probe_url($url, $mode = PROBE_NORMAL, $level = 1) { require_once('include/email.php'); $result = array(); @@ -509,6 +502,7 @@ function probe_url($url, $mode = PROBE_NORMAL) { } if($mode == PROBE_NORMAL) { + if(strlen($zot)) { $s = fetch_url($zot); if($s) { @@ -528,6 +522,7 @@ function probe_url($url, $mode = PROBE_NORMAL) { } } + if(strlen($dfrn)) { $ret = scrape_dfrn(($hcard) ? $hcard : $dfrn); if(is_array($ret) && x($ret,'dfrn-request')) { @@ -634,6 +629,7 @@ function probe_url($url, $mode = PROBE_NORMAL) { if($check_feed) { $feedret = scrape_feed(($poll) ? $poll : $url); + logger('probe_url: scrape_feed ' . (($poll)? $poll : $url) . ' returns: ' . print_r($feedret,true), LOGGER_DATA); if(count($feedret) && ($feedret['feed_atom'] || $feedret['feed_rss'])) { $poll = ((x($feedret,'feed_atom')) ? unamp($feedret['feed_atom']) : unamp($feedret['feed_rss'])); @@ -653,7 +649,7 @@ function probe_url($url, $mode = PROBE_NORMAL) { logger('probe_url: scrape_feed: headers: ' . $a->get_curl_headers(), LOGGER_DATA); // Don't try and parse an empty string - $feed->set_raw_data(($xml) ? $xml : ''); + $feed->set_raw_data(($xml) ? $xml : ''); $feed->init(); if($feed->error()) @@ -670,6 +666,7 @@ function probe_url($url, $mode = PROBE_NORMAL) { $vcard['fn'] = trim(unxmlify($author->get_email())); if(strpos($vcard['fn'],'@') !== false) $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@')); + $email = unxmlify($author->get_email()); if(! $profile && $author->get_link()) $profile = trim(unxmlify($author->get_link())); @@ -681,6 +678,15 @@ function probe_url($url, $mode = PROBE_NORMAL) { $vcard['photo'] = $elems['link'][0]['attribs']['']['href']; } } + // Fetch fullname via poco:displayName + $pocotags = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); + if ($pocotags) { + $elems = $pocotags[0]['child']['http://portablecontacts.net/spec/1.0']; + if (isset($elems["displayName"])) + $vcard['fn'] = $elems["displayName"][0]["data"]; + if (isset($elems["preferredUsername"])) + $vcard['nick'] = $elems["preferredUsername"][0]["data"]; + } } else { $item = $feed->get_item(0); @@ -757,18 +763,18 @@ function probe_url($url, $mode = PROBE_NORMAL) { $vcard['fn'] = $url; if (($notify != "") AND ($poll != "")) { - $baseurl = matching($notify, $poll); + $baseurl = matching(normalise_link($notify), normalise_link($poll)); - $baseurl2 = matching($baseurl, $profile); + $baseurl2 = matching($baseurl, normalise_link($profile)); if ($baseurl2 != "") $baseurl = $baseurl2; } if (($baseurl == "") AND ($notify != "")) - $baseurl = matching($profile, $notify); + $baseurl = matching(normalise_link($profile), normalise_link($notify)); if (($baseurl == "") AND ($poll != "")) - $baseurl = matching($profile, $poll); + $baseurl = matching(normalise_link($profile), normalise_link($poll)); $baseurl = rtrim($baseurl, "/"); @@ -794,16 +800,28 @@ function probe_url($url, $mode = PROBE_NORMAL) { logger('probe_url: ' . print_r($result,true), LOGGER_DEBUG); - // Trying if it maybe a diaspora account - if (($result['network'] == NETWORK_FEED) OR ($result['addr'] == "")) { - require_once('include/bbcode.php'); - $address = GetProfileUsername($url, "", true); - $result2 = probe_url($address, $mode); - if ($result2['network'] != "") - $result = $result2; + if ($level == 1) { + // Trying if it maybe a diaspora account + if (($result['network'] == NETWORK_FEED) OR ($result['addr'] == "")) { + require_once('include/bbcode.php'); + $address = GetProfileUsername($url, "", true); + $result2 = probe_url($address, $mode, ++$level); + if ($result2['network'] != "") + $result = $result2; + } + + // Maybe it's some non standard GNU Social installation (Single user, subfolder or no uri rewrite) + if (($result['network'] == NETWORK_FEED) AND ($result['baseurl'] != "") AND ($result['nick'] != "")) { + $addr = $result['nick'].'@'.str_replace("http://", "", $result['baseurl']); + $result2 = probe_url($addr, $mode, ++$level); + if (($result2['network'] != "") AND ($result2['network'] != NETWORK_FEED)) + $result = $result2; + } } - Cache::set("probe_url:".$mode.":".$url,serialize($result)); + // Only store into the cache if the value seems to be valid + if ($result['network'] != NETWORK_FEED) + Cache::set("probe_url:".$mode.":".$url,serialize($result), CACHE_DAY); return $result; } diff --git a/include/acl_selectors.php b/include/acl_selectors.php index 668544b0b..f628b9730 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -78,19 +78,19 @@ function contact_selector($selname, $selclass, $preselected = false, $options) { if(x($options,'networks')) { switch($options['networks']) { case 'DFRN_ONLY': - $networks = array('dfrn'); + $networks = array(NETWORK_DFRN); break; case 'PRIVATE': if(is_array($a->user) && $a->user['prvnets']) - $networks = array('dfrn','mail','dspr'); + $networks = array(NETWORK_DFRN,NETWORK_MAIL,NETWORK_DIASPORA); else - $networks = array('dfrn','face','mail', 'dspr'); + $networks = array(NETWORK_DFRN,NETWORK_FACEBOOK,NETWORK_MAIL, NETWORK_DIASPORA); break; case 'TWO_WAY': if(is_array($a->user) && $a->user['prvnets']) - $networks = array('dfrn','mail','dspr'); + $networks = array(NETWORK_DFRN,NETWORK_MAIL,NETWORK_DIASPORA); else - $networks = array('dfrn','face','mail','dspr','stat'); + $networks = array(NETWORK_DFRN,NETWORK_FACEBOOK,NETWORK_MAIL,NETWORK_DIASPORA,NETWORK_OSTATUS); break; default: break; @@ -181,17 +181,23 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p $sql_extra .= sprintf(" AND `rel` = %d ", intval(CONTACT_IS_FRIEND)); } - if($privmail) { - $sql_extra .= " AND `network` IN ( 'dfrn', 'dspr' ) "; - } - elseif($privatenet) { - $sql_extra .= " AND `network` IN ( 'dfrn', 'mail', 'face', 'dspr' ) "; - } + if($privmail) + $sql_extra .= sprintf(" AND `network` IN ('%s' , '%s') ", + NETWORK_DFRN, NETWORK_DIASPORA); + elseif($privatenet) + $sql_extra .= sprintf(" AND `network` IN ('%s' , '%s', '%s', '%s') ", + NETWORK_DFRN, NETWORK_MAIL, NETWORK_FACEBOOK, NETWORK_DIASPORA); $tabindex = ($tabindex > 0 ? "tabindex=\"$tabindex\"" : ""); + if ($privmail AND $preselected) { + $sql_extra .= " AND `id` IN (".implode(",", $preselected).")"; + $hidepreselected = ' style="display: none;"'; + } else + $hidepreselected = ""; + if($privmail) - $o .= "\r\n"; else $o .= "\r\n"; + if ($privmail AND $preselected) + $o .= implode(", ", $receiverlist); + call_hooks($a->module . '_post_' . $selname, $o); return $o; @@ -283,62 +296,57 @@ function get_acl_permissions($user = null) { } -function populate_acl($user = null,$celeb = false) { +function populate_acl($user = null, $show_jotnets = false) { $perms = get_acl_permissions($user); - // We shouldn't need to prune deadguys from the block list. Either way they can't get the message. - // Also no point enumerating groups and checking them, that will take place on delivery. + $jotnets = ''; + if($show_jotnets) { + $mail_disabled = ((function_exists('imap_open') && (! get_config('system','imap_disabled'))) ? 0 : 1); -// $deny_cid = prune_deadguys($deny_cid); + $mail_enabled = false; + $pubmail_enabled = false; + if(! $mail_disabled) { + $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d AND `server` != '' LIMIT 1", + intval(local_user()) + ); + if(count($r)) { + $mail_enabled = true; + if(intval($r[0]['pubmail'])) + $pubmail_enabled = true; + } + } - /*$o = ''; - $o .= '
'; - $o .= '
'; - $o .= '
' . t('Visible To:') . '
' . t('everybody') . '
'; - $o .= '
'; - $o .= '
'; - $o .= '
'; - $o .= ''; - $o .= group_select('group_allow','group_allow',$allow_gid); - $o .= '
'; - $o .= '
'; - $o .= ''; - $o .= contact_select('contact_allow','contact_allow',$allow_cid,4,false,$celeb,true); - $o .= '
'; - $o .= '
' . "\r\n"; - $o .= '
' . "\r\n"; - $o .= '
'; - $o .= '
'; - $o .= '
' . t('Except For:') . '
'; - $o .= '
'; - $o .= '
'; - $o .= '
'; - $o .= ''; - $o .= group_select('group_deny','group_deny', $deny_gid); - $o .= '
'; - $o .= '
'; - $o .= ''; - $o .= contact_select('contact_deny','contact_deny', $deny_cid,4,false, $celeb,true); - $o .= '
'; - $o .= '
' . "\r\n"; - $o .= '
' . "\r\n"; - $o .= '
'; - $o .= '
' . "\r\n"; - $o .= '
' . "\r\n";*/ + if (!$user['hidewall']) { + if($mail_enabled) { + $selected = (($pubmail_enabled) ? ' checked="checked" ' : ''); + $jotnets .= '
' . t("Post to Email") . '
'; + } + + call_hooks('jot_networks', $jotnets); + } else + $jotnets .= sprintf(t('Connectors disabled, since "%s" is enabled.'), + t('Hide your profile details from unknown viewers?')); + } $tpl = get_markup_template("acl_selector.tpl"); $o = replace_macros($tpl, array( '$showall'=> t("Visible to everybody"), - '$show' => t("show"), - '$hide' => t("don't show"), + '$show' => t("show"), + '$hide' => t("don't show"), '$allowcid' => json_encode($perms['allow_cid']), '$allowgid' => json_encode($perms['allow_gid']), '$denycid' => json_encode($perms['deny_cid']), '$denygid' => json_encode($perms['deny_gid']), + '$networks' => $show_jotnets, + '$emailcc' => t('CC: email addresses'), + '$emtitle' => t('Example: bob@example.com, mary@example.com'), + '$jotnets' => $jotnets, + '$aclModalTitle' => t('Permissions'), + '$aclModalDismiss' => t('Close'), '$features' => array( - "aclautomention"=>(feature_enabled($user['uid'],"aclautomention")?"true":"false") + "aclautomention"=>(feature_enabled($user['uid'],"aclautomention")?"true":"false") ), )); @@ -399,6 +407,7 @@ function acl_lookup(&$a, $out_type = 'json') { $search = $_REQUEST['query']; } +// logger("Searching for ".$search." - type ".$type, LOGGER_DEBUG); if ($search!=""){ $sql_extra = "AND `name` LIKE '%%".dbesc($search)."%%'"; @@ -496,9 +505,11 @@ function acl_lookup(&$a, $out_type = 'json') { $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, forum FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 AND `archive` = 0 AND `notify` != '' + AND NOT (`network` IN ('%s', '%s')) $sql_extra2 ORDER BY `name` ASC ", - intval(local_user()) + intval(local_user()), + dbesc(NETWORK_OSTATUS), dbesc(NETWORK_STATUSNET) ); } elseif($type == 'm') { diff --git a/include/api.php b/include/api.php index 600cc0c3d..2fd7d6d45 100644 --- a/include/api.php +++ b/include/api.php @@ -7,6 +7,17 @@ require_once("include/conversation.php"); require_once("include/oauth.php"); require_once("include/html2plain.php"); + require_once("mod/share.php"); + require_once("include/Photo.php"); + require_once("mod/item.php"); + require_once('include/security.php'); + require_once('include/contact_selectors.php'); + require_once('include/html2bbcode.php'); + require_once('mod/wall_upload.php'); + require_once("mod/proxy.php"); + require_once("include/message.php"); + + /* * Twitter-Like API * @@ -99,6 +110,9 @@ $password = $_SERVER['PHP_AUTH_PW']; $encrypted = hash('whirlpool',trim($password)); + // allow "user@server" login (but ignore 'server' part) + $at=strstr($user, "@", true); + if ( $at ) $user=$at; /** * next code from mod/auth.php. needs better solution @@ -106,7 +120,7 @@ $record = null; $addon_auth = array( - 'username' => trim($user), + 'username' => trim($user), 'password' => trim($password), 'authenticated' => 0, 'user_record' => null @@ -145,7 +159,6 @@ die('This api requires login'); } - require_once('include/security.php'); authenticate_success($record); $_SESSION["allow_api"] = true; call_hooks('logged_in', $a->user); @@ -179,7 +192,11 @@ if (strpos($a->query_string, ".atom")>0) $type="atom"; if (strpos($a->query_string, ".as")>0) $type="as"; + $stamp = microtime(true); $r = call_user_func($info['func'], $a, $type); + $duration = (float)(microtime(true)-$stamp); + logger("API call duration: ".round($duration, 2)."\t".$a->query_string, LOGGER_DEBUG); + if ($r===false) return; switch($type){ @@ -389,19 +406,27 @@ 'screen_name' => (($r[0]['nick']) ? $r[0]['nick'] : $r[0]['name']), 'location' => NULL, 'description' => NULL, - 'profile_image_url' => $r[0]["avatar"], - 'profile_image_url_https' => $r[0]["avatar"], 'url' => $r[0]["url"], 'protected' => false, 'followers_count' => 0, 'friends_count' => 0, + 'listed_count' => 0, 'created_at' => api_date(0), 'favourites_count' => 0, 'utc_offset' => 0, 'time_zone' => 'UTC', - 'statuses_count' => 0, - 'following' => false, + 'geo_enabled' => false, 'verified' => false, + 'statuses_count' => 0, + 'lang' => '', + 'contributors_enabled' => false, + 'is_translator' => false, + 'is_translation_enabled' => false, + 'profile_image_url' => $r[0]["avatar"], + 'profile_image_url_https' => $r[0]["avatar"], + 'following' => false, + 'follow_request_sent' => false, + 'notifications' => false, 'statusnet_blocking' => false, 'notifications' => false, 'statusnet_profile_url' => $r[0]["url"], @@ -490,8 +515,7 @@ $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1", dbesc(normalise_link($uinfo[0]['url']))); } - require_once('include/contact_selectors.php'); - $network_name = network_to_name($uinfo[0]['network']); + $network_name = network_to_name($uinfo[0]['network'], $uinfo[0]['url']); $ret = Array( 'id' => intval($r[0]['id']), @@ -672,10 +696,10 @@ $txt = requestdata('status'); //$txt = urldecode(requestdata('status')); - require_once('library/HTMLPurifier.auto.php'); - require_once('include/html2bbcode.php'); - if((strpos($txt,'<') !== false) || (strpos($txt,'>') !== false)) { + + require_once('library/HTMLPurifier.auto.php'); + $txt = html2bb_video($txt); $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.DefinitionImpl', null); @@ -687,12 +711,10 @@ $a->argv[1]=$user_info['screen_name']; //should be set to username? $_REQUEST['hush']='yeah'; //tell wall_upload function to return img info instead of echo - require_once('mod/wall_upload.php'); $bebop = wall_upload_post($a); //now that we have the img url in bbcode we can add it to the status and insert the wall item. $_REQUEST['body']=$txt."\n\n".$bebop; - require_once('mod/item.php'); item_post($a); // this should output the last post (the one we just posted). @@ -715,18 +737,16 @@ // logger('api_post: ' . print_r($_POST,true)); if(requestdata('htmlstatus')) { - require_once('library/HTMLPurifier.auto.php'); - require_once('include/html2bbcode.php'); - $txt = requestdata('htmlstatus'); if((strpos($txt,'<') !== false) || (strpos($txt,'>') !== false)) { + require_once('library/HTMLPurifier.auto.php'); + $txt = html2bb_video($txt); $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.DefinitionImpl', null); - $purifier = new HTMLPurifier($config); $txt = $purifier->purify($txt); @@ -739,6 +759,11 @@ $_REQUEST['title'] = requestdata('title'); $parent = requestdata('in_reply_to_status_id'); + + // Twidere sends "-1" if it is no reply ... + if ($parent == -1) + $parent = ""; + if(ctype_digit($parent)) $_REQUEST['parent'] = $parent; else @@ -815,12 +840,23 @@ if(x($_FILES,'media')) { // upload the image if we have one $_REQUEST['hush']='yeah'; //tell wall_upload function to return img info instead of echo - require_once('mod/wall_upload.php'); $media = wall_upload_post($a); if(strlen($media)>0) $_REQUEST['body'] .= "\n\n".$media; } + // To-Do: Multiple IDs + if (requestdata('media_ids')) { + $r = q("SELECT `resource-id`, `scale`, `nickname`, `type` FROM `photo` INNER JOIN `user` ON `user`.`uid` = `photo`.`uid` WHERE `resource-id` IN (SELECT `resource-id` FROM `photo` WHERE `id` = %d) AND `scale` > 0 AND `photo`.`uid` = %d ORDER BY `photo`.`width` DESC LIMIT 1", + intval(requestdata('media_ids')), api_user()); + if ($r) { + $phototypes = Photo::supportedTypes(); + $ext = $phototypes[$r[0]['type']]; + $_REQUEST['body'] .= "\n\n".'[url='.$a->get_baseurl().'/photos/'.$r[0]['nickname'].'/image/'.$r[0]['resource-id'].']'; + $_REQUEST['body'] .= '[img]'.$a->get_baseurl()."/photo/".$r[0]['resource-id']."-".$r[0]['scale'].".".$ext."[/img][/url]"; + } + } + // set this so that the item_post() function is quiet and doesn't redirect or emit json $_REQUEST['api_source'] = true; @@ -830,7 +866,6 @@ // call out normal post function - require_once('mod/item.php'); item_post($a); // this should output the last post (the one we just posted). @@ -840,6 +875,40 @@ api_register_func('api/statuses/update_with_media','api_statuses_update', true); + function api_media_upload(&$a, $type) { + if (api_user()===false) { + logger('no user'); + return false; + } + + $user_info = api_get_user($a); + + if(!x($_FILES,'media')) { + // Output error + return false; + } + + $media = wall_upload_post($a, false); + if(!$media) { + // Output error + return false; + } + + $returndata = array(); + $returndata["media_id"] = $media["id"]; + $returndata["media_id_string"] = (string)$media["id"]; + $returndata["size"] = $media["size"]; + $returndata["image"] = array("w" => $media["width"], + "h" => $media["height"], + "image_type" => $media["type"]); + + logger("Media uploaded: ".print_r($returndata, true), LOGGER_DEBUG); + + return array("media" => $returndata); + } + + api_register_func('api/media/upload','api_media_upload', true); + function api_status_show(&$a, $type){ $user_info = api_get_user($a); @@ -901,20 +970,29 @@ $converted = api_convert_item($item); $status_info = array( - 'text' => $converted["text"], - 'truncated' => false, 'created_at' => api_date($lastwall['created']), - 'in_reply_to_status_id' => $in_reply_to_status_id, - 'in_reply_to_status_id_str' => $in_reply_to_status_id_str, - 'source' => (($lastwall['app']) ? $lastwall['app'] : 'web'), 'id' => intval($lastwall['id']), 'id_str' => (string) $lastwall['id'], + 'text' => $converted["text"], + 'source' => (($lastwall['app']) ? $lastwall['app'] : 'web'), + 'truncated' => false, + 'in_reply_to_status_id' => $in_reply_to_status_id, + 'in_reply_to_status_id_str' => $in_reply_to_status_id_str, 'in_reply_to_user_id' => $in_reply_to_user_id, 'in_reply_to_user_id_str' => $in_reply_to_user_id_str, 'in_reply_to_screen_name' => $in_reply_to_screen_name, - 'geo' => NULL, - 'favorited' => $lastwall['starred'] ? true : false, 'user' => $user_info, + 'geo' => NULL, + 'coordinates' => "", + 'place' => "", + 'contributors' => "", + 'is_quote_status' => false, + 'retweet_count' => 0, + 'favorite_count' => 0, + 'favorited' => $lastwall['starred'] ? true : false, + 'retweeted' => false, + 'possibly_sensitive' => false, + 'lang' => "", 'statusnet_html' => $converted["html"], 'statusnet_conversation_id' => $lastwall['parent'], ); @@ -926,9 +1004,9 @@ $status_info["entities"] = $converted["entities"]; if (($lastwall['item_network'] != "") AND ($status["source"] == 'web')) - $status_info["source"] = network_to_name($lastwall['item_network']); - elseif (($lastwall['item_network'] != "") AND (network_to_name($lastwall['item_network']) != $status_info["source"])) - $status_info["source"] = trim($status_info["source"].' ('.network_to_name($lastwall['item_network']).')'); + $status_info["source"] = network_to_name($lastwall['item_network'], $user_info['url']); + elseif (($lastwall['item_network'] != "") AND (network_to_name($lastwall['item_network'], $user_info['url']) != $status_info["source"])) + $status_info["source"] = trim($status_info["source"].' ('.network_to_name($lastwall['item_network'], $user_info['url']).')'); // "uid" and "self" are only needed for some internal stuff, so remove it from here unset($status_info["user"]["uid"]); @@ -1025,9 +1103,9 @@ $user_info["status"]["entities"] = $converted["entities"]; if (($lastwall['item_network'] != "") AND ($user_info["status"]["source"] == 'web')) - $user_info["status"]["source"] = network_to_name($lastwall['item_network']); - if (($lastwall['item_network'] != "") AND (network_to_name($lastwall['item_network']) != $user_info["status"]["source"])) - $user_info["status"]["source"] = trim($user_info["status"]["source"].' ('.network_to_name($lastwall['item_network']).')'); + $user_info["status"]["source"] = network_to_name($lastwall['item_network'], $user_info['url']); + if (($lastwall['item_network'] != "") AND (network_to_name($lastwall['item_network'], $user_info['url']) != $user_info["status"]["source"])) + $user_info["status"]["source"] = trim($user_info["status"]["source"].' ('.network_to_name($lastwall['item_network'], $user_info['url']).')'); } @@ -1109,7 +1187,7 @@ if ($conversation_id > 0) $sql_extra .= ' AND `item`.`parent` = '.intval($conversation_id); - $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, + $r = q("SELECT STRAIGHT_JOIN `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` @@ -1136,7 +1214,8 @@ $idlist = implode(",", $idarray); - $r = q("UPDATE `item` SET `unseen` = 0 WHERE `unseen` AND `id` IN (%s)", $idlist); + if ($idlist != "") + $r = q("UPDATE `item` SET `unseen` = 0 WHERE `unseen` AND `id` IN (%s)", $idlist); $data = array('$statuses' => $ret); @@ -1315,6 +1394,10 @@ logger('API: api_conversation_show: '.$id); + $r = q("SELECT `parent` FROM `item` WHERE `id` = %d", intval($id)); + if ($r) + $id = $r[0]["parent"]; + $sql_extra = ''; if ($max_id > 0) @@ -1389,10 +1472,8 @@ $pos = strpos($r[0]['body'], "[share"); $post = substr($r[0]['body'], $pos); } else { - $post = "[share author='".str_replace("'", "'", $r[0]['author-name']). - "' profile='".$r[0]['author-link']. - "' avatar='".$r[0]['author-avatar']. - "' link='".$r[0]['plink']."']"; + $post = share_header($r[0]['author-name'], $r[0]['author-link'], $r[0]['author-avatar'], $r[0]['guid'], $r[0]['created'], $r[0]['plink']); + $post .= $r[0]['body']; $post .= "[/share]"; } @@ -1407,7 +1488,6 @@ if (!x($_REQUEST, "source")) $_REQUEST["source"] = api_source(); - require_once('mod/item.php'); item_post($a); } @@ -1439,7 +1519,6 @@ $ret = api_statuses_show($a, $type); - require_once('include/items.php'); drop_item($id, false); return($ret); @@ -1876,8 +1955,6 @@ if (!$ret) return false; - require_once("include/Photo.php"); - $attachments = array(); foreach ($images[1] AS $image) { @@ -1905,7 +1982,6 @@ $include_entities = strtolower(x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:"false"); if ($include_entities != "true") { - require_once("mod/proxy.php"); preg_match_all("/\[img](.*?)\[\/img\]/ism", $bbcode, $images); @@ -2003,13 +2079,11 @@ $start = iconv_strpos($text, $url, $offset, "UTF-8"); if (!($start === false)) { - require_once("include/Photo.php"); $image = get_photo_info($url); if ($image) { // If image cache is activated, then use the following sizes: // thumb (150), small (340), medium (600) and large (1024) if (!get_config("system", "proxy_disabled")) { - require_once("mod/proxy.php"); $media_url = proxy_url($url); $sizes = array(); @@ -2144,9 +2218,9 @@ $status["entities"] = $converted["entities"]; if (($item['item_network'] != "") AND ($status["source"] == 'web')) - $status["source"] = network_to_name($item['item_network']); - else if (($item['item_network'] != "") AND (network_to_name($item['item_network']) != $status["source"])) - $status["source"] = trim($status["source"].' ('.network_to_name($item['item_network']).')'); + $status["source"] = network_to_name($item['item_network'], $user_info['url']); + else if (($item['item_network'] != "") AND (network_to_name($item['item_network'], $user_info['url']) != $status["source"])) + $status["source"] = trim($status["source"].' ('.network_to_name($item['item_network'], $user_info['url']).')'); // Retweets are only valid for top postings @@ -2166,9 +2240,14 @@ unset($status["user"]["uid"]); unset($status["user"]["self"]); - // 'geo' => array('type' => 'Point', - // 'coordinates' => array((float) $notice->lat, - // (float) $notice->lon)); + if ($item["coord"] != "") { + $coords = explode(' ',$item["coord"]); + if (count($coords) == 2) { + $status["geo"] = array('type' => 'Point', + 'coordinates' => array((float) $coords[0], + (float) $coords[1])); + } + } $ret[] = $status; }; @@ -2396,8 +2475,6 @@ $sender = api_get_user($a); - require_once("include/message.php"); - if ($_POST['screen_name']) { $r = q("SELECT `id`, `nurl`, `network` FROM `contact` WHERE `uid`=%d AND `nick`='%s'", intval(api_user()), @@ -2620,6 +2697,70 @@ + /** + * similar as /mod/redir.php + * redirect to 'url' after dfrn auth + * + * why this when there is mod/redir.php already? + * This use api_user() and api_login() + * + * params + * c_url: url of remote contact to auth to + * url: string, url to redirect after auth + */ + function api_friendica_remoteauth(&$a) { + $url = ((x($_GET,'url')) ? $_GET['url'] : ''); + $c_url = ((x($_GET,'c_url')) ? $_GET['c_url'] : ''); + + if ($url === '' || $c_url === '') + die((api_error($a, 'json', "Wrong parameters"))); + + $c_url = normalise_link($c_url); + + // traditional DFRN + + $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `nurl` = '%s' LIMIT 1", + dbesc($c_url), + intval(api_user()) + ); + + if ((! count($r)) || ($r[0]['network'] !== NETWORK_DFRN)) + die((api_error($a, 'json', "Unknown contact"))); + + $cid = $r[0]['id']; + + $dfrn_id = $orig_id = (($r[0]['issued-id']) ? $r[0]['issued-id'] : $r[0]['dfrn-id']); + + if($r[0]['duplex'] && $r[0]['issued-id']) { + $orig_id = $r[0]['issued-id']; + $dfrn_id = '1:' . $orig_id; + } + if($r[0]['duplex'] && $r[0]['dfrn-id']) { + $orig_id = $r[0]['dfrn-id']; + $dfrn_id = '0:' . $orig_id; + } + + $sec = random_string(); + + q("INSERT INTO `profile_check` ( `uid`, `cid`, `dfrn_id`, `sec`, `expire`) + VALUES( %d, %s, '%s', '%s', %d )", + intval(api_user()), + intval($cid), + dbesc($dfrn_id), + dbesc($sec), + intval(time() + 45) + ); + + logger($r[0]['name'] . ' ' . $sec, LOGGER_DEBUG); + $dest = (($url) ? '&destination_url=' . $url : ''); + goaway ($r[0]['poll'] . '?dfrn_id=' . $dfrn_id + . '&dfrn_version=' . DFRN_PROTOCOL_VERSION + . '&type=profile&sec=' . $sec . $dest . $quiet ); + } + api_register_func('api/friendica/remoteauth', 'api_friendica_remoteauth', true); + + + function api_share_as_retweet(&$item) { $body = trim($item["body"]); @@ -2838,7 +2979,21 @@ function api_best_nickname(&$contacts) { $contacts = array($contacts[0]); } + /* +To.Do: + [pagename] => api/1.1/statuses/lookup.json + [id] => 605138389168451584 + [include_cards] => true + [cards_platform] => Android-12 + [include_entities] => true + [include_my_retweet] => 1 + [include_rts] => 1 + [include_reply_count] => true + [include_descendent_reply_count] => true + + + Not implemented by now: statuses/retweets_of_me friendships/create diff --git a/include/bb2diaspora.php b/include/bb2diaspora.php index 919bfc331..fc05e720f 100644 --- a/include/bb2diaspora.php +++ b/include/bb2diaspora.php @@ -34,6 +34,15 @@ function diaspora2bb($s) { $s = str_replace('#','#',$s); + $search = array(" \n", "\n "); + $replace = array("\n", "\n"); + do { + $oldtext = $s; + $s = str_replace($search, $replace, $s); + } while ($oldtext != $s); + + $s = str_replace("\n\n", "
", $s); + $s = html2bbcode($s); // protect the recycle symbol from turning into a tag, but without unescaping angles and naked ampersands @@ -95,6 +104,9 @@ function bb2diaspora($Text,$preserve_nl = false, $fordiaspora = true) { } else $Text = bbcode($Text, $preserve_nl, false, 4); + // mask some special HTML chars from conversation to markdown + $Text = str_replace(array('<','>','&'),array('&_lt_;','&_gt_;','&_amp_;'),$Text); + // If a link is followed by a quote then there should be a newline before it // Maybe we should make this newline at every time before a quote. $Text = str_replace(array("
"), array("
"), $Text); @@ -104,6 +116,9 @@ function bb2diaspora($Text,$preserve_nl = false, $fordiaspora = true) { // Now convert HTML to Markdown $Text = new HTML_To_Markdown($Text); + // unmask the special chars back to HTML + $Text = str_replace(array('&_lt_;','&_gt_;','&_amp_;'),array('<','>','&'),$Text); + $a->save_timestamp($stamp1, "parser"); // Libertree has a problem with escaped hashtags. @@ -137,22 +152,22 @@ function format_event_diaspora($ev) { $o .= '**' . (($ev['summary']) ? bb2diaspora($ev['summary']) : bb2diaspora($ev['desc'])) . '**' . "\n"; $o .= t('Starts:') . ' ' . '[' - . (($ev['adjust']) ? day_translate(datetime_convert('UTC', 'UTC', + . (($ev['adjust']) ? day_translate(datetime_convert('UTC', 'UTC', $ev['start'] , $bd_format )) - : day_translate(datetime_convert('UTC', 'UTC', + : day_translate(datetime_convert('UTC', 'UTC', $ev['start'] , $bd_format))) . '](' . $a->get_baseurl() . '/localtime/?f=&time=' . urlencode(datetime_convert('UTC','UTC',$ev['start'])) . ")\n"; if(! $ev['nofinish']) - $o .= t('Finishes:') . ' ' . '[' - . (($ev['adjust']) ? day_translate(datetime_convert('UTC', 'UTC', + $o .= t('Finishes:') . ' ' . '[' + . (($ev['adjust']) ? day_translate(datetime_convert('UTC', 'UTC', $ev['finish'] , $bd_format )) - : day_translate(datetime_convert('UTC', 'UTC', + : day_translate(datetime_convert('UTC', 'UTC', $ev['finish'] , $bd_format ))) . '](' . $a->get_baseurl() . '/localtime/?f=&time=' . urlencode(datetime_convert('UTC','UTC',$ev['finish'])) . ")\n"; if(strlen($ev['location'])) - $o .= t('Location:') . bb2diaspora($ev['location']) + $o .= t('Location:') . bb2diaspora($ev['location']) . "\n"; $o .= "\n"; diff --git a/include/bbcode.php b/include/bbcode.php index ef791d9e7..13061958c 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -1,10 +1,21 @@ ' . generate_map(str_replace('/',' ',$match[1])) . '', $match[0]); +} +function bb_map_location($match) { + // the extra space in the following line is intentional + return str_replace($match[0],'
' . generate_named_map($match[1]) . '
', $match[0]); +} + +function bb_attachment($Text, $simplehtml = false, $tryoembed = true) { $Text = preg_replace_callback("/(.*?)\[attachment(.*?)\](.*?)\[\/attachment\]/ism", - function ($match) use ($plaintext){ + function ($match) use ($simplehtml, $tryoembed){ $attributes = $match[2]; @@ -72,7 +83,10 @@ function bb_attachment($Text, $plaintext = false, $tryoembed = true) { $image = ""; } - if ($plaintext) + if ($simplehtml == 7) + $text = sprintf('%s', + $url, $title, $title); + elseif (($simplehtml != 4) AND ($simplehtml != 0)) $text = sprintf('%s
', $url, $title); else { $text = sprintf('', $type); @@ -83,14 +97,18 @@ function bb_attachment($Text, $plaintext = false, $tryoembed = true) { else $oembed = $bookmark[0]; - if (($image != "") AND !strstr(strtolower($oembed), "
', $url, $image, $title); - elseif (($preview != "") AND !strstr(strtolower($oembed), "
', $url, $preview, $title); + if (strstr(strtolower($oembed), "'; + return ''; } diff --git a/include/onepoll.php b/include/onepoll.php index 7b93a9a2f..1fc861afa 100644 --- a/include/onepoll.php +++ b/include/onepoll.php @@ -1,6 +1,7 @@ 1) && (intval($argv[1]))) $contact_id = intval($argv[1]); + if(($argc > 2) && ($argv[2] == "force")) + $force = true; + if(! $contact_id) { logger('onepoll: no contact'); return; @@ -78,24 +82,58 @@ function onepoll_run(&$argv, &$argc){ $contacts = q("SELECT `contact`.* FROM `contact` WHERE ( `rel` = %d OR `rel` = %d ) AND `poll` != '' - AND NOT `network` IN ( '%s', '%s', '%s' ) + AND NOT `network` IN ( '%s', '%s' ) AND `contact`.`id` = %d AND `self` = 0 AND `contact`.`blocked` = 0 AND `contact`.`readonly` = 0 AND `contact`.`archive` = 0 LIMIT 1", intval(CONTACT_IS_SHARING), intval(CONTACT_IS_FRIEND), - dbesc(NETWORK_DIASPORA), dbesc(NETWORK_FACEBOOK), dbesc(NETWORK_PUMPIO), intval($contact_id) ); - if(! count($contacts)) { + if(! count($contacts)) return; - } $contact = $contacts[0]; + // load current friends if possible. + if (($contact['poco'] != "") AND ($contact['success_update'] > $contact['failure_update'])) { + $r = q("SELECT count(*) as total from glink + where `cid` = %d and updated > UTC_TIMESTAMP() - INTERVAL 1 DAY", + intval($contact['id']) + ); + if (count($r)) + if (!$r[0]['total']) + poco_load($contact['id'],$importer_uid,0,$contact['poco']); + } + + // To-Do: + // - Check why we don't poll the Diaspora feed at the moment (some guid problem in the items?) + // - Check whether this is possible with Redmatrix + if ($contact["network"] == NETWORK_DIASPORA) { + if (poco_do_update($contact["created"], $contact["last-item"], $contact["failure_update"], $contact["success_update"])) { + $last_updated = poco_last_updated($contact["url"]); + $updated = datetime_convert(); + if ($last_updated) { + q("UPDATE `contact` SET `last-item` = '%s', `last-update` = '%s', `success_update` = '%s' WHERE `id` = %d", + dbesc($last_updated), + dbesc($updated), + dbesc($updated), + intval($contact['id']) + ); + } else { + q("UPDATE `contact` SET `last-update` = '%s', `failure_update` = '%s' WHERE `id` = %d", + dbesc($updated), + dbesc($updated), + intval($contact['id']) + ); + } + } + return; + } + $xml = false; $t = $contact['last-update']; @@ -129,6 +167,10 @@ function onepoll_run(&$argv, &$argc){ : datetime_convert('UTC','UTC',$contact['last-update'], ATOM_TIME) ); + // Update the contact entry + if(($contact['network'] === NETWORK_OSTATUS) || ($contact['network'] === NETWORK_DIASPORA) || ($contact['network'] === NETWORK_DFRN)) + update_contact($contact["id"]); + if($contact['network'] === NETWORK_DFRN) { @@ -168,7 +210,8 @@ function onepoll_run(&$argv, &$argc){ mark_for_death($contact); // set the last-update so we don't keep polling - $r = q("UPDATE `contact` SET `last-update` = '%s' WHERE `id` = %d", + $r = q("UPDATE `contact` SET `last-update` = '%s', `failure_update` = '%s' WHERE `id` = %d", + dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact['id']) ); @@ -181,7 +224,8 @@ function onepoll_run(&$argv, &$argc){ mark_for_death($contact); - $r = q("UPDATE `contact` SET `last-update` = '%s' WHERE `id` = %d", + $r = q("UPDATE `contact` SET `last-update` = '%s', `failure_update` = '%s' WHERE `id` = %d", + dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact['id']) ); @@ -198,7 +242,8 @@ function onepoll_run(&$argv, &$argc){ // set the last-update so we don't keep polling - $r = q("UPDATE `contact` SET `last-update` = '%s' WHERE `id` = %d", + $r = q("UPDATE `contact` SET `last-update` = '%s', `failure_update` = '%s' WHERE `id` = %d", + dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact['id']) ); @@ -243,8 +288,8 @@ function onepoll_run(&$argv, &$argc){ $final_dfrn_id = substr($final_dfrn_id,2); if($final_dfrn_id != $orig_id) { - logger('poller: ID did not decode: ' . $contact['id'] . ' orig: ' . $orig_id . ' final: ' . $final_dfrn_id); - // did not decode properly - cannot trust this site + logger('poller: ID did not decode: ' . $contact['id'] . ' orig: ' . $orig_id . ' final: ' . $final_dfrn_id); + // did not decode properly - cannot trust this site return; } @@ -255,7 +300,7 @@ function onepoll_run(&$argv, &$argc){ $xml = post_url($contact['poll'],$postvars); } - elseif(($contact['network'] === NETWORK_OSTATUS) + elseif(($contact['network'] === NETWORK_OSTATUS) || ($contact['network'] === NETWORK_DIASPORA) || ($contact['network'] === NETWORK_FEED) ) { @@ -265,7 +310,8 @@ function onepoll_run(&$argv, &$argc){ $stat_writeable = ((($contact['notify']) && ($contact['rel'] == CONTACT_IS_FOLLOWER || $contact['rel'] == CONTACT_IS_FRIEND)) ? 1 : 0); - if($contact['network'] === NETWORK_OSTATUS && get_pconfig($importer_uid,'system','ostatus_autofriend')) + // Contacts from OStatus are always writable + if($contact['network'] === NETWORK_OSTATUS) $stat_writeable = 1; if($stat_writeable != $contact['writable']) { @@ -544,7 +590,8 @@ function onepoll_run(&$argv, &$argc){ logger('poller: received xml : ' . $xml, LOGGER_DATA); if(! strstr($xml,'<')) { logger('poller: post_handshake: response from ' . $url . ' did not contain XML.'); - $r = q("UPDATE `contact` SET `last-update` = '%s' WHERE `id` = %d", + $r = q("UPDATE `contact` SET `last-update` = '%s', `failure_update` = '%s' WHERE `id` = %d", + dbesc(datetime_convert()), dbesc(datetime_convert()), intval($contact['id']) ); @@ -552,8 +599,9 @@ function onepoll_run(&$argv, &$argc){ } - consume_feed($xml,$importer,$contact,$hub,1,1); + logger("Consume feed of contact ".$contact['id']); + consume_feed($xml,$importer,$contact,$hub,1,1); // do it twice. Ensures that children of parents which may be later in the stream aren't tossed @@ -566,6 +614,11 @@ function onepoll_run(&$argv, &$argc){ if(($contact['network'] === NETWORK_OSTATUS || $contact['network'] == NETWORK_FEED) && (! $contact['hub-verify'])) $hub_update = true; + if ($force) + $hub_update = true; + + logger("Contact ".$contact['id']." returned hub: ".$hub." Network: ".$contact['network']." Relation: ".$contact['rel']." Update: ".$hub_update); + if((strlen($hub)) && ($hub_update) && (($contact['rel'] != CONTACT_IS_FOLLOWER) || $contact['network'] == NETWORK_FEED) ) { logger('poller: hub ' . $hubmode . ' : ' . $hub . ' contact name : ' . $contact['name'] . ' local user : ' . $importer['name']); $hubs = explode(',', $hub); @@ -578,29 +631,38 @@ function onepoll_run(&$argv, &$argc){ } } } - } - $updated = datetime_convert(); + $updated = datetime_convert(); - $r = q("UPDATE `contact` SET `last-update` = '%s', `success_update` = '%s' WHERE `id` = %d", - dbesc($updated), - dbesc($updated), - intval($contact['id']) - ); - - - // load current friends if possible. - - if($contact['poco']) { - $r = q("SELECT count(*) as total from glink - where `cid` = %d and updated > UTC_TIMESTAMP() - INTERVAL 1 DAY", + $r = q("UPDATE `contact` SET `last-update` = '%s', `success_update` = '%s' WHERE `id` = %d", + dbesc($updated), + dbesc($updated), + intval($contact['id']) + ); + + q("UPDATE `gcontact` SET `last_contact` = '%s' WHERE `nurl` = '%s'", + dbesc($updated), + dbesc($contact['nurl']) + ); + + } elseif (in_array($contact["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, NETWORK_FEED))) { + $updated = datetime_convert(); + + $r = q("UPDATE `contact` SET `last-update` = '%s', `failure_update` = '%s' WHERE `id` = %d", + dbesc($updated), + dbesc($updated), + intval($contact['id']) + ); + + q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'", + dbesc($updated), + dbesc($contact['nurl']) + ); + } else { + $r = q("UPDATE `contact` SET `last-update` = '%s' WHERE `id` = %d", + dbesc($updated), intval($contact['id']) ); - } - if(count($r)) { - if(! $r[0]['total']) { - poco_load($contact['id'],$importer_uid,0,$contact['poco']); - } } return; diff --git a/include/ostatus.php b/include/ostatus.php new file mode 100644 index 000000000..d39059eb6 --- /dev/null +++ b/include/ostatus.php @@ -0,0 +1,1066 @@ +statusnet_profile_url; + $r = q("SELECT `url` FROM `contact` WHERE `uid` = %d AND + (`nurl` = '%s' OR `alias` = '%s' OR `alias` = '%s') AND + `network` != '%s' LIMIT 1", + intval($uid), dbesc(normalise_link($url)), + dbesc(normalise_link($url)), dbesc($url), dbesc(NETWORK_STATUSNET)); + if (!$r) { + $data = probe_url($friend->statusnet_profile_url); + if ($data["network"] == NETWORK_OSTATUS) { + $result = new_contact($uid,$friend->statusnet_profile_url); + if ($result["success"]) + logger($friend->name." ".$url." - success", LOGGER_DEBUG); + else + logger($friend->name." ".$url." - failed", LOGGER_DEBUG); + } else + logger($friend->name." ".$url." - not OStatus", LOGGER_DEBUG); + } + } +} + +function ostatus_fetchauthor($xpath, $context, $importer, &$contact, $onlyfetch) { + + $author = array(); + $author["author-link"] = $xpath->evaluate('atom:author/atom:uri/text()', $context)->item(0)->nodeValue; + $author["author-name"] = $xpath->evaluate('atom:author/atom:name/text()', $context)->item(0)->nodeValue; + + // Preserve the value + $authorlink = $author["author-link"]; + + $alternate = $xpath->query("atom:author/atom:link[@rel='alternate']", $context)->item(0)->attributes; + if (is_object($alternate)) + foreach($alternate AS $attributes) + if ($attributes->name == "href") + $author["author-link"] = $attributes->textContent; + + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` IN ('%s', '%s') AND `network` != '%s'", + intval($importer["uid"]), dbesc(normalise_link($author["author-link"])), + dbesc(normalise_link($authorlink)), dbesc(NETWORK_STATUSNET)); + if ($r) { + $contact = $r[0]; + $author["contact-id"] = $r[0]["id"]; + } else + $author["contact-id"] = $contact["id"]; + + $avatarlist = array(); + $avatars = $xpath->query("atom:author/atom:link[@rel='avatar']", $context); + foreach($avatars AS $avatar) { + $href = ""; + $width = 0; + foreach($avatar->attributes AS $attributes) { + if ($attributes->name == "href") + $href = $attributes->textContent; + if ($attributes->name == "width") + $width = $attributes->textContent; + } + if (($width > 0) AND ($href != "")) + $avatarlist[$width] = $href; + } + if (count($avatarlist) > 0) { + krsort($avatarlist); + $author["author-avatar"] = current($avatarlist); + } + + $displayname = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue; + if ($displayname != "") + $author["author-name"] = $displayname; + + $author["owner-name"] = $author["author-name"]; + $author["owner-link"] = $author["author-link"]; + $author["owner-avatar"] = $author["author-avatar"]; + + if ($r AND !$onlyfetch) { + // Update contact data + $update_contact = ($r[0]['name-date'] < datetime_convert('','','now -12 hours')); + if ($update_contact) { + logger("Update contact data for contact ".$contact["id"], LOGGER_DEBUG); + + $value = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["name"] = $value; + + $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["nick"] = $value; + + $value = $xpath->evaluate('atom:author/poco:note/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["about"] = $value; + + $value = $xpath->evaluate('atom:author/poco:address/poco:formatted/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["location"] = $value; + + q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `about` = '%s', `location` = '%s', `name-date` = '%s' WHERE `id` = %d", + dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["about"]), dbesc($contact["location"]), + dbesc(datetime_convert()), intval($contact["id"])); + + poco_check($contact["url"], $contact["name"], $contact["network"], $author["author-avatar"], $contact["about"], $contact["location"], + "", "", "", datetime_convert(), 2, $contact["id"], $contact["uid"]); + } + + $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours')); + + if ($update_photo AND isset($author["author-avatar"])) { + logger("Update profile picture for contact ".$contact["id"], LOGGER_DEBUG); + + $photos = import_profile_photo($author["author-avatar"], $importer["uid"], $contact["id"]); + + q("UPDATE `contact` SET `photo` = '%s', `thumb` = '%s', `micro` = '%s', `avatar-date` = '%s' WHERE `id` = %d", + dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), + dbesc(datetime_convert()), intval($contact["id"])); + } + } + + return($author); +} + +function ostatus_salmon_author($xml, $importer) { + $a = get_app(); + + if ($xml == "") + return; + + $doc = new DOMDocument(); + @$doc->loadXML($xml); + + $xpath = new DomXPath($doc); + $xpath->registerNamespace('atom', "http://www.w3.org/2005/Atom"); + $xpath->registerNamespace('thr', "http://purl.org/syndication/thread/1.0"); + $xpath->registerNamespace('georss', "http://www.georss.org/georss"); + $xpath->registerNamespace('activity', "http://activitystrea.ms/spec/1.0/"); + $xpath->registerNamespace('media', "http://purl.org/syndication/atommedia"); + $xpath->registerNamespace('poco', "http://portablecontacts.net/spec/1.0"); + $xpath->registerNamespace('ostatus', "http://ostatus.org/schema/1.0"); + $xpath->registerNamespace('statusnet', "http://status.net/schema/api/1/"); + + $entries = $xpath->query('/atom:entry'); + + foreach ($entries AS $entry) { + // fetch the author + $author = ostatus_fetchauthor($xpath, $entry, $importer, $contact, true); + return $author; + } +} + +function ostatus_import($xml,$importer,&$contact, &$hub) { + + $a = get_app(); + + logger("Import OStatus message", LOGGER_DEBUG); + + if ($xml == "") + return; + + $doc = new DOMDocument(); + @$doc->loadXML($xml); + + $xpath = new DomXPath($doc); + $xpath->registerNamespace('atom', "http://www.w3.org/2005/Atom"); + $xpath->registerNamespace('thr', "http://purl.org/syndication/thread/1.0"); + $xpath->registerNamespace('georss', "http://www.georss.org/georss"); + $xpath->registerNamespace('activity', "http://activitystrea.ms/spec/1.0/"); + $xpath->registerNamespace('media', "http://purl.org/syndication/atommedia"); + $xpath->registerNamespace('poco', "http://portablecontacts.net/spec/1.0"); + $xpath->registerNamespace('ostatus', "http://ostatus.org/schema/1.0"); + $xpath->registerNamespace('statusnet', "http://status.net/schema/api/1/"); + + $gub = ""; + $hub_attributes = $xpath->query("/atom:feed/atom:link[@rel='hub']")->item(0)->attributes; + if (is_object($hub_attributes)) + foreach($hub_attributes AS $hub_attribute) + if ($hub_attribute->name == "href") { + $hub = $hub_attribute->textContent; + logger("Found hub ".$hub, LOGGER_DEBUG); + } + + $header = array(); + $header["uid"] = $importer["uid"]; + $header["network"] = NETWORK_OSTATUS; + $header["type"] = "remote"; + $header["wall"] = 0; + $header["origin"] = 0; + $header["gravity"] = GRAVITY_PARENT; + + // it could either be a received post or a post we fetched by ourselves + // depending on that, the first node is different + $first_child = $doc->firstChild->tagName; + + if ($first_child == "feed") + $entries = $xpath->query('/atom:feed/atom:entry'); + else + $entries = $xpath->query('/atom:entry'); + + $conversation = ""; + $conversationlist = array(); + $item_id = 0; + + // Reverse the order of the entries + $entrylist = array(); + + foreach ($entries AS $entry) + $entrylist[] = $entry; + + foreach (array_reverse($entrylist) AS $entry) { + + $mention = false; + + // fetch the author + if ($first_child == "feed") + $author = ostatus_fetchauthor($xpath, $doc->firstChild, $importer, $contact, false); + else + $author = ostatus_fetchauthor($xpath, $entry, $importer, $contact, false); + + $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue; + if ($value != "") + $nickname = $value; + else + $nickname = $author["author-name"]; + + $item = array_merge($header, $author); + + // Now get the item + $item["uri"] = $xpath->query('atom:id/text()', $entry)->item(0)->nodeValue; + + $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", + intval($importer["uid"]), dbesc($item["uri"])); + if ($r) { + logger("Item with uri ".$item["uri"]." for user ".$importer["uid"]." already existed under id ".$r[0]["id"], LOGGER_DEBUG); + continue; + } + + $item["body"] = add_page_info_to_body(html2bbcode($xpath->query('atom:content/text()', $entry)->item(0)->nodeValue)); + $item["object-type"] = $xpath->query('activity:object-type/text()', $entry)->item(0)->nodeValue; + + if (($item["object-type"] == ACTIVITY_OBJ_BOOKMARK) OR ($item["object-type"] == ACTIVITY_OBJ_EVENT)) { + $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue; + $item["body"] = $xpath->query('atom:summary/text()', $entry)->item(0)->nodeValue; + } elseif ($item["object-type"] == ACTIVITY_OBJ_QUESTION) + $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue; + + $item["object"] = $xml; + $item["verb"] = $xpath->query('activity:verb/text()', $entry)->item(0)->nodeValue; + + // To-Do: + // Delete a message + if ($item["verb"] == "qvitter-delete-notice") { + // ignore "Delete" messages (by now) + logger("Ignore delete message ".print_r($item, true)); + continue; + } + + if ($item["verb"] == ACTIVITY_JOIN) { + // ignore "Join" messages + logger("Ignore join message ".print_r($item, true)); + continue; + } + + if ($item["verb"] == ACTIVITY_FOLLOW) { + new_follower($importer, $contact, $item, $nickname); + continue; + } + + if ($item["verb"] == NAMESPACE_OSTATUS."/unfollow") { + lose_follower($importer, $contact, $item, $dummy); + continue; + } + + if ($item["verb"] == ACTIVITY_FAVORITE) { + $orig_uri = $xpath->query("activity:object/atom:id", $entry)->item(0)->nodeValue; + logger("Favorite ".$orig_uri." ".print_r($item, true)); + + $item["verb"] = ACTIVITY_LIKE; + $item["parent-uri"] = $orig_uri; + $item["gravity"] = GRAVITY_LIKE; + } + + if ($item["verb"] == NAMESPACE_OSTATUS."/unfavorite") { + // Ignore "Unfavorite" message + logger("Ignore unfavorite message ".print_r($item, true)); + continue; + } + + // http://activitystrea.ms/schema/1.0/rsvp-yes + if (!in_array($item["verb"], array(ACTIVITY_POST, ACTIVITY_LIKE, ACTIVITY_SHARE))) + logger("Unhandled verb ".$item["verb"]." ".print_r($item, true)); + + $item["created"] = $xpath->query('atom:published/text()', $entry)->item(0)->nodeValue; + $item["edited"] = $xpath->query('atom:updated/text()', $entry)->item(0)->nodeValue; + $conversation = $xpath->query('ostatus:conversation/text()', $entry)->item(0)->nodeValue; + + $related = ""; + + $inreplyto = $xpath->query('thr:in-reply-to', $entry); + if (is_object($inreplyto->item(0))) { + foreach($inreplyto->item(0)->attributes AS $attributes) { + if ($attributes->name == "ref") + $item["parent-uri"] = $attributes->textContent; + if ($attributes->name == "href") + $related = $attributes->textContent; + } + } + + $georsspoint = $xpath->query('georss:point', $entry); + if ($georsspoint) + $item["coord"] = $georsspoint->item(0)->nodeValue; + + // To-Do + // $item["location"] = + + $categories = $xpath->query('atom:category', $entry); + if ($categories) { + foreach ($categories AS $category) { + foreach($category->attributes AS $attributes) + if ($attributes->name == "term") { + $term = $attributes->textContent; + if(strlen($item["tag"])) + $item["tag"] .= ','; + $item["tag"] .= "#[url=".$a->get_baseurl()."/search?tag=".$term."]".$term."[/url]"; + } + } + } + + $self = ""; + $enclosure = ""; + + $links = $xpath->query('atom:link', $entry); + if ($links) { + $rel = ""; + $href = ""; + $type = ""; + $length = "0"; + $title = ""; + foreach ($links AS $link) { + foreach($link->attributes AS $attributes) { + if ($attributes->name == "href") + $href = $attributes->textContent; + if ($attributes->name == "rel") + $rel = $attributes->textContent; + if ($attributes->name == "type") + $type = $attributes->textContent; + if ($attributes->name == "length") + $length = $attributes->textContent; + if ($attributes->name == "title") + $title = $attributes->textContent; + } + if (($rel != "") AND ($href != "")) + switch($rel) { + case "alternate": + $item["plink"] = $href; + if (($item["object-type"] == ACTIVITY_OBJ_QUESTION) OR + ($item["object-type"] == ACTIVITY_OBJ_EVENT)) + $item["body"] .= add_page_info($href); + break; + case "ostatus:conversation": + $conversation = $href; + break; + case "enclosure": + $enclosure = $href; + if(strlen($item["attach"])) + $item["attach"] .= ','; + + $item["attach"] .= '[attach]href="'.$href.'" length="'.$length.'" type="'.$type.'" title="'.$title.'"[/attach]'; + break; + case "related": + if ($item["object-type"] != ACTIVITY_OBJ_BOOKMARK) { + if (!isset($item["parent-uri"])) + $item["parent-uri"] = $href; + + if ($related == "") + $related = $href; + } else + $item["body"] .= add_page_info($href); + break; + case "self": + $self = $href; + break; + case "mentioned": + // Notification check + if ($importer["nurl"] == normalise_link($href)) + $mention = true; + break; + } + } + } + + $local_id = ""; + $repeat_of = ""; + + $notice_info = $xpath->query('statusnet:notice_info', $entry); + if ($notice_info AND ($notice_info->length > 0)) { + foreach($notice_info->item(0)->attributes AS $attributes) { + if ($attributes->name == "source") + $item["app"] = strip_tags($attributes->textContent); + if ($attributes->name == "local_id") + $local_id = $attributes->textContent; + if ($attributes->name == "repeat_of") + $repeat_of = $attributes->textContent; + } + } + + // Is it a repeated post? + if ($repeat_of != "") { + $activityobjects = $xpath->query('activity:object', $entry)->item(0); + + if (is_object($activityobjects)) { + + $orig_uri = $xpath->query("activity:object/atom:id", $activityobjects)->item(0)->nodeValue; + if (!isset($orig_uri)) + $orig_uri = $xpath->query('atom:id/text()', $activityobjects)->item(0)->nodeValue; + + $orig_links = $xpath->query("activity:object/atom:link[@rel='alternate']", $activityobjects); + if ($orig_links AND ($orig_links->length > 0)) + foreach($orig_links->item(0)->attributes AS $attributes) + if ($attributes->name == "href") + $orig_link = $attributes->textContent; + + if (!isset($orig_link)) + $orig_link = $xpath->query("atom:link[@rel='alternate']", $activityobjects)->item(0)->nodeValue; + + if (!isset($orig_link)) + $orig_link = ostatus_convert_href($orig_uri); + + $orig_body = $xpath->query('activity:object/atom:content/text()', $activityobjects)->item(0)->nodeValue; + if (!isset($orig_body)) + $orig_body = $xpath->query('atom:content/text()', $activityobjects)->item(0)->nodeValue; + + $orig_created = $xpath->query('atom:published/text()', $activityobjects)->item(0)->nodeValue; + + $orig_contact = $contact; + $orig_author = ostatus_fetchauthor($xpath, $activityobjects, $importer, $orig_contact, false); + + //if (!intval(get_config('system','wall-to-wall_share'))) { + // $prefix = share_header($orig_author['author-name'], $orig_author['author-link'], $orig_author['author-avatar'], "", $orig_created, $orig_link); + // $item["body"] = $prefix.add_page_info_to_body(html2bbcode($orig_body))."[/share]"; + //} else { + $item["author-name"] = $orig_author["author-name"]; + $item["author-link"] = $orig_author["author-link"]; + $item["author-avatar"] = $orig_author["author-avatar"]; + $item["body"] = add_page_info_to_body(html2bbcode($orig_body)); + $item["created"] = $orig_created; + + $item["uri"] = $orig_uri; + $item["plink"] = $orig_link; + //} + + $item["verb"] = $xpath->query('activity:verb/text()', $activityobjects)->item(0)->nodeValue; + + $item["object-type"] = $xpath->query('activity:object/activity:object-type/text()', $activityobjects)->item(0)->nodeValue; + if (!isset($item["object-type"])) + $item["object-type"] = $xpath->query('activity:object-type/text()', $activityobjects)->item(0)->nodeValue; + } + } + + //if ($enclosure != "") + // $item["body"] .= add_page_info($enclosure); + + if (isset($item["parent-uri"])) { + $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", + intval($importer["uid"]), dbesc($item["parent-uri"])); + + if (!$r AND ($related != "")) { + $reply_path = str_replace("/notice/", "/api/statuses/show/", $related).".atom"; + + if ($reply_path != $related) { + logger("Fetching related items for user ".$importer["uid"]." from ".$reply_path, LOGGER_DEBUG); + $reply_xml = fetch_url($reply_path); + + $reply_contact = $contact; + ostatus_import($reply_xml,$importer,$reply_contact, $reply_hub); + + // After the import try to fetch the parent item again + $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", + intval($importer["uid"]), dbesc($item["parent-uri"])); + } + } + if ($r) { + $item["type"] = 'remote-comment'; + $item["gravity"] = GRAVITY_COMMENT; + } + } else + $item["parent-uri"] = $item["uri"]; + + $item_id = ostatus_completion($conversation, $importer["uid"], $item); + + if (!$item_id) { + logger("Error storing item", LOGGER_DEBUG); + continue; + } + + logger("Item was stored with id ".$item_id, LOGGER_DEBUG); + $item["id"] = $item_id; + + if ($mention) { + $u = q("SELECT `notify-flags`, `language`, `username`, `email` FROM user WHERE uid = %d LIMIT 1", intval($item['uid'])); + $r = q("SELECT `parent` FROM `item` WHERE `id` = %d", intval($item_id)); + + notification(array( + 'type' => NOTIFY_TAGSELF, + 'notify_flags' => $u[0]["notify-flags"], + 'language' => $u[0]["language"], + 'to_name' => $u[0]["username"], + 'to_email' => $u[0]["email"], + 'uid' => $item["uid"], + 'item' => $item, + 'link' => $a->get_baseurl().'/display/'.urlencode(get_item_guid($item_id)), + 'source_name' => $item["author-name"], + 'source_link' => $item["author-link"], + 'source_photo' => $item["author-avatar"], + 'verb' => ACTIVITY_TAG, + 'otype' => 'item', + 'parent' => $r[0]["parent"] + )); + } + } +} + +function ostatus_convert_href($href) { + $elements = explode(":",$href); + + if ((count($elements) <= 2) OR ($elements[0] != "tag")) + return $href; + + $server = explode(",", $elements[1]); + $conversation = explode("=", $elements[2]); + + if ((count($elements) == 4) AND ($elements[2] == "post")) + return "http://".$server[0]."/notice/".$elements[3]; + + if ((count($conversation) != 2) OR ($conversation[1] =="")) + return $href; + + if ($elements[3] == "objectType=thread") + return "http://".$server[0]."/conversation/".$conversation[1]; + else + return "http://".$server[0]."/notice/".$conversation[1]; + + return $href; +} + +function check_conversations($mentions = false, $override = false) { + $last = get_config('system','ostatus_last_poll'); + + $poll_interval = intval(get_config('system','ostatus_poll_interval')); + if(! $poll_interval) + $poll_interval = OSTATUS_DEFAULT_POLL_INTERVAL; + + // Don't poll if the interval is set negative + if (($poll_interval < 0) AND !$override) + return; + + if (!$mentions) { + $poll_timeframe = intval(get_config('system','ostatus_poll_timeframe')); + if (!$poll_timeframe) + $poll_timeframe = OSTATUS_DEFAULT_POLL_TIMEFRAME; + } else { + $poll_timeframe = intval(get_config('system','ostatus_poll_timeframe')); + if (!$poll_timeframe) + $poll_timeframe = OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS; + } + + + if ($last AND !$override) { + $next = $last + ($poll_interval * 60); + if ($next > time()) { + logger('poll interval not reached'); + return; + } + } + + logger('cron_start'); + + $start = date("Y-m-d H:i:s", time() - ($poll_timeframe * 60)); + + if ($mentions) + $conversations = q("SELECT `term`.`oid`, `term`.`url`, `term`.`uid` FROM `term` + STRAIGHT_JOIN `thread` ON `thread`.`iid` = `term`.`oid` AND `thread`.`uid` = `term`.`uid` + WHERE `term`.`type` = 7 AND `term`.`term` > '%s' AND `thread`.`mention` + GROUP BY `term`.`url`, `term`.`uid` ORDER BY `term`.`term` DESC", dbesc($start)); + else + $conversations = q("SELECT `oid`, `url`, `uid` FROM `term` + WHERE `type` = 7 AND `term` > '%s' + GROUP BY `url`, `uid` ORDER BY `term` DESC", dbesc($start)); + + foreach ($conversations AS $conversation) { + ostatus_completion($conversation['url'], $conversation['uid']); + } + + logger('cron_end'); + + set_config('system','ostatus_last_poll', time()); +} + +function ostatus_completion($conversation_url, $uid, $item = array()) { + + $a = get_app(); + + $item_stored = -1; + + $conversation_url = ostatus_convert_href($conversation_url); + + // If the thread shouldn't be completed then store the item and go away + if ((intval(get_config('system','ostatus_poll_interval')) == -2) AND (count($item) > 0)) { + //$arr["app"] .= " (OStatus-NoCompletion)"; + $item_stored = item_store($item, true); + return($item_stored); + } + + // Get the parent + $parents = q("SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN + (SELECT `parent` FROM `item` WHERE `id` IN + (SELECT `oid` FROM `term` WHERE `uid` = %d AND `otype` = %d AND `type` = %d AND `url` = '%s'))", + intval($uid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION), dbesc($conversation_url)); + + if ($parents) + $parent = $parents[0]; + elseif (count($item) > 0) { + $parent = $item; + $parent["type"] = "remote"; + $parent["verb"] = ACTIVITY_POST; + $parent["visible"] = 1; + } else { + // Preset the parent + $r = q("SELECT `id` FROM `contact` WHERE `self` AND `uid`=%d", $uid); + if (!$r) + return(-2); + + $parent = array(); + $parent["id"] = 0; + $parent["parent"] = 0; + $parent["uri"] = ""; + $parent["contact-id"] = $r[0]["id"]; + $parent["type"] = "remote"; + $parent["verb"] = ACTIVITY_POST; + $parent["visible"] = 1; + } + + $conv = str_replace("/conversation/", "/api/statusnet/conversation/", $conversation_url).".as"; + $pageno = 1; + $items = array(); + + logger('fetching conversation url '.$conv.' for user '.$uid); + + do { + $conv_arr = z_fetch_url($conv."?page=".$pageno); + + // If it is a non-ssl site and there is an error, then try ssl or vice versa + if (!$conv_arr["success"] AND (substr($conv, 0, 7) == "http://")) { + $conv = str_replace("http://", "https://", $conv); + $conv_as = fetch_url($conv."?page=".$pageno); + } elseif (!$conv_arr["success"] AND (substr($conv, 0, 8) == "https://")) { + $conv = str_replace("https://", "http://", $conv); + $conv_as = fetch_url($conv."?page=".$pageno); + } else + $conv_as = $conv_arr["body"]; + + $conv_as = str_replace(',"statusnet:notice_info":', ',"statusnet_notice_info":', $conv_as); + $conv_as = json_decode($conv_as); + + if (@is_array($conv_as->items)) + $items = array_merge($items, $conv_as->items); + else + break; + + $pageno++; + + } while (true); + + logger('fetching conversation done. Found '.count($items).' items'); + + if (!sizeof($items)) { + if (count($item) > 0) { + //$arr["app"] .= " (OStatus-NoConvFetched)"; + $item_stored = item_store($item, true); + + if ($item_stored) { + logger("Conversation ".$conversation_url." couldn't be fetched. Item uri ".$item["uri"]." stored: ".$item_stored, LOGGER_DEBUG); + ostatus_store_conversation($item_id, $conversation_url); + } + + return($item_stored); + } else + return(-3); + } + + $items = array_reverse($items); + + $r = q("SELECT `nurl` FROM `contact` WHERE `uid` = %d AND `self`", intval($uid)); + $importer = $r[0]; + + foreach ($items as $single_conv) { + + // Test - remove before flight + //$tempfile = tempnam(get_temppath(), "conversation"); + //file_put_contents($tempfile, json_encode($single_conv)); + + $mention = false; + + if (isset($single_conv->object->id)) + $single_conv->id = $single_conv->object->id; + + $plink = ostatus_convert_href($single_conv->id); + if (isset($single_conv->object->url)) + $plink = ostatus_convert_href($single_conv->object->url); + + if (@!$single_conv->id) + continue; + + logger("Got id ".$single_conv->id, LOGGER_DEBUG); + + if ($first_id == "") { + $first_id = $single_conv->id; + + // The first post of the conversation isn't our first post. There are three options: + // 1. Our conversation hasn't the "real" thread starter + // 2. This first post is a post inside our thread + // 3. This first post is a post inside another thread + if (($first_id != $parent["uri"]) AND ($parent["uri"] != "")) { + $new_parents = q("SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN + (SELECT `parent` FROM `item` + WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s')) LIMIT 1", + intval($uid), dbesc($first_id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); + if ($new_parents) { + if ($new_parents[0]["parent"] == $parent["parent"]) { + // Option 2: This post is already present inside our thread - but not as thread starter + logger("Option 2: uri present in our thread: ".$first_id, LOGGER_DEBUG); + $first_id = $parent["uri"]; + } else { + // Option 3: Not so good. We have mixed parents. We have to see how to clean this up. + // For now just take the new parent. + $parent = $new_parents[0]; + $first_id = $parent["uri"]; + logger("Option 3: mixed parents for uri ".$first_id, LOGGER_DEBUG); + } + } else { + // Option 1: We hadn't got the real thread starter + // We have to clean up our existing messages. + $parent["id"] = 0; + $parent["uri"] = $first_id; + logger("Option 1: we have a new parent: ".$first_id, LOGGER_DEBUG); + } + } elseif ($parent["uri"] == "") { + $parent["id"] = 0; + $parent["uri"] = $first_id; + } + } + + $parent_uri = $parent["uri"]; + + // "context" only seems to exist on older servers + if (isset($single_conv->context->inReplyTo->id)) { + $parent_exists = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1", + intval($uid), dbesc($single_conv->context->inReplyTo->id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); + if ($parent_exists) + $parent_uri = $single_conv->context->inReplyTo->id; + } + + // This is the current way + if (isset($single_conv->object->inReplyTo->id)) { + $parent_exists = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1", + intval($uid), dbesc($single_conv->object->inReplyTo->id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); + if ($parent_exists) + $parent_uri = $single_conv->object->inReplyTo->id; + } + + $message_exists = q("SELECT `id`, `parent`, `uri` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1", + intval($uid), dbesc($single_conv->id), + dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); + if ($message_exists) { + logger("Message ".$single_conv->id." already existed on the system", LOGGER_DEBUG); + + if ($parent["id"] != 0) { + $existing_message = $message_exists[0]; + + // We improved the way we fetch OStatus messages, this shouldn't happen very often now + // To-Do: we have to change the shadow copies as well. This way here is really ugly. + if ($existing_message["parent"] != $parent["id"]) { + logger('updating id '.$existing_message["id"].' with parent '.$existing_message["parent"].' to parent '.$parent["id"].' uri '.$parent["uri"].' thread '.$parent_uri, LOGGER_DEBUG); + + // Update the parent id of the selected item + $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `id` = %d", + intval($parent["id"]), dbesc($parent["uri"]), intval($existing_message["id"])); + + // Update the parent uri in the thread - but only if it points to itself + $r = q("UPDATE `item` SET `thr-parent` = '%s' WHERE `id` = %d AND `uri` = `thr-parent`", + dbesc($parent_uri), intval($existing_message["id"])); + + // try to change all items of the same parent + $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `parent` = %d", + intval($parent["id"]), dbesc($parent["uri"]), intval($existing_message["parent"])); + + // Update the parent uri in the thread - but only if it points to itself + $r = q("UPDATE `item` SET `thr-parent` = '%s' WHERE (`parent` = %d) AND (`uri` = `thr-parent`)", + dbesc($parent["uri"]), intval($existing_message["parent"])); + + // Now delete the thread + delete_thread($existing_message["parent"]); + } + } + + // The item we are having on the system is the one that we wanted to store via the item array + if (isset($item["uri"]) AND ($item["uri"] == $existing_message["uri"])) { + $item = array(); + $item_stored = 0; + } + + continue; + } + + if (is_array($single_conv->to)) + foreach($single_conv->to AS $to) + if ($importer["nurl"] == normalise_link($to->id)) + $mention = true; + + $actor = $single_conv->actor->id; + if (isset($single_conv->actor->url)) + $actor = $single_conv->actor->url; + + $contact = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` != '%s'", + $uid, normalise_link($actor), NETWORK_STATUSNET); + + if (count($contact)) { + logger("Found contact for url ".$actor, LOGGER_DEBUG); + $contact_id = $contact[0]["id"]; + } else { + logger("No contact found for url ".$actor, LOGGER_DEBUG); + + // Adding a global contact + // To-Do: Use this data for the post + $global_contact_id = get_contact($actor, 0); + + logger("Global contact ".$global_contact_id." found for url ".$actor, LOGGER_DEBUG); + + $contact_id = $parent["contact-id"]; + } + + $arr = array(); + $arr["network"] = NETWORK_OSTATUS; + $arr["uri"] = $single_conv->id; + $arr["plink"] = $plink; + $arr["uid"] = $uid; + $arr["contact-id"] = $contact_id; + $arr["parent-uri"] = $parent_uri; + $arr["created"] = $single_conv->published; + $arr["edited"] = $single_conv->published; + $arr["owner-name"] = $single_conv->actor->displayName; + if ($arr["owner-name"] == '') + $arr["owner-name"] = $single_conv->actor->contact->displayName; + if ($arr["owner-name"] == '') + $arr["owner-name"] = $single_conv->actor->portablecontacts_net->displayName; + + $arr["owner-link"] = $actor; + $arr["owner-avatar"] = $single_conv->actor->image->url; + $arr["author-name"] = $arr["owner-name"]; + $arr["author-link"] = $actor; + $arr["author-avatar"] = $single_conv->actor->image->url; + $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->content)); + + if (isset($single_conv->status_net->notice_info->source)) + $arr["app"] = strip_tags($single_conv->status_net->notice_info->source); + elseif (isset($single_conv->statusnet->notice_info->source)) + $arr["app"] = strip_tags($single_conv->statusnet->notice_info->source); + elseif (isset($single_conv->statusnet_notice_info->source)) + $arr["app"] = strip_tags($single_conv->statusnet_notice_info->source); + elseif (isset($single_conv->provider->displayName)) + $arr["app"] = $single_conv->provider->displayName; + else + $arr["app"] = "OStatus"; + + //$arr["app"] .= " (Conversation)"; + + $arr["object"] = json_encode($single_conv); + $arr["verb"] = $parent["verb"]; + $arr["visible"] = $parent["visible"]; + $arr["location"] = $single_conv->location->displayName; + $arr["coord"] = trim($single_conv->location->lat." ".$single_conv->location->lon); + + // Is it a reshared item? + if (isset($single_conv->verb) AND ($single_conv->verb == "share") AND isset($single_conv->object)) { + if (is_array($single_conv->object)) + $single_conv->object = $single_conv->object[0]; + + logger("Found reshared item ".$single_conv->object->id); + + // $single_conv->object->context->conversation; + + if (isset($single_conv->object->object->id)) + $arr["uri"] = $single_conv->object->object->id; + else + $arr["uri"] = $single_conv->object->id; + + if (isset($single_conv->object->object->url)) + $plink = ostatus_convert_href($single_conv->object->object->url); + else + $plink = ostatus_convert_href($single_conv->object->url); + + if (isset($single_conv->object->object->content)) + $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->object->object->content)); + else + $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->object->content)); + + $arr["plink"] = $plink; + + $arr["created"] = $single_conv->object->published; + $arr["edited"] = $single_conv->object->published; + + $arr["author-name"] = $single_conv->object->actor->displayName; + if ($arr["owner-name"] == '') + $arr["author-name"] = $single_conv->object->actor->contact->displayName; + + $arr["author-link"] = $single_conv->object->actor->url; + $arr["author-avatar"] = $single_conv->object->actor->image->url; + + $arr["app"] = $single_conv->object->provider->displayName."#"; + //$arr["verb"] = $single_conv->object->verb; + + $arr["location"] = $single_conv->object->location->displayName; + $arr["coord"] = trim($single_conv->object->location->lat." ".$single_conv->object->location->lon); + } + + if ($arr["location"] == "") + unset($arr["location"]); + + if ($arr["coord"] == "") + unset($arr["coord"]); + + // Copy fields from given item array + if (isset($item["uri"]) AND (($item["uri"] == $arr["uri"]) OR ($item["uri"] == $single_conv->id))) { + $copy_fields = array("owner-name", "owner-link", "owner-avatar", "author-name", "author-link", "author-avatar", + "gravity", "body", "object-type", "object", "verb", "created", "edited", "coord", "tag", + "title", "attach", "app", "type", "location", "contact-id", "uri"); + foreach ($copy_fields AS $field) + if (isset($item[$field])) + $arr[$field] = $item[$field]; + + //$arr["app"] .= " (OStatus)"; + } + + $newitem = item_store($arr); + if (!$newitem) { + logger("Item wasn't stored ".print_r($arr, true), LOGGER_DEBUG); + continue; + } + + if (isset($item["uri"]) AND ($item["uri"] == $arr["uri"])) { + $item = array(); + $item_stored = $newitem; + } + + logger('Stored new item '.$plink.' for parent '.$arr["parent-uri"].' under id '.$newitem, LOGGER_DEBUG); + + // Add the conversation entry (but don't fetch the whole conversation) + ostatus_store_conversation($newitem, $conversation_url); + + if ($mention) { + $u = q("SELECT `notify-flags`, `language`, `username`, `email` FROM user WHERE uid = %d LIMIT 1", intval($uid)); + $r = q("SELECT `parent` FROM `item` WHERE `id` = %d", intval($newitem)); + + notification(array( + 'type' => NOTIFY_TAGSELF, + 'notify_flags' => $u[0]["notify-flags"], + 'language' => $u[0]["language"], + 'to_name' => $u[0]["username"], + 'to_email' => $u[0]["email"], + 'uid' => $uid, + 'item' => $arr, + 'link' => $a->get_baseurl().'/display/'.urlencode(get_item_guid($newitem)), + 'source_name' => $arr["author-name"], + 'source_link' => $arr["author-link"], + 'source_photo' => $arr["author-avatar"], + 'verb' => ACTIVITY_TAG, + 'otype' => 'item', + 'parent' => $r[0]["parent"] + )); + } + + // If the newly created item is the top item then change the parent settings of the thread + // This shouldn't happen anymore. This is supposed to be absolote. + if ($arr["uri"] == $first_id) { + logger('setting new parent to id '.$newitem); + $new_parents = q("SELECT `id`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `uid` = %d AND `id` = %d LIMIT 1", + intval($uid), intval($newitem)); + if ($new_parents) + $parent = $new_parents[0]; + } + } + + if (($item_stored < 0) AND (count($item) > 0)) { + //$arr["app"] .= " (OStatus-NoConvFound)"; + $item_stored = item_store($item, true); + if ($item_stored) { + logger("Uri ".$item["uri"]." wasn't found in conversation ".$conversation_url, LOGGER_DEBUG); + ostatus_store_conversation($item_stored, $conversation_url); + } + } + + return($item_stored); +} + +function ostatus_store_conversation($itemid, $conversation_url) { + global $a; + + $conversation_url = ostatus_convert_href($conversation_url); + + $messages = q("SELECT `uid`, `parent`, `created`, `received`, `guid` FROM `item` WHERE `id` = %d LIMIT 1", intval($itemid)); + if (!$messages) + return; + $message = $messages[0]; + + // Store conversation url if not done before + $conversation = q("SELECT `url` FROM `term` WHERE `uid` = %d AND `oid` = %d AND `otype` = %d AND `type` = %d", + intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION)); + + if (!$conversation) { + $r = q("INSERT INTO `term` (`uid`, `oid`, `otype`, `type`, `term`, `url`, `created`, `received`, `guid`) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", + intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION), + dbesc($message["created"]), dbesc($conversation_url), dbesc($message["created"]), dbesc($message["received"]), dbesc($message["guid"])); + logger('Storing conversation url '.$conversation_url.' for id '.$itemid); + } +} +?> diff --git a/include/ostatus_conversation.php b/include/ostatus_conversation.php deleted file mode 100644 index 667f7dde4..000000000 --- a/include/ostatus_conversation.php +++ /dev/null @@ -1,224 +0,0 @@ - time()) { - logger('poll interval not reached'); - return; - } - } - - logger('cron_start'); - - $start = date("Y-m-d H:i:s", time() - ($poll_timeframe * 60)); - $conversations = q("SELECT * FROM `term` WHERE `type` = 7 AND `term` > '%s'", - dbesc($start)); - foreach ($conversations AS $conversation) { - $id = $conversation['oid']; - $url = $conversation['url']; - complete_conversation($id, $url); - } - - logger(' cron_end'); - - set_config('system','ostatus_last_poll', time()); -} - -function complete_conversation($itemid, $conversation_url, $only_add_conversation = false) { - global $a; - - if (intval(get_config('system','ostatus_poll_interval')) == -2) - return; - - if ($a->last_ostatus_conversation_url == $conversation_url) - return; - - $a->last_ostatus_conversation_url = $conversation_url; - - $messages = q("SELECT `uid`, `parent`, `created`, `received`, `guid` FROM `item` WHERE `id` = %d LIMIT 1", intval($itemid)); - if (!$messages) - return; - $message = $messages[0]; - - // Store conversation url if not done before - $conversation = q("SELECT `url` FROM `term` WHERE `uid` = %d AND `oid` = %d AND `otype` = %d AND `type` = %d", - intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION)); - - if (!$conversation) { - $r = q("INSERT INTO `term` (`uid`, `oid`, `otype`, `type`, `term`, `url`, `created`, `received`, `guid`) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", - intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION), - dbesc($message["created"]), dbesc($conversation_url), dbesc($message["created"]), dbesc($message["received"]), dbesc($message["guid"])); - logger('complete_conversation: Storing conversation url '.$conversation_url.' for id '.$itemid); - } - - if ($only_add_conversation) - return; - - // Get the parent - $parents = q("SELECT `id`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `uid` = %d AND `id` = %d LIMIT 1", - intval($message["uid"]), intval($message["parent"])); - if (!$parents) - return; - $parent = $parents[0]; - - require_once('include/html2bbcode.php'); - require_once('include/items.php'); - - $conv = str_replace("/conversation/", "/api/statusnet/conversation/", $conversation_url).".as"; - $pageno = 1; - $items = array(); - - logger('complete_conversation: fetching conversation url '.$conv.' for '.$itemid); - - do { - $conv_as = fetch_url($conv."?page=".$pageno); - $conv_as = str_replace(',"statusnet:notice_info":', ',"statusnet_notice_info":', $conv_as); - $conv_as = json_decode($conv_as); - - if (@is_array($conv_as->items)) - $items = array_merge($items, $conv_as->items); - else - break; - - $pageno++; - - } while (true); - - if (!sizeof($items)) - return; - - $items = array_reverse($items); - - foreach ($items as $single_conv) { - // status.net changed the format of the activity streams. This is a quick fix. - if (@is_string($single_conv->object->id)) - $single_conv->id = $single_conv->object->id; - - if (@!$single_conv->id AND $single_conv->provider->url AND $single_conv->statusnet_notice_info->local_id) - $single_conv->id = $single_conv->provider->url."notice/".$single_conv->statusnet_notice_info->local_id; - - if (@!$single_conv->id) - continue; - - if ($first_id == "") { - $first_id = $single_conv->id; - - $new_parents = q("SELECT `id`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `uid` = %d AND `uri` = '%s' LIMIT 1", - intval($message["uid"]), dbesc($first_id)); - if ($new_parents) { - $parent = $new_parents[0]; - logger('adopting new parent '.$parent["id"].' for '.$itemid); - } else { - $parent["id"] = 0; - $parent["uri"] = $first_id; - } - } - - if (isset($single_conv->context->inReplyTo->id)) - $parent_uri = $single_conv->context->inReplyTo->id; - else - $parent_uri = $parent["uri"]; - - $message_exists = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' LIMIT 1", - intval($message["uid"]), dbesc($single_conv->id)); - if ($message_exists) { - if ($parent["id"] != 0) { - $existing_message = $message_exists[0]; - - // This is partly bad, since the entry in the thread table isn't updated - $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s', `thr-parent` = '%s' WHERE `id` = %d", - intval($parent["id"]), - dbesc($parent["uri"]), - dbesc($parent_uri), - intval($existing_message["id"])); - } - continue; - } - - $contact = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` != '%s'", - $message["uid"], normalise_link($single_conv->actor->id), NETWORK_STATUSNET); - - if (count($contact)) { - logger("Found contact for url ".$single_conv->actor->id, LOGGER_DEBUG); - $contact_id = $contact[0]["id"]; - } else { - logger("No contact found for url ".$single_conv->actor->id, LOGGER_DEBUG); - $contact_id = $parent["contact-id"]; - } - - $arr = array(); - $arr["network"] = NETWORK_OSTATUS; - $arr["uri"] = $single_conv->id; - $arr["plink"] = $single_conv->id; - $arr["uid"] = $message["uid"]; - $arr["contact-id"] = $contact_id; - if ($parent["id"] != 0) - $arr["parent"] = $parent["id"]; - $arr["parent-uri"] = $parent["uri"]; - $arr["thr-parent"] = $parent_uri; - $arr["created"] = $single_conv->published; - $arr["edited"] = $single_conv->published; - //$arr["owner-name"] = $single_conv->actor->contact->displayName; - $arr["owner-name"] = $single_conv->actor->contact->preferredUsername; - if ($arr["owner-name"] == '') - $arr["owner-name"] = $single_conv->actor->portablecontacts_net->preferredUsername; - if ($arr["owner-name"] == '') - $arr["owner-name"] = $single_conv->actor->displayName; - - $arr["owner-link"] = $single_conv->actor->id; - $arr["owner-avatar"] = $single_conv->actor->image->url; - //$arr["author-name"] = $single_conv->actor->contact->displayName; - //$arr["author-name"] = $single_conv->actor->contact->preferredUsername; - $arr["author-name"] = $arr["owner-name"]; - $arr["author-link"] = $single_conv->actor->id; - $arr["author-avatar"] = $single_conv->actor->image->url; - $arr["body"] = html2bbcode($single_conv->content); - $arr["app"] = strip_tags($single_conv->statusnet_notice_info->source); - if ($arr["app"] == "") - $arr["app"] = $single_conv->provider->displayName; - $arr["verb"] = $parent["verb"]; - $arr["visible"] = $parent["visible"]; - $arr["location"] = $single_conv->location->displayName; - $arr["coord"] = trim($single_conv->location->lat." ".$single_conv->location->lon); - - if ($arr["location"] == "") - unset($arr["location"]); - - if ($arr["coord"] == "") - unset($arr["coord"]); - - $newitem = item_store($arr); - - // Add the conversation entry (but don't fetch the whole conversation) - complete_conversation($newitem, $conversation_url, true); - - // If the newly created item is the top item then change the parent settings of the thread - if ($newitem AND ($arr["uri"] == $first_id)) { - logger('setting new parent to id '.$newitem); - $new_parents = q("SELECT `id`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `uid` = %d AND `id` = %d LIMIT 1", - intval($message["uid"]), intval($newitem)); - if ($new_parents) { - $parent = $new_parents[0]; - logger('done changing parents to parent '.$newitem); - } - } - } -} -?> diff --git a/include/plaintext.php b/include/plaintext.php index 88febbfff..4f435fc6a 100644 --- a/include/plaintext.php +++ b/include/plaintext.php @@ -52,7 +52,7 @@ function get_attached_data($body) { if (preg_match_all("(\[url=([$URLSearchString]*)\]\s*\[img\]([$URLSearchString]*)\[\/img\]\s*\[\/url\])ism", $body, $pictures, PREG_SET_ORDER)) { if (count($pictures) == 1) { // Checking, if the link goes to a picture - $data = parseurl_getsiteinfo($pictures[0][1], true); + $data = parseurl_getsiteinfo_cached($pictures[0][1], true); if ($data["type"] == "photo") { $post["type"] = "photo"; if (isset($data["images"][0])) @@ -95,7 +95,7 @@ function get_attached_data($body) { } } elseif (isset($post["url"]) AND ($post["type"] == "video")) { require_once("mod/parse_url.php"); - $data = parseurl_getsiteinfo($post["url"], true); + $data = parseurl_getsiteinfo_cached($post["url"], true); if (isset($data["images"][0])) $post["image"] = $data["images"][0]["src"]; @@ -182,6 +182,8 @@ function plaintext($a, $b, $limit = 0, $includedlinks = false, $htmlmode = 2) { $post["url"] = $b["plink"]; } elseif (strpos($b["body"], "[share") !== false) $post["url"] = $b["plink"]; + elseif (get_pconfig($b["uid"], "system", "no_intelligent_shortening")) + $post["url"] = $b["plink"]; $msg = shortenmsg($msg, $limit); } diff --git a/include/plugin.php b/include/plugin.php index f4861536c..5a4755c31 100644 --- a/include/plugin.php +++ b/include/plugin.php @@ -162,6 +162,8 @@ function call_hooks($name, &$data = null) { $a = get_app(); + #logger($name, LOGGER_ALL); + if((is_array($a->hooks)) && (array_key_exists($name,$a->hooks))) { foreach($a->hooks[$name] as $hook) { // Don't run a theme's hook if the user isn't using the theme @@ -171,6 +173,7 @@ function call_hooks($name, &$data = null) { @include_once($hook[0]); if(function_exists($hook[1])) { $func = $hook[1]; + //logger($name." => ".$hook[0].":".$func."()", LOGGER_DEBUG); $func($a,$data); } else { @@ -386,11 +389,11 @@ function install_theme($theme) { // check service_class restrictions. If there are no service_classes defined, everything is allowed. -// if $usage is supplied, we check against a maximum count and return true if the current usage is +// if $usage is supplied, we check against a maximum count and return true if the current usage is // less than the subscriber plan allows. Otherwise we return boolean true or false if the property -// is allowed (or not) in this subscriber plan. An unset property for this service plan means -// the property is allowed, so it is only necessary to provide negative properties for each plan, -// or what the subscriber is not allowed to do. +// is allowed (or not) in this subscriber plan. An unset property for this service plan means +// the property is allowed, so it is only necessary to provide negative properties for each plan, +// or what the subscriber is not allowed to do. function service_class_allows($uid,$property,$usage = false) { diff --git a/include/poller.php b/include/poller.php index cef11a744..28dc0c0cd 100644 --- a/include/poller.php +++ b/include/poller.php @@ -36,6 +36,7 @@ function poller_run(&$argv, &$argc){ require_once('include/email.php'); require_once('include/socgraph.php'); require_once('include/pidfile.php'); + require_once('mod/nodeinfo.php'); load_config('config'); load_config('system'); @@ -46,7 +47,7 @@ function poller_run(&$argv, &$argc){ if(function_exists('sys_getloadavg')) { $load = sys_getloadavg(); if(intval($load[0]) > $maxsysload) { - logger('system: load ' . $load . ' too high. Poller deferred to next scheduled run.'); + logger('system: load ' . $load[0] . ' too high. Poller deferred to next scheduled run.'); return; } } @@ -82,6 +83,14 @@ function poller_run(&$argv, &$argc){ proc_run('php',"include/dsprphotoq.php"); + // run the process to discover global contacts in the background + + proc_run('php',"include/discover_poco.php"); + + // run the process to update locally stored global contacts in the background + + proc_run('php',"include/discover_poco.php", "checkcontact"); + // expire any expired accounts q("UPDATE user SET `account_expired` = 1 where `account_expired` = 0 @@ -103,7 +112,17 @@ function poller_run(&$argv, &$argc){ $abandon_days = 0; // Check OStatus conversations - check_conversations(); + // Check only conversations with mentions (for a longer time) + check_conversations(true); + + // Check every conversation + check_conversations(false); + + // Follow your friends from your legacy OStatus account + ostatus_check_follow_friends(); + + // update nodeinfo data + nodeinfo_cron(); // To-Do: Regenerate usage statistics // q("ANALYZE TABLE `item`"); diff --git a/include/profile_advanced.php b/include/profile_advanced.php deleted file mode 100644 index 05fd56d02..000000000 --- a/include/profile_advanced.php +++ /dev/null @@ -1,93 +0,0 @@ -' . t('Profile') . ''; - - if($a->profile['name']) { - - $tpl = get_markup_template('profile_advanced.tpl'); - - $profile = array(); - - $profile['fullname'] = array( t('Full Name:'), $a->profile['name'] ) ; - - if($a->profile['gender']) $profile['gender'] = array( t('Gender:'), $a->profile['gender'] ); - - - if(($a->profile['dob']) && ($a->profile['dob'] != '0000-00-00')) { - - $year_bd_format = t('j F, Y'); - $short_bd_format = t('j F'); - - - $val = ((intval($a->profile['dob'])) - ? day_translate(datetime_convert('UTC','UTC',$a->profile['dob'] . ' 00:00 +00:00',$year_bd_format)) - : day_translate(datetime_convert('UTC','UTC','2001-' . substr($a->profile['dob'],5) . ' 00:00 +00:00',$short_bd_format))); - - $profile['birthday'] = array( t('Birthday:'), $val); - - } - - if($age = age($a->profile['dob'],$a->profile['timezone'],'')) $profile['age'] = array( t('Age:'), $age ); - - - if($a->profile['marital']) $profile['marital'] = array( t('Status:'), $a->profile['marital']); - - - if($a->profile['with']) $profile['marital']['with'] = $a->profile['with']; - - if(strlen($a->profile['howlong']) && $a->profile['howlong'] !== '0000-00-00 00:00:00') { - $profile['howlong'] = relative_date($a->profile['howlong'], t('for %1$d %2$s')); - } - - if($a->profile['sexual']) $profile['sexual'] = array( t('Sexual Preference:'), $a->profile['sexual'] ); - - if($a->profile['homepage']) $profile['homepage'] = array( t('Homepage:'), linkify($a->profile['homepage']) ); - - if($a->profile['hometown']) $profile['hometown'] = array( t('Hometown:'), linkify($a->profile['hometown']) ); - - if($a->profile['pub_keywords']) $profile['pub_keywords'] = array( t('Tags:'), $a->profile['pub_keywords']); - - if($a->profile['politic']) $profile['politic'] = array( t('Political Views:'), $a->profile['politic']); - - if($a->profile['religion']) $profile['religion'] = array( t('Religion:'), $a->profile['religion']); - - if($txt = prepare_text($a->profile['about'])) $profile['about'] = array( t('About:'), $txt ); - - if($txt = prepare_text($a->profile['interest'])) $profile['interest'] = array( t('Hobbies/Interests:'), $txt); - - if($txt = prepare_text($a->profile['likes'])) $profile['likes'] = array( t('Likes:'), $txt); - - if($txt = prepare_text($a->profile['dislikes'])) $profile['dislikes'] = array( t('Dislikes:'), $txt); - - - if($txt = prepare_text($a->profile['contact'])) $profile['contact'] = array( t('Contact information and Social Networks:'), $txt); - - if($txt = prepare_text($a->profile['music'])) $profile['music'] = array( t('Musical interests:'), $txt); - - if($txt = prepare_text($a->profile['book'])) $profile['book'] = array( t('Books, literature:'), $txt); - - if($txt = prepare_text($a->profile['tv'])) $profile['tv'] = array( t('Television:'), $txt); - - if($txt = prepare_text($a->profile['film'])) $profile['film'] = array( t('Film/dance/culture/entertainment:'), $txt); - - if($txt = prepare_text($a->profile['romance'])) $profile['romance'] = array( t('Love/Romance:'), $txt); - - if($txt = prepare_text($a->profile['work'])) $profile['work'] = array( t('Work/employment:'), $txt); - - if($txt = prepare_text($a->profile['education'])) $profile['education'] = array( t('School/education:'), $txt ); - - if ($a->profile['uid'] == local_user()) - $profile['edit'] = array($a->get_baseurl(). '/profiles/'.$a->profile['id'], t('Edit profile'),"", t('Edit profile')); - - return replace_macros($tpl, array( - '$title' => t('Profile'), - '$profile' => $profile - )); - } - - return ''; -} diff --git a/include/profile_update.php b/include/profile_update.php index 9da96442c..0fcf3617f 100644 --- a/include/profile_update.php +++ b/include/profile_update.php @@ -12,7 +12,7 @@ function profile_change() { return; // $url = $a->get_baseurl() . '/profile/' . $a->user['nickname']; -// if($url && strlen(get_config('system','directory_submit_url'))) +// if($url && strlen(get_config('system','directory'))) // proc_run('php',"include/directory.php","$url"); $recips = q("SELECT `id`,`name`,`network`,`pubkey`,`notify` FROM `contact` WHERE `network` = '%s' diff --git a/include/pubsubpublish.php b/include/pubsubpublish.php new file mode 100644 index 000000000..bc81fd786 --- /dev/null +++ b/include/pubsubpublish.php @@ -0,0 +1,113 @@ + 0, + // i.e. there has been an update (set in notifier.php). + + $r = q("SELECT * FROM `push_subscriber` WHERE `push` > 0"); + + foreach($r as $rr) { + $params = get_feed_for($a, '', $rr['nickname'], $rr['last_update'], 0, true); + $hmac_sig = hash_hmac("sha1", $params, $rr['secret']); + + $headers = array("Content-type: application/atom+xml", + sprintf("Link: <%s>;rel=hub," . + "<%s>;rel=self", + $a->get_baseurl() . '/pubsubhubbub', + $rr['topic']), + "X-Hub-Signature: sha1=" . $hmac_sig); + + logger('POST '. print_r($headers, true)."\n".$params, LOGGER_DEBUG); + + post_url($rr['callback_url'], $params, $headers); + $ret = $a->get_curl_code(); + + if ($ret >= 200 && $ret <= 299) { + logger('successfully pushed to '.$rr['callback_url']); + + // set last_update to "now", and reset push=0 + $date_now = datetime_convert('UTC','UTC','now','Y-m-d H:i:s'); + q("UPDATE `push_subscriber` SET `push` = 0, last_update = '%s' WHERE id = %d", + dbesc($date_now), + intval($rr['id'])); + + } else { + logger('error when pushing to '.$rr['callback_url'].' HTTP: '.$ret); + + // we use the push variable also as a counter, if we failed we + // increment this until some upper limit where we give up + $new_push = intval($rr['push']) + 1; + + if ($new_push > 30) // OK, let's give up + $new_push = 0; + + q("UPDATE `push_subscriber` SET `push` = %d WHERE id = %d", + $new_push, + intval($rr['id'])); + } + } + + logger('done'); +} + + +function pubsubpublish_run(&$argv, &$argc){ + global $a, $db; + + 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); + }; + + require_once('include/items.php'); + require_once('include/pidfile.php'); + + load_config('config'); + load_config('system'); + + $lockpath = get_lockpath(); + if ($lockpath != '') { + $pidfile = new pidfile($lockpath, 'pubsubpublish'); + if($pidfile->is_already_running()) { + logger("Already running"); + if ($pidfile->running_time() > 9*60) { + $pidfile->kill(); + logger("killed stale process"); + // Calling a new instance + proc_run('php',"include/pubsubpublish.php"); + } + return; + } + } + + $a->set_baseurl(get_config('system','url')); + + load_hooks(); + + if($argc > 1) + $pubsubpublish_id = intval($argv[1]); + else + $pubsubpublish_id = 0; + + handle_pubsubhubbub(); + + return; + +} + +if (array_search(__file__,get_included_files())===0){ + pubsubpublish_run($_SERVER["argv"],$_SERVER["argc"]); + killme(); +} + diff --git a/include/queue.php b/include/queue.php index 128dfcba5..0edd64fdb 100644 --- a/include/queue.php +++ b/include/queue.php @@ -2,64 +2,6 @@ require_once("boot.php"); require_once('include/queue_fn.php'); -function handle_pubsubhubbub() { - global $a, $db; - - logger('queue [pubsubhubbub]: start'); - - // We'll push to each subscriber that has push > 0, - // i.e. there has been an update (set in notifier.php). - - $r = q("SELECT * FROM `push_subscriber` WHERE `push` > 0"); - - foreach($r as $rr) { - $params = get_feed_for($a, '', $rr['nickname'], $rr['last_update']); - $hmac_sig = hash_hmac("sha1", $params, $rr['secret']); - - $headers = array("Content-type: application/atom+xml", - sprintf("Link: <%s>;rel=hub," . - "<%s>;rel=self", - $a->get_baseurl() . '/pubsubhubbub', - $rr['topic']), - "X-Hub-Signature: sha1=" . $hmac_sig); - - logger('queue [pubsubhubbub]: POST', $headers); - - post_url($rr['callback_url'], $params, $headers); - $ret = $a->get_curl_code(); - - if ($ret >= 200 && $ret <= 299) { - logger('queue [pubsubhubbub]: successfully pushed to ' . - $rr['callback_url']); - - // set last_update to "now", and reset push=0 - $date_now = datetime_convert('UTC','UTC','now','Y-m-d H:i:s'); - q("UPDATE `push_subscriber` SET `push` = 0, last_update = '%s' " . - "WHERE id = %d", - dbesc($date_now), - intval($rr['id'])); - - } else { - logger('queue [pubsubhubbub]: error when pushing to ' . - $rr['callback_url'] . 'HTTP: ', $ret); - - // we use the push variable also as a counter, if we failed we - // increment this until some upper limit where we give up - $new_push = intval($rr['push']) + 1; - - if ($new_push > 30) // OK, let's give up - $new_push = 0; - - q("UPDATE `push_subscriber` SET `push` = %d, last_update = '%s' " . - "WHERE id = %d", - $new_push, - dbesc($date_now), - intval($rr['id'])); - } - } -} - - function queue_run(&$argv, &$argc){ global $a, $db; @@ -112,7 +54,8 @@ function queue_run(&$argv, &$argc){ logger('queue: start'); - handle_pubsubhubbub(); + // Handling the pubsubhubbub requests + proc_run('php','include/pubsubpublish.php'); $interval = ((get_config('system','delivery_interval') === false) ? 2 : intval(get_config('system','delivery_interval'))); @@ -126,8 +69,8 @@ function queue_run(&$argv, &$argc){ } } - $r = q("SELECT `queue`.*, `contact`.`name`, `contact`.`uid` FROM `queue` - INNER JOIN `contact` ON `queue`.`cid` = `contact`.`id` + $r = q("SELECT `queue`.*, `contact`.`name`, `contact`.`uid` FROM `queue` + INNER JOIN `contact` ON `queue`.`cid` = `contact`.`id` WHERE `queue`.`created` < UTC_TIMESTAMP() - INTERVAL 3 DAY"); if($r) { foreach($r as $rr) { diff --git a/include/salmon.php b/include/salmon.php index 3d525f51a..757437490 100644 --- a/include/salmon.php +++ b/include/salmon.php @@ -7,7 +7,7 @@ require_once('include/crypto.php'); function get_salmon_key($uri,$keyhash) { $ret = array(); - logger('Fetching salmon key'); + logger('Fetching salmon key for '.$uri); $arr = lrdd($uri); @@ -44,10 +44,10 @@ function get_salmon_key($uri,$keyhash) { if(count($ret) == 1) { // We only found one one key so we don't care if the hash matches. - // If it's the wrong key we'll find out soon enough because - // message verification will fail. This also covers some older + // If it's the wrong key we'll find out soon enough because + // message verification will fail. This also covers some older // software which don't supply a keyhash. As long as they only - // have one key we'll be right. + // have one key we'll be right. return $ret[0]; } @@ -62,20 +62,20 @@ function get_salmon_key($uri,$keyhash) { return ''; } - - + + function slapper($owner,$url,$slap) { - logger('slapper called. Data: ' . $slap); + logger('slapper called for '.$url.'. Data: ' . $slap); - // does contact have a salmon endpoint? + // does contact have a salmon endpoint? if(! strlen($url)) return; if(! $owner['sprvkey']) { - logger(sprintf("slapper: user '%s' (%d) does not have a salmon private key. Send failed.", + logger(sprintf("user '%s' (%d) does not have a salmon private key. Send failed.", $owner['username'],$owner['uid'])); return; } @@ -96,7 +96,7 @@ $namespaces = <<< EOT EOT; $slap = str_replace('',$namespaces,$slap); - + // create a magic envelope $data = base64url_encode($slap); @@ -125,7 +125,7 @@ EOT; '$signature' => $signature )); - // slap them + // slap them post_url($url,$salmon, array( 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) @@ -138,7 +138,7 @@ EOT; if($return_code > 299) { - logger('slapper: compliant salmon failed. Falling back to status.net hack2'); + logger('compliant salmon failed. Falling back to status.net hack2'); // Entirely likely that their salmon implementation is // non-compliant. Let's try once more, this time only signing @@ -152,7 +152,7 @@ EOT; '$signature' => $signature2 )); - // slap them + // slap them post_url($url,$salmon, array( 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) @@ -162,11 +162,11 @@ EOT; if($return_code > 299) { - logger('slapper: compliant salmon failed. Falling back to status.net hack3'); + logger('compliant salmon failed. Falling back to status.net hack3'); // Entirely likely that their salmon implementation is // non-compliant. Let's try once more, this time only signing - // the data, without the precomputed blob + // the data, without the precomputed blob $salmon = replace_macros($salmon_tpl,array( '$data' => $data, @@ -176,7 +176,7 @@ EOT; '$signature' => $signature3 )); - // slap them + // slap them post_url($url,$salmon, array( 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) @@ -184,7 +184,7 @@ EOT; $return_code = $a->get_curl_code(); } } - logger('slapper returned ' . $return_code); + logger('slapper for '.$url.' returned ' . $return_code); if(! $return_code) return(-1); if(($return_code == 503) && (stristr($a->get_curl_headers(),'retry-after'))) diff --git a/include/security.php b/include/security.php index 6e722d673..2d6db95f4 100644 --- a/include/security.php +++ b/include/security.php @@ -55,7 +55,7 @@ function authenticate_success($user_record, $login_initial = false, $interactive else $a->identities = array(); - $r = q("select `user`.`uid`, `user`.`username`, `user`.`nickname` + $r = q("select `user`.`uid`, `user`.`username`, `user`.`nickname` from manage INNER JOIN user on manage.mid = user.uid where `user`.`account_removed` = 0 and `manage`.`uid` = %d", intval($master_record['uid']) @@ -86,6 +86,15 @@ function authenticate_success($user_record, $login_initial = false, $interactive dbesc($l), intval($_SESSION['uid']) ); + + // Set the login date for all identities of the user + q("UPDATE `user` SET `login_date` = '%s' WHERE `password` = '%s' AND `email` = '%s' AND `account_removed` = 0", + dbesc(datetime_convert()), + dbesc($master_record['password']), + dbesc($master_record['email']) + ); + + } if($login_initial) { call_hooks('logged_in', $a->user); diff --git a/include/socgraph.php b/include/socgraph.php index 23db35cab..d380af434 100644 --- a/include/socgraph.php +++ b/include/socgraph.php @@ -1,6 +1,15 @@ = %d OR `generation` = 0) AND `nurl` = '%s'", dbesc($name), @@ -249,6 +306,7 @@ function poco_check($profile_url, $name, $network, $profile_photo, $about, $loca dbesc($profile_photo), dbesc($connect_url), dbesc($profile_url), + dbesc($server_url), dbesc($updated), dbesc($location), dbesc($about), @@ -260,14 +318,17 @@ function poco_check($profile_url, $name, $network, $profile_photo, $about, $loca ); } } else { - q("INSERT INTO `gcontact` (`name`,`network`, `url`,`nurl`,`photo`,`connect`, `updated`, `location`, `about`, `keywords`, `gender`, `generation`) - VALUES ('%s', '%s', '%s', '%s', '%s','%s', '%s', '%s', '%s', '%s', '%s', %d)", + q("INSERT INTO `gcontact` (`name`, `nick`, `network`, `url`, `nurl`, `photo`, `connect`, `server_url`, `created`, `updated`, `location`, `about`, `keywords`, `gender`, `generation`) + VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)", dbesc($name), + dbesc($nick), dbesc($network), dbesc($profile_url), dbesc(normalise_link($profile_url)), dbesc($profile_photo), dbesc($connect_url), + dbesc($server_url), + dbesc(datetime_convert()), dbesc($updated), dbesc($location), dbesc($about), @@ -319,6 +380,576 @@ function poco_check($profile_url, $name, $network, $profile_photo, $about, $loca return $gcid; } +function poco_reachable($profile, $server = "", $network = "", $force = false) { + + if ($server == "") + $server = poco_detect_server($profile); + + if ($server == "") + return true; + + return poco_check_server($server, $network, $force); +} + +function poco_detect_server($profile) { + + // Try to detect the server path based upon some known standard paths + $server_url = ""; + + if ($server_url == "") { + $friendica = preg_replace("=(https?://)(.*)/profile/(.*)=ism", "$1$2", $profile); + if ($friendica != $profile) { + $server_url = $friendica; + $network = NETWORK_DFRN; + } + } + + if ($server_url == "") { + $diaspora = preg_replace("=(https?://)(.*)/u/(.*)=ism", "$1$2", $profile); + if ($diaspora != $profile) { + $server_url = $diaspora; + $network = NETWORK_DIASPORA; + } + } + + if ($server_url == "") { + $red = preg_replace("=(https?://)(.*)/channel/(.*)=ism", "$1$2", $profile); + if ($red != $profile) { + $server_url = $red; + $network = NETWORK_DIASPORA; + } + } + + return $server_url; +} + +function poco_alternate_ostatus_url($url) { + return(preg_match("=https?://.+/user/\d+=ism", $url, $matches)); +} + +function poco_last_updated($profile, $force = false) { + + $gcontacts = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'", + dbesc(normalise_link($profile))); + + if ($gcontacts[0]["created"] == "0000-00-00 00:00:00") + q("UPDATE `gcontact` SET `created` = '%s' WHERE `nurl` = '%s'", + dbesc(datetime_convert()), dbesc(normalise_link($profile))); + + if ($gcontacts[0]["server_url"] != "") + $server_url = $gcontacts[0]["server_url"]; + else + $server_url = poco_detect_server($profile); + + if ($server_url != "") { + if (!poco_check_server($server_url, $gcontacts[0]["network"], $force)) { + + if ($force) + q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'", + dbesc(datetime_convert()), dbesc(normalise_link($profile))); + + return false; + } + + q("UPDATE `gcontact` SET `server_url` = '%s' WHERE `nurl` = '%s'", + dbesc($server_url), dbesc(normalise_link($profile))); + } + + if (in_array($gcontacts[0]["network"], array("", NETWORK_FEED))) { + $server = q("SELECT `network` FROM `gserver` WHERE `nurl` = '%s' AND `network` != ''", + dbesc(normalise_link($server_url))); + + if ($server) + q("UPDATE `gcontact` SET `network` = '%s' WHERE `nurl` = '%s'", + dbesc($server[0]["network"]), dbesc(normalise_link($profile))); + else + return; + } + + // noscrape is really fast so we don't cache the call. + if (($gcontacts[0]["server_url"] != "") AND ($gcontacts[0]["nick"] != "")) { + + // Use noscrape if possible + $server = q("SELECT `noscrape` FROM `gserver` WHERE `nurl` = '%s' AND `noscrape` != ''", dbesc(normalise_link($gcontacts[0]["server_url"]))); + + if ($server) { + $noscraperet = z_fetch_url($server[0]["noscrape"]."/".$gcontacts[0]["nick"]); + + if ($noscraperet["success"] AND ($noscraperet["body"] != "")) { + + $noscrape = json_decode($noscraperet["body"], true); + + if (($noscrape["fn"] != "") AND ($noscrape["fn"] != $gcontacts[0]["name"])) + q("UPDATE `gcontact` SET `name` = '%s' WHERE `nurl` = '%s'", + dbesc($noscrape["fn"]), dbesc(normalise_link($profile))); + + if (($noscrape["photo"] != "") AND ($noscrape["photo"] != $gcontacts[0]["photo"])) + q("UPDATE `gcontact` SET `photo` = '%s' WHERE `nurl` = '%s'", + dbesc($noscrape["photo"]), dbesc(normalise_link($profile))); + + if (($noscrape["updated"] != "") AND ($noscrape["updated"] != $gcontacts[0]["updated"])) + q("UPDATE `gcontact` SET `updated` = '%s' WHERE `nurl` = '%s'", + dbesc($noscrape["updated"]), dbesc(normalise_link($profile))); + + if (($noscrape["gender"] != "") AND ($noscrape["gender"] != $gcontacts[0]["gender"])) + q("UPDATE `gcontact` SET `gender` = '%s' WHERE `nurl` = '%s'", + dbesc($noscrape["gender"]), dbesc(normalise_link($profile))); + + if (($noscrape["pdesc"] != "") AND ($noscrape["pdesc"] != $gcontacts[0]["about"])) + q("UPDATE `gcontact` SET `about` = '%s' WHERE `nurl` = '%s'", + dbesc($noscrape["pdesc"]), dbesc(normalise_link($profile))); + + if (($noscrape["about"] != "") AND ($noscrape["about"] != $gcontacts[0]["about"])) + q("UPDATE `gcontact` SET `about` = '%s' WHERE `nurl` = '%s'", + dbesc($noscrape["about"]), dbesc(normalise_link($profile))); + + if (isset($noscrape["comm"]) AND ($noscrape["comm"] != $gcontacts[0]["community"])) + q("UPDATE `gcontact` SET `community` = %d WHERE `nurl` = '%s'", + intval($noscrape["comm"]), dbesc(normalise_link($profile))); + + if (isset($noscrape["tags"])) + $keywords = implode(" ", $noscrape["tags"]); + else + $keywords = ""; + + if (($keywords != "") AND ($keywords != $gcontacts[0]["keywords"])) + q("UPDATE `gcontact` SET `keywords` = '%s' WHERE `nurl` = '%s'", + dbesc($keywords), dbesc(normalise_link($profile))); + + $location = $noscrape["locality"]; + + if ($noscrape["region"] != "") { + if ($location != "") + $location .= ", "; + + $location .= $noscrape["region"]; + } + + if ($noscrape["country-name"] != "") { + if ($location != "") + $location .= ", "; + + $location .= $noscrape["country-name"]; + } + + if (($location != "") AND ($location != $gcontacts[0]["location"])) + q("UPDATE `gcontact` SET `location` = '%s' WHERE `nurl` = '%s'", + dbesc($location), dbesc(normalise_link($profile))); + + // If we got data from noscrape then mark the contact as reachable + if (is_array($noscrape) AND count($noscrape)) + q("UPDATE `gcontact` SET `last_contact` = '%s' WHERE `nurl` = '%s'", + dbesc(datetime_convert()), dbesc(normalise_link($profile))); + + return $noscrape["updated"]; + } + } + } + + // If we only can poll the feed, then we only do this once a while + if (!$force AND !poco_do_update($gcontacts[0]["created"], $gcontacts[0]["updated"], $gcontacts[0]["last_failure"], $gcontacts[0]["last_contact"])) + return $gcontacts[0]["updated"]; + + $data = probe_url($profile); + + // Is the profile link the alternate OStatus link notation? (http://domain.tld/user/4711) + // Then check the other link and delete this one + if (($data["network"] == NETWORK_OSTATUS) AND poco_alternate_ostatus_url($profile) AND + (normalise_link($profile) == normalise_link($data["alias"])) AND + (normalise_link($profile) != normalise_link($data["url"]))) { + + // Delete the old entry + q("DELETE FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($profile))); + q("DELETE FROM `glink` WHERE `gcid` = %d", intval($gcontacts[0]["id"])); + + poco_check($data["url"], $data["name"], $data["network"], $data["photo"], $gcontacts[0]["about"], $gcontacts[0]["location"], + $gcontacts[0]["gender"], $gcontacts[0]["keywords"], $data["addr"], $gcontacts[0]["updated"], $gcontacts[0]["generation"]); + + poco_last_updated($data["url"], $force); + + return false; + } + + if (($data["poll"] == "") OR ($data["network"] == NETWORK_FEED)) { + q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'", + dbesc(datetime_convert()), dbesc(normalise_link($profile))); + return false; + } + + if (($data["name"] != "") AND ($data["name"] != $gcontacts[0]["name"])) + q("UPDATE `gcontact` SET `name` = '%s' WHERE `nurl` = '%s'", + dbesc($data["name"]), dbesc(normalise_link($profile))); + + if (($data["nick"] != "") AND ($data["nick"] != $gcontacts[0]["nick"])) + q("UPDATE `gcontact` SET `nick` = '%s' WHERE `nurl` = '%s'", + dbesc($data["nick"]), dbesc(normalise_link($profile))); + + if (($data["addr"] != "") AND ($data["addr"] != $gcontacts[0]["connect"])) + q("UPDATE `gcontact` SET `connect` = '%s' WHERE `nurl` = '%s'", + dbesc($data["addr"]), dbesc(normalise_link($profile))); + + if (($data["photo"] != "") AND ($data["photo"] != $gcontacts[0]["photo"])) + q("UPDATE `gcontact` SET `photo` = '%s' WHERE `nurl` = '%s'", + dbesc($data["photo"]), dbesc(normalise_link($profile))); + + if (($data["baseurl"] != "") AND ($data["baseurl"] != $gcontacts[0]["server_url"])) + q("UPDATE `gcontact` SET `server_url` = '%s' WHERE `nurl` = '%s'", + dbesc($data["baseurl"]), dbesc(normalise_link($profile))); + + $feedret = z_fetch_url($data["poll"]); + + if (!$feedret["success"]) { + q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'", + dbesc(datetime_convert()), dbesc(normalise_link($profile))); + return false; + } + + $doc = new DOMDocument(); + @$doc->loadXML($feedret["body"]); + + $xpath = new DomXPath($doc); + $xpath->registerNamespace('atom', "http://www.w3.org/2005/Atom"); + + $entries = $xpath->query('/atom:feed/atom:entry'); + + $last_updated = ""; + + foreach ($entries AS $entry) { + $published = $xpath->query('atom:published/text()', $entry)->item(0)->nodeValue; + $updated = $xpath->query('atom:updated/text()', $entry)->item(0)->nodeValue; + + if ($last_updated < $published) + $last_updated = $published; + + if ($last_updated < $updated) + $last_updated = $updated; + } + + // Maybe there aren't any entries. Then check if it is a valid feed + if ($last_updated == "") + if ($xpath->query('/atom:feed')->length > 0) + $last_updated = "0000-00-00 00:00:00"; + + q("UPDATE `gcontact` SET `updated` = '%s', `last_contact` = '%s' WHERE `nurl` = '%s'", + dbesc($last_updated), dbesc(datetime_convert()), dbesc(normalise_link($profile))); + + if (($gcontacts[0]["generation"] == 0)) + q("UPDATE `gcontact` SET `generation` = 9 WHERE `nurl` = '%s'", + dbesc(normalise_link($profile))); + + return($last_updated); +} + +function poco_do_update($created, $updated, $last_failure, $last_contact) { + $now = strtotime(datetime_convert()); + + if ($updated > $last_contact) + $contact_time = strtotime($updated); + else + $contact_time = strtotime($last_contact); + + $failure_time = strtotime($last_failure); + $created_time = strtotime($created); + + // If there is no "created" time then use the current time + if ($created_time <= 0) + $created_time = $now; + + // If the last contact was less than 24 hours then don't update + if (($now - $contact_time) < (60 * 60 * 24)) + return false; + + // If the last failure was less than 24 hours then don't update + if (($now - $failure_time) < (60 * 60 * 24)) + return false; + + // If the last contact was less than a week ago and the last failure is older than a week then don't update + //if ((($now - $contact_time) < (60 * 60 * 24 * 7)) AND ($contact_time > $failure_time)) + // return false; + + // If the last contact time was more than a week ago and the contact was created more than a week ago, then only try once a week + if ((($now - $contact_time) > (60 * 60 * 24 * 7)) AND (($now - $created_time) > (60 * 60 * 24 * 7)) AND (($now - $failure_time) < (60 * 60 * 24 * 7))) + return false; + + // If the last contact time was more than a month ago and the contact was created more than a month ago, then only try once a month + if ((($now - $contact_time) > (60 * 60 * 24 * 30)) AND (($now - $created_time) > (60 * 60 * 24 * 30)) AND (($now - $failure_time) < (60 * 60 * 24 * 30))) + return false; + + return true; +} + +function poco_to_boolean($val) { + if (($val == "true") OR ($val == 1)) + return(true); + if (($val == "false") OR ($val == 0)) + return(false); + + return ($val); +} + +function poco_check_server($server_url, $network = "", $force = false) { + + if ($server_url == "") + return false; + + $servers = q("SELECT * FROM `gserver` WHERE `nurl` = '%s'", dbesc(normalise_link($server_url))); + if ($servers) { + + if ($servers[0]["created"] == "0000-00-00 00:00:00") + q("UPDATE `gserver` SET `created` = '%s' WHERE `nurl` = '%s'", + dbesc(datetime_convert()), dbesc(normalise_link($server_url))); + + $poco = $servers[0]["poco"]; + $noscrape = $servers[0]["noscrape"]; + + if ($network == "") + $network = $servers[0]["network"]; + + $last_contact = $servers[0]["last_contact"]; + $last_failure = $servers[0]["last_failure"]; + $version = $servers[0]["version"]; + $platform = $servers[0]["platform"]; + $site_name = $servers[0]["site_name"]; + $info = $servers[0]["info"]; + $register_policy = $servers[0]["register_policy"]; + + if (!$force AND !poco_do_update($servers[0]["created"], "", $last_failure, $last_contact)) { + logger("Use cached data for server ".$server_url, LOGGER_DEBUG); + return ($last_contact >= $last_failure); + } + } else { + $poco = ""; + $noscrape = ""; + $version = ""; + $platform = ""; + $site_name = ""; + $info = ""; + $register_policy = -1; + + $last_contact = "0000-00-00 00:00:00"; + $last_failure = "0000-00-00 00:00:00"; + } + logger("Server ".$server_url." is outdated or unknown. Start discovery. Force: ".$force." Created: ".$servers[0]["created"]." Failure: ".$last_failure." Contact: ".$last_contact, LOGGER_DEBUG); + + $failure = false; + $orig_last_failure = $last_failure; + + // Check if the page is accessible via SSL. + $server_url = str_replace("http://", "https://", $server_url); + $serverret = z_fetch_url($server_url."/.well-known/host-meta"); + + // Maybe the page is unencrypted only? + $xmlobj = @simplexml_load_string($serverret["body"],'SimpleXMLElement',0, "http://docs.oasis-open.org/ns/xri/xrd-1.0"); + if (!$serverret["success"] OR ($serverret["body"] == "") OR (@sizeof($xmlobj) == 0) OR !is_object($xmlobj)) { + $server_url = str_replace("https://", "http://", $server_url); + $serverret = z_fetch_url($server_url."/.well-known/host-meta"); + + $xmlobj = @simplexml_load_string($serverret["body"],'SimpleXMLElement',0, "http://docs.oasis-open.org/ns/xri/xrd-1.0"); + } + + if (!$serverret["success"] OR ($serverret["body"] == "") OR (sizeof($xmlobj) == 0) OR !is_object($xmlobj)) { + $last_failure = datetime_convert(); + $failure = true; + } elseif ($network == NETWORK_DIASPORA) + $last_contact = datetime_convert(); + + if (!$failure) { + // Test for Diaspora + $serverret = z_fetch_url($server_url); + + $lines = explode("\n",$serverret["header"]); + if(count($lines)) + foreach($lines as $line) { + $line = trim($line); + if(stristr($line,'X-Diaspora-Version:')) { + $platform = "Diaspora"; + $version = trim(str_replace("X-Diaspora-Version:", "", $line)); + $version = trim(str_replace("x-diaspora-version:", "", $version)); + $network = NETWORK_DIASPORA; + } + } + } + + if (!$failure) { + // Test for Statusnet + // Will also return data for Friendica and GNU Social - but it will be overwritten later + // The "not implemented" is a special treatment for really, really old Friendica versions + $serverret = z_fetch_url($server_url."/api/statusnet/version.json"); + if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND ($serverret["body"] != '') AND (strlen($serverret["body"]) < 250)) { + $platform = "StatusNet"; + $version = trim($serverret["body"], '"'); + $network = NETWORK_OSTATUS; + } + + // Test for GNU Social + $serverret = z_fetch_url($server_url."/api/gnusocial/version.json"); + if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND ($serverret["body"] != '') AND (strlen($serverret["body"]) < 250)) { + $platform = "GNU Social"; + $version = trim($serverret["body"], '"'); + $network = NETWORK_OSTATUS; + } + + $serverret = z_fetch_url($server_url."/api/statusnet/config.json"); + if ($serverret["success"]) { + $data = json_decode($serverret["body"]); + + if (isset($data->site->server)) { + $last_contact = datetime_convert(); + + if (isset($data->site->hubzilla)) { + $platform = $data->site->hubzilla->PLATFORM_NAME; + $version = $data->site->hubzilla->RED_VERSION; + $network = NETWORK_DIASPORA; + } + if (isset($data->site->redmatrix)) { + if (isset($data->site->redmatrix->PLATFORM_NAME)) + $platform = $data->site->redmatrix->PLATFORM_NAME; + elseif (isset($data->site->redmatrix->RED_PLATFORM)) + $platform = $data->site->redmatrix->RED_PLATFORM; + + $version = $data->site->redmatrix->RED_VERSION; + $network = NETWORK_DIASPORA; + } + if (isset($data->site->friendica)) { + $platform = $data->site->friendica->FRIENDICA_PLATFORM; + $version = $data->site->friendica->FRIENDICA_VERSION; + $network = NETWORK_DFRN; + } + + $site_name = $data->site->name; + + $data->site->closed = poco_to_boolean($data->site->closed); + $data->site->private = poco_to_boolean($data->site->private); + $data->site->inviteonly = poco_to_boolean($data->site->inviteonly); + + if (!$data->site->closed AND !$data->site->private and $data->site->inviteonly) + $register_policy = REGISTER_APPROVE; + elseif (!$data->site->closed AND !$data->site->private) + $register_policy = REGISTER_OPEN; + else + $register_policy = REGISTER_CLOSED; + } + } + } + + // Query statistics.json. Optional package for Diaspora, Friendica and Redmatrix + if (!$failure) { + $serverret = z_fetch_url($server_url."/statistics.json"); + if ($serverret["success"]) { + $data = json_decode($serverret["body"]); + if ($version == "") + $version = $data->version; + + $site_name = $data->name; + + if (isset($data->network) AND ($platform == "")) + $platform = $data->network; + + if ($platform == "Diaspora") + $network = NETWORK_DIASPORA; + + if ($data->registrations_open) + $register_policy = REGISTER_OPEN; + else + $register_policy = REGISTER_CLOSED; + + if (isset($data->version)) + $last_contact = datetime_convert(); + } + } + + // Check for noscrape + // Friendica servers could be detected as OStatus servers + if (!$failure AND in_array($network, array(NETWORK_DFRN, NETWORK_OSTATUS))) { + $serverret = z_fetch_url($server_url."/friendica/json"); + + if (!$serverret["success"]) + $serverret = z_fetch_url($server_url."/friendika/json"); + + if ($serverret["success"]) { + $data = json_decode($serverret["body"]); + + if (isset($data->version)) { + $last_contact = datetime_convert(); + $network = NETWORK_DFRN; + + $noscrape = $data->no_scrape_url; + $version = $data->version; + $site_name = $data->site_name; + $info = $data->info; + $register_policy_str = $data->register_policy; + $platform = $data->platform; + + switch ($register_policy_str) { + case "REGISTER_CLOSED": + $register_policy = REGISTER_CLOSED; + break; + case "REGISTER_APPROVE": + $register_policy = REGISTER_APPROVE; + break; + case "REGISTER_OPEN": + $register_policy = REGISTER_OPEN; + break; + } + } + } + } + + // Look for poco + if (!$failure) { + $serverret = z_fetch_url($server_url."/poco"); + if ($serverret["success"]) { + $data = json_decode($serverret["body"]); + if (isset($data->totalResults)) { + $poco = $server_url."/poco"; + $last_contact = datetime_convert(); + } + } + } + + // Check again if the server exists + $servers = q("SELECT `nurl` FROM `gserver` WHERE `nurl` = '%s'", dbesc(normalise_link($server_url))); + + if ($servers) + q("UPDATE `gserver` SET `url` = '%s', `version` = '%s', `site_name` = '%s', `info` = '%s', `register_policy` = %d, `poco` = '%s', `noscrape` = '%s', + `network` = '%s', `platform` = '%s', `last_contact` = '%s', `last_failure` = '%s' WHERE `nurl` = '%s'", + dbesc($server_url), + dbesc($version), + dbesc($site_name), + dbesc($info), + intval($register_policy), + dbesc($poco), + dbesc($noscrape), + dbesc($network), + dbesc($platform), + dbesc($last_contact), + dbesc($last_failure), + dbesc(normalise_link($server_url)) + ); + else + q("INSERT INTO `gserver` (`url`, `nurl`, `version`, `site_name`, `info`, `register_policy`, `poco`, `noscrape`, `network`, `platform`, `created`, `last_contact`, `last_failure`) + VALUES ('%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s')", + dbesc($server_url), + dbesc(normalise_link($server_url)), + dbesc($version), + dbesc($site_name), + dbesc($info), + intval($register_policy), + dbesc($poco), + dbesc($noscrape), + dbesc($network), + dbesc($platform), + dbesc(datetime_convert()), + dbesc($last_contact), + dbesc($last_failure), + dbesc(datetime_convert()) + ); + + logger("End discovery for server ".$server_url, LOGGER_DEBUG); + + return !$failure; +} + function poco_contact_from_body($body, $created, $cid, $uid) { preg_replace_callback("/\[share(.*?)\].*?\[\/share\]/ism", function ($match) use ($created, $cid, $uid){ @@ -327,20 +958,20 @@ function poco_contact_from_body($body, $created, $cid, $uid) { } function sub_poco_from_share($share, $created, $cid, $uid) { - $profile = ""; - preg_match("/profile='(.*?)'/ism", $share[1], $matches); - if ($matches[1] != "") - $profile = $matches[1]; + $profile = ""; + preg_match("/profile='(.*?)'/ism", $share[1], $matches); + if ($matches[1] != "") + $profile = $matches[1]; - preg_match('/profile="(.*?)"/ism', $share[1], $matches); - if ($matches[1] != "") - $profile = $matches[1]; + preg_match('/profile="(.*?)"/ism', $share[1], $matches); + if ($matches[1] != "") + $profile = $matches[1]; if ($profile == "") return; logger("prepare poco_check for profile ".$profile, LOGGER_DEBUG); - poco_check($profile, "", "", "", "", "", "", "", "", $created, 3, $cid, $uid); + poco_check($profile, "", "", "", "", "", "", "", "", $created, 3, $cid, $uid); } function poco_store($item) { @@ -541,6 +1172,7 @@ function suggestion_query($uid, $start = 0, $limit = 80) { and not gcontact.name in ( select name from contact where uid = %d ) and not gcontact.id in ( select gcid from gcign where uid = %d ) AND `gcontact`.`updated` != '0000-00-00 00:00:00' + AND `gcontact`.`last_contact` >= `gcontact`.`last_failure` AND `gcontact`.`network` IN (%s) group by glink.gcid order by gcontact.updated desc,total desc limit %d, %d ", intval($uid), @@ -587,16 +1219,20 @@ function update_suggestions() { $done = array(); + // To-Do: Check if it is really neccessary to poll the own server poco_load(0,0,0,$a->get_baseurl() . '/poco'); $done[] = $a->get_baseurl() . '/poco'; - if(strlen(get_config('system','directory_submit_url'))) { - $x = fetch_url('http://dir.friendica.com/pubsites'); + if(strlen(get_config('system','directory'))) { + $x = fetch_url(get_server()."/pubsites"); if($x) { $j = json_decode($x); if($j->entries) { foreach($j->entries as $entry) { + + poco_check_server($entry->url); + $url = $entry->url . '/poco'; if(! in_array($url,$done)) poco_load(0,0,0,$entry->url . '/poco'); @@ -605,8 +1241,9 @@ function update_suggestions() { } } - $r = q("select distinct(poco) as poco from contact where network = '%s'", - dbesc(NETWORK_DFRN) + // Query your contacts from Friendica and Redmatrix/Hubzilla for their contacts + $r = q("SELECT DISTINCT(`poco`) AS `poco` FROM `contact` WHERE `network` IN ('%s', '%s')", + dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA) ); if(count($r)) { @@ -617,3 +1254,205 @@ function update_suggestions() { } } } + +function poco_discover_federation() { + $last = get_config('poco','last_federation_discovery'); + + if($last) { + $next = $last + (24 * 60 * 60); + if($next > time()) + return; + } + + $serverdata = fetch_url("http://the-federation.info/pods.json"); + + if (!$serverdata) + return; + + $servers = json_decode($serverdata); + + foreach($servers->pods AS $server) + poco_check_server("https://".$server->host); + + set_config('poco','last_federation_discovery', time()); + +} + +function poco_discover($complete = false) { + + // Update the server list + poco_discover_federation(); + + $no_of_queries = 5; + + $requery_days = intval(get_config("system", "poco_requery_days")); + + if ($requery_days == 0) + $requery_days = 7; + + $last_update = date("c", time() - (60 * 60 * 24 * $requery_days)); + + $r = q("SELECT `poco`, `nurl`, `url`, `network` FROM `gserver` WHERE `last_contact` >= `last_failure` AND `poco` != '' AND `last_poco_query` < '%s' ORDER BY RAND()", dbesc($last_update)); + if ($r) + foreach ($r AS $server) { + + if (!poco_check_server($server["url"], $server["network"])) { + // The server is not reachable? Okay, then we will try it later + q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"])); + continue; + } + + // Fetch all users from the other server + $url = $server["poco"]."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation"; + + logger("Fetch all users from the server ".$server["nurl"], LOGGER_DEBUG); + + $retdata = z_fetch_url($url); + if ($retdata["success"]) { + $data = json_decode($retdata["body"]); + + poco_discover_server($data, 2); + + if (get_config('system','poco_discovery') > 1) { + + $timeframe = get_config('system','poco_discovery_since'); + if ($timeframe == 0) + $timeframe = 30; + + $updatedSince = date("Y-m-d H:i:s", time() - $timeframe * 86400); + + // Fetch all global contacts from the other server (Not working with Redmatrix and Friendica versions before 3.3) + $url = $server["poco"]."/@global?updatedSince=".$updatedSince."&fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation"; + + $success = false; + + $retdata = z_fetch_url($url); + if ($retdata["success"]) { + logger("Fetch all global contacts from the server ".$server["nurl"], LOGGER_DEBUG); + $success = poco_discover_server(json_decode($retdata["body"])); + } + + if (!$success AND (get_config('system','poco_discovery') > 2)) { + logger("Fetch contacts from users of the server ".$server["nurl"], LOGGER_DEBUG); + poco_discover_server_users($data, $server); + } + } + + q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"])); + if (!$complete AND (--$no_of_queries == 0)) + break; + } else { + // If the server hadn't replied correctly, then force a sanity check + poco_check_server($server["url"], $server["network"], true); + + // If we couldn't reach the server, we will try it some time later + q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"])); + } + } +} + +function poco_discover_server_users($data, $server) { + + if (!isset($data->entry)) + return; + + foreach ($data->entry AS $entry) { + $username = ""; + if (isset($entry->urls)) { + foreach($entry->urls as $url) + if($url->type == 'profile') { + $profile_url = $url->value; + $urlparts = parse_url($profile_url); + $username = end(explode("/", $urlparts["path"])); + } + } + if ($username != "") { + logger("Fetch contacts for the user ".$username." from the server ".$server["nurl"], LOGGER_DEBUG); + + // Fetch all contacts from a given user from the other server + $url = $server["poco"]."/".$username."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation"; + + $retdata = z_fetch_url($url); + if ($retdata["success"]) + poco_discover_server(json_decode($retdata["body"]), 3); + } + } +} + +function poco_discover_server($data, $default_generation = 0) { + + if (!isset($data->entry) OR !count($data->entry)) + return false; + + $success = false; + + foreach ($data->entry AS $entry) { + $profile_url = ''; + $profile_photo = ''; + $connect_url = ''; + $name = ''; + $network = ''; + $updated = '0000-00-00 00:00:00'; + $location = ''; + $about = ''; + $keywords = ''; + $gender = ''; + $generation = $default_generation; + + $name = $entry->displayName; + + if(isset($entry->urls)) { + foreach($entry->urls as $url) { + if($url->type == 'profile') { + $profile_url = $url->value; + continue; + } + if($url->type == 'webfinger') { + $connect_url = str_replace('acct:' , '', $url->value); + continue; + } + } + } + + if(isset($entry->photos)) { + foreach($entry->photos as $photo) { + if($photo->type == 'profile') { + $profile_photo = $photo->value; + continue; + } + } + } + + if(isset($entry->updated)) + $updated = date("Y-m-d H:i:s", strtotime($entry->updated)); + + if(isset($entry->network)) + $network = $entry->network; + + if(isset($entry->currentLocation)) + $location = $entry->currentLocation; + + if(isset($entry->aboutMe)) + $about = html2bbcode($entry->aboutMe); + + if(isset($entry->gender)) + $gender = $entry->gender; + + if(isset($entry->generation) AND ($entry->generation > 0)) + $generation = ++$entry->generation; + + if(isset($entry->tags)) + foreach($entry->tags as $tag) + $keywords = implode(", ", $tag); + + if ($generation > 0) { + $success = true; + + logger("Store profile ".$profile_url, LOGGER_DEBUG); + poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, 0, 0, 0); + logger("Done for profile ".$profile_url, LOGGER_DEBUG); + } + } + return $success; +} +?> diff --git a/include/text.php b/include/text.php index 8ce54b3c6..0002f074e 100644 --- a/include/text.php +++ b/include/text.php @@ -2,8 +2,10 @@ require_once("include/template_processor.php"); require_once("include/friendica_smarty.php"); +require_once("include/map.php"); require_once("mod/proxy.php"); + if(! function_exists('replace_macros')) { /** * This is our template processor @@ -218,7 +220,7 @@ function xmlify($str) { $buffer = mb_ereg_replace("<", "<", $buffer); $buffer = mb_ereg_replace(">", ">", $buffer); */ - $buffer = htmlspecialchars($str, ENT_QUOTES); + $buffer = htmlspecialchars($str, ENT_QUOTES, "UTF-8"); $buffer = trim($buffer); return($buffer); @@ -468,11 +470,17 @@ if(! function_exists('item_new_uri')) { * @param int $uid * @return string */ -function item_new_uri($hostname,$uid) { +function item_new_uri($hostname,$uid, $guid = "") { do { $dups = false; - $hash = random_string(); + + if ($guid == "") + $hash = get_guid(32); + else { + $hash = $guid; + $guid = ""; + } $uri = "urn:X-dfrn:" . $hostname . ':' . $uid . ':' . $hash; @@ -833,10 +841,16 @@ function get_mentions($item) { foreach($arr as $x) { $matches = null; if(preg_match('/@\[url=([^\]]*)\]/',$x,$matches)) { - $o .= "\t\t" . '' . "\r\n"; $o .= "\t\t" . '' . "\r\n"; + $o .= "\t\t" . '' . "\r\n"; } } + + if (!$item['private']) { + $o .= "\t\t".''."\r\n"; + $o .= "\t\t".''."\r\n"; + } + return $o; }} @@ -860,8 +874,14 @@ function contact_block() { if((! is_array($a->profile)) || ($a->profile['hide-friends'])) return $o; - $r = q("SELECT COUNT(*) AS `total` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0 AND `hidden` = 0 AND `archive` = 0", - intval($a->profile['uid']) + $r = q("SELECT COUNT(*) AS `total` FROM `contact` + WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0 + AND `hidden` = 0 AND `archive` = 0 + AND `network` IN ('%s', '%s', '%s')", + intval($a->profile['uid']), + dbesc(NETWORK_DFRN), + dbesc(NETWORK_OSTATUS), + dbesc(NETWORK_DIASPORA) ); if(count($r)) { $total = intval($r[0]['total']); @@ -871,8 +891,14 @@ function contact_block() { $micropro = Null; } else { - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0 AND `hidden` = 0 AND `archive` = 0 ORDER BY RAND() LIMIT %d", + $r = q("SELECT * FROM `contact` + WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0 + AND `hidden` = 0 AND `archive` = 0 + AND `network` IN ('%s', '%s', '%s') ORDER BY RAND() LIMIT %d", intval($a->profile['uid']), + dbesc(NETWORK_DFRN), + dbesc(NETWORK_OSTATUS), + dbesc(NETWORK_DIASPORA), intval($shown) ); if(count($r)) { @@ -920,7 +946,7 @@ function micropro($contact, $redirect = false, $class = '', $textmode = false) { if($redirect) { $a = get_app(); $redirect_url = $a->get_baseurl() . '/redir/' . $contact['id']; - if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === 'dfrn')) { + if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === NETWORK_DFRN)) { $redir = true; $url = $redirect_url; $sparkle = ' sparkle'; @@ -958,19 +984,31 @@ if(! function_exists('search')) { * @param string $s search query * @param string $id html id * @param string $url search url - * @param boolean $save show save search button - * @return string html for search box #FIXME: remove html + * @param boolean $savedsearch show save search button */ -function search($s,$id='search-box',$url='/search',$save = false) { +function search($s,$id='search-box',$url='/search',$save = false, $aside = true) { $a = get_app(); - $o = '
'; - $o .= '
'; - $o .= ''; - $o .= ''; - if($save) - $o .= ''; - $o .= '
'; - return $o; + + $values = array( + '$s' => $s, + '$id' => $id, + '$action_url' => $a->get_baseurl((stristr($url,'network')) ? true : false) . $url, + '$search_label' => t('Search'), + '$save_label' => t('Save'), + '$savedsearch' => feature_enabled(local_user(),'savedsearch'), + ); + + if (!$aside) { + $values['$searchoption'] = array( + t("Full Text"), + t("Tags"), + t("Contacts")); + + if (get_config('system','poco_local_search')) + $values['$searchoption'][] = t("Forums"); + } + + return replace_macros(get_markup_template('searchbox.tpl'), $values); }} if(! function_exists('valid_email')) { @@ -1131,9 +1169,9 @@ function smilies($s, $sample = false) { ); $icons = array( - '<3', - '</3', - '<\\3', + '<3', + '</3', + '<\\3', ':-)', ';-)', ':-(', @@ -1210,7 +1248,7 @@ function preg_heart($x) { return $x[0]; $t = ''; for($cnt = 0; $cnt < strlen($x[1]); $cnt ++) - $t .= '<3'; + $t .= '<3'; $r = str_replace($x[0],$t,$x[0]); return $r; } @@ -1461,6 +1499,14 @@ function prepare_body(&$item,$attach = false, $preview = false) { } $s = $s . $as; + // map + if(strpos($s,'
') !== false && $item['coord']) { + $x = generate_map(trim($item['coord'])); + if($x) { + $s = preg_replace('/\
/','$0' . $x,$s); + } + } + // Look for spoiler $spoilersearch = '
'; @@ -1661,11 +1707,14 @@ function get_plink($item) { //'href' => $a->get_baseurl()."/display/".$a->user['nickname']."/".$item['id'], 'href' => $a->get_baseurl()."/display/".$item['guid'], 'orig' => $a->get_baseurl()."/display/".$item['guid'], - 'title' => t('link to source'), + 'title' => t('View on separate page'), + 'orig_title' => t('view on separate page'), ); - if (x($item,'plink')) + if (x($item,'plink')) { $ret["href"] = $item['plink']; + $ret["title"] = t('link to source'); + } } elseif (x($item,'plink') && ($item['private'] != 1)) $ret = array( @@ -1758,7 +1807,7 @@ function return_bytes ($size_str) { function generate_user_guid() { $found = true; do { - $guid = random_string(16); + $guid = get_guid(32); $x = q("SELECT `uid` FROM `user` WHERE `guid` = '%s' LIMIT 1", dbesc($guid) ); @@ -2267,3 +2316,15 @@ function deindent($text, $chr="[\t ]", $count=NULL) { return implode("\n", $lines); } + +function formatBytes($bytes, $precision = 2) { + $units = array('B', 'KB', 'MB', 'GB', 'TB'); + + $bytes = max($bytes, 0); + $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); + $pow = min($pow, count($units) - 1); + + $bytes /= pow(1024, $pow); + + return round($bytes, $precision) . ' ' . $units[$pow]; +} diff --git a/include/threads.php b/include/threads.php index d88307ecf..3cfa02564 100644 --- a/include/threads.php +++ b/include/threads.php @@ -112,7 +112,7 @@ function update_thread_uri($itemuri, $uid) { } function update_thread($itemid, $setmention = false) { - $items = q("SELECT `uid`, `uri`, `created`, `edited`, `commented`, `received`, `changed`, `wall`, `private`, `pubmail`, `moderated`, `visible`, `spam`, `starred`, `bookmark`, `contact-id`, + $items = q("SELECT `uid`, `guid`, `title`, `body`, `created`, `edited`, `commented`, `received`, `changed`, `wall`, `private`, `pubmail`, `moderated`, `visible`, `spam`, `starred`, `bookmark`, `contact-id`, `deleted`, `origin`, `forum_mode`, `network` FROM `item` WHERE `id` = %d AND (`parent` = %d OR `parent` = 0) LIMIT 1", intval($itemid), intval($itemid)); if (!$items) @@ -126,7 +126,7 @@ function update_thread($itemid, $setmention = false) { $sql = ""; foreach ($item AS $field => $data) - if ($field != "uri") { + if (!in_array($field, array("guid", "title", "body"))) { if ($sql != "") $sql .= ", "; @@ -135,40 +135,20 @@ function update_thread($itemid, $setmention = false) { $result = q("UPDATE `thread` SET ".$sql." WHERE `iid` = %d", intval($itemid)); - logger("update_thread: Update thread for item ".$itemid." - ".print_r($result, true)." ".print_r($item, true), LOGGER_DEBUG); + logger("Update thread for item ".$itemid." - guid ".$item["guid"]." - ".print_r($result, true)." ".print_r($item, true), LOGGER_DEBUG); // Updating a shadow item entry - $items = q("SELECT `id`, `title`, `body`, `created`, `edited`, `commented`, `received`, `changed`, `wall`, `private`, `pubmail`, - `moderated`, `visible`, `spam`, `starred`, `bookmark`, `deleted`, `origin`, `forum_mode`, `network` - FROM `item` WHERE `uri` = '%s' AND `uid` = 0 LIMIT 1", dbesc($item["uri"])); + $items = q("SELECT `id` FROM `item` WHERE `guid` = '%s' AND `uid` = 0 LIMIT 1", dbesc($item["guid"])); if (!$items) return; - $item = $items[0]; - - $result = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `network` = '%s' WHERE `id` = %d", + $result = q("UPDATE `item` SET `title` = '%s', `body` = '%s' WHERE `id` = %d", dbesc($item["title"]), dbesc($item["body"]), - dbesc($item["network"]), - intval($item["id"]) + intval($items[0]["id"]) ); - logger("update_thread: Updating public shadow for post ".$item["id"]." Result: ".print_r($result, true), LOGGER_DEBUG); - - /* - $sql = ""; - - foreach ($item AS $field => $data) - if ($field != "id") { - if ($sql != "") - $sql .= ", "; - - $sql .= "`".$field."` = '".dbesc($data)."'"; - } - //logger("update_thread: Updating public shadow for post ".$item["id"]." SQL: ".$sql, LOGGER_DEBUG); - $result = q("UPDATE `item` SET ".$sql." WHERE `id` = %d", intval($item["id"])); - logger("update_thread: Updating public shadow for post ".$item["id"]." Result: ".print_r($result, true), LOGGER_DEBUG); - */ + logger("Updating public shadow for post ".$items[0]["id"]." - guid ".$item["guid"]." Result: ".print_r($result, true), LOGGER_DEBUG); } function delete_thread_uri($itemuri, $uid) { diff --git a/include/uimport.php b/include/uimport.php index 6808c706f..78471af15 100644 --- a/include/uimport.php +++ b/include/uimport.php @@ -156,7 +156,9 @@ function import_account(&$a, $file) { $newuid = last_insert_id(); //~ $newuid = 1; - + // Generate a new guid for the account. Otherwise there will be problems with diaspora + q("UPDATE `user` SET `guid` = '%s' WHERE `uid` = %d", + dbesc(generate_user_guid()), intval($newuid)); foreach ($account['profile'] as &$profile) { foreach ($profile as $k => &$v) { diff --git a/include/user.php b/include/user.php index f8b1578ce..d989664ac 100644 --- a/include/user.php +++ b/include/user.php @@ -27,11 +27,19 @@ function create_user($arr) { $openid_url = ((x($arr,'openid_url')) ? notags(trim($arr['openid_url'])) : ''); $photo = ((x($arr,'photo')) ? notags(trim($arr['photo'])) : ''); $password = ((x($arr,'password')) ? trim($arr['password']) : ''); + $password1 = ((x($arr,'password1')) ? trim($arr['password1']) : ''); + $confirm = ((x($arr,'confirm')) ? trim($arr['confirm']) : ''); $blocked = ((x($arr,'blocked')) ? intval($arr['blocked']) : 0); $verified = ((x($arr,'verified')) ? intval($arr['verified']) : 0); $publish = ((x($arr,'profile_publish_reg') && intval($arr['profile_publish_reg'])) ? 1 : 0); - $netpublish = ((strlen(get_config('system','directory_submit_url'))) ? $publish : 0); + $netpublish = ((strlen(get_config('system','directory'))) ? $publish : 0); + + if ($password1 != $confirm) { + $result['message'] .= t('Passwords do not match. Password unchanged.') . EOL; + return $result; + } elseif ($password1 != "") + $password = $password1; $tmp_str = $openid_url; @@ -128,8 +136,8 @@ function create_user($arr) { $nickname = $arr['nickname'] = strtolower($nickname); - if(! preg_match("/^[a-z][a-z0-9\-\_]*$/",$nickname)) - $result['message'] .= t('Your "nickname" can only contain "a-z", "0-9", "-", and "_", and must also begin with a letter.') . EOL; + if(! preg_match("/^[a-z0-9][a-z0-9\_]*$/",$nickname)) + $result['message'] .= t('Your "nickname" can only contain "a-z", "0-9" and "_".') . EOL; $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1", dbesc($nickname) diff --git a/index.php b/index.php index a4a862443..02316e1e9 100644 --- a/index.php +++ b/index.php @@ -53,6 +53,20 @@ if(!$install) { load_config('config'); load_config('system'); + $maxsysload_frontend = intval(get_config('system','maxloadavg_frontend')); + if($maxsysload_frontend < 1) + $maxsysload_frontend = 50; + if(function_exists('sys_getloadavg')) { + $load = sys_getloadavg(); + if(intval($load[0]) > $maxsysload_frontend) { + logger('system: load ' . $load[0] . ' too high. Service Temporarily Unavailable.'); + header($_SERVER["SERVER_PROTOCOL"].' 503 Service Temporarily Unavailable'); + header('Retry-After: 300'); + die("System is currently unavailable. Please try again later"); + } + } + + if (get_config('system','force_ssl') AND ($a->get_scheme() == "http") AND (intval(get_config('system','ssl_policy')) == SSL_POLICY_FULL) AND (substr($a->get_baseurl(), 0, 8) == "https://")) { diff --git a/js/filebrowser.js b/js/filebrowser.js new file mode 100644 index 000000000..66db2833c --- /dev/null +++ b/js/filebrowser.js @@ -0,0 +1,142 @@ +/** + * Filebrowser - Friendica Communications Server + * + * Copyright (c) 2010-2013 the Friendica Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code handle user interaction for image/file upload/browser dialog. + * Is loaded from filebrowser_plain.tpl + * + * To load filebrowser in colorbox, call + * + * $.colorbox({href: ulr, iframe:true,innerWidth:'500px',innerHeight:'400px'}) + * + * where url is: + * + * /fbrowser//?mode=minimal[#-] + * + * baseurl: baseurl from friendica + * type: one of "image", "file" + * eventname: event name to catch return value + * id: id returned to event handler + * + * When user select an item, an event in fired in parent page, on body element + * The event is named + * + * fbrowser..[] + * + * with params: + * + * filemane: filename of item choosed by user + * embed: bbcode to embed element into posts + * id: id from url + * + * example: + * + * // open dialog for select an image for a textarea with id "myeditor" + * var id="myeditor"; + * $.colorbox({href: baseurl + "/fbrowser/image/?mode=minimal#example-"+id, iframe:true,innerWidth:'500px',innerHeight:'400px'}) + * + * // setup event handler to get user selection + * $("body").on("fbrowser.image.example", function(event, filename, bbcode, id) { + * // close colorbox + * $.colorbox.close(); + * // replace textxarea text with bbcode + * $(id).value = bbcode; + * }); + **/ + +var FileBrowser = { + nickname : "", + type : "", + event: "", + id : null, + + init: function(nickname, type) { + FileBrowser.nickname = nickname; + FileBrowser.type = type; + FileBrowser.event = "fbrowser."+type; + if (location['hash']!=="") { + var h = location['hash'].replace("#",""); + FileBrowser.event = FileBrowser.event + "." + h.split("-")[0]; + FileBrowser.id = h.split("-")[1]; + } + + console.log("FileBrowser:", nickname, type,FileBrowser.event, FileBrowser.id ); + + $(".error a.close").on("click", function(e) { + e.preventDefault(); + $(".error").addClass("hidden"); + }); + + $(".folders a, .path a").on("click", function(e){ + e.preventDefault(); + var url = baseurl + "/fbrowser/" + FileBrowser.type + "/" + this.dataset.folder + "?mode=minimal" + location['hash']; + location.href = url; + }); + + $(".photo-album-photo-link").on('click', function(e){ + e.preventDefault(); + + var embed = ""; + if (FileBrowser.type == "image") { + embed = "[url="+this.dataset.link+"][img]"+this.dataset.img+"[/img][/url]"; + } + if (FileBrowser.type=="file") { + // attachment links are "baseurl/attach/id"; we need id + embed = "[attachment]"+this.dataset.link.split("/").pop()+"[/attachment]"; + } + console.log(FileBrowser.event, this.dataset.filename, embed, FileBrowser.id); + parent.$("body").trigger(FileBrowser.event, [ + this.dataset.filename, + embed, + FileBrowser.id + ]); + + }); + + if ($("#upload-image").length) + var image_uploader = new window.AjaxUpload( + 'upload-image', + { action: 'wall_upload/'+FileBrowser.nickname+'?response=json', + name: 'userfile', + responseType: 'json', + onSubmit: function(file,ext) { $('#profile-rotator').show(); $(".error").addClass('hidden'); }, + onComplete: function(file,response) { + if (response['error']!= undefined) { + $(".error span").html(response['error']); + $(".error").removeClass('hidden'); + $('#profile-rotator').hide(); + return; + } + location = baseurl + "/fbrowser/image/?mode=minimal"+location['hash']; + location.reload(true); + } + } + ); + + if ($("#upload-file").length) + var file_uploader = new window.AjaxUpload( + 'upload-file', + { action: 'wall_attach/'+FileBrowser.nickname+'?response=json', + name: 'userfile', + onSubmit: function(file,ext) { $('#profile-rotator').show(); $(".error").addClass('hidden'); }, + onComplete: function(file,response) { + if (response['error']!= undefined) { + $(".error span").html(response['error']); + $(".error").removeClass('hidden'); + $('#profile-rotator').hide(); + return; + } + location = baseurl + "/fbrowser/file/?mode=minimal"+location['hash']; + location.reload(true); + } + } + ); + } +}; + diff --git a/js/main.js b/js/main.js index 5f4b56f4d..e1e852cba 100644 --- a/js/main.js +++ b/js/main.js @@ -1,3 +1,7 @@ + function resizeIframe(obj) { + obj.style.height = 0; + obj.style.height = obj.contentWindow.document.body.scrollHeight + 'px'; + } function openClose(theID) { if(document.getElementById(theID).style.display == "block") { @@ -47,6 +51,38 @@ e.tipTip({defaultPosition: pos, edgeOffset: 8}); });*/ + /* setup comment textarea buttons */ + /* comment textarea buttons needs some "data-*" attributes to work: + * data-role="insert-formatting" : to mark the element as a formatting button + * data-comment="" : string for "Comment", used by insertFormatting() function + * data-bbcode="" : name of the bbcode element to insert. insertFormatting() will insert it as "[name][/name]" + * data-id="" : id of the comment, used to find other comment-related element, like the textarea + * */ + $('body').on('click','[data-role="insert-formatting"]', function(e) { + e.preventDefault(); + var o = $(this); + var comment = o.data('comment'); + var bbcode = o.data('bbcode'); + var id = o.data('id'); + if (bbcode=="img") { + $.colorbox({href: baseurl + "/fbrowser/image/?mode=minimal#comment-"+id, iframe:true,innerWidth:'500px',innerHeight:'400px'}) + return; + } + + insertFormatting(comment, bbcode, id); + }); + + /* event from comment textarea button popups */ + /* insert returned bbcode at cursor position or replace selected text */ + $("body").on("fbrowser.image.comment", function(e, filename, bbcode, id) { + console.log("on", id); + $.colorbox.close(); + var textarea = document.getElementById("comment-edit-text-" +id); + var start = textarea.selectionStart; + var end = textarea.selectionEnd; + textarea.value = textarea.value.substring(0, start) + bbcode + textarea.value.substring(end, textarea.value.length); + }); + /* setup onoff widgets */ @@ -71,14 +107,14 @@ setupFieldRichtext(); /* popup menus */ - function close_last_popup_menu() { - if(last_popup_menu) { - last_popup_menu.hide(); - last_popup_button.removeClass("selected"); - last_popup_menu = null; - last_popup_button = null; - } - } + function close_last_popup_menu() { + if(last_popup_menu) { + last_popup_menu.hide(); + last_popup_button.removeClass("selected"); + last_popup_menu = null; + last_popup_button = null; + } + } $('a[rel^=#]').click(function(e){ e.preventDefault(); var parent = $(this).parent(); @@ -101,7 +137,7 @@ return false; }); $('html').click(function() { - close_last_popup_menu(); + close_last_popup_menu(); }); // fancyboxes @@ -177,12 +213,41 @@ nnm = $("#nav-notifications-menu"); nnm.html(notifications_all + notifications_mark); //nnm.attr('popup','true'); + + var notification_lastitem = parseInt(localStorage.getItem("notification-lastitem")); + var notification_id = 0; eNotif.children("note").each(function(){ e = $(this); - text = e.text().format(""+e.attr('name')+""); - html = notifications_tpl.format(e.attr('href'),e.attr('photo'), text, e.attr('date'), e.attr('seen')); + var text = e.text().format(""+e.attr('name')+""); + var seenclass = (e.attr('seen')==1)?"notify-seen":"notify-unseen"; + var html = notifications_tpl.format(e.attr('href'), + e.attr('photo'), // {0} + text, // {1} + e.attr('date'), // {2} + seenclass, // {3} + new Date(e.attr('timestamp')*1000) // {4} + ); nnm.append(html); }); + $(eNotif.children("note").get().reverse()).each(function(){ + e = $(this); + notification_id = parseInt(e.attr('timestamp')); + if (notification_lastitem!== null && notification_id > notification_lastitem) { + if (getNotificationPermission()==="granted") { + var notification = new Notification(document.title, { + body: e.text().replace('→ ','').format(e.attr('name')), + icon: e.attr('photo'), + }); + notification['url'] = e.attr('href'); + notification.addEventListener("click", function(ev){ + window.location = ev.target.url; + }); + } + } + + }); + notification_lastitem = notification_id; + localStorage.setItem("notification-lastitem", notification_lastitem) $("img[data-src]", nnm).each(function(i, el){ // Add src attribute for images with a data-src attribute @@ -746,3 +811,18 @@ function previewTheme(elm) { }); } + +// notification permission settings in localstorage +// set by settings page +function getNotificationPermission() { + if (window["Notification"] === undefined) { + return null; + } + if (Notification.permission === 'granted') { + var val = localStorage.getItem('notification-permissions'); + if (val === null) return 'denied'; + return val; + } else { + return Notification.permission; + } +} diff --git a/library/datetimepicker/MIT-LICENSE.txt b/library/datetimepicker/MIT-LICENSE.txt new file mode 100644 index 000000000..2e68e7d6a --- /dev/null +++ b/library/datetimepicker/MIT-LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2013 http://xdsoft.net + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/library/datetimepicker/README.md b/library/datetimepicker/README.md new file mode 100644 index 000000000..f0d3f40e3 --- /dev/null +++ b/library/datetimepicker/README.md @@ -0,0 +1,20 @@ +datetimepicker +============== +[Documentation][doc] + + +jQuery Plugin Date and Time Picker + +DateTimePicker + +![ScreenShot](https://raw2.github.com/xdan/datetimepicker/master/screen/1.png) + +DatePicker + +![ScreenShot](https://raw2.github.com/xdan/datetimepicker/master/screen/2.png) + +TimePicker + +![ScreenShot](https://raw2.github.com/xdan/datetimepicker/master/screen/3.png) + +[doc]: http://xdsoft.net/jqplugins/datetimepicker/ diff --git a/library/datetimepicker/jquery.datetimepicker.css b/library/datetimepicker/jquery.datetimepicker.css new file mode 100644 index 000000000..acf674af7 --- /dev/null +++ b/library/datetimepicker/jquery.datetimepicker.css @@ -0,0 +1,418 @@ +.xdsoft_datetimepicker{ + box-shadow: 0px 5px 15px -5px rgba(0, 0, 0, 0.506); + background: #FFFFFF; + border-bottom: 1px solid #BBBBBB; + border-left: 1px solid #CCCCCC; + border-right: 1px solid #CCCCCC; + border-top: 1px solid #CCCCCC; + color: #333333; + font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif; + padding: 8px; + padding-left: 0px; + padding-top: 2px; + position: absolute; + z-index: 9999; + -moz-box-sizing: border-box; + box-sizing: border-box; + display:none; +} + +.xdsoft_datetimepicker iframe { + position: absolute; + left: 0; + top: 0; + width: 75px; + height: 210px; + background: transparent; + border:none; +} +/*For IE8 or lower*/ +.xdsoft_datetimepicker button { + border:none !important; +} + +.xdsoft_noselect{ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} +.xdsoft_noselect::selection { background: transparent; } +.xdsoft_noselect::-moz-selection { background: transparent; } +.xdsoft_datetimepicker.xdsoft_inline{ + display: inline-block; + position: static; + box-shadow: none; +} +.xdsoft_datetimepicker *{ + -moz-box-sizing: border-box; + box-sizing: border-box; + padding:0px; + margin:0px; +} +.xdsoft_datetimepicker .xdsoft_datepicker, .xdsoft_datetimepicker .xdsoft_timepicker{ + display:none; +} +.xdsoft_datetimepicker .xdsoft_datepicker.active, .xdsoft_datetimepicker .xdsoft_timepicker.active{ + display:block; +} +.xdsoft_datetimepicker .xdsoft_datepicker{ + width: 224px; + float:left; + margin-left:8px; +} +.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_datepicker{ + width: 256px; +} +.xdsoft_datetimepicker .xdsoft_timepicker{ + width: 58px; + float:left; + text-align:center; + margin-left:8px; + margin-top:0px; +} +.xdsoft_datetimepicker .xdsoft_datepicker.active+.xdsoft_timepicker{ + margin-top:8px; + margin-bottom:3px +} +.xdsoft_datetimepicker .xdsoft_mounthpicker{ + position: relative; + text-align: center; +} + +.xdsoft_datetimepicker .xdsoft_label i, +.xdsoft_datetimepicker .xdsoft_prev, +.xdsoft_datetimepicker .xdsoft_next, +.xdsoft_datetimepicker .xdsoft_today_button{ + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Q0NBRjI1NjM0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Q0NBRjI1NjQ0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDQ0FGMjU2MTQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDQ0FGMjU2MjQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoNEP54AAAIOSURBVHja7Jq9TsMwEMcxrZD4WpBYeKUCe+kTMCACHZh4BFfHO/AAIHZGFhYkBBsSEqxsLCAgXKhbXYOTxh9pfJVP+qutnZ5s/5Lz2Y5I03QhWji2GIcgAokWgfCxNvcOCCGKqiSqhUp0laHOne05vdEyGMfkdxJDVjgwDlEQgYQBgx+ULJaWSXXS6r/ER5FBVR8VfGftTKcITNs+a1XpcFoExREIDF14AVIFxgQUS+h520cdud6wNkC0UBw6BCO/HoCYwBhD8QCkQ/x1mwDyD4plh4D6DDV0TAGyo4HcawLIBBSLDkHeH0Mg2yVP3l4TQMZQDDsEOl/MgHQqhMNuE0D+oBh0CIr8MAKyazBH9WyBuKxDWgbXfjNf32TZ1KWm/Ap1oSk/R53UtQ5xTh3LUlMmT8gt6g51Q9p+SobxgJQ/qmsfZhWywGFSl0yBjCLJCMgXail3b7+rumdVJ2YRss4cN+r6qAHDkPWjPjdJCF4n9RmAD/V9A/Wp4NQassDjwlB6XBiCxcJQWmZZb8THFilfy/lfrTvLghq2TqTHrRMTKNJ0sIhdo15RT+RpyWwFdY96UZ/LdQKBGjcXpcc1AlSFEfLmouD+1knuxBDUVrvOBmoOC/rEcN7OQxKVeJTCiAdUzUJhA2Oez9QTkp72OTVcxDcXY8iKNkxGAJXmJCOQwOa6dhyXsOa6XwEGAKdeb5ET3rQdAAAAAElFTkSuQmCC); +} + +.xdsoft_datetimepicker .xdsoft_label i{ + opacity:0.5; + background-position:-92px -19px; + display: inline-block; + width: 9px; + height: 20px; + vertical-align: middle; +} + +.xdsoft_datetimepicker .xdsoft_prev{ + float: left; + background-position:-20px 0px; +} +.xdsoft_datetimepicker .xdsoft_today_button{ + float: left; + background-position:-70px 0px; + margin-left:5px; +} + +.xdsoft_datetimepicker .xdsoft_next{ + float: right; + background-position:0px 0px; +} + +.xdsoft_datetimepicker .xdsoft_next, +.xdsoft_datetimepicker .xdsoft_prev , +.xdsoft_datetimepicker .xdsoft_today_button{ + background-color: transparent; + background-repeat: no-repeat; + border: 0px none currentColor; + cursor: pointer; + display: block; + height: 30px; + opacity: 0.5; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + outline: medium none currentColor; + overflow: hidden; + padding: 0px; + position: relative; + text-indent: 100%; + white-space: nowrap; + width: 20px; +} +.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev, +.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_next{ + float:none; + background-position:-40px -15px; + height: 15px; + width: 30px; + display: block; + margin-left:14px; + margin-top:7px; +} +.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev{ + background-position:-40px 0px; + margin-bottom:7px; + margin-top:0px; +} +.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box{ + height:151px; + overflow:hidden; + border-bottom:1px solid #DDDDDD; +} +.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div{ + background: #F5F5F5; + border-top:1px solid #DDDDDD; + color: #666666; + font-size: 12px; + text-align: center; + border-collapse:collapse; + cursor:pointer; + border-bottom-width:0px; + height:25px; + line-height:25px; +} + +.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div > div:first-child{ + border-top-width:0px; +} +.xdsoft_datetimepicker .xdsoft_today_button:hover, +.xdsoft_datetimepicker .xdsoft_next:hover, +.xdsoft_datetimepicker .xdsoft_prev:hover { + opacity: 1; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; +} +.xdsoft_datetimepicker .xdsoft_label{ + display: inline; + position: relative; + z-index: 9999; + margin: 0; + padding: 5px 3px; + font-size: 14px; + line-height: 20px; + font-weight: bold; + background-color: #fff; + float:left; + width:182px; + text-align:center; + cursor:pointer; +} +.xdsoft_datetimepicker .xdsoft_label:hover>span{ + text-decoration:underline; +} +.xdsoft_datetimepicker .xdsoft_label:hover i{ + opacity:1.0; +} +.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select{ + border:1px solid #ccc; + position:absolute; + right:0px; + top:30px; + z-index:101; + display:none; + background:#fff; + max-height:160px; + overflow-y:hidden; +} +.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select.xdsoft_monthselect{right:-7px;} +.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select.xdsoft_yearselect{right:2px;} +.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option:hover{ + color: #fff; + background: #ff8000; +} +.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option{ + padding:2px 10px 2px 5px; + text-decoration:none !important; +} +.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current{ + background: #33AAFF; + box-shadow: #178FE5 0px 1px 3px 0px inset; + color:#fff; + font-weight: 700; +} +.xdsoft_datetimepicker .xdsoft_month{ + width:100px; + text-align:right; +} +.xdsoft_datetimepicker .xdsoft_calendar{ + clear:both; +} +.xdsoft_datetimepicker .xdsoft_year{ + width: 48px; + margin-left: 5px; +} +.xdsoft_datetimepicker .xdsoft_calendar table{ + border-collapse:collapse; + width:100%; + +} +.xdsoft_datetimepicker .xdsoft_calendar td > div{ + padding-right:5px; +} +.xdsoft_datetimepicker .xdsoft_calendar th{ + height: 25px; +} +.xdsoft_datetimepicker .xdsoft_calendar td,.xdsoft_datetimepicker .xdsoft_calendar th{ + width:14.2857142%; + background: #F5F5F5; + border:1px solid #DDDDDD; + color: #666666; + font-size: 12px; + text-align: right; + vertical-align: middle; + padding:0px; + border-collapse:collapse; + cursor:pointer; + height: 25px; +} +.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar td,.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar th{ + width:12.5%; +} +.xdsoft_datetimepicker .xdsoft_calendar th{ + background: #F1F1F1; +} +.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_today{ + color:#33AAFF; +} +.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_default, +.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current, +.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_current{ + background: #33AAFF; + box-shadow: #178FE5 0px 1px 3px 0px inset; + color:#fff; + font-weight: 700; +} +.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month, +.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled, +.xdsoft_datetimepicker .xdsoft_time_box >div >div.xdsoft_disabled{ + opacity:0.5; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; +} +.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month.xdsoft_disabled{ + opacity:0.2; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)"; +} +.xdsoft_datetimepicker .xdsoft_calendar td:hover, +.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div:hover{ + color: #fff !important; + background: #ff8000 !important; + box-shadow: none !important; +} +.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled:hover, +.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_disabled:hover{ + color: inherit !important; + background: inherit !important; + box-shadow: inherit !important; +} +.xdsoft_datetimepicker .xdsoft_calendar th{ + font-weight: 700; + text-align: center; + color: #999; + cursor:default; +} +.xdsoft_datetimepicker .xdsoft_copyright{ color:#ccc !important; font-size:10px;clear:both;float:none;margin-left:8px;} +.xdsoft_datetimepicker .xdsoft_copyright a{ color:#eee !important;} +.xdsoft_datetimepicker .xdsoft_copyright a:hover{ color:#aaa !important;} + + +.xdsoft_time_box{ + position:relative; + border:1px solid #ccc; +} +.xdsoft_scrollbar >.xdsoft_scroller{ + background:#ccc !important; + height:20px; + border-radius:3px; +} +.xdsoft_scrollbar{ + position:absolute; + width:7px; + right:0px; + top:0px; + bottom:0px; + cursor:pointer; +} +.xdsoft_scroller_box{ +position:relative; +} + + +.xdsoft_datetimepicker.xdsoft_dark{ + box-shadow: 0px 5px 15px -5px rgba(255, 255, 255, 0.506); + background: #000000; + border-bottom: 1px solid #444444; + border-left: 1px solid #333333; + border-right: 1px solid #333333; + border-top: 1px solid #333333; + color: #cccccc; +} + +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box{ + border-bottom:1px solid #222222; +} +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div{ + background: #0a0a0a; + border-top:1px solid #222222; + color: #999999; +} + +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label{ + background-color: #000; +} +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select{ + border:1px solid #333; + background:#000; +} + +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select > div > .xdsoft_option:hover{ + color: #000; + background: #007fff; +} + +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current{ + background: #cc5500; + box-shadow: #b03e00 0px 1px 3px 0px inset; + color:#000; +} + +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label i, +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_prev, +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_next, +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_today_button{ + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUExQUUzOTA0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUExQUUzOTE0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQTFBRTM4RTQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQTFBRTM4RjQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pp0VxGEAAAIASURBVHja7JrNSgMxEMebtgh+3MSLr1T1Xn2CHoSKB08+QmR8Bx9A8e7RixdB9CKCoNdexIugxFlJa7rNZneTbLIpM/CnNLsdMvNjM8l0mRCiQ9Ye61IKCAgZAUnH+mU3MMZaHYChBnJUDzWOFZdVfc5+ZFLbrWDeXPwbxIqrLLfaeS0hEBVGIRQCEiZoHQwtlGSByCCdYBl8g8egTTAWoKQMRBRBcZxYlhzhKegqMOageErsCHVkk3hXIFooDgHB1KkHIHVgzKB4ADJQ/A1jAFmAYhkQqA5TOBtocrKrgXwQA8gcFIuAIO8sQSA7hidvPwaQGZSaAYHOUWJABhWWw2EMIH9QagQERU4SArJXo0ZZL18uvaxejXt/Em8xjVBXmvFr1KVm/AJ10tRe2XnraNqaJvKE3KHuUbfK1E+VHB0q40/y3sdQSxY4FHWeKJCunP8UyDdqJZenT3ntVV5jIYCAh20vT7ioP8tpf6E2lfEMwERe+whV1MHjwZB7PBiCxcGQWwKZKD62lfGNnP/1poFAA60T7rF1UgcKd2id3KDeUS+oLWV8DfWAepOfq00CgQabi9zjcgJVYVD7PVzQUAUGAQkbNJTBICDhgwYTjDYD6XeW08ZKh+A4pYkzenOxXUbvZcWz7E8ykRMnIHGX1XPl+1m2vPYpL+2qdb8CDAARlKFEz/ZVkAAAAABJRU5ErkJggg==); +} + +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td, +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{ + background: #0a0a0a; + border:1px solid #222222; + color: #999999; +} + +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{ + background: #0e0e0e; +} +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_today{ + color:#cc5500; +} +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_default, +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_current, +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_current{ + background: #cc5500; + box-shadow: #b03e00 0px 1px 3px 0px inset; + color:#000; +} + +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td:hover, +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div:hover{ + color: #000 !important; + background: #007fff !important; +} + +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th{ + color: #666; +} +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright{ color:#333 !important;} +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a{ color:#111 !important;} +.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a:hover{ color:#555 !important;} + + +.xdsoft_dark .xdsoft_time_box{ + border:1px solid #333; +} +.xdsoft_dark .xdsoft_scrollbar >.xdsoft_scroller{ + background:#333 !important; +} diff --git a/library/datetimepicker/jquery.datetimepicker.js b/library/datetimepicker/jquery.datetimepicker.js new file mode 100644 index 000000000..d4e93dd13 --- /dev/null +++ b/library/datetimepicker/jquery.datetimepicker.js @@ -0,0 +1,1661 @@ +/** + * @preserve jQuery DateTimePicker plugin v2.4.0 + * @homepage http://xdsoft.net/jqplugins/datetimepicker/ + * (c) 2014, Chupurnov Valeriy. + */ +/*global document,window,jQuery,setTimeout,clearTimeout*/ +(function ($) { + 'use strict'; + var default_options = { + i18n: { + ar: { // Arabic + months: [ + "كانون الثاني", "شباط", "آذار", "نيسان", "مايو", "حزيران", "تموز", "آب", "أيلول", "تشرين الأول", "تشرين الثاني", "كانون الأول" + ], + dayOfWeek: [ + "ن", "ث", "ع", "خ", "ج", "س", "ح" + ] + }, + ro: { // Romanian + months: [ + "ianuarie", "februarie", "martie", "aprilie", "mai", "iunie", "iulie", "august", "septembrie", "octombrie", "noiembrie", "decembrie" + ], + dayOfWeek: [ + "l", "ma", "mi", "j", "v", "s", "d" + ] + }, + id: { // Indonesian + months: [ + "Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember" + ], + dayOfWeek: [ + "Sen", "Sel", "Rab", "Kam", "Jum", "Sab", "Min" + ] + }, + bg: { // Bulgarian + months: [ + "Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември" + ], + dayOfWeek: [ + "Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб" + ] + }, + fa: { // Persian/Farsi + months: [ + 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند' + ], + dayOfWeek: [ + 'یکشنبه', 'دوشنبه', 'سه شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه' + ] + }, + ru: { // Russian + months: [ + 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь' + ], + dayOfWeek: [ + "Вск", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб" + ] + }, + uk: { // Ukrainian + months: [ + 'Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень' + ], + dayOfWeek: [ + "Ндл", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Сбт" + ] + }, + en: { // English + months: [ + "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" + ], + dayOfWeek: [ + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + ] + }, + el: { // Ελληνικά + months: [ + "Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος" + ], + dayOfWeek: [ + "Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ" + ] + }, + de: { // German + months: [ + 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' + ], + dayOfWeek: [ + "So", "Mo", "Di", "Mi", "Do", "Fr", "Sa" + ] + }, + nl: { // Dutch + months: [ + "januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december" + ], + dayOfWeek: [ + "zo", "ma", "di", "wo", "do", "vr", "za" + ] + }, + tr: { // Turkish + months: [ + "Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık" + ], + dayOfWeek: [ + "Paz", "Pts", "Sal", "Çar", "Per", "Cum", "Cts" + ] + }, + fr: { //French + months: [ + "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre" + ], + dayOfWeek: [ + "Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam" + ] + }, + es: { // Spanish + months: [ + "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre" + ], + dayOfWeek: [ + "Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb" + ] + }, + th: { // Thai + months: [ + 'มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม' + ], + dayOfWeek: [ + 'อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.' + ] + }, + pl: { // Polish + months: [ + "styczeń", "luty", "marzec", "kwiecień", "maj", "czerwiec", "lipiec", "sierpień", "wrzesień", "październik", "listopad", "grudzień" + ], + dayOfWeek: [ + "nd", "pn", "wt", "śr", "cz", "pt", "sb" + ] + }, + pt: { // Portuguese + months: [ + "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro" + ], + dayOfWeek: [ + "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab" + ] + }, + ch: { // Simplified Chinese + months: [ + "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" + ], + dayOfWeek: [ + "日", "一", "二", "三", "四", "五", "六" + ] + }, + se: { // Swedish + months: [ + "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December" + ], + dayOfWeek: [ + "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör" + ] + }, + kr: { // Korean + months: [ + "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월" + ], + dayOfWeek: [ + "일", "월", "화", "수", "목", "금", "토" + ] + }, + it: { // Italian + months: [ + "Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre" + ], + dayOfWeek: [ + "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab" + ] + }, + da: { // Dansk + months: [ + "January", "Februar", "Marts", "April", "Maj", "Juni", "July", "August", "September", "Oktober", "November", "December" + ], + dayOfWeek: [ + "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør" + ] + }, + no: { // Norwegian + months: [ + "Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember" + ], + dayOfWeek: [ + "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør" + ] + }, + ja: { // Japanese + months: [ + "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月" + ], + dayOfWeek: [ + "日", "月", "火", "水", "木", "金", "土" + ] + }, + vi: { // Vietnamese + months: [ + "Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12" + ], + dayOfWeek: [ + "CN", "T2", "T3", "T4", "T5", "T6", "T7" + ] + }, + sl: { // Slovenščina + months: [ + "Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December" + ], + dayOfWeek: [ + "Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob" + ] + }, + cs: { // Čeština + months: [ + "Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec" + ], + dayOfWeek: [ + "Ne", "Po", "Út", "St", "Čt", "Pá", "So" + ] + }, + hu: { // Hungarian + months: [ + "Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December" + ], + dayOfWeek: [ + "Va", "Hé", "Ke", "Sze", "Cs", "Pé", "Szo" + ] + } + }, + value: '', + lang: 'en', + + format: 'Y/m/d H:i', + formatTime: 'H:i', + formatDate: 'Y/m/d', + + startDate: false, // new Date(), '1986/12/08', '-1970/01/05','-1970/01/05', + step: 60, + monthChangeSpinner: true, + + closeOnDateSelect: false, + closeOnWithoutClick: true, + closeOnInputClick: true, + + timepicker: true, + datepicker: true, + weeks: false, + + defaultTime: false, // use formatTime format (ex. '10:00' for formatTime: 'H:i') + defaultDate: false, // use formatDate format (ex new Date() or '1986/12/08' or '-1970/01/05' or '-1970/01/05') + + minDate: false, + maxDate: false, + minTime: false, + maxTime: false, + + allowTimes: [], + opened: false, + initTime: true, + inline: false, + theme: '', + + onSelectDate: function () {}, + onSelectTime: function () {}, + onChangeMonth: function () {}, + onChangeYear: function () {}, + onChangeDateTime: function () {}, + onShow: function () {}, + onClose: function () {}, + onGenerate: function () {}, + + withoutCopyright: true, + inverseButton: false, + hours12: false, + next: 'xdsoft_next', + prev : 'xdsoft_prev', + dayOfWeekStart: 0, + parentID: 'body', + timeHeightInTimePicker: 25, + timepickerScrollbar: true, + todayButton: true, + defaultSelect: true, + + scrollMonth: true, + scrollTime: true, + scrollInput: true, + + lazyInit: false, + mask: false, + validateOnBlur: true, + allowBlank: true, + yearStart: 1950, + yearEnd: 2050, + style: '', + id: '', + fixed: false, + roundTime: 'round', // ceil, floor + className: '', + weekends: [], + disabledDates : [], + yearOffset: 0, + beforeShowDay: null, + + enterLikeTab: true + }; + // fix for ie8 + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (obj, start) { + var i, j; + for (i = (start || 0), j = this.length; i < j; i += 1) { + if (this[i] === obj) { return i; } + } + return -1; + }; + } + Date.prototype.countDaysInMonth = function () { + return new Date(this.getFullYear(), this.getMonth() + 1, 0).getDate(); + }; + $.fn.xdsoftScroller = function (percent) { + return this.each(function () { + var timeboxparent = $(this), + pointerEventToXY = function (e) { + var out = {x: 0, y: 0}, + touch; + if (e.type === 'touchstart' || e.type === 'touchmove' || e.type === 'touchend' || e.type === 'touchcancel') { + touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; + out.x = touch.clientX; + out.y = touch.clientY; + } else if (e.type === 'mousedown' || e.type === 'mouseup' || e.type === 'mousemove' || e.type === 'mouseover' || e.type === 'mouseout' || e.type === 'mouseenter' || e.type === 'mouseleave') { + out.x = e.clientX; + out.y = e.clientY; + } + return out; + }, + move = 0, + timebox, + parentHeight, + height, + scrollbar, + scroller, + maximumOffset = 100, + start = false, + startY = 0, + startTop = 0, + h1 = 0, + touchStart = false, + startTopScroll = 0, + calcOffset = function () {}; + if (percent === 'hide') { + timeboxparent.find('.xdsoft_scrollbar').hide(); + return; + } + if (!$(this).hasClass('xdsoft_scroller_box')) { + timebox = timeboxparent.children().eq(0); + parentHeight = timeboxparent[0].clientHeight; + height = timebox[0].offsetHeight; + scrollbar = $('
'); + scroller = $('
'); + scrollbar.append(scroller); + + timeboxparent.addClass('xdsoft_scroller_box').append(scrollbar); + calcOffset = function calcOffset(event) { + var offset = pointerEventToXY(event).y - startY + startTopScroll; + if (offset < 0) { + offset = 0; + } + if (offset + scroller[0].offsetHeight > h1) { + offset = h1 - scroller[0].offsetHeight; + } + timeboxparent.trigger('scroll_element.xdsoft_scroller', [maximumOffset ? offset / maximumOffset : 0]); + }; + + scroller + .on('touchstart.xdsoft_scroller mousedown.xdsoft_scroller', function (event) { + if (!parentHeight) { + timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]); + } + + startY = pointerEventToXY(event).y; + startTopScroll = parseInt(scroller.css('margin-top'), 10); + h1 = scrollbar[0].offsetHeight; + + if (event.type === 'mousedown') { + if (document) { + $(document.body).addClass('xdsoft_noselect'); + } + $([document.body, window]).on('mouseup.xdsoft_scroller', function arguments_callee() { + $([document.body, window]).off('mouseup.xdsoft_scroller', arguments_callee) + .off('mousemove.xdsoft_scroller', calcOffset) + .removeClass('xdsoft_noselect'); + }); + $(document.body).on('mousemove.xdsoft_scroller', calcOffset); + } else { + touchStart = true; + event.stopPropagation(); + event.preventDefault(); + } + }) + .on('touchmove', function (event) { + if (touchStart) { + event.preventDefault(); + calcOffset(event); + } + }) + .on('touchend touchcancel', function (event) { + touchStart = false; + startTopScroll = 0; + }); + + timeboxparent + .on('scroll_element.xdsoft_scroller', function (event, percentage) { + if (!parentHeight) { + timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percentage, true]); + } + percentage = percentage > 1 ? 1 : (percentage < 0 || isNaN(percentage)) ? 0 : percentage; + + scroller.css('margin-top', maximumOffset * percentage); + + setTimeout(function () { + timebox.css('marginTop', -parseInt((timebox[0].offsetHeight - parentHeight) * percentage, 10)); + }, 10); + }) + .on('resize_scroll.xdsoft_scroller', function (event, percentage, noTriggerScroll) { + var percent, sh; + parentHeight = timeboxparent[0].clientHeight; + height = timebox[0].offsetHeight; + percent = parentHeight / height; + sh = percent * scrollbar[0].offsetHeight; + if (percent > 1) { + scroller.hide(); + } else { + scroller.show(); + scroller.css('height', parseInt(sh > 10 ? sh : 10, 10)); + maximumOffset = scrollbar[0].offsetHeight - scroller[0].offsetHeight; + if (noTriggerScroll !== true) { + timeboxparent.trigger('scroll_element.xdsoft_scroller', [percentage || Math.abs(parseInt(timebox.css('marginTop'), 10)) / (height - parentHeight)]); + } + } + }); + + timeboxparent.on('mousewheel', function (event) { + var top = Math.abs(parseInt(timebox.css('marginTop'), 10)); + + top = top - (event.deltaY * 20); + if (top < 0) { + top = 0; + } + + timeboxparent.trigger('scroll_element.xdsoft_scroller', [top / (height - parentHeight)]); + event.stopPropagation(); + return false; + }); + + timeboxparent.on('touchstart', function (event) { + start = pointerEventToXY(event); + startTop = Math.abs(parseInt(timebox.css('marginTop'), 10)); + }); + + timeboxparent.on('touchmove', function (event) { + if (start) { + event.preventDefault(); + var coord = pointerEventToXY(event); + timeboxparent.trigger('scroll_element.xdsoft_scroller', [(startTop - (coord.y - start.y)) / (height - parentHeight)]); + } + }); + + timeboxparent.on('touchend touchcancel', function (event) { + start = false; + startTop = 0; + }); + } + timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]); + }); + }; + + $.fn.datetimepicker = function (opt) { + var KEY0 = 48, + KEY9 = 57, + _KEY0 = 96, + _KEY9 = 105, + CTRLKEY = 17, + DEL = 46, + ENTER = 13, + ESC = 27, + BACKSPACE = 8, + ARROWLEFT = 37, + ARROWUP = 38, + ARROWRIGHT = 39, + ARROWDOWN = 40, + TAB = 9, + F5 = 116, + AKEY = 65, + CKEY = 67, + VKEY = 86, + ZKEY = 90, + YKEY = 89, + ctrlDown = false, + options = ($.isPlainObject(opt) || !opt) ? $.extend(true, {}, default_options, opt) : $.extend(true, {}, default_options), + + lazyInitTimer = 0, + createDateTimePicker, + destroyDateTimePicker, + _xdsoft_datetime, + + lazyInit = function (input) { + input + .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function initOnActionCallback(event) { + if (input.is(':disabled') || input.is(':hidden') || !input.is(':visible') || input.data('xdsoft_datetimepicker')) { + return; + } + clearTimeout(lazyInitTimer); + lazyInitTimer = setTimeout(function () { + + if (!input.data('xdsoft_datetimepicker')) { + createDateTimePicker(input); + } + input + .off('open.xdsoft focusin.xdsoft mousedown.xdsoft', initOnActionCallback) + .trigger('open.xdsoft'); + }, 100); + }); + }; + + createDateTimePicker = function (input) { + var datetimepicker = $('
'), + xdsoft_copyright = $(''), + datepicker = $('
'), + mounth_picker = $('
' + + '
' + + '
' + + '
'), + calendar = $('
'), + timepicker = $('
'), + timeboxparent = timepicker.find('.xdsoft_time_box').eq(0), + timebox = $('
'), + /*scrollbar = $('
'), + scroller = $('
'),*/ + monthselect = $('
'), + yearselect = $('
'), + triggerAfterOpen = false, + XDSoft_datetime, + //scroll_element, + xchangeTimer, + timerclick, + current_time_index, + setPos, + timer = 0, + timer1 = 0; + + mounth_picker + .find('.xdsoft_month span') + .after(monthselect); + mounth_picker + .find('.xdsoft_year span') + .after(yearselect); + + mounth_picker + .find('.xdsoft_month,.xdsoft_year') + .on('mousedown.xdsoft', function (event) { + var select = $(this).find('.xdsoft_select').eq(0), + val = 0, + top = 0, + visible = select.is(':visible'), + items, + i; + + mounth_picker + .find('.xdsoft_select') + .hide(); + if (_xdsoft_datetime.currentTime) { + val = _xdsoft_datetime.currentTime[$(this).hasClass('xdsoft_month') ? 'getMonth' : 'getFullYear'](); + } + + select[visible ? 'hide' : 'show'](); + for (items = select.find('div.xdsoft_option'), i = 0; i < items.length; i += 1) { + if (items.eq(i).data('value') === val) { + break; + } else { + top += items[0].offsetHeight; + } + } + + select.xdsoftScroller(top / (select.children()[0].offsetHeight - (select[0].clientHeight))); + event.stopPropagation(); + return false; + }); + + mounth_picker + .find('.xdsoft_select') + .xdsoftScroller() + .on('mousedown.xdsoft', function (event) { + event.stopPropagation(); + event.preventDefault(); + }) + .on('mousedown.xdsoft', '.xdsoft_option', function (event) { + var year = _xdsoft_datetime.currentTime.getFullYear(); + if (_xdsoft_datetime && _xdsoft_datetime.currentTime) { + _xdsoft_datetime.currentTime[$(this).parent().parent().hasClass('xdsoft_monthselect') ? 'setMonth' : 'setFullYear']($(this).data('value')); + } + + $(this).parent().parent().hide(); + + datetimepicker.trigger('xchange.xdsoft'); + if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { + options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + + if (year !== _xdsoft_datetime.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) { + options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + }); + + datetimepicker.setOptions = function (_options) { + options = $.extend(true, {}, options, _options); + + if (_options.allowTimes && $.isArray(_options.allowTimes) && _options.allowTimes.length) { + options.allowTimes = $.extend(true, [], _options.allowTimes); + } + + if (_options.weekends && $.isArray(_options.weekends) && _options.weekends.length) { + options.weekends = $.extend(true, [], _options.weekends); + } + + if (_options.disabledDates && $.isArray(_options.disabledDates) && _options.disabledDates.length) { + options.disabledDates = $.extend(true, [], _options.disabledDates); + } + + if ((options.open || options.opened) && (!options.inline)) { + input.trigger('open.xdsoft'); + } + + if (options.inline) { + triggerAfterOpen = true; + datetimepicker.addClass('xdsoft_inline'); + input.after(datetimepicker).hide(); + } + + if (options.inverseButton) { + options.next = 'xdsoft_prev'; + options.prev = 'xdsoft_next'; + } + + if (options.datepicker) { + datepicker.addClass('active'); + } else { + datepicker.removeClass('active'); + } + + if (options.timepicker) { + timepicker.addClass('active'); + } else { + timepicker.removeClass('active'); + } + + if (options.value) { + if (input && input.val) { + input.val(options.value); + } + _xdsoft_datetime.setCurrentTime(options.value); + } + + if (isNaN(options.dayOfWeekStart)) { + options.dayOfWeekStart = 0; + } else { + options.dayOfWeekStart = parseInt(options.dayOfWeekStart, 10) % 7; + } + + if (!options.timepickerScrollbar) { + timeboxparent.xdsoftScroller('hide'); + } + + if (options.minDate && /^-(.*)$/.test(options.minDate)) { + options.minDate = _xdsoft_datetime.strToDateTime(options.minDate).dateFormat(options.formatDate); + } + + if (options.maxDate && /^\+(.*)$/.test(options.maxDate)) { + options.maxDate = _xdsoft_datetime.strToDateTime(options.maxDate).dateFormat(options.formatDate); + } + + mounth_picker + .find('.xdsoft_today_button') + .css('visibility', !options.todayButton ? 'hidden' : 'visible'); + + if (options.mask) { + var e, + getCaretPos = function (input) { + try { + if (document.selection && document.selection.createRange) { + var range = document.selection.createRange(); + return range.getBookmark().charCodeAt(2) - 2; + } + if (input.setSelectionRange) { + return input.selectionStart; + } + } catch (e) { + return 0; + } + }, + setCaretPos = function (node, pos) { + node = (typeof node === "string" || node instanceof String) ? document.getElementById(node) : node; + if (!node) { + return false; + } + if (node.createTextRange) { + var textRange = node.createTextRange(); + textRange.collapse(true); + textRange.moveEnd('character', pos); + textRange.moveStart('character', pos); + textRange.select(); + return true; + } + if (node.setSelectionRange) { + node.setSelectionRange(pos, pos); + return true; + } + return false; + }, + isValidValue = function (mask, value) { + var reg = mask + .replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g, '\\$1') + .replace(/_/g, '{digit+}') + .replace(/([0-9]{1})/g, '{digit$1}') + .replace(/\{digit([0-9]{1})\}/g, '[0-$1_]{1}') + .replace(/\{digit[\+]\}/g, '[0-9_]{1}'); + return (new RegExp(reg)).test(value); + }; + input.off('keydown.xdsoft'); + + if (options.mask === true) { + options.mask = options.format + .replace(/Y/g, '9999') + .replace(/F/g, '9999') + .replace(/m/g, '19') + .replace(/d/g, '39') + .replace(/H/g, '29') + .replace(/i/g, '59') + .replace(/s/g, '59'); + } + + if ($.type(options.mask) === 'string') { + if (!isValidValue(options.mask, input.val())) { + input.val(options.mask.replace(/[0-9]/g, '_')); + } + + input.on('keydown.xdsoft', function (event) { + var val = this.value, + key = event.which, + pos, + digit; + + if (((key >= KEY0 && key <= KEY9) || (key >= _KEY0 && key <= _KEY9)) || (key === BACKSPACE || key === DEL)) { + pos = getCaretPos(this); + digit = (key !== BACKSPACE && key !== DEL) ? String.fromCharCode((_KEY0 <= key && key <= _KEY9) ? key - KEY0 : key) : '_'; + + if ((key === BACKSPACE || key === DEL) && pos) { + pos -= 1; + digit = '_'; + } + + while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) { + pos += (key === BACKSPACE || key === DEL) ? -1 : 1; + } + + val = val.substr(0, pos) + digit + val.substr(pos + 1); + if ($.trim(val) === '') { + val = options.mask.replace(/[0-9]/g, '_'); + } else { + if (pos === options.mask.length) { + event.preventDefault(); + return false; + } + } + + pos += (key === BACKSPACE || key === DEL) ? 0 : 1; + while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) { + pos += (key === BACKSPACE || key === DEL) ? -1 : 1; + } + + if (isValidValue(options.mask, val)) { + this.value = val; + setCaretPos(this, pos); + } else if ($.trim(val) === '') { + this.value = options.mask.replace(/[0-9]/g, '_'); + } else { + input.trigger('error_input.xdsoft'); + } + } else { + if (([AKEY, CKEY, VKEY, ZKEY, YKEY].indexOf(key) !== -1 && ctrlDown) || [ESC, ARROWUP, ARROWDOWN, ARROWLEFT, ARROWRIGHT, F5, CTRLKEY, TAB, ENTER].indexOf(key) !== -1) { + return true; + } + } + + event.preventDefault(); + return false; + }); + } + } + if (options.validateOnBlur) { + input + .off('blur.xdsoft') + .on('blur.xdsoft', function () { + if (options.allowBlank && !$.trim($(this).val()).length) { + $(this).val(null); + datetimepicker.data('xdsoft_datetime').empty(); + } else if (!Date.parseDate($(this).val(), options.format)) { + $(this).val((_xdsoft_datetime.now()).dateFormat(options.format)); + datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val()); + } else { + datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val()); + } + datetimepicker.trigger('changedatetime.xdsoft'); + }); + } + options.dayOfWeekStartPrev = (options.dayOfWeekStart === 0) ? 6 : options.dayOfWeekStart - 1; + + datetimepicker + .trigger('xchange.xdsoft') + .trigger('afterOpen.xdsoft'); + }; + + datetimepicker + .data('options', options) + .on('mousedown.xdsoft', function (event) { + event.stopPropagation(); + event.preventDefault(); + yearselect.hide(); + monthselect.hide(); + return false; + }); + + //scroll_element = timepicker.find('.xdsoft_time_box'); + timeboxparent.append(timebox); + timeboxparent.xdsoftScroller(); + + datetimepicker.on('afterOpen.xdsoft', function () { + timeboxparent.xdsoftScroller(); + }); + + datetimepicker + .append(datepicker) + .append(timepicker); + + if (options.withoutCopyright !== true) { + datetimepicker + .append(xdsoft_copyright); + } + + datepicker + .append(mounth_picker) + .append(calendar); + + $(options.parentID) + .append(datetimepicker); + + XDSoft_datetime = function () { + var _this = this; + _this.now = function (norecursion) { + var d = new Date(), + date, + time; + + if (!norecursion && options.defaultDate) { + date = _this.strToDate(options.defaultDate); + d.setFullYear(date.getFullYear()); + d.setMonth(date.getMonth()); + d.setDate(date.getDate()); + } + + if (options.yearOffset) { + d.setFullYear(d.getFullYear() + options.yearOffset); + } + + if (!norecursion && options.defaultTime) { + time = _this.strtotime(options.defaultTime); + d.setHours(time.getHours()); + d.setMinutes(time.getMinutes()); + } + + return d; + }; + + _this.isValidDate = function (d) { + if (Object.prototype.toString.call(d) !== "[object Date]") { + return false; + } + return !isNaN(d.getTime()); + }; + + _this.setCurrentTime = function (dTime) { + _this.currentTime = (typeof dTime === 'string') ? _this.strToDateTime(dTime) : _this.isValidDate(dTime) ? dTime : _this.now(); + datetimepicker.trigger('xchange.xdsoft'); + }; + + _this.empty = function () { + _this.currentTime = null; + }; + + _this.getCurrentTime = function (dTime) { + return _this.currentTime; + }; + + _this.nextMonth = function () { + var month = _this.currentTime.getMonth() + 1, + year; + if (month === 12) { + _this.currentTime.setFullYear(_this.currentTime.getFullYear() + 1); + month = 0; + } + + year = _this.currentTime.getFullYear(); + + _this.currentTime.setDate( + Math.min( + new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(), + _this.currentTime.getDate() + ) + ); + _this.currentTime.setMonth(month); + + if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { + options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + + if (year !== _this.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) { + options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + + datetimepicker.trigger('xchange.xdsoft'); + return month; + }; + + _this.prevMonth = function () { + var month = _this.currentTime.getMonth() - 1; + if (month === -1) { + _this.currentTime.setFullYear(_this.currentTime.getFullYear() - 1); + month = 11; + } + _this.currentTime.setDate( + Math.min( + new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(), + _this.currentTime.getDate() + ) + ); + _this.currentTime.setMonth(month); + if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { + options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + datetimepicker.trigger('xchange.xdsoft'); + return month; + }; + + _this.getWeekOfYear = function (datetime) { + var onejan = new Date(datetime.getFullYear(), 0, 1); + return Math.ceil((((datetime - onejan) / 86400000) + onejan.getDay() + 1) / 7); + }; + + _this.strToDateTime = function (sDateTime) { + var tmpDate = [], timeOffset, currentTime; + + if (sDateTime && sDateTime instanceof Date && _this.isValidDate(sDateTime)) { + return sDateTime; + } + + tmpDate = /^(\+|\-)(.*)$/.exec(sDateTime); + if (tmpDate) { + tmpDate[2] = Date.parseDate(tmpDate[2], options.formatDate); + } + if (tmpDate && tmpDate[2]) { + timeOffset = tmpDate[2].getTime() - (tmpDate[2].getTimezoneOffset()) * 60000; + currentTime = new Date((_xdsoft_datetime.now()).getTime() + parseInt(tmpDate[1] + '1', 10) * timeOffset); + } else { + currentTime = sDateTime ? Date.parseDate(sDateTime, options.format) : _this.now(); + } + + if (!_this.isValidDate(currentTime)) { + currentTime = _this.now(); + } + + return currentTime; + }; + + _this.strToDate = function (sDate) { + if (sDate && sDate instanceof Date && _this.isValidDate(sDate)) { + return sDate; + } + + var currentTime = sDate ? Date.parseDate(sDate, options.formatDate) : _this.now(true); + if (!_this.isValidDate(currentTime)) { + currentTime = _this.now(true); + } + return currentTime; + }; + + _this.strtotime = function (sTime) { + if (sTime && sTime instanceof Date && _this.isValidDate(sTime)) { + return sTime; + } + var currentTime = sTime ? Date.parseDate(sTime, options.formatTime) : _this.now(true); + if (!_this.isValidDate(currentTime)) { + currentTime = _this.now(true); + } + return currentTime; + }; + + _this.str = function () { + return _this.currentTime.dateFormat(options.format); + }; + _this.currentTime = this.now(); + }; + + _xdsoft_datetime = new XDSoft_datetime(); + + mounth_picker + .find('.xdsoft_today_button') + .on('mousedown.xdsoft', function () { + datetimepicker.data('changed', true); + _xdsoft_datetime.setCurrentTime(0); + datetimepicker.trigger('afterOpen.xdsoft'); + }).on('dblclick.xdsoft', function () { + input.val(_xdsoft_datetime.str()); + datetimepicker.trigger('close.xdsoft'); + }); + mounth_picker + .find('.xdsoft_prev,.xdsoft_next') + .on('mousedown.xdsoft', function () { + var $this = $(this), + timer = 0, + stop = false; + + (function arguments_callee1(v) { + var month = _xdsoft_datetime.currentTime.getMonth(); + if ($this.hasClass(options.next)) { + _xdsoft_datetime.nextMonth(); + } else if ($this.hasClass(options.prev)) { + _xdsoft_datetime.prevMonth(); + } + if (options.monthChangeSpinner) { + if (!stop) { + timer = setTimeout(arguments_callee1, v || 100); + } + } + }(500)); + + $([document.body, window]).on('mouseup.xdsoft', function arguments_callee2() { + clearTimeout(timer); + stop = true; + $([document.body, window]).off('mouseup.xdsoft', arguments_callee2); + }); + }); + + timepicker + .find('.xdsoft_prev,.xdsoft_next') + .on('mousedown.xdsoft', function () { + var $this = $(this), + timer = 0, + stop = false, + period = 110; + (function arguments_callee4(v) { + var pheight = timeboxparent[0].clientHeight, + height = timebox[0].offsetHeight, + top = Math.abs(parseInt(timebox.css('marginTop'), 10)); + if ($this.hasClass(options.next) && (height - pheight) - options.timeHeightInTimePicker >= top) { + timebox.css('marginTop', '-' + (top + options.timeHeightInTimePicker) + 'px'); + } else if ($this.hasClass(options.prev) && top - options.timeHeightInTimePicker >= 0) { + timebox.css('marginTop', '-' + (top - options.timeHeightInTimePicker) + 'px'); + } + timeboxparent.trigger('scroll_element.xdsoft_scroller', [Math.abs(parseInt(timebox.css('marginTop'), 10) / (height - pheight))]); + period = (period > 10) ? 10 : period - 10; + if (!stop) { + timer = setTimeout(arguments_callee4, v || period); + } + }(500)); + $([document.body, window]).on('mouseup.xdsoft', function arguments_callee5() { + clearTimeout(timer); + stop = true; + $([document.body, window]) + .off('mouseup.xdsoft', arguments_callee5); + }); + }); + + xchangeTimer = 0; + // base handler - generating a calendar and timepicker + datetimepicker + .on('xchange.xdsoft', function (event) { + clearTimeout(xchangeTimer); + xchangeTimer = setTimeout(function () { + var table = '', + start = new Date(_xdsoft_datetime.currentTime.getFullYear(), _xdsoft_datetime.currentTime.getMonth(), 1, 12, 0, 0), + i = 0, + j, + today = _xdsoft_datetime.now(), + maxDate = false, + minDate = false, + d, + y, + m, + w, + classes = [], + customDateSettings, + newRow = true, + time = '', + h = '', + line_time; + + while (start.getDay() !== options.dayOfWeekStart) { + start.setDate(start.getDate() - 1); + } + + table += ''; + + if (options.weeks) { + table += ''; + } + + for (j = 0; j < 7; j += 1) { + table += ''; + } + + table += ''; + table += ''; + + if (options.maxDate !== false) { + maxDate = _xdsoft_datetime.strToDate(options.maxDate); + maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate(), 23, 59, 59, 999); + } + + if (options.minDate !== false) { + minDate = _xdsoft_datetime.strToDate(options.minDate); + minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()); + } + + while (i < _xdsoft_datetime.currentTime.countDaysInMonth() || start.getDay() !== options.dayOfWeekStart || _xdsoft_datetime.currentTime.getMonth() === start.getMonth()) { + classes = []; + i += 1; + + d = start.getDate(); + y = start.getFullYear(); + m = start.getMonth(); + w = _xdsoft_datetime.getWeekOfYear(start); + + classes.push('xdsoft_date'); + + if (options.beforeShowDay && $.isFunction(options.beforeShowDay.call)) { + customDateSettings = options.beforeShowDay.call(datetimepicker, start); + } else { + customDateSettings = null; + } + + if ((maxDate !== false && start > maxDate) || (minDate !== false && start < minDate) || (customDateSettings && customDateSettings[0] === false)) { + classes.push('xdsoft_disabled'); + } else if (options.disabledDates.indexOf(start.dateFormat(options.formatDate)) !== -1) { + classes.push('xdsoft_disabled'); + } + + if (customDateSettings && customDateSettings[1] !== "") { + classes.push(customDateSettings[1]); + } + + if (_xdsoft_datetime.currentTime.getMonth() !== m) { + classes.push('xdsoft_other_month'); + } + + if ((options.defaultSelect || datetimepicker.data('changed')) && _xdsoft_datetime.currentTime.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) { + classes.push('xdsoft_current'); + } + + if (today.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) { + classes.push('xdsoft_today'); + } + + if (start.getDay() === 0 || start.getDay() === 6 || options.weekends.indexOf(start.dateFormat(options.formatDate)) === -1) { + classes.push('xdsoft_weekend'); + } + + if (options.beforeShowDay && $.isFunction(options.beforeShowDay)) { + classes.push(options.beforeShowDay(start)); + } + + if (newRow) { + table += ''; + newRow = false; + if (options.weeks) { + table += ''; + } + } + + table += ''; + + if (start.getDay() === options.dayOfWeekStartPrev) { + table += ''; + newRow = true; + } + + start.setDate(d + 1); + } + table += '
' + options.i18n[options.lang].dayOfWeek[(j + options.dayOfWeekStart) % 7] + '
' + w + '' + + '
' + d + '
' + + '
'; + + calendar.html(table); + + mounth_picker.find('.xdsoft_label span').eq(0).text(options.i18n[options.lang].months[_xdsoft_datetime.currentTime.getMonth()]); + mounth_picker.find('.xdsoft_label span').eq(1).text(_xdsoft_datetime.currentTime.getFullYear()); + + // generate timebox + time = ''; + h = ''; + m = ''; + line_time = function line_time(h, m) { + var now = _xdsoft_datetime.now(); + now.setHours(h); + h = parseInt(now.getHours(), 10); + now.setMinutes(m); + m = parseInt(now.getMinutes(), 10); + + classes = []; + if ((options.maxTime !== false && _xdsoft_datetime.strtotime(options.maxTime).getTime() < now.getTime()) || (options.minTime !== false && _xdsoft_datetime.strtotime(options.minTime).getTime() > now.getTime())) { + classes.push('xdsoft_disabled'); + } + if ((options.initTime || options.defaultSelect || datetimepicker.data('changed')) && parseInt(_xdsoft_datetime.currentTime.getHours(), 10) === parseInt(h, 10) && (options.step > 59 || Math[options.roundTime](_xdsoft_datetime.currentTime.getMinutes() / options.step) * options.step === parseInt(m, 10))) { + if (options.defaultSelect || datetimepicker.data('changed')) { + classes.push('xdsoft_current'); + } else if (options.initTime) { + classes.push('xdsoft_init_time'); + } + } + if (parseInt(today.getHours(), 10) === parseInt(h, 10) && parseInt(today.getMinutes(), 10) === parseInt(m, 10)) { + classes.push('xdsoft_today'); + } + time += '
' + now.dateFormat(options.formatTime) + '
'; + }; + + if (!options.allowTimes || !$.isArray(options.allowTimes) || !options.allowTimes.length) { + for (i = 0, j = 0; i < (options.hours12 ? 12 : 24); i += 1) { + for (j = 0; j < 60; j += options.step) { + h = (i < 10 ? '0' : '') + i; + m = (j < 10 ? '0' : '') + j; + line_time(h, m); + } + } + } else { + for (i = 0; i < options.allowTimes.length; i += 1) { + h = _xdsoft_datetime.strtotime(options.allowTimes[i]).getHours(); + m = _xdsoft_datetime.strtotime(options.allowTimes[i]).getMinutes(); + line_time(h, m); + } + } + + timebox.html(time); + + opt = ''; + i = 0; + + for (i = parseInt(options.yearStart, 10) + options.yearOffset; i <= parseInt(options.yearEnd, 10) + options.yearOffset; i += 1) { + opt += '
' + i + '
'; + } + yearselect.children().eq(0) + .html(opt); + + for (i = 0, opt = ''; i <= 11; i += 1) { + opt += '
' + options.i18n[options.lang].months[i] + '
'; + } + monthselect.children().eq(0).html(opt); + $(datetimepicker) + .trigger('generate.xdsoft'); + }, 10); + event.stopPropagation(); + }) + .on('afterOpen.xdsoft', function () { + if (options.timepicker) { + var classType, pheight, height, top; + if (timebox.find('.xdsoft_current').length) { + classType = '.xdsoft_current'; + } else if (timebox.find('.xdsoft_init_time').length) { + classType = '.xdsoft_init_time'; + } + if (classType) { + pheight = timeboxparent[0].clientHeight; + height = timebox[0].offsetHeight; + top = timebox.find(classType).index() * options.timeHeightInTimePicker + 1; + if ((height - pheight) < top) { + top = height - pheight; + } + timeboxparent.trigger('scroll_element.xdsoft_scroller', [parseInt(top, 10) / (height - pheight)]); + } else { + timeboxparent.trigger('scroll_element.xdsoft_scroller', [0]); + } + } + }); + + timerclick = 0; + calendar + .on('click.xdsoft', 'td', function (xdevent) { + xdevent.stopPropagation(); // Prevents closing of Pop-ups, Modals and Flyouts in Bootstrap + timerclick += 1; + var $this = $(this), + currentTime = _xdsoft_datetime.currentTime; + + if (currentTime === undefined || currentTime === null) { + _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); + currentTime = _xdsoft_datetime.currentTime; + } + + if ($this.hasClass('xdsoft_disabled')) { + return false; + } + + currentTime.setDate(1); + currentTime.setFullYear($this.data('year')); + currentTime.setMonth($this.data('month')); + currentTime.setDate($this.data('date')); + + datetimepicker.trigger('select.xdsoft', [currentTime]); + + input.val(_xdsoft_datetime.str()); + if ((timerclick > 1 || (options.closeOnDateSelect === true || (options.closeOnDateSelect === 0 && !options.timepicker))) && !options.inline) { + datetimepicker.trigger('close.xdsoft'); + } + + if (options.onSelectDate && $.isFunction(options.onSelectDate)) { + options.onSelectDate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); + } + + datetimepicker.data('changed', true); + datetimepicker.trigger('xchange.xdsoft'); + datetimepicker.trigger('changedatetime.xdsoft'); + setTimeout(function () { + timerclick = 0; + }, 200); + }); + + timebox + .on('click.xdsoft', 'div', function (xdevent) { + xdevent.stopPropagation(); + var $this = $(this), + currentTime = _xdsoft_datetime.currentTime; + + if (currentTime === undefined || currentTime === null) { + _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); + currentTime = _xdsoft_datetime.currentTime; + } + + if ($this.hasClass('xdsoft_disabled')) { + return false; + } + currentTime.setHours($this.data('hour')); + currentTime.setMinutes($this.data('minute')); + datetimepicker.trigger('select.xdsoft', [currentTime]); + + datetimepicker.data('input').val(_xdsoft_datetime.str()); + if (!options.inline) { + datetimepicker.trigger('close.xdsoft'); + } + + if (options.onSelectTime && $.isFunction(options.onSelectTime)) { + options.onSelectTime.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); + } + datetimepicker.data('changed', true); + datetimepicker.trigger('xchange.xdsoft'); + datetimepicker.trigger('changedatetime.xdsoft'); + }); + + + datepicker + .on('mousewheel.xdsoft', function (event) { + if (!options.scrollMonth) { + return true; + } + if (event.deltaY < 0) { + _xdsoft_datetime.nextMonth(); + } else { + _xdsoft_datetime.prevMonth(); + } + return false; + }); + + input + .on('mousewheel.xdsoft', function (event) { + if (!options.scrollInput) { + return true; + } + if (!options.datepicker && options.timepicker) { + current_time_index = timebox.find('.xdsoft_current').length ? timebox.find('.xdsoft_current').eq(0).index() : 0; + if (current_time_index + event.deltaY >= 0 && current_time_index + event.deltaY < timebox.children().length) { + current_time_index += event.deltaY; + } + if (timebox.children().eq(current_time_index).length) { + timebox.children().eq(current_time_index).trigger('mousedown'); + } + return false; + } + if (options.datepicker && !options.timepicker) { + datepicker.trigger(event, [event.deltaY, event.deltaX, event.deltaY]); + if (input.val) { + input.val(_xdsoft_datetime.str()); + } + datetimepicker.trigger('changedatetime.xdsoft'); + return false; + } + }); + + datetimepicker + .on('changedatetime.xdsoft', function (event) { + if (options.onChangeDateTime && $.isFunction(options.onChangeDateTime)) { + var $input = datetimepicker.data('input'); + options.onChangeDateTime.call(datetimepicker, _xdsoft_datetime.currentTime, $input, event); + delete options.value; + $input.trigger('change'); + } + }) + .on('generate.xdsoft', function () { + if (options.onGenerate && $.isFunction(options.onGenerate)) { + options.onGenerate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + if (triggerAfterOpen) { + datetimepicker.trigger('afterOpen.xdsoft'); + triggerAfterOpen = false; + } + }) + .on('click.xdsoft', function (xdevent) { + xdevent.stopPropagation(); + }); + + current_time_index = 0; + + setPos = function () { + var offset = datetimepicker.data('input').offset(), top = offset.top + datetimepicker.data('input')[0].offsetHeight - 1, left = offset.left, position = "absolute"; + if (options.fixed) { + top -= $(window).scrollTop(); + left -= $(window).scrollLeft(); + position = "fixed"; + } else { + if (top + datetimepicker[0].offsetHeight > $(window).height() + $(window).scrollTop()) { + top = offset.top - datetimepicker[0].offsetHeight + 1; + } + if (top < 0) { + top = 0; + } + if (left + datetimepicker[0].offsetWidth > $(window).width()) { + left = $(window).width() - datetimepicker[0].offsetWidth; + } + } + datetimepicker.css({ + left: left, + top: top, + position: position + }); + }; + datetimepicker + .on('open.xdsoft', function (event) { + var onShow = true; + if (options.onShow && $.isFunction(options.onShow)) { + onShow = options.onShow.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event); + } + if (onShow !== false) { + datetimepicker.show(); + setPos(); + $(window) + .off('resize.xdsoft', setPos) + .on('resize.xdsoft', setPos); + + if (options.closeOnWithoutClick) { + $([document.body, window]).on('mousedown.xdsoft', function arguments_callee6() { + datetimepicker.trigger('close.xdsoft'); + $([document.body, window]).off('mousedown.xdsoft', arguments_callee6); + }); + } + } + }) + .on('close.xdsoft', function (event) { + var onClose = true; + mounth_picker + .find('.xdsoft_month,.xdsoft_year') + .find('.xdsoft_select') + .hide(); + if (options.onClose && $.isFunction(options.onClose)) { + onClose = options.onClose.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event); + } + if (onClose !== false && !options.opened && !options.inline) { + datetimepicker.hide(); + } + event.stopPropagation(); + }) + .on('toggle.xdsoft', function (event) { + datetimepicker.is(':visible') ? datetimepicker.trigger('close.xdsoft') : datetimepicker.trigger('open.xdsoft'); + }) + .data('input', input); + + timer = 0; + timer1 = 0; + + datetimepicker.data('xdsoft_datetime', _xdsoft_datetime); + datetimepicker.setOptions(options); + + function getCurrentValue() { + + var ct = false, time; + + if (options.startDate) { + ct = _xdsoft_datetime.strToDate(options.startDate); + } else { + ct = options.value || ((input && input.val && input.val()) ? input.val() : ''); + if (ct) { + ct = _xdsoft_datetime.strToDateTime(ct); + } else if (options.defaultDate) { + ct = _xdsoft_datetime.strToDate(options.defaultDate); + if (options.defaultTime) { + time = _xdsoft_datetime.strtotime(options.defaultTime); + ct.setHours(time.getHours()); + ct.setMinutes(time.getMinutes()); + } + } + } + + if (ct && _xdsoft_datetime.isValidDate(ct)) { + datetimepicker.data('changed', true); + } else { + ct = ''; + } + + return ct || 0; + } + + _xdsoft_datetime.setCurrentTime(getCurrentValue()); + + input + .data('xdsoft_datetimepicker', datetimepicker) + .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function (event) { + if (input.is(':disabled') || input.is(':hidden') || !input.is(':visible') || (input.data('xdsoft_datetimepicker').is(':visible') && options.closeOnInputClick)) { + return; + } + clearTimeout(timer); + timer = setTimeout(function () { + if (input.is(':disabled') || input.is(':hidden') || !input.is(':visible')) { + return; + } + + triggerAfterOpen = true; + _xdsoft_datetime.setCurrentTime(getCurrentValue()); + + datetimepicker.trigger('open.xdsoft'); + }, 100); + }) + .on('keydown.xdsoft', function (event) { + var val = this.value, elementSelector, + key = event.which; + if ([ENTER].indexOf(key) !== -1 && options.enterLikeTab) { + elementSelector = $("input:visible,textarea:visible"); + datetimepicker.trigger('close.xdsoft'); + elementSelector.eq(elementSelector.index(this) + 1).focus(); + return false; + } + if ([TAB].indexOf(key) !== -1) { + datetimepicker.trigger('close.xdsoft'); + return true; + } + }); + }; + destroyDateTimePicker = function (input) { + var datetimepicker = input.data('xdsoft_datetimepicker'); + if (datetimepicker) { + datetimepicker.data('xdsoft_datetime', null); + datetimepicker.remove(); + input + .data('xdsoft_datetimepicker', null) + .off('.xdsoft'); + $(window).off('resize.xdsoft'); + $([window, document.body]).off('mousedown.xdsoft'); + if (input.unmousewheel) { + input.unmousewheel(); + } + } + }; + $(document) + .off('keydown.xdsoftctrl keyup.xdsoftctrl') + .on('keydown.xdsoftctrl', function (e) { + if (e.keyCode === CTRLKEY) { + ctrlDown = true; + } + }) + .on('keyup.xdsoftctrl', function (e) { + if (e.keyCode === CTRLKEY) { + ctrlDown = false; + } + }); + return this.each(function () { + var datetimepicker = $(this).data('xdsoft_datetimepicker'); + if (datetimepicker) { + if ($.type(opt) === 'string') { + switch (opt) { + case 'show': + $(this).select().focus(); + datetimepicker.trigger('open.xdsoft'); + break; + case 'hide': + datetimepicker.trigger('close.xdsoft'); + break; + case 'toggle': + datetimepicker.trigger('toggle.xdsoft'); + break; + case 'destroy': + destroyDateTimePicker($(this)); + break; + case 'reset': + this.value = this.defaultValue; + if (!this.value || !datetimepicker.data('xdsoft_datetime').isValidDate(Date.parseDate(this.value, options.format))) { + datetimepicker.data('changed', false); + } + datetimepicker.data('xdsoft_datetime').setCurrentTime(this.value); + break; + } + } else { + datetimepicker + .setOptions(opt); + } + return 0; + } + if ($.type(opt) !== 'string') { + if (!options.lazyInit || options.open || options.inline) { + createDateTimePicker($(this)); + } else { + lazyInit($(this)); + } + } + }); + }; + $.fn.datetimepicker.defaults = default_options; +}(jQuery)); +(function () { + +/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh) + * Licensed under the MIT License (LICENSE.txt). + * + * Version: 3.1.12 + * + * Requires: jQuery 1.2.2+ + */ +!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})}); + +// Parse and Format Library +//http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/ +/* + * Copyright (C) 2004 Baron Schwartz + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, version 2.1. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ +Date.parseFunctions={count:0};Date.parseRegexes=[];Date.formatFunctions={count:0};Date.prototype.dateFormat=function(b){if(b=="unixtime"){return parseInt(this.getTime()/1000);}if(Date.formatFunctions[b]==null){Date.createNewFormat(b);}var a=Date.formatFunctions[b];return this[a]();};Date.createNewFormat=function(format){var funcName="format"+Date.formatFunctions.count++;Date.formatFunctions[format]=funcName;var code="Date.prototype."+funcName+" = function() {return ";var special=false;var ch="";for(var i=0;i 0) {";var regex="";var special=false;var ch="";for(var i=0;i 0 && z > 0){\nvar doyDate = new Date(y,0);\ndoyDate.setDate(z);\nm = doyDate.getMonth();\nd = doyDate.getDate();\n}";code+="if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n{return new Date(y, m, d, h, i, s);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n{return new Date(y, m, d, h, i);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0)\n{return new Date(y, m, d, h);}\nelse if (y > 0 && m >= 0 && d > 0)\n{return new Date(y, m, d);}\nelse if (y > 0 && m >= 0)\n{return new Date(y, m);}\nelse if (y > 0)\n{return new Date(y);}\n}return null;}";Date.parseRegexes[regexNum]=new RegExp("^"+regex+"$");eval(code);};Date.formatCodeToRegex=function(b,a){switch(b){case"D":return{g:0,c:null,s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};case"j":case"d":return{g:1,c:"d = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"l":return{g:0,c:null,s:"(?:"+Date.dayNames.join("|")+")"};case"S":return{g:0,c:null,s:"(?:st|nd|rd|th)"};case"w":return{g:0,c:null,s:"\\d"};case"z":return{g:1,c:"z = parseInt(results["+a+"], 10);\n",s:"(\\d{1,3})"};case"W":return{g:0,c:null,s:"(?:\\d{2})"};case"F":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"].substring(0, 3)], 10);\n",s:"("+Date.monthNames.join("|")+")"};case"M":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"]], 10);\n",s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};case"n":case"m":return{g:1,c:"m = parseInt(results["+a+"], 10) - 1;\n",s:"(\\d{1,2})"};case"t":return{g:0,c:null,s:"\\d{1,2}"};case"L":return{g:0,c:null,s:"(?:1|0)"};case"Y":return{g:1,c:"y = parseInt(results["+a+"], 10);\n",s:"(\\d{4})"};case"y":return{g:1,c:"var ty = parseInt(results["+a+"], 10);\ny = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",s:"(\\d{1,2})"};case"a":return{g:1,c:"if (results["+a+"] == 'am') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(am|pm)"};case"A":return{g:1,c:"if (results["+a+"] == 'AM') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(AM|PM)"};case"g":case"G":case"h":case"H":return{g:1,c:"h = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"i":return{g:1,c:"i = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"s":return{g:1,c:"s = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"O":return{g:0,c:null,s:"[+-]\\d{4}"};case"T":return{g:0,c:null,s:"[A-Z]{3}"};case"Z":return{g:0,c:null,s:"[+-]\\d{1,5}"};default:return{g:0,c:null,s:String.escape(b)};}};Date.prototype.getTimezone=function(){return this.toString().replace(/^.*? ([A-Z]{3}) [0-9]{4}.*$/,"$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/,"$1$2$3");};Date.prototype.getGMTOffset=function(){return(this.getTimezoneOffset()>0?"-":"+")+String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset())/60),2,"0")+String.leftPad(Math.abs(this.getTimezoneOffset())%60,2,"0");};Date.prototype.getDayOfYear=function(){var a=0;Date.daysInMonth[1]=this.isLeapYear()?29:28;for(var b=0;b 255 * $digest_length) { + throw new CannotPerformOperationException(); + } + + // "if [salt] not provided, is set to a string of HashLen zeroes." + if (is_null($salt)) { + $salt = str_repeat("\x00", $digest_length); + } + + // HKDF-Extract: + // PRK = HMAC-Hash(salt, IKM) + // The salt is the HMAC key. + $prk = hash_hmac($hash, $ikm, $salt, true); + + // HKDF-Expand: + + // This check is useless, but it serves as a reminder to the spec. + if (self::our_strlen($prk) < $digest_length) { + throw new CannotPerformOperationException(); + } + + // T(0) = '' + $t = ''; + $last_block = ''; + for ($block_index = 1; self::our_strlen($t) < $length; $block_index++) { + // T(i) = HMAC-Hash(PRK, T(i-1) | info | 0x??) + $last_block = hash_hmac( + $hash, + $last_block . $info . chr($block_index), + $prk, + true + ); + // T = T(1) | T(2) | T(3) | ... | T(N) + $t .= $last_block; + } + + // ORM = first L octets of T + $orm = self::our_substr($t, 0, $length); + if ($orm === FALSE) { + throw new CannotPerformOperationException(); + } + return $orm; + } + + private static function VerifyHMAC($correct_hmac, $message, $key) + { + $message_hmac = hash_hmac(self::HASH_FUNCTION, $message, $key, true); + + // We can't just compare the strings with '==', since it would make + // timing attacks possible. We could use the XOR-OR constant-time + // comparison algorithm, but I'm not sure if that's good enough way up + // here in an interpreted language. So we use the method of HMACing the + // strings we want to compare with a random key, then comparing those. + + // NOTE: This leaks information when the strings are not the same + // length, but they should always be the same length here. Enforce it: + if (self::our_strlen($correct_hmac) !== self::our_strlen($message_hmac)) { + throw new CannotPerformOperationException(); + } + + $blind = self::CreateNewRandomKey(); + $message_compare = hash_hmac(self::HASH_FUNCTION, $message_hmac, $blind); + $correct_compare = hash_hmac(self::HASH_FUNCTION, $correct_hmac, $blind); + return $correct_compare === $message_compare; + } + + private static function TestEncryptDecrypt() + { + $key = self::CreateNewRandomKey(); + $data = "EnCrYpT EvErYThInG\x00\x00"; + + // Make sure encrypting then decrypting doesn't change the message. + $ciphertext = self::Encrypt($data, $key); + try { + $decrypted = self::Decrypt($ciphertext, $key); + } catch (InvalidCiphertextException $ex) { + // It's important to catch this and change it into a + // CryptoTestFailedException, otherwise a test failure could trick + // the user into thinking it's just an invalid ciphertext! + throw new CryptoTestFailedException(); + } + if($decrypted !== $data) + { + throw new CryptoTestFailedException(); + } + + // Modifying the ciphertext: Appending a string. + try { + self::Decrypt($ciphertext . "a", $key); + throw new CryptoTestFailedException(); + } catch (InvalidCiphertextException $e) { /* expected */ } + + // Modifying the ciphertext: Changing an IV byte. + try { + $ciphertext[0] = chr((ord($ciphertext[0]) + 1) % 256); + self::Decrypt($ciphertext, $key); + throw new CryptoTestFailedException(); + } catch (InvalidCiphertextException $e) { /* expected */ } + + // Decrypting with the wrong key. + $key = self::CreateNewRandomKey(); + $data = "abcdef"; + $ciphertext = self::Encrypt($data, $key); + $wrong_key = self::CreateNewRandomKey(); + try { + self::Decrypt($ciphertext, $wrong_key); + throw new CryptoTestFailedException(); + } catch (InvalidCiphertextException $e) { /* expected */ } + + // Ciphertext too small (shorter than HMAC). + $key = self::CreateNewRandomKey(); + $ciphertext = str_repeat("A", self::MAC_BYTE_SIZE - 1); + try { + self::Decrypt($ciphertext, $key); + throw new CryptoTestFailedException(); + } catch (InvalidCiphertextException $e) { /* expected */ } + } + + private static function HKDFTestVector() + { + // HKDF test vectors from RFC 5869 + + // Test Case 1 + $ikm = str_repeat("\x0b", 22); + $salt = self::hexToBytes("000102030405060708090a0b0c"); + $info = self::hexToBytes("f0f1f2f3f4f5f6f7f8f9"); + $length = 42; + $okm = self::hexToBytes( + "3cb25f25faacd57a90434f64d0362f2a" . + "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" . + "34007208d5b887185865" + ); + $computed_okm = self::HKDF("sha256", $ikm, $length, $info, $salt); + if ($computed_okm !== $okm) { + throw new CryptoTestFailedException(); + } + + // Test Case 7 + $ikm = str_repeat("\x0c", 22); + $length = 42; + $okm = self::hexToBytes( + "2c91117204d745f3500d636a62f64f0a" . + "b3bae548aa53d423b0d1f27ebba6f5e5" . + "673a081d70cce7acfc48" + ); + $computed_okm = self::HKDF("sha1", $ikm, $length); + if ($computed_okm !== $okm) { + throw new CryptoTestFailedException(); + } + + } + + private static function HMACTestVector() + { + // HMAC test vector From RFC 4231 (Test Case 1) + $key = str_repeat("\x0b", 20); + $data = "Hi There"; + $correct = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"; + if (hash_hmac(self::HASH_FUNCTION, $data, $key) != $correct) { + throw new CryptoTestFailedException(); + } + } + + private static function AESTestVector() + { + // AES CBC mode test vector from NIST SP 800-38A + $key = self::hexToBytes("2b7e151628aed2a6abf7158809cf4f3c"); + $iv = self::hexToBytes("000102030405060708090a0b0c0d0e0f"); + $plaintext = self::hexToBytes( + "6bc1bee22e409f96e93d7e117393172a" . + "ae2d8a571e03ac9c9eb76fac45af8e51" . + "30c81c46a35ce411e5fbc1191a0a52ef" . + "f69f2445df4f9b17ad2b417be66c3710" + ); + $ciphertext = self::hexToBytes( + "7649abac8119b246cee98e9b12e9197d" . + "5086cb9b507219ee95db113a917678b2" . + "73bed6b8e3c1743b7116e69e22229516" . + "3ff1caa1681fac09120eca307586e1a7" . + /* Block due to padding. Not from NIST test vector. + Padding Block: 10101010101010101010101010101010 + Ciphertext: 3ff1caa1681fac09120eca307586e1a7 + (+) 2fe1dab1780fbc19021eda206596f1b7 + AES 8cb82807230e1321d3fae00d18cc2012 + + */ + "8cb82807230e1321d3fae00d18cc2012" + ); + + $computed_ciphertext = self::PlainEncrypt($plaintext, $key, $iv); + if ($computed_ciphertext !== $ciphertext) { + throw new CryptoTestFailedException(); + } + + $computed_plaintext = self::PlainDecrypt($ciphertext, $key, $iv); + if ($computed_plaintext !== $plaintext) { + throw new CryptoTestFailedException(); + } + } + + /* WARNING: Do not call this function on secrets. It creates side channels. */ + private static function hexToBytes($hex_string) + { + return pack("H*", $hex_string); + } + + private static function EnsureConstantExists($name) + { + if (!defined($name)) { + throw new CannotPerformOperationException(); + } + } + + private static function EnsureFunctionExists($name) + { + if (!function_exists($name)) { + throw new CannotPerformOperationException(); + } + } + + /* + * We need these strlen() and substr() functions because when + * 'mbstring.func_overload' is set in php.ini, the standard strlen() and + * substr() are replaced by mb_strlen() and mb_substr(). + */ + + private static function our_strlen($str) + { + if (function_exists('mb_strlen')) { + $length = mb_strlen($str, '8bit'); + if ($length === FALSE) { + throw new CannotPerformOperationException(); + } + return $length; + } else { + return strlen($str); + } + } + + private static function our_substr($str, $start, $length = NULL) + { + if (function_exists('mb_substr')) + { + // mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP + // 5.3, so we have to find the length ourselves. + if (!isset($length)) { + if ($start >= 0) { + $length = self::our_strlen($str) - $start; + } else { + $length = -$start; + } + } + + return mb_substr($str, $start, $length, '8bit'); + } + + // Unlike mb_substr(), substr() doesn't accept NULL for length + if (isset($length)) { + return substr($str, $start, $length); + } else { + return substr($str, $start); + } + } + +} + +/* + * We want to catch all uncaught exceptions that come from the Crypto class, + * since by default, PHP will leak the key in the stack trace from an uncaught + * exception. This is a really ugly hack, but I think it's justified. + * + * Everything up to handler() getting called should be reliable, so this should + * reliably suppress the stack traces. The rest is just a bonus so that we don't + * make it impossible to debug other exceptions. + * + * This bit of code was adapted from: http://stackoverflow.com/a/7939492 + */ + +class CryptoExceptionHandler +{ + private $rethrow = NULL; + + public function __construct() + { + set_exception_handler(array($this, "handler")); + } + + public function handler($ex) + { + if ( + $ex instanceof InvalidCiphertextException || + $ex instanceof CannotPerformOperationException || + $ex instanceof CryptoTestFailedException + ) { + echo "FATAL ERROR: Uncaught crypto exception. Suppresssing output.\n"; + } else { + /* Re-throw the exception in the destructor. */ + $this->rethrow = $ex; + } + } + + public function __destruct() { + if ($this->rethrow) { + throw $this->rethrow; + } + } +} + +$crypto_exception_handler_object_dont_touch_me = new CryptoExceptionHandler(); + diff --git a/library/defuse/php-encryption-1.2.1/README.md b/library/defuse/php-encryption-1.2.1/README.md new file mode 100644 index 000000000..292ecf957 --- /dev/null +++ b/library/defuse/php-encryption-1.2.1/README.md @@ -0,0 +1,79 @@ +php-encryption +=============== + +This is a class for doing symmetric encryption in PHP. **Requires PHP 5.4 or newer.** + +[![Build Status](https://travis-ci.org/defuse/php-encryption.svg?branch=master)](https://travis-ci.org/defuse/php-encryption) + +Implementation +-------------- + +Messages are encrypted with AES-128 in CBC mode and are authenticated with +HMAC-SHA256 (Encrypt-then-Mac). PKCS7 padding is used to pad the message to +a multiple of the block size. HKDF is used to split the user-provided key into +two keys: one for encryption, and the other for authentication. It is +implemented using the `openssl_` and `hash_hmac` functions. + +Warning +-------- + +This is new code, and it hasn't received much review by experts. I have spent +many hours making it as secure as possible (extensive runtime tests, secure +coding practices), and auditing it for problems, but I may have missed some +issues. So be careful. Don't trust it with your life. Check out the open GitHub +issues for a list of known issues. If you find a problem with this library, +please report it by opening a GitHub issue. + +That said, you're probably much better off using this library than any other +encryption library written in PHP. + +Philosophy +----------- + +This library was created after noticing how much insecure PHP encryption code +there is. I once did a Google search for "php encryption" and found insecure +code or advice on 9 of the top 10 results. + +Encryption is becoming an essential component of modern websites. This library +aims to fulfil a subset of that need: Authenticated symmetric encryption of +short strings, given a random key. + +This library is developed around several core values: + +- Rule #1: Security is prioritized over everything else. + + > Whenever there is a conflict between security and some other property, + > security will be favored. For example, the library has runtime tests, + > which make it slower, but will hopefully stop it from encrypting stuff + > if the platform it's running on is broken. + +- Rule #2: It should be difficult to misuse the library. + + > We assume the developers using this library have no experience with + > cryptography. We only assume that they know that the "key" is something + > you need to encrypt and decrypt the messages, and that it must be + > protected. Whenever possible, the library should refuse to encrypt or + > decrypt messages when it is not being used correctly. + +- Rule #3: The library aims only to be compatible with itself. + + > Other PHP encryption libraries try to support every possible type of + > encryption, even the insecure ones (e.g. ECB mode). Because there are so + > many options, inexperienced developers must make decisions between + > things like "CBC" mode and "ECB" mode, knowing nothing about either one, + > which inevitably creates vulnerabilities. + + > This library will only support one secure mode. A developer using this + > library will call "encrypt" and "decrypt" not caring about how they are + > implemented. + +- Rule #4: The library should consist of a single PHP file and nothing more. + + > Some PHP encryption libraries, like libsodium-php [1], are not + > straightforward to install and cannot packaged with "just download and + > extract" applications. This library will always be just one PHP file + > that you can put in your source tree and require(). + +References: + + [1] https://github.com/jedisct1/libsodium-php diff --git a/library/defuse/php-encryption-1.2.1/benchmark.php b/library/defuse/php-encryption-1.2.1/benchmark.php new file mode 100644 index 000000000..3da61a628 --- /dev/null +++ b/library/defuse/php-encryption-1.2.1/benchmark.php @@ -0,0 +1,42 @@ + diff --git a/library/defuse/php-encryption-1.2.1/composer.json b/library/defuse/php-encryption-1.2.1/composer.json new file mode 100644 index 000000000..6856b9c72 --- /dev/null +++ b/library/defuse/php-encryption-1.2.1/composer.json @@ -0,0 +1,20 @@ +{ + "name": "defuse/php-encryption", + "description": "Secure PHP Encryption Library", + "license": "MIT", + "keywords": ["security", "encryption", "AES", "mcrypt", "cipher"], + "authors": [ + { + "name": "Taylor Hornby", + "email": "havoc@defuse.ca" + } + ], + "autoload": { + "files": ["Crypto.php"] + }, + "require": { + "php": ">=5.4.0", + "ext-openssl": "*", + "ext-mcrypt": "*" + } +} diff --git a/library/defuse/php-encryption-1.2.1/example.php b/library/defuse/php-encryption-1.2.1/example.php new file mode 100644 index 000000000..10e73f915 --- /dev/null +++ b/library/defuse/php-encryption-1.2.1/example.php @@ -0,0 +1,36 @@ + diff --git a/library/defuse/php-encryption-1.2.1/test.sh b/library/defuse/php-encryption-1.2.1/test.sh new file mode 100644 index 000000000..d1691e7c6 --- /dev/null +++ b/library/defuse/php-encryption-1.2.1/test.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +echo "Normal" +echo "--------------------------------------------------" +php -d mbstring.func_overload=0 tests/runtime.php +if [ $? -ne 0 ]; then + echo "FAIL." + exit 1 +fi +echo "--------------------------------------------------" + +echo "" + +echo "Multibyte" +echo "--------------------------------------------------" +php -d mbstring.func_overload=7 tests/runtime.php +if [ $? -ne 0 ]; then + echo "FAIL." + exit 1 +fi +echo "--------------------------------------------------" + +echo "" + +if [ -z "$(php Crypto.php)" ]; then + echo "PASS: Crypto.php output is empty." +else + echo "FAIL: Crypto.php output is not empty." + exit 1 +fi diff --git a/library/defuse/php-encryption-1.2.1/tests/runtime.php b/library/defuse/php-encryption-1.2.1/tests/runtime.php new file mode 100644 index 000000000..76565c58e --- /dev/null +++ b/library/defuse/php-encryption-1.2.1/tests/runtime.php @@ -0,0 +1,32 @@ + diff --git a/mod/_well_known.php b/mod/_well_known.php index 6792d36fe..33070a1ec 100644 --- a/mod/_well_known.php +++ b/mod/_well_known.php @@ -1,14 +1,66 @@ argc > 1) { - switch($a->argv[1]) { - case "host-meta": - hostxrd_init($a); - break; - } - } - http_status_exit(404); - killme(); + if ($a->argc > 1) { + switch($a->argv[1]) { + case "host-meta": + hostxrd_init($a); + break; + case "x-social-relay": + wk_social_relay($a); + break; + case "nodeinfo": + nodeinfo_wellknown($a); + break; + } + } + http_status_exit(404); + killme(); +} + +function wk_social_relay(&$a) { + + define('SR_SCOPE_ALL', 'all'); + define('SR_SCOPE_TAGS', 'tags'); + + $subscribe = (bool)get_config('system', 'relay_subscribe'); + + if ($subscribe) + $scope = get_config('system', 'relay_scope'); + else + $scope = ""; + + $tags = array(); + + if ($scope == SR_SCOPE_TAGS) { + + $server_tags = get_config('system', 'relay_server_tags'); + $tagitems = explode(",", $server_tags); + + foreach($tagitems AS $tag) + $tags[trim($tag, "# ")] = trim($tag, "# "); + + if (get_config('system', 'relay_user_tags')) { + $terms = q("SELECT DISTINCT(`term`) FROM `search`"); + + foreach($terms AS $term) { + $tag = trim($term["term"], "#"); + $tags[$tag] = $tag; + } + } + } + + $taglist = array(); + foreach($tags AS $tag) + $taglist[] = $tag; + + $relay = array("subscribe" => $subscribe, + "scope" => $scope, + "tags" => $taglist); + + header('Content-type: application/json; charset=utf-8'); + echo json_encode($relay, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + exit; } diff --git a/mod/admin.php b/mod/admin.php index 0aa902373..878508a23 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -105,7 +105,8 @@ function admin_content(&$a) { 'users' => Array($a->get_baseurl(true)."/admin/users/", t("Users") , "users"), 'plugins'=> Array($a->get_baseurl(true)."/admin/plugins/", t("Plugins") , "plugins"), 'themes' => Array($a->get_baseurl(true)."/admin/themes/", t("Themes") , "themes"), - 'dbsync' => Array($a->get_baseurl(true)."/admin/dbsync/", t('DB updates'), "dbsync"), + 'dbsync' => Array($a->get_baseurl(true)."/admin/dbsync/", t('DB updates'), "dbsync"), + 'queue' => Array($a->get_baseurl(true)."/admin/queue/", t('Inspect Queue'), "queue"), //'update' => Array($a->get_baseurl(true)."/admin/update/", t("Software Update") , "update") ); @@ -165,6 +166,9 @@ function admin_content(&$a) { case 'update': $o = admin_page_remoteupdate($a); break; + case 'queue': + $o = admin_page_queue($a); + break; default: notice( t("Item not found.") ); } @@ -181,7 +185,30 @@ function admin_content(&$a) { } } +/** + * Admin Inspect Queue Page + * @param App $a + * return string + */ +function admin_page_queue(&$a) { + // get content from the queue table + $r = q("SELECT c.name,c.nurl,q.id,q.network,q.created,q.last from queue as q, contact as c where c.id=q.cid order by q.cid, q.created;"); + $t = get_markup_template("admin_queue.tpl"); + return replace_macros($t, array( + '$title' => t('Administration'), + '$page' => t('Inspect Queue'), + '$count' => sizeof($r), + 'id_header' => t('ID'), + '$to_header' => t('Recipient Name'), + '$url_header' => t('Recipient Profile'), + '$network_header' => t('Network'), + '$created_header' => t('Created'), + '$last_header' => t('Last Tried'), + '$info' => t('This page lists the content of the queue for outgoing postings. These are postings the initial delivery failed for. They will be resend later and eventually deleted if the delivery fails permanently.'), + '$entries' => $r, + )); +} /** * Admin Summary Page * @param App $a @@ -226,6 +253,7 @@ function admin_page_summary(&$a) { '$accounts' => $accounts, '$pending' => Array( t('Pending registrations'), $pending), '$version' => Array( t('Version'), FRIENDICA_VERSION), + '$baseurl' => $a->get_baseurl(), '$platform' => FRIENDICA_PLATFORM, '$codename' => FRIENDICA_CODENAME, '$build' => get_config('system','build'), @@ -335,7 +363,7 @@ function admin_page_site_post(&$a){ $allowed_email = ((x($_POST,'allowed_email')) ? notags(trim($_POST['allowed_email'])) : ''); $block_public = ((x($_POST,'block_public')) ? True : False); $force_publish = ((x($_POST,'publish_all')) ? True : False); - $global_directory = ((x($_POST,'directory_submit_url')) ? notags(trim($_POST['directory_submit_url'])) : ''); + $global_directory = ((x($_POST,'directory')) ? notags(trim($_POST['directory'])) : ''); $thread_allow = ((x($_POST,'thread_allow')) ? True : False); $newuser_private = ((x($_POST,'newuser_private')) ? True : False); $enotify_no_content = ((x($_POST,'enotify_no_content')) ? True : False); @@ -357,6 +385,13 @@ function admin_page_site_post(&$a){ $delivery_interval = ((x($_POST,'delivery_interval')) ? intval(trim($_POST['delivery_interval'])) : 0); $poll_interval = ((x($_POST,'poll_interval')) ? intval(trim($_POST['poll_interval'])) : 0); $maxloadavg = ((x($_POST,'maxloadavg')) ? intval(trim($_POST['maxloadavg'])) : 50); + $maxloadavg_frontend = ((x($_POST,'maxloadavg_frontend')) ? intval(trim($_POST['maxloadavg_frontend'])) : 50); + $poco_completion = ((x($_POST,'poco_completion')) ? intval(trim($_POST['poco_completion'])) : false); + $poco_requery_days = ((x($_POST,'poco_requery_days')) ? intval(trim($_POST['poco_requery_days'])) : 7); + $poco_discovery = ((x($_POST,'poco_discovery')) ? intval(trim($_POST['poco_discovery'])) : 0); + $poco_discovery_since = ((x($_POST,'poco_discovery_since')) ? intval(trim($_POST['poco_discovery_since'])) : 30); + $poco_local_search = ((x($_POST,'poco_local_search')) ? intval(trim($_POST['poco_local_search'])) : false); + $nodeinfo = ((x($_POST,'nodeinfo')) ? intval(trim($_POST['nodeinfo'])) : false); $dfrn_only = ((x($_POST,'dfrn_only')) ? True : False); $ostatus_disabled = !((x($_POST,'ostatus_disabled')) ? True : False); $ostatus_poll_interval = ((x($_POST,'ostatus_poll_interval')) ? intval(trim($_POST['ostatus_poll_interval'])) : 0); @@ -378,6 +413,9 @@ function admin_page_site_post(&$a){ $proxy_disabled = ((x($_POST,'proxy_disabled')) ? True : False); $old_pager = ((x($_POST,'old_pager')) ? True : False); $only_tag_search = ((x($_POST,'only_tag_search')) ? True : False); + $rino = ((x($_POST,'rino')) ? intval($_POST['rino']) : 0); + $embedly = ((x($_POST,'embedly')) ? notags(trim($_POST['embedly'])) : ''); + if($ssl_policy != intval(get_config('system','ssl_policy'))) { if($ssl_policy == SSL_POLICY_FULL) { @@ -423,6 +461,13 @@ function admin_page_site_post(&$a){ set_config('system','delivery_interval',$delivery_interval); set_config('system','poll_interval',$poll_interval); set_config('system','maxloadavg',$maxloadavg); + set_config('system','maxloadavg_frontend',$maxloadavg_frontend); + set_config('system','poco_completion',$poco_completion); + set_config('system','poco_requery_days',$poco_requery_days); + set_config('system','poco_discovery',$poco_discovery); + set_config('system','poco_discovery_since',$poco_discovery_since); + set_config('system','poco_local_search',$poco_local_search); + set_config('system','nodeinfo',$nodeinfo); set_config('config','sitename',$sitename); set_config('config','hostname',$hostname); set_config('config','sender_email', $sender_email); @@ -430,6 +475,7 @@ function admin_page_site_post(&$a){ set_config('system','suppress_tags',$suppress_tags); set_config('system','shortcut_icon',$shortcut_icon); set_config('system','touch_icon',$touch_icon); + if ($banner==""){ // don't know why, but del_config doesn't work... q("DELETE FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", @@ -468,15 +514,7 @@ function admin_page_site_post(&$a){ set_config('system','allowed_email', $allowed_email); set_config('system','block_public', $block_public); set_config('system','publish_all', $force_publish); - if ($global_directory==""){ - // don't know why, but del_config doesn't work... - q("DELETE FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", - dbesc("system"), - dbesc("directory_submit_url") - ); - } else { - set_config('system','directory_submit_url', $global_directory); - } + set_config('system','directory', $global_directory); set_config('system','thread_allow', $thread_allow); set_config('system','newuser_private', $newuser_private); set_config('system','enotify_no_content', $enotify_no_content); @@ -513,6 +551,16 @@ function admin_page_site_post(&$a){ set_config('system','old_pager', $old_pager); set_config('system','only_tag_search', $only_tag_search); + + if ($rino==2 and !function_exists('mcrypt_create_iv')){ + notice(t("RINO2 needs mcrypt php extension to work.")); + } else { + set_config('system','rino_encrypt', $rino); + } + + set_config('system','embedly', $embedly); + + info( t('Site settings updated.') . EOL); goaway($a->get_baseurl(true) . '/admin/site' ); return; // NOTREACHED @@ -527,7 +575,7 @@ function admin_page_site(&$a) { /* Installed langs */ $lang_choices = array(); - $langs = glob('view/*/strings.php'); + $langs = glob('view/*/strings.php'); /**/ if(is_array($langs) && count($langs)) { if(! in_array('view/en/strings.php',$langs)) @@ -539,6 +587,12 @@ function admin_page_site(&$a) { } } + if (strlen(get_config('system','directory_submit_url')) AND + !strlen(get_config('system','directory'))) { + set_config('system','directory', dirname(get_config('system','directory_submit_url'))); + del_config('system','directory_submit_url'); + } + /* Installed themes */ $theme_choices = array(); $theme_choices_mobile = array(); @@ -574,6 +628,20 @@ function admin_page_site(&$a) { "1440" => t("Daily") ); + $poco_discovery_choices = array( + "0" => t("Disabled"), + "1" => t("Users"), + "2" => t("Users, Global Contacts"), + "3" => t("Users, Global Contacts/fallback"), + ); + + $poco_discovery_since_choices = array( + "30" => t("One month"), + "91" => t("Three months"), + "182" => t("Half a year"), + "365" => t("One year"), + ); + /* get user names to make the install a personal install of X */ $user_names = array(); $user_names['---'] = t('Multi user instance'); @@ -622,17 +690,18 @@ function admin_page_site(&$a) { '$upload' => t('File upload'), '$corporate' => t('Policies'), '$advanced' => t('Advanced'), + '$portable_contacts' => t('Auto Discovered Contact Directory'), '$performance' => t('Performance'), '$relocate'=> t('Relocate - WARNING: advanced function. Could make this server unreachable.'), '$baseurl' => $a->get_baseurl(true), // name, label, value, help string, extra data... - '$sitename' => array('sitename', t("Site name"), htmlentities($a->config['sitename'], ENT_QUOTES), 'UTF-8'), + '$sitename' => array('sitename', t("Site name"), $a->config['sitename'],''), '$hostname' => array('hostname', t("Host name"), $a->config['hostname'], ""), - '$sender_email' => array('sender_email', t("Sender Email"), $a->config['sender_email'], "The email address your server shall use to send notification emails from.", "", "", "email"), + '$sender_email' => array('sender_email', t("Sender Email"), $a->config['sender_email'], t("The email address your server shall use to send notification emails from."), "", "", "email"), '$banner' => array('banner', t("Banner/Logo"), $banner, ""), - '$shortcut_icon' => array('shortcut_icon', t("Shortcut icon"), get_config('system','shortcut_icon'), "Link to an icon that will be used for browsers."), - '$touch_icon' => array('touch_icon', t("Touch icon"), get_config('system','touch_icon'), "Link to an icon that will be used for tablets and mobiles."), - '$info' => array('info',t('Additional Info'), $info, t('For public servers: you can add additional information here that will be listed at dir.friendica.com/siteinfo.')), + '$shortcut_icon' => array('shortcut_icon', t("Shortcut icon"), get_config('system','shortcut_icon'), t("Link to an icon that will be used for browsers.")), + '$touch_icon' => array('touch_icon', t("Touch icon"), get_config('system','touch_icon'), t("Link to an icon that will be used for tablets and mobiles.")), + '$info' => array('info',t('Additional Info'), $info, sprintf(t('For public servers: you can add additional information here that will be listed at %s/siteinfo.'), get_server())), '$language' => array('language', t("System language"), get_config('system','language'), "", $lang_choices), '$theme' => array('theme', t("System theme"), get_config('system','theme'), t("Default system theme - may be over-ridden by user profiles - change theme settings"), $theme_choices), '$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile-theme'), t("Theme for mobile devices"), $theme_choices_mobile), @@ -647,13 +716,13 @@ function admin_page_site(&$a) { '$register_policy' => array('register_policy', t("Register policy"), $a->config['register_policy'], "", $register_choices), '$daily_registrations' => array('max_daily_registrations', t("Maximum Daily Registrations"), get_config('system', 'max_daily_registrations'), t("If registration is permitted above, this sets the maximum number of new user registrations to accept per day. If register is set to closed, this setting has no effect.")), - '$register_text' => array('register_text', t("Register text"), htmlentities($a->config['register_text'], ENT_QUOTES, 'UTF-8'), t("Will be displayed prominently on the registration page.")), + '$register_text' => array('register_text', t("Register text"), $a->config['register_text'], t("Will be displayed prominently on the registration page.")), '$abandon_days' => array('abandon_days', t('Accounts abandoned after x days'), get_config('system','account_abandon_days'), t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.')), '$allowed_sites' => array('allowed_sites', t("Allowed friend domains"), get_config('system','allowed_sites'), t("Comma separated list of domains which are allowed to establish friendships with this site. Wildcards are accepted. Empty to allow any domains")), '$allowed_email' => array('allowed_email', t("Allowed email domains"), get_config('system','allowed_email'), t("Comma separated list of domains which are allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains")), '$block_public' => array('block_public', t("Block public"), get_config('system','block_public'), t("Check to block public access to all otherwise public personal pages on this site unless you are currently logged in.")), '$force_publish' => array('publish_all', t("Force publish"), get_config('system','publish_all'), t("Check to force all profiles on this site to be listed in the site directory.")), - '$global_directory' => array('directory_submit_url', t("Global directory update URL"), get_config('system','directory_submit_url'), t("URL to update the global directory. If this is not set, the global directory is completely unavailable to the application.")), + '$global_directory' => array('directory', t("Global directory URL"), get_config('system','directory'), t("URL to the global directory. If this is not set, the global directory is completely unavailable to the application.")), '$thread_allow' => array('thread_allow', t("Allow threaded items"), get_config('system','thread_allow'), t("Allow infinite level threading for items on this site.")), '$newuser_private' => array('newuser_private', t("Private posts by default for new users"), get_config('system','newuser_private'), t("Set default post permissions for all new members to the default privacy group rather than public.")), '$enotify_no_content' => array('enotify_no_content', t("Don't include post content in email notifications"), get_config('system','enotify_no_content'), t("Don't include the content of a post/comment/private message/etc. in the email notifications that are sent out from this site, as a privacy measure.")), @@ -677,22 +746,35 @@ function admin_page_site(&$a) { '$delivery_interval' => array('delivery_interval', t("Delivery interval"), (x(get_config('system','delivery_interval'))?get_config('system','delivery_interval'):2), t("Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.")), '$poll_interval' => array('poll_interval', t("Poll interval"), (x(get_config('system','poll_interval'))?get_config('system','poll_interval'):2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")), '$maxloadavg' => array('maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.")), + '$maxloadavg_frontend' => array('maxloadavg_frontend', t("Maximum Load Average (Frontend)"), ((intval(get_config('system','maxloadavg_frontend')) > 0)?get_config('system','maxloadavg_frontend'):50), t("Maximum system load before the frontend quits service - default 50.")), + + '$poco_completion' => array('poco_completion', t("Periodical check of global contacts"), get_config('system','poco_completion'), t("If enabled, the global contacts are checked periodically for missing or outdated data and the vitality of the contacts and servers.")), + '$poco_requery_days' => array('poco_requery_days', t("Days between requery"), get_config('system','poco_requery_days'), t("Number of days after which a server is requeried for his contacts.")), + '$poco_discovery' => array('poco_discovery', t("Discover contacts from other servers"), (string) intval(get_config('system','poco_discovery')), t("Periodically query other servers for contacts. You can choose between 'users': the users on the remote system, 'Global Contacts': active contacts that are known on the system. The fallback is meant for Redmatrix servers and older friendica servers, where global contacts weren't available. The fallback increases the server load, so the recommened setting is 'Users, Global Contacts'."), $poco_discovery_choices), + '$poco_discovery_since' => array('poco_discovery_since', t("Timeframe for fetching global contacts"), (string) intval(get_config('system','poco_discovery_since')), t("When the discovery is activated, this value defines the timeframe for the activity of the global contacts that are fetched from other servers."), $poco_discovery_since_choices), + '$poco_local_search' => array('poco_local_search', t("Search the local directory"), get_config('system','poco_local_search'), t("Search the local directory instead of the global directory. When searching locally, every search will be executed on the global directory in the background. This improves the search results when the search is repeated.")), + + '$nodeinfo' => array('nodeinfo', t("Publish server information"), get_config('system','nodeinfo'), t("If enabled, general server and usage data will be published. The data contains the name and version of the server, number of users with public profiles, number of posts and the activated protocols and connectors. See the-federation.info for details.")), '$use_fulltext_engine' => array('use_fulltext_engine', t("Use MySQL full text engine"), get_config('system','use_fulltext_engine'), t("Activates the full text engine. Speeds up search - but can only search for four and more characters.")), '$suppress_language' => array('suppress_language', t("Suppress Language"), get_config('system','suppress_language'), t("Suppress language information in meta information about a posting.")), '$suppress_tags' => array('suppress_tags', t("Suppress Tags"), get_config('system','suppress_tags'), t("Suppress showing a list of hashtags at the end of the posting.")), - '$itemcache' => array('itemcache', t("Path to item cache"), get_config('system','itemcache'), "The item caches buffers generated bbcode and external images."), + '$itemcache' => array('itemcache', t("Path to item cache"), get_config('system','itemcache'), t("The item caches buffers generated bbcode and external images.")), '$itemcache_duration' => array('itemcache_duration', t("Cache duration in seconds"), get_config('system','itemcache_duration'), t("How long should the cache files be hold? Default value is 86400 seconds (One day). To disable the item cache, set the value to -1.")), '$max_comments' => array('max_comments', t("Maximum numbers of comments per post"), get_config('system','max_comments'), t("How much comments should be shown for each post? Default value is 100.")), - '$lockpath' => array('lockpath', t("Path for lock file"), get_config('system','lockpath'), "The lock file is used to avoid multiple pollers at one time. Only define a folder here."), - '$temppath' => array('temppath', t("Temp path"), get_config('system','temppath'), "If you have a restricted system where the webserver can't access the system temp path, enter another path here."), - '$basepath' => array('basepath', t("Base path to installation"), get_config('system','basepath'), "If the system cannot detect the correct path to your installation, enter the correct path here. This setting should only be set if you are using a restricted system and symbolic links to your webroot."), + '$lockpath' => array('lockpath', t("Path for lock file"), get_config('system','lockpath'), t("The lock file is used to avoid multiple pollers at one time. Only define a folder here.")), + '$temppath' => array('temppath', t("Temp path"), get_config('system','temppath'), t("If you have a restricted system where the webserver can't access the system temp path, enter another path here.")), + '$basepath' => array('basepath', t("Base path to installation"), get_config('system','basepath'), t("If the system cannot detect the correct path to your installation, enter the correct path here. This setting should only be set if you are using a restricted system and symbolic links to your webroot.")), '$proxy_disabled' => array('proxy_disabled', t("Disable picture proxy"), get_config('system','proxy_disabled'), t("The picture proxy increases performance and privacy. It shouldn't be used on systems with very low bandwith.")), '$old_pager' => array('old_pager', t("Enable old style pager"), get_config('system','old_pager'), t("The old style pager has page numbers but slows down massively the page speed.")), '$only_tag_search' => array('only_tag_search', t("Only search in tags"), get_config('system','only_tag_search'), t("On large systems the text search can slow down the system extremely.")), - '$relocate_url' => array('relocate_url', t("New base url"), $a->get_baseurl(), "Change base url for this server. Sends relocate message to all DFRN contacts of all users."), - '$form_security_token' => get_form_security_token("admin_site") + '$relocate_url' => array('relocate_url', t("New base url"), $a->get_baseurl(), t("Change base url for this server. Sends relocate message to all DFRN contacts of all users.")), + + '$rino' => array('rino', t("RINO Encryption"), intval(get_config('system','rino_encrypt')), t("Encryption layer between nodes."), array("Disabled", "RINO1 (deprecated)", "RINO2")), + '$embedly' => array('embedly', t("Embedly API key"), get_config('system','embedly'), t("Embedly is used to fetch additional data for web pages. This is an optional parameter.")), + + '$form_security_token' => get_form_security_token("admin_site") )); @@ -943,11 +1025,8 @@ function admin_page_users(&$a){ intval($a->pager['itemspage']) ); - function _setup_users($e){ - $a = get_app(); - - $adminlist = explode(",", str_replace(" ", "", $a->config['admin_email'])); - + $adminlist = explode(",", str_replace(" ", "", $a->config['admin_email'])); + $_setup_users = function ($e) use ($adminlist){ $accounts = Array( t('Normal Account'), t('Soapbox Account'), @@ -960,10 +1039,11 @@ function admin_page_users(&$a){ $e['lastitem_date'] = relative_date($e['lastitem_date']); //$e['is_admin'] = ($e['email'] === $a->config['admin_email']); $e['is_admin'] = in_array($e['email'], $adminlist); + $e['is_deletable'] = (intval($e['uid']) != local_user()); $e['deleted'] = ($e['account_removed']?relative_date($e['account_expires_on']):False); return $e; - } - $users = array_map("_setup_users", $users); + }; + $users = array_map($_setup_users, $users); // Get rid of dashes in key names, Smarty3 can't handle them @@ -1126,6 +1206,13 @@ function admin_page_plugins(&$a){ * List plugins */ + if (x($_GET,"a") && $_GET['a']=="r"){ + check_form_security_token_redirectOnErr($a->get_baseurl().'/admin/plugins', 'admin_themes', 't'); + reload_plugins(); + info("Plugins reloaded"); + goaway($a->get_baseurl().'/admin/plugins'); + } + $plugins = array(); $files = glob("addon/*/"); /* */ if($files) { diff --git a/mod/allfriends.php b/mod/allfriends.php index f675b8e29..1a45775fb 100644 --- a/mod/allfriends.php +++ b/mod/allfriends.php @@ -20,18 +20,22 @@ function allfriends_content(&$a) { intval(local_user()) ); - $a->page['aside'] .= '
' - . '
' . $c[0]['name'] . '
' - . '
' - . '' . $c[0]['name'] . '
' - . '
'; - + $vcard_widget .= replace_macros(get_markup_template("vcard-widget.tpl"),array( + '$name' => $c[0]['name'], + '$photo' => $c[0]['photo'], + 'url' => z_root() . '/contacts/' . $cid + )); + + if(! x($a->page,'aside')) + $a->page['aside'] = ''; + $a->page['aside'] .= $vcard_widget; if(! count($c)) return; - $o .= '

' . sprintf( t('Friends of %s'), $c[0]['name']) . '

'; + $o .= replace_macros(get_markup_template("section_title.tpl"),array( + '$title' => sprintf( t('Friends of %s'), $c[0]['name']) + )); $r = all_friends(local_user(),$cid); diff --git a/mod/api.php b/mod/api.php index 63f8a934c..e3b5ea3a9 100644 --- a/mod/api.php +++ b/mod/api.php @@ -4,19 +4,19 @@ require_once('include/api.php'); function oauth_get_client($request){ - + $params = $request->get_parameters(); $token = $params['oauth_token']; - - $r = q("SELECT `clients`.* - FROM `clients`, `tokens` - WHERE `clients`.`client_id`=`tokens`.`client_id` + + $r = q("SELECT `clients`.* + FROM `clients`, `tokens` + WHERE `clients`.`client_id`=`tokens`.`client_id` AND `tokens`.`id`='%s' AND `tokens`.`scope`='request'", dbesc($token)); if (!count($r)) return null; - + return $r[0]; } @@ -36,31 +36,31 @@ function api_post(&$a) { function api_content(&$a) { if ($a->cmd=='api/oauth/authorize'){ - /* + /* * api/oauth/authorize interact with the user. return a standard page */ - + $a->page['template'] = "minimal"; - - + + // get consumer/client from request token try { $request = OAuthRequest::from_request(); } catch(Exception $e) { echo "
"; var_dump($e); killme();
 		}
-		
-		
+
+
 		if (x($_POST,'oauth_yes')){
-		
+
 			$app = oauth_get_client($request);
 			if (is_null($app)) return "Invalid request. Unknown token.";
 			$consumer = new OAuthConsumer($app['client_id'], $app['pw'], $app['redirect_uri']);
 
 			$verifier = md5($app['secret'].local_user());
 			set_config("oauth", $verifier, local_user());
-			
-			
+
+
 			if ($consumer->callback_url!=null) {
 				$params = $request->get_parameters();
 				$glue="?";
@@ -68,35 +68,35 @@ function api_content(&$a) {
 				goaway($consumer->callback_url.$glue."oauth_token=".OAuthUtil::urlencode_rfc3986($params['oauth_token'])."&oauth_verifier=".OAuthUtil::urlencode_rfc3986($verifier));
 				killme();
 			}
-			
-			
-			
+
+
+
 			$tpl = get_markup_template("oauth_authorize_done.tpl");
 			$o = replace_macros($tpl, array(
 				'$title' => t('Authorize application connection'),
 				'$info' => t('Return to your app and insert this Securty Code:'),
 				'$code' => $verifier,
 			));
-		
+
 			return $o;
-		
-		
+
+
 		}
-		
-		
+
+
 		if(! local_user()) {
 			//TODO: we need login form to redirect to this page
 			notice( t('Please login to continue.') . EOL );
 			return login(false,$request->get_parameters());
 		}
 		//FKOAuth1::loginUser(4);
-		
+
 		$app = oauth_get_client($request);
 		if (is_null($app)) return "Invalid request. Unknown token.";
-		
-		
 
-		
+
+
+
 		$tpl = get_markup_template('oauth_authorize.tpl');
 		$o = replace_macros($tpl, array(
 			'$title' => t('Authorize application connection'),
@@ -105,15 +105,12 @@ function api_content(&$a) {
 			'$yes'	=> t('Yes'),
 			'$no'	=> t('No'),
 		));
-		
+
 		//echo "
"; var_dump($app); killme();
-		
+
 		return $o;
 	}
 
 	echo api_call($a);
 	killme();
 }
-
-
-
diff --git a/mod/bookmarklet.php b/mod/bookmarklet.php
index e1759ab65..be8645c1f 100644
--- a/mod/bookmarklet.php
+++ b/mod/bookmarklet.php
@@ -27,7 +27,7 @@ function bookmarklet_content(&$a) {
 			'nickname' => $a->user['nickname'],
 			'lockstate' => ((is_array($a->user) && ((strlen($a->user['allow_cid'])) || (strlen($a->user['allow_gid'])) || (strlen($a->user['deny_cid'])) || (strlen($a->user['deny_gid'])))) ? 'lock' : 'unlock'),
 			'default_perms' => get_acl_permissions($a->user),
-			'acl' => populate_acl($a->user, $celeb),
+			'acl' => populate_acl($a->user,true),
 			'bang' => '',
 			'visitor' => 'block',
 			'profile_uid' => local_user(),
diff --git a/mod/common.php b/mod/common.php
index 617b5b670..3118d1247 100644
--- a/mod/common.php
+++ b/mod/common.php
@@ -28,18 +28,22 @@ function common_content(&$a) {
 		);
 	}	
 
-	$a->page['aside'] .= '
' - . '
' . $c[0]['name'] . '
' - . '
' - . '' . $c[0]['name'] . '
' - . '
'; - + $vcard_widget .= replace_macros(get_markup_template("vcard-widget.tpl"),array( + '$name' => $c[0]['name'], + '$photo' => $c[0]['photo'], + 'url' => z_root() . '/contacts/' . $cid + )); + + if(! x($a->page,'aside')) + $a->page['aside'] = ''; + $a->page['aside'] .= $vcard_widget; if(! count($c)) return; - $o .= '

' . t('Common Friends') . '

'; + $o .= replace_macros(get_markup_template("section_title.tpl"),array( + '$title' => t('Common Friends') + )); if(! $cid) { diff --git a/mod/contacts.php b/mod/contacts.php index 95b1944dc..89154eded 100644 --- a/mod/contacts.php +++ b/mod/contacts.php @@ -3,7 +3,9 @@ require_once('include/Contact.php'); require_once('include/socgraph.php'); require_once('include/contact_selectors.php'); +require_once('include/Scrape.php'); require_once('mod/proxy.php'); +require_once('include/Photo.php'); function contacts_init(&$a) { if(! local_user()) @@ -33,7 +35,7 @@ function contacts_init(&$a) { $vcard_widget = replace_macros(get_markup_template("vcard-widget.tpl"),array( '$name' => $a->data['contact']['name'], '$photo' => $a->data['contact']['photo'], - '$url' => ($a->data['contact']['network'] == 'dfrn') ? $a->get_baseurl()."/redir/".$a->data['contact']['id'] : $a->data['contact']['url'] + '$url' => ($a->data['contact']['network'] == NETWORK_DFRN) ? $a->get_baseurl()."/redir/".$a->data['contact']['id'] : $a->data['contact']['url'] )); $follow_widget = ''; } @@ -204,9 +206,99 @@ function contacts_post(&$a) { /*contact actions*/ function _contact_update($contact_id) { - // pull feed and consume it, which should subscribe to the hub. - proc_run('php',"include/poller.php","$contact_id"); + $r = q("SELECT `uid`, `url`, `network` FROM `contact` WHERE `id` = %d", intval($contact_id)); + if (!$r) + return; + + $uid = $r[0]["uid"]; + + if ($uid != local_user()) + return; + + if ($r[0]["network"] == NETWORK_OSTATUS) { + $result = new_contact($uid, $r[0]["url"], false); + + if ($result['success']) + $r = q("UPDATE `contact` SET `subhub` = 1 WHERE `id` = %d", + intval($contact_id)); + } else + // pull feed and consume it, which should subscribe to the hub. + proc_run('php',"include/onepoll.php","$contact_id", "force"); } + +function _contact_update_profile($contact_id) { + $r = q("SELECT `uid`, `url`, `network` FROM `contact` WHERE `id` = %d", intval($contact_id)); + if (!$r) + return; + + $uid = $r[0]["uid"]; + + if ($uid != local_user()) + return; + + $data = probe_url($r[0]["url"]); + + // "Feed" is mostly a sign of communication problems + if (($data["network"] == NETWORK_FEED) AND ($data["network"] != $r[0]["network"])) + return; + + $updatefields = array("name", "nick", "url", "addr", "batch", "notify", "poll", "request", "confirm", + "poco", "network", "alias", "pubkey"); + $update = array(); + + if ($data["network"] == NETWORK_OSTATUS) { + $result = new_contact($uid, $data["url"], false); + + if ($result['success']) + $update["subhub"] = true; + } + + foreach($updatefields AS $field) + if (isset($data[$field]) AND ($data[$field] != "")) + $update[$field] = $data[$field]; + + $update["nurl"] = normalise_link($data["url"]); + + $query = ""; + + if (isset($data["priority"]) AND ($data["priority"] != 0)) + $query = "`priority` = ".intval($data["priority"]); + + foreach($update AS $key => $value) { + if ($query != "") + $query .= ", "; + + $query .= "`".$key."` = '".dbesc($value)."'"; + } + + if ($query == "") + return; + + $r = q("UPDATE `contact` SET $query WHERE `id` = %d AND `uid` = %d", + intval($contact_id), + intval(local_user()) + ); + + $photos = import_profile_photo($data['photo'], local_user(), $contact_id); + + $r = q("UPDATE `contact` SET `photo` = '%s', + `thumb` = '%s', + `micro` = '%s', + `name-date` = '%s', + `uri-date` = '%s', + `avatar-date` = '%s' + WHERE `id` = %d", + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($contact_id) + ); + +} + function _contact_block($contact_id, $orig_record) { $blocked = (($orig_record['blocked']) ? 0 : 1); $r = q("UPDATE `contact` SET `blocked` = %d WHERE `id` = %d AND `uid` = %d", @@ -284,6 +376,12 @@ function contacts_content(&$a) { // NOTREACHED } + if($cmd === 'updateprofile') { + _contact_update_profile($contact_id); + goaway($a->get_baseurl(true) . '/crepair/' . $contact_id); + // NOTREACHED + } + if($cmd === 'block') { $r = _contact_block($contact_id, $orig_record[0]); if($r) { @@ -334,7 +432,9 @@ function contacts_content(&$a) { } $a->page['aside'] = ''; - return replace_macros(get_markup_template('confirm.tpl'), array( + + return replace_macros(get_markup_template('contact_drop_confirm.tpl'), array( + '$contact' => _contact_detail_for_template($orig_record[0]), '$method' => 'get', '$message' => t('Do you really want to delete this contact?'), '$extra_inputs' => $inputs, @@ -433,7 +533,7 @@ function contacts_content(&$a) { $poll_enabled = in_array($contact['network'], array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_FEED, NETWORK_MAIL, NETWORK_MAIL2)); - $nettype = sprintf( t('Network type: %s'),network_to_name($contact['network'])); + $nettype = sprintf( t('Network type: %s'),network_to_name($contact['network'], $contact["url"])); $common = count_common_friends(local_user(),$contact['id']); $common_text = (($common) ? sprintf( tt('%d contact in common','%d contacts in common', $common),$common) : ''); @@ -450,12 +550,14 @@ function contacts_content(&$a) { 'url' => $a->get_baseurl(true) . '/contacts/' . $contact_id . '/block', 'sel' => '', 'title' => t('Toggle Blocked status'), + 'accesskey' => 'b', ), array( 'label' => (($contact['readonly']) ? t('Unignore') : t('Ignore') ), 'url' => $a->get_baseurl(true) . '/contacts/' . $contact_id . '/ignore', 'sel' => '', 'title' => t('Toggle Ignored status'), + 'accesskey' => 'i', ), array( @@ -463,12 +565,14 @@ function contacts_content(&$a) { 'url' => $a->get_baseurl(true) . '/contacts/' . $contact_id . '/archive', 'sel' => '', 'title' => t('Toggle Archive status'), + 'accesskey' => 'v', ), array( 'label' => t('Repair'), 'url' => $a->get_baseurl(true) . '/crepair/' . $contact_id, 'sel' => '', 'title' => t('Advanced Contact Settings'), + 'accesskey' => 'r', ) ); $tab_tpl = get_markup_template('common_tabs.tpl'); @@ -533,6 +637,8 @@ function contacts_content(&$a) { '$alt_text' => $alt_text, '$sparkle' => $sparkle, '$url' => $url, + '$profileurllabel' => t('Profile URL'), + '$profileurl' => $contact['url'], )); @@ -578,21 +684,24 @@ function contacts_content(&$a) { $tabs = array( array( 'label' => t('Suggestions'), - 'url' => $a->get_baseurl(true) . '/suggest', + 'url' => $a->get_baseurl(true) . '/suggest', 'sel' => '', 'title' => t('Suggest potential friends'), + 'accesskey' => 'g', ), array( 'label' => t('All Contacts'), - 'url' => $a->get_baseurl(true) . '/contacts/all', + 'url' => $a->get_baseurl(true) . '/contacts/all', 'sel' => ($all) ? 'active' : '', 'title' => t('Show all contacts'), + 'accesskey' => 'l', ), array( 'label' => t('Unblocked'), 'url' => $a->get_baseurl(true) . '/contacts', 'sel' => ((! $all) && (! $blocked) && (! $hidden) && (! $search) && (! $nets) && (! $ignored) && (! $archived)) ? 'active' : '', 'title' => t('Only show unblocked contacts'), + 'accesskey' => 'o', ), array( @@ -600,6 +709,7 @@ function contacts_content(&$a) { 'url' => $a->get_baseurl(true) . '/contacts/blocked', 'sel' => ($blocked) ? 'active' : '', 'title' => t('Only show blocked contacts'), + 'accesskey' => 'b', ), array( @@ -607,6 +717,7 @@ function contacts_content(&$a) { 'url' => $a->get_baseurl(true) . '/contacts/ignored', 'sel' => ($ignored) ? 'active' : '', 'title' => t('Only show ignored contacts'), + 'accesskey' => 'i', ), array( @@ -614,6 +725,7 @@ function contacts_content(&$a) { 'url' => $a->get_baseurl(true) . '/contacts/archived', 'sel' => ($archived) ? 'active' : '', 'title' => t('Only show archived contacts'), + 'accesskey' => 'y', ), array( @@ -621,6 +733,7 @@ function contacts_content(&$a) { 'url' => $a->get_baseurl(true) . '/contacts/hidden', 'sel' => ($hidden) ? 'active' : '', 'title' => t('Only show hidden contacts'), + 'accesskey' => 'h', ), ); @@ -662,54 +775,9 @@ function contacts_content(&$a) { $contacts = array(); if(count($r)) { - foreach($r as $rr) { - - switch($rr['rel']) { - case CONTACT_IS_FRIEND: - $dir_icon = 'images/lrarrow.gif'; - $alt_text = t('Mutual Friendship'); - break; - case CONTACT_IS_FOLLOWER; - $dir_icon = 'images/larrow.gif'; - $alt_text = t('is a fan of yours'); - break; - case CONTACT_IS_SHARING; - $dir_icon = 'images/rarrow.gif'; - $alt_text = t('you are a fan of'); - break; - default: - break; - } - if(($rr['network'] === 'dfrn') && ($rr['rel'])) { - $url = "redir/{$rr['id']}"; - $sparkle = ' class="sparkle" '; - } - else { - $url = $rr['url']; - $sparkle = ''; - } - - - $contacts[] = array( - 'img_hover' => sprintf( t('Visit %s\'s profile [%s]'),$rr['name'],$rr['url']), - 'edit_hover' => t('Edit contact'), - 'photo_menu' => contact_photo_menu($rr), - 'id' => $rr['id'], - 'alt_text' => $alt_text, - 'dir_icon' => $dir_icon, - 'thumb' => proxy_url($rr['thumb']), - 'name' => $rr['name'], - 'username' => $rr['name'], - 'sparkle' => $sparkle, - 'itemurl' => $rr['url'], - 'url' => $url, - 'network' => network_to_name($rr['network']), - ); + $contacts[] = _contact_detail_for_template($rr); } - - - } $tpl = get_markup_template("contacts-template.tpl"); @@ -734,7 +802,52 @@ function contacts_content(&$a) { ), '$paginate' => paginate($a), - )); - + )); + return $o; } + +function _contact_detail_for_template($rr){ + switch($rr['rel']) { + case CONTACT_IS_FRIEND: + $dir_icon = 'images/lrarrow.gif'; + $alt_text = t('Mutual Friendship'); + break; + case CONTACT_IS_FOLLOWER; + $dir_icon = 'images/larrow.gif'; + $alt_text = t('is a fan of yours'); + break; + case CONTACT_IS_SHARING; + $dir_icon = 'images/rarrow.gif'; + $alt_text = t('you are a fan of'); + break; + default: + break; + } + if(($rr['network'] === NETWORK_DFRN) && ($rr['rel'])) { + $url = "redir/{$rr['id']}"; + $sparkle = ' class="sparkle" '; + } + else { + $url = $rr['url']; + $sparkle = ''; + } + + + return array( + 'img_hover' => sprintf( t('Visit %s\'s profile [%s]'),$rr['name'],$rr['url']), + 'edit_hover' => t('Edit contact'), + 'photo_menu' => contact_photo_menu($rr), + 'id' => $rr['id'], + 'alt_text' => $alt_text, + 'dir_icon' => $dir_icon, + 'thumb' => proxy_url($rr['thumb']), + 'name' => $rr['name'], + 'username' => $rr['name'], + 'sparkle' => $sparkle, + 'itemurl' => $rr['url'], + 'url' => $url, + 'network' => network_to_name($rr['network'], $rr['url']), + ); + +} diff --git a/mod/content.php b/mod/content.php index 1e44bf160..cec23a914 100644 --- a/mod/content.php +++ b/mod/content.php @@ -131,7 +131,9 @@ function content_content(&$a, $update = 0) { } $sql_extra = " AND `item`.`parent` IN ( SELECT DISTINCT(`parent`) FROM `item` WHERE 1 $sql_options AND ( `contact-id` IN ( $contact_str ) OR `allow_gid` like '" . protect_sprintf('%<' . intval($group) . '>%') . "' ) and deleted = 0 ) "; - $o = '

' . t('Group: ') . $r[0]['name'] . '

' . $o; + $o = replace_macros(get_markup_template("section_title.tpl"),array( + '$title' => sprintf( t('Group: %s'), $r[0]['name']) + )) . $o; } elseif($cid) { @@ -423,7 +425,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) { $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => ''); call_hooks('render_location',$locate); - $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_google($locate)); + $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_dummy($locate)); localize_item($item); if($mode === 'network-new') @@ -797,7 +799,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) { $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => ''); call_hooks('render_location',$locate); - $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_google($locate)); + $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_dummy($locate)); $indent = (($toplevelpost) ? '' : ' comment'); diff --git a/mod/crepair.php b/mod/crepair.php index 400817d6f..457a06685 100644 --- a/mod/crepair.php +++ b/mod/crepair.php @@ -22,11 +22,12 @@ function crepair_init(&$a) { if($contact_id) { $a->data['contact'] = $r[0]; - $o .= '
'; - $o .= '
' . $a->data['contact']['name'] . '
'; - $o .= '
' . $a->data['contact']['name'] . '
'; - $o .= '
'; - $a->page['aside'] .= $o; + $tpl = get_markup_template("vcard-widget.tpl"); + $vcard_widget .= replace_macros($tpl, array( + '$name' => $a->data['contact']['name'], + '$photo' => $a->data['contact']['photo'] + )); + $a->page['aside'] .= $vcard_widget; } } @@ -160,8 +161,12 @@ function crepair_content(&$a) { else $remote_self_options = array('0'=>t('No mirroring'), '2'=>t('Mirror as my own posting')); + $update_profile = in_array($contact['network'], array(NETWORK_DFRN, NETWORK_DSPR, NETWORK_OSTATUS)); + $tpl = get_markup_template('crepair.tpl'); $o .= replace_macros($tpl, array( + '$update_profile' => update_profile, + '$udprofilenow' => t('Refetch contact data'), '$label_name' => t('Name'), '$label_nick' => t('Account Nickname'), '$label_attag' => t('@Tagname - overrides Name/Nickname'), diff --git a/mod/dfrn_confirm.php b/mod/dfrn_confirm.php index f1ce296d9..1cc258853 100644 --- a/mod/dfrn_confirm.php +++ b/mod/dfrn_confirm.php @@ -340,7 +340,7 @@ function dfrn_confirm_post(&$a,$handsfree = null) { `pending` = 0, `duplex` = %d, `hidden` = %d, - `network` = 'dfrn' WHERE `id` = %d + `network` = '%s' WHERE `id` = %d ", dbesc($photos[0]), dbesc($photos[1]), @@ -351,6 +351,7 @@ function dfrn_confirm_post(&$a,$handsfree = null) { dbesc(datetime_convert()), intval($duplex), intval($hidden), + dbesc(NETWORK_DFRN), intval($contact_id) ); } diff --git a/mod/dfrn_notify.php b/mod/dfrn_notify.php index 43414dc87..fcc572654 100644 --- a/mod/dfrn_notify.php +++ b/mod/dfrn_notify.php @@ -4,6 +4,7 @@ require_once('library/simplepie/simplepie.inc'); require_once('include/items.php'); require_once('include/event.php'); +require_once('library/defuse/php-encryption-1.2.1/Crypto.php'); function dfrn_notify_post(&$a) { logger(__function__, LOGGER_TRACE); @@ -12,6 +13,7 @@ function dfrn_notify_post(&$a) { $challenge = ((x($_POST,'challenge')) ? notags(trim($_POST['challenge'])) : ''); $data = ((x($_POST,'data')) ? $_POST['data'] : ''); $key = ((x($_POST,'key')) ? $_POST['key'] : ''); + $rino_remote = ((x($_POST,'rino')) ? intval($_POST['rino']) : 0); $dissolve = ((x($_POST,'dissolve')) ? intval($_POST['dissolve']) : 0); $perm = ((x($_POST,'perm')) ? notags(trim($_POST['perm'])) : 'r'); $ssl_policy = ((x($_POST,'ssl_policy')) ? notags(trim($_POST['ssl_policy'])): 'none'); @@ -91,6 +93,8 @@ function dfrn_notify_post(&$a) { $importer = $r[0]; + logger("Remote rino version: ".$rino_remote." for ".$importer["url"], LOGGER_DEBUG); + if((($writable != (-1)) && ($writable != $importer['writable'])) || ($importer['forum'] != $forum) || ($importer['prv'] != $prv)) { q("UPDATE `contact` SET `writable` = %d, forum = %d, prv = %d WHERE `id` = %d", intval(($writable == (-1)) ? $importer['writable'] : $writable), @@ -126,12 +130,27 @@ function dfrn_notify_post(&$a) { // If we are setup as a soapbox we aren't accepting input from this person + // This behaviour is deactivated since it really doesn't make sense to even disallow comments + // The check if someone is a friend or simply a follower is done in a later place so it needn't to be done here + //if($importer['page-flags'] == PAGE_SOAPBOX) + // xml_status(0); - if($importer['page-flags'] == PAGE_SOAPBOX) - xml_status(0); + $rino = get_config('system','rino_encrypt'); + $rino = intval($rino); + // use RINO1 if mcrypt isn't installed and RINO2 was selected + if ($rino==2 and !function_exists('mcrypt_create_iv')) $rino=1; + logger("Local rino version: ". $rino, LOGGER_DEBUG); if(strlen($key)) { + + // if local rino is lower than remote rino, abort: should not happen! + // but only for $remote_rino > 1, because old code did't send rino version + if ($rino_remote_version > 1 && $rino < $rino_remote) { + logger("rino version '$rino_remote' is lower than supported '$rino'"); + xml_status(0,"rino version '$rino_remote' is lower than supported '$rino'"); + } + $rawkey = hex2bin(trim($key)); logger('rino: md5 raw key: ' . md5($rawkey)); $final_key = ''; @@ -153,12 +172,43 @@ function dfrn_notify_post(&$a) { } } - logger('rino: received key : ' . $final_key); - $data = aes_decrypt(hex2bin($data),$final_key); + #logger('rino: received key : ' . $final_key); + + switch($rino_remote) { + case 0: + case 1: + // we got a key. old code send only the key, without RINO version. + // we assume RINO 1 if key and no RINO version + $data = aes_decrypt(hex2bin($data),$final_key); + break; + case 2: + try { + $data = Crypto::decrypt(hex2bin($data),$final_key); + } catch (InvalidCiphertext $ex) { // VERY IMPORTANT + // Either: + // 1. The ciphertext was modified by the attacker, + // 2. The key is wrong, or + // 3. $ciphertext is not a valid ciphertext or was corrupted. + // Assume the worst. + logger('The ciphertext has been tampered with!'); + xml_status(0,'The ciphertext has been tampered with!'); + } catch (Ex\CryptoTestFailed $ex) { + logger('Cannot safely perform dencryption'); + xml_status(0,'CryptoTestFailed'); + } catch (Ex\CannotPerformOperation $ex) { + logger('Cannot safely perform decryption'); + xml_status(0,'Cannot safely perform decryption'); + } + break; + default: + logger("rino: invalid sent verision '$rino_remote'"); + xml_status(0); + } + + logger('rino: decrypted data: ' . $data, LOGGER_DATA); } - $ret = local_delivery($importer,$data); xml_status($ret); @@ -175,6 +225,7 @@ function dfrn_notify_content(&$a) { $dfrn_id = notags(trim($_GET['dfrn_id'])); $dfrn_version = (float) $_GET['dfrn_version']; + $rino_remote = ((x($_GET,'rino')) ? intval($_GET['rino']) : 0); $type = ""; $last_update = ""; @@ -201,7 +252,7 @@ function dfrn_notify_content(&$a) { dbesc($last_update) ); - logger('dfrn_notify: challenge=' . $hash, LOGGER_DEBUG ); + logger('dfrn_notify: challenge=' . $hash, LOGGER_DEBUG); $sql_extra = ''; switch($direction) { @@ -231,6 +282,8 @@ function dfrn_notify_content(&$a) { if(! count($r)) $status = 1; + logger("Remote rino version: ".$rino_remote." for ".$r[0]["url"], LOGGER_DEBUG); + $challenge = ''; $encrypted_id = ''; $id_str = $my_id . '.' . mt_rand(1000,9999); @@ -253,12 +306,17 @@ function dfrn_notify_content(&$a) { $challenge = bin2hex($challenge); $encrypted_id = bin2hex($encrypted_id); - $rino = ((function_exists('mcrypt_encrypt')) ? 1 : 0); - $rino_enable = get_config('system','rino_encrypt'); + $rino = get_config('system','rino_encrypt'); + $rino = intval($rino); + // use RINO1 if mcrypt isn't installed and RINO2 was selected + if ($rino==2 and !function_exists('mcrypt_create_iv')) $rino=1; + + logger("Local rino version: ". $rino, LOGGER_DEBUG); - if(! $rino_enable) - $rino = 0; + // if requested rino is lower than enabled local rino, lower local rino version + // if requested rino is higher than enabled local rino, reply with local rino + if ($rino_remote < $rino) $rino = $rino_remote; if((($r[0]['rel']) && ($r[0]['rel'] != CONTACT_IS_SHARING)) || ($r[0]['page-flags'] == PAGE_COMMUNITY)) { $perm = 'rw'; diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index aee8c0306..7e5b67b7c 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -664,6 +664,21 @@ function dfrn_request_content(&$a) { $dfrn_url = notags(trim(hex2bin($_GET['dfrn_url']))); $aes_allow = (((x($_GET,'aes_allow')) && ($_GET['aes_allow'] == 1)) ? 1 : 0); $confirm_key = (x($_GET,'confirm_key') ? $_GET['confirm_key'] : ""); + + // Checking fastlane for validity + if (x($_SESSION, "fastlane") AND (normalise_link($_SESSION["fastlane"]) == normalise_link($dfrn_url))) { + $_POST["dfrn_url"] = $dfrn_url; + $_POST["confirm_key"] = $confirm_key; + $_POST["localconfirm"] = 1; + $_POST["hidden-contact"] = 0; + $_POST["submit"] = t('Confirm'); + + dfrn_request_post($a); + + killme(); + return; // NOTREACHED + } + $tpl = get_markup_template("dfrn_req_confirm.tpl"); $o = replace_macros($tpl,array( '$dfrn_url' => $dfrn_url, @@ -821,7 +836,10 @@ function dfrn_request_content(&$a) { //$emailnet = (($mail_disabled) ? '' : t("Connect as an email follower \x28Coming soon\x29")); $emailnet = ""; - $invite_desc = t('If you are not yet a member of the free social web,
follow this link to find a public Friendica site and join us today.'); + $invite_desc = sprintf( + t('If you are not yet a member of the free social web, follow this link to find a public Friendica site and join us today.'), + get_server() + ); $o .= replace_macros($tpl,array( '$header' => t('Friend/Connection Request'), diff --git a/mod/directory.php b/mod/directory.php index 7fab53b68..fa3a89e45 100644 --- a/mod/directory.php +++ b/mod/directory.php @@ -26,10 +26,12 @@ function directory_post(&$a) { function directory_content(&$a) { + global $db; require_once("mod/proxy.php"); - if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) { + if((get_config('system','block_public')) && (! local_user()) && (! remote_user()) || + (get_config('system','block_local_dir')) && (! local_user()) && (! remote_user())) { notice( t('Public access denied.') . EOL); return; } @@ -45,7 +47,7 @@ function directory_content(&$a) { $tpl = get_markup_template('directory_header.tpl'); $globaldir = ''; - $gdirpath = dirname(get_config('system','directory_submit_url')); + $gdirpath = get_config('system','directory'); if(strlen($gdirpath)) { $globaldir = ''; @@ -63,24 +65,38 @@ function directory_content(&$a) { '$submit' => t('Find') )); - if($search) + 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) " : ""); + + $sql_extra = " AND ((`profile`.`name` LIKE '%$search%') OR + (`user`.`nickname` LIKE '%$search%') OR + (`pdesc` LIKE '%$search%') OR + (`locality` LIKE '%$search%') OR + (`region` LIKE '%$search%') OR + (`country-name` LIKE '%$search%') OR + (`gender` LIKE '%$search%') OR + (`marital` LIKE '%$search%') OR + (`sexual` LIKE '%$search%') OR + (`about` LIKE '%$search%') OR + (`romance` LIKE '%$search%') OR + (`work` LIKE '%$search%') OR + (`education` LIKE '%$search%') OR + (`pub_keywords` LIKE '%$search%') OR + (`prv_keywords` LIKE '%$search%'))"; + } $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 $sql_extra "); + $r = $db->q("SELECT COUNT(*) AS `total` FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` WHERE `is-default` = 1 $publish AND `user`.`blocked` = 0 $sql_extra "); if(count($r)) $a->set_pager_total($r[0]['total']); - $order = " ORDER BY `name` ASC "; + $order = " ORDER BY `name` ASC "; + $limit = intval($a->pager['start']).",".intval($a->pager['itemspage']); - $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 $sql_extra $order LIMIT %d , %d ", - intval($a->pager['start']), - intval($a->pager['itemspage']) - ); + $r = $db->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 $sql_extra $order LIMIT ".$limit); if(count($r)) { if(in_array('small', $a->argv)) diff --git a/mod/dirfind.php b/mod/dirfind.php index 5c5d0e933..b0d578a3c 100644 --- a/mod/dirfind.php +++ b/mod/dirfind.php @@ -1,9 +1,9 @@ page,'aside')) $a->page['aside'] = ''; @@ -14,29 +14,97 @@ function dirfind_init(&$a) { -function dirfind_content(&$a) { +function dirfind_content(&$a, $prefix = "") { - $search = notags(trim($_REQUEST['search'])); + $community = false; + + $local = get_config('system','poco_local_search'); + + $search = $prefix.notags(trim($_REQUEST['search'])); if(strpos($search,'@') === 0) $search = substr($search,1); - + + if(strpos($search,'!') === 0) { + $search = substr($search,1); + $community = true; + } + $o = ''; - $o .= '

' . t('People Search') . ' - ' . $search . '

'; - + $o .= replace_macros(get_markup_template("section_title.tpl"),array( + '$title' => sprintf( t('People Search - %s'), $search) + )); + if($search) { - $p = (($a->pager['page'] != 1) ? '&p=' . $a->pager['page'] : ''); - - if(strlen(get_config('system','directory_submit_url'))) - $x = fetch_url('http://dir.friendica.com/lsearch?f=' . $p . '&search=' . urlencode($search)); + if ($local) { -//TODO fallback local search if global dir not available. -// else -// $x = post_url($a->get_baseurl() . '/lsearch', $params); + if ($community) + $extra_sql = " AND `community`"; + else + $extra_sql = ""; - $j = json_decode($x); + $perpage = 80; + $startrec = (($a->pager['page']) * $perpage) - $perpage; + + $count = q("SELECT count(*) AS `total` FROM `gcontact` WHERE `network` IN ('%s', '%s', '%s') AND + (`url` REGEXP '%s' OR `name` REGEXP '%s' OR `location` REGEXP '%s' OR + `about` REGEXP '%s' OR `keywords` REGEXP '%s')".$extra_sql, + dbesc(NETWORK_DFRN), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DIASPORA), + dbesc(escape_tags($search)), dbesc(escape_tags($search)), dbesc(escape_tags($search)), + dbesc(escape_tags($search)), dbesc(escape_tags($search))); + + $results = q("SELECT `contact`.`id` AS `cid`, `gcontact`.`url`, `gcontact`.`name`, `gcontact`.`photo`, `gcontact`.`keywords` + FROM `gcontact` + LEFT JOIN `contact` ON `contact`.`nurl` = `gcontact`.`nurl` + AND `contact`.`uid` = %d AND NOT `contact`.`blocked` + AND NOT `contact`.`pending` AND `contact`.`rel` IN ('%s', '%s') + WHERE `gcontact`.`network` IN ('%s', '%s', '%s') AND + ((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`)) AND + (`gcontact`.`url` REGEXP '%s' OR `gcontact`.`name` REGEXP '%s' OR `gcontact`.`location` REGEXP '%s' OR + `gcontact`.`about` REGEXP '%s' OR `gcontact`.`keywords` REGEXP '%s') $extra_sql + GROUP BY `gcontact`.`nurl` + ORDER BY `gcontact`.`updated` DESC LIMIT %d, %d", + intval(local_user()), dbesc(CONTACT_IS_SHARING), dbesc(CONTACT_IS_FRIEND), + dbesc(NETWORK_DFRN), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DIASPORA), + dbesc(escape_tags($search)), dbesc(escape_tags($search)), dbesc(escape_tags($search)), + dbesc(escape_tags($search)), dbesc(escape_tags($search)), + intval($startrec), intval($perpage)); + $j = new stdClass(); + $j->total = $count[0]["total"]; + $j->items_page = $perpage; + $j->page = $a->pager['page']; + foreach ($results AS $result) { + if (poco_alternate_ostatus_url($result["url"])) + continue; + + if ($result["name"] == "") { + $urlparts = parse_url($result["url"]); + $result["name"] = end(explode("/", $urlparts["path"])); + } + + $objresult = new stdClass(); + $objresult->cid = $result["cid"]; + $objresult->name = $result["name"]; + $objresult->url = $result["url"]; + $objresult->photo = $result["photo"]; + $objresult->tags = $result["keywords"]; + + $j->results[] = $objresult; + } + + // Add found profiles from the global directory to the local directory + proc_run('php','include/discover_poco.php', "dirsearch", urlencode($search)); + } else { + + $p = (($a->pager['page'] != 1) ? '&p=' . $a->pager['page'] : ''); + + if(strlen(get_config('system','directory'))) + $x = fetch_url(get_server().'/lsearch?f=' . $p . '&search=' . urlencode($search)); + + $j = json_decode($x); + } if($j->total) { $a->set_pager_total($j->total); @@ -44,21 +112,34 @@ function dirfind_content(&$a) { } if(count($j->results)) { - + $tpl = get_markup_template('match.tpl'); foreach($j->results as $jj) { - + + // If We already know this contact then don't show the "connect" button + if ($jj->cid > 0) { + $connlnk = ""; + $conntxt = ""; + } else { + $connlnk = $a->get_baseurl().'/follow/?url='.(($jj->connect) ? $jj->connect : $jj->url); + $conntxt = t('Connect'); + } + + $jj->photo = str_replace("http:///photo/", get_server()."/photo/", $jj->photo); + $o .= replace_macros($tpl,array( '$url' => zrl($jj->url), '$name' => $jj->name, - '$photo' => $jj->photo, - '$tags' => $jj->tags + '$photo' => proxy_url($jj->photo), + '$tags' => $jj->tags, + '$conntxt' => $conntxt, + '$connlnk' => $connlnk, )); } } else { info( t('No matches') . EOL); - } + } } diff --git a/mod/display.php b/mod/display.php index f483977cd..be5dd7cae 100644 --- a/mod/display.php +++ b/mod/display.php @@ -18,7 +18,7 @@ function display_init(&$a) { if (local_user()) { $r = q("SELECT `id`, `parent`, `author-name`, `author-link`, `author-avatar`, `network`, `body`, `uid` FROM `item` WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0 - AND `guid` = '%s' AND `uid` = %d", $a->argv[1], local_user()); + AND `guid` = '%s' AND `uid` = %d", dbesc($a->argv[1]), local_user()); if (count($r)) { $nick = $a->user["nickname"]; $itemuid = local_user(); @@ -34,7 +34,7 @@ function display_init(&$a) { AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' AND `item`.`private` = 0 AND NOT `user`.`hidewall` - AND `item`.`guid` = '%s'", $a->argv[1]); + AND `item`.`guid` = '%s'", dbesc($a->argv[1])); // AND `item`.`private` = 0 AND `item`.`wall` = 1 if (count($r)) { $nick = $r[0]["nickname"]; @@ -50,7 +50,7 @@ function display_init(&$a) { AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' AND `item`.`private` = 0 AND `item`.`uid` = 0 - AND `item`.`guid` = '%s'", $a->argv[1]); + AND `item`.`guid` = '%s'", dbesc($a->argv[1])); // AND `item`.`private` = 0 AND `item`.`wall` = 1 } if (count($r)) { @@ -102,8 +102,17 @@ function display_fetchauthor($a, $item) { $profiledata["network"] = $item["network"]; // Fetching further contact data from the contact table - $r = q("SELECT `photo`, `nick`, `location`, `about` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d", - normalise_link($profiledata["url"]), $item["uid"]); + $r = q("SELECT `photo`, `nick`, `location`, `about` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d AND `network` = '%s'", + dbesc(normalise_link($profiledata["url"])), intval($item["uid"]), dbesc($item["network"])); + + if (!count($r)) + $r = q("SELECT `photo`, `nick`, `location`, `about` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d", + dbesc(normalise_link($profiledata["url"])), intval($item["uid"])); + + if (!count($r)) + $r = q("SELECT `photo`, `nick`, `location`, `about` FROM `contact` WHERE `nurl` = '%s' AND `uid` = 0", + dbesc(normalise_link($profiledata["url"]))); + if (count($r)) { $profiledata["photo"] = proxy_url($r[0]["photo"]); $profiledata["address"] = proxy_parse_html(bbcode($r[0]["location"])); @@ -113,7 +122,7 @@ function display_fetchauthor($a, $item) { } // Fetching profile data from unique contacts - $r = q("SELECT `avatar`, `nick`, `location`, `about` FROM `unique_contacts` WHERE `url` = '%s'", normalise_link($profiledata["url"])); + $r = q("SELECT `avatar`, `nick`, `location`, `about` FROM `unique_contacts` WHERE `url` = '%s'", dbesc(normalise_link($profiledata["url"]))); if (count($r)) { if ($profiledata["photo"] == "") $profiledata["photo"] = proxy_url($r[0]["avatar"]); @@ -181,7 +190,7 @@ function display_fetchauthor($a, $item) { // Fetching profile data from unique contacts if ($profiledata["url"] != "") { - $r = q("SELECT `avatar`, `nick`, `location`, `about` FROM `unique_contacts` WHERE `url` = '%s'", normalise_link($profiledata["url"])); + $r = q("SELECT `avatar`, `nick`, `location`, `about` FROM `unique_contacts` WHERE `url` = '%s'", dbesc(normalise_link($profiledata["url"]))); if (count($r)) { $profiledata["photo"] = proxy_url($r[0]["avatar"]); $profiledata["address"] = proxy_parse_html(bbcode($r[0]["location"])); @@ -193,11 +202,13 @@ function display_fetchauthor($a, $item) { } if (local_user()) { - if ($profiledata["network"] == NETWORK_DFRN) { - $connect = str_replace("/profile/", "/dfrn_request/", $profiledata["url"])."&addr=".bin2hex($a->get_baseurl()."/profile/".$a->user["nickname"]); - $profiledata["remoteconnect"] = $connect; - } elseif ($profiledata["network"] == NETWORK_DIASPORA) - $profiledata["remoteconnect"] = $a->get_baseurl()."/contacts?add=".GetProfileUsername($profiledata["url"], "", true); + if (in_array($profiledata["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS))) + $profiledata["remoteconnect"] = $a->get_baseurl()."/follow?url=".urlencode($profiledata["url"]); + //if ($profiledata["network"] == NETWORK_DFRN) { + // $connect = str_replace("/profile/", "/dfrn_request/", $profiledata["url"])."&addr=".bin2hex($a->get_baseurl()."/profile/".$a->user["nickname"]); + // $profiledata["remoteconnect"] = $connect; + //} elseif ($profiledata["network"] == NETWORK_DIASPORA) + // $profiledata["remoteconnect"] = $a->get_baseurl()."/contacts?add=".GetProfileUsername($profiledata["url"], "", true); } elseif ($profiledata["network"] == NETWORK_DFRN) { $connect = str_replace("/profile/", "/dfrn_request/", $profiledata["url"]); $profiledata["remoteconnect"] = $connect; @@ -244,7 +255,7 @@ function display_content(&$a, $update = 0) { if (local_user()) { $r = q("SELECT `id` FROM `item` WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0 - AND `guid` = '%s' AND `uid` = %d", $a->argv[1], local_user()); + AND `guid` = '%s' AND `uid` = %d", dbesc($a->argv[1]), local_user()); if (count($r)) { $item_id = $r[0]["id"]; $nick = $a->user["nickname"]; @@ -257,7 +268,7 @@ function display_content(&$a, $update = 0) { AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' AND `item`.`private` = 0 AND NOT `user`.`hidewall` - AND `item`.`guid` = '%s'", $a->argv[1]); + AND `item`.`guid` = '%s'", dbesc($a->argv[1])); // AND `item`.`private` = 0 AND `item`.`wall` = 1 if (count($r)) { $item_id = $r[0]["id"]; @@ -270,7 +281,7 @@ function display_content(&$a, $update = 0) { AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' AND `item`.`private` = 0 AND `item`.`uid` = 0 - AND `item`.`guid` = '%s'", $a->argv[1]); + AND `item`.`guid` = '%s'", dbesc($a->argv[1])); // AND `item`.`private` = 0 AND `item`.`wall` = 1 if (count($r)) { $item_id = $r[0]["id"]; @@ -334,15 +345,13 @@ function display_content(&$a, $update = 0) { } if ($is_owner) { - $celeb = ((($a->user['page-flags'] == PAGE_SOAPBOX) || ($a->user['page-flags'] == PAGE_COMMUNITY)) ? true : false); - $x = array( 'is_owner' => true, 'allow_location' => $a->user['allow_location'], 'default_location' => $a->user['default-location'], 'nickname' => $a->user['nickname'], 'lockstate' => ( (is_array($a->user)) && ((strlen($a->user['allow_cid'])) || (strlen($a->user['allow_gid'])) || (strlen($a->user['deny_cid'])) || (strlen($a->user['deny_gid']))) ? 'lock' : 'unlock'), - 'acl' => populate_acl($a->user, $celeb), + 'acl' => populate_acl($a->user, true), 'bang' => '', 'visitor' => 'block', 'profile_uid' => local_user(), @@ -403,7 +412,7 @@ function display_content(&$a, $update = 0) { $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, - `contact`.`network`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`, + `contact`.`network`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`, `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` FROM `item` INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 diff --git a/mod/editpost.php b/mod/editpost.php index 3b8c11281..b03934102 100644 --- a/mod/editpost.php +++ b/mod/editpost.php @@ -36,12 +36,14 @@ function editpost_content(&$a) { $plaintext = false; - $o .= '

' . t('Edit post') . '

'; + $o .= replace_macros(get_markup_template("section_title.tpl"),array( + '$title' => t('Edit post') + )); $tpl = get_markup_template('jot-header.tpl'); $a->page['htmlhead'] .= replace_macros($tpl, array( '$baseurl' => $a->get_baseurl(), - '$editselect' => (($plaintext) ? 'none' : '/(profile-jot-text|prvmail-text)/'), + '$editselect' => (($plaintext) ? 'none' : '/(profile-jot-text|prvmail-text)/'), '$ispublic' => ' ', // t('Visible to everybody'), '$geotag' => $geotag, '$nickname' => $a->user['nickname'] @@ -64,8 +66,6 @@ function editpost_content(&$a) { else $lockstate = 'unlock'; - $celeb = ((($a->user['page-flags'] == PAGE_SOAPBOX) || ($a->user['page-flags'] == PAGE_COMMUNITY)) ? true : false); - $jotplugins = ''; $jotnets = ''; @@ -139,7 +139,7 @@ function editpost_content(&$a) { '$placeholdercategory' => (feature_enabled(local_user(),'categories') ? t('Categories (comma-separated list)') : ''), '$emtitle' => t('Example: bob@example.com, mary@example.com'), '$lockstate' => $lockstate, - '$acl' => '', // populate_acl((($group) ? $group_acl : $a->user), $celeb), + '$acl' => '', // populate_acl((($group) ? $group_acl : $a->user)), '$bang' => (($group) ? '!' : ''), '$profile_uid' => $_SESSION['uid'], '$preview' => t('Preview'), diff --git a/mod/events.php b/mod/events.php old mode 100755 new mode 100644 index 94e2ba6b4..242f27f13 --- a/mod/events.php +++ b/mod/events.php @@ -7,23 +7,17 @@ require_once('include/items.php'); function events_post(&$a) { + logger('post: ' . print_r($_REQUEST,true)); + if(! local_user()) return; $event_id = ((x($_POST,'event_id')) ? intval($_POST['event_id']) : 0); $cid = ((x($_POST,'cid')) ? intval($_POST['cid']) : 0); $uid = local_user(); - $startyear = intval($_POST['startyear']); - $startmonth = intval($_POST['startmonth']); - $startday = intval($_POST['startday']); - $starthour = intval($_POST['starthour']); - $startminute = intval($_POST['startminute']); - $finishyear = intval($_POST['finishyear']); - $finishmonth = intval($_POST['finishmonth']); - $finishday = intval($_POST['finishday']); - $finishhour = intval($_POST['finishhour']); - $finishminute = intval($_POST['finishminute']); + $start_text = escape_tags($_REQUEST['start_text']); + $finish_text = escape_tags($_REQUEST['finish_text']); $adjust = intval($_POST['adjust']); $nofinish = intval($_POST['nofinish']); @@ -31,12 +25,23 @@ function events_post(&$a) { // The default setting for the `private` field in event_store() is false, so mirror that $private_event = false; + if($start_text) { + $start = $start_text; + } + else { + $start = sprintf('%d-%d-%d %d:%d:0',$startyear,$startmonth,$startday,$starthour,$startminute); + } - $start = sprintf('%d-%d-%d %d:%d:0',$startyear,$startmonth,$startday,$starthour,$startminute); - if($nofinish) + if($nofinish) { $finish = '0000-00-00 00:00:00'; - else + } + + if($finish_text) { + $finish = $finish_text; + } + else { $finish = sprintf('%d-%d-%d %d:%d:0',$finishyear,$finishmonth,$finishday,$finishhour,$finishminute); + } if($adjust) { $start = datetime_convert(date_default_timezone_get(),'UTC',$start); @@ -51,20 +56,33 @@ function events_post(&$a) { // Don't allow the event to finish before it begins. // It won't hurt anything, but somebody will file a bug report - // and we'll waste a bunch of time responding to it. Time that - // could've been spent doing something else. - - if(strcmp($finish,$start) < 0) - $finish = $start; + // and we'll waste a bunch of time responding to it. Time that + // could've been spent doing something else. $summary = escape_tags(trim($_POST['summary'])); $desc = escape_tags(trim($_POST['desc'])); $location = escape_tags(trim($_POST['location'])); $type = 'event'; + $action = ($event_id == '') ? 'new' : "event/" . $event_id; + $onerror_url = $a->get_baseurl() . "/events/" . $action . "?summary=$summary&description=$desc&location=$location&start=$start_text&finish=$finish_text&adjust=$adjust&nofinish=$nofinish"; + + if(strcmp($finish,$start) < 0 && !$nofinish) { + notice( t('Event can not end before it has started.') . EOL); + if(intval($_REQUEST['preview'])) { + echo( t('Event can not end before it has started.')); + killme(); + } + goaway($onerror_url); + } + if((! $summary) || (! $start)) { notice( t('Event title and start time are required.') . EOL); - goaway($a->get_baseurl() . '/events/new'); + if(intval($_REQUEST['preview'])) { + echo( t('Event title and start time are required.')); + killme(); + } + goaway($onerror_url); } $share = ((intval($_POST['share'])) ? intval($_POST['share']) : 0); @@ -125,6 +143,12 @@ function events_post(&$a) { $datarray['created'] = $created; $datarray['edited'] = $edited; + if(intval($_REQUEST['preview'])) { + $html = format_event_html($datarray); + echo $html; + killme(); + } + $item_id = event_store($datarray); if(! $cid) @@ -174,7 +198,7 @@ function events_content(&$a) { $o =""; // tabs - $tabs = profile_tabs($a, True); + $tabs = profile_tabs($a, True); @@ -200,10 +224,10 @@ function events_content(&$a) { } if($mode == 'view') { - - - $thisyear = datetime_convert('UTC',date_default_timezone_get(),'now','Y'); - $thismonth = datetime_convert('UTC',date_default_timezone_get(),'now','m'); + + + $thisyear = datetime_convert('UTC',date_default_timezone_get(),'now','Y'); + $thismonth = datetime_convert('UTC',date_default_timezone_get(),'now','m'); if(! $y) $y = intval($thisyear); if(! $m) @@ -231,7 +255,7 @@ function events_content(&$a) { $prevmonth = 12; $prevyear --; } - + $dim = get_dim($y,$m); $start = sprintf('%d-%d-%d %d:%d:%d',$y,$m,1,0,0,0); $finish = sprintf('%d-%d-%d %d:%d:%d',$y,$m,$dim,23,59,59); @@ -241,7 +265,7 @@ function events_content(&$a) { if (x($_GET,'start')) $start = date("Y-m-d h:i:s", $_GET['start']); if (x($_GET,'end')) $finish = date("Y-m-d h:i:s", $_GET['end']); } - + $start = datetime_convert('UTC','UTC',$start); $finish = datetime_convert('UTC','UTC',$finish); @@ -251,17 +275,19 @@ function events_content(&$a) { if (x($_GET,'id')){ $r = q("SELECT `event`.*, `item`.`id` AS `itemid`,`item`.`plink`, - `item`.`author-name`, `item`.`author-avatar`, `item`.`author-link` FROM `event` LEFT JOIN `item` ON `item`.`event-id` = `event`.`id` + `item`.`author-name`, `item`.`author-avatar`, `item`.`author-link` FROM `event` + LEFT JOIN `item` ON `item`.`event-id` = `event`.`id` AND `item`.`uid` = `event`.`uid` WHERE `event`.`uid` = %d AND `event`.`id` = %d", intval(local_user()), intval($_GET['id']) ); } else { $r = q("SELECT `event`.*, `item`.`id` AS `itemid`,`item`.`plink`, - `item`.`author-name`, `item`.`author-avatar`, `item`.`author-link` FROM `event` LEFT JOIN `item` ON `item`.`event-id` = `event`.`id` + `item`.`author-name`, `item`.`author-avatar`, `item`.`author-link` FROM `event` + LEFT JOIN `item` ON `item`.`event-id` = `event`.`id` AND `item`.`uid` = `event`.`uid` WHERE `event`.`uid` = %d and event.ignore = %d - AND (( `adjust` = 0 AND ( `finish` >= '%s' OR ( nofinish AND start >= '%s' ) ) AND `start` <= '%s' ) - OR ( `adjust` = 1 AND ( `finish` >= '%s' OR ( nofinish AND start >= '%s' ) ) AND `start` <= '%s' )) ", + AND ((`adjust` = 0 AND (`finish` >= '%s' OR (nofinish AND start >= '%s')) AND `start` <= '%s') + OR (`adjust` = 1 AND (`finish` >= '%s' OR (nofinish AND start >= '%s')) AND `start` <= '%s')) ", intval(local_user()), intval($ignored), dbesc($start), @@ -279,7 +305,7 @@ function events_content(&$a) { $r = sort_by_date($r); foreach($r as $rr) { $j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['start'], 'j') : datetime_convert('UTC','UTC',$rr['start'],'j')); - if(! x($links,$j)) + if(! x($links,$j)) $links[$j] = $a->get_baseurl() . '/' . $a->cmd . '#link-' . $j; } } @@ -293,22 +319,22 @@ function events_content(&$a) { if(count($r)) { $r = sort_by_date($r); foreach($r as $rr) { - - + + $j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['start'], 'j') : datetime_convert('UTC','UTC',$rr['start'],'j')); $d = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['start'], $fmt) : datetime_convert('UTC','UTC',$rr['start'],$fmt)); $d = day_translate($d); - + $start = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['start'], 'c') : datetime_convert('UTC','UTC',$rr['start'],'c')); if ($rr['nofinish']){ $end = null; } else { $end = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['finish'], 'c') : datetime_convert('UTC','UTC',$rr['finish'],'c')); } - - + + $is_first = ($d !== $last_date); - + $last_date = $d; $edit = ((! $rr['cid']) ? array($a->get_baseurl().'/events/event/'.$rr['id'],t('Edit event'),'','') : null); $title = strip_tags(html_entity_decode(bbcode($rr['summary']),ENT_QUOTES,'UTF-8')); @@ -325,7 +351,7 @@ function events_content(&$a) { 'end' => $end, 'allDay' => false, 'title' => $title, - + 'j' => $j, 'd' => $d, 'edit' => $edit, @@ -338,11 +364,11 @@ function events_content(&$a) { } } - + if ($a->argv[1] === 'json'){ echo json_encode($events); killme(); } - + // links: array('href', 'text', 'extra css classes', 'title') if (x($_GET,'id')){ $tpl = get_markup_template("event.tpl"); @@ -393,6 +419,19 @@ function events_content(&$a) { $orig_event = $r[0]; } + // Passed parameters overrides anything found in the DB + if($mode === 'edit' || $mode === 'new') { + if(!x($orig_event)) $orig_event = array(); + // In case of an error the browser is redirected back here, with these parameters filled in with the previous values + if(x($_REQUEST,'nofinish')) $orig_event['nofinish'] = $_REQUEST['nofinish']; + if(x($_REQUEST,'adjust')) $orig_event['adjust'] = $_REQUEST['adjust']; + if(x($_REQUEST,'summary')) $orig_event['summary'] = $_REQUEST['summary']; + if(x($_REQUEST,'description')) $orig_event['description'] = $_REQUEST['description']; + if(x($_REQUEST,'location')) $orig_event['location'] = $_REQUEST['location']; + if(x($_REQUEST,'start')) $orig_event['start'] = $_REQUEST['start']; + if(x($_REQUEST,'finish')) $orig_event['finish'] = $_REQUEST['finish']; + } + if($mode === 'edit' || $mode === 'new') { $n_checked = ((x($orig_event) && $orig_event['nofinish']) ? ' checked="checked" ' : ''); @@ -414,9 +453,6 @@ function events_content(&$a) { $sh_checked .= ' disabled="disabled" '; - - $tpl = get_markup_template('event_form.tpl'); - $sdt = ((x($orig_event)) ? $orig_event['start'] : 'now'); $fdt = ((x($orig_event)) ? $orig_event['finish'] : 'now'); @@ -442,31 +478,27 @@ function events_content(&$a) { if(! $f) $f = 'ymd'; - $dateformat = datesel_format($f); - $timeformat = t('hour:minute'); - require_once('include/acl_selectors.php'); + $tpl = get_markup_template('event_form.tpl'); + $o .= replace_macros($tpl,array( '$post' => $a->get_baseurl() . '/events', - '$eid' => $eid, + '$eid' => $eid, '$cid' => $cid, '$uri' => $uri, - + '$title' => t('Event details'), - '$desc' => sprintf( t('Format is %s %s. Starting date and Title are required.'),$dateformat,$timeformat), - + '$desc' => t('Starting date and Title are required.'), '$s_text' => t('Event Starts:') . ' *', - '$s_dsel' => datesel($f,'start',$syear+5,$syear,false,$syear,$smonth,$sday), - '$s_tsel' => timesel('start',$shour,$sminute), + '$s_dsel' => datetimesel($f,new DateTime(),DateTime::createFromFormat('Y',$syear+5),DateTime::createFromFormat('Y-m-d H:i',"$syear-$smonth-$sday $shour:$sminute"),'start_text',true,true,'','',true), '$n_text' => t('Finish date/time is not known or not relevant'), '$n_checked' => $n_checked, '$f_text' => t('Event Finishes:'), - '$f_dsel' => datesel($f,'finish',$fyear+5,$fyear,false,$fyear,$fmonth,$fday), - '$f_tsel' => timesel('finish',$fhour,$fminute), + '$f_dsel' => datetimesel($f,new DateTime(),DateTime::createFromFormat('Y',$fyear+5),DateTime::createFromFormat('Y-m-d H:i',"$fyear-$fmonth-$fday $fhour:$fminute"),'finish_text',true,true,'start_text'), '$a_text' => t('Adjust for viewer timezone'), '$a_checked' => $a_checked, - '$d_text' => t('Description:'), + '$d_text' => t('Description:'), '$d_orig' => $d_orig, '$l_text' => t('Location:'), '$l_orig' => $l_orig, @@ -474,7 +506,8 @@ function events_content(&$a) { '$t_orig' => $t_orig, '$sh_text' => t('Share this event'), '$sh_checked' => $sh_checked, - '$acl' => (($cid) ? '' : populate_acl(((x($orig_event)) ? $orig_event : $a->user),false)), + '$preview' => t('Preview'), + '$acl' => (($cid) ? '' : populate_acl(((x($orig_event)) ? $orig_event : $a->user))), '$submit' => t('Submit') )); diff --git a/mod/fbrowser.php b/mod/fbrowser.php index 075846e50..e7f4b76d4 100644 --- a/mod/fbrowser.php +++ b/mod/fbrowser.php @@ -11,47 +11,54 @@ require_once('include/Photo.php'); * @param App $a */ function fbrowser_content($a){ - + if (!local_user()) killme(); if ($a->argc==1) killme(); - - //echo "
"; var_dump($a->argv); killme();	
-	
+
+	$template_file = "filebrowser.tpl";
+	$mode = "";
+	if (x($_GET,'mode')) {
+		$template_file = "filebrowser_plain.tpl";
+		$mode  = "?mode=".$_GET['mode'];
+	}
+
+	//echo "
"; var_dump($a->argv); killme();
+
 	switch($a->argv[1]){
 		case "image":
-			$path = array( array($a->get_baseurl()."/fbrowser/image/", t("Photos")));
+			$path = array( array("", t("Photos")));
 			$albums = false;
 			$sql_extra = "";
 			$sql_extra2 = " ORDER BY created DESC LIMIT 0, 10";
-			
+
 			if ($a->argc==2){
 				$albums = q("SELECT distinct(`album`) AS `album` FROM `photo` WHERE `uid` = %d ",
 					intval(local_user())
 				);
 				// anon functions only from 5.3.0... meglio tardi che mai..
-				function folder1($el){return array(bin2hex($el['album']),$el['album']);}	
-				$albums = array_map( "folder1" , $albums);
-				
+				$folder1 = function($el) use ($mode) {return array(bin2hex($el['album']),$el['album']);};
+				$albums = array_map( $folder1 , $albums);
+
 			}
-			
+
 			$album = "";
 			if ($a->argc==3){
 				$album = hex2bin($a->argv[2]);
 				$sql_extra = sprintf("AND `album` = '%s' ",dbesc($album));
 				$sql_extra2 = "";
-				$path[]=array($a->get_baseurl()."/fbrowser/image/".$a->argv[2]."/", $album);
+				$path[]=array($a->argv[2], $album);
 			}
-				
-			$r = q("SELECT `resource-id`, `id`, `filename`, type, min(`scale`) AS `hiq`,max(`scale`) AS `loq`, `desc`  
-					FROM `photo` WHERE `uid` = %d AND (height <= 320 AND width <= 320) $sql_extra
+
+			$r = q("SELECT `resource-id`, `id`, `filename`, type, min(`scale`) AS `hiq`,max(`scale`) AS `loq`, `desc`
+					FROM `photo` WHERE `uid` = %d  $sql_extra
 					GROUP BY `resource-id` $sql_extra2",
-				intval(local_user())					
+				intval(local_user())
 			);
-			
-			function files1($rr){ 
+
+			function files1($rr){
 				global $a;
 				$types = Photo::supportedTypes();
 				$ext = $types[$rr['type']];
@@ -63,33 +70,35 @@ function fbrowser_content($a){
 					$filename_e = $rr['filename'];
 				}
 
-				return array( 
-					$a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['hiq'] . '.' .$ext, 
-					$filename_e, 
+				return array(
+					$a->get_baseurl() . '/photo/' . $rr['resource-id'] . '.' .$ext,
+					$filename_e,
 					$a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['loq'] . '.'. $ext
 				);
 			}
 			$files = array_map("files1", $r);
-			
-			$tpl = get_markup_template("filebrowser.tpl");
-			echo replace_macros($tpl, array(
+
+			$tpl = get_markup_template($template_file);
+
+			$o =  replace_macros($tpl, array(
 				'$type' => 'image',
 				'$baseurl' => $a->get_baseurl(),
 				'$path' => $path,
 				'$folders' => $albums,
 				'$files' =>$files,
 				'$cancel' => t('Cancel'),
+				'$nickname' => $a->user['nickname'],
 			));
-				
-				
+
+
 			break;
 		case "file":
 			if ($a->argc==2){
 				$files = q("SELECT id, filename, filetype FROM `attach` WHERE `uid` = %d ",
 					intval(local_user())
 				);
-				
-				function files2($rr){ global $a; 
+
+				function files2($rr){ global $a;
 					list($m1,$m2) = explode("/",$rr['filetype']);
 					$filetype = ( (file_exists("images/icons/$m1.png"))?$m1:"zip");
 
@@ -100,28 +109,34 @@ function fbrowser_content($a){
 						$filename_e = $rr['filename'];
 					}
 
-					return array( $a->get_baseurl() . '/attach/' . $rr['id'], $filename_e, $a->get_baseurl() . '/images/icons/16/' . $filetype . '.png'); 
+					return array( $a->get_baseurl() . '/attach/' . $rr['id'], $filename_e, $a->get_baseurl() . '/images/icons/16/' . $filetype . '.png');
 				}
 				$files = array_map("files2", $files);
 				//echo "
"; var_dump($files); killme();
-			
-							
-				$tpl = get_markup_template("filebrowser.tpl");
-				echo replace_macros($tpl, array(
+
+
+				$tpl = get_markup_template($template_file);
+				$o = replace_macros($tpl, array(
 					'$type' => 'file',
 					'$baseurl' => $a->get_baseurl(),
-					'$path' => array( array($a->get_baseurl()."/fbrowser/image/", t("Files")) ),
+					'$path' => array( array( "", t("Files")) ),
 					'$folders' => false,
 					'$files' =>$files,
 					'$cancel' => t('Cancel'),
+					'$nickname' => $a->user['nickname'],
 				));
-				
+
 			}
-		
+
 			break;
 	}
-	
 
-	killme();
-	
+	if (x($_GET,'mode')) {
+		return $o;
+	} else {
+		echo $o;
+		killme();
+	}
+
+
 }
diff --git a/mod/follow.php b/mod/follow.php
old mode 100644
new mode 100755
index 5c2c764c7..bdcde7743
--- a/mod/follow.php
+++ b/mod/follow.php
@@ -2,8 +2,9 @@
 
 require_once('include/Scrape.php');
 require_once('include/follow.php');
+require_once('include/contact_selectors.php');
 
-function follow_init(&$a) {
+function follow_content(&$a) {
 
 	if(! local_user()) {
 		notice( t('Permission denied.') . EOL);
@@ -11,10 +12,100 @@ function follow_init(&$a) {
 		// NOTREACHED
 	}
 
+	$uid = local_user();
+	$url = notags(trim($_REQUEST['url']));
+
+	// There is a current issue. It seems as if you can't start following a Friendica that is following you
+	// With Diaspora this works - but Friendica is special, it seems ...
+	$r = q("SELECT `url` FROM `contact` WHERE `uid` = %d AND ((`rel` != %d) OR (`network` = '%s')) AND
+		(`nurl` = '%s' OR `alias` = '%s' OR `alias` = '%s') AND
+		`network` != '%s' LIMIT 1",
+		intval(local_user()), dbesc(CONTACT_IS_FOLLOWER), dbesc(NETWORK_DFRN), dbesc(normalise_link($url)),
+		dbesc(normalise_link($url)), dbesc($url), dbesc(NETWORK_STATUSNET));
+
+	if ($r) {
+		notice(t('You already added this contact.').EOL);
+		goaway($_SESSION['return_url']);
+		// NOTREACHED
+	}
+
+	$ret = probe_url($url);
+
+	if ($ret["network"] == NETWORK_MAIL)
+		$ret["url"] = $ret["addr"];
+
+	if($ret['network'] === NETWORK_DFRN) {
+		$request = $ret["request"];
+		$tpl = get_markup_template('dfrn_request.tpl');
+	} else {
+		$request = $a->get_baseurl()."/follow";
+		$tpl = get_markup_template('auto_request.tpl');
+	}
+
+	$r = q("SELECT `url` FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1", intval($uid));
+
+	if (!$r) {
+		notice( t('Permission denied.') . EOL);
+		goaway($_SESSION['return_url']);
+		// NOTREACHED
+	}
+
+	$myaddr = $r[0]["url"];
+
+	// Makes the connection request for friendica contacts easier
+	$_SESSION["fastlane"] = $ret["url"];
+
+	$header = $ret["name"];
+
+	if ($ret["addr"] != "")
+		$header .= " <".$ret["addr"].">";
+
+	$header .= " (".network_to_name($ret['network']).")";
+
+	$o  = replace_macros($tpl,array(
+			'$header' => htmlentities($header),
+			'$photo' => $ret["photo"],
+                        '$desc' => "",
+                        '$pls_answer' => t('Please answer the following:'),
+                        '$does_know_you' => array('knowyou', sprintf(t('Does %s know you?'),$ret["name"]), false, '', array(t('No'),t('Yes'))),
+                        '$add_note' => t('Add a personal note:'),
+                        '$page_desc' => "",
+                        '$friendica' => "",
+                        '$statusnet' => "",
+                        '$diaspora' => "",
+                        '$diasnote' => "",
+                        '$your_address' => t('Your Identity Address:'),
+                        '$invite_desc' => "",
+                        '$emailnet' => "",
+                        '$submit' => t('Submit Request'),
+                        '$cancel' => t('Cancel'),
+                        '$nickname' => "",
+                        '$name' => $ret["name"],
+                        '$url' => $ret["url"],
+                        '$myaddr' => $myaddr,
+			'$request' => $request
+	));
+	return $o;
+}
+
+function follow_post(&$a) {
+
+	if(! local_user()) {
+		notice( t('Permission denied.') . EOL);
+		goaway($_SESSION['return_url']);
+		// NOTREACHED
+	}
+
+	if ($_REQUEST['cancel'])
+		goaway($_SESSION['return_url']);
+
 	$uid = local_user();
 	$url = notags(trim($_REQUEST['url']));
 	$return_url = $_SESSION['return_url'];
 
+	// Makes the connection request for friendica contacts easier
+	// This is just a precaution if maybe this page is called somewhere directly via POST
+	$_SESSION["fastlane"] = $url;
 
 	$result = new_contact($uid,$url,true);
 
@@ -22,12 +113,13 @@ function follow_init(&$a) {
 		if($result['message'])
 			notice($result['message']);
 		goaway($return_url);
-	}
+	} elseif ($result['cid'])
+		goaway($a->get_baseurl().'/contacts/'.$result['cid']);
 
-	info( t('Contact added') . EOL);
+	info( t('Contact added').EOL);
 
 	if(strstr($return_url,'contacts'))
-		goaway($a->get_baseurl() . '/contacts/' . $contact_id);
+		goaway($a->get_baseurl().'/contacts/'.$contact_id);
 
 	goaway($return_url);
 	// NOTREACHED
diff --git a/mod/friendica.php b/mod/friendica.php
index 7b99f9e6f..9fa203f58 100644
--- a/mod/friendica.php
+++ b/mod/friendica.php
@@ -61,7 +61,7 @@ function friendica_content(&$a) {
 
 	$o .= t('Please visit Friendica.com to learn more about the Friendica project.') . '

'; - $o .= t('Bug reports and issues: please visit') . ' ' . 'the bucktracker at github

'; + $o .= t('Bug reports and issues: please visit') . ' ' . ''.t('the bugtracker at github').'

'; $o .= t('Suggestions, praise, donations, etc. - please email "Info" at Friendica - dot com') . '

'; $o .= '

'; diff --git a/mod/group.php b/mod/group.php index 906d649a6..4a447713d 100644 --- a/mod/group.php +++ b/mod/group.php @@ -172,8 +172,6 @@ function group_content(&$a) { '$form_security_token' => get_form_security_token("group_drop"), )); - $celeb = ((($a->user['page-flags'] == PAGE_SOAPBOX) || ($a->user['page-flags'] == PAGE_COMMUNITY)) ? true : false); - $context = $context + array( '$title' => t('Group Editor'), diff --git a/mod/install.php b/mod/install.php index c6fe70836..182495d25 100755 --- a/mod/install.php +++ b/mod/install.php @@ -73,7 +73,7 @@ function install_post(&$a) { // connect to db $db = new dba($dbhost, $dbuser, $dbpass, $dbdata, true); - $tpl = get_intltext_template('htconfig.tpl'); + $tpl = get_markup_template('htconfig.tpl'); $txt = replace_macros($tpl,array( '$dbhost' => $dbhost, '$dbuser' => $dbuser, @@ -84,13 +84,15 @@ function install_post(&$a) { '$phpath' => $phpath, '$adminmail' => $adminmail )); + $result = file_put_contents('.htconfig.php', $txt); if(! $result) { $a->data['txt'] = $txt; } - + $errors = load_database($db); + if($errors) $a->data['db_failed'] = $errors; @@ -148,7 +150,7 @@ function install_content(&$a) { return replace_macros($tpl, array( '$title' => $install_title, '$pass' => '', - '$status' => t('Permission denied.'), + '$status' => t('Database already in use.'), '$text' => '', )); } @@ -390,6 +392,7 @@ function check_funcs(&$checks) { check_add($ck_funcs, t('OpenSSL PHP module'), true, true, ""); check_add($ck_funcs, t('mysqli PHP module'), true, true, ""); check_add($ck_funcs, t('mb_string PHP module'), true, true, ""); + check_add($ck_funcs, t('mcrypt PHP module'), true, true, ""); if(function_exists('apache_get_modules')){ @@ -420,7 +423,13 @@ function check_funcs(&$checks) { $ck_funcs[4]['status']= false; $ck_funcs[4]['help']= t('Error: mb_string PHP module required but not installed.'); } + if(! function_exists('mcrypt_create_iv')){ + $ck_funcs[5]['status']= false; + $ck_funcs[5]['help']= t('Error: mcrypt PHP module required but not installed.'); + } + + $checks = array_merge($checks, $ck_funcs); /*if((x($_SESSION,'sysmsg')) && is_array($_SESSION['sysmsg']) && count($_SESSION['sysmsg'])) @@ -466,16 +475,19 @@ function check_htaccess(&$checks) { $status = true; $help = ""; if (function_exists('curl_init')){ - $test = fetch_url($a->get_baseurl()."/install/testrewrite"); - if ($test!="ok") { - $status = false; - $help = t('Url rewrite in .htaccess is not working. Check your server configuration.'); - } - check_add($checks, t('Url rewrite is working'), $status, true, $help); - } else { - // cannot check modrewrite if libcurl is not installed - } + $test = fetch_url($a->get_baseurl()."/install/testrewrite"); + if ($test!="ok") + $test = fetch_url(normalise_link($a->get_baseurl()."/install/testrewrite")); + + if ($test!="ok") { + $status = false; + $help = t('Url rewrite in .htaccess is not working. Check your server configuration.'); + } + check_add($checks, t('Url rewrite is working'), $status, true, $help); + } else { + // cannot check modrewrite if libcurl is not installed + } } diff --git a/mod/invite.php b/mod/invite.php index aea92a663..ccf876c7c 100644 --- a/mod/invite.php +++ b/mod/invite.php @@ -112,15 +112,15 @@ function invite_content(&$a) { notice( t('You have no more invitations available') . EOL); return ''; } - } + } - $dirloc = get_config('system','directory_submit_url'); + $dirloc = get_config('system','directory'); if(strlen($dirloc)) { if($a->config['register_policy'] == REGISTER_CLOSED) - $linktxt = sprintf( t('Visit %s for a list of public sites that you can join. Friendica members on other sites can all connect with each other, as well as with members of many other social networks.'), dirname($dirloc) . '/siteinfo'); + $linktxt = sprintf( t('Visit %s for a list of public sites that you can join. Friendica members on other sites can all connect with each other, as well as with members of many other social networks.'), $dirloc . '/siteinfo'); elseif($a->config['register_policy'] != REGISTER_CLOSED) $linktxt = sprintf( t('To accept this invitation, please visit and register at %s or any other public Friendica website.'), $a->get_baseurl()) - . "\r\n" . "\r\n" . sprintf( t('Friendica sites all inter-connect to create a huge privacy-enhanced social web that is owned and controlled by its members. They can also connect with many traditional social networks. See %s for a list of alternate Friendica sites you can join.'),dirname($dirloc) . '/siteinfo'); + . "\r\n" . "\r\n" . sprintf( t('Friendica sites all inter-connect to create a huge privacy-enhanced social web that is owned and controlled by its members. They can also connect with many traditional social networks. See %s for a list of alternate Friendica sites you can join.'),$dirloc . '/siteinfo'); } else { $o = t('Our apologies. This system is not currently configured to connect with other public sites or invite members.'); @@ -141,4 +141,4 @@ function invite_content(&$a) { )); return $o; -} \ No newline at end of file +} diff --git a/mod/item.php b/mod/item.php index ec6f81f6b..d203ce64d 100644 --- a/mod/item.php +++ b/mod/item.php @@ -24,6 +24,7 @@ require_once('include/files.php'); require_once('include/threads.php'); require_once('include/text.php'); require_once('include/items.php'); +require_once('include/Scrape.php'); function item_post(&$a) { @@ -128,27 +129,27 @@ function item_post(&$a) { intval($parent_item['contact-id']), intval($uid) ); - if(count($r)) { + if(count($r)) $parent_contact = $r[0]; - // If the contact id doesn't fit with the contact, then set the contact to null - $thrparent = q("SELECT `author-link`, `network` FROM `item` WHERE `uri` = '%s' LIMIT 1", dbesc($thr_parent)); - if (count($thrparent) AND ($thrparent[0]["network"] === NETWORK_OSTATUS) - AND (normalise_link($parent_contact["url"]) != normalise_link($thrparent[0]["author-link"]))) { - $parent_contact = null; + // If the contact id doesn't fit with the contact, then set the contact to null + $thrparent = q("SELECT `author-link`, `network` FROM `item` WHERE `uri` = '%s' LIMIT 1", dbesc($thr_parent)); + if (count($thrparent) AND ($thrparent[0]["network"] === NETWORK_OSTATUS) + AND (normalise_link($parent_contact["url"]) != normalise_link($thrparent[0]["author-link"]))) { + $parent_contact = null; - require_once("include/Scrape.php"); - $probed_contact = probe_url($thrparent[0]["author-link"]); - if ($probed_contact["network"] != NETWORK_FEED) { - $parent_contact = $probed_contact; - $parent_contact["nurl"] = normalise_link($probed_contact["url"]); - $parent_contact["thumb"] = $probed_contact["photo"]; - $parent_contact["micro"] = $probed_contact["photo"]; - } - logger('parent contact: '.print_r($parent_contact, true), LOGGER_DEBUG); - } else - logger('no contact found: '.print_r($thrparent, true), LOGGER_DEBUG); - } + require_once("include/Scrape.php"); + $probed_contact = probe_url($thrparent[0]["author-link"]); + if ($probed_contact["network"] != NETWORK_FEED) { + $parent_contact = $probed_contact; + $parent_contact["nurl"] = normalise_link($probed_contact["url"]); + $parent_contact["thumb"] = $probed_contact["photo"]; + $parent_contact["micro"] = $probed_contact["photo"]; + $parent_contact["addr"] = $probed_contact["addr"]; + } + logger('no contact found: '.print_r($thrparent, true), LOGGER_DEBUG); + } else + logger('parent contact: '.print_r($parent_contact, true), LOGGER_DEBUG); } } @@ -569,10 +570,30 @@ function item_post(&$a) { * and we are replying, and there isn't one already */ - if(($parent_contact) && ($parent_contact['network'] === NETWORK_OSTATUS) - && ($parent_contact['nick']) && (! in_array('@' . $parent_contact['nick'],$tags))) { - $body = '@' . $parent_contact['nick'] . ' ' . $body; - $tags[] = '@' . $parent_contact['nick']; + if($parent AND ($parent_contact['network'] === NETWORK_OSTATUS)) { + if ($parent_contact['id'] != "") + $contact = '@'.$parent_contact['nick'].'+'.$parent_contact['id']; + else + $contact = '@[url='.$parent_contact['url'].']'.$parent_contact['nick'].'[/url]'; + + if (!in_array($contact,$tags)) { + $body = $contact.' '.$body; + $tags[] = $contact; + } + + $toplevel_contact = ""; + $toplevel_parent = q("SELECT `contact`.* FROM `contact` + INNER JOIN `item` ON `item`.`contact-id` = `contact`.`id` AND `contact`.`url` = `item`.`author-link` + WHERE `item`.`id` = `item`.`parent` AND `item`.`parent` = %d", intval($parent)); + if ($toplevel_parent) + $toplevel_contact = '@'.$toplevel_parent[0]['nick'].'+'.$toplevel_parent[0]['id']; + else { + $toplevel_parent = q("SELECT `author-link`, `author-name` FROM `item` WHERE `id` = `parent` AND `parent` = %d", intval($parent)); + $toplevel_contact = '@[url='.$toplevel_parent[0]['author-link'].']'.$toplevel_parent[0]['author-name'].'[/url]'; + } + + if (!in_array($toplevel_contact,$tags)) + $tags[] = $toplevel_contact; } $tagged = array(); @@ -653,7 +674,7 @@ function item_post(&$a) { $notify_type = (($parent) ? 'comment-new' : 'wall-new' ); - $uri = (($message_id) ? $message_id : item_new_uri($a->get_hostname(),$profile_uid)); + $uri = (($message_id) ? $message_id : item_new_uri($a->get_hostname(),$profile_uid, $guid)); // Fallback so that we alway have a thr-parent if(!$thr_parent) @@ -841,6 +862,9 @@ function item_post(&$a) { // NOTREACHED } + // Store the guid and other relevant data + add_guid($datarray); + $post_id = $r[0]['id']; logger('mod_item: saved item ' . $post_id); @@ -1073,8 +1097,30 @@ function handle_tag($a, &$body, &$inform, &$str_tags, $profile_uid, $tag, $netwo //is it a person tag? if(strpos($tag,'@') === 0) { //is it already replaced? - if(strpos($tag,'[url=')) + if(strpos($tag,'[url=')) { + //append tag to str_tags + if(!stristr($str_tags,$tag)) { + if(strlen($str_tags)) + $str_tags .= ','; + $str_tags .= $tag; + } + + // Checking for the alias that is used for OStatus + $pattern = "/@\[url\=(.*?)\](.*?)\[\/url\]/ism"; + if (preg_match($pattern, $tag, $matches)) { + $data = probe_url($matches[1]); + if ($data["alias"] != "") { + $newtag = '@[url='.$data["alias"].']'.$data["name"].'[/url]'; + if(!stristr($str_tags,$newtag)) { + if(strlen($str_tags)) + $str_tags .= ','; + $str_tags .= $newtag; + } + } + } + return $replaced; + } $stat = false; //get the person's name $name = substr($tag,1); diff --git a/mod/match.php b/mod/match.php index 926df1dff..74f83a6cc 100644 --- a/mod/match.php +++ b/mod/match.php @@ -1,5 +1,7 @@ page['aside'] .= follow_widget(); + $a->page['aside'] .= findpeople_widget(); + $_SESSION['return_url'] = $a->get_baseurl() . '/' . $a->cmd; - $o .= '

' . t('Profile Match') . '

'; + $o .= replace_macros(get_markup_template("section_title.tpl"),array( + '$title' => t('Profile Match') + )); $r = q("SELECT `pub_keywords`, `prv_keywords` FROM `profile` WHERE `is-default` = 1 AND `uid` = %d LIMIT 1", intval(local_user()) ); if(! count($r)) - return; + return; if(! $r[0]['pub_keywords'] && (! $r[0]['prv_keywords'])) { notice( t('No keywords to match. Please add keywords to your default profile.') . EOL); return; @@ -24,14 +31,14 @@ function match_content(&$a) { $params = array(); $tags = trim($r[0]['pub_keywords'] . ' ' . $r[0]['prv_keywords']); - + if($tags) { $params['s'] = $tags; if($a->pager['page'] != 1) $params['p'] = $a->pager['page']; - - if(strlen(get_config('system','directory_submit_url'))) - $x = post_url('http://dir.friendica.com/msearch', $params); + + if(strlen(get_config('system','directory'))) + $x = post_url(get_server().'/msearch', $params); else $x = post_url($a->get_baseurl() . '/msearch', $params); @@ -45,25 +52,30 @@ function match_content(&$a) { if(count($j->results)) { - + $tpl = get_markup_template('match.tpl'); foreach($j->results as $jj) { - - $connlnk = $a->get_baseurl() . '/follow/?url=' . $jj->url; - $o .= replace_macros($tpl,array( - '$url' => zrl($jj->url), - '$name' => $jj->name, - '$photo' => $jj->photo, - '$inttxt' => ' ' . t('is interested in:'), - '$conntxt' => t('Connect'), - '$connlnk' => $connlnk, - '$tags' => $jj->tags - )); + $match_nurl = normalise_link($jj->url); + $match = q("SELECT `nurl` FROM `contact` WHERE `uid` = '%d' AND nurl='%s' LIMIT 1", + intval(local_user()), + dbesc($match_nurl)); + if (!count($match)) { + $jj->photo = str_replace("http:///photo/", get_server()."/photo/", $jj->photo); + $connlnk = $a->get_baseurl() . '/follow/?url=' . $jj->url; + $o .= replace_macros($tpl,array( + '$url' => zrl($jj->url), + '$name' => $jj->name, + '$photo' => proxy_url($jj->photo), + '$inttxt' => ' ' . t('is interested in:'), + '$conntxt' => t('Connect'), + '$connlnk' => $connlnk, + '$tags' => $jj->tags + )); + } } - } - else { + } else { info( t('No matches') . EOL); - } + } } diff --git a/mod/message.php b/mod/message.php index 178d99bba..1331ce7fb 100644 --- a/mod/message.php +++ b/mod/message.php @@ -9,8 +9,9 @@ function message_init(&$a) { 'label' => t('New Message'), 'url' => $a->get_baseurl(true) . '/message/new', 'sel'=> ($a->argv[1] == 'new'), + 'accesskey' => 'm', ); - + $tpl = get_markup_template('message_side.tpl'); $a->page['aside'] = replace_macros($tpl, array( '$tabs'=>$tabs, @@ -29,7 +30,7 @@ function message_init(&$a) { '$baseurl' => $a->get_baseurl(true), '$base' => $base )); - + } function message_post(&$a) { diff --git a/mod/network.php b/mod/network.php index 0f9b0d233..a92e0c691 100644 --- a/mod/network.php +++ b/mod/network.php @@ -45,7 +45,7 @@ function network_init(&$a) { else if($sel_groups !== false) { $net_baseurl .= '/' . $sel_groups; } - + if($remember_tab) { // redirect if current selected tab is '/network' and // last selected tab is _not_ '/network?f=&order=comment'. @@ -55,19 +55,19 @@ function network_init(&$a) { '', //all '', //postord '', //conv - '/new', //new + '/new', //new '', //starred '', //bookmarked '', //spam ); $tab_args = array( 'f=&order=comment', //all - 'f=&order=post', //postord - 'f=&conv=1', //conv - '', //new - 'f=&star=1', //starred - 'f=&bmark=1', //bookmarked - 'f=&spam=1', //spam + 'f=&order=post', //postord + 'f=&conv=1', //conv + '', //new + 'f=&star=1', //starred + 'f=&bmark=1', //bookmarked + 'f=&spam=1', //spam ); $k = array_search('active', $last_sel_tabs); @@ -91,17 +91,17 @@ function network_init(&$a) { else if($sel_nets!==false) { $net_args['nets'] = $sel_nets; } - + if($remember_tab || $remember_net || $remember_group) { $net_args = array_merge($query_array, $net_args); $net_queries = build_querystring($net_args); - + $redir_url = ($net_queries ? $net_baseurl."?".$net_queries : $net_baseurl); - + goaway($a->get_baseurl() . $redir_url); } } - + if(x($_GET['nets']) && $_GET['nets'] === 'all') unset($_GET['nets']); @@ -139,7 +139,9 @@ function network_init(&$a) { // search terms header if(x($_GET,'search')) { - $a->page['content'] .= '

' . t('Search Results For:') . ' ' . $search . '

'; + $a->page['content'] .= replace_macros(get_markup_template("section_title.tpl"),array( + '$title' => sprintf( t('Search Results For: %s'), $search) + )); } $a->page['aside'] .= (feature_enabled(local_user(),'groups') ? group_side('network/0','network',true,$group_id) : ''); @@ -179,11 +181,11 @@ function saved_searches($search) { if(count($r)) { foreach($r as $rr) { $saved[] = array( - 'id' => $rr['id'], - 'term' => $rr['term'], + 'id' => $rr['id'], + 'term' => $rr['term'], 'encodedterm' => urlencode($rr['term']), - 'delete' => t('Remove term'), - 'selected' => ($search==$rr['term']), + 'delete' => t('Remove term'), + 'selected' => ($search==$rr['term']), ); } } @@ -191,10 +193,10 @@ function saved_searches($search) { $tpl = get_markup_template("saved_searches_aside.tpl"); $o = replace_macros($tpl, array( - '$title' => t('Saved Searches'), - '$add' => t('add'), - '$searchbox' => search($search,'netsearch-box',$srchurl,true), - '$saved' => $saved, + '$title' => t('Saved Searches'), + '$add' => t('add'), + '$searchbox' => search($search,'netsearch-box',$srchurl,true), + '$saved' => $saved, )); return $o; @@ -357,12 +359,14 @@ function network_content(&$a, $update = 0) { 'url'=>$a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . '?f=&order=comment' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''), 'sel'=>$all_active, 'title'=> t('Sort by Comment Date'), + 'accesskey' => "e", ), array( 'label' => t('Posted Order'), 'url'=>$a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . '?f=&order=post' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''), 'sel'=>$postord_active, 'title' => t('Sort by Post Date'), + 'accesskey' => "t", ), ); @@ -372,6 +376,7 @@ function network_content(&$a, $update = 0) { 'url' => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&conv=1', 'sel' => $conv_active, 'title' => t('Posts that mention or involve you'), + 'accesskey' => "r", ); } @@ -381,6 +386,7 @@ function network_content(&$a, $update = 0) { 'url' => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ($len_naked_cmd ? '/' : '') . 'new' . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : ''), 'sel' => $new_active, 'title' => t('Activity Stream - by date'), + 'accesskey' => "w", ); } @@ -390,6 +396,7 @@ function network_content(&$a, $update = 0) { 'url'=>$a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&bmark=1', 'sel'=>$bookmarked_active, 'title'=> t('Interesting Links'), + 'accesskey' => "b", ); } @@ -399,6 +406,7 @@ function network_content(&$a, $update = 0) { 'url'=>$a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&star=1', 'sel'=>$starred_active, 'title' => t('Favourite Posts'), + 'accesskey' => "m", ); } @@ -452,11 +460,6 @@ function network_content(&$a, $update = 0) { } set_pconfig(local_user(), 'network.view', 'net.selected', ($nets ? $nets : 'all')); -/*if ($update) { -print_r($_GET); -die("ss"); -}*/ - if(! $update) { if($group) { if(($t = group_public_members($group)) && (! get_pconfig(local_user(),'system','nowarn_insecure'))) { @@ -469,20 +472,29 @@ die("ss"); nav_set_selected('network'); - $celeb = ((($a->user['page-flags'] == PAGE_SOAPBOX) || ($a->user['page-flags'] == PAGE_COMMUNITY)) ? true : false); + $content = ""; + + if ($cid) { + $contact = q("SELECT `nick` FROM `contact` WHERE `id` = %d AND `uid` = %d AND `forum`", intval($cid), intval(local_user())); + if ($contact) + $content = "@".$contact[0]["nick"]."+".$cid; + } $x = array( 'is_owner' => true, 'allow_location' => $a->user['allow_location'], 'default_location' => $a->user['default-location'], 'nickname' => $a->user['nickname'], - 'lockstate' => ((($group) || ($cid) || ($nets) || (is_array($a->user) && ((strlen($a->user['allow_cid'])) || (strlen($a->user['allow_gid'])) || (strlen($a->user['deny_cid'])) || (strlen($a->user['deny_gid']))))) ? 'lock' : 'unlock'), - 'default_perms' => get_acl_permissions($a->user), - 'acl' => populate_acl((($group || $cid || $nets) ? $def_acl : $a->user), $celeb), - 'bang' => (($group || $cid || $nets) ? '!' : ''), + 'lockstate'=> ((($group) || ($cid) || ($nets) || (is_array($a->user) && + ((strlen($a->user['allow_cid'])) || (strlen($a->user['allow_gid'])) || + (strlen($a->user['deny_cid'])) || (strlen($a->user['deny_gid']))))) ? 'lock' : 'unlock'), + 'default_perms' => get_acl_permissions($a->user), + 'acl' => populate_acl((($group || $cid || $nets) ? $def_acl : $a->user), true), + 'bang' => (($group || $cid || $nets) ? '!' : ''), 'visitor' => 'block', 'profile_uid' => local_user(), 'acl_data' => construct_acl_data($a, $a->user), // For non-Javascript ACL selector + 'content' => $content, ); $o .= status_editor($a,$x); @@ -533,25 +545,39 @@ die("ss"); $contact_str_self = ",".$self[0]["id"]; } else { - $contact_str = ' 0 '; - info( t('Group is empty')); + $contact_str = ' 0 '; + info( t('Group is empty')); } //$sql_post_table = " INNER JOIN (SELECT DISTINCT(`parent`) FROM `item` WHERE (`contact-id` IN ($contact_str) OR `allow_gid` like '".protect_sprintf('%<'.intval($group).'>%')."') and deleted = 0 ORDER BY `created` DESC) AS `temp1` ON $sql_table.$sql_parent = `temp1`.`parent` "; $sql_extra3 .= " AND `contact-id` IN ($contact_str$contact_str_self) "; - $sql_extra3 .= " AND EXISTS (SELECT id FROM `item` WHERE (`contact-id` IN ($contact_str) OR `allow_gid` like '".protect_sprintf('%<'.intval($group).'>%')."') and deleted = 0 AND parent = $sql_table.$sql_parent) "; - $o = '

' . t('Group: ') . $r[0]['name'] . '

' . $o; - } elseif($cid) { + $sql_extra3 .= " AND EXISTS (SELECT id FROM `item` WHERE (`contact-id` IN ($contact_str) + OR `allow_gid` like '".protect_sprintf('%<'.intval($group).'>%')."') and deleted = 0 + AND parent = $sql_table.$sql_parent) "; + + $o = replace_macros(get_markup_template("section_title.tpl"),array( + '$title' => sprintf( t('Group: %s'), $r[0]['name']) + )) . $o; + + } + elseif($cid) { $r = q("SELECT `id`,`name`,`network`,`writable`,`nurl` FROM `contact` WHERE `id` = %d AND `blocked` = 0 AND `pending` = 0 LIMIT 1", intval($cid) ); if(count($r)) { - $sql_post_table = " INNER JOIN (SELECT DISTINCT(`parent`) FROM `item` WHERE 1 $sql_options AND `contact-id` = ".intval($cid)." and deleted = 0 ORDER BY `item`.`received` DESC) AS `temp1` ON $sql_table.$sql_parent = `temp1`.`parent` "; + $sql_post_table = " INNER JOIN (SELECT DISTINCT(`parent`) FROM `item` + WHERE 1 $sql_options AND `contact-id` = ".intval($cid)." and deleted = 0 + ORDER BY `item`.`received` DESC) AS `temp1` + ON $sql_table.$sql_parent = `temp1`.`parent` "; $sql_extra = ""; - $o = '

' . t('Contact: ') . $r[0]['name'] . '

' . $o; + + $o = replace_macros(get_markup_template("section_title.tpl"),array( + '$title' => sprintf( t('Contact: %s'), $r[0]['name']) + )) . $o; + if($r[0]['network'] === NETWORK_OSTATUS && $r[0]['writable'] && (! get_pconfig(local_user(),'system','nowarn_insecure'))) { notice( t('Private messages to this person are at risk of public disclosure.') . EOL); } diff --git a/mod/nodeinfo.php b/mod/nodeinfo.php new file mode 100644 index 000000000..f013c9084 --- /dev/null +++ b/mod/nodeinfo.php @@ -0,0 +1,265 @@ + array(array("rel" => "http://nodeinfo.diaspora.software/ns/schema/1.0", + "href" => $a->get_baseurl()."/nodeinfo/1.0"))); + + header('Content-type: application/json; charset=utf-8'); + echo json_encode($nodeinfo, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + exit; +} + +function nodeinfo_init(&$a){ + if (!get_config("system", "nodeinfo")) { + http_status_exit(404); + killme(); + } + + if (($a->argc != 2) OR ($a->argv[1] != "1.0")) { + http_status_exit(404); + killme(); + } + + $smtp = (function_exists("imap_open") AND !get_config("system","imap_disabled") AND !get_config("system","dfrn_only")); + + $nodeinfo = array(); + $nodeinfo["version"] = "1.0"; + $nodeinfo["software"] = array("name" => "friendica", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION); + + $nodeinfo["protocols"] = array(); + $nodeinfo["protocols"]["inbound"] = array(); + $nodeinfo["protocols"]["outbound"] = array(); + + if (get_config("system","diaspora_enabled")) { + $nodeinfo["protocols"]["inbound"][] = "diaspora"; + $nodeinfo["protocols"]["outbound"][] = "diaspora"; + } + + $nodeinfo["protocols"]["inbound"][] = "friendica"; + $nodeinfo["protocols"]["outbound"][] = "friendica"; + + if (!get_config("system","ostatus_disabled")) { + $nodeinfo["protocols"]["inbound"][] = "gnusocial"; + $nodeinfo["protocols"]["outbound"][] = "gnusocial"; + } + + $nodeinfo["services"] = array(); + $nodeinfo["services"]["inbound"] = array(); + $nodeinfo["services"]["outbound"] = array(); + + $nodeinfo["openRegistrations"] = ($a->config['register_policy'] != 0); + + $nodeinfo["usage"] = array(); + $nodeinfo["usage"]["users"] = array("total" => (int)get_config("nodeinfo","total_users"), + "activeHalfyear" => (int)get_config("nodeinfo","active_users_halfyear"), + "activeMonth" => (int)get_config("nodeinfo","active_users_monthly")); + $nodeinfo["usage"]["localPosts"] = (int)get_config("nodeinfo","local_posts"); + $nodeinfo["usage"]["localComments"] = (int)get_config("nodeinfo","local_comments"); + + $nodeinfo["metadata"] = array("nodeName" => $a->config["sitename"]); + + if (nodeinfo_plugin_enabled("appnet")) + $nodeinfo["services"]["inbound"][] = "appnet"; + + if (nodeinfo_plugin_enabled("appnet") OR nodeinfo_plugin_enabled("buffer")) + $nodeinfo["services"]["outbound"][] = "appnet"; + + if (nodeinfo_plugin_enabled("blogger")) + $nodeinfo["services"]["outbound"][] = "blogger"; + + if (nodeinfo_plugin_enabled("dwpost")) + $nodeinfo["services"]["outbound"][] = "dreamwidth"; + + if (nodeinfo_plugin_enabled("fbpost") OR nodeinfo_plugin_enabled("buffer")) + $nodeinfo["services"]["outbound"][] = "facebook"; + + if (nodeinfo_plugin_enabled("statusnet")) { + $nodeinfo["services"]["inbound"][] = "gnusocial"; + $nodeinfo["services"]["outbound"][] = "gnusocial"; + } + + if (nodeinfo_plugin_enabled("gpluspost") OR nodeinfo_plugin_enabled("buffer")) + $nodeinfo["services"]["outbound"][] = "google"; + + if (nodeinfo_plugin_enabled("ijpost")) + $nodeinfo["services"]["outbound"][] = "insanejournal"; + + if (nodeinfo_plugin_enabled("libertree")) + $nodeinfo["services"]["outbound"][] = "libertree"; + + if (nodeinfo_plugin_enabled("buffer")) + $nodeinfo["services"]["outbound"][] = "linkedin"; + + if (nodeinfo_plugin_enabled("ljpost")) + $nodeinfo["services"]["outbound"][] = "livejournal"; + + if (nodeinfo_plugin_enabled("buffer")) + $nodeinfo["services"]["outbound"][] = "pinterest"; + + if (nodeinfo_plugin_enabled("posterous")) + $nodeinfo["services"]["outbound"][] = "posterous"; + + if (nodeinfo_plugin_enabled("pumpio")) { + $nodeinfo["services"]["inbound"][] = "pumpio"; + $nodeinfo["services"]["outbound"][] = "pumpio"; + } + + // redmatrix + + if ($smtp) + $nodeinfo["services"]["outbound"][] = "smtp"; + + if (nodeinfo_plugin_enabled("tumblr")) + $nodeinfo["services"]["outbound"][] = "tumblr"; + + if (nodeinfo_plugin_enabled("twitter") OR nodeinfo_plugin_enabled("buffer")) + $nodeinfo["services"]["outbound"][] = "twitter"; + + if (nodeinfo_plugin_enabled("wppost")) + $nodeinfo["services"]["outbound"][] = "wordpress"; + + $nodeinfo["metadata"]["protocols"] = $nodeinfo["protocols"]; + $nodeinfo["metadata"]["protocols"]["outbound"][] = "atom1.0"; + $nodeinfo["metadata"]["protocols"]["inbound"][] = "atom1.0"; + $nodeinfo["metadata"]["protocols"]["inbound"][] = "rss2.0"; + + $nodeinfo["metadata"]["services"] = $nodeinfo["services"]; + + if (nodeinfo_plugin_enabled("twitter")) + $nodeinfo["metadata"]["services"]["inbound"][] = "twitter"; + + header('Content-type: application/json; charset=utf-8'); + echo json_encode($nodeinfo, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + exit; +} + +function nodeinfo_plugin_enabled($plugin) { + $r = q("SELECT * FROM `addon` WHERE `installed` = 1 AND `name` = '%s'", $plugin); + return((bool)(count($r) > 0)); +} + +function nodeinfo_cron() { + + $a = get_app(); + + // If the plugin "statistics_json" is enabled then disable it and actrivate nodeinfo. + if (nodeinfo_plugin_enabled("statistics_json")) { + set_config("system", "nodeinfo", true); + + $plugin = "statistics_json"; + $plugins = get_config("system","addon"); + $plugins_arr = array(); + + if($plugins) { + $plugins_arr = explode(",",str_replace(" ", "",$plugins)); + + $idx = array_search($plugin, $plugins_arr); + if ($idx !== false){ + unset($plugins_arr[$idx]); + uninstall_plugin($plugin); + set_config("system","addon", implode(", ",$plugins_arr)); + } + } + } + + if (!get_config("system", "nodeinfo")) + return; + + $last = get_config('nodeinfo','last_calucation'); + + if($last) { + // Calculate every 24 hours + $next = $last + (24 * 60 * 60); + if($next > time()) { + logger("calculation intervall not reached"); + return; + } + } + logger("cron_start"); + + $users = q("SELECT profile.*, `user`.`login_date`, `lastitem`.`lastitem_date` + FROM (SELECT MAX(`item`.`changed`) as `lastitem_date`, `item`.`uid` + FROM `item` + WHERE `item`.`type` = 'wall' + GROUP BY `item`.`uid`) AS `lastitem` + RIGHT OUTER JOIN `user` ON `user`.`uid` = `lastitem`.`uid`, `contact`, `profile` + WHERE + `user`.`uid` = `contact`.`uid` AND `profile`.`uid` = `user`.`uid` + AND `profile`.`is-default` AND (`profile`.`publish` OR `profile`.`net-publish`) + AND `user`.`verified` AND `contact`.`self` + AND NOT `user`.`blocked` + AND NOT `user`.`account_removed` + AND NOT `user`.`account_expired`"); + + if (is_array($users)) { + $total_users = count($users); + $active_users_halfyear = 0; + $active_users_monthly = 0; + + $halfyear = time() - (180 * 24 * 60 * 60); + $month = time() - (30 * 24 * 60 * 60); + + foreach ($users AS $user) { + if ((strtotime($user['login_date']) > $halfyear) OR + (strtotime($user['lastitem_date']) > $halfyear)) + ++$active_users_halfyear; + + if ((strtotime($user['login_date']) > $month) OR + (strtotime($user['lastitem_date']) > $month)) + ++$active_users_monthly; + + } + set_config('nodeinfo','total_users', $total_users); + logger("total_users: ".$total_users, LOGGER_DEBUG); + + set_config('nodeinfo','active_users_halfyear', $active_users_halfyear); + set_config('nodeinfo','active_users_monthly', $active_users_monthly); + } + + //$posts = q("SELECT COUNT(*) AS local_posts FROM `item` WHERE `wall` AND `uid` != 0 AND `id` = `parent` AND left(body, 6) != '[share'"); + $posts = q("SELECT COUNT(*) AS `local_posts` FROM `item` + INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` + WHERE `contact`.`self` and `item`.`id` = `item`.`parent` AND left(body, 6) != '[share' AND `item`.`network` IN ('%s', '%s', '%s')", + dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_DFRN)); + + if (!is_array($posts)) + $local_posts = -1; + else + $local_posts = $posts[0]["local_posts"]; + + set_config('nodeinfo','local_posts', $local_posts); + + logger("local_posts: ".$local_posts, LOGGER_DEBUG); + + $posts = q("SELECT COUNT(*) AS `local_comments` FROM `item` + INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` + WHERE `contact`.`self` and `item`.`id` != `item`.`parent` AND `item`.`network` IN ('%s', '%s', '%s')", + dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_DFRN)); + + if (!is_array($posts)) + $local_comments = -1; + else + $local_comments = $posts[0]["local_comments"]; + + set_config('nodeinfo','local_comments', $local_comments); + + // Now trying to register + $url = "http://the-federation.info/register/".$a->get_hostname(); + logger('registering url: '.$url, LOGGER_DEBUG); + $ret = fetch_url($url); + logger('registering answer: '.$ret, LOGGER_DEBUG); + + logger("cron_end"); + set_config('nodeinfo','last_calucation', time()); +} + +?> diff --git a/mod/nogroup.php b/mod/nogroup.php index 24d99a59b..adbcfcb51 100644 --- a/mod/nogroup.php +++ b/mod/nogroup.php @@ -43,13 +43,13 @@ function nogroup_content(&$a) { 'id' => $rr['id'], 'alt_text' => $alt_text, 'dir_icon' => $dir_icon, - 'thumb' => $rr['thumb'], + 'thumb' => $rr['thumb'], 'name' => $rr['name'], 'username' => $rr['name'], 'sparkle' => $sparkle, 'itemurl' => $rr['url'], 'url' => $url, - 'network' => network_to_name($rr['network']), + 'network' => network_to_name($rr['network'], $url), ); } } @@ -59,8 +59,8 @@ function nogroup_content(&$a) { '$header' => t('Contacts who are not members of a group'), '$contacts' => $contacts, '$paginate' => paginate($a), - )); - + )); + return $o; } diff --git a/mod/notes.php b/mod/notes.php index 09dac72b0..b96092495 100644 --- a/mod/notes.php +++ b/mod/notes.php @@ -46,25 +46,21 @@ function notes_content(&$a,$update = false) { $commpage = false; $commvisitor = false; - $celeb = false; - - - $x = array( 'is_owner' => $is_owner, - 'allow_location' => (($a->user['allow_location']) ? true : false), - 'default_location' => $a->user['default-location'], - 'nickname' => $a->user['nickname'], - 'lockstate' => 'lock', - 'acl' => '', - 'bang' => '', - 'visitor' => 'block', - 'profile_uid' => local_user(), + 'allow_location' => (($a->user['allow_location']) ? true : false), + 'default_location' => $a->user['default-location'], + 'nickname' => $a->user['nickname'], + 'lockstate' => 'lock', + 'acl' => '', + 'bang' => '', + 'visitor' => 'block', + 'profile_uid' => local_user(), 'button' => t('Save'), 'acl_data' => '', - ); + ); - $o .= status_editor($a,$x,$a->contact['id']); + $o .= status_editor($a,$x,$a->contact['id']); } diff --git a/mod/notifications.php b/mod/notifications.php index 2ecc2621e..44f6dd567 100644 --- a/mod/notifications.php +++ b/mod/notifications.php @@ -1,4 +1,5 @@ t('System'), 'url'=>$a->get_baseurl(true) . '/notifications/system', 'sel'=> (($a->argv[1] == 'system') ? 'active' : ''), + 'accesskey' => 'y', ), array( 'label' => t('Network'), 'url'=>$a->get_baseurl(true) . '/notifications/network', 'sel'=> (($a->argv[1] == 'network') ? 'active' : ''), + 'accesskey' => 'w', ), array( 'label' => t('Personal'), 'url'=>$a->get_baseurl(true) . '/notifications/personal', 'sel'=> (($a->argv[1] == 'personal') ? 'active' : ''), + 'accesskey' => 'r', ), array( 'label' => t('Home'), 'url' => $a->get_baseurl(true) . '/notifications/home', 'sel'=> (($a->argv[1] == 'home') ? 'active' : ''), + 'accesskey' => 'h', ), array( 'label' => t('Introductions'), 'url' => $a->get_baseurl(true) . '/notifications/intros', 'sel'=> (($a->argv[1] == 'intros') ? 'active' : ''), + 'accesskey' => 'i', ), /*array( 'label' => t('Messages'), @@ -130,8 +136,13 @@ function notifications_content(&$a) { $a->set_pager_itemspage(20); } - $r = q("SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*, `fcontact`.`name` AS `fname`,`fcontact`.`url` AS `furl`,`fcontact`.`photo` AS `fphoto`,`fcontact`.`request` AS `frequest` - FROM `intro` LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id` LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id` + $r = q("SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*, `fcontact`.`name` AS `fname`,`fcontact`.`url` AS `furl`,`fcontact`.`photo` AS `fphoto`,`fcontact`.`request` AS `frequest`, + `gcontact`.`location` AS `glocation`, `gcontact`.`about` AS `gabout`, + `gcontact`.`keywords` AS `gkeywords`, `gcontact`.`gender` AS `ggender` + FROM `intro` + LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id` + LEFT JOIN `gcontact` ON `gcontact`.`nurl` = `contact`.`nurl` + LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id` WHERE `intro`.`uid` = %d $sql_extra AND `intro`.`blocked` = 0 ", intval($_SESSION['uid'])); @@ -203,8 +214,17 @@ function notifications_content(&$a) { '$uid' => $_SESSION['uid'], '$intro_id' => $rr['intro_id'], '$contact_id' => $rr['contact-id'], - '$photo' => ((x($rr,'photo')) ? $rr['photo'] : "images/person-175.jpg"), + '$photo' => ((x($rr,'photo')) ? proxy_url($rr['photo']) : "images/person-175.jpg"), '$fullname' => $rr['name'], + '$location_label' => t('Location:'), + '$location' => $rr['glocation'], + '$location_label' => t('Location:'), + '$about' => proxy_parse_html(bbcode($rr['gabout'], false, false)), + '$about_label' => t('About:'), + '$keywords' => $rr['gkeywords'], + '$keywords_label' => t('Tags:'), + '$gender' => $rr['ggender'], + '$gender_label' => t('Gender:'), '$hidden' => array('hidden', t('Hide this contact from others'), ($rr['hidden'] == 1), ''), '$activity' => array('activity', t('Post a new friend activity'), (intval(get_pconfig(local_user(),'system','post_newfriend')) ? '1' : 0), t('if applicable')), '$url' => zrl($rr['url']), diff --git a/mod/notify.php b/mod/notify.php index f9c5a09b2..02260514a 100644 --- a/mod/notify.php +++ b/mod/notify.php @@ -18,11 +18,8 @@ function notify_init(&$a) { intval(local_user()) ); - // Friendica-Client - $friendicamobile = ($_SERVER['HTTP_USER_AGENT'] == "Apache-HttpClient/UNAVAILABLE (java 1.4)"); - // The friendica client has problems with the GUID. this is some workaround - if ($friendicamobile) { + if ($a->is_friendica_app()) { require_once("include/items.php"); $urldata = parse_url($r[0]['link']); $guid = basename($urldata["path"]); diff --git a/mod/ostatus_subscribe.php b/mod/ostatus_subscribe.php new file mode 100644 index 000000000..c950e9700 --- /dev/null +++ b/mod/ostatus_subscribe.php @@ -0,0 +1,78 @@ +".t("Subsribing to OStatus contacts").""; + + $uid = local_user(); + + $a = get_app(); + + $counter = intval($_REQUEST['counter']); + + if (get_pconfig($uid, "ostatus", "legacy_friends") == "") { + + if ($_REQUEST["url"] == "") + return $o.t("No contact provided."); + + $contact = probe_url($_REQUEST["url"]); + + if (!$contact) + return $o.t("Couldn't fetch information for contact."); + + $api = $contact["baseurl"]."/api/"; + + // Fetching friends + $data = z_fetch_url($api."statuses/friends.json?screen_name=".$contact["nick"]); + + if (!$data["success"]) + return $o.t("Couldn't fetch friends for contact."); + + set_pconfig($uid, "ostatus", "legacy_friends", $data["body"]); + } + + $friends = json_decode(get_pconfig($uid, "ostatus", "legacy_friends")); + + $total = sizeof($friends); + + if ($counter >= $total) { + $a->page['htmlhead'] = ''; + del_pconfig($uid, "ostatus", "legacy_friends"); + del_pconfig($uid, "ostatus", "legacy_contact"); + $o .= t("Done"); + return $o; + } + + $friend = $friends[$counter++]; + + $url = $friend->statusnet_profile_url; + + $o .= "

".$counter."/".$total.": ".$url; + + $data = probe_url($url); + if ($data["network"] == NETWORK_OSTATUS) { + $result = new_contact($uid,$url,true); + if ($result["success"]) + $o .= " - ".t("success"); + else + $o .= " - ".t("failed"); + } else + $o .= " - ".t("ignored"); + + $o .= "

"; + + $o .= "

".t("Keep this window open until done.")."

"; + + $a->page['htmlhead'] = ''; + + return $o; +} diff --git a/mod/p.php b/mod/p.php new file mode 100644 index 000000000..5c4b61984 --- /dev/null +++ b/mod/p.php @@ -0,0 +1,81 @@ +argc != 2) { + header($_SERVER["SERVER_PROTOCOL"].' 510 '.t('Not Extended')); + killme(); + } + + $guid = $a->argv[1]; + + if (strtolower(substr($guid, -4)) != ".xml") { + header($_SERVER["SERVER_PROTOCOL"].' 404 '.t('Not Found')); + killme(); + } + + $guid = strtolower(substr($guid, 0, -4)); + + $item = q("SELECT `title`, `body`, `guid`, `contact-id`, `private`, `created`, `app` FROM `item` WHERE `uid` = 0 AND `guid` = '%s' AND `network` IN ('%s', '%s') LIMIT 1", + dbesc($guid), NETWORK_DFRN, NETWORK_DIASPORA); + if (!$item) { + header($_SERVER["SERVER_PROTOCOL"].' 404 '.t('Not Found')); + killme(); + } + + $post = array(); + + $reshared = diaspora_is_reshare($item[0]["body"]); + + if ($reshared) { + $nodename = "reshare"; + $post["root_diaspora_id"] = $reshared["root_handle"]; + $post["root_guid"] = $reshared["root_guid"]; + $post["guid"] = $item[0]["guid"]; + $post["diaspora_handle"] = diaspora_handle_from_contact($item[0]["contact-id"]); + $post["public"] = (!$item[0]["private"] ? 'true':'false'); + $post["created_at"] = datetime_convert('UTC','UTC',$item[0]["created"]); + } else { + + $body = bb2diaspora($item[0]["body"]); + + if(strlen($item[0]["title"])) + $body = "## ".html_entity_decode($item[0]["title"])."\n\n".$body; + + $nodename = "status_message"; + $post["raw_message"] = str_replace("&", "&", $body); + $post["guid"] = $item[0]["guid"]; + $post["diaspora_handle"] = diaspora_handle_from_contact($item[0]["contact-id"]); + $post["public"] = (!$item[0]["private"] ? 'true':'false'); + $post["created_at"] = datetime_convert('UTC','UTC',$item[0]["created"]); + $post["provider_display_name"] = $item[0]["app"]; + } + + $dom = new DOMDocument("1.0"); + $root = $dom->createElement("XML"); + $dom->appendChild($root); + $postelement = $dom->createElement("post"); + $root->appendChild($postelement); + $statuselement = $dom->createElement($nodename); + $postelement->appendChild($statuselement); + + foreach($post AS $index => $value) { + $postnode = $dom->createElement($index, $value); + $statuselement->appendChild($postnode); + } + + header("Content-Type: application/xml; charset=utf-8"); + $xml = $dom->saveXML(); + + // Diaspora doesn't send the XML header, so we remove them as well. + // So we avoid possible compatibility problems. + if (substr($xml, 0, 21) == '') + $xml = trim(substr($xml, 21)); + + echo $xml; + + killme(); +} diff --git a/mod/parse_url.php b/mod/parse_url.php index 7f4bd29a0..97eebb89a 100644 --- a/mod/parse_url.php +++ b/mod/parse_url.php @@ -50,6 +50,21 @@ function completeurl($url, $scheme) { return($complete); } +function parseurl_getsiteinfo_cached($url, $no_guessing = false, $do_oembed = true) { + + $data = Cache::get("parse_url:".$no_guessing.":".$do_oembed.":".$url); + if (!is_null($data)) { + $data = unserialize($data); + return $data; + } + + $data = parseurl_getsiteinfo($url, $no_guessing, $do_oembed); + + Cache::set("parse_url:".$no_guessing.":".$do_oembed.":".$url,serialize($data), CACHE_DAY); + + return $data; +} + function parseurl_getsiteinfo($url, $no_guessing = false, $do_oembed = true, $count = 1) { require_once("include/network.php"); @@ -97,15 +112,6 @@ function parseurl_getsiteinfo($url, $no_guessing = false, $do_oembed = true, $co return($siteinfo); } - if ($do_oembed) { - require_once("include/oembed.php"); - - $oembed_data = oembed_fetch_url($url); - - if ($oembed_data->type != "error") - $siteinfo["type"] = $oembed_data->type; - } - // if the file is too large then exit if ($curl_info["download_content_length"] > 1000000) return($siteinfo); @@ -114,6 +120,24 @@ function parseurl_getsiteinfo($url, $no_guessing = false, $do_oembed = true, $co if (($curl_info["content_type"] != "") AND !strstr(strtolower($curl_info["content_type"]),"html")) return($siteinfo); + if ($do_oembed) { + require_once("include/oembed.php"); + + $oembed_data = oembed_fetch_url($url); + + if ($oembed_data->type != "error") + $siteinfo["type"] = $oembed_data->type; + + if (($oembed_data->type == "link") AND ($siteinfo["type"] != "photo")) { + if (isset($oembed_data->title)) + $siteinfo["title"] = $oembed_data->title; + if (isset($oembed_data->description)) + $siteinfo["text"] = trim($oembed_data->description); + if (isset($oembed_data->thumbnail_url)) + $siteinfo["image"] = $oembed_data->thumbnail_url; + } + } + $stamp1 = microtime(true); // Now fetch the body as well @@ -283,15 +307,6 @@ function parseurl_getsiteinfo($url, $no_guessing = false, $do_oembed = true, $co } } - if (isset($oembed_data) AND ($oembed_data->type == "link") AND ($siteinfo["type"] != "photo")) { - if (isset($oembed_data->title) AND (trim($oembed_data->title) != "")) - $siteinfo["title"] = $oembed_data->title; - if (isset($oembed_data->description) AND (trim($oembed_data->description) != "")) - $siteinfo["text"] = trim($oembed_data->description); - if (isset($oembed_data->thumbnail_url) AND (trim($oembed_data->thumbnail_url) != "")) - $siteinfo["image"] = $oembed_data->thumbnail_url; - } - if ((@$siteinfo["image"] == "") AND !$no_guessing) { $list = $xpath->query("//img[@src]"); foreach ($list as $node) { diff --git a/mod/photos.php b/mod/photos.php index 003bcd915..3d3b92a46 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -29,50 +29,72 @@ function photos_init(&$a) { return; $a->data['user'] = $r[0]; + $a->profile_uid = $r[0]['uid']; - $o .= '
'; - $o .= '
' . $a->data['user']['username'] . '
'; - $o .= '
' . $a->data['user']['username'] . '
'; - $o .= '
'; + $profilephoto = $a->get_cached_avatar_image($a->get_baseurl() . '/photo/profile/' . $a->data['user']['uid'] . '.jpg'); + + $tpl = get_markup_template("vcard-widget.tpl"); + + $vcard_widget .= replace_macros($tpl, array( + '$name' => $a->data['user']['username'], + '$photo' => $profilephoto + )); $sql_extra = permissions_sql($a->data['user']['uid']); - $albums = q("SELECT distinct(`album`) AS `album` FROM `photo` WHERE `uid` = %d $sql_extra order by created desc", - intval($a->data['user']['uid']) + $albums = q("SELECT count(distinct `resource-id`) AS `total`, `album` FROM `photo` WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s' + $sql_extra group by album order by created desc", + intval($a->data['user']['uid']), + dbesc('Contact Photos'), + dbesc( t('Contact Photos')) ); - if(count($albums)) { + $albums_visible = ((intval($a->data['user']['hidewall']) && (! local_user()) && (! remote_user())) ? false : true); + + // add various encodings to the array so we can just loop through and pick them out in a template + $ret = array('success' => false); + + if($albums) { $a->data['albums'] = $albums; + if ($albums_visible) + $ret['success'] = true; - $albums_visible = ((intval($a->data['user']['hidewall']) && (! local_user()) && (! remote_user())) ? false : true); - - if($albums_visible) { - $o .= ''; } + $albums = $ret; + + if(local_user() && $a->data['user']['uid'] == local_user()) + $can_post = true; + + if($albums['success']) { + $photo_albums_widget = replace_macros(get_markup_template('photo_albums.tpl'),array( + '$nick' => $a->data['user']['nickname'], + '$title' => t('Photo Albums'), + 'recent' => t('Recent Photos'), + '$albums' => $albums['albums'], + '$baseurl' => z_root(), + '$upload' => array( t('Upload New Photos'), $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/upload'), + '$can_post' => $can_post + )); + } + + if(! x($a->page,'aside')) $a->page['aside'] = ''; - $a->page['aside'] .= $o; + $a->page['aside'] .= $vcard_widget; + $a->page['aside'] .= $photo_albums_widget; $tpl = get_markup_template("photos_head.tpl"); @@ -135,7 +157,7 @@ function photos_post(&$a) { killme(); } - $r = q("SELECT `contact`.*, `user`.`nickname` FROM `contact` LEFT JOIN `user` ON `user`.`uid` = `contact`.`uid` + $r = q("SELECT `contact`.*, `user`.`nickname` FROM `contact` LEFT JOIN `user` ON `user`.`uid` = `contact`.`uid` WHERE `user`.`uid` = %d AND `self` = 1 LIMIT 1", intval($page_owner_uid) ); @@ -146,7 +168,7 @@ function photos_post(&$a) { killme(); } - $owner_record = $r[0]; + $owner_record = $r[0]; if(($a->argc > 3) && ($a->argv[2] === 'album')) { @@ -356,7 +378,7 @@ function photos_post(&$a) { $albname = datetime_convert('UTC',date_default_timezone_get(),'now', 'Y'); - if((x($_POST,'rotate') !== false) && + if((x($_POST,'rotate') !== false) && ( (intval($_POST['rotate']) == 1) || (intval($_POST['rotate']) == 2) )) { logger('rotate'); @@ -467,8 +489,8 @@ function photos_post(&$a) { $arr['visible'] = $visibility; $arr['origin'] = 1; - $arr['body'] = '[url=' . $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $p[0]['resource-id'] . ']' - . '[img]' . $a->get_baseurl() . '/photo/' . $p[0]['resource-id'] . '-' . $p[0]['scale'] . '.'. $ext . '[/img]' + $arr['body'] = '[url=' . $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $p[0]['resource-id'] . ']' + . '[img]' . $a->get_baseurl() . '/photo/' . $p[0]['resource-id'] . '-' . $p[0]['scale'] . '.'. $ext . '[/img]' . '[/url]'; $item_id = item_store($arr); @@ -720,7 +742,7 @@ function photos_post(&$a) { * overwhelm the data stream with a hundred newly uploaded photos. * So we will make the first photo uploaded to this album in the last several hours * visible by default, the rest will become visible over time when and if - * they acquire comments, likes, dislikes, and/or tags + * they acquire comments, likes, dislikes, and/or tags * */ @@ -732,7 +754,7 @@ function photos_post(&$a) { $visible = 1; else $visible = 0; - + if(intval($_REQUEST['not_visible']) || $_REQUEST['not_visible'] === 'true') $visible = 0; @@ -764,7 +786,7 @@ function photos_post(&$a) { $maximagesize = get_config('system','maximagesize'); if(($maximagesize) && ($filesize > $maximagesize)) { - notice( t('Image exceeds size limit of ') . $maximagesize . EOL); + notice( sprintf(t('Image exceeds size limit of %s'), formatBytes($maximagesize)) . EOL); @unlink($src); $foo = 0; call_hooks('photo_post_end',$foo); @@ -798,7 +820,7 @@ function photos_post(&$a) { call_hooks('photo_post_end',$foo); killme(); } - + $ph = new Photo($imagedata, $type); @@ -846,7 +868,7 @@ function photos_post(&$a) { $ph->store($page_owner_uid, $visitor, $photo_hash, $filename, $album, 2, 0, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny); $smallest = 2; } - + $basename = basename($filename); $uri = item_new_uri($a->get_hostname(), $page_owner_uid); @@ -876,8 +898,8 @@ function photos_post(&$a) { $arr['visible'] = $visible; $arr['origin'] = 1; - $arr['body'] = '[url=' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $photo_hash . ']' - . '[img]' . $a->get_baseurl() . "/photo/{$photo_hash}-{$smallest}.".$ph->getExt() . '[/img]' + $arr['body'] = '[url=' . $a->get_baseurl() . '/photos/' . $owner_record['nickname'] . '/image/' . $photo_hash . ']' + . '[img]' . $a->get_baseurl() . "/photo/{$photo_hash}-{$smallest}.".$ph->getExt() . '[/img]' . '[/url]'; $item_id = item_store($arr); @@ -936,7 +958,7 @@ function photos_content(&$a) { $_SESSION['photo_return'] = $a->cmd; // - // Parse arguments + // Parse arguments // if($a->argc > 3) { @@ -1068,8 +1090,6 @@ function photos_content(&$a) { } } - $celeb = ((($a->user['page-flags'] == PAGE_SOAPBOX) || ($a->user['page-flags'] == PAGE_COMMUNITY)) ? true : false); - $uploader = ''; $ret = array('post_url' => $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'], @@ -1118,11 +1138,11 @@ function photos_content(&$a) { if($a->theme['template_engine'] === 'internal') { $albumselect_e = template_escape($albumselect); - $aclselect_e = (($visitor) ? '' : template_escape(populate_acl($a->user, $celeb))); + $aclselect_e = (($visitor) ? '' : template_escape(populate_acl($a->user))); } else { $albumselect_e = $albumselect; - $aclselect_e = (($visitor) ? '' : populate_acl($a->user, $celeb)); + $aclselect_e = (($visitor) ? '' : populate_acl($a->user)); } $o .= replace_macros($tpl,array( @@ -1153,14 +1173,14 @@ function photos_content(&$a) { )); - return $o; + return $o; } if($datatype === 'album') { $album = hex2bin($datum); - $r = q("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = %d AND `album` = '%s' + $r = q("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = %d AND `album` = '%s' AND `scale` <= 4 $sql_extra GROUP BY `resource-id`", intval($owner_uid), dbesc($album) @@ -1175,7 +1195,7 @@ function photos_content(&$a) { else $order = 'DESC'; - $r = q("SELECT `resource-id`, `id`, `filename`, type, max(`scale`) AS `scale`, `desc` FROM `photo` WHERE `uid` = %d AND `album` = '%s' + $r = q("SELECT `resource-id`, `id`, `filename`, type, max(`scale`) AS `scale`, `desc` FROM `photo` WHERE `uid` = %d AND `album` = '%s' AND `scale` <= 4 $sql_extra GROUP BY `resource-id` ORDER BY `created` $order LIMIT %d , %d", intval($owner_uid), dbesc($album), @@ -1239,7 +1259,7 @@ function photos_content(&$a) { $imgalt_e = $rr['filename']; $desc_e = $rr['desc']; } - + $photos[] = array( 'id' => $rr['id'], 'twist' => ' ' . $twist . rand(2,4), @@ -1278,7 +1298,7 @@ function photos_content(&$a) { //$o = ''; // fetch image, item containing image, then comments - $ph = q("SELECT * FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' + $ph = q("SELECT * FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' $sql_extra ORDER BY `scale` ASC ", intval($owner_uid), dbesc($datum) @@ -1354,8 +1374,8 @@ function photos_content(&$a) { ); // lock - $lock = ( ( ($ph[0]['uid'] == local_user()) && (strlen($ph[0]['allow_cid']) || strlen($ph[0]['allow_gid']) - || strlen($ph[0]['deny_cid']) || strlen($ph[0]['deny_gid'])) ) + $lock = ( ( ($ph[0]['uid'] == local_user()) && (strlen($ph[0]['allow_cid']) || strlen($ph[0]['allow_gid']) + || strlen($ph[0]['deny_cid']) || strlen($ph[0]['deny_gid'])) ) ? t('Private Message') : Null); @@ -1497,19 +1517,18 @@ function photos_content(&$a) { $edit = replace_macros($edit_tpl, array( '$id' => $ph[0]['id'], - '$rotatecw' => t('Rotate CW (right)'), - '$rotateccw' => t('Rotate CCW (left)'), - '$album' => $album_e, - '$newalbum' => t('New album name'), + '$album' => array('albname', t('New album name'), $album_e,''), + '$caption' => array('desc', t('Caption'), $caption_e, ''), + '$tags' => array('newtag', t('Add a Tag'), "", t('Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping')), + '$rotate_none' => array('rotate',t('Do not rotate'),0,'', true), + '$rotate_cw' => array('rotate',t('Rotate CW (right)'),1,''), + '$rotate_ccw' => array('rotate',t('Rotate CCW (left)'),2,''), + '$nickname' => $a->data['user']['nickname'], '$resource_id' => $ph[0]['resource-id'], - '$capt_label' => t('Caption'), - '$caption' => $caption_e, - '$tag_label' => t('Add a Tag'), - '$tags' => $link_item['tag'], '$permissions' => t('Permissions'), '$aclselect' => $aclselect_e, - '$help_tags' => t('Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping'), + '$item_id' => ((count($linked_items)) ? $link_item['id'] : 0), '$submit' => t('Submit'), '$delete' => t('Delete Photo'), @@ -1628,7 +1647,7 @@ function photos_content(&$a) { if(local_user() && ($item['contact-uid'] == local_user()) - && ($item['network'] == 'dfrn') && (! $item['self'] )) { + && ($item['network'] == NETWORK_DFRN) && (! $item['self'] )) { $profile_url = $redirect_url; $sparkle = ' sparkle'; } @@ -1644,8 +1663,8 @@ function photos_content(&$a) { $profile_link = $profile_url; - - + + $dropping = (($item['contact-id'] == $contact_id) || ($item['uid'] == local_user())); $drop = array( 'dropping' => $dropping, @@ -1753,7 +1772,7 @@ function photos_content(&$a) { // Default - show recent photos with upload link (if applicable) //$o = ''; - $r = q("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s' + $r = q("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s' $sql_extra GROUP BY `resource-id`", intval($a->data['user']['uid']), dbesc('Contact Photos'), @@ -1765,7 +1784,7 @@ function photos_content(&$a) { } $r = q("SELECT `resource-id`, `id`, `filename`, type, `album`, max(`scale`) AS `scale` FROM `photo` - WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s' + WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s' $sql_extra GROUP BY `resource-id` ORDER BY `created` DESC LIMIT %d , %d", intval($a->data['user']['uid']), dbesc('Contact Photos'), @@ -1785,7 +1804,7 @@ function photos_content(&$a) { else $twist = 'rotright'; $ext = $phototypes[$rr['type']]; - + if($a->theme['template_engine'] === 'internal') { $alt_e = template_escape($rr['filename']); $name_e = template_escape($rr['album']); diff --git a/mod/ping.php b/mod/ping.php index 405edd3c4..e87ed9855 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -1,6 +1,7 @@ 1'; killme(); } - $firehose = intval(get_pconfig(local_user(),'system','notify_full')); - - $t = q("select count(*) as total from notify where uid = %d and seen = 0", - intval(local_user()) - ); - if($t && intval($t[0]['total']) > 49) { - $z = q("select * from notify where uid = %d - and seen = 0 order by date desc limit 0, 50", - intval(local_user()) - ); - $sysnotify = $t[0]['total']; - } - else { - $z1 = q("select * from notify where uid = %d - and seen = 0 order by date desc limit 0, 50", - intval(local_user()) - ); - - $z2 = q("select * from notify where uid = %d - and seen = 1 order by date desc limit 0, %d", - intval(local_user()), - intval(50 - intval($t[0]['total'])) - ); - $z = array_merge($z1,$z2); - $sysnotify = 0; // we will update this in a moment - } - - + $notifs = ping_get_notifications(local_user()); + $sysnotify = 0; // we will update this in a moment $tags = array(); $comments = array(); @@ -128,13 +103,12 @@ function ping_init(&$a) { $myurl = $a->get_baseurl() . '/profile/' . $a->user['nickname'] ; - $mails = q("SELECT *, COUNT(*) AS `total` FROM `mail` + $mails = q("SELECT * FROM `mail` WHERE `uid` = %d AND `seen` = 0 AND `from-url` != '%s' ", intval(local_user()), dbesc($myurl) ); - if($mails) - $mail = $mails[0]['total']; + $mail = count($mails); if ($a->config['register_policy'] == REGISTER_APPROVE && is_site_admin()){ $regs = q("SELECT `contact`.`name`, `contact`.`url`, `contact`.`micro`, `register`.`created`, COUNT(*) as `total` FROM `contact` RIGHT JOIN `register` ON `register`.`uid`=`contact`.`uid` WHERE `contact`.`self`=1"); @@ -186,15 +160,40 @@ function ping_init(&$a) { } + /** + * return xml from notification array + * + * @param array $n Notification array: + * 'href' => notification link + * 'name' => subject name + * 'url' => subject url + * 'photo' => subject photo + * 'date' => notification date + * 'seen' => bool true/false + * 'message' => notification message. "{0}" will be replaced by subject name + **/ + function xmlize($n){ + $n['photo'] = proxy_url($n['photo']); - function xmlize($href, $name, $url, $photo, $date, $seen, $message){ - require_once("mod/proxy.php"); - $photo = proxy_url($photo); - $data = array('href' => &$href, 'name' => &$name, 'url'=>&$url, 'photo'=>&$photo, 'date'=>&$date, 'seen'=>&$seen, 'messsage'=>&$message); - call_hooks('ping_xmlize', $data); - $notsxml = '%s'; + $n['message'] = html_entity_decode($n['message'], ENT_COMPAT | ENT_HTML401, "UTF-8"); + $n['name'] = html_entity_decode($n['name'], ENT_COMPAT | ENT_HTML401, "UTF-8"); + + // Are the nofications calles from the regular process or via the friendica app? + $regularnotifications = (intval($_GET['uid']) AND intval($_GET['_'])); + + $a = get_app(); + + if ($a->is_friendica_app() OR !$regularnotifications) + $n['message'] = str_replace("{0}", $n['name'], $n['message']); + + $local_time = datetime_convert('UTC',date_default_timezone_get(),$n['date']); + + call_hooks('ping_xmlize', $n); + $notsxml = '%s'."\n"; return sprintf ( $notsxml, - xmlify($href), xmlify($name), xmlify($url), xmlify($photo), xmlify($date), xmlify($seen), xmlify($message) + xmlify($n['href']), xmlify($n['name']), xmlify($n['url']), xmlify($n['photo']), + xmlify(relative_date($n['date'])), xmlify($n['seen']), xmlify(strtotime($local_time)), + xmlify($n['message']) ); } @@ -211,83 +210,78 @@ function ping_init(&$a) { $birthdays $birthdays_today\r\n"; - $tot = $mail+$intro+$register+count($comments)+count($likes)+count($dislikes)+count($friends)+count($posts)+count($tags); - require_once('include/bbcode.php'); - - if($firehose) { - echo ' '; - } - else { - if(count($z) && (! $sysnotify)) { - foreach($z as $zz) { - if($zz['seen'] == 0) - $sysnotify ++; - } - } - - echo ' '; - if(count($z)) { - foreach($z as $zz) { - echo xmlize($a->get_baseurl() . '/notify/view/' . $zz['id'], $zz['name'],$zz['url'],$zz['photo'],relative_date($zz['date']), ($zz['seen'] ? 'notify-seen' : 'notify-unseen'), ($zz['seen'] ? '' : '→ ') .strip_tags(bbcode($zz['msg']))); - } + if(count($notifs) && (! $sysnotify)) { + foreach($notifs as $zz) { + if($zz['seen'] == 0) + $sysnotify ++; } } - if($firehose) { - if ($intro>0){ - foreach ($intros as $i) { - echo xmlize( $a->get_baseurl().'/notifications/intros/'.$i['id'], $i['name'], $i['url'], $i['photo'], relative_date($i['datetime']), 'notify-unseen',t("{0} wants to be your friend") ); - }; - } - if ($mail>0){ - foreach ($mails as $i) { - echo xmlize( $a->get_baseurl().'/message/'.$i['id'], $i['from-name'], $i['from-url'], $i['from-photo'], relative_date($i['created']), 'notify-unseen',t("{0} sent you a message") ); - }; - } - if ($register>0){ - foreach ($regs as $i) { - echo xmlize( $a->get_baseurl().'/admin/users/', $i['name'], $i['url'], $i['micro'], relative_date($i['created']), 'notify-unseen',t("{0} requested registration") ); - }; - } + echo ' '; - if (count($comments)){ - foreach ($comments as $i) { - echo xmlize( $a->get_baseurl().'/display/'.$a->user['nickname']."/".$i['parent'], $i['author-name'], $i['author-link'], $i['author-avatar'], relative_date($i['created']), 'notify-unseen',sprintf( t("{0} commented %s's post"), $i['pname'] ) ); - }; - } - if (count($likes)){ - foreach ($likes as $i) { - echo xmlize( $a->get_baseurl().'/display/'.$a->user['nickname']."/".$i['parent'], $i['author-name'], $i['author-link'], $i['author-avatar'], relative_date($i['created']), 'notify-unseen',sprintf( t("{0} liked %s's post"), $i['pname'] ) ); - }; - } - if (count($dislikes)){ - foreach ($dislikes as $i) { - echo xmlize( $a->get_baseurl().'/display/'.$a->user['nickname']."/".$i['parent'], $i['author-name'], $i['author-link'], $i['author-avatar'], relative_date($i['created']), 'notify-unseen',sprintf( t("{0} disliked %s's post"), $i['pname'] ) ); - }; - } - if (count($friends)){ - foreach ($friends as $i) { - echo xmlize($a->get_baseurl().'/display/'.$a->user['nickname']."/".$i['parent'],$i['author-name'],$i['author-link'], $i['author-avatar'], relative_date($i['created']), 'notify-unseen',sprintf( t("{0} is now friends with %s"), $i['fname'] ) ); - }; - } - if (count($posts)){ - foreach ($posts as $i) { - echo xmlize( $a->get_baseurl().'/display/'.$a->user['nickname']."/".$i['parent'], $i['author-name'], $i['author-link'], $i['author-avatar'], relative_date($i['created']), 'notify-unseen',sprintf( t("{0} posted") ) ); - }; - } - if (count($tags)){ - foreach ($tags as $i) { - echo xmlize( $a->get_baseurl().'/display/'.$a->user['nickname']."/".$i['parent'], $i['author-name'], $i['author-link'], $i['author-avatar'], relative_date($i['created']), 'notify-unseen',sprintf( t("{0} tagged %s's post with #%s"), $i['pname'], $i['tname'] ) ); - }; - } - - if (count($cit)){ - foreach ($cit as $i) { - echo xmlize( $a->get_baseurl().'/display/'.$a->user['nickname']."/".$i['parent'], $i['author-name'], $i['author-link'], $i['author-avatar'], relative_date($i['created']), 'notify-unseen',t("{0} mentioned you in a post") ); - }; + // merge all notification types in one array + if ($intro>0){ + foreach ($intros as $i) { + $n = array( + 'href' => $a->get_baseurl().'/notifications/intros/'.$i['id'], + 'name' => $i['name'], + 'url' => $i['url'], + 'photo' => $i['photo'], + 'date' => $i['datetime'], + 'seen' => false, + 'message' => t("{0} wants to be your friend"), + ); + $notifs[] = $n; } } + + if ($mail>0){ + foreach ($mails as $i) { + $n = array( + 'href' => $a->get_baseurl().'/message/'.$i['id'], + 'name' => $i['from-name'], + 'url' => $i['from-url'], + 'photo' => $i['from-photo'], + 'date' => $i['created'], + 'seen' => false, + 'message' => t("{0} sent you a message"), + ); + $notifs[] = $n; + } + } + + if ($register>0){ + foreach ($regs as $i) { + $n = array( + 'href' => $a->get_baseurl().'/admin/users/', + 'name' => $i['name'], + 'url' => $i['url'], + 'photo' => $i['micro'], + 'date' => $i['created'], + 'seen' => false, + 'message' => t("{0} requested registration"), + ); + $notifs[] = $n; + } + } + // sort notifications by $[]['date'] + $sort_function = function($a, $b) { + $adate = date($a['date']); + $bdate = date($b['date']); + if ($adate == $bdate) { + return 0; + } + return ($adate < $bdate) ? 1 : -1; + }; + usort($notifs, $sort_function); + + if(count($notifs)) { + foreach($notifs as $n) { + echo xmlize($n); + } + } + echo " "; } @@ -305,7 +299,7 @@ function ping_init(&$a) { } unset($_SESSION['sysmsg_info']); } - + echo " "; echo" "; @@ -313,3 +307,70 @@ function ping_init(&$a) { killme(); } +function ping_get_notifications($uid) { + + $result = array(); + $offset = 0; + $seen = false; + $seensql = "NOT"; + $order = "DESC"; + $quit = false; + + $a = get_app(); + + do { + $r = q("SELECT `notify`.*, `item`.`visible`, `item`.`spam`, `item`.`deleted` + FROM `notify` LEFT JOIN `item` ON `item`.`id` = `notify`.`iid` + WHERE `notify`.`uid` = %d AND `notify`.`msg` != '' + AND NOT (`notify`.`type` IN (%d, %d)) + AND $seensql `notify`.`seen` ORDER BY `notify`.`date` $order LIMIT %d, 50", + intval($uid), + intval(NOTIFY_INTRO), + intval(NOTIFY_MAIL), + intval($offset) + ); + + if (!$r AND !$seen) { + $seen = true; + $seensql = ""; + $order = "DESC"; + $offset = 0; + } elseif (!$r) + $quit = true; + else + $offset += 50; + + + foreach ($r AS $notification) { + if (is_null($notification["visible"])) + $notification["visible"] = true; + + if (is_null($notification["spam"])) + $notification["spam"] = 0; + + if (is_null($notification["deleted"])) + $notification["deleted"] = 0; + + $notification["message"] = strip_tags(bbcode($notification["msg"])); + $notification["name"] = strip_tags(bbcode($notification["name"])); + + // Replace the name with {0} but ensure to make that only once + // The {0} is used later and prints the name in bold. + + $pos = strpos($notification["message"],$notification['name']); + if ($pos !== false) + $notification["message"] = substr_replace($notification["message"],"{0}",$pos,strlen($notification["name"])); + + $notification['href'] = $a->get_baseurl() . '/notify/view/' . $notification['id']; + + if ($notification["visible"] AND !$notification["spam"] AND + !$notification["deleted"] AND !is_array($result[$notification["parent"]])) { + $result[$notification["parent"]] = $notification; + } + } + + } while ((count($result) < 50) AND !$quit); + + + return($result); +} diff --git a/mod/poco.php b/mod/poco.php index 86b43d651..f84fc964d 100644 --- a/mod/poco.php +++ b/mod/poco.php @@ -5,7 +5,7 @@ function poco_init(&$a) { $system_mode = false; - if(intval(get_config('system','block_public'))) + if(intval(get_config('system','block_public')) || (get_config('system','block_local_dir'))) http_status_exit(401); @@ -61,12 +61,16 @@ function poco_init(&$a) { $update_limit = date("Y-m-d H:i:s",strtotime($_GET['updatedSince'])); if ($global) { - $r = q("SELECT count(*) AS `total` FROM `gcontact` WHERE `updated` >= '%s' AND `network` IN ('%s')", + //$r = q("SELECT count(*) AS `total` FROM `gcontact` WHERE `updated` >= '%s' AND ((`last_contact` >= `last_failure`) OR (`updated` >= `last_failure`)) AND `network` IN ('%s')", + $r = q("SELECT count(*) AS `total` FROM `gcontact` WHERE `updated` >= '%s' AND `updated` >= `last_failure` AND `network` IN ('%s', '%s', '%s')", dbesc($update_limit), - dbesc(NETWORK_DFRN) + dbesc(NETWORK_DFRN), + dbesc(NETWORK_DIASPORA), + dbesc(NETWORK_OSTATUS) ); } elseif($system_mode) { - $r = q("SELECT count(*) AS `total` FROM `contact` WHERE `self` = 1 AND `network` IN ('%s', '%s', '%s', '%s', '') + $r = q("SELECT count(*) AS `total` FROM `contact` WHERE `self` = 1 AND `network` IN ('%s', '%s', '%s', '%s') + AND (`success_update` >= `failure_update` OR `last-item` >= `failure_update`) AND `uid` IN (SELECT `uid` FROM `pconfig` WHERE `cat` = 'system' AND `k` = 'suggestme' AND `v` = 1) ", dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), @@ -75,7 +79,8 @@ function poco_init(&$a) { ); } else { $r = q("SELECT count(*) AS `total` FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0 - AND `network` IN ('%s', '%s', '%s', '%s', '') $sql_extra", + AND (`success_update` >= `failure_update` OR `last-item` >= `failure_update`) + AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra", intval($user['uid']), dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), @@ -93,19 +98,25 @@ function poco_init(&$a) { $startIndex = 0; $itemsPerPage = ((x($_GET,'count') && intval($_GET['count'])) ? intval($_GET['count']) : $totalResults); - if ($global) { - $r = q("SELECT * FROM `gcontact` WHERE `updated` > '%s' AND `network` IN ('%s') LIMIT %d, %d", + logger("Start global query", LOGGER_DEBUG); + //$r = q("SELECT * FROM `gcontact` WHERE `updated` > '%s' AND `network` IN ('%s') AND ((`last_contact` >= `last_failure`) OR (`updated` > `last_failure`)) LIMIT %d, %d", + $r = q("SELECT * FROM `gcontact` WHERE `updated` > '%s' AND `network` IN ('%s', '%s', '%s') AND `updated` > `last_failure` + ORDER BY `updated` DESC LIMIT %d, %d", dbesc($update_limit), dbesc(NETWORK_DFRN), + dbesc(NETWORK_DIASPORA), + dbesc(NETWORK_OSTATUS), intval($startIndex), intval($itemsPerPage) ); } elseif($system_mode) { + logger("Start system mode query", LOGGER_DEBUG); $r = q("SELECT `contact`.*, `profile`.`about` AS `pabout`, `profile`.`locality` AS `plocation`, `profile`.`pub_keywords`, `profile`.`gender` AS `pgender`, `profile`.`address` AS `paddress`, `profile`.`region` AS `pregion`, `profile`.`postal-code` AS `ppostalcode`, `profile`.`country-name` AS `pcountry` FROM `contact` INNER JOIN `profile` ON `profile`.`uid` = `contact`.`uid` - WHERE `self` = 1 AND `network` IN ('%s', '%s', '%s', '%s', '') AND `profile`.`is-default` + WHERE `self` = 1 AND `network` IN ('%s', '%s', '%s', '%s') AND `profile`.`is-default` + AND ((`contact`.`success_update` >= `contact`.`failure_update`) OR (`contact`.`last-item` >= `contact`.`failure_update`)) AND `contact`.`uid` IN (SELECT `uid` FROM `pconfig` WHERE `cat` = 'system' AND `k` = 'suggestme' AND `v` = 1) LIMIT %d, %d", dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), @@ -115,8 +126,10 @@ function poco_init(&$a) { intval($itemsPerPage) ); } else { + logger("Start query for user ".$user['nickname'], LOGGER_DEBUG); $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0 - AND `network` IN ('%s', '%s', '%s', '%s', '') $sql_extra LIMIT %d, %d", + AND (`success_update` >= `failure_update` OR `last-item` >= `failure_update`) + AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra LIMIT %d, %d", intval($user['uid']), dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), @@ -126,6 +139,7 @@ function poco_init(&$a) { intval($itemsPerPage) ); } + logger("Query done", LOGGER_DEBUG); $ret = array(); if(x($_GET,'sorted')) @@ -206,13 +220,19 @@ function poco_init(&$a) { if (($rr['keywords'] == "") AND isset($rr['pub_keywords'])) $rr['keywords'] = $rr['pub_keywords']; + $about = Cache::get("about:".$rr['updated'].":".$rr['nurl']); + if (is_null($about)) { + $about = bbcode($rr['about'], false, false); + Cache::set("about:".$rr['updated'].":".$rr['nurl'],$about); + } + $entry = array(); if($fields_ret['id']) $entry['id'] = (int)$rr['id']; if($fields_ret['displayName']) $entry['displayName'] = $rr['name']; if($fields_ret['aboutMe']) - $entry['aboutMe'] = bbcode($rr['about'], false, false); + $entry['aboutMe'] = $about; if($fields_ret['currentLocation']) $entry['currentLocation'] = $rr['location']; if($fields_ret['gender']) @@ -295,6 +315,8 @@ function poco_init(&$a) { else http_status_exit(500); + logger("End of poco", LOGGER_DEBUG); + if($format === 'xml') { header('Content-type: text/xml'); echo replace_macros(get_markup_template('poco_xml.tpl'),array_xmlify(array('$response' => $ret))); diff --git a/mod/profile.php b/mod/profile.php index bb45d1fec..608971d08 100644 --- a/mod/profile.php +++ b/mod/profile.php @@ -44,7 +44,7 @@ function profile_init(&$a) { if(x($a->profile,'openidserver')) $a->page['htmlhead'] .= '' . "\r\n"; if(x($a->profile,'openid')) { - $delegate = ((strstr($a->profile['openid'],'://')) ? $a->profile['openid'] : 'http://' . $a->profile['openid']); + $delegate = ((strstr($a->profile['openid'],'://')) ? $a->profile['openid'] : 'https://' . $a->profile['openid']); $a->page['htmlhead'] .= '' . "\r\n"; } // site block @@ -166,7 +166,6 @@ function profile_content(&$a, $update = 0) { if($tab === 'profile') { - require_once('include/profile_advanced.php'); $o .= advanced_profile($a); call_hooks('profile_advanced',$o); return $o; @@ -182,27 +181,27 @@ function profile_content(&$a, $update = 0) { $commpage = (($a->profile['page-flags'] == PAGE_COMMUNITY) ? true : false); $commvisitor = (($commpage && $remote_contact == true) ? true : false); - $celeb = ((($a->profile['page-flags'] == PAGE_SOAPBOX) || ($a->profile['page-flags'] == PAGE_COMMUNITY)) ? true : false); - - $a->page['aside'] .= posted_date_widget($a->get_baseurl(true) . '/profile/' . $a->profile['nickname'],$a->profile['profile_uid'],true); + $a->page['aside'] .= posted_date_widget($a->get_baseurl(true) . '/profile/' . $a->profile['nickname'],$a->profile['profile_uid'],true); $a->page['aside'] .= categories_widget($a->get_baseurl(true) . '/profile/' . $a->profile['nickname'],(x($category) ? xmlify($category) : '')); if(can_write_wall($a,$a->profile['profile_uid'])) { $x = array( 'is_owner' => $is_owner, - 'allow_location' => ((($is_owner || $commvisitor) && $a->profile['allow_location']) ? true : false), - 'default_location' => (($is_owner) ? $a->user['default-location'] : ''), - 'nickname' => $a->profile['nickname'], - 'lockstate' => (((is_array($a->user) && ((strlen($a->user['allow_cid'])) || (strlen($a->user['allow_gid'])) || (strlen($a->user['deny_cid'])) || (strlen($a->user['deny_gid']))))) ? 'lock' : 'unlock'), - 'acl' => (($is_owner) ? populate_acl($a->user, $celeb) : ''), - 'bang' => '', - 'visitor' => (($is_owner || $commvisitor) ? 'block' : 'none'), - 'profile_uid' => $a->profile['profile_uid'], + 'allow_location' => ((($is_owner || $commvisitor) && $a->profile['allow_location']) ? true : false), + 'default_location' => (($is_owner) ? $a->user['default-location'] : ''), + 'nickname' => $a->profile['nickname'], + 'lockstate' => (((is_array($a->user) && ((strlen($a->user['allow_cid'])) || + (strlen($a->user['allow_gid'])) || (strlen($a->user['deny_cid'])) || + (strlen($a->user['deny_gid']))))) ? 'lock' : 'unlock'), + 'acl' => (($is_owner) ? populate_acl($a->user, true) : ''), + 'bang' => '', + 'visitor' => (($is_owner || $commvisitor) ? 'block' : 'none'), + 'profile_uid' => $a->profile['profile_uid'], 'acl_data' => ( $is_owner ? construct_acl_data($a, $a->user) : '' ), // For non-Javascript ACL selector - ); + ); - $o .= status_editor($a,$x); + $o .= status_editor($a,$x); } } @@ -303,9 +302,9 @@ function profile_content(&$a, $update = 0) { foreach($r as $rr) $parents_arr[] = $rr['item_id']; $parents_str = implode(', ', $parents_arr); - + $items = q("SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, - `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`alias`, `contact`.`network`, `contact`.`rel`, + `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`alias`, `contact`.`network`, `contact`.`rel`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`, `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` FROM `item`, `contact` diff --git a/mod/profile_photo.php b/mod/profile_photo.php index 6ca7c4b6d..aa8809059 100644 --- a/mod/profile_photo.php +++ b/mod/profile_photo.php @@ -118,7 +118,7 @@ function profile_photo_post(&$a) { info( t('Shift-reload the page or clear browser cache if the new photo does not display immediately.') . EOL); // Update global directory in background $url = $a->get_baseurl() . '/profile/' . $a->user['nickname']; - if($url && strlen(get_config('system','directory_submit_url'))) + if($url && strlen(get_config('system','directory'))) proc_run('php',"include/directory.php","$url"); require_once('include/profile_update.php'); @@ -141,7 +141,7 @@ function profile_photo_post(&$a) { $maximagesize = get_config('system','maximagesize'); if(($maximagesize) && ($filesize > $maximagesize)) { - notice( sprintf(t('Image exceeds size limit of %d'), $maximagesize) . EOL); + notice( sprintf(t('Image exceeds size limit of %s'), formatBytes($maximagesize)) . EOL); @unlink($src); return; } @@ -217,7 +217,7 @@ function profile_photo_content(&$a) { // Update global directory in background $url = $_SESSION['my_url']; - if($url && strlen(get_config('system','directory_submit_url'))) + if($url && strlen(get_config('system','directory'))) proc_run('php',"include/directory.php","$url"); goaway($a->get_baseurl() . '/profiles'); diff --git a/mod/profiles.php b/mod/profiles.php index 2b4952c3b..6c1a82c7b 100644 --- a/mod/profiles.php +++ b/mod/profiles.php @@ -190,27 +190,23 @@ function profiles_post(&$a) { return; } - $year = intval($_POST['year']); - if($year < 1900 || $year > 2100 || $year < 0) - $year = 0; - $month = intval($_POST['month']); - if(($month > 12) || ($month < 0)) - $month = 0; - $mtab = array(0,31,29,31,30,31,30,31,31,30,31,30,31); - $day = intval($_POST['day']); - if(($day > $mtab[$month]) || ($day < 0)) - $day = 0; - - // It's OK to have an empty (0) year, but if you supplied a year you have to have a non-zero month and day - if($year && ! $month) - $month = 1; - if($year && ! $day) - $day = 1; - - $dob = '0000-00-00'; - $dob = sprintf('%04d-%02d-%02d',$year,$month,$day); - + $dob = $_POST['dob'] ? escape_tags(trim($_POST['dob'])) : '0000-00-00'; // FIXME: Needs to be validated? + $y = substr($dob,0,4); + if((! ctype_digit($y)) || ($y < 1900)) + $ignore_year = true; + else + $ignore_year = false; + if($dob != '0000-00-00') { + if(strpos($dob,'0000-') === 0) { + $ignore_year = true; + $dob = substr($dob,5); + } + $dob = datetime_convert('UTC','UTC',(($ignore_year) ? '1900-' . $dob : $dob),(($ignore_year) ? 'm-d' : 'Y-m-d')); + if($ignore_year) + $dob = '0000-' . $dob; + } + $name = notags(trim($_POST['name'])); if(! strlen($name)) { @@ -474,7 +470,8 @@ function profiles_post(&$a) { if($namechanged && $is_default) { - $r = q("UPDATE `contact` SET `name-date` = '%s' WHERE `self` = 1 AND `uid` = %d", + $r = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `self` = 1 AND `uid` = %d", + dbesc($name), dbesc(datetime_convert()), intval(local_user()) ); @@ -511,7 +508,7 @@ function profiles_post(&$a) { // Update global directory in background $url = $_SESSION['my_url']; - if($url && strlen(get_config('system','directory_submit_url'))) + if($url && strlen(get_config('system','directory'))) proc_run('php',"include/directory.php","$url"); require_once('include/profile_update.php'); @@ -701,7 +698,7 @@ function profiles_content(&$a) { '$lbl_fullname' => t('Your Full Name:'), '$lbl_title' => t('Title/Description:'), '$lbl_gender' => t('Your Gender:'), - '$lbl_bd' => sprintf( t("Birthday \x28%s\x29:"),datesel_format($f)), + '$lbl_bd' => t("Birthday :"), '$lbl_address' => t('Street Address:'), '$lbl_city' => t('Locality/City:'), '$lbl_zip' => t('Postal/Zip Code:'), diff --git a/mod/profperm.php b/mod/profperm.php index 444f7a5ea..077f695be 100644 --- a/mod/profperm.php +++ b/mod/profperm.php @@ -37,7 +37,8 @@ function profperm_content(&$a) { if(($a->argc > 2) && intval($a->argv[1]) && intval($a->argv[2])) { $r = q("SELECT `id` FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 AND `self` = 0 - AND `network` = 'dfrn' AND `id` = %d AND `uid` = %d LIMIT 1", + AND `network` = '%s' AND `id` = %d AND `uid` = %d LIMIT 1", + dbesc(NETWORK_DFRN), intval($a->argv[2]), intval(local_user()) ); @@ -130,10 +131,11 @@ function profperm_content(&$a) { $o .= '

' . t("All Contacts \x28with secure profile access\x29") . '

'; $o .= '
'; $o .= '
'; - - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 and `pending` = 0 and `self` = 0 - AND `network` = 'dfrn' ORDER BY `name` ASC", - intval(local_user()) + + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 and `pending` = 0 and `self` = 0 + AND `network` = '%s' ORDER BY `name` ASC", + intval(local_user()), + dbesc(NETWORK_DFRN) ); if(count($r)) { diff --git a/mod/pubsub.php b/mod/pubsub.php index 82458f46a..beb73b4e2 100644 --- a/mod/pubsub.php +++ b/mod/pubsub.php @@ -63,14 +63,15 @@ function pubsub_init(&$a) { intval($owner['uid']) ); if(! count($r)) { - logger('pubsub: contact not found.'); + logger('pubsub: contact '.$contact_id.' not found.'); hub_return(false, ''); } - if(! link_compare($hub_topic,$r[0]['poll'])) { - logger('pubsub: hub topic ' . $hub_topic . ' != ' . $r[0]['poll']); - // should abort but let's humour them. - } + if ($hub_topic) + if(! link_compare($hub_topic,$r[0]['poll'])) { + logger('pubsub: hub topic ' . $hub_topic . ' != ' . $r[0]['poll']); + // should abort but let's humour them. + } $contact = $r[0]; @@ -85,10 +86,11 @@ function pubsub_init(&$a) { logger('pubsub: unsubscribe success'); } - $r = q("UPDATE `contact` SET `subhub` = %d WHERE `id` = %d", - intval($subscribe), - intval($contact['id']) - ); + if ($hub_mode) + $r = q("UPDATE `contact` SET `subhub` = %d WHERE `id` = %d", + intval($subscribe), + intval($contact['id']) + ); hub_return(true, $hub_challenge); } @@ -120,7 +122,7 @@ function pubsub_post(&$a) { $importer = $r[0]; - $r = q("SELECT * FROM `contact` WHERE `subhub` = 1 AND `id` = %d AND `uid` = %d + $r = q("SELECT * FROM `contact` WHERE `subhub` = 1 AND `id` = %d AND `uid` = %d AND ( `rel` = %d OR `rel` = %d OR network = '%s' ) AND `blocked` = 0 AND `readonly` = 0 LIMIT 1", intval($contact_id), intval($importer['uid']), diff --git a/mod/receive.php b/mod/receive.php index ee15ebe8a..95a510167 100644 --- a/mod/receive.php +++ b/mod/receive.php @@ -9,7 +9,7 @@ require_once('include/salmon.php'); require_once('include/crypto.php'); require_once('include/diaspora.php'); - + function receive_post(&$a) { diff --git a/mod/register.php b/mod/register.php index 4c0860e6e..1963bd7a6 100644 --- a/mod/register.php +++ b/mod/register.php @@ -79,25 +79,27 @@ function register_post(&$a) { set_pconfig($user['uid'],'system','invites_remaining',$num_invites); } - $res = send_register_open_eml( - $user['email'], - $a->config['sitename'], - $a->get_baseurl(), - $user['username'], - $result['password']); + // Only send a password mail when the password wasn't manually provided + if (!x($_POST,'password1') OR !x($_POST,'confirm')) { + $res = send_register_open_eml( + $user['email'], + $a->config['sitename'], + $a->get_baseurl(), + $user['username'], + $result['password']); - if($res) { - info( t('Registration successful. Please check your email for further instructions.') . EOL ) ; - goaway(z_root()); - } - else { - notice( - sprintf( - t('Failed to send email message. Here your accout details:
login: %s
password: %s

You can change your password after login.'), - $user['email'], - $result['password'] - ). EOL - ); + if($res) { + info( t('Registration successful. Please check your email for further instructions.') . EOL ) ; + goaway(z_root()); + } else { + notice( + sprintf( + t('Failed to send email message. Here your accout details:
login: %s
password: %s

You can change your password after login.'), + $user['email'], + $result['password'] + ). EOL + ); + } } } elseif($a->config['register_policy'] == REGISTER_APPROVE) { @@ -235,6 +237,9 @@ function register_content(&$a) { )); } + $r = q("SELECT count(*) AS `contacts` FROM `contact`"); + $passwords = !$r[0]["contacts"]; + $license = ''; $o = get_markup_template("register.tpl"); @@ -262,6 +267,9 @@ function register_content(&$a) { '$openid' => $openid_url, '$namelabel' => t('Your Full Name ' . "\x28" . 'e.g. Joe Smith' . "\x29" . ': '), '$addrlabel' => t('Your Email Address: '), + '$passwords' => $passwords, + '$password1' => array('password1', t('New Password:'), '', t('Leave empty for an auto generated password.')), + '$password2' => array('confirm', t('Confirm:'), '', ''), '$nickdesc' => str_replace('$sitename',$a->get_hostname(),t('Choose a profile nickname. This must begin with a text character. Your profile address on this site will then be \'nickname@$sitename\'.')), '$nicklabel' => t('Choose a nickname: '), '$photo' => $photo, diff --git a/mod/regmod.php b/mod/regmod.php index 53e6716f2..5a90db1f9 100644 --- a/mod/regmod.php +++ b/mod/regmod.php @@ -36,7 +36,7 @@ function user_allow($hash) { ); if(count($r) && $r[0]['net-publish']) { $url = $a->get_baseurl() . '/profile/' . $user[0]['nickname']; - if($url && strlen(get_config('system','directory_submit_url'))) + if($url && strlen(get_config('system','directory'))) proc_run('php',"include/directory.php","$url"); } @@ -121,13 +121,13 @@ function regmod_content(&$a) { if($cmd === 'deny') { user_deny($hash); - goaway("/admin/users/"); + goaway($a->get_baseurl()."/admin/users/"); killme(); } if($cmd === 'allow') { user_allow($hash); - goaway("/admin/users/"); + goaway($a->get_baseurl()."/admin/users/"); killme(); } } diff --git a/mod/repair_ostatus.php b/mod/repair_ostatus.php new file mode 100755 index 000000000..06e34a63e --- /dev/null +++ b/mod/repair_ostatus.php @@ -0,0 +1,57 @@ +".t("Resubsribing to OStatus contacts").""; + + $uid = local_user(); + + $a = get_app(); + + $counter = intval($_REQUEST['counter']); + + $r = q("SELECT COUNT(*) AS `total` FROM `contact` WHERE + `uid` = %d AND `network` = '%s' AND `rel` IN (%d, %d)", + intval($uid), + dbesc(NETWORK_OSTATUS), + intval(CONTACT_IS_FRIEND), + intval(CONTACT_IS_SHARING)); + + if (!$r) + return($o.t("Error")); + + $total = $r[0]["total"]; + + $r = q("SELECT `url` FROM `contact` WHERE + `uid` = %d AND `network` = '%s' AND `rel` IN (%d, %d) + ORDER BY `url` + LIMIT %d, 1", + intval($uid), + dbesc(NETWORK_OSTATUS), + intval(CONTACT_IS_FRIEND), + intval(CONTACT_IS_SHARING), $counter++); + + if (!$r) { + $o .= t("Done"); + return $o; + } + + $o .= "

".$counter."/".$total.": ".$r[0]["url"]."

"; + + $o .= "

".t("Keep this window open until done.")."

"; + + $result = new_contact($uid,$r[0]["url"],true); + + $a->page['htmlhead'] = ''; + + return $o; +} diff --git a/mod/salmon.php b/mod/salmon.php index 11e42d943..9c22e42d1 100644 --- a/mod/salmon.php +++ b/mod/salmon.php @@ -1,12 +1,10 @@ /','',$data); - - // Create a fake feed wrapper so simplepie doesn't choke - - $tpl = get_markup_template('fake_feed.tpl'); - - $base = substr($data,strpos($data,''; - - logger('mod-salmon: Processed feed: ' . $feedxml); - - // Now parse it like a normal atom feed to scrape out the author URI - - $feed = new SimplePie(); - $feed->set_raw_data($feedxml); - $feed->enable_order_by_date(false); - $feed->init(); - - logger('mod-salmon: Feed parsed.'); - - if($feed->get_item_quantity()) { - foreach($feed->get_items() as $item) { - $author = $item->get_author(); - $author_link = unxmlify($author->get_link()); - break; - } - } + $author = ostatus_salmon_author($data,$importer); + $author_link = $author["author-link"]; if(! $author_link) { logger('mod-salmon: Could not retrieve author URI.'); @@ -123,8 +94,7 @@ function salmon_post(&$a) { // Once we have the author URI, go to the web and try to find their public key - logger('mod-salmon: Fetching key for ' . $author_link ); - + logger('mod-salmon: Fetching key for ' . $author_link); $key = get_salmon_key($author_link,$keyhash); @@ -144,17 +114,17 @@ function salmon_post(&$a) { // We should have everything we need now. Let's see if it verifies. - $verify = rsa_verify($compliant_format,$signature,$pubkey); + $verify = rsa_verify($compliant_format,$signature,$pubkey); if(! $verify) { logger('mod-salmon: message did not verify using protocol. Trying padding hack.'); $verify = rsa_verify($signed_data,$signature,$pubkey); - } + } if(! $verify) { logger('mod-salmon: message did not verify using padding. Trying old statusnet hack.'); $verify = rsa_verify($stnet_signed_data,$signature,$pubkey); - } + } if(! $verify) { logger('mod-salmon: Message did not verify. Discarding.'); @@ -170,20 +140,22 @@ function salmon_post(&$a) { * */ - $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND ( `url` = '%s' OR `alias` = '%s' ) - AND `uid` = %d LIMIT 1", + $r = q("SELECT * FROM `contact` WHERE `network` IN ('%s', '%s') + AND (`nurl` = '%s' OR `alias` = '%s' OR `alias` = '%s') + AND `uid` = %d LIMIT 1", dbesc(NETWORK_OSTATUS), + dbesc(NETWORK_DFRN), + dbesc(normalise_link($author_link)), dbesc($author_link), - dbesc($author_link), + dbesc(normalise_link($author_link)), intval($importer['uid']) ); if(! count($r)) { logger('mod-salmon: Author unknown to us.'); if(get_pconfig($importer['uid'],'system','ostatus_autofriend')) { - require_once('include/follow.php'); $result = new_contact($importer['uid'],$author_link); if($result['success']) { - $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND ( `url` = '%s' OR `alias` = '%s' ) + $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND ( `url` = '%s' OR `alias` = '%s') AND `uid` = %d LIMIT 1", dbesc(NETWORK_OSTATUS), dbesc($author_link), @@ -194,32 +166,22 @@ function salmon_post(&$a) { } } - // is this a follower? Or have we ignored the person? + // Have we ignored the person? // If so we can not accept this post. - if((count($r)) && (($r[0]['readonly']) || ($r[0]['rel'] == CONTACT_IS_FOLLOWER) || ($r[0]['blocked']))) { + //if((count($r)) && (($r[0]['readonly']) || ($r[0]['rel'] == CONTACT_IS_FOLLOWER) || ($r[0]['blocked']))) { + if(count($r) && $r[0]['blocked']) { logger('mod-salmon: Ignoring this author.'); http_status_exit(202); // NOTREACHED } - require_once('include/items.php'); - - // Placeholder for hub discovery. We shouldn't find any hubs - // since we supplied the fake feed header - and it doesn't have any. - + // Placeholder for hub discovery. $hub = ''; - /** - * - * anti-spam measure: consume_feed will accept a follow activity from - * this person (and nothing else) if there is no existing contact record. - * - */ - $contact_rec = ((count($r)) ? $r[0] : null); - consume_feed($feedxml,$importer,$contact_rec,$hub); + ostatus_import($data,$importer,$contact_rec, $hub); http_status_exit(200); } diff --git a/mod/search.php b/mod/search.php index b9bad6405..251dd4778 100644 --- a/mod/search.php +++ b/mod/search.php @@ -1,4 +1,8 @@ $rr['id'], - 'term' => $rr['term'], - 'encodedterm' => urlencode($rr['term']), - 'delete' => t('Remove term'), - 'selected' => ($search==$rr['term']), + 'id' => $rr['id'], + 'term' => $rr['term'], + 'encodedterm' => urlencode($rr['term']), + 'delete' => t('Remove term'), + 'selected' => ($search==$rr['term']), ); } @@ -27,10 +31,10 @@ function search_saved_searches() { $tpl = get_markup_template("saved_searches_aside.tpl"); $o .= replace_macros($tpl, array( - '$title' => t('Saved Searches'), - '$add' => '', - '$searchbox' => '', - '$saved' => $saved, + '$title' => t('Saved Searches'), + '$add' => '', + '$searchbox' => '', + '$saved' => $saved, )); } @@ -45,12 +49,12 @@ function search_init(&$a) { if(local_user()) { if(x($_GET,'save') && $search) { - $r = q("select * from `search` where `uid` = %d and `term` = '%s' limit 1", + $r = q("SELECT * FROM `search` WHERE `uid` = %d AND `term` = '%s' LIMIT 1", intval(local_user()), dbesc($search) ); if(! count($r)) { - q("insert into `search` ( `uid`,`term` ) values ( %d, '%s') ", + q("INSERT INTO `search` (`uid`,`term`) VALUES ( %d, '%s')", intval(local_user()), dbesc($search) ); @@ -90,11 +94,15 @@ function search_content(&$a) { return; } + if(get_config('system','local_search') AND !local_user()) { + notice(t('Public access denied.').EOL); + return; + //http_status_exit(403); + //killme(); + } + nav_set_selected('search'); - require_once("include/bbcode.php"); - require_once('include/security.php'); - require_once('include/conversation.php'); $o = '

' . t('Search') . '

'; @@ -110,16 +118,33 @@ function search_content(&$a) { } - $o .= search($search,'search-box','/search',((local_user()) ? true : false)); + $o .= search($search,'search-box','/search',((local_user()) ? true : false), false); if(strpos($search,'#') === 0) { $tag = true; $search = substr($search,1); } if(strpos($search,'@') === 0) { - require_once('mod/dirfind.php'); return dirfind_content($a); } + if(strpos($search,'!') === 0) { + return dirfind_content($a); + } + + if(x($_GET,'search-option')) + switch($_GET['search-option']) { + case 'fulltext': + break; + case 'tags': + $tag = true; + break; + case 'contacts': + return dirfind_content($a, "@"); + break; + case 'forums': + return dirfind_content($a, "!"); + break; + } if(! $search) return $o; @@ -135,7 +160,7 @@ function search_content(&$a) { if($tag) { logger("Start tag search for '".$search."'", LOGGER_DEBUG); - $r = q("SELECT `item`.`uri`, `item`.*, `item`.`id` AS `item_id`, + $r = q("SELECT STRAIGHT_JOIN `item`.`uri`, `item`.*, `item`.`id` AS `item_id`, `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`alias`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`, `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` @@ -156,7 +181,7 @@ function search_content(&$a) { $sql_extra = sprintf(" AND `item`.`body` REGEXP '%s' ", dbesc(protect_sprintf(preg_quote($search)))); } - $r = q("SELECT `item`.`uri`, `item`.*, `item`.`id` AS `item_id`, + $r = q("SELECT STRAIGHT_JOIN `item`.`uri`, `item`.*, `item`.`id` AS `item_id`, `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`alias`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`, `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` @@ -177,9 +202,13 @@ function search_content(&$a) { if($tag) - $o .= '

Items tagged with: ' . $search . '

'; + $title = sprintf( t('Items tagged with: %s'), $search); else - $o .= '

Search results for: ' . $search . '

'; + $title = sprintf( t('Search results for: %s'), $search); + + $o .= replace_macros(get_markup_template("section_title.tpl"),array( + '$title' => $title + )); logger("Start Conversation for '".$search."'", LOGGER_DEBUG); $o .= conversation($a,$r,'search',false); diff --git a/mod/settings.php b/mod/settings.php index 5ae30d7d3..556fb63e8 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -41,47 +41,56 @@ function settings_init(&$a) { 'label' => t('Account'), 'url' => $a->get_baseurl(true).'/settings', 'selected' => (($a->argc == 1) && ($a->argv[0] === 'settings')?'active':''), + 'accesskey' => 'o', ), array( 'label' => t('Additional features'), 'url' => $a->get_baseurl(true).'/settings/features', 'selected' => (($a->argc > 1) && ($a->argv[1] === 'features') ? 'active' : ''), + 'accesskey' => 't', ), array( 'label' => t('Display'), 'url' => $a->get_baseurl(true).'/settings/display', 'selected' => (($a->argc > 1) && ($a->argv[1] === 'display')?'active':''), + 'accesskey' => 'i', ), array( 'label' => t('Social Networks'), 'url' => $a->get_baseurl(true).'/settings/connectors', 'selected' => (($a->argc > 1) && ($a->argv[1] === 'connectors')?'active':''), + 'accesskey' => 'w', ), array( 'label' => t('Plugins'), 'url' => $a->get_baseurl(true).'/settings/addon', 'selected' => (($a->argc > 1) && ($a->argv[1] === 'addon')?'active':''), + 'accesskey' => 'l', ), array( 'label' => t('Delegations'), 'url' => $a->get_baseurl(true).'/delegate', 'selected' => (($a->argc == 1) && ($a->argv[0] === 'delegate')?'active':''), + 'accesskey' => 'd', ), array( 'label' => t('Connected apps'), 'url' => $a->get_baseurl(true) . '/settings/oauth', 'selected' => (($a->argc > 1) && ($a->argv[1] === 'oauth')?'active':''), + 'accesskey' => 'b', ), array( 'label' => t('Export personal data'), 'url' => $a->get_baseurl(true) . '/uexport', 'selected' => (($a->argc == 1) && ($a->argv[0] === 'uexport')?'active':''), + 'accesskey' => 'e', ), array( 'label' => t('Remove account'), 'url' => $a->get_baseurl(true) . '/removeme', 'selected' => (($a->argc == 1) && ($a->argv[0] === 'removeme')?'active':''), + 'accesskey' => 'r', ) ); @@ -177,7 +186,11 @@ function settings_post(&$a) { check_form_security_token_redirectOnErr('/settings/connectors', 'settings_connectors'); - if(x($_POST, 'imap-submit')) { + if(x($_POST, 'general-submit')) { + set_pconfig(local_user(), 'system', 'no_intelligent_shortening', intval($_POST['no_intelligent_shortening'])); + set_pconfig(local_user(), 'system', 'ostatus_autofriend', intval($_POST['snautofollow'])); + set_pconfig(local_user(), 'ostatus', 'legacy_contact', $_POST['legacy_contact']); + } elseif(x($_POST, 'imap-submit')) { $mail_server = ((x($_POST,'mail_server')) ? $_POST['mail_server'] : ''); $mail_port = ((x($_POST,'mail_port')) ? $_POST['mail_port'] : ''); @@ -474,7 +487,7 @@ function settings_post(&$a) { $str_contact_deny = perms2str($_POST['contact_deny']); $openidserver = $a->user['openidserver']; - $openid = normalise_openid($openid); + //$openid = normalise_openid($openid); // If openid has changed or if there's an openid but no openidserver, try and discover it. @@ -569,7 +582,7 @@ function settings_post(&$a) { if(($old_visibility != $net_publish) || ($page_flags != $old_page_flags)) { // Update global directory in background $url = $_SESSION['my_url']; - if($url && strlen(get_config('system','directory_submit_url'))) + if($url && strlen(get_config('system','directory'))) proc_run('php',"include/directory.php","$url"); } @@ -733,13 +746,52 @@ function settings_content(&$a) { if(($a->argc > 1) && ($a->argv[1] === 'connectors')) { - $settings_connectors = ""; + $settings_connectors = ''; + $settings_connectors .= '

'. t('General Social Media Settings').'

'; + $settings_connectors .= '
'; + $settings_connectors .= '
'; call_hooks('connector_settings', $settings_connectors); if (is_site_admin()) { $diasp_enabled = sprintf( t('Built-in support for %s connectivity is %s'), t('Diaspora'), ((get_config('system','diaspora_enabled')) ? t('enabled') : t('disabled'))); - $ostat_enabled = sprintf( t('Built-in support for %s connectivity is %s'), t('StatusNet'), ((get_config('system','ostatus_disabled')) ? t('disabled') : t('enabled'))); + $ostat_enabled = sprintf( t('Built-in support for %s connectivity is %s'), t('GNU Social (OStatus)'), ((get_config('system','ostatus_disabled')) ? t('disabled') : t('enabled'))); } else { $diasp_enabled = ""; $ostat_enabled = ""; @@ -896,6 +948,7 @@ function settings_content(&$a) { '$infinite_scroll' => array('infinite_scroll', t("Infinite scroll"), $infinite_scroll, ''), '$no_auto_update' => array('no_auto_update', t("Automatic updates only at the top of the network page"), $no_auto_update, 'When disabled, the network page is updated all the time, which could be confusing while reading.'), + 'stitle' => t('Theme settings'), '$theme_config' => $theme_config, )); @@ -1002,7 +1055,7 @@ function settings_content(&$a) { $openid_field = false; } else { - $openid_field = array('openid_url', t('OpenID:'),$openid, t("\x28Optional\x29 Allow this OpenID to login to this account.")); + $openid_field = array('openid_url', t('OpenID:'),$openid, t("\x28Optional\x29 Allow this OpenID to login to this account."), "", "", "url"); } @@ -1016,7 +1069,7 @@ function settings_content(&$a) { )); } - if(strlen(get_config('system','directory_submit_url'))) { + if(strlen(get_config('system','directory'))) { $profile_in_net_dir = replace_macros($opt_tpl,array( '$field' => array('profile_in_netdirectory', t('Publish your default profile in the global social directory?'), $profile['net-publish'], '', array(t('No'),t('Yes'))), )); @@ -1064,21 +1117,17 @@ function settings_content(&$a) { info( t('Profile is not published.') . EOL ); - $subdir = ((strlen($a->get_path())) ? '
' . t('or') . ' ' . $a->get_baseurl(true) . '/profile/' . $nickname : ''); + //$subdir = ((strlen($a->get_path())) ? '
' . t('or') . ' ' . $a->get_baseurl(true) . '/profile/' . $nickname : ''); $tpl_addr = get_markup_template("settings_nick_set.tpl"); $prof_addr = replace_macros($tpl_addr,array( - '$desc' => t('Your Identity Address is'), - '$nickname' => $nickname, - '$subdir' => $subdir, + '$desc' => sprintf(t("Your Identity Address is '%s' or '%s'."), $nickname.'@'.$a->get_hostname().$a->get_path(), $a->get_baseurl().'/profile/'.$nickname), '$basepath' => $a->get_hostname() )); $stpl = get_markup_template('settings.tpl'); - $celeb = ((($a->user['page-flags'] == PAGE_SOAPBOX) || ($a->user['page-flags'] == PAGE_COMMUNITY)) ? true : false); - $expire_arr = array( 'days' => array('expire', t("Automatically expire posts after this many days:"), $expire, t('If empty, posts will not expire. Expired posts will be deleted')), 'advanced' => t('Advanced expiration settings'), @@ -1143,7 +1192,7 @@ function settings_content(&$a) { '$permissions' => t('Default Post Permissions'), '$permdesc' => t("\x28click to open/close\x29"), '$visibility' => $profile['net-publish'], - '$aclselect' => populate_acl($a->user,$celeb), + '$aclselect' => populate_acl($a->user), '$suggestme' => $suggestme, '$blockwall'=> $blockwall, // array('blockwall', t('Allow friends to post to your profile page:'), !$blockwall, ''), '$blocktags'=> $blocktags, // array('blocktags', t('Allow friends to tag your posts:'), !$blocktags, ''), @@ -1187,6 +1236,8 @@ function settings_content(&$a) { '$notify7' => array('notify7', t('You are tagged in a post'), ($notify & NOTIFY_TAGSELF), NOTIFY_TAGSELF, ''), '$notify8' => array('notify8', t('You are poked/prodded/etc. in a post'), ($notify & NOTIFY_POKE), NOTIFY_POKE, ''), + '$desktop_notifications' => array('desktop_notifications', t('Activate desktop notifications') , false, t('Show desktop popup on new notifications')), + '$email_textonly' => array('email_textonly', t('Text-only notification emails'), get_pconfig(local_user(),'system','email_textonly'), t('Send text only notification emails, without the html part')), diff --git a/mod/share.php b/mod/share.php index e9127b647..085da4e30 100644 --- a/mod/share.php +++ b/mod/share.php @@ -1,7 +1,4 @@ argc > 1) ? intval($a->argv[1]) : 0); @@ -23,11 +20,8 @@ function share_init(&$a) { $pos = strpos($r[0]['body'], "[share"); $o = substr($r[0]['body'], $pos); } else { - $o = "[share author='".str_replace("'", "'",$r[0]['author-name']). - "' profile='".$r[0]['author-link']. - "' avatar='".$r[0]['author-avatar']. - "' link='".$r[0]['plink']. - "' posted='".$r[0]['created']."']\n"; + $o = share_header($r[0]['author-name'], $r[0]['author-link'], $r[0]['author-avatar'], $r[0]['guid'], $r[0]['created'], $r[0]['plink']); + if($r[0]['title']) $o .= '[b]'.$r[0]['title'].'[/b]'."\n"; $o .= $r[0]['body']; @@ -46,3 +40,19 @@ function share_init(&$a) { echo $o; killme(); } + +function share_header($author, $profile, $avatar, $guid, $posted, $link) { + $header = "[share author='".str_replace(array("'", "[", "]"), array("'", "[", "]"),$author). + "' profile='".str_replace(array("'", "[", "]"), array("'", "[", "]"),$profile). + "' avatar='".str_replace(array("'", "[", "]"), array("'", "[", "]"),$avatar); + + if ($guid) + $header .= "' guid='".str_replace(array("'", "[", "]"), array("'", "[", "]"),$guid); + + if ($posted) + $header .= "' posted='".str_replace(array("'", "[", "]"), array("'", "[", "]"),$posted); + + $header .= "' link='".str_replace(array("'", "[", "]"), array("'", "[", "]"),$link)."']"; + + return $header; +} diff --git a/mod/statistics_json.php b/mod/statistics_json.php new file mode 100644 index 000000000..9f97d6ac7 --- /dev/null +++ b/mod/statistics_json.php @@ -0,0 +1,55 @@ + $a->config["sitename"], + "network" => FRIENDICA_PLATFORM, + "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION, + "registrations_open" => ($a->config['register_policy'] != 0), + "total_users" => get_config('nodeinfo','total_users'), + "active_users_halfyear" => get_config('nodeinfo','active_users_halfyear'), + "active_users_monthly" => get_config('nodeinfo','active_users_monthly'), + "local_posts" => get_config('nodeinfo','local_posts') + ); + + $statistics["services"] = array(); + $statistics["services"]["appnet"] = nodeinfo_plugin_enabled("appnet"); + $statistics["services"]["blogger"] = nodeinfo_plugin_enabled("blogger"); + $statistics["services"]["buffer"] = nodeinfo_plugin_enabled("buffer"); + $statistics["services"]["dreamwidth"] = nodeinfo_plugin_enabled("dwpost"); + $statistics["services"]["facebook"] = nodeinfo_plugin_enabled("fbpost"); + $statistics["services"]["gnusocial"] = nodeinfo_plugin_enabled("statusnet"); + $statistics["services"]["googleplus"] = nodeinfo_plugin_enabled("gpluspost"); + $statistics["services"]["libertree"] = nodeinfo_plugin_enabled("libertree"); + $statistics["services"]["livejournal"] = nodeinfo_plugin_enabled("ljpost"); + $statistics["services"]["pumpio"] = nodeinfo_plugin_enabled("pumpio"); + $statistics["services"]["twitter"] = nodeinfo_plugin_enabled("twitter"); + $statistics["services"]["tumblr"] = nodeinfo_plugin_enabled("tumblr"); + $statistics["services"]["wordpress"] = nodeinfo_plugin_enabled("wppost"); + + $statistics["appnet"] = $statistics["services"]["appnet"]; + $statistics["blogger"] = $statistics["services"]["blogger"]; + $statistics["buffer"] = $statistics["services"]["buffer"]; + $statistics["dreamwidth"] = $statistics["services"]["dreamwidth"]; + $statistics["facebook"] = $statistics["services"]["facebook"]; + $statistics["gnusocial"] = $statistics["services"]["gnusocial"]; + $statistics["googleplus"] = $statistics["services"]["googleplus"]; + $statistics["libertree"] = $statistics["services"]["libertree"]; + $statistics["livejournal"] = $statistics["services"]["livejournal"]; + $statistics["pumpio"] = $statistics["services"]["pumpio"]; + $statistics["twitter"] = $statistics["services"]["twitter"]; + $statistics["tumblr"] = $statistics["services"]["tumblr"]; + $statistics["wordpress"] = $statistics["services"]["wordpress"]; + + header("Content-Type: application/json"); + echo json_encode($statistics, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + logger("statistics_init: printed ".print_r($statistics, true), LOGGER_DATA); + killme(); +} diff --git a/mod/suggest.php b/mod/suggest.php index af16197a0..e07e93311 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -65,7 +65,9 @@ function suggest_content(&$a) { $a->page['aside'] .= findpeople_widget(); - $o .= '

' . t('Friend Suggestions') . '

'; + $o .= replace_macros(get_markup_template("section_title.tpl"),array( + '$title' => t('Friend Suggestions') + )); $r = suggestion_query(local_user()); diff --git a/mod/videos.php b/mod/videos.php index 0f29e631b..df47262f1 100644 --- a/mod/videos.php +++ b/mod/videos.php @@ -27,11 +27,16 @@ function videos_init(&$a) { return; $a->data['user'] = $r[0]; + $a->profile_uid = $r[0]['uid']; - $o .= '
'; - $o .= '
' . $a->data['user']['username'] . '
'; - $o .= '
' . $a->data['user']['username'] . '
'; - $o .= '
'; + $profilephoto = $a->get_cached_avatar_image($a->get_baseurl() . '/photo/profile/' . $a->data['user']['uid'] . '.jpg'); + + $tpl = get_markup_template("vcard-widget.tpl"); + + $vcard_widget = replace_macros($tpl, array( + '$name' => $a->data['user']['username'], + '$photo' => $profilephoto + )); /*$sql_extra = permissions_sql($a->data['user']['uid']); @@ -43,12 +48,12 @@ function videos_init(&$a) { if(count($albums)) { $a->data['albums'] = $albums; - $albums_visible = ((intval($a->data['user']['hidewall']) && (! local_user()) && (! remote_user())) ? false : true); + $albums_visible = ((intval($a->data['user']['hidewall']) && (! local_user()) && (! remote_user())) ? false : true); if($albums_visible) { $o .= ' +{{if $networks}} +
+
{{$emailcc}}
+
+{{if $jotnets}} +{{$jotnets}} +
+{{/if}}{{/if}} + + diff --git a/view/theme/frost-mobile/templates/head.tpl b/view/theme/frost-mobile/templates/head.tpl index abc4fa731..5684ca7be 100644 --- a/view/theme/frost-mobile/templates/head.tpl +++ b/view/theme/frost-mobile/templates/head.tpl @@ -4,6 +4,8 @@ + + diff --git a/view/theme/frost-mobile/templates/jot.tpl b/view/theme/frost-mobile/templates/jot.tpl index a8bf4ea79..eb326b1da 100644 --- a/view/theme/frost-mobile/templates/jot.tpl +++ b/view/theme/frost-mobile/templates/jot.tpl @@ -18,6 +18,9 @@
+ {{if $notes_cid}} + + {{/if}} {{if $placeholdercategory}}
{{/if}} @@ -74,10 +77,6 @@
{{$acl}} -
-
{{$emailcc}}
- {{$jotnets}} -
diff --git a/view/theme/frost-mobile/templates/wall_thread.tpl b/view/theme/frost-mobile/templates/wall_thread.tpl index 3983ebca9..c96129195 100644 --- a/view/theme/frost-mobile/templates/wall_thread.tpl +++ b/view/theme/frost-mobile/templates/wall_thread.tpl @@ -42,7 +42,9 @@
{{**}} {{$item.name}}{{if $item.owner_url}} {{$item.to}} {{$item.owner_name}} {{$item.vwall}}{{/if}}
-
{{$item.ago}}
+
+ {{if $item.plink}}{{$item.ago}}{{else}} {{$item.ago}} {{/if}} +
{{**}}
{{$item.title}}
diff --git a/view/theme/frost/js/theme.js b/view/theme/frost/js/theme.js index 5d61d5ae8..a14a034bc 100644 --- a/view/theme/frost/js/theme.js +++ b/view/theme/frost/js/theme.js @@ -159,6 +159,11 @@ $(document).ready(function() { eventClick: function(calEvent, jsEvent, view) { showEvent(calEvent.id); }, + loading: function(isLoading, view) { + if(!isLoading) { + $('td.fc-day').dblclick(function() { window.location.href='/events/new?start='+$(this).data('date'); }); + } + }, eventRender: function(event, element, view) { //console.log(view.name); @@ -353,6 +358,14 @@ function showEvent(eventid) { ); } +function doEventPreview() { + $('#event-edit-preview').val(1); + $.post('events',$('#event-edit-form').serialize(), function(data) { + $.colorbox({ html: data }); + }); + $('#event-edit-preview').val(0); +} + function initCrop() { function onEndCrop( coords, dimensions ) { $PR( 'x1' ).value = coords.x1; diff --git a/view/theme/frost/style.css b/view/theme/frost/style.css index aed0dc0ab..8b87c3bd4 100644 --- a/view/theme/frost/style.css +++ b/view/theme/frost/style.css @@ -21,7 +21,7 @@ body { div.container { display: block; - width: 785 px; + width: 785px; margin-top: 0px; margin-bottom: 0px; margin-left: auto; @@ -111,6 +111,8 @@ blockquote { margin-right: 5px; } +.pull-right { float: right } + /* nav */ @@ -2327,11 +2329,11 @@ a.mail-list-link { margin-top: 10px; } -.nets-ul, .fileas-ul, .categories-ul { +.nets-ul, .fileas-ul, .categories-ul, .datebrowse-ul { list-style-type: none; } -.nets-ul li, .fileas-ul li, .categories-ul li { +.nets-ul li, .fileas-ul li, .categories-ul li, .datebrowse-ul li { margin-top: 10px; } @@ -4094,7 +4096,7 @@ ul.notifications-menu-popup { #recip { } -.autocomplete-w1 { background: #ffffff; no-repeat bottom right; position:absolute; top:0px; left:0px; margin:6px 0 0 6px; /* IE6 fix: */ _background:none; _margin:1px 0 0 0; } +.autocomplete-w1 { background: #ffffff no-repeat bottom right; position:absolute; top:0px; left:0px; margin:6px 0 0 6px; /* IE6 fix: */ _background:none; _margin:1px 0 0 0; } .autocomplete { color:#000; border:1px solid #999; background:#FFF; cursor:default; text-align:left; max-height:350px; overflow:auto; margin:-6px 6px 6px -6px; /* IE6 specific: */ _height:350px; _margin:0; _overflow-x:hidden; } .autocomplete .selected { background:#F0F0F0; } .autocomplete div { padding:2px 5px; white-space:nowrap; overflow:hidden; } diff --git a/view/theme/frost/templates/acl_selector.tpl b/view/theme/frost/templates/acl_selector.tpl index 7f459587a..fbec3af62 100644 --- a/view/theme/frost/templates/acl_selector.tpl +++ b/view/theme/frost/templates/acl_selector.tpl @@ -15,6 +15,14 @@ {{$hide}}
+{{if $networks}} +
+
{{$emailcc}}
+
+{{if $jotnets}} +{{$jotnets}} +{{/if}}{{/if}} + {{**}} + diff --git a/view/theme/frost/templates/event_form.tpl b/view/theme/frost/templates/event_form.tpl index f8e1d6560..43f2a782f 100644 --- a/view/theme/frost/templates/event_form.tpl +++ b/view/theme/frost/templates/event_form.tpl @@ -5,17 +5,18 @@ {{$desc}}

-
+ +
{{$s_text}}
-{{$s_dsel}} {{$s_tsel}} +{{$s_dsel}}
{{$f_text}}
-{{$f_dsel}} {{$f_tsel}} +{{$f_dsel}}
@@ -45,6 +46,7 @@ {{$acl}}
+
diff --git a/view/theme/frost/templates/head.tpl b/view/theme/frost/templates/head.tpl index 93f48f092..fc56c24b7 100644 --- a/view/theme/frost/templates/head.tpl +++ b/view/theme/frost/templates/head.tpl @@ -5,6 +5,7 @@ {{**}} + diff --git a/view/theme/frost/templates/jot.tpl b/view/theme/frost/templates/jot.tpl index 2ec160231..08627b04d 100644 --- a/view/theme/frost/templates/jot.tpl +++ b/view/theme/frost/templates/jot.tpl @@ -16,6 +16,9 @@ + {{if $notes_cid}} + + {{/if}}
{{if $placeholdercategory}}
@@ -76,10 +79,6 @@
{{$acl}} -
-
{{$emailcc}}
-
- {{$jotnets}}
diff --git a/view/theme/quattro/dark/style.css b/view/theme/quattro/dark/style.css index 5f4b005c8..89f3649ab 100644 --- a/view/theme/quattro/dark/style.css +++ b/view/theme/quattro/dark/style.css @@ -43,6 +43,9 @@ .icon.edit { background-image: url("../../../images/icons/22/edit.png"); } +.icon.pencil { + background-image: url("../../../images/icons/22/edit.png"); +} .icon.star { background-image: url("../../../images/icons/22/star.png"); } @@ -52,6 +55,9 @@ .icon.link { background-image: url("../../../images/icons/22/link.png"); } +.icon.remote-link { + background-image: url("../../../images/icons/22/link.png"); +} .icon.lock { background-image: url("../../../images/icons/22/lock.png"); } @@ -116,6 +122,9 @@ .icon.s10.edit { background-image: url("../../../images/icons/10/edit.png"); } +.icon.s10.pencil { + background-image: url("../../../images/icons/10/edit.png"); +} .icon.s10.star { background-image: url("../../../images/icons/10/star.png"); } @@ -125,6 +134,9 @@ .icon.s10.link { background-image: url("../../../images/icons/10/link.png"); } +.icon.s10.remote-link { + background-image: url("../../../images/icons/10/link.png"); +} .icon.s10.lock { background-image: url("../../../images/icons/10/lock.png"); } @@ -189,6 +201,9 @@ .icon.s16.edit { background-image: url("../../../images/icons/16/edit.png"); } +.icon.s16.pencil { + background-image: url("../../../images/icons/16/edit.png"); +} .icon.s16.star { background-image: url("../../../images/icons/16/star.png"); } @@ -198,6 +213,9 @@ .icon.s16.link { background-image: url("../../../images/icons/16/link.png"); } +.icon.s16.remote-link { + background-image: url("../../../images/icons/16/link.png"); +} .icon.s16.lock { background-image: url("../../../images/icons/16/lock.png"); } @@ -262,6 +280,9 @@ .icon.s22.edit { background-image: url("../../../images/icons/22/edit.png"); } +.icon.s22.pencil { + background-image: url("../../../images/icons/22/edit.png"); +} .icon.s22.star { background-image: url("../../../images/icons/22/star.png"); } @@ -271,6 +292,9 @@ .icon.s22.link { background-image: url("../../../images/icons/22/link.png"); } +.icon.s22.remote-link { + background-image: url("../../../images/icons/22/link.png"); +} .icon.s22.lock { background-image: url("../../../images/icons/22/lock.png"); } @@ -335,6 +359,9 @@ .icon.s48.edit { background-image: url("../../../images/icons/48/edit.png"); } +.icon.s48.pencil { + background-image: url("../../../images/icons/48/edit.png"); +} .icon.s48.star { background-image: url("../../../images/icons/48/star.png"); } @@ -344,6 +371,9 @@ .icon.s48.link { background-image: url("../../../images/icons/48/link.png"); } +.icon.s48.remote-link { + background-image: url("../../../images/icons/48/link.png"); +} .icon.s48.lock { background-image: url("../../../images/icons/48/lock.png"); } @@ -840,6 +870,9 @@ aside #dfrn-request-link:hover { aside #profiles-menu { width: 20em; } +aside .posted-date-selector-months { + margin-left: 10px; +} #contact-block { overflow: auto; height: auto; @@ -1284,15 +1317,12 @@ section { }*/ .wwto { position: absolute !important; - width: 25px; - height: 25px; background: #FFFFFF; border: 2px solid #364e59; height: 25px; width: 25px; overflow: hidden; padding: 1px; - position: absolute !important; top: 40px; left: 30px; -webkit-box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.7); @@ -1969,6 +1999,27 @@ ul.tabs li .active { width: 50px; float: left; } +/* videos page */ +.videos .video-top-wrapper { + width: 200px; + float: left; + margin: 0px 10px 10px 0px; + position: relative; +} +.videos .video-top-wrapper .video-js { + width: 200px!important; + height: 132px!important; +} +.videos .video-top-wrapper .video-delete { + position: absolute; + opacity: 0; + right: 0px; + top: 0px; + transition: opacity 0.5s; +} +.videos .video-top-wrapper:hover .video-delete { + opacity: 1; +} /* photo albums */ #photo-edit-link-wrap { margin-bottom: 10px; @@ -2060,6 +2111,7 @@ ul.tabs li .active { min-height: 22px; padding-top: 6px; /* a { display: block;}*/ + } #photo-caption { display: block; @@ -2093,6 +2145,9 @@ ul.tabs li .active { #photo-like-div .like-rotator { float: right; } +#photo_edit_form { + padding: 1em; +} /* profile match wrapper */ .profile-match-wrapper { float: left; @@ -2425,3 +2480,70 @@ footer { border: 0px; color: #999999; } +/* buttons for the event view */ +.plink-event-link { + float: left; + margin-left: 2px; +} +/* upload/select popup */ +.fbrowser { + overflow: auto; + position: absolute; + top: 0px; + width: 100%; + height: 100%; +} +.fbrowser .path { + background-color: #0e232e; +} +.fbrowser .path a { + padding: 5px; + margin: 0px 2px; + display: inline-block; +} +.fbrowser .path a, +.fbrowser .path a:active, +.fbrowser .path a:visited, +.fbrowser .path a:link, +.fbrowser .path a:hover { + color: #ffffff; + text-decoration: none; + outline: none; +} +.fbrowser .folders ul { + list-style: url("icons/folder.png"); + padding-left: 22px; +} +.fbrowser .list { + padding: 10px; +} +.fbrowser.image .photo-album-image-wrapper { + width: 48px; + height: 48px; +} +.fbrowser.image a img { + width: auto; + height: 48px; +} +.fbrowser.image a p { + display: none; +} +.fbrowser.file .photo-album-image-wrapper { + float: none; + white-space: nowrap; + width: 100%; + height: auto; +} +.fbrowser.file img { + display: inline; + width: 16px; + height: 16px; +} +.fbrowser.file p { + display: inline; + white-space: nowrap; +} +.fbrowser .upload { + clear: both; + padding-top: 1em; +} diff --git a/view/theme/quattro/green/style.css b/view/theme/quattro/green/style.css index c2826a3c0..e49a0b16c 100644 --- a/view/theme/quattro/green/style.css +++ b/view/theme/quattro/green/style.css @@ -43,6 +43,9 @@ .icon.edit { background-image: url("../../../images/icons/22/edit.png"); } +.icon.pencil { + background-image: url("../../../images/icons/22/edit.png"); +} .icon.star { background-image: url("../../../images/icons/22/star.png"); } @@ -52,6 +55,9 @@ .icon.link { background-image: url("../../../images/icons/22/link.png"); } +.icon.remote-link { + background-image: url("../../../images/icons/22/link.png"); +} .icon.lock { background-image: url("../../../images/icons/22/lock.png"); } @@ -116,6 +122,9 @@ .icon.s10.edit { background-image: url("../../../images/icons/10/edit.png"); } +.icon.s10.pencil { + background-image: url("../../../images/icons/10/edit.png"); +} .icon.s10.star { background-image: url("../../../images/icons/10/star.png"); } @@ -125,6 +134,9 @@ .icon.s10.link { background-image: url("../../../images/icons/10/link.png"); } +.icon.s10.remote-link { + background-image: url("../../../images/icons/10/link.png"); +} .icon.s10.lock { background-image: url("../../../images/icons/10/lock.png"); } @@ -189,6 +201,9 @@ .icon.s16.edit { background-image: url("../../../images/icons/16/edit.png"); } +.icon.s16.pencil { + background-image: url("../../../images/icons/16/edit.png"); +} .icon.s16.star { background-image: url("../../../images/icons/16/star.png"); } @@ -198,6 +213,9 @@ .icon.s16.link { background-image: url("../../../images/icons/16/link.png"); } +.icon.s16.remote-link { + background-image: url("../../../images/icons/16/link.png"); +} .icon.s16.lock { background-image: url("../../../images/icons/16/lock.png"); } @@ -262,6 +280,9 @@ .icon.s22.edit { background-image: url("../../../images/icons/22/edit.png"); } +.icon.s22.pencil { + background-image: url("../../../images/icons/22/edit.png"); +} .icon.s22.star { background-image: url("../../../images/icons/22/star.png"); } @@ -271,6 +292,9 @@ .icon.s22.link { background-image: url("../../../images/icons/22/link.png"); } +.icon.s22.remote-link { + background-image: url("../../../images/icons/22/link.png"); +} .icon.s22.lock { background-image: url("../../../images/icons/22/lock.png"); } @@ -335,6 +359,9 @@ .icon.s48.edit { background-image: url("../../../images/icons/48/edit.png"); } +.icon.s48.pencil { + background-image: url("../../../images/icons/48/edit.png"); +} .icon.s48.star { background-image: url("../../../images/icons/48/star.png"); } @@ -344,6 +371,9 @@ .icon.s48.link { background-image: url("../../../images/icons/48/link.png"); } +.icon.s48.remote-link { + background-image: url("../../../images/icons/48/link.png"); +} .icon.s48.lock { background-image: url("../../../images/icons/48/lock.png"); } @@ -840,6 +870,9 @@ aside #dfrn-request-link:hover { aside #profiles-menu { width: 20em; } +aside .posted-date-selector-months { + margin-left: 10px; +} #contact-block { overflow: auto; height: auto; @@ -1284,15 +1317,12 @@ section { }*/ .wwto { position: absolute !important; - width: 25px; - height: 25px; background: #FFFFFF; border: 2px solid #364e59; height: 25px; width: 25px; overflow: hidden; padding: 1px; - position: absolute !important; top: 40px; left: 30px; -webkit-box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.7); @@ -1969,6 +1999,27 @@ ul.tabs li .active { width: 50px; float: left; } +/* videos page */ +.videos .video-top-wrapper { + width: 200px; + float: left; + margin: 0px 10px 10px 0px; + position: relative; +} +.videos .video-top-wrapper .video-js { + width: 200px!important; + height: 132px!important; +} +.videos .video-top-wrapper .video-delete { + position: absolute; + opacity: 0; + right: 0px; + top: 0px; + transition: opacity 0.5s; +} +.videos .video-top-wrapper:hover .video-delete { + opacity: 1; +} /* photo albums */ #photo-edit-link-wrap { margin-bottom: 10px; @@ -2060,6 +2111,7 @@ ul.tabs li .active { min-height: 22px; padding-top: 6px; /* a { display: block;}*/ + } #photo-caption { display: block; @@ -2093,6 +2145,9 @@ ul.tabs li .active { #photo-like-div .like-rotator { float: right; } +#photo_edit_form { + padding: 1em; +} /* profile match wrapper */ .profile-match-wrapper { float: left; @@ -2425,3 +2480,70 @@ footer { border: 0px; color: #999999; } +/* buttons for the event view */ +.plink-event-link { + float: left; + margin-left: 2px; +} +/* upload/select popup */ +.fbrowser { + overflow: auto; + position: absolute; + top: 0px; + width: 100%; + height: 100%; +} +.fbrowser .path { + background-color: #009100; +} +.fbrowser .path a { + padding: 5px; + margin: 0px 2px; + display: inline-block; +} +.fbrowser .path a, +.fbrowser .path a:active, +.fbrowser .path a:visited, +.fbrowser .path a:link, +.fbrowser .path a:hover { + color: #ffffff; + text-decoration: none; + outline: none; +} +.fbrowser .folders ul { + list-style: url("icons/folder.png"); + padding-left: 22px; +} +.fbrowser .list { + padding: 10px; +} +.fbrowser.image .photo-album-image-wrapper { + width: 48px; + height: 48px; +} +.fbrowser.image a img { + width: auto; + height: 48px; +} +.fbrowser.image a p { + display: none; +} +.fbrowser.file .photo-album-image-wrapper { + float: none; + white-space: nowrap; + width: 100%; + height: auto; +} +.fbrowser.file img { + display: inline; + width: 16px; + height: 16px; +} +.fbrowser.file p { + display: inline; + white-space: nowrap; +} +.fbrowser .upload { + clear: both; + padding-top: 1em; +} diff --git a/view/theme/quattro/icons.less b/view/theme/quattro/icons.less index 830e64782..af8f4ef9f 100644 --- a/view/theme/quattro/icons.less +++ b/view/theme/quattro/icons.less @@ -7,15 +7,17 @@ &.mail { background-image: url("icons/messages_off.png"); } &.gear { background-image: url("../../../images/icons/@{size}/gear.png"); } - &.like { background-image: url("icons/like.png"); } + &.like { background-image: url("icons/like.png"); } &.dislike { background-image: url("icons/dislike.png"); } &.add { background-image: url("../../../images/icons/@{size}/add.png"); } &.delete { background-image: url("../../../images/icons/@{size}/delete.png"); } &.edit { background-image: url("../../../images/icons/@{size}/edit.png"); } + &.pencil { background-image: url("../../../images/icons/@{size}/edit.png"); } &.star { background-image: url("../../../images/icons/@{size}/star.png"); } &.menu { background-image: url("../../../images/icons/@{size}/menu.png"); } &.link { background-image: url("../../../images/icons/@{size}/link.png"); } + &.remote-link { background-image: url("../../../images/icons/@{size}/link.png"); } &.lock { background-image: url("../../../images/icons/@{size}/lock.png"); } &.unlock { background-image: url("../../../images/icons/@{size}/unlock.png"); } &.plugin { background-image: url("../../../images/icons/@{size}/plugin.png"); } diff --git a/view/theme/quattro/lilac/style.css b/view/theme/quattro/lilac/style.css index 28c5cd47a..5ea6b40c3 100644 --- a/view/theme/quattro/lilac/style.css +++ b/view/theme/quattro/lilac/style.css @@ -43,6 +43,9 @@ .icon.edit { background-image: url("../../../images/icons/22/edit.png"); } +.icon.pencil { + background-image: url("../../../images/icons/22/edit.png"); +} .icon.star { background-image: url("../../../images/icons/22/star.png"); } @@ -52,6 +55,9 @@ .icon.link { background-image: url("../../../images/icons/22/link.png"); } +.icon.remote-link { + background-image: url("../../../images/icons/22/link.png"); +} .icon.lock { background-image: url("../../../images/icons/22/lock.png"); } @@ -116,6 +122,9 @@ .icon.s10.edit { background-image: url("../../../images/icons/10/edit.png"); } +.icon.s10.pencil { + background-image: url("../../../images/icons/10/edit.png"); +} .icon.s10.star { background-image: url("../../../images/icons/10/star.png"); } @@ -125,6 +134,9 @@ .icon.s10.link { background-image: url("../../../images/icons/10/link.png"); } +.icon.s10.remote-link { + background-image: url("../../../images/icons/10/link.png"); +} .icon.s10.lock { background-image: url("../../../images/icons/10/lock.png"); } @@ -189,6 +201,9 @@ .icon.s16.edit { background-image: url("../../../images/icons/16/edit.png"); } +.icon.s16.pencil { + background-image: url("../../../images/icons/16/edit.png"); +} .icon.s16.star { background-image: url("../../../images/icons/16/star.png"); } @@ -198,6 +213,9 @@ .icon.s16.link { background-image: url("../../../images/icons/16/link.png"); } +.icon.s16.remote-link { + background-image: url("../../../images/icons/16/link.png"); +} .icon.s16.lock { background-image: url("../../../images/icons/16/lock.png"); } @@ -262,6 +280,9 @@ .icon.s22.edit { background-image: url("../../../images/icons/22/edit.png"); } +.icon.s22.pencil { + background-image: url("../../../images/icons/22/edit.png"); +} .icon.s22.star { background-image: url("../../../images/icons/22/star.png"); } @@ -271,6 +292,9 @@ .icon.s22.link { background-image: url("../../../images/icons/22/link.png"); } +.icon.s22.remote-link { + background-image: url("../../../images/icons/22/link.png"); +} .icon.s22.lock { background-image: url("../../../images/icons/22/lock.png"); } @@ -335,6 +359,9 @@ .icon.s48.edit { background-image: url("../../../images/icons/48/edit.png"); } +.icon.s48.pencil { + background-image: url("../../../images/icons/48/edit.png"); +} .icon.s48.star { background-image: url("../../../images/icons/48/star.png"); } @@ -344,6 +371,9 @@ .icon.s48.link { background-image: url("../../../images/icons/48/link.png"); } +.icon.s48.remote-link { + background-image: url("../../../images/icons/48/link.png"); +} .icon.s48.lock { background-image: url("../../../images/icons/48/lock.png"); } @@ -840,6 +870,9 @@ aside #dfrn-request-link:hover { aside #profiles-menu { width: 20em; } +aside .posted-date-selector-months { + margin-left: 10px; +} #contact-block { overflow: auto; height: auto; @@ -1284,15 +1317,12 @@ section { }*/ .wwto { position: absolute !important; - width: 25px; - height: 25px; background: #FFFFFF; border: 2px solid #364e59; height: 25px; width: 25px; overflow: hidden; padding: 1px; - position: absolute !important; top: 40px; left: 30px; -webkit-box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.7); @@ -1969,6 +1999,27 @@ ul.tabs li .active { width: 50px; float: left; } +/* videos page */ +.videos .video-top-wrapper { + width: 200px; + float: left; + margin: 0px 10px 10px 0px; + position: relative; +} +.videos .video-top-wrapper .video-js { + width: 200px!important; + height: 132px!important; +} +.videos .video-top-wrapper .video-delete { + position: absolute; + opacity: 0; + right: 0px; + top: 0px; + transition: opacity 0.5s; +} +.videos .video-top-wrapper:hover .video-delete { + opacity: 1; +} /* photo albums */ #photo-edit-link-wrap { margin-bottom: 10px; @@ -2060,6 +2111,7 @@ ul.tabs li .active { min-height: 22px; padding-top: 6px; /* a { display: block;}*/ + } #photo-caption { display: block; @@ -2093,6 +2145,9 @@ ul.tabs li .active { #photo-like-div .like-rotator { float: right; } +#photo_edit_form { + padding: 1em; +} /* profile match wrapper */ .profile-match-wrapper { float: left; @@ -2425,3 +2480,70 @@ footer { border: 0px; color: #999999; } +/* buttons for the event view */ +.plink-event-link { + float: left; + margin-left: 2px; +} +/* upload/select popup */ +.fbrowser { + overflow: auto; + position: absolute; + top: 0px; + width: 100%; + height: 100%; +} +.fbrowser .path { + background-color: #521f5c; +} +.fbrowser .path a { + padding: 5px; + margin: 0px 2px; + display: inline-block; +} +.fbrowser .path a, +.fbrowser .path a:active, +.fbrowser .path a:visited, +.fbrowser .path a:link, +.fbrowser .path a:hover { + color: #ffffff; + text-decoration: none; + outline: none; +} +.fbrowser .folders ul { + list-style: url("icons/folder.png"); + padding-left: 22px; +} +.fbrowser .list { + padding: 10px; +} +.fbrowser.image .photo-album-image-wrapper { + width: 48px; + height: 48px; +} +.fbrowser.image a img { + width: auto; + height: 48px; +} +.fbrowser.image a p { + display: none; +} +.fbrowser.file .photo-album-image-wrapper { + float: none; + white-space: nowrap; + width: 100%; + height: auto; +} +.fbrowser.file img { + display: inline; + width: 16px; + height: 16px; +} +.fbrowser.file p { + display: inline; + white-space: nowrap; +} +.fbrowser .upload { + clear: both; + padding-top: 1em; +} diff --git a/view/theme/quattro/quattro.less b/view/theme/quattro/quattro.less index 172fad31b..368e26c3a 100644 --- a/view/theme/quattro/quattro.less +++ b/view/theme/quattro/quattro.less @@ -12,9 +12,9 @@ body { h4 { font-size: 1.1em } .shadow(@x: 0px, @y: 5px){ - -webkit-box-shadow:@x @y 10px rgba(0, 0, 0, 0.7); - -moz-box-shadow:@x @y 10px rgba(0, 0, 0, 0.7); - box-shadow:@x @y 10px rgba(0, 0, 0, 0.7); + -webkit-box-shadow:@x @y 10px rgba(0, 0, 0, 0.7); + -moz-box-shadow:@x @y 10px rgba(0, 0, 0, 0.7); + box-shadow:@x @y 10px rgba(0, 0, 0, 0.7); } .rounded(@tr: 5px, @tl: 5px, @bl: 5px, @br: 5px){ @@ -36,7 +36,7 @@ h4 { font-size: 1.1em } -o-transition: all @d ease-in-out; -ms-transition: all @d ease-in-out; transition: all @d ease-in-out; -} +} a, a:link { color: @Link; text-decoration: none; } @@ -52,11 +52,11 @@ a:hover {color: @LinkHover; text-decoration: underline; } .fakelink:hover { color: @LinkHover; text-decoration: underline; } blockquote { - background:@BlockquoteBackgroundColor; - padding: 1em; - margin-left: 1em; - border-left: 1em solid @BlockquoteBorderColor; - + background:@BlockquoteBackgroundColor; + padding: 1em; + margin-left: 1em; + border-left: 1em solid @BlockquoteBorderColor; + } code { @@ -68,7 +68,7 @@ code { background: #EEE; color: #444; padding: 10px; - margin-top: 20px; + margin-top: 20px; } #panel { @@ -81,8 +81,8 @@ code { list-style: none; border: 3px solid @MenuBorder; z-index: 100000; - - .shadow(); + + .shadow(); } @@ -134,7 +134,7 @@ header { #site-location { display: none; } - + #banner { overflow: hidden; text-align: center; @@ -145,7 +145,7 @@ header { } } /* nav */ -nav { +nav { width: 100%; height: 32px; position: fixed; left: 0px; top: 0px; padding: 0px; @@ -159,13 +159,13 @@ nav { ul { margin: 0px; padding: 0px 20px; - li { - list-style: none; + li { + list-style: none; margin: 0px; padding: 0px; float: left; .menu-popup{ left: 0px; right: auto; } } - + } .nav-menu-icon { @@ -174,11 +174,11 @@ nav { padding: 5px; margin: 0px 10px; .roundtop(); - + &.selected { background-color: @NavbarSelectedBg; } - + img { width: 22px; height: 22px; } .nav-notify { top: 3px; } } @@ -193,7 +193,7 @@ nav { &.selected { border-bottom: 3px solid @NavbarSelectedBorder; } - + } .nav-notify { @@ -207,26 +207,26 @@ nav { right: -10px; min-width: 15px; text-align: right; - + &.show{ display: block; } } - - + + #nav-help-link, #nav-search-link, #nav-directory-link, #nav-apps-link, - #nav-site-linkmenu { + #nav-site-linkmenu { float: right; .menu-popup{ right: 0px; left: auto; } } - + #nav-notifications-linkmenu.on .icon.s22.notify, #nav-notifications-linkmenu.selected .icon.s22.notify { background-image: url("../../../images/icons/22/notify_on.png") } - #nav-introductions-link.on .icon.s22.intro, + #nav-introductions-link.on .icon.s22.intro, #nav-introductions-link.selected .icon.s22.intro { background-image: url("icons/contacts_on.png") } #nav-messages-link.on .icon.s22.mail, - #nav-messages-link.selected .icon.s22.mail { background-image: url("icons/messages_on.png") } + #nav-messages-link.selected .icon.s22.mail { background-image: url("icons/messages_on.png") } #nav-apps-link.selected { background-color: @NavbarSelectedBg; } } @@ -242,9 +242,9 @@ ul.menu-popup { list-style: none; border: 3px solid @MenuBorder; z-index: 100000; - + .shadow(); - + a { display: block; color: @MenuItem; padding: 5px 10px; text-decoration: none;} a:hover { background-color: @MenuItemHoverBg; } .menu-sep { border-top: 1px solid @MenuItemSeparator; } @@ -256,12 +256,12 @@ ul.menu-popup { color: @MenuEmpty; } .toolbar { - background-color: @MenuEmpty; + background-color: @MenuEmpty; height: auto; overflow: auto; a { float: right; } a:hover { background-color: @MenuBg; } } - + } /* autocomplete popup */ @@ -275,7 +275,7 @@ ul.menu-popup { z-index:100000; .shadow(); } -.autocomplete > div, +.autocomplete > div, .acpopupitem { color: @MenuItem; padding: 4px; clear:left; @@ -305,7 +305,7 @@ ul.menu-popup { /* aside 230px*/ -aside { +aside { display: table-cell; vertical-align: top; width: 200px; @@ -320,7 +320,7 @@ aside { dl { height: auto; overflow: auto; } dt {float: left; margin-left: 0px; width: 35%; text-align: right; color: @VCardLabelColor; } dd {float: left; margin-left: 4px; width: 60%;} - + } #profile-extra-links { @@ -337,7 +337,7 @@ aside { text-transform:uppercase; padding: 4px 2px 2px 35px; margin-top: 3px; - + &:hover { text-decoration: none; background-color: @AsideConnectHoverBg; } } #dfrn-request-link { @@ -348,13 +348,13 @@ aside { font-weight: bold; text-transform:uppercase; padding: 4px 2px 2px 35px; - + &:hover { text-decoration: none; background-color: @AsideConnectHoverBg; } } #profiles-menu { width: 20em; } - + .posted-date-selector-months { margin-left: 10px; } } #contact-block { @@ -420,11 +420,11 @@ aside { width: 60px; height: 60px; }*/ - + /* widget */ .widget { margin-bottom: 2em; - + h3 { padding: 0px; margin: 2px;} .action { .opaque(0.1); } input.action { .opaque(0.5); } @@ -434,14 +434,14 @@ aside { ul { padding: 0px;} ul li {padding-left: 16px; min-height: 16px; list-style: none; } - + .tool.selected { background: url('../../../images/selected.png') no-repeat left center; } - + /*.action .s10 { width: 10px; overflow: hidden; padding: 0px;} .action .s16 { width: 16px; overflow: hidden; padding: 0px;}*/ - + } /* widget: search */ @@ -452,7 +452,7 @@ aside { /* section 800px */ -section { +section { display: table-cell; vertical-align: top; width: 770px; @@ -464,7 +464,7 @@ section { } /* wall item */ -.tread-wrapper { +.tread-wrapper { background-color: @ThreadBackgroundColor; position: relative; padding: 10px; @@ -478,10 +478,10 @@ section { .wall-item-container { display: table; width: 750px; - + .wall-item-item, .wall-item-bottom { display: table-row; } - + .wall-item-bottom { .opaque(0.5); } &:hover .wall-item-bottom { .opaque(1); } .wall-item-info { @@ -489,8 +489,8 @@ section { vertical-align: top; text-align: left; width: 60px; - - } + + } .wall-item-location { word-wrap: break-word; width: 50px; @@ -503,45 +503,45 @@ section { } .wall-item-content img { max-width: 700px; } .wall-item-links, - .wall-item-actions { - display: table-cell; - vertical-align: middle; - + .wall-item-actions { + display: table-cell; + vertical-align: middle; + .icon { .opaque(0.5); } .icon:hover { .opaque(1.0); - } + } } - + .wall-item-ago { padding-right: 40px; } .wall-item-name { font-weight: bold; } - + .wall-item-actions-author { float: left; width: 20em; margin-top: 0.5em; } .wall-item-actions-social { float: left; margin-top: 0.5em; a { margin-right: 3em; } } - .wall-item-actions-tools { float: right; width: 15%; + .wall-item-actions-tools { float: right; width: 15%; a { float: right; } input { float: right; } } - + } .wall-item-container.comment { .contact-photo-wrapper { margin-left: 16px; } - .contact-photo { - width: 32px; height: 32px; + .contact-photo { + width: 32px; height: 32px; } - + .contact-photo-menu-button { top: 15px !important; left: 0px !important; } .wall-item-links { padding-left: 12px; } - + .commentbox { height: 0px; overflow: hidden; @@ -551,7 +551,7 @@ section { } .transition(); } - + &:hover .commentbox { height:auto; overflow: visible; .wall-item-comment-wrapper { @@ -568,9 +568,9 @@ section { .opaque(0.5); } .contact-photo-wrapper { margin-left: 32px; } - .contact-photo { + .contact-photo { width: 16px; height: 16px; - } + } .contact-photo-menu-button { top: 15px !important; left: 15px !important; @@ -581,8 +581,8 @@ section { .wall-item-comment-wrapper { margin: 1em 2em 1em 60px; .comment-edit-photo { display: none; } - - textarea { + + textarea { height: 1em; width: 100%; font-size: 10px; color: @CommentBoxEmptyColor; border: 1px solid @CommentBoxEmptyBorderColor; @@ -593,7 +593,7 @@ section { color: @CommentBoxFullColor; border: 1px solid @CommentBoxFullBorderColor; } - + &.photo { margin: 1em 2em 1em 0px; } @@ -606,10 +606,10 @@ section { border: 1px solid @Grey5; margin-top: 10px; background-color: @JotPreviewBackgroundColor; - + .contact-photo { width: 32px; height: 32px; margin-left: 16px; /*background: url(../../../images/icons/22/user.png) no-repeat center center;*/ - } + } .contact-photo-menu-button { top: 15px !important; left: 15px !important; @@ -617,8 +617,8 @@ section { .wall-item-links { padding-left: 12px; } .wall-item-container { width: 90%; } - .tread-wrapper { - width: 90%; padding: 0; margin: 10px 0; + .tread-wrapper { + width: 90%; padding: 0; margin: 10px 0; background-color: @JotPreviewBackgroundColor; border-bottom: 0px; } @@ -664,78 +664,76 @@ section { opacity: 0.5; }*/ -.wwto { - position: absolute !important; - width: 25px; height: 25px; - background: #FFFFFF; - border: 2px solid @Metalic3; - height: 25px; - width: 25px; - overflow: hidden; - padding: 1px; - position: absolute !important; - top: 40px; - left: 30px; - - .shadow(0px, 0px) +.wwto { + position: absolute !important; + background: #FFFFFF; + border: 2px solid @Metalic3; + height: 25px; + width: 25px; + overflow: hidden; + padding: 1px; + top: 40px; + left: 30px; + + .shadow(0px, 0px); } .wwto .contact-photo { width: 25px; height: 25px; } /* reshare e embed */ .wall-item-container .wall-item-content .type-link img, .type-link img { - max-width: 160px; - max-height: 160px; - float: left; - margin-right: 10px; + max-width: 160px; + max-height: 160px; + float: left; + margin-right: 10px; + + &.attachment-image { + max-width: 650px; + max-height: inital; + float: none; + margin-right: 0px; + } - &.attachment-image { - max-width: 650px; - max-height: inital; - float: none; - margin-right: 0px; - } - } .type-link { - blockquote { - margin: 1em 0px; - max-height: 160px; - overflow: hidden; - padding-left: 1em; - } - .oembed {} + blockquote { + margin: 1em 0px; + max-height: 160px; + overflow: hidden; + padding-left: 1em; + } + .oembed {} } .type-video blockquote { padding-left: 1em; } .shared_header { - height: 32px; - color: #999; - border-top: 1px solid @ThreadBottomBorderColor; - padding-top: 5px; - margin-top: 5px; - - img { - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - float: left; - } - - span { margin-left: 9px; } + height: 32px; + color: #999; + border-top: 1px solid @ThreadBottomBorderColor; + padding-top: 5px; + margin-top: 5px; + + img { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + float: left; + } + + span { margin-left: 9px; } } blockquote.shared_content { - margin-left: 32px; - color: #000; - border: none; + margin-left: 32px; + color: #000; + border: none; } .oembed.video { - > a.embed_video { + > a.embed_video { display: block; float: none; > div { @@ -753,66 +751,66 @@ blockquote.shared_content { .children { margin-top: 1em; .hide-comments-outer { margin-left:60px; } - + .wwto { display: none; } - + .comment-edit-preview { width: 660px; .wall-item-container { width: 610px; } } - + & .children { - + margin-left: 40px; .wall-item-container { width: 710px; } .comment-edit-preview { width: 620px; .wall-item-container { width: 620px; } } - + & .children { .wall-item-container { width: 670px; } - .comment-edit-preview { width: 580px; + .comment-edit-preview { width: 580px; .wall-item-container { width: 580px; } } - + & .children { .wall-item-container { width: 630px; } - .comment-edit-preview { width: 540px; + .comment-edit-preview { width: 540px; .wall-item-container { width: 540px; } } & .children { .wall-item-container { width: 590px; } - .comment-edit-preview { width: 500px; + .comment-edit-preview { width: 500px; .wall-item-container { width: 500px; } } - + .children { margin-left: 0px; .hide-comments-outer { margin-left: 0px; } } } - } + } } } } /*.threaded .hide-comments-outer { margin-left: 20px; }*/ span[id^="showmore-teaser"]{ - background: url("showmore-bg.jpg") no-repeat center bottom; + background: url("showmore-bg.jpg") no-repeat center bottom; } span[id^="showmore-wrap"] { - border-top: 1px solid #999999; - color: #999999; - display: block; - text-align: center; - background-color: @ThreadBackgroundColor; + border-top: 1px solid #999999; + color: #999999; + display: block; + text-align: center; + background-color: @ThreadBackgroundColor; } #pause { - position: fixed; - bottom: 5px; - right: 5px; + position: fixed; + bottom: 5px; + right: 5px; } @@ -820,8 +818,8 @@ span[id^="showmore-wrap"] { .contact-select { position: absolute; top:64px; left:64px; display:none; } .contact-select:checked, .contact-photo:hover .contact-select { display:block; } -#contacts-actions { - position: absolute; +#contacts-actions { + position: absolute; left: 800px; width: 200px; background-color: @MenuBg; @@ -845,12 +843,12 @@ span[id^="showmore-wrap"] { #contacts-actions { left: 40px; } } -.contact-photo { +.contact-photo { width: 48px; height: 48px; img { width: 48px; height: 48px; } overflow: hidden; display: block; - } + } .contact-photo-menu-button { display: none; position: absolute; @@ -864,30 +862,30 @@ span[id^="showmore-wrap"] { height: 90px; padding-right: 10px; margin: 0 10px 10px 0px; - .contact-photo-wrapper { - float: left; + .contact-photo-wrapper { + float: left; margin-right: 10px; } - .contact-photo { + .contact-photo { width: 80px; height: 80px; img { width: 80px; height: 80px; } } .contact-photo-menu-button { left: 0px; top: 63px; - } + } } .directory-item { float: left; width: 200px; height: 200px; - .contact-photo { + .contact-photo { width: 175px; height: 175px; img { width: 175px; height: 175px; } } } .contact-name { font-weight: bold; padding-top: 15px; } -.contact-details { +.contact-details { color: @Grey3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -899,8 +897,7 @@ span[id^="showmore-wrap"] { width: 100%; margin: 0px 2em 20px 0px; - - + .profile-jot-text { height: 1em; width: 99%; font-size: 10px; color: @CommentBoxEmptyColor; @@ -947,7 +944,7 @@ span[id^="showmore-wrap"] { border-left: 10px solid @JotPermissionLockBackgroundColor; background-color: @JotPermissionLockBackgroundColor; } - + } li.submit { float: right; @@ -993,12 +990,12 @@ span[id^="showmore-wrap"] { &:-moz-placeholder { font-weight: normal; - } - + } + &:hover { border: 1px solid @CommentBoxEmptyBorderColor } &:focus { border: 1px solid @CommentBoxEmptyBorderColor } } - + #character-counter { width: 40px; float: right; @@ -1007,7 +1004,7 @@ span[id^="showmore-wrap"] { line-height: 20px; padding-right: 20px; } - + #jot-category { border: 0px; margin: 0px; @@ -1076,7 +1073,7 @@ span[id^="showmore-wrap"] { overflow: auto; } #acl-list-content { - + } .acl-list-item { display: block; @@ -1093,7 +1090,7 @@ span[id^="showmore-wrap"] { margin: 4px; } .acl-list-item p { height: 12px; font-size: 10px; margin: 0px; padding: 2px 0px 1px; overflow: hidden;} -.acl-list-item a { +.acl-list-item a { font-size: 8px; display: block; width: 40px; @@ -1138,12 +1135,12 @@ ul.tabs { li { float: left; margin-left: 20px; - + .active { border-bottom: 1px solid @LinkVisited; } } - + } @@ -1152,7 +1149,7 @@ ul.tabs { #group-update-wrapper{ height: auto; overflow: auto; #group { - width:300px; + width:300px; float:left; margin-right:20px; } @@ -1194,13 +1191,12 @@ ul.tabs { input[type="checkbox"], input[type="radio"]{ width: auto; } - + textarea { height: 100px; } .field_help { display: block; margin-left: 200px; color: @FieldHelpColor; - } @@ -1275,7 +1271,7 @@ ul.tabs { } #profile-edit-profile-name, #profile-edit-name, -#gender-select, +#gender-select, #profile-edit-pdesc, #profile-edit-gender, #profile-edit-dob, @@ -1292,7 +1288,7 @@ ul.tabs { #profile-edit-pubkeywords, #profile-edit-prvkeywords, #profile-edit-homepage { - margin-top: 5px; + margin-top: 5px; } /* oauth */ @@ -1300,7 +1296,7 @@ ul.tabs { height: auto; overflow: auto; border-bottom: 2px solid #cccccc; padding-bottom: 1em; - margin-bottom: 1em; + margin-bottom: 1em; } .oauthapp img { float: left; @@ -1321,11 +1317,37 @@ ul.tabs { width: 50px; float: left; } +/* videos page */ +.videos { + .video-top-wrapper { + width: 200px; float: left; + margin: 0px 10px 10px 0px; + position: relative; + + .video-js { + width: 200px!important; + height: 132px!important; + } + + .video-delete { + position: absolute; + opacity: 0; + right: 0px; + top: 0px; + transition: opacity 0.5s; + } + + &:hover .video-delete { + opacity: 1; + } + } +} + /* photo albums */ @photosize: 150px; #photo-edit-link-wrap { margin-bottom: 10px; } - + #album-edit-link { border-right: 1px solid @MenuBorder; float: left; @@ -1358,21 +1380,21 @@ ul.tabs { width:@photosize; height: @photosize; position: relative; overflow: hidden; - + img { width: @photosize; } - + .photo-top-album-name, .caption{ position: absolute; color: @Menu; background-color: @MenuBg; - + width: 100%; .shadow(0px, 5px); .transition(0.5s); bottom: -@photosize; } - + &:hover .photo-top-album-name, &:hover .caption { bottom: 0px; @@ -1392,8 +1414,8 @@ ul.tabs { background: url("../../../images/icons/22/image.png") no-repeat top left; padding-left: 23px; min-height: 22px; - padding-top: 6px; - /* a { display: block;}*/ + padding-top: 6px; + /* a { display: block;}*/ } #photo-caption { @@ -1420,6 +1442,11 @@ ul.tabs { .icon {float: left;} .like-rotator {float: right;} } + +#photo_edit_form { + padding: 1em; +} + /* profile match wrapper */ .profile-match-wrapper { float: left; @@ -1441,14 +1468,14 @@ ul.tabs { top: 10px; left: -10px; } - .contact-photo { + .contact-photo { width: 80px; height: 80px; img { width: 80px; height: 80px; } } .contact-photo-menu-button { left: 0px; top: 63px; - } + } } /* messages */ @@ -1469,10 +1496,10 @@ ul.tabs { background-color: @MailListBackgroundColor; margin-bottom: 5px; width: 100%; height: auto; overflow: hidden; - + span { display: block; float: left; width: 20%; overflow: hidden;} - - .mail-subject { + + .mail-subject { width: 30%; padding:4px 0px 0px 4px; a { display: block; } @@ -1481,7 +1508,7 @@ ul.tabs { .mail-date { padding: 4px 4px 0px 4px; } .mail-from { padding: 4px 4px 0px 4px; } .mail-count { padding: 4px 4px 0px 4px; text-align: right;} - + .mail-delete { float: right; } } @@ -1493,7 +1520,7 @@ ul.tabs { span { float: left; overflow: hidden; padding: 4px 0px 0px 10px;} .mail-delete { float: right; .opaque(0.5);} &:hover .mail-delete { .opaque(1); } - + } /* theme screenshot */ @@ -1509,8 +1536,8 @@ ul.tabs { footer { height: 100px; display: table-row; } .pager { - margin-top: 25px; - clear: both; + margin-top: 25px; + clear: both; } /** @@ -1522,7 +1549,7 @@ footer { height: 100px; display: table-row; } font-weight: bold; background-color: #FF0000; padding: 0em 0.3em; - + } #adminpage { dl { @@ -1569,20 +1596,20 @@ footer { height: 100px; display: table-row; } } table { width:100%; - border-bottom: 1px solid #000000; + border-bottom: 1px solid #000000; margin: 5px 0px; - th { + th { text-align: left; } - td .icon { + td .icon { float: left; } - tr:hover { + tr:hover { background-color: #bbc7d7; } } - table#users img { - width: 16px; height: 16px; + table#users img { + width: 16px; height: 16px; } .selectall { text-align: right; } } @@ -1604,12 +1631,12 @@ footer { height: 100px; display: table-row; } } .editicon { - display: inline-block; - width: 16px; - height: 16px; - background-image: url(icons/bbedit.png); - text-decoration: none; - :hover {background-color: #ccc;} + display: inline-block; + width: 16px; + height: 16px; + background-image: url(icons/bbedit.png); + text-decoration: none; + :hover {background-color: #ccc;} } .boldbb { background-position: 0px 0px; } .boldbb:hover { background-position: 0px -16px; } @@ -1632,37 +1659,36 @@ footer { height: 100px; display: table-row; } /** range input css **/ /* slider root element */ .slider { - height:2px; - position:relative; - cursor:pointer; - border:1px solid #333; - width:200px; - margin:10px 0px 10px 0px; - float: left; + height:2px; + position:relative; + cursor:pointer; + border:1px solid #333; + width:200px; + margin:10px 0px 10px 0px; + float: left; } /* progress bar (enabled with progress: true) */ .progress { - height:9px; - background-color:#C5FF00; - display:none; - opacity:0.6; + height:9px; + background-color:#C5FF00; + display:none; + opacity:0.6; } /* drag handle */ .handle { - background-color:#ccc; - height:16px; - width:8px; - top:-8px; - position:absolute; - display:block; - margin-top:1px; - border:1px solid #000; - cursor:move; - .roundbottom(); - .shadow(); - + background-color:#ccc; + height:16px; + width:8px; + top:-8px; + position:absolute; + display:block; + margin-top:1px; + border:1px solid #000; + cursor:move; + .roundbottom(); + .shadow(); } /* the input field */ @@ -1674,3 +1700,33 @@ footer { height: 100px; display: table-row; } color: @FieldHelpColor; } +/* buttons for the event view */ +.plink-event-link { + float: left; + margin-left: 2px; +} + +/* upload/select popup */ +.fbrowser { + overflow: auto; + position: absolute; + top: 0px; + width: 100%; + height: 100%; +} +.fbrowser .path { + background-color: @NavbarBackground; + a { padding: 5px; margin: 0px 2px; display: inline-block; } + a, a:active, a:visited, a:link, a:hover { color: @Banner; text-decoration: none; outline: none; } +} +.fbrowser .folders ul { list-style: url("icons/folder.png"); padding-left: 22px;} +.fbrowser .list { padding: 10px; } +.fbrowser.image .photo-album-image-wrapper { width: 48px; height: 48px; } +.fbrowser.image a img { width: auto; height: 48px; } +.fbrowser.image a p { display: none;} +.fbrowser.file .photo-album-image-wrapper { float:none; white-space: nowrap; width: 100%; height: auto; } +.fbrowser.file img { display: inline; width: 16px; height: 16px} +.fbrowser.file p { display: inline; white-space: nowrap; } + +.fbrowser .upload { clear: both; padding-top: 1em;} + diff --git a/view/theme/quattro/style.php b/view/theme/quattro/style.php index c75091eb5..bf0b1e042 100644 --- a/view/theme/quattro/style.php +++ b/view/theme/quattro/style.php @@ -1,12 +1,14 @@ {{$u.lastitem_date}} {{$u.page_flags}} {{if $u.is_admin}}({{$siteadmin}}){{/if}} {{if $u.account_expired}}({{$accountexpired}}){{/if}} - {{if $u.is_admin}} -   - {{else}} - - {{/if}} + {{if $u.is_deletable}} + + {{else}} +   + {{/if}} - {{if $u.is_admin}} -   - {{else}} - - - {{/if}} + {{if $u.is_deletable}} + + + {{else}} +   + {{/if}} {{/foreach}} diff --git a/view/theme/quattro/templates/comment_item.tpl b/view/theme/quattro/templates/comment_item.tpl index d3d5c4632..20417c1e1 100644 --- a/view/theme/quattro/templates/comment_item.tpl +++ b/view/theme/quattro/templates/comment_item.tpl @@ -15,34 +15,33 @@ class="comment-edit-bb">
  • + data-role="insert-formatting" data-comment="{{$comment}}" data-bbcode="b" data-id="{{$id}}">
  • + data-role="insert-formatting" data-comment="{{$comment}}" data-bbcode="i" data-id="{{$id}}">
  • + data-role="insert-formatting" data-comment="{{$comment}}" data-bbcode="u" data-id="{{$id}}">
  • + data-role="insert-formatting" data-comment="{{$comment}}" data-bbcode="quote" data-id="{{$id}}">
  • + data-role="insert-formatting" data-comment="{{$comment}}" data-bbcode="code" data-id="{{$id}}">
  • + data-role="insert-formatting" data-comment="{{$comment}}" data-bbcode="img" data-id="{{$id}}">
  • + data-role="insert-formatting" data-comment="{{$comment}}" data-bbcode="url" data-id="{{$id}}">
  • + data-role="insert-formatting" data-comment="{{$comment}}" data-bbcode="video" data-id="{{$id}}"> + onFocus="commentOpen(this,{{$id}}) && cmtBbOpen({{$id}});" >{{$comment}} {{if $qcomment}} + {{/if}} {{if $contact.photo_menu}} menu
    {{$contact_block}} - - diff --git a/view/theme/vier/templates/search_item.tpl b/view/theme/vier/templates/search_item.tpl index 598eb4e9e..d8cad5cd0 100644 --- a/view/theme/vier/templates/search_item.tpl +++ b/view/theme/vier/templates/search_item.tpl @@ -9,14 +9,14 @@
    -
    +
    + {{$item.name}} - menu -