Compare commits

...
61 Commits
Author SHA1 Message Date
pvincent 784438c864 tagged as 1.15.1 2022-12-11 12:18:34 +04:00
pvincent 6494f92b54 meilleur consentement cookies 2022-12-11 12:18:23 +04:00
pvincent a7e75fb40b tagged as 1.15.0 2022-12-10 19:48:39 +04:00
pvincent 1b6fc0e646 tout accepter 2022-12-10 19:48:04 +04:00
pvincent 01612db559 tagged as 1.14.3 2022-12-10 19:36:44 +04:00
pvincent a0405e885d cookie consent OK 2022-12-10 19:36:21 +04:00
pvincent fc5e1cc4d2 tagged as 1.14.2 2022-12-10 12:55:47 +04:00
pvincent 5db8f5f1f3 mentions légales 2022-12-10 12:55:23 +04:00
pvincent 0c45dffc1e tagged as 1.14.1 2022-11-29 10:52:35 +04:00
pvincent 5bc2ad1517 view misses div class row 2022-11-29 10:52:17 +04:00
pvincent a6eed18ac1 tagged as 1.14.0 2022-11-29 10:29:24 +04:00
pvincent 67bbb02a2f footer reuse 2022-11-29 10:29:03 +04:00
pvincent 8fe3153c10 tagged as 1.13.6 2022-11-25 09:55:46 +04:00
pvincent de0f10425b Main redirect if group 1 exists 2022-11-25 09:55:38 +04:00
pvincent 4bd7c3d174 tagged as 1.13.5 2022-11-23 11:05:10 +04:00
pvincent ad1ae04624 logo france relance 2022-11-23 11:04:54 +04:00
pvincent dad6babe97 tagged as 1.13.4 2022-11-22 12:00:13 +04:00
pvincent 240c63d9fd wiki update 2022-11-22 12:00:02 +04:00
pvincent d6675758ff tagged as 1.13.3 2022-11-21 22:54:40 +04:00
pvincent 61ce852dbc package json fixed 2022-11-21 22:54:30 +04:00
pvincent 789a4eb7ec tagged as 1.13.2 2022-11-21 22:32:06 +04:00
pvincent 2b0f3557fc no more www/file in git 2022-11-21 22:31:58 +04:00
pvincent 10688b9892 tagged as 1.13.1 2022-11-21 22:22:36 +04:00
pvincent c86eafa130 ok 2022-11-21 22:22:27 +04:00
pvincent b2b7e88e3c by default /group/1 2022-11-21 22:21:56 +04:00
pvincent c078d7cf16 tagged as 1.12.0 2022-11-16 09:11:06 +04:00
pvincent f2c6c05a00 bandeau_region via config.xml 2022-11-16 09:09:36 +04:00
pvincent c163807c96 tagged as 1.11.9 2022-08-24 10:04:36 +04:00
pvincent 5cbc81a07a file/.htaccess required 2022-08-24 10:04:25 +04:00
pvincent 22a78d1fef tagged as 1.11.8 2022-08-23 22:05:07 +04:00
pvincent 6eb02e94ce makefile 2022-08-23 22:04:57 +04:00
pvincent 5116f6b60d tagged as 1.11.7 2022-08-23 21:56:32 +04:00
pvincent 9edfa4f40e package.json 2022-08-23 21:54:01 +04:00
pvincent 4b9fd45d92 tagged as 1.11.6 2022-08-23 21:49:31 +04:00
pvincent 42b07fea6a lang owend by www-data 2022-08-23 21:49:19 +04:00
pvincent 04a526e728 tagged as 1.11.5 2022-08-22 10:59:40 +04:00
pvincent e28efeab7e re-enable config.xml 2022-08-22 10:59:30 +04:00
pvincent bc939a5bdb tagged as 1.11.4 2022-08-22 10:48:54 +04:00
pvincent 4a052ea707 no more config.xml first time 2022-08-22 10:48:41 +04:00
pvincent f139fcfefb Update 'README.md' 2021-12-03 20:18:54 +04:00
pvincent 347a81dc66 Update 'README.md' 2021-08-31 21:34:26 +04:00
pvincent d142fc1eac Update 'README.md' 2021-08-31 21:22:59 +04:00
pvincent e039ed4fe5 better readme 2021-08-31 20:04:52 +04:00
pvincent 44d2248ba5 screenshot 2021-08-31 19:54:35 +04:00
pvincent 34e7527442 screenshot 2021-08-31 19:15:28 +04:00
pvincent ecffe9bb4e tagged as 1.11.3 2021-08-30 23:22:46 +04:00
pvincent 6e7382633f css footer margin 0 2021-08-30 23:22:19 +04:00
pvincent bb50c92230 tagged as 1.11.2 2021-08-30 22:49:49 +04:00
pvincent 98dffaebed fix config missing key 2021-08-30 22:49:34 +04:00
pvincent ca6aceffbc tagged as 1.11.1 2021-08-30 21:48:13 +04:00
pvincent c4ea83cca9 better README 2021-08-30 21:47:25 +04:00
pvincent 6c4aa6d9ea tagged as 1.11.0 2021-08-30 21:10:58 +04:00
pvincent cdfe52715f simpler config + new logo 2021-08-30 21:10:33 +04:00
pvincent 8bd119a818 maintain 2021-08-30 18:30:00 +04:00
pvincent 0854d556fe cagettepéi 2021-08-30 17:44:14 +04:00
pvincent 278f69e7d9 tagged as 1.10.2 2021-08-29 21:31:33 +04:00
pvincent 8699728593 version cagettepei 2021-08-29 21:31:16 +04:00
pvincent b14688417d tagged as 1.10.1 2021-08-28 16:41:12 +04:00
pvincent ea4da3c270 reply-to working 2021-08-28 16:40:37 +04:00
pvincent ef7b09454c cagettepéi 2021-08-28 11:05:37 +04:00
pvincent c23e3533d8 reply-to missing yet 2021-08-28 01:16:54 +04:00
47 changed files with 2924 additions and 2992 deletions
+2 -2
View File
@@ -25,11 +25,11 @@ lang/es/tmp/*.n
www/lang/*.mo
www/file/*
www/js/app.js
www/js/libs.js
www/file/*
www/css/.sass-cache
www/css/*.css
!www/css/bootstrap.min.css
+1 -1
View File
@@ -1 +1 @@
1.10.0
1.15.1
+2 -1
View File
@@ -41,7 +41,7 @@ install_dev:
#compile backend to Neko
compile:
@if [ $(PLUGINS) = 1 ]; then \
echo "compile Cagette.net with plugins"; \
echo "compile CagettePéi plugins"; \
haxe cagetteAllPlugins.hxml; \
else \
echo "compile CagettePéi core"; \
@@ -73,6 +73,7 @@ templates:
haxe cagette.hxml -D i18n_generation
@make LANG=fr ctemplates
msgfmt www/lang/texts_fr.po -o www/lang/texts_fr.mo
chown www-data:www-data -R www
chown www-data:www-data -R lang
+66 -50
View File
@@ -1,69 +1,82 @@
CAGETTE PEI
===========
Fourche du logiciel [Cagette.net](https://github.com/CagetteNet) à partir de la version 0.9.2 Novembre 2018
Cagette Péi est un logiciel libre qui favorise les circuits courts.
Il a été adapté pour les besoins des AMAPs sur le marché réunionnais.
Certaines portions du code source de Cagette.net n'étant plus disponibles en téléchargement,
j'ai repris sur la base d'un existant pour adapter au marché local réunionnais et leurs AMAPéi.
> <french>
> Fourche du logiciel [Cagette.net](https://github.com/CagetteNet) à partir de la version 0.9.2 Novembre 2018. Certaines portions de code n'étant plus disponibles en téléchargement à partir de cette date, le logiciel a été repris, adapté puis réactualisé. Pour plus d'information, contact@artcode.re
> </french>
## INSTALL FROM SCRATCH (debian-buster)
![Screenshot](www/img/screenshot.png)
* apt install -y curl
* curl -L https://deb.nodesource.com/setup_8.x | bash -
* apt install -y nodejs=8.17.0-1nodesource1
* apt install -y sudo haxe apache2 make git imagemagick gettext libapache2-mod-neko mariadb-server
* mysql -u root <<EOF
## PREREQUISITE MARIADB
```bash
apt install -y mariadb-server
mysql -u root <<EOF
CREATE DATABASE cagettepei;
GRANT ALL ON cagettepei.* TO 'cagettepei'@'%' IDENTIFIED BY 'cagettepei';
EOF
* cd /var/www
* git clone https://git.artcode.re/cagetters/cagettepei.git
* cd cagettepei
* make install ENV=dev # first time
* make compile # classes
* make templates # templates + i18n
* vim /etc/apache2/sites-available/cagettepei.conf
```
## INSTALL FROM SCRATCH
Rk: Works on any Debian (>=Buster) compatible server!
```
apt install -y curl
curl -L https://deb.nodesource.com/setup_8.x | bash -
apt install -y nodejs=8.17.0-1nodesource1
apt install -y sudo haxe apache2 make git imagemagick gettext libapache2-mod-neko mariadb-server sendemail libio-socket-ssl-perl libnet-ssleay-perl
cd /var/www
git clone https://git.artcode.re/cagetters/cagettepei.git
cd cagettepei
make install ENV=dev
vim /etc/apache2/sites-available/cagettepei.conf
```
```
<VirtualHost *:80>
ServerName cagettepei
DirectoryIndex index.n
DocumentRoot /var/www/cagettepei/www/
DocumentRoot /var/www/cagettepei/www
DirectoryIndex index.n
CustomLog ${APACHE_LOG_DIR}/cagettepei/access.log combined
</VirtualHost>
```
* sed -i '/<Directory \/var\/www\/>/,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf
* a2dissite 000-default.conf
* a2ensite cagettepei.conf
* a2enmod neko rewrite
* systemctl restart apache2
```bash
sed -i '/<Directory \/var\/www\/>/,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf
a2dissite 000-default.conf
a2ensite cagettepei.conf
a2enmod rewrite
mkdir -p /var/log/apache2/cagettepei
systemctl restart apache2
```
## CONFIGURATION
* cd /var/www/cagettepei
* vim config.xml
```bash
cd /var/www/cagettepei
vim config.xml
```
```
<config
host="<DOMAIN.TLD>"
key="<SECRET_PHRASE>"
name = "CagettePéi"
host = "<DOMAIN.TLD>"
database = "mysql://cagettepei:cagettepei@localhost/cagettepei"
key = "<SECRET_PHRASE>"
default_email = "<EMAIL>"
webmaster_email = "<EMAIL>"
database="mysql://cagettepei:cagettepei@localhost/cagettepei"
smtp_host="localhost"
smtp_port="25"
smtp_user=""
smtp_pass=""
debug = "0"
sqllog = "0"
maintain = "0"
lang="fr"
langs="fr"
langnames="Français"
maintain="0"
debug="0"
sqllog="0"
cache="1"
cachetpl="0"
cache = "1"
cachetpl = "0"
/>
```
@@ -72,16 +85,19 @@ ADMIN RESET PASSWORD
go to http://localhost for ERROR + table regeneration, then
* cd /var/www/cagettepei
* SECRET=$(grep key= config.xml | cut -d= -f2 | cut -d'"' -f2)
* echo "insert into User values (1,'fr',MD5('${SECRET}cagettepei'),1,'Administrateur','Cagette Péi', 'admin', null, null, null, null, null, null, null, null, null, now(), now(),6,null, null)" | mysql -h localhost -u cagettepei -pcagettepei cagettepei
```
cd /var/www/cagettepei
SECRET=$(grep key config.xml | cut -d= -f2 | cut -d'"' -f2)
echo "insert into User values (1,'fr',MD5('${SECRET}cagettepei'),1,'Administrateur','Cagette Péi', 'admin', null, null, null, null, null, null, null, null, null, now(), now(),6,null, null)" | mysql -h localhost -u cagettepei -pcagettepei cagettepei
```
then, connect with admin / cagettepei
### change admin password
* echo "update User set pass=MD5('${SECRET}cagettepei') where id=1" | mysql -h localhost -u cagettepei -pcagettepei cagettepei
```
echo "update User set pass=MD5('${SECRET}cagettepei') where id=1" | mysql -h localhost -u cagettepei -pcagettepei cagettepei
```
SYSTEMD-TIMERS
--------------
@@ -98,7 +114,7 @@ SYSTEMD-TIMERS
SELECTOR=$1
for i in /var/www/cagettepei-* ; do
for i in /var/www/cagettepei* ; do
if [[ -d $i ]]; then
cd $i/www
echo "cron-$SELECTOR in: $i"
+1
View File
@@ -21,6 +21,7 @@
-lib tink_streams:0.2.1
-lib tink_syntaxhub:0.4.3
-lib tink_typecrawler:0.7.0
-lib thx.text:0.2.1
## GIT
# -lib sugoi:git:https://git.artcode.re/cagetters/sugoi.git
+3 -8
View File
@@ -1,19 +1,14 @@
<config
database = "mysql://<USER>:<PASSWORD>@<HOST>/<DBNAME>"
host = "<URL without http>"
name = "<HTML head title>"
default_email = "<EMAIL>"
webmaster_email = "<EMAIL>"
key = "<SECRET_PHRASE>"
maintain="0"
cache="1"
cachetpl="0"
debug="0"
sqllog="0"
maintain="0"
lang="fr"
langs="fr"
langnames="Français"
cache="1"
cachetpl="0"
/>
+89 -85
View File
@@ -1,33 +1,33 @@
//React lib
// React lib
import react.ReactMacro.jsx;
import react.ReactDOM;
import react.*;
import react.router.*;
//custom components
// custom components
import react.order.*;
import react.product.*;
import react.store.*;
import react.map.*;
import react.user.*;
//require bootstrap JS since it's bundled with browserify
//@:jsRequire('bootstrap') extern class Bootstrap{}
//@:jsRequire('jquery') extern class JQ extends js.jquery.JQuery{}
// require bootstrap JS since it's bundled with browserify
// @:jsRequire('bootstrap') extern class Bootstrap{}
// @:jsRequire('jquery') extern class JQ extends js.jquery.JQuery{}
class App {
public static var instance:App;
public static var instance : App;
public var LANG : String;
public var currency : String; //currency symbol like &euro; or $
public var t : sugoi.i18n.GetText;//gettext translator
public var LANG:String;
public var currency:String; // currency symbol like &euro; or $
public var t:sugoi.i18n.GetText; // gettext translator
//i dont want to use redux now... saved state from react.OrderBox
public static var SAVED_ORDER_STATE : Dynamic;
// i dont want to use redux now... saved state from react.OrderBox
public static var SAVED_ORDER_STATE:Dynamic;
function new(?lang="fr",?currency="&euro;") {
//singleton
function new(?lang = "fr", ?currency = "&euro;") {
// singleton
instance = this;
if(lang!=null) this.LANG = lang;
if (lang != null)
this.LANG = lang;
this.currency = currency;
}
@@ -39,7 +39,7 @@ class App {
return new js.JQuery(r);
}
public static inline function jq(r:Dynamic):js.jquery.JQuery{
public static inline function jq(r:Dynamic):js.jquery.JQuery {
return new js.jquery.JQuery(r);
}
@@ -47,8 +47,7 @@ class App {
* The JS App will be available as "_" in the document.
*/
public static function main() {
//untyped js.Browser.window.$ = js.Lib.require("jQuery");
// untyped js.Browser.window.$ = js.Lib.require("jQuery");
untyped js.Browser.window._ = new App();
}
@@ -56,32 +55,31 @@ class App {
return new ShopCart();
}
public function getTagger(cid:Int ) {
public function getTagger(cid:Int) {
return new Tagger(cid);
}
public function getTuto(name:String, step:Int) {
new Tuto(name,step);
new Tuto(name, step);
}
/**
* remove method for IE compat
*/
public function remove(el:js.html.Element){
if (el == null) return;
public function remove(el:js.html.Element) {
if (el == null)
return;
el.parentElement.removeChild(el);
}
public function getVATBox(ttcprice:Float,currency:String,rates:String,vat:Float,formName:String){
public function getVATBox(ttcprice:Float, currency:String, rates:String, vat:Float, formName:String) {
var input = js.Browser.document.querySelector('form input[name="${formName}_price"]');
remove( js.Browser.document.querySelector('form input[name="${formName}_vat"]').parentElement.parentElement );
remove(js.Browser.document.querySelector('form input[name="${formName}_vat"]').parentElement.parentElement);
ReactDOM.render(jsx('<$VATBox ttc="$ttcprice" currency="$currency" vatRates="$rates" vat="$vat" formName="$formName"/>'), input.parentElement);
//remove(input);
// remove(input);
}
/*public function getProductComposer(){
@@ -89,7 +87,6 @@ class App {
//ReactDOM.render(jsx('<$ComposerApp/>'), js.Browser.document.getElementById("app"));
//});
}*/
/**
* Removes the form element and replace it by a react js component
* @param divId
@@ -97,72 +94,71 @@ class App {
* @param txpProductId
* @param formName
*/
public function getProductInput(divId:String, productName:String, txpProductId:String, formName:String ){
public function getProductInput(divId:String, productName:String, txpProductId:String, formName:String) {
js.Browser.document.addEventListener("DOMContentLoaded", function(event) {
// dirty stuff to remove "real" input, and replace it by the react one
App.j("form input[name='" + formName + "_name']").parent().parent().remove();
App.j("form select[name='" + formName + "_txpProductId']")
.parent()
.parent()
.remove();
//dirty stuff to remove "real" input, and replace it by the react one
App.j("form input[name='"+formName+"_name']").parent().parent().remove();
App.j("form select[name='" + formName+"_txpProductId']").parent().parent().remove();
if (txpProductId == null)
txpProductId = "";
if (txpProductId == null) txpProductId = "";
ReactDOM.render(jsx('<$ProductInput productName="$productName" txpProductId="$txpProductId" formName="$formName"/>'), js.Browser.document.getElementById(divId));
ReactDOM.render(jsx('<$ProductInput productName="$productName" txpProductId="$txpProductId" formName="$formName"/>'),
js.Browser.document.getElementById(divId));
});
}
public function initReportHeader(){
public function initReportHeader() {
ReactDOM.render(jsx('<$ReportHeader />'), js.Browser.document.querySelector('div.reportHeaderContainer'));
}
public function initOrderBox(userId:Int, distributionId:Int, contractId:Int, contractType:Int, date:String, place:String, userName:String, currency:String, hasPayments:Bool,callbackUrl:String){
public function initOrderBox(userId:Int, distributionId:Int, contractId:Int, contractType:Int, date:String, place:String, userName:String,
currency:String, hasPayments:Bool, callbackUrl:String) {
untyped App.j("#myModal").modal();
var onValidate = function() js.Browser.location.href = callbackUrl;
var node = js.Browser.document.querySelector('#myModal .modal-body');
ReactDOM.unmountComponentAtNode(node); //the previous modal DOM element is still there, so we need to destroy it
ReactDOM.unmountComponentAtNode(node); // the previous modal DOM element is still there, so we need to destroy it
ReactDOM.render(jsx('<$OrderBox userId="$userId" distributionId="$distributionId"
contractId="$contractId" contractType="$contractType" date="$date" place="$place" userName="$userName"
onValidate=$onValidate currency=$currency hasPayments=$hasPayments />'),node,postReact);
onValidate=$onValidate currency=$currency hasPayments=$hasPayments />'), node, postReact);
}
function postReact(){
function postReact() {
trace("post react");
haxe.Timer.delay(function(){
haxe.Timer.delay(function() {
untyped jq('[data-toggle="tooltip"]').tooltip();
untyped jq('[data-toggle="popover"]').popover();
},500);
}, 500);
}
public static function roundTo(n:Float, r:Int):Float {
return Math.round(n * Math.pow(10,r)) / Math.pow(10,r) ;
return Math.round(n * Math.pow(10, r)) / Math.pow(10, r);
}
/**
* Ajax loads a page and display it in a modal window
* @param url
* @param title
*/
public function overlay(url:String,?title,?large=true) {
if (title != null) title = StringTools.urlDecode(title);
public function overlay(url:String, ?title, ?large = true) {
if (title != null)
title = StringTools.urlDecode(title);
var r = new haxe.Http(url);
r.onData = function(data) {
//setup body and title
// setup body and title
var m = App.j("#myModal");
m.find(".modal-body").html(data);
if (title != null) m.find(".modal-title").html(title);
if (title != null)
m.find(".modal-title").html(title);
if (!large) m.find(".modal-dialog").removeClass("modal-lg");
untyped App.j('#myModal').modal(); //bootstrap 3 modal window
if (!large)
m.find(".modal-dialog").removeClass("modal-lg");
untyped App.j('#myModal').modal(); // bootstrap 3 modal window
}
r.request();
}
@@ -170,24 +166,37 @@ class App {
/**
* Displays a login box
*/
public function loginBox(redirectUrl:String,?message:String,?phoneRequired=false) {
public function loginBox(redirectUrl:String, ?message:String, ?phoneRequired = false) {
var m = App.j("#myModal");
m.find(".modal-title").html("S'identifier");
m.find(".modal-dialog").removeClass("modal-lg");
untyped m.modal();
ReactDOM.render(jsx('<$LoginBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'), js.Browser.document.querySelector('#myModal .modal-body'));
ReactDOM.render(jsx('<$LoginBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'),
js.Browser.document.querySelector('#myModal .modal-body'));
return false;
}
/**
* Displays a sign up box
*/
public function registerBox(redirectUrl:String,?message:String,?phoneRequired=false) {
public function registerBox(redirectUrl:String, ?message:String, ?phoneRequired = false) {
var m = App.j("#myModal");
m.find(".modal-title").html("S'inscrire");
m.find(".modal-dialog").removeClass("modal-lg");
untyped m.modal();
ReactDOM.render(jsx('<$RegisterBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'), js.Browser.document.querySelector('#myModal .modal-body'));
ReactDOM.render(jsx('<$RegisterBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'),
js.Browser.document.querySelector('#myModal .modal-body'));
return false;
}
public function cookieConsentBox(redirectUrl:String, ?message:String, ?phoneRequired = false) {
js.Browser.console.log("cookieConsentBox should load modal...");
var m = App.j("#myModal");
m.find(".modal-title").html("Consentement aux cookies");
m.find(".modal-dialog").removeClass("modal-lg");
untyped m.modal();
ReactDOM.render(jsx('<$CookieConsentBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'),
js.Browser.document.querySelector('#myModal .modal-body'));
return false;
}
@@ -195,7 +204,7 @@ class App {
ReactDOM.render(jsx('<$Store date=$date place=$place/>'), js.Browser.document.querySelector('#shop'));
}
public function groupMap(lat:String,lng:String,address:String) {
public function groupMap(lat:String, lng:String, address:String) {
ReactDOM.render(jsx('<$GroupMapRoot lat="$lat" lng="$lng" address="$address"/>'), js.Browser.document.querySelector('#map'));
}
@@ -203,19 +212,19 @@ class App {
* Helper to get values of a bunch of checked checkboxes
* @param formSelector
*/
public function getCheckboxesId(formSelector:String):Array<String>{
public function getCheckboxesId(formSelector:String):Array<String> {
var out = [];
var checkboxes = js.Browser.document.querySelectorAll(formSelector + " input[type=checkbox]");
for ( input in checkboxes ){
var input : js.html.InputElement = cast input;
if ( input.checked ) out.push(input.value);
for (input in checkboxes) {
var input:js.html.InputElement = cast input;
if (input.checked)
out.push(input.value);
}
return out;
}
#if plugins
public function getHostedPlugin(){
public function getHostedPlugin() {
return new hosted.js.App();
}
#end
@@ -223,40 +232,35 @@ class App {
/**
* set up a warning message when leaving the page
*/
public function setWarningOnUnload(active:Bool, ?msg:String){
if (active){
public function setWarningOnUnload(active:Bool, ?msg:String) {
if (active) {
js.Browser.window.addEventListener("beforeunload", warn);
}else{
} else {
js.Browser.window.removeEventListener("beforeunload", warn);
}
}
function warn(e:js.html.Event) {
var msg = "Voulez vous vraiment quitter cette page ?";
//js.Browser.window.confirm(msg);
untyped e.returnValue = msg; //Gecko + IE
// js.Browser.window.confirm(msg);
untyped e.returnValue = msg; // Gecko + IE
e.preventDefault();
return msg; //Gecko + Webkit, Safari, Chrome etc.
return msg; // Gecko + Webkit, Safari, Chrome etc.
}
/**
* Anti Doubleclick with btn elements.
* Can be bypassed by adding a .btn-noAntiDoubleClick class
*/
public function antiDoubleClick(){
for( n in js.Browser.document.querySelectorAll(".btn:not(.btn-noAntiDoubleClick)") ){
n.addEventListener("click",function(e:js.html.MouseEvent){
public function antiDoubleClick() {
for (n in js.Browser.document.querySelectorAll(".btn:not(.btn-noAntiDoubleClick)")) {
n.addEventListener("click", function(e:js.html.MouseEvent) {
var x = untyped e.target;
x.classList.add("disabled");
haxe.Timer.delay(function(){
haxe.Timer.delay(function() {
x.classList.remove("disabled");
},1000);
}, 1000);
});
}
}
}
+103
View File
@@ -0,0 +1,103 @@
package react.user;
import react.ReactComponent;
import react.ReactMacro.jsx;
import Common;
typedef CookieConsentBoxProps = {
redirectUrl:String,
message:String,
?phoneRequired:Bool
}
typedef CookieConsentBoxState = {
email:String,
password:String,
error:String
}
/**
* CookieConsent Box
* @author fbarbut
*/
class CookieConsentBox extends react.ReactComponentOfPropsAndState<CookieConsentBoxProps, CookieConsentBoxState> {
public function new(props:CookieConsentBoxProps) {
if (props.redirectUrl == null || props.redirectUrl == "null")
props.redirectUrl = "/";
if (props.message == "")
props.message = null;
super(props);
this.state = {email: "", password: "", error: null};
}
function setError(err:String) {
this.setState(cast {error: err});
}
override public function render() {
return jsx('<div onKeyPress=$onKeyPress>
<$Error error="${state.error}" />
<$Message message="${props.message}" />
<form action="" method="post" className="form-horizontal">
<h4>Personnalisation du paramétrage</h4>
<p>L\'outil CagettePéi n\'utilise que des cookies nécessaires pour vous aider à naviguer efficacement et à exécuter certaines fonctionnalités. Pas de cookie publicitaire, en provenance de site tiers ou de mesure d\'audience.
</p>
<p>
Seuls les cookies nécessaires sont cruciaux pour les fonctions de base du site Web et celui-ci ne fonctionnera pas comme prévu sans eux. Ces cookies ne stockent aucune donnée personnellement identifiable.
</p>
<p className="text-center">
<a onClick={deny} className="btn btn-secondary btn-lg" ><span className="glyphicon glyphicon-remove"></span> Tout Refuser</a>
&nbsp;
<a onClick={submit} className="btn btn-primary btn-lg" ><span className="glyphicon glyphicon-ok"></span> Tout Accepter</a>
</p>
</form>
</div>');
}
/**
* @doc https://facebook.github.io/react/docs/forms.html
*/
function onChange(e:js.html.Event) {
e.preventDefault();
var name:String = untyped e.target.name;
var value:String = untyped /*(e.target.value == "") ? null :*/ e.target.value;
Reflect.setField(state, name, value);
this.setState(this.state);
}
public function submit(?e:js.html.Event) {
// lock button
var el:js.html.Element = null;
if (e != null) {
el = cast e.target;
el.classList.add("disabled");
}
// var cookie = js.Browser.document.cookie;
var newCookie = "rgpd=true; SameSite=Lax";
js.Browser.document.cookie = newCookie;
// js.Browser.window.location.href = props.redirectUrl;
js.Browser.window.location.href = '/';
}
public function deny(?e:js.html.Event) {
// lock button
var el:js.html.Element = null;
if (e != null) {
el = cast e.target;
el.classList.add("disabled");
}
var newCookie = "rgpd=false; SameSite=Lax";
js.Browser.document.cookie = newCookie;
// js.Browser.window.location.href = props.redirectUrl;
js.Browser.window.location.href = '/';
}
function onKeyPress(e:js.html.KeyboardEvent) {
if (e.key == "Enter")
submit();
}
}
+34 -38
View File
@@ -1,4 +1,5 @@
package react.user;
import react.ReactComponent;
import react.ReactMacro.jsx;
import Common;
@@ -19,23 +20,21 @@ typedef LoginBoxState = {
* Login Box
* @author fbarbut
*/
class LoginBox extends react.ReactComponentOfPropsAndState<LoginBoxProps,LoginBoxState>
{
public function new(props:LoginBoxProps)
{
if (props.redirectUrl == null) props.redirectUrl = "/";
if (props.message == "") props.message = null;
class LoginBox extends react.ReactComponentOfPropsAndState<LoginBoxProps, LoginBoxState> {
public function new(props:LoginBoxProps) {
if (props.redirectUrl == null)
props.redirectUrl = "/";
if (props.message == "")
props.message = null;
super(props);
this.state = {email:"", password:"", error:null};
this.state = {email: "", password: "", error: null};
}
function setError(err:String){
this.setState(cast {error:err});
function setError(err:String) {
this.setState(cast {error: err});
}
override public function render(){
override public function render() {
return jsx('<div onKeyPress=$onKeyPress>
<$Error error="${state.error}" />
<$Message message="${props.message}" />
@@ -62,7 +61,7 @@ class LoginBox extends react.ReactComponentOfPropsAndState<LoginBoxProps,LoginBo
<!--
<hr/>
<p className="text-center">
<b>C\'est votre première visite sur Cagette.net ?</b>&nbsp;&nbsp;
<b>C\'est votre première visite sur CagettePéi ?</b>&nbsp;&nbsp;
<a onClick={registerBox} className="btn btn-default"><span className="glyphicon glyphicon-chevron-right"></span> S\'inscrire</a>
</p>
-->
@@ -72,11 +71,11 @@ class LoginBox extends react.ReactComponentOfPropsAndState<LoginBoxProps,LoginBo
/**
* @doc https://facebook.github.io/react/docs/forms.html
*/
function onChange(e:js.html.Event){
function onChange(e:js.html.Event) {
e.preventDefault();
var name :String = untyped e.target.name;
var value :String = untyped /*(e.target.value == "") ? null :*/ e.target.value;
var name:String = untyped e.target.name;
var value:String = untyped /*(e.target.value == "") ? null :*/ e.target.value;
Reflect.setField(state, name, value);
this.setState(this.state);
}
@@ -84,56 +83,53 @@ class LoginBox extends react.ReactComponentOfPropsAndState<LoginBoxProps,LoginBo
/**
* displays a registerBox
*/
public function registerBox(){
public function registerBox() {
var body = js.Browser.document.querySelector('#myModal .modal-body');
ReactDOM.unmountComponentAtNode( body );
ReactDOM.unmountComponentAtNode(body);
js.Browser.document.querySelector("#myModal .modal-title").innerHTML = "Inscription";
ReactDOM.render(jsx('<$RegisterBox redirectUrl="${props.redirectUrl}" phoneRequired="${props.phoneRequired}"/>'), body );
ReactDOM.render(jsx('<$RegisterBox redirectUrl="${props.redirectUrl}" phoneRequired="${props.phoneRequired}"/>'), body);
}
public function submit(?e:js.html.Event){
if (state.email == ""){
public function submit(?e:js.html.Event) {
if (state.email == "") {
setError("Veuillez saisir votre email");
return;
}
if (state.password == ""){
if (state.password == "") {
setError("Veuillez saisir votre mot de passe");
return;
}
//lock button
var el: js.html.Element = null;
if(e!=null){
// lock button
var el:js.html.Element = null;
if (e != null) {
el = cast e.target;
el.classList.add("disabled");
}
var req = new haxe.Http("/api/user/login");
req.addParameter("email", state.email);
req.addParameter("password", state.password);
req.addParameter("redirecturl", props.redirectUrl);
req.onData = req.onError = function(d){
req.onData = req.onError = function(d) {
var d = req.responseData;
if(e!=null) el.classList.remove("disabled");
if (e != null)
el.classList.remove("disabled");
var d = haxe.Json.parse(d);
if (Reflect.hasField(d, "error")) setError(d.error.message);
if (Reflect.hasField(d, "success")) js.Browser.window.location.href = props.redirectUrl;
if (Reflect.hasField(d, "error"))
setError(d.error.message);
if (Reflect.hasField(d, "success"))
js.Browser.window.location.href = props.redirectUrl;
}
req.request(true);
}
function onKeyPress(e:js.html.KeyboardEvent){
if(e.key=="Enter") submit();
function onKeyPress(e:js.html.KeyboardEvent) {
if (e.key == "Enter")
submit();
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
::use 'admin/design.mtt'::
<h3>Console d'administration de Cagette.net</h3>
<h3>Console d'administration de CagettePéi</h3>
<p>
::hDate(now)::
</p>
+1 -1
View File
@@ -11,7 +11,7 @@
::end::
</title>
<!-- Cagette CSS -->
<!-- CagettePéi CSS -->
<link rel="stylesheet" type="text/css" href="/css/::getVariable('css')::/style.css"/>
<!--
<link rel="stylesheet" type="text/css" href="/css/::getVariable('css')::/shop.css"/>
+3 -140
View File
@@ -13,11 +13,7 @@
::if groupName!=null && noGroup!=true::
<h1>::groupName::</h1>
::else::
<div style="margin-bottom:22px;" >
<a href="https://git.artcode.re/cagetters/cagette" target="_blank">
<img src="/img/logo3.png" alt="::NAME::" />
</a>
</div>
<h1>::_("Cagette.net")::</h1>
::end::
</div>
@@ -121,147 +117,14 @@
<!--<div class="row">
<div class="col-md-12 alert-danger text-center" style="border-radius: 8px;margin:8px 0;padding:8px 0;">
<span class="glyphicon glyphicon-cog"></span>
Attention : Cagette.net sera indisponible le <b>mardi 7 août de 9:00 à 11:00</b>.
Attention : CagettePéi sera indisponible le <b>mardi 7 août de 9:00 à 11:00</b>.
<br/>
Nous déménageons sur un nouveau serveur pour plus de rapidité et de fiabilité.
</div>
</div>-->
<div class="row" id="footer">
<div class="col-md-9" >
<a href="https://git.artcode.re/cagetters/cagette" target="_blank">
<img src="/img/logo.png" alt="::_("Cagette.net")::"/>
</a>
</div>
<div class="col-md-3" >
::_("HELP")::
<ul>
<li>
<a href="http://www.cagette.net/wiki" target="_blank">::_("Documentation")::</a>
</li>
<li>
<a href="https://pad1.zourit.net/p/4a3b09afd25b4fdcb3f0a9598db21e61" target="_blank">Proposer une amélioration</a>
</li>
<li>
<a href="https://pad1.zourit.net/p/4a3b09afd25b4fdcb3f0a9598db21e61" target="_blank">Soumettre une anomalie</a>
</li>
</ul>
<!--<p style="position: absolute;display: block;top: 96px;width: 700px;">
<b>::raw _("Vous êtes producteur ?")::</b>
::raw _("Découvrez nos formations gratuites pour utiliser <b>Cagette Pro</b> ")::
&nbsp;&nbsp;<a href="http://alilo.fr/formations-producteurs" target="_blank" class="btn btn-xs btn-info">C'est parti !</a>
</p>-->
</div>
<!--
<div class="col-md-3" >
::_("FOLLOW US")::
<ul class="cagsocialmedia">
<li class="cagfb">
<a title="Facebook" href="https://www.facebook.com/cagette" target="_blank"> <i class="fa fa-facebook"></i></a>
</li>
<li class="cagtwitter">
<a title="Twitter" href="https://twitter.com/Cagettenet" target="_blank"> <i class="fa fa-twitter"></i></a>
</li>
<li class="cagyoutube">
<a title="Youtube" href="https://www.youtube.com/channel/UC3cvGxAUrbN9oSZmr1oZEaw" target="_blank"> <i class="fa fa-youtube"></i></a>
</li>
<li style="background-color:#333;">
<a title="Github" href="https://github.com/bablukid/cagette" target="_blank"> <i class="fa fa-github"></i></a>
</li>
</ul>
</div>
-->
<!--
<div class="col-md-3">
::_("CONTACT US")::
<ul>
<li>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick"/>
<input type="hidden" name="hosted_button_id" value="S9KT7FQS7P622"/>
<input type="image" src="https://www.paypalobjects.com/fr_FR/FR/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal, le réflexe sécurité pour payer en ligne"/>
<img alt="" border="0" src="https://www.paypalobjects.com/fr_FR/i/scr/pixel.gif" width="1" height="1"/>
</form>
</li>
</ul>
</div>
-->
</div>
<div class="row" id="subfooter">
<!--
::raw _("Cagette.net is made by <a href='https://www.alilo.fr' target='_blank'><img src='/img/alilo-trans-black.png' /></a>")::<br/>
-->
<span>version ::VERSION::</span>
</div>
<div class="row">
<div class="col-md-12">
<!-- SQL LOG -->
<table class="sqlLog table table-bordered" ::cond sqlLog::>
::foreach r sqlLog::
<tr ::attr class if(r.bad) 'badSql'::>
<td>::r.t::ms</td>
<td>::r.length::</td>
<td><a href="#" onclick="javascript:alert('::r.explain::');return false;">Exp</a></td>
<td><a href="#" onclick="javascript:alert('::r.stack::');return false;">Stack</a></td>
<td>::r.sql::</td>
</tr>
::use 'footer.mtt'::
::end::
</table>
</div>
</div>
::if (false && user!=null && user.isAmapManager())::
<script>
// Include the UserVoice JavaScript SDK (only needed once on a page)
UserVoice=window.UserVoice||[];(function(){var uv=document.createElement('script');uv.type='text/javascript';uv.async=true;uv.src='//widget.uservoice.com/sayZ1eIZLfSZUa3IwslHCw.js';var s=document.getElementsByTagName('script')[0];s.parentNode.insertBefore(uv,s)})();
//
// UserVoice Javascript SDK developer documentation:
// https://www.uservoice.com/o/javascript-sdk
//
// Set colors
UserVoice.push(['set', {
accent_color: '#6aba2e',
trigger_color: 'white',
trigger_background_color: '#e2753a'
}]);
// Identify the user and pass traits
// To enable, replace sample data with actual user traits and uncomment the line
UserVoice.push(['identify', {
//email: 'john.doe@example.com', // Users email address
//name: 'John Doe', // Users real name
//created_at: 1364406966, // Unix timestamp for the date the user signed up
//id: 123, // Optional: Unique id of the user (if set, this should not change)
//type: 'Owner', // Optional: segment your users by type
//account: {
// id: 123, // Optional: associate multiple users with a single account
// name: 'Acme, Co.', // Account name
// created_at: 1364406966, // Unix timestamp for the date the account was created
// monthly_rate: 9.99, // Decimal; monthly rate of the account
// ltv: 1495.00, // Decimal; lifetime value of the account
// plan: 'Enhanced' // Plan name for the account
//}
}]);
// Add default trigger to the bottom-right corner of the window:
UserVoice.push(['addTrigger', { mode: 'smartvote', trigger_position: 'bottom-right' }]);
// Or, use your own custom trigger:
//UserVoice.push(['addTrigger', '#id', { mode: 'smartvote' }]);
// Autoprompt for Satisfaction and SmartVote (only displayed under certain conditions)
UserVoice.push(['autoprompt', {}]);
</script>
::end::
::end::
+56
View File
@@ -0,0 +1,56 @@
<div class="row" id="footer">
<div class="col-md-2" >
<img width="120" style="border: white 5px solid;" src="/img/logo-mini.jpg" alt="::_("Cagette.net")::" title="Logo ::_("Cagette.net")::"/>
</div>
<div class="col-md-7" style="padding-top:1em">
Cagette Péi est un logiciel libre qui favorise les circuits courts.<br/>
Il a été adapté pour les besoins des AMAPs sur le marché réunionnais.<br/>
</div>
<div class="col-md-3" >
::_("HELP")::
<ul>
<li>
<a href="https://wiki.cagette.net/doku.php" target="_blank">::_("Documentation")::</a>
</li>
<li>
<a href="https://pad1.zourit.net/p/4a3b09afd25b4fdcb3f0a9598db21e61" target="_blank">Proposer une amélioration</a>
</li>
<li>
<a href="https://pad1.zourit.net/p/4a3b09afd25b4fdcb3f0a9598db21e61" target="_blank">Soumettre une anomalie</a>
</li>
</ul>
</div>
</div>
<div class="row" id="subfooter">
<p><a href="/mentions">MENTIONS LÉGALES</a></p>
<p>
version cagettepei-<a target="_blank" href="https://git.artcode.re/cagetters/cagettepei/src/tag/::VERSION::">::VERSION::</a>
</p>
::if BANDEAU_REGION::
<img src='/img/bandeau_region_france_relance.png' alt='bandeau Région Réunion' width='640'></img>
::end::
</div>
<div class="row">
<div class="col-md-12">
<!-- SQL LOG -->
<table class="sqlLog table table-bordered" ::cond sqlLog::>
::foreach r sqlLog::
<tr ::attr class if(r.bad) 'badSql'::>
<td>::r.t::ms</td>
<td>::r.length::</td>
<td><a href="#" onclick="javascript:alert('::r.explain::');return false;">Exp</a></td>
<td><a href="#" onclick="javascript:alert('::r.stack::');return false;">Stack</a></td>
<td>::r.sql::</td>
</tr>
::end::
</table>
</div>
</div>
+14 -6
View File
@@ -1,6 +1,6 @@
::use 'base.mtt'::
<div class="row header" style="margin-top:10px;">
<div class="col-md-8 col-md-offset-2" style="margin-top:12px;">
<!-- Notifications -->
@@ -185,11 +185,19 @@
</div>
<div class="col-md-12">
<div id="footer">
<a href="::_("http://www.cagette.net")::">::_("Cagette.net, the free software of food direct-selling")::</a>.
</div>
::if user == null::
<script language="javascript">
if (document.cookie.includes("rgpd=true")) console.log("RGPD consent OK");
else {
console.log("no consent YET");
_.cookieConsentBox('::redirect::');
}
</script>
::end::
</div>
</div>
::use 'footer.mtt'::
::end::
::end::
+2 -2
View File
@@ -1,4 +1,4 @@
::use 'design.mtt'::
<!--::use 'design.mtt'::-->
<div class="text-center" style="height:400px;width:100%;">
<h1>
<span class="glyphicon glyphicon-cog"></span>
@@ -14,4 +14,4 @@
</div>
::end::
<!--::end::-->
@@ -0,0 +1,62 @@
::use 'design.mtt'::
<div class="col-md-9">
<div class="article">
<h4>Mentions Légales</h4>
<table class="table table-bordered table-condensed">
<tbody><tr>
<th>Champ</th>
<th>Valeur</th>
</tr>
<tr>
<td>Adresse de courrier électronique</td>
<td>contact@mangue-et-solidaire.re</td>
</tr>
<tr>
<td>Numéro Siren</td>
<td>879178739</td>
</tr>
<tr>
<td>Numéro Siret</td>
<td>87917873900022</td>
</tr>
<tr>
<td>Code NAF / APE</td>
<td>4711B</td>
</tr>
<tr>
<td>Forme juridique et capital</td>
<td>Association Loi 1901</td>
</tr>
<tr>
<td>Responsable éditorial</td>
<td>Yann Jacob</td>
</tr>
<tr>
<td>Hébergeur</td>
<td><a href="www.artcode.re">Artcode</a></td>
</tr>
</tbody>
</table>
</div>
</div>
::end::
+14
View File
@@ -0,0 +1,14 @@
::use 'design.mtt'::
<div class="col-md-12" style="height:500px;">
::if user != null::
consent agreed
::else::
<script language="javascript">
_.cookieConsentBox('::redirect::');
</script>
::end::
</div>
::end::
+1 -51
View File
@@ -1,51 +1 @@
{
"name": "CagettePei",
"description": "le logiciel libre du circuit court",
"license": "AGPL-3.0",
"dependencies": {
"bootstrap": "^3.3.4",
"eonasdan-bootstrap-datetimepicker": "^4.7.14",
"geolib": "^2.0.24",
"haxe-modular": "^0.7.0",
"jquery": "^2.2.4",
"leaflet": "^1.3.1",
"react": "^15.6.1",
"react-bootstrap-typeahead": "1.4.2",
"react-datetime": "^2.9.0",
"react-dom": "^15.6.1",
"react-leaflet": "^1.8.0",
"react-places-autocomplete": "^6.0.2",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2"
},
"devDependencies": {
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babelify": "^7.3.0",
"browserify": "^14.4.0",
"cross-env": "^5.1.1",
"node-sass": "^4.5.3",
"npm-run-all": "^4.1.2",
"uglify-js": "^3.1.9",
"watchify": "^3.9.0"
},
"scripts": {
"build": "npm-run-all --parallel build:*",
"build:js": "haxe cagetteJs.hxml",
"build:sass": "node-sass www/css -o www/css",
"watch:sass": "npm run build:sass -- --watch",
"build2": "babel src -d lib",
"libs:dev": "browserify js/libs/libs.js -o www/js/libs.js -d",
"libs:prod": "cross-env NODE_ENV=production browserify js/libs/libs.js | uglifyjs -c -m > www/js/libs.js",
"test": "haxe testsJS.hxml && node js/test/tests.js",
"prepush": "npm test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/bablukid/cagette.git"
},
"bugs": {
"url": "https://github.com/bablukid/cagette/issues"
},
"version": "1.10.0"
}
{"name": "cagette-pei", "description": "le logiciel libre du circuit court", "license": "AGPL-3.0", "dependencies": {"bootstrap": "^3.3.4", "eonasdan-bootstrap-datetimepicker": "^4.7.14", "geolib": "^2.0.24", "haxe-modular": "^0.7.0", "jquery": "^2.2.4", "leaflet": "^1.3.1", "react": "^15.6.1", "react-bootstrap-typeahead": "1.4.2", "react-datetime": "^2.9.0", "react-dom": "^15.6.1", "react-leaflet": "^1.8.0", "react-places-autocomplete": "^6.0.2", "react-router": "^4.2.0", "react-router-dom": "^4.2.2"}, "devDependencies": {"babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "babelify": "^7.3.0", "browserify": "^14.4.0", "cross-env": "^5.1.1", "node-sass": "^4.5.3", "npm-run-all": "^4.1.2", "uglify-js": "^3.1.9", "watchify": "^3.9.0"}, "scripts": {"build": "npm-run-all --parallel build:*", "build:js": "haxe cagetteJs.hxml", "build:sass": "node-sass www/css -o www/css", "watch:sass": "npm run build:sass -- --watch", "build2": "babel src -d lib", "libs:dev": "browserify js/libs/libs.js -o www/js/libs.js -d", "libs:prod": "cross-env NODE_ENV=production browserify js/libs/libs.js | uglifyjs -c -m > www/js/libs.js", "test": "haxe testsJS.hxml && node js/test/tests.js", "prepush": "npm test"}, "repository": {"type": "git", "url": "git+https://github.com/bablukid/cagette.git"}, "bugs": {"url": "https://github.com/bablukid/cagette/issues"}, "version": "1.15.1"}
+3 -10
View File
@@ -1,5 +1,4 @@
import db.User;
import thx.semver.Version;
import Common;
class App extends sugoi.BaseApp {
@@ -10,13 +9,7 @@ class App extends sugoi.BaseApp {
public var eventDispatcher:hxevents.Dispatcher<Event>;
public var plugins:Array<sugoi.plugin.IPlugIn>;
/**
* Version management
* @doc https://github.com/fponticelli/thx.semver
*/
// public static var VERSION = ([0,9,2] : Version).withPre("july");
// public static var VERSION = ([0,9,2] : Version).withPre(MyMacros.getGitShortSHA(), MyMacros.getGitCommitDate());
public static var VERSION = "cagette-péi " + MyMacros.getGitCommitDate();
public static var VERSION = "cagette-pei"; // empty yet, cf.BaseApp.loadConfig
public function new() {
super();
@@ -210,7 +203,7 @@ class App extends sugoi.BaseApp {
out.set("percent", "Pourcentage");
out.set("pinned", "Mets en avant les produits");
out.set("CagetteNetwork", "Me lister dans l'annuaire des groupes Cagette.net");
out.set("CagetteNetwork", "Me lister dans l'annuaire des groupes");
out.set("unitType", "Unité");
out.set("qt", "Quantité");
out.set("Unit", "Pièce");
@@ -287,7 +280,7 @@ class App extends sugoi.BaseApp {
var e = new sugoi.mail.Mail();
e.setSubject(subject);
e.setRecipient(to);
e.setSender(App.config.get("default_email"), "Cagette.net");
e.setSender(App.config.get("default_email"), t._("CagettePéi"));
var html = App.current.processTemplate("mail/message.mtt", {text: html, group: group});
e.setHtmlBody(html);
App.sendMail(e);
+186 -194
View File
@@ -1,4 +1,5 @@
package controller;
import db.UserContract;
import sugoi.form.elements.DateDropdowns;
import sugoi.form.elements.Input;
@@ -7,14 +8,13 @@ import sugoi.form.Form;
import db.Contract;
import Common;
import plugin.Tutorial;
using Std;
import service.OrderService;
class Contract extends Controller
{
public function new()
{
class Contract extends Controller {
public function new() {
super();
}
@@ -29,58 +29,58 @@ class Contract extends Controller
*/
@tpl("contract/default.mtt")
function doDefault() {
//Create the list of links to change the language
var langs = App.config.get("langs").split(";");
var langNames = App.config.get("langnames").split(";");
var i=0;
// Create the list of links to change the language
var langs = App.config.get("langs", "fr").split(";");
var langNames = App.config.get("langnames", "Français").split(";");
var i = 0;
var langLinks = "";
for (lang in langs)
{
for (lang in langs) {
langLinks += "<li><a href=\"?lang=" + langs[i] + "\">" + langNames[i] + "</a></li>";
i++;
}
view.langLinks = langLinks;
view.langText = langNames[langs.indexOf(app.session.lang)];
//change account lang
if (app.params.exists("lang") && app.user!=null){
// change account lang
if (app.params.exists("lang") && app.user != null) {
app.user.lock();
app.user.lang = app.params.get("lang");
app.user.update();
}
var ua = db.UserAmap.get(app.user, app.user.amap);
if (ua == null) throw Error("/", t._("You are not a member of this group"));
if (ua == null)
throw Error("/", t._("You are not a member of this group"));
var constOrders = null;
var varOrders = new Map<String,Array<db.UserContract>>();
var varOrders = new Map<String, Array<db.UserContract>>();
var a = App.current.user.amap;
var oneMonthAgo = DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24 * 30);
//constant orders
// constant orders
var contracts = db.Contract.manager.search($type == db.Contract.TYPE_CONSTORDERS && $amap == a && $endDate > oneMonthAgo, false);
constOrders = [];
for ( c in contracts){
for (c in contracts) {
var orders = app.user.getOrdersFromContracts([c]);
if (orders.length == 0) continue;
constOrders.push({contract:c, orders:service.OrderService.prepare(orders) });
if (orders.length == 0)
continue;
constOrders.push({contract: c, orders: service.OrderService.prepare(orders)});
}
//variable orders, grouped by date
// variable orders, grouped by date
var contracts = db.Contract.manager.search($type == db.Contract.TYPE_VARORDER && $amap == a && $endDate > oneMonthAgo, false);
for (c in contracts) {
var ds = c.getDistribs(false);
for (d in ds) {
//store orders in a stringmap like "2015-01-01" => [order1,order2,...]
// store orders in a stringmap like "2015-01-01" => [order1,order2,...]
var k = d.date.toString().substr(0, 10);
var orders = app.user.getOrdersFromDistrib(d);
if (orders.length > 0) {
if (!varOrders.exists(k)) {
varOrders.set(k, Lambda.array(orders));
}else {
} else {
var z = varOrders.get(k).concat(Lambda.array(orders));
varOrders.set(k, z);
}
@@ -88,58 +88,53 @@ class Contract extends Controller
}
}
//final structure
var varOrders2 = new Array<{date:Date,orders:Array<UserOrder>}>();
for ( k in varOrders.keys()) {
// final structure
var varOrders2 = new Array<{date:Date, orders:Array<UserOrder>}>();
for (k in varOrders.keys()) {
var d = new Date(k.split("-")[0].parseInt(), k.split("-")[1].parseInt() - 1, k.split("-")[2].parseInt(), 0, 0, 0);
var orders = service.OrderService.prepare( Lambda.list(varOrders[k]) );
var orders = service.OrderService.prepare(Lambda.list(varOrders[k]));
varOrders2.push({date:d,orders:orders});
varOrders2.push({date: d, orders: orders});
}
//sort by date desc
// sort by date desc
varOrders2.sort(function(b, a) {
return Math.round(a.date.getTime()/1000)-Math.round(b.date.getTime()/1000);
return Math.round(a.date.getTime() / 1000) - Math.round(b.date.getTime() / 1000);
});
view.varOrders = varOrders2;
view.constOrders = constOrders;
// tutorials
if (app.user.isAmapManager()) {
//actions
if (app.params.exists('startTuto') ) {
//start a tuto
// actions
if (app.params.exists('startTuto')) {
// start a tuto
app.user.lock();
var t = app.params.get('startTuto');
app.user.tutoState = {name:t,step:0};
app.user.tutoState = {name: t, step: 0};
app.user.update();
}
// tuto state
var tutos = new Array<{name:String, completion:Float, key:String}>();
//tuto state
var tutos = new Array<{name:String,completion:Float,key:String}>();
for ( k in Tutorial.all().keys() ) {
for (k in Tutorial.all().keys()) {
var t = Tutorial.all().get(k);
var completion = null;
if (app.user.tutoState!=null && app.user.tutoState.name == k) completion = app.user.tutoState.step / t.steps.length;
if (app.user.tutoState != null && app.user.tutoState.name == k)
completion = app.user.tutoState.step / t.steps.length;
tutos.push( { name:t.name, completion:completion , key:k } );
tutos.push({name: t.name, completion: completion, key: k});
}
view.tutos = tutos;
}
//should be able to stop tuto in any case
// should be able to stop tuto in any case
if (app.params.exists('stopTuto')) {
//stopped tuto from a tuto window
// stopped tuto from a tuto window
app.user.lock();
app.user.tutoState = null;
app.user.update();
@@ -155,28 +150,29 @@ class Contract extends Controller
*/
@tpl("form.mtt")
function doEdit(c:db.Contract) {
view.category = 'contractadmin';
if (!app.user.isContractManager(c)) throw Error('/', t._("Forbidden action"));
if (!app.user.isContractManager(c))
throw Error('/', t._("Forbidden action"));
view.title = t._("Edit contract ::contractName::",{contractName:c.name});
view.title = t._("Edit contract ::contractName::", {contractName: c.name});
var group = c.amap;
var currentContact = c.contact;
var form = Form.fromSpod(c);
form.removeElement( form.getElement("amapId") );
form.removeElement(form.getElement("amapId"));
form.removeElement(form.getElement("type"));
form.getElement("userId").required = true;
app.event(EditContract(c,form));
app.event(EditContract(c, form));
if (form.checkToken()) {
form.toSpod(c);
c.amap = group;
//checks & warnings
if (c.hasPercentageOnOrders() && c.percentageValue==null) throw Error("/contract/edit/"+c.id, t._("If you would like to add fees to the order, define a rate (%) and a label."));
// checks & warnings
if (c.hasPercentageOnOrders() && c.percentageValue == null)
throw Error("/contract/edit/" + c.id, t._("If you would like to add fees to the order, define a rate (%) and a label."));
if (c.hasStockManagement()) {
for (p in c.getProducts()) {
@@ -187,24 +183,23 @@ class Contract extends Controller
}
}
//no stock mgmt for constant orders
if (c.hasStockManagement() && c.type==db.Contract.TYPE_CONSTORDERS) {
// no stock mgmt for constant orders
if (c.hasStockManagement() && c.type == db.Contract.TYPE_CONSTORDERS) {
c.flags.unset(ContractFlags.StockManagement);
app.session.addMessage(t._("Managing stock is not available for CSA contracts"), true);
}
c.update();
//update rights
if ( c.contact != null && (currentContact==null || c.contact.id!=currentContact.id) ) {
// update rights
if (c.contact != null && (currentContact == null || c.contact.id != currentContact.id)) {
var ua = db.UserAmap.get(c.contact, app.user.amap, true);
ua.giveRight(ContractAdmin(c.id));
ua.giveRight(Messages);
ua.giveRight(Membership);
ua.update();
//remove rights to old contact
// remove rights to old contact
if (currentContact != null) {
var x = db.UserAmap.get(currentContact, c.amap, true);
if (x != null) {
@@ -212,10 +207,9 @@ class Contract extends Controller
x.update();
}
}
}
throw Ok("/contractAdmin/view/"+c.id, t._("Contract updated"));
throw Ok("/contractAdmin/view/" + c.id, t._("Contract updated"));
}
view.form = form;
@@ -223,8 +217,7 @@ class Contract extends Controller
@tpl("contract/insertChoose.mtt")
function doInsertChoose() {
//checkToken();
// checkToken();
}
/**
@@ -232,27 +225,29 @@ class Contract extends Controller
*/
@tpl("form.mtt")
function doInsert(?type:Int) {
if (!app.user.canManageAllContracts()) throw Error('/', t._("Forbidden action"));
if (type == null) throw Redirect('/contract/insertChoose');
if (!app.user.canManageAllContracts())
throw Error('/', t._("Forbidden action"));
if (type == null)
throw Redirect('/contract/insertChoose');
view.title = if (type == db.Contract.TYPE_CONSTORDERS)t._("Create a contract with fixed orders") else t._("Create a contract with variable orders");
view.title = if (type == db.Contract.TYPE_CONSTORDERS) t._("Create a contract with fixed orders") else t._("Create a contract with variable orders");
var c = new db.Contract();
var form = Form.fromSpod(c);
form.removeElement( form.getElement("amapId") );
form.removeElement(form.getElement("amapId"));
form.removeElement(form.getElement("type"));
form.getElement("userId").required = true;
if (form.checkToken()) {
form.toSpod(c);
c.amap = app.user.amap;
//trace(app.user.amap);
//trace(c.amap);
// trace(app.user.amap);
// trace(c.amap);
c.type = type;
c.insert();
//right
// right
if (c.contact != null) {
var ua = db.UserAmap.get(c.contact, app.user.amap, true);
ua.giveRight(ContractAdmin(c.id));
@@ -261,7 +256,7 @@ class Contract extends Controller
ua.update();
}
throw Ok("/contractAdmin/view/"+c.id, t._("New contract created"));
throw Ok("/contractAdmin/view/" + c.id, t._("New contract created"));
}
view.form = form;
@@ -271,24 +266,25 @@ class Contract extends Controller
* Delete a contract (... and its products, orders & distributions)
*/
function doDelete(c:db.Contract) {
if (!app.user.canManageAllContracts()) throw Error("/contractAdmin", t._("You don't have the authorization to remove a contract"));
if (!app.user.canManageAllContracts())
throw Error("/contractAdmin", t._("You don't have the authorization to remove a contract"));
if (checkToken()) {
c.lock();
//check if there is orders in this contract
// check if there is orders in this contract
var products = c.getProducts();
var orders = db.UserContract.manager.search($productId in Lambda.map(products, function(p) return p.id));
var qt = 0.0;
for ( o in orders) qt += o.quantity; //there could be "zero c qt" orders
for (o in orders)
qt += o.quantity; // there could be "zero c qt" orders
if (qt > 0) {
throw Error("/contractAdmin", t._("You cannot delete this contract because some orders are linked to it."));
}
//remove admin rights and delete contract
if(c.contact!=null){
// remove admin rights and delete contract
if (c.contact != null) {
var ua = db.UserAmap.get(c.contact, c.amap, true);
if (ua != null) {
ua.removeRight(ContractAdmin(c.id));
@@ -314,89 +310,89 @@ class Contract extends Controller
*
*/
@tpl("contract/order.mtt")
function doOrder(c:db.Contract ) {
//checks
if (app.user.amap.hasPayments()) throw Redirect("/contract/orderAndPay/" + c.id);
if (app.user.amap.hasShopMode()) throw Redirect("/shop");
if (!c.isUserOrderAvailable()) throw Error("/", t._("This contract is not opened for orders"));
function doOrder(c:db.Contract) {
// checks
if (app.user.amap.hasPayments())
throw Redirect("/contract/orderAndPay/" + c.id);
if (app.user.amap.hasShopMode())
throw Redirect("/shop");
if (!c.isUserOrderAvailable())
throw Error("/", t._("This contract is not opened for orders"));
var distributions = [];
// If its a varying contract, we display a column by distribution
if (c.type == db.Contract.TYPE_VARORDER) {
distributions = db.Distribution.getOpenToOrdersDeliveries(c);
}else{
} else {
distributions = [null];
}
//list of distribs with a list of product and optionnaly an order
var userOrders = new Array< {distrib:db.Distribution,datas:Array<{order:db.UserContract,product:db.Product}>} >();
// list of distribs with a list of product and optionnaly an order
var userOrders = new Array<{distrib:db.Distribution, datas:Array<{order:db.UserContract, product:db.Product}>}>();
var products = c.getProducts();
if ( c.type == db.Contract.TYPE_VARORDER ){
for ( d in distributions){
if (c.type == db.Contract.TYPE_VARORDER) {
for (d in distributions) {
var datas = [];
for ( p in products) {
var ua = { order:null, product:p };
for (p in products) {
var ua = {order: null, product: p};
var order = db.UserContract.manager.select($user == app.user && $productId == p.id && $distributionId==d.id, true);
var order = db.UserContract.manager.select($user == app.user && $productId == p.id && $distributionId == d.id, true);
if (order != null) ua.order = order;
if (order != null)
ua.order = order;
datas.push(ua);
}
userOrders.push({distrib:d,datas:datas});
userOrders.push({distrib: d, datas: datas});
}
}else{
} else {
var datas = [];
for ( p in products) {
var ua = { order:null, product:p };
for (p in products) {
var ua = {order: null, product: p};
var order = db.UserContract.manager.select($user == app.user && $productId == p.id, true);
if (order != null) ua.order = order;
if (order != null)
ua.order = order;
datas.push(ua);
}
userOrders.push({distrib:null,datas:datas});
userOrders.push({distrib: null, datas: datas});
}
//form check
// form check
if (checkToken()) {
//get dsitrib if needed
//var distrib : db.Distribution = null;
//if (c.type == db.Contract.TYPE_VARORDER) {
//distrib = db.Distribution.manager.get(Std.parseInt(app.params.get("distribution")), false);
//}
// get dsitrib if needed
// var distrib : db.Distribution = null;
// if (c.type == db.Contract.TYPE_VARORDER) {
// distrib = db.Distribution.manager.get(Std.parseInt(app.params.get("distribution")), false);
// }
for (k in app.params.keys()) {
if (k.substr(0, 1) != "d") continue;
if (k.substr(0, 1) != "d")
continue;
var qt = app.params.get(k);
if (qt == "") continue;
if (qt == "")
continue;
var pid = null;
var did = null;
try{
try {
pid = Std.parseInt(k.split("-")[1].substr(1));
did = Std.parseInt(k.split("-")[0].substr(1));
}catch (e:Dynamic){trace("unable to parse key "+k); }
} catch (e:Dynamic) {
trace("unable to parse key " + k);
}
//find related element in userOrders
// find related element in userOrders
var uo = null;
for ( x in userOrders){
if (x.distrib!=null && x.distrib.id != did) {
for (x in userOrders) {
if (x.distrib != null && x.distrib.id != did) {
continue;
}else{
for (a in x.datas){
if (a.product.id == pid){
} else {
for (a in x.datas) {
if (a.product.id == pid) {
uo = a;
break;
}
@@ -404,43 +400,43 @@ class Contract extends Controller
}
}
if (uo == null) throw t._("Could not find the product ::produ:: and delivery ::deliv::", {produ:pid, deliv:did});
if (uo == null)
throw t._("Could not find the product ::produ:: and delivery ::deliv::", {produ: pid, deliv: did});
var q = 0.0;
if (uo.product.hasFloatQt ) {
if (uo.product.hasFloatQt) {
var param = StringTools.replace(qt, ",", ".");
q = Std.parseFloat(param);
}else {
} else {
q = Std.parseInt(qt);
}
if (uo.order != null) {
OrderService.edit(uo.order, q);
}else {
} else {
OrderService.make(app.user, q, uo.product, did);
}
}
throw Ok("/contract/order/"+c.id, t._("Your order has been updated"));
throw Ok("/contract/order/" + c.id, t._("Your order has been updated"));
}
view.c = view.contract = c;
view.userOrders = userOrders;
}
/**
* Make an order by contract ( standard mode ) + payment process
*/
@tpl("contract/orderAndPay.mtt")
function doOrderAndPay(c:db.Contract ) {
//checks
if (!app.user.amap.hasPayments()) throw Redirect("/contract/order/" + c.id);
if (app.user.amap.hasShopMode()) throw Redirect("/");
if (!c.isUserOrderAvailable()) throw Error("/", t._("This contract is not opened for orders"));
function doOrderAndPay(c:db.Contract) {
// checks
if (!app.user.amap.hasPayments())
throw Redirect("/contract/order/" + c.id);
if (app.user.amap.hasShopMode())
throw Redirect("/");
if (!c.isUserOrderAvailable())
throw Error("/", t._("This contract is not opened for orders"));
var distributions = [];
/* If its a varying contract, we display a column by distribution*/
@@ -448,64 +444,64 @@ class Contract extends Controller
distributions = db.Distribution.getOpenToOrdersDeliveries(c);
}
//list of distribs with a list of product and optionnaly an order
var userOrders = new Array< {distrib:db.Distribution,datas:Array<{order:db.UserContract,product:db.Product}>} >();
// list of distribs with a list of product and optionnaly an order
var userOrders = new Array<{distrib:db.Distribution, datas:Array<{order:db.UserContract, product:db.Product}>}>();
var products = c.getProducts();
for ( d in distributions){
for (d in distributions) {
var datas = [];
for ( p in products) {
var ua = { order:null, product:p };
for (p in products) {
var ua = {order: null, product: p};
var order : db.UserContract = null;
var order:db.UserContract = null;
if (c.type == db.Contract.TYPE_VARORDER) {
order = db.UserContract.manager.select($user == app.user && $productId == p.id && $distributionId==d.id, true);
}else {
order = db.UserContract.manager.select($user == app.user && $productId == p.id && $distributionId == d.id, true);
} else {
order = db.UserContract.manager.select($user == app.user && $productId == p.id, true);
}
if (order != null) ua.order = order;
if (order != null)
ua.order = order;
datas.push(ua);
}
userOrders.push({distrib:d,datas:datas});
userOrders.push({distrib: d, datas: datas});
}
//form check
// form check
if (checkToken()) {
//get distrib if needed
// get distrib if needed
var distrib = null;
if (c.type == db.Contract.TYPE_VARORDER) {
distrib = db.Distribution.manager.get(Std.parseInt(app.params.get("distribution")), false);
}
var orders : OrderInSession = {products:[],userId:app.user.id,total:0};
var orders:OrderInSession = {products: [], userId: app.user.id, total: 0};
for (k in app.params.keys()) {
if (k.substr(0, 1) != "d") continue;
if (k.substr(0, 1) != "d")
continue;
var qt = app.params.get(k);
if (qt == "") continue;
if (qt == "")
continue;
var pid = null;
var did = null;
try{
try {
pid = Std.parseInt(k.split("-")[1].substr(1));
did = Std.parseInt(k.split("-")[0].substr(1));
}catch (e:Dynamic){
trace("unable to parse key "+k);
} catch (e:Dynamic) {
trace("unable to parse key " + k);
}
//find related element in userOrders
// find related element in userOrders
var uo = null;
for ( x in userOrders){
if (x.distrib!=null && x.distrib.id != did) {
for (x in userOrders) {
if (x.distrib != null && x.distrib.id != did) {
continue;
}else{
for (a in x.datas){
if (a.product.id == pid){
} else {
for (a in x.datas) {
if (a.product.id == pid) {
uo = a;
break;
}
@@ -513,58 +509,53 @@ class Contract extends Controller
}
}
if (uo == null) throw t._("Could not find the product ::produ:: and delivery ::deliv::", {produ:pid, deliv:did});
if (uo == null)
throw t._("Could not find the product ::produ:: and delivery ::deliv::", {produ: pid, deliv: did});
//quantity
// quantity
var q = 0.0;
if (uo.product.hasFloatQt ) {
if (uo.product.hasFloatQt) {
var param = StringTools.replace(qt, ",", ".");
q = Std.parseFloat(param);
}else {
} else {
q = Std.parseInt(qt);
}
orders.products.push({productId:pid, quantity:q, distributionId:did});
orders.products.push({productId: pid, quantity: q, distributionId: did});
var p = db.Product.manager.get(pid, false);
orders.total += p.getPrice() * q;
}
App.current.session.data.order = orders;
//Go to payments page
// Go to payments page
if (c.type == db.Contract.TYPE_CONSTORDERS) {
throw Ok("/contract/order/"+c.id, t._("Your CSA order has been saved"));
}else{
throw Ok("/contract/order/" + c.id, t._("Your CSA order has been saved"));
} else {
throw Ok("/transaction/pay/", t._("In order to save your order, please choose a means of payment."));
}
}
view.c = view.contract = c;
view.userOrders = userOrders;
}
/**
* A user edit an order for a multidistrib.
*/
@tpl("contract/orderByDate.mtt")
function doEditOrderByDate(date:Date) {
if (app.user.amap.hasPayments()) {
//when payments are active, the user cannot modify his order
// when payments are active, the user cannot modify his order
throw Redirect("/");
}
// cannot edit order if date is in the past
if (Date.now().getTime() > date.getTime()) {
var msg = t._("This delivery has already taken place, you can no longer modify the order.");
if (app.user.isContractManager()) msg += t._("<br/>As the manager of the contract you can modify the order from this page: <a href='/contractAdmin'>Management of contracts</a>");
if (app.user.isContractManager())
msg += t._("<br/>As the manager of the contract you can modify the order from this page: <a href='/contractAdmin'>Management of contracts</a>");
throw Error("/contract", msg);
}
@@ -574,39 +565,40 @@ class Contract extends Controller
var d2 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59);
var cids = Lambda.map(app.user.amap.getActiveContracts(true), function(c) return c.id);
var distribs = db.Distribution.manager.search(($contractId in cids) && $date >= d1 && $date <=d2 , false);
var orders = db.UserContract.manager.search($userId==app.user.id && $distributionId in Lambda.map(distribs,function(d)return d.id) );
var distribs = db.Distribution.manager.search(($contractId in cids) && $date >= d1 && $date <= d2, false);
var orders = db.UserContract.manager.search($userId == app.user.id
&& $distributionId in Lambda.map(distribs, function(d) return d.id));
view.orders = service.OrderService.prepare(orders);
view.date = date;
//form check
// form check
if (checkToken()) {
var orders_out = [];
for (k in app.params.keys()) {
var param = app.params.get(k);
if (k.substr(0, "product".length) == "product") {
//trouve le produit dans userOrders
// trouve le produit dans userOrders
var pid = Std.parseInt(k.substr("product".length));
var order = Lambda.find(orders, function(uo) return uo.product.id == pid);
if (order == null) throw t._("Error, could not find the order");
if (order == null)
throw t._("Error, could not find the order");
var q = 0.0;
if (order.product.hasFloatQt ) {
if (order.product.hasFloatQt) {
param = StringTools.replace(param, ",", ".");
q = Std.parseFloat(param);
}else {
} else {
q = Std.parseInt(param);
}
var quantity = Math.abs( q==null?0:q );
var quantity = Math.abs(q == null ? 0 : q);
if ( order.distribution.canOrderNow() ) {
//met a jour la commande
if (order.distribution.canOrderNow()) {
// met a jour la commande
var o = OrderService.edit(order, quantity);
if(o!=null) orders_out.push( o );
if (o != null)
orders_out.push(o);
}
}
}
-2
View File
@@ -19,8 +19,6 @@ class ContractAdmin extends Controller {
if (!app.user.isContractManager())
throw Error("/", t._("You don't have the authorization to manage contracts"));
view.nav = ["contractadmin"];
App.log("first message");
}
public function sendNav(c) {
+2 -2
View File
@@ -77,7 +77,7 @@ class Cron extends Controller {
}
var m = new Mail();
m.setSender(App.config.get("default_email"), "Cagette.net");
m.setSender(App.config.get("default_email"), t._("Cagette.net"));
m.addRecipient(App.config.get("webmaster_email"));
m.setSubject(App.config.NAME + " Errors");
m.setHtmlBody(app.processTemplate("mail/message.mtt", {text: report.toString()}));
@@ -306,7 +306,7 @@ class Cron extends Controller {
try {
var m = new Mail();
m.setSender(App.config.get("default_email"), "Cagette.net");
m.setSender(App.config.get("default_email"), t._("Cagette.net"));
if (group.contact != null)
m.setReplyTo(group.contact.email, group.name);
m.addRecipient(u.user.email, u.user.getName());
+1 -1
View File
@@ -54,7 +54,7 @@ class Group extends controller.Controller
form.addElement(new StringInput("userLastName", t._("Your lastname") ,"",true));
form.addElement(new StringInput("userEmail", t._("Your e-mail"), "", true));
}
form.addElement(new sugoi.form.elements.TextArea("msg", t._("Leave a message")));
form.addElement(new sugoi.form.elements.TextArea("msg", t._("Leave a message"),"",false));
if (form.isValid()){
try{
+39 -43
View File
@@ -1,4 +1,5 @@
package controller;
import db.Distribution;
import db.UserContract;
import haxe.Json;
@@ -9,12 +10,10 @@ import Common;
import tools.ArrayTool;
class Main extends Controller {
/**
* public pages
*/
function doGroup(d:haxe.web.Dispatch){
function doGroup(d:haxe.web.Dispatch) {
d.dispatch(new controller.Group());
}
@@ -26,49 +25,52 @@ class Main extends Controller {
view.category = 'home';
var group = app.getCurrentGroup();
if ( app.user!=null && group == null) {
if (app.user != null && group == null) {
throw Redirect("/user/choose");
}else if (app.user == null && (group==null || group.regOption!=db.Amap.RegOption.Open) ) {
} else if (app.user == null && (group == null || group.regOption != db.Amap.RegOption.Open)) {
if (db.Amap.manager.get(1, false) != null)
throw Redirect("/group/1");
else
throw Redirect("/user/login");
}
view.amap = group;
//contract with open orders
// contract with open orders
var openContracts = Lambda.filter(group.getActiveContracts(), function(c) return c.isUserOrderAvailable());
view.openContracts = openContracts;
//register to become "distributor"
view.contractsWithDistributors = app.user==null ? [] : Lambda.filter(app.user.amap.getActiveContracts(), function(c) return c.distributorNum > 0);
// register to become "distributor"
view.contractsWithDistributors = app.user == null ? [] : Lambda.filter(app.user.amap.getActiveContracts(), function(c) return c.distributorNum > 0);
//freshly created group
// freshly created group
view.newGroup = app.session.data.newGroup == true;
var n = Date.now();
var now = new Date(n.getFullYear(), n.getMonth(), n.getDate(), 0, 0, 0);
var in3Month = DateTools.delta(now, 1000.0 * 60 * 60 * 24 * 30 * 3);
var distribs = MultiDistrib.getFromTimeRange(group,now,in3Month);
var distribs = MultiDistrib.getFromTimeRange(group, now, in3Month);
view.distribs = distribs;
//view functions
// view functions
view.getWhosTurn = function(orderId:Int, distrib:Distribution) {
return db.UserContract.manager.get(orderId, false).getWhosTurn(distrib);
}
//event for additionnal blocks on home page
// event for additionnal blocks on home page
var e = Blocks([], "home");
app.event(e);
view.blocks = e.getParameters()[0];
//message if phone is required
if(app.user!=null && app.user.amap.flags.has(db.Amap.AmapFlags.PhoneRequired) && app.user.phone==null){
app.session.addMessage(t._("Members of this group should provide a phone number. <a href='/account/edit'>Please click here to update your account</a>."),true);
// message if phone is required
if (app.user != null && app.user.amap.flags.has(db.Amap.AmapFlags.PhoneRequired) && app.user.phone == null) {
app.session.addMessage(t._("Members of this group should provide a phone number. <a href='/account/edit'>Please click here to update your account</a>."),
true);
}
}
}
//login and stuff
// login and stuff
function doUser(d:Dispatch) {
d.dispatch(new controller.User());
}
@@ -81,26 +83,19 @@ class Main extends Controller {
* JSON REST API Entry point
*/
function doApi(d:Dispatch) {
try {
d.dispatch(new controller.Api());
}catch (e:tink.core.Error){
//manage tink Errors (service errors)
} catch (e:tink.core.Error) {
// manage tink Errors (service errors)
sugoi.Web.setReturnCode(e.code);
Sys.print(Json.stringify( {error:{code:e.code,message:e.message,stack:e.exceptionStack}} ));
}catch (e:Dynamic){
//manage other errors
Sys.print(Json.stringify({error: {code: e.code, message: e.message, stack: e.exceptionStack}}));
} catch (e:Dynamic) {
// manage other errors
sugoi.Web.setReturnCode(500);
var stack = if ( App.config.DEBUG ) haxe.CallStack.toString(haxe.CallStack.exceptionStack()) else "";
var stack = if (App.config.DEBUG) haxe.CallStack.toString(haxe.CallStack.exceptionStack()) else "";
App.current.logError(e, stack);
Sys.print(Json.stringify( {error:{code:500,message : Std.string(e), stack:stack }} ));
Sys.print(Json.stringify({error: {code: 500, message: Std.string(e), stack: stack}}));
}
}
@tpl("cssDemo.mtt")
@@ -113,15 +108,13 @@ class Main extends Controller {
d.dispatch(new controller.Install());
}
function doP(d:Dispatch) {
/*
* Invalid array access
Stack (ADMIN|DEBUG)
Stack (ADMIN|DEBUG)
Called from C:\HaxeToolkit\haxe\std/haxe/web/Dispatch.hx line 463
Called from controller/Main.hx line 117
Called from C:\HaxeToolkit\haxe\std/haxe/web/Dispatch.hx line 463
Called from controller/Main.hx line 117
*
var plugin = d.parts.shift();
for ( p in App.plugins) {
@@ -139,7 +132,6 @@ Called from controller/Main.hx line 117
d.dispatch(new controller.Plugin());
}
@logged
function doMember(d:Dispatch) {
view.category = 'members';
@@ -148,10 +140,17 @@ Called from controller/Main.hx line 117
@logged
function doStats(d:Dispatch) {
App.log('doStats called');
view.category = 'stats';
d.dispatch(new Stats());
}
function doMentions(d:Dispatch) {
App.log('do Mentions called');
view.category = 'mentions';
d.dispatch(new MentionsLegales());
}
@logged
function doAccount(d:Dispatch) {
view.category = 'account';
@@ -237,8 +236,7 @@ Called from controller/Main.hx line 117
}
@logged
function doValidate(date:Date, place:db.Place, user:db.User, d:haxe.web.Dispatch){
function doValidate(date:Date, place:db.Place, user:db.User, d:haxe.web.Dispatch) {
var v = new controller.Validate();
v.date = date;
v.place = place;
@@ -253,9 +251,7 @@ Called from controller/Main.hx line 117
@admin
function doDb(d:Dispatch) {
d.parts = []; //disable haxe.web.Dispatch
d.parts = []; // disable haxe.web.Dispatch
sys.db.Admin.handler();
}
}
+278 -274
View File
@@ -1,4 +1,5 @@
package controller;
import Common;
import haxe.Utf8;
import sugoi.form.Form;
@@ -6,111 +7,114 @@ import sugoi.form.elements.Selectbox;
import sugoi.form.validators.EmailValidator;
import sugoi.tools.Utils;
class Member extends Controller
{
public function new()
{
class Member extends Controller {
public function new() {
super();
if (!app.user.canAccessMembership()) throw Redirect("/");
if (!app.user.canAccessMembership())
throw Redirect("/");
}
@logged
@tpl('member/default.mtt')
function doDefault(?args: { ?search:String, ?select:String } ) {
function doDefault(?args:{?search:String, ?select:String}) {
checkToken();
var browse:Int->Int->List<Dynamic>;
var uids = db.UserAmap.manager.search($amap == app.user.getAmap(), false);
var uids = Lambda.map(uids, function(ua) return ua.user.id);
if (args != null && args.search != null) {
//SEARCH
// SEARCH
browse = function(index:Int, limit:Int) {
var search = "%"+StringTools.trim(args.search)+"%";
return db.User.manager.search(
($lastName.like(search) ||
$lastName2.like(search) ||
$address1.like(search) ||
$address2.like(search) ||
$firstName.like(search) ||
$firstName2.like(search)
) && $id in uids , { orderBy:-id }, false);
var search = "%" + StringTools.trim(args.search) + "%";
return db.User.manager.search(($lastName.like(search) || $lastName2.like(search) || $address1.like(search) || $address2.like(search)
|| $firstName.like(search) || $firstName2.like(search))
&& $id in uids,
{
orderBy: -id
}, false);
}
view.search = args.search;
} else if (args != null && args.select != null) {
// SELECTION
}else if(args!=null && args.select!=null){
//SELECTION
switch(args.select) {
switch (args.select) {
case "nocontract":
if (app.params.exists("csv")) {
sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_NoContracts()), ["firstName", "lastName", "email"], t._("Without contracts"));
sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_NoContracts()), ["firstName", "lastName", "email"],
t._("Without contracts"));
return;
}else {
browse = function(index:Int, limit:Int) { return db.User.getUsers_NoContracts(index, limit); }
} else {
browse = function(index:Int, limit:Int) {
return db.User.getUsers_NoContracts(index, limit);
}
}
case "contract":
if (app.params.exists("csv")) {
sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_Contracts()), ["firstName", "lastName", "email"], t._("With orders"));
sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_Contracts()), ["firstName", "lastName", "email"],
t._("With orders"));
return;
}else {
browse = function(index:Int, limit:Int) { return db.User.getUsers_Contracts(index, limit); }
} else {
browse = function(index:Int, limit:Int) {
return db.User.getUsers_Contracts(index, limit);
}
}
case "nomembership" :
case "nomembership":
if (app.params.exists("csv")) {
sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_NoMembership()), ["firstName", "lastName", "email"], t._("Memberships to be renewed"));
sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_NoMembership()), ["firstName", "lastName", "email"],
t._("Memberships to be renewed"));
return;
}else {
browse = function(index:Int, limit:Int) { return db.User.getUsers_NoMembership(index, limit); }
} else {
browse = function(index:Int, limit:Int) {
return db.User.getUsers_NoMembership(index, limit);
}
case "newusers" :
}
case "newusers":
if (app.params.exists("csv")) {
sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_NewUsers()), ["firstName", "lastName", "email"], t._("Never connected"));
sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_NewUsers()), ["firstName", "lastName", "email"],
t._("Never connected"));
return;
}else {
browse = function(index:Int, limit:Int) { return db.User.getUsers_NewUsers(index, limit); }
} else {
browse = function(index:Int, limit:Int) {
return db.User.getUsers_NewUsers(index, limit);
}
}
default:
throw t._("Unknown selection");
}
view.select = args.select;
}else {
} else {
if (app.params.exists("csv")) {
var headers = ["firstName", "lastName", "email","phone", "firstName2", "lastName2","email2","phone2", "address1","address2","zipCode","city"];
sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.manager.search( $id in uids, {orderBy:lastName}, false)), headers, t._("Members"));
var headers = [
"firstName", "lastName", "email", "phone", "firstName2", "lastName2", "email2", "phone2", "address1", "address2", "zipCode", "city"
];
sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.manager.search($id in uids, {orderBy: lastName}, false)), headers,
t._("Members"));
return;
}else {
//default display
} else {
// default display
browse = function(index:Int, limit:Int) {
return db.User.manager.search( $id in uids, { limit:[index,limit], orderBy:lastName }, false);
return db.User.manager.search($id in uids, {limit: [index, limit], orderBy: lastName}, false);
}
}
}
var count = uids.length;
var rb = new sugoi.tools.ResultsBrowser(count, (args.select!=null||args.search!=null)?1000:10, browse);
var rb = new sugoi.tools.ResultsBrowser(count, (args.select != null || args.search != null) ? 1000 : 10, browse);
view.members = rb;
if (args.select == null || args.select != "newusers") {
//count new users
// count new users
view.newUsers = db.User.getUsers_NewUsers().length;
}
view.waitingList = db.WaitingList.manager.count($group == app.user.amap);
}
/**
* Move to waiting list
*/
function doMovetowl(u:db.User){
function doMovetowl(u:db.User) {
var ua = db.UserAmap.get(u, app.user.amap, true);
ua.delete();
@@ -119,93 +123,84 @@ class Member extends Controller
wl.group = app.user.amap;
wl.insert();
throw Ok("/member", u.getName() +" "+ t._("is now on waiting list.") );
throw Ok("/member", u.getName() + " " + t._("is now on waiting list."));
}
/**
* Display waiting list
*/
@tpl('member/waiting.mtt')
function doWaiting(?args:{?add:db.User,?remove:db.User}){
if (args != null){
if (args.add != null){
service.WaitingListService.approveRequest(args.add,app.user.amap);
throw Ok("/member/waiting", t._("Membership request accepted") );
}else if (args.remove != null){
service.WaitingListService.cancelRequest(args.remove,app.user.amap);
throw Ok("/member/waiting", t._("Membership request refused") );
function doWaiting(?args:{?add:db.User, ?remove:db.User}) {
if (args != null) {
if (args.add != null) {
service.WaitingListService.approveRequest(args.add, app.user.amap);
throw Ok("/member/waiting", t._("Membership request accepted"));
} else if (args.remove != null) {
service.WaitingListService.cancelRequest(args.remove, app.user.amap);
throw Ok("/member/waiting", t._("Membership request refused"));
}
}
view.waitingList = db.WaitingList.manager.search($group == app.user.amap,{orderBy:-date});
view.waitingList = db.WaitingList.manager.search($group == app.user.amap, {orderBy: -date});
}
/**
* Send an invitation to a new member
*/
function doInviteMember(u:db.User){
if (checkToken() ) {
function doInviteMember(u:db.User) {
if (checkToken()) {
u.sendInvitation(app.user.amap);
throw Ok('/member/view/'+u.id, t._("Invitation sent.") );
throw Ok('/member/view/' + u.id, t._("Invitation sent."));
}
}
/**
* Invite 'never logged' users
*/
function doInvite() {
if (checkToken()) {
var users = db.User.getUsers_NewUsers();
try{
for ( u in users) {
try {
for (u in users) {
u.sendInvitation(app.user.amap);
Sys.sleep(0.2);
}
}catch (e:String){
if (e.indexOf("curl") >-1) {
} catch (e:String) {
if (e.indexOf("curl") > -1) {
App.current.logError(e, haxe.CallStack.toString(haxe.CallStack.exceptionStack()));
throw Error("/member", t._("An error occurred while sending emails, please retry"));
}
}
throw Ok('/member', t._("Congratulations, you just sent <b>::userLength::</b> invitations", {userLength:users.length}));
throw Ok('/member', t._("Congratulations, you just sent <b>::userLength::</b> invitations", {userLength: users.length}));
}
}
@tpl("member/view.mtt")
function doView(member:db.User) {
view.member = member;
var userAmap = db.UserAmap.get(member, app.user.amap);
if (userAmap == null) throw Error("/member", t._("This person does not belong to your group"));
if (userAmap == null)
throw Error("/member", t._("This person does not belong to your group"));
view.userAmap = userAmap;
view.canLoginAs = (db.UserAmap.manager.count($userId == member.id) == 1 && app.user.isAmapManager()) || app.user.isAdmin();
//orders
var row = { constOrders:new Array<UserOrder>(), varOrders:new Map<String,Array<UserOrder>>() };
// orders
var row = {constOrders: new Array<UserOrder>(), varOrders: new Map<String, Array<UserOrder>>()};
//commandes fixes
var contracts = db.Contract.manager.search($type == db.Contract.TYPE_CONSTORDERS && $amap == app.user.amap && $endDate > DateTools.delta(Date.now(),-1000.0*60*60*24*30), false);
// commandes fixes
var contracts = db.Contract.manager.search($type == db.Contract.TYPE_CONSTORDERS
&& $amap == app.user.amap
&& $endDate > DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24 * 30), false);
var orders = member.getOrdersFromContracts(contracts);
row.constOrders = service.OrderService.prepare(orders);
//commandes variables groupées par date de distrib
var contracts = db.Contract.manager.search($type == db.Contract.TYPE_VARORDER && $amap == app.user.amap && $endDate > DateTools.delta(Date.now(),-1000.0*60*60*24*30), false);
var distribs = new Map<String,List<db.UserContract>>();
// commandes variables groupées par date de distrib
var contracts = db.Contract.manager.search($type == db.Contract.TYPE_VARORDER
&& $amap == app.user.amap
&& $endDate > DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24 * 30), false);
var distribs = new Map<String, List<db.UserContract>>();
for (c in contracts) {
var ds = c.getDistribs();
for (d in ds) {
@@ -214,25 +209,23 @@ class Member extends Controller
if (orders.length > 0) {
if (!distribs.exists(k)) {
distribs.set(k, orders);
}else {
} else {
var v = distribs.get(k);
for ( o in orders ) v.add(o);
for (o in orders)
v.add(o);
distribs.set(k, v);
}
}
}
}
for ( k in distribs.keys()){
for (k in distribs.keys()) {
var d = distribs.get(k);
var d2 = service.OrderService.prepare(d);
row.varOrders.set(k,d2);
row.varOrders.set(k, d2);
}
view.userContracts = row;
checkToken(); //to insert a token in tpl
checkToken(); // to insert a token in tpl
}
/**
@@ -241,35 +234,42 @@ class Member extends Controller
* @param amap
*/
function doLoginas(member:db.User, amap:db.Amap) {
if (!app.user.isAdmin()){
if (!app.user.isAmapManager()) return;
if (member.isAdmin()) return;
if ( db.UserAmap.manager.count($userId == member.id) > 1 ) return;
if (!app.user.isAdmin()) {
if (!app.user.isAmapManager())
return;
if (member.isAdmin())
return;
if (db.UserAmap.manager.count($userId == member.id) > 1)
return;
}
App.current.session.setUser(member);
App.current.session.data.amapId = amap.id;
throw Redirect("/member/view/" + member.id );
throw Redirect("/member/view/" + member.id);
}
@tpl('member/lastMessages.mtt')
function doLastMessages(member:db.User){
var out = new Array<{date:Date,subject:String,success:String,failure:String}>();
function doLastMessages(member:db.User) {
var out = new Array<{
date:Date,
subject:String,
success:String,
failure:String
}>();
var threeMonth = DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24 * 30.5 * 3);
for ( m in sugoi.db.BufferedMail.manager.search($remoteId == app.user.amap.id && $cdate > threeMonth, {limit:10, orderBy:-cdate})){
var status : sugoi.mail.IMailer.MailerResult = m.status;
if ( status!=null && status.get(member.email)!=null ){
for (m in sugoi.db.BufferedMail.manager.search($remoteId == app.user.amap.id && $cdate > threeMonth, {limit: 10, orderBy: -cdate})) {
var status:sugoi.mail.IMailer.MailerResult = m.status;
if (status != null && status.get(member.email) != null) {
var r = m.getMailerResultMessage(member.email);
out.push( {date:m.cdate,subject:m.title,success:r.success,failure:r.failure} );
out.push({
date: m.cdate,
subject: m.title,
success: r.success,
failure: r.failure
});
}
}
view.emails = out;
}
@@ -279,127 +279,134 @@ class Member extends Controller
*/
@tpl('form.mtt')
function doEdit(member:db.User) {
if (member.isAdmin() && !app.user.isAdmin()) throw Error("/", t._("You cannot modify the account of an administrator"));
if (member.isAdmin() && !app.user.isAdmin())
throw Error("/", t._("You cannot modify the account of an administrator"));
var form = sugoi.form.Form.fromSpod(member);
//cleaning
form.removeElement( form.getElement("rights") );
form.removeElement( form.getElement("lang") );
form.removeElement( form.getElement("ldate") );
form.removeElement( form.getElement("apiKey") );
// cleaning
form.removeElement(form.getElement("rights"));
form.removeElement(form.getElement("lang"));
form.removeElement(form.getElement("ldate"));
form.removeElement(form.getElement("apiKey"));
var isReg = member.isFullyRegistred();
var groupNum = db.UserAmap.manager.count($userId == member.id);
//an administrator can modify a user's email only if he's not member elsewhere
if (groupNum > 1){
// an administrator can modify a user's email only if he's not member elsewhere
if (groupNum > 1) {
form.removeElementByName("email");
form.removeElementByName("email2");
app.session.addMessage(t._("For security reasons, you cannot modify the e-mail of this person because this person is a member of more than 1 group."));
}
//an administrator can modify a user's pass only if he's a not registred user.
if (!isReg){
// an administrator can modify a user's pass only if he's a not registred user.
if (!isReg) {
app.session.addMessage(t._("This person did not define yet a password. You are exceptionaly authorized to do it. Please don't forget to tell this person."));
form.getElement("pass").required = false;
}else{
form.removeElement( form.getElement("pass") );
} else {
form.removeElement(form.getElement("pass"));
}
if (form.checkToken()) {
if (app.user.amap.flags.has(db.Amap.AmapFlags.PhoneRequired) && form.getValueOf("phone") == null ){
throw Error("/member/edit/"+member.id, t._("Phone number is required in this group."));
if (app.user.amap.flags.has(db.Amap.AmapFlags.PhoneRequired) && form.getValueOf("phone") == null) {
throw Error("/member/edit/" + member.id, t._("Phone number is required in this group."));
}
form.toSpod(member);
//check that the given emails are not already used elsewhere
var sim = db.User.getSameEmail(member.email,member.email2);
for ( s in sim) {
if (s.id == member.id) sim.remove(s);
// check that the given emails are not already used elsewhere
var sim = db.User.getSameEmail(member.email, member.email2);
for (s in sim) {
if (s.id == member.id)
sim.remove(s);
}
if (sim.length > 0) {
//Let's merge the 2 users if it has no orders.
// Let's merge the 2 users if it has no orders.
var id = sim.first().id;
if (db.UserContract.manager.search( $userId == id || $userId2 == id , false).length == 0) {
//merge
member.merge( sim.first() );
if (db.UserContract.manager.search($userId == id || $userId2 == id, false).length == 0) {
// merge
member.merge(sim.first());
app.session.addMessage(t._("This e-mail was used by another user account. As this user account was not used, it has been merged into the current user account."));
} else {
var str = t._("Warning, this e-mail or this name already exists for another account : ");
str += Lambda.map(sim, function(u) return "<a href='/member/view/" + u.id + "'>" + u.getCoupleName() + "</a>").join(",");
str += " "+t._("These accounts can't be merged because the second account has orders");
str += " " + t._("These accounts can't be merged because the second account has orders");
throw Error("/member/edit/" + member.id, str);
}
}
if (!isReg) member.setPass(form.getValueOf("pass"));
if (!isReg)
member.setPass(form.getValueOf("pass"));
member.update();
if (!App.config.DEBUG && groupNum == 1) {
//warn the user that his email has been updated
// warn the user that his email has been updated
if (form.getValueOf("email") != member.email) {
var m = new sugoi.mail.Mail();
m.setSender(App.config.get("default_email"), t._("Cagette.net"));
m.addRecipient(member.email);
m.setSubject(t._("Change your e-mail in your account Cagette.net"));
m.setHtmlBody( app.processTemplate("mail/message.mtt", { text:app.user.getName() + t._(" just modified your e-mail in your account Cagette.net.<br/>Your e-mail is now:")+form.getValueOf("email") } ) );
m.setHtmlBody(app.processTemplate("mail/message.mtt",
{text: app.user.getName()
+ t._(" just modified your e-mail in your account Cagette.net.<br/>Your e-mail is now:")
+ form.getValueOf("email")}));
App.sendMail(m);
}
if (form.getValueOf("email2") != member.email2 && member.email2!=null) {
if (form.getValueOf("email2") != member.email2 && member.email2 != null) {
var m = new sugoi.mail.Mail();
m.setSender(App.config.get("default_email"),"Cagette.net");
m.setSender(App.config.get("default_email"), t._("Cagette.net"));
m.addRecipient(member.email2);
m.setSubject(t._("Change the e-mail of your account Cagette.net"));
m.setHtmlBody( app.processTemplate("mail/message.mtt", { text:app.user.getName() +t._(" just modified your e-mail in your account Cagette.net.<br/>Your e-mail is now:")+form.getValueOf("email2") } ) );
m.setHtmlBody(app.processTemplate("mail/message.mtt",
{text: app.user.getName()
+ t._(" just modified your e-mail in your account Cagette.net.<br/>Your e-mail is now:")
+ form.getValueOf("email2")}));
App.sendMail(m);
}
}
throw Ok('/member/view/'+member.id, t._("This member has beed updated"));
throw Ok('/member/view/' + member.id, t._("This member has beed updated"));
}
view.form = form;
}
/**
* Remove a user from this group
*/
function doDelete(user:db.User,?args:{confirm:Bool,token:String}) {
function doDelete(user:db.User, ?args:{confirm:Bool, token:String}) {
if (checkToken()) {
if (!app.user.canAccessMembership()) throw t._("You cannot do that.");
if (user.id == app.user.id) throw Error("/member/view/" + user.id, t._("You cannot delete yourself."));
if ( Lambda.count(user.getOrders(app.user.amap),function(x) return x.quantity>0) > 0 && !args.confirm) {
throw Error("/member/view/"+user.id, t._("Warning, this account has orders. <a class='btn btn-default btn-xs' href='/member/delete/::userid::?token=::argstoken::&confirm=1'>Remove anyway</a>", {userid:user.id, argstoken:args.token}));
if (!app.user.canAccessMembership())
throw t._("You cannot do that.");
if (user.id == app.user.id)
throw Error("/member/view/" + user.id, t._("You cannot delete yourself."));
if (Lambda.count(user.getOrders(app.user.amap), function(x) return x.quantity > 0) > 0 && !args.confirm) {
throw Error("/member/view/" + user.id,
t._("Warning, this account has orders. <a class='btn btn-default btn-xs' href='/member/delete/::userid::?token=::argstoken::&confirm=1'>Remove anyway</a>",
{
userid: user.id,
argstoken: args.token
}));
}
var ua = db.UserAmap.get(user, app.user.amap, true);
if (ua != null) {
ua.delete();
throw Ok("/member", t._("::user:: has been removed from your group",{user:user.getName()}));
}else {
throw Error("/member", t._("This person does not belong to \"::amapname::\"", {amapname:app.user.amap.name}));
throw Ok("/member", t._("::user:: has been removed from your group", {user: user.getName()}));
} else {
throw Error("/member", t._("This person does not belong to \"::amapname::\"", {amapname: app.user.amap.name}));
}
}else {
throw Redirect("/member/view/"+user.id);
} else {
throw Redirect("/member/view/" + user.id);
}
}
@tpl('form.mtt')
function doMerge(user:db.User) {
if (!app.user.canAccessMembership()) throw Error("/","Action interdite");
if (!app.user.canAccessMembership())
throw Error("/", "Action interdite");
view.title = t._("Merge an account with another one");
view.text = t._("This action allows you to merge two accounts (when you have duplicates in the database for example).<br/>Contracts of account 2 will be moved to account 1, and account 2 will be deleted. Warning, it is not possible to cancel this action.");
@@ -407,80 +414,77 @@ class Member extends Controller
var form = new Form("merge");
var members = app.user.amap.getMembers();
var members = Lambda.array(Lambda.map(members, function(x) return { key:Std.string(x.id), value:x.getName() } ));
var members = Lambda.array(Lambda.map(members, function(x) return {key: Std.string(x.id), value: x.getName()}));
var mlist = new Selectbox("member1", t._("Account 1"), members, Std.string(user.id));
form.addElement( mlist );
form.addElement(mlist);
var mlist = new Selectbox("member2", t._("Account 2"), members);
form.addElement( mlist );
form.addElement(mlist);
if (form.checkToken()) {
var m1 = Std.parseInt(form.getElement("member1").value);
var m2 = Std.parseInt(form.getElement("member2").value);
var m1 = db.User.manager.get(m1,true);
var m2 = db.User.manager.get(m2,true);
var m1 = db.User.manager.get(m1, true);
var m2 = db.User.manager.get(m2, true);
//if (m1.amapId != m2.amapId) throw "ils ne sont pas de la même amap !";
// if (m1.amapId != m2.amapId) throw "ils ne sont pas de la même amap !";
//on prend tout à m2 pour donner à m1
//change usercontracts
var contracts = db.UserContract.manager.search($user==m2 || $user2==m2,true);
// on prend tout à m2 pour donner à m1
// change usercontracts
var contracts = db.UserContract.manager.search($user == m2 || $user2 == m2, true);
for (c in contracts) {
if (c.user.id == m2.id) c.user = m1;
if (c.user2!=null && c.user2.id == m2.id) c.user2 = m1;
if (c.user.id == m2.id)
c.user = m1;
if (c.user2 != null && c.user2.id == m2.id)
c.user2 = m1;
c.update();
}
//group memberships
// group memberships
var adh = db.UserAmap.manager.search($user == m2, true);
for ( a in adh) {
for (a in adh) {
a.user = m1;
a.update();
}
//change contacts
var contacts = db.Contract.manager.search($contact==m2,true);
// change contacts
var contacts = db.Contract.manager.search($contact == m2, true);
for (c in contacts) {
c.contact = m1;
c.update();
}
//if (m2.amap.contact == m2) {
//m1.amap.lock();
//m1.amap.contact = m1;
//m1.amap.update();
//}
// if (m2.amap.contact == m2) {
// m1.amap.lock();
// m1.amap.contact = m1;
// m1.amap.update();
// }
m2.delete();
throw Ok("/member/view/" + m1.id, t._("Both accounts have been merged"));
}
view.form = form;
}
@tpl('member/import.mtt')
function doImport(?args: { confirm:Bool } ) {
function doImport(?args:{confirm:Bool}) {
var step = 1;
var request = Utils.getMultipart(1024 * 1024 * 4); //4mb
var request = Utils.getMultipart(1024 * 1024 * 4); // 4mb
//on recupere le contenu de l'upload
// on recupere le contenu de l'upload
var data = request.get("file");
if ( data != null) {
if (data != null) {
var csv = new sugoi.tools.Csv();
csv.setHeaders([t._("Firstname"), t._("Lastname"), t._("E-mail"), t._("Mobile phone"), t._("Partner's firstname"), t._("Partner's lastname"), t._("Partner's e-mail"), t._("Partner's Mobile phone"), t._("Address 1"), t._("Address 2"), t._("Post code"), t._("City")]);
csv.setHeaders([
t._("Firstname"), t._("Lastname"), t._("E-mail"), t._("Mobile phone"), t._("Partner's firstname"), t._("Partner's lastname"),
t._("Partner's e-mail"), t._("Partner's Mobile phone"), t._("Address 1"), t._("Address 2"), t._("Post code"), t._("City")]);
//utf8 encode if needed
try{
if (!haxe.Utf8.validate(data)){
// utf8 encode if needed
try {
if (!haxe.Utf8.validate(data)) {
data = haxe.Utf8.encode(data);
}
}catch (e:Dynamic){ }
} catch (e:Dynamic) {}
var unregistred = csv.importDatas(data);
/*var checkEmail = function(email){
@@ -489,55 +493,57 @@ class Member extends Controller
}
}*/
//cleaning
for ( user in unregistred.copy() ) {
//check nom+prenom
// cleaning
for (user in unregistred.copy()) {
// check nom+prenom
if (user[0] == null || user[1] == null) {
throw Error("/member/import", t._("You must fill the name and the firstname of the person. This line is incomplete: ") + user);
}
if (user[2] == null) {
throw Error("/member/import", t._("Each person must have an e-mail to be able to log in. ::user0:: ::user1:: don't have one. ", {user0:user[0], user1:user[1]}) +user);
throw Error("/member/import",
t._("Each person must have an e-mail to be able to log in. ::user0:: ::user1:: don't have one. ",
{user0: user[0], user1: user[1]}) + user);
}
//uppercase du nom
if (user[1] != null) user[1] = user[1].toUpperCase();
if (user[5] != null) user[5] = user[5].toUpperCase();
//lowercase email
if (user[2] != null){
// uppercase du nom
if (user[1] != null)
user[1] = user[1].toUpperCase();
if (user[5] != null)
user[5] = user[5].toUpperCase();
// lowercase email
if (user[2] != null) {
user[2] = user[2].toLowerCase();
//checkEmail(user[2]);
// checkEmail(user[2]);
}
if (user[6] != null){
if (user[6] != null) {
user[6] = user[6].toLowerCase();
//checkEmail(user[6]);
// checkEmail(user[6]);
}
}
//utf-8 check
for ( row in unregistred.copy()) {
for ( i in 0...row.length) {
// utf-8 check
for (row in unregistred.copy()) {
for (i in 0...row.length) {
var t = row[i];
if (t != "" && t != null) {
try{
try {
if (!Utf8.validate(t)) {
t = Utf8.encode(t);
}
}catch (e:Dynamic) {}
} catch (e:Dynamic) {}
row[i] = t;
}
}
}
//put already registered people in another list
// put already registered people in another list
var registred = [];
for (r in unregistred.copy()) {
//var firstName = r[0];
//var lastName = r[1];
// var firstName = r[0];
// var lastName = r[1];
var email = r[2];
//var firstName2 = r[4];
//var lastName2 = r[5];
// var firstName2 = r[4];
// var lastName2 = r[5];
var email2 = r[6];
var us = db.User.getSameEmail(email, email2);
@@ -548,7 +554,6 @@ class Member extends Controller
}
}
app.session.data.csvUnregistered = unregistred;
app.session.data.csvRegistered = registred;
@@ -557,20 +562,19 @@ class Member extends Controller
step = 2;
}
if (args != null && args.confirm) {
//import unregistered members
var i : Iterable<Dynamic> = cast app.session.data.csvUnregistered;
// import unregistered members
var i:Iterable<Dynamic> = cast app.session.data.csvUnregistered;
for (u in i) {
if (u[0] == null || u[0] == "null" || u[0] == "") continue;
if (u[0] == null || u[0] == "null" || u[0] == "")
continue;
var user = new db.User();
user.firstName = u[0];
user.lastName = u[1];
user.email = u[2];
if (user.email != null && user.email != "null" &&!EmailValidator.check(user.email)) {
throw t._("The E-mail ::useremail:: is invalid, please modify your file", {useremail:user.email});
if (user.email != null && user.email != "null" && !EmailValidator.check(user.email)) {
throw t._("The E-mail ::useremail:: is invalid, please modify your file", {useremail: user.email});
}
user.phone = u[3];
@@ -579,7 +583,8 @@ class Member extends Controller
user.email2 = u[6];
if (user.email2 != null && user.email2 != "null" && !EmailValidator.check(user.email2)) {
App.log(u);
throw t._("The E-mail of the partner of ::userFirstName:: ::userLastName:: '::userEmail::' is invalid, please check your file", {userFirstName:user.firstName, userLastName:user.lastName, userEmail:user.email2});
throw t._("The E-mail of the partner of ::userFirstName:: ::userLastName:: '::userEmail::' is invalid, please check your file",
{userFirstName: user.firstName, userLastName: user.lastName, userEmail: user.email2});
}
user.phone2 = u[7];
user.address1 = u[8];
@@ -594,16 +599,17 @@ class Member extends Controller
ua.insert();
}
//import registered members
var i : Iterable<Array<String>> = cast app.session.data.csvRegistered;
// import registered members
var i:Iterable<Array<String>> = cast app.session.data.csvRegistered;
for (u in i) {
var email = u[2];
var email2 = u[6];
var us = db.User.getSameEmail(email, email2);
var userAmaps = db.UserAmap.manager.search($amap == app.user.amap && $userId in Lambda.map(us, function(u) return u.id), false);
var userAmaps = db.UserAmap.manager.search($amap == app.user.amap
&& $userId in Lambda.map(us, function(u) return u.id), false);
//member exists but is not member of this group.
// member exists but is not member of this group.
if (userAmaps.length == 0) {
var ua = new db.UserAmap();
ua.user = us.first();
@@ -620,7 +626,7 @@ class Member extends Controller
}
if (step == 1) {
//reset import when back to import page
// reset import when back to import page
app.session.data.csvUnregistered = null;
app.session.data.csvRegistered = null;
}
@@ -630,35 +636,34 @@ class Member extends Controller
@tpl("user/insert.mtt")
public function doInsert() {
if (!app.user.canAccessMembership()) throw Error("/", t._("Forbidden action"));
if (!app.user.canAccessMembership())
throw Error("/", t._("Forbidden action"));
var m = new db.User();
var form = sugoi.form.Form.fromSpod(m);
form.removeElement(form.getElement("lang"));
form.removeElement(form.getElement("rights"));
form.removeElement(form.getElement("pass"));
form.removeElement(form.getElement("ldate") );
form.removeElement( form.getElement("apiKey") );
form.removeElement(form.getElement("ldate"));
form.removeElement(form.getElement("apiKey"));
form.addElement(new sugoi.form.elements.Checkbox("warnAmapManager", t._("Send an E-mail to the person in charge of the group"), true));
form.getElement("email").addValidator(new EmailValidator());
form.getElement("email2").addValidator(new EmailValidator());
if (form.isValid()) {
//check doublon de User et de UserAmap
var userSims = db.User.getSameEmail(form.getValueOf("email"),form.getValueOf("email2"));
// check doublon de User et de UserAmap
var userSims = db.User.getSameEmail(form.getValueOf("email"), form.getValueOf("email2"));
view.userSims = userSims;
var userAmaps = db.UserAmap.manager.search($amap == app.user.amap && $userId in Lambda.map(userSims, function(u) return u.id), false);
var userAmaps = db.UserAmap.manager.search($amap == app.user.amap
&& $userId in Lambda.map(userSims, function(u) return u.id), false);
view.userAmaps = userAmaps;
if (userAmaps.length > 0) {
//user deja enregistré dans cette amap
// user deja enregistré dans cette amap
throw Error('/member/view/' + userAmaps.first().user.id, t._("This person is already member of this group"));
}else if (userSims.length > 0) {
//des users existent avec ce nom ,
//if (userSims.length == 1) {
} else if (userSims.length > 0) {
// des users existent avec ce nom ,
// if (userSims.length == 1) {
// si yen a qu'un on l'inserte
var ua = new db.UserAmap();
ua.user = userSims.first();
@@ -671,19 +676,18 @@ class Member extends Controller
throw Error('/member', t._("Not possible to add this person because there are already some people in the database having the same firstname and name. Please contact the administrator.")+userSims);
}*/
return;
}else {
if (app.user.amap.flags.has(db.Amap.AmapFlags.PhoneRequired) && form.getValueOf("phone") == null ){
} else {
if (app.user.amap.flags.has(db.Amap.AmapFlags.PhoneRequired) && form.getValueOf("phone") == null) {
throw Error("/member/insert", t._("Phone number is required in this group."));
}
//insert user
// insert user
var u = new db.User();
form.toSpod(u);
u.lang = app.user.lang;
u.insert();
//insert userAmap
// insert userAmap
var ua = new db.UserAmap();
ua.user = u;
ua.amap = app.user.getAmap();
@@ -691,38 +695,39 @@ class Member extends Controller
if (form.getValueOf("warnAmapManager") == "1") {
var url = "http://" + App.config.HOST + "/member/view/" + u.id;
var text = t._("::admin:: just keyed-in contact details of a new member: <br/><strong>::newMember::</strong><br/> <a href='::url::'>See contact details</a>",{admin:app.user.getName(),newMember:u.getCoupleName(),url:url});
App.quickMail(
app.user.getAmap().contact.email,
app.user.amap.name +" - "+ t._("New member") + " : " + u.getCoupleName(),
app.processTemplate("mail/message.mtt", { text:text } )
);
var text = t._("::admin:: just keyed-in contact details of a new member: <br/><strong>::newMember::</strong><br/> <a href='::url::'>See contact details</a>",
{
admin: app.user.getName(),
newMember: u.getCoupleName(),
url: url
});
App.quickMail(app.user.getAmap().contact.email, app.user.amap.name + " - " + t._("New member") + " : " + u.getCoupleName(),
app.processTemplate("mail/message.mtt", {
text: text
}));
}
throw Ok('/member/', t._("This person is now member of the group"));
}
}
view.form = form;
}
/**
* user payments history
*/
@tpl('member/payments.mtt')
function doPayments(m:db.User){
function doPayments(m:db.User) {
service.PaymentService.updateUserBalance(m, app.user.amap);
var browse:Int->Int->List<Dynamic>;
//default display
// default display
browse = function(index:Int, limit:Int) {
return db.Operation.getOperationsWithIndex(m,app.user.amap,index,limit,true);
return db.Operation.getOperationsWithIndex(m, app.user.amap, index, limit, true);
}
var count = db.Operation.countOperations(m,app.user.amap);
var count = db.Operation.countOperations(m, app.user.amap);
var rb = new sugoi.tools.ResultsBrowser(count, 10, browse);
view.rb = rb;
view.member = m;
@@ -732,10 +737,9 @@ class Member extends Controller
}
@tpl('member/balance.mtt')
function doBalance(){
function doBalance() {
view.balanced = db.UserAmap.manager.search($amap == app.user.amap && $balance == 0.0, false);
view.credit = db.UserAmap.manager.search($amap == app.user.amap && $balance > 0, false);
view.debt = db.UserAmap.manager.search($amap == app.user.amap && $balance < 0, false);
}
}
+10
View File
@@ -0,0 +1,10 @@
package controller;
class MentionsLegales extends Controller {
public function new() {
super();
}
@tpl("mentions_legales/default.mtt")
function doDefault() {}
}
+10 -22
View File
@@ -31,8 +31,8 @@ class Messages extends Controller {
var lists = getLists();
form.addElement(new StringInput("senderName", t._("Sender name"), senderName, true));
form.addElement(new StringInput("senderMail", t._("Sender E-Mail"), senderMail, true));
form.addElement(new StringSelect("list", t._("Recipients"), lists, "1", true, null, "style='width:500px;'"));
form.addElement(new StringInput("subject", t._("Subject:"), "", false, null, "style='width:500px;'"));
form.addElement(new StringSelect("list", t._("Recipients"), lists, "3", true, null, "style='width:500px;'"));
form.addElement(new StringInput("subject", t._("Subject:"), "", true, null, "style='width:500px;'"));
form.addElement(new TextArea("text", t._("Message:"), "", false, null, "style='width:500px;height:350px;'"));
if (form.checkToken()) {
@@ -41,11 +41,6 @@ class Messages extends Controller {
var subject = form.getValueOf("subject");
App.log('about to send email <$subject> to listId=<$listId> to ${dest.length} recipient(s)');
// Sys.command("sh", [
// "-c",
// "sendemail -f no-reply@comptoirduvrac.re -s mail1.zourit.net:587 -t pvincent@comptoirduvrac.re -bcc 'pvincent974@gmail.com, pvincent974@laposte.net' -u TEST -m coucou -xu postmaster@comptoirduvrac.re -xp QqQeAPT6EpoK"
// ]);
var mails = [];
for (d in dest) {
if (d.email != null)
@@ -56,32 +51,25 @@ class Messages extends Controller {
// send mail confirmation link
var e = new sugoi.mail.Mail();
e.setSender(App.config.get("default_email"));
e.setSubject(subject);
e.setSender(App.config.get("default_email"), t._("Cagette.net"));
e.setSubject('[cagettepei] $subject');
var replyTo = form.getValueOf("senderMail");
var replyToName = form.getValueOf("senderName");
if (replyTo != null)
e.setReplyTo(replyTo, replyToName);
if (mails.length > 1) {
var cc = mails.join(",");
App.log('add header Cc: $cc');
e.setHeader("CC:", cc);
// for (x in mails)
// e.addRecipient(x);
e.addRecipient(e.getSender().email);
} else {
App.log('no header');
e.addRecipient(mails[0]);
}
for (x in mails)
e.addRecipient(x);
// sender : default email ( explicitly tells that the server send an email on behalf of the user )
// e.setHeader("Sender", App.config.get("default_email"));
var text:String = form.getValueOf("text");
if (text == null)
text = "";
var html = app.processTemplate("mail/message.mtt", {text: text, group: app.user.amap, list: getListName(listId)});
e.setHtmlBody(html);
App.log('text=[$text]');
App.log('html= ${e.getHtmlBody()}');
App.sendMail(e, app.user.getAmap(), listId, app.user);
// store message
+86 -86
View File
@@ -1,4 +1,5 @@
package controller;
import haxe.crypto.Md5;
import sugoi.form.elements.Input;
import sugoi.form.Form;
@@ -7,57 +8,65 @@ import sugoi.form.elements.StringInput;
import sugoi.form.validators.EmailValidator;
import ufront.mail.*;
class User extends Controller
{
public function new()
{
class User extends Controller {
public function new() {
super();
}
@tpl("user/default.mtt")
function doDefault() {
}
function doDefault() {}
@tpl("user/login.mtt")
function doLogin() {
if (App.current.user != null) {
throw Redirect('/');
}
//if its needed to redirect after login
if (app.params.exists("redirect")){
// if its needed to redirect after login
if (app.params.exists("redirect")) {
view.redirect = app.params.exists("redirect");
}else{
} else {
view.redirect = "/";
}
}
@tpl("user/cookieConsent.mtt")
function doCookieConsent() {
App.log("Cookie Consent called!");
// if (App.current.user != null) {
// throw Redirect('/');
// }
// if its needed to redirect after login
if (app.params.exists("redirect")) {
view.redirect = app.params.exists("redirect");
} else {
view.redirect = "/";
}
}
/**
* Choose which group to connect to.
*/
@logged
@tpl("user/choose.mtt")
function doChoose(?args: { amap:db.Amap } ) {
if (app.user == null) throw t._("You are not connected");
function doChoose(?args:{amap:db.Amap}) {
if (app.user == null)
throw t._("You are not connected");
var amaps = db.UserAmap.manager.search($user == app.user, false);
if (amaps.length == 1 && !app.params.exists("show")) {
//qu'une amap
// qu'une amap
app.session.data.amapId = amaps.first().amap.id;
throw Redirect('/');
}else{
view.noGroup = true; //force template to not display current group
} else {
view.noGroup = true; // force template to not display current group
}
if (args!=null && args.amap!=null) {
//select a group
var which = app.session.data==null ? 0 : app.session.data.whichUser ;
if (args != null && args.amap != null) {
// select a group
var which = app.session.data == null ? 0 : app.session.data.whichUser;
app.session.data.order = null;
app.session.data.newGroup = null;
app.session.data.amapId = args.amap.id;
@@ -68,7 +77,6 @@ class User extends Controller
view.amaps = amaps;
view.wl = db.WaitingList.manager.search($user == app.user, false);
#if plugins
view.pros = pro.db.PUserCompany.getCompanies(app.user);
#end
@@ -84,75 +92,76 @@ class User extends Controller
* when password is forgotten
*/
@tpl("user/forgottenPassword.mtt")
function doForgottenPassword(?key:String, ?u:db.User){
//STEP 1
function doForgottenPassword(?key:String, ?u:db.User) {
// STEP 1
var step = 1;
var error : String = null;
var error:String = null;
var url = "/user/forgottenPassword";
//ask for mail
// ask for mail
var askmailform = new Form("askemail");
askmailform.addElement(new StringInput("email", t._("Please key-in your E-Mail address"),null,true));
askmailform.addElement(new StringInput("email", t._("Please key-in your E-Mail address"), null, true));
//change pass form
// change pass form
var chpassform = new Form("chpass");
var pass1 = new StringInput("pass1", t._("Your new password"),null,true);
var pass1 = new StringInput("pass1", t._("Your new password"), null, true);
pass1.password = true;
chpassform.addElement(pass1);
var pass2 = new StringInput("pass2", t._("Again your new password"),null,true);
var pass2 = new StringInput("pass2", t._("Again your new password"), null, true);
pass2.password = true;
chpassform.addElement(pass2);
var uid = new IntInput("uid","uid", u == null?null:u.id);
var uid = new IntInput("uid", "uid", u == null ? null : u.id);
uid.inputType = ITHidden;
chpassform.addElement(uid);
if (askmailform.isValid()) {
//STEP 2
//send password renewal email
// STEP 2
// send password renewal email
step = 2;
var email :String = askmailform.getValueOf("email");
var email:String = askmailform.getValueOf("email");
var user = db.User.manager.select(email == $email, false);
//could be user 2
if(user==null) user = db.User.manager.select(email == $email2, false);
// could be user 2
if (user == null)
user = db.User.manager.select(email == $email2, false);
//user not found
if (user == null) throw Error(url, t._("This E-mail is not linked to a known account"));
// user not found
if (user == null)
throw Error(url, t._("This E-mail is not linked to a known account"));
//create token
var token = haxe.crypto.Md5.encode("chp"+Std.random(1000000000));
// create token
var token = haxe.crypto.Md5.encode("chp" + Std.random(1000000000));
sugoi.db.Cache.set(token, user.id, 60 * 60 * 24 * 30);
var m = new sugoi.mail.Mail();
m.setSender(App.config.get("default_email"), t._("Cagette.net"));
m.setRecipient(email, user.name);
m.setSubject( "["+App.config.NAME+"] : "+t._("Password change"));
m.setHtmlBody( app.processTemplate('mail/forgottenPassword.mtt', { user:user, link:'http://' + App.config.HOST + '/user/forgottenPassword/'+token+"/"+user.id }) );
m.setSubject("[" + App.config.NAME + "] " + t._("Password change"));
m.setHtmlBody(app.processTemplate('mail/forgottenPassword.mtt',
{user: user, link: 'http://' + App.config.HOST + '/user/forgottenPassword/' + token + "/" + user.id}));
App.sendMail(m);
}
if (key != null && u!=null) {
//check key and propose to change pass
if (key != null && u != null) {
// check key and propose to change pass
step = 3;
if ( u.id == sugoi.db.Cache.get(key) ) {
if (u.id == sugoi.db.Cache.get(key)) {
view.form = chpassform;
}else {
} else {
error = t._("Invalid request");
}
}
if (chpassform.isValid()) {
//change pass
// change pass
step = 4;
if ( chpassform.getValueOf("pass1") == chpassform.getValueOf("pass2")) {
var uid = Std.parseInt( chpassform.getValueOf("uid") );
if (chpassform.getValueOf("pass1") == chpassform.getValueOf("pass2")) {
var uid = Std.parseInt(chpassform.getValueOf("uid"));
var user = db.User.manager.get(uid, true);
var pass = chpassform.getValueOf("pass1");
user.setPass(pass);
@@ -161,20 +170,21 @@ class User extends Controller
var m = new sugoi.mail.Mail();
m.setSender(App.config.get("default_email"), t._("Cagette.net"));
m.setRecipient(user.email, user.name);
if(user.email2!=null) m.setRecipient(user.email2, user.name);
m.setSubject( "["+App.config.NAME+"] : "+t._("New password confirmed"));
if (user.email2 != null)
m.setRecipient(user.email2, user.name);
m.setSubject("[" + App.config.NAME + "] : " + t._("New password confirmed"));
var emails = [user.email];
if(user.email2!=null) emails.push(user.email2);
if (user.email2 != null)
emails.push(user.email2);
var params = {
user:user,
emails:emails.join(", "),
password:pass,
NAME:App.config.NAME
user: user,
emails: emails.join(", "),
password: pass,
NAME: App.config.NAME
}
m.setHtmlBody( app.processTemplate('mail/newPasswordConfirmed.mtt', params) );
m.setHtmlBody(app.processTemplate('mail/newPasswordConfirmed.mtt', params));
App.sendMail(m);
}else {
} else {
error = t._("You must key-in two times the same password");
}
}
@@ -187,20 +197,18 @@ class User extends Controller
view.error = error;
}
/**
* generate a custom key for transactionnal emails, valid during the current day
*/
//function getKey(m:db.User) {
//return haxe.crypto.Md5.encode(App.config.get("key")+m.email+(Date.now().getDate())).substr(0,12);
//}
// function getKey(m:db.User) {
// return haxe.crypto.Md5.encode(App.config.get("key")+m.email+(Date.now().getDate())).substr(0,12);
// }
@logged
@tpl("form.mtt")
function doDefinePassword(?key:String, ?u:db.User){
if (app.user.isFullyRegistred()) throw Error("/", t._("You already have a password"));
function doDefinePassword(?key:String, ?u:db.User) {
if (app.user.isFullyRegistred())
throw Error("/", t._("You already have a password"));
var form = new Form("definepass");
var pass1 = new StringInput("pass1", t._("Your new password"));
@@ -211,16 +219,13 @@ class User extends Controller
form.addElement(pass2);
if (form.isValid()) {
if ( form.getValueOf("pass1") == form.getValueOf("pass2")) {
if (form.getValueOf("pass1") == form.getValueOf("pass2")) {
app.user.lock();
app.user.setPass(form.getValueOf("pass1"));
app.user.update();
throw Ok('/', t._("Congratulations, your account is now protected by a password."));
}else {
form.addError( t._("You must key-in two times the same password"));
} else {
form.addError(t._("You must key-in two times the same password"));
}
}
view.form = form;
@@ -231,25 +236,20 @@ class User extends Controller
* landing page when coming from an invitation
* @param k
*/
public function doValidate(k:String ) {
public function doValidate(k:String) {
var uid = Std.parseInt(sugoi.db.Cache.get("validation" + k));
if (uid == null || uid==0) throw Error('/user/login', t._("Your invitation is invalid or expired ($k)"));
if (uid == null || uid == 0)
throw Error('/user/login', t._("Your invitation is invalid or expired ($k)"));
var user = db.User.manager.get(uid, true);
db.User.login(user, user.email);
var groups = user.getAmaps();
if(groups.length>0) app.session.data.amapId = groups.first().id;
if (groups.length > 0)
app.session.data.amapId = groups.first().id;
sugoi.db.Cache.destroy("validation" + k);
throw Ok("/user/definePassword", t._("Congratulations ::userName::, your account is validated!", {userName:user.getName()}));
throw Ok("/user/definePassword", t._("Congratulations ::userName::, your account is validated!", {userName: user.getName()}));
}
}
+66 -97
View File
@@ -1,19 +1,18 @@
package controller.admin;
import haxe.web.Dispatch;
import Common;
class Admin extends Controller {
public function new() {
super();
view.category = 'admin';
//trigger a "Nav" event
// trigger a "Nav" event
var nav = new Array<Link>();
var e = Nav(nav,"admin");
var e = Nav(nav, "admin");
app.event(e);
view.nav = e.getParameters()[0];
}
@tpl("admin/default.mtt")
@@ -24,44 +23,36 @@ class Admin extends Controller {
@tpl("admin/emails.mtt")
function doEmails() {
var browse = function(index:Int, limit:Int) {
return sugoi.db.BufferedMail.manager.search($sdate==null,{limit:[index,limit],orderBy:-cdate},false);
return sugoi.db.BufferedMail.manager.search($sdate == null, {limit: [index, limit], orderBy: -cdate}, false);
}
var count = sugoi.db.BufferedMail.manager.count($sdate==null);
view.browser = new sugoi.tools.ResultsBrowser(count,10,browse);
var count = sugoi.db.BufferedMail.manager.count($sdate == null);
view.browser = new sugoi.tools.ResultsBrowser(count, 10, browse);
view.num = count;
}
@tpl("form.mtt")
function doSmtp() {
var f = new sugoi.form.Form("emails");
var data = [
{label:"SMTP",value:"smtp"},
{label:"Mandrill API",value:"mandrill"},
];
var data = [{label: "SMTP", value: "smtp"}, {label: "Mandrill API", value: "mandrill"},];
var mailer = sugoi.db.Variable.get("mailer")==null ? "smtp" : sugoi.db.Variable.get("mailer");
var host = sugoi.db.Variable.get("smtp_host")==null ? App.config.get("smtp_host") : sugoi.db.Variable.get("smtp_host");
var port = sugoi.db.Variable.get("smtp_port")==null ? App.config.get("smtp_port") : sugoi.db.Variable.get("smtp_port");
var user = sugoi.db.Variable.get("smtp_user")==null ? App.config.get("smtp_user") : sugoi.db.Variable.get("smtp_user");
var pass = sugoi.db.Variable.get("smtp_pass")==null ? App.config.get("smtp_pass") : sugoi.db.Variable.get("smtp_pass");
var mailer = sugoi.db.Variable.get("mailer") == null ? "smtp" : sugoi.db.Variable.get("mailer");
var host = sugoi.db.Variable.get("smtp_host") == null ? "<SMTP_SERVER>" : sugoi.db.Variable.get("smtp_host");
var port = sugoi.db.Variable.get("smtp_port") == null ? "587" : sugoi.db.Variable.get("smtp_port");
var user = sugoi.db.Variable.get("smtp_user") == null ? "" : sugoi.db.Variable.get("smtp_user");
var pass = sugoi.db.Variable.get("smtp_pass") == null ? "" : sugoi.db.Variable.get("smtp_pass");
f.addElement(new sugoi.form.elements.StringSelect("mailer", "Mailer", data, mailer ));
f.addElement(new sugoi.form.elements.StringSelect("mailer", "Mailer", data, mailer));
f.addElement(new sugoi.form.elements.StringInput("smtp_host", "host", host));
f.addElement(new sugoi.form.elements.StringInput("smtp_port", "port", port));
f.addElement(new sugoi.form.elements.StringInput("smtp_user", "user", user));
f.addElement(new sugoi.form.elements.StringInput("smtp_pass", "pass", pass));
if (f.isValid()){
for ( k in ["mailer","smtp_host","smtp_port","smtp_user","smtp_pass"]){
if (f.isValid()) {
for (k in ["mailer", "smtp_host", "smtp_port", "smtp_user", "smtp_pass"]) {
sugoi.db.Variable.set(k, f.getValueOf(k));
}
throw Ok("/admin/emails", t._("Configuration updated") );
throw Ok("/admin/emails", t._("Configuration updated"));
}
view.title = t._("Email service configuration");
@@ -72,67 +63,60 @@ class Admin extends Controller {
d.dispatch(new controller.admin.Plugins());
}
@tpl("admin/taxo.mtt")
function doTaxo(){
function doTaxo() {
view.categ = db.TxpCategory.manager.all();
}
/**
* Display errors logged in DB
*/
@tpl("admin/errors.mtt")
function doErrors( args:{?user: Int, ?like: String, ?empty:Bool} ) {
function doErrors(args:{?user:Int, ?like:String, ?empty:Bool}) {
view.now = Date.now();
view.u = args.user!=null ? db.User.manager.get(args.user,false) : null;
view.like = args.like!=null ? args.like : "";
view.u = args.user != null ? db.User.manager.get(args.user, false) : null;
view.like = args.like != null ? args.like : "";
var sql = "";
if( args.user!=null ) sql += " AND uid="+args.user;
//if( args.like!=null && args.like != "" ) sql += " AND error like "+sys.db.Manager.cnx.quote("%"+args.like+"%");
if (args.user != null)
sql += " AND uid=" + args.user;
// if( args.like!=null && args.like != "" ) sql += " AND error like "+sys.db.Manager.cnx.quote("%"+args.like+"%");
if (args.empty) {
sys.db.Manager.cnx.request("truncate table Error");
}
var errorsStats = sys.db.Manager.cnx.request("select count(id) as c, DATE_FORMAT(date,'%y-%m-%d') as day from Error where date > NOW()- INTERVAL 1 MONTH "+sql+" group by day order by day").results();
var errorsStats = sys.db.Manager.cnx.request("select count(id) as c, DATE_FORMAT(date,'%y-%m-%d') as day from Error where date > NOW()- INTERVAL 1 MONTH "
+ sql
+ " group by day order by day")
.results();
view.errorsStats = errorsStats;
view.browser = new sugoi.tools.ResultsBrowser(
sugoi.db.Error.manager.unsafeCount("SELECT count(*) FROM Error WHERE 1 "+sql),
20,
function(start, limit) { return sugoi.db.Error.manager.unsafeObjects("SELECT * FROM Error WHERE 1 "+sql+" ORDER BY date DESC LIMIT "+start+","+limit,false); }
);
view.browser = new sugoi.tools.ResultsBrowser(sugoi.db.Error.manager.unsafeCount("SELECT count(*) FROM Error WHERE 1 " + sql), 20,
function(start, limit) {
return sugoi.db.Error.manager.unsafeObjects("SELECT * FROM Error WHERE 1 " + sql + " ORDER BY date DESC LIMIT " + start + "," + limit, false);
});
}
@tpl("admin/graph.mtt")
function doGraph(?key:String,?year:Int,?month:Int){
function doGraph(?key:String, ?year:Int, ?month:Int) {
var from = new Date(year, month, 1, 0, 0, 0);
var to = new Date(year, month + 1, 0, 23, 59, 59);
var from = new Date(year,month,1,0,0,0);
var to = new Date(year,month+1,0,23,59,59);
if(app.params.exists("recompute")){
switch(key){
if (app.params.exists("recompute")) {
switch (key) {
case "basket":
for( d in 1...to.getDate()){
var _from = new Date(year,month,d,0,0,0);
var _to = new Date(year,month,d,23,59,59);
var value = db.Basket.manager.count($cdate>=_from && $cdate<=_to);
var g = db.Graph.record(key,value, _from );
for (d in 1...to.getDate()) {
var _from = new Date(year, month, d, 0, 0, 0);
var _to = new Date(year, month, d, 23, 59, 59);
var value = db.Basket.manager.count($cdate >= _from && $cdate <= _to);
var g = db.Graph.record(key, value, _from);
// trace(value,_from,g);
}
}
}
}
}
var data = db.Graph.getRange(key,from,to);
var data = db.Graph.getRange(key, from, to);
view.data = data;
view.from = from;
view.to = to;
@@ -140,55 +124,46 @@ class Admin extends Controller {
var averageValue = 0.0;
var total = 0.0;
for( d in data) total += d.value;
averageValue = total/data.length;
for (d in data)
total += d.value;
averageValue = total / data.length;
view.total = total;
view.averageValue = averageValue;
}
function doFixDistribValidation() {
Sys.println("===== Liste des distributions ayant été re-validées ====<br>");
//Get the current group
// Get the current group
var group = app.user.amap;
//Get all the contracts for this given group
var contractIds = Lambda.map(group.getContracts(),function(x) return x.id);
//Get all the validated distributions for this given group
var validatedDistribs = db.Distribution.manager.search( ($contractId in contractIds) && $validated == true, {orderBy:date}, false);
for (distrib in validatedDistribs){
// Get all the contracts for this given group
var contractIds = Lambda.map(group.getContracts(), function(x) return x.id);
// Get all the validated distributions for this given group
var validatedDistribs = db.Distribution.manager.search(($contractId in contractIds) && $validated == true, {orderBy: date}, false);
for (distrib in validatedDistribs) {
service.PaymentService.validateDistribution(distrib);
Sys.println(distrib.toString() + "<br>");
}
Sys.println("===== Fin de la liste ====");
}
function doCheckDistribValidation() {
Sys.println("===== Liste des distributions validées ayant des opérations/paiements non validés ====<br>");
//Get the current group
// Get the current group
var group = app.user.amap;
//Get all the contracts for this given group
var contractIds = Lambda.map(group.getContracts(),function(x) return x.id);
//Get all the validated distributions for this given group
var validatedDistribs = db.Distribution.manager.search( ($contractId in contractIds) && $validated == true, {orderBy:date}, false);
for (distrib in validatedDistribs){
for (user in distrib.getUsers()){
// Get all the contracts for this given group
var contractIds = Lambda.map(group.getContracts(), function(x) return x.id);
// Get all the validated distributions for this given group
var validatedDistribs = db.Distribution.manager.search(($contractId in contractIds) && $validated == true, {orderBy: date}, false);
for (distrib in validatedDistribs) {
for (user in distrib.getUsers()) {
var basket = db.Basket.get(user, distrib.place, distrib.date);
if (basket == null || basket.isValidated()) continue;
if (basket == null || basket.isValidated())
continue;
for (order in basket.getOrders()){
for (order in basket.getOrders()) {
if (!order.paid) {
Sys.println(order.distribution.toString() + "<br>");
Sys.println(order.toString() + "<br>");
@@ -196,28 +171,22 @@ class Admin extends Controller {
}
var operation = basket.getOrderOperation(false);
if (operation != null){
if (operation != null) {
if (operation.pending) {
Sys.println(distrib.toString() + "<br>");
Sys.println(operation.toString() + "<br>");
}
for ( payment in basket.getPayments()){
if (payment.pending){
for (payment in basket.getPayments()) {
if (payment.pending) {
Sys.println(distrib.toString() + "<br>");
Sys.println(payment.toString() + "<br>");
}
}
}
}
}
Sys.println("===== Fin de la liste ====");
}
}
+14 -21
View File
@@ -1,38 +1,34 @@
package form;
import sugoi.form.elements.RadioGroup;
import Common;
class ColorRadioGroup extends RadioGroup
{
public function new(name:String, label:String,selected:String)
{
class ColorRadioGroup extends RadioGroup {
public function new(name:String, label:String, selected:String) {
var data = [];
var i = 0;
for (c in db.CategoryGroup.COLORS) {
data.push( { value:Std.string(c), label:Std.string(i) } );
data.push({value: Std.string(c), label: Std.string(i)});
i++;
}
super(name, label, data, selected, "1", false, true);
}
override public function render():String
{
override public function render():String {
var s = "";
var n = parentForm.name + "_" +name;
var n = parentForm.name + "_" + name;
var c = 0;
if (data != null)
{
for (row in data)
{
if (data != null) {
for (row in data) {
var radio = "<input type=\"radio\" name=\"" + n + "\" id=\"" + n + c + "\" value=\"" + row.label + "\" "
+ (row.label == Std.string(value) ? "checked" : "") + " />\n";
var img = "<div style='margin-right:16px;width:32px;height:32px;background:"
+ App.current.view.intToHex(Std.parseInt(row.value))
+ ";'></div>";
var radio = "<input type=\"radio\" name=\""+n+"\" id=\""+n+c+"\" value=\"" + row.label + "\" " + (row.label == Std.string(value) ? "checked":"") +" />\n";
var img = "<div style='margin-right:16px;width:32px;height:32px;background:"+App.current.view.intToHex(Std.parseInt(row.value))+";'></div>";
s += "<label for=\"" + n+c + "\" class='checkbox' style='display: inline-block;'>"+radio + " "+img+" </label>";
s += "<label for=\"" + n + c + "\" class='checkbox' style='display: inline-block;'>" + radio + " " + img + " </label>";
c++;
}
@@ -40,7 +36,4 @@ class ColorRadioGroup extends RadioGroup
return s;
}
}
+171 -171
View File
@@ -1,4 +1,5 @@
package service;
import Common;
import tink.core.Error;
@@ -6,10 +7,8 @@ import tink.core.Error;
* Order Service
* @author web-wizard,fbarbut
*/
class OrderService
{
static function canHaveFloatQt(product:db.Product):Bool{
class OrderService {
static function canHaveFloatQt(product:db.Product):Bool {
return product.hasFloatQt || product.wholesale || product.variablePrice;
}
@@ -20,42 +19,45 @@ class OrderService
* @param productId
*/
public static function make(user:db.User, quantity:Float, product:db.Product, ?distribId:Int, ?paid:Bool, ?user2:db.User, ?invert:Bool):db.UserContract {
var t = sugoi.i18n.Locale.texts;
if(product.contract.type==db.Contract.TYPE_VARORDER && distribId==null) throw "You have to provide a distribId";
if(quantity==null) throw "Quantity is null";
if (product.contract.type == db.Contract.TYPE_VARORDER && distribId == null)
throw "You have to provide a distribId";
if (quantity == null)
throw "Quantity is null";
//quantity
if ( !canHaveFloatQt(product) ){
if( !tools.FloatTool.isInt(quantity) ) {
throw new tink.core.Error(t._("Error : product \"::product::\" quantity should be integer",{product:product.name}));
// quantity
if (!canHaveFloatQt(product)) {
if (!tools.FloatTool.isInt(quantity)) {
throw new tink.core.Error(t._("Error : product \"::product::\" quantity should be integer", {product: product.name}));
}
}
//multiweight : make one row per product
if (product.multiWeight && quantity > 1.0){
if (product.multiWeight && quantity != Math.abs(quantity)) throw t._("multi-weighing products should be ordered only with integer quantities");
// multiweight : make one row per product
if (product.multiWeight && quantity > 1.0) {
if (product.multiWeight && quantity != Math.abs(quantity))
throw t._("multi-weighing products should be ordered only with integer quantities");
var o = null;
for ( i in 0...Math.round(quantity)){
for (i in 0...Math.round(quantity)) {
o = make(user, 1, product, distribId, paid, user2, invert);
}
return o;
}
//checks
if (quantity <= 0) return null;
// checks
if (quantity <= 0)
return null;
//check for previous orders on the same distrib
// check for previous orders on the same distrib
var prevOrders = new List<db.UserContract>();
if (distribId == null) {
prevOrders = db.UserContract.manager.search($product==product && $user==user, true);
}else {
prevOrders = db.UserContract.manager.search($product==product && $user==user && $distributionId==distribId, true);
prevOrders = db.UserContract.manager.search($product == product && $user == user, true);
} else {
prevOrders = db.UserContract.manager.search($product == product && $user == user && $distributionId == distribId, true);
}
//Create order object
// Create order object
var o = new db.UserContract();
o.product = product;
o.quantity = quantity;
@@ -66,23 +68,26 @@ class OrderService
o.user = user;
if (user2 != null) {
o.user2 = user2;
if (invert != null) o.flags.set(InvertSharedOrder);
if (invert != null)
o.flags.set(InvertSharedOrder);
}
if (paid != null) o.paid = paid;
if (distribId != null) o.distribution = db.Distribution.manager.get(distribId);
if (paid != null)
o.paid = paid;
if (distribId != null)
o.distribution = db.Distribution.manager.get(distribId);
//cumulate quantities if there is a similar previous order
// cumulate quantities if there is a similar previous order
if (prevOrders.length > 0 && !product.multiWeight) {
for (prevOrder in prevOrders) {
//if (!prevOrder.paid) {
// if (!prevOrder.paid) {
o.quantity += prevOrder.quantity;
prevOrder.delete();
//}
// }
}
}
//create a basket object
if (distribId != null){
// create a basket object
if (distribId != null) {
var dist = o.distribution;
var basket = db.Basket.getOrCreate(user, dist.place, dist.date);
o.basket = basket;
@@ -90,129 +95,130 @@ class OrderService
o.insert();
//Stocks
// Stocks
if (o.product.stock != null) {
var c = o.product.contract;
if (c.hasStockManagement()) {
//trace("stock for "+quantity+" x "+product.name);
// trace("stock for "+quantity+" x "+product.name);
if (o.product.stock == 0) {
if (App.current.session != null) {
App.current.session.addMessage(t._("There is no more '::productName::' in stock, we removed it from your order", {productName:o.product.name}), true);
App.current.session.addMessage(t._("There is no more '::productName::' in stock, we removed it from your order",
{productName: o.product.name}), true);
}
o.quantity -= quantity;
if ( o.quantity <= 0 ) {
if (o.quantity <= 0) {
o.delete();
return null;
}
}else if (o.product.stock - quantity < 0) {
} else if (o.product.stock - quantity < 0) {
var canceled = quantity - o.product.stock;
o.quantity -= canceled;
o.update();
if (App.current.session != null) {
var msg = t._("We reduced your order of '::productName::' to quantity ::oQuantity:: because there is no available products anymore", {productName:o.product.name, oQuantity:o.quantity});
var msg = t._("We reduced your order of '::productName::' to quantity ::oQuantity:: because there is no available products anymore",
{productName: o.product.name, oQuantity: o.quantity});
App.current.session.addMessage(msg, true);
}
o.product.lock();
o.product.stock = 0;
o.product.update();
App.current.event(StockMove({product:o.product, move:0 - (quantity - canceled) }));
}else {
App.current.event(StockMove({product: o.product, move: 0 - (quantity - canceled)}));
} else {
o.product.lock();
o.product.stock -= quantity;
o.product.update();
App.current.event(StockMove({product:o.product, move:0 - quantity}));
App.current.event(StockMove({product: o.product, move: 0 - quantity}));
}
}
}
return o;
}
/**
* Edit an existing order (quantity)
*/
public static function edit(order:db.UserContract, newquantity:Float, ?paid:Bool , ?user2:db.User,?invert:Bool) {
public static function edit(order:db.UserContract, newquantity:Float, ?paid:Bool, ?user2:db.User, ?invert:Bool) {
var t = sugoi.i18n.Locale.texts;
order.lock();
//quantity
if (newquantity == null) newquantity = 0;
if ( !canHaveFloatQt(order.product) ){
if( !tools.FloatTool.isInt(newquantity) ) {
throw new tink.core.Error(t._("Error : product \"::product::\" quantity should be integer",{product:order.product.name}));
// quantity
if (newquantity == null)
newquantity = 0;
if (!canHaveFloatQt(order.product)) {
if (!tools.FloatTool.isInt(newquantity)) {
throw new tink.core.Error(t._("Error : product \"::product::\" quantity should be integer", {product: order.product.name}));
}
}
//paid
// paid
if (paid != null) {
order.paid = paid;
}else {
if (order.quantity < newquantity) order.paid = false;
} else {
if (order.quantity < newquantity)
order.paid = false;
}
//shared order
if (user2 != null){
// shared order
if (user2 != null) {
order.user2 = user2;
if (invert == true) order.flags.set(InvertSharedOrder);
if (invert == false) order.flags.unset(InvertSharedOrder);
}else{
if (invert == true)
order.flags.set(InvertSharedOrder);
if (invert == false)
order.flags.unset(InvertSharedOrder);
} else {
order.user2 = null;
order.flags.unset(InvertSharedOrder);
}
//stocks
var e : Event = null;
// stocks
var e:Event = null;
if (order.product.stock != null) {
var c = order.product.contract;
if (c.hasStockManagement()) {
if (newquantity < order.quantity) {
//on commande moins que prévu : incrément de stock
// on commande moins que prévu : incrément de stock
order.product.lock();
order.product.stock += (order.quantity-newquantity);
e = StockMove({product:order.product, move:0 - (order.quantity-newquantity) });
}else {
//on commande plus que prévu : décrément de stock
order.product.stock += (order.quantity - newquantity);
e = StockMove({product: order.product, move: 0 - (order.quantity - newquantity)});
} else {
// on commande plus que prévu : décrément de stock
var addedquantity = newquantity - order.quantity;
if (order.product.stock - addedquantity < 0) {
//stock is not enough, reduce order
// stock is not enough, reduce order
newquantity = order.quantity + order.product.stock;
if( App.current.session!=null) App.current.session.addMessage(t._("We reduced your order of '::productName::' to quantity ::oQuantity:: because there is no available products anymore", {productName:order.product.name, oQuantity:newquantity}), true);
if (App.current.session != null)
App.current.session.addMessage(t._("We reduced your order of '::productName::' to quantity ::oQuantity:: because there is no available products anymore",
{
productName: order.product.name,
oQuantity: newquantity
}), true);
e = StockMove({product:order.product, move: 0 - order.product.stock });
e = StockMove({product: order.product, move: 0 - order.product.stock});
order.product.lock();
order.product.stock = 0;
}else{
//stock is big enough
} else {
// stock is big enough
order.product.lock();
order.product.stock -= addedquantity;
e = StockMove({ product:order.product, move: 0 - addedquantity });
e = StockMove({product: order.product, move: 0 - addedquantity});
}
}
order.product.update();
}
}
//update order
// update order
if (newquantity == 0) {
order.quantity = 0;
order.paid = true;
order.update();
}else {
} else {
order.quantity = newquantity;
order.update();
}
@@ -222,58 +228,54 @@ class OrderService
return order;
}
/**
* Delete an order
*/
public static function delete(order:db.UserContract) {
var t = sugoi.i18n.Locale.texts;
if(order==null) throw new Error(t._("This order has already been deleted."));
if (order == null)
throw new Error(t._("This order has already been deleted."));
order.lock();
if (order.quantity == 0) {
var contract = order.product.contract;
var user = order.user;
//Amap Contract
if ( contract.type == db.Contract.TYPE_CONSTORDERS ) {
// Amap Contract
if (contract.type == db.Contract.TYPE_CONSTORDERS) {
order.delete();
if( contract.amap.hasPayments() ){
if (contract.amap.hasPayments()) {
var orders = contract.getUserOrders(user);
if( orders.length == 0 ){
if (orders.length == 0) {
var operation = db.Operation.findCOrderTransactionFor(contract, user);
if(operation!=null) operation.delete();
if (operation != null)
operation.delete();
}
}
}
else { //Variable orders contract
} else { // Variable orders contract
//Get the basket for this user
// Get the basket for this user
var place = order.distribution.place;
var basket = db.Basket.get(user, place, order.distribution.date);
if( contract.amap.hasPayments() ){
if (contract.amap.hasPayments()) {
var orders = basket.getOrders();
//Check if it is the last order, if yes then delete the related operation
if( orders.length == 1 && orders.first().id==order.id ){
// Check if it is the last order, if yes then delete the related operation
if (orders.length == 1 && orders.first().id == order.id) {
var operation = db.Operation.findVOrderTransactionFor(order.distribution.getKey(), user, place.amap);
if(operation!=null) operation.delete();
if (operation != null)
operation.delete();
}
}
order.delete();
}
}
else {
} else {
throw new Error(t._("Deletion not possible: quantity is not zero."));
}
}
/**
@@ -285,21 +287,20 @@ class OrderService
var view = App.current.view;
var t = sugoi.i18n.Locale.texts;
for (o in orders) {
var x : UserOrder = cast { };
var x:UserOrder = cast {};
x.id = o.id;
x.userId = o.user.id;
x.userName = o.user.getCoupleName();
x.userEmail = o.user.email;
//shared order
if (o.user2 != null){
// shared order
if (o.user2 != null) {
x.userId2 = o.user2.id;
x.userName2 = o.user2.getCoupleName();
x.userEmail2 = o.user2.email;
}
//deprecated
// deprecated
x.productId = o.product.id;
x.productRef = o.product.ref;
x.productQt = o.product.qt;
@@ -308,45 +309,42 @@ class OrderService
x.productImage = o.product.getImage();
x.productHasFloatQt = o.product.hasFloatQt;
x.productHasVariablePrice = o.product.variablePrice;
//new way
// new way
x.product = o.product.infos();
x.product.price = o.productPrice;//do not use current price, but price of the order
x.product.price = o.productPrice; // do not use current price, but price of the order
x.quantity = o.quantity;
//smartQt
if (x.quantity == 0.0){
// smartQt
if (x.quantity == 0.0) {
x.smartQt = t._("Canceled");
}else if(x.productHasFloatQt || x.productHasVariablePrice || o.product.wholesale){
} else if (x.productHasFloatQt || x.productHasVariablePrice || o.product.wholesale) {
x.smartQt = view.smartQt(x.quantity, x.productQt, x.productUnit);
}else{
} else {
x.smartQt = Std.string(x.quantity);
}
//product name.
if ( x.productHasVariablePrice || x.productQt==null || x.productUnit==null ){
// product name.
if (x.productHasVariablePrice || x.productQt == null || x.productUnit == null) {
x.productName = o.product.name;
}else{
x.productName = o.product.name + " " + view.formatNum(x.productQt) +" "+ view.unit(x.productUnit,x.productQt>1);
} else {
x.productName = o.product.name + " " + view.formatNum(x.productQt) + " " + view.unit(x.productUnit, x.productQt > 1);
}
x.subTotal = o.quantity * o.productPrice;
var c = o.product.contract;
if ( o.feesRate!=0 ) {
x.fees = x.subTotal * (o.feesRate/100);
if (o.feesRate != 0) {
x.fees = x.subTotal * (o.feesRate / 100);
x.percentageName = c.percentageName;
x.percentageValue = o.feesRate;
x.total = x.subTotal + x.fees;
}else {
} else {
x.total = x.subTotal;
}
//flags
// flags
x.paid = o.paid;
x.invertSharedOrder = o.flags.has(InvertSharedOrder);
x.contractId = c.id;
@@ -363,12 +361,12 @@ class OrderService
* Confirms an order : create real orders from tmp orders in session
* @param order
*/
public static function confirmSessionOrder(tmpOrder:OrderInSession){
public static function confirmSessionOrder(tmpOrder:OrderInSession) {
var orders = [];
var user = db.User.manager.get(tmpOrder.userId);
for (o in tmpOrder.products){
for (o in tmpOrder.products) {
o.product = db.Product.manager.get(o.productId);
orders.push( make(user, o.quantity, o.product, o.distributionId) );
orders.push(make(user, o.quantity, o.product, o.distributionId));
}
App.current.event(MakeOrder(orders));
@@ -377,82 +375,89 @@ class OrderService
return orders;
}
/**
* Send an order-by-products report to the coordinator
*/
public static function sendOrdersByProductReport(d:db.Distribution){
public static function sendOrdersByProductReport(d:db.Distribution) {
var t = sugoi.i18n.Locale.texts;
var m = new sugoi.mail.Mail();
m.addRecipient(d.contract.contact.email , d.contract.contact.getName());
m.setSender(App.config.get("default_email"),"Cagette.net");
m.addRecipient(d.contract.contact.email, d.contract.contact.getName());
m.setSender(App.config.get("default_email"), t._("Cagette.net"));
m.setSubject('[${d.contract.amap.name}] Distribution du ${App.current.view.dDate(d.date)} (${d.contract.name})');
var orders = service.ReportService.getOrdersByProduct(d);
var html = App.current.processTemplate("mail/ordersByProduct.mtt", {
contract:d.contract,
distribution:d,
orders:orders,
formatNum:App.current.view.formatNum,
currency:App.current.view.currency,
dDate:App.current.view.dDate,
hHour:App.current.view.hHour,
group:d.contract.amap
} );
contract: d.contract,
distribution: d,
orders: orders,
formatNum: App.current.view.formatNum,
currency: App.current.view.currency,
dDate: App.current.view.dDate,
hHour: App.current.view.hHour,
group: d.contract.amap
});
m.setHtmlBody(html);
App.sendMail(m);
}
/**
* Order summary for a member
* WARNING : its for one distrib, not for a whole basket !
*/
public static function sendOrderSummaryToMembers(d:db.Distribution){
public static function sendOrderSummaryToMembers(d:db.Distribution) {
var t = sugoi.i18n.Locale.texts;
var title = '[${d.contract.amap.name}] Votre commande pour le ${App.current.view.dDate(d.date)} (${d.contract.name})';
for( user in d.getUsers() ){
for (user in d.getUsers()) {
var m = new sugoi.mail.Mail();
m.addRecipient(user.email , user.getName(),user.id);
if(user.email2!=null) m.addRecipient(user.email2 , user.getName(),user.id);
m.setSender(App.config.get("default_email"),"Cagette.net");
m.addRecipient(user.email, user.getName(), user.id);
if (user.email2 != null)
m.addRecipient(user.email2, user.getName(), user.id);
m.setSender(App.config.get("default_email"), t._("Cagette.net"));
m.setSubject(title);
var orders = prepare(d.contract.getUserOrders(user,d));
var orders = prepare(d.contract.getUserOrders(user, d));
var html = App.current.processTemplate("mail/orderSummaryForMember.mtt", {
contract:d.contract,
distribution:d,
orders:orders,
formatNum:App.current.view.formatNum,
currency:App.current.view.currency,
dDate:App.current.view.dDate,
hHour:App.current.view.hHour,
group:d.contract.amap
} );
contract: d.contract,
distribution: d,
orders: orders,
formatNum: App.current.view.formatNum,
currency: App.current.view.currency,
dDate: App.current.view.dDate,
hHour: App.current.view.hHour,
group: d.contract.amap
});
m.setHtmlBody(html);
App.sendMail(m);
}
}
public static function sort(orders:Array<UserOrder>){
//order by lastname (+lastname2 if exists), then contract
public static function sort(orders:Array<UserOrder>) {
// order by lastname (+lastname2 if exists), then contract
orders.sort(function(a, b) {
if (a.userName + a.userId + a.userName2 + a.userId2 + a.contractId > b.userName + b.userId + b.userName2 + b.userId2 + b.contractId ) {
if (a.userName
+ a.userId
+ a.userName2
+ a.userId2
+ a.contractId > b.userName
+ b.userId
+ b.userName2
+ b.userId2
+ b.contractId) {
return 1;
}
if (a.userName + a.userId + a.userName2 + a.userId2 + a.contractId < b.userName + b.userId + b.userName2 + b.userId2 + b.contractId ) {
if (a.userName
+ a.userId
+ a.userName2
+ a.userId2
+ a.contractId < b.userName
+ b.userId
+ b.userName2
+ b.userId2
+ b.contractId) {
return -1;
}
return 0;
@@ -460,9 +465,4 @@ class OrderService
return orders;
}
}
+2
View File
@@ -13,6 +13,8 @@ class WaitingListService{
var w = new db.WaitingList();
w.user = user;
w.group = group;
if (message == null ) message = "";
//message = "souscription = " + message;
w.message = message;
w.insert();
+135 -126
View File
@@ -1,25 +1,25 @@
package sugoi;
import sugoi.i18n.TemplateTranslator;
import sugoi.Web;
class BaseApp {
public var cnx:sys.db.Connection;
public var template:templo.Loader;
public var maintain:Bool;
public var session:sugoi.db.Session;
public var view:View;
public var user:db.User;
public var params:Map<String, String>;
public var cookieName:String;
public var cookieDomain:String;
public var uri:String;
public var cnx : sys.db.Connection;
public var template : templo.Loader;
public var maintain : Bool;
public var session : sugoi.db.Session;
public var view : View;
public var user : db.User;
public var params : Map<String,String>;
public var cookieName : String;
public var cookieDomain : String;
public var uri : String;
public static var config:Config;
public static var config: Config;
//public static var classPathes = sugoi.tools.Macros.getClassPathes();
// public static var classPathes = sugoi.tools.Macros.getClassPathes();
public function new() {
if (config == null) {
loadConfig();
}
@@ -28,24 +28,32 @@ class BaseApp {
cookieDomain = "." + App.config.HOST;
#if plugins
if( false ) sugoi.plugin.PlugIn.copyTpl();
if (false)
sugoi.plugin.PlugIn.copyTpl();
#end
// This macro generates translated templates for each langage
#if i18n_generation
if( false ) TemplateTranslator.parse("lang/master");
if (false)
TemplateTranslator.parse("lang/master");
#end
}
public function loadConfig() {
App.config = BaseApp.config = new sugoi.Config();
var PATH = Web.getCwd() + "../";
var json = haxe.Json.parse(sys.io.File.getContent(PATH + "package.json"));
App.VERSION = json.version;
App.log('configuration has been reloaded, VERSION=${App.VERSION}');
}
public function loadTemplate( t : String ) {
public function loadTemplate(t:String) {
templo.Loader.OPTIMIZED = App.config.DEBUG == false;
templo.Loader.BASE_DIR = App.config.TPL;
templo.Loader.TMP_DIR = App.config.TPL_TMP;
if ( t == null ) return null;
if (t == null)
return null;
#if neko
return new templo.Loader(t, App.config.getBool("cachetpl"));
#else
@@ -53,39 +61,39 @@ class BaseApp {
#end
}
public function setTemplate( t : String ) {
public function setTemplate(t:String) {
template = t == null ? null : loadTemplate(t);
}
public function initLang( lang : String ) {
public function initLang(lang:String) {
if (lang == null || lang == "")
lang = config.LANG;
if (lang == null || lang == "") lang = config.LANG;
//Define template path
// Define template path
var path;
if (!App.config.DEBUG){
if (!App.config.DEBUG) {
path = Web.getCwd() + "../lang/" + lang + "/";
}else{
} else {
path = Web.getCwd() + "../lang/master/";
}
App.config.TPL = path + "tpl/";
App.config.TPL_TMP = path + "tmp/";
//init system locale
if ( !Sys.setTimeLocale("en_US.UTF-8") ) {
// init system locale
if (!Sys.setTimeLocale("en_US.UTF-8")) {
Sys.setTimeLocale("en");
}
//init gettext translator
// init gettext translator
sugoi.i18n.Locale.init(lang);
return true;
}
function saveAndClose() {
if( cnx == null ) return;
if( session.sid != null )
if (cnx == null)
return;
if (session.sid != null)
session.update();
cnx.commit();
@@ -94,26 +102,28 @@ class BaseApp {
untyped cnx.request = function(s) return null;
}
function executeTemplate( ?save ) {
function executeTemplate(?save) {
view.init();
var result = template.execute(view);
if ( save ) saveAndClose();
if (save)
saveAndClose();
#if php
//strange bug with templo in PHP
if (result.substr(0, 4) == "null") result = result.substr(4);
// strange bug with templo in PHP
if (result.substr(0, 4) == "null")
result = result.substr(4);
#end
Sys.print(result);
}
function onMeta( m : String, args : Array<Dynamic> ) {
switch( m ) {
function onMeta(m:String, args:Array<Dynamic>) {
switch (m) {
case "tpl":
setTemplate(args[0]);
case "logged":
if ( user == null )
throw sugoi.ControllerAction.RedirectAction("/?__redirect="+Web.getURI());
if (user == null)
throw sugoi.ControllerAction.RedirectAction("/?__redirect=" + Web.getURI());
case "admin":
if( user == null || !user.isAdmin() )
if (user == null || !user.isAdmin())
throw sugoi.ControllerAction.RedirectAction("/");
default:
}
@@ -124,111 +134,109 @@ class BaseApp {
*/
function detectLang() {
var l = Web.getClientHeader("Accept-Language");
if( l != null )
for( l in l.split(",") ) {
if (l != null)
for (l in l.split(",")) {
l = l.split(";")[0];
l = l.split("-")[0];
l = StringTools.trim(l);
for( a in App.config.LANGS )
if( a == l )
for (a in App.config.LANGS)
if (a == l)
return a;
}
return App.config.LANG;
}
/**
* Setup current app language
*/
function setupLang() {
// this app is monolingual and doesn't manage i18n
if (App.config.LANG == "master")
return;
//this app is monolingual and doesn't manage i18n
if (App.config.LANG == "master") return;
//lang is taken from user object or from HTTP headers
if ( session.lang == null || !Lambda.has(App.config.LANGS, session.lang) ){
// lang is taken from user object or from HTTP headers
if (session.lang == null || !Lambda.has(App.config.LANGS, session.lang)) {
session.lang = (user == null) ? detectLang() : user.lang;
}
//override if param is given
// override if param is given
var lang = params.get("lang");
if ( lang != null && Lambda.has(App.config.LANGS, lang) ){
if (lang != null && Lambda.has(App.config.LANGS, lang)) {
session.lang = lang;
}
//init lang
// init lang
initLang(session.lang);
}
/**
* Get current application langage (2 letters lowercase)
*/
public function getLang(){
public function getLang() {
return (session != null && session.lang != null && session.lang != "") ? session.lang : App.config.LANG;
}
public function rollback() {
if( cnx != null ) cnx.rollback();
if (cnx != null)
cnx.rollback();
sys.db.Manager.cleanup();
if( user != null && session != null )
if (user != null && session != null)
user = session.user;
// does not reset session
}
public function setCookie( oldCookie : String ){
if( session != null && session.sid != null && session.sid != oldCookie ) {
Web.setHeader("Set-Cookie", cookieName+"=" + session.sid + "; path=/;");
public function setCookie(oldCookie:String) {
if (session != null && session.sid != null && session.sid != oldCookie) {
Web.setHeader("Set-Cookie", cookieName + "=" + session.sid + "; path=/;");
}
}
function mainLoop() {
params = Web.getParams();
//Get session
// Get session
var sids = [];
var cookieSid = Web.getCookies().get(cookieName);
if( params.exists("sid") ) sids.push(params.get("sid"));
if( cookieSid != null ) sids.push(cookieSid);
if (params.exists("sid"))
sids.push(params.get("sid"));
if (cookieSid != null)
sids.push(cookieSid);
session = sugoi.db.Session.init(sids);
//Check for maintenance
// Check for maintenance
maintain = sugoi.db.Variable.getInt("maintain") != 0;
user = session.user;
//setup langage
// setup langage
setupLang();
if( maintain && ((user != null && user.isAdmin()) ) )
if (maintain && ((user != null && user.isAdmin())))
maintain = false;
setCookie(cookieSid);
if( maintain ) {
if (maintain) {
setTemplate("maintain.mtt");
executeTemplate();
return;
}
//dispatching
// dispatching
try {
uri = Web.getURI();
if ( StringTools.endsWith(uri, "/index.n") ) uri = uri.substr(0, -8);
if (StringTools.endsWith(uri, "/index.n"))
uri = uri.substr(0, -8);
//"before dispatch" callback
// "before dispatch" callback
beforeDispatch();
var d = new haxe.web.Dispatch(uri, params);
d.onMeta = onMeta;
d.dispatch(new controller.Main());
} catch ( e : haxe.web.Dispatch.DispatchError ) {
//dispatch / routing error
if ( App.config.DEBUG ) {
} catch (e:haxe.web.Dispatch.DispatchError) {
// dispatch / routing error
if (App.config.DEBUG) {
#if neko
neko.Lib.rethrow(e);
#else
@@ -238,32 +246,34 @@ class BaseApp {
cnx.rollback();
Web.redirect("/");
return;
} catch ( e : sugoi.ControllerAction) {
switch( e ) {
} catch (e:sugoi.ControllerAction) {
switch (e) {
case RedirectAction(url):
Web.redirect(url);
template = null;
case ErrorAction(url, text), OkAction(url,text):
if( text == null ) {
case ErrorAction(url, text), OkAction(url, text):
if (text == null) {
text = url;
url = Web.getURI();
}
Web.redirect(url);
var error = switch(e) { case ErrorAction(_): true; default: false; };
if( error ) rollback();
if ( error ) {
session.addMessage(text,true);
}else {
var error = switch (e) {
case ErrorAction(_): true;
default: false;
};
if (error)
rollback();
if (error) {
session.addMessage(text, true);
} else {
session.addMessage(text);
}
template = null;
}
}
//Render template
if ( template == null ) {
// Render template
if (template == null) {
saveAndClose();
} else {
executeTemplate(true); // will saveAndClose
@@ -274,12 +284,10 @@ class BaseApp {
* Override this function if you want
* to insert some actions
*/
public function beforeDispatch() {
public function beforeDispatch() {}
}
public function logError( e : Dynamic, ?stack : String ) {
var stack = if( stack != null ) stack else haxe.CallStack.toString(haxe.CallStack.exceptionStack());
public function logError(e:Dynamic, ?stack:String) {
var stack = if (stack != null) stack else haxe.CallStack.toString(haxe.CallStack.exceptionStack());
var message = new StringBuf();
message.add(Std.string(e));
message.add("\n");
@@ -288,43 +296,42 @@ class BaseApp {
var e = new sugoi.db.Error();
e.url = Web.getURI();
e.ip = Web.getClientIP();
e.user = if( user != null ) user else null;
e.user = if (user != null) user else null;
e.date = Date.now();
e.userAgent = Web.getClientHeader("User-Agent");
e.error = message.toString();
e.insert();
}
function errorHandler( e:Dynamic ) {
function errorHandler(e:Dynamic) {
try {
var stack = haxe.CallStack.toString(haxe.CallStack.exceptionStack());
// ROLLBACK and LOG
if ( cnx != null ) {
if (cnx != null) {
cnx.rollback();
logError(e,stack);
logError(e, stack);
}
//log also in a file, in case we don't have a valid connexion to DB
Web.logMessage(e+"\n" + stack);
// log also in a file, in case we don't have a valid connexion to DB
Web.logMessage(e + "\n" + stack);
maintain = true;
view = new View();
view.message = Std.string(e);
if ( App.config.DEBUG || (user != null && user.isAdmin()) ) {
if (App.config.DEBUG || (user != null && user.isAdmin())) {
view.stack = stack;
}
setTemplate("error.mtt");
executeTemplate(false);
} catch( e : Dynamic ) {
} catch (e:Dynamic) {
Sys.print("<pre>");
Sys.println("Error : "+try Std.string(e) catch( e : Dynamic ) "???");
Sys.println("Error : " + try Std.string(e) catch (e:Dynamic) "???");
Sys.println(haxe.CallStack.toString(haxe.CallStack.exceptionStack()));
try {
if( cnx != null )
sugoi.db.Error.manager.get(0,false);
} catch( e : Dynamic ) {
if (cnx != null)
sugoi.db.Error.manager.get(0, false);
} catch (e:Dynamic) {
Sys.println("Initializing Database...");
sys.db.Admin.initializeDatabase();
Sys.println("Done");
@@ -339,7 +346,7 @@ class BaseApp {
*/
function init() {
maintain = App.config.getBool("maintain");
if( maintain ) {
if (maintain) {
view = new View();
setTemplate("maintain.mtt");
executeTemplate(false);
@@ -348,23 +355,23 @@ class BaseApp {
try {
var dbstr = App.config.get("database");
var dbreg = ~/([^:]+):\/\/([^:]+):([^@]*?)@([^:]+)(:[0-9]+)?\/(.*?)$/;
if( !dbreg.match(dbstr) )
if (!dbreg.match(dbstr))
throw "Configuration requires a valid database attribute, format is : mysql://user:password@host:port/dbname";
var port = dbreg.matched(5);
var dbparams = {
user:dbreg.matched(2),
pass:dbreg.matched(3),
host:dbreg.matched(4),
port:port == null ? 3306 : Std.parseInt(port.substr(1)),
database:dbreg.matched(6),
socket:null
user: dbreg.matched(2),
pass: dbreg.matched(3),
host: dbreg.matched(4),
port: port == null ? 3306 : Std.parseInt(port.substr(1)),
database: dbreg.matched(6),
socket: null
};
cnx = sys.db.Mysql.connect(dbparams);
} catch( e : Dynamic ) {
} catch (e:Dynamic) {
errorHandler(e);
return false;
}
if( App.config.SQL_LOG )
if (App.config.SQL_LOG)
cnx = new sugoi.tools.DebugConnection(cnx);
return true;
}
@@ -372,7 +379,7 @@ class BaseApp {
function cloneApp() {
// ensure that we have no variable initialized in app loop
var app = new App();
var bapp : BaseApp = app;
var bapp:BaseApp = app;
bapp.cnx = cnx;
bapp.view = new View();
App.current = app;
@@ -380,13 +387,15 @@ class BaseApp {
}
function run() {
// Will close the connection
sys.db.Transaction.main(cnx, cloneApp, function(e) { var b : BaseApp = App.current; b.errorHandler(e); });
sys.db.Transaction.main(cnx, cloneApp, function(e) {
var b:BaseApp = App.current;
b.errorHandler(e);
});
App.current = null;
}
function sendHeaders(){
function sendHeaders() {
Web.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
Web.setHeader("Pragma", "no-cache");
Web.setHeader("Expires", "-1");
@@ -396,28 +405,28 @@ class BaseApp {
}
static function main() {
/**
* this macro will parse the code and generate the allTexts.pot file
* which will be used as a template for translation files (*.po and *.mo)
*/
#if i18n_parsing
if( false ) sugoi.i18n.GetText.parse(["src", "lang/master","js","common"], "www/lang/allTexts.pot");
if (false)
sugoi.i18n.GetText.parse(["src", "lang/master", "js", "common"], "www/lang/allTexts.pot");
#end
App.current = new App();
var a : BaseApp = App.current;
var a:BaseApp = App.current;
a.sendHeaders();
if( !a.init() ) {
if (!a.init()) {
a = null;
return;
}
a.run();
a = null;
#if neko
if ( App.config.getInt("cache", 0) == 1 ) {
if (App.config.getInt("cache", 0) == 1) {
neko.Web.cacheModule(App.main);
}
#end
+2
View File
@@ -20,12 +20,14 @@ class BaseView implements Dynamic {
this.HOST = App.config.HOST;
this.DATA_HOST = App.config.DATA_HOST;
this.DEBUG = App.config.DEBUG;
this.BANDEAU_REGION = App.config.BANDEAU_REGION;
this.NAME = App.config.NAME;
this.isAdmin = app.user != null && app.user.isAdmin();
//Access basic functions in views
this.Std = Std;
this.Math = Math;
this.Config = App.config;
if ( App.config.SQL_LOG ) {
+34 -33
View File
@@ -3,61 +3,62 @@ package sugoi;
import sugoi.Web;
class Config {
public var PATH :String;
//public var json : Dynamic;
public var xml : Xml;
public var LANG :String;
public var LANGS :Array<String>;
public var TPL :String;
public var TPL_TMP :String;
public var DEBUG :Bool;
public var HOST :String;
public var NAME :String;
public var KEY :String;
public var DATA_HOST :String;
public var SQL_LOG :Bool;
public var PATH:String;
// public var json : Dynamic;
public var xml:Xml;
public var LANG:String;
public var LANGS:Array<String>;
public var TPL:String;
public var TPL_TMP:String;
public var DEBUG:Bool;
public var BANDEAU_REGION:Bool;
public var HOST:String;
public var NAME:String;
public var KEY:String;
public var DATA_HOST:String;
public var SQL_LOG:Bool;
public function new(?path:String) {
PATH = (path != null) ? path : Web.getCwd() + "../";
//json = haxe.Json.parse(sys.io.File.getContent(PATH + "config.json"));
xml = Xml.parse(sys.io.File.getContent(PATH + "config.xml")).firstElement();
LANG = get("lang");
LANGS = get("langs").split(";");
NAME = get("name", "cagettepei");
LANG = get("lang", "fr");
LANGS = get("langs", "fr").split(";");
TPL = PATH + "lang/" + LANG + "/tpl/";
TPL_TMP = PATH + "lang/" + LANG + "/tmp/";
DEBUG = get("debug","0") == "1";
DEBUG = get("debug", "0") == "1";
BANDEAU_REGION = get("bandeau_region", "0") == "1";
HOST = get("host");
NAME = get("name");
KEY = get("key");
DATA_HOST = get("dataHost","data."+HOST);
DATA_HOST = get("dataHost", "data." + HOST);
SQL_LOG = getBool("sqllog", false);
}
public function defined( val : String ) {
//return Reflect.field(xml,val) != null;
public function defined(val:String) {
// return Reflect.field(xml,val) != null;
return xml.get(val) != null;
}
public function getBool( val : String, ?def ) {
public function getBool(val:String, ?def) {
var v = get(val);
if( v == null ) return def;
return( v == "1" || v == "true" );
if (v == null)
return def;
return (v == "1" || v == "true");
}
public function get( val : String, ?def : String ) : String {
//var v = Reflect.field(xml,val);
public function get(val:String, ?def:String):String {
// var v = Reflect.field(xml,val);
var v = xml.get(val);
if( v == null )
if (v == null)
v = def;
if( v == null )
throw "Missing config attribute : '"+val+"'";
if (v == null)
throw "Missing config attribute : '" + val + "'";
return v;
}
public function getInt( val : String, ?def : Int ) : Int {
return Std.parseInt(get(val,Std.string(def)));
public function getInt(val:String, ?def:Int):Int {
return Std.parseInt(get(val, Std.string(def)));
}
}
+47 -40
View File
@@ -1,38 +1,46 @@
package sugoi.mail;
using Lambda;
class Mail implements IMail
{
public var title : String;
public var htmlBody : String;
public var textBody : String;
var headers : Map<String,String>;
var sender : {name:String,email:String,?userId:Int};
var recipients : Array<{name:String,email:String,?userId:Int}>;
class Mail implements IMail {
public var title:String;
public var htmlBody:String;
public var textBody:String;
var headers:Map<String, String>;
var sender:{name:String, email:String, ?userId:Int};
var recipients:Array<{name:String, email:String, ?userId:Int}>;
static public function fromString(name:String, email:String) {
if (name == null)
return email;
else {
var finalName = thx.text.Diactrics.clean(name);
return '${finalName} <${email}>';
}
}
public function new() {
recipients = [];
headers = new Map();
}
public function getRecipients(){
public function getRecipients() {
return recipients;
}
public function setSender(email, ?name,?userId) {
if(!isValid(email)) throw "invalid sender email : \""+email+"\"";
sender = {name:name,email:email,userId:userId};
public function setSender(email, ?name, ?userId) {
if (!isValid(email))
throw "invalid sender email : \"" + email + "\"";
var finalName = (name != null) ? thx.text.Diactrics.clean(name) : null;
sender = {name: finalName, email: email, userId: userId};
return this;
}
public function setReplyTo(email, ?name) {
if(!isValid(email)) throw "invalid reply-to email : \""+email+"\"";
setHeader("Reply-To","<"+email+">"+(name==null?"":name));
if (!isValid(email))
throw "invalid reply-to email : \"" + email + "\"";
setHeader("Reply-To", fromString(name, email));
}
public function setSubject(s:String) {
@@ -47,8 +55,9 @@ class Mail implements IMail
* @param ?userId
*/
public function addRecipient(email:String, ?name:String, ?userId:Int) {
if(!isValid(email)) throw "invalid recipient \""+email+"\"";
recipients.push( {email:email, name:name, userId:userId } );
if (!isValid(email))
throw "invalid recipient \"" + email + "\"";
recipients.push({email: email, name: name, userId: userId});
return this;
}
@@ -63,7 +72,7 @@ class Mail implements IMail
return this;
}
public static function isValid( addr : String ){
public static function isValid(addr:String) {
var reg = ~/^[^()<>@,;:\\"\[\]\s[:cntrl:]]+@[A-Z0-9][A-Z0-9-]*(\.[A-Z0-9][A-Z0-9-]*)*\.(xn--[A-Z0-9]+|[A-Z]{2,8})$/i;
return addr != null && reg.match(addr);
}
@@ -77,7 +86,7 @@ class Mail implements IMail
* generate a custom key for transactionnal emails, valid during the current day
*/
public function getKey() {
return haxe.crypto.Md5.encode(App.config.get("key")+recipients[0].email+(Date.now().getDate())).substr(0,12);
return haxe.crypto.Md5.encode(App.config.get("key") + recipients[0].email + (Date.now().getDate())).substr(0, 12);
}
/**
@@ -88,7 +97,8 @@ class Mail implements IMail
public function setHtmlBodyWithTemplate(tpl, ctx:Dynamic) {
var app = App.current;
var tpl = app.loadTemplate(tpl);
if( ctx == null ) ctx = { };
if (ctx == null)
ctx = {};
ctx.HOST = App.config.HOST;
ctx.key = getKey();
ctx.senderName = sender.name;
@@ -98,7 +108,6 @@ class Mail implements IMail
ctx.recipients = recipients;
CSSInlining(ctx);
htmlBody = tpl.execute(ctx);
}
public function setHtmlBody(s) {
@@ -109,17 +118,17 @@ class Mail implements IMail
public function setTextBodyWithTemplate(tpl, ctx:Dynamic) {
var app = App.current;
var tpl = app.loadTemplate(tpl);
if( ctx == null ) ctx = { };
if (ctx == null)
ctx = {};
ctx.HOST = App.config.HOST;
ctx.key = getKey();
textBody = tpl.execute(ctx);
return this;
}
function CSSInlining(ctx) {
// CSS inlining
var css : Map<String,Array<String>> = new Map();
var css:Map<String, Array<String>> = new Map();
ctx.addStyle = function(sel:String, style:String) {
sel = sel.toLowerCase();
if (css.exists(sel))
@@ -130,11 +139,11 @@ class Mail implements IMail
}
var applyStyleRec = null;
applyStyleRec = function(x:Xml) {
if (x.nodeType==Xml.Element) {
if (x.nodeType == Xml.Element) {
var name = x.nodeName.toLowerCase();
if( css.exists(name) )
if (x.get("style")!=null)
x.set("style", x.get("style")+";"+css.get(name).join(";"));
if (css.exists(name))
if (x.get("style") != null)
x.set("style", x.get("style") + ";" + css.get(name).join(";"));
else
x.set("style", css.get(name).join(";"));
for (n in x)
@@ -143,40 +152,38 @@ class Mail implements IMail
}
ctx.applyStyle = function(raw:String) {
var x = Xml.parse(raw);
for(n in x)
for (n in x)
applyStyleRec(n);
return x.toString();
}
}
public function getSubject(){
public function getSubject() {
return title;
}
public function getTitle(){
public function getTitle() {
return getSubject();
}
public function getHtmlBody(){
public function getHtmlBody() {
return htmlBody;
}
public function getTextBody(){
public function getTextBody() {
return textBody;
}
public function setTextBody(t){
public function setTextBody(t) {
textBody = t;
return this;
}
public function getHeaders(){
public function getHeaders() {
return headers;
}
public function getSender(){
public function getSender() {
return sender;
}
}
+45 -31
View File
@@ -41,52 +41,66 @@ class SendEmailMailer implements IMailer {
}
public function send(e:sugoi.mail.IMail, ?params:Dynamic, ?callback:MailerResult->Void) {
var mailServer = "mail1.zourit.net";
var mailPort = 587;
var to:String;
var bcc:String;
var fromName = "John Doe";
var fromEmail = "no-reply@comptoirduvrac.re";
var recipientCounts = e.getRecipients().length;
if (recipientCounts > 1) {
to = sugoi.mail.Mail.fromString(e.getSender().name, e.getSender().email);
bcc = [for (i in e.getRecipients()) i.email].join(",");
App.log('recipients has been transformed to ${recipientCounts} bcc');
App.log(bcc);
} else {
to = e.getRecipients()[0].email;
bcc = "";
}
var args = [
"-f",
'${fromName} <${fromEmail}>',
// arguments
"-s",
"mail1.zourit.net:587",
'${this.m.host}:${this.m.port}',
"-xu",
${this.m.auth.username},
"-xp",
${this.m.auth.password},
"-o",
"message-charset=utf-8",
"-o",
"message-format=html",
"-o",
"message-header=X-Mailer: CagettePei",
"-f",
sugoi.mail.Mail.fromString(e.getSender().name, e.getSender().email),
"-t",
"pvincent@comptoirduvrac.re",
// "-bcc", "pvincent974@gmail.com,pvincent974@laposte.net",
to,
"-bcc",
bcc,
"-u",
e.getSubject(),
"-m",
"alors tout va bien",
"-xu",
"postmaster@comptoirduvrac.re",
"-xp",
"QqQeAPT6EpoK"
];
App.log('args=${args.join(" ")}');
// FIME: deal with replyTo
if (e.getHeaders().exists("Reply-To")) {
var replyTo = e.getHeaders()["Reply-To"];
App.log('replyTo "$replyTo" detected');
args.push("-o");
args.push('message-header=Reply-To: ${replyTo}');
}
args.push("-m");
args.push('<html>${e.getHtmlBody()}</html>');
// if (App.config.DEBUG)
// App.log('args=${args.join(" ")}');
var exitCode = Sys.command("sendemail", args);
// var exitCode = 0;
var summary = 'email from="${sugoi.mail.Mail.fromString(e.getSender().name, e.getSender().email)}" subject=<${e.getSubject()}>';
if (exitCode == 0)
App.log('email from="${e.getSender().name} <${e.getSender().email}"> subject=<${e.getSubject()}> successfully sent');
App.log('$summary successfully sent');
else
App.log('ERROR: email from="${e.getSender().name} <${e.getSender().email}"> subject=<${e.getSubject()}> cannot be sent');
// sys.io.File.saveContent('/tmp/my_file.json', "CONTENT");
// var surprise = m.send({
// subject: e.getSubject(),
// from: new Address({address: e.getSender().email}),
// to: Lambda.array(Lambda.map(e.getRecipients(), function(x) return new Address({address: x.email}))),
// headers: e.getHeaders(),
// content: {
// text: e.getTextBody(),
// html: e.getHtmlBody()
// }
// });
App.log('ERROR: $summary cannot be sent, please review logs');
if (callback != null) {
var map = new MailerResult();
-68
View File
@@ -1,68 +0,0 @@
package sugoi.mail;
import tink.core.Future;
import tink.core.Noise;
import sugoi.mail.IMailer;
import smtpmailer.Address;
/**
* Send emails thru SMTP by using ben merckx's library
* @ref https://github.com/benmerckx/smtpmailer
*/
class SmtpMailer implements IMailer
{
var m : smtpmailer.SmtpMailer;
public function new(){}
public function init(?conf:{smtp_host:String,smtp_port:Int,smtp_user:String,smtp_pass:String}) :IMailer
{
m = new smtpmailer.SmtpMailer({
host: conf.smtp_host,
port: conf.smtp_port,
auth: {
username: conf.smtp_user,
password: conf.smtp_pass
}
});
return this;
}
public function send(e:sugoi.mail.IMail,?params:Dynamic,?callback:MailerResult->Void)
{
var surprise = m.send({
subject: e.getSubject(),
/*from: e.getSender().email,
to: Lambda.array(Lambda.map(e.getRecipients(), function(x) return smtpmailer.Address.ofString(x.email) )),
//headers : e.getHeaders(),*/
from: new Address({address:e.getSender().email}),
to: Lambda.array(Lambda.map(e.getRecipients(), function(x) return new Address({address:x.email}) )),
headers : e.getHeaders(),
content: {
text: e.getTextBody(),
html: e.getHtmlBody()
}/*,
attachments: []*/
});
if (callback != null){
surprise.handle(function(s){
var map = new MailerResult();
switch(s){
case Success(_):
map.set("*",Success(Sent));
case Failure(e):
map.set("*",Failure(GenericError(e)));
}
callback(map);
});
}
}
}
+395 -444
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
ErrorDocument 404 /index.n
Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

+5 -4
View File
@@ -14,6 +14,7 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Poedit 2.0.6\n"
#: src/payment/Cash.hx:294
msgid "Cash"
msgstr "Espèces / Carte Bancaire"
@@ -1538,7 +1539,7 @@ msgid ""
"Group and user 'admin' created. Your email is 'admin@cagette.net' and your "
"password is 'admin'"
msgstr ""
"Groupe et utilisateur 'admin' créé. Votre email est 'admin@cagette.net' et "
"Groupe et utilisateur 'admin' créé. Votre email est 'admin@cagettepei.re' et "
"votre mot de passe est 'admin'"
#: src/controller/Install.hx:4677
@@ -2237,7 +2238,7 @@ msgid ""
"Welcome in the group of ::name::!\n"
" You can look at the delivery planning or make a new order."
msgstr ""
"Bienvenue sur la cagette de ::name:: !\n"
"Bienvenue sur le CagettePéi de ::name:: !\n"
" Vous pouvez consulter votre planning de distribution ou faire une nouvelle "
"commande."
@@ -4551,7 +4552,7 @@ msgstr "Extrait non exhaustif des produits proposés :"
#: lang/master/tpl/group/view.mtt:5027
msgid "http://www.cagette.net"
msgstr "http://www.cagette.net"
msgstr "https://git.artcode.re/cagetters/cagettepei"
#: lang/master/tpl/group/view.mtt:5060
msgid "Cagette.net, the free software of food direct-selling"
@@ -4618,7 +4619,7 @@ msgid ""
"Cagette.net is made by <a href='https://www.alilo.fr' target='_blank'><img "
"src='/img/alilo-trans-black.png' /></a>"
msgstr ""
"CagettePéi est une fourche du logiciel Cagette.net réalisé par <a href='https://www.alilo.fr' "
"CagettePéi est un logiciel libre, fourche du logiciel Cagette "
"target='_blank'><img src='/img/alilo-trans-black.png' /></a>"
#: lang/master/tpl/validate/user.mtt:2124