From 7bddf39b2671ea3e0945dd187dc9e85062d715c8 Mon Sep 17 00:00:00 2001 From: Gudule Lapointe Date: Thu, 13 Mar 2025 16:04:41 +0100 Subject: [PATCH 01/20] * fix wrong URLs in cron example --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 89f8b29..2ac4d30 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,8 @@ Yes, my first suggestion is not to install this, funny, isn't it? Relying on an ``` [Search] - SearchURL = "http://2do.directory/helpers/query.php?gk=http://yourgrid.org:8002" + SearchURL = "https://2do.directory/helpers/query.php?gk=${Const|BaseURL}:${Const|PublicPort}" + ;SearchURL = "http://2do.directory/helpers/query.php?gk=http://yourgrid.org:8002" [DataSnapshot] index_sims = true @@ -61,7 +62,7 @@ If you want to keep the control, or combine with a wider range of services, here 1. Copy the needed modules, according to your OpenSim version from this bin/ directory to your OpenSim bin/ directory. Copy only the ones you need, as OpenSim loads them all and will trigger errors if they are not properly configured. (e.g. do not copy MoneyServer if you use Gloebit and vice-versa, do not copy any of those if you don't plan to use currency.) 2. Copy the content of this directory on your web server, in a folder named "helpers" (or anything you want, adjust your settings accordingly). It should be reachable as something like -3. Rename includes/config.example.php as includes/config.php and edit with your grid and database settings +3. Copy includes/config.example.php to includes/config.php and edit with your grid and database settings 4. Edit your OpenSim config file for search (adjust your grid name, server url and login uri). Edit MoneyServer.ini or Gloebit.ini according to their instructions. ``` @@ -83,8 +84,8 @@ If you have trust issues with Gloebit (I mean seriously, if your console shows e Use a task scheduler to parse data regularly. It's useless to parse data too often, as they don't change that fast, it's better to keep both search and opensim servers load low and avoid triggering spam reject. With cron, it could be something like this. ``` -0 * * * * curl -s http://2do.directory/helpers/parse.php -30 */2 * * * curl -s http://2do.directory/helpers/eventsparser.php +0 * * * * curl -s http://yourgrid.org/parser.php +30 */2 * * * curl -s http://yourgrid.org/eventsparser.php ``` ## Credits From fb6bea07b7518892cc31f775f151cd9811b0ef09 Mon Sep 17 00:00:00 2001 From: Gudule Lapointe Date: Thu, 13 Mar 2025 16:41:51 +0100 Subject: [PATCH 02/20] * fix duplicate TPLINK* constant declarations --- includes/functions.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index bebe357..9dac4ec 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -517,16 +517,18 @@ function set_helpers_locale( $locale = null, $domain = 'messages' ) { textdomain( $domain ); } -define( 'NULL_KEY', '00000000-0000-0000-0000-000000000000' ); -define( 'TPLINK_LOCAL', 1 ); // seconlife://Region/x/y/z -define( 'TPLINK_HG', 2 ); // seconlife://yourgrid.org:8002 Region/x/y/z -define( 'TPLINK_V3HG', 4 ); // the overcomplicated stuff! -define( 'TPLINK_HOP', 8 ); // hop://yourgrid.org:8002:Region/x/y/z -define( 'TPLINK_TXT', 16 ); // yourgrid.org:8002:Region/x/y/z -define( 'TPLINK_APPTP', 32 ); // secondlife:///app/teleport/yourgrid.org:8002:Region/x/y/z -define( 'TPLINK_MAP', 64 ); // secondlife:///app/map/yourgrid.org:8002:Region/x/y/z -define( 'TPLINK', pow( 2, 8 ) - 1 ); // all formats -define( 'TPLINK_DEFAULT', TPLINK_HOP ); // default +if( ! defined( 'NULL_KEY') ) define( 'NULL_KEY', '00000000-0000-0000-0000-000000000000' ); +if( ! defined( 'TPLINK_LOCAL') ) { + define( 'TPLINK_LOCAL', 1 ); // seconlife://Region/x/y/z + define( 'TPLINK_HG', 2 ); // seconlife://yourgrid.org:8002 Region/x/y/z + define( 'TPLINK_V3HG', 4 ); // the overcomplicated stuff! + define( 'TPLINK_HOP', 8 ); // hop://yourgrid.org:8002:Region/x/y/z + define( 'TPLINK_TXT', 16 ); // yourgrid.org:8002:Region/x/y/z + define( 'TPLINK_APPTP', 32 ); // secondlife:///app/teleport/yourgrid.org:8002:Region/x/y/z + define( 'TPLINK_MAP', 64 ); // secondlife:///app/map/yourgrid.org:8002:Region/x/y/z + define( 'TPLINK', pow( 2, 8 ) - 1 ); // all formats + define( 'TPLINK_DEFAULT', TPLINK_HOP ); // default +} define( 'HELPERS_LOCALE_DIR', dirname( __DIR__ ) . '/languages' ); From 1a8478d8c2808d7b2c3abc24ab2ab7c5d84c0030 Mon Sep 17 00:00:00 2001 From: Gudule Lapointe Date: Thu, 13 Mar 2025 16:47:49 +0100 Subject: [PATCH 03/20] * fix apache xml error (expat reports error code 3) for empty search results --- query.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/query.php b/query.php index 7b1da1e..e6f8513 100644 --- a/query.php +++ b/query.php @@ -114,8 +114,8 @@ function dir_places_query( $method_name, $params, $app_data ) { } if ( empty( $data ) ) { - // osXmlDie( 'Nothing found' ); - osXmlResponse( true, 'No results', $data ); + osXmlResponse( false, 'No results', $data ); + // osXmlResponse( false ); } else { osXmlResponse( true, '', $data ); } From dec05dfb6550eeb611693c9439e268b7cb1235da Mon Sep 17 00:00:00 2001 From: Gudule Lapointe Date: Fri, 14 Mar 2025 03:42:53 +0100 Subject: [PATCH 04/20] * fix error notification displayed 3 times when no place search results --- includes/functions.php | 46 ++++++++++++++++-- query.php | 25 ++++++++++ tests/search | 103 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 4 deletions(-) create mode 100755 tests/search diff --git a/includes/functions.php b/includes/functions.php index 9dac4ec..4e844e9 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -378,19 +378,41 @@ function oxXmlRequest( $gatekeeper, $method, $request ) { } function osXmlResponse( $success = true, $errorMessage = false, $data = false ) { + global $request_key; + + // Data given, output as xmlrpc if ( is_array( $data ) ) { + if( ! $success && ! empty( $errorMessage ) ) { + # Avoid duplicate error messages + # TODO: improve to make sure we don't cache error messages for different requests + # (currently $request_key only identifies search arguments, client IP and gateway url, + # but client IP is the simulator, not the actual user, so in theory, the key could + # be identical for two simultaneous requests by different users in the same region, + # although this is unlikely to happen) + + $tmp_dir = get_writable_tmp_dir(); + $cache = $tmp_dir . '/cache-' . $request_key; + # Check if file exists and is not older than 5 seconds + if ( file_exists( $cache ) && ( time() - filemtime( $cache ) < 1.5 ) ) { + $errorMessage = ''; + } else { + file_put_contents( $cache, $errorMessage ); + } + } + $array = array( 'success' => $success, 'errorMessage' => $errorMessage, + 'data' => $data, ); - if ( ! empty( $data ) ) { - $array['data'] = $data; - } - array_filter( $array ); + $response_xml = xmlrpc_encode( $array ); + echo $response_xml; return; } + + // No data given, output simple boolean or error message, no change here if ( $success ) { $answer = new SimpleXMLElement( 'true' ); } else { @@ -517,6 +539,22 @@ function set_helpers_locale( $locale = null, $domain = 'messages' ) { textdomain( $domain ); } +function get_writable_tmp_dir() { + if(isset($_GLOBALS['tmp_dir'])) { + return $_GLOBALS['tmp_dir']; + } + $dirs = array( sys_get_temp_dir(), ini_get('upload_tmp_dir'), '/tmp', '/var/tmp', '/usr/tmp', '.' ); + foreach ( $dirs as $dir ) { + if ( @is_writable( $dir ) ) { + $_GLOBALS['tmp_dir'] = $dir; + return $dir; + } + } + error_log( __FILE__ . ':' . __LINE__ . ' ERROR - could not find a writable temporary directory, check web server and PHP config' ); + return false; + // return '/tmp'; +} + if( ! defined( 'NULL_KEY') ) define( 'NULL_KEY', '00000000-0000-0000-0000-000000000000' ); if( ! defined( 'TPLINK_LOCAL') ) { define( 'TPLINK_LOCAL', 1 ); // seconlife://Region/x/y/z diff --git a/query.php b/query.php index e6f8513..61e78c8 100644 --- a/query.php +++ b/query.php @@ -629,10 +629,35 @@ function classifieds_info_query( $method_name, $params, $app_data ) { osXmlResponse( true, '', $data ); } +$tmp_dir = get_writable_tmp_dir(); + // // Process the request // $request_xml = file_get_contents( 'php://input' ); + +// error_log( __FILE__ . ':' . __LINE__ . ' request xml ' . $request_xml ); + +if( ! $request_xml ) { + // Wrong request, probably called directly instead of by sending xml request, + // or no data sent, issue proper http response and error message, then die + header( 'HTTP/1.1 400 Bad Request' ); + header( 'Content-Type: text/plain' ); + die( '400 Bad Request' . PHP_EOL ); +} + +$request_array = array( + $_SERVER['REMOTE_ADDR'], + $_SERVER['REQUEST_URI'], + $request_xml +); +$request_key = md5( serialize( $request_array ) ); + +// error_log( __FILE__ . ':' . __LINE__ +// . PHP_EOL . 'request xml ' . $request_xml +// . PHP_EOL . 'client ip ' . $_SERVER['REMOTE_ADDR'] +// . PHP_EOL . 'request uri ' . $_SERVER['REQUEST_URI'] ); + xmlrpc_server_call_method( $xmlrpc_server, $request_xml, '' ); xmlrpc_server_destroy( $xmlrpc_server ); diff --git a/tests/search b/tests/search new file mode 100755 index 0000000..0a764c7 --- /dev/null +++ b/tests/search @@ -0,0 +1,103 @@ +#!/bin/bash + +SearchURL="https://2do.directory/helpers/query.php" +GatekeeperURL="http://speculoos.world:8002" + +PGM=$(basename $0) +searchstring="$@" + + +echo "Searching for: $searchstring" > /dev/stderr + +url=$SearchURL?gk=$GatekeeperURL +echo "SearchURL: $url" > /dev/stderr + +xml=" + + dir_places_query + + + + + + category + -1 + + + text + $searchstring + + + flags + 117441536 + + + query_start + 0 + + + sim_name + + + + + + +" + +# Sample responses: +# Search with no results: +# +# +# +# success +# +# 0 +# +# +# +# errorMessage +# +# No results +# +# +# +# +# Search with results: +# +# +# +# success +# +# 1 +# +# +# +# errorMessage +# +# +# +# +# +# data +# (etc) +# + + +# Send POST request with the XML content so that query.php receives it via php://input +response=$(curl -s -X POST -H "Content-Type: text/xml" --data "$xml" "$url") + +# Remove extra spaces between tags without eliminating newlines entirely +response_oneline="$(echo "$response" | sed ':a;N;$!ba;s/>\s\+success<\/name>.*\([01]\)<\/boolean>.*/\1/p') +errorMessage=$(echo "$response_oneline" | sed -n 's/.*errorMessage<\/name>.*\(.*\)<\/string>.*/\1/p') + +# If errorMessage is "False", change it to empty string. +if [ "$errorMessage" = "False" ]; then + errorMessage="" +fi + +echo "$PGM: Success=$success, Error Message='$errorMessage'" + From 5563ce3b1ea677995b560e9e24df102d4f5eec64 Mon Sep 17 00:00:00 2001 From: Gudule Lapointe Date: Sat, 15 Mar 2025 12:46:33 +0100 Subject: [PATCH 05/20] merge templates/ and classes/ from 3.x --- classes/class-exception.php | 60 ++++ classes/class-form.php | 486 ++++++++++++++++++++++++++ classes/class-grid.php | 290 +++++++++++++++ classes/class-ini.php | 94 +++++ classes/class-locale.php | 97 +++++ classes/class-page.php | 51 +++ classes/init.php | 680 ++++++++++++++++++++++++++++++++++++ css/bootstrap.css | 0 css/form.css | 5 + js/form.js | 0 templates/bootstrap.css | 41 +++ templates/bootstrap.js | 17 + templates/template-page.php | 74 ++++ templates/templates.php | 130 +++++++ 14 files changed, 2025 insertions(+) create mode 100644 classes/class-exception.php create mode 100644 classes/class-form.php create mode 100644 classes/class-grid.php create mode 100644 classes/class-ini.php create mode 100644 classes/class-locale.php create mode 100644 classes/class-page.php create mode 100644 classes/init.php create mode 100644 css/bootstrap.css create mode 100644 css/form.css create mode 100644 js/form.js create mode 100644 templates/bootstrap.css create mode 100644 templates/bootstrap.js create mode 100644 templates/template-page.php create mode 100644 templates/templates.php diff --git a/classes/class-exception.php b/classes/class-exception.php new file mode 100644 index 0000000..195adb1 --- /dev/null +++ b/classes/class-exception.php @@ -0,0 +1,60 @@ +__toString() ); + } + + // Disabled custom string representation of the exception, it is worst than the default one. + // public function __toString() { + // $prefix = ''; + // // return __CLASS__ . ": [{$this->code}]: {$this->message}\n"; + // $message = strip_tags( $this->message ); + // $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + // if( ! empty( $trace[1] ) ) { + // $class = $trace[1]['class'] ?? ''; + // $function = $trace[1]['function'] ?? ''; + // } + // if( ! empty( trim ( $class . $function ) ) ) { + // $prefix .= '(' . ( empty( $class ) ? '' : $class . '::' ) . $function . ') '; + // } + // return $prefix . $message; + // } +} + +/** + * This is temporary, it's misleading to replace Errors with Exceptions,but it's a way + * to make sure I can replace all new Error() calls with new OpenSim_Exception() calls. + * + * I pledge to check soon, but I spend too much time finetuning the Error catchers, so I + * want to keep a way to switch back fast if needed. + * + * TODO: + * - Test again every use case where OpenSim_Error is used, make sure interrupts happen as expected. + * - Replace all OpenSim_Error calls with OpenSim_Exception. + * - Remove this class. + */ +class OpenSim_Error extends OpenSim_Exception { + + public function __construct( $message, $code = 0, Exception $previous = null ) { + parent::__construct( $message, $code, $previous ); + // error_log( $this->__toString() ); + } +} diff --git a/classes/class-form.php b/classes/class-form.php new file mode 100644 index 0000000..e8a0d6a --- /dev/null +++ b/classes/class-form.php @@ -0,0 +1,486 @@ + unique id + * 'html' => html code + * 'callback' => callback to call to process form + * ) + * - render_form() return the form html code + * - get_values() return an array of field_id => value pairs + * - get_fields() return the array of defined fields + * - process() process the form callback + * + * @package magicoli/opensim-helpers +**/ + +require_once( dirname(__DIR__) . '/classes/init.php' ); + +class OpenSim_Form { + private $form_id; + private $fields = array(); + private $callback; + private static $forms = array(); + private $errors; + private $html; + private $completed; + private $multistep; + public $tasks; + + public function __construct($args = array(), $step = 0) { + if( is_string( $args )) { + // If only a string is passed, consider it as form_id for a pending form + $args = array( 'form_id' => $args ); + } + if (!is_array($args)) { + error_log(__METHOD__ . ' invalid argument type ' . gettype($args)); + throw new InvalidArgumentException('Invalid argument type: ' . gettype($args)); + } + + $args = OpenSim::parse_args($args, array( + 'form_id' => uniqid('form-', true), + 'fields' => array(), + 'callback' => null, + 'multistep' => false, + )); + $this->form_id = $args['form_id']; + $this->multistep = $args['multistep']; + $this->steps = $args['steps'] ?? false; + $this->callback = $args['callback']; + $this->add_fields($args['fields']); + + $this->completed = $_SESSION[$this->form_id]['completed'] ?? $this->completed; + self::$forms[$this->form_id] = $this; + // $this->refresh_steps(); + } + + /** + * Static factory method to register an instance of OpenSim_Form. + * Handles exceptions internally to avoid requiring try-catch blocks during instantiation. + * + * @param array $args Arguments for form initialization. + * @param int $step Optional step parameter. + * @return OpenSim_Form|false Returns an instance of OpenSim_Form on success, or false on failure. + */ + public static function register($args = array(), $step = 0) { + try { + return new self($args, $step); + } catch (InvalidArgumentException $e) { + error_log($e->getMessage()); + OpenSim::notify_error($e->getMessage() ); + return false; + } + } + + public function add_steps($steps) { + if( empty( $steps )) { + return false; + } + $this->steps = OpenSim::parse_args( $steps, $this->steps ); + } + + public function add_fields( $fields) { + if( empty( $fields )) { + return; + } + $this->fields = OpenSim::parse_args( $fields, $this->fields ); + $this->get_next_step(); + } + + public function task_error( $field_id, $message, $type = 'warning' ) { + $this->errors[$field_id] = array( + 'message' => $message ?? 'Error', + 'type' => empty( $type ) ? 'warning' : $type, + ); + } + + public function render_form() { + if( ! empty( $this->html )) { + return $this->html; + } + if( $this->multistep ) { + $this->refresh_steps(); + $fields = $this->get_step_fields() ?? array(); + if( ! is_array( $fields )) { + error_log( __METHOD__ . ' wrong field format ' ); + return false; + } + } else { + $fields = $this->fields; + } + + $form_id = $this->form_id; + + // Update Reset button to remain a submit button and bypass validation + $reset_button = ( empty($_SESSION[$form_id])) ? '' : sprintf( + '', + _( 'Reset Form' ) + ); + + if( empty( $fields ) && empty( $reset_button ) ) { + error_log( __METHOD__ . ' called with empty fields' ); + return false; + } + + $html = ''; + $fields = empty($fields) ? array() : $fields; + foreach ( $fields as $field => $data ) { + $add_class = ''; + $add_attrs = ''; + if( ! empty( $this->errors[$field] ) ) { + $field_error = $this->errors[$field]; + $data['help'] = OpenSim::error_html( $field_error, 'warning' ) . ( $data['help'] ?? '' ); + if( $field_error['type'] == 'danger' ) { + $add_class .= ' is-invalid'; + } + } + if( $data['type'] == 'plaintext' ) { + $data['type'] = 'text'; + $add_class .= ' form-control-plaintext'; + } + if( isset( $data['readonly'] ) && $data['readonly'] ) { + $add_class .= ' text-muted'; + $add_attrs .= ' readonly'; + } + $add_attrs .= isset( $data['disabled'] ) && $data['disabled'] ? ' disabled' : ''; + $add_attrs .= isset( $data['required'] ) && $data['required'] ? ' required' : ''; + // $placeholder = isset( $data['placeholder'] ) ? $data['placeholder'] : ''; + + $html .= sprintf( + '
+ + + %s +
', + $field, + $data['label'], + $data['type'], + $field, + $add_attrs . $add_class, + $_POST[$field] ?? $data['value'] ?? $data['default'] ?? '', + $data['placeholder'] ?? '', + $add_attrs, + $data['help'] ?? '' + ); + } + + $submit = empty( $html ) ? '' : sprintf( + '', + _('Submit') + ); + + $buttons = sprintf( + '' + . '' + . '
%s
', + $this->form_id, + $this->next_step_key ?? '', + $reset_button . $submit + ); + $html = sprintf( + '
%s
', + $this->form_id, + $_SERVER['PHP_SELF'], + $html . $buttons + ); + + OpenSim::enqueue_script( 'form', 'js/form.js' ); + OpenSim::enqueue_style( 'form', 'css/form.css' ); + + $this->html = $html; + return $html; + } + + public function process() { + if( empty( $_POST )) { + // Only init values if form is not submitted + return $this->get_values(); + } + if (is_callable($this->callback)) { + return call_user_func($this->callback, $this->get_values()); + } else { + if (is_array($this->callback)) { + $callback_name = get_class($this->callback[0]) . '::' . $this->callback[1]; + } else { + $callback_name = $this->callback; + } + error_log( $callback_name . ' is not callable from ' . __METHOD__ ); + return false; + } + } + + /** + * Get values from fields definition and post. + * + * TODO: make sure values are not replaced with post values before this step, + * although it doesn't hurt with the current usage, it might be useful to + * compare old and new value in the process() method called later. + */ + public function get_values() { + $form_id = $this->form_id; + $values = array(); + if( $this->multistep ) { + $fields = $this->get_step_fields() ?? array(); + if( ! is_array( $fields )) { + error_log( __METHOD__ . ' wrong field format ' ); + return false; + } + } else { + $fields = $this->fields; + } + // error_log( 'Fields: ' . print_r( $fields, true ) ); + foreach( $fields as $key => $field ) { + $values[$key] = $_POST[$key] ?? $_SESSION[$form_id][$key] ?? $field['value'] ?? null; + } + return $values; + } + + private function get_step_fields() { + if( ! isset( $this->next_step_key ) || ! isset( $this->fields[$this->next_step_key] ) ) { + return array(); + } + return $this->fields[$this->next_step_key]; + } + + // Get defined fields + public function get_fields() { + return $this->fields; + } + + public function get_form( $form_id ) { + if( empty( $form_id )) { + return false; + } + return isset( self::$forms[$form_id] ) ? self::$forms[$form_id] : false; + } + + public function get_forms() { + return self::$forms ?? false; + } + + public function get_next_step() { + if( empty( $this->steps )) { + return false; + } + $steps = $this->steps; + $current_step = array_search($this->completed, array_keys($steps)); + if( empty( $this->completed ) ) { + $next_step_key = key($steps); + $next_step_label = $steps[$next_step_key]; + } else { + $next_step_key = array_keys($steps)[$current_step + 1] ?? null; + if( empty($steps[$next_step_key])) { + $next_step_key='completed'; + $next_step_label = _('Completed'); + } else { + $next_step_label = $steps[$next_step_key] ?? null; + } + } + // $this->next_step = $next_step_label; + $this->next_step_key = $next_step_key; + $this->tasks = $this->steps[$next_step_key]['tasks'] ?? false; + return $this->next_step_key; + } + /** + * Use the value of $this->complete as last completed step, get the next step and + * build a navigation html. + */ + private function refresh_steps() { + if( empty( $this->steps )) { + return false; + } + + $steps = $this->steps; + if( ! empty($_POST['form_id']) ) { + $form_id = $_POST['form_id']; + $form = self::$forms[$form_id]; + if( $form ) { + $form->process(); + } else { + error_log( __METHOD__ . ' Form ' . $form_id . ' is not registered' ); + return false; + } + } + $next_step_key = $this->get_next_step(); + + // Set progression table + $progress = array(); + $status = 'completed'; + foreach( $steps as $key => $step ) { + if( $key == $next_step_key ) { + $progress[$key] = 'active'; + $status = ''; + } else { + $progress[$key] = $status; + } + } + $this->progression = $progress; + } + + /** + * Build HTML progress bar with bootstrap classes + */ + public function render_progress() { + $this->refresh_steps(); + + if( empty( $this->steps )) { + return false; + } + + $steps = $this->steps; + $progress = $this->progression; + + $status = 'completed'; + $html = ''; + return $html; + } + + public function validate_file( $field_id, $file_path, $strict = false ) { + if( empty( $file_path ) ) { + if( $strict ) { + $message = sprintf( _('File %s is required'), $field_id ); + $this->task_error( $field_id, $message, 'danger' ); + return false; + } + // if not strict, allow it to be empty + return true; + } + if( ! file_exists( $file_path )) { + $message = sprintf( _('File %s not found'), $file_path ); + $this->task_error( $field_id, $message, 'danger' ); + return false; + } + return true; + } + + /** + * Make sure the file is a valid .ini file + */ + public function is_valid_ini_file( $field_id, $file_path, $strict = false ) { + if( ! $this->validate_file( $field_id, $file_path, true )) { + $this->task_error( $field_id, _('File not found'), 'danger' ); + return false; + } + + // If strict, use parse_ini_file + if( $strict ) { + $ini = parse_ini_file( $file_path ); + if( empty( $ini )) { + $message = sprintf( _('File %s does not comply with .ini standards.'), $file_path ); + $this->task_error( $field_id, $message, 'danger' ); + // throw new Exception( $message ); + return false; + } + return true; + } + + // If not strict, a light check is enough + // + // OpenSim uses some non-standard formatting that are not supported by parse_ini_file. + // Ignore comments and empty lines, then make sure the remaining contains + // only key = value pairs or [sections] + $ini = file_get_contents( $file_path ); + $lines = explode( "\n", $ini ); + $valid = true; + + // Filter out comments and empty lines + $lines = array_filter( array_map( 'trim', $lines ), function( $line ) { + return ! empty( $line ) && ! preg_match( '/^\s*;/', $line ); + }); + + // Filter out valid lines, leaving only invalid ones + $valid = array_filter( array_map( function( $line ) { + return preg_match( '/^\[.*\]$|.*=.*$/', $line ) ? false : $line; + }, $lines )); + + if( ! empty( $valid )) { + $message = sprintf( _('File %s is not a valid .ini file'), $file_path ); + error_log( $message . ', found invalid lines: ' . print_r( $valid, true ) ); + $this->task_error( $field_id, $message, 'danger' ); + return false; + } + return true; + } + + /** + * Make sure the file is a valid Robust.ini file + */ + public function is_robust_ini_file( $field_id, $file_path ) { + if( ! $this->is_valid_ini_file( $field_id, $file_path )) { + throw new Exception( _('Not a valid ini file') ); + // return false; + } + + $required_sections = array( + 'DatabaseService', + 'GridInfoService', + 'LoginService', + ); + + // Check if all required sections are present, with array_map (without parse_ini_file) + $ini = file_get_contents( $file_path ); + $lines = explode( "\n", $ini ); + $sections = array_map( function( $line ) { + if( preg_match( '/^\[(.*)\]\s*$/', $line, $matches )) { + return $matches[1]; + } + return false; + }, $lines ); + + $missing = array_diff( $required_sections, $sections ); + if( ! empty( $missing )) { + $message = sprintf( _('Not a valid Robust config file.'), $file_path, implode( ', ', $missing )); + $this->task_error( $field_id, $message, 'danger' ); + $message = sprintf( _('%s is missing required sections: %s'), $file_path, '
  • ' . implode( '
  • ', $missing ) . '
' ); + throw new Exception( $message ); + } + + return true; + } + + public function complete( $step ) { + $this->completed = $step; + $_SESSION[$this->form_id]['completed'] = $step; + $this->refresh_steps(); + } +} diff --git a/classes/class-grid.php b/classes/class-grid.php new file mode 100644 index 0000000..e180afe --- /dev/null +++ b/classes/class-grid.php @@ -0,0 +1,290 @@ +constants(); + // $this->grid_stats = $this->get_grid_stats(); + // $this->grid_info = $this->get_grid_info(); + // $this->grid_info_card = $this->get_grid_info_card(); + // $this->grid_stats_card = $this->get_grid_stats_card(); + } + + public function constants() { + self::$labels = array( + 'status' => _('Status'), + 'members' => _('Members'), + 'active_members' => _('Active members (30 days)'), + 'members_in_world' => _('Members in world'), + 'active_users' => _('Active users (30 days)'), + 'total_users' => _('Total users in world'), + 'regions' => _('Regions'), + 'total_area' => _('Total area'), + ); + } + + public static function get_grid_info( $grid_uri = false, $args = array() ) { + $info = array(); + $HomeURI = OpenSim::get_option( 'Hypergrid.HomeURI' ); + if( ! $grid_uri || $grid_uri === $HomeURI ) { + $is_local_grid = true; + // Default, get login_uri from config, query grid for live grid_info + $grid_uri = OpenSim::get_option( 'Hypergrid.HomeURI' ); + if( empty( $grid_uri ) ) { + return false; + } + } else { + $is_local_grid = false; + // External grid lookup, not yet implemented + $info = array( + 'Grid Name' => 'External Grid, not implemented.', + ); + return false; + } + + // Fetch live info from grid using $login_uri/get_grid_info and parse xml result in array + // Example xml result: + $xml_url = $grid_uri . '/get_grid_info'; + try { + $xml = simplexml_load_file( $xml_url ); + } catch( Exception $e ) { + $xml = false; + } + if( ! $xml ) { + $info = array( + 'online' => false, + 'login' => $grid_uri, + ); + } else { + $info['online'] = true; + try { + $array = (array) $xml; + if( ! $array ) { + throw new Exception( 'Error parsing grid info.' ); + } + $info = array_merge( $info, $array ); + } catch( Exception $e ) { + return $e; + } + } + + if( $is_local_grid ) { + $config_info = OpenSim::get_option( 'GridInfoService' ); + if( is_array( $config_info ) ) { + $info = array_merge( $config_info, $info ); + } + } + + return $info; + } + + public static function array_to_card( $id, $info, $args = array() ) { + if( empty( $info ) ) { + return; + } + $hide_first = ''; + if( ! empty( $args['title'] ) && is_string( $args['title'] ) ) { + $title = $args['title']; + } else { + $title = array_values( $info )[0]; + if( is_numeric( array_keys( $info )[0] ) || ( isset($args['hide_first']) && $args['hide_first'] === true ) ) { + $hide_first = 'hidden d-none'; + } + } + + $collapse_head = ''; + $collapse_class = ''; + $collapse_data = ''; + $html = sprintf( + '
+
+
+ +
+
    ', + $id, + $title, + $collapse_head, + $collapse_class, + $collapse_data + ); + + foreach( $info as $key => $value ) { + $html .= sprintf( + ' +
  • + %3$s %4$s +
  • ', + $id, + $hide_first, + is_numeric( $key ) ? '' : $key . ':', + $value + ); + $hide_first = ''; + $class=""; + } + $html .= '
'; + $html .= '
'; + $html .= '
'; + return $html; + } + + /** + * Get grid information as a card + */ + public static function grid_info_card( $grid_uri = false, $args = array() ) { + $grid_info = self::get_grid_info( $grid_uri, $args ); + if( ! $grid_info || OpenSim::is_error( $grid_info ) ) { + return false; + } + + $info = array( + _('Grid Name') => $grid_info['gridname'], + _('Login URI') => OpenSim::hop( $grid_info['login'] ), + ); + + $title = false; + if( ! empty( $args['title'])) { + $title = $args['title'] === true ? _( 'Grid Information' ) : $args['title']; + } else { + $title = _( 'Grid Information' ); + } + return self::array_to_card( 'grid-info', $info, array( + 'title' => $title, + ) ); + } + + public static function grid_stats_card( $args = null ) { + $grid_stats = self::get_grid_stats( $args ); + + if( ! $grid_stats || OpenSim::is_error( $grid_stats ) ) { + return false; + } + + $title = false; + if( ! empty( $args['title'])) { + $title = $args['title'] === true ? _( 'Grid Information' ) : $args['title']; + } else { + $title = _( 'Grid Status' ); + } + + return self::array_to_card( 'grid-status', $grid_stats, array( + 'title' => $title, + ) ); + } + + private static function array_to_xml($array, $xml) { + foreach($array as $key => $value) { + if(is_array($value)) { + $subnode = $xml->addChild($key); + self::array_to_xml($value, $subnode); + } else { + $xml->addChild($key, htmlspecialchars($value)); + } + } + } + + public static function get_grid_stats( $args = null ) { + $grid_info = self::get_grid_info(); + if( empty( $grid_info ) ) { + return false; + } + + $grid_uri = $grid_info['login']; + + $args = array_merge(array( + 'output' => 'array', + 'title' => true, + )); + + $stats = array( + 'status' => $grid_info['online'] ? _('Online') : _('Offline'), + ); + + $robust_db = OpenSim::$robust_db; + if ( ! $robust_db || OpenSim::is_error($robust_db) ) { + $stats['error'] = _('Database not connected.'); + } else { + $lastmonth = time() - 30 * 86400; + $gridonline = $grid_info['online'] ? _('Yes') : _('No'); + + $filter = ''; + // if ( get_option( 'w4os_exclude_models' ) ) { + // $filter .= "u.FirstName != '" . get_option( 'w4os_model_firstname' ) . "' + // AND u.LastName != '" . get_option( 'w4os_model_lastname' ) . "'"; + // } + // if ( get_option( 'w4os_exclude_nomail' ) ) { + // $filter .= " AND u.Email != ''"; + // } + if ( ! empty( $filter ) ) { + $filter = "$filter AND "; + } + + $stats = array( + 'status' => $grid_info['online'] ? _('Online') : _('Offline'), + 'members' => $robust_db->get_var( "SELECT COUNT(*) + FROM UserAccounts as u WHERE $filter active=1" + ), + 'active_members' => $robust_db->get_var( "SELECT COUNT(*) + FROM GridUser as g, UserAccounts as u + WHERE $filter PrincipalID = UserID AND g.Login > :lastmonth", + array( + 'lastmonth' => $lastmonth, + ) + ), + 'members_in_world' => $robust_db->get_var( "SELECT COUNT(*) + FROM Presence AS p, UserAccounts AS u + WHERE $filter RegionID != '00000000-0000-0000-0000-000000000000' + AND p.UserID = u.PrincipalID;" + ), + 'active_users' => $robust_db->get_var( "SELECT COUNT(*) + FROM GridUser WHERE Login > :lastmonth", + array( + 'lastmonth' => $lastmonth, + ) + ), + 'total_users' => $robust_db->get_var( "SELECT COUNT(*) + FROM Presence WHERE RegionID != '00000000-0000-0000-0000-000000000000';" + ), + 'regions' => $robust_db->get_var( "SELECT COUNT(*) + FROM regions" + ), + 'total_area' => $robust_db->get_var( "SELECT round(sum(sizex * sizey / 1000000),2) + FROM regions" + ) . ' km²', + ); + + // Replace keys with values of self::$labels + $labels = self::$labels; + $labels = array_intersect_key( self::$labels, $stats ); + $stats = array_combine( $labels, $stats ); + + } + + switch( $args['output'] ) { + case 'xml': + $xml = new SimpleXMLElement(''); + self::array_to_xml($stats, $xml); + return $xml->asXML(); + case 'array': + return $stats; + default: + return $stats; + } + + return $stats; + } +} diff --git a/classes/class-ini.php b/classes/class-ini.php new file mode 100644 index 0000000..48e700c --- /dev/null +++ b/classes/class-ini.php @@ -0,0 +1,94 @@ +notify_error( $e, 'Error reading file' ); + } + $content = file_get_contents( $args ); + $this->raw_ini_array = explode( "\n", $content ); + } elseif( is_string( $args ) ) { + $this->raw_ini_array = explode( "\n", $args ); + } elseif( is_array( $args ) ) { + $this->raw_ini_array = $args; + } else { + throw new OpenSim_Error( __CLASS__ .' accepts only string, array or file path value' ); + } + + $this->sanitize_and_parse( $this->raw_ini_array ); + } + + public function get_config() { + return $this->config; + } + + public function get_ini() { + return $this->ini; + } + + /** + * Sanitize an INI string. Make sure each value is encosed in quotes. + * Convert constants to their value. + */ + private function sanitize_and_parse() { + $this->ini = ''; + $this->config = array(); + + $lines = $this->raw_ini_array; + + $section = '_'; + foreach ( $lines as $line ) { + $line = trim( $line ); + if ( empty( $line ) || preg_match('/^\s*;/', $line ) ) { + $this->ini .= "$line\n"; + continue; + } + $parts = explode( '=', $line ); + if( preg_match( '/^\[[a-zA-Z]+\]$/' , $line)) { + $section = trim( $line, '[]' ); + $this->ini .= "$line\n"; + continue; + } + if ( count( $parts ) < 2 ) { + $this->ini .= "$line\n"; + continue; + } + // use first part as key, the rest as value + $key = trim( array_shift( $parts ) ); + $value = trim( implode( '=', $parts ), '\" '); + + // Replace constants with their value for $config array, leave untouched for $ini. + $config_value = $value; + while ( preg_match( '/\${Const\|([a-zA-Z]+)}/', $config_value, $matches ) ) { + $const = $matches[1]; + $config_value = str_replace( '${Const|' . $const . '}', $this->config['Const'][$const], $config_value ); + } + $this->config[$section][$key] = $config_value; + + if( is_numeric( $value ) || in_array( $value, array( "true", "false" ) ) ) { + $this->ini .= "$key = $value\n"; + } else { + $this->ini .= "$key = \"$value\"\n"; + } + } + } +} diff --git a/classes/class-locale.php b/classes/class-locale.php new file mode 100644 index 0000000..4b9ec8a --- /dev/null +++ b/classes/class-locale.php @@ -0,0 +1,97 @@ + 'en-CA', 'q' => 1.0], ['locale' => 'ar-EG', 'q' => 0.5]] + foreach (explode(',', $httpAcceptLanguageHeader) as $locale) { + // separate the locale key ("ar-EG") from its weight ("q=0.5") + $localeParts = explode(';', $locale); + $weightedLocale = ['locale' => $localeParts[0]]; + if (count($localeParts) == 2) { + // explicit weight e.g. 'q=0.5' + $weightParts = explode('=', $localeParts[1]); + // grab the '0.5' bit and parse it to a float + $weightedLocale['q'] = floatval($weightParts[1]); + } else { + // no weight given in string, ie. implicit weight of 'q=1.0' + $weightedLocale['q'] = 1.0; + } + $weightedLocales[] = $weightedLocale; + } + return $weightedLocales; + } + + /** + * Sort by high to low `q` value + */ + private static function sortLocalesByWeight($locales) { + usort($locales, function ($a, $b) { + // usort will cast float values that we return here into integers, + // which can mess up our sorting. So instead of subtracting the `q`, + // values and returning the difference, we compare the `q` values and + // explicitly return integer values. + if ($a['q'] == $b['q']) { + return 0; + } + if ($a['q'] > $b['q']) { + return -1; + } + return 1; + }); + + return $locales; + } +} diff --git a/classes/class-page.php b/classes/class-page.php new file mode 100644 index 0000000..d8be259 --- /dev/null +++ b/classes/class-page.php @@ -0,0 +1,51 @@ +page_title; + } + + public function get_content() { + return $this->content; + } + + public function get_sidebar_left() { + return ''; + } + + public function get_sidebar( $id = 'right' ) { + if( empty( $id ) ) { + return ''; + } + $html = ''; + switch( $id ) { + case 'left': + // $html = OpenSim_Grid::grid_stats_card(); + break; + case 'right': + $html .= OpenSim_Grid::grid_info_card(); + $html .= OpenSim_Grid::grid_stats_card(); + break; + default: + break; + } + + $class="flex"; + // if( ! empty( $html ) ) { + // $html = sprintf( + // '', + // $id, + // $id, + // $html + // ); + // } + return $html; + } +} diff --git a/classes/init.php b/classes/init.php new file mode 100644 index 0000000..38c474c --- /dev/null +++ b/classes/init.php @@ -0,0 +1,680 @@ +constants(); + $this->includes(); + + $this->grid = new OpenSim_Grid(); + } + + public function constants() { + if( ! defined( 'ABSPATH' ) ) { + define( 'ABSPATH', dirname( __FILE__ ) . '/' ); + } + define( 'OSHELPERS', true ); + define( 'OSHELPERS_DIR', self::trailingslashit( dirname( __DIR__ ) ) ); + define( 'OSHELPERS_URL', self::get_helpers_url() ); + // ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http" ) . "://$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]" ); + + } + + public function includes() { + require_once( OSHELPERS_DIR . 'classes/class-exception.php' ); + require_once( OSHELPERS_DIR . 'includes/databases.php' ); + require_once( OSHELPERS_DIR . 'includes/functions.php' ); + $this->db_connect(); + + require_once( OSHELPERS_DIR . 'classes/class-locale.php' ); + require_once( OSHELPERS_DIR . 'classes/class-ini.php' ); + require_once( OSHELPERS_DIR . 'classes/class-grid.php' ); + } + + public function db_connect() { + $DatabaseService = self::get_option( 'DatabaseService', false ); + + $connectionstring = self::get_option( 'DatabaseService.ConnectionString', false); + if( $connectionstring ) { + $creds = self::connectionstring_to_array( $connectionstring ); + $dsn = sprintf( + 'mysql:host=%s;dbname=%s', + $creds['host'] . ( empty( $creds['port'] ) ? '' : ':' . $creds['port'] ), + $creds['name'], + ); + $db = new OSPDO( $dsn, $creds['user'], $creds['pass'] ); + if( $db->connected ) { + $db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); + $db->setAttribute( PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC ); + self::$robust_db = $db; + } + } else { + return false; + } + } + + public function get_helpers_url() { + $helpers_path = dirname( __DIR__ ); + $url_path = self::trailingslashit( str_replace( $_SERVER['DOCUMENT_ROOT'], '', $helpers_path ) ); + + $parsed = array( + 'scheme' => isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http', + 'host' => $_SERVER['HTTP_HOST'], + ); + $url = self::build_url( $parsed ) . ltrim( $url_path ); + + return $url; + } + + public static function get_version( $sanitized = false ) { + if( $sanitized && self::$version_slug ) { + return self::$version_slug; + } else if ( ! $sanitized && self::$version ) { + return self::$version; + } + if( file_exists( OSHELPERS_DIR . '.version' ) ) { + $version = file_get_contents( '.version' ); + } else { + $version = '0.0.0'; + } + if( file_exists( '.git/HEAD' ) ) { + $hash = trim( file_get_contents( '.git/HEAD' ) ); + $hash = trim( preg_replace( '+.*[:/]+', '', $hash ) ); + if( !empty( $hash ) && file_exists( '.git/refs/heads/' . $hash ) ) { + $hash = substr( file_get_contents( '.git/refs/heads/' . $hash ), 0, 7 ) . " ($hash)"; + } else { + $hash = substr( $hash, 0, 7 ); + $hash .= ' (detached)'; + } + + $version .= empty( $hash ) ? ' git ' : ' git ' . $hash; + self::$is_dev = ( empty( $hash ) ) ? self::$is_dev : true; + } + + self::$version = $version; + self::$version_slug = self::sanitize_slug( $version ); + if( $sanitized && self::$version_slug ) { + return self::$version_slug; + } + return $version; + } + + public static function get_temp_dir( $dir = false ) { + if( isset( self::$tmp_dir ) ) { + return self::$tmp_dir; + } + + if ( ! empty( $dir ) && is_dir( $dir ) && is_writable( $dir ) ) { + $dir = realpath( $dir ); + } else { + $dirs = array( + sys_get_temp_dir(), + dirname( $_SERVER['DOCUMENT_ROOT'] ) . '/tmp', + ini_get( 'upload_tmp_dir' ), + '/var/tmp', + '~/tmp', + ); + foreach( $dirs as $key => $dir ) { + if( is_dir( $dir ) && is_writable( $dir ) ) { + $dir = realpath( $dir ); + break; + } + } + } + if ( ! $dir ) { + throw new OpenSim_Error( 'No writable temporary directory found.' ); + } + + self::$tmp_dir = $dir; + return $dir; + } + + public static function parse_args( $args, $defaults ) { + if( empty( $defaults ) ) { + $defaults = array(); + } + if( is_object( $args ) ) { + $args = get_object_vars( $args ); + } elseif( is_array( $args ) ) { + $args = $args; + } else { + parse_str( $args, $args ); + } + return array_merge( $defaults, $args ); + } + + public static function connectionstring_to_array( $connectionstring ) { + $parts = explode( ';', $connectionstring ); + $creds = array(); + foreach ( $parts as $part ) { + $pair = explode( '=', $part ); + $creds[ $pair[0] ] = $pair[1] ?? ''; + } + if( preg_match( '/:[0-9]+$/', $creds['Data Source'] ) ) { + $host = explode( ':', $creds['Data Source'] ); + $creds['Data Source'] = $host[0]; + $creds['Port'] = empty( $host[1] || $host[1] == 3306 ) ? null : $creds['Port']; + } + $result = array( + 'host' => $creds['Data Source'], + 'port' => $creds['Port'] ?? null, + 'name' => $creds['Database'], + 'user' => $creds['User ID'], + 'pass' => $creds['Password'], + ); + return $result; + } + + // Clone of WP trailingslashit function + public static function trailingslashit( $value ) { + return self::untrailingslashit( $value ) . '/'; + } + + // Clone of WP untrailingslashit function + public static function untrailingslashit( $value ) { + return rtrim( $value, '/\\' ); + } + + /** + * Notify user of an error, log it and display it in the admin area + * + * @param mixed $error (string) Error message or (Throwable) Exception + * @param string $type Error severity: 'info', 'warning', 'danger' + * @return void + */ + public static function notify_error( $error, $type = 'warning' ) { + // Initialize the prefix before error type check + $prefix = '[' . strtoupper( $type ) . '] '; + + // Retrieve the calling method's information + if ( $error instanceof Throwable ) { + $message = $error->getMessage(); + } elseif( is_string($error) ) { + $message = $error; + } else { + $message = _('Unknown error, see log for details'); + error_log( $prefix . 'Unidentified error type: ' . gettype( $error ) . ' ' . print_r( $error, true ) ); + } + if( ! empty( $message ) ) { + self::notify( $message, $type ); + } + } + + public static function notify( $message, $type = 'info' ) { + $key = md5( $type . $message ); // Make sure we don't have duplicates + self::$user_notices[$key] = array( + 'message' => $message, + 'type' => $type, + ); + } + + public static function get_notices() { + $html = ''; + foreach( self::$user_notices as $key => $notice ) { + $type = $notice['type'] ?? 'info'; + switch( $type ) { + case 'task-checked': + $html .= sprintf( + '
+ + +
', + $type, + $notice['message'] + ); + break; + + default: + $html .= sprintf( + '
%s
', + $type, + $notice['message'] + ); + } + } + return $html; + } + + public static function validate_error_type( $type, $fallback = 'light' ) { + $given = $type; + $type = in_array( $type, array( + 'primary', + 'secondary', + 'success', + 'danger', + 'warning', + 'info', + 'light', + 'dark', + ) ) ? $type : $fallback; + return $type; + } + + public static function validate_error ( $error, $type = 'light' ) { + if( is_string( $error )) { + $error = array( 'message', $error ); + } + $error = self::parse_args( $error, array( + 'message' => _('Error'), + 'type' => $type, + )); + $error['type'] = self::validate_error_type( $error['type'], $type ); + return $error; + } + + public static function error_html( $error, $type = null ) { + $error = self::validate_error( $error, $type ); + $html = sprintf( + '
%s
', + $error['type'], + $error['message'], + ); + return $html; + } + + /** + * Log message to error_log, adding calling [CLASS] and function before message, and severity if given + */ + public static function log( $message, $severity = none ) { + $caller = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 ); + $caller = $caller[1]; + $class = $caller['class'] ?? ''; + $function = $caller['function'] ?? ''; + $message = sprintf( + '[%s%s%s] %s%s', + $class, + empty( $class ) ? '' : '::', + $function, + empty( $severity ) ? '' : strtoupper( $severity ) . ' ', + $message + ); + // Add severity if given + error_log( $message ); + } + + public static function callback_name_string( $callback ) { + if( is_string( $callback ) ) { + return $callback; + } + if( is_array( $callback ) && is_object( $callback[0] ) ) { + $callback_name = get_class($callback[0]) . '::' . $callback[1]; + return $callback_name; + } + if( is_array( $callback ) ) { + return $callback[0] . '::' . $callback[1]; + } + if( is_object( $callback ) ) { + return get_class( $callback ); + } + return 'Unknown'; + } + + public static function build_url( $parsed ) { + if( empty( $parsed['host'] ) ) { + $url = ''; + } else { + $url = ( $parsed['scheme'] ?? 'https' ) . '://' . $parsed['host']; + } + $url .= $parsed['path'] ?? ''; + if( ! empty( $parsed['query'] ) ) { + $url .= '?' . $parsed['query']; + } + return $url; + } + + public static function add_query_args( $url, $args ) { + $parsed = parse_url( $url ); + $query = $parsed['query'] ?? ''; + $query = self::parse_args( $query, array() ); + $query = array_merge( $query, $args ); + $query = http_build_query( $query ); + $parsed['query'] = $query; + $url = self::build_url( $parsed ); + return $url; + } + + /** + * Basic function to replace WP enqueue_script when not in WP environment. + * Add the script to a private property that will be used with another method to output all scripts. + * Use OSHELPERS_URL constant to build the URL unless it's already full. + * Use self::get_version() to define the version of the script unless it is already defined. + */ + public static function enqueue_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) { + if( ! file_exists( $src ) ) { + error_log( __FUNCTION__ . ' file not found: ' . $src ); + return false; + } + + $handle = preg_match( '/^oshelpers-/', $handle ) ? $handle : 'oshelpers-' . $handle; + $handle = ( rtrim ( $handle, '-css' ) ) . '-js'; + + self::$scripts = self::$scripts ?? array( 'head' => array(), 'footer' => array() ); + if( strpos( $src, '://' ) === false ) { + $src = OSHELPERS_URL . ltrim( $src, '/' ); + } + $ver = empty( $ver ) ? self::get_version( true ) : self::sanitize_slug( $ver ); + $src = self::add_query_args( $src, array( 'ver' => $ver ) ); + + $section = $in_footer ? 'footer' : 'head'; + self::$scripts[$section][$handle] = array( + 'src' => $src, + 'deps' => $deps, + 'ver' => $ver ?? self::get_version( true ), + 'in_footer' => $in_footer, + ); + } + + /** + * Return or output the html for scripts in the head or footer + * + * @param string $section 'head' or 'footer' + * @param bool $echo Output the html if true, return it if false + */ + public static function get_scripts( $section, $echo = false ) { + if( ! isset( self::$scripts[$section] ) ) { + return ''; + } + $html = ''; + if(empty( self::$scripts[$section] ) ) { + return ''; + } + + $template = ''; + + $scripts = self::$scripts[$section]; + foreach( $scripts as $handle => $script ) { + // error_log( 'Script: ' . print_r( $script, true ) ); + $html .= sprintf( + $template, + $handle, + $script['src'], + empty( $script['ver'] ) ? '' : 'version="' . $script['ver'] . '"' + ); + } + if( $echo ) { + echo $html; + } + // error_log( $html ); + return $html; + } + + public static function enqueue_style( $handle, $src, $deps = array(), $ver = false, $media = 'all' ) { + if( ! file_exists( $src ) ) { + error_log( __FUNCTION__ . ' file not found: ' . $src ); + return false; + } + + $handle = preg_match( '/^oshelpers-/', $handle ) ? $handle : 'oshelpers-' . $handle; + $handle = ( rtrim ( $handle, '-css' ) ) . '-css'; + + self::$styles = self::$styles ?? array( 'head' => array(), 'footer' => array() ); + if( strpos( $src, '://' ) === false ) { + $src = OSHELPERS_URL . ltrim( $src, '/' ); + } + $ver = empty( $ver ) ? self::get_version( true ) : self::sanitize_slug( $ver ); + $src = self::add_query_args( $src, array( 'ver' => $ver ) ); + + self::$styles['head'][$handle] = array( + 'src' => $src, + 'deps' => $deps, + 'ver' => $ver, + 'media' => $media, + ); + } + + public static function get_styles( $echo = false ) { + if( ! isset( self::$styles['head'] ) ) { + return ''; + } + $html = ''; + if(empty( self::$styles['head'] ) ) { + return ''; + } + + $template = ''; + + $styles = self::$styles['head']; + foreach( $styles as $handle => $style ) { + $html .= sprintf( + $template, + $handle, + $style['src'], + $style['media'], + ); + } + if( $echo ) { + echo $html; + } + return $html; + } + + public static function sanitize_id( $string ) { + if( empty( $string ) ) { + return false; + } + + $id = $string; + try { + $id = transliterator_transliterate("Any-Latin; NFD; [:Nonspacing Mark:] Remove; NFC; [:Punctuation:] Remove; Lower();", $id ); + $id = preg_replace('/[-\s]+/', '-', $id ); + } catch ( Error $e ) { + error_log( 'Error sanitizing slug: ' . $e->getMessage() ); + $id = $string; + } + + return $id; + } + + public static function sanitize_slug( $string ) { + return self::sanitize_id( $string ); + } + + public static function sanitize_url( $url ) { + $url = trim( $url ); + $url = str_replace( ' ', '+', $url ); + $url = filter_var( $url, FILTER_SANITIZE_URL ); + return $url; + } + + public static function validate_condition( $condition ) { + if( is_callable( $condition ) ) { + return $condition(); + } + if( is_bool( $condition ) ) { + return $condition; + } + switch( $condition ) { + case 'logged_in': + return self::is_logged_in(); + case 'logged_out': + return self::is_logged_out(); + default: + return false; + } + } + + /** + * Get user preferred language from browser settings. + * + * @param bool $long Full locale string if true (en_US), language code otherwise (en) + * @return string + */ + public static function user_locale( $long = true ) { + return $long ? OpenSim_Locale::locale() : OpenSim_Locale::lang(); + } + + public static function user_lang( $long = false ) { + return $long ? OpenSim_Locale::locale() : OpenSim_Locale::lang(); + } + + /** + * Return the actual language of the content if localization is setup + */ + public static function content_lang( $long = false ) { + // When localization is setup, we will return user language + // For now, we return english. + $lang = 'en_US'; + + // return self::user_locale( $long ); + return $long ? $lang : substr( $lang, 0, 2 ); + } + + public static function is_logged_in() { + // WP is not loaded so constants like COOKIEHASH are not available. + // Any cookie matching wordpress_logged_in or wordpress_logged_in_* + // is considered a valid login cookie. + // If logged_in, use first part of cookie value as user_id + foreach( $_COOKIE as $key => $value ) { + if( preg_match( '/^wordpress_logged_in/', $key ) ) { + $parts = explode( '|', $value ); + $_SESSION['user_id'] = $parts[0]; + break; + } + } + + return isset( $_SESSION['user_id'] ); + } + + public static function is_logged_out() { + return ! self::is_logged_in(); + } + + public static function get_user_id() { + return $_SESSION['user_id'] ?? false; + } + + public static function display_name( $fallback = false ) { + if( ! self::is_logged_in() ) { + return $fallback; + } + + // For now, we return the user_id + return self::get_user_id() ?? $fallback; + } + + public static function icon( $icon, $size = 'inherit' ) { + if( is_callable( $icon ) ) { + $callback = $icon; + return call_user_func( $callback, $size ); + } + $size = is_numeric( $size ) ? $size . 'px' : $size; + return ' '; + } + + /** + * Display small user icon based on in-world Avatar profile picture. + * Not implemented yet. For now, we use bootstrap icons. + * In any case, we don't use gravatar yet. + */ + public static function user_icon( $size = 'inherit' ) { + if( ! self::is_logged_in() ) { + return null; + } + $size = is_numeric( $size ) ? $size . 'px' : $size; + return self::icon( 'person-circle', $size ); + // For now, we return user icon with bootstrap library + // return ' '; + } + + /** + * get_option() + * + * Retrieve an option value from + * - $_SESSION['installation']['config'], if exists, organized as an array( + * 'section' => array( + * 'option' => 'value', + * 'option2' => 'value2', + * ) + * - self::$config, if exists, organized the same way as $_SESSION['installation']['config'] + * - constants defined includes/config.php (map to be implemented later) + * - site configuration data (to be implemented later) + */ + public static function get_option( $option, $default = null ) { + $config = $_SESSION['installation']['config'] ?? self::$config ?? array(); + if( empty( $config ) ) { + return $default; + } + // Give value of $config[$section][$key] if when given $option = 'section.key' + if( strpos( $option, '.' ) !== false ) { + $parts = explode( '.', $option ); + $section = $config[$parts[0]]; + $key = trim($parts[1]); + return $section[$key] ?? $default; + } + + // Otherwise return global option + return $config[$option] ?? $default; + } + + static function hop( $url = null, $string = null, $format = true ) { + if ( empty( $url ) ) { + // $url = get_option( 'w4os_login_uri' ); + return $string; + } + $url = opensim_format_tp( $url, TPLINK_HOP ); + + if ( ! $format ) { + return $url; + } + + $string = ( empty( $string ) ) ? $url : $string; + $classes[] = 'hop'; + $classes[] = 'hop-link'; + if ( preg_match( ':/app/agent/:', $url ) ) { + $classes[] = 'profile'; + } + + $string = preg_replace( '+.*://+', '', $string ); + return sprintf( + '%s %s', + implode( ' ', $classes ), + $url, + self::icon( 'door-open' ), + $string, + // self::icon( 'box-arrow-up-right' ), + // self::icon( 'arrow-right-square' ), + ); + } + + public static function is_error( $thing ) { + // Throwable includes Exception and probably other things. + if ( $thing instanceof Throwable ) { + return true; + } + return false; + } + +} + +$OpenSim = new OpenSim(); +$OpenSim->init(); diff --git a/css/bootstrap.css b/css/bootstrap.css new file mode 100644 index 0000000..e69de29 diff --git a/css/form.css b/css/form.css new file mode 100644 index 0000000..8a7a778 --- /dev/null +++ b/css/form.css @@ -0,0 +1,5 @@ +::placeholder, +input::placeholder { + display: none; + opacity: 0.5; +} diff --git a/js/form.js b/js/form.js new file mode 100644 index 0000000..e69de29 diff --git a/templates/bootstrap.css b/templates/bootstrap.css new file mode 100644 index 0000000..5c6f170 --- /dev/null +++ b/templates/bootstrap.css @@ -0,0 +1,41 @@ +/* + * Template page CSS +**/ + +:root { + --bs-primary-rgb: 0, 192, 0; + --bs-secondary-rgb: 63, 159, 63; +} + +[data-bs-theme="dark"] { +} + +/* Add this to your CSS file */ +.dropdown-hover:hover .dropdown-menu { + display: block; +} + +.block a, +a.hop-link { + text-decoration: none !important; +} + +.cards .accordion-button { + border: 0; +} + +.cards .accordion-button:not(.collapsed) { + color: inherit; + background-color: inherit; + box-shadow: none; +} + +.cards .accordion-button::after { + background-image: url("data:image/svg+xml,") !important; + transform: rotate(180deg); +} + +.accordion-button:not(.collapsed)::after { + color: white; + transform: rotate(0); +} diff --git a/templates/bootstrap.js b/templates/bootstrap.js new file mode 100644 index 0000000..83a53d7 --- /dev/null +++ b/templates/bootstrap.js @@ -0,0 +1,17 @@ +document.addEventListener('DOMContentLoaded', () => { + // Function to apply the color scheme + const applyColorScheme = (e) => { + if (e.matches) { + document.documentElement.setAttribute('data-bs-theme', 'dark'); + } else { + document.documentElement.setAttribute('data-bs-theme', 'light'); + } + }; + + // Detect and apply user's preferred color scheme on initial load + const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + applyColorScheme(darkModeMediaQuery); + + // Listen for changes in the user's color scheme preference + darkModeMediaQuery.addEventListener('change', applyColorScheme); +}); diff --git a/templates/template-page.php b/templates/template-page.php new file mode 100644 index 0000000..4d6d49e --- /dev/null +++ b/templates/template-page.php @@ -0,0 +1,74 @@ + + + + + + <?php echo $page_title; ?> + + + + + + +
+ + +
+
+ +
+ Card
'; + // $sidebar_right = '
Card
'; + ?> +
+

+
+ +
+
+ get_sidebar('left'); + if ( ! empty( $sidebar_left ) ) : + ?> + + + get_sidebar('right'); + if ( ! empty( $sidebar_right ) ) : + ?> + + +
+
+
+ +
+ + + + diff --git a/templates/templates.php b/templates/templates.php new file mode 100644 index 0000000..d10db30 --- /dev/null +++ b/templates/templates.php @@ -0,0 +1,130 @@ + array( +// 'url' => '/', +// 'label' => 'Home', +// ), +// 'about' => array( +// 'url' => '/about', +// 'label' => 'About', +// ), +// ); + +$menus['user'] = array( + 'userprofile' => array( + 'url' => '/profile', + 'label' => OpenSim::display_name( _('Profile') ), + 'icon' => [ 'OpenSim', 'user_icon' ], + 'condition' => 'logged_in', + 'children' => array( + 'account' => array( + 'url' => '/account', + 'label' => _('Account Settings'), + 'icon' => 'sliders' + ), + 'logout' => array( + 'url' => '?action=logout', + 'label' => _('Logout'), + 'icon' => 'box-arrow-right', + ), + ), + ), + 'login' => array( + 'url' => '/login', + 'label' => 'Login', + 'icon' => 'box-arrow-in-right', + 'condition' => 'logged_out', + ), +); + +$menus['footer'] = array( + 'github' => array( + 'url' => 'http://github.com/magicoli/opensim-helpers', + 'label' => 'GitHub Repository', + ), + 'w4os' => array( + 'url' => 'https://w4os.org', + 'label' => 'W4OS Project', + ), +); + +/** + * Build HTML menu using Bootstrap styles + */ +function format_menu( $menu, $slug = 'main', $class = '' ) { + $id = "navbar-$slug"; + $html = sprintf( + ''; + + return $html; +} + +$branding = '' . htmlspecialchars($GLOBALS['site_title']) . ''; + +// Generate HTML for each menu +$main_menu_html = format_menu( ($menus['main'] ?? null ), 'main' ); +$user_menu_html = format_menu( $menus['user'], 'user' ); +$footer_menu_html = format_menu( $menus['footer'], 'footer' ); + +OpenSim::enqueue_script( 'template-page', 'templates/bootstrap.js' ); +OpenSim::enqueue_style( 'template-page', 'templates/bootstrap.css' ); + +require( 'template-page.php' ); From b1ebde4a9fa925fe62deb6edf340b62d6a8a6b6b Mon Sep 17 00:00:00 2001 From: Gudule Lapointe Date: Sun, 16 Mar 2025 17:22:52 +0100 Subject: [PATCH 06/20] include includes/config.php if file exists --- classes/init.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/classes/init.php b/classes/init.php index 38c474c..80867e3 100644 --- a/classes/init.php +++ b/classes/init.php @@ -52,6 +52,15 @@ class OpenSim { require_once( OSHELPERS_DIR . 'classes/class-exception.php' ); require_once( OSHELPERS_DIR . 'includes/databases.php' ); require_once( OSHELPERS_DIR . 'includes/functions.php' ); + + if ( file_exists( OSHELPERS_DIR . 'includes/config.php' ) ) { + try { + include_once( OSHELPERS_DIR . 'includes/config.php' ); + } catch ( Error $e ) { + self::notify_error( $e ); + } + } + $this->db_connect(); require_once( OSHELPERS_DIR . 'classes/class-locale.php' ); From 4a208aae963bdf367c8f0f05f7af47e606d6b929 Mon Sep 17 00:00:00 2001 From: Gudule Lapointe Date: Sun, 16 Mar 2025 17:23:44 +0100 Subject: [PATCH 07/20] page use $page_title and $content arguments if provided --- classes/class-page.php | 5 +++-- templates/templates.php | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/classes/class-page.php b/classes/class-page.php index d8be259..7fa2ca6 100644 --- a/classes/class-page.php +++ b/classes/class-page.php @@ -4,8 +4,9 @@ class OpenSim_Page { protected $page_title; protected $content; - public function __construct() { - + public function __construct( $page_title = null, $content = null, $args = array() ) { + $this->page_title = $page_title; + $this->content = $content; } public function get_page_title() { diff --git a/templates/templates.php b/templates/templates.php index d10db30..715715c 100644 --- a/templates/templates.php +++ b/templates/templates.php @@ -1,11 +1,12 @@ get_page_title() ?? 'Unknown page'; +$content = $content ?? $page->get_content() ?? 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam in dui mauris.'; +$sidebar_left = $sidebar_left ?? $page->get_sidebar('left'); +$sidebar_right = $sidebar_right ?? $page->get_sidebar('right'); + $version = OpenSim::get_version(); $footer = $footer ?? sprintf( _('OpenSimulator Helpers %s'), $version ); From 6fd033331f83ba4264fbf46a8d865ece253fd60c Mon Sep 17 00:00:00 2001 From: Gudule Lapointe Date: Sun, 16 Mar 2025 17:23:55 +0100 Subject: [PATCH 08/20] composer update --- composer.lock | 862 +++++++++++++++++++++++--------------------------- 1 file changed, 399 insertions(+), 463 deletions(-) diff --git a/composer.lock b/composer.lock index a3c2a21..e8789b4 100644 --- a/composer.lock +++ b/composer.lock @@ -9,24 +9,24 @@ "packages-dev": [ { "name": "composer/semver", - "version": "3.4.0", + "version": "3.4.3", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { @@ -70,7 +70,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.0" + "source": "https://github.com/composer/semver/tree/3.4.3" }, "funding": [ { @@ -86,29 +86,29 @@ "type": "tidelift" } ], - "time": "2023-08-31T09:50:34+00:00" + "time": "2024-09-19T14:15:21+00:00" }, { "name": "consolidation/annotated-command", - "version": "4.9.1", + "version": "4.10.1", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "e01152f698eff4cb5df3ebfe5e097ef335dbd3c9" + "reference": "362310b13ececa9f6f0a4a880811fa08fecc348b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/e01152f698eff4cb5df3ebfe5e097ef335dbd3c9", - "reference": "e01152f698eff4cb5df3ebfe5e097ef335dbd3c9", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/362310b13ececa9f6f0a4a880811fa08fecc348b", + "reference": "362310b13ececa9f6f0a4a880811fa08fecc348b", "shasum": "" }, "require": { "consolidation/output-formatters": "^4.3.1", "php": ">=7.1.3", "psr/log": "^1 || ^2 || ^3", - "symfony/console": "^4.4.8 || ^5 || ^6", - "symfony/event-dispatcher": "^4.4.8 || ^5 || ^6", - "symfony/finder": "^4.4.8 || ^5 || ^6" + "symfony/console": "^4.4.8 || ^5 || ^6 || ^7", + "symfony/event-dispatcher": "^4.4.8 || ^5 || ^6 || ^7", + "symfony/finder": "^4.4.8 || ^5 || ^6 || ^7" }, "require-dev": { "composer-runtime-api": "^2.0", @@ -140,9 +140,9 @@ "description": "Initialize Symfony Console commands from annotated command class methods.", "support": { "issues": "https://github.com/consolidation/annotated-command/issues", - "source": "https://github.com/consolidation/annotated-command/tree/4.9.1" + "source": "https://github.com/consolidation/annotated-command/tree/4.10.1" }, - "time": "2023-05-20T04:19:01+00:00" + "time": "2024-12-13T19:55:40+00:00" }, { "name": "consolidation/config", @@ -206,22 +206,22 @@ }, { "name": "consolidation/log", - "version": "3.0.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/consolidation/log.git", - "reference": "caaad9d70dae54eb49002666f000e3c607066878" + "reference": "3ad08dc57e8aff9400111bad36beb0ed387fe6a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/caaad9d70dae54eb49002666f000e3c607066878", - "reference": "caaad9d70dae54eb49002666f000e3c607066878", + "url": "https://api.github.com/repos/consolidation/log/zipball/3ad08dc57e8aff9400111bad36beb0ed387fe6a9", + "reference": "3ad08dc57e8aff9400111bad36beb0ed387fe6a9", "shasum": "" }, "require": { - "php": ">=8.0.0", - "psr/log": "^3", - "symfony/console": "^5 || ^6" + "php": ">=7.1.3", + "psr/log": "^1 || ^2", + "symfony/console": "^4 || ^5 || ^6" }, "require-dev": { "phpunit/phpunit": ">=7.5.20", @@ -252,36 +252,36 @@ "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", "support": { "issues": "https://github.com/consolidation/log/issues", - "source": "https://github.com/consolidation/log/tree/3.0.0" + "source": "https://github.com/consolidation/log/tree/2.1.1" }, - "time": "2022-04-05T16:53:32+00:00" + "time": "2022-02-24T04:27:32+00:00" }, { "name": "consolidation/output-formatters", - "version": "4.3.2", + "version": "4.6.0", "source": { "type": "git", "url": "https://github.com/consolidation/output-formatters.git", - "reference": "06711568b4cd169700ff7e8075db0a9a341ceb58" + "reference": "5fd5656718d7068a02d046f418a7ba873d5abbfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/06711568b4cd169700ff7e8075db0a9a341ceb58", - "reference": "06711568b4cd169700ff7e8075db0a9a341ceb58", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/5fd5656718d7068a02d046f418a7ba873d5abbfe", + "reference": "5fd5656718d7068a02d046f418a7ba873d5abbfe", "shasum": "" }, "require": { "dflydev/dot-access-data": "^1.1.0 || ^2 || ^3", "php": ">=7.1.3", - "symfony/console": "^4 || ^5 || ^6", - "symfony/finder": "^4 || ^5 || ^6" + "symfony/console": "^4 || ^5 || ^6 || ^7", + "symfony/finder": "^4 || ^5 || ^6 || ^7" }, "require-dev": { "php-coveralls/php-coveralls": "^2.4.2", "phpunit/phpunit": "^7 || ^8 || ^9", "squizlabs/php_codesniffer": "^3", - "symfony/var-dumper": "^4 || ^5 || ^6", - "symfony/yaml": "^4 || ^5 || ^6", + "symfony/var-dumper": "^4 || ^5 || ^6 || ^7", + "symfony/yaml": "^4 || ^5 || ^6 || ^7", "yoast/phpunit-polyfills": "^1" }, "suggest": { @@ -306,39 +306,38 @@ "description": "Format text by applying transformations provided by plug-in formatters.", "support": { "issues": "https://github.com/consolidation/output-formatters/issues", - "source": "https://github.com/consolidation/output-formatters/tree/4.3.2" + "source": "https://github.com/consolidation/output-formatters/tree/4.6.0" }, - "time": "2023-07-06T04:45:41+00:00" + "time": "2024-10-18T14:02:48+00:00" }, { "name": "consolidation/robo", - "version": "4.0.6", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/consolidation/robo.git", - "reference": "55a272370940607649e5c46eb173c5c54f7c166d" + "reference": "ccf80963abf11bdb8e90659aa99a7449b21e9452" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/robo/zipball/55a272370940607649e5c46eb173c5c54f7c166d", - "reference": "55a272370940607649e5c46eb173c5c54f7c166d", + "url": "https://api.github.com/repos/consolidation/robo/zipball/ccf80963abf11bdb8e90659aa99a7449b21e9452", + "reference": "ccf80963abf11bdb8e90659aa99a7449b21e9452", "shasum": "" }, "require": { - "consolidation/annotated-command": "^4.8.1", - "consolidation/config": "^2.0.1", - "consolidation/log": "^2.0.2 || ^3", + "consolidation/annotated-command": "^4.3", + "consolidation/config": "^1.2.1 || ^2.0.1", + "consolidation/log": "^1.1.1 || ^2.0.2", "consolidation/output-formatters": "^4.1.2", "consolidation/self-update": "^2.0", "league/container": "^3.3.1 || ^4.0", - "php": ">=8.0", - "phpowermove/docblock": "^4.0", - "symfony/console": "^6", - "symfony/event-dispatcher": "^6", - "symfony/filesystem": "^6", - "symfony/finder": "^6", - "symfony/process": "^6", - "symfony/yaml": "^6" + "php": ">=7.1.3", + "symfony/console": "^4.4.19 || ^5 || ^6", + "symfony/event-dispatcher": "^4.4.19 || ^5 || ^6", + "symfony/filesystem": "^4.4.9 || ^5 || ^6", + "symfony/finder": "^4.4.9 || ^5 || ^6", + "symfony/process": "^4.4.9 || ^5 || ^6", + "symfony/yaml": "^4.4 || ^5 || ^6" }, "conflict": { "codegyre/robo": "*" @@ -361,6 +360,33 @@ "robo" ], "type": "library", + "extra": { + "scenarios": { + "symfony4": { + "config": { + "platform": { + "php": "7.1.3" + } + }, + "remove": [ + "codeception/phpunit-wrapper" + ], + "require": { + "symfony/finder": "^4.4.11", + "phpunit/phpunit": "^6", + "symfony/console": "^4.4.11", + "symfony/process": "^4.4.11", + "nikic/php-parser": "^2", + "symfony/filesystem": "^4.4.11", + "symfony/event-dispatcher": "^4.4.11" + } + } + }, + "branch-alias": { + "dev-main": "2.x-dev", + "dev-master": "2.x-dev" + } + }, "autoload": { "psr-4": { "Robo\\": "src" @@ -379,9 +405,9 @@ "description": "Modern task runner", "support": { "issues": "https://github.com/consolidation/robo/issues", - "source": "https://github.com/consolidation/robo/tree/4.0.6" + "source": "https://github.com/consolidation/robo/tree/4.0.2" }, - "time": "2023-04-30T21:49:04+00:00" + "time": "2022-04-21T09:29:58+00:00" }, { "name": "consolidation/self-update", @@ -440,16 +466,16 @@ }, { "name": "dflydev/dot-access-data", - "version": "v3.0.2", + "version": "v3.0.3", "source": { "type": "git", "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "f41715465d65213d644d3141a6a93081be5d3549" + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", - "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", "shasum": "" }, "require": { @@ -509,34 +535,33 @@ ], "support": { "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" }, - "time": "2022-10-27T11:44:00+00:00" + "time": "2024-07-08T12:26:09+00:00" }, { "name": "grasmash/expander", - "version": "3.0.0", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/grasmash/expander.git", - "reference": "bb1c1a2430957945cf08c5a62f5d72a6aa6a2c82" + "reference": "b7cbc1f2fdf9a9c0e253a424c2a4058316b7cb6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/grasmash/expander/zipball/bb1c1a2430957945cf08c5a62f5d72a6aa6a2c82", - "reference": "bb1c1a2430957945cf08c5a62f5d72a6aa6a2c82", + "url": "https://api.github.com/repos/grasmash/expander/zipball/b7cbc1f2fdf9a9c0e253a424c2a4058316b7cb6e", + "reference": "b7cbc1f2fdf9a9c0e253a424c2a4058316b7cb6e", "shasum": "" }, "require": { "dflydev/dot-access-data": "^3.0.0", - "php": ">=8.0", - "psr/log": "^2 | ^3" + "php": ">=7.1", + "psr/log": "^1 | ^2 | ^3" }, "require-dev": { "greg-1-anderson/composer-test-scenarios": "^1", - "php-coveralls/php-coveralls": "^2.5", - "phpunit/phpunit": "^9", - "squizlabs/php_codesniffer": "^3.3" + "phpunit/phpunit": "^6.0 || ^8.0 || ^9", + "squizlabs/php_codesniffer": "^2.7 || ^3.3" }, "type": "library", "extra": { @@ -561,22 +586,22 @@ "description": "Expands internal property references in PHP arrays file.", "support": { "issues": "https://github.com/grasmash/expander/issues", - "source": "https://github.com/grasmash/expander/tree/3.0.0" + "source": "https://github.com/grasmash/expander/tree/2.0.3" }, - "time": "2022-05-10T13:14:49+00:00" + "time": "2022-04-25T22:17:46+00:00" }, { "name": "league/container", - "version": "4.2.0", + "version": "4.2.4", "source": { "type": "git", "url": "https://github.com/thephpleague/container.git", - "reference": "375d13cb828649599ef5d48a339c4af7a26cd0ab" + "reference": "7ea728b013b9a156c409c6f0fc3624071b742dec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/container/zipball/375d13cb828649599ef5d48a339c4af7a26cd0ab", - "reference": "375d13cb828649599ef5d48a339c4af7a26cd0ab", + "url": "https://api.github.com/repos/thephpleague/container/zipball/7ea728b013b9a156c409c6f0fc3624071b742dec", + "reference": "7ea728b013b9a156c409c6f0fc3624071b742dec", "shasum": "" }, "require": { @@ -601,11 +626,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev", - "dev-4.x": "4.x-dev", - "dev-3.x": "3.x-dev", + "dev-1.x": "1.x-dev", "dev-2.x": "2.x-dev", - "dev-1.x": "1.x-dev" + "dev-3.x": "3.x-dev", + "dev-4.x": "4.x-dev", + "dev-master": "4.x-dev" } }, "autoload": { @@ -637,7 +662,7 @@ ], "support": { "issues": "https://github.com/thephpleague/container/issues", - "source": "https://github.com/thephpleague/container/tree/4.2.0" + "source": "https://github.com/thephpleague/container/tree/4.2.4" }, "funding": [ { @@ -645,7 +670,7 @@ "type": "github" } ], - "time": "2021-11-16T10:29:06+00:00" + "time": "2024-11-10T12:42:13+00:00" }, { "name": "magicoli/php-bump-library", @@ -682,162 +707,6 @@ }, "time": "2023-07-21T01:56:17+00:00" }, - { - "name": "phootwork/collection", - "version": "v3.2.2", - "source": { - "type": "git", - "url": "https://github.com/phootwork/collection.git", - "reference": "46dde20420fba17766c89200bc3ff91d3e58eafa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phootwork/collection/zipball/46dde20420fba17766c89200bc3ff91d3e58eafa", - "reference": "46dde20420fba17766c89200bc3ff91d3e58eafa", - "shasum": "" - }, - "require": { - "phootwork/lang": "^3.0", - "php": ">=8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "phootwork\\collection\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Thomas Gossmann", - "homepage": "http://gos.si" - } - ], - "description": "The phootwork library fills gaps in the php language and provides better solutions than the existing ones php offers.", - "homepage": "https://phootwork.github.io/collection/", - "keywords": [ - "Array object", - "Text object", - "collection", - "collections", - "json", - "list", - "map", - "queue", - "set", - "stack", - "xml" - ], - "support": { - "issues": "https://github.com/phootwork/phootwork/issues", - "source": "https://github.com/phootwork/collection/tree/v3.2.2" - }, - "time": "2022-08-27T12:51:24+00:00" - }, - { - "name": "phootwork/lang", - "version": "v3.2.2", - "source": { - "type": "git", - "url": "https://github.com/phootwork/lang.git", - "reference": "baaf154ae7d521ebeee5e89105f5b12b0f234597" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phootwork/lang/zipball/baaf154ae7d521ebeee5e89105f5b12b0f234597", - "reference": "baaf154ae7d521ebeee5e89105f5b12b0f234597", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "symfony/polyfill-mbstring": "^1.12", - "symfony/polyfill-php81": "^1.22" - }, - "type": "library", - "autoload": { - "psr-4": { - "phootwork\\lang\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Thomas Gossmann", - "homepage": "http://gos.si" - } - ], - "description": "Missing PHP language constructs", - "homepage": "https://phootwork.github.io/lang/", - "keywords": [ - "array", - "comparator", - "comparison", - "string" - ], - "support": { - "issues": "https://github.com/phootwork/phootwork/issues", - "source": "https://github.com/phootwork/lang/tree/v3.2.2" - }, - "time": "2023-05-26T05:37:59+00:00" - }, - { - "name": "phpowermove/docblock", - "version": "v4.0", - "source": { - "type": "git", - "url": "https://github.com/phpowermove/docblock.git", - "reference": "a73f6e17b7d4e1b92ca5378c248c952c9fae7826" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpowermove/docblock/zipball/a73f6e17b7d4e1b92ca5378c248c952c9fae7826", - "reference": "a73f6e17b7d4e1b92ca5378c248c952c9fae7826", - "shasum": "" - }, - "require": { - "phootwork/collection": "^3.0", - "phootwork/lang": "^3.0", - "php": ">=8.0" - }, - "require-dev": { - "phootwork/php-cs-fixer-config": "^0.4", - "phpunit/phpunit": "^9.0", - "psalm/phar": "^4.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "phpowermove\\docblock\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Thomas Gossmann", - "homepage": "http://gos.si" - } - ], - "description": "PHP Docblock parser and generator. An API to read and write Docblocks.", - "keywords": [ - "docblock", - "generator", - "parser" - ], - "support": { - "issues": "https://github.com/phpowermove/docblock/issues", - "source": "https://github.com/phpowermove/docblock/tree/v4.0" - }, - "time": "2021-09-22T16:57:06+00:00" - }, { "name": "psr/container", "version": "2.0.2", @@ -943,30 +812,30 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "src" + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -987,53 +856,58 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/1.1.4" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2021-05-03T11:20:27+00:00" }, { "name": "symfony/console", - "version": "v6.4.1", + "version": "v5.4.47", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd" + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a550a7c99daeedef3f9d23fb82e3531525ff11fd", - "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd", + "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" }, "provide": { - "psr/log-implementation": "1.0|2.0|3.0" + "psr/log-implementation": "1.0|2.0" }, "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" }, "type": "library", "autoload": { @@ -1067,7 +941,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.1" + "source": "https://github.com/symfony/console/tree/v5.4.47" }, "funding": [ { @@ -1083,33 +957,33 @@ "type": "tidelift" } ], - "time": "2023-11-30T10:54:28+00:00" + "time": "2024-11-06T11:30:55+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.4.0", + "version": "v2.5.4", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/605389f2a7e5625f273b53960dc46aeaf9c62918", + "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "2.5-dev" } }, "autoload": { @@ -1134,7 +1008,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.4" }, "funding": [ { @@ -1150,43 +1024,48 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.4.0", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "d76d2632cfc2206eecb5ad2b26cd5934082941b6" + "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d76d2632cfc2206eecb5ad2b26cd5934082941b6", - "reference": "d76d2632cfc2206eecb5ad2b26cd5934082941b6", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/72982eb416f61003e9bb6e91f8b3213600dcf9e9", + "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/event-dispatcher-contracts": "^2.5|^3" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/event-dispatcher-contracts": "^2|^3", + "symfony/polyfill-php80": "^1.16" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/service-contracts": "<2.5" + "symfony/dependency-injection": "<4.4" }, "provide": { "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0|3.0" + "symfony/event-dispatcher-implementation": "2.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^5.4|^6.0|^7.0" + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" }, "type": "library", "autoload": { @@ -1214,7 +1093,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.45" }, "funding": [ { @@ -1230,34 +1109,37 @@ "type": "tidelift" } ], - "time": "2023-07-27T06:52:43+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.4.0", + "version": "v2.5.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" + "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", - "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f", + "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=7.2.5", "psr/event-dispatcher": "^1" }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "2.5-dev" } }, "autoload": { @@ -1290,7 +1172,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.4" }, "funding": [ { @@ -1306,26 +1188,30 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "symfony/filesystem", - "version": "v6.4.0", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59" + "reference": "57c8294ed37d4a055b77057827c67f9558c95c54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/952a8cb588c3bc6ce76f6023000fb932f16a6e59", - "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/57c8294ed37d4a055b77057827c67f9558c95c54", + "reference": "57c8294ed37d4a055b77057827c67f9558c95c54", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=7.2.5", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8" + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "symfony/process": "^5.4|^6.4" }, "type": "library", "autoload": { @@ -1353,7 +1239,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.0" + "source": "https://github.com/symfony/filesystem/tree/v5.4.45" }, "funding": [ { @@ -1369,27 +1255,26 @@ "type": "tidelift" } ], - "time": "2023-07-26T17:27:13+00:00" + "time": "2024-10-22T13:05:35+00:00" }, { "name": "symfony/finder", - "version": "v6.4.0", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "11d736e97f116ac375a81f96e662911a34cd50ce" + "reference": "63741784cd7b9967975eec610b256eed3ede022b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/11d736e97f116ac375a81f96e662911a34cd50ce", - "reference": "11d736e97f116ac375a81f96e662911a34cd50ce", + "url": "https://api.github.com/repos/symfony/finder/zipball/63741784cd7b9967975eec610b256eed3ede022b", + "reference": "63741784cd7b9967975eec610b256eed3ede022b", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "require-dev": { - "symfony/filesystem": "^6.0|^7.0" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { @@ -1417,7 +1302,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.0" + "source": "https://github.com/symfony/finder/tree/v5.4.45" }, "funding": [ { @@ -1433,24 +1318,24 @@ "type": "tidelift" } ], - "time": "2023-10-31T17:30:12+00:00" + "time": "2024-09-28T13:32:08+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.28.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -1460,12 +1345,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1499,7 +1381,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -1515,36 +1397,33 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.28.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "875e90aeea2777b6f135677f618529449334a612" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", - "reference": "875e90aeea2777b6f135677f618529449334a612", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1580,7 +1459,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -1596,36 +1475,33 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.28.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1664,7 +1540,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -1680,24 +1556,24 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -1707,12 +1583,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1747,7 +1620,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -1763,33 +1636,30 @@ "type": "tidelift" } ], - "time": "2023-07-28T09:04:16+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.28.0", + "name": "symfony/polyfill-php73", + "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b" + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b", - "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1797,7 +1667,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Polyfill\\Php73\\": "" }, "classmap": [ "Resources/stubs" @@ -1817,7 +1687,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -1826,7 +1696,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" }, "funding": [ { @@ -1842,24 +1712,105 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/process", - "version": "v6.4.0", + "name": "symfony/polyfill-php80", + "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "191703b1566d97a5425dc969e4350d32b8ef17aa" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/191703b1566d97a5425dc969e4350d32b8ef17aa", - "reference": "191703b1566d97a5425dc969e4350d32b8ef17aa", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v5.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/5d1662fb32ebc94f17ddb8d635454a776066733d", + "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { @@ -1887,7 +1838,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.4.0" + "source": "https://github.com/symfony/process/tree/v5.4.47" }, "funding": [ { @@ -1903,46 +1854,39 @@ "type": "tidelift" } ], - "time": "2023-11-17T21:06:49+00:00" + "time": "2024-11-06T11:36:42+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.4.0", + "version": "v1.1.2", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" + "reference": "191afdcb5804db960d26d8566b7e9a2843cab3a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", - "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/191afdcb5804db960d26d8566b7e9a2843cab3a0", + "reference": "191afdcb5804db960d26d8566b7e9a2843cab3a0", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/container": "^2.0" + "php": "^7.1.3" }, - "conflict": { - "ext-psr": "<1.1|>=2" + "suggest": { + "psr/container": "", + "symfony/service-implementation": "" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1969,54 +1913,40 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/service-contracts/tree/v1.1.2" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-07-30T20:28:31+00:00" + "time": "2019-05-28T07:50:59+00:00" }, { "name": "symfony/string", - "version": "v7.0.0", + "version": "v5.4.47", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620" + "reference": "136ca7d72f72b599f2631aca474a4f8e26719799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/92bd2bfbba476d4a1838e5e12168bef2fd1e6620", - "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620", + "url": "https://api.github.com/repos/symfony/string/zipball/136ca7d72f72b599f2631aca474a4f8e26719799", + "reference": "136ca7d72f72b599f2631aca474a4f8e26719799", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=7.2.5", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" }, "conflict": { - "symfony/translation-contracts": "<2.5" + "symfony/translation-contracts": ">=3.0" }, "require-dev": { - "symfony/error-handler": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0|^6.0" }, "type": "library", "autoload": { @@ -2055,7 +1985,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.0.0" + "source": "https://github.com/symfony/string/tree/v5.4.47" }, "funding": [ { @@ -2071,32 +2001,35 @@ "type": "tidelift" } ], - "time": "2023-11-29T08:40:23+00:00" + "time": "2024-11-10T20:33:58+00:00" }, { "name": "symfony/yaml", - "version": "v6.4.0", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "4f9237a1bb42455d609e6687d2613dde5b41a587" + "reference": "a454d47278cc16a5db371fe73ae66a78a633371e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/4f9237a1bb42455d609e6687d2613dde5b41a587", - "reference": "4f9237a1bb42455d609e6687d2613dde5b41a587", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a454d47278cc16a5db371fe73ae66a78a633371e", + "reference": "a454d47278cc16a5db371fe73ae66a78a633371e", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<5.4" + "symfony/console": "<5.3" }, "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0" + "symfony/console": "^5.3|^6.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" }, "bin": [ "Resources/bin/yaml-lint" @@ -2127,7 +2060,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.0" + "source": "https://github.com/symfony/yaml/tree/v5.4.45" }, "funding": [ { @@ -2143,7 +2076,7 @@ "type": "tidelift" } ], - "time": "2023-11-06T11:00:25+00:00" + "time": "2024-09-25T14:11:13+00:00" } ], "aliases": [], @@ -2153,5 +2086,8 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform-overrides": { + "php": "7.4" + }, + "plugin-api-version": "2.6.0" } From a0d8dfbfa7fdbe37332b139e2592fc465e1153b7 Mon Sep 17 00:00:00 2001 From: Gudule Lapointe Date: Sun, 16 Mar 2025 18:49:32 +0100 Subject: [PATCH 09/20] get_grid_info accepts external grid (cached, fetched once per grid in the same http request) --- classes/class-grid.php | 35 ++++++++++++++++++++++------------- includes/functions.php | 10 ++++++++++ includes/search.php | 19 ++++++++++++++----- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/classes/class-grid.php b/classes/class-grid.php index e180afe..25322bd 100644 --- a/classes/class-grid.php +++ b/classes/class-grid.php @@ -35,24 +35,26 @@ class OpenSim_Grid { } public static function get_grid_info( $grid_uri = false, $args = array() ) { - $info = array(); + // global $oshelpers_cache; + + $key = __METHOD__ . md5( serialize( func_get_args() ) ); + $grids_info = os_cache_get( 'grid_info', array() ); + if( isset( $grids_info[$key] ) ) { + error_log( 'Cache hit: ' . $key ); + return $grids_info[$key]; + } + $HomeURI = OpenSim::get_option( 'Hypergrid.HomeURI' ); - if( ! $grid_uri || $grid_uri === $HomeURI ) { - $is_local_grid = true; - // Default, get login_uri from config, query grid for live grid_info - $grid_uri = OpenSim::get_option( 'Hypergrid.HomeURI' ); + $is_local_grid = ( ! $grid_uri || $grid_uri === $HomeURI ); + if( $is_local_grid ) { + $grid_uri = $HomeURI; if( empty( $grid_uri ) ) { return false; } - } else { - $is_local_grid = false; - // External grid lookup, not yet implemented - $info = array( - 'Grid Name' => 'External Grid, not implemented.', - ); - return false; } - + + $info = array(); + // Fetch live info from grid using $login_uri/get_grid_info and parse xml result in array // Example xml result: $xml_url = $grid_uri . '/get_grid_info'; @@ -86,6 +88,13 @@ class OpenSim_Grid { } } + if(empty($info)) { + $info = false; + } + + // $oshelpers_cache[$key] = $info; + $grids_info[$key] = $info; + os_cache_set( 'grid_info', $grids_info ); return $info; } diff --git a/includes/functions.php b/includes/functions.php index 4e844e9..758aabe 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -570,6 +570,16 @@ if( ! defined( 'TPLINK_LOCAL') ) { define( 'HELPERS_LOCALE_DIR', dirname( __DIR__ ) . '/languages' ); +function os_cache_get( $key, $default = null ) { + global $oshelpers_cache; + return isset( $oshelpers_cache[$key] ) ? $oshelpers_cache[$key] : $default; +} + +function os_cache_set( $key, $value, $expire = 0 ) { + global $oshelpers_cache; + $oshelpers_cache[$key] = $value; +} + /** * OpenSim source to help further attempts to allow Hypergrid search results. * Infouuid is a fake parcelid resolving to region handle and (region-level?) diff --git a/includes/search.php b/includes/search.php index 739c51c..46a505b 100644 --- a/includes/search.php +++ b/includes/search.php @@ -24,7 +24,9 @@ function ossearch_db_tables( $db ) { if ( ! $db->connected ) { return false; } - + $SEARCH_TABLE_EVENTS = SEARCH_TABLE_EVENTS; + $SEARCH_REGION_TABLE = SEARCH_REGION_TABLE; + $query = $db->prepare( "CREATE TABLE IF NOT EXISTS `allparcels` ( `regionUUID` char(36) NOT NULL, @@ -59,7 +61,7 @@ function ossearch_db_tables( $db ) { PRIMARY KEY (`classifieduuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - CREATE TABLE IF NOT EXISTS `" . SEARCH_TABLE_EVENTS . "` ( + CREATE TABLE IF NOT EXISTS `$SEARCH_TABLE_EVENTS` ( `owneruuid` char(36) NOT NULL, `name` varchar(255) NOT NULL, `eventid` int(11) unsigned NOT NULL AUTO_INCREMENT, @@ -151,7 +153,7 @@ function ossearch_db_tables( $db ) { PRIMARY KEY (`parcelUUID`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - CREATE TABLE IF NOT EXISTS `" . SEARCH_REGION_TABLE . '` ( + CREATE TABLE IF NOT EXISTS `$SEARCH_REGION_TABLE` ( `regionname` varchar(255) NOT NULL, `regionUUID` char(36) NOT NULL, `regionhandle` varchar(255) NOT NULL, @@ -161,7 +163,14 @@ function ossearch_db_tables( $db ) { `gatekeeperURL` varchar(255), PRIMARY KEY (`regionUUID`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - ' + + CREATE TABLE IF NOT EXISTS oshelpers_cache ( + `key` VARCHAR(255) NOT NULL, + `value` LONGBLOB, + `expires_at` TIMESTAMP NULL DEFAULT NULL, + PRIMARY KEY (`key`) + ) ENGINE=InnoDB; + " ); $result = $query->execute(); @@ -266,7 +275,7 @@ if ( $SearchDB && $SearchDB->connected ) { } define( 'SEARCH_REGION_TABLE', $regions_table ); - if ( ! tableExists( $SearchDB, array( SEARCH_REGION_TABLE, 'parcels', 'parcelsales', 'allparcels', 'objects', 'popularplaces', SEARCH_TABLE_EVENTS, 'classifieds', 'hostsregister' ) ) ) { + if ( ! tableExists( $SearchDB, array( 'oshelpers_cache', SEARCH_REGION_TABLE, 'parcels', 'parcelsales', 'allparcels', 'objects', 'popularplaces', SEARCH_TABLE_EVENTS, 'classifieds', 'hostsregister' ) ) ) { error_log( 'Creating missing OpenSimSearch tables in ' . SEARCH_DB_NAME ); ossearch_db_tables( $SearchDB ); } From e73e3e95e2cf34aca05195d2c2bb2d9cc1c36744 Mon Sep 17 00:00:00 2001 From: Gudule Lapointe Date: Sun, 16 Mar 2025 21:57:13 +0100 Subject: [PATCH 10/20] osdb_cache_get() and osdb_cache_set() for persistent cache --- classes/class-grid.php | 24 +++++++++++--- classes/init.php | 6 ++-- includes/databases.php | 11 +++++-- includes/search.php | 73 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 100 insertions(+), 14 deletions(-) diff --git a/classes/class-grid.php b/classes/class-grid.php index 25322bd..0289e36 100644 --- a/classes/class-grid.php +++ b/classes/class-grid.php @@ -6,6 +6,8 @@ if( ! defined( 'OSHELPERS' ) ) { exit; } +require_once( dirname(__DIR__) . '/includes/functions.php' ); + class OpenSim_Grid { private static $grid_stats; private $grid_info; @@ -38,10 +40,12 @@ class OpenSim_Grid { // global $oshelpers_cache; $key = __METHOD__ . md5( serialize( func_get_args() ) ); - $grids_info = os_cache_get( 'grid_info', array() ); + $grids_info = os_cache_get( 'grids_info', array() ); if( isset( $grids_info[$key] ) ) { - error_log( 'Cache hit: ' . $key ); - return $grids_info[$key]; + $info = $grids_info[$key]; + if( ! empty( $info['gridname'] ) ) { + return $grids_info[$key]; + } } $HomeURI = OpenSim::get_option( 'Hypergrid.HomeURI' ); @@ -53,13 +57,19 @@ class OpenSim_Grid { } } + // If gridname not found, only try once to fetch grid info again + $info = os_cache_get( 'grid_info_' . $key, false ); + if( $info ) { + return $info; + } + $info = array(); // Fetch live info from grid using $login_uri/get_grid_info and parse xml result in array // Example xml result: $xml_url = $grid_uri . '/get_grid_info'; try { - $xml = simplexml_load_file( $xml_url ); + $xml = @simplexml_load_file( $xml_url ); } catch( Exception $e ) { $xml = false; } @@ -94,7 +104,11 @@ class OpenSim_Grid { // $oshelpers_cache[$key] = $info; $grids_info[$key] = $info; - os_cache_set( 'grid_info', $grids_info ); + os_cache_set( 'grids_info', $grids_info ); + if( is_array( $info ) && empty( $info['gridname'] ) ) { + $info['gridname'] = opensim_format_tp( $grid_uri, TPLINK_TXT ); + } + os_cache_set( 'grid_info_' . $key, $info ); return $info; } diff --git a/classes/init.php b/classes/init.php index 80867e3..20993ff 100644 --- a/classes/init.php +++ b/classes/init.php @@ -494,12 +494,12 @@ class OpenSim { $id = $string; try { - $id = transliterator_transliterate("Any-Latin; NFD; [:Nonspacing Mark:] Remove; NFC; [:Punctuation:] Remove; Lower();", $id ); - $id = preg_replace('/[-\s]+/', '-', $id ); + $id = @transliterator_transliterate("Any-Latin; NFD; [:Nonspacing Mark:] Remove; NFC; [:Punctuation:] Remove; Lower();", $id ); } catch ( Error $e ) { - error_log( 'Error sanitizing slug: ' . $e->getMessage() ); + error_log( 'Warning (php-intl missing) ' . $e->getMessage() ); $id = $string; } + $id = preg_replace('/[-\s]+/', '-', $id ); return $id; } diff --git a/includes/databases.php b/includes/databases.php index 9eb7389..beccea4 100644 --- a/includes/databases.php +++ b/includes/databases.php @@ -33,8 +33,15 @@ class OSPDO extends PDO { $trace = debug_backtrace()[0]; $trace = $trace['file'] . ':' . $trace['line']; - $statement = $this->prepare( $query, $options ); - $result = $statement->execute( $params ); + try { + $statement = $this->prepare( $query, $options ); + $result = $statement->execute( $params ); + } catch ( PDOException $e ) { + error_log( 'Error ' . $e->getCode() . ' ' . $e->getMessage() . ' query ' . print_r( $query, true ) . ' params ' . print_r( $params, true ) . ' ' . $trace ); + return false; + } + // $statement = $this->prepare( $query, $options ); + // $result = $statement->execute( $params ); if ( $result ) { return $statement; diff --git a/includes/search.php b/includes/search.php index 46a505b..442a5fb 100644 --- a/includes/search.php +++ b/includes/search.php @@ -165,10 +165,10 @@ function ossearch_db_tables( $db ) { ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; CREATE TABLE IF NOT EXISTS oshelpers_cache ( - `key` VARCHAR(255) NOT NULL, - `value` LONGBLOB, - `expires_at` TIMESTAMP NULL DEFAULT NULL, - PRIMARY KEY (`key`) + `cache_key` VARCHAR(255) NOT NULL, + `cache_value` LONGBLOB, + `cache_expires` int(10) default 0, + PRIMARY KEY (`cache_key`) ) ENGINE=InnoDB; " ); @@ -252,6 +252,71 @@ function ossearch_hostUnregister( $hostname, $port ) { } } +function osdb_cache_get( $key, $default = null ) { + global $SearchDB; + if ( ! $SearchDB ) { + return $default; + } + + $now = time(); + $query = $SearchDB->prepareAndExecute( + 'SELECT cache_value FROM oshelpers_cache WHERE cache_key = :key AND (cache_expires IS NULL OR cache_expires = 0 OR cache_expires > :now)', + array( + 'key' => $key, + 'now' => $now, + ), + ); + + if ( $query ) { + $result = $query->fetchAll( PDO::FETCH_ASSOC ); + if( is_array( $result ) && isset( $result[0] ) ) { + $raw = $result[0]['cache_value']; + + if ( $raw ) { + $value = json_decode( $raw, true ); + if( ! $value ) { + $value = $raw; + } + return $value; + } else if( $result ) { + return $result; + } + } + } + return $default; +} + +function osdb_cache_set( $key, $value, $expire_delay = 0 ) { + global $SearchDB; + if ( ! $SearchDB ) { + return false; + } + + $now = time(); + if( $expire_delay > $now ) { + $expires = $expire_delay; + } else if( ! empty( $expire_delay ) ) { + $expires = $now + $expire_delay; + } else { + $expires = 0; + } + + $sql = 'INSERT INTO oshelpers_cache (`cache_key`, `cache_value`, `cache_expires`) + VALUES (:key, :value, :expires) + ON DUPLICATE KEY UPDATE `cache_value` = :value, `cache_expires` = :expires'; + + $query = $SearchDB->prepareAndExecute( + $sql, + array( + 'key' => $key, + 'value' => json_encode( $value ), + 'expires' => $expires, + ), + ); + + return $query; +} + try { $SearchDB = new OSPDO( 'mysql:host=' . SEARCH_DB_HOST . ';dbname=' . SEARCH_DB_NAME, SEARCH_DB_USER, SEARCH_DB_PASS ); } catch ( PDOException $e ) { From e5b6074305c5ecbe625881b3b3e23bbff34a64eb Mon Sep 17 00:00:00 2001 From: Gudule Lapointe Date: Sun, 16 Mar 2025 22:14:27 +0100 Subject: [PATCH 11/20] * add directory_info.php script, prividing basic stats of search directory content --- directory_info.php | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 directory_info.php diff --git a/directory_info.php b/directory_info.php new file mode 100644 index 0000000..520643f --- /dev/null +++ b/directory_info.php @@ -0,0 +1,91 @@ +prepareAndExecute( + 'SELECT * FROM parcels + INNER JOIN ' . SEARCH_REGION_TABLE . ' AS r ON parcels.regionUUID = r.regionUUID', +); +$registered_parcels = $query->fetchAll(PDO::FETCH_ASSOC); +$grids_info = osdb_cache_get( 'grids_info', array() ); + +// Calculate stats per gatekeeperURL +$stats = []; +foreach ($registered_parcels as $host) { + $gatekeeperURL = $host['gatekeeperURL']; + $grids_info[$gatekeeperURL] = $grids_info[$gatekeeperURL] ?? OpenSim_Grid::get_grid_info($gatekeeperURL); + if (!isset($stats[$gatekeeperURL])) { + $stats[$gatekeeperURL] = [ + 'regions' => [], + 'parcels' => 0, + ]; + } + $stats[$gatekeeperURL]['parcels']++; + $region = $host['regionUUID']; + $stats[$gatekeeperURL]['regions'][$region] = true; +} + +osdb_cache_set( 'grids_info', $grids_info, 86400 ); + +$grid_count = count($stats); +$allregionscount = 0; +$allparcelscount = 0; +foreach ($stats as $gatekeeperURL => $data) { + $regionCount = count($data['regions']); + $allregionscount += $regionCount; + $parcelCount = $data['parcels']; + $allparcelscount += $parcelCount; + $gridname = $grids_info[$gatekeeperURL]['gridname'] ?? $gatekeeperURL; + $stats_display .= sprintf( + '%s (%d regions, %d parcels)   ', + $gatekeeperURL, + $gridname, + $regionCount, + $parcelCount + ); + + // "$gatekeeperURL ($regionCount regions, $parcelCount parcels)   "; +} + +$content = sprintf( + '%d grids, %d regions, %d parcels ', + $grid_count, + $allregionscount, + $allparcelscount +); +$content .= $stats_display; +// $content .= "

Grid info cache:

" . print_r($grids_info, true) . "

"; + +if(! empty($debug)) { + $content .= '
 

Debug data:

' . print_r( $debug, true ) . '
'; +} + +$sidebar_right= "Right"; +$sidebar_left = "Left"; + +// $content = $page->get_content(); + +// Last step is to load template to display the page. +require( 'templates/templates.php' ); From e997338e0f26b57d733f8eb4c0c8e20d77260e90 Mon Sep 17 00:00:00 2001 From: "Olivier van Helden (oshelpers)" Date: Mon, 31 Mar 2025 19:24:49 +0000 Subject: [PATCH 12/20] * new comprehensive installation instructions (merged from 3.x branch) --- INSTALLATION.md | 206 +++++++++++++++++++++++++++++ README-Gloebit.md | 22 ---- README.md | 80 +++++------- TROUBLESHOOTING.md | 251 ++++++++++++++++++++++++++++++++++++ includes/config.example.php | 103 ++++++++++++--- 5 files changed, 573 insertions(+), 89 deletions(-) create mode 100644 INSTALLATION.md delete mode 100644 README-Gloebit.md create mode 100644 TROUBLESHOOTING.md diff --git a/INSTALLATION.md b/INSTALLATION.md new file mode 100644 index 0000000..dd64ac6 --- /dev/null +++ b/INSTALLATION.md @@ -0,0 +1,206 @@ +# OpenSim Helpers Installation + +If you only need in-world search (places, land for sale, events), do not install the helpers, follow "Option 1" instructions in README.md to use 2do.directory service instead. + +If you use w4os WordPress plugin, you do not need to install helpers, they are already provided by the plugin. + +Proceed to full installation if you have at least one of these requirements +- you need to implement in-world currency +- you need to implement classifieds +- you prefer to implement your own search engine + +Even in one of these cases, it's a good idea to first use 2do directory service only, to make sure the basic requirements are met before proceeding to the full helpers install. + +## Get OpenSimulator up and running + +Download the latest OpenSimulator release from http://opensimulator.org/wiki/Download + +Extract the archive in a location outside your web root directory, e.g. `/opt/opensim/` or `/usr/local/share/opensim`. Standard OpenSimulator installation contains configuration files with db credentials and other sensitive data, they cannot be accessible directly from your website. + +Follow instructions in Robust(.HG).ini to get the grid up and running. + +**Keep it simple and stupid**: for a new installation, first only change the basic settings: BaseURL, Public and Private ports, ConnectionString. The other settings will be changed later according to helpers configuration. + +OpenSim Helpers should be compatible with any OpenSimulator 0.9.x release. Earlier versions are not supported anymore. + +## Add helpers/ on your website + +Copy the opensim helpers directory as helpers/ in your website document root. + +E.g. if your document root is `/var/www/html`, this directory should be in `/var/www/html/helpers/` and should be reachable by calling https://example.org/helpers/ + +Copy `includes/config.example.php` as `includes/config.php` and adjust values according to your Robust settings. + +Required: +- OPENSIM_GRID_NAME ("Your Grid") +- OPENSIM_LOGIN_URI ("yourgrid.org:8002") +- ROBUST_DB credentials (also used for standalone simulators) + +Optional: +- search settings +- currency settings + +### Database Configuration + +The helpers need access to various databases. You can use the same database as your Robust installation for everything, but for better security and organization, you might want to use separate databases: + +- **Robust DB**: Main OpenSimulator database (required) +- **Search DB**: For search functionality (required if using local search) +- **Currency DB**: For Podex/MoneyServer (required if using this currency system) + +Each database configuration in `config.php` needs host, database name, username and password. + +At this stage, the helpers should be accessible and working. Most of them would only display a blank page if called directly. + +Use cron or crontab to trigger the database update on a regular basis +```cron +0 * * * * curl -s http://example.org/helpers/parser.php +30 */2 * * * curl -s http://example.org/helpers/eventsparser.php +``` + +## Install required OpenSim modules (dll) + +According to your needs, copy modules provided in addons/bin in your opensim bin/ directory (alongside Robust.exe) or download them from their original authors. + +**Only copy the modules you actually need**: some of them might crash your simulator if not configured. + +To enable **in-world search**, you need: +- `OpenSimSearch.Modules.dll` + +To enable **Gloebit currency** +- `Gloebit.dll` + +To enable **Podex currency** (or fake currency) +- Install and run an instance of MoneyServer (this is an executable, not a DLL) +- Follow MoneyServer instruction to configure, create a certificate and a Banker avatar + +## Configure Robust grid-wide settings (or standalone simulator) + +According to your setup, you need to adjust some settings in one of these files: +- `Robust.HG.ini` (grid with hypergrid enabled) +- `Robust.ini` (private grid, hypergrid not enabled) +- `config-include/StandaloneCommon.ini` (standalone simulator, no grid) + +```Robust.HG.ini +[LoginService] + ; Currency = YC$ ;; Your Currency symbol, optional + + ;; V3 viewer search, not implemented yet in helpers + ;; This is not same as SearchURL value in OpenSim.ini + ; SearchURL = https://example.org/helpers/search.php + +[GridInfoService] + economy = https://example.org/helpers/ + + ;; V3 viewer search, not implemented yet in helpers + ;; This is not same as SearchURL value in OpenSim.ini + ; search = https://example.org/helpers/search.php +``` + +## Configure simulators (for grids only) + +Each simulator also needs specific settings. They are usually set in +- `OpenSim.ini` +- or `config-include/StandaloneCommon.ini` + +### Search Configuration + +To enable in-world search, add the following configuration to each simulator: + +```OpenSim.ini +[Search] + Module = OpenSimSearch + SearchURL = "http://example.org/helpers/query.php?gk=http://yourgrid.org:8002" + +[DataSnapshot] + index_sims = true + gridname = "Your Grid" + ;; Multiple data servers can be enabled to register on multiple engines + DATA_SRV_YourGrid = "https://example.org/helpers/register.php" + DATA_SRV_2do = "http://2do.directory/helpers/register.php" + ; DATA_SRV_OtherEngine = "http://example.org/register.php" +``` + +The search system works in two parts: +1. The `[DataSnapshot]` section tells the simulator to send parcel and region data to the specified helper URLs +2. The `[Search]` section tells the simulator where users' search queries should be sent + +Make sure both sections are properly configured for search to work correctly. + +### Currency Configuration + +#### For Gloebit currency: + +Gloebit is a third-party currency provider that works across multiple grids. It requires registering on the [Gloebit website](https://www.gloebit.com/) to get API keys. + +```OpenSim.ini +[Economy] + economymodule = Gloebit + economy = https://example.org/helpers/ + SellEnabled = true + ; PriceUpload = 0 + ; PriceGroupCreate = 0 +[Gloebit] + Enabled = true + GLBEnvironment = production + GLBKey = (your Gloebit app key) + GLBSecret = (your Gloebit app secret) + GLBOwnerName = Banker Name + GLBOwnerEmail = banker@example.org + ; GLBSpecificConnectionString = "Data Source=localhost;Database=gloebit;User ID=opensim;Password=your_password;Old Guids=true;" +``` + +If you have issues with Gloebit showing "TrustFailure" or "CERTIFICATE_VERIFY_FAILED" errors, please see the TROUBLESHOOTING.md file for specific instructions on fixing certificate issues on Linux. + +#### For Podex or fake currency: + +This method requires running a separate MoneyServer instance that handles the currency transactions. + +Step 1: Install and configure MoneyServer +- Download MoneyServer (not included in the helpers - you'll need to get it from the original source) +- **Important note**: MoneyServer is a standalone executable (.exe), not a DLL. Don't look for "MoneyServer.dll" as it doesn't exist +- Run MoneyServer.exe as a separate process on a different port than Robust (e.g., port 8004) +- Your processes should include: + - Robust.exe (grid services) + - MoneyServer.exe (currency services) + - OpenSim.exe (region simulators) + +Step 2: Configure OpenSim to use MoneyServer +```OpenSim.ini +[Economy] + economymodule = DTLMoneyModule + ; economymodule = DTLNSLMoneyModule ;; for some MoneyServer versions + + economy = https://example.org/helpers/ + SellEnabled = true + ; PriceUpload = 0 + ; PriceGroupCreate = 0 +``` + +Step 3: Configure MoneyServer settings +```MoneyServer.ini +;; See additional parameters in MoneyServer.ini.example +[MoneyServer] + EnableScriptSendMoney = true + MoneyScriptAccessKey = 1234567890 ; CHANGE THIS VALUE, copy it in includes/config.php +[MySql] + hostname = localhost + database = moneyserver ; Create this database separately + username = opensim + password = (your password) +``` + +Make sure the MoneyScriptAccessKey value in MoneyServer.ini matches the one in your config.php file. + +## Load new configuration + +- Restart Robust instance +- Restart MoneyServer (if using Podex/DTL currency) +- Restart all simulator instances +- Enable search for at least one parcel +- Set at least one parcel for sale +- Test in-world search, land sale, object sales... + +**Note:** the search database is refreshed by a cron task. Some data might not be available immediately. In the testing phase, it might be useful to restart region after a test change to ensure search data is immediately updated. + +See TROUBLESHOOTING.md if you encounter issues. diff --git a/README-Gloebit.md b/README-Gloebit.md deleted file mode 100644 index b242ab6..0000000 --- a/README-Gloebit.md +++ /dev/null @@ -1,22 +0,0 @@ -# Gloebit TrustFailure CERTIFICATE_VERIFY_FAILED - -In some Linux distributions, mono doesn't handle LetsEncrypt certificates, which happen to be the ones used by Gloebit and many website. - -If Gloebit doesn't work and you have messages like "TrustFailure (...) CERTIFICATE_VERIFY_FAILED" in your logs, don't worry, the fix is easy: - -1. Make sure package ca-certificates-mono is installed, or install it with the command - - ```bash - sudo apt install ca-certificates-mono - ``` - -2. Download Letsencrypt root certificate from [their website](https://letsencrypt.org/certificates/). You can pick the first one, ISRG Root X1, in pem format - -3. Make mono trust this root certificate - - ```bash - sudo cert-sync isrgrootx1.pem - # replace the name of the file if needed - ``` - -It should work immediately, you shoudln't need to restart the sim. diff --git a/README.md b/README.md index 2ac4d30..5f586a4 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,25 @@ # OpenSimulator Helpers -![Version 2.4.0](https://badgen.net/badge/Version/2.4.0/blue) ![Stable 2.4.0](https://badgen.net/badge/Stable/2.4.0/green) ![Requires PHP 7.3](https://badgen.net/badge/PHP/7.3/7884bf) ![License AGPLv3](https://badgen.net/badge/License/AGPLv3/552b55) +![Version 2.4.0](https://badgen.net/badge/Version/2.4.0/blue) +![Stable 2.4.0](https://badgen.net/badge/Stable/2.4.0/green) +![Requires PHP 5.7](https://badgen.net/badge/PHP/5.7/7884bf) +![License AGPLv3](https://badgen.net/badge/License/AGPLv3/552b55) -- Project URL: -- Donate: +Collection of PHP scripts to complement OpenSimulator features. ## Description -Helpers required to enable common functionalties like search, currency, events in OpenSimulator grids. +Collection of PHP scripts to enable OpenSimulator features that are not implemented in the core, like search, currency, events in OpenSimulator grids (see Features below). + +Most files are used directly by viewer to allow features not implemented in OpenSimulator, like classifieds, events, etc. They were initially based on a collection of different projects (see Credits below), but were entirely rewritten to use an unified code and set of parameters, as well as for integration in larger projects like [w4os OpenSim WordPress Interface](https://w4os.org/). Formerly known as Flexible Helper Scripts. +- Project URL: +- Donate: + ### Features - **In-world Search**: enable standard search in the viewer for places, land for sale, events and classifieds. @@ -22,71 +29,52 @@ Formerly known as Flexible Helper Scripts. - **Multi-grid**: can be used for standalone or closed grid as well as to provide a cross-grid search engine - **Unified library**: rewritten to allow easier integration in bigger projects +### Roadmap + +- [ ] Avatar authentication +- [ ] Avatar registration +- [ ] Web profiles +- [ ] Web assets server +- [ ] Web search +- [ ] Grid info +- [ ] Grid status +- [ ] Splash page + ## Installation -### Option 1: Just enable in-world search (don't install this) +For detailed installation instructions, please see [INSTALLATION.md](INSTALLATION.md). -Yes, my first suggestion is not to install this, funny, isn't it? Relying on an external search engine may have advantages: +If you encounter issues during installation or usage, please refer to [TROUBLESHOOTING.md](TROUBLESHOOTING.md). -- For small grids and standalone, it can be confusing to install the whole package, while it's easy to benefit of its main advantage. -- An external engine can collect and distribute data from several grids, which is good for an hypergrid-enabled installation. +### Quick Start Options -[2do directory](http://2do.directory/) provides a cross-grid search engine. This is the easiest solution if you don't have specific needs (and you don't need classifieds, which require access to both Robust and search databases). +#### Option 1: Just enable in-world search (no helpers installation) -1. Download [OpenSimSearch.Module.dll](https://github.com/GuduleLapointe/flexible_helper_scripts/tree/master/bin) and put it inside your OpenSim bin/ folder. +If you only need basic in-world search, you can use an external service like [2do directory](http://2do.directory/) without installing the helpers. -2. Add the following settings to each simulator OpenSim.ini file. Replace "Your Grid" and "yourgrid.org:8002" with your own values. +1. Download [OpenSimSearch.Module.dll](https://github.com/magicoli/opensim-helpers/tree/master/bin) and put it inside your OpenSim bin/ folder. +2. Add search settings to OpenSim.ini: ``` [Search] SearchURL = "https://2do.directory/helpers/query.php?gk=${Const|BaseURL}:${Const|PublicPort}" - ;SearchURL = "http://2do.directory/helpers/query.php?gk=http://yourgrid.org:8002" [DataSnapshot] index_sims = true gridname = "Your Grid" snapshot_cache_directory = "./DataSnapshot" DATA_SRV_2do = http://2do.directory/helpers/register.php - ;DATA_SRV_OtherEngine = http://example.org/helpers/register.py ``` 3. Restart the simulators. -### Option 2: WordPress website +#### Option 2: WordPress website -If you have a WordPress website, we suggest you to install [W4OS Plugin](https://w4os.org/) instead. It includes these helpers and a lot of other cool features like avatar registration, web profiles, web assets server, grid info (...), along with documented admin pages. +If you have a WordPress website, we recommend installing [W4OS Plugin](https://w4os.org/) which includes these helpers along with many other features like avatar registration and web profiles. -### Option 3: The real stuff +#### Option 3: Full helpers installation -If you want to keep the control, or combine with a wider range of services, here is how to setup your search engine. - -1. Copy the needed modules, according to your OpenSim version from this bin/ directory to your OpenSim bin/ directory. Copy only the ones you need, as OpenSim loads them all and will trigger errors if they are not properly configured. (e.g. do not copy MoneyServer if you use Gloebit and vice-versa, do not copy any of those if you don't plan to use currency.) -2. Copy the content of this directory on your web server, in a folder named "helpers" (or anything you want, adjust your settings accordingly). It should be reachable as something like -3. Copy includes/config.example.php to includes/config.php and edit with your grid and database settings -4. Edit your OpenSim config file for search (adjust your grid name, server url and login uri). Edit MoneyServer.ini or Gloebit.ini according to their instructions. - -``` -[Search] -SearchURL = "http://yourgrid.org/helpers/query.php?gk=http://yourgrid.org:8002" - -[DataSnapshot] -index_sims = true -gridname = "Your Grid" -snapshot_cache_directory = "./DataSnapshot" -DATA_SRV_YourGrid = http://yourgrid.org/helpers/register.php -;DATA_SRV_2do = http://2do.directory/helpers/register.php -``` - -(you can add several DATA_SRV_* lines if you want to register on multiple search engines) - -If you have trust issues with Gloebit (I mean seriously, if your console shows errors about Trust Failures), see README-Gloebit.md for instructions. - -Use a task scheduler to parse data regularly. It's useless to parse data too often, as they don't change that fast, it's better to keep both search and opensim servers load low and avoid triggering spam reject. With cron, it could be something like this. - -``` -0 * * * * curl -s http://yourgrid.org/parser.php -30 */2 * * * curl -s http://yourgrid.org/eventsparser.php -``` +For a complete installation with all features (search, currency, events, etc.), see the detailed instructions in [INSTALLATION.md](INSTALLATION.md). ## Credits @@ -101,6 +89,6 @@ While most of the code is new or rewritten from scratch, portions of code may re - [Offline Messaging](http://opensimulator.org/wiki/Offline_Messaging) - [w4os OpenSim Web Interface](https://w4os.org/) - [2do HYPEvents](https://2do.directory) -- [Speculoos' Flexible helper scripts](https://github.com/GuduleLapointe/flexible_helper_scripts) +- [Speculoos' Flexible helper scripts](https://github.com/magicoli/opensim-helpers) Unless otherwise specified, code is distributed as part of Flexible Helper Scripts package, under Affero GPL v3 license. diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md new file mode 100644 index 0000000..5d8a0ed --- /dev/null +++ b/TROUBLESHOOTING.md @@ -0,0 +1,251 @@ +# Troubleshooting + +Before troubleshooting a specific issue, make sure the basics are met: +- The grid is up and running, you can log in-world properly +- The website is up and running +- You can reach your helpers subfolder (e.g. https://example.org/helpers/). It should display the opensim-helpers version. + +## Search Not Working + +1. **Verify Module Installation** + - Make sure `OpenSimSearch.Modules.dll` is in your OpenSim bin/ directory + +2. **Check Configuration** + - Ensure both the `[Search]` and `[DataSnapshot]` sections are properly configured in OpenSim.ini + - Double check the URLs in your configuration (they should point to your helpers installation) + +3. **Verify Data Collection** + - Make sure cron jobs are running (parser.php and eventsparser.php) + - Check your search database tables to see if they contain data: + ```sql + SELECT * FROM regions LIMIT 10; + SELECT * FROM parcels LIMIT 10; + ``` + +4. **Force Data Update** + - Try restarting the affected region after making configuration changes + - Manually run `parser.php` to force an update of search data + +5. **Viewer Settings** + - Make sure you're using the right search server in your viewer + - Some features like places search require clicking "All" or "Anywhere" to display results + +### How to Use Search in Viewer + +Many users are confused about how to actually use search once it's configured. Here's how: + +1. **Places Search**: + - Click the Search button in the bottom taskbar of the viewer + - Select "Places" tab + - Choose "All" or "Anywhere" in the dropdown + - Type your search term and press Enter + +2. **Land Sales**: + - Click the Search button + - Select "Land Sales" tab + - Set your criteria and press Search + - Make sure some parcels are actually for sale on your grid + +3. **Events**: + - Click the Search button + - Select "Events" tab + - Choose the category or "All" and press Search + - Note: Events need to be added to the database before they appear + +4. **Classifieds**: + - Click the Search button + - Select "Classifieds" tab + - Type a search term and press Enter + +If search is properly configured but no results appear, it's likely that either: +- The search data hasn't been collected yet (wait for cron job or run parser.php manually) +- There are no matching items in your grid +- You need to use "All" or "Anywhere" instead of limiting to a specific region + +### Classic Search vs. Web Search in Modern Viewers + +It's important to understand that there are two different search systems in modern viewers: + +1. **Classic Search Tabs** (implemented by this project): + - Places, Land Sales, Events, and Classifieds tabs + - These use the OpenSimSearch module and the helpers in this project + - They are accessed through the individual tabs in the search panel + - These work with the OpenSimSearch.Modules.dll and the search databases + - **Configuration**: These use the `SearchURL` parameter in `[Search]` section of OpenSim.ini (simulator config) + ```ini + [Search] + Module = OpenSimSearch + SearchURL = "http://example.org/helpers/query.php?gk=http://yourgrid.org:8002" + ``` + +2. **Global/Web Search** (not implemented in this project yet): + - In modern viewers like Firestorm, there's also a "Web" or "All" tab + - This is meant to load a web page with more advanced search capabilities + - This feature is **not yet implemented** in the helpers + - This tab will display the page provided by a different search URL setting + - **Configuration**: This uses the `SearchURL` parameter in `[LoginService]` section of Robust.ini (grid config) + ```ini + [LoginService] + SearchURL = "http://example.org/search.php" + ``` + + **What to put at this URL while waiting for implementation:** + - You can create a simple HTML page explaining search functionality + - You can redirect to your grid's website or a custom search page + - You can set it to a third-party search engine like opensimworld.com + - Note: Always set this to a valid URL to avoid errors in server logs + +When troubleshooting search, make sure you're testing the right type of search and that you've configured the correct URL in the appropriate configuration file: +- Use the specific tabs (Places, Land Sales, Events, Classifieds) to test the functionality provided by these helpers +- The "Web" or "All" tab requires a separate web interface that is not part of this package yet + +## Currency Issues + +### Gloebit Issues + +1. **Certificate/SSL Problems** + If you're seeing errors like "TrustFailure" or "CERTIFICATE_VERIFY_FAILED": + + ```bash + sudo apt install ca-certificates-mono + sudo cert-sync /etc/ssl/certs/ca-certificates.crt + ``` + + For LetsEncrypt certificates specifically: + ```bash + # Download ISRG Root X1 certificate + wget https://letsencrypt.org/certs/isrgrootx1.pem + sudo cert-sync isrgrootx1.pem + ``` + +2. **Configuration Issues** + - Verify that `Gloebit.dll` is correctly copied to the bin/ directory + - Ensure your API keys are correct in OpenSim.ini + - Check that the `economy` URL in OpenSim.ini points to your helpers installation + +### Podex/MoneyServer Issues + +1. **MoneyServer Confusion** + - **Important**: MoneyServer is a standalone executable program, not a DLL + - You need to download and run MoneyServer.exe separately from OpenSim + - The simulator module that connects to MoneyServer is called DTLMoneyModule.dll + - Do not look for a file named "MoneyServer.dll" as it doesn't exist + +2. **Running MoneyServer** + - MoneyServer should run as a separate process like Robust + - It typically uses a different port (e.g., 8004) than Robust (8002/8003) + - The process hierarchy should be: + - Robust.exe (grid services) + - MoneyServer.exe (currency services) + - OpenSim.exe (one or more region simulators) + +3. **MySQL SSL Issues** + If you're having connection issues with MySQL, try disabling SSL for the connection: + + ``` + [mysqld] + ssl=0 + skip-ssl + disable-ssl + ``` + +4. **Configuration Mismatch** + - Make sure MoneyScriptAccessKey in MoneyServer.ini matches the one in config.php + - Verify database settings in MoneyServer.ini + - Check that `DTLMoneyModule` (or `DTLNSLMoneyModule`) is properly configured in OpenSim.ini + +## Database Issues + +### Database Configuration + +1. **Multiple Databases** + - OpenSim helpers can use multiple databases for different functions: + - Robust database (grid data) + - Search database (can be the same as Robust) + - MoneyServer database (separate for Podex/DTL) + - In config.php, make sure each database section has correct credentials + +2. **Database Creation** + - The Robust database is created during OpenSim installation + - The Search database tables are created automatically by the helpers + - For MoneyServer, you may need to create a new database: + ```sql + CREATE DATABASE moneyserver; + GRANT ALL PRIVILEGES ON moneyserver.* TO 'opensim'@'localhost'; + ``` + +3. **Connection Failures** + - Verify database credentials in config.php + - Check that the database server is running and accessible + - Ensure the user has appropriate permissions + +4. **Missing Tables** + - Tables will be created automatically by the modules, but they need proper permissions + - If tables are missing, you might need to manually create them + +5. **Separate Databases** + - If using separate databases for different functions (search, currency), make sure each is properly configured + - For MoneyServer, a separate database is typically used and needs to be created beforehand + +## HTTPS/SSL Issues + +1. **Connection Failed** + - If using HTTPS URLs and experiencing connection issues, try switching to HTTP for testing + - Ensure your web server has a valid SSL certificate + +2. **Mono Certificate Issues** + - Mono has its own certificate store that needs to be updated + - Use cert-sync to update the mono certificate store (see Gloebit troubleshooting above) + +## Configuration Hierarchy + +Understanding which configuration goes where can be confusing: + +1. **Robust.ini / Robust.HG.ini (Grid-wide settings)** + - Contains grid-wide settings that affect all regions + - Set currency symbol and economy URL here + - Example: + ``` + [GridInfoService] + economy = https://example.org/helpers/ + [LoginService] + Currency = YC$ + ``` + +2. **OpenSim.ini (Simulator-specific)** + - Contains settings for regions run by this simulator + - Each simulator needs its own search and economy configuration + - You can test changes on one simulator before updating all + - Example: + ``` + [Economy] + economymodule = DTLMoneyModule + economy = https://example.org/helpers/ + ``` + +3. **MoneyServer.ini** + - Completely separate configuration file + - Only needed if using Podex/DTL currency + - Must match access key with config.php + +## General Troubleshooting + +1. **Check Log Files** + - OpenSim log files (typically in bin/OpenSim.log) + - Web server error logs + - MoneyServer logs (if using Podex) + +2. **PHP Errors** + - Enable PHP error reporting in config.php or .htaccess: + ```php + error_reporting(E_ALL); + ini_set('display_errors', 1); + ``` + +3. **Permissions** + - Ensure web server has write access to any directories requiring it + - Check file permissions on configuration files + +4. **Version Compatibility** + - These helpers are designed for OpenSimulator 0.9.x + - Some features might not work with older or newer versions diff --git a/includes/config.example.php b/includes/config.example.php index e4d7af9..bdd4a10 100644 --- a/includes/config.example.php +++ b/includes/config.example.php @@ -34,12 +34,42 @@ define( 'HYPEVENTS_URL', preg_replace( ':/$:', '', 'https://2do.directory/events * a multi-grid search engine). In this case search will only provide results * for places, land for sale and events. */ -define( 'OPENSIM_DB', true ); // Set to false for search only, see above -define( 'OPENSIM_DB_HOST', 'localhost' ); -define( 'OPENSIM_DB_NAME', 'opensim' ); -define( 'OPENSIM_DB_USER', 'opensim' ); -define( 'OPENSIM_DB_PASS', 'password' ); -define( 'SEARCH_TABLE_EVENTS', 'events' ); + +// Main database is now set with ROBUST_DB constant. Use of OPENSIM_DB_* constants will be deprecated. +define( 'ROBUST_DB', array ( + 'host' => 'localhost', + 'port' => 3306, + 'name' => 'opensim', + 'user' => 'opensim', + 'pass' => '', +) ); + +/** + * Robust console settings. + * + * It will allow more interactions with the Robust server in the future. + * e.g. access to some region information without requiring a direct connection + * to each simulator. + */ +define( 'ROBUST_CONSOLE', array( + // 'ConsoleUser' => 'user', + // 'ConsolePass' => 'password', + // 'ConsolePort' => 8004, +) ); + +/** + * OpenSim DB refers here to the main database, i.e. Robust DB in the case + * of a grid, or OpenSim db for a standalone simulator. It is only the historical + * name of the settings and can be left as aliases of Robust DB credentials. + * Transition is in progress to better differentiate the Robust/Standalone + * main database from potential simulators/regions specific databases. + */ +define( 'OPENSIM_DB', true ); // Set to false for search only, see below +define( 'OPENSIM_DB_HOST', ROBUST_DB['host'] ); +define( 'OPENSIM_DB_PORT', ROBUST_DB['port'] ); +define( 'OPENSIM_DB_NAME', ROBUST_DB['name'] ); +define( 'OPENSIM_DB_USER', ROBUST_DB['user'] ); +define( 'OPENSIM_DB_PASS', ROBUST_DB['pass'] ); /** * Search database credentials and settings. @@ -49,17 +79,29 @@ define( 'SEARCH_TABLE_EVENTS', 'events' ); * - strongly recommended if the search engine is shared by several grids * - recommended and more efficient for large and/or hypergrid-enabled grids * - optional for closed grids and standalone simulators - * These are recommendations, the Robust database can safely be used instead. + * These are recommendations, the Robust/Main database can safely be used + * in all cases. */ -define( 'SEARCH_DB_HOST', OPENSIM_DB_HOST ); -define( 'SEARCH_DB_NAME', OPENSIM_DB_NAME ); -define( 'SEARCH_DB_USER', OPENSIM_DB_USER ); -define( 'SEARCH_DB_PASS', OPENSIM_DB_PASS ); + +define( 'SEARCH_DB', array( + 'host' => OPENSIM_DB_HOST, + 'port' => null, // Leave null for default port + 'name' => OPENSIM_DB_NAME, + 'user' => OPENSIM_DB_USER, + 'pass' => OPENSIM_DB_PASS, +) ); +define( 'SEARCH_DB_HOST', SEARCH_DB['host'] ); +define( 'SEARCH_DB_PORT', SEARCH_DB['port'] ); +define( 'SEARCH_DB_NAME', SEARCH_DB['name'] ); +define( 'SEARCH_DB_USER', SEARCH_DB['user'] ); +define( 'SEARCH_DB_PASS', SEARCH_DB['pass'] ); + +define( 'SEARCH_TABLE_EVENTS', 'events' ); /** - * Other registrars to forward hosts registrations. + * Other registrars to forward hosts registrations (deprecated) * - * This method is not needed as with current OpenSim server (0.9.x) which allow + * This method is not needed since OpenSim server (0.9.x) which allow * specifying multiple registrars, but could be used in the future to implement * peer to peer information sharing. * @@ -68,6 +110,7 @@ define( 'SEARCH_DB_PASS', OPENSIM_DB_PASS ); define( 'SEARCH_REGISTRARS', array( + // 'http://yourgrid.org/helpers/register.php', // 'http://2do.directory/helpers/register.php', // 'http://metaverseink.com/cgi-bin/register.py', ) @@ -78,10 +121,19 @@ define( * Needed if currency is enabled on OpenSim server. * A dedicated database is recommended, but not mandatory. */ -define( 'CURRENCY_DB_HOST', OPENSIM_DB_HOST ); -define( 'CURRENCY_DB_NAME', OPENSIM_DB_NAME ); -define( 'CURRENCY_DB_USER', OPENSIM_DB_USER ); -define( 'CURRENCY_DB_PASS', OPENSIM_DB_PASS ); +define( 'CURRENCY_DB', array( + 'host' => OPENSIM_DB_HOST, + 'port' => null, // Leave null for default port + 'name' => OPENSIM_DB_NAME, + 'user' => OPENSIM_DB_USER, + 'pass' => OPENSIM_DB_PASS, +) ); +define( 'CURRENCY_DB_HOST', CURRENCY_DB['host'] ); +define( 'CURRENCY_DB_PORT', CURRENCY_DB['port'] ); +define( 'CURRENCY_DB_NAME', CURRENCY_DB['name'] ); +define( 'CURRENCY_DB_USER', CURRENCY_DB['user'] ); +define( 'CURRENCY_DB_PASS', CURRENCY_DB['pass'] ); + define( 'CURRENCY_MONEY_TBL', 'balances' ); define( 'CURRENCY_TRANSACTION_TBL', 'transactions' ); @@ -110,10 +162,19 @@ if ( OPENSIM_USE_UTC_TIME ) { * * @var [type] */ -define( 'OFFLINE_DB_HOST', OPENSIM_DB_HOST ); -define( 'OFFLINE_DB_NAME', OPENSIM_DB_NAME ); -define( 'OFFLINE_DB_USER', OPENSIM_DB_USER ); -define( 'OFFLINE_DB_PASS', OPENSIM_DB_PASS ); +define( 'OFFLINE_DB', array( + 'host' => OPENSIM_DB_HOST, + 'port' => null, // Leave null for default port + 'name' => OPENSIM_DB_NAME, + 'user' => OPENSIM_DB_USER, + 'pass' => OPENSIM_DB_PASS, +) ); +define( 'OFFLINE_DB_HOST', OFFLINE_DB['host'] ); +define( 'OFFLINE_DB_PORT', OFFLINE_DB['port'] ); +define( 'OFFLINE_DB_NAME', OFFLINE_DB['name'] ); +define( 'OFFLINE_DB_USER', OFFLINE_DB['user'] ); +define( 'OFFLINE_DB_PASS', OFFLINE_DB['pass'] ); + define( 'OFFLINE_MESSAGE_TBL', 'im_offline' ); // Same DB as Offline Module V2? /** From 660e8796ff75a9decb36a1ec0baf5e1a9c5b3537 Mon Sep 17 00:00:00 2001 From: "Olivier van Helden (oshelpers)" Date: Thu, 3 Apr 2025 21:46:04 +0000 Subject: [PATCH 13/20] fix invalid code in INSTALLATION.md --- INSTALLATION.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/INSTALLATION.md b/INSTALLATION.md index dd64ac6..a826734 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -81,7 +81,8 @@ According to your setup, you need to adjust some settings in one of these files: - `Robust.ini` (private grid, hypergrid not enabled) - `config-include/StandaloneCommon.ini` (standalone simulator, no grid) -```Robust.HG.ini +```ini +;; Robust.HG.ini [LoginService] ; Currency = YC$ ;; Your Currency symbol, optional @@ -107,7 +108,8 @@ Each simulator also needs specific settings. They are usually set in To enable in-world search, add the following configuration to each simulator: -```OpenSim.ini +```ini +;; OpenSim.ini [Search] Module = OpenSimSearch SearchURL = "http://example.org/helpers/query.php?gk=http://yourgrid.org:8002" @@ -133,7 +135,8 @@ Make sure both sections are properly configured for search to work correctly. Gloebit is a third-party currency provider that works across multiple grids. It requires registering on the [Gloebit website](https://www.gloebit.com/) to get API keys. -```OpenSim.ini +```ini +;; OpenSim.ini [Economy] economymodule = Gloebit economy = https://example.org/helpers/ @@ -166,7 +169,8 @@ Step 1: Install and configure MoneyServer - OpenSim.exe (region simulators) Step 2: Configure OpenSim to use MoneyServer -```OpenSim.ini +```ini +;; OpenSim.ini [Economy] economymodule = DTLMoneyModule ; economymodule = DTLNSLMoneyModule ;; for some MoneyServer versions From 85e6354734cd99dd062aaf910a388101b031c269 Mon Sep 17 00:00:00 2001 From: "Olivier van Helden (oshelpers)" Date: Fri, 4 Apr 2025 00:53:46 +0000 Subject: [PATCH 14/20] fix more invalid code in INSTALLATION.md --- INSTALLATION.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/INSTALLATION.md b/INSTALLATION.md index a826734..1b92e9a 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -182,7 +182,9 @@ Step 2: Configure OpenSim to use MoneyServer ``` Step 3: Configure MoneyServer settings -```MoneyServer.ini +```ini +;; MoneyServer.ini + ;; See additional parameters in MoneyServer.ini.example [MoneyServer] EnableScriptSendMoney = true From a165ddcfacff790bb82c6e88d7febf346ae151e9 Mon Sep 17 00:00:00 2001 From: "Olivier van Helden (oshelpers)" Date: Fri, 4 Apr 2025 02:28:33 +0000 Subject: [PATCH 15/20] v2.5.0 * new comprehensive installation instructions (merged from 3.x branch) * add directory_info.php script, prividing basic stats of search directory content * fix error notification displayed 3 times when no place search results * fix apache xml error (expat reports error code 3) for empty search results * fix duplicate TPLINK* constant declarations * fix wrong URLs in cron example * new textgen.php script (create dynamic texture from url) * fix pos not mandatory for hop links in opensim_format_tp (merge changes from w4os project) * use SEARCH_TABLE_EVENTS constant instead of default events table name * offline message : move sender info after the message for better display in mailbox list --- .version | 2 +- README.md | 4 +- composer.lock | 670 +++++++++++++++++++++++++++----------------------- 3 files changed, 365 insertions(+), 311 deletions(-) diff --git a/.version b/.version index 9183195..fad066f 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.4.0 \ No newline at end of file +2.5.0 \ No newline at end of file diff --git a/README.md b/README.md index 5f586a4..5d23ccf 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # OpenSimulator Helpers -![Version 2.4.0](https://badgen.net/badge/Version/2.4.0/blue) -![Stable 2.4.0](https://badgen.net/badge/Stable/2.4.0/green) +![Version 2.5.0](https://badgen.net/badge/Version/2.5.0/blue) +![Stable 2.5.0](https://badgen.net/badge/Stable/2.5.0/green) ![Requires PHP 5.7](https://badgen.net/badge/PHP/5.7/7884bf) ![License AGPLv3](https://badgen.net/badge/License/AGPLv3/552b55) diff --git a/composer.lock b/composer.lock index e8789b4..3541542 100644 --- a/composer.lock +++ b/composer.lock @@ -206,32 +206,32 @@ }, { "name": "consolidation/log", - "version": "2.1.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/consolidation/log.git", - "reference": "3ad08dc57e8aff9400111bad36beb0ed387fe6a9" + "reference": "c27a3beb36137c141ccbce0d89f64befb243c015" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/3ad08dc57e8aff9400111bad36beb0ed387fe6a9", - "reference": "3ad08dc57e8aff9400111bad36beb0ed387fe6a9", + "url": "https://api.github.com/repos/consolidation/log/zipball/c27a3beb36137c141ccbce0d89f64befb243c015", + "reference": "c27a3beb36137c141ccbce0d89f64befb243c015", "shasum": "" }, "require": { - "php": ">=7.1.3", - "psr/log": "^1 || ^2", - "symfony/console": "^4 || ^5 || ^6" + "php": ">=8.0.0", + "psr/log": "^3", + "symfony/console": "^5 || ^6 || ^7" }, "require-dev": { - "phpunit/phpunit": ">=7.5.20", + "phpunit/phpunit": "^7.5.20 || ^8 || ^9", "squizlabs/php_codesniffer": "^3", "yoast/phpunit-polyfills": "^0.2.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.x-dev" + "platform": { + "php": "8.2.17" } }, "autoload": { @@ -252,9 +252,9 @@ "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", "support": { "issues": "https://github.com/consolidation/log/issues", - "source": "https://github.com/consolidation/log/tree/2.1.1" + "source": "https://github.com/consolidation/log/tree/3.1.0" }, - "time": "2022-02-24T04:27:32+00:00" + "time": "2024-04-04T23:50:25+00:00" }, { "name": "consolidation/output-formatters", @@ -312,32 +312,33 @@ }, { "name": "consolidation/robo", - "version": "4.0.2", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/consolidation/robo.git", - "reference": "ccf80963abf11bdb8e90659aa99a7449b21e9452" + "reference": "55a272370940607649e5c46eb173c5c54f7c166d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/robo/zipball/ccf80963abf11bdb8e90659aa99a7449b21e9452", - "reference": "ccf80963abf11bdb8e90659aa99a7449b21e9452", + "url": "https://api.github.com/repos/consolidation/robo/zipball/55a272370940607649e5c46eb173c5c54f7c166d", + "reference": "55a272370940607649e5c46eb173c5c54f7c166d", "shasum": "" }, "require": { - "consolidation/annotated-command": "^4.3", - "consolidation/config": "^1.2.1 || ^2.0.1", - "consolidation/log": "^1.1.1 || ^2.0.2", + "consolidation/annotated-command": "^4.8.1", + "consolidation/config": "^2.0.1", + "consolidation/log": "^2.0.2 || ^3", "consolidation/output-formatters": "^4.1.2", "consolidation/self-update": "^2.0", "league/container": "^3.3.1 || ^4.0", - "php": ">=7.1.3", - "symfony/console": "^4.4.19 || ^5 || ^6", - "symfony/event-dispatcher": "^4.4.19 || ^5 || ^6", - "symfony/filesystem": "^4.4.9 || ^5 || ^6", - "symfony/finder": "^4.4.9 || ^5 || ^6", - "symfony/process": "^4.4.9 || ^5 || ^6", - "symfony/yaml": "^4.4 || ^5 || ^6" + "php": ">=8.0", + "phpowermove/docblock": "^4.0", + "symfony/console": "^6", + "symfony/event-dispatcher": "^6", + "symfony/filesystem": "^6", + "symfony/finder": "^6", + "symfony/process": "^6", + "symfony/yaml": "^6" }, "conflict": { "codegyre/robo": "*" @@ -360,33 +361,6 @@ "robo" ], "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "config": { - "platform": { - "php": "7.1.3" - } - }, - "remove": [ - "codeception/phpunit-wrapper" - ], - "require": { - "symfony/finder": "^4.4.11", - "phpunit/phpunit": "^6", - "symfony/console": "^4.4.11", - "symfony/process": "^4.4.11", - "nikic/php-parser": "^2", - "symfony/filesystem": "^4.4.11", - "symfony/event-dispatcher": "^4.4.11" - } - } - }, - "branch-alias": { - "dev-main": "2.x-dev", - "dev-master": "2.x-dev" - } - }, "autoload": { "psr-4": { "Robo\\": "src" @@ -405,9 +379,9 @@ "description": "Modern task runner", "support": { "issues": "https://github.com/consolidation/robo/issues", - "source": "https://github.com/consolidation/robo/tree/4.0.2" + "source": "https://github.com/consolidation/robo/tree/4.0.6" }, - "time": "2022-04-21T09:29:58+00:00" + "time": "2023-04-30T21:49:04+00:00" }, { "name": "consolidation/self-update", @@ -541,27 +515,28 @@ }, { "name": "grasmash/expander", - "version": "2.0.3", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/grasmash/expander.git", - "reference": "b7cbc1f2fdf9a9c0e253a424c2a4058316b7cb6e" + "reference": "eea11b9afb0c32483b18b9009f4ca07b770e39f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/grasmash/expander/zipball/b7cbc1f2fdf9a9c0e253a424c2a4058316b7cb6e", - "reference": "b7cbc1f2fdf9a9c0e253a424c2a4058316b7cb6e", + "url": "https://api.github.com/repos/grasmash/expander/zipball/eea11b9afb0c32483b18b9009f4ca07b770e39f4", + "reference": "eea11b9afb0c32483b18b9009f4ca07b770e39f4", "shasum": "" }, "require": { "dflydev/dot-access-data": "^3.0.0", - "php": ">=7.1", - "psr/log": "^1 | ^2 | ^3" + "php": ">=8.0", + "psr/log": "^2 | ^3" }, "require-dev": { "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^6.0 || ^8.0 || ^9", - "squizlabs/php_codesniffer": "^2.7 || ^3.3" + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.3" }, "type": "library", "extra": { @@ -586,9 +561,9 @@ "description": "Expands internal property references in PHP arrays file.", "support": { "issues": "https://github.com/grasmash/expander/issues", - "source": "https://github.com/grasmash/expander/tree/2.0.3" + "source": "https://github.com/grasmash/expander/tree/3.0.1" }, - "time": "2022-04-25T22:17:46+00:00" + "time": "2024-11-25T23:28:05+00:00" }, { "name": "league/container", @@ -707,6 +682,162 @@ }, "time": "2023-07-21T01:56:17+00:00" }, + { + "name": "phootwork/collection", + "version": "v3.2.3", + "source": { + "type": "git", + "url": "https://github.com/phootwork/collection.git", + "reference": "46dde20420fba17766c89200bc3ff91d3e58eafa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phootwork/collection/zipball/46dde20420fba17766c89200bc3ff91d3e58eafa", + "reference": "46dde20420fba17766c89200bc3ff91d3e58eafa", + "shasum": "" + }, + "require": { + "phootwork/lang": "^3.0", + "php": ">=8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "phootwork\\collection\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thomas Gossmann", + "homepage": "http://gos.si" + } + ], + "description": "The phootwork library fills gaps in the php language and provides better solutions than the existing ones php offers.", + "homepage": "https://phootwork.github.io/collection/", + "keywords": [ + "Array object", + "Text object", + "collection", + "collections", + "json", + "list", + "map", + "queue", + "set", + "stack", + "xml" + ], + "support": { + "issues": "https://github.com/phootwork/phootwork/issues", + "source": "https://github.com/phootwork/collection/tree/v3.2.3" + }, + "time": "2022-08-27T12:51:24+00:00" + }, + { + "name": "phootwork/lang", + "version": "v3.2.3", + "source": { + "type": "git", + "url": "https://github.com/phootwork/lang.git", + "reference": "52ec8cce740ce1c424eef02f43b43d5ddfec7b5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phootwork/lang/zipball/52ec8cce740ce1c424eef02f43b43d5ddfec7b5e", + "reference": "52ec8cce740ce1c424eef02f43b43d5ddfec7b5e", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/polyfill-php81": "^1.22" + }, + "type": "library", + "autoload": { + "psr-4": { + "phootwork\\lang\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thomas Gossmann", + "homepage": "http://gos.si" + } + ], + "description": "Missing PHP language constructs", + "homepage": "https://phootwork.github.io/lang/", + "keywords": [ + "array", + "comparator", + "comparison", + "string" + ], + "support": { + "issues": "https://github.com/phootwork/phootwork/issues", + "source": "https://github.com/phootwork/lang/tree/v3.2.3" + }, + "time": "2024-10-03T13:43:19+00:00" + }, + { + "name": "phpowermove/docblock", + "version": "v4.0", + "source": { + "type": "git", + "url": "https://github.com/phpowermove/docblock.git", + "reference": "a73f6e17b7d4e1b92ca5378c248c952c9fae7826" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpowermove/docblock/zipball/a73f6e17b7d4e1b92ca5378c248c952c9fae7826", + "reference": "a73f6e17b7d4e1b92ca5378c248c952c9fae7826", + "shasum": "" + }, + "require": { + "phootwork/collection": "^3.0", + "phootwork/lang": "^3.0", + "php": ">=8.0" + }, + "require-dev": { + "phootwork/php-cs-fixer-config": "^0.4", + "phpunit/phpunit": "^9.0", + "psalm/phar": "^4.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpowermove\\docblock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thomas Gossmann", + "homepage": "http://gos.si" + } + ], + "description": "PHP Docblock parser and generator. An API to read and write Docblocks.", + "keywords": [ + "docblock", + "generator", + "parser" + ], + "support": { + "issues": "https://github.com/phpowermove/docblock/issues", + "source": "https://github.com/phpowermove/docblock/tree/v4.0" + }, + "time": "2021-09-22T16:57:06+00:00" + }, { "name": "psr/container", "version": "2.0.2", @@ -812,30 +943,30 @@ }, { "name": "psr/log", - "version": "1.1.4", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -856,58 +987,53 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "symfony/console", - "version": "v5.4.47", + "version": "v6.4.20", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed" + "reference": "2e4af9c952617cc3f9559ff706aee420a8464c36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", - "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", + "url": "https://api.github.com/repos/symfony/console/zipball/2e4af9c952617cc3f9559ff706aee420a8464c36", + "reference": "2e4af9c952617cc3f9559ff706aee420a8464c36", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -941,7 +1067,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.47" + "source": "https://github.com/symfony/console/tree/v6.4.20" }, "funding": [ { @@ -957,24 +1083,24 @@ "type": "tidelift" } ], - "time": "2024-11-06T11:30:55+00:00" + "time": "2025-03-03T17:16:38+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.4", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/605389f2a7e5625f273b53960dc46aeaf9c62918", - "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { @@ -983,7 +1109,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.5-dev" } }, "autoload": { @@ -1008,7 +1134,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.4" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -1024,48 +1150,43 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.4.45", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9" + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/72982eb416f61003e9bb6e91f8b3213600dcf9e9", - "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher-contracts": "^2|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<4.4" + "symfony/dependency-injection": "<5.4", + "symfony/service-contracts": "<2.5" }, "provide": { "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0" + "symfony/event-dispatcher-implementation": "2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -1093,7 +1214,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.45" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.13" }, "funding": [ { @@ -1109,29 +1230,26 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v2.5.4", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f" + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f", - "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "psr/event-dispatcher": "^1" }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, "type": "library", "extra": { "thanks": { @@ -1139,7 +1257,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.5-dev" } }, "autoload": { @@ -1172,7 +1290,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.4" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" }, "funding": [ { @@ -1188,30 +1306,29 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/filesystem", - "version": "v5.4.45", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "57c8294ed37d4a055b77057827c67f9558c95c54" + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/57c8294ed37d4a055b77057827c67f9558c95c54", - "reference": "57c8294ed37d4a055b77057827c67f9558c95c54", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" + "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^5.4|^6.4" + "symfony/process": "^5.4|^6.4|^7.0" }, "type": "library", "autoload": { @@ -1239,7 +1356,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.45" + "source": "https://github.com/symfony/filesystem/tree/v6.4.13" }, "funding": [ { @@ -1255,26 +1372,27 @@ "type": "tidelift" } ], - "time": "2024-10-22T13:05:35+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { "name": "symfony/finder", - "version": "v5.4.45", + "version": "v6.4.17", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "63741784cd7b9967975eec610b256eed3ede022b" + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/63741784cd7b9967975eec610b256eed3ede022b", - "reference": "63741784cd7b9967975eec610b256eed3ede022b", + "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" }, "type": "library", "autoload": { @@ -1302,7 +1420,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.45" + "source": "https://github.com/symfony/finder/tree/v6.4.17" }, "funding": [ { @@ -1318,7 +1436,7 @@ "type": "tidelift" } ], - "time": "2024-09-28T13:32:08+00:00" + "time": "2024-12-29T13:51:37+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1639,17 +1757,17 @@ "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-php73", + "name": "symfony/polyfill-php81", "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { @@ -1667,7 +1785,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, "classmap": [ "Resources/stubs" @@ -1687,7 +1805,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -1696,87 +1814,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.31.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" }, "funding": [ { @@ -1796,21 +1834,20 @@ }, { "name": "symfony/process", - "version": "v5.4.47", + "version": "v6.4.20", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d" + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5d1662fb32ebc94f17ddb8d635454a776066733d", - "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d", + "url": "https://api.github.com/repos/symfony/process/zipball/e2a61c16af36c9a07e5c9906498b73e091949a20", + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -1838,7 +1875,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.47" + "source": "https://github.com/symfony/process/tree/v6.4.20" }, "funding": [ { @@ -1854,39 +1891,47 @@ "type": "tidelift" } ], - "time": "2024-11-06T11:36:42+00:00" + "time": "2025-03-10T17:11:00+00:00" }, { "name": "symfony/service-contracts", - "version": "v1.1.2", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "191afdcb5804db960d26d8566b7e9a2843cab3a0" + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/191afdcb5804db960d26d8566b7e9a2843cab3a0", - "reference": "191afdcb5804db960d26d8566b7e9a2843cab3a0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, - "suggest": { - "psr/container": "", - "symfony/service-implementation": "" + "conflict": { + "ext-psr": "<1.1|>=2" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-master": "1.1-dev" + "dev-main": "3.5-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1913,40 +1958,55 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v1.1.2" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, - "time": "2019-05-28T07:50:59+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/string", - "version": "v5.4.47", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "136ca7d72f72b599f2631aca474a4f8e26719799" + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/136ca7d72f72b599f2631aca474a4f8e26719799", - "reference": "136ca7d72f72b599f2631aca474a4f8e26719799", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": ">=3.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -1985,7 +2045,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.47" + "source": "https://github.com/symfony/string/tree/v7.2.0" }, "funding": [ { @@ -2001,35 +2061,32 @@ "type": "tidelift" } ], - "time": "2024-11-10T20:33:58+00:00" + "time": "2024-11-13T13:31:26+00:00" }, { "name": "symfony/yaml", - "version": "v5.4.45", + "version": "v6.4.20", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "a454d47278cc16a5db371fe73ae66a78a633371e" + "reference": "28ee818fce4a73ac1474346b94e4b966f665c53f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a454d47278cc16a5db371fe73ae66a78a633371e", - "reference": "a454d47278cc16a5db371fe73ae66a78a633371e", + "url": "https://api.github.com/repos/symfony/yaml/zipball/28ee818fce4a73ac1474346b94e4b966f665c53f", + "reference": "28ee818fce4a73ac1474346b94e4b966f665c53f", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<5.3" + "symfony/console": "<5.4" }, "require-dev": { - "symfony/console": "^5.3|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "symfony/console": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -2060,7 +2117,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.45" + "source": "https://github.com/symfony/yaml/tree/v6.4.20" }, "funding": [ { @@ -2076,18 +2133,15 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2025-02-27T20:15:30+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "platform-overrides": { - "php": "7.4" - }, + "platform": {}, + "platform-dev": {}, "plugin-api-version": "2.6.0" } From 5e5f4c483b3437f1ff21a6c51e6fdb1fea67d2f2 Mon Sep 17 00:00:00 2001 From: Olivier van Helden Date: Fri, 4 Apr 2025 18:22:49 -0400 Subject: [PATCH 16/20] Create FUNDING.yml --- .github/FUNDING.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..e59c482 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,15 @@ +# These are supported funding model platforms + +github: GuduleLapointe +patreon: # Replace with a single Patreon username +open_collective: speculoosworld +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +polar: # Replace with a single Polar username +buy_me_a_coffee: speculoos.world +thanks_dev: # Replace with a single thanks.dev username +custom: https://magiiic.com/donate/project/?project=php-site-generator From a87427c29127e9fb800707ce68b6dd819821ca9f Mon Sep 17 00:00:00 2001 From: Olivier van Helden Date: Fri, 4 Apr 2025 18:31:28 -0400 Subject: [PATCH 17/20] Update FUNDING.yml --- .github/FUNDING.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index e59c482..bbadfdd 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,8 +1,9 @@ # These are supported funding model platforms github: GuduleLapointe -patreon: # Replace with a single Patreon username +buy_me_a_coffee: speculoos.world open_collective: speculoosworld +patreon: # Replace with a single Patreon username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry @@ -10,6 +11,5 @@ liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry polar: # Replace with a single Polar username -buy_me_a_coffee: speculoos.world thanks_dev: # Replace with a single thanks.dev username custom: https://magiiic.com/donate/project/?project=php-site-generator From dfc77898d540a13314354af0d21105874f1c0ae4 Mon Sep 17 00:00:00 2001 From: Olivier van Helden Date: Fri, 4 Apr 2025 18:49:03 -0400 Subject: [PATCH 18/20] Update FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index bbadfdd..7d6d541 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -12,4 +12,4 @@ issuehunt: # Replace with a single IssueHunt username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry polar: # Replace with a single Polar username thanks_dev: # Replace with a single thanks.dev username -custom: https://magiiic.com/donate/project/?project=php-site-generator +custom: https://magiiic.com/donate/project/?project=opensim-helpers From cbd226202113c5d9ea5436a5aaaadc43cdbb6c1d Mon Sep 17 00:00:00 2001 From: "Olivier van Helden (oshelpers)" Date: Fri, 4 Apr 2025 23:30:32 +0000 Subject: [PATCH 19/20] * restore wp-load.php * Revert "ignore wp-load.php" This reverts commit fbc3071572a0f3caa6efedb658b99f47e555bc4c. --- .gitignore | 1 - wp-load.php | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 wp-load.php diff --git a/.gitignore b/.gitignore index 4b71385..81a9e6f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,3 @@ sources/ tmp/ includes/config.php /vendor/ -wp-load.php diff --git a/wp-load.php b/wp-load.php new file mode 100644 index 0000000..bab84a0 --- /dev/null +++ b/wp-load.php @@ -0,0 +1,71 @@ + + */ + +if ( ! defined( 'W4OS_PLUGIN' ) ) { + die; +} + +$url = parse_url( getenv( 'REQUEST_URI' ), PHP_URL_PATH ); + +if ( get_option( 'w4os_provide_economy_helpers' ) == true & ! empty( W4OS_GRID_INFO['economy'] ) ) { + $economy = parse_url( W4OS_GRID_INFO['economy'] )['path']; + if ( preg_match( ":^$economy(currency.php|landtool.php):", $url ) ) { + $helper = preg_replace( ":^$economy:", '', $url ); + require $helper; + die(); + } elseif ( $url == $economy ) { + // Probably the url check, just accept it + // TODO: redirect to support page if set + die(); + } +} + +if ( get_option( 'w4os_provide_offline_messages' ) == true & ! empty( W4OS_GRID_INFO['OfflineMessageURL'] ) ) { + $message = parse_url( W4OS_GRID_INFO['OfflineMessageURL'] )['path']; + if ( preg_match( ":^$message/(SaveMessage|RetrieveMessages|offlineim)/:", "$url/" ) ) { + require 'offline.php'; + } elseif ( $message == $url ) { + die(); // ignore but don't trigger an error + } +} + +if ( get_option( 'w4os_provide_search' ) == true ) { + if ( ! empty( get_option( 'w4os_search_url' ) ) ) { + $search = parse_url( get_option( 'w4os_search_url' ) )['path']; + if ( preg_match( ":^$search/:", "$url/" ) ) { + // error_log("search $search"); + require 'query.php'; + die(); + } + $parser = preg_replace( ':^//:', '/', dirname( $search ) . '/parser.php' ); + if ( preg_match( ":^$parser/:", "$url/" ) ) { + require 'parser.php'; + die(); + } + + if ( ! empty( get_option( 'w4os_hypevents_url' ) ) ) { + $hypevents = preg_replace( ':^//:', '/', dirname( $search ) . '/eventsparser.php' ); + if ( preg_match( ":^$hypevents:", "$url/" ) ) { + require 'eventsparser.php'; + die(); + } + } + } + + if ( ! empty( get_option( 'w4os_search_register' ) ) ) { + $register = parse_url( get_option( 'w4os_search_register' ) )['path']; + if ( preg_match( ":^$register:", "$url/" ) ) { + require 'register.php'; + } + } +} From 35581aaa17ebc7dc45039408cb8e7ecd2cd6c1de Mon Sep 17 00:00:00 2001 From: "Olivier van Helden (grd01)" Date: Sat, 5 Apr 2025 00:35:53 +0000 Subject: [PATCH 20/20] * added config template for wp (handled by w4os plugin --- includes/config.wp.php | 159 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 includes/config.wp.php diff --git a/includes/config.wp.php b/includes/config.wp.php new file mode 100644 index 0000000..010ef8c --- /dev/null +++ b/includes/config.wp.php @@ -0,0 +1,159 @@ + + */ + +/** + * Grid info + */ +define( 'OPENSIM_GRID_NAME', get_option( 'w4os_grid_name', 'OpenSimulator' ) ); +define( 'OPENSIM_LOGIN_URI', W4OS_GRID_LOGIN_URI ); +define( 'OPENSIM_MAIL_SENDER', get_option( 'w4os_offline_sender', 'no-reply@' . $_SERVER['SERVER_NAME'] ) ); +// define('OPENSIM_GRID_LOGO_URL', (get_theme_mod( 'custom_logo' )) ? wp_get_attachment_image_src( get_theme_mod( 'custom_logo' ) , 'full' )[0] : ''); + +define( 'HYPEVENTS_URL', preg_replace( ':/$:', '', get_option( 'w4os_hypevents_url', 'https://2do.pm/events' ) ) ); + +/** + * Main database. + * For grids, use Robust database credentials. + * For standalone simulators, use OpenSim database credentials. + * + * Access to OpenSim database is required + * - for search including classifieds + * - for offline messages processing + * - for economy + * It is not required if only search is needed, without classifieds (e.g. to for + * a multi-grid search engine). In this case search will only provide results + * for places, land for sale and events. + */ +define( 'OPENSIM_DB_HOST', get_option( 'w4os_db_host' ) ); +define( 'OPENSIM_DB_NAME', get_option( 'w4os_db_database' ) ); +define( 'OPENSIM_DB_USER', get_option( 'w4os_db_user' ) ); +define( 'OPENSIM_DB_PASS', get_option( 'w4os_db_pass' ) ); +define( 'OPENSIM_DB', ( ! empty( OPENSIM_DB_HOST ) & ! empty( OPENSIM_DB_NAME ) & ! empty( OPENSIM_DB_USER ) & ! empty( OPENSIM_DB_PASS ) ) ? true : false ); + +/** + * Search database credentials and settings. + * Needed if you enable search in OpenSim server. + * + * A dedicated database is: + * - strongly recommended if the search engine is shared by several grids + * - recommended and more efficient for large and/or hypergrid-enabled grids + * - optional for closed grids and standalone simulators + * These are recommendations, the Robust database can safely be used instead. + */ +define( 'SEARCH_DB_HOST', get_option( 'w4os_search_db_host', OPENSIM_DB_HOST ) ); +define( 'SEARCH_DB_NAME', get_option( 'w4os_search_db_database', OPENSIM_DB_NAME ) ); +define( 'SEARCH_DB_USER', get_option( 'w4os_search_db_user', OPENSIM_DB_USER ) ); +define( 'SEARCH_DB_PASS', get_option( 'w4os_search_db_pass', OPENSIM_DB_PASS ) ); + +/** + * Other registrars to forward hosts registrations. + * + * This method is not needed as with current OpenSim server (0.9.x) which allow + * specifying multiple registrars, but could be used in the future to implement + * peer to peer information sharing. + * + * @var array + */ +define( + 'SEARCH_REGISTRARS', + array( + // 'http://2do.directory/helpers/register.php', + // 'http://metaverseink.com/cgi-bin/register.py', + ) +); + +/** + * Currency database credentials and settings. + * Needed if currency is enabled on OpenSim server. + * A dedicated database is recommended, but not mandatory. + */ +define( 'CURRENCY_DB_HOST', get_option( 'w4os_economy_db_host' ) ); +define( 'CURRENCY_DB_NAME', get_option( 'w4os_economy_db_database' ) ); +define( 'CURRENCY_DB_USER', get_option( 'w4os_economy_db_user' ) ); +define( 'CURRENCY_DB_PASS', get_option( 'w4os_economy_db_pass' ) ); +define( 'CURRENCY_MONEY_TBL', 'balances' ); +define( 'CURRENCY_TRANSACTION_TBL', 'transactions' ); + +/** + * Money Server settings. + */ +define( 'CURRENCY_USE_MONEYSERVER', true ); +define( 'CURRENCY_SCRIPT_KEY', get_option( 'w4os_money_script_access_key', '123456789' ) ); +$currency_rate = (float) get_option( 'w4os_currency_rate', 10 ); +define( 'CURRENCY_RATE', ( $currency_rate <= 0 ? 10 : $currency_rate ) ); // amount in dollar... +$currency_per = (int) get_option( 'w4os_currency_rate_per', 1000 ); +define( 'CURRENCY_RATE_PER', ( $currency_per <= 0 ? 1000 : $currency_per ) ); // ... for this amount in virtuall currency +define( 'CURRENCY_PROVIDER', get_option( 'w4os_currency_provider' ) ); +if ( ! defined( 'CURRENCY_HELPER_URL' ) ) { + define( 'CURRENCY_HELPER_URL', ( ! empty( W4OS_GRID_INFO['economy'] ) ) ? W4OS_GRID_INFO['economy'] : get_home_url( null, '/economy/' ) ); +} +switch ( CURRENCY_PROVIDER ) { + case 'podex': + if ( ! empty( get_option( 'w4os_podex_error_message' ) ) ) { + define( 'PODEX_ERROR_MESSAGE', get_option( 'w4os_podex_error_message' ) ); + } + if ( ! empty( get_option( 'w4os_podex_redirect_url' ) ) ) { + define( 'PODEX_REDIRECT_URL', get_option( 'w4os_podex_redirect_url' ) ); + } + break; + +} + +// if (!defined('CURRENCY_HELPER_PATH')) define('CURRENCY_HELPER_PATH', dirname(__DIR__)); + +/** + * Timezone settings. Leave commented if included in a larger project. You + * dont want to mess up with timezone randomly in the middle of a process! + */ +// define('OPENSIM_USE_UTC_TIME', false); +// if (OPENSIM_USE_UTC_TIME) date_default_timezone_set('UTC'); + +/** + * OffLine messages DB credentials. + * + * @var [type] + */ +define( 'OFFLINE_DB_HOST', OPENSIM_DB_HOST ); +define( 'OFFLINE_DB_NAME', OPENSIM_DB_NAME ); +define( 'OFFLINE_DB_USER', OPENSIM_DB_USER ); +define( 'OFFLINE_DB_PASS', OPENSIM_DB_PASS ); +define( 'OFFLINE_MESSAGE_TBL', 'im_offline' ); // Same DB as Offline Module V2? + +/** + * Mute list database. + * mute is now handled by OpenSim server (0.9.x), so we shouldn't need this + */ +// define('MUTE_DB_HOST', OPENSIM_DB_HOST); +// define('MUTE_DB_NAME', OPENSIM_DB_NAME); +// define('MUTE_DB_USER', OPENSIM_DB_USER); +// define('MUTE_DB_PASS', OPENSIM_DB_PASS); +// define('MUTE_LIST_TBL', 'mute_list'); + + +/** + * Additional custom config + * (e.g. define custom values for addons here) + */ +// define('MY_CONSTANT_NAME', 'my value'); + +/** + * DO NOT MAKE CHANGES BELOW THIS + * Add your custom values above. + */ +require_once 'databases.php'; +require_once 'functions.php'; + +$currency_addon = dirname( __DIR__ ) . '/addons/' . CURRENCY_PROVIDER . '.php'; +if ( file_exists( $currency_addon ) ) { + require_once $currency_addon; +}