Merge pull request #2749 from monishdeb/HR-317
[civicrm-core.git] / CRM / Core / Error.php
index 4f8b855a2a224af8b9714f1057af0993bb158796..55e2cbeaf0567c6b5a69b5a551dc16c5b58ed08c 100644 (file)
@@ -106,7 +106,7 @@ class CRM_Core_Error extends PEAR_ErrorStack {
     $this->setDefaultCallback(array($this, 'handlePES'));
   }
 
-  function getMessages(&$error, $separator = '<br />') {
+  static public function getMessages(&$error, $separator = '<br />') {
     if (is_a($error, 'CRM_Core_Error')) {
       $errors = $error->getErrors();
       $message = array();
@@ -228,7 +228,9 @@ class CRM_Core_Error extends PEAR_ErrorStack {
     $error['user_info']  = $pearError->getUserInfo();
     $error['to_string']  = $pearError->toString();
 
-    CRM_Core_Error::debug('Initialization Error', $error);
+    // ensure that debug does not check permissions since we are in bootstrap
+    // mode and need to print a decent message to help the user
+    CRM_Core_Error::debug('Initialization Error', $error, TRUE, TRUE, FALSE);
 
     // always log the backtrace to a file
     self::backtrace('backTrace', TRUE);
@@ -309,6 +311,21 @@ class CRM_Core_Error extends PEAR_ErrorStack {
       }
     }
 
+    // If we are in an ajax callback, format output appropriately
+    if (CRM_Utils_Array::value('snippet', $_REQUEST) === CRM_Core_Smarty::PRINT_JSON) {
+      $out = array(
+        'status' => 'fatal',
+        'content' => '<div class="messages status no-popup"><div class="icon inform-icon"></div>' . ts('Sorry but we are not able to provide this at the moment.') . '</div>',
+      );
+      if ($config->backtrace && CRM_Core_Permission::check('view debug output')) {
+        $out['backtrace'] = self::parseBacktrace(debug_backtrace());
+        $message .= '<p><em>See console for backtrace</em></p>';
+      }
+      CRM_Core_Session::setStatus($message, ts('Sorry an Error Occured'), 'error');
+      CRM_Core_Transaction::forceRollbackIfEnabled();
+      CRM_Core_Page_AJAX::returnJsonResponse($out);
+    }
+
     if ($config->backtrace) {
       self::backtrace();
     }
@@ -319,7 +336,13 @@ class CRM_Core_Error extends PEAR_ErrorStack {
     CRM_Core_Error::debug_var('Fatal Error Details', $vars);
     CRM_Core_Error::backtrace('backTrace', TRUE);
     $content = $template->fetch($config->fatalErrorTemplate);
-    if ($config->userFramework == 'Joomla' && class_exists('JError')) {
+    // JErrorPage exists only in 3.x and not 2.x
+    // CRM-13714
+    if ($config->userFramework == 'Joomla' && class_exists('JErrorPage')) {
+      $error = new Exception($content);
+      JErrorPage::render($error);
+    }
+    else if ($config->userFramework == 'Joomla' && class_exists('JError')) {
       JError::raiseError('CiviCRM-001', $content);
     }
     else {
@@ -410,12 +433,15 @@ class CRM_Core_Error extends PEAR_ErrorStack {
    * @param  mixed  reference to variables that we need a trace of
    * @param  bool   should we log or return the output
    * @param  bool   whether to generate a HTML-escaped output
+   * @param  bool   should we check permissions before displaying output
+   *                useful when we die during initialization and permissioning
+   *                subsystem is not initialized - CRM-13765
    *
    * @return string the generated output
    * @access public
    * @static
    */
-  static function debug($name, $variable = NULL, $log = TRUE, $html = TRUE) {
+  static function debug($name, $variable = NULL, $log = TRUE, $html = TRUE, $checkPermission = TRUE) {
     $error = self::singleton();
 
     if ($variable === NULL) {
@@ -438,7 +464,10 @@ class CRM_Core_Error extends PEAR_ErrorStack {
       }
       $out = "{$prefix}$out\n";
     }
-    if ($log && CRM_Core_Permission::check('view debug output')) {
+    if (
+      $log &&
+      (!$checkPermission || CRM_Core_Permission::check('view debug output'))
+    ) {
       echo $out;
     }
 
@@ -590,15 +619,32 @@ class CRM_Core_Error extends PEAR_ErrorStack {
    * @param boolean $showArgs TRUE if we should try to display content of function arguments (which could be sensitive); FALSE to display only the type of each function argument
    * @param int $maxArgLen maximum number of characters to show from each argument string
    * @return string printable plain-text
-   * @see debug_backtrace
-   * @see Exception::getTrace()
    */
   static function formatBacktrace($backTrace, $showArgs = TRUE, $maxArgLen = 80) {
     $message = '';
-    foreach ($backTrace as $idx => $trace) {
+    foreach (self::parseBacktrace($backTrace, $showArgs, $maxArgLen) as $idx => $trace) {
+      $message .= sprintf("#%s %s\n", $idx, $trace);
+    }
+    $message .= sprintf("#%s {main}\n", 1+$idx);
+    return $message;
+  }
+
+  /**
+   * Render a backtrace array as an array
+   *
+   * @param array $backTrace array of stack frames
+   * @param boolean $showArgs TRUE if we should try to display content of function arguments (which could be sensitive); FALSE to display only the type of each function argument
+   * @param int $maxArgLen maximum number of characters to show from each argument string
+   * @return array
+   * @see debug_backtrace
+   * @see Exception::getTrace()
+   */
+  static function parseBacktrace($backTrace, $showArgs = TRUE, $maxArgLen = 80) {
+    $ret = array();
+    foreach ($backTrace as $trace) {
       $args = array();
       $fnName = CRM_Utils_Array::value('function', $trace);
-      $className = array_key_exists('class', $trace) ? ($trace['class'] . $trace['type']) : '';
+      $className = isset($trace['class']) ? ($trace['class'] . $trace['type']) : '';
 
       // do now show args for a few password related functions
       $skipArgs = ($className == 'DB::' && $fnName == 'connect') ? TRUE : FALSE;
@@ -637,9 +683,8 @@ class CRM_Core_Error extends PEAR_ErrorStack {
         }
       }
 
-      $message .= sprintf(
-        "#%s %s(%s): %s%s(%s)\n",
-        $idx,
+      $ret[] = sprintf(
+        "%s(%s): %s%s(%s)",
         CRM_Utils_Array::value('file', $trace, '[internal function]'),
         CRM_Utils_Array::value('line', $trace, ''),
         $className,
@@ -647,8 +692,7 @@ class CRM_Core_Error extends PEAR_ErrorStack {
         implode(", ", $args)
       );
     }
-    $message .= sprintf("#%s {main}\n", 1+$idx);
-    return $message;
+    return $ret;
   }
 
   /**
@@ -728,6 +772,9 @@ class CRM_Core_Error extends PEAR_ErrorStack {
       $redirect = $session->readUserContext();
     }
     $session->setStatus($status, $title);
+    if (CRM_Utils_Array::value('snippet', $_REQUEST) === CRM_Core_Smarty::PRINT_JSON) {
+      CRM_Core_Page_AJAX::returnJsonResponse(array('status' => 'error'));
+    }
     CRM_Utils_System::redirect($redirect);
   }
 
@@ -743,25 +790,21 @@ class CRM_Core_Error extends PEAR_ErrorStack {
     $error->_errorsByLevel = array();
   }
 
-  public static function ignoreException($callback = NULL) {
-    if (!$callback) {
-      $callback = array('CRM_Core_Error', 'nullHandler');
-    }
-
-    $GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_CALLBACK;
-    $GLOBALS['_PEAR_default_error_options'] = $callback;
-  }
-
+  /**
+   * PEAR error-handler which converts errors to exceptions
+   *
+   * @param $pearError
+   * @throws PEAR_Exception
+   */
   public static function exceptionHandler($pearError) {
     CRM_Core_Error::backtrace('backTrace', TRUE);
     throw new PEAR_Exception($pearError->getMessage(), $pearError);
   }
 
   /**
-   * Error handler to quietly catch otherwise fatal smtp transport errors.
+   * PEAR error-handler to quietly catch otherwise fatal errors. Intended for use with smtp transport.
    *
    * @param object $obj       The PEAR_ERROR object
-   *
    * @return object $obj
    * @access public
    * @static
@@ -772,21 +815,6 @@ class CRM_Core_Error extends PEAR_ErrorStack {
     return $obj;
   }
 
-  /**
-   * (Re)set the default callback method
-   *
-   * @return void
-   * @access public
-   * @static
-   */
-  public static function setCallback($callback = NULL) {
-    if (!$callback) {
-      $callback = array('CRM_Core_Error', 'handle');
-    }
-    $GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_CALLBACK;
-    $GLOBALS['_PEAR_default_error_options'] = $callback;
-  }
-
   /*
    * @deprecated
    * This function is no longer used by v3 api.
@@ -828,7 +856,7 @@ class CRM_Core_Error extends PEAR_ErrorStack {
   }
 
   public static function isAPIError($error, $type = CRM_Core_Error::FATAL_ERROR) {
-    if (is_array($error) && CRM_Utils_Array::value('is_error', $error)) {
+    if (is_array($error) && !empty($error['is_error'])) {
       $code = $error['error_message']['code'];
       if ($code == $type) {
         return TRUE;