505 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			505 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /*
 | |
|  +--------------------------------------------------------------------+
 | |
|  | CiviCRM version 4.7                                                |
 | |
|  +--------------------------------------------------------------------+
 | |
|  | Copyright Tech To The People http:tttp.eu (c) 2008                 |
 | |
|  +--------------------------------------------------------------------+
 | |
|  |                                                                    |
 | |
|  | CiviCRM is free software; you can copy, modify, and distribute it  |
 | |
|  | under the terms of the GNU Affero General Public License           |
 | |
|  | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
 | |
|  |                                                                    |
 | |
|  | CiviCRM 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 Affero General Public License for more details.        |
 | |
|  |                                                                    |
 | |
|  | You should have received a copy of the GNU Affero General Public   |
 | |
|  | License and the CiviCRM Licensing Exception along                  |
 | |
|  | with this program; if not, contact CiviCRM LLC                     |
 | |
|  | at info[AT]civicrm[DOT]org. If you have questions about the        |
 | |
|  | GNU Affero General Public License or the licensing of CiviCRM,     |
 | |
|  | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
 | |
|  +--------------------------------------------------------------------+
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * This files provides several classes for doing command line work with
 | |
|  * CiviCRM. civicrm_cli is the base class. It's used by cli.php.
 | |
|  *
 | |
|  * In addition, there are several additional classes that inherit
 | |
|  * civicrm_cli to do more precise functions.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * base class for doing all command line operations via civicrm
 | |
|  * used by cli.php
 | |
|  */
 | |
