dbcc5e9caf01268e28dc77b6e38b980b31863dbb
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright Tech To The People http:tttp.eu (c) 2008 |
7 +--------------------------------------------------------------------+
9 | CiviCRM is free software; you can copy, modify, and distribute it |
10 | under the terms of the GNU Affero General Public License |
11 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | CiviCRM is distributed in the hope that it will be useful, but |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
16 | See the GNU Affero General Public License for more details. |
18 | You should have received a copy of the GNU Affero General Public |
19 | License and the CiviCRM Licensing Exception along |
20 | with this program; if not, contact CiviCRM LLC |
21 | at info[AT]civicrm[DOT]org. If you have questions about the |
22 | GNU Affero General Public License or the licensing of CiviCRM, |
23 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
24 +--------------------------------------------------------------------+
28 * This files provides several classes for doing command line work with
29 * CiviCRM. civicrm_cli is the base class. It's used by cli.php.
31 * In addition, there are several additional classes that inherit
32 * civicrm_cli to do more precise functions.
37 * base class for doing all command line operations via civicrm
41 // required values that must be passed
42 // via the command line
43 var $_required_arguments = array('action', 'entity');
44 var $_additional_arguments = array();
49 var $_semicolon = FALSE;
53 var $_site = 'localhost';
55 var $_password = NULL;
57 // all other arguments populate the parameters
58 // array that is passed to civicrm_api
59 var $_params = array('version' => 3);
61 var $_errors = array();
66 public function initialize() {
67 if (!$this->_accessing_from_cli()) {
70 if (!$this->_parseOptions()) {
73 if (!$this->_bootstrap()) {
76 if (!$this->_validateOptions()) {
83 * Ensure function is being run from the cli.
87 public function _accessing_from_cli() {
88 if (PHP_SAPI
=== 'cli') {
92 die("cli.php can only be run from command line.");
99 public function callApi() {
100 require_once 'api/api.php';
102 // CRM-9822 -'execute' action always goes thru Job api and always writes to log
103 if ($this->_action
!= 'execute' && $this->_joblog
) {
104 require_once 'CRM/Core/JobManager.php';
105 $facility = new CRM_Core_JobManager();
106 $facility->setSingleRunParams($this->_entity
, $this->_action
, $this->_params
, 'From Cli.php');
107 $facility->executeJobByAction($this->_entity
, $this->_action
);
110 // CRM-9822 cli.php calls don't require site-key, so bypass site-key authentication
111 $this->_params
['auth'] = FALSE;
112 $result = civicrm_api($this->_entity
, $this->_action
, $this->_params
);
115 if (!empty($result['is_error'])) {
116 $this->_log($result['error_message']);
119 elseif ($this->_output
=== 'json') {
120 echo json_encode($result, defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT
: 0);
122 elseif ($this->_output
) {
123 print_r($result['values']);
131 private function _parseOptions() {
132 $args = $_SERVER['argv'];
133 // remove the first argument, which is the name
137 while (list($k, $arg) = each($args)) {
138 // sanitize all user input
139 $arg = $this->_sanitize($arg);
141 // if we're not parsing an option signifier
142 // continue to the next one
143 if (!preg_match('/^-/', $arg)) {
147 // find the value of this arg
148 if (preg_match('/=/', $arg)) {
149 $parts = explode('=', $arg);
154 if (isset($args[$k +
1])) {
155 $next_arg = $this->_sanitize($args[$k +
1]);
156 // if the next argument is not another option
157 // it's the value for this argument
158 if (!preg_match('/^-/', $next_arg)) {
164 // parse the special args first
165 if ($arg == '-e' ||
$arg == '--entity') {
166 $this->_entity
= $value;
168 elseif ($arg == '-a' ||
$arg == '--action') {
169 $this->_action
= $value;
171 elseif ($arg == '-s' ||
$arg == '--site') {
172 $this->_site
= $value;
174 elseif ($arg == '-u' ||
$arg == '--user') {
175 $this->_user
= $value;
177 elseif ($arg == '-p' ||
$arg == '--password') {
178 $this->_password
= $value;
180 elseif ($arg == '-o' ||
$arg == '--output') {
181 $this->_output
= TRUE;
183 elseif ($arg == '-J' ||
$arg == '--json') {
184 $this->_output
= 'json';
186 elseif ($arg == '-j' ||
$arg == '--joblog') {
187 $this->_joblog
= TRUE;
189 elseif ($arg == '-sem' ||
$arg == '--semicolon') {
190 $this->_semicolon
= TRUE;
193 foreach ($this->_additional_arguments
as $short => $long) {
194 if ($arg == '-' . $short ||
$arg == '--' . $long) {
195 $property = '_' . $long;
196 $this->$property = $value;
200 // all other arguments are parameters
201 $key = ltrim($arg, '--');
202 $this->_params
[$key] = isset($value) ?
$value : NULL;
211 private function _bootstrap() {
212 // so the configuration works with php-cli
213 $_SERVER['PHP_SELF'] = "/index.php";
214 $_SERVER['HTTP_HOST'] = $this->_site
;
215 $_SERVER['REMOTE_ADDR'] = "127.0.0.1";
216 $_SERVER['SERVER_SOFTWARE'] = NULL;
217 $_SERVER['REQUEST_METHOD'] = 'GET';
219 // SCRIPT_FILENAME needed by CRM_Utils_System::cmsRootPath
220 $_SERVER['SCRIPT_FILENAME'] = __FILE__
;
222 // CRM-8917 - check if script name starts with /, if not - prepend it.
223 if (ord($_SERVER['SCRIPT_NAME']) != 47) {
224 $_SERVER['SCRIPT_NAME'] = '/' . $_SERVER['SCRIPT_NAME'];
227 $civicrm_root = dirname(__DIR__
);
228 chdir($civicrm_root);
229 if (getenv('CIVICRM_SETTINGS')) {
230 require_once getenv('CIVICRM_SETTINGS');
233 require_once 'civicrm.config.php';
236 if (!class_exists('CRM_Core_ClassLoader')) {
237 require_once $civicrm_root . '/CRM/Core/ClassLoader.php';
239 CRM_Core_ClassLoader
::singleton()->register();
241 $this->_config
= CRM_Core_Config
::singleton();
243 // HTTP_HOST will be 'localhost' unless overwritten with the -s argument.
244 // Now we have a Config object, we can set it from the Base URL.
245 if ($_SERVER['HTTP_HOST'] == 'localhost') {
246 $_SERVER['HTTP_HOST'] = preg_replace(
247 '!^https?://([^/]+)/$!i',
249 $this->_config
->userFrameworkBaseURL
);
252 $class = 'CRM_Utils_System_' . $this->_config
->userFramework
;
255 if (!CRM_Utils_System
::loadBootstrap(array(), FALSE, FALSE, $civicrm_root)) {
256 $this->_log(ts("Failed to bootstrap CMS"));
260 if (strtolower($this->_entity
) == 'job') {
262 $this->_log(ts("Jobs called from cli.php require valid user as parameter"));
267 if (!empty($this->_user
)) {
268 if (!CRM_Utils_System
::authenticateScript(TRUE, $this->_user
, $this->_password
, TRUE, FALSE, FALSE)) {
269 $this->_log(ts("Failed to login as %1. Wrong username or password.", array('1' => $this->_user
)));
272 if (($this->_config
->userFramework
== 'Joomla' && !$cms->loadUser($this->_user
, $this->_password
)) ||
!$cms->loadUser($this->_user
)) {
273 $this->_log(ts("Failed to login as %1", array('1' => $this->_user
)));
284 private function _validateOptions() {
285 $required = $this->_required_arguments
;
286 while (list(, $var) = each($required)) {
288 if (empty($this->$index)) {
289 $missing_arg = '--' . $var;
290 $this->_log(ts("The %1 argument is required", array(1 => $missing_arg)));
291 $this->_log($this->_getUsage());
303 private function _sanitize($value) {
304 // restrict user input - we should not be needing anything
305 // other than normal alpha numeric plus - and _.
306 return trim(preg_replace('#^[^a-zA-Z0-9\-_=/]$#', '', $value));
312 private function _getUsage() {
313 $out = "Usage: cli.php -e entity -a action [-u user] [-s site] [--output|--json] [PARAMS]\n";
314 $out .= " entity is the name of the entity, e.g. Contact, Event, etc.\n";
315 $out .= " action is the name of the action e.g. Get, Create, etc.\n";
316 $out .= " user is an optional username to run the script as\n";
317 $out .= " site is the domain name of the web site (for Drupal multi site installs)\n";
318 $out .= " --output will pretty print the result from the api call\n";
319 $out .= " --json will print the result from the api call as JSON\n";
320 $out .= " PARAMS is one or more --param=value combinations to pass to the api\n";
327 private function _log($error) {
328 // fixme, this should call some CRM_Core_Error:: function
329 // that properly logs
336 * class used by csv/export.php to export records from
337 * the database in a csv file format.
339 class civicrm_cli_csv_exporter
extends civicrm_cli
{
340 var $separator = ',';
344 public function __construct() {
345 $this->_required_arguments
= array('entity');
346 parent
::initialize();
352 public function run() {
353 if ($this->_semicolon
) {
354 $this->separator
= ';';
357 $out = fopen("php://output", 'w');
358 fputcsv($out, $this->columns
, $this->separator
, '"');
361 $result = civicrm_api($this->_entity
, 'Get', $this->_params
);
363 foreach ($result['values'] as $row) {
365 $columns = array_keys($row);
366 fputcsv($out, $columns, $this->separator
, '"');
369 //handle values returned as arrays (i.e. custom fields that allow multiple selections) by inserting a control character
370 foreach ($row as &$field) {
371 if (is_array($field)) {
373 $field = implode($field, CRM_Core_DAO
::VALUE_SEPARATOR
) . CRM_Core_DAO
::VALUE_SEPARATOR
;
376 fputcsv($out, $row, $this->separator
, '"');
385 * base class used by both civicrm_cli_csv_import
386 * and civicrm_cli_csv_deleter to add or delete
387 * records based on those found in a csv file
388 * passed to the script.
390 class civicrm_cli_csv_file
extends civicrm_cli
{
392 var $separator = ',';
396 public function __construct() {
397 $this->_required_arguments
= array('entity', 'file');
398 $this->_additional_arguments
= array('f' => 'file');
399 parent
::initialize();
405 public function run() {
407 $handle = fopen($this->_file
, "r");
410 die("Could not open file: " . $this->_file
. ". Please provide an absolute path.\n");
414 $header = fgetcsv($handle, 0, $this->separator
);
415 // In case fgetcsv couldn't parse the header and dumped the whole line in 1 array element
416 // Try a different separator char
417 if (count($header) == 1) {
418 $this->separator
= ";";
420 $header = fgetcsv($handle, 0, $this->separator
);
421 if (count($header) == 1) {
422 die("Invalid file format for " . $this->_file
. ". It must be a valid csv with separator ',' or ';'\n");
426 $this->header
= $header;
427 while (($data = fgetcsv($handle, 0, $this->separator
)) !== FALSE) {
429 if (count($data) == 1 && is_null($data[0])) {
433 $params = $this->convertLine($data);
434 $this->processLine($params);
439 /* return a params as expected */
445 public function convertLine($data) {
447 foreach ($this->header
as $i => $field) {
448 //split any multiselect data, denoted with CRM_Core_DAO::VALUE_SEPARATOR
449 if (strpos($data[$i], CRM_Core_DAO
::VALUE_SEPARATOR
) !== FALSE) {
450 $data[$i] = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $data[$i]);
451 $data[$i] = array_combine($data[$i], $data[$i]);
453 $params[$field] = $data[$i];
455 $params['version'] = 3;
462 * class for processing records to add
463 * used by csv/import.php
466 class civicrm_cli_csv_importer
extends civicrm_cli_csv_file
{
468 * @param array $params
470 public function processline($params) {
471 $result = civicrm_api($this->_entity
, 'Create', $params);
472 if ($result['is_error']) {
473 echo "\nERROR line " . $this->row
. ": " . $result['error_message'] . "\n";
476 echo "\nline " . $this->row
. ": created " . $this->_entity
. " id: " . $result['id'] . "\n";
483 * class for processing records to delete
484 * used by csv/delete.php
487 class civicrm_cli_csv_deleter
extends civicrm_cli_csv_file
{
489 * @param array $params
491 public function processline($params) {
492 $result = civicrm_api($this->_entity
, 'Delete', $params);
493 if ($result['is_error']) {
494 echo "\nERROR line " . $this->row
. ": " . $result['error_message'] . "\n";
497 echo "\nline " . $this->row
. ": deleted\n";