diff --git a/boot.php b/boot.php index 4e1249e197..2bcbb4ebdd 100644 --- a/boot.php +++ b/boot.php @@ -355,7 +355,7 @@ function feed_birthday($uid, $tz) $tz = 'UTC'; } - $profile = DBA::selectFirst('profile', ['dob'], ['is-default' => true, 'uid' => $uid]); + $profile = DBA::selectFirst('profile', ['dob'], ['uid' => $uid]); if (DBA::isResult($profile)) { $tmp_dob = substr($profile['dob'], 5); if (intval($tmp_dob)) { diff --git a/composer.json b/composer.json index e372547aac..915c2b08cc 100644 --- a/composer.json +++ b/composer.json @@ -52,6 +52,7 @@ "bower-asset/dompurify": "^1.0", "bower-asset/perfect-scrollbar": "^0.6", "bower-asset/vue": "^2.6", + "npm-asset/es-jquery-sortable": "^0.9.13", "npm-asset/jquery": "^2.0", "npm-asset/jquery-colorbox": "^1.6", "npm-asset/jquery-datetimepicker": "^2.5", diff --git a/composer.lock b/composer.lock index 3a4670b20a..c8fa900ac0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "34ad225ce21474eb84ce78047d9f2c01", + "content-hash": "6659cc3edcbdb9d706019810788d8dc3", "packages": [ { "name": "asika/simple-console", @@ -1215,6 +1215,49 @@ ], "time": "2018-01-03T13:39:39+00:00" }, + { + "name": "npm-asset/es-jquery-sortable", + "version": "0.9.13-patch2", + "dist": { + "type": "tar", + "url": "https://registry.npmjs.org/es-jquery-sortable/-/es-jquery-sortable-0.9.13-patch2.tgz", + "shasum": "a4db16d133fbce1bcd1543c98279902a6b0812a3" + }, + "require": { + "npm-asset/jquery": ">=2.1.2,<3.0.0" + }, + "type": "npm-asset-library", + "extra": { + "npm-asset-bugs": { + "url": "https://github.com/johnny/jquery-sortable/issues" + }, + "npm-asset-main": "./source/js/jquery-sortable.js", + "npm-asset-directories": [], + "npm-asset-repository": { + "type": "git", + "url": "git+https://github.com/johnny/jquery-sortable.git" + }, + "npm-asset-scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + } + }, + "license": [ + "BSD-3" + ], + "authors": [ + "" + ], + "description": "jquery plugin for sortable, nestable lists", + "homepage": "https://github.com/johnny/jquery-sortable", + "keywords": [ + "drag", + "dragging", + "sort", + "sortable", + "sorting" + ], + "time": "2019-11-20T03:55:51+00:00" + }, { "name": "npm-asset/ev-emitter", "version": "1.1.1", diff --git a/database.sql b/database.sql index 8db6c1d6f0..78c71efeef 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2020.03-dev (Dalmatian Bellflower) --- DB_UPDATE_VERSION 1330 +-- DB_UPDATE_VERSION 1332 -- ------------------------------------------ @@ -233,7 +233,7 @@ CREATE TABLE IF NOT EXISTS `contact` ( `reason` text COMMENT '', `closeness` tinyint unsigned NOT NULL DEFAULT 99 COMMENT '', `info` mediumtext COMMENT '', - `profile-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `profile-id` int unsigned COMMENT 'Deprecated', `bdyear` varchar(4) NOT NULL DEFAULT '' COMMENT '', `bd` date NOT NULL DEFAULT '0001-01-01' COMMENT '', `notify_new_posts` boolean NOT NULL DEFAULT '0' COMMENT '', @@ -995,8 +995,8 @@ CREATE TABLE IF NOT EXISTS `process` ( CREATE TABLE IF NOT EXISTS `profile` ( `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id', - `profile-name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name of the profile', - `is-default` boolean NOT NULL DEFAULT '0' COMMENT 'Mark this profile as default profile', + `profile-name` varchar(255) COMMENT 'Deprecated', + `is-default` boolean COMMENT 'Deprecated', `hide-friends` boolean NOT NULL DEFAULT '0' COMMENT 'Hide friend list from viewers of this profile', `name` varchar(255) NOT NULL DEFAULT '' COMMENT '', `pdesc` varchar(255) NOT NULL DEFAULT '' COMMENT 'Title or description', @@ -1006,29 +1006,29 @@ CREATE TABLE IF NOT EXISTS `profile` ( `region` varchar(255) NOT NULL DEFAULT '' COMMENT '', `postal-code` varchar(32) NOT NULL DEFAULT '' COMMENT '', `country-name` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `hometown` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `gender` varchar(32) NOT NULL DEFAULT '' COMMENT '', - `marital` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `with` text COMMENT '', - `howlong` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `sexual` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `politic` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `religion` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `hometown` varchar(255) COMMENT 'Deprecated', + `gender` varchar(32) COMMENT 'Deprecated', + `marital` varchar(255) COMMENT 'Deprecated', + `with` text COMMENT 'Deprecated', + `howlong` datetime COMMENT 'Deprecated', + `sexual` varchar(255) COMMENT 'Deprecated', + `politic` varchar(255) COMMENT 'Deprecated', + `religion` varchar(255) COMMENT 'Deprecated', `pub_keywords` text COMMENT '', `prv_keywords` text COMMENT '', - `likes` text COMMENT '', - `dislikes` text COMMENT '', - `about` text COMMENT '', - `summary` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `music` text COMMENT '', - `book` text COMMENT '', - `tv` text COMMENT '', - `film` text COMMENT '', - `interest` text COMMENT '', - `romance` text COMMENT '', - `work` text COMMENT '', - `education` text COMMENT '', - `contact` text COMMENT '', + `likes` text COMMENT 'Deprecated', + `dislikes` text COMMENT 'Deprecated', + `about` text COMMENT 'Deprecated', + `summary` varchar(255) COMMENT 'Deprecated', + `music` text COMMENT 'Deprecated', + `book` text COMMENT 'Deprecated', + `tv` text COMMENT 'Deprecated', + `film` text COMMENT 'Deprecated', + `interest` text COMMENT 'Deprecated', + `romance` text COMMENT 'Deprecated', + `work` text COMMENT 'Deprecated', + `education` text COMMENT 'Deprecated', + `contact` text COMMENT 'Deprecated', `homepage` varchar(255) NOT NULL DEFAULT '' COMMENT '', `xmpp` varchar(255) NOT NULL DEFAULT '' COMMENT '', `photo` varchar(255) NOT NULL DEFAULT '' COMMENT '', @@ -1053,6 +1053,25 @@ CREATE TABLE IF NOT EXISTS `profile_check` ( PRIMARY KEY(`id`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='DFRN remote auth use'; +-- +-- TABLE profile_field +-- +CREATE TABLE IF NOT EXISTS `profile_field` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'sequential ID', + `uid` mediumint(8) unsigned NOT NULL DEFAULT 0 COMMENT 'Owner user id', + `psid` int(10) unsigned DEFAULT NULL COMMENT 'ID of the permission set of this profile field - 0 = public', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name of the field', + `value` text COMMENT 'Value of the field', + `order` mediumint(8) unsigned NOT NULL DEFAULT 1 COMMENT 'Field ordering per user', + `label` varchar(255) NOT NULL DEFAULT '' COMMENT 'Label of the field', + `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', + `edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', + PRIMARY KEY (`id`), + KEY `uid` (`uid`), + KEY `psid` (`psid`), + KEY `order` (`order`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Custom profile fields'; + -- -- TABLE push_subscriber -- diff --git a/doc/API-Friendica.md b/doc/API-Friendica.md index 76440600c4..84467430ae 100644 --- a/doc/API-Friendica.md +++ b/doc/API-Friendica.md @@ -655,7 +655,6 @@ Returns the [Profile](help/API-Entities#Profile) data of all profiles or a singl On success: Array of: -* `multi_profiles`: true if user has activated multi_profiles * `global_dir`: URL of the global directory set in server settings * `friendica_owner`: user data of the authenticated user * `profiles`: array of the profile data diff --git a/doc/Profiles.md b/doc/Profiles.md deleted file mode 100644 index aceb94da39..0000000000 --- a/doc/Profiles.md +++ /dev/null @@ -1,77 +0,0 @@ -Profiles -======== - -* [Home](help) - -Friendica has unlimited profiles. -You may use different profiles to present different aspects of yourself to different audiences. - -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 usually cannot be hidden. -You may, and probably should restrict the personal 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: - -* Your real name -* A photo of **you** -* Your location, preferably at least the country. - -In addition, if you'd like to meet people that share some general interests with you, 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. - -Alternate/ private 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 "clone" your 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. - -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. - -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 admin) allows you to publish your profile in a [Global Directory](Making-Friends.md#the-directories). - -If you do not wish to be visible to any of these directories, do not published your profile. - -Although you may have multiple profiles, you only have one profile photo. -This is intentional; it avoids confusion by potentially seeing different profile pictures of a contact depending on what website you visit or conversation you participate in. -You can always can use the free text information boxes within a profile such as "Tell us about yourself" and link other photos for yourself. - -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 on your profile, but will bring up your profile when matched in a search of the site directory. -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. - -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. -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/de/Account-Basics.md b/doc/de/Account-Basics.md index 193835f5a9..81ce68c041 100644 --- a/doc/de/Account-Basics.md +++ b/doc/de/Account-Basics.md @@ -109,8 +109,6 @@ Ein ['Tipp für neue Mitglieder'](newmember)-Link zeigt sich in den ersten beide ## Schau Dir ebenfalls folgende Seiten an -* [Profile](help/Profiles) - * [Gruppen und Privatssphäre](help/Groups-and-Privacy) * [Account löschen](help/Remove-Account) diff --git a/doc/de/Home.md b/doc/de/Home.md index 536bff7201..d008b90c36 100644 --- a/doc/de/Home.md +++ b/doc/de/Home.md @@ -9,7 +9,6 @@ Friendica - Dokumentation und Ressourcen * [Beiträge erstellen](help/Text_editor) * [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) * Du und andere Nutzer diff --git a/doc/de/Profiles.md b/doc/de/Profiles.md deleted file mode 100644 index 311f1c7929..0000000000 --- a/doc/de/Profiles.md +++ /dev/null @@ -1,79 +0,0 @@ -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. - -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 ... - -* Dein richtiger Name -* 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. - -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. - -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. - -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. - -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. - -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. - -(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. - -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. diff --git a/mod/display.php b/mod/display.php index b5edafc5f9..8be5520138 100644 --- a/mod/display.php +++ b/mod/display.php @@ -102,7 +102,7 @@ function display_init(App $a) if ($nickname != $a->user["nickname"]) { $profile = DBA::fetchFirst("SELECT `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` = ? AND `profile`.`is-default` AND `contact`.`self` LIMIT 1", + WHERE `user`.`nickname` = ? AND `contact`.`self` LIMIT 1", $nickname ); if (DBA::isResult($profile)) { diff --git a/mod/msearch.php b/mod/msearch.php index 67b8ea6fd5..9584776002 100644 --- a/mod/msearch.php +++ b/mod/msearch.php @@ -26,8 +26,7 @@ function msearch_post(App $a) "SELECT COUNT(*) AS `total` FROM `profile` JOIN `user` ON `user`.`uid` = `profile`.`uid` - WHERE `is-default` = 1 - AND `user`.`hidewall` = 0 + WHERE `user`.`hidewall` = 0 AND MATCH(`pub_keywords`) AGAINST (?)", $search ); @@ -42,8 +41,7 @@ function msearch_post(App $a) "SELECT `pub_keywords`, `username`, `nickname`, `user`.`uid` FROM `user` JOIN `profile` ON `user`.`uid` = `profile`.`uid` - WHERE `is-default` = 1 - AND `user`.`hidewall` = 0 + WHERE `user`.`hidewall` = 0 AND MATCH(`pub_keywords`) AGAINST (?) LIMIT ?, ?", $search, diff --git a/mod/network.php b/mod/network.php index db6d8eaf45..1bbddf2196 100644 --- a/mod/network.php +++ b/mod/network.php @@ -879,7 +879,7 @@ function network_tabs(App $a) { // item filter tabs /// @TODO fix this logic, reduce duplication - /// $a->page['content'] .= '
'; + /// DI::page()['content'] .= '
'; list($no_active, $all_active, $post_active, $conv_active, $new_active, $starred_active, $bookmarked_active) = network_query_get_sel_tab($a); // if no tabs are selected, defaults to activitys diff --git a/mod/poco.php b/mod/poco.php index 8e7d63418a..d8941e51ad 100644 --- a/mod/poco.php +++ b/mod/poco.php @@ -67,7 +67,7 @@ function poco_init(App $a) { if (! $system_mode && ! $global) { $users = q("SELECT `user`.*,`profile`.`hide-friends` from user left join profile on `user`.`uid` = `profile`.`uid` - where `user`.`nickname` = '%s' and `profile`.`is-default` = 1 limit 1", + where `user`.`nickname` = '%s' limit 1", DBA::escape($nickname) ); if (! DBA::isResult($users) || $users[0]['hidewall'] || $users[0]['hide-friends']) { @@ -140,7 +140,7 @@ function poco_init(App $a) { `profile`.`postal-code` AS `ppostalcode`, `profile`.`country-name` AS `pcountry`, `user`.`account-type` FROM `contact` INNER JOIN `profile` ON `profile`.`uid` = `contact`.`uid` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid` - WHERE `self` = 1 AND `profile`.`is-default` + WHERE `self` = 1 AND `contact`.`uid` IN (SELECT `uid` FROM `pconfig` WHERE `cat` = 'system' AND `k` = 'suggestme' AND `v` = 1) LIMIT %d, %d", intval($startIndex), intval($itemsPerPage) diff --git a/mod/profiles.php b/mod/profiles.php deleted file mode 100644 index 53da8510c4..0000000000 --- a/mod/profiles.php +++ /dev/null @@ -1,675 +0,0 @@ -argc > 2) && ($a->argv[1] === "drop") && intval($a->argv[2])) { - $r = q("SELECT * FROM `profile` WHERE `id` = %d AND `uid` = %d AND `is-default` = 0 LIMIT 1", - intval($a->argv[2]), - intval(local_user()) - ); - if (! DBA::isResult($r)) { - notice(DI::l10n()->t('Profile not found.') . EOL); - DI::baseUrl()->redirect('profiles'); - return; // NOTREACHED - } - - BaseModule::checkFormSecurityTokenRedirectOnError('/profiles', 'profile_drop', 't'); - - // move every contact using this profile as their default to the user default - - q("UPDATE `contact` SET `profile-id` = (SELECT `profile`.`id` AS `profile-id` FROM `profile` WHERE `profile`.`is-default` = 1 AND `profile`.`uid` = %d LIMIT 1) WHERE `profile-id` = %d AND `uid` = %d ", - intval(local_user()), - intval($a->argv[2]), - intval(local_user()) - ); - q("DELETE FROM `profile` WHERE `id` = %d AND `uid` = %d", - intval($a->argv[2]), - intval(local_user()) - ); - if (DBA::isResult($r)) { - info(DI::l10n()->t('Profile deleted.').EOL); - } - - DI::baseUrl()->redirect('profiles'); - return; // NOTREACHED - } - - if (($a->argc > 1) && ($a->argv[1] === 'new')) { - - BaseModule::checkFormSecurityTokenRedirectOnError('/profiles', 'profile_new', 't'); - - $r0 = q("SELECT `id` FROM `profile` WHERE `uid` = %d", - intval(local_user())); - - $num_profiles = (DBA::isResult($r0) ? count($r0) : 0); - - $name = DI::l10n()->t('Profile-') . ($num_profiles + 1); - - $r1 = q("SELECT `name`, `photo`, `thumb` FROM `profile` WHERE `uid` = %d AND `is-default` = 1 LIMIT 1", - intval(local_user())); - - q("INSERT INTO `profile` (`uid` , `profile-name` , `name`, `photo`, `thumb`) - VALUES ( %d, '%s', '%s', '%s', '%s' )", - intval(local_user()), - DBA::escape($name), - DBA::escape($r1[0]['name']), - DBA::escape($r1[0]['photo']), - DBA::escape($r1[0]['thumb']) - ); - - $r3 = q("SELECT `id` FROM `profile` WHERE `uid` = %d AND `profile-name` = '%s' LIMIT 1", - intval(local_user()), - DBA::escape($name) - ); - - info(DI::l10n()->t('New profile created.') . EOL); - if (DBA::isResult($r3) && count($r3) == 1) { - DI::baseUrl()->redirect('profiles/' . $r3[0]['id']); - } - - DI::baseUrl()->redirect('profiles'); - } - - if (($a->argc > 2) && ($a->argv[1] === 'clone')) { - - BaseModule::checkFormSecurityTokenRedirectOnError('/profiles', 'profile_clone', 't'); - - $r0 = q("SELECT `id` FROM `profile` WHERE `uid` = %d", - intval(local_user())); - - $num_profiles = (DBA::isResult($r0) ? count($r0) : 0); - - $name = DI::l10n()->t('Profile-') . ($num_profiles + 1); - $r1 = q("SELECT * FROM `profile` WHERE `uid` = %d AND `id` = %d LIMIT 1", - intval(local_user()), - intval($a->argv[2]) - ); - if(! DBA::isResult($r1)) { - notice(DI::l10n()->t('Profile unavailable to clone.') . EOL); - exit(); - } - unset($r1[0]['id']); - $r1[0]['is-default'] = 0; - $r1[0]['publish'] = 0; - $r1[0]['net-publish'] = 0; - $r1[0]['profile-name'] = DBA::escape($name); - - DBA::insert('profile', $r1[0]); - - $r3 = q("SELECT `id` FROM `profile` WHERE `uid` = %d AND `profile-name` = '%s' LIMIT 1", - intval(local_user()), - DBA::escape($name) - ); - info(DI::l10n()->t('New profile created.') . EOL); - if ((DBA::isResult($r3)) && (count($r3) == 1)) { - DI::baseUrl()->redirect('profiles/'.$r3[0]['id']); - } - - DI::baseUrl()->redirect('profiles'); - - return; // NOTREACHED - } - - - if (($a->argc > 1) && (intval($a->argv[1]))) { - $r = q("SELECT id FROM `profile` WHERE `id` = %d AND `uid` = %d LIMIT 1", - intval($a->argv[1]), - intval(local_user()) - ); - if (! DBA::isResult($r)) { - notice(DI::l10n()->t('Profile not found.') . EOL); - exit(); - } - - Profile::load($a, $a->user['nickname'], $r[0]['id']); - } -} - -function profile_clean_keywords($keywords) -{ - $keywords = str_replace(",", " ", $keywords); - $keywords = explode(" ", $keywords); - - $cleaned = []; - foreach ($keywords as $keyword) { - $keyword = trim(strtolower($keyword)); - $keyword = trim($keyword, "#"); - if ($keyword != "") { - $cleaned[] = $keyword; - } - } - - $keywords = implode(", ", $cleaned); - - return $keywords; -} - -function profiles_post(App $a) { - - if (! local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); - return; - } - - $namechanged = false; - - Hook::callAll('profile_post', $_POST); - - if (($a->argc > 1) && ($a->argv[1] !== "new") && intval($a->argv[1])) { - $orig = q("SELECT * FROM `profile` WHERE `id` = %d AND `uid` = %d LIMIT 1", - intval($a->argv[1]), - intval(local_user()) - ); - if (! DBA::isResult($orig)) { - notice(DI::l10n()->t('Profile not found.') . EOL); - return; - } - - BaseModule::checkFormSecurityTokenRedirectOnError('/profiles', 'profile_edit'); - - $is_default = (($orig[0]['is-default']) ? 1 : 0); - - $profile_name = Strings::escapeTags(trim($_POST['profile_name'])); - if (! strlen($profile_name)) { - notice(DI::l10n()->t('Profile Name is required.') . EOL); - return; - } - - $dob = !empty($_POST['dob']) ? Strings::escapeHtml(trim($_POST['dob'])) : '0000-00-00'; - - $y = substr($dob, 0, 4); - if ((! ctype_digit($y)) || ($y < 1900)) { - $ignore_year = true; - } else { - $ignore_year = false; - } - if (!in_array($dob, ['0000-00-00', DBA::NULL_DATE])) { - if (strpos($dob, '0000-') === 0 || strpos($dob, '0001-') === 0) { - $ignore_year = true; - $dob = substr($dob, 5); - } - - if ($ignore_year) { - $dob = '0000-' . DateTimeFormat::utc('1900-' . $dob, 'm-d'); - } else { - $dob = DateTimeFormat::utc($dob, 'Y-m-d'); - } - } - - $name = Strings::escapeTags(trim($_POST['name'])); - - if (! strlen($name)) { - $name = '[No Name]'; - } - - if ($orig[0]['name'] != $name) { - $namechanged = true; - } - - $pdesc = Strings::escapeTags(trim($_POST['pdesc'] ?? '')); - $gender = Strings::escapeTags(trim($_POST['gender'] ?? '')); - $address = Strings::escapeTags(trim($_POST['address'] ?? '')); - $locality = Strings::escapeTags(trim($_POST['locality'] ?? '')); - $region = Strings::escapeTags(trim($_POST['region'] ?? '')); - $postal_code = Strings::escapeTags(trim($_POST['postal_code'] ?? '')); - $country_name = Strings::escapeTags(trim($_POST['country_name'] ?? '')); - $pub_keywords = profile_clean_keywords(Strings::escapeTags(trim($_POST['pub_keywords'] ?? ''))); - $prv_keywords = profile_clean_keywords(Strings::escapeTags(trim($_POST['prv_keywords'] ?? ''))); - $marital = Strings::escapeTags(trim($_POST['marital'] ?? '')); - $howlong = Strings::escapeTags(trim($_POST['howlong'] ?? '')); - - $with = (!empty($_POST['with']) ? Strings::escapeTags(trim($_POST['with'])) : ''); - - if (! strlen($howlong)) { - $howlong = DBA::NULL_DATETIME; - } else { - $howlong = DateTimeFormat::convert($howlong, 'UTC', date_default_timezone_get()); - } - // linkify the relationship target if applicable - - $withchanged = false; - - if (strlen($with)) { - if ($with != strip_tags($orig[0]['with'])) { - $withchanged = true; - $prf = ''; - $lookup = $with; - if (strpos($lookup, '@') === 0) { - $lookup = substr($lookup, 1); - } - $lookup = str_replace('_',' ', $lookup); - if (strpos($lookup, '@') || (strpos($lookup, 'http://'))) { - $newname = $lookup; - $links = @Probe::lrdd($lookup); - if (count($links)) { - foreach ($links as $link) { - if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { - $prf = $link['@attributes']['href']; - } - } - } - } else { - $newname = $lookup; - - $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1", - DBA::escape($newname), - intval(local_user()) - ); - if (! DBA::isResult($r)) { - $r = q("SELECT * FROM `contact` WHERE `nick` = '%s' AND `uid` = %d LIMIT 1", - DBA::escape($lookup), - intval(local_user()) - ); - } - if (DBA::isResult($r)) { - $prf = $r[0]['url']; - $newname = $r[0]['name']; - } - } - - if ($prf) { - $with = str_replace($lookup, '' . $newname . '', $with); - if (strpos($with, '@') === 0) { - $with = substr($with, 1); - } - } - } else { - $with = $orig[0]['with']; - } - } - - /// @TODO Not flexible enough for later expansion, let's have more OOP here - $sexual = Strings::escapeTags(trim($_POST['sexual'])); - $xmpp = Strings::escapeTags(trim($_POST['xmpp'])); - $homepage = Strings::escapeTags(trim($_POST['homepage'])); - if ((strpos($homepage, 'http') !== 0) && (strlen($homepage))) { - // neither http nor https in URL, add them - $homepage = 'http://'.$homepage; - } - $hometown = Strings::escapeTags(trim($_POST['hometown'])); - $politic = Strings::escapeTags(trim($_POST['politic'])); - $religion = Strings::escapeTags(trim($_POST['religion'])); - - $likes = Strings::escapeHtml(trim($_POST['likes'])); - $dislikes = Strings::escapeHtml(trim($_POST['dislikes'])); - - $about = Strings::escapeHtml(trim($_POST['about'])); - $interest = Strings::escapeHtml(trim($_POST['interest'])); - $contact = Strings::escapeHtml(trim($_POST['contact'])); - $music = Strings::escapeHtml(trim($_POST['music'])); - $book = Strings::escapeHtml(trim($_POST['book'])); - $tv = Strings::escapeHtml(trim($_POST['tv'])); - $film = Strings::escapeHtml(trim($_POST['film'])); - $romance = Strings::escapeHtml(trim($_POST['romance'])); - $work = Strings::escapeHtml(trim($_POST['work'])); - $education = Strings::escapeHtml(trim($_POST['education'])); - - $hide_friends = (($_POST['hide-friends'] == 1) ? 1: 0); - - DI::pConfig()->set(local_user(), 'system', 'detailled_profile', !empty($_POST['detailed_profile']) ? 1: 0); - - $changes = []; - if ($is_default) { - if ($marital != $orig[0]['marital']) { - $changes[] = '[color=#ff0000]♥[/color] ' . DI::l10n()->t('Marital Status'); - } - if ($withchanged) { - $changes[] = '[color=#ff0000]♥[/color] ' . DI::l10n()->t('Romantic Partner'); - } - if ($likes != $orig[0]['likes']) { - $changes[] = DI::l10n()->t('Likes'); - } - if ($dislikes != $orig[0]['dislikes']) { - $changes[] = DI::l10n()->t('Dislikes'); - } - if ($work != $orig[0]['work']) { - $changes[] = DI::l10n()->t('Work/Employment'); - } - if ($religion != $orig[0]['religion']) { - $changes[] = DI::l10n()->t('Religion'); - } - if ($politic != $orig[0]['politic']) { - $changes[] = DI::l10n()->t('Political Views'); - } - if ($gender != $orig[0]['gender']) { - $changes[] = DI::l10n()->t('Gender'); - } - if ($sexual != $orig[0]['sexual']) { - $changes[] = DI::l10n()->t('Sexual Preference'); - } - if ($xmpp != $orig[0]['xmpp']) { - $changes[] = DI::l10n()->t('XMPP'); - } - if ($homepage != $orig[0]['homepage']) { - $changes[] = DI::l10n()->t('Homepage'); - } - if ($interest != $orig[0]['interest']) { - $changes[] = DI::l10n()->t('Interests'); - } - if ($address != $orig[0]['address']) { - $changes[] = DI::l10n()->t('Address'); - // New address not sent in notifications, potential privacy issues - // in case this leaks to unintended recipients. Yes, it's in the public - // profile but that doesn't mean we have to broadcast it to everybody. - } - if ($locality != $orig[0]['locality'] || $region != $orig[0]['region'] - || $country_name != $orig[0]['country-name']) { - $changes[] = DI::l10n()->t('Location'); - } - } - - $r = q("UPDATE `profile` - SET `profile-name` = '%s', - `name` = '%s', - `pdesc` = '%s', - `gender` = '%s', - `dob` = '%s', - `address` = '%s', - `locality` = '%s', - `region` = '%s', - `postal-code` = '%s', - `country-name` = '%s', - `marital` = '%s', - `with` = '%s', - `howlong` = '%s', - `sexual` = '%s', - `xmpp` = '%s', - `homepage` = '%s', - `hometown` = '%s', - `politic` = '%s', - `religion` = '%s', - `pub_keywords` = '%s', - `prv_keywords` = '%s', - `likes` = '%s', - `dislikes` = '%s', - `about` = '%s', - `interest` = '%s', - `contact` = '%s', - `music` = '%s', - `book` = '%s', - `tv` = '%s', - `film` = '%s', - `romance` = '%s', - `work` = '%s', - `education` = '%s', - `hide-friends` = %d - WHERE `id` = %d AND `uid` = %d", - DBA::escape($profile_name), - DBA::escape($name), - DBA::escape($pdesc), - DBA::escape($gender), - DBA::escape($dob), - DBA::escape($address), - DBA::escape($locality), - DBA::escape($region), - DBA::escape($postal_code), - DBA::escape($country_name), - DBA::escape($marital), - DBA::escape($with), - DBA::escape($howlong), - DBA::escape($sexual), - DBA::escape($xmpp), - DBA::escape($homepage), - DBA::escape($hometown), - DBA::escape($politic), - DBA::escape($religion), - DBA::escape($pub_keywords), - DBA::escape($prv_keywords), - DBA::escape($likes), - DBA::escape($dislikes), - DBA::escape($about), - DBA::escape($interest), - DBA::escape($contact), - DBA::escape($music), - DBA::escape($book), - DBA::escape($tv), - DBA::escape($film), - DBA::escape($romance), - DBA::escape($work), - DBA::escape($education), - intval($hide_friends), - intval($a->argv[1]), - intval(local_user()) - ); - - /// @TODO decide to use DBA::isResult() here and check $r - if ($r) { - info(DI::l10n()->t('Profile updated.') . EOL); - } - - if ($is_default) { - if ($namechanged) { - q("UPDATE `user` set `username` = '%s' where `uid` = %d", - DBA::escape($name), - intval(local_user()) - ); - } - - Contact::updateSelfFromUserID(local_user()); - - // Update global directory in background - $url = $_SESSION['my_url']; - if ($url && strlen(DI::config()->get('system', 'directory'))) { - Worker::add(PRIORITY_LOW, "Directory", $url); - } - - Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user()); - - // Update the global contact for the user - GContact::updateForUser(local_user()); - } - } -} - -function profiles_content(App $a) { - - if (! local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); - return Login::form(); - } - - $o = ''; - - if (($a->argc > 1) && (intval($a->argv[1]))) { - $r = q("SELECT * FROM `profile` WHERE `id` = %d AND `uid` = %d LIMIT 1", - intval($a->argv[1]), - intval(local_user()) - ); - if (! DBA::isResult($r)) { - notice(DI::l10n()->t('Profile not found.') . EOL); - return; - } - - DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('profed_head.tpl'), [ - '$baseurl' => DI::baseUrl()->get(true), - ]); - - $opt_tpl = Renderer::getMarkupTemplate("profile-hide-friends.tpl"); - $hide_friends = Renderer::replaceMacros($opt_tpl,[ - '$yesno' => [ - 'hide-friends', //Name - DI::l10n()->t('Hide contacts and friends:'), //Label - !!$r[0]['hide-friends'], //Value - '', //Help string - [DI::l10n()->t('No'), DI::l10n()->t('Yes')] //Off - On strings - ], - '$desc' => DI::l10n()->t('Hide your contact/friend list from viewers of this profile?'), - '$yes_str' => DI::l10n()->t('Yes'), - '$no_str' => DI::l10n()->t('No'), - '$yes_selected' => (($r[0]['hide-friends']) ? " checked=\"checked\" " : ""), - '$no_selected' => (($r[0]['hide-friends'] == 0) ? " checked=\"checked\" " : "") - ]); - - $personal_account = !(in_array($a->user["page-flags"], - [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP])); - - $detailed_profile = (DI::pConfig()->get(local_user(), 'system', 'detailled_profile') AND $personal_account); - - $is_default = (($r[0]['is-default']) ? 1 : 0); - $tpl = Renderer::getMarkupTemplate("profile_edit.tpl"); - $o .= Renderer::replaceMacros($tpl, [ - '$personal_account' => $personal_account, - '$detailled_profile' => $detailed_profile, - - '$details' => [ - 'detailed_profile', //Name - DI::l10n()->t('Show more profile fields:'), //Label - $detailed_profile, //Value - '', //Help string - [DI::l10n()->t('No'), DI::l10n()->t('Yes')] //Off - On strings - ], - - '$multi_profiles' => Feature::isEnabled(local_user(), 'multi_profiles'), - '$form_security_token' => BaseModule::getFormSecurityToken("profile_edit"), - '$form_security_token_photo' => BaseModule::getFormSecurityToken("profile_photo"), - '$profile_clone_link' => ((Feature::isEnabled(local_user(), 'multi_profiles')) ? 'profiles/clone/' . $r[0]['id'] . '?t=' . BaseModule::getFormSecurityToken("profile_clone") : ""), - '$profile_drop_link' => 'profiles/drop/' . $r[0]['id'] . '?t=' . BaseModule::getFormSecurityToken("profile_drop"), - - '$profile_action' => DI::l10n()->t('Profile Actions'), - '$banner' => DI::l10n()->t('Edit Profile Details'), - '$submit' => DI::l10n()->t('Submit'), - '$profpic' => DI::l10n()->t('Change Profile Photo'), - '$profpiclink' => '/photos/' . $a->user['nickname'], - '$viewprof' => DI::l10n()->t('View this profile'), - '$viewallprof' => DI::l10n()->t('View all profiles'), - '$editvis' => DI::l10n()->t('Edit visibility'), - '$cr_prof' => DI::l10n()->t('Create a new profile using these settings'), - '$cl_prof' => DI::l10n()->t('Clone this profile'), - '$del_prof' => DI::l10n()->t('Delete this profile'), - - '$lbl_basic_section' => DI::l10n()->t('Basic information'), - '$lbl_picture_section' => DI::l10n()->t('Profile picture'), - '$lbl_location_section' => DI::l10n()->t('Location'), - '$lbl_preferences_section' => DI::l10n()->t('Preferences'), - '$lbl_status_section' => DI::l10n()->t('Status information'), - '$lbl_about_section' => DI::l10n()->t('Additional information'), - '$lbl_interests_section' => DI::l10n()->t('Interests'), - '$lbl_personal_section' => DI::l10n()->t('Personal'), - '$lbl_relation_section' => DI::l10n()->t('Relation'), - '$lbl_miscellaneous_section' => DI::l10n()->t('Miscellaneous'), - - '$lbl_profile_photo' => DI::l10n()->t('Upload Profile Photo'), - '$lbl_gender' => DI::l10n()->t('Your Gender:'), - '$lbl_marital' => DI::l10n()->t(' Marital Status:'), - '$lbl_sexual' => DI::l10n()->t('Sexual Preference:'), - '$lbl_ex2' => DI::l10n()->t('Example: fishing photography software'), - - '$disabled' => (($is_default) ? 'onclick="return false;" style="color: #BBBBFF;"' : ''), - '$baseurl' => DI::baseUrl()->get(true), - '$profile_id' => $r[0]['id'], - '$profile_name' => ['profile_name', DI::l10n()->t('Profile Name:'), $r[0]['profile-name'], DI::l10n()->t('Required'), '*'], - '$is_default' => $is_default, - '$default' => (($is_default) ? '

' . DI::l10n()->t('This is your public profile.
It may be visible to anybody using the internet.') . '

' : ""), - '$name' => ['name', DI::l10n()->t('Your Full Name:'), $r[0]['name']], - '$pdesc' => ['pdesc', DI::l10n()->t('Title/Description:'), $r[0]['pdesc']], - '$dob' => Temporal::getDateofBirthField($r[0]['dob'], $a->user['timezone']), - '$hide_friends' => $hide_friends, - '$address' => ['address', DI::l10n()->t('Street Address:'), $r[0]['address']], - '$locality' => ['locality', DI::l10n()->t('Locality/City:'), $r[0]['locality']], - '$region' => ['region', DI::l10n()->t('Region/State:'), $r[0]['region']], - '$postal_code' => ['postal_code', DI::l10n()->t('Postal/Zip Code:'), $r[0]['postal-code']], - '$country_name' => ['country_name', DI::l10n()->t('Country:'), $r[0]['country-name']], - '$age' => ((intval($r[0]['dob'])) ? '(' . DI::l10n()->t('Age: ') . DI::l10n()->tt('%d year old', '%d years old', Temporal::getAgeByTimezone($r[0]['dob'], $a->user['timezone'])) . ')' : ''), - '$gender' => DI::l10n()->t(ContactSelector::gender($r[0]['gender'])), - '$marital' => ['selector' => ContactSelector::maritalStatus($r[0]['marital']), 'value' => DI::l10n()->t($r[0]['marital'])], - '$with' => ['with', DI::l10n()->t("Who: \x28if applicable\x29"), strip_tags($r[0]['with']), DI::l10n()->t('Examples: cathy123, Cathy Williams, cathy@example.com')], - '$howlong' => ['howlong', DI::l10n()->t('Since [date]:'), ($r[0]['howlong'] <= DBA::NULL_DATETIME ? '' : DateTimeFormat::local($r[0]['howlong']))], - '$sexual' => ['selector' => ContactSelector::sexualPreference($r[0]['sexual']), 'value' => DI::l10n()->t($r[0]['sexual'])], - '$about' => ['about', DI::l10n()->t('Tell us about yourself...'), $r[0]['about']], - '$xmpp' => ['xmpp', DI::l10n()->t("XMPP \x28Jabber\x29 address:"), $r[0]['xmpp'], DI::l10n()->t("The XMPP address will be propagated to your contacts so that they can follow you.")], - '$homepage' => ['homepage', DI::l10n()->t('Homepage URL:'), $r[0]['homepage']], - '$hometown' => ['hometown', DI::l10n()->t('Hometown:'), $r[0]['hometown']], - '$politic' => ['politic', DI::l10n()->t('Political Views:'), $r[0]['politic']], - '$religion' => ['religion', DI::l10n()->t('Religious Views:'), $r[0]['religion']], - '$pub_keywords' => ['pub_keywords', DI::l10n()->t('Public Keywords:'), $r[0]['pub_keywords'], DI::l10n()->t("\x28Used for suggesting potential friends, can be seen by others\x29")], - '$prv_keywords' => ['prv_keywords', DI::l10n()->t('Private Keywords:'), $r[0]['prv_keywords'], DI::l10n()->t("\x28Used for searching profiles, never shown to others\x29")], - '$likes' => ['likes', DI::l10n()->t('Likes:'), $r[0]['likes']], - '$dislikes' => ['dislikes', DI::l10n()->t('Dislikes:'), $r[0]['dislikes']], - '$music' => ['music', DI::l10n()->t('Musical interests'), $r[0]['music']], - '$book' => ['book', DI::l10n()->t('Books, literature'), $r[0]['book']], - '$tv' => ['tv', DI::l10n()->t('Television'), $r[0]['tv']], - '$film' => ['film', DI::l10n()->t('Film/dance/culture/entertainment'), $r[0]['film']], - '$interest' => ['interest', DI::l10n()->t('Hobbies/Interests'), $r[0]['interest']], - '$romance' => ['romance', DI::l10n()->t('Love/romance'), $r[0]['romance']], - '$work' => ['work', DI::l10n()->t('Work/employment'), $r[0]['work']], - '$education' => ['education', DI::l10n()->t('School/education'), $r[0]['education']], - '$contact' => ['contact', DI::l10n()->t('Contact information and Social Networks'), $r[0]['contact']], - ]); - - $arr = ['profile' => $r[0], 'entry' => $o]; - Hook::callAll('profile_edit', $arr); - - return $o; - } else { - // If we don't support multi profiles, don't display this list. - if (!Feature::isEnabled(local_user(), 'multi_profiles')) { - $r = q("SELECT * FROM `profile` WHERE `uid` = %d AND `is-default`=1", - local_user() - ); - if (DBA::isResult($r)) { - //Go to the default profile. - DI::baseUrl()->redirect('profiles/' . $r[0]['id']); - } - } - - $r = q("SELECT * FROM `profile` WHERE `uid` = %d", - local_user()); - - if (DBA::isResult($r)) { - - $tpl = Renderer::getMarkupTemplate('profile_entry.tpl'); - - $profiles = ''; - foreach ($r as $rr) { - $profiles .= Renderer::replaceMacros($tpl, [ - '$photo' => DI::baseUrl()->remove($rr['thumb']), - '$id' => $rr['id'], - '$alt' => DI::l10n()->t('Profile Image'), - '$profile_name' => $rr['profile-name'], - '$visible' => (($rr['is-default']) ? '' . DI::l10n()->t('visible to everybody') . '' - : '' . DI::l10n()->t('Edit visibility') . '') - ]); - } - - $tpl_header = Renderer::getMarkupTemplate('profile_listing_header.tpl'); - $o .= Renderer::replaceMacros($tpl_header,[ - '$header' => DI::l10n()->t('Edit/Manage Profiles'), - '$chg_photo' => DI::l10n()->t('Change profile photo'), - '$cr_new' => DI::l10n()->t('Create New Profile'), - '$cr_new_link' => 'profiles/new?t=' . BaseModule::getFormSecurityToken("profile_new"), - '$profiles' => $profiles - ]); - } - return $o; - } - -} diff --git a/mod/profperm.php b/mod/profperm.php deleted file mode 100644 index f17d1d66b7..0000000000 --- a/mod/profperm.php +++ /dev/null @@ -1,171 +0,0 @@ -user['nickname']; - $profile = $a->argv[1]; - - Profile::load($a, $which, $profile); -} - - -function profperm_content(App $a) { - - if (!local_user()) { - notice(DI::l10n()->t('Permission denied') . EOL); - return; - } - - - if ($a->argc < 2) { - notice(DI::l10n()->t('Invalid profile identifier.') . EOL ); - return; - } - - $o = ''; - - // Switch to text mod interface if we have more than 'n' contacts or group members - - $switchtotext = DI::pConfig()->get(local_user(),'system','groupedit_image_limit'); - if (is_null($switchtotext)) { - $switchtotext = DI::config()->get('system','groupedit_image_limit', 400); - } - - 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` = '%s' AND `id` = %d AND `uid` = %d LIMIT 1", - DBA::escape(Protocol::DFRN), - intval($a->argv[2]), - intval(local_user()) - ); - - if (DBA::isResult($r)) { - $change = intval($a->argv[2]); - } - } - - - if (($a->argc > 1) && (intval($a->argv[1]))) { - $r = q("SELECT * FROM `profile` WHERE `id` = %d AND `uid` = %d AND `is-default` = 0 LIMIT 1", - intval($a->argv[1]), - intval(local_user()) - ); - if (!DBA::isResult($r)) { - notice(DI::l10n()->t('Invalid profile identifier.') . EOL ); - return; - } - $profile = $r[0]; - - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `profile-id` = %d", - intval(local_user()), - intval($a->argv[1]) - ); - - $ingroup = []; - if (DBA::isResult($r)) - foreach($r as $member) - $ingroup[] = $member['id']; - - $members = $r; - - if (!empty($change)) { - if (in_array($change,$ingroup)) { - q("UPDATE `contact` SET `profile-id` = 0 WHERE `id` = %d AND `uid` = %d", - intval($change), - intval(local_user()) - ); - } - else { - q("UPDATE `contact` SET `profile-id` = %d WHERE `id` = %d AND `uid` = %d", - intval($a->argv[1]), - intval($change), - intval(local_user()) - ); - - } - - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `profile-id` = %d", - intval(local_user()), - intval($a->argv[1]) - ); - - $members = $r; - - $ingroup = []; - if (DBA::isResult($r)) - foreach($r as $member) - $ingroup[] = $member['id']; - } - - $o .= '

' . DI::l10n()->t('Profile Visibility Editor') . '

'; - - $o .= '

' . DI::l10n()->t('Profile') . ' \'' . $profile['profile-name'] . '\'

'; - - $o .= '
' . DI::l10n()->t('Click on a contact to add or remove.') . '
'; - - } - - $o .= '
'; - if (!empty($change)) - $o = ''; - - $o .= '
'; - $o .= '

' . DI::l10n()->t('Visible To') . '

'; - $o .= '
'; - $o .= '
'; - - $textmode = (($switchtotext && (count($members) > $switchtotext)) ? true : false); - - foreach($members as $member) { - if ($member['url']) { - $member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['id'] . '); return true;'; - $o .= HTML::micropro($member,true,'mpprof', $textmode); - } - } - $o .= '
'; - $o .= '
'; - - $o .= '
'; - $o .= '

' . DI::l10n()->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` = '%s' ORDER BY `name` ASC", - intval(local_user()), - DBA::escape(Protocol::DFRN) - ); - - if (DBA::isResult($r)) { - $textmode = (($switchtotext && (count($r) > $switchtotext)) ? true : false); - foreach($r as $member) { - if (!in_array($member['id'],$ingroup)) { - $member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['id'] . '); return true;'; - $o .= HTML::micropro($member,true,'mpprof',$textmode); - } - } - } - - $o .= '
'; - - if (!empty($change)) { - echo $o; - exit(); - } - $o .= '
'; - return $o; - -} diff --git a/mod/regmod.php b/mod/regmod.php index a3039b7984..ed030723d3 100644 --- a/mod/regmod.php +++ b/mod/regmod.php @@ -27,7 +27,7 @@ function user_allow($hash) DBA::update('user', ['blocked' => false, 'verified' => true], ['uid' => $register['uid']]); - $profile = DBA::selectFirst('profile', ['net-publish'], ['uid' => $register['uid'], 'is-default' => true]); + $profile = DBA::selectFirst('profile', ['net-publish'], ['uid' => $register['uid']]); if (DBA::isResult($profile) && $profile['net-publish'] && DI::config()->get('system', 'directory')) { $url = DI::baseUrl() . '/profile/' . $user['nickname']; diff --git a/mod/settings.php b/mod/settings.php index 00991d8919..3ba5dc2b37 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -73,9 +73,9 @@ function settings_init(App $a) ]; $tabs[] = [ - 'label' => DI::l10n()->t('Profiles'), - 'url' => 'profiles', - 'selected' => (($a->argc == 1) && ($a->argv[0] === 'profiles')?'active':''), + 'label' => DI::l10n()->t('Profile'), + 'url' => 'settings/profile', + 'selected' => (($a->argc > 1) && ($a->argv[1] === 'profile')?'active':''), 'accesskey' => 'p', ]; @@ -611,7 +611,7 @@ function settings_post(App $a) `name` = '%s', `net-publish` = %d, `hide-friends` = %d - WHERE `is-default` = 1 AND `uid` = %d", + WHERE `uid` = %d", intval($publish), DBA::escape($username), intval($net_publish), @@ -978,7 +978,7 @@ function settings_content(App $a) * ACCOUNT SETTINGS */ - $profile = DBA::selectFirst('profile', [], ['is-default' => true, 'uid' => local_user()]); + $profile = DBA::selectFirst('profile', [], ['uid' => local_user()]); if (!DBA::isResult($profile)) { notice(DI::l10n()->t('Unable to find your profile. Please contact your admin.') . EOL); return; diff --git a/mod/update_profile.php b/mod/update_profile.php deleted file mode 100644 index 348a384c2e..0000000000 --- a/mod/update_profile.php +++ /dev/null @@ -1,49 +0,0 @@ -\r\n"; - - // We can remove this hack once Internet Explorer recognises HTML5 natively - echo "
"; - - /** - * Grab the page inner contents by calling the content function from the profile module directly, - * but move any image src attributes to another attribute name. This is because - * some browsers will prefetch all the images for the page even if we don't need them. - * The only ones we need to fetch are those for new page additions, which we'll discover - * on the client side and then swap the image back. - */ - - $text = Profile::content([], $profile_uid); - - if (DI::pConfig()->get(local_user(), "system", "bandwidth_saver")) { - $replace = "
" . DI::l10n()->t("[Embedded content - reload page to view]") . "
"; - $pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i"; - $text = preg_replace($pattern, $replace, $text); - } - - // reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well - echo str_replace("\t", " ", $text); - echo "
"; - echo "\r\n"; - exit(); -} \ No newline at end of file diff --git a/src/BaseCollection.php b/src/BaseCollection.php index 4d5803d585..5a20acee7f 100644 --- a/src/BaseCollection.php +++ b/src/BaseCollection.php @@ -24,7 +24,6 @@ abstract class BaseCollection extends \ArrayIterator { parent::__construct($models); - $this->models = $models; $this->totalCount = $totalCount ?? count($models); } diff --git a/src/Collection/ProfileFields.php b/src/Collection/ProfileFields.php new file mode 100644 index 0000000000..16c267621b --- /dev/null +++ b/src/Collection/ProfileFields.php @@ -0,0 +1,27 @@ +\r\n"; - - $s = DBA::select('profile', ['id', 'profile-name', 'is-default'], ['uid' => $_SESSION['uid']]); - $r = DBA::toArray($s); - - if (DBA::isResult($r)) { - foreach ($r as $rr) { - $selected = (($rr['id'] == $current || ($current == 0 && $rr['is-default'] == 1)) ? " selected=\"selected\" " : ""); - $o .= "\r\n"; - } - } - $o .= "\r\n"; - return $o; - } - /** * @param string $current current * @param boolean $disabled optional, default false diff --git a/src/Content/Feature.php b/src/Content/Feature.php index 91a319677f..59eea16377 100644 --- a/src/Content/Feature.php +++ b/src/Content/Feature.php @@ -79,7 +79,6 @@ class Feature 'general' => [ DI::l10n()->t('General Features'), //array('expire', DI::l10n()->t('Content Expiration'), DI::l10n()->t('Remove old posts/comments after a period of time')), - ['multi_profiles', DI::l10n()->t('Multiple Profiles'), DI::l10n()->t('Ability to create multiple profiles'), false, DI::config()->get('feature_lock', 'multi_profiles', false)], ['photo_location', DI::l10n()->t('Photo Location'), DI::l10n()->t("Photo metadata is normally stripped. This extracts the location \x28if present\x29 prior to stripping metadata and links it to a map."), false, DI::config()->get('feature_lock', 'photo_location', false)], ['export_calendar', DI::l10n()->t('Export Public Calendar'), DI::l10n()->t('Ability for visitors to download the public calendar'), false, DI::config()->get('feature_lock', 'export_calendar', false)], ['trending_tags', DI::l10n()->t('Trending Tags'), DI::l10n()->t('Show a community page widget with a list of the most popular tags in recent public posts.'), false, DI::config()->get('feature_lock', 'trending_tags', false)], diff --git a/src/Content/Nav.php b/src/Content/Nav.php index 6b4de60361..3024fcaea1 100644 --- a/src/Content/Nav.php +++ b/src/Content/Nav.php @@ -258,10 +258,6 @@ class Nav $nav['settings'] = ['settings', DI::l10n()->t('Settings'), '', DI::l10n()->t('Account settings')]; - if (Feature::isEnabled(local_user(), 'multi_profiles')) { - $nav['profiles'] = ['profiles', DI::l10n()->t('Profiles'), '', DI::l10n()->t('Manage/Edit Profiles')]; - } - $nav['contacts'] = ['contact', DI::l10n()->t('Contacts'), '', DI::l10n()->t('Manage/edit friends and contacts')]; } diff --git a/src/Core/UserImport.php b/src/Core/UserImport.php index c77674127a..f09db98686 100644 --- a/src/Core/UserImport.php +++ b/src/Core/UserImport.php @@ -8,8 +8,10 @@ use Friendica\App; use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\DI; +use Friendica\Model\Contact; use Friendica\Model\Photo; use Friendica\Object\Image; +use Friendica\Repository\PermissionSet; use Friendica\Util\Strings; use Friendica\Worker\Delivery; @@ -161,23 +163,6 @@ class UserImport DI::pConfig()->set($newuid, 'system', 'previous_addr', $old_handle); - foreach ($account['profile'] as &$profile) { - foreach ($profile as $k => &$v) { - $v = str_replace([$oldbaseurl, $oldaddr], [$newbaseurl, $newaddr], $v); - foreach (["profile", "avatar"] as $k) { - $v = str_replace($oldbaseurl . "/photo/" . $k . "/" . $olduid . ".jpg", $newbaseurl . "/photo/" . $k . "/" . $newuid . ".jpg", $v); - } - } - $profile['uid'] = $newuid; - $r = self::dbImportAssoc('profile', $profile); - if ($r === false) { - Logger::log("uimport:insert profile " . $profile['profile-name'] . " : ERROR : " . DBA::errorMessage(), Logger::INFO); - info(DI::l10n()->t("User profile creation error")); - DBA::delete('user', ['uid' => $newuid]); - return; - } - } - $errorcount = 0; foreach ($account['contact'] as &$contact) { if ($contact['uid'] == $olduid && $contact['self'] == '1') { @@ -253,6 +238,50 @@ class UserImport } } + foreach ($account['profile'] as &$profile) { + unset($profile['id']); + $profile['uid'] = $newuid; + + foreach ($profile as $k => &$v) { + $v = str_replace([$oldbaseurl, $oldaddr], [$newbaseurl, $newaddr], $v); + foreach (["profile", "avatar"] as $k) { + $v = str_replace($oldbaseurl . "/photo/" . $k . "/" . $olduid . ".jpg", $newbaseurl . "/photo/" . $k . "/" . $newuid . ".jpg", $v); + } + } + + if (count($account['profile']) === 1 || $profile['is-default']) { + $r = self::dbImportAssoc('profile', $profile); + + if ($r === false) { + Logger::log("uimport:insert profile: ERROR : " . DBA::errorMessage(), Logger::INFO); + info(DI::l10n()->t("User profile creation error")); + DBA::delete('user', ['uid' => $newuid]); + DBA::delete('profile_field', ['uid' => $newuid]); + return; + } + + $profile['id'] = DBA::lastInsertId(); + } + + DI::profileField()->migrateFromLegacyProfile($profile); + } + + ///@TODO Replace with permissionset import + $self_contact = Contact::selectFirst(['id'], ['uid' => $newuid, 'self' => true]); + $allow_cid = DI::aclFormatter()->toString($self_contact['id']); + $self_psid = DI::permissionSet()->getIdFromACL($newuid, $allow_cid); + + foreach ($account['profile_fields'] ?? [] as $profile_field) { + $profile_field['uid'] = $newuid; + + ///@TODO Replace with permissionset import + $profile_field['psid'] = $profile_field['psid'] ? $self_psid : PermissionSet::PUBLIC; + + if (self::dbImportAssoc('profile_field', $profile_field) === false) { + Logger::info("uimport:insert profile field " . $profile_field['id'] . " : ERROR : " . DBA::errorMessage()); + } + } + foreach ($account['photo'] as &$photo) { $photo['uid'] = $newuid; $photo['data'] = hex2bin($photo['data']); diff --git a/src/DI.php b/src/DI.php index 67e0b17315..530d79580c 100644 --- a/src/DI.php +++ b/src/DI.php @@ -292,6 +292,14 @@ abstract class DI return self::$dice->create(Repository\PermissionSet::class); } + /** + * @return Repository\ProfileField + */ + public static function profileField() + { + return self::$dice->create(Repository\ProfileField::class); + } + // // "Protocol" namespace instances // diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 08c67f8efa..d20872ed64 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -723,7 +723,7 @@ class Contact $fields = ['name', 'photo', 'thumb', 'about', 'address', 'locality', 'region', 'country-name', 'gender', 'pub_keywords', 'xmpp', 'net-publish']; - $profile = DBA::selectFirst('profile', $fields, ['uid' => $uid, 'is-default' => true]); + $profile = DBA::selectFirst('profile', $fields, ['uid' => $uid]); if (!DBA::isResult($profile)) { return; } @@ -800,7 +800,7 @@ class Contact // Update the profile $fields = ['photo' => DI::baseUrl() . '/photo/profile/' .$uid . '.' . $file_suffix, 'thumb' => DI::baseUrl() . '/photo/avatar/' . $uid .'.' . $file_suffix]; - DBA::update('profile', $fields, ['uid' => $uid, 'is-default' => true]); + DBA::update('profile', $fields, ['uid' => $uid]); } } diff --git a/src/Model/Profile.php b/src/Model/Profile.php index b993562cec..86fb1a94cb 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -5,10 +5,7 @@ namespace Friendica\Model; use Friendica\App; -use Friendica\Content\Feature; -use Friendica\Content\ForumManager; use Friendica\Content\Text\BBCode; -use Friendica\Content\Text\HTML; use Friendica\Content\Widget\ContactBlock; use Friendica\Core\Cache\Duration; use Friendica\Core\Hook; @@ -25,7 +22,6 @@ use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; -use Friendica\Util\Temporal; class Profile { @@ -39,8 +35,7 @@ class Profile */ public static function getByUID($uid) { - $profile = DBA::selectFirst('profile', [], ['uid' => $uid, 'is-default' => true]); - return $profile; + return DBA::selectFirst('profile', [], ['uid' => $uid]); } /** @@ -155,7 +150,7 @@ class Profile } } - $profile = self::getByNickname($nickname, $user['uid'], $profile_id); + $profile = self::getByNickname($nickname, $user['uid']); if (empty($profile) && empty($profiledata)) { Logger::log('profile error: ' . DI::args()->getQueryString(), Logger::DEBUG); @@ -163,17 +158,7 @@ class Profile } if (empty($profile)) { - $profile = ['uid' => 0, 'is-default' => false,'name' => $nickname]; - } - - // fetch user tags if this isn't the default profile - - if (!$profile['is-default']) { - $condition = ['uid' => $profile['uid'], 'is-default' => true]; - $profile_id = DBA::selectFirst('profile', ['pub_keywords'], $condition); - if (DBA::isResult($profile_id)) { - $profile['pub_keywords'] = $profile_id['pub_keywords']; - } + $profile = ['uid' => 0, 'name' => $nickname]; } $a->profile = $profile; @@ -202,7 +187,7 @@ class Profile if (local_user() && local_user() == $a->profile['uid'] && $profiledata) { DI::page()['aside'] .= Renderer::replaceMacros( - Renderer::getMarkupTemplate('profile_edlink.tpl'), + Renderer::getMarkupTemplate('settings/profile/link.tpl'), [ '$editprofile' => DI::l10n()->t('Edit profile'), '$profid' => $a->profile['id'] @@ -240,44 +225,20 @@ class Profile * @return array * @throws \Exception */ - public static function getByNickname($nickname, $uid = 0, $profile_id = 0) + public static function getByNickname($nickname, $uid = 0) { - if (!empty(Session::getRemoteContactID($uid))) { - $contact = DBA::selectFirst('contact', ['profile-id'], ['id' => Session::getRemoteContactID($uid)]); - if (DBA::isResult($contact)) { - $profile_id = $contact['profile-id']; - } - } - - $profile = null; - - if ($profile_id) { - $profile = DBA::fetchFirst( - "SELECT `contact`.`id` AS `contact_id`, `contact`.`photo` AS `contact_photo`, - `contact`.`thumb` AS `contact_thumb`, `contact`.`micro` AS `contact_micro`, - `profile`.*, - `contact`.`avatar-date` AS picdate, `contact`.`addr`, `contact`.`url`, `user`.* - FROM `profile` - INNER JOIN `contact` on `contact`.`uid` = `profile`.`uid` AND `contact`.`self` - INNER JOIN `user` ON `profile`.`uid` = `user`.`uid` - WHERE `user`.`nickname` = ? AND `profile`.`id` = ? LIMIT 1", - $nickname, - intval($profile_id) - ); - } - if (!DBA::isResult($profile)) { - $profile = DBA::fetchFirst( - "SELECT `contact`.`id` AS `contact_id`, `contact`.`photo` as `contact_photo`, - `contact`.`thumb` AS `contact_thumb`, `contact`.`micro` AS `contact_micro`, - `profile`.*, - `contact`.`avatar-date` AS picdate, `contact`.`addr`, `contact`.`url`, `user`.* - FROM `profile` - INNER JOIN `contact` ON `contact`.`uid` = `profile`.`uid` AND `contact`.`self` - INNER JOIN `user` ON `profile`.`uid` = `user`.`uid` - WHERE `user`.`nickname` = ? AND `profile`.`is-default` LIMIT 1", - $nickname - ); - } + $profile = DBA::fetchFirst( + "SELECT `contact`.`id` AS `contact_id`, `contact`.`photo` AS `contact_photo`, + `contact`.`thumb` AS `contact_thumb`, `contact`.`micro` AS `contact_micro`, + `profile`.*, + `contact`.`avatar-date` AS picdate, `contact`.`addr`, `contact`.`url`, `user`.* + FROM `profile` + INNER JOIN `contact` on `contact`.`uid` = `profile`.`uid` AND `contact`.`self` + INNER JOIN `user` ON `profile`.`uid` = `user`.`uid` + WHERE `user`.`nickname` = ? AND `profile`.`uid` = ? LIMIT 1", + $nickname, + intval($uid) + ); return $profile; } @@ -388,40 +349,12 @@ class Profile // show edit profile to yourself if (!$is_contact && $local_user_is_self) { - if (Feature::isEnabled(local_user(), 'multi_profiles')) { - $profile['edit'] = [DI::baseUrl() . '/profiles', DI::l10n()->t('Profiles'), '', DI::l10n()->t('Manage/edit profiles')]; - $r = q( - "SELECT * FROM `profile` WHERE `uid` = %d", - local_user() - ); - - $profile['menu'] = [ - 'chg_photo' => DI::l10n()->t('Change profile photo'), - 'cr_new' => DI::l10n()->t('Create New Profile'), - 'entries' => [], - ]; - - if (DBA::isResult($r)) { - foreach ($r as $rr) { - $profile['menu']['entries'][] = [ - 'photo' => $rr['thumb'], - 'id' => $rr['id'], - 'alt' => DI::l10n()->t('Profile Image'), - 'profile_name' => $rr['profile-name'], - 'isdefault' => $rr['is-default'], - 'visibile_to_everybody' => DI::l10n()->t('visible to everybody'), - 'edit_visibility' => DI::l10n()->t('Edit visibility'), - ]; - } - } - } else { - $profile['edit'] = [DI::baseUrl() . '/profiles/' . $profile['id'], DI::l10n()->t('Edit profile'), '', DI::l10n()->t('Edit profile')]; - $profile['menu'] = [ - 'chg_photo' => DI::l10n()->t('Change profile photo'), - 'cr_new' => null, - 'entries' => [], - ]; - } + $profile['edit'] = [DI::baseUrl() . '/settings/profile', DI::l10n()->t('Edit profile'), '', DI::l10n()->t('Edit profile')]; + $profile['menu'] = [ + 'chg_photo' => DI::l10n()->t('Change profile photo'), + 'cr_new' => null, + 'entries' => [], + ]; } // Fetch the account type @@ -742,149 +675,6 @@ class Profile ]); } - public static function getAdvanced(App $a) - { - $uid = intval($a->profile['uid']); - - if ($a->profile['name']) { - $profile = []; - - $profile['fullname'] = [DI::l10n()->t('Full Name:'), $a->profile['name']]; - - if (Feature::isEnabled($uid, 'profile_membersince')) { - $profile['membersince'] = [DI::l10n()->t('Member since:'), DateTimeFormat::local($a->profile['register_date'])]; - } - - if ($a->profile['gender']) { - $profile['gender'] = [DI::l10n()->t('Gender:'), DI::l10n()->t($a->profile['gender'])]; - } - - if (!empty($a->profile['dob']) && $a->profile['dob'] > DBA::NULL_DATE) { - $year_bd_format = DI::l10n()->t('j F, Y'); - $short_bd_format = DI::l10n()->t('j F'); - - $val = DI::l10n()->getDay( - intval($a->profile['dob']) ? - DateTimeFormat::utc($a->profile['dob'] . ' 00:00 +00:00', $year_bd_format) - : DateTimeFormat::utc('2001-' . substr($a->profile['dob'], 5) . ' 00:00 +00:00', $short_bd_format) - ); - - $profile['birthday'] = [DI::l10n()->t('Birthday:'), $val]; - } - - if (!empty($a->profile['dob']) - && $a->profile['dob'] > DBA::NULL_DATE - && $age = Temporal::getAgeByTimezone($a->profile['dob'], $a->profile['timezone']) - ) { - $profile['age'] = [DI::l10n()->t('Age: ') , DI::l10n()->tt('%d year old', '%d years old', $age)]; - } - - if ($a->profile['marital']) { - $profile['marital'] = [DI::l10n()->t('Status:'), DI::l10n()->t($a->profile['marital'])]; - } - - /// @TODO Maybe use x() here, plus below? - if ($a->profile['with']) { - $profile['marital']['with'] = $a->profile['with']; - } - - if (strlen($a->profile['howlong']) && $a->profile['howlong'] > DBA::NULL_DATETIME) { - $profile['howlong'] = Temporal::getRelativeDate($a->profile['howlong'], DI::l10n()->t('for %1$d %2$s')); - } - - if ($a->profile['sexual']) { - $profile['sexual'] = [DI::l10n()->t('Sexual Preference:'), DI::l10n()->t($a->profile['sexual'])]; - } - - if ($a->profile['homepage']) { - $profile['homepage'] = [DI::l10n()->t('Homepage:'), HTML::toLink($a->profile['homepage'])]; - } - - if ($a->profile['hometown']) { - $profile['hometown'] = [DI::l10n()->t('Hometown:'), HTML::toLink($a->profile['hometown'])]; - } - - if ($a->profile['pub_keywords']) { - $profile['pub_keywords'] = [DI::l10n()->t('Tags:'), $a->profile['pub_keywords']]; - } - - if ($a->profile['politic']) { - $profile['politic'] = [DI::l10n()->t('Political Views:'), $a->profile['politic']]; - } - - if ($a->profile['religion']) { - $profile['religion'] = [DI::l10n()->t('Religion:'), $a->profile['religion']]; - } - - if ($txt = BBCode::convert($a->profile['about'])) { - $profile['about'] = [DI::l10n()->t('About:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['interest'])) { - $profile['interest'] = [DI::l10n()->t('Hobbies/Interests:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['likes'])) { - $profile['likes'] = [DI::l10n()->t('Likes:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['dislikes'])) { - $profile['dislikes'] = [DI::l10n()->t('Dislikes:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['contact'])) { - $profile['contact'] = [DI::l10n()->t('Contact information and Social Networks:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['music'])) { - $profile['music'] = [DI::l10n()->t('Musical interests:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['book'])) { - $profile['book'] = [DI::l10n()->t('Books, literature:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['tv'])) { - $profile['tv'] = [DI::l10n()->t('Television:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['film'])) { - $profile['film'] = [DI::l10n()->t('Film/dance/culture/entertainment:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['romance'])) { - $profile['romance'] = [DI::l10n()->t('Love/Romance:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['work'])) { - $profile['work'] = [DI::l10n()->t('Work/employment:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['education'])) { - $profile['education'] = [DI::l10n()->t('School/education:'), $txt]; - } - - //show subcribed forum if it is enabled in the usersettings - if (Feature::isEnabled($uid, 'forumlist_profile')) { - $profile['forumlist'] = [DI::l10n()->t('Forums:'), ForumManager::profileAdvanced($uid)]; - } - - if ($a->profile['uid'] == local_user()) { - $profile['edit'] = [DI::baseUrl() . '/profiles/' . $a->profile['id'], DI::l10n()->t('Edit profile'), '', DI::l10n()->t('Edit profile')]; - } - - $tpl = Renderer::getMarkupTemplate('profile/advanced.tpl'); - return Renderer::replaceMacros($tpl, [ - '$title' => DI::l10n()->t('Profile'), - '$basic' => DI::l10n()->t('Basic'), - '$advanced' => DI::l10n()->t('Advanced'), - '$profile' => $profile - ]); - } - - return ''; - } - /** * @param App $a * @param string $current @@ -902,22 +692,22 @@ class Profile $baseProfileUrl = DI::baseUrl() . '/profile/' . $nickname; $tabs = [ - [ - 'label' => DI::l10n()->t('Status'), - 'url' => $baseProfileUrl, - 'sel' => !$current ? 'active' : '', - 'title' => DI::l10n()->t('Status Messages and Posts'), - 'id' => 'status-tab', - 'accesskey' => 'm', - ], [ 'label' => DI::l10n()->t('Profile'), - 'url' => $baseProfileUrl . '/?tab=profile', + 'url' => $baseProfileUrl, 'sel' => $current == 'profile' ? 'active' : '', 'title' => DI::l10n()->t('Profile Details'), 'id' => 'profile-tab', 'accesskey' => 'r', ], + [ + 'label' => DI::l10n()->t('Status'), + 'url' => $baseProfileUrl . '/status', + 'sel' => $current == 'status' ? 'active' : '', + 'title' => DI::l10n()->t('Status Messages and Posts'), + 'id' => 'status-tab', + 'accesskey' => 'm', + ], [ 'label' => DI::l10n()->t('Photos'), 'url' => DI::baseUrl() . '/photos/' . $nickname, @@ -970,17 +760,7 @@ class Profile ]; } - if (!empty($_SESSION['new_member']) && $is_owner) { - $tabs[] = [ - 'label' => DI::l10n()->t('Tips for New Members'), - 'url' => DI::baseUrl() . '/newmember', - 'sel' => false, - 'title' => DI::l10n()->t('Tips for New Members'), - 'id' => 'newmember-tab', - ]; - } - - if ($is_owner || empty($a->profile['hide-friends'])) { + if (empty($a->profile['hide-friends'])) { $tabs[] = [ 'label' => DI::l10n()->t('Contacts'), 'url' => $baseProfileUrl . '/contacts', @@ -991,7 +771,18 @@ class Profile ]; } + if (!empty($_SESSION['new_member']) && $is_owner) { + $tabs[] = [ + 'label' => DI::l10n()->t('Tips for New Members'), + 'url' => DI::baseUrl() . '/newmember', + 'sel' => false, + 'title' => DI::l10n()->t('Tips for New Members'), + 'id' => 'newmember-tab', + ]; + } + $arr = ['is_owner' => $is_owner, 'nickname' => $nickname, 'tab' => $current, 'tabs' => $tabs]; + Hook::callAll('profile_tabs', $arr); $tpl = Renderer::getMarkupTemplate('common_tabs.tpl'); @@ -1229,7 +1020,7 @@ class Profile */ public static function searchProfiles($start = 0, $count = 100, $search = null) { - $publish = (DI::config()->get('system', 'publish_all') ? '' : " AND `publish` = 1 "); + $publish = (DI::config()->get('system', 'publish_all') ? '' : "`publish` = 1"); $total = 0; if (!empty($search)) { @@ -1237,7 +1028,7 @@ class Profile $cnt = DBA::fetchFirst("SELECT COUNT(*) AS `total` FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` - WHERE `is-default` $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` + WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND ((`profile`.`name` LIKE ?) OR (`user`.`nickname` LIKE ?) OR (`profile`.`pdesc` LIKE ?) OR @@ -1259,7 +1050,7 @@ class Profile $cnt = DBA::fetchFirst("SELECT COUNT(*) AS `total` FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` - WHERE `is-default` $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed`"); + WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed`"); } if (DBA::isResult($cnt)) { @@ -1279,7 +1070,7 @@ class Profile FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` LEFT JOIN `contact` ON `contact`.`uid` = `user`.`uid` - WHERE `is-default` $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `contact`.`self` + WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `contact`.`self` AND ((`profile`.`name` LIKE ?) OR (`user`.`nickname` LIKE ?) OR (`profile`.`pdesc` LIKE ?) OR @@ -1306,7 +1097,7 @@ class Profile FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` LEFT JOIN `contact` ON `contact`.`uid` = `user`.`uid` - WHERE `is-default` $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `contact`.`self` + WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `contact`.`self` $order LIMIT ?,?", $start, $count ); diff --git a/src/Model/ProfileField.php b/src/Model/ProfileField.php new file mode 100644 index 0000000000..e4192a9809 --- /dev/null +++ b/src/Model/ProfileField.php @@ -0,0 +1,59 @@ +permissionSetRepository = $permissionSetRepository; + } + + public function __get($name) + { + $this->checkValid(); + + switch ($name) { + case 'permissionset': + $this->permissionset = + $this->permissionset ?? + $this->permissionSetRepository->selectFirst(['id' => $this->psid, 'uid' => $this->uid]); + + $return = $this->permissionset; + break; + default: + $return = parent::__get($name); + break; + } + + return $return; + } +} diff --git a/src/Model/User.php b/src/Model/User.php index 0e8d11f733..fd7238819d 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -782,9 +782,7 @@ class User 'photo' => DI::baseUrl() . "/photo/profile/{$uid}.jpg", 'thumb' => DI::baseUrl() . "/photo/avatar/{$uid}.jpg", 'publish' => $publish, - 'is-default' => 1, 'net-publish' => $netpublish, - 'profile-name' => DI::l10n()->t('default') ]); if (!$insert_result) { DBA::delete('user', ['uid' => $uid]); @@ -1113,7 +1111,7 @@ class User $userStmt = DBA::p("SELECT `user`.`uid`, `user`.`login_date`, `contact`.`last-item` FROM `user` - INNER JOIN `profile` ON `profile`.`uid` = `user`.`uid` AND `profile`.`is-default` + INNER JOIN `profile` ON `profile`.`uid` = `user`.`uid` INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self` WHERE (`profile`.`publish` OR `profile`.`net-publish`) AND `user`.`verified` AND NOT `user`.`blocked` AND NOT `user`.`account_removed` diff --git a/src/Module/BaseSettingsModule.php b/src/Module/BaseSettingsModule.php index 2f51dde645..225f5ed4ae 100644 --- a/src/Module/BaseSettingsModule.php +++ b/src/Module/BaseSettingsModule.php @@ -35,9 +35,9 @@ class BaseSettingsModule extends BaseModule ]; $tabs[] = [ - 'label' => DI::l10n()->t('Profiles'), - 'url' => 'profiles', - 'selected' => (($a->argc == 1) && ($a->argv[0] === 'profiles') ? 'active' : ''), + 'label' => DI::l10n()->t('Profile'), + 'url' => 'settings/profile', + 'selected' => (($a->argc > 1) && ($a->argv[1] === 'profile') ? 'active' : ''), 'accesskey' => 'p', ]; diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 563adef74a..1cb0132a0e 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -100,14 +100,6 @@ class Contact extends BaseModule Hook::callAll('contact_edit_post', $_POST); - $profile_id = intval($_POST['profile-assign'] ?? 0); - if ($profile_id) { - if (!DBA::exists('profile', ['id' => $profile_id, 'uid' => local_user()])) { - notice(DI::l10n()->t('Could not locate selected profile.') . EOL); - return; - } - } - $hidden = !empty($_POST['hidden']); $notify = !empty($_POST['notify']); @@ -124,7 +116,6 @@ class Contact extends BaseModule $info = Strings::escapeHtml(trim($_POST['info'] ?? '')); $r = DBA::update('contact', [ - 'profile-id' => $profile_id, 'priority' => $priority, 'info' => $info, 'hidden' => $hidden, @@ -555,20 +546,13 @@ class Contact extends BaseModule $poll_interval = ContactSelector::pollInterval($contact['priority'], !$poll_enabled); } - $profile_select = null; - if ($contact['network'] == Protocol::DFRN) { - $profile_select = ContactSelector::profileAssign($contact['profile-id'], $contact['network'] !== Protocol::DFRN); - } - // Load contactact related actions like hide, suggest, delete and others $contact_actions = self::getContactActions($contact); if ($contact['uid'] != 0) { - $lbl_vis1 = DI::l10n()->t('Profile Visibility'); $lbl_info1 = DI::l10n()->t('Contact Information / Notes'); $contact_settings_label = DI::l10n()->t('Contact Settings'); } else { - $lbl_vis1 = null; $lbl_info1 = null; $contact_settings_label = null; } @@ -578,8 +562,6 @@ class Contact extends BaseModule '$header' => DI::l10n()->t('Contact'), '$tab_str' => $tab_str, '$submit' => DI::l10n()->t('Submit'), - '$lbl_vis1' => $lbl_vis1, - '$lbl_vis2' => DI::l10n()->t('Please choose the profile you would like to display to %s when viewing your profile securely.', $contact['name']), '$lbl_info1' => $lbl_info1, '$lbl_info2' => DI::l10n()->t('Their personal note'), '$reason' => trim(Strings::escapeTags($contact['reason'])), @@ -600,7 +582,6 @@ class Contact extends BaseModule '$updpub' => DI::l10n()->t('Update public posts'), '$last_update' => $last_update, '$udnow' => DI::l10n()->t('Update now'), - '$profile_select' => $profile_select, '$contact_id' => $contact['id'], '$block_text' => ($contact['blocked'] ? DI::l10n()->t('Unblock') : DI::l10n()->t('Block')), '$ignore_text' => ($contact['readonly'] ? DI::l10n()->t('Unignore') : DI::l10n()->t('Ignore')), diff --git a/src/Module/Profile.php b/src/Module/Profile.php deleted file mode 100644 index e3ae7b3f57..0000000000 --- a/src/Module/Profile.php +++ /dev/null @@ -1,361 +0,0 @@ -argc < 2) { - throw new \Friendica\Network\HTTPException\BadRequestException(); - } - - self::$which = filter_var($a->argv[1], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK); - - // @TODO: Replace with parameter from router - if (local_user() && $a->argc > 2 && $a->argv[2] === 'view') { - self::$which = $a->user['nickname']; - self::$profile = filter_var($a->argv[1], FILTER_SANITIZE_NUMBER_INT); - } - } - - public static function rawContent(array $parameters = []) - { - if (ActivityPub::isRequest()) { - $user = DBA::selectFirst('user', ['uid'], ['nickname' => self::$which]); - if (DBA::isResult($user)) { - // The function returns an empty array when the account is removed, expired or blocked - $data = ActivityPub\Transmitter::getProfile($user['uid']); - if (!empty($data)) { - System::jsonExit($data, 'application/activity+json'); - } - } - - if (DBA::exists('userd', ['username' => self::$which])) { - // Known deleted user - $data = ActivityPub\Transmitter::getDeletedUser(self::$which); - - System::jsonError(410, $data); - } else { - // Any other case (unknown, blocked, unverified, expired, no profile, no self contact) - System::jsonError(404, []); - } - } - } - - public static function content(array $parameters = [], $update = 0) - { - $a = DI::app(); - - if (!$update) { - ProfileModel::load($a, self::$which, self::$profile); - - $page = DI::page(); - - $page['htmlhead'] .= "\n"; - - $blocked = !local_user() && !Session::getRemoteContactID($a->profile['uid']) && DI::config()->get('system', 'block_public'); - $userblock = !local_user() && !Session::getRemoteContactID($a->profile['uid']) && $a->profile['hidewall']; - - if (!empty($a->profile['page-flags']) && $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY) { - $page['htmlhead'] .= '' . "\n"; - } - - if (!empty($a->profile['openidserver'])) { - $page['htmlhead'] .= '' . "\n"; - } - - if (!empty($a->profile['openid'])) { - $delegate = strstr($a->profile['openid'], '://') ? $a->profile['openid'] : 'https://' . $a->profile['openid']; - $page['htmlhead'] .= '' . "\n"; - } - - // site block - if (!$blocked && !$userblock) { - $keywords = str_replace(['#', ',', ' ', ',,'], ['', ' ', ',', ','], $a->profile['pub_keywords'] ?? ''); - if (strlen($keywords)) { - $page['htmlhead'] .= '' . "\n"; - } - } - - $page['htmlhead'] .= '' . "\n"; - - if (!$a->profile['net-publish'] || $a->profile['hidewall']) { - $page['htmlhead'] .= '' . "\n"; - } - - $page['htmlhead'] .= '' . "\n"; - $page['htmlhead'] .= '' . "\n"; - $page['htmlhead'] .= '' . "\n"; - $page['htmlhead'] .= '' . "\n"; - $uri = urlencode('acct:' . $a->profile['nickname'] . '@' . DI::baseUrl()->getHostname() . (DI::baseUrl()->getUrlPath() ? '/' . DI::baseUrl()->getUrlPath() : '')); - $page['htmlhead'] .= '' . "\n"; - header('Link: <' . DI::baseUrl() . '/xrd/?uri=' . $uri . '>; rel="lrdd"; type="application/xrd+xml"', false); - - $dfrn_pages = ['request', 'confirm', 'notify', 'poll']; - foreach ($dfrn_pages as $dfrn) { - $page['htmlhead'] .= '' . "\n"; - } - $page['htmlhead'] .= '' . "\n"; - } - - $category = $datequery = $datequery2 = ''; - - if ($a->argc > 2) { - for ($x = 2; $x < $a->argc; $x ++) { - if (DI::dtFormat()->isYearMonth($a->argv[$x])) { - if ($datequery) { - $datequery2 = Strings::escapeHtml($a->argv[$x]); - } else { - $datequery = Strings::escapeHtml($a->argv[$x]); - } - } else { - $category = $a->argv[$x]; - } - } - } - - if (empty($category)) { - $category = $_GET['category'] ?? ''; - } - - $hashtags = $_GET['tag'] ?? ''; - - if (DI::config()->get('system', 'block_public') && !local_user() && !Session::getRemoteContactID($a->profile['uid'])) { - return Login::form(); - } - - $o = ''; - - if ($update) { - // Ensure we've got a profile owner if updating. - $a->profile['uid'] = $update; - } elseif ($a->profile['uid'] == local_user()) { - Nav::setSelected('home'); - } - - $remote_contact = Session::getRemoteContactID($a->profile['uid']); - $is_owner = local_user() == $a->profile['uid']; - $last_updated_key = "profile:" . $a->profile['uid'] . ":" . local_user() . ":" . $remote_contact; - - if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact) { - notice(DI::l10n()->t('Access to this profile has been restricted.') . EOL); - return ''; - } - - if (!$update) { - $tab = Strings::escapeTags(trim($_GET['tab'] ?? '')); - - $o .= ProfileModel::getTabs($a, $tab, $is_owner, $a->profile['nickname']); - - if ($tab === 'profile') { - $o .= ProfileModel::getAdvanced($a); - Hook::callAll('profile_advanced', $o); - return $o; - } - - $o .= Widget::commonFriendsVisitor($a->profile['uid']); - - $commpage = $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY; - $commvisitor = $commpage && $remote_contact; - - DI::page()['aside'] .= Widget::postedByYear(DI::baseUrl()->get(true) . '/profile/' . $a->profile['nickname'], $a->profile['uid'] ?? 0, true); - DI::page()['aside'] .= Widget::categories(DI::baseUrl()->get(true) . '/profile/' . $a->profile['nickname'], XML::escape($category)); - DI::page()['aside'] .= Widget::tagCloud(); - - if (Security::canWriteToUserWall($a->profile['uid'])) { - $x = [ - 'is_owner' => $is_owner, - 'allow_location' => ($is_owner || $commvisitor) && $a->profile['allow_location'], - '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 ? ACL::getFullSelectorHTML(DI::page(), $a->user, true) : '', - 'bang' => '', - 'visitor' => $is_owner || $commvisitor ? 'block' : 'none', - 'profile_uid' => $a->profile['uid'], - ]; - - $o .= status_editor($a, $x); - } - } - - // Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups - $sql_extra = Item::getPermissionsSQLByUserId($a->profile['uid']); - $sql_extra2 = ''; - - $last_updated_array = Session::get('last_updated', []); - - if ($update) { - $last_updated = $last_updated_array[$last_updated_key] ?? 0; - - // If the page user is the owner of the page we should query for unseen - // items. Otherwise use a timestamp of the last succesful update request. - if ($is_owner || !$last_updated) { - $sql_extra4 = " AND `item`.`unseen`"; - } else { - $gmupdate = gmdate(DateTimeFormat::MYSQL, $last_updated); - $sql_extra4 = " AND `item`.`received` > '" . $gmupdate . "'"; - } - - $items_stmt = DBA::p( - "SELECT DISTINCT(`parent-uri`) AS `uri`, `item`.`created` - FROM `item` - INNER JOIN `contact` - ON `contact`.`id` = `item`.`contact-id` - AND NOT `contact`.`blocked` - AND NOT `contact`.`pending` - WHERE `item`.`uid` = ? - AND `item`.`visible` - AND (NOT `item`.`deleted` OR `item`.`gravity` = ?) - AND NOT `item`.`moderated` - AND `item`.`wall` - $sql_extra4 - $sql_extra - ORDER BY `item`.`received` DESC", - $a->profile['uid'], - GRAVITY_ACTIVITY - ); - - if (!DBA::isResult($items_stmt)) { - return ''; - } - - $pager = new Pager(DI::args()->getQueryString()); - } else { - $sql_post_table = ""; - - if (!empty($category)) { - $sql_post_table = sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", - DBA::escape(Strings::protectSprintf($category)), intval(TERM_OBJ_POST), intval(TERM_CATEGORY), intval($a->profile['uid'])); - } - - if (!empty($hashtags)) { - $sql_post_table .= sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", - DBA::escape(Strings::protectSprintf($hashtags)), intval(TERM_OBJ_POST), intval(TERM_HASHTAG), intval($a->profile['uid'])); - } - - if (!empty($datequery)) { - $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`received` <= '%s' ", DBA::escape(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get())))); - } - if (!empty($datequery2)) { - $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`received` >= '%s' ", DBA::escape(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get())))); - } - - // Does the profile page belong to a forum? - // If not then we can improve the performance with an additional condition - $condition = ['uid' => $a->profile['uid'], 'page-flags' => [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP]]; - if (!DBA::exists('user', $condition)) { - $sql_extra3 = sprintf(" AND `thread`.`contact-id` = %d ", intval(intval($a->profile['contact_id']))); - } else { - $sql_extra3 = ""; - } - - // check if we serve a mobile device and get the user settings - // accordingly - if (DI::mode()->isMobile()) { - $itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network', 10); - } else { - $itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_network', 20); - } - - // now that we have the user settings, see if the theme forces - // a maximum item number which is lower then the user choice - if (($a->force_max_items > 0) && ($a->force_max_items < $itemspage_network)) { - $itemspage_network = $a->force_max_items; - } - - $pager = new Pager(DI::args()->getQueryString(), $itemspage_network); - - $pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage()); - - $items_stmt = DBA::p( - "SELECT `item`.`uri` - FROM `thread` - STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` - $sql_post_table - STRAIGHT_JOIN `contact` - ON `contact`.`id` = `thread`.`contact-id` - AND NOT `contact`.`blocked` - AND NOT `contact`.`pending` - WHERE `thread`.`uid` = ? - AND `thread`.`visible` - AND NOT `thread`.`deleted` - AND NOT `thread`.`moderated` - AND `thread`.`wall` - $sql_extra3 - $sql_extra - $sql_extra2 - ORDER BY `thread`.`received` DESC - $pager_sql", - $a->profile['uid'] - ); - } - - // Set a time stamp for this page. We will make use of it when we - // search for new items (update routine) - $last_updated_array[$last_updated_key] = time(); - Session::set('last_updated', $last_updated_array); - - if ($is_owner && !$update && !DI::config()->get('theme', 'hide_eventlist')) { - $o .= ProfileModel::getBirthdays(); - $o .= ProfileModel::getEventsReminderHTML(); - } - - if ($is_owner) { - $unseen = Item::exists(['wall' => true, 'unseen' => true, 'uid' => local_user()]); - if ($unseen) { - Item::update(['unseen' => false], ['wall' => true, 'unseen' => true, 'uid' => local_user()]); - } - } - - $items = DBA::toArray($items_stmt); - - if ($pager->getStart() == 0 && !empty($a->profile['uid'])) { - $pinned_items = Item::selectPinned($a->profile['uid'], ['uri', 'pinned'], ['true' . $sql_extra]); - $pinned = Item::inArray($pinned_items); - $items = array_merge($items, $pinned); - } - - $o .= conversation($a, $items, $pager, 'profile', $update, false, 'pinned_received', $a->profile['uid']); - - if (!$update) { - $o .= $pager->renderMinimal(count($items)); - } - - return $o; - } -} diff --git a/src/Module/Profile/Index.php b/src/Module/Profile/Index.php new file mode 100644 index 0000000000..95a929d4b6 --- /dev/null +++ b/src/Module/Profile/Index.php @@ -0,0 +1,298 @@ + $parameters['nickname']]); + if (DBA::isResult($user)) { + // The function returns an empty array when the account is removed, expired or blocked + $data = ActivityPub\Transmitter::getProfile($user['uid']); + if (!empty($data)) { + System::jsonExit($data, 'application/activity+json'); + } + } + + if (DBA::exists('userd', ['username' => $parameters['nickname']])) { + // Known deleted user + $data = ActivityPub\Transmitter::getDeletedUser($parameters['nickname']); + + System::jsonError(410, $data); + } else { + // Any other case (unknown, blocked, nverified, expired, no profile, no self contact) + System::jsonError(404, []); + } + } + } + + public static function content(array $parameters = []) + { + $a = DI::app(); + + Profile::load($a, $parameters['nickname']); + + if (!$a->profile) { + throw new HTTPException\NotFoundException(DI::l10n()->t('Profile not found.')); + } + + $remote_contact_id = Session::getRemoteContactID($a->profile_uid); + + if (DI::config()->get('system', 'block_public') && !local_user() && !$remote_contact_id) { + return Login::form(); + } + + $is_owner = local_user() == $a->profile_uid; + + if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact_id) { + throw new HTTPException\ForbiddenException(DI::l10n()->t('Access to this profile has been restricted.')); + } + + if (!empty($a->profile['page-flags']) && $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY) { + DI::page()['htmlhead'] .= '' . "\n"; + } + + DI::page()['htmlhead'] .= self::buildHtmlHead($a->profile, $parameters['nickname'], $remote_contact_id); + + Nav::setSelected('home'); + + $is_owner = local_user() == $a->profile['uid']; + $o = Profile::getTabs($a, 'profile', $is_owner, $a->profile['nickname']); + + if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact_id) { + notice(DI::l10n()->t('Access to this profile has been restricted.')); + return ''; + } + + $view_as_contacts = []; + $view_as_contact_id = 0; + if ($is_owner) { + $view_as_contact_id = intval($_GET['viewas'] ?? 0); + + $view_as_contacts = Contact::selectToArray(['id', 'name'], [ + 'uid' => local_user(), + 'rel' => [Contact::FOLLOWER, Contact::SHARING, Contact::FRIEND], + 'network' => Protocol::DFRN, + 'blocked' => false, + ]); + + // User manually provided a contact ID they aren't privy to, silently defaulting to their own view + if (!in_array($view_as_contact_id, array_column($view_as_contacts, 'id'))) { + $view_as_contact_id = 0; + } + } + + $basic_fields = []; + + $basic_fields += self::buildField('fullname', DI::l10n()->t('Full Name:'), $a->profile['name']); + + if (Feature::isEnabled($a->profile_uid, 'profile_membersince')) { + $basic_fields += self::buildField( + 'membersince', + DI::l10n()->t('Member since:'), + DateTimeFormat::local($a->profile['register_date']) + ); + } + + if (!empty($a->profile['dob']) && $a->profile['dob'] > DBA::NULL_DATE) { + $year_bd_format = DI::l10n()->t('j F, Y'); + $short_bd_format = DI::l10n()->t('j F'); + + $dob = DI::l10n()->getDay( + intval($a->profile['dob']) ? + DateTimeFormat::utc($a->profile['dob'] . ' 00:00 +00:00', $year_bd_format) + : DateTimeFormat::utc('2001-' . substr($a->profile['dob'], 5) . ' 00:00 +00:00', $short_bd_format) + ); + + $basic_fields += self::buildField('dob', DI::l10n()->t('Birthday:'), $dob); + + if ($age = Temporal::getAgeByTimezone($a->profile['dob'], $a->profile['timezone'])) { + $basic_fields += self::buildField('age', DI::l10n()->t('Age: '), DI::l10n()->tt('%d year old', '%d years old', $age)); + } + } + + if ($a->profile['pdesc']) { + $basic_fields += self::buildField('pdesc', DI::l10n()->t('Description:'), HTML::toLink($a->profile['pdesc'])); + } + + if ($a->profile['xmpp']) { + $basic_fields += self::buildField('xmpp', DI::l10n()->t('XMPP:'), $a->profile['xmpp']); + } + + if ($a->profile['homepage']) { + $basic_fields += self::buildField('homepage', DI::l10n()->t('Homepage:'), HTML::toLink($a->profile['homepage'])); + } + + $o .= Profile::getTabs($a, 'profile', $is_owner, $a->profile['nickname']); + if ( + $a->profile['address'] + || $a->profile['locality'] + || $a->profile['postal-code'] + || $a->profile['region'] + || $a->profile['country-name'] + ) { + $basic_fields += self::buildField('location', DI::l10n()->t('Location:'), Profile::formatLocation($a->profile)); + } + + if ($a->profile['pub_keywords']) { + $tags = []; + foreach (explode(',', $a->profile['pub_keywords']) as $tag_label) { + $tags[] = [ + 'url' => '/search?tag=' . $tag_label, + 'label' => Term::TAG_CHARACTER[Term::HASHTAG] . $tag_label, + ]; + } + + $basic_fields += self::buildField('pub_keywords', DI::l10n()->t('Tags:'), $tags); + } + + $custom_fields = []; + + // Defaults to the current logged in user self contact id to show self-only fields + $contact_id = $view_as_contact_id ?: $remote_contact_id ?: 0; + + if ($is_owner && $contact_id === 0) { + $profile_fields = DI::profileField()->selectByUserId($a->profile_uid); + } else { + $profile_fields = DI::profileField()->selectByContactId($contact_id, $a->profile_uid); + } + + foreach ($profile_fields as $profile_field) { + $custom_fields += self::buildField( + 'custom_' . $profile_field->order, + $profile_field->label, + BBCode::convert($profile_field->value), + 'aprofile custom' + ); + }; + + //show subcribed forum if it is enabled in the usersettings + if (Feature::isEnabled($a->profile_uid, 'forumlist_profile')) { + $custom_fields += self::buildField( + 'forumlist', + DI::l10n()->t('Forums:'), + ForumManager::profileAdvanced($a->profile_uid) + ); + } + + $tpl = Renderer::getMarkupTemplate('profile/index.tpl'); + $o .= Renderer::replaceMacros($tpl, [ + '$title' => DI::l10n()->t('Profile'), + '$view_as_contacts' => $view_as_contacts, + '$view_as_contact_id' => $view_as_contact_id, + '$view_as' => DI::l10n()->t('View profile as:'), + '$basic' => DI::l10n()->t('Basic'), + '$advanced' => DI::l10n()->t('Advanced'), + '$is_owner' => $a->profile_uid == local_user(), + '$query_string' => DI::args()->getQueryString(), + '$basic_fields' => $basic_fields, + '$custom_fields' => $custom_fields, + '$profile' => $a->profile, + '$edit_link' => [ + 'url' => DI::baseUrl() . '/settings/profile', DI::l10n()->t('Edit profile'), + 'title' => '', + 'label' => DI::l10n()->t('Edit profile') + ], + ]); + + Hook::callAll('profile_advanced', $o); + + return $o; + } + + /** + * Creates a profile field structure to be used in the profile template + * + * @param string $name Arbitrary name of the field + * @param string $label Display label of the field + * @param mixed $value Display value of the field + * @param string $class Optional CSS class to apply to the field + * @return array + */ + private static function buildField(string $name, string $label, $value, string $class = 'aprofile') + { + return [$name => [ + 'id' => 'aprofile-' . $name, + 'class' => $class, + 'label' => $label, + 'value' => $value, + ]]; + } + + private static function buildHtmlHead(array $profile, string $nickname, int $remote_contact_id) + { + $baseUrl = DI::baseUrl(); + + $htmlhead = "\n"; + + if (!empty($profile['page-flags']) && $profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY) { + $htmlhead .= '' . "\n"; + } + + if (!empty($profile['openidserver'])) { + $htmlhead .= '' . "\n"; + } + + if (!empty($profile['openid'])) { + $delegate = strstr($profile['openid'], '://') ? $profile['openid'] : 'https://' . $profile['openid']; + $htmlhead .= '' . "\n"; + } + + // site block + $blocked = !local_user() && !$remote_contact_id && DI::config()->get('system', 'block_public'); + $userblock = !local_user() && !$remote_contact_id && $profile['hidewall']; + if (!$blocked && !$userblock) { + $keywords = str_replace(['#', ',', ' ', ',,'], ['', ' ', ',', ','], $profile['pub_keywords'] ?? ''); + if (strlen($keywords)) { + $htmlhead .= '' . "\n"; + } + } + + $htmlhead .= '' . "\n"; + + if (!$profile['net-publish'] || $profile['hidewall']) { + $htmlhead .= '' . "\n"; + } + + $htmlhead .= '' . "\n"; + $htmlhead .= '' . "\n"; + $htmlhead .= '' . "\n"; + $htmlhead .= '' . "\n"; + $uri = urlencode('acct:' . $profile['nickname'] . '@' . $baseUrl->getHostname() . ($baseUrl->getUrlPath() ? '/' . $baseUrl->getUrlPath() : '')); + $htmlhead .= '' . "\n"; + header('Link: <' . $baseUrl . '/xrd/?uri=' . $uri . '>; rel="lrdd"; type="application/xrd+xml"', false); + + $dfrn_pages = ['request', 'confirm', 'notify', 'poll']; + foreach ($dfrn_pages as $dfrn) { + $htmlhead .= '' . "\n"; + } + $htmlhead .= '' . "\n"; + + return $htmlhead; + } +} diff --git a/src/Module/Profile/Status.php b/src/Module/Profile/Status.php new file mode 100644 index 0000000000..1300fdc97f --- /dev/null +++ b/src/Module/Profile/Status.php @@ -0,0 +1,215 @@ +profile['net-publish'] || $a->profile['hidewall']) { + DI::page()['htmlhead'] .= '' . "\n"; + } + + DI::page()['htmlhead'] .= '' . "\n"; + DI::page()['htmlhead'] .= '' . "\n"; + DI::page()['htmlhead'] .= '' . "\n"; + DI::page()['htmlhead'] .= '' . "\n"; + + $category = $datequery = $datequery2 = ''; + + $dtFormat = DI::dtFormat(); + + if ($args->getArgc() > 2) { + for ($x = 2; $x < $args->getArgc(); $x++) { + if ($dtFormat->isYearMonth($args->get($x))) { + if ($datequery) { + $datequery2 = Strings::escapeHtml($args->get($x)); + } else { + $datequery = Strings::escapeHtml($args->get($x)); + } + } else { + $category = $args->get($x); + } + } + } + + if (empty($category)) { + $category = $_GET['category'] ?? ''; + } + + $hashtags = $_GET['tag'] ?? ''; + + if (DI::config()->get('system', 'block_public') && !local_user() && !Session::getRemoteContactID($a->profile['uid'])) { + return Login::form(); + } + + $o = ''; + + if ($a->profile['uid'] == local_user()) { + Nav::setSelected('home'); + } + + $remote_contact = Session::getRemoteContactID($a->profile['uid']); + $is_owner = local_user() == $a->profile['uid']; + $last_updated_key = "profile:" . $a->profile['uid'] . ":" . local_user() . ":" . $remote_contact; + + if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact) { + notice(DI::l10n()->t('Access to this profile has been restricted.') . EOL); + return ''; + } + + $o .= ProfileModel::getTabs($a, 'status', $is_owner, $a->profile['nickname']); + + $o .= Widget::commonFriendsVisitor($a->profile['uid']); + + $commpage = $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY; + $commvisitor = $commpage && $remote_contact; + + DI::page()['aside'] .= Widget::postedByYear(DI::baseUrl() . '/profile/' . $a->profile['nickname'] . '/status', $a->profile['profile_uid'] ?? 0, true); + DI::page()['aside'] .= Widget::categories(DI::baseUrl() . '/profile/' . $a->profile['nickname'] . '/status', XML::escape($category)); + DI::page()['aside'] .= Widget::tagCloud(); + + if (Security::canWriteToUserWall($a->profile['uid'])) { + $x = [ + 'is_owner' => $is_owner, + 'allow_location' => ($is_owner || $commvisitor) && $a->profile['allow_location'], + '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 ? ACL::getFullSelectorHTML(DI::page(), $a->user, true) : '', + 'bang' => '', + 'visitor' => $is_owner || $commvisitor ? 'block' : 'none', + 'profile_uid' => $a->profile['uid'], + ]; + + $o .= status_editor($a, $x); + } + + // Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups + $sql_extra = Item::getPermissionsSQLByUserId($a->profile['uid']); + $sql_extra2 = ''; + + $last_updated_array = Session::get('last_updated', []); + + $sql_post_table = ""; + + if (!empty($category)) { + $sql_post_table = sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", + DBA::escape(Strings::protectSprintf($category)), intval(TERM_OBJ_POST), intval(TERM_CATEGORY), intval($a->profile['uid'])); + } + + if (!empty($hashtags)) { + $sql_post_table .= sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", + DBA::escape(Strings::protectSprintf($hashtags)), intval(TERM_OBJ_POST), intval(TERM_HASHTAG), intval($a->profile['uid'])); + } + + if (!empty($datequery)) { + $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`received` <= '%s' ", DBA::escape(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get())))); + } + if (!empty($datequery2)) { + $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`received` >= '%s' ", DBA::escape(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get())))); + } + + // Does the profile page belong to a forum? + // If not then we can improve the performance with an additional condition + $condition = ['uid' => $a->profile['uid'], 'page-flags' => [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP]]; + if (!DBA::exists('user', $condition)) { + $sql_extra3 = sprintf(" AND `thread`.`contact-id` = %d ", intval(intval($a->profile['contact_id']))); + } else { + $sql_extra3 = ""; + } + + // check if we serve a mobile device and get the user settings + // accordingly + if (DI::mode()->isMobile()) { + $itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network', 10); + } else { + $itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_network', 20); + } + + // now that we have the user settings, see if the theme forces + // a maximum item number which is lower then the user choice + if (($a->force_max_items > 0) && ($a->force_max_items < $itemspage_network)) { + $itemspage_network = $a->force_max_items; + } + + $pager = new Pager($args->getQueryString(), $itemspage_network); + + $pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage()); + + $items_stmt = DBA::p( + "SELECT `item`.`uri` + FROM `thread` + STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` + $sql_post_table + STRAIGHT_JOIN `contact` + ON `contact`.`id` = `thread`.`contact-id` + AND NOT `contact`.`blocked` + AND NOT `contact`.`pending` + WHERE `thread`.`uid` = ? + AND `thread`.`visible` + AND NOT `thread`.`deleted` + AND NOT `thread`.`moderated` + AND `thread`.`wall` + $sql_extra3 + $sql_extra + $sql_extra2 + ORDER BY `thread`.`received` DESC + $pager_sql", + $a->profile['uid'] + ); + + // Set a time stamp for this page. We will make use of it when we + // search for new items (update routine) + $last_updated_array[$last_updated_key] = time(); + Session::set('last_updated', $last_updated_array); + + if ($is_owner && !DI::config()->get('theme', 'hide_eventlist')) { + $o .= ProfileModel::getBirthdays(); + $o .= ProfileModel::getEventsReminderHTML(); + } + + if ($is_owner) { + $unseen = Item::exists(['wall' => true, 'unseen' => true, 'uid' => local_user()]); + if ($unseen) { + Item::update(['unseen' => false], ['wall' => true, 'unseen' => true, 'uid' => local_user()]); + } + } + + $items = DBA::toArray($items_stmt); + + $o .= conversation($a, $items, $pager, 'profile', false, false, 'received', $a->profile['uid']); + + $o .= $pager->renderMinimal(count($items)); + + return $o; + } +} diff --git a/src/Module/Settings/Profile/Index.php b/src/Module/Settings/Profile/Index.php new file mode 100644 index 0000000000..b0eb194edd --- /dev/null +++ b/src/Module/Settings/Profile/Index.php @@ -0,0 +1,306 @@ +t('Profile Name is required.')); + return; + } + + $namechanged = $profile['name'] != $name; + + $pdesc = Strings::escapeTags(trim($_POST['pdesc'])); + $address = Strings::escapeTags(trim($_POST['address'])); + $locality = Strings::escapeTags(trim($_POST['locality'])); + $region = Strings::escapeTags(trim($_POST['region'])); + $postal_code = Strings::escapeTags(trim($_POST['postal_code'])); + $country_name = Strings::escapeTags(trim($_POST['country_name'])); + $pub_keywords = self::cleanKeywords(Strings::escapeTags(trim($_POST['pub_keywords']))); + $prv_keywords = self::cleanKeywords(Strings::escapeTags(trim($_POST['prv_keywords']))); + $xmpp = Strings::escapeTags(trim($_POST['xmpp'])); + $homepage = Strings::escapeTags(trim($_POST['homepage'])); + if ((strpos($homepage, 'http') !== 0) && (strlen($homepage))) { + // neither http nor https in URL, add them + $homepage = 'http://' . $homepage; + } + + $hide_friends = intval(!empty($_POST['hide-friends'])); + + $profileFields = DI::profileField()->selectByUserId(local_user()); + + $profileFields = DI::profileField()->updateCollectionFromForm( + local_user(), + $profileFields, + $_REQUEST['profile_field'], + $_REQUEST['profile_field_order'] + ); + + DI::profileField()->saveCollection($profileFields); + + $result = DBA::update( + 'profile', + [ + 'name' => $name, + 'pdesc' => $pdesc, + 'dob' => $dob, + 'address' => $address, + 'locality' => $locality, + 'region' => $region, + 'postal-code' => $postal_code, + 'country-name' => $country_name, + 'xmpp' => $xmpp, + 'homepage' => $homepage, + 'pub_keywords' => $pub_keywords, + 'prv_keywords' => $prv_keywords, + 'hide-friends' => $hide_friends, + ], + ['uid' => local_user()] + ); + + if ($result) { + info(DI::l10n()->t('Profile updated.')); + } else { + notice(DI::l10n()->t('Profile couldn\'t be updated.')); + return; + } + + if ($namechanged) { + DBA::update('user', ['username' => $name], ['uid' => local_user()]); + } + + Contact::updateSelfFromUserID(local_user()); + + // Update global directory in background + if (Session::get('my_url') && strlen(DI::config()->get('system', 'directory'))) { + Worker::add(PRIORITY_LOW, 'Directory', Session::get('my_url')); + } + + Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user()); + + // Update the global contact for the user + GContact::updateForUser(local_user()); + } + + public static function content(array $parameters = []) + { + if (!local_user()) { + notice(DI::l10n()->t('You must be logged in to use this module')); + return Login::form(); + } + + parent::content(); + + $o = ''; + + $profile = Profile::getByUID(local_user()); + if (!DBA::isResult($profile)) { + throw new HTTPException\NotFoundException(); + } + + $a = DI::app(); + + DI::page()->registerFooterScript('view/asset/es-jquery-sortable/source/js/jquery-sortable-min.js'); + DI::page()->registerFooterScript(Theme::getPathForFile('js/module/settings/profile/index.js')); + + $custom_fields = []; + + $profileFields = DI::profileField()->selectByUserId(local_user()); + foreach ($profileFields as $profileField) { + /** @var ProfileField $profileField */ + $defaultPermissions = ACL::getDefaultUserPermissions($profileField->permissionset->toArray()); + + $custom_fields[] = [ + 'id' => $profileField->id, + 'legend' => $profileField->label, + 'fields' => [ + 'label' => ['profile_field[' . $profileField->id . '][label]', DI::l10n()->t('Label:'), $profileField->label], + 'value' => ['profile_field[' . $profileField->id . '][value]', DI::l10n()->t('Value:'), $profileField->value], + 'acl' => ACL::getFullSelectorHTML( + DI::page(), + $a->user, + false, + $defaultPermissions, + ['network' => Protocol::DFRN], + 'profile_field[' . $profileField->id . ']' + ), + ], + 'permissions' => DI::l10n()->t('Field Permissions'), + 'permdesc' => DI::l10n()->t("(click to open/close)"), + ]; + }; + + $custom_fields[] = [ + 'id' => 'new', + 'legend' => DI::l10n()->t('Add a new profile field'), + 'fields' => [ + 'label' => ['profile_field[new][label]', DI::l10n()->t('Label:')], + 'value' => ['profile_field[new][value]', DI::l10n()->t('Value:')], + 'acl' => ACL::getFullSelectorHTML( + DI::page(), + $a->user, + false, + ['allow_cid' => []], + ['network' => Protocol::DFRN], + 'profile_field[new]' + ), + ], + 'permissions' => DI::l10n()->t('Field Permissions'), + 'permdesc' => DI::l10n()->t("(click to open/close)"), + ]; + + DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/profile/index_head.tpl'), [ + '$baseurl' => DI::baseUrl()->get(true), + ]); + + $opt_tpl = Renderer::getMarkupTemplate('settings/profile/hide-friends.tpl'); + $hide_friends = Renderer::replaceMacros($opt_tpl, [ + '$yesno' => [ + 'hide-friends', //Name + DI::l10n()->t('Hide contacts and friends:'), //Label + !!$profile['hide-friends'], //Value + '', //Help string + [DI::l10n()->t('No'), DI::l10n()->t('Yes')] //Off - On strings + ], + '$desc' => DI::l10n()->t('Hide your contact/friend list from viewers of this profile?'), + '$yes_str' => DI::l10n()->t('Yes'), + '$no_str' => DI::l10n()->t('No'), + '$yes_selected' => (($profile['hide-friends']) ? ' checked="checked"' : ''), + '$no_selected' => (($profile['hide-friends'] == 0) ? ' checked="checked"' : '') + ]); + + $personal_account = !in_array($a->user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP]); + + $tpl = Renderer::getMarkupTemplate('settings/profile/index.tpl'); + $o .= Renderer::replaceMacros($tpl, [ + '$personal_account' => $personal_account, + + '$form_security_token' => self::getFormSecurityToken('settings_profile'), + '$form_security_token_photo' => self::getFormSecurityToken('settings_profile_photo'), + + '$profile_action' => DI::l10n()->t('Profile Actions'), + '$banner' => DI::l10n()->t('Edit Profile Details'), + '$submit' => DI::l10n()->t('Submit'), + '$profpic' => DI::l10n()->t('Change Profile Photo'), + '$profpiclink' => '/photos/' . $a->user['nickname'], + '$viewprof' => DI::l10n()->t('View this profile'), + + '$lbl_personal_section' => DI::l10n()->t('Personal'), + '$lbl_picture_section' => DI::l10n()->t('Profile picture'), + '$lbl_location_section' => DI::l10n()->t('Location'), + '$lbl_miscellaneous_section' => DI::l10n()->t('Miscellaneous'), + '$lbl_custom_fields_section' => DI::l10n()->t('Custom Profile Fields'), + + '$lbl_profile_photo' => DI::l10n()->t('Upload Profile Photo'), + + '$baseurl' => DI::baseUrl()->get(true), + '$nickname' => $a->user['nickname'], + '$name' => ['name', DI::l10n()->t('Display name:'), $profile['name']], + '$pdesc' => ['pdesc', DI::l10n()->t('Title/Description:'), $profile['pdesc']], + '$dob' => Temporal::getDateofBirthField($profile['dob'], $a->user['timezone']), + '$hide_friends' => $hide_friends, + '$address' => ['address', DI::l10n()->t('Street Address:'), $profile['address']], + '$locality' => ['locality', DI::l10n()->t('Locality/City:'), $profile['locality']], + '$region' => ['region', DI::l10n()->t('Region/State:'), $profile['region']], + '$postal_code' => ['postal_code', DI::l10n()->t('Postal/Zip Code:'), $profile['postal-code']], + '$country_name' => ['country_name', DI::l10n()->t('Country:'), $profile['country-name']], + '$age' => ((intval($profile['dob'])) ? '(' . DI::l10n()->t('Age: ') . DI::l10n()->tt('%d year old', '%d years old', Temporal::getAgeByTimezone($profile['dob'], $a->user['timezone'])) . ')' : ''), + '$xmpp' => ['xmpp', DI::l10n()->t('XMPP (Jabber) address:'), $profile['xmpp'], DI::l10n()->t('The XMPP address will be propagated to your contacts so that they can follow you.')], + '$homepage' => ['homepage', DI::l10n()->t('Homepage URL:'), $profile['homepage']], + '$pub_keywords' => ['pub_keywords', DI::l10n()->t('Public Keywords:'), $profile['pub_keywords'], DI::l10n()->t('(Used for suggesting potential friends, can be seen by others)')], + '$prv_keywords' => ['prv_keywords', DI::l10n()->t('Private Keywords:'), $profile['prv_keywords'], DI::l10n()->t('(Used for searching profiles, never shown to others)')], + '$custom_fields_description' => DI::l10n()->t("

