Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

512 lines
12 KiB

  1. <?php
  2. /**
  3. * @file src/Content/Widget.php
  4. */
  5. namespace Friendica\Content;
  6. use Friendica\Core\Addon;
  7. use Friendica\Core\Config;
  8. use Friendica\Core\L10n;
  9. use Friendica\Core\Protocol;
  10. use Friendica\Core\Renderer;
  11. use Friendica\Core\Session;
  12. use Friendica\Database\DBA;
  13. use Friendica\DI;
  14. use Friendica\Model\Contact;
  15. use Friendica\Model\FileTag;
  16. use Friendica\Model\GContact;
  17. use Friendica\Model\Item;
  18. use Friendica\Model\Profile;
  19. use Friendica\Util\DateTimeFormat;
  20. use Friendica\Util\Proxy as ProxyUtils;
  21. use Friendica\Util\Strings;
  22. use Friendica\Util\Temporal;
  23. class Widget
  24. {
  25. /**
  26. * Return the follow widget
  27. *
  28. * @param string $value optional, default empty
  29. * @return string
  30. * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  31. */
  32. public static function follow($value = "")
  33. {
  34. return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/follow.tpl'), array(
  35. '$connect' => L10n::t('Add New Contact'),
  36. '$desc' => L10n::t('Enter address or web location'),
  37. '$hint' => L10n::t('Example: bob@example.com, http://example.com/barbara'),
  38. '$value' => $value,
  39. '$follow' => L10n::t('Connect')
  40. ));
  41. }
  42. /**
  43. * Return Find People widget
  44. */
  45. public static function findPeople()
  46. {
  47. $global_dir = Config::get('system', 'directory');
  48. if (Config::get('system', 'invitation_only')) {
  49. $x = intval(DI::pConfig()->get(local_user(), 'system', 'invites_remaining'));
  50. if ($x || is_site_admin()) {
  51. DI::page()['aside'] .= '<div class="side-link widget" id="side-invite-remain">'
  52. . L10n::tt('%d invitation available', '%d invitations available', $x)
  53. . '</div>';
  54. }
  55. }
  56. $nv = [];
  57. $nv['findpeople'] = L10n::t('Find People');
  58. $nv['desc'] = L10n::t('Enter name or interest');
  59. $nv['label'] = L10n::t('Connect/Follow');
  60. $nv['hint'] = L10n::t('Examples: Robert Morgenstein, Fishing');
  61. $nv['findthem'] = L10n::t('Find');
  62. $nv['suggest'] = L10n::t('Friend Suggestions');
  63. $nv['similar'] = L10n::t('Similar Interests');
  64. $nv['random'] = L10n::t('Random Profile');
  65. $nv['inv'] = L10n::t('Invite Friends');
  66. $nv['directory'] = L10n::t('Global Directory');
  67. $nv['global_dir'] = $global_dir;
  68. $nv['local_directory'] = L10n::t('Local Directory');
  69. $aside = [];
  70. $aside['$nv'] = $nv;
  71. return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/peoplefind.tpl'), $aside);
  72. }
  73. /**
  74. * Return unavailable networks
  75. */
  76. public static function unavailableNetworks()
  77. {
  78. // Always hide content from these networks
  79. $networks = ['face', 'apdn'];
  80. if (!Addon::isEnabled("discourse")) {
  81. $networks[] = Protocol::DISCOURSE;
  82. }
  83. if (!Addon::isEnabled("statusnet")) {
  84. $networks[] = Protocol::STATUSNET;
  85. }
  86. if (!Addon::isEnabled("pumpio")) {
  87. $networks[] = Protocol::PUMPIO;
  88. }
  89. if (!Addon::isEnabled("twitter")) {
  90. $networks[] = Protocol::TWITTER;
  91. }
  92. if (Config::get("system", "ostatus_disabled")) {
  93. $networks[] = Protocol::OSTATUS;
  94. }
  95. if (!Config::get("system", "diaspora_enabled")) {
  96. $networks[] = Protocol::DIASPORA;
  97. }
  98. if (!Addon::isEnabled("pnut")) {
  99. $networks[] = Protocol::PNUT;
  100. }
  101. if (!sizeof($networks)) {
  102. return "";
  103. }
  104. $network_filter = implode("','", $networks);
  105. $network_filter = "AND `network` NOT IN ('$network_filter')";
  106. return $network_filter;
  107. }
  108. /**
  109. * Display a generic filter widget based on a list of options
  110. *
  111. * The options array must be the following format:
  112. * [
  113. * [
  114. * 'ref' => {filter value},
  115. * 'name' => {option name}
  116. * ],
  117. * ...
  118. * ]
  119. *
  120. * @param string $type The filter query string key
  121. * @param string $title
  122. * @param string $desc
  123. * @param string $all The no filter label
  124. * @param string $baseUrl The full page request URI
  125. * @param array $options
  126. * @param string $selected The currently selected filter option value
  127. * @return string
  128. * @throws \Exception
  129. */
  130. private static function filter($type, $title, $desc, $all, $baseUrl, array $options, $selected = null)
  131. {
  132. $queryString = parse_url($baseUrl, PHP_URL_QUERY);
  133. $queryArray = [];
  134. if ($queryString) {
  135. parse_str($queryString, $queryArray);
  136. unset($queryArray[$type]);
  137. if (count($queryArray)) {
  138. $baseUrl = substr($baseUrl, 0, strpos($baseUrl, '?')) . '?' . http_build_query($queryArray) . '&';
  139. } else {
  140. $baseUrl = substr($baseUrl, 0, strpos($baseUrl, '?')) . '?';
  141. }
  142. } else {
  143. $baseUrl = trim($baseUrl, '?') . '?';
  144. }
  145. return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/filter.tpl'), [
  146. '$type' => $type,
  147. '$title' => $title,
  148. '$desc' => $desc,
  149. '$selected' => $selected,
  150. '$all_label' => $all,
  151. '$options' => $options,
  152. '$base' => $baseUrl,
  153. ]);
  154. }
  155. /**
  156. * Return networks widget
  157. *
  158. * @param string $baseurl baseurl
  159. * @param string $selected optional, default empty
  160. * @return string
  161. * @throws \Exception
  162. */
  163. public static function contactRels($baseurl, $selected = '')
  164. {
  165. if (!local_user()) {
  166. return '';
  167. }
  168. $options = [
  169. ['ref' => 'followers', 'name' => L10n::t('Followers')],
  170. ['ref' => 'following', 'name' => L10n::t('Following')],
  171. ['ref' => 'mutuals', 'name' => L10n::t('Mutual friends')],
  172. ];
  173. return self::filter(
  174. 'rel',
  175. L10n::t('Relationships'),
  176. '',
  177. L10n::t('All Contacts'),
  178. $baseurl,
  179. $options,
  180. $selected
  181. );
  182. }
  183. /**
  184. * Return networks widget
  185. *
  186. * @param string $baseurl baseurl
  187. * @param string $selected optional, default empty
  188. * @return string
  189. * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  190. */
  191. public static function networks($baseurl, $selected = '')
  192. {
  193. if (!local_user()) {
  194. return '';
  195. }
  196. if (!Feature::isEnabled(local_user(), 'networks')) {
  197. return '';
  198. }
  199. $extra_sql = self::unavailableNetworks();
  200. $r = DBA::p("SELECT DISTINCT(`network`) FROM `contact` WHERE `uid` = ? AND NOT `deleted` AND `network` != '' $extra_sql ORDER BY `network`",
  201. local_user()
  202. );
  203. $nets = array();
  204. while ($rr = DBA::fetch($r)) {
  205. $nets[] = ['ref' => $rr['network'], 'name' => ContactSelector::networkToName($rr['network'])];
  206. }
  207. DBA::close($r);
  208. if (count($nets) < 2) {
  209. return '';
  210. }
  211. return self::filter(
  212. 'nets',
  213. L10n::t('Protocols'),
  214. '',
  215. L10n::t('All Protocols'),
  216. $baseurl,
  217. $nets,
  218. $selected
  219. );
  220. }
  221. /**
  222. * Return file as widget
  223. *
  224. * @param string $baseurl baseurl
  225. * @param string $selected optional, default empty
  226. * @return string|void
  227. * @throws \Exception
  228. */
  229. public static function fileAs($baseurl, $selected = '')
  230. {
  231. if (!local_user()) {
  232. return '';
  233. }
  234. $saved = DI::pConfig()->get(local_user(), 'system', 'filetags');
  235. if (!strlen($saved)) {
  236. return;
  237. }
  238. $terms = [];
  239. foreach (FileTag::fileToArray($saved) as $savedFolderName) {
  240. $terms[] = ['ref' => $savedFolderName, 'name' => $savedFolderName];
  241. }
  242. usort($terms, function ($a, $b) {
  243. return strcmp($a['name'], $b['name']);
  244. });
  245. return self::filter(
  246. 'file',
  247. L10n::t('Saved Folders'),
  248. '',
  249. L10n::t('Everything'),
  250. $baseurl,
  251. $terms,
  252. $selected
  253. );
  254. }
  255. /**
  256. * Return categories widget
  257. *
  258. * @param string $baseurl baseurl
  259. * @param string $selected optional, default empty
  260. * @return string|void
  261. * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  262. */
  263. public static function categories($baseurl, $selected = '')
  264. {
  265. $a = DI::app();
  266. $uid = intval($a->profile['profile_uid']);
  267. if (!Feature::isEnabled($uid, 'categories')) {
  268. return '';
  269. }
  270. $saved = DI::pConfig()->get($uid, 'system', 'filetags');
  271. if (!strlen($saved)) {
  272. return;
  273. }
  274. $terms = array();
  275. foreach (FileTag::fileToArray($saved, 'category') as $savedFolderName) {
  276. $terms[] = ['ref' => $savedFolderName, 'name' => $savedFolderName];
  277. }
  278. return self::filter(
  279. 'category',
  280. L10n::t('Categories'),
  281. '',
  282. L10n::t('Everything'),
  283. $baseurl,
  284. $terms,
  285. $selected
  286. );
  287. }
  288. /**
  289. * Return common friends visitor widget
  290. *
  291. * @param string $profile_uid uid
  292. * @return string|void
  293. * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  294. */
  295. public static function commonFriendsVisitor($profile_uid)
  296. {
  297. if (local_user() == $profile_uid) {
  298. return;
  299. }
  300. $zcid = 0;
  301. $cid = Session::getRemoteContactID($profile_uid);
  302. if (!$cid) {
  303. if (Profile::getMyURL()) {
  304. $contact = DBA::selectFirst('contact', ['id'],
  305. ['nurl' => Strings::normaliseLink(Profile::getMyURL()), 'uid' => $profile_uid]);
  306. if (DBA::isResult($contact)) {
  307. $cid = $contact['id'];
  308. } else {
  309. $gcontact = DBA::selectFirst('gcontact', ['id'], ['nurl' => Strings::normaliseLink(Profile::getMyURL())]);
  310. if (DBA::isResult($gcontact)) {
  311. $zcid = $gcontact['id'];
  312. }
  313. }
  314. }
  315. }
  316. if ($cid == 0 && $zcid == 0) {
  317. return;
  318. }
  319. if ($cid) {
  320. $t = GContact::countCommonFriends($profile_uid, $cid);
  321. } else {
  322. $t = GContact::countCommonFriendsZcid($profile_uid, $zcid);
  323. }
  324. if (!$t) {
  325. return;
  326. }
  327. if ($cid) {
  328. $r = GContact::commonFriends($profile_uid, $cid, 0, 5, true);
  329. } else {
  330. $r = GContact::commonFriendsZcid($profile_uid, $zcid, 0, 5, true);
  331. }
  332. if (!DBA::isResult($r)) {
  333. return;
  334. }
  335. $entries = [];
  336. foreach ($r as $rr) {
  337. $entry = [
  338. 'url' => Contact::magicLink($rr['url']),
  339. 'name' => $rr['name'],
  340. 'photo' => ProxyUtils::proxifyUrl($rr['photo'], false, ProxyUtils::SIZE_THUMB),
  341. ];
  342. $entries[] = $entry;
  343. }
  344. $tpl = Renderer::getMarkupTemplate('widget/remote_friends_common.tpl');
  345. return Renderer::replaceMacros($tpl, [
  346. '$desc' => L10n::tt("%d contact in common", "%d contacts in common", $t),
  347. '$base' => DI::baseUrl(),
  348. '$uid' => $profile_uid,
  349. '$cid' => (($cid) ? $cid : '0'),
  350. '$linkmore' => (($t > 5) ? 'true' : ''),
  351. '$more' => L10n::t('show more'),
  352. '$items' => $entries
  353. ]);
  354. }
  355. /**
  356. * Insert a tag cloud widget for the present profile.
  357. *
  358. * @brief Insert a tag cloud widget for the present profile.
  359. * @param int $limit Max number of displayed tags.
  360. * @return string HTML formatted output.
  361. * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  362. * @throws \ImagickException
  363. */
  364. public static function tagCloud($limit = 50)
  365. {
  366. $a = DI::app();
  367. $uid = intval($a->profile['profile_uid']);
  368. if (!$uid || !$a->profile['url']) {
  369. return '';
  370. }
  371. if (Feature::isEnabled($uid, 'tagadelic')) {
  372. $owner_id = Contact::getIdForURL($a->profile['url'], 0, true);
  373. if (!$owner_id) {
  374. return '';
  375. }
  376. return Widget\TagCloud::getHTML($uid, $limit, $owner_id, 'wall');
  377. }
  378. return '';
  379. }
  380. /**
  381. * @param string $url Base page URL
  382. * @param int $uid User ID consulting/publishing posts
  383. * @param bool $wall True: Posted by User; False: Posted to User (network timeline)
  384. * @return string
  385. * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  386. */
  387. public static function postedByYear(string $url, int $uid, bool $wall)
  388. {
  389. $o = '';
  390. if (!Feature::isEnabled($uid, 'archives')) {
  391. return $o;
  392. }
  393. $visible_years = DI::pConfig()->get($uid, 'system', 'archive_visible_years', 5);
  394. /* arrange the list in years */
  395. $dnow = DateTimeFormat::localNow('Y-m-d');
  396. $ret = [];
  397. $dthen = Item::firstPostDate($uid, $wall);
  398. if ($dthen) {
  399. // Set the start and end date to the beginning of the month
  400. $dnow = substr($dnow, 0, 8) . '01';
  401. $dthen = substr($dthen, 0, 8) . '01';
  402. /*
  403. * Starting with the current month, get the first and last days of every
  404. * month down to and including the month of the first post
  405. */
  406. while (substr($dnow, 0, 7) >= substr($dthen, 0, 7)) {
  407. $dyear = intval(substr($dnow, 0, 4));
  408. $dstart = substr($dnow, 0, 8) . '01';
  409. $dend = substr($dnow, 0, 8) . Temporal::getDaysInMonth(intval($dnow), intval(substr($dnow, 5)));
  410. $start_month = DateTimeFormat::utc($dstart, 'Y-m-d');
  411. $end_month = DateTimeFormat::utc($dend, 'Y-m-d');
  412. $str = L10n::getDay(DateTimeFormat::utc($dnow, 'F'));
  413. if (empty($ret[$dyear])) {
  414. $ret[$dyear] = [];
  415. }
  416. $ret[$dyear][] = [$str, $end_month, $start_month];
  417. $dnow = DateTimeFormat::utc($dnow . ' -1 month', 'Y-m-d');
  418. }
  419. }
  420. if (!DBA::isResult($ret)) {
  421. return $o;
  422. }
  423. $cutoff_year = intval(DateTimeFormat::localNow('Y')) - $visible_years;
  424. $cutoff = array_key_exists($cutoff_year, $ret);
  425. $o = Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/posted_date.tpl'),[
  426. '$title' => L10n::t('Archives'),
  427. '$size' => $visible_years,
  428. '$cutoff_year' => $cutoff_year,
  429. '$cutoff' => $cutoff,
  430. '$url' => $url,
  431. '$dates' => $ret,
  432. '$showmore' => L10n::t('show more')
  433. ]);
  434. return $o;
  435. }
  436. }