]> git.agnieray.net Git - galette.git/commitdiff
CSV imports; fixes #176
authorJohan Cwiklinski <johan@x-tnd.be>
Tue, 27 Aug 2013 04:01:49 +0000 (06:01 +0200)
committerJohan Cwiklinski <johan@x-tnd.be>
Sat, 12 Oct 2013 09:25:39 +0000 (11:25 +0200)
31 files changed:
.gitignore
galette/config/paths.inc.php
galette/doandget_export.php
galette/export.php
galette/get_export.php
galette/get_import.php [new file with mode: 0644]
galette/import.php [new file with mode: 0644]
galette/import_model.php [new file with mode: 0644]
galette/imports/.htaccess [new file with mode: 0644]
galette/imports/readme.txt [new file with mode: 0644]
galette/includes/galette.inc.php
galette/install/index.php
galette/install/sql/mysql.sql
galette/install/sql/pgsql.sql
galette/install/sql/sqlite.sql
galette/install/sql/upgrade-to-0.76-mysql.sql [new file with mode: 0644]
galette/install/sql/upgrade-to-0.76-pgsql.sql [new file with mode: 0644]
galette/install/sql/upgrade-to-0.76-sqlite.sql [new file with mode: 0644]
galette/lib/Galette/Core/History.php
galette/lib/Galette/Core/Picture.php
galette/lib/Galette/Entity/Adherent.php
galette/lib/Galette/Entity/ImportModel.php [new file with mode: 0644]
galette/lib/Galette/IO/Csv.php
galette/lib/Galette/IO/CsvIn.php [new file with mode: 0644]
galette/lib/Galette/IO/CsvOut.php [new file with mode: 0644]
galette/templates/default/export.tpl
galette/templates/default/galette.css
galette/templates/default/images/icon-prefs.png [new file with mode: 0644]
galette/templates/default/import.tpl [new file with mode: 0644]
galette/templates/default/import_model.tpl [new file with mode: 0644]
galette/templates/default/page.tpl

index fab2d40b5d6dbb8a7a2181c638e030c14fd3a196..128e902b48e1fe33347816f2a187466446e11636 100644 (file)
@@ -24,6 +24,11 @@ quick_gen_project_*
 !/galette/exports/readme.txt
 !/galette/exports/.htaccess
 
