diff --git a/galette/lib/Galette/Entity/GaletteEntity.php b/galette/lib/Galette/Entity/GaletteEntity.php new file mode 100644 index 0000000000..252d5b29bb --- /dev/null +++ b/galette/lib/Galette/Entity/GaletteEntity.php @@ -0,0 +1,177 @@ +. + * + * @category Entity + * @package Galette + * + * @author Johan Cwiklinski + * @copyright 2020 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.9.5dev - 2020-12-12 + */ + +namespace Galette\Entity; + + +/** + * Entity abstract class for Galette + * + * @category Entity + * @name GaletteEntity + * @package Galette + * @author Johan Cwiklinski + * @copyright 2020 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.eu + * @since Available since 0.9.5dev - 2020-12-12 + */ +abstract class GaletteEntity +{ + /** + * Entities are often based on database tables. + * In that case, following constants must be daclared: + * + * public const TABLE = 'adherents'; + * public const PK = 'id_adh'; + */ + + /** + * @Inject("zdb") + */ + protected $zdb; + + /** + * @Inject("preferences") + */ + protected $preferences; + + /** + * @Inject("history") + */ + protected $history; + + private $deps = array( + /*'picture' => true, + 'groups' => true, + 'dues' => true, + 'parent' => false, + 'children' => false, + 'dynamics' => false*/ + ); + + private $errors = []; + + /** FIELDS */ + protected $creation_date; + protected $modification_date; + /** END FIELDS */ + + /** + * Loads a member from its id + * + * @param integer $id ID (primary key) of the object to load + * + * @return boolean + */ + abstract public function load(int $id): bool; + + /** + * Populate object from a resultset row + * + * @param ResultSet $r the resultset row + * + * @return void + */ + abstract protected function loadFromRS($r): void; + + /** + * Get field label + * + * @param string $field Field name + * + * @return string + */ + protected function getFieldLabel($field) + { + $label = $this->fields[$field]['label']; + //remove trailing ':' and then nbsp (for french at least) + $label = trim(trim($label, ':'), ' '); + return $label; + } + + /** + * Retrieve fields from database + * + * @param Db $zdb Database instance + * + * @return array + */ + public static function getDbFields(Db $zdb) + { + $columns = $zdb->getColumns(self::TABLE); + $fields = array(); + foreach ($columns as $col) { + $fields[] = $col->getName(); + } + return $fields; + } + + /** + * Update modification date + * + * @return void + */ + /*protected function updateModificationDate() + { + try { + $modif_date = date('Y-m-d'); + $update = $this->zdb->update(self::TABLE); + $update->set( + array('date_modif_adh' => $modif_date) + )->where(self::PK . '=' . $this->_id); + + $edit = $this->zdb->execute($update); + $this->_modification_date = $modif_date; + } catch (Throwable $e) { + Analog::log( + 'Something went wrong updating modif date :\'( | ' . + $e->getMessage() . "\n" . $e->getTraceAsString(), + Analog::ERROR + ); + } + }*/ + + /** + * Get current errors + * + * @return array + */ + public function getErrors(): array + { + return $this->errors; + } +} diff --git a/galette/lib/Galette/UI/AbstractList.php b/galette/lib/Galette/UI/AbstractList.php new file mode 100644 index 0000000000..a0c0689523 --- /dev/null +++ b/galette/lib/Galette/UI/AbstractList.php @@ -0,0 +1,375 @@ +. + * + * @category UI + * @package Galette + * + * @author Johan Cwiklinski + * @copyright 2020 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 2020-12-10 + */ + +namespace Galette\UI; + +/** + * Abstract Galette list for display + * + * @name Lists + * @category UI + * @package Galette + * + * @author Johan Cwiklinski + * @copyright 2020 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 + */ + +abstract class AbstractList +{ + /** @var boolean */ + private $has_pagination = false; + + /** @var boolean */ + private $has_search = false; + + /** @var array */ + private $search_parameters = []; + + /** @var boolean */ + private $has_massive = false; + + /** @var boolean */ + private $has_legend = false; + + /** @var boolean */ + private $has_footer = false; + + /** @var array */ + private $known_actions = [ + 'edit', + 'remove', + 'translate', + 'print', + ]; + + /** @var array */ + private $default_actions = [ + 'edit', + 'remove', + 'translate' + ]; + + /** @var array */ + private $actions = []; + + /** @var array */ + private $headers = []; + + /** @var array */ + private $footers = []; + + /** + * Constructor + */ + public function __construct() + { + return $this->withPagination(); + } + + /** + * Enable simple search + * + * @return AbstractList + */ + protected function withSearch() + { + $this->enable('search'); + return $this; + } + + /** + * Enable massive actions + * + * @return AbstractList + */ + protected function withMassive() + { + $this->enable('massive'); + return $this; + } + + /** + * Enable legend + * + * @return AbstractList + */ + protected function withLegend() + { + $this->enable('legend'); + return $this; + } + + /** + * Enable footer + * + * @return AbstractList + */ + protected function withFooter() + { + $this->enable('search'); + return $this; + } + + /** + * Enable pagination + * + * @return AbstractList + */ + protected function withPagination() + { + $this->enable('pagination'); + return $this; + } + + /** + * Enable feature + * + * @param string $name Feature name + * + * @return void + */ + private function enable($name): void + { + $propname = 'has_' . $name; + if (property_exists($this, $propname)) { + $this->$propname = true; + } + throw new \RuntimeException( + sprintf( + 'Property %1$s does not exists.', + $name + ) + ); + } + + /** + * Does list supports pagination? + * + * @return boolean + */ + public function hasPagination(): bool + { + return $this->has_pagination; + } + + /** + * Does list supports simple search? + * + * @return boolean + */ + public function hasSearch(): bool + { + return $this->has_search; + } + + /** + * Does list supports massive actions? + * + * @return boolean + */ + public function hasMassiveActions(): bool + { + return $this->has_massive; + } + + /** + * Get simple search parameters + * + * @return array + */ + public function getSearchParameters(): array + { + $this->search_parameters = $this->registerSearchParameters(); + $this->search_parameters += $this->registerSearchParametersSecondLine(); + return $this->search_parameters; + } + + /** + * Register search parameters + * + * @return array + */ + abstract protected function registerSearchParameters(): array; + + /** + * Register second line search parameters + * + * @return array + */ + protected function registerSearchParametersSecondLine(): array + { + return []; + } + + /** + * Get lists actions + * + * @return array + */ + public function getActions(): array + { + $this->actions = $this->registerActions(); + if (!count($this->actions)) { + $this->actions = $this->default_actions; + } + return $this->actions; + } + + /** + * Register actions + * + * @return array + */ + abstract protected function registerActions(): array; + + /** + * Get list headers for display + * + * @return array + */ + public function getHeaders(): array + { + $this->headers = $this->generateHeaders(); + return $this->headers; + } + + /** + * Generate list headers + * + * @return array + */ + abstract protected function generateHeaders(): array; + + /** + * Get list rows for display + * + * @return array + */ + public function getRows(): array + { + $this->rows = $this->generateRows(); + return $this->rows; + } + + /** + * Generate list rows + * + * @return array + */ + abstract protected function generateRows(): array; + + /** + * Does list have footer? + * + * @return boolean + */ + public function hasFooter(): bool + { + return $this->has_footer; + } + + /** + * Get list footers for display + * + * @return array + */ + public function getFooters(): array + { + $this->footers = $this->generateFooters(); + return $this->footers; + } + + /** + * Generate list footers + * + * @return array + */ + protected function generateFooters(): array + { + //per default, many list does not have footer. + } + + /** + * Retrieve default actions + * + * @return array + */ + public function getDefaultActions(): array + { + return $this->default_actions(); + } + + /** + * Get filter path + * + * @return string + */ + public function getFilterPath() + { + //TODO: build, or make abstract + return 'filter-memberslist'; + } + + /** + * Get batch path + * + * @return string + */ + public function getBatchPath() + { + //TODO: build, or make abstract + return 'batch-memberslist'; + } + + /** + * Get count info line, localized + * + * @param interger $count Count + * + * @return string + */ + public function getCountInfo($count) + { + //TODO: make abstract + return str_replace( + '%count', + $count, + _Tx('%count member', '%count members', $count) + ); + } +} diff --git a/galette/lib/Galette/UI/ContributionList.php b/galette/lib/Galette/UI/ContributionList.php new file mode 100644 index 0000000000..4e5b0605fc --- /dev/null +++ b/galette/lib/Galette/UI/ContributionList.php @@ -0,0 +1,106 @@ +. + * + * @category UI + * @package Galette + * + * @author Johan Cwiklinski + * @copyright 2020 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 2020-12-10 + */ + +namespace Galette\UI; + +/** + * Galette contributions list + * + * @name Lists + * @category UI + * @package Galette + * + * @author Johan Cwiklinski + * @copyright 2020 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 + */ + +class COntributionList extends AbstractList +{ + /** + * Constructor + */ + public function __construct() + { + return $this + ->withSearch() + ->withPagination() + ->withLegend() + ->withFooter() + ; + } + + /** + * Register search parameters + * + * @return array + */ + protected function registerSearchParameters(): array + { + //TODO + } + + /** + * Register actions + * + * @return array + */ + protected function registerActions(): array + { + return $this->getDefaultActions() + ['contrib_pdf']; + } + + /** + * Generate list headers + * + * @return array + */ + protected function generateHeaders(): array + { + //TODO + } + + /** + * Generate list rows + * + * @return array + */ + protected function generateRows(): array + { + //TODO + } +} diff --git a/galette/lib/Galette/UI/MemberList.php b/galette/lib/Galette/UI/MemberList.php new file mode 100644 index 0000000000..1233669bc9 --- /dev/null +++ b/galette/lib/Galette/UI/MemberList.php @@ -0,0 +1,127 @@ +. + * + * @category UI + * @package Galette + * + * @author Johan Cwiklinski + * @copyright 2020 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 2020-12-10 + */ + +namespace Galette\UI; + +/** + * Galette members list + * + * @name Lists + * @category UI + * @package Galette + * + * @author Johan Cwiklinski + * @copyright 2020 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 + */ + +class MemberList extends AbstractList +{ + /** @var array */ + private $search_parameters = []; + + /** @var array */ + private $actions = [ + 'edit', + 'contributions', + 'remove', + 'impersonate' + ]; + + /** + * Constructor + */ + public function __construct() + { + return $this + ->withSearch() + ->withMassive() + ->withPagination() + ->withLegend() + ; + } + + /** + * Register search parameters + * + * @return array + */ + protected function registerSearchParameters(): array + { + //TODO + } + + /** + * Register second line search parameters + * + * @return array + */ + protected function registerSearchParametersSecondLine(): array + { + //TODO + } + + /** + * Register actions + * + * @return array + */ + protected function registerActions(): array + { + //TODO + } + + /** + * Generate list headers + * + * @return array + */ + protected function generateHeaders(): array + { + //TODO + } + + /** + * Generate list rows + * + * @return array + */ + protected function generateRows(): array + { + //TODO + } +} diff --git a/galette/templates/default/galette_list.tpl b/galette/templates/default/galette_list.tpl new file mode 100644 index 0000000000..ad456bf9cd --- /dev/null +++ b/galette/templates/default/galette_list.tpl @@ -0,0 +1,746 @@ +{* Galette lists template *} +{extends file="page.tpl"} + +{function name=draw_actions} +{* + {foreach from=$list_actions item=list_action} + + $member->id]}" + class="tooltip action" + > + + {_T string="%membername: edit information" pattern="/%membername/" replace=$member->sname} + +{if $login->isAdmin() or $login->isStaff()} + "contributions", "option" => "member", "value" => $member->id]}" + class="tooltip" + > + + {_T string="%membername: contributions" pattern="/%membername/" replace=$member->sname} + + $member->id]}" + class="delete tooltip" + > + + {_T string="%membername: remove from database" pattern="/%membername/" replace=$member->sname} + +{/if} +{if $login->isSuperAdmin()} + $member->id]}" + class="tooltip" + > + + {_T string="Log in in as %membername" pattern="/%membername/" replace=$member->sname} + +{/if} +*} +{* If some additionnals actions should be added from plugins, we load the relevant template file +We have to use a template file, so Smarty will do its work (like replacing variables). *} +{* +{if $plugin_actions|@count != 0} + {foreach from=$plugin_actions key=plugin_name item=action} + {include file=$action module_id=$plugin_name|replace:'actions_':''} + {/foreach} +{/if} +*} + +{/function} + +{block name="content"} + {if $list->hasSearch()} +
getFilterPath()"}" method="post" id="filtre"> +
+{* +{if !isset($adv_filters) || !$adv_filters} + +   + {_T string="in:"}  + + {_T string="among:"}  + + + + + + +
+ {_T string="Members that have an email address:"} + email_filter eq constant('Galette\Repository\Members::FILTER_DC_EMAIL')} checked="checked"{/if}> + + email_filter eq constant('Galette\Repository\Members::FILTER_W_EMAIL')} checked="checked"{/if}> + + email_filter eq constant('Galette\Repository\Members::FILTER_WO_EMAIL')} checked="checked"{/if}> + +
+{else} +

