extendedprofilewidget.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. <?php
  2. // This file is part of GNU social - https://www.gnu.org/software/social
  3. //
  4. // GNU social is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // GNU social is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
  16. /*
  17. * Class for outputting a widget to display or edit
  18. * extended profiles
  19. *
  20. * @copyright 2011 StatusNet, Inc.
  21. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  22. */
  23. defined('GNUSOCIAL') || die();
  24. class ExtendedProfileWidget extends Form
  25. {
  26. const EDITABLE = true;
  27. /**
  28. * The parent profile
  29. *
  30. * @var Profile
  31. */
  32. protected $profile;
  33. /**
  34. * The extended profile
  35. *
  36. * @var ExtendedProfile
  37. */
  38. protected $ext;
  39. /**
  40. * Constructor
  41. *
  42. * @param Action $out
  43. * @param Profile $profile
  44. * @param boolean $editable
  45. * @throws NoSuchUserException
  46. */
  47. public function __construct(Action $out = null, Profile $profile = null, $editable = false)
  48. {
  49. parent::__construct($out);
  50. $this->profile = $profile;
  51. $this->ext = new ExtendedProfile($this->profile);
  52. $this->editable = $editable;
  53. }
  54. /**
  55. * Show the extended profile, or the edit form
  56. */
  57. public function show()
  58. {
  59. if ($this->editable) {
  60. parent::show();
  61. } else {
  62. $this->showSections();
  63. }
  64. }
  65. /**
  66. * Show form data
  67. */
  68. public function formData()
  69. {
  70. // For JQuery UI modal dialog
  71. $this->out->elementStart(
  72. 'div',
  73. // TRANS: Title for extended profile entry deletion dialog.
  74. array('id' => 'confirm-dialog', 'title' => _m('Confirmation Required'))
  75. );
  76. // TRANS: Confirmation text for extended profile entry deletion dialog.
  77. $this->out->text(_m('Really delete this entry?'));
  78. $this->out->elementEnd('div');
  79. $this->showSections();
  80. }
  81. /**
  82. * Show each section of the extended profile
  83. */
  84. public function showSections()
  85. {
  86. $sections = $this->ext->getSections();
  87. foreach ($sections as $name => $section) {
  88. $this->showExtendedProfileSection($name, $section);
  89. }
  90. }
  91. /**
  92. * Show an extended profile section
  93. *
  94. * @param string $name name of the section
  95. * @param array $section array of fields for the section
  96. * @throws Exception
  97. */
  98. protected function showExtendedProfileSection($name, $section)
  99. {
  100. $this->out->element('h3', null, $section['label']);
  101. $this->out->elementStart('table', array('class' => 'extended-profile'));
  102. foreach ($section['fields'] as $fieldName => $field) {
  103. switch ($fieldName) {
  104. case 'phone':
  105. case 'im':
  106. case 'website':
  107. case 'experience':
  108. case 'education':
  109. $this->showMultiple($fieldName, $field);
  110. break;
  111. default:
  112. $this->showExtendedProfileField($fieldName, $field);
  113. }
  114. }
  115. $this->out->elementEnd('table');
  116. }
  117. /**
  118. * Show an extended profile field
  119. *
  120. * @param string $name name of the field
  121. * @param array $field set of key/value pairs for the field
  122. * @throws Exception
  123. */
  124. protected function showExtendedProfileField($name, $field)
  125. {
  126. $this->out->elementStart('tr');
  127. $this->out->element('th', str_replace(' ', '_', strtolower($field['label'])), $field['label']);
  128. $this->out->elementStart('td');
  129. if ($this->editable) {
  130. $this->showEditableField($name, $field);
  131. } else {
  132. $this->showFieldValue($name, $field);
  133. }
  134. $this->out->elementEnd('td');
  135. $this->out->elementEnd('tr');
  136. }
  137. protected function showMultiple($name, $fields)
  138. {
  139. foreach ($fields as $field) {
  140. $this->showExtendedProfileField($name, $field);
  141. }
  142. }
  143. // XXX: showPhone, showIm and showWebsite all work the same, so
  144. // combine
  145. protected function showPhone($name, $field)
  146. {
  147. $this->out->elementStart('div', ['class' => 'phone-display']);
  148. if (!empty($field['value'])) {
  149. $this->out->text($field['value']);
  150. if (!empty($field['rel'])) {
  151. // TRANS: Value between parentheses (phone number, website, or IM address).
  152. $outtext = sprintf(_m('(%s)'), $field['rel']);
  153. $this->out->text(' ' . $outtext);
  154. }
  155. }
  156. $this->out->elementEnd('div');
  157. }
  158. protected function showIm($name, $field)
  159. {
  160. $this->out->elementStart('div', ['class' => 'im-display']);
  161. if (!empty($field['value'])) {
  162. $this->out->text($field['value']);
  163. if (!empty($field['rel'])) {
  164. // TRANS: Value between parentheses (phone number, website, or IM address).
  165. $outtext = sprintf(_m('(%s)'), $field['rel']);
  166. $this->out->text(' ' . $outtext);
  167. }
  168. }
  169. $this->out->elementEnd('div');
  170. }
  171. protected function showWebsite($name, $field)
  172. {
  173. $this->out->elementStart('div', ['class' => 'website-display']);
  174. if (!empty($field['value'])) {
  175. $url = $field['value'];
  176. $this->out->element('a', [
  177. 'href' => $url,
  178. 'class' => 'extended-profile-link',
  179. 'target' => '_blank',
  180. ], $url);
  181. if (!empty($field['rel'])) {
  182. // TRANS: Value between parentheses (phone number, website, or IM address).
  183. $outtext = sprintf(_m('(%s)'), $field['rel']);
  184. $this->out->text(' ' . $outtext);
  185. }
  186. }
  187. $this->out->elementEnd('div');
  188. }
  189. protected function showEditableIm($name, $field)
  190. {
  191. $index = (int) ($field['index'] ?? 0);
  192. $id = "extprofile-{$name}-{$index}";
  193. $rel = $id . '-rel';
  194. $this->out->elementStart(
  195. 'div',
  196. array(
  197. 'id' => $id . '-edit',
  198. 'class' => 'im-item'
  199. )
  200. );
  201. $this->out->input(
  202. $id,
  203. null,
  204. ($field['value'] ?? null)
  205. );
  206. $this->out->dropdown(
  207. $id . '-rel',
  208. 'Type',
  209. array(
  210. 'jabber' => 'Jabber',
  211. 'gtalk' => 'GTalk',
  212. 'aim' => 'AIM',
  213. 'yahoo' => 'Yahoo! Messenger',
  214. 'msn' => 'MSN',
  215. 'skype' => 'Skype',
  216. 'other' => 'Other'
  217. ),
  218. null,
  219. false,
  220. ($field['rel'] ?? null)
  221. );
  222. $this->showMultiControls();
  223. $this->out->elementEnd('div');
  224. }
  225. protected function showEditablePhone($name, $field)
  226. {
  227. $index = (int) ($field['index'] ?? 0);
  228. $id = "extprofile-{$name}-{$index}";
  229. $rel = $id . '-rel';
  230. $this->out->elementStart(
  231. 'div',
  232. array(
  233. 'id' => $id . '-edit',
  234. 'class' => 'phone-item'
  235. )
  236. );
  237. $this->out->input(
  238. $id,
  239. null,
  240. ($field['value'] ?? null)
  241. );
  242. $this->out->dropdown(
  243. $id . '-rel',
  244. 'Type',
  245. array(
  246. 'office' => 'Office',
  247. 'mobile' => 'Mobile',
  248. 'home' => 'Home',
  249. 'pager' => 'Pager',
  250. 'other' => 'Other'
  251. ),
  252. null,
  253. false,
  254. ($field['rel'] ?? null)
  255. );
  256. $this->showMultiControls();
  257. $this->out->elementEnd('div');
  258. }
  259. protected function showEditableWebsite($name, $field)
  260. {
  261. $index = (int) ($field['index'] ?? 0);
  262. $id = "extprofile-{$name}-{$index}";
  263. $rel = $id . '-rel';
  264. $this->out->elementStart(
  265. 'div',
  266. array(
  267. 'id' => $id . '-edit',
  268. 'class' => 'website-item'
  269. )
  270. );
  271. $this->out->input(
  272. $id,
  273. null,
  274. ($field['value'] ?? null)
  275. );
  276. $this->out->dropdown(
  277. $id . '-rel',
  278. 'Type',
  279. array(
  280. 'blog' => 'Blog',
  281. 'homepage' => 'Homepage',
  282. 'facebook' => 'Facebook',
  283. 'linkedin' => 'LinkedIn',
  284. 'flickr' => 'Flickr',
  285. 'other' => 'Other',
  286. 'twitter' => 'Twitter'
  287. ),
  288. null,
  289. false,
  290. ($field['rel'] ?? null)
  291. );
  292. $this->showMultiControls();
  293. $this->out->elementEnd('div');
  294. }
  295. protected function showExperience($name, $field)
  296. {
  297. $this->out->elementStart('div', 'experience-item');
  298. // TRANS: Field label in experience area of extended profile.
  299. $this->out->element('div', 'label', _m('Company'));
  300. if (!empty($field['company'])) {
  301. $this->out->element('div', 'field', $field['company']);
  302. // TRANS: Field label in extended profile (when did one start a position or education).
  303. $this->out->element('div', 'label', _m('Start'));
  304. $this->out->element(
  305. 'div',
  306. array('class' => 'field date'),
  307. date(
  308. 'j M Y',
  309. strtotime($field['start'])
  310. )
  311. );
  312. // TRANS: Field label in extended profile (when did one end a position or education).
  313. $this->out->element('div', 'label', _m('End'));
  314. $this->out->element(
  315. 'div',
  316. array('class' => 'field date'),
  317. date(
  318. 'j M Y',
  319. strtotime($field['end'])
  320. )
  321. );
  322. if ($field['current']) {
  323. $this->out->element(
  324. 'div',
  325. array('class' => 'field current'),
  326. // TRANS: Field value in experience area of extended profile (one still holds a position).
  327. _m('(Current)')
  328. );
  329. }
  330. }
  331. $this->out->elementEnd('div');
  332. }
  333. protected function showEditableExperience($name, $field)
  334. {
  335. $index = (int) ($field['index'] ?? 0);
  336. $id = "extprofile-{$name}-{$index}";
  337. $this->out->elementStart(
  338. 'div',
  339. array(
  340. 'id' => $id . '-edit',
  341. 'class' => 'experience-item'
  342. )
  343. );
  344. // TRANS: Field label in experience edit area of extended profile (which company does one work for).
  345. $this->out->element('div', 'label', _m('Company'));
  346. $this->out->input(
  347. $id,
  348. null,
  349. ($field['company'] ?? null)
  350. );
  351. // TRANS: Field label in extended profile (when did one start a position or education).
  352. $this->out->element('div', 'label', _m('Start'));
  353. $this->out->input(
  354. $id . '-start',
  355. null,
  356. isset($field['start']) ? date('j M Y', strtotime($field['start'])) : null
  357. );
  358. // TRANS: Field label in extended profile (when did one end a position or education).
  359. $this->out->element('div', 'label', _m('End'));
  360. $this->out->input(
  361. $id . '-end',
  362. null,
  363. isset($field['end']) ? date('j M Y', strtotime($field['end'])) : null
  364. );
  365. $this->out->hidden(
  366. $id . '-current',
  367. 'false'
  368. );
  369. $this->out->elementStart('div', 'current-checkbox');
  370. $this->out->checkbox(
  371. $id . '-current',
  372. // TRANS: Checkbox label in experience edit area of extended profile (one still works at a company).
  373. _m('Current'),
  374. $field['current']
  375. );
  376. $this->out->elementEnd('div');
  377. $this->showMultiControls();
  378. $this->out->elementEnd('div');
  379. }
  380. protected function showEducation($name, $field)
  381. {
  382. $this->out->elementStart('div', 'education-item');
  383. // TRANS: Field label in education area of extended profile.
  384. $this->out->element('div', 'label', _m('Institution'));
  385. if (!empty($field['school'])) {
  386. $this->out->element('div', 'field', $field['school']);
  387. // TRANS: Field label in extended profile for specifying an academic degree.
  388. $this->out->element('div', 'label', _m('Degree'));
  389. $this->out->element('div', 'field', $field['degree']);
  390. // TRANS: Field label in education area of extended profile.
  391. $this->out->element('div', 'label', _m('Description'));
  392. $this->out->element('div', 'field', $field['description']);
  393. // TRANS: Field label in extended profile (when did one start a position or education).
  394. $this->out->element('div', 'label', _m('Start'));
  395. $this->out->element(
  396. 'div',
  397. array('class' => 'field date'),
  398. date(
  399. 'j M Y',
  400. strtotime($field['start'])
  401. )
  402. );
  403. // TRANS: Field label in extended profile (when did one end a position or education).
  404. $this->out->element('div', 'label', _m('End'));
  405. $this->out->element(
  406. 'div',
  407. array('class' => 'field date'),
  408. date(
  409. 'j M Y',
  410. strtotime($field['end'])
  411. )
  412. );
  413. }
  414. $this->out->elementEnd('div');
  415. }
  416. protected function showEditableEducation($name, $field)
  417. {
  418. $index = (int) ($field['index'] ?? 0);
  419. $id = "extprofile-{$name}-{$index}";
  420. $this->out->elementStart(
  421. 'div',
  422. array(
  423. 'id' => $id . '-edit',
  424. 'class' => 'education-item'
  425. )
  426. );
  427. // TRANS: Field label in education edit area of extended profile.
  428. $this->out->element('div', 'label', _m('Institution'));
  429. $this->out->input(
  430. $id,
  431. null,
  432. ($field['school'] ?? null)
  433. );
  434. // TRANS: Field label in extended profile for specifying an academic degree.
  435. $this->out->element('div', 'label', _m('Degree'));
  436. $this->out->input(
  437. $id . '-degree',
  438. null,
  439. ($field['degree'] ?? null)
  440. );
  441. // TRANS: Field label in education edit area of extended profile.
  442. $this->out->element('div', 'label', _m('Description'));
  443. $this->out->textarea(
  444. $id . '-description',
  445. null,
  446. ($field['description'] ?? null)
  447. );
  448. // TRANS: Field label in extended profile (when did one start a position or education).
  449. $this->out->element('div', 'label', _m('Start'));
  450. $this->out->input(
  451. $id . '-start',
  452. null,
  453. // @todo FIXME: does date format need i18n? If so, should probly be dealt with in core.
  454. isset($field['start']) ? date('j M Y', strtotime($field['start'])) : null
  455. );
  456. // TRANS: Field label in extended profile (when did one end a position or education).
  457. $this->out->element('div', 'label', _m('End'));
  458. $this->out->input(
  459. $id . '-end',
  460. null,
  461. // @todo FIXME: does date format need i18n? If so, should probly be dealt with in core.
  462. isset($field['end']) ? date('j M Y', strtotime($field['end'])) : null
  463. );
  464. $this->showMultiControls();
  465. $this->out->elementEnd('div');
  466. }
  467. public function showMultiControls()
  468. {
  469. $this->out->element(
  470. 'a',
  471. array(
  472. 'class' => 'remove_row',
  473. 'href' => 'javascript://',
  474. 'style' => 'display: none;'
  475. ),
  476. '-'
  477. );
  478. $this->out->element(
  479. 'a',
  480. array(
  481. 'class' => 'add_row',
  482. 'href' => 'javascript://',
  483. 'style' => 'display: none;'
  484. ),
  485. // TRANS: Link description in extended profile page to add another profile element.
  486. _m('Add another item')
  487. );
  488. }
  489. /**
  490. * Outputs the value of a field
  491. *
  492. * @param string $name name of the field
  493. * @param array $field set of key/value pairs for the field
  494. */
  495. protected function showFieldValue($name, $field)
  496. {
  497. $type = (string) ($field['type'] ?? '');
  498. switch ($type) {
  499. case '':
  500. case 'text':
  501. case 'textarea':
  502. case 'person':
  503. $this->out->text($this->ext->getTextValue($name) ?? '');
  504. break;
  505. case 'custom-text':
  506. case 'custom-textarea':
  507. $this->out->text($field['value'] ?? '');
  508. break;
  509. case 'date':
  510. $value = $this->ext->getDateValue($name);
  511. if (!empty($value)) {
  512. $this->out->element(
  513. 'div',
  514. array('class' => 'field date'),
  515. date('j M Y', strtotime($value))
  516. );
  517. }
  518. break;
  519. case 'tags':
  520. $this->out->text($this->ext->getTags());
  521. break;
  522. case 'phone':
  523. $this->showPhone($name, $field);
  524. break;
  525. case 'website':
  526. $this->showWebsite($name, $field);
  527. break;
  528. case 'im':
  529. $this->showIm($name, $field);
  530. break;
  531. case 'experience':
  532. $this->showExperience($name, $field);
  533. break;
  534. case 'education':
  535. $this->showEducation($name, $field);
  536. break;
  537. default:
  538. $this->out->text("TYPE: $type");
  539. }
  540. }
  541. /**
  542. * Show an editable version of the field
  543. *
  544. * @param string $name name fo the field
  545. * @param array $field array of key/value pairs for the field
  546. * @throws Exception
  547. */
  548. protected function showEditableField($name, $field)
  549. {
  550. $out = $this->out;
  551. $type = (string) ($field['type'] ?? '');
  552. $id = "extprofile-" . $name;
  553. $value = 'placeholder';
  554. switch ($type) {
  555. case '':
  556. case 'text':
  557. case 'person':
  558. $out->input($id, null, $this->ext->getTextValue($name));
  559. break;
  560. case 'custom-text':
  561. case 'custom-textarea':
  562. $out->input($id, null, ($field['value'] ?? null));
  563. break;
  564. case 'date':
  565. $value = $this->ext->getDateValue($name);
  566. $out->input(
  567. $id,
  568. null,
  569. empty($value) ? null : date('j M Y', strtotime($value))
  570. );
  571. break;
  572. case 'textarea':
  573. $out->textarea($id, null, $this->ext->getTextValue($name));
  574. break;
  575. case 'tags':
  576. $out->input($id, null, $this->ext->getTags());
  577. break;
  578. case 'phone':
  579. $this->showEditablePhone($name, $field);
  580. break;
  581. case 'im':
  582. $this->showEditableIm($name, $field);
  583. break;
  584. case 'website':
  585. $this->showEditableWebsite($name, $field);
  586. break;
  587. case 'experience':
  588. $this->showEditableExperience($name, $field);
  589. break;
  590. case 'education':
  591. $this->showEditableEducation($name, $field);
  592. break;
  593. default:
  594. // TRANS: Field label for undefined field in extended profile.
  595. $out->input($id, null, sprintf(_m('TYPE: %s'), $type));
  596. }
  597. }
  598. /**
  599. * Action elements
  600. *
  601. * @return void
  602. * @throws Exception
  603. */
  604. public function formActions()
  605. {
  606. $this->out->submit(
  607. 'save',
  608. // TRANS: Button text for saving extended profile properties.
  609. _m('BUTTON', 'Save'),
  610. 'submit form_action-secondary',
  611. 'save',
  612. // TRANS: .
  613. // TRANS: Button title for saving extended profile properties.
  614. _m('Save details')
  615. );
  616. }
  617. /**
  618. * ID of the form
  619. *
  620. * @return string ID of the form
  621. */
  622. public function id()
  623. {
  624. return 'profile-details-' . $this->profile->id;
  625. }
  626. /**
  627. * class of the form
  628. *
  629. * @return string of the form class
  630. */
  631. public function formClass()
  632. {
  633. return 'form_profile_details form_settings';
  634. }
  635. /**
  636. * Action of the form
  637. *
  638. * @return string URL of the action
  639. */
  640. public function action()
  641. {
  642. return common_local_url('profiledetailsettings');
  643. }
  644. }