| class civicrm_cli {
 | |
|   // required values that must be passed
 | |
|   // via the command line
 | |
|   var $_required_arguments = array('action', 'entity');
 | |
|   var $_additional_arguments = array();
 | |
|   var $_entity = NULL;
 | |
|   var $_action = NULL;
 | |
|   var $_output = FALSE;
 | |
|   var $_joblog = FALSE;
 | |
|   var $_semicolon = FALSE;
 | |
|   var $_config;
 | |
| 
 | |
|   // optional arguments
 | |
|   var $_site = 'localhost';
 | |
|   var $_user = NULL;
 | |
|   var $_password = NULL;
 | |
| 
 | |
|   // all other arguments populate the parameters
 | |
|   // array that is passed to civicrm_api
 | |
|   var $_params = array('version' => 3);
 | |
| 
 | |
|   var $_errors = array();
 | |
| 
 | |
|   /**
 | |
|    * @return bool
 | |
|    */
 | |
|   public function initialize() {
 | |
|     if (!$this->_accessing_from_cli()) {
 | |
|       return FALSE;
 | |
|     }
 | |
|     if (!$this->_parseOptions()) {
 | |
|       return FALSE;
 | |
|     }
 | |
|     if (!$this->_bootstrap()) {
 | |
|       return FALSE;
 | |
|     }
 | |
|     if (!$this->_validateOptions()) {
 | |
|       return FALSE;
 | |
|     }
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Ensure function is being run from the cli.
 | |
|    *
 | |
|    * @return bool
 | |
|    */
 | |
|   public function _accessing_from_cli() {
 | |
|     if (PHP_SAPI === 'cli') {
 | |
|       return TRUE;
 | |
|     }
 | |
|     else {
 | |
|       die("cli.php can only be run from command line.");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @return bool
 | |
|    */
 | |
|   public function callApi() {
 | |
|     require_once 'api/api.php';
 | |
| 
 | |
|     CRM_Core_Config::setPermitCacheFlushMode(FALSE);
 | |
|     //  CRM-9822 -'execute' action always goes thru Job api and always writes to log
 | |
|     if ($this->_action != 'execute' && $this->_joblog) {
 | |
|       require_once 'CRM/Core/JobManager.php';
 | |
|       $facility = new CRM_Core_JobManager();
 | |
|       $facility->setSingleRunParams($this->_entity, $this->_action, $this->_params, 'From Cli.php');
 | |
|       $facility->executeJobByAction($this->_entity, $this->_action);
 | |
|     }
 | |
|     else {
 | |
|       // CRM-9822 cli.php calls don't require site-key, so bypass site-key authentication
 | |
|       $this->_params['auth'] = FALSE;
 | |
|       $result = civicrm_api($this->_entity, $this->_action, $this->_params);
 | |
|     }
 | |
|     CRM_Core_Config::setPermitCacheFlushMode(TRUE);
 | |
|     CRM_Contact_BAO_Contact_Utils::clearContactCaches();
 | |
| 
 | |
|     if (!empty($result['is_error'])) {
 | |
|       $this->_log($result['error_message']);
 | |
|       return FALSE;
 | |
|     }
 | |
|     elseif ($this->_output === 'json') {
 | |
|       echo json_encode($result, defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0);
 | |
|     }
 | |
|     elseif ($this->_output) {
 | |
|       print_r($result['values']);
 | |
|     }
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @return bool
 | |
|    */
 | |
|   private function _parseOptions() {
 | |
|     $args = $_SERVER['argv'];
 | |
|     // remove the first argument, which is the name
 | |
|     // of this script
 | |
|     array_shift($args);
 | |
| 
 | |
|     while (list($k, $arg) = each($args)) {
 | |
|       // sanitize all user input
 | |
|       $arg = $this->_sanitize($arg);
 | |
| 
 | |
|       // if we're not parsing an option signifier
 | |
|       // continue to the next one
 | |
|       if (!preg_match('/^-/', $arg)) {
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       // find the value of this arg
 | |
|       if (preg_match('/=/', $arg)) {
 | |
|         $parts = explode('=', $arg);
 | |
|         $arg = $parts[0];
 | |
|         $value = $parts[1];
 | |
|       }
 | |
|       else {
 | |
|         if (isset($args[$k + 1])) {
 | |
|           $next_arg = $this->_sanitize($args[$k + 1]);
 | |
|           // if the next argument is not another option
 | |
|           // it's the value for this argument
 | |
|           if (!preg_match('/^-/', $next_arg)) {
 | |
|             $value = $next_arg;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // parse the special args first
 | |
|       if ($arg == '-e' || $arg == '--entity') {
 | |
|         $this->_entity = $value;
 | |
|       }
 | |
|       elseif ($arg == '-a' || $arg == '--action') {
 | |
|         $this->_action = $value;
 | |
|       }
 | |
|       elseif ($arg == '-s' || $arg == '--site') {
 | |
|         $this->_site = $value;
 | |
|       }
 | |
|       elseif ($arg == '-u' || $arg == '--user') {
 | |
|         $this->_user = $value;
 | |
|       }
 | |
|       elseif ($arg == '-p' || $arg == '--password') {
 | |
|         $this->_password = $value;
 | |
|       }
 | |
|       elseif ($arg == '-o' || $arg == '--output') {
 | |
|         $this->_output = TRUE;
 | |
|       }
 | |
|       elseif ($arg == '-J' || $arg == '--json') {
 | |
|         $this->_output = 'json';
 | |
|       }
 | |
|       elseif ($arg == '-j' || $arg == '--joblog') {
 | |
|         $this->_joblog = TRUE;
 | |
|       }
 | |
|       elseif ($arg == '-sem' || $arg == '--semicolon') {
 | |
|         $this->_semicolon = TRUE;
 | |
|       }
 | |
|       else {
 | |
|         foreach ($this->_additional_arguments as $short => $long) {
 | |
|           if ($arg == '-' . $short || $arg == '--' . $long) {
 | |
|             $property = '_' . $long;
 | |
|             $this->$property = $value;
 | |
|             continue;
 | |
|           }
 | |
|         }
 | |
|         // all other arguments are parameters
 | |
|         $key = ltrim($arg, '--');
 | |
|         $this->_params[$key] = isset($value) ? $value : NULL;
 | |
|       }
 | |
|     }
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @return bool
 | |
|    */
 | |
|   private function _bootstrap() {
 | |
|     // so the configuration works with php-cli
 | |
|     $_SERVER['PHP_SELF'] = "/index.php";
 | |
|     $_SERVER['HTTP_HOST'] = $this->_site;
 | |
|     $_SERVER['REMOTE_ADDR'] = "127.0.0.1";
 | |
|     $_SERVER['SERVER_SOFTWARE'] = NULL;
 | |
|     $_SERVER['REQUEST_METHOD'] = 'GET';
 | |
| 
 | |
|     // SCRIPT_FILENAME needed by CRM_Utils_System::cmsRootPath
 | |
|     $_SERVER['SCRIPT_FILENAME'] = __FILE__;
 | |
| 
 | |
|     // CRM-8917 - check if script name starts with /, if not - prepend it.
 | |
|     if (ord($_SERVER['SCRIPT_NAME']) != 47) {
 | |
|       $_SERVER['SCRIPT_NAME'] = '/' . $_SERVER['SCRIPT_NAME'];
 | |
|     }
 | |
| 
 | |
|     $civicrm_root = dirname(__DIR__);
 | |
|     chdir($civicrm_root);
 | |
|     if (getenv('CIVICRM_SETTINGS')) {
 | |
|       require_once getenv('CIVICRM_SETTINGS');
 | |
|     }
 | |
|     else {
 | |
|       require_once 'civicrm.config.php';
 | |
|     }
 | |
|     // autoload
 | |
|     if (!class_exists('CRM_Core_ClassLoader')) {
 | |
|       require_once $civicrm_root . '/CRM/Core/ClassLoader.php';
 | |
|     }
 | |
|     CRM_Core_ClassLoader::singleton()->register();
 | |
| 
 | |
|     $this->_config = CRM_Core_Config::singleton();
 | |
| 
 | |
|     // HTTP_HOST will be 'localhost' unless overwritten with the -s argument.
 | |
|     // Now we have a Config object, we can set it from the Base URL.
 | |
|     if ($_SERVER['HTTP_HOST'] == 'localhost') {
 | |
|       $_SERVER['HTTP_HOST'] = preg_replace(
 | |
|         '!^https?://([^/]+)/$!i',
 | |
|         '$1',
 | |
|         $this->_config->userFrameworkBaseURL);
 | |
|     }
 | |
| 
 | |
|     $class = 'CRM_Utils_System_' . $this->_config->userFramework;
 | |
| 
 | |
|     $cms = new $class();
 | |
|     if (!CRM_Utils_System::loadBootstrap(array(), FALSE, FALSE, $civicrm_root)) {
 | |
|       $this->_log(ts("Failed to bootstrap CMS"));
 | |
|       return FALSE;
 | |
|     }
 | |
| 
 | |
|     if (strtolower($this->_entity) == 'job') {
 | |
|       if (!$this->_user) {
 | |
|         $this->_log(ts("Jobs called from cli.php require valid user as parameter"));
 | |
|         return FALSE;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (!empty($this->_user)) {
 | |
|       if (!CRM_Utils_System::authenticateScript(TRUE, $this->_user, $this->_password, TRUE, FALSE, FALSE)) {
 | |
|         $this->_log(ts("Failed to login as %1. Wrong username or password.", array('1' => $this->_user)));
 | |
|         return FALSE;
 | |
|       }
 | |
|       if (($this->_config->userFramework == 'Joomla' && !$cms->loadUser($this->_user, $this->_password)) || !$cms->loadUser($this->_user)) {
 | |
|         $this->_log(ts("Failed to login as %1", array('1' => $this->_user)));
 | |
|         return FALSE;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @return bool
 | |
|    */
 | |
|   private function _validateOptions() {
 | |
|     $required = $this->_required_arguments;
 | |
|     while (list(, $var) = each($required)) {
 | |
|       $index = '_' . $var;
 | |
|       if (empty($this->$index)) {
 | |
|         $missing_arg = '--' . $var;
 | |
|         $this->_log(ts("The %1 argument is required", array(1 => $missing_arg)));
 | |
|         $this->_log($this->_getUsage());
 | |
|         return FALSE;
 | |
|       }
 | |
|     }
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @param $value
 | |
|    *
 | |
|    * @return string
 | |
|    */
 | |
|   private function _sanitize($value) {
 | |
|     // restrict user input - we should not be needing anything
 | |
|     // other than normal alpha numeric plus - and _.
 | |
|     return trim(preg_replace('#^[^a-zA-Z0-9\-_=/]$#', '', $value));
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @return string
 | |
|    */
 | |
|   private function _getUsage() {
 | |
|     $out = "Usage: cli.php -e entity -a action [-u user] [-s site] [--output|--json] [PARAMS]\n";
 | |
|     $out .= "  entity is the name of the entity, e.g. Contact, Event, etc.\n";
 | |
|     $out .= "  action is the name of the action e.g. Get, Create, etc.\n";
 | |
|     $out .= "  user is an optional username to run the script as\n";
 | |
|     $out .= "  site is the domain name of the web site (for Drupal multi site installs)\n";
 | |
|     $out .= "  --output will pretty print the result from the api call\n";
 | |
|     $out .= "  --json will print the result from the api call as JSON\n";
 | |
|     $out .= "  PARAMS is one or more --param=value combinations to pass to the api\n";
 | |
|     return ts($out);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @param $error
 | |
|    */
 | |
|   private function _log($error) {
 | |
|     // fixme, this should call some CRM_Core_Error:: function
 | |
|     // that properly logs
 | |
|     print "$error\n";
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * class used by csv/export.php to export records from
 | |
|  * the database in a csv file format.
 | |
|  */
 | |
| class civicrm_cli_csv_exporter extends civicrm_cli {
 | |
|   var $separator = ',';
 | |
| 
 | |
|   /**
 | |
|    */
 | |
|   public function __construct() {
 | |
|     $this->_required_arguments = array('entity');
 | |
|     parent::initialize();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Run the script.
 | |
|    */
 | |
|   public function run() {
 | |
|     if ($this->_semicolon) {
 | |
|       $this->separator = ';';
 | |
|     }
 | |
| 
 | |
|     $out = fopen("php://output", 'w');
 | |
|     fputcsv($out, $this->columns, $this->separator, '"');
 | |
| 
 | |
|     $this->row = 1;
 | |
|     $result = civicrm_api($this->_entity, 'Get', $this->_params);
 | |
|     $first = TRUE;
 | |
|     foreach ($result['values'] as $row) {
 | |
|       if ($first) {
 | |
|         $columns = array_keys($row);
 | |
|         fputcsv($out, $columns, $this->separator, '"');
 | |
|         $first = FALSE;
 | |
|       }
 | |
|       //handle values returned as arrays (i.e. custom fields that allow multiple selections) by inserting a control character
 | |
|       foreach ($row as &$field) {
 | |
|         if (is_array($field)) {
 | |
|           //convert to string
 | |
|           $field = implode($field, CRM_Core_DAO::VALUE_SEPARATOR) . CRM_Core_DAO::VALUE_SEPARATOR;
 | |
|         }
 | |
|       }
 | |
|       fputcsv($out, $row, $this->separator, '"');
 | |
|     }
 | |
|     fclose($out);
 | |
|     echo "\n";
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * base class used by both civicrm_cli_csv_import
 | |
|  * and civicrm_cli_csv_deleter to add or delete
 | |
|  * records based on those found in a csv file
 | |
|  * passed to the script.
 | |
|  */
 | |
| class civicrm_cli_csv_file extends civicrm_cli {
 | |
|   var $header;
 | |
|   var $separator = ',';
 | |
| 
 | |
|   /**
 | |
|    */
 | |
|   public function __construct() {
 | |
|     $this->_required_arguments = array('entity', 'file');
 | |
|     $this->_additional_arguments = array('f' => 'file');
 | |
|     parent::initialize();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Run CLI function.
 | |
|    */
 | |
|   public function run() {
 | |
|     $this->row = 1;
 | |
|     $handle = fopen($this->_file, "r");
 | |
| 
 | |
|     if (!$handle) {
 | |
|       die("Could not open file: " . $this->_file . ". Please provide an absolute path.\n");
 | |
|     }
 | |
| 
 | |
|     //header
 | |
|     $header = fgetcsv($handle, 0, $this->separator);
 | |
|     // In case fgetcsv couldn't parse the header and dumped the whole line in 1 array element
 | |
|     // Try a different separator char
 | |
|     if (count($header) == 1) {
 | |
|       $this->separator = ";";
 | |
|       rewind($handle);
 | |
|       $header = fgetcsv($handle, 0, $this->separator);
 | |
|     }
 | |
| 
 | |
|     $this->header = $header;
 | |
|     while (($data = fgetcsv($handle, 0, $this->separator)) !== FALSE) {
 | |
|       // skip blank lines
 | |
|       if (count($data) == 1 && is_null($data[0])) {
 | |
|         continue;
 | |
|       }
 | |
|       $this->row++;
 | |
|       if ($this->row % 1000 == 0) {
 | |
|         // Reset PEAR_DB_DATAOBJECT cache to prevent memory leak
 | |
|         CRM_Core_DAO::freeResult();
 | |
|       }
 | |
|       $params = $this->convertLine($data);
 | |
|       $this->processLine($params);
 | |
|     }
 | |
|     fclose($handle);
 | |
|   }
 | |
| 
 | |
|   /* return a params as expected */
 | |
|   /**
 | |
|    * @param $data
 | |
|    *
 | |
|    * @return array
 | |
|    */
 | |
|   public function convertLine($data) {
 | |
|     $params = array();
 | |
|     foreach ($this->header as $i => $field) {
 | |
|       //split any multiselect data, denoted with CRM_Core_DAO::VALUE_SEPARATOR
 | |
|       if (strpos($data[$i], CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE) {
 | |
|         $data[$i] = explode(CRM_Core_DAO::VALUE_SEPARATOR, $data[$i]);
 | |
|         $data[$i] = array_combine($data[$i], $data[$i]);
 | |
|       }
 | |
|       $params[$field] = $data[$i];
 | |
|     }
 | |
|     $params['version'] = 3;
 | |
|     return $params;
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * class for processing records to add
 | |
|  * used by csv/import.php
 | |
|  *
 | |
|  */
 | |
| class civicrm_cli_csv_importer extends civicrm_cli_csv_file {
 | |
|   /**
 | |
|    * @param array $params
 | |
|    */
 | |
|   public function processline($params) {
 | |
|     $result = civicrm_api($this->_entity, 'Create', $params);
 | |
|     if ($result['is_error']) {
 | |
|       echo "\nERROR line " . $this->row . ": " . $result['error_message'] . "\n";
 | |
|     }
 | |
|     else {
 | |
|       echo "\nline " . $this->row . ": created " . $this->_entity . " id: " . $result['id'] . "\n";
 | |
|     }
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * class for processing records to delete
 | |
|  * used by csv/delete.php
 | |
|  *
 | |
|  */
 | |
| class civicrm_cli_csv_deleter extends civicrm_cli_csv_file {
 | |
|   /**
 | |
|    * @param array $params
 | |
|    */
 | |
|   public function processline($params) {
 | |
|     $result = civicrm_api($this->_entity, 'Delete', $params);
 | |
|     if ($result['is_error']) {
 | |
|       echo "\nERROR line " . $this->row . ": " . $result['error_message'] . "\n";
 | |
|     }
 | |
|     else {
 | |
|       echo "\nline " . $this->row . ": deleted\n";
 | |
|     }
 | |
|   }
 | |
| 
 | |
| }
 |