+# /galette/imports/
+/galette/imports/*
+!/galette/imports/readme.txt
+!/galette/imports/.htaccess
+
 # /galette/logs/
 /galette/logs/*.log
 
index 9ba0f2a1fd7acc8c1869525f58d975804d53d6a2..a6a60ba28b1d841ed61ccb86fe3750ecdde4eaf4 100644 (file)
@@ -106,6 +106,9 @@ if ( !defined('GALETTE_PLUGINS_PATH') ) {
 if ( !defined('GALETTE_EXPORTS_PATH') ) {
     define('GALETTE_EXPORTS_PATH', GALETTE_ROOT . 'exports/');
 }
+if ( !defined('GALETTE_IMPORTS_PATH') ) {
+    define('GALETTE_IMPORTS_PATH', GALETTE_ROOT . 'imports/');
+}
 if ( !defined('GALETTE_PHOTOS_PATH') ) {
     define('GALETTE_PHOTOS_PATH', GALETTE_ROOT . 'photos/');
 }
index 6726a6c690cf452c5b4ab3547973c3b6c89b67f8..a40652477c9a5a61cb60ca73648a52fad833176f 100644 (file)
@@ -37,6 +37,7 @@
 
 use Analog\Analog as Analog;
 use Galette\IO\Csv;
+use Galette\IO\CsvOut;
 use Galette\Filters\MembersList;
 use Galette\Entity\FieldsConfig;
 use Galette\Entity\Adherent;
@@ -50,7 +51,7 @@ require_once 'includes/galette.inc.php';
 //Exports main contain user confidential data, they're accessible only for
 //admins or staff members
 if ( $login->isAdmin() || $login->isStaff() ) {
-    $csv = new Csv();
+    $csv = new CsvOut();
 
     if ( isset($session['filters']['members'])
         && !isset($_POST['mailing'])
@@ -195,7 +196,7 @@ if ( $login->isAdmin() || $login->isStaff() ) {
         }
     }
     $filename = 'filtered_memberslist.csv';
-    $filepath = Csv::DEFAULT_DIRECTORY . $filename;
+    $filepath = CsvOut::DEFAULT_DIRECTORY . $filename;
     $fp = fopen($filepath, 'w');
     if ( $fp ) {
         $res = $csv->export(
@@ -212,11 +213,11 @@ if ( $login->isAdmin() || $login->isStaff() ) {
         );
     }
 
-    if (file_exists(Csv::DEFAULT_DIRECTORY . $filename) ) {
+    if (file_exists(CsvOut::DEFAULT_DIRECTORY . $filename) ) {
         header('Content-Type: text/csv');
         header('Content-Disposition: attachment; filename="' . $filename . '";');
         header('Pragma: no-cache');
-        readfile(Csv::DEFAULT_DIRECTORY . $filename);
+        readfile(CsvOut::DEFAULT_DIRECTORY . $filename);
     } else {
         Analog::log(
             'A request has been made to get an exported file named `' .
index 858279690fe221acaf255304aec148c033f0acdb..c532db745a8e99e73475b5115709dcf829286e3d 100644 (file)
@@ -5,7 +5,7 @@
 /**
  * Export
  *
- * Permet l'export de données au format CSV
+ * Data export in CSV format
  *
  * PHP version 5
  *
@@ -49,18 +49,19 @@ if ( !$login->isAdmin() && !$login->isStaff() ) {
 }
 
 use Galette\IO\Csv;
+use Galette\IO\CsvOut;
 use Galette\Entity\Adherent;
 use Galette\Entity\FieldsConfig;
 use Galette\Repository\Members;
 
-$csv = new Csv();
+$csv = new CsvOut();
 
 $written = array();
 
 $tables_list = $zdb->getTables();
 
 if ( isset($_GET['sup']) ) {
-    $res = $csv->removeExport($_GET['sup']);
+    $res = $csv->remove($_GET['sup']);
     if ( $res === true ) {
         $success_detected[] = str_replace(
             '%export',
@@ -84,7 +85,7 @@ if ( isset( $_POST['export_tables'] ) && $_POST['export_tables'] != '' ) {
 
         if ( count($result) > 0 ) {
             $filename = $table . '_full.csv';
-            $filepath = Csv::DEFAULT_DIRECTORY . $filename;
+            $filepath = CsvOut::DEFAULT_DIRECTORY . $filename;
             $fp = fopen($filepath, 'w');
             if ( $fp ) {
                 $res = $csv->export(
@@ -147,73 +148,8 @@ if ( isset( $_POST['export_parameted'] ) && $_POST['export_parameted'] != '' ) {
     }
 }
 
-if ( isset($_GET['current_filter']) ) {
-
-    if ( isset($session['filters']['members'])
-        && !isset($_POST['mailing'])
-        && !isset($_POST['mailing_new'])
-    ) {
-        //CAUTION: this one may be simple or advanced, display must change
-        $filters = unserialize($session['filters']['members']);
-    } else {
-        $filters = new MembersList();
-    }
-
-    $export_fields = null;
-    if ( file_exists(GALETTE_CONFIG_PATH  . 'local_export_fields.inc.php') ) {
-        include_once GALETTE_CONFIG_PATH  . 'local_export_fields.inc.php';
-        $export_fields = $fields;
-    }
-
-    // fields visibility
-    $a = new Adherent();
-    $fc = new FieldsConfig(Adherent::TABLE, $a->fields);
-    $visibles = $fc->getVisibilities();
-    $fields = array();
-    $headers = array();
-    foreach ( $members_fields as $k=>$f ) {
-        if ( $k !== 'mdp_adh'
-            && $export_fields === null
-            || in_array($k, $export_fields)
-        ) {
-            if ( $visibles[$k] === FieldsConfig::VISIBLE ) {
-                $fields[] = $k;
-                $labels[] = $f['label'];
-            } else if ( ($login->isAdmin()
-                || $login->isStaff()
-                || $login->isSuperAdmin())
-                && $visibles[$k] === FieldsConfig::ADMIN
-            ) {
-                $fields[] = $k;
-                $labels[] = $f['label'];
-            }
-        }
-    }
-
-    $members = new Members($filters);
-    $members_list = $members->getMembersList(false, $fields);
-
-    $filename = 'filtered_memberslist.csv';
-    $filepath = Csv::DEFAULT_DIRECTORY . $filename;
-    $fp = fopen($filepath, 'w');
-    if ( $fp ) {
-        $res = $csv->export(
-            $members_list,
-            Csv::DEFAULT_SEPARATOR,
-            Csv::DEFAULT_QUOTE,
-            $labels,
-            $fp
-        );
-        fclose($fp);
-        $written[] = array(
-            'name' => $filename,
-            'file' => $filepath
-        );
-    }
-}
-
 $parameted = $csv->getParametedExports();
-$existing = Csv::getExistingExports();
+$existing = $csv->getExisting();
 
 $tpl->assign('page_title', _T("CVS database Export"));
 $tpl->assign('tables_list', $tables_list);
index c32eb9abcd100f62da6e252815d2c7e5a9c2e513..97ee25c20eca925c2578956f1f5689525f58001c 100644 (file)
@@ -51,24 +51,24 @@ if ( !isset($_GET['file']) ) {
 
 $filename = $_GET['file'];
 
-use Galette\IO\Csv;
+use Galette\IO\CsvOut;
 
 //Exports main contain user confidential data, they're accessible only for
 //admins or staff members
 if ( $login->isAdmin() || $login->isStaff() ) {
 
-    if (file_exists(Csv::DEFAULT_DIRECTORY . $filename) ) {
+    if (file_exists(CsvOut::DEFAULT_DIRECTORY . $filename) ) {
         header('Content-Type: text/csv');
         header('Content-Disposition: attachment; filename="' . $filename . '";');
         header('Pragma: no-cache');
-        readfile(Csv::DEFAULT_DIRECTORY . $filename);
+        readfile(CsvOut::DEFAULT_DIRECTORY . $filename);
     } else {
         Analog::log(
             'A request has been made to get an exported file named `' .
             $filename .'` that does not exists.',
             Analog::WARNING
         );
-        header('HTTP/1.0 404 Not Found');        
+        header('HTTP/1.0 404 Not Found');
     }
 } else {
     Analog::log(
diff --git a/galette/get_import.php b/galette/get_import.php
new file mode 100644 (file)
index 0000000..1b3ec1b
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Get an import file
+ *
+ * PHP version 5
+ *
+ * Copyright © 2013 The Galette Team
+ *
+ * This file is part of Galette (http://galette.tuxfamily.org).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Main
+ * @package   Galette
+ *
+ * @author    Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2013 The Galette Team
+ * @license   http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @link      http://galette.tuxfamily.org
+ * @since     Available since 0.7.6dev - 2013-08-27
+ */
+
+use Analog\Analog as Analog;
+
+/** @ignore */
+require_once 'includes/galette.inc.php';
+
+if ( !isset($_GET['file']) ) {
+    Analog::log(
+        'No requested file',
+        Analog::INFO
+    );
+    header("HTTP/1.1 500 Internal Server Error");
+    die();
+}
+
+$filename = $_GET['file'];
+
+use Galette\IO\CsvIn;
+
+//Exports main contain user confidential data, they're accessible only for
+//admins or staff members
+if ( $login->isAdmin() || $login->isStaff() ) {
+
+    if (file_exists(CsvIn::DEFAULT_DIRECTORY . $filename) ) {
+        header('Content-Type: text/csv');
+        header('Content-Disposition: attachment; filename="' . $filename . '";');
+        header('Pragma: no-cache');
+        readfile(CsvIn::DEFAULT_DIRECTORY . $filename);
+    } else {
+        Analog::log(
+            'A request has been made to get an import file named `' .
+            $filename .'` that does not exists.',
+            Analog::WARNING
+        );
+        header('HTTP/1.0 404 Not Found');
+    }
+} else {
+    Analog::log(
+        'A non authorized person asked to retrieve import file named `' .
+        $filename . '`. Access has not been granted.',
+        Analog::WARNING
+    );
+    header('HTTP/1.0 403 Forbidden');
+}
diff --git a/galette/import.php b/galette/import.php
new file mode 100644 (file)
index 0000000..98d10a5
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Import members from CSV file
+ *
+ * PHP version 5
+ *
+ * Copyright © 2013 The Galette Team
+ *
+ * This file is part of Galette (http://galette.tuxfamily.org).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Main
+ * @package   Galette
+ *
+ * @author    Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2013 The Galette Team
+ * @license   http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @link      http://galette.tuxfamily.org
+ * @since     Available since 0.7ev - 2013-08-27
+ */
+
+require_once 'includes/galette.inc.php';
+
+if ( !$login->isLogged() ) {
+    header('location: index.php');
+    die();
+}
+if ( !$login->isAdmin() && !$login->isStaff() ) {
+    header('location: voir_adherent.php');
+    die();
+}
+
+use Galette\IO\Csv;
+use Galette\IO\CsvIn;
+use Galette\Entity\Adherent;
+use Galette\Entity\FieldsConfig;
+use Galette\Repository\Members;
+
+$csv = new CsvIn();
+
+$written = array();
+$dryrun = true;
+
+if ( isset($_GET['sup']) ) {
+    $res = $csv->remove($_GET['sup']);
+    if ( $res === true ) {
+        $success_detected[] = str_replace(
+            '%export',
+            $_GET['sup'],
+            _T("'%export' file has been removed from disk.")
+        );
+    } else {
+        $error_detected[] = str_replace(
+            '%export',
+            $_GET['sup'],
+            _T("Cannot remove '%export' from disk :/")
+        );
+    }
+}
+
+
+// CSV file upload
+if ( isset($_FILES['new_file']) ) {
+    if ( $_FILES['new_file']['error'] === UPLOAD_ERR_OK ) {
+        if ( $_FILES['new_file']['tmp_name'] !='' ) {
+            if ( is_uploaded_file($_FILES['new_file']['tmp_name']) ) {
+                $res = $csv->store($_FILES['new_file']);
+                if ( $res < 0 ) {
+                    $error_detected[] = $csv->getErrorMessage($res);
+                }
+            }
+        }
+    } else if ($_FILES['new_file']['error'] !== UPLOAD_ERR_NO_FILE) {
+        Analog::log(
+            $csv->getPhpErrorMessage($_FILES['new_file']['error']),
+            Analog::WARNING
+        );
+        $error_detected[] = $csv->getPhpErrorMessage(
+            $_FILES['new_file']['error']
+        );
+    } else {
+        $error_detected[] = _T("No files has been seleted for upload!");
+    }
+}
+
+if ( isset($_POST['import']) && isset($_POST['import_file']) ) {
+    if ( isset($_POST['dryrun']) ) {
+        $dryrun = true;
+    } else {
+        $dryrun = false;
+    }
+    $res = $csv->import($_POST['import_file'], $members_fields, $dryrun);
+    if ( $res !== true ) {
+        if ( $res < 0 ) {
+            $error_detected[] = $csv->getErrorMessage($res);
+            if ( count($csv->getErrors()) > 0 ) {
+                $error_detected = array_merge($error_detected, $csv->getErrors());
+            }
+        } else {
+            $error_detected[] = _T("An error occured importing the file :(");
+        }
+        $tpl->assign('import_file', $_POST['import_file']);
+    } else {
+        $success_detected[] = str_replace(
+            '%filename%',
+            $_POST['import_file'],
+            _T("File '%filename%' has been successfully imported :)")
+        );
+    }
+}
+
+$tpl->assign('dryrun', $dryrun);
+$existing = $csv->getExisting();
+
+$tpl->assign('page_title', _T("CVS members import"));
+$tpl->assign('require_dialog', true);
+//$tpl->assign('written', $written);
+$tpl->assign('existing', $existing);
+$tpl->assign('success_detected', $success_detected);
+$tpl->assign('error_detected', $error_detected);
+$tpl->assign('warning_detected', $warning_detected);
+$content = $tpl->fetch('import.tpl');
+$tpl->assign('content', $content);
+
+$tpl->display('page.tpl');
diff --git a/galette/import_model.php b/galette/import_model.php
new file mode 100644 (file)
index 0000000..df9ebf9
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * CSV import model
+ *
+ * PHP version 5
+ *
+ * Copyright © 2013 The Galette Team
+ *
+ * This file is part of Galette (http://galette.tuxfamily.org).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Main
+ * @package   Galette
+ *
+ * @author    Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2013 The Galette Team
+ * @license   http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @version   SVN: $Id$
+ * @link      http://galette.tuxfamily.org
+ * @since     Availaible since 0.7.6dev - 2013-09-26
+ */
+
+use Analog\Analog as Analog;
+use Galette\IO\Csv as Csv;
+use Galette\IO\CsvIn as CsvIn;
+use Galette\IO\CsvOut as CsvOut;
+use Galette\Entity\ImportModel as ImportModel;
+
+require_once 'includes/galette.inc.php';
+
+if ( !$login->isLogged() ) {
+    header("location: index.php");
+    die();
+}
+if ( !$login->isAdmin() ) {
+    header("location: voir_adherent.php");
+    die();
+}
+
+$model = new ImportModel();
+
+if ( isset($_POST['fields']) ) {
+    $model->setFields($_POST['fields']);
+    $res = $model->store($zdb);
+    if ( $res === true ) {
+        $success_detected[] = _T("Import model has been successfully stored :)");
+    } else {
+        $error_detected[] = _T("Import model has not been stored :(");
+    }
+}
+
+if ( isset($_GET['remove']) ) {
+    $model->remove($zdb);
+}
+
+$csv = new CsvIn();
+
+/** FIXME: 
+ * - set fields that should not be part of import
+ * - set fields that must be part of import, and visually disable them in the list
+ */
+
+$model->load();
+$fields = $model->getFields();
+$defaults = $csv->getDefaultFields();
+$defaults_loaded = false;
+
+if ( $fields === null ) {
+    $fields = $defaults;
+    $defaults_loaded = true;
+}
+
+if ( isset($_GET['generate'] ) ) {
+    $ocsv = new CsvOut();
+    $res = $ocsv->export(
+        $fields,
+        Csv::DEFAULT_SEPARATOR,
+        Csv::DEFAULT_QUOTE,
+        $fields
+    );
+    $filename = _T("galette_import_model.csv");
+    header('Content-Type: text/csv');
+    header('Content-Disposition: attachment; filename="' . $filename . '";');
+    header('Pragma: no-cache');
+    echo $res;
+} else {
+    $tpl->assign('success_detected', $success_detected);
+    $tpl->assign('error_detected', $error_detected);
+
+    $tpl->assign('fields', $fields);
+    $tpl->assign('model', $model);
+    $tpl->assign('defaults', $defaults);
+    $tpl->assign('members_fields', $members_fields);
+    $tpl->assign('defaults_loaded', $defaults_loaded);
+
+    $tpl->assign('require_tabs', true);
+    $tpl->assign('require_dialog', true);
+    $tpl->assign('page_title', _T("CVS import model"));
+    $content = $tpl->fetch('import_model.tpl');
+    $tpl->assign('content', $content);
+    $tpl->display('page.tpl');
+}
+
diff --git a/galette/imports/.htaccess b/galette/imports/.htaccess
new file mode 100644 (file)
index 0000000..ccbfba5
--- /dev/null
@@ -0,0 +1,9 @@
+Options -Indexes
+<IfModule mod_authz_core.c>
+    Require all denied
+</IfModule>
+<IfModule !mod_authz_core.c>
+    Order deny,allow
+    Deny from All
+    Allow from 127.0.0.1
+</IfModule>
diff --git a/galette/imports/readme.txt b/galette/imports/readme.txt
new file mode 100644 (file)
index 0000000..a341c0b
--- /dev/null
@@ -0,0 +1,5 @@
+
+This file is here because most ftp-clients don't upload empty directory.
+
+You can delete this file after your upload.
+
index 8c54f0278e19e698d674b7288da0f93b3ef173c7..9cf6470eba767a406a49bbc94d3df206ffc5d209 100644 (file)
@@ -119,7 +119,7 @@ session_start();
 
 define('GALETTE_VERSION', 'v0.7.5.5');
 define('GALETTE_COMPAT_VERSION', '0.7.5');
-define('GALETTE_DB_VERSION', '0.703');
+define('GALETTE_DB_VERSION', '0.704');
 if ( !defined('GALETTE_MODE') ) {
     define('GALETTE_MODE', 'PROD'); //DEV, PROD or DEMO
 }
index ebc2e76da7dbe6782e9a8a9a4201f9427e095457..ec06a522aae10a53af202f0db1130dafa3f9c81f 100644 (file)
@@ -453,6 +453,7 @@ case 'u3':
         GALETTE_TEMPIMAGES_PATH,
         GALETTE_CONFIG_PATH,
         GALETTE_EXPORTS_PATH,
+        GALETTE_IMPORTS_PATH,
         GALETTE_LOGS_PATH
     );
 
index 1cf1bc843f1c2b06ab635083546c08c78ed8e04c..349a4120f9c4aa6724c65267daf56ed97878557f 100644 (file)
@@ -291,11 +291,20 @@ CREATE TABLE galette_pdfmodels (
   PRIMARY KEY (model_id)
 ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
 
+-- Table for import models
+DROP TABLE IF EXISTS galette_import_model;
+CREATE TABLE galette_import_model (
+  model_id smallint(6) NOT NULL auto_increment,
+  model_fields text,
+  model_creation_date datetime NOT NULL,
+  PRIMARY KEY (model_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
 -- table for database version
 DROP TABLE IF EXISTS galette_database;
 CREATE TABLE galette_database (
   version DECIMAL(4,3) NOT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-INSERT INTO galette_database(version) VALUES(0.703);
+INSERT INTO galette_database(version) VALUES(0.704);
 
 SET FOREIGN_KEY_CHECKS=1;
index ffe5c572594324b4d72ba95bc5bacf350e20426f..bbd0bd0c662a22fac9aec653eb10973939ff023f 100644 (file)
@@ -111,6 +111,15 @@ CREATE SEQUENCE galette_pdfmodels_id_seq
     MINVALUE 1
     CACHE 1;
 
+-- sequence for import model
+DROP SEQUENCE IF EXISTS galette_import_model_id_seq;
+CREATE SEQUENCE galette_import_model_id_seq
+    START 1
+    INCREMENT 1
+    MAXVALUE 2147483647
+    MINVALUE 1
+    CACHE 1;
+
 -- Schema
 -- REMINDER: Create order IS important, dependencies first !!
 DROP TABLE IF EXISTS galette_statuts CASCADE;
@@ -386,9 +395,18 @@ CREATE TABLE galette_pdfmodels (
   PRIMARY KEY (model_id)
 );
 
+-- Table for import models
+DROP TABLE IF EXISTS galette_import_model;
+CREATE TABLE galette_import_model (
+  model_id integer DEFAULT nextval('galette_import_model_id_seq'::text) NOT NULL,
+  model_fields text,
+  model_creation_date timestamp NOT NULL,
+  PRIMARY KEY (model_id)
+);
+
 -- table for database version
 DROP TABLE IF EXISTS galette_database;
 CREATE TABLE galette_database (
   version decimal NOT NULL
 );
-INSERT INTO galette_database (version) VALUES(0.703);
+INSERT INTO galette_database (version) VALUES(0.704);
index c1f9fea2b87ec39df93ba0eba85294ca1067e158..91a70bb5528277bc7bfacf9060ad94d25af71cb8 100644 (file)
@@ -277,11 +277,19 @@ CREATE TABLE galette_pdfmodels (
   FOREIGN KEY (model_parent) REFERENCES galette_pdfmodels (model_id)
 );
 
+-- Table for import models
+DROP TABLE IF EXISTS galette_import_model;
+CREATE TABLE galette_import_model (
+  model_id INTEGER NOT NULL PRIMARY KEY,
+  model_fields TEXT,
+  model_creation_date TEXT NOT NULL
+);
+
 -- table for database version
 DROP TABLE IF EXISTS galette_database;
 CREATE TABLE galette_database (
   version REAL NOT NULL
 );
-INSERT INTO galette_database(version) VALUES(0.703);
+INSERT INTO galette_database(version) VALUES(0.704);
 
 PRAGMA foreign_keys = ON;
diff --git a/galette/install/sql/upgrade-to-0.76-mysql.sql b/galette/install/sql/upgrade-to-0.76-mysql.sql
new file mode 100644 (file)
index 0000000..42d2805
--- /dev/null
@@ -0,0 +1,10 @@
+-- Table for import models
+DROP TABLE IF EXISTS galette_import_model;
+CREATE TABLE galette_import_model (
+  model_id smallint(6) NOT NULL auto_increment,
+  model_fields text,
+  model_creation_date datetime NOT NULL,
+  PRIMARY KEY (model_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+UPDATE galette_database SET version = 0.704;
diff --git a/galette/install/sql/upgrade-to-0.76-pgsql.sql b/galette/install/sql/upgrade-to-0.76-pgsql.sql
new file mode 100644 (file)
index 0000000..d0eae89
--- /dev/null
@@ -0,0 +1,19 @@
+-- sequence for import model
+DROP SEQUENCE IF EXISTS galette_import_model_id_seq;
+CREATE SEQUENCE galette_import_model_id_seq
+    START 1
+    INCREMENT 1
+    MAXVALUE 2147483647
+    MINVALUE 1
+    CACHE 1;
+
+-- Table for import models
+DROP TABLE IF EXISTS galette_import_model;
+CREATE TABLE galette_import_model (
+  model_id integer DEFAULT nextval('galette_import_model_id_seq'::text) NOT NULL,
+  model_fields text,
+  model_creation_date timestamp NOT NULL,
+  PRIMARY KEY (model_id)
+);
+
+UPDATE galette_database SET version = 0.704;
diff --git a/galette/install/sql/upgrade-to-0.76-sqlite.sql b/galette/install/sql/upgrade-to-0.76-sqlite.sql
new file mode 100644 (file)
index 0000000..e04c985
--- /dev/null
@@ -0,0 +1,9 @@
+-- Table for import models
+DROP TABLE IF EXISTS galette_import_model;
+CREATE TABLE galette_import_model (
+  model_id INTEGER NOT NULL PRIMARY KEY,
+  model_fields TEXT,
+  model_creation_date TEXT NOT NULL
+);
+
+UPDATE galette_database SET version = 0.704;
index 2da1bfd495bcbaff116a5712e3c8f5c3ba6e5a23..aed9e29d8f16a6baa90ce7ab4494ca6ca1c193cf 100644 (file)
@@ -160,7 +160,7 @@ class History extends Pagination
                     'An error occured cleaning history. ',
                     Analog::WARNING
                 );
-                $this->add('Arror flushing logs');
+                $this->add('Error flushing logs');
                 return false;
             }
             $this->add('Logs flushed');
index dd5041683b6288573348e5a8567bf7e78ed17fb2..e1209e390835904b9a019bcb29128d46d9d853ac 100644 (file)
@@ -781,7 +781,7 @@ class Picture
             $error = preg_replace(
                 '|%d|',
                 self::MAX_FILE_SIZE,
-                _T("File is too big. Maximum allowed size is %d")
+                _T("File is too big. Maximum allowed size is %dKo")
             );
             break;
         case self::MIME_NOT_ALLOWED:
@@ -798,7 +798,7 @@ class Picture
     }
 
     /**
-     * Return textual erro rmessage send by PHP after upload attempt
+     * Return textual erromessage send by PHP after upload attempt
      *
      * @param int $error_code The error code
      *
index 7802828090ebd4f781b03c0f79b3cdafc67e164a..b03d9314373a97bcc3f4189d6662f672016e3809 100644 (file)
@@ -210,7 +210,7 @@ class Adherent
             } else {
                 $this->_active = true;
                 $this->_language = $i18n->getID();
-                $this->_creation_date = date("Y-m-d");
+                $this->_creation_date = date(_T("Y-m-d"));
                 $this->_status = Status::DEFAULT_STATUS;
                 $this->_title = null;
                 $this->_gender = self::NC;
@@ -784,7 +784,15 @@ class Adherent
                 case 'bool_display_info':
                     $value = 0;
                     break;
+                case 'activite_adh':
+                    //values that are setted at object instanciation
+                    $value = true;
+                    break;
                 case 'date_crea_adh':
+                case 'sexe_adh':
+                case 'titre_adh':
+                case 'id_statut':
+                case 'pref_lang':
                     //values that are setted at object instanciation
                     $value = $this->$prop;
                     break;
diff --git a/galette/lib/Galette/Entity/ImportModel.php b/galette/lib/Galette/Entity/ImportModel.php
new file mode 100644 (file)
index 0000000..27099d4
--- /dev/null
@@ -0,0 +1,215 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Import model
+ *
+ * PHP version 5
+ *
+ * Copyright © 2013 The Galette Team
+ *
+ * This file is part of Galette (http://galette.tuxfamily.org).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Entity
+ * @package   Galette
+ *
+ * @author    Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2013 The Galette Team
+ * @license   http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @version   SVN: $Id$
+ * @link      http://galette.tuxfamily.org
+ * @since     Available since 0.7.6dev - 2013-09-26
+ */
+
+namespace Galette\Entity;
+
+use Analog\Analog as Analog;
+
+/**
+ * Import model entity
+ *
+ * @category  Entity
+ * @name      ImportModel
+ * @package   Galette
+ * @author    Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2013 The Galette Team
+ * @license   http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @link      http://galette.tuxfamily.org
+ * @since     Available since 0.7.6dev - 2013-09-26
+ */
+class ImportModel
+{
+    const TABLE = 'import_model';
+    const PK = 'model_id';
+
+    private $_id;
+    private $_fields;
+    private $_creation_date;
+
+    /**
+     * Loads model
+     *
+     * @return bool true if query succeed, false otherwise
+     */
+    public function load()
+    {
+        global $zdb;
+
+        try {
+            $select = new \Zend_Db_Select($zdb->db);
+
+            $select->from(PREFIX_DB . self::TABLE)
+                ->limit(1);
+            $result = $select->query()->fetchObject();
+            if ( $result ) {
+                $this->_loadFromRS($result);
+                return true;
+            } else {
+                return false;
+            }
+        } catch (\Exception $e) {
+            Analog::log(
+                'Cannot load import model | ' . $e->getMessage() .
+                'Query was: ' . $select->__toString() . ' ' . $e->__toString(),
+                Analog::ERROR
+            );
+            return false;
+        }
+    }
+
+    /**
+     * Populate object from a resultset row
+     *
+     * @param ResultSet $r the resultset row
+     *
+     * @return void
+     */
+    private function _loadFromRS($r)
+    {
+        $this->_id = $r->model_id;
+        $this->_fields = unserialize($r->model_fields);
+        $this->_creation_date = $r->model_creation_date;
+    }
+
+    /**
+     * Remove model
+     *
+     * @param Db $zdb Database instance
+     *
+     * @return boolean
+     */
+    public function remove($zdb)
+    {
+        try {
+            $result = $zdb->db->query('TRUNCATE TABLE ' . PREFIX_DB . self::TABLE);
+            return $result;
+        } catch (\Exception $e) {
+            $zdb->db->rollBack();
+            Analog::log(
+                'Unable to remove import model ' . $e->getMessage(),
+                Analog::ERROR
+            );
+            return false;
+        }
+    }
+
+    /**
+     * Store the model
+     *
+     * @param Db $zdb Database instance
+     *
+     * @return boolean
+     */
+    public function store($zdb)
+    {
+        try {
+            $values = array(
+                self::PK        => $this->_id,
+                'model_fields'  => serialize($this->_fields)
+            );
+
+            if ( !isset($this->_id) || $this->_id == '') {
+                //we're inserting a new model
+                unset($values[self::PK]);
+                $this->_creation_date = date("Y-m-d H:i:s");
+                $values['model_creation_date'] = $this->_creation_date;
+                $add = $zdb->db->insert(PREFIX_DB . self::TABLE, $values);
+                if ( $add > 0) {
+                    return true;
+                } else {
+                    throw new \Exception(
+                        'An error occured inserting new import model!'
+                    );
+                }
+            } else {
+                //we're editing an existing group
+                $edit = $zdb->db->update(
+                    PREFIX_DB . self::TABLE,
+                    $values,
+                    self::PK . '=' . $this->_id
+                );
+                return true;
+            }
+        } catch ( \Exception $e ) {
+            Analog::log(
+                'Something went wrong storing import model :\'( | ' .
+                $e->getMessage() . "\n" . $e->getTraceAsString(),
+                Analog::ERROR
+            );
+            return false;
+        }
+    }
+
+    /**
+     * Get fields
+     *
+     * @return array
+     */
+    public function getFields()
+    {
+        return $this->_fields;
+    }
+
+    /**
+     * Get creation date
+     *
+     * @param boolean $formatted Return date formatted, raw if false
+     *
+     * @return string
+     */
+    public function getCreationDate($formatted = true)
+    {
+        if ( $formatted === true ) {
+            $date = new \DateTime($this->_creation_date);
+            return $date->format(_T("Y-m-d"));
+        } else {
+            return $this->_creation_date;
+        }
+    }
+
+    /**
+     * Set fields
+     *
+     * @param array $fields Fields list
+     *
+     * @return void
+     */
+    public function setFields($fields)
+    {
+        $this->_fields = $fields;
+    }
+}
index 9d08eeb95a520ce1002234c70094895e8c81c5b3..f5136f927c653ca1693127148ecd495398eae9e0 100644 (file)
@@ -3,7 +3,7 @@
 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
 /**
- * CSV exports
+ * CSV files
  *
  * PHP version 5
  *
@@ -40,7 +40,7 @@ namespace Galette\IO;
 use Analog\Analog as Analog;
 
 /**
- * CSV exports
+ * CSV files
  *
  * @category  IO
  * @name      Csv
@@ -52,197 +52,70 @@ use Analog\Analog as Analog;
  * @since     Disponible depuis la Release 0.7alpha - 2009-02-09
  */
 
-class Csv
+abstract class Csv
 {
     const NEWLINE = "\r\n";
     const BUFLINES = 100;
 
     const DEFAULT_SEPARATOR = ';';
     const DEFAULT_QUOTE = '"';
-    const DEFAULT_DIRECTORY = GALETTE_EXPORTS_PATH;
 
     const FILE_NOT_WRITABLE = -1;
     const DB_ERROR = -2;
 
-    private $_rs;
-    private $_separator;
-    private $_quote;
-    private $_escaped;
-    private $_file;
-    private $_result;
-    private $_current_line;
+    protected $separator;
+    protected $quote;
+    protected $escaped;
+    protected $file;
+    protected $result;
+    protected $current_line;
 
-    private $_parameted_path;
-    private $_parameted_file = 'exports.xml';
+    protected $allowed_extensions = array('csv');
+    protected $allowed_mimes = array(
+        'csv'    =>    'text/csv'
+    );
 
-    private $_accepted_separators = array(
+    protected $accepted_separators = array(
         ',',
         ';',
         '\t'
     );
 
-    private $_accepted_quotes = array(
+    protected $accepted_quotes = array(
         '"',
         "'"
     );
 
-    /**
-    * Default constructor
-    */
-    public function __construct()
-    {
-        $this->_parameted_path = GALETTE_CONFIG_PATH;
-        $this->_parameted_file = $this->_parameted_path . $this->_parameted_file;
-    }
-
+    private $_errors = array();
+    private $_default_directory;
 
     /**
-    * Export Array result set to CSV
-    *
-    * @param aray   $rs        Results as an array
-    * @param string $separator The CSV separator (either '\t', ';' or ','
-    *                          are accepted)
-    * @param char   $quote     how does fields should be quoted
-    * @param bool   $titles    does export shows column titles or not.
-    *                          Defaults to false.
-    * @param object $file      export to a file on disk. A file pointer
-    *                          should be passed here. Defaults to false.
-    *
-    * @return string CSV result
-    */
-    function export($rs, $separator, $quote, $titles=false, $file=false)
-    {
-        if (!$rs) {
-            return '';
-        }
-        //switch back to the default separator if not in accepted_separators array
-        if ( !in_array($separator, $this->_accepted_separators) ) {
-            $separator = self::DEFAULT_SEPARATOR;
-        }
-        //switch back to the default quote if not in accepted_quotes array
-        if ( !in_array($quote, $this->_accepted_quotes) ) {
-            $quote = self::DEFAULT_QUOTE;
-        }
-
-        $this->_result = '';
-        $this->_rs = $rs;
-        $this->max = count($this->_rs);
-        $this->_separator = $separator;
-        $this->_quote = $quote;
-        //dubbing quote for escaping
-        $this->_escaped = $quote . $quote;
-        $this->_file = $file;
-        $this->_current_line = 0;
-
-        $fields = array();
-        if ( $titles && !count($titles>1) ) {
-            foreach ( array_key($this->_rs) as $field ) {
-                $fields[] = $this->_quote . str_replace(
-                    $this->_quote, $this->_escaped, $field
-                ) . $this->_quote;
-            }
-            $this->_result .= implode($this->_separator, $fields) . self::NEWLINE;
-        } else if ( $titles && is_array($titles) && count($titles)>1 ) {
-            foreach ( $titles as $field ) {
-                $fields[] = $this->_quote . str_replace(
-                    $this->_quote, $this->_escaped, $field
-                ) . $this->_quote;
-            }
-            $this->_result .= implode($this->_separator, $fields) . self::NEWLINE;
-        }
-
-        foreach ( $this->_rs as $row ) {
-            $elts = array();
-
-            foreach ($row as $k => $v) {
-                $elts[] = $this->_quote . str_replace(
-                    $this->_quote, $this->_escaped, $v
-                ) . $this->_quote;
-            }
-
-            $this->_result .= implode($this->_separator, $elts) . self::NEWLINE;
-
-            $this->_current_line += 1;
-
-            $this->_write();
-        }
-        $this->_write(true);
-        return $this->_result;
-    }
-
-    /**
-    * Write export.
-    * If a file is defined, export will be outpoutted into it.
-    *   If not, it will be returned
-    *
-    * @param bool $last true if we write the latest line
-    *
-    * @return void
-    */
-    private function _write($last=false)
-    {
-        if (   $last && $this->_file
-            || !$last && $this->_file
-            && ($this->_current_line % self::BUFLINES) == 0
-        ) {
-            if ($this->_file === true) {
-                echo $this->_result;
-            } else {
-                fwrite($this->_file, $this->_result);
-            }
-            $this->_result = '';
-        }
-    }
-
-    /**
-     * Retrieve parameted export name
-     *
-     * @param string $id Parameted export identifier
+     * Default constructor
      *
-     * @return string
+     * @param string $default_dir Default directory
      */
-    public function getParamedtedExportName($id)
+    public function __construct($default_dir)
     {
-        $xml = simplexml_load_file($this->_parameted_file);
-        $xpath = $xml->xpath(
-            '/exports/export[@id=\'' . $id . '\'][1]/@name'
-        );
-        return (string)$xpath[0];
+        $this->_default_directory = $default_dir;
     }
 
     /**
-    * Get al list of all parameted exports
-    *
-    * @return array
-    */
-    public function getParametedExports()
-    {
-        $parameted = array();
-
-        $xml = simplexml_load_file($this->_parameted_file);
-
-        foreach ( $xml->export as $export) {
-            if ( !($export['inactive'] == 'inactive') ) {
-                $parameted[] = array(
-                    'id'          => (string)$export['id'],
-                    'name'        => (string)$export['name'],
-                    'description' => (string)$export['description']
-                );
-            }
-        }
-        return $parameted;
-    }
-
-    /**
-     * Retrieve a list of already existing exports
+     * Retrieve a list of already existing CSV files
      *
      * @return array
      */
-    public static function getExistingExports()
+    public function getExisting()
     {
-        $exports = array();
-        $files = glob(self::DEFAULT_DIRECTORY . '*.csv');
+        $csv_files = array();
+        $files = glob(
+            $this->_default_directory . '*.{' .
+            implode(',', $this->allowed_extensions) . '}',
+            GLOB_BRACE
+        );
         foreach ( $files as $file ) {
+            if ( $file === $this->_default_directory . 'readme.txt' ) {
+                continue;
+            }
             $mdate = date(_T("Y-m-d H:i:s"), filemtime($file));
 
             $raw_size = filesize($file);
@@ -257,34 +130,34 @@ class Csv
                 $size = $raw_size . ' octets';
             }
 
-            $exports[] = array(
-                'name'  => str_replace(self::DEFAULT_DIRECTORY, '', $file),
+            $csv_files[] = array(
+                'name'  => str_replace($this->_default_directory, '', $file),
                 'size'  => $size,
                 'date'  => $mdate
             );
         }
-        return $exports;
+        return $csv_files;
     }
 
     /**
-     * Remove existing export file
+     * Remove existing CSV file
      *
      * @param string $name File name
      *
      * @return boolean
      */
-    public function removeExport($name)
+    public function remove($name)
     {
         //let's ensure we do not have a path here
         $name = basename($name);
-        $filename=self::DEFAULT_DIRECTORY . $name;
+        $filename=$this->_default_directory . $name;
 
         if ( file_exists($filename) ) {
             $removed = unlink($filename);
             return $removed;
         } else {
             Analog::log(
-                'Export file ' . $filename .
+                'CSV file ' . $filename .
                 ' does not exists, no way to remove it!',
                 Analog::ERROR
             );
@@ -294,91 +167,49 @@ class Csv
     }
 
     /**
-    * Run selected export
-    *
-    * @param string $id export's id to run
-    *
-    * @return string filename used
-    */
-    public function runParametedExport($id)
+     * Accepted separators
+     *
+     * @return array list of accepted separators
+     */
+    public function getAcceptedSeparators()
     {
-        global $zdb;
-
-        $xml = simplexml_load_file($this->_parameted_file);
-
-        $xpath = $xml->xpath(
-            '/exports/export[@id=\'' . $id . '\'][not(@inactive)][1]'
-        );
-        $export = $xpath[0];
-
-        try {
-            $result = $zdb->db->query(
-                str_replace('galette_', PREFIX_DB, $export->query)
-            )->fetchAll(
-                \Zend_Db::FETCH_ASSOC
-            );
-
-            $filename=self::DEFAULT_DIRECTORY . $export['filename'];
-
-            $fp = fopen($filename, 'w');
-            if ( $fp ) {
-                $separator = ( $export->separator )
-                    ? $export->separator
-                    : self::DEFAULT_SEPARATOR;
-                $quote = ( $export->quote ) ? $export->quote : self::DEFAULT_QUOTE;
-                if ( $export->headers->none ) {
-                    //No title
-                    $title = false;
-                } else {
-                    $xpath = $export->xpath('headers/header');
-                    if ( count($xpath) == 0 ) {
-                        //show titles
-                        $title = true;
-                    } else {
-                        //titles from array
-                        foreach ( $xpath as $header ) {
-                            $title[] = (string)$header;
-                        }
-                    }
-                }
-
-                $this->export($result, $separator, $quote, $title, $fp);
-                fclose($fp);
-            } else {
-                Analog::log(
-                    'File ' . $filename . ' is not writeable.',
-                    Analog::ERROR
-                );
-                return self::FILE_NOT_WRITABLE;
-            }
-            return $export['filename'];
-        } catch (\Exception $e) {
-            Analog::log(
-                'An error occured while exporting | ' . $e->getMessage(),
-                Analog::ERROR
-            );
-            return self::DB_ERROR;
-        }
+        return $this->accepted_separators;
+    }
 
+    /**
+     * Accepted quotes
+     *
+     * @return array list of accepted quotes
+     */
+    public function getAcceptedQuotes()
+    {
+        return $this->accepted_quotes;
     }
 
     /**
-    * Accepted separators
-    *
-    * @return array list of accepted separators
-    */
-    public function getAcceptedSeparators()
+     * Add an error
+     *
+     * @param string $msg Error message
+     *
+     * @return void
+     */
+    public function addError($msg)
     {
-        return $this->_accepted_separators;
+        $class = get_class($this);
+        Analog::log(
+            '[' . $class  . '] ' . $msg,
+            Analog::ERROR
+        );
+        $this->_errors[] = $msg;
     }
 
     /**
-    * Accepted quotes
-    *
-    * @return array list of accepted quotes
-    */
-    public function getAcceptedQuotes()
+     * Get errors
+     *
+     * @return array
+     */
+    public function getErrors()
     {
-        return $this->_accepted_quotes;
+        return $this->_errors;
     }
 }
diff --git a/galette/lib/Galette/IO/CsvIn.php b/galette/lib/Galette/IO/CsvIn.php
new file mode 100644 (file)
index 0000000..ad85dd6
--- /dev/null
@@ -0,0 +1,570 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * CSV imports
+ *
+ * PHP version 5
+ *
+ * Copyright © 2013 The Galette Team
+ *
+ * This file is part of Galette (http://galette.tuxfamily.org).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  IO
+ * @package   Galette
+ *
+ * @author    Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2013 The Galette Team
+ * @license   http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @version   SVN: $Id$
+ * @link      http://galette.tuxfamily.org
+ * @since     Disponible depuis la Release 0.7.6dev - 2013-08-27
+ */
+
+namespace Galette\IO;
+
+use Galette\Entity\Adherent as Adherent;
+use Galette\Entity\ImportModel as ImportModel;
+use Galette\Entity\FieldsConfig as FieldsConfig;
+use Analog\Analog as Analog;
+
+/**
+ * CSV imports
+ *
+ * @category  IO
+ * @name      Csv
+ * @package   Galette
+ * @author    Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2013 The Galette Team
+ * @license   http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @link      http://galette.tuxfamily.org
+ * @since     Disponible depuis la Release 0.7.6dev - 2013-08-27
+ */
+
+class CsvIn extends Csv
+{
+    const DEFAULT_DIRECTORY = GALETTE_IMPORTS_PATH;
+
+    //constants that will not be overrided
+    const INVALID_FILENAME = -1;
+    const INVALID_EXTENSION = -2;
+    const FILE_TOO_BIG = -3;
+    const MIME_NOT_ALLOWED = -4;
+    const NEW_FILE_EXISTS = -5;
+    const INVALID_FILE = -6;
+    const DATA_IMPORT_ERROR = -7;
+    const CANT_WRITE = -8;
+    const MAX_FILE_SIZE = 2048;
+
+    //array keys contain litteral value of each forbidden character
+    //(to be used when showing an error).
+    //Maybe is there a better way to handle this...
+    private $_bad_chars = array(
+        '.'    =>    '\.',
+        '\\'    =>    '\\\\',
+        "'"    =>    "'",
+        ' '    =>    ' ',
+        '/'    =>    '\/',
+        ':'    =>    ':',
+        '*'    =>    '\*',
+        '?'    =>    '\?',
+        '"'    =>    '"',
+        '<'    =>    '<',
+        '>'    =>    '>',
+        '|'    =>    '|'
+    );
+    protected $allowed_extensions = array('csv', 'txt');
+    protected $allowed_mimes = array(
+        'csv'    =>    'text/csv',
+        'txt'    =>    'text/plain'
+    );
+
+    private $_fields;
+    private $_default_fields = array(
+        'nom_adh',
+        'prenom_adh',
+        'ddn_adh',
+        'adresse_adh',
+        'cp_adh',
+        'ville_adh',
+        'pays_adh',
+        'tel_adh',
+        'gsm_adh',
+        'email_adh',
+        'url_adh',
+        'prof_adh',
+        'pseudo_adh',
+        'societe_adh',
+        'login_adh',
+        'date_crea_adh',
+        'id_statut',
+        'info_public_adh',
+        'info_adh'
+    );
+
+    private $_dryrun = true;
+
+    private $_members_fields;
+    private $_required;
+
+    /**
+     * Default constructor
+     */
+    public function __construct()
+    {
+        parent::__construct(self::DEFAULT_DIRECTORY);
+    }
+
+    /**
+     * Load fields list from database or from default values
+     *
+     * @return void
+     */
+    private function _loadFields()
+    {
+        //at last, we got the defaults
+        $this->_fields = $this->_default_fields;
+
+        $model = new ImportModel();
+        //we go with default fields if model cannot be loaded
+        if ( $model->load() ) {
+            $this->_fields = $model->getFields();
+        }
+    }
+
+    /**
+     * Get default fields
+     *
+     * @return array
+     */
+    public function getDefaultFields()
+    {
+        return $this->_default_fields;
+    }
+
+    /**
+     * Import members from CSV file
+     *
+     * @param string  $filename       CSV filename
+     * @param array   $members_fields Members fields
+     * @param boolean $dryrun         Run in dry run mode (do not store in database)
+     *
+     * @return boolean
+     */
+    public function import($filename, $members_fields, $dryrun)
+    {
+        if ( !file_exists(self::DEFAULT_DIRECTORY . '/' . $filename)
+            || !is_readable(self::DEFAULT_DIRECTORY . '/' . $filename)
+        ) {
+            Analog::log(
+                'File ' . $filename . ' does not exists or cannot be read.',
+                Analog::ERROR
+            );
+            return false;
+        }
+
+        if ( $dryrun === false ) {
+            $this->_dryrun = false;
+        }
+
+        $this->_loadFields();
+        $this->_members_fields = $members_fields;
+
+        if ( !$this->_check($filename) ) {
+            return self::INVALID_FILE;
+        }
+
+        if ( !$this->_storeMembers($filename) ) {
+            return self::DATA_IMPORT_ERROR;
+        }
+
+        return true;
+    }
+
+    /**
+     * Check if input file meet requirements
+     *
+     * @param string $filename File name
+     *
+     * @return boolean
+     */
+    private function _check($filename)
+    {
+        //deal with mac e-o-l encoding -- see if needed
+        //@ini_set('auto_detect_line_endings', true);
+        $handle = fopen(self::DEFAULT_DIRECTORY . '/' . $filename, 'r');
+        if (! $handle) {
+            Analog::log(
+                'File ' . $filename . ' cannot be open!',
+                Analog::ERROR
+            );
+            $this->addError(
+                str_replace(
+                    '%filename',
+                    $filename,
+                    _T('File %filename cannot be open!')
+                )
+            );
+            return false;
+        }
+
+        if ( $handle !== false ) {
+
+            $cnt_fields = count($this->_fields);
+
+            //check required fields
+            $fc = new FieldsConfig(Adherent::TABLE, $this->_members_fields);
+            $config_required = $fc->getRequired();
+            $this->_required = array();
+
+            foreach ( array_keys($config_required) as $field ) {
+                if ( in_array($field, $this->_fields) ) {
+                    $this->_required[$field] = $field;
+                }
+            }
+
+            $row = 0;
+            while ( ($data = fgetcsv(
+                $handle,
+                1000,
+                self::DEFAULT_SEPARATOR,
+                self::DEFAULT_QUOTE
+            )) !== false) {
+
+                //check fields count
+                $count = count($data);
+                if ( $count != $cnt_fields ) {
+                    $this->addError(
+                        str_replace(
+                            array('%should_count', '%count', '%row'),
+                            array($cnt_fields, $count, $row),
+                            _T("Fields count mismatch... There should be %should_count fields and there are %count (row %row)")
+                        )
+                    );
+                    return false;
+                }
+
+                //check required fields
+                if ( $row > 0 ) {
+                    //header line is the first one. Here comes data
+                    $col = 0;
+                    foreach ( $data as $column ) {
+                        if ( in_array($this->_fields[$col], $this->_required)
+                            && trim($column) == ''
+                        ) {
+                            $this->addError(
+                                str_replace(
+                                    array('%field', '%row'),
+                                    array($this->_fields[$col], $row),
+                                    _T("Field %field is required, but missing in row %row")
+                                )
+                            );
+                            return false;
+                        }
+                        $col++;
+                    }
+                }
+
+                $row++;
+            }
+            fclose($handle);
+
+            if ( !($row > 1) ) {
+                //no data in file, just headers line
+                $this->addError(
+                    _T("File is empty!")
+                );
+                return false;
+            } else {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Store members in database
+     *
+     * @param string $filename CSV filename
+     *
+     * @return boolean
+     */
+    private function _storeMembers($filename)
+    {
+        $handle = fopen(self::DEFAULT_DIRECTORY . '/' . $filename, 'r');
+
+        $row = 0;
+
+        try {
+            while ( ($data = fgetcsv(
+                $handle,
+                1000,
+                self::DEFAULT_SEPARATOR,
+                self::DEFAULT_QUOTE
+            )) !== false) {
+                if ( $row > 0 ) {
+                    $col = 0;
+                    $values = array();
+                    foreach ( $data as $column ) {
+                        $values[$this->_fields[$col]] = $column;
+                        $col++;
+                    }
+                    //import member itself
+                    $member = new Adherent();
+                    $valid = $member->check($values, $this->_required, null);
+                    if ( $valid === true ) {
+                        if ( $this->_dryrun === false ) {
+                            $store = $member->store();
+                            if ( $store !== true ) {
+                                $this->addError(
+                                    str_replace(
+                                        array('%row', '%name'),
+                                        array($row, $member->sname),
+                                        _T("An error occured storing member at row %row (%name):")
+                                    )
+                                );
+                                return false;
+                            }
+                        }
+                    } else {
+                        $this->addError(
+                            str_replace(
+                                array('%row', '%name'),
+                                array($row, $member->sname),
+                                _T("An error occured storing member at row %row (%name):")
+                            )
+                        );
+                        if ( is_array($valid) ) {
+                            foreach ( $valid as $e ) {
+                                $this->addError($e);
+                            }
+                        }
+                        return false;
+                    }
+                }
+                $row++;
+            }
+            return true;
+        } catch ( \Exception $e ) {
+            $this->addError($e->getMessage());
+        }
+
+        return false;
+    }
+
+    /**
+     * Stores an file on the disk and in the database
+     *
+     * @param object  $file the uploaded file
+     * @param boolean $ajax If the file cames from an ajax call (dnd)
+     *
+     * @return true|false result of the storage process
+     */
+    public function store($file, $ajax = false)
+    {
+        /** TODO:
+            - fix max size (by preferences ?)
+        */
+        $class = get_class($this);
+
+        $name = $file['name'];
+        $tmpfile = $file['tmp_name'];
+
+        //First, does the file have a valid name?
+        $reg = "/^(.[^" . implode('', $this->_bad_chars) . "]+)\.(" .
+            implode('|', $this->allowed_extensions) . ")$/i";
+        if ( preg_match($reg, $name, $matches) ) {
+            Analog::log(
+                '[' . $class . '] Filename and extension are OK, proceed.',
+                Analog::DEBUG
+            );
+            $extension = strtolower($matches[2]);
+        } else {
+            $erreg = "/^(.[^" . implode('', $this->_bad_chars) . "]+)\.(.*)/i";
+            $m = preg_match($erreg, $name, $errmatches);
+
+            $err_msg = '[' . $class . '] ';
+            if ( $m == 1 ) {
+                //ok, we got a good filename and an extension. Extension is bad :)
+                $err_msg .= 'Invalid extension for file ' . $name . '.';
+                $ret = self::INVALID_EXTENSION;
+            } else {
+                $err_msg = 'Invalid filename `' . $name  . '` (Tip: ';
+                $err_msg .= preg_replace(
+                    '|%s|',
+                    htmlentities($this->getbadChars()),
+                    "file name should not contain any of: %s). "
+                );
+                $ret = self::INVALID_FILENAME;
+            }
+
+            Analog::log(
+                $err_msg,
+                Analog::ERROR
+            );
+            return $ret;
+        }
+
+        //Second, let's check file size
+        if ( $file['size'] > ( $class::MAX_FILE_SIZE * 1024 ) ) {
+            Analog::log(
+                '[' . $class . '] File is too big (' . ( $file['size'] * 1024 ) .
+                'Ko for maximum authorized ' . ( $class::MAX_FILE_SIZE * 1024 ) .
+                'Ko',
+                Analog::ERROR
+            );
+            return self::FILE_TOO_BIG;
+        } else {
+            Analog::log('[' . $class . '] Filesize is OK, proceed', Analog::DEBUG);
+        }
+
+        $mime = mime_content_type($tmpfile);
+
+        if ( !in_array($mime, $this->allowed_mimes) ) {
+            Analog::log(
+                '[' . $class . '] Mimetype `' . $mime . '` not allowed',
+                Analog::ERROR
+            );
+            return self::MIME_NOT_ALLOWED;
+        } else {
+            Analog::log(
+                '[' . $class . '] Mimetype is allowed, proceed',
+                Analog::DEBUG
+            );
+        }
+
+        $new_file = self::DEFAULT_DIRECTORY . $name;
+
+        if ( file_exists($new_file) ) {
+            Analog::log(
+                '[' . $class . '] File `' . $new_file . '` already exists',
+                Analog::ERROR
+            );
+            return self::NEW_FILE_EXISTS;
+        }
+
+        $in_place = false;
+        if ( $ajax === true ) {
+            $in_place = rename($tmpfile, $new_file);
+        } else {
+            $in_place = move_uploaded_file($tmpfile, $new_file);
+        }
+
+        if ( $in_place === false ) {
+            return self::CANT_WRITE;
+        }
+        return $in_place;
+    }
+
+    /**
+     * Returns allowed extensions
+     *
+     * @return string comma separated allowed extensiosn
+     */
+    public function getAllowedExts()
+    {
+        return implode(', ', $this->allowed_extensions);
+    }
+
+    /**
+     * Return the array of allowed mime types
+     *
+     * @return array
+     */
+    public function getAllowedMimeTypes()
+    {
+        return $this->allowed_mimes;
+    }
+
+    /**
+     * Return textual error message
+     *
+     * @param int $code The error code
+     *
+     * @return string Localized message
+     */
+    public function getErrorMessage($code)
+    {
+        $error = _T("An error occued.");
+        switch( $code ) {
+        case self::INVALID_FILENAME:
+            $error = _T("File name is invalid, it should not contain any special character or space.");
+            break;
+        case self::INVALID_EXTENSION:
+            $error = preg_replace(
+                '|%s|',
+                $this->getAllowedExts(),
+                _T("- File extension is not allowed, only %s files are.")
+            );
+            break;
+        case self::FILE_TOO_BIG:
+            $error = preg_replace(
+                '|%d|',
+                self::MAX_FILE_SIZE,
+                _T("File is too big. Maximum allowed size is %dKo")
+            );
+            break;
+        case self::MIME_NOT_ALLOWED:
+            /** FIXME: should be more descriptive */
+            $error = _T("Mime-Type not allowed");
+            break;
+        case self::NEW_FILE_EXISTS:
+            $error = _T("A file with that name already exists!");
+            break;
+        case self::INVALID_FILE:
+            $error = _T("File does not comply with requirements.");
+            break;
+        case self::DATA_IMPORT_ERROR:
+            $error = _T("An error occured while importing members");
+            break;
+        case self::CANT_WRITE:
+            $error = _T("Unable to write file or temporary file");
+            break;
+        }
+        return $error;
+    }
+
+    /**
+     * Return textual error message send by PHP after upload attempt
+     *
+     * @param int $error_code The error code
+     *
+     * @return string Localized message
+     */
+    public function getPhpErrorMessage($error_code)
+    {
+        switch ($error_code) {
+        case UPLOAD_ERR_INI_SIZE:
+            return _T("The uploaded file exceeds the upload_max_filesize directive in php.ini");
+        case UPLOAD_ERR_FORM_SIZE:
+            return _T("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form");
+        case UPLOAD_ERR_PARTIAL:
+            return _T("The uploaded file was only partially uploaded");
+        case UPLOAD_ERR_NO_FILE:
+            return _T("No file was uploaded");
+        case UPLOAD_ERR_NO_TMP_DIR:
+            return _T("Missing a temporary folder");
+        case UPLOAD_ERR_CANT_WRITE:
+            return _T("Failed to write file to disk");
+        case UPLOAD_ERR_EXTENSION:
+            return _T("File upload stopped by extension");
+        default:
+            return _T("Unknown upload error");
+        }
+    }
+}
diff --git a/galette/lib/Galette/IO/CsvOut.php b/galette/lib/Galette/IO/CsvOut.php
new file mode 100644 (file)
index 0000000..2990d93
--- /dev/null
@@ -0,0 +1,280 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * CSV exports
+ *
+ * PHP version 5
+ *
+ * Copyright © 2009-2013 The Galette Team
+ *
+ * This file is part of Galette (http://galette.tuxfamily.org).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  IO
+ * @package   Galette
+ *
+ * @author    Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2009-2013 The Galette Team
+ * @license   http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @version   SVN: $Id$
+ * @link      http://galette.tuxfamily.org
+ * @since     Disponible depuis la Release 0.7alpha - 2009-02-09
+ */
+
+namespace Galette\IO;
+
+use Analog\Analog as Analog;
+
+/**
+ * CSV exports
+ *
+ * @category  IO
+ * @name      Csv
+ * @package   Galette
+ * @author    Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2009-2013 The Galette Team
+ * @license   http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @link      http://galette.tuxfamily.org
+ * @since     Disponible depuis la Release 0.7alpha - 2009-02-09
+ */
+
+class CsvOut extends Csv
+{
+    const DEFAULT_DIRECTORY = GALETTE_EXPORTS_PATH;
+
+    private $_rs;
+    private $_parameted_path;
+    private $_parameted_file = 'exports.xml';
+
+    /**
+     * Default constructor
+     */
+    public function __construct()
+    {
+        parent::__construct(self::DEFAULT_DIRECTORY);
+        $this->_parameted_path = GALETTE_CONFIG_PATH;
+        $this->_parameted_file = $this->_parameted_path . $this->_parameted_file;
+    }
+
+    /**
+     * Export Array result set to CSV
+     *
+     * @param aray   $rs        Results as an array
+     * @param string $separator The CSV separator (either '\t', ';' or ','
+     *                          are accepted)
+     * @param char   $quote     how does fields should be quoted
+     * @param bool   $titles    does export shows column titles or not.
+     *                          Defaults to false.
+     * @param object $file      export to a file on disk. A file pointer
+     *                          should be passed here. Defaults to false.
+     *
+     * @return string CSV result
+     */
+    function export($rs, $separator, $quote, $titles=false, $file=false)
+    {
+        if (!$rs) {
+            return '';
+        }
+        //switch back to the default separator if not in accepted_separators array
+        if ( !in_array($separator, $this->accepted_separators) ) {
+            $separator = self::DEFAULT_SEPARATOR;
+        }
+        //switch back to the default quote if not in accepted_quotes array
+        if ( !in_array($quote, $this->accepted_quotes) ) {
+            $quote = self::DEFAULT_QUOTE;
+        }
+
+        $this->result = '';
+        $this->_rs = $rs;
+        $this->max = count($this->_rs);
+        $this->separator = $separator;
+        $this->quote = $quote;
+        //dubbing quote for escaping
+        $this->escaped = $quote . $quote;
+        $this->file = $file;
+        $this->current_line = 0;
+
+        $fields = array();
+        if ( $titles && !count($titles>1) ) {
+            foreach ( array_key($this->_rs) as $field ) {
+                $fields[] = $this->quote . str_replace(
+                    $this->quote, $this->escaped, $field
+                ) . $this->quote;
+            }
+            $this->result .= implode($this->separator, $fields) . self::NEWLINE;
+        } else if ( $titles && is_array($titles) && count($titles)>1 ) {
+            foreach ( $titles as $field ) {
+                $fields[] = $this->quote . str_replace(
+                    $this->quote, $this->escaped, $field
+                ) . $this->quote;
+            }
+            $this->result .= implode($this->separator, $fields) . self::NEWLINE;
+        }
+
+        foreach ( $this->_rs as $row ) {
+            $elts = array();
+
+            if ( is_array($row) > 0 ) {
+                foreach ($row as $k => $v) {
+                    $elts[] = $this->quote . str_replace(
+                        $this->quote, $this->escaped, $v
+                    ) . $this->quote;
+                }
+
+                $this->result .= implode($this->separator, $elts) . self::NEWLINE;
+
+                $this->current_line += 1;
+
+                $this->_write();
+            }
+        }
+        $this->_write(true);
+        return $this->result;
+    }
+
+    /**
+     * Write export.
+     * If a file is defined, export will be outpoutted into it.
+     *   If not, it will be returned
+     *
+     * @param bool $last true if we write the latest line
+     *
+     * @return void
+     */
+    private function _write($last=false)
+    {
+        if (   $last && $this->file
+            || !$last && $this->file
+            && ($this->current_line % self::BUFLINES) == 0
+        ) {
+            if ($this->file === true) {
+                echo $this->result;
+            } else {
+                fwrite($this->file, $this->result);
+            }
+            $this->result = '';
+        }
+    }
+
+    /**
+     * Retrieve parameted export name
+     *
+     * @param string $id Parameted export identifier
+     *
+     * @return string
+     */
+    public function getParamedtedExportName($id)
+    {
+        $xml = simplexml_load_file($this->_parameted_file);
+        $xpath = $xml->xpath(
+            '/exports/export[@id=\'' . $id . '\'][1]/@name'
+        );
+        return (string)$xpath[0];
+    }
+
+    /**
+     * Get al list of all parameted exports
+     *
+     * @return array
+     */
+    public function getParametedExports()
+    {
+        $parameted = array();
+
+        $xml = simplexml_load_file($this->_parameted_file);
+
+        foreach ( $xml->export as $export) {
+            if ( !($export['inactive'] == 'inactive') ) {
+                $parameted[] = array(
+                    'id'          => (string)$export['id'],
+                    'name'        => (string)$export['name'],
+                    'description' => (string)$export['description']
+                );
+            }
+        }
+        return $parameted;
+    }
+
+    /**
+     * Run selected export
+     *
+     * @param string $id export's id to run
+     *
+     * @return string filename used
+     */
+    public function runParametedExport($id)
+    {
+        global $zdb;
+
+        $xml = simplexml_load_file($this->_parameted_file);
+
+        $xpath = $xml->xpath(
+            '/exports/export[@id=\'' . $id . '\'][not(@inactive)][1]'
+        );
+        $export = $xpath[0];
+
+        try {
+            $result = $zdb->db->query(
+                str_replace('galette_', PREFIX_DB, $export->query)
+            )->fetchAll(
+                \Zend_Db::FETCH_ASSOC
+            );
+
+            $filename=self::DEFAULT_DIRECTORY . $export['filename'];
+
+            $fp = fopen($filename, 'w');
+            if ( $fp ) {
+                $separator = ( $export->separator )
+                    ? $export->separator
+                    : self::DEFAULT_SEPARATOR;
+                $quote = ( $export->quote ) ? $export->quote : self::DEFAULT_QUOTE;
+                if ( $export->headers->none ) {
+                    //No title
+                    $title = false;
+                } else {
+                    $xpath = $export->xpath('headers/header');
+                    if ( count($xpath) == 0 ) {
+                        //show titles
+                        $title = true;
+                    } else {
+                        //titles from array
+                        foreach ( $xpath as $header ) {
+                            $title[] = (string)$header;
+                        }
+                    }
+                }
+
+                $this->export($result, $separator, $quote, $title, $fp);
+                fclose($fp);
+            } else {
+                Analog::log(
+                    'File ' . $filename . ' is not writeable.',
+                    Analog::ERROR
+                );
+                return self::FILE_NOT_WRITABLE;
+            }
+            return $export['filename'];
+        } catch (\Exception $e) {
+            Analog::log(
+                'An error occured while exporting | ' . $e->getMessage(),
+                Analog::ERROR
+            );
+            return self::DB_ERROR;
+        }
+
+    }
+}
index 1e9585eb0a9cae8cc39f5bf64b75003bc94f29b3..97f4c811971c7375c291cbaa697326e83212b72d 100644 (file)
@@ -5,7 +5,7 @@
             <p>{_T string="The following files have been written on disk:"}</p>
             <ul>
 {foreach item=ex from=$written}
-                <li><a href="get_export.php?file={$ex.file}">{$ex.name} ({$ex.file})</a></li>
+                <li><a href="get_export.php?file={$ex.name}">{$ex.name} ({$ex.file})</a></li>
 {/foreach}
             </ul>
         </div>
@@ -37,7 +37,7 @@
                                     {$export.size}
                                 </td>
                                 <td class="actions_row">
-                                    <a href="export.php?sup={$export.name}" title="{_T string="Remove '%export' from disk" pattern="/%export/" replace=$export.name}"><img src="{$template_subdir}images/delete.png" alt="{_T string="Delete"}"/></a>
+                                    <a href="export.php?sup={$export.name}" title="{_T string="Remove '%file' from disk" pattern="/%file/" replace=$export.name}"><img src="{$template_subdir}images/delete.png" alt="{_T string="Delete"}"/></a>
                                 </td>
                             </tr>
     {/foreach}
index 09860c8d8f705eac718defc9a8756a47c5081196..a2da48515bfbf532909ae8ff40070a5add280ebc 100644 (file)
@@ -656,6 +656,13 @@ legend #btnadd_small {
     background-image:url(images/delete.png), url("jquery-ui/images/ui-bg_glass_75_ffb619_1x400.png");
 }
 
+#preferences {
+    background-image:url(images/icon-prefs.png), url("jquery-ui/images/ui-bg_glass_75_e6e6e6_1x400.png");
+}
+#preferences.ui-state-hover {
+    background-image:url(images/icon-prefs.png), url("jquery-ui/images/ui-bg_glass_75_ffb619_1x400.png");
+}
+
 #next, #installpage input[type="submit"] {
     background-image:url(images/next.png), url("jquery-ui/images/ui-bg_glass_75_e6e6e6_1x400.png");
     padding-left: 0.4em!important;
diff --git a/galette/templates/default/images/icon-prefs.png b/galette/templates/default/images/icon-prefs.png
new file mode 100644 (file)
index 0000000..13395f0
Binary files /dev/null and b/galette/templates/default/images/icon-prefs.png differ
diff --git a/galette/templates/default/import.tpl b/galette/templates/default/import.tpl
new file mode 100644 (file)
index 0000000..7dd051b
--- /dev/null
@@ -0,0 +1,112 @@
+        <p class="center">
+            <a class="button" id="preferences" href="import_model.php">{_T string="Configure import model"}</a>
+        </p>
+        <form class="form" action="import.php" method="post" enctype="multipart/form-data">
+            <fieldset>
+                <legend class="ui-state-active ui-corner-top">{_T string="Existing files"}</legend>
+                <div class="warningbox">
+                    {_T string="Warning: Don't forget to backup your current database."}
+                </div>
+                <div>
+{if $existing|@count gt 0}
+                    <p>{_T string="The following files seems ready to import on the disk:"}</p>
+                    <table class="listing">
+                        <thead>
+                            <tr>
+                                <th>{_T string="Name"}</th>
+                                <th>{_T string="Date"}</th>
+                                <th>{_T string="Size"}</th>
+                                <th class="actions_row"></th>
+                            </tr>
+                        </thead>
+                        <tbody>
+    {foreach item=import from=$existing name=existing_list}
+                            <tr class="{if $smarty.foreach.existing_list.iteration % 2 eq 0}even{else}odd{/if}">
+                                <td>
+                                    <input type="radio" name="import_file" id="file{$smarty.foreach.existing_list.iteration}" value="{$import.name}"{if isset($import_file) and $import_file eq $import.name} checked="checked"{/if}/>
+                                    <label for="file{$smarty.foreach.existing_list.iteration}">{$import.name}</label> (<a href="get_import.php?file={$import.name}">{_T string="see"}</a>)
+                                </td>
+                                <td>
+                                    {$import.date}
+                                </td>
+                                <td>
+                                    {$import.size}
+                                </td>
+                                <td class="actions_row">
+                                    <a href="import.php?sup={$import.name}" title="{_T string="Remove '%file' from disk" pattern="/%file/" replace=$import.name}"><img src="{$template_subdir}images/delete.png" alt="{_T string="Delete"}"/></a>
+                                </td>
+                            </tr>
+    {/foreach}
+                        </tbody>
+                    </table>
+                    <div class="button-container">
+                        <label for="dryrun" title="{_T string="Run the import process, but do *not* store anything in the database"}">{_T string="Dry run"}</label>
+                        <input type="checkbox" name="dryrun" id="dryrun" value="1"{if isset($dryrun) and $dryrun eq true} checked="checked"{/if}/><br/>
+                        <input type="submit" name="import" id="import" value="{_T string="Import"}"/>
+                    </div>
+{else}
+                    <p>{_T string="No import file actually exists."}<br/>{_T string="Use upload form below to send a new file on server, or copy it directly in the imports directory."}</p>
+{/if}
+                </div>
+            </fieldset>
+
+            <fieldset>
+                <legend class="ui-state-active ui-corner-top">{_T string="Upload new file"}</legend>
+                <div>
+                    <p>
+                        <label for="new_file">{_T string="Select a file:"}</label>
+                        <input class="labelalign" type="file" name="new_file" accept="text/csv" id="new_file"/>
+                    </p>
+                    <div class="button-container">
+                        <input type="submit" name="upload" value="{_T string="Upload file"}" id="upload"/>
+                    </div>
+                </div>
+            </fieldset>
+        </form>
+
+        <script type="text/javascript">
+            $(function() {
+                _collapsibleFieldsets();
+                //bind import click to check if one is selected
+                $('#import').on('click', function(){
+                    if ( $('input[name=import_file]:checked').length > 0 ) {
+                        return true;
+                    } else {
+                        var _el = $('<div id="pleaseselect" title="{_T string="No file selected" escape="js"}">{_T string="Please make sure to select one file to import." escape="js"}</div>');
+                        _el.appendTo('body').dialog({
+                            modal: true,
+                            buttons: {
+                                Ok: function() {
+                                    $(this).dialog( "close" );
+                                }
+                            },
+                            close: function(event, ui){
+                                _el.remove();
+                            }
+                        });
+                        return false;
+                    }
+                });
+
+                $('#upload').click(function(){
+                    var _selected = $('#new_file')[0].files.length;
+                    if ( _selected == 0 ) {
+                         var _el = $('<div id="pleaseupload" title="{_T string="No file to upload" escape="js"}">{_T string="Please make sure to select one file to upload." escape="js"}</div>');
+                        _el.appendTo('body').dialog({
+                            modal: true,
+                            buttons: {
+                                Ok: function() {
+                                    $(this).dialog( "close" );
+                                }
+                            },
+                            close: function(event, ui){
+                                _el.remove();
+                            }
+                        });
+                        return false;
+                    } else {
+                        return true;
+                    }
+                });
+            });
+        </script>
diff --git a/galette/templates/default/import_model.tpl b/galette/templates/default/import_model.tpl
new file mode 100644 (file)
index 0000000..1b29fd6
--- /dev/null
@@ -0,0 +1,88 @@
+<div id="model_tabs" class="tabbed">
+    <ul>
+        <li><a href="#current">{_T string="Current model"}</a></li>
+        <li><a href="#change">{_T string="Change model"}</a></li>
+    </ul>
+    <div id="current">
+        <table class="listing">
+            <caption>
+                {if $defaults_loaded}
+                    {_T string="Default fields"}
+                {else}
+                    {_T string="Model parameted on %date" pattern="/%date/" replace=$model->getCreationDate()}
+                {/if}
+            </caption>
+            <thead>
+                <tr>
+                    <th>{_T string="Field"}</th>
+                </tr>
+            </thead>
+            <tbody>
+        {foreach item=field from=$fields name=fields_list}
+                <tr class="{if $smarty.foreach.fields_list.iteration % 2 eq 0}even{else}odd{/if}">
+                    <td>{$members_fields[$field]['label']|replace:':':''}</td>
+                </tr>
+        {/foreach}
+        </table>
+        <div class="button-container">
+            <a id="memberslist" class="button" href="import_model.php?generate=true">{_T string="Generate empty CSV file"}</a>
+            {if !$defaults_loaded}
+            <a id="delete" class="button" href="import_model.php?remove=true">{_T string="Remove model and back to defaults"}</a>
+            {/if}
+        </div>
+    </div>
+    <div id="change">
+        <form action="import_model.php" method="POST">
+        <table class="listing">
+            <thead>
+                <tr>
+                    <th></th>
+                    <th>{_T string="Field"}</th>
+                </tr>
+            </thead>
+            <tbody>
+        {foreach item=field from=$members_fields name=members_fields_list key=k}
+                <tr class="{if $smarty.foreach.members_fields_list.iteration % 2 eq 0}even{else}odd{/if}">
+                    <td>
+                        <input type="checkbox" name="fields[]" id="field_{$k}" value="{$k}"{if in_array($k, $fields)} checked="checked"{/if}/>
+                    </td>
+                    <td>
+                        <label for="field_{$k}">{$field['label']|replace:':':''}</label>
+                    </td>
+                </tr>
+        {/foreach}
+        </table>
+        <div class="button-container">
+            <input type="submit" name="upload" value="{_T string="Store new model"}"/>
+        </div>
+        </form>
+    </div>
+</div>
+<p class="center">
+    <a class="button" id="btnback" href="import.php">{_T string="Go back to import page"}</a>
+</p>
+
+<script type="text/javascript">
+    $(function(){
+        $('#model_tabs').tabs();
+
+        $('input[type="submit"]').click(function(){
+            var _checkeds = $('table.listing').find('input[type=checkbox]:checked').length;
+            if ( _checkeds == 0 ) {
+                var _el = $('<div id="pleaseselect" title="{_T string="No field selected" escape="js"}">{_T string="Please make sure to select at least one field from the list to perform this action." escape="js"}</div>');
+                _el.appendTo('body').dialog({
+                    modal: true,
+                    buttons: {
+                        Ok: function() {
+                            $(this).dialog( "close" );
+                        }
+                    },
+                    close: function(event, ui){
+                        _el.remove();
+                    }
+                });
+                return false;
+            }
+        });
+    });
+</script>
index 8dd11266ad26f3c0ef9411778cce4f498246009d..7fea8d52c115d090bdf70b48013fc6ec83345a66 100644 (file)
@@ -123,6 +123,7 @@ We have to use a template file, so Smarty will do its work (like replacing varia
             <li{if $PAGENAME eq "history.php"} class="selected"{/if}><a href="{$galette_base_path}history.php" title="{_T string="View application's logs"}">{_T string="Logs"}</a></li>
             <li{if $PAGENAME eq "gestion_mailings.php"} class="selected"{/if}><a href="{$galette_base_path}gestion_mailings.php" title="{_T string="Manage mailings that has been sent"}">{_T string="Manage mailings"}</a></li>
             <li{if $PAGENAME eq "export.php"} class="selected"{/if}><a href="{$galette_base_path}export.php" title="{_T string="Export some data in various formats"}">{_T string="Exports"}</a></li>
+            <li{if $PAGENAME eq "import.php" or $PAGENAME eq "import_model.php"} class="selected"{/if}><a href="{$galette_base_path}import.php" title="{_T string="Import members from CSV files"}">{_T string="Imports"}</a></li>
             <li class="mnu_last{if $PAGENAME eq "charts.php"} selected{/if}"><a href="{$galette_base_path}charts.php" title="{_T string="Various charts"}">{_T string="Charts"}</a></li>
   {else}
             <li{if $PAGENAME eq "gestion_contributions.php"} class="selected"{/if}><a href="{$galette_base_path}gestion_contributions.php" title="{_T string="View and filter all my contributions"}">{_T string="My contributions"}</a></li>