+ {_T string="Advanced search mode"} + + + + +
+ {_T string="Show/hide query"} +

+ +{/if} +*} +
+
+ {$list->getCountInfo($nb_members)} +
+ + + +
+
+
+ +{if $list->hasMassiveActions()} +
+{/if} + + + + +{foreach item=column from=$galette_list} + {if $column->field_id eq 'id_adh'} + {if $preferences->pref_show_id} + + {else} + + {/if} + {else} + + {/if} +{/foreach} + + + + +{foreach from=$members item=member key=ordre} + {assign var=rclass value=$member->getRowClass() } + + {foreach item=column from=$galette_list} + {if $column->field_id eq 'id_adh'} + + {elseif $column->field_id eq 'list_adh_name'} + + {else} + {assign var="lrclass" value=$rclass} + {assign var="propname" value=$column->propname} + {assign var=value value=$member->$propname} + + {if $column->field_id eq 'pseudo_adh'} + {assign var="lrclass" value="$rclass nowrap"} + {assign var=value value=$member->$propname|htmlspecialchars} + {elseif $column->field_id eq 'tel_adh' or $column->field_id eq 'gsm_adh'} + {assign var="lrclass" value="$rclass nowrap"} + {elseif $column->field_id eq 'id_statut'} + {assign var="lrclass" value="$rclass nowrap"} + {assign var=value value={statusLabel id=$member->$propname}} + {elseif $column->field_id eq 'titre_adh'} + {if is_object($member->title)} + {assign var=value value=$member->title->long} + {/if} + {elseif $column->field_id eq 'pref_lang'} + {assign var="value" value=$i18n->getNameFromId($member->language)} + {elseif $column->field_id eq 'adresse_adh'} + {assign var="value" value=$member->saddress|escape|nl2br} + {elseif $column->field_id eq 'bool_display_info'} + {assign var="value" value=$member->sappears_in_list} + {elseif $column->field_id eq 'activite_adh'} + {assign var="value" value=$member->sactive} + {elseif $column->field_id eq 'id_statut'} + {assign var="value" value=$member->sstatus} + {elseif $column->field_id eq 'bool_admin_adh'} + {assign var="value" value=$member->sadmin} + {elseif $column->field_id eq 'bool_exempt_adh'} + {assign var="value" value=$member->sdue_free} + {elseif $column->field_id eq 'sexe_adh'} + {assign var="value" value=$member->sgender} + {/if} + + {/if} + {/foreach} + {draw_actions class=$rclass member=$member login=$login plugin_actions=$plugin_actions} + +{foreachelse} + +{/foreach} + +
+ "order", "value" => "Galette\Repository\Members::ORDERBY_ID"|constant]}"> + {_T string="Mbr num"} + {if $filters->orderby eq constant('galette\Repository\Members::ORDERBY_ID')} + {if $filters->ordered eq constant('Galette\Filters\MembersList::ORDER_ASC')} + + {else} + + {/if} + {/if} + + # + "order", "value" => $column->field_id]}"> + {$column->label} + {if $filters->orderby eq $column->field_id} + {if $filters->ordered eq constant('Galette\Filters\MembersList::ORDER_ASC')} + + {else} + + {/if} + {/if} + + {_T string="Actions"}
+ {if $preferences->pref_show_id} + {$member->id} + {else} + {$ordre+1+($filters->current_page - 1)*$numrows} + {/if} + + + {if $member->isCompany()} + + + {_T string="Is a company"} + + {elseif $member->isMan()} + + + {_T string="Is a man"} + + {elseif $member->isWoman()} + + + {_T string="Is a woman"} + + {else} + + {/if} + {if $member->email != ''} + + + {_T string="Mail"} + + {else} + + {/if} + {if $member->website != ''} + + + {_T string="Website"} + + {else} + + {/if} + {if $member->isAdmin()} + + + {_T string="Admin"} + + {elseif $member->isStaff()} + + + {_T string="Staff member"} + + {else} + + {/if} + {assign var="mid" value=$member->id} + $member->id]}">{$member->sname}{if $member->company_name} ({$member->company_name}){/if} + + {* Display column. + A check is done here to adapt display, this is may not the best way to go + but for notw, that works as excpected. + *} + {if not empty($value)} + {if $column->field_id eq 'email_adh' or $column->field_id eq 'msn_adh'} + {$value} + {elseif $column->field_id eq 'tel_adh' or $column->field_id eq 'gsm_adh'} + {$value} + {elseif $column->field_id eq 'url_adh'} + {$value} + {elseif $column->field_id eq 'parent_id'} + $member->parent]}">{memberName id=$member->parent} + {elseif $column->field_id eq 'ddn_adh'} + {$value} {$member->getAge()} + {else} + {$value} + {/if} + {/if} +
{_T string="No member has been found"}
+ +{if $list->hasMassiveActions()} +{* + {if $nb_members != 0} +
+ {_T string="Pages:"}
+
    {$pagination}
+
+
    +
  • {_T string="For the selection:"}
  • + {if $login->isAdmin() or $login->isStaff()} +
  • + +
  • +
  • + +
  • + {if $pref_mail_method neq constant('Galette\Core\GaletteMail::METHOD_DISABLED')} +
  • + +
  • + {/if} + {/if} +
  • + +
  • +
  • + +
  • +
  • + +
  • + {if $login->isAdmin() or $login->isStaff()} +
  • + +
  • + {/if} + {if $plugin_batch_actions|@count != 0} + {foreach from=$plugin_batch_actions key=plugin_name item=action} + {include file=$action module_id=$plugin_name|replace:'batch_action_':''} + {/foreach} + {/if} +
+ {/if} +*} +
+{/if} + +{* +{if $nb_members != 0} +
+

{_T string="Legend"}

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{_T string="Reading the list"}
{_T string="Name"}{_T string="Active account"}{_T string="Name"}{_T string="Inactive account"}
 {_T string="Membership in order"} {_T string="Membership will expire soon (<30d)"}
 {_T string="Never contributed"} {_T string="Lateness in fee"}
{_T string="Actions"}
+ + {_T string="Modification"} + + {_T string="Contributions"}
+ + {_T string="Deletion"}
{_T string="User status/interactions"}
{_T string={_T string="Send an email"}{_T string={_T string="Visit website"}
{_T string={_T string="Is a man"}{_T string={_T string="Is a woman"}
{_T string={_T string="Is a company"}
{_T string={_T string="Admin"}{_T string={_T string="Staff member"}
+
+{/if} +*} +{/block} + +{block name="javascripts"} +{* + +{/block} diff --git a/galette/templates/default/lists_types/column.tpl b/galette/templates/default/lists_types/column.tpl new file mode 100644 index 0000000000..788c6407c9 --- /dev/null +++ b/galette/templates/default/lists_types/column.tpl @@ -0,0 +1 @@ +{* TODO *} diff --git a/galette/templates/default/lists_types/list.tpl b/galette/templates/default/lists_types/list.tpl new file mode 100644 index 0000000000..788c6407c9 --- /dev/null +++ b/galette/templates/default/lists_types/list.tpl @@ -0,0 +1 @@ +{* TODO *} diff --git a/galette/templates/default/lists_types/row.tpl b/galette/templates/default/lists_types/row.tpl new file mode 100644 index 0000000000..788c6407c9 --- /dev/null +++ b/galette/templates/default/lists_types/row.tpl @@ -0,0 +1 @@ +{* TODO *} diff --git a/galette/templates/default/lists_types/search.tpl b/galette/templates/default/lists_types/search.tpl new file mode 100644 index 0000000000..788c6407c9 --- /dev/null +++ b/galette/templates/default/lists_types/search.tpl @@ -0,0 +1 @@ +{* TODO *}