File src/include/lib/Garradin/Membres/Import.php artifact 9fc2a43ba6 part of check-in 67a32db6a4


<?php

namespace Garradin\Membres;

use Garradin\Membres;
use Garradin\Config;
use Garradin\DB;
use Garradin\Utils;
use Garradin\CSV;
use Garradin\CSV_Custom;
use Garradin\UserException;

class Import
{
	protected $champs;

	/**
	 * Importer un CSV générique
	 * @return boolean                   TRUE en cas de succès
	 */
	public function fromCustomCSV(CSV_Custom $csv, int $current_user_id)
	{
		$db = DB::getInstance();
		$db->begin();
		$membres = new Membres;

		foreach ($csv->iterate() as $line => $row)
		{
			if (!empty($row->numero) && $row->numero > 0)
			{
				$numero = (int)$row->numero;
			}
			else
			{
				unset($row->numero);
				$numero = false;
			}

			try {
				if ($numero && ($id = $membres->getIDWithNumero($numero)))
				{
					if ($id === $current_user_id)
					{
						// Ne pas modifier le membre courant, on risque de se tirer une balle dans le pied
						continue;
					}

					$membres->edit($id, (array)$row);
				}
				else
				{
					$membres->add((array)$row, false);
				}
			}
			catch (UserException $e)
			{
				$db->rollback();
				throw new UserException('Erreur sur la ligne ' . $line . ' : ' . $e->getMessage());
			}
		}

		$db->commit();
		return true;
	}

	/**
	 * Importer un CSV de la liste des membres depuis un export Garradin
	 * @param  string $path     Chemin vers le CSV
	 * @param  int    $current_user_id
	 * @return boolean          TRUE en cas de succès
	 */
	public function fromGarradinCSV($path, $current_user_id)
	{
		if (!file_exists($path) || !is_readable($path))
		{
			throw new \RuntimeException('Fichier inconnu : '.$path);
		}

		$fp = CSV::open($path);

		if (!$fp)
		{
			return false;
		}

		$db = DB::getInstance();
		$db->begin();
		$membres = new Membres;

		// On récupère les champs qu'on peut importer
		$champs_membres = Config::getInstance()->get('champs_membres');
		$champs_multiples = $champs_membres->getMultiples();
		$champs = $champs_membres->getKeys();
		$champs[] = 'date_inscription';

		$line = 0;
		$delim = CSV::findDelimiter($fp);
		CSV::skipBOM($fp);

		while (!feof($fp))
		{
			$row = fgetcsv($fp, 4096, $delim);

			$line++;

			if (empty($row))
			{
				continue;
			}

			if ($line == 1)
			{
				if (empty($row[0]) || !is_string($row[0]) || is_numeric($row[0]))
				{
					$db->rollback();
					throw new UserException('Erreur sur la ligne 1 : devrait contenir l\'en-tête des colonnes.');
				}

				$columns = array_flip($row);
				continue;
			}

			if (!isset($columns)) {
				throw new UserException('Entête introuvable');
			}

			if (count($row) != count($columns))
			{
				$db->rollback();
				throw new UserException('Erreur sur la ligne ' . $line . ' : le nombre de colonnes est incorrect.');
			}

			$data = [];

			foreach ($columns as $name=>$id)
			{
				$name = trim($name);

				// Champs qui n'existent pas dans le schéma actuel
				if (!in_array($name, $champs))
					continue;

				// Ignorer les champs vides
				if (trim($row[$id]) === '') {
					continue;
				}

				$data[$name] = $row[$id];

				// Restitution de la valeur binaire des champs à choix multiple
				if (isset($champs_multiples[$name])) {
					$values = explode(';', $data[$name]);
					$data[$name] = 0;

					foreach ($values as $v) {
						$v = trim($v);
						$found = array_search($v, $champs_multiples[$name]->options);

						if ($found !== false) {
							$data[$name] |= 0x01 << $found;
						}
					}
				}
			}

			if (!empty($data['numero']) && $data['numero'] > 0)
			{
				$numero = (int)$data['numero'];
			}
			else
			{
				unset($data['numero']);
				$numero = false;
			}

			try {
				if ($numero && ($id = $membres->getIDWithNumero($numero)))
				{
					if ($id === $current_user_id)
					{
						// Ne pas modifier le membre courant, on risque de se tirer une balle dans le pied
						continue;
					}

					$membres->edit($id, $data);
				}
				else
				{
					$membres->add($data, false);
				}
			}
			catch (UserException $e)
			{
				$db->rollback();
				throw new UserException('Erreur sur la ligne ' . $line . ' : ' . $e->getMessage());
			}
		}

		$db->commit();

		fclose($fp);
		return true;
	}

	protected function export(array $list = null)
	{
		$db = DB::getInstance();

		$champs = Config::getInstance()->get('champs_membres')->getKeys();
		$champs = array_map([$db, 'quoteIdentifier'], $champs);
		$fields = 'm.' . implode(', m.', $champs);

		if ($list) {
			$list = array_map('intval', $list);
			$where = sprintf('WHERE m.%s', $db->where('id', $list));
		}
		else {
			$where = '';
		}

		$sql = sprintf('SELECT %s, c.name AS "Catégorie membre" FROM membres AS m
			INNER JOIN users_categories AS c ON m.id_category = c.id
			%s ORDER BY c.id;', $fields, $where);

		$res = $db->iterate($sql);

		return [
			array_keys((array) $res->current()),
			$res,
			sprintf('Export membres - %s - %s', Config::getInstance()->get('nom_asso'), date('Y-m-d')),
		];
	}

	public function toCSV(array $list = null): void
	{
		list($champs, $result, $name) = $this->export($list);
		CSV::toCSV($name, $result, $champs, [$this, 'exportRow']);
	}

	public function toODS(array $list = null): void
	{
		list($champs, $result, $name) = $this->export($list);
		CSV::toODS($name, $result, $champs, [$this, 'exportRow']);
	}

	public function exportRow(\stdClass $row) {
		if (null === $this->champs) {
			$this->champs = Config::getInstance()->get('champs_membres')->getAll();
		}

		foreach ($this->champs as $id => $config) {
			if ($config->type == 'date') {
				$row->$id = \DateTime::createFromFormat('!Y-m-d', $row->$id);
			}
			elseif ($config->type == 'datetime') {
				$row->$id = \DateTime::createFromFormat('!Y-m-d H:i:s', $row->$id);
			}
			// convertir les champs à choix multiple de binaire vers liste séparée par des points virgules
			elseif ($config->type == 'multiple') {
				$out = [];

				foreach ($config->options as $b => $name)
				{
					if ($row->$id & (0x01 << $b)) {
						$out[] = $name;
					}
				}

				$row->$id = implode(';', $out);
			}
		}

		return $row;
	}
}