Index: admin/system_presets/simple/users_u.php
===================================================================
--- admin/system_presets/simple/users_u.php (revision 14949)
+++ admin/system_presets/simple/users_u.php (working copy)
@@ -70,7 +70,7 @@
// fields to hide
$hidden_fields = Array (
/* 'PortalUserId', 'Username', 'Password', 'FirstName','LastName', 'Company', 'Email', 'CreatedOn',
- 'Phone', 'Fax', 'Street', 'Street2', 'City', 'State' , 'Zip', 'Country', 'ResourceId', 'Status',
+ 'Phone', 'Fax', 'Street', 'Street2', 'City', 'State' , 'Zip', 'Country', 'ResourceId', 'Status', 'EmailVerified',
'Modified', 'dob', 'tz',*/ 'IPAddress', /*'IsBanned', 'PwResetConfirm', 'PwRequestTime',*/ 'IPRestrictions',
);
@@ -82,7 +82,7 @@
// fields to make required
$required_fields = Array (
/*'PortalUserId',*/ 'Username', /*'Password', 'FirstName', 'LastName', 'Company', */'Email', /*'CreatedOn',
- 'Phone', 'Fax', 'Street', 'Street2', 'City', 'State' , 'Zip', 'Country', 'ResourceId', 'Status',
+ 'Phone', 'Fax', 'Street', 'Street2', 'City', 'State' , 'Zip', 'Country', 'ResourceId', 'Status', 'EmailVerified',
'Modified', 'dob', 'tz', 'IPAddress', 'IsBanned', 'PwResetConfirm', 'PwRequestTime',*/
);
@@ -115,5 +115,5 @@
// 'Admins' => Array ('PortalUserId', 'Username', 'FirstName', 'LastName', 'Email'),
// users list; section: Users Management -> Users
- 'RegularUsers' => Array (/*'PortalUserId', 'Username', 'FirstName', 'LastName', 'Email',*/ 'PrimaryGroup', 'CreatedOn', 'Modified', /* 'Status',*/ 'IPAddress'),
+ 'RegularUsers' => Array (/*'PortalUserId', 'Username', 'FirstName', 'LastName', 'Email',*/ 'PrimaryGroup', 'CreatedOn', 'Modified', /* 'Status',*/ 'IPAddress', 'EmailVerified'),
);
Index: core/admin_templates/users/users_edit.tpl
===================================================================
--- core/admin_templates/users/users_edit.tpl (revision 14968)
+++ core/admin_templates/users/users_edit.tpl (working copy)
@@ -83,8 +83,9 @@
-
+
+
Index: core/install/english.lang
===================================================================
--- core/install/english.lang (revision 14969)
+++ core/install/english.lang (working copy)
@@ -383,6 +383,7 @@
RW1haWxzIGluIFF1ZXVl
RW1haWxzIFNlbnQ=
RW1haWxzIFRvdGFs
+ RW1haWwgVmVyaWZpZWQ=
RW5hYmxl
RW5hYmxlZA==
RW5hYmxlIENhY2hpbmcgZm9yIHRoaXMgU2VjdGlvbg==
@@ -1586,15 +1587,17 @@
U3ViamVjdDogVGhhbmsgWW91IGZvciBDb250YWN0aW5nIFVzIQoKPHA+VGhhbmsgeW91IGZvciBjb250YWN0aW5nIHVzLiBXZSdsbCBiZSBpbiB0b3VjaCB3aXRoIHlvdSBzaG9ydGx5ITwvcD4=
U3ViamVjdDogTmV3IGZvcm0gc3VibWlzc2lvbgoKPHA+Rm9ybSBoYXMgYmVlbiBzdWJtaXR0ZWQuIFBsZWFzZSBwcm9jZWVkIHRvIHRoZSBBZG1pbiBDb25zb2xlIHRvIHJldmlldyB0aGUgc3VibWlzc2lvbiE8L3A+
U3ViamVjdDogUm9vdCBSZXNldCBQYXNzd29yZAoKWW91ciBuZXcgcGFzc3dvcmQgaXM6IDxpbnAyOm1fUGFyYW0gbmFtZT0icGFzc3dvcmQiLz4=
- U3ViamVjdDogSW4tcG9ydGFsIHJlZ2lzdHJhdGlvbgoKRGVhciA8aW5wMjp1X0ZpZWxkIG5hbWU9IkZpcnN0TmFtZSIgLz4gPGlucDI6dV9GaWVsZCBuYW1lPSJMYXN0TmFtZSIgLz4sDQoNClRoYW5rIHlvdSBmb3IgcmVnaXN0ZXJpbmcgb24gPGlucDI6bV9CYXNlVXJsLz4uIFlvdXIgcmVnaXN0cmF0aW9uIGlzIG5vdyBhY3RpdmUu
- U3ViamVjdDogTmV3IFVzZXIgUmVnaXN0cmF0aW9uICg8aW5wMjp1X0ZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+KQoKQSBuZXcgdXNlciAiPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiIgaGFzIGJlZW4gYWRkZWQu
+ U3ViamVjdDogSW4tcG9ydGFsIHJlZ2lzdHJhdGlvbgoKRGVhciA8aW5wMjp1LnJlZ2lzdGVyX0ZpZWxkIG5hbWU9IkZpcnN0TmFtZSIgLz4gPGlucDI6dS5yZWdpc3Rlcl9GaWVsZCBuYW1lPSJMYXN0TmFtZSIgLz4sDQoNClRoYW5rIHlvdSBmb3IgcmVnaXN0ZXJpbmcgb24gPGlucDI6bV9CYXNlVXJsLz4uIFlvdXIgcmVnaXN0cmF0aW9uIGlzIG5vdyBhY3RpdmUuDQo8aW5wMjptX2lmIGNoZWNrPSJ1LnJlZ2lzdGVyX0ZpZWxkIiBuYW1lPSJFbWFpbCI+DQo8YnIvPjxici8+DQpQbGVhc2UgY2xpY2sgaGVyZSB0byB2ZXJpZnkgeW91ciBFLW1haWwgYWRkcmVzczoNCjxhIGhyZWY9IjxpbnAyOnUucmVnaXN0ZXJfQ29uZmlybVBhc3N3b3JkTGluayB0PSJwbGF0Zm9ybS9teV9hY2NvdW50L3ZlcmlmeV9lbWFpbCIgbm9fYW1wPSIxIi8+Ij48aW5wMjp1LnJlZ2lzdGVyX0NvbmZpcm1QYXNzd29yZExpbmsgdD0icGxhdGZvcm0vbXlfYWNjb3VudC92ZXJpZnlfZW1haWwiIG5vX2FtcD0iMSIvPjwvYT48YnIvPjxici8+DQo8L2lucDI6bV9pZj4=
+ U3ViamVjdDogTmV3IFVzZXIgUmVnaXN0cmF0aW9uICg8aW5wMjp1LnJlZ2lzdGVyX0ZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+KQoKQSBuZXcgdXNlciAiPGlucDI6dS5yZWdpc3Rlcl9GaWVsZCBuYW1lPSdVc2VybmFtZScvPiIgaGFzIGJlZW4gYWRkZWQu
U3ViamVjdDogTmV3IHVzZXIgaGFzIGJlZW4gY3JlYXRlZAoKRGVhciA8aW5wMjp1X0ZpZWxkIG5hbWU9IkZpcnN0TmFtZSIvPiwNCg0KQSBuZXcgdXNlciBoYXMgYmVlbiBjcmVhdGVkIGFuZCBhc3NpZ25lZCB0byB5b3UNCg0KTm93IHlvdSBjYW4gbG9naW4gdXNpbmcgdGhlIGZvbGxvd2luZyBjcmVkZW50aWFsczoNCg0KPGlucDI6bV9pZiBjaGVjaz0idV9GaWVsZCIgbmFtZT0iVXNlcm5hbWUiPlVzZXJuYW1lOiA8aW5wMjp1X0ZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+PGlucDI6bV9lbHNlLz5FLW1haWw6IDxpbnAyOnVfRmllbGQgbmFtZT0iRW1haWwiLz48L2lucDI6bV9pZj4gDQpQYXNzd29yZDogPGlucDI6dV9GaWVsZCBuYW1lPSJQYXNzd29yZF9wbGFpbiIvPiANCg==
- U3ViamVjdDogTmV3IFVzZXIgUmVnaXN0cmF0aW9uICg8aW5wMjp1X0ZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+PGlucDI6bV9pZiBjaGVjaz0ibV9HZXRDb25maWciIG5hbWU9IlVzZXJfQWxsb3dfTmV3IiBlcXVhbHNfdG89IjQiPiAtIEFjdGl2YXRpb24gRW1haWw8L2lucDI6bV9pZj4pCgpEZWFyIDxpbnAyOnVfRmllbGQgbmFtZT0iRmlyc3ROYW1lIiAvPiA8aW5wMjp1X0ZpZWxkIG5hbWU9Ikxhc3ROYW1lIiAvPiw8YnIgLz4NCjxiciAvPg0KPGlucDI6bV9pZiBjaGVjaz0ibV9HZXRDb25maWciIG5hbWU9IlVzZXJfQWxsb3dfTmV3IiBlcXVhbHNfdG89IjQiPiBUaGFuayB5b3UgZm9yIHJlZ2lzdGVyaW5nIG9uIDxpbnAyOm1fTGluayB0ZW1wbGF0ZT0iaW5kZXgiLz4gd2Vic2l0ZS4gVG8gYWN0aXZhdGUgeW91ciByZWdpc3RyYXRpb24gcGxlYXNlIGZvbGxvdyBsaW5rIGJlbG93LiA8aW5wMjp1X0FjdGl2YXRpb25MaW5rIHRlbXBsYXRlPSJwbGF0Zm9ybS9sb2dpbi9hY3RpdmF0ZV9jb25maXJtIi8+IDxpbnAyOm1fZWxzZS8+IFRoYW5rIHlvdSBmb3IgcmVnaXN0ZXJpbmcgb24gPGlucDI6bV9MaW5rIHRlbXBsYXRlPSJpbmRleCIvPiB3ZWJzaXRlLiBZb3VyIHJlZ2lzdHJhdGlvbiB3aWxsIGJlIGFjdGl2ZSBhZnRlciBhcHByb3ZhbC4gPC9pbnAyOm1faWY+
- U3ViamVjdDogTmV3IFVzZXIgUmVnaXN0ZXJlZAoKQSBuZXcgdXNlciAiPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiIgaGFzIHJlZ2lzdGVyZWQgYW5kIGlzIHBlbmRpbmcgYWRtaW5pc3RyYXRpdmUgYXBwcm92YWwu
+ U3ViamVjdDogTmV3IFVzZXIgUmVnaXN0cmF0aW9uICg8aW5wMjp1LnJlZ2lzdGVyX0ZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+PGlucDI6bV9pZiBjaGVjaz0ibV9HZXRDb25maWciIG5hbWU9IlVzZXJfQWxsb3dfTmV3IiBlcXVhbHNfdG89IjQiPiAtIEFjdGl2YXRpb24gRW1haWw8L2lucDI6bV9pZj4pCgpEZWFyIDxpbnAyOnUucmVnaXN0ZXJfRmllbGQgbmFtZT0iRmlyc3ROYW1lIiAvPiA8aW5wMjp1LnJlZ2lzdGVyX0ZpZWxkIG5hbWU9Ikxhc3ROYW1lIiAvPiw8YnIgLz4NCjxiciAvPg0KPGlucDI6bV9pZiBjaGVjaz0ibV9HZXRDb25maWciIG5hbWU9IlVzZXJfQWxsb3dfTmV3IiBlcXVhbHNfdG89IjQiPg0KCVRoYW5rIHlvdSBmb3IgcmVnaXN0ZXJpbmcgb24gPGlucDI6bV9MaW5rIHRlbXBsYXRlPSJpbmRleCIvPiB3ZWJzaXRlLiBUbyBhY3RpdmF0ZSB5b3VyIHJlZ2lzdHJhdGlvbiBwbGVhc2UgZm9sbG93IGxpbmsgYmVsb3cuIDxpbnAyOnUucmVnaXN0ZXJfQWN0aXZhdGlvbkxpbmsgdGVtcGxhdGU9InBsYXRmb3JtL2xvZ2luL2FjdGl2YXRlX2NvbmZpcm0iLz4NCjxpbnAyOm1fZWxzZS8+DQoJVGhhbmsgeW91IGZvciByZWdpc3RlcmluZyBvbiA8aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+IHdlYnNpdGUuIFlvdXIgcmVnaXN0cmF0aW9uIHdpbGwgYmUgYWN0aXZlIGFmdGVyIGFwcHJvdmFsLiANCgkNCgk8aW5wMjptX2lmIGNoZWNrPSJ1LnJlZ2lzdGVyX0ZpZWxkIiBuYW1lPSJFbWFpbCI+DQoJCTxici8+PGJyLz4NCgkJUGxlYXNlIGNsaWNrIGhlcmUgdG8gdmVyaWZ5IHlvdXIgRS1tYWlsIGFkZHJlc3M6DQoJCTxhIGhyZWY9IjxpbnAyOnUucmVnaXN0ZXJfQ29uZmlybVBhc3N3b3JkTGluayB0PSJwbGF0Zm9ybS9teV9hY2NvdW50L3ZlcmlmeV9lbWFpbCIgbm9fYW1wPSIxIi8+Ij48aW5wMjp1LnJlZ2lzdGVyX0NvbmZpcm1QYXNzd29yZExpbmsgdD0icGxhdGZvcm0vbXlfYWNjb3VudC92ZXJpZnlfZW1haWwiIG5vX2FtcD0iMSIvPjwvYT48YnIvPjxici8+DQoJPC9pbnAyOm1faWY+DQo8L2lucDI6bV9pZj4=
+ U3ViamVjdDogTmV3IFVzZXIgUmVnaXN0ZXJlZAoKQSBuZXcgdXNlciAiPGlucDI6dS5yZWdpc3Rlcl9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiIgaGFzIHJlZ2lzdGVyZWQgYW5kIGlzIHBlbmRpbmcgYWRtaW5pc3RyYXRpdmUgYXBwcm92YWwu
U3ViamVjdDogWW91ciBBY2NvdW50IGlzIEFjdGl2ZQoKV2VsY29tZSB0byA8aW5wMjptX0Jhc2VVcmwvPiENCg0KWW91ciB1c2VyIHJlZ2lzdHJhdGlvbiBoYXMgYmVlbiBhcHByb3ZlZC4gWW91ciB1c2VyIG5hbWUgaXM6ICI8aW5wMjp1X0ZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+Ii4=
U3ViamVjdDogTmV3IFVzZXIgQWNjb3VudCAiPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiIgd2FzIEFwcHJvdmVkCgpVc2VyICI8aW5wMjp1X0ZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+IiBoYXMgYmVlbiBhcHByb3ZlZC4=
U3ViamVjdDogWW91ciBSZWdpc3RyYXRpb24gaGFzIGJlZW4gRGVuaWVkCgpZb3VyIHJlZ2lzdHJhdGlvbiBvbiA8YSBocmVmPSI8aW5wMjptX0Jhc2VVcmwvPiI+PGlucDI6bV9CYXNlVXJsLz48L2E+IHdlYnNpdGUgaGFzIGJlZW4gZGVuaWVkLg==
U3ViamVjdDogVXNlciBSZWdpc3RyYXRpb24gZm9yICAiPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiIgaGFzIGJlZW4gRGVuaWVkCgpVc2VyICI8aW5wMjp1X0ZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+IiBoYXMgYmVlbiBkZW5pZWQu
+ U3ViamVjdDogQ2hhbmdlZCBFLW1haWwgUm9sbGJhY2sKCkhlbGxvLDxici8+PGJyLz4NCg0KSXQgc2VlbXMgdGhhdCB5b3UgaGF2ZSBjaGFuZ2VkIGUtbWFpbCBpbiB5b3VyIEluLXBvcnRhbCBhY2NvdW50LiBZb3UgbWF5IHVuZG8gdGhpcyBjaGFuZ2UgYnkgY2xpY2tpbmcgb24gdGhlIGxpbmsgYmVsb3c6PGJyLz48YnIvPg0KDQo8YSBocmVmPSI8aW5wMjp1X1VuZG9FbWFpbENoYW5nZUxpbmsgdGVtcGxhdGU9InBsYXRmb3JtL215X2FjY291bnQvcmVzdG9yZV9lbWFpbCIvPiI+PGlucDI6dV9VbmRvRW1haWxDaGFuZ2VMaW5rIHRlbXBsYXRlPSJwbGF0Zm9ybS9teV9hY2NvdW50L3Jlc3RvcmVfZW1haWwiLz48L2E+PGJyLz48YnIvPg0KDQpJZiB5b3UgYmVsaWV2ZSB5b3UgaGF2ZSByZWNlaXZlZCB0aGlzIGVtYWlsIGluIGVycm9yLCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwuIFlvdXIgYWNjb3VudCB3aWxsIGJlIGxpbmtlZCB0byBhbm90aGVyIGUtbWFpbCB1bmxlc3MgeW91IGhhdmUgY2xpY2tlZCBvbiB0aGUgYWJvdmUgbGluay4=
+ U3ViamVjdDogQ2hhbmdlZCBFLW1haWwgVmVyaWZpY2F0aW9uCgpIZWxsbyw8YnIvPjxici8+DQoNCkl0IHNlZW1zIHRoYXQgeW91IGhhdmUgY2hhbmdlZCBlLW1haWwgaW4geW91ciBJbi1wb3J0YWwgYWNjb3VudC4gUGxlYXNlIHZlcmlmeSB0aGlzIG5ldyBlLW1haWwgYnkgY2xpY2tpbmcgb24gdGhlIGxpbmsgYmVsb3c6PGJyLz48YnIvPg0KDQo8YSBocmVmPSI8aW5wMjp1X0NvbmZpcm1QYXNzd29yZExpbmsgdD0icGxhdGZvcm0vbXlfYWNjb3VudC92ZXJpZnlfZW1haWwiIG5vX2FtcD0iMSIvPiI+PGlucDI6dV9Db25maXJtUGFzc3dvcmRMaW5rIHQ9InBsYXRmb3JtL215X2FjY291bnQvdmVyaWZ5X2VtYWlsIiBub19hbXA9IjEiLz48L2E+PGJyLz48YnIvPg0KDQpJZiB5b3UgYmVsaWV2ZSB5b3UgaGF2ZSByZWNlaXZlZCB0aGlzIGVtYWlsIGluIGVycm9yLCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwuIFlvdXIgZW1haWwgd2lsbCBub3QgZ2V0IHZlcmlmaWVkIHN0YXR1cyB1bmxlc3MgeW91IGhhdmUgY2xpY2tlZCBvbiB0aGUgYWJvdmUgbGluay4NCg==
U3ViamVjdDogTWVtYmVyc2hpcCBFeHBpcmF0aW9uIE5vdGljZQoKWW91ciBtZW1iZXJzaGlwIG9uIDxpbnAyOm1fQmFzZVVybC8+IHdlYnNpdGUgd2lsbCBzb29uIGV4cGlyZS4=
U3ViamVjdDogTWVtYmVyc2hpcCBFeHBpcmF0aW9uIE5vdGljZSBmb3IgIjxpbnAyOnVfRmllbGQgbmFtZT0iVXNlcm5hbWUiLz4iIFNlbnQKClVzZXIgPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiBtZW1iZXJzaGlwIHdpbGwgZXhwaXJlIHNvb24u
U3ViamVjdDogWW91ciBNZW1iZXJzaGlwIEV4cGlyZWQKCllvdXIgbWVtYmVyc2hpcCBvbiA8aW5wMjptX0Jhc2VVcmwvPiB3ZWJzaXRlIGhhcyBleHBpcmVkLg==
Index: core/install/install_data.sql
===================================================================
--- core/install/install_data.sql (revision 14964)
+++ core/install/install_data.sql (working copy)
@@ -175,6 +175,8 @@
INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.NEW.PASSWORD', NULL, 1, 0, 'Core', 'Sends new password to an existing user', 0, 1, 0);
INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.ADD.BYADMIN', NULL, 1, 0, 'Core', 'Sends password to a new user', 0, 1, 0);
INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'ROOT.RESET.PASSWORD', NULL, 1, 0, 'Core', 'Root Reset Password', 1, 1, 0);
+INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.EMAIL.CHANGE.VERIFY', NULL, 1, 0, 'Core', 'Changed E-mail Verification', 0, 1, 1);
+INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.EMAIL.CHANGE.UNDO', NULL, 1, 0, 'Core', 'Changed E-mail Rollback', 0, 1, 1);
INSERT INTO IdGenerator VALUES ('100');
Index: core/install/install_schema.sql
===================================================================
--- core/install/install_schema.sql (revision 14968)
+++ core/install/install_schema.sql (working copy)
@@ -250,6 +250,7 @@
LastName varchar(255) NOT NULL DEFAULT '',
Company varchar(255) NOT NULL DEFAULT '',
Email varchar(255) NOT NULL DEFAULT '',
+ PrevEmails text,
CreatedOn int(11) DEFAULT NULL,
Phone varchar(255) NOT NULL DEFAULT '',
Fax varchar(255) NOT NULL DEFAULT '',
@@ -261,6 +262,7 @@
Country varchar(20) NOT NULL DEFAULT '',
ResourceId int(11) NOT NULL DEFAULT '0',
`Status` tinyint(4) NOT NULL DEFAULT '1',
+ EmailVerified tinyint(4) NOT NULL,
Modified int(11) DEFAULT NULL,
dob int(11) DEFAULT NULL,
tz int(11) DEFAULT NULL,
Index: core/install/upgrades.sql
===================================================================
--- core/install/upgrades.sql (revision 14968)
+++ core/install/upgrades.sql (working copy)
@@ -2509,4 +2509,13 @@
UPDATE PortalUser
SET FrontLanguage = 1
-WHERE UserType = 0;
\ No newline at end of file
+WHERE UserType = 0;
+
+ALTER TABLE PortalUser
+ ADD PrevEmails TEXT NULL AFTER Email,
+ ADD EmailVerified TINYINT NOT NULL AFTER `Status`;
+
+UPDATE PortalUser SET EmailVerified = 1;
+
+INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.EMAIL.CHANGE.VERIFY', NULL, 1, 0, 'Core', 'Changed E-mail Verification', 0, 1, 1);
+INSERT INTO Events (EventId, Event, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.EMAIL.CHANGE.UNDO', NULL, 1, 0, 'Core', 'Changed E-mail Rollback', 0, 1, 1);
Index: core/units/helpers/user_helper.php
===================================================================
--- core/units/helpers/user_helper.php (revision 14968)
+++ core/units/helpers/user_helper.php (working copy)
@@ -522,6 +522,7 @@
$expiration_timeouts = Array (
'forgot_password' => 'config:Users_AllowReset',
'activation' => 'config:UserEmailActivationTimeout',
+ 'verify_email' => 'config:Users_AllowReset',
'custom' => '',
);
@@ -550,4 +551,47 @@
return $user_info['PortalUserId'];
}
+
+ /**
+ * Restores user's email, returns error label, if error occurred
+ *
+ * @param string $hash
+ * @return string
+ * @access public
+ */
+ public function restoreEmail($hash)
+ {
+ if ( !preg_match('/^[a-f0-9]{32}$/', $hash) ) {
+ return 'invalid_hash';
+ }
+
+ $sql = 'SELECT PortalUserId, PrevEmails
+ FROM ' . TABLE_PREFIX . 'PortalUser
+ WHERE PrevEmails LIKE ' . $this->Conn->qstr('%' . $hash . '%');
+ $user_info = $this->Conn->GetRow($sql);
+
+ if ( $user_info === false ) {
+ return 'invalid_hash';
+ }
+
+ $prev_emails = $user_info['PrevEmails'];
+ $prev_emails = $prev_emails ? unserialize($prev_emails) : Array ();
+
+ if ( !isset($prev_emails[$hash]) ) {
+ return 'invalid_hash';
+ }
+
+ $email_to_restore = $prev_emails[$hash];
+ unset($prev_emails[$hash]);
+
+ $object =& $this->Application->recallObject('u.email-restore', null, Array ('skip_autoload' => true));
+ /* @var $object UsersItem */
+
+ $object->Load($user_info['PortalUserId']);
+ $object->SetDBField('PrevEmails', serialize($prev_emails));
+ $object->SetDBField('Email', $email_to_restore);
+ $object->SetDBField('EmailVerified', 1);
+
+ return $object->Update() ? '' : 'restore_impossible';
+ }
}
Index: core/units/users/users_config.php
===================================================================
--- core/units/users/users_config.php (revision 14968)
+++ core/units/users/users_config.php (working copy)
@@ -419,6 +419,11 @@
'default' => 0
),
'IPRestrictions' => Array ('default' => NULL),
+ 'EmailVerified' => Array (
+ 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
+ 'default' => 0
+ ),
+ 'PrevEmails' => Array ('default' => NULL),
),
'VirtualFields' => Array (
@@ -551,6 +556,8 @@
'PrimaryGroupId' => Array ('type' => 'int'),
'OldStyleLogin' => Array ('type' => 'int', 'not_null' => 1),
'IPRestrictions' => Array ('type' => 'string'),
+ 'EmailVerified' => Array ('type' => 'int', 'not_null' => 1),
+ 'PrevEmails' => Array ('type' => 'string'),
),
'VirtualFields' => Array (
@@ -644,6 +651,7 @@
'CreatedOn' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 100),
'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 100),
'IPAddress' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 100, 'hidden' => 1),
+ 'EmailVerified' => Array ('filter_block' => 'grid_options_filter', 'width' => 100, 'hidden' => 1),
),
),
),
Index: core/units/users/users_event_handler.php
===================================================================
--- core/units/users/users_event_handler.php (revision 14971)
+++ core/units/users/users_event_handler.php (working copy)
@@ -910,18 +910,22 @@
$object =& $event->getObject();
/* @var $object kDBItem */
- if ($event->Special == 'forgot') {
+ if ( $event->Special == 'forgot' ) {
$object->SetDBField('PwResetConfirm', '');
$object->SetDBField('PwRequestTime_date', NULL);
$object->SetDBField('PwRequestTime_time', NULL);
}
- $changed_fields = array_keys( $object->GetChangedFields() );
+ $changed_fields = array_keys($object->GetChangedFields());
if ( $changed_fields && !in_array('Modified', $changed_fields) ) {
$object->SetDBField('Modified_date', adodb_mktime());
$object->SetDBField('Modified_time', adodb_mktime());
}
+
+ if ( !$this->Application->isAdmin && in_array('Email', $changed_fields) && ($event->Special != 'email-restore') ) {
+ $object->SetDBField('EmailVerified', 0);
+ }
}
/**
@@ -1286,10 +1290,14 @@
$object =& $event->getObject();
/* @var $object UsersItem */
- if (!$this->Application->isAdmin || $object->IsTempTable()) {
- return ;
+ if ( !$this->Application->isAdmin && ($event->Special != 'email-restore') ) {
+ $this->sendEmailChangeEvent($event);
}
+ if ( !$this->Application->isAdmin || $object->IsTempTable() ) {
+ return;
+ }
+
$this->sendStatusChangeEvent($object->GetID(), $object->GetOriginalField('Status'), $object->GetDBField('Status'));
}
@@ -1433,6 +1441,57 @@
}
/**
+ * Sends restore/validation email event on user email change
+ *
+ * @param kEvent $event
+ * @return void
+ * @access protected
+ */
+ protected function sendEmailChangeEvent(kEvent &$event)
+ {
+ $object =& $event->getObject();
+ /* @var $object UsersItem */
+
+ $new_email = $object->GetDBField('Email');
+ $prev_email = $object->GetOriginalField('Email');
+
+ if ( !$new_email || ($prev_email == $new_email) ) {
+ return;
+ }
+
+ $prev_emails = $object->GetDBField('PrevEmails');
+ $prev_emails = $prev_emails ? unserialize($prev_emails) : Array ();
+
+ $fields_hash = Array (
+ 'PrevEmails' => serialize($prev_emails),
+ 'EmailVerified' => 0,
+ );
+
+ $user_id = $object->GetID();
+
+ if ( $prev_email ) {
+ $hash = md5(TIMENOW + $user_id);
+ $prev_emails[$hash] = $prev_email;
+ $fields_hash['PrevEmails'] = serialize($prev_emails);
+
+ $send_params = Array (
+ 'hash' => $hash,
+ 'to_email' => $prev_email,
+ 'to_name' => trim($object->GetDBField('FirstName') . ' ' . $object->GetDBField('LastName')),
+ );
+
+ $this->Application->EmailEventUser('USER.EMAIL.CHANGE.UNDO', null, $send_params);
+ }
+
+ if ( $new_email ) {
+ $this->Application->EmailEventUser('USER.EMAIL.CHANGE.VERIFY', $user_id);
+ }
+
+ // direct DB update, since USER.EMAIL.CHANGE.VERIFY puts verification code in user record, that we don't want to loose
+ $this->Conn->doUpdate($fields_hash, $object->TableName, 'PortalUserId = ' . $user_id);
+ }
+
+ /**
* OnAfterConfigRead for users
*
* @param kEvent $event
Index: core/units/users/users_tag_processor.php
===================================================================
--- core/units/users/users_tag_processor.php (revision 14949)
+++ core/units/users/users_tag_processor.php (working copy)
@@ -114,6 +114,34 @@
}
/**
+ * Tries to restore user email
+ *
+ * @param Array $params
+ * @return bool
+ * @access protected
+ */
+ protected function RestoreEmail($params)
+ {
+ $user_helper =& $this->Application->recallObject('UserHelper');
+ /* @var $user_helper UserHelper */
+
+ $hash = $this->Application->GetVar('hash');
+ $error_code = $user_helper->restoreEmail($hash);
+
+ if ( $error_code ) {
+ // used for error reporting only -> rewrite code + theme (by Alex)
+ $object =& $this->getObject(Array ('skip_autoload' => true)); // TODO: change theme too
+ /* @var $object UsersItem */
+
+ $object->SetError('PwResetConfirm', 'restore', $params[$error_code]);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
* Returns error message set by given code type
*
* @param string $error_code
@@ -133,6 +161,11 @@
'code_is_not_valid' => 'lu_error_ActivationCodeNotValid',
'code_expired' => 'lu_error_ActivationCodeExpired',
),
+
+ 'verify_email' => Array (
+ 'code_is_not_valid' => 'lu_error_VerificationCodeNotValid',
+ 'code_expired' => 'lu_error_VerificationCodeExpired',
+ ),
);
if ($code_type == 'custom') {
@@ -264,33 +297,84 @@
}
/**
+ * Returns link to revert e-mail change in user record
+ *
+ * @param Array $params
+ * @return string
+ * @access protected
+ */
+ protected function UndoEmailChangeLink($params)
+ {
+ $params['hash'] = $this->Application->Parser->GetParam('hash');
+
+ if ( !$this->SelectParam($params, 'template,t') ) {
+ $params['template'] = $this->Application->GetVar('undo_email_template');
+ }
+
+ return $this->Application->ProcessParsedTag('m', 'Link', $params);
+ }
+
+ /**
* Activates user using given code
*
* @param Array $params
+ * @return string
+ * @access protected
*/
- function ActivateUser($params)
+ protected function ActivateUser($params)
{
- $passed_key = trim($this->Application->GetVar('user_key'));
+ $this->_updateAndLogin(Array ('Status' => STATUS_ACTIVE, 'EmailVerified' => 1));
+ return '';
+ }
+
+ /**
+ * Marks user e-mail as verified using given code
+ *
+ * @param Array $params
+ * @return string
+ * @access protected
+ */
+ protected function MarkUserEmailAsVerified($params)
+ {
+ $this->_updateAndLogin(Array ('EmailVerified' => 1));
+
+ return '';
+ }
+
+ /**
+ * Activates user using given code
+ *
+ * @param Array $fields_hash
+ * @return void
+ * @access protected
+ */
+ protected function _updateAndLogin($fields_hash)
+ {
$user_helper =& $this->Application->recallObject('UserHelper');
- /* @var $user_helper UserHelper */
+ /* @var $user_helper UserHelper */
- $user =& $user_helper->getUserObject();
- $user->Load($passed_key, 'PwResetConfirm');
+ $user =& $this->Application->recallObject($this->Prefix . '.activate', null, Array ('skip_autoload' => true));
+ /* @var $user UsersItem */
+
+ $user->Load(trim($this->Application->GetVar('user_key')), 'PwResetConfirm');
+
if ( !$user->isLoaded() ) {
return ;
}
- $user->SetDBField('Status', STATUS_ACTIVE);
+ $user->SetFieldsFromHash($fields_hash);
$user->SetDBField('PwResetConfirm', '');
$user->SetDBField('PwRequestTime_date', NULL);
$user->SetDBField('PwRequestTime_time', NULL);
$user->Update();
- if ( $user_helper->checkLoginPermission() ) {
- $user_helper->loginUserById( $user->GetID() );
+ $login_user =& $user_helper->getUserObject();
+ $login_user->Load( $user->GetID() );
+
+ if ( ($login_user->GetDBField('Status') == STATUS_ACTIVE) && $user_helper->checkLoginPermission() ) {
+ $user_helper->loginUserById( $login_user->GetID() );
}
}
-
}
\ No newline at end of file