/home/vianto5/cititower.mx/wp-content/plugins/sitepress-multilingual-cms/classes/jobs
NameSizeModeActions
endpoint/-0755rm
Log/-0755rm
query/-0755rm
utils/-0755rm
class-wpml-tm-job-element-entity.php20430644editdlrm
class-wpml-tm-job-elements-repository.php12520644editdlrm
class-wpml-tm-job-entity.php65310644editdlrm
class-wpml-tm-job-ts-status.php7000644editdlrm
class-wpml-tm-jobs-batch.php6550644editdlrm
class-wpml-tm-jobs-collection.php28150644editdlrm
class-wpml-tm-jobs-date-range.php8920644editdlrm
class-wpml-tm-jobs-needs-update-param.php9030644editdlrm
class-wpml-tm-jobs-repository.php52580644editdlrm
class-wpml-tm-jobs-search-params.php123090644editdlrm
class-wpml-tm-jobs-sorting-param.php6280644editdlrm
class-wpml-tm-post-job-entity.php41370644editdlrm
JobLog.php158970644editdlrm
JobLogFsStorage.php36770644editdlrm
Loader.php11090644editdlrm
Manual.php73200644editdlrm
Edit: /home/vianto5/cititower.mx/wp-content/plugins/sitepress-multilingual-cms/classes/jobs/JobLog.php (15897B)
string, // REST or admin URL * requestParams => array, // Parsed input params * requestDateTime => string (ISO-8601), * hasErrorLogs => bool, * logsByGroup => [ * [ * groupId => int, * label => string, * logs => [ * [ * id => string, // Log message * data => array, // Arbitrary payload * trace => string[], // Call stack (file:line class/method) * logType => int, // LOG_TYPE_* * ...extra fields (apiCall, type, element_id, etc.) * ], * ... * ], * data => array // Group-level metadata * ], * ... * ], * logUid => string * ] * * Case B: Admin "Send to translation" request * - Same structure as Case A * - requestParams may include posts, strings, batch info * - logs may include st-batch element_id expansion (string_ids_in_batch) */ /** * Option name that enables/disables logging. * @var string */ const IS_ENABLED_OPTION_NAME = 'wpml_tm_job_log_is_enabled'; /** * Log type: informational. * @var int */ const LOG_TYPE_INFO = 0; /** * Log type: error. * @var int */ const LOG_TYPE_ERROR = 1; /** * Group ID for sending jobs. Used when we send content for translation in the /inc/translation-management/translation-management.class.php. * @var int */ const GROUP_ID_SEND_JOBS = 0; /** * Group ID for syncing jobs. Used when we sync jobs in the /classes/ATE/Sync/Process.php. * @var int */ const GROUP_ID_SYNC_JOBS = 1; /** * Group ID for downloading jobs. Used when we download jobs in the /classes/ATE/Download/Process.php. * @var int */ const GROUP_ID_DOWNLOAD_JOBS = 2; /** * Prevents calling from unit tests methods from $sitepress and $wpdb. * @var bool */ private static $isInitialised = false; /** * Whether current request has any error logs. When it is setup request will render having error state in the logs UI. * @var bool */ private static $hasRequestAnyErrorLog = false; /** * Cached flag whether logging is enabled. It is enabled by the button in the logging screen. * That screen is accessible from the WPML support page. * * @var bool|null */ private static $isEnabled; /** * Stores current request URL. If this value is null that means request was never initialized. * We want to log only explicit places in the code and avoid cases when we created some logs from some child class * that called addLog out of the scope when we explicitly initialised the request. Such calls can create many * useless entries in the database which give no value for the inspection of those logs and could make option entry grow fast. * @var string|null */ private static $requestUrl; /** * Stores parsed request parameters from the current request. * @var array */ private static $requestParams = []; /** * Request datetime in ISO-8601 UTC format. * @var string|null */ private static $requestDateTime; /** * Logs grouped by logical groups. In Case A: REST / ATE sync/download request there can be 1 logical group. * * In Case B: Admin "Send to translation" request there can be multiple logical groups, 1 group per each send_jobs function call. * * This will allow to group logs into collapsible containers in the UI. * * @var array */ private static $logsByGroup = []; /** * Current opened group ID. * @var int|null */ private static $groupId; /** * Current opened group label. * @var string|null */ private static $groupLabel; /** * Logs of the current opened group. * @var array */ private static $groupLogs = []; /** * Arbitrary data attached to current opened group. * @var array */ private static $groupData = []; /** * Object IDs already logged to prevent recursion in dataToArray function call. * @var array */ private static $alreadyAddedToLog = []; /** * Extra data merged into each log entry. The caller should manually call removeExtraLog data to remove it. * Otherwise it will be added to all next logs. It is needed to track some optional properties inside each log * inside current group, for example current processed language during the step when we send content for translation. * @var array */ private static $extraLogData = []; /** * Collected string batch IDs. We need to store them to select strings later and show string names and ids from each batch in UI. * @var array */ private static $stringBatchIds = []; /** * @param int $groupId * @return bool */ public static function isSendJobsLogsGroup( $groupId ) { return self::GROUP_ID_SEND_JOBS === (int) $groupId; } public static function init() { self::$isInitialised = true; add_action( 'shutdown', [ __CLASS__, 'shutdown' ], PHP_INT_MAX ); } /** * Checking in wp_options if logging was enabled from the UI by user. * * @return bool */ public static function isEnabled() { if ( ! self::$isInitialised ) { return false; } if ( ! is_null( self::$isEnabled ) ) { return self::$isEnabled; } global $sitepress; if ( ! $sitepress ) { return false; } return self::$isEnabled = (bool) $sitepress->get_setting( self::IS_ENABLED_OPTION_NAME, false ); } /** * Checks whether logging is allowed for current request. * * @return bool */ private static function canLog() { return self::wasRequestInitialised() && self::isEnabled(); } /** * @param bool $isEnabled */ public static function setIsEnabled( $isEnabled ) { global $sitepress; $sitepress->set_setting( self::IS_ENABLED_OPTION_NAME, (bool) $isEnabled ); $sitepress->save_settings(); } /** * This call should start the logging process and be called before starting any logging. * * @return void */ public static function maybeInitRequest() { if ( ! is_null( self::$requestDateTime ) ) { return; } self::$requestUrl = $_SERVER['REQUEST_URI'] ?? null; self::$requestParams = self::getUrlParams(); self::$requestDateTime = gmdate('Y-m-d\TH:i:s\Z'); } /** * Check if request data was initialized. * * @return bool */ public static function wasRequestInitialised() { return is_string( self::$requestUrl ); } /** * Start a new logging group. * * @param int $groupId * @param string $groupLabel * @param array $groupData */ public static function createNewGroup( $groupId, $groupLabel = '', $groupData = [] ) { if ( ! self::canLog() ) { return; } self::$groupId = $groupId; self::$groupLabel = $groupLabel; self::$groupLogs = []; self::$groupData = $groupData; } public static function finishCurrentGroup() { if ( ! self::canLog() ) { return; } self::$logsByGroup[] = [ 'groupId' => self::$groupId, 'label' => self::$groupLabel, 'logs' => self::$groupLogs, 'data' => self::$groupData, ]; self::$groupLogs = []; self::$groupId = null; self::$groupLabel = null; self::$groupData = []; } /** * Add extra metadata which will be auto appended to all next logs. * * @param string $key * @param mixed $value * @return void */ public static function addExtraLogData( $key, $value ) { if ( ! self::canLog() ) { return; } if ( $key === 'element_id' && isset( self::$extraLogData['type'] ) && self::$extraLogData['type'] === 'st-batch' ) { if ( ! in_array( $value, self::$stringBatchIds, true ) ) { self::$stringBatchIds[] = $value; } } self::$extraLogData[ $key ] = $value; } /** * Remove extra metadata key. * * @param string $key */ public static function removeExtraLogData( $key ) { if ( ! self::canLog() ) { return; } unset( self::$extraLogData[ $key ] ); } /** * Add a log entry to the current log group. * * @param string|int $id * @param mixed $data * @param int $logType */ public static function add( $id, $data = [], $logType = self::LOG_TYPE_INFO ) { if ( ! self::canLog() ) { return; } self::$alreadyAddedToLog = []; $logs = [ 'id' => $id, 'data' => self::dataToArray( $data ), 'trace' => self::getTrace(), 'logType' => $logType, ]; $logs = array_merge( $logs, self::$extraLogData ); self::$groupLogs[] = $logs; } /** * Check whether given log entry is an error. * * @return bool */ public static function isErrorLog( $log ) { return (int) $log['logType'] === self::LOG_TYPE_ERROR; } /** * Add an error log entry. Error log entries will be displayed in special way in the UX. * * @param string|int $id * @param mixed $data */ public static function addError( $id, $data = [] ) { self::$hasRequestAnyErrorLog = true; self::add( $id, $data, self::LOG_TYPE_ERROR ); } public static function getLogsCount() { return FsJobLogStorage::getLogsCount(); } /** * Get all stored logs. * * @return array */ public static function getLogs() { return FsJobLogStorage::getRequestLogs(); } /** * Clear all stored logs. * * @return bool */ public static function clearLogs() { return FsJobLogStorage::clearAllLogs(); } /** * Shutdown handler – persists logs to DB. */ public static function shutdown() { if ( ! self::canLog() ) { return; } if ( count( self::$logsByGroup ) === 0 && count( self::$groupLogs ) === 0 ) { return; } // Group did not closed properly because of the error or exception. if ( count( self::$groupLogs ) > 0 ) { self::finishCurrentGroup(); } global $wpdb; $requestParams = self::$requestParams; if ( isset( $requestParams['posts'] ) && is_array( $requestParams['posts'] ) ) { for ( $i = 0; $i < count( $requestParams['posts'] ); $i++ ) { $postId = $requestParams['posts'][ $i ]; $post = get_post( $postId ); if ( is_object( $post ) ) { $requestParams['posts'][ $i ] = $requestParams['posts'][ $i ] . ' (post title=' . $post->post_name . ')'; } } } if ( isset( $requestParams['strings'] ) && is_array( $requestParams['strings'] ) ) { $stringIds = array_map( 'intval', $requestParams['strings'] ); if ( $stringIds ) { $placeholders = implode( ',', array_fill( 0, count( $stringIds ), '%d' ) ); $sql = " SELECT id, value, context, gettext_context FROM {$wpdb->prefix}icl_strings WHERE id IN ($placeholders) "; $results = $wpdb->get_results( $wpdb->prepare( $sql, $stringIds ), OBJECT_K ); foreach ( $requestParams['strings'] as $i => $stringId ) { $id = (int) $stringId; if ( isset( $results[ $id ] ) ) { $value = $results[ $id ]->value; $domain = $results[ $id ]->context; $context = $results[ $id ]->gettext_context; $requestParams['strings'][ $i ] = $id . ' (string value=`' . $value . '`, context=`' . $context . '`, domain=`' . $domain . '`)'; } } } } if ( count( self::$stringBatchIds ) > 0 ) { $stringBatchIds = array_map( 'intval', self::$stringBatchIds ); if ( $stringBatchIds ) { $placeholders = implode( ',', array_fill( 0, count( $stringBatchIds ), '%d' ) ); $sql = " SELECT * FROM {$wpdb->prefix}icl_string_batches WHERE batch_id IN ($placeholders) "; $results = $wpdb->get_results( $wpdb->prepare( $sql, $stringBatchIds ), ARRAY_A ); $stringIdsByBatchId = []; foreach ( $results as $result ) { $batchId = (int) $result['batch_id']; $stringId = (int) $result['string_id']; if ( ! isset( $stringIdsByBatchId[ $batchId ] ) ) { $stringIdsByBatchId[ $batchId ] = []; } $stringIdsByBatchId[ $batchId ][] = $stringId; } // We need this info only for first log within same element_id group sequence. $alreadyAddedBatches = []; foreach ( self::$logsByGroup as &$groupLogs ) { foreach ( $groupLogs['logs'] as &$log ) { if ( ! isset( $log['type'] ) || $log['type'] !== 'st-batch' || ! isset( $log['element_id'] ) ) { unset( $log ); continue; } $batchId = (int) $log['element_id']; $stringIds = $stringIdsByBatchId[ $batchId ] ?? null; if ( in_array( $batchId, $alreadyAddedBatches ) || ! is_array( $stringIds ) ) { unset( $log ); continue; } $log['string_ids_in_batch'] = $stringIds; unset( $log ); $alreadyAddedBatches[] = $batchId; } unset( $groupLogs ); } } } $log = [ 'requestUrl' => self::$requestUrl, 'requestParams' => $requestParams, 'requestDateTime' => self::$requestDateTime, 'hasErrorLogs' => self::$hasRequestAnyErrorLog, 'logsByGroup' => self::$logsByGroup, 'logUid' => uniqid(), ]; FsJobLogStorage::writeRequestLog( $log ); } /** * @param object $object * * @return string */ private static function getObjectId( $object ) { return (string) spl_object_hash( $object ); } const MAX_DEPTH = 10; const MAX_STRING_LENGTH = 1000; const MAX_ARRAY_ITEMS = 1000; /** * Normalize log data to array-safe format. * * @param mixed $data * @param int $depth * * @return array */ private static function dataToArray( $data, $depth = 0 ) { if ( $depth > self::MAX_DEPTH ) { return '[DEPTH_LIMIT]'; } if ( is_callable( $data ) ) { return 'callable'; } if ( is_resource( $data ) ) { return 'resource'; } if ( is_string( $data ) && strlen( $data ) > self::MAX_STRING_LENGTH ) { return substr( $data, 0, self::MAX_STRING_LENGTH ) . '…[TRUNCATED]'; } if ( is_object( $data ) ) { $id = self::getObjectId( $data ); if ( in_array( $id, self::$alreadyAddedToLog ) ) { return '[RECURSION]'; } self::$alreadyAddedToLog[] = $id; $data = get_object_vars( $data ); } $count = 0; if ( is_array( $data ) ) { $result = []; foreach ( $data as $key => $value ) { if ( ++$count > self::MAX_ARRAY_ITEMS ) { $result[ $key ] = '[ARRAY_TRUNCATED]'; break; } if ( $key === 'signedUrl' && is_string( $value ) && strpos( $value, 'signature=' ) !== false ) { $result[ $key ] = preg_replace( '/(?:^|[?&])(signature|token|shared_key)=([^&]+)/i', '$1=[REMOVED]', $value ); continue; } $result[ $key ] = self::dataToArray( $value, $depth + 1 ); } return $result; } return $data; } /** * @param string $url * @return string */ private static function sanitizeSignedUrlWithRegex( $url ) { $res = preg_replace( '/(?:^|[?&])(signature|token|shared_key)=([^&]+)/', '$1=[REMOVED]', $url ); return is_string( $res ) ? $res : '[EMPTY]'; } /** * Parse JSON request body into array. * * @return array */ private static function getUrlParams() { $body = file_get_contents('php://input'); if ($body === false || $body === '') { return []; } $params = json_decode( $body, true ); return is_array($params) ? $params : []; } /** * Get simplified backtrace for logging. * * @return array */ private static function getTrace() { $debug_backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); $traces = []; foreach ( $debug_backtrace as $trace ) { $trace = [ 'file' => $trace['file'] ?? '', 'line' => $trace['line'] ?? '', 'class' => $trace['class'] ?? '', 'type' => $trace['type'] ?? '', 'function' => $trace['function'] ?? '', ]; $traces[] = $trace['file'] . ':' . $trace['line'] . ' ' . $trace['class'] . '/' . $trace['function']; } return $traces; } }