Файловый менеджер - Редактировать - /home/goldin10/public_html/getoutyourbox.com/wp-content/plugins/caldera-forms/classes/forms.php
Error occurred
Назад
<?php /** * Gets form configs in and out of the database * * @package Caldera_Forms * @author Josh Pollock <Josh@JoshPress.net> * @license GPL-2.0+ * @link * @copyright 2016 CalderaWP LLC */ /** * Class Caldera_Forms_Forms * * @since 1.3.4 */ class Caldera_Forms_Forms { /** * Holds registry of forms * * @since 1.3.4 * * @var array */ protected static $registry_cache; /** * Cache key for storing form registry in * * @since 1.3.4 * * @var string */ protected static $registry_cache_key = '_cadera_forms'; /** * Holds simple index of form IDs * * @since 1.3.4 * * @var array */ protected static $index; /** * Holds stored forms * * @since 1.3.4 * * @var array */ protected static $stored_forms; /** * Option key for storing registry in * * @since 1.3.4 * * @var string */ protected static $registry_option_key = '_caldera_forms_forms'; /** * Fields used when converting flat registry to detailed registry * * @since 1.3.4 * * @var array */ protected static $detail_fields = array( 'ID', 'name', 'description', 'success', 'form_ajax', 'hide_form', 'db_support', 'mailer', 'pinned', 'pin_roles', 'hidden', 'form_draft' ); /** * Load a form by ID or name * * @since 1.3.4 * * @param string $id_name ID or name of form. * @param string $type Optional. Default is "primary" the main form. Can also be "revision" for getting a revision. * * @return array|null Form config array if found. If not null. */ public static function get_form( $id_name, $type = 'primary' ){ $id_name = sanitize_text_field( $id_name ); $forms = self::get_forms(); $form = null; if ( self::is_internal_form( $id_name ) ) { if ( isset( $forms[ $id_name ] ) ) { $form = self::get_from_db( $id_name, $type ); if( empty( $form ) ){ $form = get_option( $forms[ $id_name ] ); if( ! empty( $form ) ){ $form = self::maybe_migrate( $form ); } } } else { $forms = self::get_forms( true ); foreach ( $forms as $form_id => $form_maybe ) { if ( trim( strtolower( $id_name ) ) == strtolower( $form_maybe[ 'name' ] ) && empty( $form_maybe[ '_external_form' ] ) ) { $form = self::get_from_db( $form_maybe[ 'ID' ], $type ); if( empty( $form ) ){ $form = get_option( $forms[ $id_name ] ); if( ! empty( $form ) ){ $form = self::maybe_migrate( $form ); } } } } } } if( empty( $form ) ){ $external = true; }else{ $form = self::db_to_return( $form ); } $form = self::filter_form( $id_name, $form ); if( is_array( $form ) && empty( $form['ID'] ) ){ $form['ID'] = $id_name; } if( ! empty( $form ) && ! empty( $external ) ){ $form['_external_form'] = true; } // remove submit on editing if( !empty( $_GET['modal'] ) && $_GET['modal'] == 'view_entry' && !empty( $_GET['group'] ) && $_GET['group'] == 'editentry' ){ if( !empty( $form['fields'] ) ){ foreach( $form['fields'] as $field_id=>$field ){ if( $field['type'] == 'button' && $field['config']['type'] == 'submit' ){ unset( $form['fields'][ $field_id ] ); } } } } return $form; } /** * Get registry of forms * * @since 1.3.4 * * @param bool|false $with_details Optional. If false, the default, just form IDs are returned. If true, basic details of each are returned. * @param bool|false $internal_only Optional. If false, the default, all forms -- in DB and in files system -- are returned -- If true, only those in DB are returned. * * @return array */ public static function get_forms( $with_details = false, $internal_only = false, $orderby = false ){ if( isset( $_GET[ 'cf-cache-clear' ] ) ){ self::clear_cache(); } if( empty( static::$index ) ){ static::$index = static::get_stored_forms(); } if ( false === $internal_only ) { /** * Runs after getting internal forms, use to add forms defined in file system * * @since unknown * * @param array $base_forms Forms saved in DB */ $forms = apply_filters('caldera_forms_get_forms', static::$index); if (!empty($forms) && is_array($forms)) { foreach ($forms as $form_id => $form) { $forms[$form_id] = $form_id; } } } if( $with_details ){ $forms = self::add_details( static::$index ); }else{ $forms = static::$index; } if( $orderby && ! empty( $forms ) ){ $forms = self::order_forms( $forms, $orderby ); } return is_array($forms) ? $forms : []; } /** * Check if a form is a revision * * @since 1.5.4 * * @param array $form Form config * * @return bool */ public static function is_revision( array $form ){ return isset( $form[ 'type' ] ) && 'revision' === $form[ 'type' ]; } /** * Make a reivison the primary form * * @since 1.5.3 * * @param int $revision_id Revision ID * * @return false|int */ public static function restore_revision( $revision_id ){ return Caldera_Forms_DB_Form::get_instance( )->make_revision_primary( $revision_id ); } /** * Get forms stored in DB * * @since 1.3.4 * * @return array */ protected static function get_stored_forms() { if ( empty( self::$stored_forms ) ) { $forms = Caldera_Forms_DB_Form::get_instance()->get_all(); if( ! empty( $forms ) ){ foreach( $forms as $form ){ if ( ! empty( $form[ 'config' ] ) ) { static::$stored_forms[$form['form_id']] = $form['form_id']; } } } } return is_array(self::$stored_forms ) ? self::$stored_forms : []; } /** * Import form * * @since 1.3.4 * * @param array $data Form config * @param bool $trusted Is import data trusted? Default is to trust. * * @return string Form ID */ public static function import_form( $data, $trusted = true ){ $forms = self::get_forms(); if ( isset( $data[ 'ID' ] ) && array_key_exists( $data[ 'ID' ], $forms ) ) { // generate a new ID $data[ 'ID' ] = self::create_unique_form_id(); } if( isset( $data[ 'ID' ] ) ){ $id = $data[ 'ID' ]; }else{ $id = $data[ 'ID' ] = self::create_unique_form_id(); } $data[ 'ID' ] = trim( $id ); unset( $data[ 'db_id' ] ); $importer = new Caldera_Forms_Import_Form($data, $trusted ); $data = $importer->get_prepared_form(); $new_form = self::save_form( $data ); if( is_array( $new_form ) && isset( $new_form[ 'ID' ] ) ){ $new_form = $new_form[ 'ID' ]; } return $new_form; } /** * Apply caldera_forms_get_form filters * * @since 1.5.3 * * @param string $id_name Form ID or name * @param array $config Form config * @param bool $is_revision Optional. Is this a revision of a form? Default is false * * @return mixed|void */ protected static function filter_form( $id_name, $config, $is_revision = false ){ if( ! isset( $config[ 'type' ] ) ){ if( $is_revision ){ $config[ 'type' ] = 'revision'; }else{ $config[ 'type' ] = 'primary'; } } /** * Filter settings of a form or all forms or use to define a form in file * * @since unknown * * @param array $config Form config * @param string $id_name ID or name of form * @param bool $is_revision Is this a revision of a form? @since 1.5.3 */ $config = apply_filters( 'caldera_forms_get_form', $config, $id_name, $is_revision ); /** * Filter settings of a specific form or all forms or use to define a form in file * * @since unknown * * @param array $config Form config * @param string $id_name ID or name of form * @param bool $is_revision Is this a revision of a form? @since 1.5.3 */ $config = apply_filters( 'caldera_forms_get_form-' . $id_name, $config, $id_name, $is_revision ); return $config; } /** * Convert array returned from database to form config array expected by admin and front-end and like everything * * @since 1.5.3 * * @param $form * * @return mixed */ protected static function db_to_return( $form ){ $_form = $form[ 'config' ]; $_form[ 'db_id' ] = $form[ 'id' ]; $_form[ 'type' ] = $form[ 'type' ]; return $_form; } /** * Get the filter value for maximum revisions * * @since 1.5.3 * * @param array $form Form config * * @return int */ protected static function max_revisions( array $form ){ /** * Change the number fo form revisions to store per form * * @since 1.5.3 * * @param int|bool Number of revisions to save. Set to false or 0 to disable form revisions. * @param array $form Form Config */ $max_revisions = apply_filters( 'caldera_forms_max_form_revisions', 15, $form ); return (int) $max_revisions; } /** * Use to switch form arrays to ID strings * * @since 1.3.4 * * @param array|int $val Index to convert * * @return string */ protected function force_string( $val ){ if( is_array( $val ) ){ $val = $val[ 'ID' ]; } return $val; } /** * Add details to form registry * * @since 1.3.4 * * @param array $forms * * @return array */ protected static function add_details( $forms ){ if( empty( $forms ) ){ return []; } if( ! empty( $valid_forms = get_transient( self::$registry_cache_key ) ) ) { return $valid_forms; }else{ $valid_forms = []; } foreach( $forms as $id => $form ){ $_form = self::get_form( $id ); if( empty( $_form ) ){ //if its empty, there is no form. we can't just make up stuff. continue; } $valid_forms[ $id ] = array(); foreach( self::$detail_fields as $key ){ if ( isset( $_form[ $key ] ) ) { $valid_forms[ $id ][ $key ] = $_form[ $key ]; } elseif ( 'name' == $key ) { $valid_forms[ $id ][ $key ] = $id; } elseif ( 'mailer' == $key ) { $valid_forms[ $id ][ $key ] = array( 'on_insert' => 1 ); } elseif ( in_array( $key, array( 'form_ajax', 'check_honey', 'hide_form', 'db_support' ) ) ) { $valid_forms[ $id ][ $key ] = 1; } elseif( 'form_draft' == $key ) { $valid_forms[ $id ][ $key ] = 0; }else { $valid_forms[ $id ][ $key ] = ''; } } } $base_forms = self::get_stored_forms(); foreach ( $valid_forms as $form_id => $form ) { if ( ! isset( $base_forms[ $form_id ] ) ) { $valid_forms[ $form_id ][ '_external_form' ] = true; if ( empty( $forms[ $form_id ][ 'ID' ] ) ) { $valid_forms[ $form_id ][ 'ID' ] = $form_id; } } } if ( ! empty( $valid_forms ) ) { set_transient( self::$registry_cache_key, $valid_forms, HOUR_IN_SECONDS ); } self::$registry_cache = $valid_forms; return self::$registry_cache; } /** * Save a form * * @since 1.3.4 * * @param array $data Form config * @param string $type Optional. Default is "primary" the main form. Can also be "revision" for saving a revision. @since 1.5.3 * * @return string|bool Form ID if updated, false if not */ public static function save_form( $data, $type = 'primary' ){ if(!empty($data['fields'])){ foreach($data['fields'] as &$field){ // option value labels if(!empty($field['config']['option']) && is_array($field['config']['option'])){ foreach($field['config']['option'] as &$option){ if(!isset($option['value'])){ $option['value'] = $option['label']; } } // trim manual calculations } else if(!empty($field["config"]["manual_formula"]) && is_string($field["config"]["manual_formula"])){ $field["config"]["manual_formula"] = Caldera_Forms_Sanitize::finish_trim( Caldera_Forms_Sanitize::sanitize_header( $field["config"]["manual_formula"] ) ); } } } // combine structure pages if ( isset( $data['layout_grid']['structure'] ) && is_array( $data['layout_grid']['structure'] ) ) { $data[ 'layout_grid' ][ 'structure' ] = implode( '#', $data[ 'layout_grid' ][ 'structure' ] ); } // remove fields from conditions if( !empty( $data['conditional_groups']['fields'] ) ){ unset( $data['conditional_groups']['fields'] ); } // remove magics ( yes, not used yet.) if( !empty( $data['conditional_groups']['magic'] ) ){ unset( $data['conditional_groups']['magic'] ); } // sanitize condition values if( !empty( $data['conditional_groups']['conditions'] ) ){ foreach( $data['conditional_groups']['conditions'] as $condition_id => &$condition ){ if( !empty( $condition['group'] ) ){ $condition['name'] = htmlentities( $condition['name'] ); foreach ($condition['group'] as $group_id => &$group) { foreach( $group as $case_id=>&$case ){ $case['value'] = htmlentities( $case['value'] ); } } } } } $data[ '_last_updated' ] = date('r'); $data[ 'version' ] = CFCORE_VER; /** * Filter form config directly before saving * * @since 1.4.0 * * @param array $data Form config */ $data = apply_filters( 'caldera_forms_presave_form', $data ); // add form to registry self::update_registry( $data[ "ID" ] ); self::save_to_db( $data, $type ); self::clear_cache(); /** * Fires after a form is saved * * @since unknown * * @param array $data The form data * @param string $from_id The form ID */ do_action('caldera_forms_save_form', $data, $data['ID']); return $data[ 'ID' ]; } /** * Create a new form * * @since 1.3.4 * * @param array $newform Data for new form * * @return array|mixed|void */ public static function create_form( $newform ){ require_once( CFCORE_PATH . 'classes/admin.php' ); // get form templates (PROBABLY NEED TO MOVE METHOD INTO THIS CLASS) $form_templates = Caldera_Forms_Admin::internal_form_templates(); $original_function_args = $newform; if(!empty($newform['clone'])){ $clone = $newform['clone']; } // load template if any if( !empty( $newform['template'] ) ){ if( isset( $form_templates[ $newform['template'] ] ) && !empty( $form_templates[ $newform['template'] ]['template'] ) ){ $form_template = $form_templates[ $newform['template'] ]['template']; } } $forms = self::get_forms(); if( ! isset( $newform[ 'ID' ] ) || ( ! isset( $newform[ 'ID' ] ) && array_key_exists( $newform[ 'ID' ], $forms ) ) ) { $id = uniqid('CF'); }else{ $id = $newform[ 'ID' ]; } $id = trim( $id ); $defaults = array( "ID" => $id, "name" => '', "description" => '', "success" => __( 'Form has been successfully submitted. Thank you.', 'caldera-forms' ), "form_ajax" => 1, "hide_form" => 1, "check_honey" => 1, "db_support" => 1, 'mailer' => array( 'on_insert' => 1 ) ); $newform = wp_parse_args( $newform, $defaults ); // is template? if( !empty( $form_template ) && is_array( $form_template ) ){ $newform = array_merge( $form_template, $newform ); $newform[ 'ID' ] = $id; } /** * Filter newly created form before saving * * @since unknown * * @param array $newform New form config */ $newform = apply_filters( 'caldera_forms_create_form', $newform); self::update_registry( $id ); if(!empty($clone)){ $clone_form = self::get_form( $clone ); if(!empty($clone_form['ID']) && $clone == $clone_form['ID']){ $newform = array_merge($clone_form, $original_function_args); unset( $newform[ 'db_id' ] ); $newform[ 'ID' ] = $id; } } // add form to db $added = self::save_to_db( $newform, 'primary' ); if( ! $added ){ return false; } /** * Runs after form is created * * @since unkown * * @param array $newform New form config */ do_action('caldera_forms_create_form', $newform); return $newform; } /** * Delete a form * * Will delete all revisions * * @since 1.3.4 * * @param string $id Form ID * * @return bool */ public static function delete_form( $id ){ $forms = self::get_forms(); if( ! isset( $forms[ $id ] ) ){ return false; } unset( $forms[ $id ] ); $deleted = Caldera_Forms_DB_Form::get_instance()->delete_by_form_id( $id ); if ( $deleted ) { self::update_registry( $forms ); return $deleted; } return false; } /** * Update form registry * * @since 1.3.4 * * @param string|array $new Depreacted argument * */ protected static function update_registry( $new ){ self::clear_cache(); $forms = self::get_forms(false,true); /** * Fires after form registry is updated by saving a from * * @since unknown * * @param array $deprecated * @param array $forms Array of forms in registry */ do_action('caldera_forms_save_form_register', array(), $forms ); } /** * Clear the caching performed by this class * * @since 1.3.4 */ protected static function clear_cache(){ self::$index = array(); self::$registry_cache = array(); self::$stored_forms = array(); wp_cache_delete( '_caldera_forms_forms', 'options' ); delete_transient( self::$registry_cache_key ); } /** * Check if a form is stored in DB by name oir ID * * @since 1.3.4 * * @param string $id_name Form name or ID * * @return bool */ public static function is_internal_form( $id_name ){ return ! empty(self::get_stored_forms() ) && in_array( $id_name, self::get_stored_forms() ); } /** * Change a form's state form enabled to disabled or vise vera * * @since 1.3.5 * * @param array $form Form config. * @param bool|true $enable Optional. If true, enable form, if false, disable form. */ public static function form_state( $form, $enable = true ){ if( $enable ){ $form['form_draft'] = 0; }else{ $form['form_draft'] = 1; } if( is_array( self::$registry_cache ) && isset( self::$registry_cache[ $form[ 'ID' ] ] ) ){ delete_transient( self::$registry_cache_key ); self::$registry_cache[ $form[ 'ID' ] ][ 'form_draft' ] = $form['form_draft']; } self::save_form( $form ); } /** * Get all fields of a form * * @since 1.4.4 * * @param array $form The form config * @param bool $in_order Optional. Return in layout order, the default, or in stored order (false). * * @return array|mixed */ public static function get_fields( array $form, $in_order = true ){ if( empty( $form[ 'fields' ] ) ){ return array(); } $fields = $form[ 'fields' ]; if ( $in_order ) { if( isset( $form[ 'layout_grid' ][ 'fields' ] ) ){ $order = array_keys( $form[ 'layout_grid' ][ 'fields' ] ); }else{ $order = array_keys( $fields ); } /** * Change order of fields * * Very useful for reordering fields outputted with {summary} magic tag * * @since 1.4.5 * * @param array $order Order -- array of field IDs * @param array $form Form config */ $order = apply_filters( 'caldera_forms_get_field_order', $order, $form ); $ordered = array(); foreach ( $order as $key ) { if ( isset( $fields[ $key ] ) ) { $ordered[ $key ] = $fields[ $key ]; } } return $ordered; }else{ return $fields; } } /** * Get entry list fields of a form * * @since 1.5.0 * * @param array $form Form config * @param bool $configs Optional. If true, field config arrays are returned. If false, the default, field IDs are returned * * @return array */ public static function entry_list_fields( array $form, $configs = false ){ $fields = self::get_fields( $form ); $entry_list_fields = array(); foreach ( $fields as $field_id => $field ){ if( ! empty( $field[ 'entry_list'])){ if ( $configs ) { $entry_list_fields[ $field_id ] = $field; }else{ $entry_list_fields[] = $field_id; } } } return $entry_list_fields; } /** * Get all fields of a form that provide personally identifying information * * @since 1.6.1 * * @param array $form Form config * @param bool $ids_only Optional. If true, indexed array of field IDs is returned. If false, the default, array of field configs, keyed by field ID is returned. * @return array */ public static function personally_identifying_fields(array $form, $ids_only = false ){ $fields = self::get_fields( $form, false ); $personally_identifying_fields = array(); if( ! empty( $fields ) ){ foreach ( $fields as $field_id => $field ){ if( Caldera_Forms_Field_Util::is_personally_identifying( $field, $form ) ){ $personally_identifying_fields[ $field_id ] = $field; } } } if( $ids_only ){ return array_keys( $personally_identifying_fields ); } return $personally_identifying_fields; } /** * Get all fields of a form that represents the email of someone who's personal information is in the form submssion. * * @since 1.7.0 * * @param array $form Form config * @param bool $ids_only Optional. If true, indexed array of field IDs is returned. If false, the default, array of field configs, keyed by field ID is returned. * @return array */ public static function email_identifying_fields( array $form, $ids_only = false ){ $fields = self::get_fields( $form, false ); $matching_fields = array(); if( ! empty( $fields ) ){ foreach ( $fields as $field_id => $field ){ if( Caldera_Forms_Field_Util::is_email_identifying_field( $field, $form ) ){ $matching_fields[ $field_id ] = $field; } } } if( $ids_only ){ return array_keys( $matching_fields ); } return $matching_fields; } /** * Discover if a form has GDPR/privacy exporter enabled * * @since 1.7.0 * * @param array $form Form config * @return bool */ public static function is_privacy_export_enabled(array $form ) { return ! empty( $form[ 'privacy_exporter_enabled' ] ); } /** * Toggle enabling of GDPR/privacy exporter enabled * * Note, does not save. Use Caldera_Forms_Forms::save_form( Caldera_Forms_Forms::update_privacy_export_enabled( $form, true ) ) ); * * @since 1.7.0 * * @param array $form Form config * @param bool $enabled Optional. To enable or not. Default is true * * @return array */ public static function update_privacy_export_enabled(array $form, $enabled = true ) { $form[ 'privacy_exporter_enabled' ] = (bool) $enabled; return $form; } /** * Get all revisions of a forms * * @since 1.5.3 * * @param string $form_id Form ID * * @return array */ public static function get_revisions( $form_id, $simple = false ){ $forms = Caldera_Forms_DB_Form::get_instance()->get_by_form_id( $form_id, false ); $revisions = array(); if( ! empty( $forms ) ){ foreach ( $forms as $i => $form ){ if( 'revision' === $form[ 'type' ] ){ if( empty( $form ) || ! isset( $form[ 'id' ] ) ){ continue; } if( $simple ){ $revisions[] = $form[ 'id' ]; }else{ $form = self::db_to_return( $form ); $revisions[] = self::filter_form( $form[ 'ID' ], $form, true ); } } } } return $revisions; } /** * Get a revision of a form * * @since 1.5.3 * * @param int $id Revision ID * * @return array */ public static function get_revision( $id ){ $form = Caldera_Forms_DB_Form::get_instance()->get_record( $id ); $form = self::db_to_return( $form ); $form = self::filter_form( $form[ 'ID' ], $form, true ); return $form; } /** * If necessary migrate to database table added in Caldera Forms 1.5.3 * * @since 1.5.3 * * @param array $form Form config * * @return array|bool */ protected static function maybe_migrate( array $form ){ $in_db = Caldera_Forms_DB_Form::get_instance()->get_by_form_id( $form[ 'ID' ] ); if( empty( $in_db ) ){ self::save_to_db( $form, 'primary' ); } return self::get_from_db( $form[ 'ID' ] ); } /** * Save form to database * * @param array $form Form config * @param string $type Optional. What type of form state is "primary" or "revision". Default is primary. * * @return bool|false|int|null */ protected static function save_to_db( array $form, $type = 'primary' ){ if( 'primary' !== $type ){ $max_revisions = self::max_revisions( $form ); if( ! $max_revisions ){ return false; } } $data = array( 'form_id' => $form[ 'ID' ], 'config' => $form, 'type' => $type ); if( isset( $form[ 'db_id' ] ) ){ $data[ 'db_id' ] = $form[ 'db_id' ]; $db_id = Caldera_Forms_DB_Form::get_instance()->update( $data ); self::maybe_delete_old_revisions( $form ); }else{ $db_id = Caldera_Forms_DB_Form::get_instance()->create( $data ); } return $db_id; } /** * Get a form or forms (IE revisions of form) from database * * @since 1.5.3 * * @param string $form_id Form ID * @param bool $primary_only Optional. Default is true, to only return primary form. False to include all revisions. * * @return array|bool */ protected static function get_from_db( $form_id, $primary_only = true ){ return Caldera_Forms_DB_Form::get_instance()->get_by_form_id( $form_id, $primary_only ); } /** * Delete form revisions if there are more than the max revisions * * * @since 1.5.3 * * @param array $form Form config */ protected static function maybe_delete_old_revisions( array $form ){ $revisions = self::get_revisions( $form[ 'ID' ], true); if( ! empty( $revisions ) ) { $max_revisions = self::max_revisions( $form ); if( $max_revisions < count( $revisions ) ){ sort( $revisions ); $deletes = array(); foreach ( $revisions as $key => $id ){ if( $key + 1 < $max_revisions ){ $deletes[] = $id; } } Caldera_Forms_DB_Form::get_instance()->delete( $deletes ); } } } /** * Order forms by a value * * @since 1.5.6 * * @param array $forms Forms to sort. * @param string $by Optional. Default is "name" as of 1.5.6, no other values are allowed. * * @return array|mixed|void */ protected static function order_forms( array $forms, $by = 'name' ){ if( ! in_array( $by, array( 'name', ))){ $by = 'name'; } if( ! empty( $forms ) ){ $values = array_values($forms); if( ! is_array( $values[0] ) ) { $forms = self::get_forms( true ); } } if( empty( $forms ) ){ return $forms; } switch( $by ) { case 'name' : default : $map = array_combine( wp_list_pluck( $forms, $by ), array_keys( $forms ) ); break; } ksort( $map, SORT_ASC ); $final = array(); foreach( $map as $name => $id ){ $final[ $id ] = $forms[ $id ]; } return $final; } /** * Create a unique form ID * * @since 1.6.0 * * @return string */ public static function create_unique_form_id(){ return uniqid( 'CF' ); } }
| ver. 1.4 |
Github
|
.
| PHP 7.2.34 | Генерация страницы: 0.05 |
proxy
|
phpinfo
|
Настройка