Jump to content

LaCollision

Clients
  • Posts

    1,001
  • Joined

  • Last visited

  • Days Won

    3

 Content Type 

Downloads

Release Notes

IPS4 Guides

IPS4 Developer Documentation

Invision Community Blog

Development Blog

Deprecation Tracker

Providers Directory

Forums

Events

Store

Gallery

Everything posted by LaCollision

  1. Thanks for your sound advice, I'll keep that in mind.
  2. Not to mention a government website. Any respectable-sized company today has become so procedural that they make you sign a 4 zillion-page document when you want to develop an HTML page for their website. All I'm saying is that, as a developer, exposing the database structure of a customer's application is really frowned upon. The reasons are obvious. There shouldn't even be a debate about this.
  3. Of course, anyone can retrieve the basic structure of Invision applications, no doubt about it. But for developers who sell their applications to customers, I find this rather awkward. Let's imagine an important customer who orders an application to enhance his Invision community site. And let's imagine that this application contains more sensitive information than the default Invision databases. The developer will create an application specifically for the customer's needs. And I don't find it particularly comfortable to tell the customer "Yes, your database structure is freely accessible on the web, but don't worry, it's not a big deal".
  4. I'm well aware that these files have been there for eternity; for so long that we no longer even question their existence. Well, I find that freely exposing the structure of databases, application parameters (even if these are their default values), or all URLs, well, I don't find that very reassuring for a potential customer. Publicly displaying database structures mechanically exposes applications to SQL injection attacks much more easily.
  5. Hi, I've just realized that all applications information in JSON and XML formats is freely available on the web. For example: https://invisioncommunity.com/applications/core/data/schema.json https://invisioncommunity.com/applications/core/data/settings.json https://invisioncommunity.com/applications/core/data/furl.json => Isn't it dangerous to leave sensitive information such as the database structure of all applications, default settings, or even all existing URLs accessible? For example, settings.json contains the parameter recaptcha2_private_key… This applies not only to Invision applications, but also to third-party applications. Thank you,
  6. I can confirm that this bug is present in other parts of Commerce. For instance in \IPS\nexus\Package->upgradeDowngrade(), at line 3524: try { $currency = $purchase->original_invoice->currency; } catch ( \OutOfRangeException $e ) { $currency = $purchase->member->defaultCurrency(); } Here, the consequence of this bug is that after upgrading/downgrading, the purchase ends up with a currency different from its current currency. If we go back to the example in my message above, then a purchase currently in Euros will end up in US dollars after the upgrade/downgrade. To solve this bug, the solution is the same: $currency = $purchase->renewal_currency; Thank you,
  7. Hi team, I think I may have found a bug in Commerce, where the cost to upgrade a package is calculated. In \IPS\nexus\Package->costToUpgrade(), at line 3334: try { $currency = $purchase->original_invoice->currency; } catch ( \Exception $e ) { $currency = $purchase->member->defaultCurrency(); } The $currency used for the calculation is either the purchase original currency, or the member default currency. But then, at line 3423, a difference is calculated: $diff = $purchase->renewals->diff( $renewalTermToUse ); And if the $currency calculated above is different from the current package currency, this difference throws an exception: DomainException currencies_dont_match /applications/nexus/sources/Purchase/RenewalTerm.php 257 In /applications/nexus/sources/Purchase/RenewalTerm.php, at line 257: /* Sanity check */ if ( $this->cost->currency !== $otherTerm->cost->currency ) { throw new \DomainException('currencies_dont_match'); } That's because the 2 RenewalTerms don't have the same currency. Let's get an example: I create a purchase with renewals, for instance in US $. So we have $purchase->original_invoice->currency = 'USD' Later, I change the purchase renewals for a new currency; Euros for instance. Now, I cannot upgrade my initial purchase, as a 'currencies_dont_match' exception is thrown. This exception is thrown because the difference in the cost to upgrade is calculated between a purchase in USD, and my current package currency, which is Euros. To solve this issue, we should have in \IPS\nexus\Package->costToUpgrade(), at line 3334: $currency = $purchase->renewal_currency; … instead of : try { $currency = $purchase->original_invoice->currency; } catch ( \Exception $e ) { $currency = $purchase->member->defaultCurrency(); } In this way, the difference will be correctly calculated between two identical currencies, rather than between the currency of the original invoice and my current currency. Please note that this bug may be present in different parts of Commerce. There are indeed several instances of '->original_invoice->currency' in Commerce source code, and I think some of them might lead to the 'currencies_dont_match' exception. Thank you for your help!
  8. Hi team, The bug mentioned in my first post has been corrected, thank you. However, I realize that there's another bug in the same part of the code: In \IPS\nexus\Package, at line 3579: if( !$diff->invert and $diff->days > 0 ) { $purchase->expire = $purchase->start->add( $diff ); } The problem is that the above code adds the $diff difference to the purchase date, which is incorrect. Let's take an example: Today is December 7, 2023. The purchase date is November 12, 2023. The initial term is 1 month. We therefore have : $initial = new \DateInterval( "P{$newPackage->initial_term}" ) = 1 month $diff = \IPS\DateTime::create()->diff( $purchase->start->add( $initial ) ) = difference between today and (purchase date + initial term) = difference between December 7 and (November 12 + 1 month) = difference between December 7 and December 12 = 5 days The code : $purchase->expire = $purchase->start->add( $diff ); ... will then set the purchase expiry date to : $purchase->expire = $purchase->start->add( $diff ); = November 12 + 5 days = November 17 ... whereas the expiry date should be December 12. This code therefore causes the purchase to expire prematurely: it is considered to have expired on November 17, whereas it should be valid until December 12. To correct this problem, add to $purchase->start the initial term, not the difference in days. The correct code should be as follows: $purchase->expire = $purchase->start->add( $initial ); Thank you for your help!
  9. Hi Marc, Thanks for your reply! By analyzing the Apache logs, I found that the problem comes from SendGrid calls: "POST /applications/core/interface/incomingemail/sendgrid.php HTTP/1.1" 500 4540 "-" "Sendlib/1.0" In \Email\Incoming\Email, there is indeed at line #523: $body = $parser->parse( $body ); We can see this parse() method calls the \Text\Parser->_parseContent() method… which is exactly where I have the issue of a PHP Fatal error (maximum execution time of 240 seconds exceeded): if ( $img->hasAttribute( 'data-ipsEmoticon-plain' ) ) So, there might be an issue with the method analyzing SendGrid calls. But now… it's beyond my capabilities! 😅 It would be interesting for the Invision development team to take a look at this issue. Thanks again,
  10. Hi! Every 3 hours, I can see in my PHP logs: Has anyone ever encountered this problem, and knows where it might be coming from? Its regularity (every 3 hours) makes me think of a task that cannot be completed. Thanks a lot! 🙏🏻
  11. Hi Team, I think there is a bug in the Commerce app for the Package->upgradeDowngrade() method. When upgrading or downgrading, you check if the new Package has a longer initial term, and then modify the current $purchase->expire date accordingly. In \IPS\nexus\Package, in upgradeDowngrade(), at line 3533: /* Initial Term, apply the difference if the new package has a longer initial term */ if( $term and $newPackage->initial_term ) { $initial = new \DateInterval( "P{$newPackage->initial_term}" ); /* If we're still in the initial period of the new package */ $diff = \IPS\DateTime::create()->diff( $purchase->start->add( $initial ) ); if( $diff->days > 0 ) { $purchase->expire = $purchase->start->add( $diff ); } } You calculate the difference between now and (the purchase start date + the initial term): $diff = \IPS\DateTime::create()->diff( $purchase->start->add( $initial ) ); Then you check if this difference has days: if( $diff->days > 0 ) The issue is the following: the $diff->days will always be positive, as you forget to check if the difference is negative. As a result, the $purchase->expire date could be set in the past, which is really problematic. The correct test, at line 3540, should be: if( !$diff->invert && $diff->days > 0 ) Thank you!
  12. Hi, I found a typo in \Email\Incoming\Email::route(), line 709 : $analyze = NULL; This var is then called « $analyse » in the rest of the code: switch ( $row['rule_criteria_field'] ) { case 'to': $analyse = implode( ',', $this->to ); break; case 'from': $analyse = $this->from; break; case 'sbjt': $analyse = $this->subject; break; case 'body': $analyse = $this->message; break; } /* Now check if we match the supplied rule */ switch ( $row['rule_criteria_type'] ) { case 'ctns': $match = (bool) ( mb_strpos( $analyse, $row['rule_criteria_value'] ) !== FALSE ); break; case 'eqls': if ( mb_strpos( $analyse, ',' ) !== FALSE ) { $match = (bool) \in_array( $analyse, explode( ',', $analyse ) ); } else { $match = (bool) ( $analyse == $row['rule_criteria_value'] ); } break; case 'regx': $match = (bool) ( preg_match( "/{$row['rule_criteria_value']}/", $analyse ) == 1 ); break; } This has no impact thanks to the switch, bit it's still a typo 🙂 Thank you,
  13. Hi team, The Nexus VAT validation for EU business addresses seems to not work any longer. In \Tax::validateVAT at line 382: if ( $xml->read() ) { if ( $xml->name === 'soap:Envelope' and $xml->read() ) { if ( $xml->name === 'soap:Body' and $xml->read() ) { if ( $xml->name === 'checkVatResponse' ) { $xml->read(); do { $responseArray[ $xml->name ] = $xml->readString(); } while ( $xml->next() and $xml->nodeType !== $xml::END_ELEMENT ); } } } } The response from the EU service seems to have changed; the XML looks something like that: <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> <env:Header/> <env:Body> <ns2:checkVatResponse xmlns:ns2="urn:ec.europa.eu:taxud:vies:services:checkVat:types"> <ns2:countryCode>XX</ns2:countryCode> <ns2:vatNumber>XXXXXX</ns2:vatNumber> <ns2:requestDate>2022-09-28+02:00</ns2:requestDate> <ns2:valid>true</ns2:valid> <ns2:name>XXXXXX</ns2:name> <ns2:address>XXXXXX</ns2:address> </ns2:checkVatResponse> </env:Body> </env:Envelope> ➡️ The namespace is "env", and a <env:Header> tag seems to have appeared before the Body; so the $xml->read() can't find the Body element any longer. Thank you!
  14. Hi, Thank you for this fix. However, there is still an issue when the account credit has exactly the same value than the invoice total. I can indeed see in /nexus/taks/generateRenewalInvoices the following code, at line 131: $take = NULL; if ( $credit->compare( $invoice->total->amount ) === 1 ) { $take = $invoice->total->amount; } else { /* Only use credit if amount remaining is greater than card gateway min amount */ if( $invoice->total->amount->subtract( $credit ) > new \IPS\Math\Number( '0.50' ) ) { $take = $credit; } } If the credit is equal to the invoice total, then the $take value stays null, and the invoice is not paid with the account credit, although the customer has enough credit. So the correct code should be the following: $take = NULL; if ( $credit->compare( $invoice->total->amount ) === 0 || $credit->compare( $invoice->total->amount ) === 1 ) { $take = $invoice->total->amount; } else { /* Only use credit if amount remaining is greater than card gateway min amount */ if( $invoice->total->amount->subtract( $credit ) > new \IPS\Math\Number( '0.50' ) ) { $take = $credit; } } ➡️ I've added the condition on the credit equal to the invoice total. Thank you for your help!
  15. Hi, Yes, that would be really great! It's a very popular request:
  16. Hi Invision, Any thought about this? That would be awesome to have Stripe Connect integrated to Invision. Thank you!
  17. Hi Marc, In the 4.6.12 Commerce releases notes, it is indicated: Removed the FX country code for france. However, the address form field uses the values from \IPS\GeoLocation::$countries And in the 4.6.12 version of \IPS\GeoLocation, we can see at line 105: 'FX', // France, Metropolitan => The FX code has not been removed. As a result, when adding / modifying an address, the « France, Metropolitan » country is still here: Therefore, I think the issue with Stripe will still happen. Thank you,
  18. Hi again, In my message above, the hook \IPS\cms\hooks\Lang::languageInit() calls \IPS\cms\Databases::getStore(), which is another culprit for extra DB queries: public static function getStore() { if ( ! isset( \IPS\Data\Store::i()->cms_databases ) ) { \IPS\Data\Store::i()->cms_databases = iterator_to_array( \IPS\Db::i()->select( static::$databaseTable . '.*, core_permission_index.perm_id, core_permission_index.perm_view, core_permission_index.perm_2, core_permission_index.perm_3, core_permission_index.perm_4, core_permission_index.perm_5, core_permission_index.perm_6, core_permission_index.perm_7', static::$databaseTable )->join( 'core_permission_index', array( "core_permission_index.app=? AND core_permission_index.perm_type=? AND core_permission_index.perm_type_id=" . static::$databaseTable . "." . static::$databasePrefix . static::$databaseColumnId, static::$permApp, static::$permType ) ) ->setKeyField('database_id') ); } return \IPS\Data\Store::i()->cms_databases; } Thank you! 😅
  19. Hi Jim, No, not at all! The problem happens even on blank templates outside the IPS.Pages app. When the app=core&module=system&controller=serviceworker page is called, several methods like \IPS\Member::loggedIn()->language() and parseOutputForDisplay() are called. You can see them in \IPS\core\modules\front\system\serviceworker.php, at line 75: \IPS\Member::loggedIn()->language()->parseOutputForDisplay( $output ); And it turns out that IPS.Pages overrides in its hooks many underlying methods of the methods above, like: public static function createFriendlyUrlFromComponents( $components, $potentialFurl ) { /* If the normal URL handling has it, or this is the root page, use normal handling, unless Pages is the default app, in which case we'll fallback to it */ if ( $return = parent::createFriendlyUrlFromComponents( $components, $potentialFurl ) or !$potentialFurl ) { if ( !\IPS\Application::load('cms')->default OR $potentialFurl ) { return $return; } } /* Try to find a page */ try { list( $pagePath, $pageNumber ) = \IPS\cms\Pages\Page::getStrippedPagePath( $potentialFurl ); try { $page = \IPS\cms\Pages\Page::loadFromPath( $pagePath ); } catch( \Exception $e ) { /* Try from furl */ try { $page = \IPS\cms\Pages\Page::load( \IPS\Db::i()->select( 'store_current_id', 'cms_url_store', array( 'store_type=? and store_path=?', 'page', $pagePath ) )->first() ); } catch( \UnderflowException $e ) { throw new \OutOfRangeException; } } return static::createFromComponents( $components[ static::COMPONENT_HOST ], $components[ static::COMPONENT_SCHEME ], $components[ static::COMPONENT_PATH ], $components[ static::COMPONENT_QUERY ], $components[ static::COMPONENT_PORT ], $components[ static::COMPONENT_USERNAME ], $components[ static::COMPONENT_PASSWORD ], $components[ static::COMPONENT_FRAGMENT ] ) ->setFriendlyUrlData( 'content_page_path', array( $potentialFurl ), array( 'path' => $potentialFurl ), $potentialFurl ); } … } and : public function languageInit() { /* Language, is it? */ parent::languageInit(); /* Don't do this during setup */ if ( \IPS\Dispatcher::hasInstance() AND \IPS\Dispatcher::i()->controllerLocation == 'setup' ) { return; } /* Ensure applications set up correctly before task is executed. Pages, for example, needs to set up spl autoloaders first */ \IPS\Application::applications(); /* Add in the database specific language bits and bobs */ foreach( \IPS\cms\Databases::getStore() as $database ) { $this->words['__indefart_content_record_comments_title_' . $database['database_id'] ] = $this->addToStack( '__indefart_content_record_comments_title' ); $this->words['__indefart_content_record_reviews_title_' . $database['database_id'] ] = $this->addToStack( '__indefart_content_record_reviews_title' ); $this->words['__indefart_content_db_lang_su_' . $database['database_id'] ] = $this->addToStack( 'content_db_lang_ia_' . $database['database_id'] ); $this->words['__defart_content_record_comments_title_' . $database['database_id'] ] = $this->addToStack( '__defart_content_record_comments_title' ); $this->words['__defart_content_record_reviews_title_' . $database['database_id'] ] = $this->addToStack( '__defart_content_record_reviews_title' ); $this->words['__defart_content_db_lang_su_' . $database['database_id'] ] = $this->addToStack( 'content_db_lang_sl_' . $database['database_id'] ); $this->words['content_record_comments_title_' . $database['database_id'] ] = $this->addToStack( 'content_record_comment_title', FALSE, array( 'sprintf' => array( $this->recordWord( 1, TRUE, $database['database_id'] ) ) ) ); $this->words['content_record_reviews_title_' . $database['database_id'] ] = $this->addToStack( 'content_record_review_title', FALSE, array( 'sprintf' => array( $this->recordWord( 1, TRUE, $database['database_id'] ) ) ) ); $this->words['content_record_comments_title_' . $database['database_id'] . '_pl' ] = $this->addToStack( 'content_record_comments_title', FALSE, array( 'sprintf' => array( $this->recordWord( 1, TRUE, $database['database_id'] ) ) ) ); $this->words['content_record_comments_title_' . $database['database_id'] . '_pl_lc' ] = $this->addToStack( 'content_record_comments_title_lc', FALSE, array( 'sprintf' => array( $this->recordWord( 1, FALSE, $database['database_id'] ) ) ) ); $this->words['content_record_comments_title_' . $database['database_id'] . '_lc' ] = $this->addToStack( 'content_record_comments_title_lc', FALSE, array( 'sprintf' => array( $this->recordWord( 1, FALSE, $database['database_id'] ) ) ) ); $this->words['content_record_reviews_title_' . $database['database_id'] . '_pl' ] = $this->addToStack( 'content_record_reviews_title', FALSE, array( 'sprintf' => array( $this->recordWord( 1, TRUE, $database['database_id'] ) ) ) ); $this->words['content_record_reviews_title_' . $database['database_id'] . '_pl_lc' ] = $this->addToStack( 'content_record_reviews_title_lc', FALSE, array( 'sprintf' => array( $this->recordWord( 1, FALSE, $database['database_id'] ) ) ) ); $this->words['content_record_reviews_title_' . $database['database_id'] . '_lc' ] = $this->addToStack( 'content_record_reviews_title_lc', FALSE, array( 'sprintf' => array( $this->recordWord( 1, FALSE, $database['database_id'] ) ) ) ); $this->words['content_db_lang_su_' . $database['database_id'] . '_pl' ] = $this->addToStack( 'content_db_lang_pu_' . $database['database_id'] ); $this->words['content_db_lang_su_' . $database['database_id'] . '_pl_lc' ] = $this->addToStack( 'content_db_lang_pl_' . $database['database_id'] ); $this->words['content_db_lang_sl_' . $database['database_id'] . '_pl_lc' ] = $this->addToStack( 'content_db_lang_pl_' . $database['database_id'] ); $fieldsClass = '\IPS\cms\Fields' . $database['database_id']; $customFields = $fieldsClass::databaseFieldIds(); foreach ( $customFields AS $id ) { $this->words['sort_field_' . $id] = $this->addToStack( 'content_field_' . $id ); } } } You can see that these overridden methods are responsible for at least some of the extra queries mentioned in my 1st post. Thank you for your help!
  20. Hi, I've noticed that when IPS.Pages (cms) is installed, and that the Service Worker makes a call to app=core&module=system&controller=serviceworker, then extra DB queries are always run, on all pages: SELECT cms_pages.*, core_permission_index.perm_id, core_permission_index.perm_view, core_permission_index.perm_2, core_permission_index.perm_3, core_permission_index.perm_4, core_permission_index.perm_5, core_permission_index.perm_6, core_permission_index.perm_7 FROM `cms_pages` LEFT JOIN `core_permission_index` ON core_permission_index.app='cms' AND core_permission_index.perm_type='pages' AND core_permission_index.perm_type_id=cms_pages.page_id WHERE cms_pages.page_full_path='app=core' SELECT * FROM `cms_folders` WHERE `folder_path`='app=core' SELECT store_current_id FROM `cms_url_store` WHERE store_type='page' and store_path='app=core' SELECT word_key, word_default, word_custom FROM `core_sys_lang_words` WHERE lang_id=3 AND word_key IN('__indefart_content_record_comments_title','__indefart_content_record_reviews_title','content_db_lang_ia_1','__defart_content_record_comments_title','__defart_content_record_reviews_title','content_db_lang_sl_1','content_db_lang_su_1','content_record_comment_title','content_record_review_title','content_record_comments_title','content_record_comments_title_lc','content_record_reviews_title','content_record_reviews_title_lc','content_db_lang_pu_1','content_db_lang_pl_1','content_field_1','content_field_2','content_field_3','default_notification_title','default_notification_body') and word_js=0 This extra queries seem to come from the IPS.Pages hooks, that add queries on the \IPS\Http\Url\Friendly, \IPS\Lang, etc. classes. These extra queries are added even when IPS.Pages is not defined as the root page of the community. => Isn't it possible to avoid such extra load on all pages? Thank you!
  21. Hi Invision, There is a very problematic bug with Business Address on Commerce 4.6.11. Here is what happens: When Commerce is configured to use a business tax type (for instance "European Union VAT Rates"), then, during registration, the type of address used is /nexus/dev/html/global/form/businessAddress.phtml Then, it is possible to choose a “Consumer” or “Business” address. 1st problem: When choosing the “Business” address type, the field for the company name does not appear when the chosen country is outside the European Union. Thus, an American user who chooses a Business address cannot fill in his company name. This problem comes from the javascript ips.forms.businessAddressVat.js, at line 28: changeRelevantField: function () { if ( this.scope.find('[data-role="addressTypeRadio"][value="business"]').is(':checked') && ['AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR','HU','IE','IT','LV','LT','LU','MT','NL','PL','PT','RO','SK','SI','ES','SE','GB','FX','GP','MQ','RE','BL','GF','MF','NC','PF','PM','TF','YT','WF'].indexOf( this.scope.find('[data-role="countrySelect"]').val() ) !== -1 ) { this.scope.find('[data-role="vatField"]').show(); this.scope.find('[data-role="business"]').show(); } else { this.scope.find('[data-role="vatField"]').hide(); this.scope.find('[data-role="business"]').hide(); } } => The business name is only displayed for UE countries. It should be displayed for all countries. This is the tax field that should be displayed only for EU countries. 2nd problem, much more important: When submitting the form, an error is thrown: “A business name is required”. However, this problem cannot be corrected, because the field for the Company name does not appear: It is therefore impossible to validate the form. This problem comes from the PHP script /nexus/sources/Form/BusinessAddress.php, at line 121: if ( $this->value->business === '' ) { throw new \DomainException('cm_business_name_required'); } An error is thrown, with no condition on the selected country. Therefore, a user with a country outside the UE cannot validate the form, as he has no Business field to fill in. => So, the field for the Company name must therefore be displayed for all countries, and not only for those of the European Union, otherwise the user cannot validate the form. Thank you!
×
×
  • Create New...