From a0fce0c603f6fc686dbab66268a786114786b188 Mon Sep 17 00:00:00 2001 From: =?utf8?q?St=C3=A9phane=20Jacob?= Date: Sun, 4 Jul 2010 19:57:07 +0200 Subject: [PATCH] Notifies profile's owner when profile modified by a third party (Closes #1038). MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Jacob --- bin/cron/profile_modification.php | 88 +++++++++++++++++++++++++++++++++ configs/mails.conf | 5 ++ configs/platal.cron.in | 3 ++ include/validations.inc.php | 42 ++++++++-------- modules/profile/addresses.inc.php | 13 +++++ modules/profile/decos.inc.php | 9 ++++ modules/profile/general.inc.php | 42 ++++++++++++++++ modules/profile/groups.inc.php | 9 ++++ modules/profile/jobs.inc.php | 14 ++++++ modules/profile/mentor.inc.php | 14 ++++++ modules/profile/page.inc.php | 65 ++++++++++++++++++++++-- modules/profile/skills.inc.php | 8 +++ templates/profile/notification.mail.tpl | 45 +++++++++++++++++ upgrade/1.0.1/01_profiles.sql | 13 +++++ 14 files changed, 343 insertions(+), 27 deletions(-) create mode 100755 bin/cron/profile_modification.php create mode 100644 templates/profile/notification.mail.tpl create mode 100644 upgrade/1.0.1/01_profiles.sql diff --git a/bin/cron/profile_modification.php b/bin/cron/profile_modification.php new file mode 100755 index 0000000..0aa2172 --- /dev/null +++ b/bin/cron/profile_modification.php @@ -0,0 +1,88 @@ +#!/usr/bin/php5 -q +next(); + +$pid = $values['pid']; +$sex = ($values['sex'] == 'female') ? 1 : 0; +$yourself = $values['yourself']; +$alias = $values['alias']; +$hrpid = $values['hrpid']; +$modifications = array(); +$modifications[] = array( + 'full_name' => $values['full_name'], + 'field' => $values['field'], + 'oldText' => $values['oldText'], + 'newText' => $values['newText'], +); + +while ($values = $res->next()) { + if ($values['pid'] != $pid) { + $mailer = new PlMailer('profile/notification.mail.tpl'); + $mailer->addTo($alias . '@' . $globals->mail->domain); + $mailer->assign('modifications', $modifications); + $mailer->assign('yourself', $yourself); + $mailer->assign('hrpid', $hrpid); + $mailer->assign('sex', $sex); + $mailer->assign('date', $date); + $mailer->send(); + $modifications = array(); + } + $pid = $values['pid']; + $sex = ($values['sex'] == 'female') ? 1 : 0; + $yourself = $values['yourself']; + $alias = $values['alias']; + $hrpid = $values['hrpid']; + $modifications[] = array( + 'full_name' => $values['full_name'], + 'field' => $values['field'], + 'oldText' => $values['oldText'], + 'newText' => $values['newText'], + ); +} +$mailer = new PlMailer('profile/notification.mail.tpl'); +$mailer->addTo($alias . '@' . $globals->mail->domain); +$mailer->assign('modifications', $modifications); +$mailer->assign('yourself', $yourself); +$mailer->assign('hrpid', $hrpid); +$mailer->assign('sex', $sex); +$mailer->assign('date', $date); +$mailer->send(); + +XDB::execute('DELETE FROM profile_modifications'); + +// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: +?> diff --git a/configs/mails.conf b/configs/mails.conf index de5ac1b..059b0d9 100644 --- a/configs/mails.conf +++ b/configs/mails.conf @@ -66,3 +66,8 @@ from="Gestion des groupes X sur Polytechnique.net" from="Webmaster Polytechnique.org" to=registration+watch@staff.m4x.org replyto=registration+watch@staff.m4x.org + +[profile] +from="Polytechnique.org" +cc="Polytechnique.org" + diff --git a/configs/platal.cron.in b/configs/platal.cron.in index f445da4..2984ef9 100644 --- a/configs/platal.cron.in +++ b/configs/platal.cron.in @@ -17,6 +17,9 @@ WD=/home/web/prod/platal/bin/cron 0 2 * * * web cd $WD; ./notifs.birthday.php 0 4 * * 6 web cd $WD; ./notifs.send.php +# profile modification notifications +0 23 * * * web cd $WD; ./profile_modification.php + # validations 0 */3 * * * web cd $WD; ./cron_validations.php diff --git a/include/validations.inc.php b/include/validations.inc.php index c3af12e..e04596a 100644 --- a/include/validations.inc.php +++ b/include/validations.inc.php @@ -437,6 +437,7 @@ abstract class ProfileValidate extends Validate public $profile; public $profileOwner; public $userIsProfileOwner; + public $ownerIsRegistered; // }}} // {{{ constructor @@ -453,11 +454,9 @@ abstract class ProfileValidate extends Validate parent::__construct($_user, $_unique, $_type); $this->profile = &$_profile; $this->profileOwner = $this->profile->owner(); - if (!is_null($this->profileOwner) && $this->profileOwner->id() == $this->user->id()) { - $this->userIsProfileOwner = true; - } else { - $this->userIsProfileOwner = false; - } + $this->userIsProfileOwner = (!is_null($this->profileOwner) + && $this->profileOwner->id() == $this->user->id()); + $this->ownerIsRegistered = $this->profile->isActive(); } // }}} @@ -525,24 +524,23 @@ abstract class ProfileValidate extends Validate protected function sendmail($isok) { - global $globals; - $mailer = new PlMailer(); - $mailer->setSubject($this->_mail_subj()); - $mailer->setFrom("validation+{$this->type}@{$globals->mail->domain}"); - $mailer->addTo("\"{$this->profile->fullName()}\" <{$this->profileOwner->bestEmail()}>"); - if (!$this->userIsProfileOwner) { - $mailer->addCc("\"{$this->user->fullName()}\" <{$this->user->bestEmail()}>"); - } - $mailer->addCc("validation+{$this->type}@{$globals->mail->domain}"); - - $body = ($this->profile->isFemale() ? "Chère camarade,\n\n" : "Cher camarade,\n\n") - . $this->_mail_body($isok) - . (Env::has('comm') ? "\n\n" . Env::v('comm') : '') - . "\n\nCordialement,\n-- \nL'équipe de Polytechnique.org\n" - . $this->_mail_ps($isok); + // Only sends email if the profile's owner exists and is registered. + if ($this->ownerIsRegistered) { + global $globals; - $mailer->setTxtBody(wordwrap($body)); - $mailer->send(); + $mailer = new PlMailer(); + $mailer->setSubject($this->_mail_subj()); + $mailer->setFrom("validation+{$this->type}@{$globals->mail->domain}"); + $mailer->addTo("\"{$this->profile->fullName()}\" <{$this->profileOwner->bestEmail()}>"); + $mailer->addCc("validation+{$this->type}@{$globals->mail->domain}"); + $body = ($this->profile->isFemale() ? "Chère camarade,\n\n" : "Cher camarade,\n\n") + . $this->_mail_body($isok) + . (Env::has('comm') ? "\n\n" . Env::v('comm') : '') + . "\n\nCordialement,\n-- \nL'équipe de Polytechnique.org\n" + . $this->_mail_ps($isok); + $mailer->setTxtBody(wordwrap($body)); + $mailer->send(); + } } // }}} diff --git a/modules/profile/addresses.inc.php b/modules/profile/addresses.inc.php index 4e6d51b..37deea8 100644 --- a/modules/profile/addresses.inc.php +++ b/modules/profile/addresses.inc.php @@ -127,6 +127,19 @@ class ProfileSettingAddress extends ProfileSettingGeocoding $profiletel->saveTels($page->pid(), 'tel', $address['tel']); } } + + public function getText($value) { + $addresses = array(); + foreach ($value as $addrid => $address) { + $phones = new ProfileSettingPhones('address', $addrid); + $addresses[] = 'Adresse : ' . $address['text'] . ', affichage : ' . $address['pub'] + . ', commentaire : ' . $address['comment'] . ', actuelle : ' . ($address['current'] ? 'oui' : 'non') + . ', temporaire : ' . ($address['temporary'] ? 'oui' : 'non') . ', secondaire : ' + . ($address['secondary'] ? 'oui' : 'non') . ', conctactable par courier : ' + . ($address['mail'] ? 'oui' : 'non') . ', ' . $phones->getText($address['tel']); + } + return implode(' ; ' , $addresses); + } } class ProfileSettingAddresses extends ProfilePage diff --git a/modules/profile/decos.inc.php b/modules/profile/decos.inc.php index 6dfc8e6..a6cfabc 100644 --- a/modules/profile/decos.inc.php +++ b/modules/profile/decos.inc.php @@ -82,6 +82,15 @@ class ProfileSettingDeco implements ProfileSetting } } } + + public function getText($value) { + $medalsList = DirEnum::getOptions(DirEnum::MEDALS); + $medals = array(); + foreach ($value as $id => $medal) { + $medals[] = $medalsList[$id]; + } + return implode(', ', $medals); + } } class ProfileSettingDecos extends ProfilePage diff --git a/modules/profile/general.inc.php b/modules/profile/general.inc.php index 09c7bae..362c5eb 100644 --- a/modules/profile/general.inc.php +++ b/modules/profile/general.inc.php @@ -234,6 +234,16 @@ class ProfileSettingSearchNames implements ProfileSetting set_profile_display($display_names, $page->pid()); } } + + public function getText($value) { + $names = array(); + foreach ($value as $name) { + if ($name['name'] != '') { + $names[] = $name['type_name'] . ' : ' . $name['name']; + } + } + return implode(', ' , $names); + } } class ProfileSettingEdu implements ProfileSetting @@ -296,6 +306,21 @@ class ProfileSettingEdu implements ProfileSetting } } } + + public function getText($value) { + $schoolsList = DirEnum::getOptions(DirEnum::EDUSCHOOLS); + $degreesList = DirEnum::getOptions(DirEnum::EDUDEGREES); + $fieldsList = DirEnum::getOptions(DirEnum::EDUFIELDS); + $educations = array(); + foreach ($value as $education) { + $educations[] = 'Université : ' . $schoolsList[$education['eduid']] + . ', diplôme : ' . $degreesList[$education['degreeid']] + . ', domaine : ' . $fieldsList[$education['fieldid']] + . ', année d\'obtention : ' . $education['grad_year'] + . ', intitulé : ' . $education['program']; + } + return implode(', ', $educations); + } } class ProfileSettingEmailDirectory implements ProfileSetting @@ -321,6 +346,10 @@ class ProfileSettingEmailDirectory implements ProfileSetting } return $value; } + + public function getText($value) { + return $value; + } } class ProfileSettingNetworking implements ProfileSetting @@ -394,6 +423,15 @@ class ProfileSettingNetworking implements ProfileSetting $page->pid(), $id, $network['type'], $network['address'], $network['pub']); } } + + public function getText($value) { + $networkings = array(); + foreach ($value as $network) { + $networkings[] = 'nom : ' . $network['name'] . ', adresse : ' . $network['address'] + . ', affichage : ' . $network['pub']; + } + return implode(' ; ' , $networkings); + } } class ProfileSettingPromo implements ProfileSetting @@ -467,6 +505,10 @@ class ProfileSettingPromo implements ProfileSetting } return intval($value); } + + public function getText($value) { + return $value; + } } diff --git a/modules/profile/groups.inc.php b/modules/profile/groups.inc.php index 62489ed..0d2df30 100644 --- a/modules/profile/groups.inc.php +++ b/modules/profile/groups.inc.php @@ -41,6 +41,11 @@ class ProfileSettingSection implements ProfileSetting WHERE pid = {?}", $value, $page->pid()); } + + public function getText($value) { + $sectionsList = DirEnum::getOptions(DirEnum::SECTIONS); + return $sectionsList[$value]; + } } class ProfileSettingBinets implements ProfileSetting @@ -85,6 +90,10 @@ class ProfileSettingBinets implements ProfileSetting XDB::execute("INSERT INTO profile_binets (pid, binet_id) VALUES " . implode(',', $insert)); } + + public function getText($value) { + return implode(', ', $value); + } } class ProfileSettingGroups extends ProfilePage diff --git a/modules/profile/jobs.inc.php b/modules/profile/jobs.inc.php index 1fa65fc..924ebac 100644 --- a/modules/profile/jobs.inc.php +++ b/modules/profile/jobs.inc.php @@ -264,6 +264,19 @@ class ProfileSettingJob extends ProfileSettingGeocoding } } } + + public function getText($value) { + $jobs = array(); + foreach ($value as $id => $job) { + $address = new ProfileSettingAddress(); + $phones = new ProfileSettingPhones('pro', $id); + $jobs[] = 'Entreprise : ' . $job['name'] . ', secteur : ' . $job['subSubSectorName'] + . ', description : ' . $job['description'] . ', web : ' . $job['w_url'] + . ', email : ' . $job['w_email'] + . ', ' . $phones->getText($job['w_phone']) . ', ' . $address->getText($job['w_address']); + } + return implode(' ; ' , $jobs); + } } class ProfileSettingJobs extends ProfilePage @@ -274,6 +287,7 @@ class ProfileSettingJobs extends ProfilePage { parent::__construct($wiz); $this->settings['cv'] = null; + /* TODO: ProfileSettingCorps, required for notifications. */ $this->settings['corps'] = null; $this->settings['jobs'] = new ProfileSettingJob(); $this->watched = array('cv' => true, 'jobs' => true, 'corps' => true); diff --git a/modules/profile/mentor.inc.php b/modules/profile/mentor.inc.php index 035bb47..dc0492a 100644 --- a/modules/profile/mentor.inc.php +++ b/modules/profile/mentor.inc.php @@ -69,6 +69,16 @@ class ProfileSettingSectors implements ProfileSetting } } } + + public function getText($value) { + $sectors = array(); + foreach ($value as $sector) { + foreach ($sector as $subsector) { + $sectors[] = $subsector; + } + } + return implode(', ', $sectors); + } } class ProfileSettingCountry implements ProfileSetting @@ -107,6 +117,10 @@ class ProfileSettingCountry implements ProfileSetting $page->pid(), $id); } } + + public function getText($value) { + return implode(', ', $value); + } } diff --git a/modules/profile/page.inc.php b/modules/profile/page.inc.php index 9d0521e..8c17a05 100644 --- a/modules/profile/page.inc.php +++ b/modules/profile/page.inc.php @@ -36,11 +36,19 @@ interface ProfileSetting /** Save the new value for the given field. */ public function save(ProfilePage &$page, $field, $new_value); + + /** Get text from the value. + */ + public function getText($value); } abstract class ProfileNoSave implements ProfileSetting { public function save(ProfilePage &$page, $field, $new_value) { } + + public function getText($value) { + return $value; + } } class ProfileSettingWeb extends ProfileNoSave @@ -200,6 +208,17 @@ class ProfileSettingPhones implements ProfileSetting $this->saveTel($pid, $telid, $phone); } } + + public function getText($value) { + $phones = array(); + foreach ($value as $phone) { + if ($phone['tel'] != '') { + $phones[] = 'type : ' . $phone['type'] .', numéro : ' . $phone['tel'] + . ', commentaire : « ' . $phone['comment'] . ' », affichage : ' . $phone['pub']; + } + } + return implode(' ; ' , $phones); + } } class ProfileSettingPub extends ProfileNoSave @@ -217,6 +236,10 @@ class ProfileSettingPub extends ProfileNoSave } return $value; } + + public function getText($value) { + return $value; + } } class ProfileSettingBool extends ProfileNoSave @@ -281,6 +304,10 @@ abstract class ProfileSettingGeocoding implements ProfileSetting $address = $gmapsGeocoder->stripGeocodingFromAddress($address); } } + + public function getText($value) { + return $value; + } } @@ -336,12 +363,26 @@ abstract class ProfilePage implements PlWizardPage protected function saveData() { require_once 'notifs.inc.php'; + $changedFields = array(); foreach ($this->settings as $field=>&$setting) { - if (!is_null($setting) && $this->changed[$field]) { - $setting->save($this, $field, $this->values[$field]); - } - if ($this->changed[$field] && @$this->watched[$field]) { - WatchProfileUpdate::register($this->profile, $field); + if ($this->changed[$field]) { + if (!is_null($setting)) { + $changedFields[$field] = array( + str_replace("\n", " - ", $setting->getText($this->orig[$field])), + str_replace("\n", " - ", $setting->getText($this->values[$field])), + ); + } else { + $changedFields[$field] = array( + str_replace("\n", " - ", $this->orig[$field]), + str_replace("\n", " - ", $this->values[$field]), + ); + } + if (!is_null($setting)) { + $setting->save($this, $field, $this->values[$field]); + } + if (isset($this->watched[$field]) && $this->watched[$field]) { + WatchProfileUpdate::register($this->profile, $field); + } } } $this->_saveData(); @@ -352,6 +393,20 @@ abstract class ProfilePage implements PlWizardPage WHERE pid = {?}', $this->pid()); global $platal; S::logger()->log('profil', $platal->pl_self(2)); + + /** If the update was made by a third party and the profile corresponds + * to a registered user, stores both former and new text. + * This will be daily sent to the user. + */ + $owner = $this->profile->owner(); + $user = S::user(); + if ($owner->isActive() && $owner->id() != $user->id()) { + foreach ($changedFields as $field => $values) { + XDB::execute('REPLACE INTO profile_modifications (pid, uid, field, oldText, newText) + VALUES ({?}, {?}, {?}, {?}, {?})', + $this->pid(), $user->id(), $field, $values[0], $values[1]); + } + } } protected function checkChanges() diff --git a/modules/profile/skills.inc.php b/modules/profile/skills.inc.php index 4a8c579..1f18133 100644 --- a/modules/profile/skills.inc.php +++ b/modules/profile/skills.inc.php @@ -76,6 +76,14 @@ class ProfileSettingSkill implements ProfileSetting $page->pid(), $id, $skill['level']); } } + + public function getText($value) { + $skills = array(); + foreach ($value as $skill) { + $skills[] = 'Compétance : ' . $skill['text'] . ', niveau : ' . $skill['level']; + } + return implode(' ; ' , $skills); + } } class ProfileSettingSkills extends ProfilePage diff --git a/templates/profile/notification.mail.tpl b/templates/profile/notification.mail.tpl new file mode 100644 index 0000000..26c3cb4 --- /dev/null +++ b/templates/profile/notification.mail.tpl @@ -0,0 +1,45 @@ +{**************************************************************************} +{* *} +{* Copyright (C) 2003-2010 Polytechnique.org *} +{* http://opensource.polytechnique.org/ *} +{* *} +{* This program is free software; you can redistribute it and/or modify *} +{* it under the terms of the GNU General Public License as published by *} +{* the Free Software Foundation; either version 2 of the License, or *} +{* (at your option) any later version. *} +{* *} +{* This program is distributed in the hope that it will be useful, *} +{* but WITHOUT ANY WARRANTY; without even the implied warranty of *} +{* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *} +{* GNU General Public License for more details. *} +{* *} +{* You should have received a copy of the GNU General Public License *} +{* along with this program; if not, write to the Free Software *} +{* Foundation, Inc., *} +{* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *} +{* *} +{**************************************************************************} + +{config_load file="mails.conf" section="profile"} +{if $mail_part eq 'head'} +{from full=#from#} +{cc full=#cc#} +{subject text="Modification de ton profil"} +{elseif $mail_part eq 'wiki'} +{if $sex}Chère{else}Cher{/if} {$yourself}, + +Les champs suivants de ton profil ont été modifiés le {$date|date_format:"%x"} : +{foreach from=$modifications item=modification} +*{$modification.field} : « {$modification.oldText} » est devenu « {$modification.newText} » (effectuée par {$modification.full_name}). +{/foreach} + +Tu peux voir ta fiche là : +*{$globals->baseurl}/profile/{$hrpid} +Tu peux aussi l'éditer toi-même là : +*{$globals->baseurl}/profile/edit/{$hrpid} + +{include file="signature.mail.tpl"} + +{/if} + +{* vim:set et sw=2 sts=2 sws=2 enc=utf-8: *} diff --git a/upgrade/1.0.1/01_profiles.sql b/upgrade/1.0.1/01_profiles.sql new file mode 100644 index 0000000..c7187ae --- /dev/null +++ b/upgrade/1.0.1/01_profiles.sql @@ -0,0 +1,13 @@ +DROP TABLE IF EXISTS profile_modifications; + +CREATE TABLE profile_modifications ( + pid INT(11) NOT NULL, + uid INT(11) NOT NULL, + field VARCHAR(60) NOT NULL, + oldText TEXT NOT NULL, + newText TEXT NOT NULL, + pub ENUM('private', 'ax', 'public') NOT NULL DEFAULT 'private', + PRIMARY KEY(pid, uid, field) +) ENGINE=InnoDB, CHARSET=utf8; + +-- vim:set syntax=mysql: -- 2.1.4