You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

574 lines
16 KiB

  1. package db;
  2. import sys.db.Object;
  3. import sys.db.Types;
  4. import db.UserAmap;
  5. import Common;
  6. enum UserFlags {
  7. HasEmailNotif4h; //send notifications by mail 4h before
  8. HasEmailNotif24h; //send notifications by mail 24h before
  9. HasEmailNotifOuverture; //send notifications by mail on command open
  10. //Tuto; //enable tutorials
  11. }
  12. /**
  13. * Site-wide right
  14. */
  15. enum RightSite {
  16. Admin;
  17. }
  18. @:index(email,unique)
  19. class User extends Object {
  20. public var id : SId;
  21. public var lang : SString<2>;
  22. @:skip public var name(get, set) : String;
  23. public var pass : STinyText;
  24. public var rights : SFlags<RightSite>;
  25. public var firstName:SString<32>;
  26. public var lastName:SString<32>;
  27. public var email : SString<64>;
  28. public var phone:SNull<SString<19>>;
  29. public var firstName2:SNull<SString<32>>;
  30. public var lastName2:SNull<SString<32>>;
  31. public var email2 : SNull<SString<64>>;
  32. public var phone2:SNull<SString<19>>;
  33. public var address1:SNull<SString<64>>;
  34. public var address2:SNull<SString<64>>;
  35. public var zipCode:SNull<SString<32>>;
  36. public var city:SNull<SString<25>>;
  37. @:skip public var amap(get_amap, null) : Amap;
  38. public var cdate : SDate; //creation
  39. public var ldate : SNull<SDateTime>; //derniere connexion
  40. public var flags : SFlags<UserFlags>;
  41. @hideInForms public var tutoState : SNull<SData<{name:String,step:Int}>>; //tutorial state
  42. public var apiKey : SNull<SString<128>>; //private API key
  43. public function new() {
  44. super();
  45. //default values
  46. cdate = Date.now();
  47. rights = sys.db.Types.SFlags.ofInt(0);
  48. flags = sys.db.Types.SFlags.ofInt(0);
  49. flags.set(HasEmailNotif24h);
  50. flags.set(HasEmailNotifOuverture);
  51. lang = "fr";
  52. pass = "";
  53. }
  54. public override function toString() {
  55. return getName()+" ["+id+"]";
  56. }
  57. public function isAdmin() {
  58. return rights.has(Admin) || id==1;
  59. }
  60. public static function login(user:db.User, email:String) {
  61. user.lock();
  62. user.ldate = Date.now();
  63. user.update();
  64. App.current.session.setUser(user);
  65. if (App.current.session.data == null) App.current.session.data = {};
  66. //Who's connected, user1 or user2 ?
  67. App.current.session.data.whichUser = (email == user.email) ? 0 : 1;
  68. }
  69. /**
  70. * is this user the manager of the current group
  71. */
  72. public function isAmapManager() {
  73. var a = getAmap();
  74. if (a == null) return false;
  75. var ua = getUserAmap(a);
  76. if (ua == null) return false;
  77. return ua.hasRight(Right.GroupAdmin);
  78. }
  79. function getUserAmap(amap:db.Amap):db.UserAmap {
  80. return db.UserAmap.get(this, amap);
  81. }
  82. public function isFullyRegistred(){
  83. return pass != null && pass != "";
  84. }
  85. public function makeMemberOf(group:db.Amap){
  86. var ua = db.UserAmap.get(this, group);
  87. if (ua == null) {
  88. ua = new db.UserAmap();
  89. ua.user = this;
  90. ua.amap = group;
  91. ua.insert();
  92. }
  93. return ua;
  94. }
  95. /**
  96. * Est ce que ce membre a la gestion de ce contrat
  97. * si null, est ce qu'il a la gestion d'un des contrat, n'importe lequel (utilse pour afficher 'gestion contrat' dans la nav )
  98. * @param contract
  99. */
  100. public function isContractManager(?contract:db.Contract ) {
  101. if (isAdmin()) return true;
  102. if (contract != null) {
  103. return canManageContract(contract);
  104. }else {
  105. var ua = getUserAmap(getAmap());
  106. if (ua == null) return false;
  107. if (ua.rights == null) return false;
  108. for (r in ua.rights) {
  109. switch(r) {
  110. case Right.ContractAdmin(cid):
  111. return true;
  112. default:
  113. }
  114. }
  115. return false;
  116. }
  117. }
  118. public function canManageAllContracts(){
  119. if (isAdmin()) return true;
  120. var ua = getUserAmap(getAmap());
  121. if (ua == null) return false;
  122. if (ua.rights == null) return false;
  123. for (r in ua.rights) {
  124. switch(r) {
  125. case Right.ContractAdmin(cid):
  126. if(cid==null) return true;
  127. default:
  128. }
  129. }
  130. return false;
  131. }
  132. public function canAccessMessages():Bool {
  133. var ua = getUserAmap(getAmap());
  134. if (ua == null) return false;
  135. if (ua.hasRight(Right.Messages)) return true;
  136. return false;
  137. }
  138. public function canAccessMembership():Bool {
  139. var ua = getUserAmap(getAmap());
  140. if (ua == null) return false;
  141. if (ua.hasRight(Right.Membership)) return true;
  142. return false;
  143. }
  144. public function canManageContract(c:db.Contract):Bool {
  145. var ua = getUserAmap(c.amap);
  146. if (ua == null) return false;
  147. if (ua.hasRight(Right.ContractAdmin())) return true;
  148. if (ua.hasRight(Right.ContractAdmin(c.id))) return true;
  149. return false;
  150. }
  151. public function getContractManager(?lock=false) {
  152. return Contract.manager.search($amap == amap && $contact == this, false);
  153. }
  154. public function getName() {
  155. return get_name();
  156. }
  157. public function get_name() {
  158. return lastName + " " + firstName;
  159. }
  160. public function getCoupleName() {
  161. var n = lastName + " " + firstName;
  162. if (lastName2 != null) {
  163. n = n + " / " + lastName2 + " " + firstName2;
  164. }
  165. return n;
  166. }
  167. public function set_name(name:String) {
  168. var name = name.split(' ');
  169. firstName = name[0];
  170. lastName = name[1];
  171. return firstName+" "+lastName;
  172. }
  173. /**
  174. * Encode a user password
  175. * @param p
  176. */
  177. public function setPass(p:String) {
  178. if (p == null){
  179. this.pass = "";
  180. }else{
  181. this.pass = haxe.crypto.Md5.encode( App.config.get('key') + StringTools.trim(p));
  182. }
  183. return this.pass;
  184. }
  185. /**
  186. * Renvoie les commandes actuelles du user
  187. * @param _amap force une amap
  188. * @param lock=false
  189. */
  190. public function getOrders(?_amap:db.Amap,?lock = false):List<UserContract> {
  191. var a = _amap == null ? getAmap() : _amap;
  192. var c = a.getActiveContracts(true);
  193. var cids = Lambda.map(c,function(m) return m.id);
  194. var pids = Lambda.map(db.Product.manager.search($contractId in cids,false), function(x) return x.id);
  195. var out = UserContract.manager.search(($userId == id || $userId2 == id) && $productId in pids, lock);
  196. return out;
  197. }
  198. /**
  199. * renvoie les commandes à partir d'une liste de contrats
  200. */
  201. public function getOrdersFromContracts(c:Iterable<db.Contract>):List<db.UserContract> {
  202. var cids = Lambda.map(c,function(m) return m.id);
  203. var pids = Lambda.map(db.Product.manager.search($contractId in cids,false), function(x) return x.id);
  204. return UserContract.manager.search(($userId == id || $userId2 == id) && $productId in pids, false);
  205. }
  206. /**
  207. * renvoie les commandes de contrat variables à partir d'une distribution
  208. */
  209. public function getOrdersFromDistrib(d:db.Distribution):List<db.UserContract> {
  210. var pids = Lambda.map(db.Product.manager.search($contractId == d.contract.id, false), function(x) return x.id);
  211. return UserContract.manager.search(($userId == id || $userId2 == id) && $distributionId==d.id && $productId in pids , false);
  212. }
  213. public function get_amap():Amap {
  214. return getAmap();
  215. }
  216. /**
  217. * renvoie l'amap selectionnée par le user en cours
  218. */
  219. public function getAmap() {
  220. if (App.current.user != null && id != App.current.user.id) throw "This function is valid only for the current user";
  221. if (App.current.session == null) return null;
  222. if (App.current.session.data == null ) return null;
  223. var a = App.current.session.data.amapId;
  224. if (a == null) {
  225. return null;
  226. }else {
  227. return Amap.manager.get(a,false);
  228. }
  229. }
  230. /**
  231. * get groups this user belongs to
  232. */
  233. public function getAmaps():List<db.Amap> {
  234. return Lambda.map(UserAmap.manager.search($user == this, false), function(o) return o.amap);
  235. }
  236. public function isMemberOf(amap:Amap) {
  237. return UserAmap.manager.select($user == this && $amapId == amap.id, false) != null;
  238. }
  239. /**
  240. * Renvoie la liste des contrats dans lequel l'adherent a des commandes
  241. * @param lock=false
  242. * @return
  243. */
  244. public function getContracts(?lock=false):Array<Contract> {
  245. var out = [];
  246. var ucs = getOrders(lock);
  247. for (uc in ucs) {
  248. if (!Lambda.has(out, uc.product.contract)) {
  249. out.push(uc.product.contract);
  250. }
  251. }
  252. return out;
  253. }
  254. /**
  255. * Merge fields of 2 users, then delete the second (u2)
  256. * Be carefull : check before calling this function that u2 can be safely deleted !
  257. */
  258. public function merge(u2:db.User) {
  259. this.lock();
  260. u2.lock();
  261. var m = function(a, b) {
  262. return a == null || a=="" ? b : a;
  263. }
  264. this.address1 = m(this.address1, u2.address1);
  265. this.address2 = m(this.address2, u2.address2);
  266. this.zipCode = m(this.zipCode, u2.zipCode);
  267. this.city = m(this.city, u2.city);
  268. //find how to merge the 2 names in each account
  269. if (this.email == u2.email) {
  270. this.firstName = m(this.firstName, u2.firstName);
  271. this.lastName = m(this.lastName, u2.lastName);
  272. this.phone = m(this.phone, u2.phone);
  273. } else if (this.email == u2.email2) {
  274. this.firstName = m(this.firstName, u2.firstName2);
  275. this.lastName = m(this.lastName, u2.lastName2);
  276. this.phone = m(this.phone, u2.phone2);
  277. }
  278. if (this.email2 == u2.email) {
  279. this.firstName2 = m(this.firstName2, u2.firstName);
  280. this.lastName2 = m(this.lastName2, u2.lastName);
  281. this.phone2 = m(this.phone2, u2.phone);
  282. } else if (this.email2 == u2.email2) {
  283. this.firstName2 = m(this.firstName2, u2.firstName2);
  284. this.lastName2 = m(this.lastName2, u2.lastName2);
  285. this.phone2 = m(this.phone2, u2.phone2);
  286. }
  287. u2.delete();
  288. this.update();
  289. }
  290. public static function getOrCreate(firstName:String, lastName:String, email:String):db.User{
  291. var u = db.User.manager.select($email == email || $email2 == email, true);
  292. if (u == null){
  293. u = new db.User();
  294. u.firstName = firstName;
  295. u.lastName = lastName;
  296. u.email = email;
  297. u.insert();
  298. }
  299. return u;
  300. }
  301. /**
  302. * Search for similar users in the DB ( same firstName+lastName or same email )
  303. */
  304. public static function __getSimilar(firstName:String, lastName:String, email:String,?firstName2:String, ?lastName2:String, ?email2:String):List<db.User> {
  305. var out = new Array();
  306. out = Lambda.array(User.manager.search($firstName.like(firstName) && $lastName.like(lastName), false));
  307. out = out.concat(Lambda.array(User.manager.search($email.like(email), false)));
  308. out = out.concat(Lambda.array(User.manager.search($firstName2.like(firstName) && $lastName2.like(lastName), false)));
  309. out = out.concat(Lambda.array(User.manager.search($email2.like(email), false)));
  310. //recherche pour le deuxieme user
  311. if (lastName2 != "" && lastName2 != null && firstName2 != "" && firstName2 != null) {
  312. out = out.concat(Lambda.array(User.manager.search($firstName.like(firstName2) && $lastName.like(lastName2), false)));
  313. out = out.concat(Lambda.array(User.manager.search($firstName2.like(firstName2) && $lastName2.like(lastName2), false)));
  314. }
  315. if (email2 != null && email2 != "") {
  316. out = out.concat(Lambda.array(User.manager.search($email.like(email2), false)));
  317. out = out.concat(Lambda.array(User.manager.search($email2.like(email2), false)));
  318. }
  319. //dedouble
  320. var x = new Map<Int,db.User>();
  321. for ( oo in out) {
  322. x.set(oo.id, oo);
  323. }
  324. return Lambda.list(x);
  325. }
  326. /**
  327. * Search for similar users in the DB ( same email )
  328. */
  329. public static function getSameEmail(email:String, ?email2:String){
  330. var out = new Array();
  331. out = out.concat(Lambda.array(User.manager.search($email.like(email), false)));
  332. out = out.concat(Lambda.array(User.manager.search($email2.like(email), false)));
  333. if (email2 != null && email2 != "") {
  334. out = out.concat(Lambda.array(User.manager.search($email.like(email2), false)));
  335. out = out.concat(Lambda.array(User.manager.search($email2.like(email2), false)));
  336. }
  337. return Lambda.list(out);
  338. }
  339. /**
  340. * Get users with no contracts
  341. **/
  342. public static function getUsers_NoContracts(?index:Int,?limit:Int):List<db.User> {
  343. var productsIds = App.current.user.getAmap().getProducts().map(function(x) return x.id);
  344. var uc = UserContract.manager.search($productId in productsIds, false);
  345. var uc2 = uc.map(function(x) return x.user.id); //liste des userId avec un contrat dans cette amap
  346. // J. Le Clerc - BUGFIX#1 Ne pas oublier les contrats alternés
  347. for (u in uc) {
  348. if (u.user2 != null) {
  349. uc2.add(u.user2.id);
  350. }
  351. }
  352. if (uc2.length > 0){
  353. //les gens qui sont dans cette amap et qui n'ont pas de contrat de cette amap
  354. var ua = db.UserAmap.manager.unsafeObjects("select * from UserAmap where amapId=" + App.current.user.getAmap().id +" and userId NOT IN(" + uc2.join(",") + ")", false);
  355. return Lambda.map(ua, function(x) return x.user);
  356. }else{
  357. return App.current.user.amap.getMembers();
  358. }
  359. }
  360. /**
  361. * User with contracts
  362. */
  363. public static function getUsers_Contracts(?index:Int,?limit:Int):List<db.User> {
  364. var productsIds = App.current.user.getAmap().getProducts().map(function(x) return x.id);
  365. if (productsIds.length == 0) return new List();
  366. return db.User.manager.unsafeObjects("select u.* from User u, UserContract uc where uc.productId IN(" + productsIds.join(",") + ") AND (uc.userId=u.id OR uc.userId2=u.id) group by u.id ORDER BY u.lastName", false);
  367. }
  368. public static function getUsers_NoMembership(?index:Int,?limit:Int):List<db.User> {
  369. var ua = new List();
  370. if (index == null && limit == null) {
  371. ua = db.UserAmap.manager.search($amap == App.current.user.amap, false);
  372. }else {
  373. ua = db.UserAmap.manager.search($amap == App.current.user.amap,{limit:[index,limit]}, false);
  374. }
  375. for (u in Lambda.array(ua)) {
  376. if (u.hasValidMembership()) ua.remove(u);
  377. }
  378. return Lambda.map(ua, function(x) return x.user);
  379. }
  380. public static function getUsers_NewUsers(?index:Int, ?limit:Int):List<db.User> {
  381. var uas = db.UserAmap.manager.search($amap == App.current.user.amap, false);
  382. var ids = Lambda.map(uas, function(x) return x.user.id);
  383. if (index == null && limit == null) {
  384. return db.User.manager.search($pass == "" && ($id in ids), {orderBy:lastName} ,false);
  385. }else {
  386. return db.User.manager.search($pass == "" && ($id in ids), {limit:[index, limit] ,orderBy:lastName} , false);
  387. }
  388. }
  389. public function sendInvitation(group:db.Amap) {
  390. var t = sugoi.i18n.Locale.texts;
  391. if (isFullyRegistred()) throw t._("This user cannot receive an invitation");
  392. /*var group : db.Amap = null;
  393. if (App.current.user == null) {
  394. group = this.getAmaps().first();
  395. }else {
  396. //prend l'amap du user connecté qui a lancé l'invite.
  397. group = App.current.user.amap;
  398. }*/
  399. //store token
  400. var k = sugoi.db.Session.generateId();
  401. sugoi.db.Cache.set("validation" + k, this.id, 60 * 60 * 24 * 30); //expire in 1 month
  402. var e = new sugoi.mail.Mail();
  403. if (group != null){
  404. e.setSubject(t._("Invitation")+" "+group.name);
  405. }else{
  406. e.setSubject(t._("Invitation Cagette.net"));
  407. }
  408. e.addRecipient(this.email,this.getName());
  409. e.setSender(App.config.get("default_email"),t._("Cagette.net"));
  410. var html = App.current.processTemplate("mail/invitation.mtt", {
  411. email:email,
  412. email2:email2,
  413. groupName:(group == null?null:group.name),
  414. name:firstName,
  415. k:k
  416. } );
  417. e.setHtmlBody(html);
  418. App.sendMail(e);
  419. }
  420. /**
  421. * cleaning before saving
  422. */
  423. override public function insert() {
  424. clean();
  425. super.insert();
  426. }
  427. override public function update() {
  428. clean();
  429. super.update();
  430. }
  431. function clean() {
  432. //emails
  433. this.email = this.email.toLowerCase();
  434. if (this.email2 != null) this.email2 = this.email2.toLowerCase();
  435. //lastname
  436. if (this.lastName != null) this.lastName = this.lastName.toUpperCase();
  437. if (this.lastName2 != null) this.lastName2 = this.lastName2.toUpperCase();
  438. if(pass==null) pass="";
  439. }
  440. public function infos():UserInfo{
  441. return {
  442. id:id,
  443. name : getName(),
  444. email : email
  445. };
  446. }
  447. /**
  448. * get form labels
  449. */
  450. public static function getLabels():Map<String,String>{
  451. var t = sugoi.i18n.Locale.texts;
  452. return [
  453. "firstName" => t._("First name"),
  454. "lastName" => t._("Last name"),
  455. "email" => t._("Email"),
  456. "phone" => t._("Phone"),
  457. "firstName2"=> t._("Partner first name"),
  458. "lastName2" => t._("Partner last name"),
  459. "email2" => t._("Partner email"),
  460. "phone2" => t._("Partner phone"),
  461. "lang" => t._("Language"),
  462. "address1" => t._("Address 1"),
  463. "address2" => t._("Address 2"),
  464. "zipCode" => t._("Zip code"),
  465. "city" => t._("City"),
  466. "rights" => t._("Rights"),
  467. "cdate" => t._("Registration date"),
  468. "flags" => t._("Options"),
  469. "pass" => t._("Password"),
  470. ];
  471. }
  472. }