Custom fields appear on your profile page.

+

You can use BBCodes in the field values.

+

Reorder by dragging the field title.

+

Empty the label field to remove a custom field.

+

Non-public fields can only be seen by the selected Friendica contacts or the Friendica contacts in the selected groups.

", + 'profile/' . $a->user['nickname'] + ), + '$custom_fields' => $custom_fields, + ]); + + $arr = ['profile' => $profile, 'entry' => $o]; + Hook::callAll('profile_edit', $arr); + + return $o; + } + + private static function cleanKeywords($keywords) + { + $keywords = str_replace(',', ' ', $keywords); + $keywords = explode(' ', $keywords); + + $cleaned = []; + foreach ($keywords as $keyword) { + $keyword = trim(strtolower($keyword)); + $keyword = trim($keyword, '#'); + if ($keyword != '') { + $cleaned[] = $keyword; + } + } + + $keywords = implode(', ', $cleaned); + + return $keywords; + } +} diff --git a/src/Module/Settings/UserExport.php b/src/Module/Settings/UserExport.php index 0b3aa85a4b..8d04e85360 100644 --- a/src/Module/Settings/UserExport.php +++ b/src/Module/Settings/UserExport.php @@ -167,7 +167,11 @@ class UserExport extends BaseSettingsModule $profile = self::exportMultiRow( - sprintf("SELECT * FROM `profile` WHERE `uid` = %d ", intval(local_user())) + sprintf("SELECT *, 'default' AS `profile_name`, 1 AS `is-default` FROM `profile` WHERE `uid` = %d ", intval(local_user())) + ); + + $profile_fields = self::exportMultiRow( + sprintf("SELECT * FROM `profile_field` WHERE `uid` = %d ", intval(local_user())) ); $photo = self::exportMultiRow( @@ -196,6 +200,7 @@ class UserExport extends BaseSettingsModule 'user' => $user, 'contact' => $contact, 'profile' => $profile, + 'profile_fields' => $profile_fields, 'photo' => $photo, 'pconfig' => $pconfig, 'group' => $group, diff --git a/src/Module/Update/Profile.php b/src/Module/Update/Profile.php new file mode 100644 index 0000000000..0f959a3602 --- /dev/null +++ b/src/Module/Update/Profile.php @@ -0,0 +1,125 @@ +get('system', 'block_public') && !local_user() && !Session::getRemoteContactID($a->profile['uid'])) { + throw new ForbiddenException(); + } + + $o = ''; + + $profile_uid = intval($_GET['p'] ?? 0); + + // Ensure we've got a profile owner if updating. + $a->profile['uid'] = $profile_uid; + + $remote_contact = Session::getRemoteContactID($a->profile['uid']); + $is_owner = local_user() == $a->profile['uid']; + $last_updated_key = "profile:" . $a->profile['uid'] . ":" . local_user() . ":" . $remote_contact; + + if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact) { + throw new ForbiddenException(DI::l10n()->t('Access to this profile has been restricted.')); + } + + // Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups + $sql_extra = Item::getPermissionsSQLByUserId($a->profile['uid']); + + $last_updated_array = Session::get('last_updated', []); + + $last_updated = $last_updated_array[$last_updated_key] ?? 0; + + // If the page user is the owner of the page we should query for unseen + // items. Otherwise use a timestamp of the last succesful update request. + if ($is_owner || !$last_updated) { + $sql_extra4 = " AND `item`.`unseen`"; + } else { + $gmupdate = gmdate(DateTimeFormat::MYSQL, $last_updated); + $sql_extra4 = " AND `item`.`received` > '" . $gmupdate . "'"; + } + + $items_stmt = DBA::p( + "SELECT DISTINCT(`parent-uri`) AS `uri`, `item`.`created` + FROM `item` + INNER JOIN `contact` + ON `contact`.`id` = `item`.`contact-id` + AND NOT `contact`.`blocked` + AND NOT `contact`.`pending` + WHERE `item`.`uid` = ? + AND `item`.`visible` + AND (NOT `item`.`deleted` OR `item`.`gravity` = ?) + AND NOT `item`.`moderated` + AND `item`.`wall` + $sql_extra4 + $sql_extra + ORDER BY `item`.`received` DESC", + $a->profile['uid'], + GRAVITY_ACTIVITY + ); + + if (!DBA::isResult($items_stmt)) { + return ''; + } + + $pager = new Pager(DI::args()->getQueryString()); + + // Set a time stamp for this page. We will make use of it when we + // search for new items (update routine) + $last_updated_array[$last_updated_key] = time(); + Session::set('last_updated', $last_updated_array); + + if ($is_owner && !$profile_uid && !DI::config()->get('theme', 'hide_eventlist')) { + $o .= ProfileModel::getBirthdays(); + $o .= ProfileModel::getEventsReminderHTML(); + } + + if ($is_owner) { + $unseen = Item::exists(['wall' => true, 'unseen' => true, 'uid' => local_user()]); + if ($unseen) { + Item::update(['unseen' => false], ['wall' => true, 'unseen' => true, 'uid' => local_user()]); + } + } + + $items = DBA::toArray($items_stmt); + + $o .= conversation($a, $items, $pager, 'profile', $profile_uid, false, 'received', $a->profile['uid']); + + header("Content-type: text/html"); + echo "\r\n"; + // We can remove this hack once Internet Explorer recognises HTML5 natively + echo "
"; + echo $o; + if (DI::pConfig()->get(local_user(), "system", "bandwidth_saver")) { + $replace = "
".DI::l10n()->t("[Embedded content - reload page to view]")."
"; + $pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i"; + $o = preg_replace($pattern, $replace, $o); + $pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i"; + $o = preg_replace($pattern, $replace, $o); + $pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i"; + $o = preg_replace($pattern, $replace, $o); + $pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i"; + $o = preg_replace($pattern, $replace, $o); + } + + // reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well + echo str_replace("\t", " ", $o); + echo "
"; + echo "\r\n"; + exit(); + } +} diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index c9d39dede7..23301456a9 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -224,7 +224,7 @@ class Transmitter } $fields = ['locality', 'region', 'country-name']; - $profile = DBA::selectFirst('profile', $fields, ['uid' => $uid, 'is-default' => true]); + $profile = DBA::selectFirst('profile', $fields, ['uid' => $uid]); if (!DBA::isResult($profile)) { return []; } diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index f649df558f..424b519063 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -687,7 +687,7 @@ class DFRN `profile`.`pub_keywords`, `profile`.`xmpp`, `profile`.`dob` FROM `profile` INNER JOIN `user` ON `user`.`uid` = `profile`.`uid` - WHERE `profile`.`is-default` AND NOT `user`.`hidewall` AND `user`.`uid` = %d", + WHERE NOT `user`.`hidewall` AND `user`.`uid` = %d", intval($owner['uid']) ); if (DBA::isResult($r)) { diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index beb0485c46..bff56d5af3 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -4092,7 +4092,7 @@ class Diaspora FROM `profile` INNER JOIN `user` ON `profile`.`uid` = `user`.`uid` INNER JOIN `contact` ON `profile`.`uid` = `contact`.`uid` - WHERE `user`.`uid` = %d AND `profile`.`is-default` AND `contact`.`self` LIMIT 1", + WHERE `user`.`uid` = %d AND `contact`.`self` LIMIT 1", intval($uid) ); diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 225c2ec2b4..dc30b596fc 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -1428,7 +1428,7 @@ class OStatus */ private static function addAuthor(DOMDocument $doc, array $owner, $show_profile = true) { - $profile = DBA::selectFirst('profile', ['homepage', 'publish'], ['uid' => $owner['uid'], 'is-default' => true]); + $profile = DBA::selectFirst('profile', ['homepage', 'publish'], ['uid' => $owner['uid']]); $author = $doc->createElement("author"); XML::addElement($doc, $author, "id", $owner["url"]); if ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) { diff --git a/src/Repository/PermissionSet.php b/src/Repository/PermissionSet.php index 7cbee4b836..86915701d9 100644 --- a/src/Repository/PermissionSet.php +++ b/src/Repository/PermissionSet.php @@ -13,6 +13,9 @@ use Psr\Log\LoggerInterface; class PermissionSet extends BaseRepository { + /** @var int Virtual permission set id for public permission */ + const PUBLIC = 0; + protected static $table_name = 'permissionset'; protected static $model_class = Model\PermissionSet::class; @@ -47,7 +50,7 @@ class PermissionSet extends BaseRepository { if (isset($condition['id']) && !$condition['id']) { return $this->create([ - 'id' => 0, + 'id' => self::PUBLIC, 'uid' => $condition['uid'] ?? 0, 'allow_cid' => '', 'allow_gid' => '', @@ -109,7 +112,7 @@ class PermissionSet extends BaseRepository // Public permission if (!$allow_cid && !$allow_gid && !$deny_cid && !$deny_gid) { - return 0; + return self::PUBLIC; } $condition = [ diff --git a/src/Repository/ProfileField.php b/src/Repository/ProfileField.php new file mode 100644 index 0000000000..b1b16dcf1f --- /dev/null +++ b/src/Repository/ProfileField.php @@ -0,0 +1,301 @@ +permissionSet = $permissionSet; + $this->aclFormatter = $aclFormatter; + $this->l10n = $l10n; + } + + /** + * @param array $data + * @return Model\ProfileField + */ + protected function create(array $data) + { + return new Model\ProfileField($this->dba, $this->logger, $this->permissionSet, $data); + } + + /** + * @param array $condition + * @return Model\ProfileField + * @throws \Friendica\Network\HTTPException\NotFoundException + */ + public function selectFirst(array $condition) + { + return parent::selectFirst($condition); + } + + /** + * @param array $condition + * @param array $params + * @return Collection\ProfileFields + * @throws \Exception + */ + public function select(array $condition = [], array $params = []) + { + return parent::select($condition, $params); + } + + /** + * @param array $condition + * @param array $params + * @param int|null $max_id + * @param int|null $since_id + * @param int $limit + * @return Collection\ProfileFields + * @throws \Exception + */ + public function selectByBoundaries(array $condition = [], array $params = [], int $max_id = null, int $since_id = null, int $limit = self::LIMIT) + { + return parent::selectByBoundaries($condition, $params, $max_id, $since_id, $limit); + } + + /** + * @param int $uid Field owner user Id + * @return Collection\ProfileFields + * @throws \Exception + */ + public function selectByUserId(int $uid) + { + return $this->select( + ['uid' => $uid], + ['order' => ['order']] + ); + } + + /** + * Retrieve all custom profile field a given contact is able to access to, including public profile fields. + * + * @param int $cid Private contact id, must be owned by $uid + * @param int $uid Field owner user id + * @return Collection\ProfileFields + * @throws \Exception + */ + public function selectByContactId(int $cid, int $uid) + { + $permissionSets = $this->permissionSet->selectByContactId($cid, $uid); + + $psids = $permissionSets->column('id'); + + // Includes public custom fields + $psids[] = 0; + + return $this->select( + ['uid' => $uid, 'psid' => $psids], + ['order' => ['order']] + ); + } + + /** + * @param array $fields + * @return Model\ProfileField|bool + * @throws \Exception + */ + public function insert(array $fields) + { + $fields['created'] = DateTimeFormat::utcNow(); + $fields['edited'] = DateTimeFormat::utcNow(); + + return parent::insert($fields); + } + + /** + * @param Model\ProfileField $model + * @return bool + * @throws \Exception + */ + public function update(BaseModel $model) + { + $model->edited = DateTimeFormat::utcNow(); + + return parent::update($model); + } + + /** + * @param int $uid User Id + * @param Collection\ProfileFields $profileFields Collection of existing profile fields + * @param array $profileFieldInputs Array of profile field form inputs indexed by profile field id + * @param array $profileFieldOrder List of profile field id in order + * @return Collection\ProfileFields + * @throws \Exception + */ + public function updateCollectionFromForm(int $uid, Collection\ProfileFields $profileFields, array $profileFieldInputs, array $profileFieldOrder) + { + // Returns an associative array of id => order values + $profileFieldOrder = array_flip($profileFieldOrder); + + // Creation of the new field + if (!empty($profileFieldInputs['new']['label'])) { + $psid = $this->permissionSet->getIdFromACL( + $uid, + $this->aclFormatter->toString($profileFieldInputs['new']['contact_allow'] ?? ''), + $this->aclFormatter->toString($profileFieldInputs['new']['group_allow'] ?? ''), + $this->aclFormatter->toString($profileFieldInputs['new']['contact_deny'] ?? ''), + $this->aclFormatter->toString($profileFieldInputs['new']['group_deny'] ?? '') + ); + + $newProfileField = $this->insert([ + 'uid' => $uid, + 'label' => $profileFieldInputs['new']['label'], + 'value' => $profileFieldInputs['new']['value'], + 'psid' => $psid, + 'order' => $profileFieldOrder['new'], + ]); + + $profileFieldInputs[$newProfileField->id] = $profileFieldInputs['new']; + $profileFieldOrder[$newProfileField->id] = $profileFieldOrder['new']; + + $profileFields[] = $newProfileField; + } + + unset($profileFieldInputs['new']); + unset($profileFieldOrder['new']); + + // Prunes profile field whose label has been emptied + $profileFields = $profileFields->filter(function (Model\ProfileField $profileField) use (&$profileFieldInputs, &$profileFieldOrder) { + $keepModel = !isset($profileFieldInputs[$profileField->id]) || !empty($profileFieldInputs[$profileField->id]['label']); + + if (!$keepModel) { + unset($profileFieldInputs[$profileField->id]); + unset($profileFieldOrder[$profileField->id]); + $this->delete($profileField); + } + + return $keepModel; + }); + + // Regenerates the order values if items were deleted + $profileFieldOrder = array_flip(array_keys($profileFieldOrder)); + + // Update existing profile fields from form values + $profileFields = $profileFields->map(function (Model\ProfileField $profileField) use ($uid, &$profileFieldInputs, &$profileFieldOrder) { + if (isset($profileFieldInputs[$profileField->id]) && isset($profileFieldOrder[$profileField->id])) { + $psid = $this->permissionSet->getIdFromACL( + $uid, + $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['contact_allow'] ?? ''), + $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['group_allow'] ?? ''), + $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['contact_deny'] ?? ''), + $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['group_deny'] ?? '') + ); + + $profileField->psid = $psid; + $profileField->label = $profileFieldInputs[$profileField->id]['label']; + $profileField->value = $profileFieldInputs[$profileField->id]['value']; + $profileField->order = $profileFieldOrder[$profileField->id]; + + unset($profileFieldInputs[$profileField->id]); + unset($profileFieldOrder[$profileField->id]); + } + + return $profileField; + }); + + return $profileFields; + } + + /** + * Migrates a legacy profile to the new slimmer profile with extra custom fields. + * Multi profiles are converted to ACl-protected custom fields and deleted. + * + * @param array $profile Profile table row + * @throws \Exception + */ + public function migrateFromLegacyProfile(array $profile) + { + // Already processed, aborting + if ($profile['is-default'] === null) { + return; + } + + if (!$profile['is-default']) { + $contacts = Model\Contact::selectToArray(['id'], ['uid' => $profile['uid'], 'profile-id' => $profile['id']]); + if (!count($contacts)) { + // No contact visibility selected defaults to user-only permission + $contacts = Model\Contact::selectToArray(['id'], ['uid' => $profile['uid'], 'self' => true]); + } + + $allow_cid = $this->aclFormatter->toString(array_column($contacts, 'id')); + } + + $psid = $this->permissionSet->getIdFromACL($profile['uid'], $allow_cid ?? ''); + + $order = 1; + + $custom_fields = [ + 'hometown' => $this->l10n->t('Hometown:'), + 'gender' => $this->l10n->t('Gender:'), + 'marital' => $this->l10n->t('Marital Status:'), + 'with' => $this->l10n->t('With:'), + 'howlong' => $this->l10n->t('Since:'), + 'sexual' => $this->l10n->t('Sexual Preference:'), + 'politic' => $this->l10n->t('Political Views:'), + 'religion' => $this->l10n->t('Religious Views:'), + 'likes' => $this->l10n->t('Likes:'), + 'dislikes' => $this->l10n->t('Dislikes:'), + 'about' => $this->l10n->t('About:'), + 'summary' => $this->l10n->t('Summary'), + 'music' => $this->l10n->t('Musical interests'), + 'book' => $this->l10n->t('Books, literature'), + 'tv' => $this->l10n->t('Television'), + 'film' => $this->l10n->t('Film/dance/culture/entertainment'), + 'interest' => $this->l10n->t('Hobbies/Interests'), + 'romance' => $this->l10n->t('Love/romance'), + 'work' => $this->l10n->t('Work/employment'), + 'education' => $this->l10n->t('School/education'), + 'contact' => $this->l10n->t('Contact information and Social Networks'), + ]; + + foreach ($custom_fields as $field => $label) { + if (!empty($profile[$field]) && $profile[$field] > DBA::NULL_DATE && $profile[$field] > DBA::NULL_DATETIME) { + $this->insert([ + 'uid' => $profile['uid'], + 'psid' => $psid, + 'order' => $order++, + 'label' => trim($label, ':'), + 'value' => $profile[$field], + ]); + } + + $profile[$field] = null; + } + + if ($profile['is-default']) { + $profile['profile-name'] = null; + $profile['is-default'] = null; + $this->dba->update('profile', $profile, ['id' => $profile['id']]); + } elseif (!empty($profile['id'])) { + $this->dba->delete('profile', ['id' => $profile['id']]); + } + } +} diff --git a/src/Worker/Directory.php b/src/Worker/Directory.php index 4d5393dd84..ac9aeae5b9 100644 --- a/src/Worker/Directory.php +++ b/src/Worker/Directory.php @@ -46,7 +46,7 @@ class Directory $r = q("SELECT `url` FROM `contact` INNER JOIN `profile` ON `profile`.`uid` = `contact`.`uid` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid` - WHERE `contact`.`self` AND `profile`.`net-publish` AND `profile`.`is-default` AND + WHERE `contact`.`self` AND `profile`.`net-publish` AND NOT `user`.`account_expired` AND `user`.`verified`"); if (DBA::isResult($r)) { diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 49934c63cf..87cfdd11c6 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -34,7 +34,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1330); + define('DB_UPDATE_VERSION', 1332); } return [ @@ -277,7 +277,7 @@ return [ "reason" => ["type" => "text", "comment" => ""], "closeness" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "99", "comment" => ""], "info" => ["type" => "mediumtext", "comment" => ""], - "profile-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "comment" => ""], + "profile-id" => ["type" => "int unsigned", "comment" => "Deprecated"], "bdyear" => ["type" => "varchar(4)", "not null" => "1", "default" => "", "comment" => ""], "bd" => ["type" => "date", "not null" => "1", "default" => DBA::NULL_DATE, "comment" => ""], "notify_new_posts" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], @@ -1084,8 +1084,8 @@ return [ "fields" => [ "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"], "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "relation" => ["user" => "uid"], "comment" => "Owner User id"], - "profile-name" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Name of the profile"], - "is-default" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Mark this profile as default profile"], + "profile-name" => ["type" => "varchar(255)", "comment" => "Deprecated"], + "is-default" => ["type" => "boolean", "comment" => "Deprecated"], "hide-friends" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Hide friend list from viewers of this profile"], "name" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "pdesc" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Title or description"], @@ -1095,29 +1095,29 @@ return [ "region" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "postal-code" => ["type" => "varchar(32)", "not null" => "1", "default" => "", "comment" => ""], "country-name" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - "hometown" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - "gender" => ["type" => "varchar(32)", "not null" => "1", "default" => "", "comment" => ""], - "marital" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - "with" => ["type" => "text", "comment" => ""], - "howlong" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""], - "sexual" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - "politic" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - "religion" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], + "hometown" => ["type" => "varchar(255)", "comment" => "Deprecated"], + "gender" => ["type" => "varchar(32)", "comment" => "Deprecated"], + "marital" => ["type" => "varchar(255)", "comment" => "Deprecated"], + "with" => ["type" => "text", "comment" => "Deprecated"], + "howlong" => ["type" => "datetime", "comment" => "Deprecated"], + "sexual" => ["type" => "varchar(255)", "comment" => "Deprecated"], + "politic" => ["type" => "varchar(255)", "comment" => "Deprecated"], + "religion" => ["type" => "varchar(255)", "comment" => "Deprecated"], "pub_keywords" => ["type" => "text", "comment" => ""], "prv_keywords" => ["type" => "text", "comment" => ""], - "likes" => ["type" => "text", "comment" => ""], - "dislikes" => ["type" => "text", "comment" => ""], - "about" => ["type" => "text", "comment" => ""], - "summary" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - "music" => ["type" => "text", "comment" => ""], - "book" => ["type" => "text", "comment" => ""], - "tv" => ["type" => "text", "comment" => ""], - "film" => ["type" => "text", "comment" => ""], - "interest" => ["type" => "text", "comment" => ""], - "romance" => ["type" => "text", "comment" => ""], - "work" => ["type" => "text", "comment" => ""], - "education" => ["type" => "text", "comment" => ""], - "contact" => ["type" => "text", "comment" => ""], + "likes" => ["type" => "text", "comment" => "Deprecated"], + "dislikes" => ["type" => "text", "comment" => "Deprecated"], + "about" => ["type" => "text", "comment" => "Deprecated"], + "summary" => ["type" => "varchar(255)", "comment" => "Deprecated"], + "music" => ["type" => "text", "comment" => "Deprecated"], + "book" => ["type" => "text", "comment" => "Deprecated"], + "tv" => ["type" => "text", "comment" => "Deprecated"], + "film" => ["type" => "text", "comment" => "Deprecated"], + "interest" => ["type" => "text", "comment" => "Deprecated"], + "romance" => ["type" => "text", "comment" => "Deprecated"], + "work" => ["type" => "text", "comment" => "Deprecated"], + "education" => ["type" => "text", "comment" => "Deprecated"], + "contact" => ["type" => "text", "comment" => "Deprecated"], "homepage" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "xmpp" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "photo" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], @@ -1145,6 +1145,25 @@ return [ "PRIMARY" => ["id"], ] ], + "profile_field" => [ + "comment" => "Custom profile fields", + "fields" => [ + "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"], + "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "relation" => ["user" => "uid"], "comment" => "Owner user id"], + "order" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "1", "comment" => "Field ordering per user"], + "psid" => ["type" => "int unsigned", "relation" => ["permissionset" => "id"], "comment" => "ID of the permission set of this profile field - 0 = public"], + "label" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Label of the field"], + "value" => ["type" => "text", "comment" => "Value of the field"], + "created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "creation time"], + "edited" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "last edit time"], + ], + "indexes" => [ + "PRIMARY" => ["id"], + "uid" => ["uid"], + "order" => ["order"], + "psid" => ["psid"], + ] + ], "push_subscriber" => [ "comment" => "Used for OStatus: Contains feed subscribers", "fields" => [ diff --git a/static/routes.config.php b/static/routes.config.php index f453856581..daa1629376 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -205,10 +205,9 @@ return [ '/probe' => [Module\Debug\Probe::class, [R::GET]], '/profile' => [ - '/{nickname}' => [Module\Profile::class, [R::GET]], - '/{nickname}/{to:\d{4}-\d{2}-\d{2}}/{from:\d{4}-\d{2}-\d{2}}' => [Module\Profile::class, [R::GET]], - '/{nickname}/contacts[/{type}]' => [Module\Profile\Contacts::class, [R::GET]], - '/{profile:\d+}/view' => [Module\Profile::class, [R::GET]], + '/{nickname}' => [Module\Profile\Index::class, [R::GET]], + '/{nickname}/contacts[/{type}]' => [Module\Profile\Contacts::class, [R::GET]], + '/{nickname}/status[/{category}[/{date1}[/{date2}]]]' => [Module\Profile\Status::class, [R::GET]], ], '/proxy' => [ @@ -239,6 +238,7 @@ return [ ], '/delegation[/{action}/{user_id}]' => [Module\Settings\Delegation::class, [R::GET, R::POST]], '/profile' => [ + '[/]' => [Module\Settings\Profile\Index::class, [R::GET, R::POST]], '/photo[/new]' => [Module\Settings\Profile\Photo\Index::class, [R::GET, R::POST]], '/photo/crop/{guid}' => [Module\Settings\Profile\Photo\Crop::class, [R::GET, R::POST]], ], @@ -254,6 +254,9 @@ return [ '/starred/{item:\d+}' => [Module\Starred::class, [R::GET]], '/toggle_mobile' => [Module\ToggleMobile::class, [R::GET]], '/tos' => [Module\Tos::class, [R::GET]], + + '/update_profile' => [Module\Update\Profile::class, [R::GET]], + '/view/theme/{theme}/style.pcss' => [Module\Theme::class, [R::GET]], '/viewsrc/{item:\d+}' => [Module\Debug\ItemBody::class, [R::GET]], '/webfinger' => [Module\Debug\WebFinger::class, [R::GET]], diff --git a/tests/src/App/RouterTest.php b/tests/src/App/RouterTest.php index fc34df9575..6d4b8770d0 100644 --- a/tests/src/App/RouterTest.php +++ b/tests/src/App/RouterTest.php @@ -159,7 +159,7 @@ class RouterTest extends TestCase '/post' => [ '/it' => [Module\NodeInfo::class, [Router::POST]], ], - '/double' => [Module\Profile::class, [Router::GET, Router::POST]] + '/double' => [Module\Profile\Index::class, [Router::GET, Router::POST]] ], ], ]; @@ -177,7 +177,7 @@ class RouterTest extends TestCase $this->assertEquals(Module\Home::class, $router->getModuleClass('/')); $this->assertEquals(Module\Friendica::class, $router->getModuleClass('/group/route')); $this->assertEquals(Module\Xrd::class, $router->getModuleClass('/group2/group3/route')); - $this->assertEquals(Module\Profile::class, $router->getModuleClass('/double')); + $this->assertEquals(Module\Profile\Index::class, $router->getModuleClass('/double')); } /** @@ -191,6 +191,6 @@ class RouterTest extends TestCase // Don't find GET $this->assertEquals(Module\NodeInfo::class, $router->getModuleClass('/post/it')); - $this->assertEquals(Module\Profile::class, $router->getModuleClass('/double')); + $this->assertEquals(Module\Profile\Index::class, $router->getModuleClass('/double')); } } diff --git a/update.php b/update.php index 22ed4ae9e3..f86fea8dfd 100644 --- a/update.php +++ b/update.php @@ -430,3 +430,18 @@ function update_1330() return Update::SUCCESS; } + +function update_1332() +{ + $condition = ["`is-default` IS NOT NULL"]; + $profiles = DBA::select('profile', [], $condition); + + while ($profile = DBA::fetch($profiles)) { + DI::profileField()->migrateFromLegacyProfile($profile); + } + DBA::close($profiles); + + DBA::update('contact', ['profile-id' => null], ['`profile-id` IS NOT NULL']); + + return Update::SUCCESS; +} diff --git a/view/global.css b/view/global.css index b82ad6e030..a50d10de40 100644 --- a/view/global.css +++ b/view/global.css @@ -639,3 +639,37 @@ span.emoji.mastodon img { display: none; } } + +/* Profile Settings Custom Fields */ +body.dragging, body.dragging * { + cursor: ns-resize !important; +} + +.dragged { + position: absolute; + opacity: 0.5; + z-index: 2000; +} + +#profile-custom-fields > fieldset > legend { + cursor: ns-resize; +} + +#profile-custom-fields div.placeholder { + position: relative; + margin: 0; + padding: 0; + border-top: 1px dotted black; +} +#profile-custom-fields div.placeholder:before { + position: absolute; + content: ""; + width: 0; + height: 0; + margin-top: -7px; + left: -7px; + top: -1px; + border: 7px solid transparent; + border-left-color: black; + border-right: none; +} diff --git a/view/js/module/settings/profile/index.js b/view/js/module/settings/profile/index.js new file mode 100644 index 0000000000..1177e51ae7 --- /dev/null +++ b/view/js/module/settings/profile/index.js @@ -0,0 +1,13 @@ +$(function () { + $("#profile-custom-fields").sortable({ + containerSelector: 'div#profile-custom-fields', + handle: 'legend', + itemSelector: 'fieldset', + placeholder: '
', + onDrag: function($item, position, _super, event) { + delete position['left']; + $item.css(position); + event.preventDefault(); + } + }); +}); diff --git a/view/templates/contact_edit.tpl b/view/templates/contact_edit.tpl index d78d4162ef..5f582e066c 100644 --- a/view/templates/contact_edit.tpl +++ b/view/templates/contact_edit.tpl @@ -84,15 +84,6 @@
{{/if}} - - {{if $profile_select}} -
-

{{$lbl_vis1}}

-

{{$lbl_vis2}}

-
- {{$profile_select nofilter}} -
- {{/if}}
diff --git a/view/templates/profile-hide-wall.tpl b/view/templates/profile-hide-wall.tpl deleted file mode 100644 index ff106fa29f..0000000000 --- a/view/templates/profile-hide-wall.tpl +++ /dev/null @@ -1,17 +0,0 @@ - -

-{{$desc nofilter}} -

- -
- - - -
-
-
- - - -
-
diff --git a/view/templates/profile-in-directory.tpl b/view/templates/profile-in-directory.tpl deleted file mode 100644 index bfc13e85eb..0000000000 --- a/view/templates/profile-in-directory.tpl +++ /dev/null @@ -1,17 +0,0 @@ - -

-{{$desc nofilter}} -

- -
- - - -
-
-
- - - -
-
diff --git a/view/templates/profile-in-netdir.tpl b/view/templates/profile-in-netdir.tpl deleted file mode 100644 index c91601bce8..0000000000 --- a/view/templates/profile-in-netdir.tpl +++ /dev/null @@ -1,17 +0,0 @@ - -

-{{$desc nofilter}} -

- -
- - - -
-
-
- - - -
-
diff --git a/view/templates/profile/advanced.tpl b/view/templates/profile/advanced.tpl deleted file mode 100644 index 44e763f393..0000000000 --- a/view/templates/profile/advanced.tpl +++ /dev/null @@ -1,184 +0,0 @@ - -{{include file="section_title.tpl"}} - -
-
{{$profile.fullname.0}}
-
{{$profile.fullname.1}}
-
- -{{if $profile.membersince}} -
-
{{$profile.membersince.0}}
-
{{$profile.membersince.1}}
-
-{{/if}} - -{{if $profile.gender}} -
-
{{$profile.gender.0}}
-
{{$profile.gender.1}}
-
-{{/if}} - -{{if $profile.birthday}} -
-
{{$profile.birthday.0}}
-
{{$profile.birthday.1}}
-
-{{/if}} - -{{if $profile.age}} -
-
{{$profile.age.0}}
-
{{$profile.age.1}}
-
-{{/if}} - -{{if $profile.marital}} -
-
{{$profile.marital.0}}
-
{{$profile.marital.1}}{{if $profile.marital.with}} ({{$profile.marital.with nofilter}}){{/if}}{{if $profile.howlong}} {{$profile.howlong}}{{/if}}
-
-{{/if}} - -{{if $profile.sexual}} -
-
{{$profile.sexual.0}}
-
{{$profile.sexual.1}}
-
-{{/if}} - -{{if $profile.pub_keywords}} -
-
{{$profile.pub_keywords.0}}
-
{{$profile.pub_keywords.1}}
-
-{{/if}} - -{{if $profile.homepage}} -
-
{{$profile.homepage.0}}
-
{{$profile.homepage.1 nofilter}}
-
-{{/if}} - -{{if $profile.hometown}} -
-
{{$profile.hometown.0}}
-
{{$profile.hometown.1}}
-
-{{/if}} - -{{if $profile.politic}} -
-
{{$profile.politic.0}}
-
{{$profile.politic.1}}
-
-{{/if}} - -{{if $profile.religion}} -
-
{{$profile.religion.0}}
-
{{$profile.religion.1}}
-
-{{/if}} - -{{if $profile.about}} -
-
{{$profile.about.0}}
-
{{$profile.about.1 nofilter}}
-
-{{/if}} - -{{if $profile.interest}} -
-
{{$profile.interest.0}}
-
{{$profile.interest.1 nofilter}}
-
-{{/if}} - -{{if $profile.likes}} -
-
{{$profile.likes.0}}
-
{{$profile.likes.1 nofilter}}
-
-{{/if}} - -{{if $profile.dislikes}} -
-
{{$profile.dislikes.0}}
-
{{$profile.dislikes.1 nofilter}}
-
-{{/if}} - -{{if $profile.contact}} -
-
{{$profile.contact.0}}
-
{{$profile.contact.1 nofilter}}
-
-{{/if}} - - -{{if $profile.music}} -
-
{{$profile.music.0}}
-
{{$profile.music.1 nofilter}}
-
-{{/if}} - - -{{if $profile.book}} -
-
{{$profile.book.0}}
-
{{$profile.book.1 nofilter}}
-
-{{/if}} - - -{{if $profile.tv}} -
-
{{$profile.tv.0}}
-
{{$profile.tv.1 nofilter}}
-
-{{/if}} - - -{{if $profile.film}} -
-
{{$profile.film.0}}
-
{{$profile.film.1 nofilter}}
-
-{{/if}} - - -{{if $profile.romance}} -
-
{{$profile.romance.0}}
-
{{$profile.romance.1 nofilter}}
-
-{{/if}} - - -{{if $profile.work}} -
-
{{$profile.work.0}}
-
{{$profile.work.1 nofilter}}
-
-{{/if}} - -{{if $profile.education}} -
-
{{$profile.education.0}}
-
{{$profile.education.1 nofilter}}
-
-{{/if}} - - -{{if $profile.forumlist}} -
-
{{$profile.forumlist.0}}
-
{{$profile.forumlist.1 nofilter}}
-
-{{/if}} - - diff --git a/view/templates/profile/index.tpl b/view/templates/profile/index.tpl new file mode 100644 index 0000000000..8ad4d5eba1 --- /dev/null +++ b/view/templates/profile/index.tpl @@ -0,0 +1,124 @@ +
+ {{include file="section_title.tpl"}} + + {{* The link to edit the profile*}} +{{if $is_owner}} + +{{/if}} + + {{* Frio does split the profile information in "standard" and "advanced". This is the tab menu for switching between this modes *}} + {{if count($custom_fields)}} + + {{/if}} + +
+
+
+
{{$basic_fields.fullname.label}}
+
{{$basic_fields.fullname.value}}
+
+ + {{if $basic_fields.membersince}} +
+
+
{{$basic_fields.membersince.label}}
+
{{$basic_fields.membersince.value}}
+
+ {{/if}} + + {{if $basic_fields.birthday}} +
+
+
{{$basic_fields.birthday.label}}
+
{{$basic_fields.birthday.value}}
+
+ {{/if}} + + {{if $basic_fields.age}} +
+
+
{{$basic_fields.age.label}}
+
{{$basic_fields.age.value}}
+
+ {{/if}} + + {{if $basic_fields.location}} +
+
+
{{$basic_fields.location.label}}
+
{{$basic_fields.location.value}}
+
+ {{/if}} + + {{if $basic_fields.homepage}} +
+
+
{{$basic_fields.homepage.label}}
+
{{$basic_fields.homepage.value nofilter}}
+
+ {{/if}} + + {{if $basic_fields.xmpp}} +
+
+
{{$basic_fields.xmpp.label}}
+
{{$basic_fields.xmpp.value nofilter}}
+
+ {{/if}} + + {{if $basic_fields.pub_keywords}} +
+
+
{{$basic_fields.pub_keywords.label}}
+
+ {{foreach $basic_fields.pub_keywords.value as $tag}} + {{$tag.label}} + {{/foreach}} +
+
+ {{/if}} +
+ + {{if count($custom_fields)}} +
+ {{foreach $custom_fields as $custom_field}} +
+
+
{{$custom_field.label}}
+
{{$custom_field.value nofilter}}
+
+ {{/foreach}} +
+ {{/if}} +
+
diff --git a/view/templates/profile_edit.tpl b/view/templates/profile_edit.tpl deleted file mode 100644 index 08e833f10a..0000000000 --- a/view/templates/profile_edit.tpl +++ /dev/null @@ -1,458 +0,0 @@ - -{{$default nofilter}} - -

{{$banner}}

- - - - - - -
-
- - -{{if $detailled_profile}} -{{include file="field_yesno.tpl" field=$details}} -
- -
*
-
-
- -
- - -
-
- -
- - -
-
- - -
- -{{$gender nofilter}} -
-
- -
-{{$dob nofilter}} -
-
- -{{$hide_friends nofilter}} - -
- -
-
- - -
- - -
-
- -
- - -
-
- - -
- - -
-
- -
- - -
-
- -
- - -
-
- -
- - -
-
- -
- -
-
- -
- -{{$marital nofilter}} -
- - - - - -
- -
- -{{$sexual nofilter}} -
-
- - - -
- - -
-
- -
- - -
{{$xmpp.3}}
-
- - -
- - -
-
- -
- - -
-
- -
- - -
{{$pub_keywords.3}}
-
- -
- - -
{{$prv_keywords.3}}
-
- - -
- -
-
- -
-

-{{$about.1}} -

- - - -
-
- - -
-

-{{$interest.1}} -

- - - -
-
- - -
-

-{{$likes.1}} -

- - - -
-
- - -
-

-{{$dislikes.1}} -

- - - -
-
- - -
-

-{{$contact.1}} -

- - - -
-
- - -
- -
-
- - -
-

-{{$music.1}} -

- - - -
-
- -
-

-{{$book.1}} -

- - - -
-
- - - -
-

-{{$tv.1}} -

- - - -
-
- - - -
-

-{{$film.1}} -

- - - -
-
- - -
- -
-
- - -
-

-{{$romance.1}} -

- - - -
-
- - - -
-

-{{$work.1}} -

- - - -
-
- - - -
-

-{{$education.1}} -

- - - -
-
- - - -
- -
-
- -{{else}} -{{if $personal_account}} -{{include file="field_yesno.tpl" field=$details}} -{{/if}} -
- -
*
-
-
- -
- - -
-
- -{{if $personal_account}} -
- -{{$gender nofilter}} -
-
- -
-{{$dob nofilter}} -
-
-{{/if}} - -
- - -
-
- -
- - -
{{$xmpp.3}}
-
- - -{{$hide_friends nofilter}} - -
- - -
-
- -
- - -
-
- - -
- - -
-
- -
- - -
-
- -
- - -
-
- -
- - -
{{$pub_keywords.3}}
-
- -
- - -
{{$prv_keywords.3}}
-
- -
-

-{{$about.1}} -

- - - -
-
- -
- -
-
- - - - - - - - - - - - - - - - - - - - - -{{/if}} -
-
- diff --git a/view/templates/profile_edlink.tpl b/view/templates/profile_edlink.tpl deleted file mode 100644 index 54d7a34dd0..0000000000 --- a/view/templates/profile_edlink.tpl +++ /dev/null @@ -1,3 +0,0 @@ - -
-
\ No newline at end of file diff --git a/view/templates/profile_entry.tpl b/view/templates/profile_entry.tpl deleted file mode 100644 index e7c3a31dda..0000000000 --- a/view/templates/profile_entry.tpl +++ /dev/null @@ -1,12 +0,0 @@ - -
-
- {{$alt}} -
-
-
- {{$profile_name}} -
-
{{$visible nofilter}}
-
-
diff --git a/view/templates/profile_listing_header.tpl b/view/templates/profile_listing_header.tpl deleted file mode 100644 index 65f38f3a64..0000000000 --- a/view/templates/profile_listing_header.tpl +++ /dev/null @@ -1,14 +0,0 @@ - -

{{$header}}

- -

- {{$chg_photo}} -

- - - -
- {{$profiles nofilter}} -
diff --git a/view/templates/settings/profile/field/edit.tpl b/view/templates/settings/profile/field/edit.tpl new file mode 100644 index 0000000000..cff432709e --- /dev/null +++ b/view/templates/settings/profile/field/edit.tpl @@ -0,0 +1,13 @@ +
+ ≡ {{$profile_field.legend}} + + + + {{include file="field_input.tpl" field=$profile_field.fields.label}} + + {{include file="field_textarea.tpl" field=$profile_field.fields.value}} +
+ Permissions + {{$profile_field.fields.acl nofilter}} +
+
\ No newline at end of file diff --git a/view/templates/profile-hide-friends.tpl b/view/templates/settings/profile/hide-friends.tpl similarity index 100% rename from view/templates/profile-hide-friends.tpl rename to view/templates/settings/profile/hide-friends.tpl diff --git a/view/templates/settings/profile/index.tpl b/view/templates/settings/profile/index.tpl new file mode 100644 index 0000000000..bf98bb1587 --- /dev/null +++ b/view/templates/settings/profile/index.tpl @@ -0,0 +1,117 @@ +

{{$banner}}

+ +{{$default nofilter}} + + + + + +
+
+ + +
+ + +
+
+
+ + +
+
+
+ {{$dob nofilter}} +
+
+ {{$hide_friends nofilter}} +
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
{{$xmpp.3}}
+
+
+ + +
+
{{$pub_keywords.3}}
+
+
+ + +
+
{{$prv_keywords.3}}
+
+
+ +
+
+ +

{{$lbl_custom_fields_section}}

+ {{$custom_fields_description nofilter}} +
+ {{foreach $custom_fields as $custom_field}} + {{include file="settings/profile/field/edit.tpl" profile_field=$custom_field}} + {{/foreach}} +
+ +
+ +
+
+
+
+ diff --git a/view/templates/profed_head.tpl b/view/templates/settings/profile/index_head.tpl similarity index 100% rename from view/templates/profed_head.tpl rename to view/templates/settings/profile/index_head.tpl diff --git a/view/templates/settings/profile/link.tpl b/view/templates/settings/profile/link.tpl new file mode 100644 index 0000000000..d387ea683c --- /dev/null +++ b/view/templates/settings/profile/link.tpl @@ -0,0 +1,2 @@ +
+
diff --git a/view/templates/welcome.tpl b/view/templates/welcome.tpl index 4e0ae7754d..0ac061b47e 100644 --- a/view/templates/welcome.tpl +++ b/view/templates/welcome.tpl @@ -26,11 +26,11 @@ {{$profile_photo_txt nofilter}}
  • - {{$profiles_link}}
    + {{$profiles_link}}
    {{$profiles_txt nofilter}}
  • - {{$profiles_keywords_link}}
    + {{$profiles_keywords_link}}
    {{$profiles_keywords_txt nofilter}}
  • diff --git a/view/theme/duepuntozero/style.css b/view/theme/duepuntozero/style.css index a06a6d9083..3e8151c5ac 100644 --- a/view/theme/duepuntozero/style.css +++ b/view/theme/duepuntozero/style.css @@ -646,10 +646,14 @@ input#dfrn-url { #profile-edit-links ul { list-style-type: none; } - #profile-edit-links li { margin-top: 10px; } + +#profile-menu { + display: none; +} + .profile-edit-side-div { float: right; } diff --git a/view/theme/frio/templates/contact_edit.tpl b/view/theme/frio/templates/contact_edit.tpl index f9454dc654..9bf7b32333 100644 --- a/view/theme/frio/templates/contact_edit.tpl +++ b/view/theme/frio/templates/contact_edit.tpl @@ -172,35 +172,6 @@ {{/if}} - {{if $lbl_vis1}} -
    - -
    -
    - {{if $profile_select}} -
    -

    {{$lbl_vis2}}

    -
    -
    - {{$profile_select nofilter}} -
    -
    - {{/if}} - -
    - -
    -
    -
    -
    -
    - {{/if}} {{* End of the form *}} diff --git a/view/theme/frio/templates/profile/advanced.tpl b/view/theme/frio/templates/profile/advanced.tpl deleted file mode 100644 index 3ca4000e6a..0000000000 --- a/view/theme/frio/templates/profile/advanced.tpl +++ /dev/null @@ -1,234 +0,0 @@ -
    -

    {{$title}}

    - - {{* The link to edit the profile*}} - {{if $profile.edit}} - -
    - {{/if}} - - {{* Frio does split the profile information in "standard" and "advanced". This is the tab menu for swithching between this modes *}} - - -
    -
    -
    -
    {{$profile.fullname.0}}
    -
    {{$profile.fullname.1}}
    -
    - - {{if $profile.membersince}} -
    -
    -
    {{$profile.membersince.0}}
    -
    {{$profile.membersince.1}}
    -
    - {{/if}} - - {{if $profile.gender}} -
    -
    -
    {{$profile.gender.0}}
    -
    {{$profile.gender.1}}
    -
    - {{/if}} - - {{if $profile.birthday}} -
    -
    -
    {{$profile.birthday.0}}
    -
    {{$profile.birthday.1}}
    -
    - {{/if}} - - {{if $profile.age}} -
    -
    -
    {{$profile.age.0}}
    -
    {{$profile.age.1}}
    -
    - {{/if}} - - {{if $profile.hometown}} -
    -
    -
    {{$profile.hometown.0}}
    -
    {{$profile.hometown.1}}
    -
    - {{/if}} - - {{if $profile.marital}} -
    -
    -
    {{$profile.marital.0}}
    -
    {{$profile.marital.1}}{{if $profile.marital.with}} ({{$profile.marital.with nofilter}}){{/if}}{{if $profile.howlong}} {{$profile.howlong}}{{/if}}
    -
    - {{/if}} - - {{if $profile.homepage}} -
    -
    -
    {{$profile.homepage.0}}
    -
    {{$profile.homepage.1 nofilter}}
    -
    - {{/if}} - - {{if $profile.about}} -
    -
    -
    {{$profile.about.0}}
    -
    {{$profile.about.1 nofilter}}
    -
    - {{/if}} - - {{if $profile.pub_keywords}} -
    -
    -
    {{$profile.pub_keywords.0}}
    -
    {{$profile.pub_keywords.1}}
    -
    - {{/if}} -
    - -
    - {{if $profile.sexual}} -
    -
    -
    {{$profile.sexual.0}}
    -
    {{$profile.sexual.1}}
    -
    - {{/if}} - - {{if $profile.politic}} -
    -
    -
    {{$profile.politic.0}}
    -
    {{$profile.politic.1}}
    -
    - {{/if}} - - {{if $profile.religion}} -
    -
    -
    {{$profile.religion.0}}
    -
    {{$profile.religion.1}}
    -
    - {{/if}} - - - {{if $profile.interest}} -
    -
    -
    {{$profile.interest.0}}
    -
    {{$profile.interest.1 nofilter}}
    -
    - {{/if}} - - {{if $profile.likes}} -
    -
    -
    {{$profile.likes.0}}
    -
    {{$profile.likes.1 nofilter}}
    -
    - {{/if}} - - {{if $profile.dislikes}} -
    -
    -
    {{$profile.dislikes.0}}
    -
    {{$profile.dislikes.1 nofilter}}
    -
    - {{/if}} - - {{if $profile.contact}} -
    -
    -
    {{$profile.contact.0}}
    -
    {{$profile.contact.1 nofilter}}
    -
    - {{/if}} - - {{if $profile.music}} -
    -
    -
    {{$profile.music.0}}
    -
    {{$profile.music.1 nofilter}}
    -
    - {{/if}} - - - {{if $profile.book}} -
    -
    -
    {{$profile.book.0}}
    -
    {{$profile.book.1 nofilter}}
    -
    - {{/if}} - - - {{if $profile.tv}} -
    -
    -
    {{$profile.tv.0}}
    -
    {{$profile.tv.1 nofilter}}
    -
    - {{/if}} - - - {{if $profile.film}} -
    -
    -
    {{$profile.film.0}}
    -
    {{$profile.film.1 nofilter}}
    -
    - {{/if}} - - - {{if $profile.romance}} -
    -
    -
    {{$profile.romance.0}}
    -
    {{$profile.romance.1 nofilter}}
    -
    - {{/if}} - - - {{if $profile.work}} -
    -
    -
    {{$profile.work.0}}
    -
    {{$profile.work.1 nofilter}}
    -
    - {{/if}} - - {{if $profile.education}} -
    -
    -
    {{$profile.education.0}}
    -
    {{$profile.education.1 nofilter}}
    -
    - {{/if}} - - {{if $profile.forumlist}} -
    -
    -
    {{$profile.forumlist.0}}
    -
    {{$profile.forumlist.1 nofilter}}
    -
    - {{/if}} -
    -
    -
    diff --git a/view/theme/frio/templates/profile_edit.tpl b/view/theme/frio/templates/profile_edit.tpl deleted file mode 100644 index 946443cf11..0000000000 --- a/view/theme/frio/templates/profile_edit.tpl +++ /dev/null @@ -1,343 +0,0 @@ - -
    - {{include file="section_title.tpl" title=$banner}} - - {{* The actions dropdown which can performed to the current profile *}} - - - - -
    - - - -
    - -
    - - -
    - -
    - -
    -
    -
    -
    - - {{* Most of the Variables used below are arrays in the following style - 0 => Some kind of identifier (e.g. for the ID) - 1 => The label description - 2 => The input values - 3 => The additional help text (if available) - *}} - -
    - - - {{* Some hints to characteristics of the current profile (if available) *}} - {{if $is_default}} - - {{/if}} - - {{* friendica differs in $detailled_profile (all fields available and a short Version if this is variable false *}} - {{if $detailled_profile}} -
    - {{* The personal settings *}} -
    - - {{* for the $detailled_profile we use bootstraps collapsable panel-groups to have expandable groups *}} -
    -
    - {{include file="field_yesno.tpl" field=$details}} - - {{include file="field_input.tpl" field=$profile_name}} - - {{include file="field_input.tpl" field=$name}} - - {{include file="field_input.tpl" field=$pdesc}} - -
    - - {{$gender nofilter}} -
    -
    - - {{$dob nofilter}} - - {{$hide_friends nofilter}} - -
    - -
    -
    -
    -
    -
    - - {{* The location settings *}} -
    - -
    -
    - {{include file="field_input.tpl" field=$address}} - - {{include file="field_input.tpl" field=$locality}} - - - {{include file="field_input.tpl" field=$postal_code}} - -
    - - -
    -
    - -
    - - -
    -
    - - {{include file="field_input.tpl" field=$hometown}} - -
    - -
    -
    -
    -
    -
    - - {{* The settings for relations *}} -
    - -
    -
    -
    - - {{$marital.selector nofilter}} -
    -
    - - {{include file="field_input.tpl" field=$with}} - - {{include file="field_input.tpl" field=$howlong}} - -
    - - {{$sexual.selector nofilter}} -
    -
    - -
    - -
    -
    -
    -
    -
    - - {{* The miscellanous other settings *}} -
    - -
    -
    - {{include file="field_input.tpl" field=$homepage}} - - {{include file="field_input.tpl" field=$xmpp}} - - {{include file="field_input.tpl" field=$pub_keywords}} - - {{include file="field_input.tpl" field=$prv_keywords}} - - {{include file="field_input.tpl" field=$politic}} - - {{include file="field_input.tpl" field=$religion}} - - - {{include file="field_textarea.tpl" field=$about}} - - {{include file="field_textarea.tpl" field=$contact}} - - {{include file="field_textarea.tpl" field=$interest}} - - {{include file="field_textarea.tpl" field=$likes}} - - {{include file="field_textarea.tpl" field=$dislikes}} - - {{include file="field_textarea.tpl" field=$music}} - - {{include file="field_textarea.tpl" field=$book}} - - {{include file="field_textarea.tpl" field=$tv}} - - {{include file="field_textarea.tpl" field=$film}} - - {{include file="field_textarea.tpl" field=$romance}} - - {{include file="field_textarea.tpl" field=$work}} - - {{include file="field_textarea.tpl" field=$education}} - -
    - -
    -
    -
    -
    -
    -
    - - {{else}} - {{* if $detailled_profile not available a short version of the setting page is displayed *}} - {{if $personal_account}} - {{include file="field_yesno.tpl" field=$details}} - {{/if}} - - {{include file="field_input.tpl" field=$profile_name}} - - {{include file="field_input.tpl" field=$name}} - - {{if $personal_account}} -
    - - {{$gender nofilter}} -
    -
    - - {{$dob nofilter}} - - {{/if}} - - {{include file="field_input.tpl" field=$homepage}} - - {{include file="field_input.tpl" field=$xmpp}} - - {{$hide_friends nofilter}} - - {{include file="field_input.tpl" field=$address}} - - {{include file="field_input.tpl" field=$locality}} - - - {{include file="field_input.tpl" field=$postal_code}} - -
    - - -
    -
    - -
    - - -
    -
    - - {{include file="field_input.tpl" field=$pub_keywords}} - - {{include file="field_input.tpl" field=$prv_keywords}} - - {{include file="field_textarea.tpl" field=$about}} - -
    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - {{/if}} -
    -
    - - diff --git a/view/theme/frio/templates/profile_entry.tpl b/view/theme/frio/templates/profile_entry.tpl deleted file mode 100644 index ba8f547171..0000000000 --- a/view/theme/frio/templates/profile_entry.tpl +++ /dev/null @@ -1,15 +0,0 @@ - -
    -
    - {{$alt}} -
    -
    -
    - {{$profile_name}} -
    -
    - {{$visible nofilter}} -
    -
    -
    - diff --git a/view/theme/frio/templates/profile_listing_header.tpl b/view/theme/frio/templates/profile_listing_header.tpl deleted file mode 100644 index 3419c41049..0000000000 --- a/view/theme/frio/templates/profile_listing_header.tpl +++ /dev/null @@ -1,16 +0,0 @@ - -
    -
    -

    {{$header}}

    -
    - - -
    -
    - {{$profiles nofilter}} -
    -
    -
    diff --git a/view/theme/frio/templates/settings/profile/field/edit.tpl b/view/theme/frio/templates/settings/profile/field/edit.tpl new file mode 100644 index 0000000000..f28e559f5e --- /dev/null +++ b/view/theme/frio/templates/settings/profile/field/edit.tpl @@ -0,0 +1,29 @@ +
    + ≡ {{$profile_field.legend}} + + + + {{include file="field_input.tpl" field=$profile_field.fields.label}} + + {{include file="field_textarea.tpl" field=$profile_field.fields.value}} + + {{* Block for setting default permissions *}} +

    + {{$profile_field.permissions}} {{$profile_field.permdesc}} +

    + + {{* We include the aclModal directly into the template since we cant use frio's default modal *}} + +
    \ No newline at end of file diff --git a/view/theme/frio/templates/settings/profile/index.tpl b/view/theme/frio/templates/settings/profile/index.tpl new file mode 100644 index 0000000000..4d7cf8d70e --- /dev/null +++ b/view/theme/frio/templates/settings/profile/index.tpl @@ -0,0 +1,184 @@ +
    + {{include file="section_title.tpl" title=$banner}} + + {{* The actions dropdown which can performed to the current profile *}} + + + + +
    + + +
    + +
    + + +
    + +
    + +
    +
    +
    +
    + + {{* Most of the Variables used below are arrays in the following style + 0 => Some kind of identifier (e.g. for the ID) + 1 => The label description + 2 => The input values + 3 => The additional help text (if available) + *}} + +
    + + +
    + {{* The personal settings *}} +
    + + {{* for the $detailed_profile we use bootstraps collapsable panel-groups to have expandable groups *}} +
    +
    + {{include file="field_input.tpl" field=$name}} + + {{include file="field_input.tpl" field=$pdesc}} + + {{$dob nofilter}} + + {{$hide_friends nofilter}} + +
    + +
    +
    +
    +
    +
    + + {{* The location settings *}} +
    + +
    +
    + {{include file="field_input.tpl" field=$address}} + + {{include file="field_input.tpl" field=$locality}} + + {{include file="field_input.tpl" field=$postal_code}} + +
    + + +
    +
    + +
    + + +
    +
    + +
    + +
    +
    +
    +
    +
    + + {{* The miscellanous other settings *}} +
    + +
    +
    + {{include file="field_input.tpl" field=$homepage}} + + {{include file="field_input.tpl" field=$xmpp}} + + {{include file="field_input.tpl" field=$pub_keywords}} + + {{include file="field_input.tpl" field=$prv_keywords}} + +
    + +
    +
    +
    +
    +
    + + {{* The miscellanous other settings *}} +
    + +
    +
    + {{$custom_fields_description nofilter}} +
    + {{foreach $custom_fields as $custom_field}} + {{include file="settings/profile/field/edit.tpl" profile_field=$custom_field}} + {{/foreach}} +
    + +
    + +
    +
    +
    +
    +
    +
    +
    +
    + + diff --git a/view/theme/quattro/dark/style.css b/view/theme/quattro/dark/style.css index 9baa46ea63..64e4840e86 100644 --- a/view/theme/quattro/dark/style.css +++ b/view/theme/quattro/dark/style.css @@ -1964,6 +1964,9 @@ ul.tabs li .active { list-style: none; margin-top: 10px; } +#profile-menu { + display: none; +} #profile-edit-default-desc { color: #FF0000; border: 1px solid #FF8888; diff --git a/view/theme/quattro/green/style.css b/view/theme/quattro/green/style.css index 2997b53734..40a0c71617 100644 --- a/view/theme/quattro/green/style.css +++ b/view/theme/quattro/green/style.css @@ -1964,6 +1964,9 @@ ul.tabs li .active { list-style: none; margin-top: 10px; } +#profile-menu { + display: none; +} #profile-edit-default-desc { color: #FF0000; border: 1px solid #FF8888; diff --git a/view/theme/quattro/lilac/style.css b/view/theme/quattro/lilac/style.css index 8ea0e2d55c..16887f1e6a 100644 --- a/view/theme/quattro/lilac/style.css +++ b/view/theme/quattro/lilac/style.css @@ -1964,6 +1964,9 @@ ul.tabs li .active { list-style: none; margin-top: 10px; } +#profile-menu { + display: none; +} #profile-edit-default-desc { color: #FF0000; border: 1px solid #FF8888; diff --git a/view/theme/quattro/quattro.less b/view/theme/quattro/quattro.less index ae2fef7936..ce5c038d7d 100644 --- a/view/theme/quattro/quattro.less +++ b/view/theme/quattro/quattro.less @@ -1257,6 +1257,10 @@ ul.tabs { margin-top: 10px; } +#profile-menu { + display: none; +} + #profile-edit-default-desc { color: #FF0000; border: 1px solid #FF8888; diff --git a/view/theme/smoothly/style.css b/view/theme/smoothly/style.css index 9ad42b0037..52324c0a97 100644 --- a/view/theme/smoothly/style.css +++ b/view/theme/smoothly/style.css @@ -104,6 +104,14 @@ input[type=submit]:active { top: 1px; } +.btn { + background-color: transparent; + box-shadow: none; + border: none; + padding: 0; + text-align: inherit; +} + #search-text, #search-submit, #search-save { @@ -2127,6 +2135,10 @@ div[id$="wrapper"] br { position: absolute; } +#profile-menu { + display: none; +} + #cropimage-wrapper { float:left; } diff --git a/view/theme/vier/style.css b/view/theme/vier/style.css index 92ac8a42a2..f28f8b556f 100644 --- a/view/theme/vier/style.css +++ b/view/theme/vier/style.css @@ -2406,6 +2406,10 @@ aside #id_password { list-style: none; } +#profile-menu { + display: none; +} + .profile-edit-side-div { /* display: none; */ float: right; @@ -3236,7 +3240,6 @@ img.photo-album-photo { } #profile-edit-wrapper .toggle-section-content { background:#ededed; - max-width:599px; padding:5px; } diff --git a/view/theme/vier/templates/contact_edit.tpl b/view/theme/vier/templates/contact_edit.tpl index bd07f18213..3eaf163fc6 100644 --- a/view/theme/vier/templates/contact_edit.tpl +++ b/view/theme/vier/templates/contact_edit.tpl @@ -85,15 +85,6 @@
    {{/if}} - - {{if $profile_select}} -
    -

    {{$lbl_vis1}}

    -

    {{$lbl_vis2}}

    -
    - {{$profile_select nofilter}} -
    - {{/if}} {{/if}} diff --git a/view/theme/vier/templates/profile/advanced.tpl b/view/theme/vier/templates/profile/advanced.tpl deleted file mode 100644 index 20c4e84b13..0000000000 --- a/view/theme/vier/templates/profile/advanced.tpl +++ /dev/null @@ -1,186 +0,0 @@ -{{if $profile.edit}} -
    - {{$profile.edit.1}} -
    -{{/if}} - -{{include file="section_title.tpl"}} - -
    -
    {{$profile.fullname.0}}
    -
    {{$profile.fullname.1}}
    -
    - -{{if $profile.membersince}} -
    -
    {{$profile.membersince.0}}
    -
    {{$profile.membersince.1}}
    -
    -{{/if}} - -{{if $profile.gender}} -
    -
    {{$profile.gender.0}}
    -
    {{$profile.gender.1}}
    -
    -{{/if}} - -{{if $profile.birthday}} -
    -
    {{$profile.birthday.0}}
    -
    {{$profile.birthday.1}}
    -
    -{{/if}} - -{{if $profile.age}} -
    -
    {{$profile.age.0}}
    -
    {{$profile.age.1}}
    -
    -{{/if}} - -{{if $profile.marital}} -
    -
    {{$profile.marital.0}}
    -
    {{$profile.marital.1}}{{if $profile.marital.with}} ({{$profile.marital.with}}){{/if}}{{if $profile.howlong}} {{$profile.howlong}}{{/if}}
    -
    -{{/if}} - -{{if $profile.sexual}} -
    -
    {{$profile.sexual.0}}
    -
    {{$profile.sexual.1}}
    -
    -{{/if}} - -{{if $profile.pub_keywords}} -
    -
    {{$profile.pub_keywords.0}}
    -
    {{$profile.pub_keywords.1}}
    -
    -{{/if}} - -{{if $profile.homepage}} -
    -
    {{$profile.homepage.0}}
    -
    {{$profile.homepage.1 nofilter}}
    -
    -{{/if}} - -{{if $profile.hometown}} -
    -
    {{$profile.hometown.0}}
    -
    {{$profile.hometown.1}}
    -
    -{{/if}} - -{{if $profile.politic}} -
    -
    {{$profile.politic.0}}
    -
    {{$profile.politic.1}}
    -
    -{{/if}} - -{{if $profile.religion}} -
    -
    {{$profile.religion.0}}
    -
    {{$profile.religion.1}}
    -
    -{{/if}} - -{{if $profile.about}} -
    -
    {{$profile.about.0}}
    -
    {{$profile.about.1 nofilter}}
    -
    -{{/if}} - -{{if $profile.interest}} -
    -
    {{$profile.interest.0}}
    -
    {{$profile.interest.1 nofilter}}
    -
    -{{/if}} - -{{if $profile.likes}} -
    -
    {{$profile.likes.0}}
    -
    {{$profile.likes.1 nofilter}}
    -
    -{{/if}} - -{{if $profile.dislikes}} -
    -
    {{$profile.dislikes.0}}
    -
    {{$profile.dislikes.1 nofilter}}
    -
    -{{/if}} - -{{if $profile.contact}} -
    -
    {{$profile.contact.0}}
    -
    {{$profile.contact.1 nofilter}}
    -
    -{{/if}} - - -{{if $profile.music}} -
    -
    {{$profile.music.0}}
    -
    {{$profile.music.1 nofilter}}
    -
    -{{/if}} - - -{{if $profile.book}} -
    -
    {{$profile.book.0}}
    -
    {{$profile.book.1 nofilter}}
    -
    -{{/if}} - - -{{if $profile.tv}} -
    -
    {{$profile.tv.0}}
    -
    {{$profile.tv.1 nofilter}}
    -
    -{{/if}} - - -{{if $profile.film}} -
    -
    {{$profile.film.0}}
    -
    {{$profile.film.1 nofilter}}
    -
    -{{/if}} - - -{{if $profile.romance}} -
    -
    {{$profile.romance.0}}
    -
    {{$profile.romance.1 nofilter}}
    -
    -{{/if}} - - -{{if $profile.work}} -
    -
    {{$profile.work.0}}
    -
    {{$profile.work.1 nofilter}}
    -
    -{{/if}} - -{{if $profile.education}} -
    -
    {{$profile.education.0}}
    -
    {{$profile.education.1 nofilter}}
    -
    -{{/if}} - -{{if $profile.forumlist}} -
    -
    {{$profile.forumlist.0}}
    -
    {{$profile.forumlist.1 nofilter}}
    -
    -{{/if}} diff --git a/view/theme/vier/templates/profile_edit.tpl b/view/theme/vier/templates/profile_edit.tpl deleted file mode 100644 index 066b2566f6..0000000000 --- a/view/theme/vier/templates/profile_edit.tpl +++ /dev/null @@ -1,454 +0,0 @@ - - -

    {{$banner}}

    - - - - -{{$default nofilter}} - -
    - -
    - - - - -{{if $detailled_profile}} -
    - {{$lbl_picture_section}} » - -
    -{{/if}} -
    - -
    - - -{{if $detailled_profile}} - -
    - {{$lbl_basic_section}} » - -
    - - -
    - {{$lbl_about_section}} » - -
    - - -
    - {{$lbl_status_section}} » - -
    - - -
    - {{$lbl_interests_section}} » - -
    -{{else}} - -{{if $personal_account}} -{{include file="field_yesno.tpl" field=$details}} -{{/if}} -
    - -
    *
    -
    -
    - -
    - - -
    -
    - -{{if $personal_account}} -
    - -{{$gender nofilter}} -
    -
    - -
    -{{$dob nofilter}} -
    -
    -{{/if}} - -
    - - -
    -
    {{$xmpp.3}}
    -
    - -
    - - -
    -
    - -{{$hide_friends nofilter}} - -
    - - -
    -
    - -
    - - -
    -
    - - -
    - - -
    -
    - -
    - - -
    -
    -
    - - -
    -
    - -
    - - -
    {{$pub_keywords.3}}
    -
    - -
    - - -
    {{$prv_keywords.3}}
    -
    - -
    -

    -{{$about.1}} -

    - - - -
    -
    - -
    - -
    -
    - - - - - - - - - - - - - - - - - - - - - -{{/if}} - -
    -
    - diff --git a/view/theme/vier/templates/settings/profile/index.tpl b/view/theme/vier/templates/settings/profile/index.tpl new file mode 100644 index 0000000000..eab7c2c4a4 --- /dev/null +++ b/view/theme/vier/templates/settings/profile/index.tpl @@ -0,0 +1,171 @@ + + +

    {{$banner}}

    + + + + +
    +
    + + + +
    + {{$lbl_picture_section}} » + +
    +
    + +
    + + + +
    + {{$lbl_personal_section}} » + +
    + +
    + {{$lbl_location_section}} » + +
    + +
    + {{$lbl_custom_fields_section}} » + +
    +
    +
    + diff --git a/view/theme/vier/templates/profile_edlink.tpl b/view/theme/vier/templates/settings/profile/link.tpl similarity index 100% rename from view/theme/vier/templates/profile_edlink.tpl rename to view/theme/vier/templates/settings/profile/link.tpl diff --git a/view/theme/vier/theme.php b/view/theme/vier/theme.php index 9188bcbc99..e5d5e1ce97 100644 --- a/view/theme/vier/theme.php +++ b/view/theme/vier/theme.php @@ -140,14 +140,14 @@ function vier_community_info() // last 9 users if ($show_lastusers) { - $publish = (DI::config()->get('system', 'publish_all') ? '' : " AND `publish` = 1 "); + $publish = (DI::config()->get('system', 'publish_all') ? '' : "`publish` = 1"); $order = " ORDER BY `register_date` DESC "; $tpl = Renderer::getMarkupTemplate('ch_directory_item.tpl'); $r = q("SELECT `profile`.*, `user`.`nickname` FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` - WHERE `is-default` = 1 $publish AND `user`.`blocked` = 0 $order LIMIT %d , %d ", + WHERE $publish AND `user`.`blocked` = 0 $order LIMIT %d , %d ", 0, 9 );