added extension
This commit is contained in:
commit
e4feb3a9e9
46
CRM/ConstituentsOnly/BAO/Query.php
Normal file
46
CRM/ConstituentsOnly/BAO/Query.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class CRM_ConstituentsOnly_BAO_Query extends CRM_Contact_BAO_Query_Interface {
|
||||||
|
|
||||||
|
static $_networkFields = array();
|
||||||
|
|
||||||
|
public function &getFields() {
|
||||||
|
return self::$_networkFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function select(&$query) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build where clause for ConstituentsOnly
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
public function where(&$query) {
|
||||||
|
$grouping = 0;
|
||||||
|
$statement = "NOT (contact_a.do_not_trade <=> 1)";
|
||||||
|
$query->_where[$grouping][] = $statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function from($name, $mode, $side) {
|
||||||
|
$from = NULL;
|
||||||
|
return $from;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTableDependency(&$tables) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPanesMapper(&$panes) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerAdvancedSearchPane(&$panes) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildAdvancedSearchPaneForm(&$form, $type) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAdvancedSearchPaneTemplatePath(&$paneTemplatePathArray, $type) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
61
CRM/Utils/API/ConstituentsOnlyAPIWrapper.php
Executable file
61
CRM/Utils/API/ConstituentsOnlyAPIWrapper.php
Executable file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once 'api/Wrapper.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CRM_Utils_API_ConstituentsOnlyAPIWrapper
|
||||||
|
*/
|
||||||
|
class CRM_Utils_API_ConstituentsOnlyAPIWrapper implements API_Wrapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CRM_Utils_API_ReloadOption
|
||||||
|
*/
|
||||||
|
private static $_singleton = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CRM_Utils_API_ReloadOption
|
||||||
|
*/
|
||||||
|
public static function singleton() {
|
||||||
|
if (self::$_singleton === NULL) {
|
||||||
|
self::$_singleton = new CRM_Utils_API_ConstituentsOnlyAPIWrapper();
|
||||||
|
}
|
||||||
|
return self::$_singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function fromApiInput($apiRequest) {
|
||||||
|
return $apiRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function toApiOutput($apiRequest, $result) {
|
||||||
|
$search_term = @$apiRequest['params']['name'];
|
||||||
|
|
||||||
|
if (strlen($search_term) > 0) {
|
||||||
|
foreach ($result['values'] as $key => $api_result) {
|
||||||
|
try {
|
||||||
|
$additional_result = civicrm_api3('Contact', 'get', array(
|
||||||
|
'debug' => 1,
|
||||||
|
'sequential' => 1,
|
||||||
|
'contact_id' => array('=' => $api_result['id']),
|
||||||
|
));
|
||||||
|
} catch (CiviCRM_API3_Exception $e) {
|
||||||
|
$error = $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If contact is not a constituent, they are already filtered.
|
||||||
|
// Remove that contact from the Quick Search results.
|
||||||
|
if ($additional_result['count'] == 0) {
|
||||||
|
unset($result['values'][$key]);
|
||||||
|
// Decrement count
|
||||||
|
$result['count'] -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
165
LICENSE.txt
Normal file
165
LICENSE.txt
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
23
README.md
Normal file
23
README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Constituents Only Filter
|
||||||
|
|
||||||
|
CiviCRM Extension to allow only AGBU constituents to appear in searches and reports.
|
||||||
|
AGBU wants only records that are flagged as a "constituent" to be included in searches and reports.
|
||||||
|
For instance, if John Doe is a donor and Jane Doe is his wife, you can see Jane's info from John's relationships tab,
|
||||||
|
but she won't show up in search.
|
||||||
|
|
||||||
|
This extension that allows us to exclude users from search but still allow them to appear in relationships.
|
||||||
|
Here we repurposed the "do_not_trade" field in the `civicrm_contact` table in the database.
|
||||||
|
Only contacts with this field/flag set is considered constituent and will be returned in the searches and reports.
|
||||||
|
|
||||||
|
|
||||||
|
Install as any other CiviCRM Extension.
|
||||||
|
|
||||||
|
Release Notes:
|
||||||
|
* This CiviCRM extension is custom developed for AGBU.org
|
||||||
|
* This is the alpha version, more changes to come as it is tested in real environment.
|
||||||
|
* This extension id designed to work on CiviCRM 4.7+.
|
||||||
|
|
||||||
|
Author: Joozer Tohfafarosh, DPCI, jtohfafarosh at dpci dot com.
|
||||||
|
|
||||||
|
Mentor: Jon Goldberg, Megaphone Technology Consulting.
|
||||||
|
|
438
constituentsonly.civix.php
Normal file
438
constituentsonly.civix.php
Normal file
@ -0,0 +1,438 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ExtensionUtil class provides small stubs for accessing resources of this
|
||||||
|
* extension.
|
||||||
|
*/
|
||||||
|
class CRM_ConstituentsOnly_ExtensionUtil {
|
||||||
|
const SHORT_NAME = "constituentsonly";
|
||||||
|
const LONG_NAME = "com.dpci.constituentsonly";
|
||||||
|
const CLASS_PREFIX = "CRM_ConstituentsOnly";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate a string using the extension's domain.
|
||||||
|
*
|
||||||
|
* If the extension doesn't have a specific translation
|
||||||
|
* for the string, fallback to the default translations.
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* Canonical message text (generally en_US).
|
||||||
|
* @param array $params
|
||||||
|
* @return string
|
||||||
|
* Translated text.
|
||||||
|
* @see ts
|
||||||
|
*/
|
||||||
|
public static function ts($text, $params = array()) {
|
||||||
|
if (!array_key_exists('domain', $params)) {
|
||||||
|
$params['domain'] = array(self::LONG_NAME, NULL);
|
||||||
|
}
|
||||||
|
return ts($text, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the URL of a resource file (in this extension).
|
||||||
|
*
|
||||||
|
* @param string|NULL $file
|
||||||
|
* Ex: 'css/foo.css'.
|
||||||
|
* @return string
|
||||||
|
* Ex: 'http://example.org/sites/default/ext/org.example.foo/css/foo.css'.
|
||||||
|
*/
|
||||||
|
public static function url($file = NULL) {
|
||||||
|
return CRM_Core_Resources::singleton()->getUrl(self::LONG_NAME, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the path of a resource file (in this extension).
|
||||||
|
*
|
||||||
|
* @param string|NULL $file
|
||||||
|
* Ex: 'css/foo.css'.
|
||||||
|
* @return string
|
||||||
|
* Ex: '/var/www/example.org/sites/default/ext/org.example.foo/css/foo.css'.
|
||||||
|
*/
|
||||||
|
public static function path($file = NULL) {
|
||||||
|
// return CRM_Core_Resources::singleton()->getPath(self::LONG_NAME, $file);
|
||||||
|
return __DIR__ . ($path === NULL ? '' : (DIRECTORY_SEPARATOR . $file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of a class within this extension.
|
||||||
|
*
|
||||||
|
* @param string $suffix
|
||||||
|
* Ex: 'Page_HelloWorld' or 'Page\\HelloWorld'.
|
||||||
|
* @return string
|
||||||
|
* Ex: 'CRM_Foo_Page_HelloWorld'.
|
||||||
|
*/
|
||||||
|
public static function findClass($suffix) {
|
||||||
|
return self::CLASS_PREFIX . '_' . str_replace('\\', '_', $suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
use CRM_ConstituentsOnly_ExtensionUtil as E;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Delegated) Implements hook_civicrm_config().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_config
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_config(&$config = NULL) {
|
||||||
|
static $configured = FALSE;
|
||||||
|
if ($configured) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$configured = TRUE;
|
||||||
|
|
||||||
|
$template =& CRM_Core_Smarty::singleton();
|
||||||
|
|
||||||
|
$extRoot = dirname(__FILE__) . DIRECTORY_SEPARATOR;
|
||||||
|
$extDir = $extRoot . 'templates';
|
||||||
|
|
||||||
|
if (is_array($template->template_dir)) {
|
||||||
|
array_unshift($template->template_dir, $extDir);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$template->template_dir = array($extDir, $template->template_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
$include_path = $extRoot . PATH_SEPARATOR . get_include_path();
|
||||||
|
set_include_path($include_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Delegated) Implements hook_civicrm_xmlMenu().
|
||||||
|
*
|
||||||
|
* @param $files array(string)
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_xmlMenu
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_xmlMenu(&$files) {
|
||||||
|
foreach (_constituentsonly_civix_glob(__DIR__ . '/xml/Menu/*.xml') as $file) {
|
||||||
|
$files[] = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_install().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_install() {
|
||||||
|
_constituentsonly_civix_civicrm_config();
|
||||||
|
if ($upgrader = _constituentsonly_civix_upgrader()) {
|
||||||
|
$upgrader->onInstall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_postInstall().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_postInstall
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_postInstall() {
|
||||||
|
_constituentsonly_civix_civicrm_config();
|
||||||
|
if ($upgrader = _constituentsonly_civix_upgrader()) {
|
||||||
|
if (is_callable(array($upgrader, 'onPostInstall'))) {
|
||||||
|
$upgrader->onPostInstall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_uninstall().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_uninstall() {
|
||||||
|
_constituentsonly_civix_civicrm_config();
|
||||||
|
if ($upgrader = _constituentsonly_civix_upgrader()) {
|
||||||
|
$upgrader->onUninstall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Delegated) Implements hook_civicrm_enable().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_enable
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_enable() {
|
||||||
|
_constituentsonly_civix_civicrm_config();
|
||||||
|
if ($upgrader = _constituentsonly_civix_upgrader()) {
|
||||||
|
if (is_callable(array($upgrader, 'onEnable'))) {
|
||||||
|
$upgrader->onEnable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Delegated) Implements hook_civicrm_disable().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_disable() {
|
||||||
|
_constituentsonly_civix_civicrm_config();
|
||||||
|
if ($upgrader = _constituentsonly_civix_upgrader()) {
|
||||||
|
if (is_callable(array($upgrader, 'onDisable'))) {
|
||||||
|
$upgrader->onDisable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Delegated) Implements hook_civicrm_upgrade().
|
||||||
|
*
|
||||||
|
* @param $op string, the type of operation being performed; 'check' or 'enqueue'
|
||||||
|
* @param $queue CRM_Queue_Queue, (for 'enqueue') the modifiable list of pending up upgrade tasks
|
||||||
|
*
|
||||||
|
* @return mixed based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending)
|
||||||
|
* for 'enqueue', returns void
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_upgrade
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
|
||||||
|
if ($upgrader = _constituentsonly_civix_upgrader()) {
|
||||||
|
return $upgrader->onUpgrade($op, $queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CRM_ConstituentsOnly_Upgrader
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_upgrader() {
|
||||||
|
if (!file_exists(__DIR__ . '/CRM/Excludefromsearch/Upgrader.php')) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return CRM_ConstituentsOnly_Upgrader_Base::instance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search directory tree for files which match a glob pattern
|
||||||
|
*
|
||||||
|
* Note: Dot-directories (like "..", ".git", or ".svn") will be ignored.
|
||||||
|
* Note: In Civi 4.3+, delegate to CRM_Utils_File::findFiles()
|
||||||
|
*
|
||||||
|
* @param $dir string, base dir
|
||||||
|
* @param $pattern string, glob pattern, eg "*.txt"
|
||||||
|
* @return array(string)
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_find_files($dir, $pattern) {
|
||||||
|
if (is_callable(array('CRM_Utils_File', 'findFiles'))) {
|
||||||
|
return CRM_Utils_File::findFiles($dir, $pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
$todos = array($dir);
|
||||||
|
$result = array();
|
||||||
|
while (!empty($todos)) {
|
||||||
|
$subdir = array_shift($todos);
|
||||||
|
foreach (_constituentsonly_civix_glob("$subdir/$pattern") as $match) {
|
||||||
|
if (!is_dir($match)) {
|
||||||
|
$result[] = $match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($dh = opendir($subdir)) {
|
||||||
|
while (FALSE !== ($entry = readdir($dh))) {
|
||||||
|
$path = $subdir . DIRECTORY_SEPARATOR . $entry;
|
||||||
|
if ($entry{0} == '.') {
|
||||||
|
}
|
||||||
|
elseif (is_dir($path)) {
|
||||||
|
$todos[] = $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir($dh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* (Delegated) Implements hook_civicrm_managed().
|
||||||
|
*
|
||||||
|
* Find any *.mgd.php files, merge their content, and return.
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_managed
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_managed(&$entities) {
|
||||||
|
$mgdFiles = _constituentsonly_civix_find_files(__DIR__, '*.mgd.php');
|
||||||
|
foreach ($mgdFiles as $file) {
|
||||||
|
$es = include $file;
|
||||||
|
foreach ($es as $e) {
|
||||||
|
if (empty($e['module'])) {
|
||||||
|
$e['module'] = E::LONG_NAME;
|
||||||
|
}
|
||||||
|
$entities[] = $e;
|
||||||
|
if (empty($e['params']['version'])) {
|
||||||
|
$e['params']['version'] = '3';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Delegated) Implements hook_civicrm_caseTypes().
|
||||||
|
*
|
||||||
|
* Find any and return any files matching "xml/case/*.xml"
|
||||||
|
*
|
||||||
|
* Note: This hook only runs in CiviCRM 4.4+.
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_caseTypes
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_caseTypes(&$caseTypes) {
|
||||||
|
if (!is_dir(__DIR__ . '/xml/case')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (_constituentsonly_civix_glob(__DIR__ . '/xml/case/*.xml') as $file) {
|
||||||
|
$name = preg_replace('/\.xml$/', '', basename($file));
|
||||||
|
if ($name != CRM_Case_XMLProcessor::mungeCaseType($name)) {
|
||||||
|
$errorMessage = sprintf("Case-type file name is malformed (%s vs %s)", $name, CRM_Case_XMLProcessor::mungeCaseType($name));
|
||||||
|
CRM_Core_Error::fatal($errorMessage);
|
||||||
|
// throw new CRM_Core_Exception($errorMessage);
|
||||||
|
}
|
||||||
|
$caseTypes[$name] = array(
|
||||||
|
'module' => E::LONG_NAME,
|
||||||
|
'name' => $name,
|
||||||
|
'file' => $file,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Delegated) Implements hook_civicrm_angularModules().
|
||||||
|
*
|
||||||
|
* Find any and return any files matching "ang/*.ang.php"
|
||||||
|
*
|
||||||
|
* Note: This hook only runs in CiviCRM 4.5+.
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_angularModules
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_angularModules(&$angularModules) {
|
||||||
|
if (!is_dir(__DIR__ . '/ang')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = _constituentsonly_civix_glob(__DIR__ . '/ang/*.ang.php');
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$name = preg_replace(':\.ang\.php$:', '', basename($file));
|
||||||
|
$module = include $file;
|
||||||
|
if (empty($module['ext'])) {
|
||||||
|
$module['ext'] = E::LONG_NAME;
|
||||||
|
}
|
||||||
|
$angularModules[$name] = $module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Glob wrapper which is guaranteed to return an array.
|
||||||
|
*
|
||||||
|
* The documentation for glob() says, "On some systems it is impossible to
|
||||||
|
* distinguish between empty match and an error." Anecdotally, the return
|
||||||
|
* result for an empty match is sometimes array() and sometimes FALSE.
|
||||||
|
* This wrapper provides consistency.
|
||||||
|
*
|
||||||
|
* @link http://php.net/glob
|
||||||
|
* @param string $pattern
|
||||||
|
* @return array, possibly empty
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_glob($pattern) {
|
||||||
|
$result = glob($pattern);
|
||||||
|
return is_array($result) ? $result : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a navigation menu item at a given place in the hierarchy.
|
||||||
|
*
|
||||||
|
* @param array $menu - menu hierarchy
|
||||||
|
* @param string $path - path where insertion should happen (ie. Administer/System Settings)
|
||||||
|
* @param array $item - menu you need to insert (parent/child attributes will be filled for you)
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_insert_navigation_menu(&$menu, $path, $item) {
|
||||||
|
// If we are done going down the path, insert menu
|
||||||
|
if (empty($path)) {
|
||||||
|
$menu[] = array(
|
||||||
|
'attributes' => array_merge(array(
|
||||||
|
'label' => CRM_Utils_Array::value('name', $item),
|
||||||
|
'active' => 1,
|
||||||
|
), $item),
|
||||||
|
);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Find an recurse into the next level down
|
||||||
|
$found = FALSE;
|
||||||
|
$path = explode('/', $path);
|
||||||
|
$first = array_shift($path);
|
||||||
|
foreach ($menu as $key => &$entry) {
|
||||||
|
if ($entry['attributes']['name'] == $first) {
|
||||||
|
if (!isset($entry['child'])) {
|
||||||
|
$entry['child'] = array();
|
||||||
|
}
|
||||||
|
$found = _constituentsonly_civix_insert_navigation_menu($entry['child'], implode('/', $path), $item, $key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Delegated) Implements hook_civicrm_navigationMenu().
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_navigationMenu(&$nodes) {
|
||||||
|
if (!is_callable(array('CRM_Core_BAO_Navigation', 'fixNavigationMenu'))) {
|
||||||
|
_constituentsonly_civix_fixNavigationMenu($nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a navigation menu, generate navIDs for any items which are
|
||||||
|
* missing them.
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_fixNavigationMenu(&$nodes) {
|
||||||
|
$maxNavID = 1;
|
||||||
|
array_walk_recursive($nodes, function($item, $key) use (&$maxNavID) {
|
||||||
|
if ($key === 'navID') {
|
||||||
|
$maxNavID = max($maxNavID, $item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_constituentsonly_civix_fixNavigationMenuItems($nodes, $maxNavID, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _constituentsonly_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID) {
|
||||||
|
$origKeys = array_keys($nodes);
|
||||||
|
foreach ($origKeys as $origKey) {
|
||||||
|
if (!isset($nodes[$origKey]['attributes']['parentID']) && $parentID !== NULL) {
|
||||||
|
$nodes[$origKey]['attributes']['parentID'] = $parentID;
|
||||||
|
}
|
||||||
|
// If no navID, then assign navID and fix key.
|
||||||
|
if (!isset($nodes[$origKey]['attributes']['navID'])) {
|
||||||
|
$newKey = ++$maxNavID;
|
||||||
|
$nodes[$origKey]['attributes']['navID'] = $newKey;
|
||||||
|
$nodes[$newKey] = $nodes[$origKey];
|
||||||
|
unset($nodes[$origKey]);
|
||||||
|
$origKey = $newKey;
|
||||||
|
}
|
||||||
|
if (isset($nodes[$origKey]['child']) && is_array($nodes[$origKey]['child'])) {
|
||||||
|
_constituentsonly_civix_fixNavigationMenuItems($nodes[$origKey]['child'], $maxNavID, $nodes[$origKey]['attributes']['navID']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Delegated) Implements hook_civicrm_alterSettingsFolders().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_alterSettingsFolders
|
||||||
|
*/
|
||||||
|
function _constituentsonly_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
|
||||||
|
static $configured = FALSE;
|
||||||
|
if ($configured) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$configured = TRUE;
|
||||||
|
|
||||||
|
$settingsDir = __DIR__ . DIRECTORY_SEPARATOR . 'settings';
|
||||||
|
if (is_dir($settingsDir) && !in_array($settingsDir, $metaDataFolders)) {
|
||||||
|
$metaDataFolders[] = $settingsDir;
|
||||||
|
}
|
||||||
|
}
|
203
constituentsonly.php
Normal file
203
constituentsonly.php
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once 'constituentsonly.civix.php';
|
||||||
|
use CRM_ConstituentsOnly_ExtensionUtil as E;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_queryObjects().
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_queryObjects(&$queryObjects, $type) {
|
||||||
|
if ($type == 'Contact') {
|
||||||
|
$queryObjects[] = new CRM_ConstituentsOnly_BAO_Query();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_apiWrappers().
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_apiWrappers(&$wrappers, $apiRequest) {
|
||||||
|
if ($apiRequest['entity'] == 'Contact' && $apiRequest['action'] == 'getquick') {
|
||||||
|
$wrappers[] = new CRM_Utils_API_ConstituentsOnlyAPIWrapper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_civicrm_alterReportVar().
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_alterReportVar($varType, &$var, &$object) {
|
||||||
|
$instanceValue = $object->getVar('_instanceValues');
|
||||||
|
if (!empty($instanceValue) &&
|
||||||
|
in_array(
|
||||||
|
$instanceValue['report_id'],
|
||||||
|
array(
|
||||||
|
'contact/summary',
|
||||||
|
'contact/detail',
|
||||||
|
'contact/currentEmployer',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
if ($varType == 'sql') {
|
||||||
|
$var->_columnHeaders['civicrm_contact_do_not_trade'] = array(
|
||||||
|
'type' => 1,
|
||||||
|
'title' => 'Constituent',
|
||||||
|
'no_display' => TRUE,
|
||||||
|
);
|
||||||
|
$var->_select .= ' , contact_civireport.do_not_trade as civicrm_contact_do_not_trade ';
|
||||||
|
|
||||||
|
$where = $var->getVar('_where');
|
||||||
|
$where .= ' AND contact_civireport.do_not_trade <> 1';
|
||||||
|
$var->setVar('_where', $where);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_config().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_config
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_config(&$config) {
|
||||||
|
_constituentsonly_civix_civicrm_config($config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_xmlMenu().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_xmlMenu
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_xmlMenu(&$files) {
|
||||||
|
_constituentsonly_civix_civicrm_xmlMenu($files);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_install().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_install() {
|
||||||
|
_constituentsonly_civix_civicrm_install();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_postInstall().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_postInstall
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_postInstall() {
|
||||||
|
_constituentsonly_civix_civicrm_postInstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_uninstall().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_uninstall() {
|
||||||
|
_constituentsonly_civix_civicrm_uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_enable().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_enable
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_enable() {
|
||||||
|
_constituentsonly_civix_civicrm_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_disable().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_disable() {
|
||||||
|
_constituentsonly_civix_civicrm_disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_upgrade().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_upgrade
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
|
||||||
|
return _constituentsonly_civix_civicrm_upgrade($op, $queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_managed().
|
||||||
|
*
|
||||||
|
* Generate a list of entities to create/deactivate/delete when this module
|
||||||
|
* is installed, disabled, uninstalled.
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_managed
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_managed(&$entities) {
|
||||||
|
_constituentsonly_civix_civicrm_managed($entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_caseTypes().
|
||||||
|
*
|
||||||
|
* Generate a list of case-types.
|
||||||
|
*
|
||||||
|
* Note: This hook only runs in CiviCRM 4.4+.
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_caseTypes
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_caseTypes(&$caseTypes) {
|
||||||
|
_constituentsonly_civix_civicrm_caseTypes($caseTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_angularModules().
|
||||||
|
*
|
||||||
|
* Generate a list of Angular modules.
|
||||||
|
*
|
||||||
|
* Note: This hook only runs in CiviCRM 4.5+. It may
|
||||||
|
* use features only available in v4.6+.
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_angularModules
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_angularModules(&$angularModules) {
|
||||||
|
_constituentsonly_civix_civicrm_angularModules($angularModules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_alterSettingsFolders().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_alterSettingsFolders
|
||||||
|
*/
|
||||||
|
function constituentsonly_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
|
||||||
|
_constituentsonly_civix_civicrm_alterSettingsFolders($metaDataFolders);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Functions below this ship commented out. Uncomment as required. ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_preProcess().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_preProcess
|
||||||
|
*
|
||||||
|
function constituentsonly_civicrm_preProcess($formName, &$form) {
|
||||||
|
|
||||||
|
} // */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_navigationMenu().
|
||||||
|
*
|
||||||
|
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_navigationMenu
|
||||||
|
*
|
||||||
|
function constituentsonly_civicrm_navigationMenu(&$menu) {
|
||||||
|
_constituentsonly_civix_insert_navigation_menu($menu, NULL, array(
|
||||||
|
'label' => E::ts('The Page'),
|
||||||
|
'name' => 'the_page',
|
||||||
|
'url' => 'civicrm/the-page',
|
||||||
|
'permission' => 'access CiviReport,access CiviContribute',
|
||||||
|
'operator' => 'OR',
|
||||||
|
'separator' => 0,
|
||||||
|
));
|
||||||
|
_constituentsonly_civix_navigationMenu($menu);
|
||||||
|
} // */
|
33
info.xml
Normal file
33
info.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<extension key="com.dpci.constituentsonly" type="module">
|
||||||
|
<file>constituentsonly</file>
|
||||||
|
<name>AGBU Constituents Only</name>
|
||||||
|
<description>Only AGBU constituents should appear in searches and reports.</description>
|
||||||
|
<license>GPLv3</license>
|
||||||
|
<maintainer>
|
||||||
|
<author>Jon Goldberg</author>
|
||||||
|
<email>jon@megaphonetech.com</email>
|
||||||
|
</maintainer>
|
||||||
|
<urls>
|
||||||
|
<url desc="Main Extension Page">http://FIXME</url>
|
||||||
|
<url desc="Documentation">http://FIXME</url>
|
||||||
|
<url desc="Support">http://FIXME</url>
|
||||||
|
<url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
|
||||||
|
</urls>
|
||||||
|
<releaseDate>2018-07-01</releaseDate>
|
||||||
|
<version>1.0</version>
|
||||||
|
<develStage>stabke</develStage>
|
||||||
|
<compatibility>
|
||||||
|
<ver>4.7</ver>
|
||||||
|
<ver>5.0</ver>
|
||||||
|
<ver>5.1</ver>
|
||||||
|
<ver>5.2</ver>
|
||||||
|
<ver>5.2</ver>
|
||||||
|
<ver>5.3</ver>
|
||||||
|
<ver>5.4</ver>
|
||||||
|
</compatibility>
|
||||||
|
<comments>This is a custom extension for AGBU to only display constituents in searches and reports.</comments>
|
||||||
|
<civix>
|
||||||
|
<namespace>CRM/ConstituentsOnly</namespace>
|
||||||
|
</civix>
|
||||||
|
</extension>
|
Loading…
Reference in New Issue
Block a user