!/galette/exports/readme.txt
!/galette/exports/.htaccess
+# /galette/imports/
+/galette/imports/*
+!/galette/imports/readme.txt
+!/galette/imports/.htaccess
+
# /galette/logs/
/galette/logs/*.log
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/');
}
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;
//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'])
}
}
$filename = 'filtered_memberslist.csv';
- $filepath = Csv::DEFAULT_DIRECTORY . $filename;
+ $filepath = CsvOut::DEFAULT_DIRECTORY . $filename;
$fp = fopen($filepath, 'w');
if ( $fp ) {
$res = $csv->export(
);
}
- 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 `' .
/**
* Export
*
- * Permet l'export de données au format CSV
+ * Data export in CSV format
*
* PHP version 5
*
}
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',
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(
}
}
-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);
$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(
--- /dev/null
+<?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');
+}
--- /dev/null
+<?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');
--- /dev/null
+<?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');
+}
+
--- /dev/null
+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>
--- /dev/null
+
+This file is here because most ftp-clients don't upload empty directory.
+
+You can delete this file after your upload.
+
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
}
GALETTE_TEMPIMAGES_PATH,
GALETTE_CONFIG_PATH,
GALETTE_EXPORTS_PATH,
+ GALETTE_IMPORTS_PATH,
GALETTE_LOGS_PATH
);
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;
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;
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);
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;
--- /dev/null
+-- 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;
--- /dev/null
+-- 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;
--- /dev/null
+-- 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;
'An error occured cleaning history. ',
Analog::WARNING
);
- $this->add('Arror flushing logs');
+ $this->add('Error flushing logs');
return false;
}
$this->add('Logs flushed');
$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:
}
/**
- * Return textual erro rmessage send by PHP after upload attempt
+ * Return textual error message send by PHP after upload attempt
*
* @param int $error_code The error code
*
} 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;
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;
--- /dev/null
+<?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;
+ }
+}
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
- * CSV exports
+ * CSV files
*
* PHP version 5
*
use Analog\Analog as Analog;
/**
- * CSV exports
+ * CSV files
*
* @category IO
* @name Csv
* @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);
$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
);
}
/**
- * 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;
}
}
--- /dev/null
+<?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");
+ }
+ }
+}
--- /dev/null
+<?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;
+ }
+
+ }
+}
<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>
{$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}
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;
--- /dev/null
+ <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>
--- /dev/null
+<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>
<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>