Compare commits

..
45 Commits
Author SHA1 Message Date
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
39 changed files with 2486 additions and 2769 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.13.4
+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
+60 -44
View File
@@ -1,67 +1,80 @@
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
DocumentRoot /var/www/cagettepei/www
DirectoryIndex index.n
DocumentRoot /var/www/cagettepei/www/
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>"
database = "mysql://cagettepei:cagettepei@localhost/cagettepei"
key = "<SECRET_PHRASE>"
name = "CagettePéi"
default_email = "<EMAIL>"
webmaster_email = "<EMAIL>"
database="mysql://cagettepei:cagettepei@localhost/cagettepei"
smtp_host="localhost"
smtp_port="25"
smtp_user=""
smtp_pass=""
lang="fr"
langs="fr"
langnames="Français"
maintain="0"
debug = "0"
sqllog = "0"
maintain = "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"
/>
+16 -20
View File
@@ -1,4 +1,5 @@
package react.user;
import react.ReactComponent;
import react.ReactMacro.jsx;
import Common;
@@ -19,13 +20,12 @@ 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};
}
@@ -35,7 +35,6 @@ class LoginBox extends react.ReactComponentOfPropsAndState<LoginBoxProps,LoginBo
}
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>
-->
@@ -85,7 +84,6 @@ class LoginBox extends react.ReactComponentOfPropsAndState<LoginBoxProps,LoginBo
* displays a registerBox
*/
public function registerBox() {
var body = js.Browser.document.querySelector('#myModal .modal-body');
ReactDOM.unmountComponentAtNode(body);
@@ -94,7 +92,6 @@ class LoginBox extends react.ReactComponentOfPropsAndState<LoginBoxProps,LoginBo
}
public function submit(?e:js.html.Event) {
if (state.email == "") {
setError("Veuillez saisir votre email");
return;
@@ -111,29 +108,28 @@ class LoginBox extends react.ReactComponentOfPropsAndState<LoginBoxProps,LoginBo
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) {
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();
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"/>
+17 -102
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,7 +117,7 @@
<!--<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>
@@ -129,17 +125,20 @@
<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 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="http://www.cagette.net/wiki" target="_blank">::_("Documentation")::</a>
<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>
@@ -149,57 +148,18 @@
</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>
<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.png' alt='bandeau Région Réunion' width='250'></img>
::end::
</div>
<div class="row">
@@ -219,49 +179,4 @@
</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::
+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::-->
+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.13.4"}
+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);
+158 -166
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,14 +29,12 @@ 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 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++;
}
@@ -51,7 +49,8 @@ class Contract extends Controller
}
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>>();
@@ -64,7 +63,8 @@ class Contract extends Controller
constOrders = [];
for (c in contracts) {
var orders = app.user.getOrdersFromContracts([c]);
if (orders.length == 0) continue;
if (orders.length == 0)
continue;
constOrders.push({contract: c, orders: service.OrderService.prepare(orders)});
}
@@ -91,7 +91,6 @@ class Contract extends Controller
// 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]));
@@ -106,14 +105,10 @@ class Contract extends Controller
view.varOrders = varOrders2;
view.constOrders = constOrders;
// tutorials
if (app.user.isAmapManager()) {
// actions
if (app.params.exists('startTuto')) {
// start a tuto
app.user.lock();
var t = app.params.get('startTuto');
@@ -121,7 +116,6 @@ class Contract extends Controller
app.user.update();
}
// tuto state
var tutos = new Array<{name:String, completion:Float, key:String}>();
@@ -129,7 +123,8 @@ class Contract extends Controller
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});
}
@@ -155,9 +150,9 @@ 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});
@@ -176,7 +171,8 @@ class Contract extends Controller
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."));
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()) {
@@ -193,7 +189,6 @@ class Contract extends Controller
app.session.addMessage(t._("Managing stock is not available for CSA contracts"), true);
}
c.update();
// update rights
@@ -212,7 +207,6 @@ class Contract extends Controller
x.update();
}
}
}
throw Ok("/contractAdmin/view/" + c.id, t._("Contract updated"));
@@ -224,7 +218,6 @@ class Contract extends Controller
@tpl("contract/insertChoose.mtt")
function doInsertChoose() {
// checkToken();
}
/**
@@ -232,8 +225,10 @@ 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");
@@ -271,8 +266,8 @@ 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();
@@ -282,7 +277,8 @@ class Contract extends Controller
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."));
}
@@ -315,12 +311,13 @@ 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"));
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
@@ -335,7 +332,6 @@ class Contract extends Controller
var products = c.getProducts();
if (c.type == db.Contract.TYPE_VARORDER) {
for (d in distributions) {
var datas = [];
for (p in products) {
@@ -343,33 +339,30 @@ class Contract extends Controller
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});
}
} else {
var datas = [];
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});
}
// form check
if (checkToken()) {
// get dsitrib if needed
// var distrib : db.Distribution = null;
// if (c.type == db.Contract.TYPE_VARORDER) {
@@ -377,117 +370,11 @@ class Contract extends Controller
// }
for (k in app.params.keys()) {
if (k.substr(0, 1) != "d") continue;
var qt = app.params.get(k);
if (qt == "") continue;
var pid = null;
var did = null;
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); }
//find related element in userOrders
var uo = null;
for ( x in userOrders){
if (x.distrib!=null && x.distrib.id != did) {
if (k.substr(0, 1) != "d")
continue;
}else{
for (a in x.datas){
if (a.product.id == pid){
uo = a;
break;
}
}
}
}
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 ) {
var param = StringTools.replace(qt, ",", ".");
q = Std.parseFloat(param);
}else {
q = Std.parseInt(qt);
}
if (uo.order != null) {
OrderService.edit(uo.order, q);
}else {
OrderService.make(app.user, q, uo.product, did);
}
}
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"));
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);
}
//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){
var datas = [];
for ( p in products) {
var ua = { order:null, product:p };
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, true);
}
if (order != null) ua.order = order;
datas.push(ua);
}
userOrders.push({distrib:d,datas:datas});
}
//form check
if (checkToken()) {
//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};
for (k in app.params.keys()) {
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;
@@ -513,7 +400,117 @@ 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) {
var param = StringTools.replace(qt, ",", ".");
q = Std.parseFloat(param);
} else {
q = Std.parseInt(qt);
}
if (uo.order != null) {
OrderService.edit(uo.order, q);
} else {
OrderService.make(app.user, q, uo.product, did);
}
}
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"));
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);
}
// 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) {
var datas = [];
for (p in products) {
var ua = {order: null, product: p};
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, true);
}
if (order != null)
ua.order = order;
datas.push(ua);
}
userOrders.push({distrib: d, datas: datas});
}
// form check
if (checkToken()) {
// 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};
for (k in app.params.keys()) {
if (k.substr(0, 1) != "d")
continue;
var qt = app.params.get(k);
if (qt == "")
continue;
var pid = null;
var did = null;
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);
}
// find related element in userOrders
var uo = null;
for (x in userOrders) {
if (x.distrib != null && x.distrib.id != did) {
continue;
} else {
for (a in x.datas) {
if (a.product.id == pid) {
uo = a;
break;
}
}
}
}
if (uo == null)
throw t._("Could not find the product ::produ:: and delivery ::deliv::", {produ: pid, deliv: did});
// quantity
var q = 0.0;
@@ -528,7 +525,6 @@ class Contract extends Controller
var p = db.Product.manager.get(pid, false);
orders.total += p.getPrice() * q;
}
App.current.session.data.order = orders;
@@ -539,22 +535,17 @@ class Contract extends Controller
} 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
throw Redirect("/");
@@ -562,9 +553,9 @@ class Contract extends Controller
// 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);
}
@@ -575,23 +566,23 @@ class Contract extends Controller
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 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
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
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) {
@@ -606,7 +597,8 @@ class Contract extends Controller
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{
+2 -1
View File
@@ -29,7 +29,8 @@ class Main extends Controller {
if ( app.user!=null && group == null) {
throw Redirect("/user/choose");
}else if (app.user == null && (group==null || group.regOption!=db.Amap.RegOption.Open) ) {
throw Redirect("/user/login");
//throw Redirect("/user/login");
throw Redirect("/group/1");
}
view.amap = group;
+125 -121
View File
@@ -1,4 +1,5 @@
package controller;
import Common;
import haxe.Utf8;
import sugoi.form.Form;
@@ -6,14 +7,11 @@ 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
@@ -25,65 +23,73 @@ class Member extends Controller
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
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);
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
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); }
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); }
browse = function(index:Int, limit:Int) {
return db.User.getUsers_Contracts(index, limit);
}
}
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); }
browse = function(index:Int, limit:Int) {
return db.User.getUsers_NoMembership(index, limit);
}
}
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); }
browse = function(index:Int, limit:Int) {
return db.User.getUsers_NewUsers(index, limit);
}
}
default:
throw t._("Unknown selection");
}
view.select = args.select;
} 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
@@ -103,14 +109,12 @@ class Member extends Controller
}
view.waitingList = db.WaitingList.manager.count($group == app.user.amap);
}
/**
* Move to waiting list
*/
function doMovetowl(u:db.User) {
var ua = db.UserAmap.get(u, app.user.amap, true);
ua.delete();
@@ -120,8 +124,6 @@ class Member extends Controller
wl.insert();
throw Ok("/member", u.getName() + " " + t._("is now on waiting list."));
}
/**
@@ -129,18 +131,13 @@ class Member extends Controller
*/
@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"));
}
}
@@ -151,21 +148,17 @@ class Member extends Controller
* Send an invitation to a new member
*/
function doInviteMember(u:db.User) {
if (checkToken()) {
u.sendInvitation(app.user.amap);
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) {
@@ -181,16 +174,14 @@ class Member extends Controller
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();
@@ -199,12 +190,16 @@ class Member extends Controller
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);
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 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();
@@ -215,9 +210,9 @@ class Member extends Controller
if (!distribs.exists(k)) {
distribs.set(k, orders);
} else {
var v = distribs.get(k);
for ( o in orders ) v.add(o);
for (o in orders)
v.add(o);
distribs.set(k, v);
}
}
@@ -229,10 +224,8 @@ class Member extends Controller
row.varOrders.set(k, d2);
}
view.userContracts = row;
checkToken(); // to insert a token in tpl
}
/**
@@ -241,12 +234,13 @@ 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.isAmapManager())
return;
if (member.isAdmin())
return;
if (db.UserAmap.manager.count($userId == member.id) > 1)
return;
}
App.current.session.setUser(member);
@@ -256,20 +250,26 @@ class Member extends Controller
@tpl('member/lastMessages.mtt')
function doLastMessages(member:db.User) {
var out = new Array<{date:Date,subject:String,success:String,failure:String}>();
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) {
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,8 +279,8 @@ 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);
@@ -290,7 +290,6 @@ class Member extends Controller
form.removeElement(form.getElement("ldate"));
form.removeElement(form.getElement("apiKey"));
var isReg = member.isFullyRegistred();
var groupNum = db.UserAmap.manager.count($userId == member.id);
@@ -310,7 +309,6 @@ class Member extends Controller
}
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."));
}
@@ -320,17 +318,16 @@ class Member extends Controller
// 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 (s.id == member.id)
sim.remove(s);
}
if (sim.length > 0) {
// 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());
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(",");
@@ -339,28 +336,33 @@ class Member extends Controller
}
}
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
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) {
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);
}
}
@@ -369,19 +371,24 @@ class Member extends Controller
}
view.form = form;
}
/**
* Remove a user from this group
*/
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 (!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}));
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);
@@ -398,8 +405,8 @@ class Member extends Controller
@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.");
@@ -414,7 +421,6 @@ class Member extends Controller
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);
@@ -426,8 +432,10 @@ class Member extends Controller
// 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();
}
@@ -453,27 +461,23 @@ class Member extends Controller
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}) {
var step = 1;
var request = Utils.getMultipart(1024 * 1024 * 4); // 4mb
// on recupere le contenu de l'upload
var data = request.get("file");
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 {
@@ -491,17 +495,20 @@ class Member extends Controller
// 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();
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();
@@ -515,7 +522,6 @@ class Member extends Controller
// utf-8 check
for (row in unregistred.copy()) {
for (i in 0...row.length) {
var t = row[i];
if (t != "" && t != null) {
@@ -548,7 +554,6 @@ class Member extends Controller
}
}
app.session.data.csvUnregistered = unregistred;
app.session.data.csvRegistered = registred;
@@ -557,13 +562,12 @@ class Member extends Controller
step = 2;
}
if (args != null && args.confirm) {
// 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];
@@ -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];
@@ -601,7 +606,8 @@ class Member extends Controller
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.
if (userAmaps.length == 0) {
@@ -630,8 +636,8 @@ 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);
@@ -645,17 +651,16 @@ class Member extends Controller
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"));
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
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) {
@@ -672,7 +677,6 @@ class Member extends Controller
}*/
return;
} 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."));
}
@@ -691,29 +695,30 @@ 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) {
service.PaymentService.updateUserBalance(m, app.user.amap);
var browse:Int->Int->List<Dynamic>;
@@ -737,5 +742,4 @@ class Member extends Controller
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 -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
+23 -38
View File
@@ -1,4 +1,5 @@
package controller;
import haxe.crypto.Md5;
import sugoi.form.elements.Input;
import sugoi.form.Form;
@@ -7,22 +8,16 @@ 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('/');
}
@@ -35,15 +30,14 @@ class User extends Controller
}
}
/**
* 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");
if (app.user == null)
throw t._("You are not connected");
var amaps = db.UserAmap.manager.search($user == app.user, false);
@@ -68,7 +62,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
@@ -85,7 +78,6 @@ class User extends Controller
*/
@tpl("user/forgottenPassword.mtt")
function doForgottenPassword(?key:String, ?u:db.User) {
// STEP 1
var step = 1;
var error:String = null;
@@ -118,10 +110,12 @@ class User extends Controller
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);
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"));
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));
@@ -130,8 +124,9 @@ class User extends Controller
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);
}
@@ -151,7 +146,6 @@ class User extends Controller
step = 4;
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");
@@ -161,10 +155,12 @@ 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);
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(", "),
@@ -173,7 +169,6 @@ class User extends Controller
}
m.setHtmlBody(app.processTemplate('mail/newPasswordConfirmed.mtt', params));
App.sendMail(m);
} else {
error = t._("You must key-in two times the same password");
}
@@ -187,7 +182,6 @@ class User extends Controller
view.error = error;
}
/**
* generate a custom key for transactionnal emails, valid during the current day
*/
@@ -195,12 +189,11 @@ class User extends Controller
// 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"));
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,14 +204,11 @@ class User extends Controller
form.addElement(pass2);
if (form.isValid()) {
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"));
}
@@ -232,24 +222,19 @@ class User extends Controller
* @param k
*/
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()}));
}
}
+20 -51
View File
@@ -1,9 +1,9 @@
package controller.admin;
import haxe.web.Dispatch;
import Common;
class Admin extends Controller {
public function new() {
super();
view.category = 'admin';
@@ -13,7 +13,6 @@ class Admin extends Controller {
var e = Nav(nav, "admin");
app.event(e);
view.nav = e.getParameters()[0];
}
@tpl("admin/default.mtt")
@@ -30,24 +29,18 @@ class Admin extends Controller {
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 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.StringInput("smtp_host", "host", host));
@@ -55,13 +48,11 @@ class Admin extends Controller {
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"]) {
sugoi.db.Variable.set(k, f.getValueOf(k));
}
throw Ok("/admin/emails", t._("Configuration updated"));
}
view.title = t._("Email service configuration");
@@ -72,12 +63,9 @@ class Admin extends Controller {
d.dispatch(new controller.admin.Plugins());
}
@tpl("admin/taxo.mtt")
function doTaxo() {
view.categ = db.TxpCategory.manager.all();
}
/**
@@ -91,31 +79,31 @@ class Admin extends Controller {
view.like = args.like != null ? args.like : "";
var sql = "";
if( args.user!=null ) sql += " AND uid="+args.user;
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) {
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) {
case "basket":
for (d in 1...to.getDate()) {
@@ -124,12 +112,8 @@ class Admin extends Controller {
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);
@@ -140,18 +124,14 @@ class Admin extends Controller {
var averageValue = 0.0;
var total = 0.0;
for( d in data) total += d.value;
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
@@ -161,18 +141,14 @@ class Admin extends Controller {
// 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
@@ -182,11 +158,10 @@ class Admin extends Controller {
// 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()) {
if (!order.paid) {
@@ -197,27 +172,21 @@ class Admin extends Controller {
var operation = basket.getOrderOperation(false);
if (operation != null) {
if (operation.pending) {
Sys.println(distrib.toString() + "<br>");
Sys.println(operation.toString() + "<br>");
}
for (payment in basket.getPayments()) {
if (payment.pending) {
Sys.println(distrib.toString() + "<br>");
Sys.println(payment.toString() + "<br>");
}
}
}
}
}
Sys.println("===== Fin de la liste ====");
}
}
+11 -18
View File
@@ -1,12 +1,10 @@
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) {
@@ -17,20 +15,18 @@ class ColorRadioGroup extends RadioGroup
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 c = 0;
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>";
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>";
s += "<label for=\"" + n + c + "\" class='checkbox' style='display: inline-block;'>" + radio + " " + img + " </label>";
@@ -40,7 +36,4 @@ class ColorRadioGroup extends RadioGroup
return s;
}
}
+66 -66
View File
@@ -1,4 +1,5 @@
package service;
import Common;
import tink.core.Error;
@@ -6,9 +7,7 @@ import tink.core.Error;
* Order Service
* @author web-wizard,fbarbut
*/
class OrderService
{
class OrderService {
static function canHaveFloatQt(product:db.Product):Bool {
return product.hasFloatQt || product.wholesale || product.variablePrice;
}
@@ -20,11 +19,12 @@ 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)) {
@@ -35,7 +35,8 @@ class OrderService
// 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");
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)) {
@@ -45,7 +46,8 @@ class OrderService
}
// checks
if (quantity <= 0) return null;
if (quantity <= 0)
return null;
// check for previous orders on the same distrib
var prevOrders = new List<db.UserContract>();
@@ -66,10 +68,13 @@ 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
if (prevOrders.length > 0 && !product.multiWeight) {
@@ -97,7 +102,8 @@ class OrderService
// 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) {
@@ -110,14 +116,14 @@ class OrderService
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 {
o.product.lock();
o.product.stock -= quantity;
@@ -129,18 +135,17 @@ class OrderService
return o;
}
/**
* Edit an existing order (quantity)
*/
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 (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}));
@@ -151,14 +156,17 @@ class OrderService
if (paid != null) {
order.paid = paid;
} else {
if (order.quantity < newquantity) order.paid = false;
if (order.quantity < newquantity)
order.paid = false;
}
// shared order
if (user2 != null) {
order.user2 = user2;
if (invert == true) order.flags.set(InvertSharedOrder);
if (invert == false) order.flags.unset(InvertSharedOrder);
if (invert == true)
order.flags.set(InvertSharedOrder);
if (invert == false)
order.flags.unset(InvertSharedOrder);
} else {
order.user2 = null;
order.flags.unset(InvertSharedOrder);
@@ -170,32 +178,30 @@ class OrderService
var c = order.product.contract;
if (c.hasStockManagement()) {
if (newquantity < order.quantity) {
// 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
var addedquantity = newquantity - order.quantity;
if (order.product.stock - addedquantity < 0) {
// 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});
order.product.lock();
order.product.stock = 0;
} else {
// stock is big enough
order.product.lock();
order.product.stock -= addedquantity;
@@ -222,36 +228,34 @@ 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) {
order.delete();
if (contract.amap.hasPayments()) {
var orders = contract.getUserOrders(user);
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
var place = order.distribution.place;
@@ -262,18 +266,16 @@ class OrderService
// 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,7 +287,6 @@ class OrderService
var view = App.current.view;
var t = sugoi.i18n.Locale.texts;
for (o in orders) {
var x:UserOrder = cast {};
x.id = o.id;
x.userId = o.user.id;
@@ -312,7 +313,6 @@ class OrderService
x.product = o.product.infos();
x.product.price = o.productPrice; // do not use current price, but price of the order
x.quantity = o.quantity;
// smartQt
@@ -336,12 +336,10 @@ class OrderService
var c = o.product.contract;
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 {
x.total = x.subTotal;
}
@@ -377,15 +375,15 @@ class OrderService
return orders;
}
/**
* Send an order-by-products report to the coordinator
*/
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.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);
@@ -402,24 +400,22 @@ class OrderService
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) {
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()) {
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");
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));
@@ -437,22 +433,31 @@ class OrderService
m.setHtmlBody(html);
App.sendMail(m);
}
}
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();
+44 -35
View File
@@ -1,9 +1,9 @@
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;
@@ -16,10 +16,10 @@ class BaseApp {
public var uri:String;
public static var config:Config;
// 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) {
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
@@ -58,8 +66,8 @@ class BaseApp {
}
public function initLang(lang:String) {
if (lang == null || lang == "") lang = config.LANG;
if (lang == null || lang == "")
lang = config.LANG;
// Define template path
var path;
@@ -83,8 +91,8 @@ class BaseApp {
}
function saveAndClose() {
if( cnx == null ) return;
if (cnx == null)
return;
if (session.sid != null)
session.update();
@@ -97,10 +105,12 @@ class BaseApp {
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);
if (result.substr(0, 4) == "null")
result = result.substr(4);
#end
Sys.print(result);
}
@@ -137,14 +147,13 @@ class BaseApp {
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;
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)) {
@@ -155,7 +164,6 @@ class BaseApp {
var lang = params.get("lang");
if (lang != null && Lambda.has(App.config.LANGS, lang)) {
session.lang = lang;
}
// init lang
@@ -170,7 +178,8 @@ class BaseApp {
}
public function rollback() {
if( cnx != null ) cnx.rollback();
if (cnx != null)
cnx.rollback();
sys.db.Manager.cleanup();
if (user != null && session != null)
user = session.user;
@@ -189,8 +198,10 @@ class BaseApp {
// 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
@@ -200,7 +211,6 @@ class BaseApp {
// setup langage
setupLang();
if (maintain && ((user != null && user.isAdmin())))
maintain = false;
@@ -214,9 +224,9 @@ class BaseApp {
// 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
beforeDispatch();
@@ -224,9 +234,7 @@ class BaseApp {
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) {
#if neko
@@ -238,9 +246,7 @@ class BaseApp {
cnx.rollback();
Web.redirect("/");
return;
} catch (e:sugoi.ControllerAction) {
switch (e) {
case RedirectAction(url):
Web.redirect(url);
@@ -251,8 +257,12 @@ class BaseApp {
url = Web.getURI();
}
Web.redirect(url);
var error = switch(e) { case ErrorAction(_): true; default: false; };
if( error ) rollback();
var error = switch (e) {
case ErrorAction(_): true;
default: false;
};
if (error)
rollback();
if (error) {
session.addMessage(text, true);
} else {
@@ -274,9 +284,7 @@ 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());
@@ -316,7 +324,6 @@ class BaseApp {
setTemplate("error.mtt");
executeTemplate(false);
} catch (e:Dynamic) {
Sys.print("<pre>");
Sys.println("Error : " + try Std.string(e) catch (e:Dynamic) "???");
@@ -380,9 +387,11 @@ 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;
}
@@ -396,13 +405,13 @@ 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();
+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 ) {
+8 -7
View File
@@ -3,7 +3,6 @@ package sugoi;
import sugoi.Web;
class Config {
public var PATH:String;
// public var json : Dynamic;
public var xml:Xml;
@@ -12,6 +11,7 @@ class Config {
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;
@@ -20,16 +20,17 @@ class Config {
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";
BANDEAU_REGION = get("bandeau_region", "0") == "1";
HOST = get("host");
NAME = get("name");
KEY = get("key");
DATA_HOST = get("dataHost", "data." + HOST);
SQL_LOG = getBool("sqllog", false);
@@ -42,7 +43,8 @@ class Config {
public function getBool(val:String, ?def) {
var v = get(val);
if( v == null ) return def;
if (v == null)
return def;
return (v == "1" || v == "true");
}
@@ -59,5 +61,4 @@ class Config {
public function getInt(val:String, ?def:Int):Int {
return Std.parseInt(get(val, Std.string(def)));
}
}
+24 -17
View File
@@ -1,17 +1,24 @@
package sugoi.mail;
using Lambda;
class Mail implements IMail
{
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 = [];
@@ -23,16 +30,17 @@ class Mail implements IMail
}
public function setSender(email, ?name, ?userId) {
if(!isValid(email)) throw "invalid sender email : \""+email+"\"";
sender = {name:name,email:email,userId: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,7 +55,8 @@ class Mail implements IMail
* @param ?userId
*/
public function addRecipient(email:String, ?name:String, ?userId:Int) {
if(!isValid(email)) throw "invalid recipient \""+email+"\"";
if (!isValid(email))
throw "invalid recipient \"" + email + "\"";
recipients.push({email: email, name: name, userId: userId});
return this;
}
@@ -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,14 +118,14 @@ 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();
@@ -149,7 +158,6 @@ class Mail implements IMail
}
}
public function getSubject() {
return title;
}
@@ -178,5 +186,4 @@ class Mail implements IMail
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);
});
}
}
}
+89 -138
View File
@@ -1,4 +1,3 @@
/**
Cagette.net SASS stylesheet
@@ -6,15 +5,18 @@
**/
@import url('https://fonts.googleapis.com/css?family=Cabin:400,400i,700');
@import url("https://fonts.googleapis.com/css?family=Cabin:400,400i,700");
//@import '_shop';
@import '_map';
@import "_map";
/*body{
background-color:#F8F4E5 !important;
}*/
h1,h2,h3,h4{
h1,
h2,
h3,
h4 {
margin-top: 4px !important;
margin-bottom: 8px !important;
font-style: italic;
@@ -66,9 +68,11 @@ ul.nav li {
/* Footer */
#footer {
background: #84BD55;
background: #84bd55;
border-radius: 8px;
padding: 20px;
margin: 0;
ul > li > a {
color: #333;
}
@@ -98,21 +102,19 @@ ul.nav li {
.cagsocialmedia li {
display: inline-block;
text-align: center;
}
.cagfb {
background-color : #4267B2;
background-color: #4267b2;
}
.cagtwitter {
background-color : #1DA1F2;
background-color: #1da1f2;
}
.cagyoutube {
background-color : #FF0000;
background-color: #ff0000;
}
}
#subfooter {
@@ -130,12 +132,14 @@ ul.nav li {
.sqlLog {
margin-top: 24px;
font-size: 10px;
background:#AAA;
a{ color:#666; }
background: #aaa;
a {
color: #666;
}
}
.contact {
border:2px solid #CCC ;
border: 2px solid #ccc;
border-radius: 8px;
margin: 8px;
padding: 8px;
@@ -145,13 +149,12 @@ ul.nav li {
.homeBlock {
margin-bottom: 32px;
background:#FFF;
background: #fff;
border-radius: 8px;
position: relative;
padding: 8px;
.content {
margin: 8px 8px 8px 90px;
}
}
@@ -164,16 +167,15 @@ span.panel-title{
.article {
margin-bottom: 16px;
background:#FFF;
background: #fff;
border-radius: 8px;
position: relative;
padding: 8px;
}
.white {
margin-bottom: 16px;
background:#FFF;
background: #fff;
border-radius: 4px;
position: relative;
}
@@ -185,7 +187,7 @@ span.panel-title{
/* block marron */
.block {
margin-bottom: 12px;
background : #FFF;
background: #fff;
border-radius: 8px;
position: relative;
padding: 8px;
@@ -194,11 +196,11 @@ span.panel-title{
/* block marron + contours */
.block2 {
margin-bottom: 32px;
background : #F0D277;
background: #f0d277;
border-radius: 8px;
position: relative;
padding: 8px;
border: 2px solid #C3A04C;
border: 2px solid #c3a04c;
}
/* product in shop */
@@ -224,13 +226,12 @@ table.product{
}
}
/* GLOBAL TABLE SKINNING */
//separator between rows
table.table tr.head > td {
//border-top: 1px solid #BBB;
border-top: 3px solid #DDD;
border-top: 3px solid #ddd;
height: 30px;
font-size: 120%;
vertical-align: bottom;
@@ -238,7 +239,7 @@ table.table tr.head>td{
//sub-row, darker bg
table.table tr.subrow {
background-color:#EEE;
background-color: #eee;
}
//listing have no overflow
@@ -247,22 +248,20 @@ table.listing tr td{
overflow-x: hidden;
}
table.table tr.disabled td,table.table tr.inactive td{
table.table tr.disabled td,
table.table tr.inactive td {
opacity: 0.4;
}
/* DIV TABLE */
// used in UserOrder react comp
div.tableHeader {
font-weight: bold;
color: #AAA;
border-bottom: 1px solid #DDD;
color: #aaa;
border-bottom: 1px solid #ddd;
margin-bottom: 12px;
}
/*table.table>tbody>tr>th{
background-color:white;
color:#BBB;
@@ -270,31 +269,22 @@ div.tableHeader{
}*/
div.reportHeader {
background-color:#DDD;
background-color: #ddd;
}
/* a product order in a table style */
.productOrder {
border-bottom:1px solid #DDD;
border-bottom: 1px solid #ddd;
margin-bottom: 6px;
.infos {
color:#AAA;
color: #aaa;
font-size: 0.8em;
}
}
/* A product reference */
.ref {
color:#AAA;
color: #aaa;
font-family: monospace;
font-size: 0.9em;
}
@@ -303,7 +293,7 @@ div.reportHeader{
.linkToShare {
font-family: Monospace;
padding: 12px;
border: 1px solid #DDD;
border: 1px solid #ddd;
border-radius: 6px;
background-color: #f5f5f5;
}
@@ -311,7 +301,7 @@ div.reportHeader{
/* make <a> like clickable divs */
.clickable {
display: block;
border:1px solid #CCC;
border: 1px solid #ccc;
border-radius: 8px;
color: inherit;
background-color: white;
@@ -320,16 +310,16 @@ div.reportHeader{
&:hover {
cursor: pointer;
background-color:lighten(#F0D277,10%);
border-color:darken(#F0D277,10%);
background-color: lighten(#f0d277, 10%);
border-color: darken(#f0d277, 10%);
text-decoration: none;
transition: all 0.15s ease;
}
&:active {
cursor: pointer;
background-color:#F0D277;
border-color:darken(#F0D277,10%);
background-color: #f0d277;
border-color: darken(#f0d277, 10%);
text-decoration: none;
transform: scale(0.95);
}
@@ -338,7 +328,6 @@ div.reportHeader{
background-color: lightgray;
cursor: not-allowed;
}
}
/* a cagette group block (use it with .clickable ) */
@@ -387,18 +376,15 @@ div.reportHeader{
a {
color: #777;
}
}
}
/*
SHOP v1
*/
.shop {
.header {
background-color:#749E3C;
background-color: #749e3c;
display: inline-block;
width: 100%;
@@ -410,10 +396,13 @@ div.reportHeader{
line-height: 22px;
color: white;
a{ color:white; }
.info{ font-weight: bold; }
a {
color: white;
}
.info {
font-weight: bold;
}
}
.body {
background: white;
@@ -425,14 +414,12 @@ div.reportHeader{
font-weight: bold;
font-size: 1.2em;
color: #583816;
border-bottom:1px solid #CCC;
border-bottom: 1px solid #ccc;
width: 100%;
}
}
.product {
display: inline-block;
.name {
@@ -444,12 +431,9 @@ div.reportHeader{
color: #900;
font-weight: bold;
}
}
}
.price {
font-weight: bold;
font-size: 1.3em;
@@ -463,13 +447,11 @@ div.reportHeader{
.detail {
color: #999999;
font-size: 90%;
}
//in shop page
#cartContainer.scrolled {
position: fixed;
top: 125px;
}
@@ -492,10 +474,8 @@ div.reportHeader{
}
#cart {
margin-bottom: 12px;
div.order {
margin-bottom: 8px;
@@ -508,12 +488,10 @@ div.reportHeader{
//font-weight:bold;
margin-bottom: 12px;
margin-top: 12px;
border-top:1px solid #FFF;
}
border-top: 1px solid #fff;
}
}
}
.place {
color: #666;
@@ -521,11 +499,11 @@ div.reportHeader{
margin-top: 13px;
padding: 8px;
font-size: 13px;
border-top:1px solid #CCC ;
border-top: 1px solid #ccc;
}
.distribMessage {
color:#FFF;
color: #fff;
font-weight: bold;
padding: 4px;
margin-top: 13px;
@@ -535,7 +513,6 @@ div.reportHeader{
/*border-top:1px solid #333 ;*/
}
.dateBoxOffset {
position: absolute;
top: -16px;
@@ -543,10 +520,9 @@ div.reportHeader{
display: block;
}
.dateBox {
border-radius: 8px;
background-color: #c59654;
color:#FFF;
color: #fff;
font-size: 13px;
line-height: 13px;
@@ -560,7 +536,7 @@ div.reportHeader{
.box {
line-height: 22px;
background-color:#FFFFFF;
background-color: #ffffff;
color: #666;
font-size: 14px;
font-weight: bold;
@@ -571,15 +547,13 @@ div.reportHeader{
/* place link */
a {
color: #FFFFFF !important;
color: #ffffff !important;
}
a:hover {
color: #FFFFFF !important;
color: #ffffff !important;
}
}
/* categories */
span.tag {
color: white;
@@ -596,45 +570,42 @@ span.tag.active{
line-height: 45px;
}
/**
Popovers for help and tutorials
**/
.popover {
color: white;
font-size: 14px;
//font-family:'Arial','sans-serif' !important;
h3.popover-title {
padding: 4px;
color:#BB9931 !important;
border-bottom:1px solid #BB9931 !important;
color: #bb9931 !important;
border-bottom: 1px solid #bb9931 !important;
background-color: none;
}
.popover-content {
font-size: 16px;
b{ color:#FC0; font-weight:normal; }
b {
color: #fc0;
font-weight: normal;
}
}
.footer {
border-top: 1px solid #BB9931;
border-top: 1px solid #bb9931;
height: 45px;
padding: 12px 0;
.pull-left .btn {
background-color: #583816;
color: white;
border: 1px solid white;
}
.pull-right .btn {
background-color:#FC0;
background-color: #fc0;
color: #583816;
border: 1px solid #583816;
}
@@ -643,11 +614,10 @@ span.tag.active{
/* skin a modal like a tuto popover */
.modal.help {
.modal-header {
font-size: 1.3em;
color:#BB9931 !important;
border-bottom:1px solid #BB9931 !important;
color: #bb9931 !important;
border-bottom: 1px solid #bb9931 !important;
font-weight: bold !important;
//font-family:'Arial','sans-serif' !important;
background-color: #583816 !important;
@@ -656,13 +626,11 @@ span.tag.active{
.modal-content {
background-color: #583816 !important;
color: white;
}
.modal-footer {
border-top:1px solid #BB9931 !important;
border-top: 1px solid #bb9931 !important;
}
}
/* hightlight a zone for tutos */
.highlight {
@@ -670,23 +638,19 @@ span.tag.active{
border-radius: 16px;
}
/* Distribution box for the homepage */
.distrib {
margin: 0px 0px 28px 0px;
position: relative;
.header {
display: block;
height: 72px;
overflow: hidden;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
color:#FFF;
background-color:#749E3C;
color: #fff;
background-color: #749e3c;
vertical-align: middle;
margin: 0;
padding: 6px 6px 6px 90px;
@@ -696,7 +660,7 @@ span.tag.active{
opacity: 1;
}
&.orders {
background-color:#B2823F;
background-color: #b2823f;
}
.info {
@@ -704,24 +668,26 @@ span.tag.active{
line-height: 22px;
/* place link */
a {
color: #FFFFFF !important;
&:hover{ color: #FFFFFF !important; }
color: #ffffff !important;
&:hover {
color: #ffffff !important;
}
}
}
.date {
line-height: 23px;
background-color:#FFFFFF;
background-color: #ffffff;
color: #666;
font-size: 14px;
text-align: center;
margin: 0 8px 0 0;
border-radius: 6px;
border: 4px solid #C6C6C6;
border: 4px solid #c6c6c6;
}
.address, .closing{
.address,
.closing {
font-weight: normal;
font-size: 0.9em;
line-height: 16px;
@@ -737,30 +703,28 @@ span.tag.active{
.content {
padding: 6px 6px 6px 6px;
background-color:#85B14D;
background-color: #85b14d;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
&.disabled {
background:#AAA;
background: #aaa;
opacity: 1;
}
&.orders {
background:#FFF;
background: #fff;
padding-left: 50px;
}
.footer {
color: #999;
a{color:#999;}
a {
color: #999;
}
font-size: 0.9em;
}
}
}
div.groupImg {
@@ -788,7 +752,6 @@ div.product{
margin-right: 12px;
vertical-align: middle;
}
}
//Product in ProductSelect React component
@@ -811,14 +774,10 @@ div.productSelect{
div.product {
display: inline-block;
}
}
// Cagette Pro Catalogs
div.productBox {
div.productImg {
background-size: cover;
height: 220px;
@@ -842,7 +801,6 @@ div.productBox{
}
}
// typeahead ( autocomplete js lib )
.tt-menu {
background-color: #f5e3ac;
@@ -851,7 +809,7 @@ div.productBox{
.tt-suggestion {
background-color: #f5e3ac;
padding: 8px;
border-top:1px solid #CCC;
border-top: 1px solid #ccc;
color: darken(#f5e3ac, 60%);
cursor: pointer;
font-style: italic;
@@ -862,9 +820,8 @@ div.productBox{
//txp product input
#pInput {
.txpProduct {
color:#AAA;
color: #aaa;
font-weight: bold;
font-size: 0.8em;
margin: 4px 0;
@@ -876,7 +833,6 @@ div.productBox{
}
}
//Product composer
/*
div.ProductComp{
@@ -894,15 +850,13 @@ div.ProductComp{
//basket
div.basketNumber {
display: inline-block;
background-color: #333;
border-radius: 10px;
padding: 6px;
font-weight: bold;
color:#CCC;
color: #ccc;
}
tr.success td div.basketNumber {
@@ -924,13 +878,10 @@ tr.danger td div.basketNumber{
width: 100%;
margin: 0;
padding: 8px;
border-top: 3px solid #CCC;
border-top: 3px solid #ccc;
z-index: 99;
}
/*!
* https://github.com/YouCanBookMe/react-datetime
*/
@@ -945,7 +896,7 @@ tr.danger td div.basketNumber{
margin-top: 1px;
z-index: 99999 !important;
background: #fff;
box-shadow: 0 1px 3px rgba(0,0,0,.1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid #f9f9f9;
}
.rdtOpen .rdtPicker {
@@ -988,7 +939,7 @@ tr.danger td div.basketNumber{
position: relative;
}
.rdtPicker td.rdtToday:before {
content: '';
content: "";
display: inline-block;
border-left: 7px solid transparent;
border-bottom: 7px solid #428bca;
+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: 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