Site Reviews - Version 5.12.0

Version Description

Download this release

Release Info

Developer geminilabs
Plugin Icon 128x128 Site Reviews
Version 5.12.0
Comparing to
See all releases

Code changes from version 5.11.1 to 5.12.0

config/settings.php CHANGED
@@ -187,8 +187,8 @@ return [
187
  ),
188
  'label' => _x('Review Assignment', 'admin-text', 'site-reviews'),
189
  'options' => [
190
- 'loose' => _x('Loose Assignment', 'admin-text', 'site-reviews'),
191
- 'strict' => _x('Strict Assignment', 'admin-text', 'site-reviews'),
192
  ],
193
  'tooltip' => _x('This setting determines how the assigned options work in the reviews and summary shortcodes and blocks.', 'admin-text', 'site-reviews'),
194
  'type' => 'select',
187
  ),
188
  'label' => _x('Review Assignment', 'admin-text', 'site-reviews'),
189
  'options' => [
190
+ 'loose' => _x('Loose Assignment (slower database queries)', 'admin-text', 'site-reviews'),
191
+ 'strict' => _x('Strict Assignment (faster database queries)', 'admin-text', 'site-reviews'),
192
  ],
193
  'tooltip' => _x('This setting determines how the assigned options work in the reviews and summary shortcodes and blocks.', 'admin-text', 'site-reviews'),
194
  'type' => 'select',
languages/site-reviews-en_US.mo CHANGED
Binary file
languages/site-reviews-en_US.po CHANGED
@@ -841,13 +841,13 @@ msgstr "Review Assignment"
841
 
842
  #: config/settings.php:190
843
  msgctxt "admin-text"
844
- msgid "Loose Assignment"
845
- msgstr "Loose Assignment"
846
 
847
  #: config/settings.php:191
848
  msgctxt "admin-text"
849
- msgid "Strict Assignment"
850
- msgstr "Strict Assignment"
851
 
852
  #: config/settings.php:193
853
  msgctxt "admin-text"
@@ -1573,7 +1573,7 @@ msgctxt "admin-text"
1573
  msgid "Terms Accepted"
1574
  msgstr "Terms Accepted"
1575
 
1576
- #: plugin/Addons/Addon.php:74
1577
  msgctxt "admin-text"
1578
  msgid "Untitled"
1579
  msgstr "Untitled"
@@ -1785,7 +1785,7 @@ msgctxt "admin-text"
1785
  msgid "Support"
1786
  msgstr "Support"
1787
 
1788
- #: plugin/Controllers/MenuController.php:108, plugin/Controllers/ToolsController.php:89
1789
  msgctxt "admin-text"
1790
  msgid "FAQ"
1791
  msgstr "FAQ"
@@ -1934,32 +1934,32 @@ msgctxt "admin-text"
1934
  msgid "A license you entered was invalid."
1935
  msgstr "A license you entered was invalid."
1936
 
1937
- #: plugin/Controllers/ToolsController.php:28
1938
  msgctxt "admin-text"
1939
  msgid "Console cleared."
1940
  msgstr "Console cleared."
1941
 
1942
- #: plugin/Controllers/ToolsController.php:53
1943
  msgctxt "admin-text"
1944
  msgid "The <code>%s</code> table was successfully converted to InnoDB."
1945
  msgstr "The <code>%s</code> table was successfully converted to InnoDB."
1946
 
1947
- #: plugin/Controllers/ToolsController.php:58
1948
  msgctxt "admin-text"
1949
  msgid "The <code>%s</code> table could not be converted to InnoDB."
1950
  msgstr "The <code>%s</code> table could not be converted to InnoDB."
1951
 
1952
- #: plugin/Controllers/ToolsController.php:63
1953
  msgctxt "admin-text"
1954
  msgid "The <code>%s</code> table was not found in the database."
1955
  msgstr "The <code>%s</code> table was not found in the database."
1956
 
1957
- #: plugin/Controllers/ToolsController.php:98
1958
  msgctxt "admin-text"
1959
  msgid "Your detected IP address is %s. If this looks incorrect, please see the %s."
1960
  msgstr "Your detected IP address is %s. If this looks incorrect, please see the %s."
1961
 
1962
- #: plugin/Controllers/ToolsController.php:93
1963
  msgctxt "admin-text"
1964
  msgid ""
1965
  "Site Reviews was unable to detect an IP address. To fix this, please see "
@@ -1968,37 +1968,42 @@ msgstr ""
1968
  "Site Reviews was unable to detect an IP address. To fix this, please see "
1969
  "the %s."
1970
 
1971
- #: plugin/Controllers/ToolsController.php:149
1972
  msgctxt "admin-text"
1973
  msgid "Console reloaded."
1974
  msgstr "Console reloaded."
1975
 
1976
- #: plugin/Controllers/ToolsController.php:194
1977
  msgctxt "admin-text"
1978
  msgid "The plugin has been migrated sucessfully, please reload the page."
1979
  msgstr "The plugin has been migrated sucessfully, please reload the page."
1980
 
1981
- #: plugin/Controllers/ToolsController.php:191
1982
  msgctxt "admin-text"
1983
  msgid "All plugin migrations have been run successfully, please reload the page."
1984
  msgstr "All plugin migrations have been run successfully, please reload the page."
1985
 
1986
- #: plugin/Controllers/ToolsController.php:217
 
 
 
 
 
1987
  msgctxt "admin-text"
1988
  msgid "The assigned meta values have been reset."
1989
  msgstr "The assigned meta values have been reset."
1990
 
1991
- #: plugin/Controllers/ToolsController.php:243
1992
  msgctxt "admin-text"
1993
  msgid "The permissions have been reset."
1994
  msgstr "The permissions have been reset."
1995
 
1996
- #: plugin/Controllers/ToolsController.php:254
1997
  msgctxt "admin-text"
1998
  msgid "reload the page"
1999
  msgstr "reload the page"
2000
 
2001
- #: plugin/Controllers/ToolsController.php:258
2002
  msgctxt "admin-text"
2003
  msgid "The permissions have been reset, please %s for them to take effect."
2004
  msgstr "The permissions have been reset, please %s for them to take effect."
@@ -3648,7 +3653,7 @@ msgctxt "admin-text"
3648
  msgid "Run Migration"
3649
  msgstr "Run Migration"
3650
 
3651
- #: views/pages/tools/general/optimise-db-tables.php:6
3652
  msgctxt "admin-text"
3653
  msgid "Optimise Your Database Tables"
3654
  msgstr "Optimise Your Database Tables"
@@ -3708,6 +3713,104 @@ msgctxt "admin-text"
3708
  msgid "Convert table engine to InnoDB"
3709
  msgstr "Convert table engine to InnoDB"
3710
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3711
  #: views/pages/tools/general/reset-assigned-meta.php:6
3712
  msgctxt "admin-text"
3713
  msgid "Reset Assigned Meta Values"
841
 
842
  #: config/settings.php:190
843
  msgctxt "admin-text"
844
+ msgid "Loose Assignment (slower database queries)"
845
+ msgstr "Loose Assignment (slower database queries)"
846
 
847
  #: config/settings.php:191
848
  msgctxt "admin-text"
849
+ msgid "Strict Assignment (faster database queries)"
850
+ msgstr "Strict Assignment (faster database queries)"
851
 
852
  #: config/settings.php:193
853
  msgctxt "admin-text"
1573
  msgid "Terms Accepted"
1574
  msgstr "Terms Accepted"
1575
 
1576
+ #: plugin/Addons/Addon.php:75
1577
  msgctxt "admin-text"
1578
  msgid "Untitled"
1579
  msgstr "Untitled"
1785
  msgid "Support"
1786
  msgstr "Support"
1787
 
1788
+ #: plugin/Controllers/MenuController.php:108, plugin/Controllers/ToolsController.php:90
1789
  msgctxt "admin-text"
1790
  msgid "FAQ"
1791
  msgstr "FAQ"
1934
  msgid "A license you entered was invalid."
1935
  msgstr "A license you entered was invalid."
1936
 
1937
+ #: plugin/Controllers/ToolsController.php:29
1938
  msgctxt "admin-text"
1939
  msgid "Console cleared."
1940
  msgstr "Console cleared."
1941
 
1942
+ #: plugin/Controllers/ToolsController.php:54
1943
  msgctxt "admin-text"
1944
  msgid "The <code>%s</code> table was successfully converted to InnoDB."
1945
  msgstr "The <code>%s</code> table was successfully converted to InnoDB."
1946
 
1947
+ #: plugin/Controllers/ToolsController.php:59
1948
  msgctxt "admin-text"
1949
  msgid "The <code>%s</code> table could not be converted to InnoDB."
1950
  msgstr "The <code>%s</code> table could not be converted to InnoDB."
1951
 
1952
+ #: plugin/Controllers/ToolsController.php:64
1953
  msgctxt "admin-text"
1954
  msgid "The <code>%s</code> table was not found in the database."
1955
  msgstr "The <code>%s</code> table was not found in the database."
1956
 
1957
+ #: plugin/Controllers/ToolsController.php:99
1958
  msgctxt "admin-text"
1959
  msgid "Your detected IP address is %s. If this looks incorrect, please see the %s."
1960
  msgstr "Your detected IP address is %s. If this looks incorrect, please see the %s."
1961
 
1962
+ #: plugin/Controllers/ToolsController.php:94
1963
  msgctxt "admin-text"
1964
  msgid ""
1965
  "Site Reviews was unable to detect an IP address. To fix this, please see "
1968
  "Site Reviews was unable to detect an IP address. To fix this, please see "
1969
  "the %s."
1970
 
1971
+ #: plugin/Controllers/ToolsController.php:150
1972
  msgctxt "admin-text"
1973
  msgid "Console reloaded."
1974
  msgstr "Console reloaded."
1975
 
1976
+ #: plugin/Controllers/ToolsController.php:195
1977
  msgctxt "admin-text"
1978
  msgid "The plugin has been migrated sucessfully, please reload the page."
1979
  msgstr "The plugin has been migrated sucessfully, please reload the page."
1980
 
1981
+ #: plugin/Controllers/ToolsController.php:192
1982
  msgctxt "admin-text"
1983
  msgid "All plugin migrations have been run successfully, please reload the page."
1984
  msgstr "All plugin migrations have been run successfully, please reload the page."
1985
 
1986
+ #: plugin/Controllers/ToolsController.php:218
1987
+ msgctxt "admin-text"
1988
+ msgid "The review relationships have been repaired."
1989
+ msgstr "The review relationships have been repaired."
1990
+
1991
+ #: plugin/Controllers/ToolsController.php:240
1992
  msgctxt "admin-text"
1993
  msgid "The assigned meta values have been reset."
1994
  msgstr "The assigned meta values have been reset."
1995
 
1996
+ #: plugin/Controllers/ToolsController.php:266
1997
  msgctxt "admin-text"
1998
  msgid "The permissions have been reset."
1999
  msgstr "The permissions have been reset."
2000
 
2001
+ #: plugin/Controllers/ToolsController.php:277
2002
  msgctxt "admin-text"
2003
  msgid "reload the page"
2004
  msgstr "reload the page"
2005
 
2006
+ #: plugin/Controllers/ToolsController.php:281
2007
  msgctxt "admin-text"
2008
  msgid "The permissions have been reset, please %s for them to take effect."
2009
  msgstr "The permissions have been reset, please %s for them to take effect."
3653
  msgid "Run Migration"
3654
  msgstr "Run Migration"
3655
 
3656
+ #: views/pages/tools/general/optimise-db-tables.php:6, views/pages/tools/general/repair-review-relations.php:17
3657
  msgctxt "admin-text"
3658
  msgid "Optimise Your Database Tables"
3659
  msgstr "Optimise Your Database Tables"
3713
  msgid "Convert table engine to InnoDB"
3714
  msgstr "Convert table engine to InnoDB"
3715
 
3716
+ #: views/pages/tools/general/repair-review-relations.php:6
3717
+ msgctxt "admin-text"
3718
+ msgid "Repair Review Relations"
3719
+ msgstr "Repair Review Relations"
3720
+
3721
+ #: views/pages/tools/general/repair-review-relations.php:36
3722
+ msgctxt "admin-text"
3723
+ msgid "Repair is unnecessary because your database tables use the InnoDB engine!"
3724
+ msgstr "Repair is unnecessary because your database tables use the InnoDB engine!"
3725
+
3726
+ #: views/pages/tools/general/repair-review-relations.php:14
3727
+ msgctxt "admin-text"
3728
+ msgid ""
3729
+ "Once you have repaired the review relationships, it is recommended that you "
3730
+ "run the %s tool to prevent the problem from happening again."
3731
+ msgstr ""
3732
+ "Once you have repaired the review relationships, it is recommended that you "
3733
+ "run the %s tool to prevent the problem from happening again."
3734
+
3735
+ #: views/pages/tools/general/repair-review-relations.php:22
3736
+ msgctxt "admin-text"
3737
+ msgid ""
3738
+ "Site Reviews stores review details in a custom database table, these "
3739
+ "entries are linked to the review post type in the WordPress posts table "
3740
+ "using the review's Post ID."
3741
+ msgstr ""
3742
+ "Site Reviews stores review details in a custom database table, these "
3743
+ "entries are linked to the review post type in the WordPress posts table "
3744
+ "using the review's Post ID."
3745
+
3746
+ #: views/pages/tools/general/repair-review-relations.php:23
3747
+ msgctxt "admin-text"
3748
+ msgid ""
3749
+ "If your database tables use the standard InnoDB engine, foreign indexes are "
3750
+ "used to maintain the relationship between the two so that when a review is "
3751
+ "deleted from the WordPress posts table, the review details are also removed "
3752
+ "from the custom table."
3753
+ msgstr ""
3754
+ "If your database tables use the standard InnoDB engine, foreign indexes are "
3755
+ "used to maintain the relationship between the two so that when a review is "
3756
+ "deleted from the WordPress posts table, the review details are also removed "
3757
+ "from the custom table."
3758
+
3759
+ #: views/pages/tools/general/repair-review-relations.php:24
3760
+ msgctxt "admin-text"
3761
+ msgid ""
3762
+ "However, if your database tables use the outdated MyISAM engine, then "
3763
+ "foreign indexes cannot be used so Site Reviews depends on the built-in "
3764
+ "WordPress hooks (which are triggered when a review is deleted) in order to "
3765
+ "remove the relationship and delete the review details from the custom "
3766
+ "database table."
3767
+ msgstr ""
3768
+ "However, if your database tables use the outdated MyISAM engine, then "
3769
+ "foreign indexes cannot be used so Site Reviews depends on the built-in "
3770
+ "WordPress hooks (which are triggered when a review is deleted) in order to "
3771
+ "remove the relationship and delete the review details from the custom "
3772
+ "database table."
3773
+
3774
+ #: views/pages/tools/general/repair-review-relations.php:25
3775
+ msgctxt "admin-text"
3776
+ msgid ""
3777
+ "Depending on the WordPress hooks is problematic because there are many "
3778
+ "unknown factors involved which may prevent these hooks from being triggered "
3779
+ "(i.e. manually deleting a review from the database, using a third-party "
3780
+ "plugin which overrides the hooks, etc.), this can mess up the relationships "
3781
+ "between the review in the WordPress posts table and the review details in "
3782
+ "the custom database table and when this happens, it may cause your reviews "
3783
+ "to incorrectly use the content of a non-review post type."
3784
+ msgstr ""
3785
+ "Depending on the WordPress hooks is problematic because there are many "
3786
+ "unknown factors involved which may prevent these hooks from being triggered "
3787
+ "(i.e. manually deleting a review from the database, using a third-party "
3788
+ "plugin which overrides the hooks, etc.), this can mess up the relationships "
3789
+ "between the review in the WordPress posts table and the review details in "
3790
+ "the custom database table and when this happens, it may cause your reviews "
3791
+ "to incorrectly use the content of a non-review post type."
3792
+
3793
+ #: views/pages/tools/general/repair-review-relations.php:26
3794
+ msgctxt "admin-text"
3795
+ msgid ""
3796
+ "This tool will repair the review relationships in your database by removing "
3797
+ "any review details in the custom database table that do not point to a "
3798
+ "valid review."
3799
+ msgstr ""
3800
+ "This tool will repair the review relationships in your database by removing "
3801
+ "any review details in the custom database table that do not point to a "
3802
+ "valid review."
3803
+
3804
+ #: views/pages/tools/general/repair-review-relations.php:31
3805
+ msgctxt "admin-text"
3806
+ msgid "Repairing relations, please wait..."
3807
+ msgstr "Repairing relations, please wait..."
3808
+
3809
+ #: views/pages/tools/general/repair-review-relations.php:31
3810
+ msgctxt "admin-text"
3811
+ msgid "Repair Relations"
3812
+ msgstr "Repair Relations"
3813
+
3814
  #: views/pages/tools/general/reset-assigned-meta.php:6
3815
  msgctxt "admin-text"
3816
  msgid "Reset Assigned Meta Values"
languages/site-reviews.pot CHANGED
@@ -480,12 +480,12 @@ msgstr ""
480
 
481
  #: config/settings.php:190
482
  msgctxt "admin-text"
483
- msgid "Loose Assignment"
484
  msgstr ""
485
 
486
  #: config/settings.php:191
487
  msgctxt "admin-text"
488
- msgid "Strict Assignment"
489
  msgstr ""
490
 
491
  #: config/settings.php:193
@@ -1196,7 +1196,7 @@ msgstr ""
1196
  msgid "This review is based on my own experience and is my genuine opinion."
1197
  msgstr ""
1198
 
1199
- #: plugin/Addons/Addon.php:74
1200
  msgctxt "admin-text"
1201
  msgid "Untitled"
1202
  msgstr ""
@@ -1424,7 +1424,7 @@ msgctxt "admin-text"
1424
  msgid "Support"
1425
  msgstr ""
1426
 
1427
- #: plugin/Controllers/MenuController.php:108, plugin/Controllers/ToolsController.php:89
1428
  msgctxt "admin-text"
1429
  msgid "FAQ"
1430
  msgstr ""
@@ -1574,67 +1574,72 @@ msgctxt "The Term ID (admin-text)"
1574
  msgid "<span>ID: %d</span>"
1575
  msgstr ""
1576
 
1577
- #: plugin/Controllers/ToolsController.php:28
1578
  msgctxt "admin-text"
1579
  msgid "Console cleared."
1580
  msgstr ""
1581
 
1582
- #: plugin/Controllers/ToolsController.php:53
1583
  msgctxt "admin-text"
1584
  msgid "The <code>%s</code> table was successfully converted to InnoDB."
1585
  msgstr ""
1586
 
1587
- #: plugin/Controllers/ToolsController.php:58
1588
  msgctxt "admin-text"
1589
  msgid "The <code>%s</code> table could not be converted to InnoDB."
1590
  msgstr ""
1591
 
1592
- #: plugin/Controllers/ToolsController.php:63
1593
  msgctxt "admin-text"
1594
  msgid "The <code>%s</code> table was not found in the database."
1595
  msgstr ""
1596
 
1597
- #: plugin/Controllers/ToolsController.php:98
1598
  msgctxt "admin-text"
1599
  msgid "Your detected IP address is %s. If this looks incorrect, please see the %s."
1600
  msgstr ""
1601
 
1602
- #: plugin/Controllers/ToolsController.php:93
1603
  msgctxt "admin-text"
1604
  msgid "Site Reviews was unable to detect an IP address. To fix this, please see the %s."
1605
  msgstr ""
1606
 
1607
- #: plugin/Controllers/ToolsController.php:149
1608
  msgctxt "admin-text"
1609
  msgid "Console reloaded."
1610
  msgstr ""
1611
 
1612
- #: plugin/Controllers/ToolsController.php:194
1613
  msgctxt "admin-text"
1614
  msgid "The plugin has been migrated sucessfully, please reload the page."
1615
  msgstr ""
1616
 
1617
- #: plugin/Controllers/ToolsController.php:191
1618
  msgctxt "admin-text"
1619
  msgid "All plugin migrations have been run successfully, please reload the page."
1620
  msgstr ""
1621
 
1622
- #: plugin/Controllers/ToolsController.php:217
 
 
 
 
 
1623
  msgctxt "admin-text"
1624
  msgid "The assigned meta values have been reset."
1625
  msgstr ""
1626
 
1627
- #: plugin/Controllers/ToolsController.php:243
1628
  msgctxt "admin-text"
1629
  msgid "The permissions have been reset."
1630
  msgstr ""
1631
 
1632
- #: plugin/Controllers/ToolsController.php:254
1633
  msgctxt "admin-text"
1634
  msgid "reload the page"
1635
  msgstr ""
1636
 
1637
- #: plugin/Controllers/ToolsController.php:258
1638
  msgctxt "admin-text"
1639
  msgid "The permissions have been reset, please %s for them to take effect."
1640
  msgstr ""
@@ -3478,7 +3483,7 @@ msgctxt "admin-text"
3478
  msgid "Run Migration"
3479
  msgstr ""
3480
 
3481
- #: views/pages/tools/general/optimise-db-tables.php:6
3482
  msgctxt "admin-text"
3483
  msgid "Optimise Your Database Tables"
3484
  msgstr ""
@@ -3518,6 +3523,56 @@ msgctxt "admin-text"
3518
  msgid "Convert table engine to InnoDB"
3519
  msgstr ""
3520
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3521
  #: views/pages/tools/general/reset-assigned-meta.php:6
3522
  msgctxt "admin-text"
3523
  msgid "Reset Assigned Meta Values"
480
 
481
  #: config/settings.php:190
482
  msgctxt "admin-text"
483
+ msgid "Loose Assignment (slower database queries)"
484
  msgstr ""
485
 
486
  #: config/settings.php:191
487
  msgctxt "admin-text"
488
+ msgid "Strict Assignment (faster database queries)"
489
  msgstr ""
490
 
491
  #: config/settings.php:193
1196
  msgid "This review is based on my own experience and is my genuine opinion."
1197
  msgstr ""
1198
 
1199
+ #: plugin/Addons/Addon.php:75
1200
  msgctxt "admin-text"
1201
  msgid "Untitled"
1202
  msgstr ""
1424
  msgid "Support"
1425
  msgstr ""
1426
 
1427
+ #: plugin/Controllers/MenuController.php:108, plugin/Controllers/ToolsController.php:90
1428
  msgctxt "admin-text"
1429
  msgid "FAQ"
1430
  msgstr ""
1574
  msgid "<span>ID: %d</span>"
1575
  msgstr ""
1576
 
1577
+ #: plugin/Controllers/ToolsController.php:29
1578
  msgctxt "admin-text"
1579
  msgid "Console cleared."
1580
  msgstr ""
1581
 
1582
+ #: plugin/Controllers/ToolsController.php:54
1583
  msgctxt "admin-text"
1584
  msgid "The <code>%s</code> table was successfully converted to InnoDB."
1585
  msgstr ""
1586
 
1587
+ #: plugin/Controllers/ToolsController.php:59
1588
  msgctxt "admin-text"
1589
  msgid "The <code>%s</code> table could not be converted to InnoDB."
1590
  msgstr ""
1591
 
1592
+ #: plugin/Controllers/ToolsController.php:64
1593
  msgctxt "admin-text"
1594
  msgid "The <code>%s</code> table was not found in the database."
1595
  msgstr ""
1596
 
1597
+ #: plugin/Controllers/ToolsController.php:99
1598
  msgctxt "admin-text"
1599
  msgid "Your detected IP address is %s. If this looks incorrect, please see the %s."
1600
  msgstr ""
1601
 
1602
+ #: plugin/Controllers/ToolsController.php:94
1603
  msgctxt "admin-text"
1604
  msgid "Site Reviews was unable to detect an IP address. To fix this, please see the %s."
1605
  msgstr ""
1606
 
1607
+ #: plugin/Controllers/ToolsController.php:150
1608
  msgctxt "admin-text"
1609
  msgid "Console reloaded."
1610
  msgstr ""
1611
 
1612
+ #: plugin/Controllers/ToolsController.php:195
1613
  msgctxt "admin-text"
1614
  msgid "The plugin has been migrated sucessfully, please reload the page."
1615
  msgstr ""
1616
 
1617
+ #: plugin/Controllers/ToolsController.php:192
1618
  msgctxt "admin-text"
1619
  msgid "All plugin migrations have been run successfully, please reload the page."
1620
  msgstr ""
1621
 
1622
+ #: plugin/Controllers/ToolsController.php:218
1623
+ msgctxt "admin-text"
1624
+ msgid "The review relationships have been repaired."
1625
+ msgstr ""
1626
+
1627
+ #: plugin/Controllers/ToolsController.php:240
1628
  msgctxt "admin-text"
1629
  msgid "The assigned meta values have been reset."
1630
  msgstr ""
1631
 
1632
+ #: plugin/Controllers/ToolsController.php:266
1633
  msgctxt "admin-text"
1634
  msgid "The permissions have been reset."
1635
  msgstr ""
1636
 
1637
+ #: plugin/Controllers/ToolsController.php:277
1638
  msgctxt "admin-text"
1639
  msgid "reload the page"
1640
  msgstr ""
1641
 
1642
+ #: plugin/Controllers/ToolsController.php:281
1643
  msgctxt "admin-text"
1644
  msgid "The permissions have been reset, please %s for them to take effect."
1645
  msgstr ""
3483
  msgid "Run Migration"
3484
  msgstr ""
3485
 
3486
+ #: views/pages/tools/general/optimise-db-tables.php:6, views/pages/tools/general/repair-review-relations.php:17
3487
  msgctxt "admin-text"
3488
  msgid "Optimise Your Database Tables"
3489
  msgstr ""
3523
  msgid "Convert table engine to InnoDB"
3524
  msgstr ""
3525
 
3526
+ #: views/pages/tools/general/repair-review-relations.php:6
3527
+ msgctxt "admin-text"
3528
+ msgid "Repair Review Relations"
3529
+ msgstr ""
3530
+
3531
+ #: views/pages/tools/general/repair-review-relations.php:36
3532
+ msgctxt "admin-text"
3533
+ msgid "Repair is unnecessary because your database tables use the InnoDB engine!"
3534
+ msgstr ""
3535
+
3536
+ #: views/pages/tools/general/repair-review-relations.php:14
3537
+ msgctxt "admin-text"
3538
+ msgid "Once you have repaired the review relationships, it is recommended that you run the %s tool to prevent the problem from happening again."
3539
+ msgstr ""
3540
+
3541
+ #: views/pages/tools/general/repair-review-relations.php:22
3542
+ msgctxt "admin-text"
3543
+ msgid "Site Reviews stores review details in a custom database table, these entries are linked to the review post type in the WordPress posts table using the review's Post ID."
3544
+ msgstr ""
3545
+
3546
+ #: views/pages/tools/general/repair-review-relations.php:23
3547
+ msgctxt "admin-text"
3548
+ msgid "If your database tables use the standard InnoDB engine, foreign indexes are used to maintain the relationship between the two so that when a review is deleted from the WordPress posts table, the review details are also removed from the custom table."
3549
+ msgstr ""
3550
+
3551
+ #: views/pages/tools/general/repair-review-relations.php:24
3552
+ msgctxt "admin-text"
3553
+ msgid "However, if your database tables use the outdated MyISAM engine, then foreign indexes cannot be used so Site Reviews depends on the built-in WordPress hooks (which are triggered when a review is deleted) in order to remove the relationship and delete the review details from the custom database table."
3554
+ msgstr ""
3555
+
3556
+ #: views/pages/tools/general/repair-review-relations.php:25
3557
+ msgctxt "admin-text"
3558
+ msgid "Depending on the WordPress hooks is problematic because there are many unknown factors involved which may prevent these hooks from being triggered (i.e. manually deleting a review from the database, using a third-party plugin which overrides the hooks, etc.), this can mess up the relationships between the review in the WordPress posts table and the review details in the custom database table and when this happens, it may cause your reviews to incorrectly use the content of a non-review post type."
3559
+ msgstr ""
3560
+
3561
+ #: views/pages/tools/general/repair-review-relations.php:26
3562
+ msgctxt "admin-text"
3563
+ msgid "This tool will repair the review relationships in your database by removing any review details in the custom database table that do not point to a valid review."
3564
+ msgstr ""
3565
+
3566
+ #: views/pages/tools/general/repair-review-relations.php:31
3567
+ msgctxt "admin-text"
3568
+ msgid "Repairing relations, please wait..."
3569
+ msgstr ""
3570
+
3571
+ #: views/pages/tools/general/repair-review-relations.php:31
3572
+ msgctxt "admin-text"
3573
+ msgid "Repair Relations"
3574
+ msgstr ""
3575
+
3576
  #: views/pages/tools/general/reset-assigned-meta.php:6
3577
  msgctxt "admin-text"
3578
  msgid "Reset Assigned Meta Values"
plugin/Addons/Addon.php CHANGED
@@ -25,6 +25,7 @@ abstract class Addon
25
  const ID = '';
26
  const LICENSED = false;
27
  const NAME = '';
 
28
  const SLUG = '';
29
  const UPDATE_URL = '';
30
 
@@ -58,7 +59,7 @@ abstract class Addon
58
  */
59
  public function posts($perPage = 50)
60
  {
61
- if (!defined('static::POST_TYPE')) {
62
  return [];
63
  }
64
  $posts = get_posts([
25
  const ID = '';
26
  const LICENSED = false;
27
  const NAME = '';
28
+ const POST_TYPE = '';
29
  const SLUG = '';
30
  const UPDATE_URL = '';
31
 
59
  */
60
  public function posts($perPage = 50)
61
  {
62
+ if (empty(static::POST_TYPE)) {
63
  return [];
64
  }
65
  $posts = get_posts([
plugin/Controllers/ReviewController.php CHANGED
@@ -211,6 +211,7 @@ class ReviewController extends Controller
211
  $data['review_id'] = $postId;
212
  $data['is_approved'] = 'publish' === get_post_status($postId);
213
  if (false === glsr(Database::class)->insert('ratings', $data)) {
 
214
  wp_delete_post($postId, true); // remove post as review was not created
215
  return;
216
  }
211
  $data['review_id'] = $postId;
212
  $data['is_approved'] = 'publish' === get_post_status($postId);
213
  if (false === glsr(Database::class)->insert('ratings', $data)) {
214
+ glsr_log()->error('To fix your reviews, please try running the "Migrate Plugin" and "Reset Assigned Meta Values" tools.');
215
  wp_delete_post($postId, true); // remove post as review was not created
216
  return;
217
  }
plugin/Controllers/ToolsController.php CHANGED
@@ -4,6 +4,7 @@ namespace GeminiLabs\SiteReviews\Controllers;
4
 
5
  use GeminiLabs\SiteReviews\Commands\ImportReviews;
6
  use GeminiLabs\SiteReviews\Commands\ImportSettings;
 
7
  use GeminiLabs\SiteReviews\Database\CountManager;
8
  use GeminiLabs\SiteReviews\Database\OptionManager;
9
  use GeminiLabs\SiteReviews\Database\SqlSchema;
@@ -207,6 +208,28 @@ class ToolsController extends Controller
207
  ]);
208
  }
209
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  /**
211
  * @return void
212
  * @action site-reviews/route/admin/reset-assigned-meta
4
 
5
  use GeminiLabs\SiteReviews\Commands\ImportReviews;
6
  use GeminiLabs\SiteReviews\Commands\ImportSettings;
7
+ use GeminiLabs\SiteReviews\Database;
8
  use GeminiLabs\SiteReviews\Database\CountManager;
9
  use GeminiLabs\SiteReviews\Database\OptionManager;
10
  use GeminiLabs\SiteReviews\Database\SqlSchema;
208
  ]);
209
  }
210
 
211
+ /**
212
+ * @return void
213
+ * @action site-reviews/route/admin/repair-review-relations
214
+ */
215
+ public function repairReviewRelations()
216
+ {
217
+ glsr(Database::class)->deleteInvalidReviews();
218
+ glsr(Notice::class)->clear()->addSuccess(_x('The review relationships have been repaired.', 'admin-text', 'site-reviews'));
219
+ }
220
+
221
+ /**
222
+ * @return void
223
+ * @action site-reviews/route/ajax/repair-review-relations
224
+ */
225
+ public function repairReviewRelationsAjax()
226
+ {
227
+ $this->repairReviewRelations();
228
+ wp_send_json_success([
229
+ 'notices' => glsr(Notice::class)->get(),
230
+ ]);
231
+ }
232
+
233
  /**
234
  * @return void
235
  * @action site-reviews/route/admin/reset-assigned-meta
plugin/Database.php CHANGED
@@ -121,6 +121,23 @@ class Database
121
  ));
122
  }
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  /**
125
  * @return int|bool
126
  */
121
  ));
122
  }
123
 
124
+ /**
125
+ * @return int|bool
126
+ */
127
+ public function deleteInvalidReviews()
128
+ {
129
+ return $this->dbQuery(sprintf(
130
+ glsr(Query::class)->sql("
131
+ DELETE r
132
+ FROM %s AS r
133
+ LEFT JOIN {$this->db->posts} AS p ON r.review_id = p.ID
134
+ WHERE (p.post_type IS NULL OR p.post_type != '%s')
135
+ "),
136
+ glsr(Query::class)->table('ratings'),
137
+ glsr()->post_type
138
+ ));
139
+ }
140
+
141
  /**
142
  * @return int|bool
143
  */
plugin/Database/NormalizeQueryArgs.php CHANGED
@@ -25,6 +25,7 @@ use GeminiLabs\SiteReviews\Modules\Sanitizer;
25
  * @property int[] $post__in;
26
  * @property int[] $post__not_in;
27
  * @property int $rating;
 
28
  * @property string $status;
29
  * @property string $terms;
30
  * @property string $type;
25
  * @property int[] $post__in;
26
  * @property int[] $post__not_in;
27
  * @property int $rating;
28
+ * @property string $rating_field;
29
  * @property string $status;
30
  * @property string $terms;
31
  * @property string $type;
plugin/Database/Query.php CHANGED
@@ -236,11 +236,11 @@ class Query
236
  protected function queryRatings()
237
  {
238
  return $this->sql("
239
- SELECT r.rating, r.type, COUNT(r.rating) AS count
240
  FROM {$this->table('ratings')} AS r
241
  {$this->sqlJoin()}
242
  {$this->sqlWhere()}
243
- GROUP BY r.type, r.rating
244
  ");
245
  }
246
 
@@ -250,13 +250,13 @@ class Query
250
  public function queryRatingsForPostmeta()
251
  {
252
  return $this->sql("
253
- SELECT apt.post_id AS ID, r.rating, r.type, COUNT(r.rating) AS count
254
  FROM {$this->table('ratings')} AS r
255
  INNER JOIN {$this->table('assigned_posts')} AS apt ON r.ID = apt.rating_id
256
  WHERE 1=1
257
  {$this->clauseAndStatus()}
258
  {$this->clauseAndType()}
259
- GROUP BY r.type, r.rating, apt.post_id
260
  ");
261
  }
262
 
@@ -266,13 +266,13 @@ class Query
266
  protected function queryRatingsForTermmeta()
267
  {
268
  return $this->sql("
269
- SELECT att.term_id AS ID, r.rating, r.type, COUNT(r.rating) AS count
270
  FROM {$this->table('ratings')} AS r
271
  INNER JOIN {$this->table('assigned_terms')} AS att ON r.ID = att.rating_id
272
  WHERE 1=1
273
  {$this->clauseAndStatus()}
274
  {$this->clauseAndType()}
275
- GROUP BY r.type, r.rating, att.term_id
276
  ");
277
  }
278
 
@@ -282,13 +282,13 @@ class Query
282
  protected function queryRatingsForUsermeta()
283
  {
284
  return $this->sql("
285
- SELECT aut.user_id AS ID, r.rating, r.type, COUNT(r.rating) AS count
286
  FROM {$this->table('ratings')} AS r
287
  INNER JOIN {$this->table('assigned_users')} AS aut ON r.ID = aut.rating_id
288
  WHERE 1=1
289
  {$this->clauseAndStatus()}
290
  {$this->clauseAndType()}
291
- GROUP BY r.type, r.rating, aut.user_id
292
  ");
293
  }
294
 
@@ -316,6 +316,7 @@ class Query
316
  protected function queryReviews($reviewIds)
317
  {
318
  $orderBy = !empty($this->args['order']) ? $this->sqlOrderBy() : '';
 
319
  return $this->sql("
320
  SELECT
321
  r.*,
@@ -333,7 +334,7 @@ class Query
333
  LEFT JOIN {$this->table('assigned_posts')} AS apt ON r.ID = apt.rating_id
334
  LEFT JOIN {$this->table('assigned_terms')} AS att ON r.ID = att.rating_id
335
  LEFT JOIN {$this->table('assigned_users')} AS aut ON r.ID = aut.rating_id
336
- WHERE r.review_id IN ({$reviewIds})
337
  GROUP BY r.ID
338
  {$orderBy}
339
  ");
236
  protected function queryRatings()
237
  {
238
  return $this->sql("
239
+ SELECT {$this->ratingColumn()} AS rating, r.type, COUNT(DISTINCT r.ID) AS count
240
  FROM {$this->table('ratings')} AS r
241
  {$this->sqlJoin()}
242
  {$this->sqlWhere()}
243
+ GROUP BY r.type, {$this->ratingColumn()}
244
  ");
245
  }
246
 
250
  public function queryRatingsForPostmeta()
251
  {
252
  return $this->sql("
253
+ SELECT apt.post_id AS ID, {$this->ratingColumn()} AS rating, r.type, COUNT(DISTINCT r.ID) AS count
254
  FROM {$this->table('ratings')} AS r
255
  INNER JOIN {$this->table('assigned_posts')} AS apt ON r.ID = apt.rating_id
256
  WHERE 1=1
257
  {$this->clauseAndStatus()}
258
  {$this->clauseAndType()}
259
+ GROUP BY r.type, {$this->ratingColumn()}, apt.post_id
260
  ");
261
  }
262
 
266
  protected function queryRatingsForTermmeta()
267
  {
268
  return $this->sql("
269
+ SELECT att.term_id AS ID, {$this->ratingColumn()} AS rating, r.type, COUNT(DISTINCT r.ID) AS count
270
  FROM {$this->table('ratings')} AS r
271
  INNER JOIN {$this->table('assigned_terms')} AS att ON r.ID = att.rating_id
272
  WHERE 1=1
273
  {$this->clauseAndStatus()}
274
  {$this->clauseAndType()}
275
+ GROUP BY r.type, {$this->ratingColumn()}, att.term_id
276
  ");
277
  }
278
 
282
  protected function queryRatingsForUsermeta()
283
  {
284
  return $this->sql("
285
+ SELECT aut.user_id AS ID, {$this->ratingColumn()} AS rating, r.type, COUNT(DISTINCT r.ID) AS count
286
  FROM {$this->table('ratings')} AS r
287
  INNER JOIN {$this->table('assigned_users')} AS aut ON r.ID = aut.rating_id
288
  WHERE 1=1
289
  {$this->clauseAndStatus()}
290
  {$this->clauseAndType()}
291
+ GROUP BY r.type, {$this->ratingColumn()}, aut.user_id
292
  ");
293
  }
294
 
316
  protected function queryReviews($reviewIds)
317
  {
318
  $orderBy = !empty($this->args['order']) ? $this->sqlOrderBy() : '';
319
+ $postType = glsr()->post_type;
320
  return $this->sql("
321
  SELECT
322
  r.*,
334
  LEFT JOIN {$this->table('assigned_posts')} AS apt ON r.ID = apt.rating_id
335
  LEFT JOIN {$this->table('assigned_terms')} AS att ON r.ID = att.rating_id
336
  LEFT JOIN {$this->table('assigned_users')} AS aut ON r.ID = aut.rating_id
337
+ WHERE r.review_id IN ({$reviewIds}) AND p.post_type = '{$postType}'
338
  GROUP BY r.ID
339
  {$orderBy}
340
  ");
plugin/Database/Sql.php CHANGED
@@ -229,8 +229,19 @@ trait Sql
229
  */
230
  protected function clauseAndRating()
231
  {
 
232
  return Helper::ifTrue($this->args['rating'] > 0,
233
- $this->db->prepare('AND r.rating > %d', --$this->args['rating'])
 
 
 
 
 
 
 
 
 
 
234
  );
235
  }
236
 
@@ -304,7 +315,7 @@ trait Sql
304
  protected function clauseJoinAssignedPosts()
305
  {
306
  return $this->clauseIfValueNotEmpty(
307
- "INNER JOIN {$this->table('assigned_posts')} AS apt ON r.ID = apt.rating_id",
308
  $this->args['assigned_posts'],
309
  $prepare = false
310
  );
@@ -316,7 +327,7 @@ trait Sql
316
  protected function clauseJoinAssignedTerms()
317
  {
318
  return $this->clauseIfValueNotEmpty(
319
- "INNER JOIN {$this->table('assigned_terms')} AS att ON r.ID = att.rating_id",
320
  $this->args['assigned_terms'],
321
  $prepare = false
322
  );
@@ -328,7 +339,7 @@ trait Sql
328
  protected function clauseJoinAssignedUsers()
329
  {
330
  return $this->clauseIfValueNotEmpty(
331
- "INNER JOIN {$this->table('assigned_users')} AS aut ON r.ID = aut.rating_id",
332
  $this->args['assigned_users'],
333
  $prepare = false
334
  );
@@ -380,6 +391,34 @@ trait Sql
380
  );
381
  }
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  /**
384
  * @return array
385
  */
@@ -401,6 +440,14 @@ trait Sql
401
  return $and;
402
  }
403
 
 
 
 
 
 
 
 
 
404
  /**
405
  * @param int $depth
406
  * @return string
229
  */
230
  protected function clauseAndRating()
231
  {
232
+ $column = $this->isCustomRatingField() ? 'pm.meta_value' : 'r.rating';
233
  return Helper::ifTrue($this->args['rating'] > 0,
234
+ $this->db->prepare("AND {$column} > %d", --$this->args['rating'])
235
+ );
236
+ }
237
+
238
+ /**
239
+ * @return string
240
+ */
241
+ protected function clauseAndRatingField()
242
+ {
243
+ return Helper::ifTrue($this->isCustomRatingField(),
244
+ $this->db->prepare("AND pm.meta_key = %s", sprintf('_custom_%s', $this->args['rating_field']))
245
  );
246
  }
247
 
315
  protected function clauseJoinAssignedPosts()
316
  {
317
  return $this->clauseIfValueNotEmpty(
318
+ "{$this->joinMethod()} {$this->table('assigned_posts')} AS apt ON r.ID = apt.rating_id",
319
  $this->args['assigned_posts'],
320
  $prepare = false
321
  );
327
  protected function clauseJoinAssignedTerms()
328
  {
329
  return $this->clauseIfValueNotEmpty(
330
+ "{$this->joinMethod()} {$this->table('assigned_terms')} AS att ON r.ID = att.rating_id",
331
  $this->args['assigned_terms'],
332
  $prepare = false
333
  );
339
  protected function clauseJoinAssignedUsers()
340
  {
341
  return $this->clauseIfValueNotEmpty(
342
+ "{$this->joinMethod()} {$this->table('assigned_users')} AS aut ON r.ID = aut.rating_id",
343
  $this->args['assigned_users'],
344
  $prepare = false
345
  );
391
  );
392
  }
393
 
394
+ /**
395
+ * @return string
396
+ */
397
+ protected function clauseJoinRatingField()
398
+ {
399
+ return Helper::ifTrue($this->isCustomRatingField(),
400
+ "INNER JOIN {$this->db->postmeta} AS pm ON r.review_id = pm.post_id"
401
+ );
402
+ }
403
+
404
+ /**
405
+ * @return bool
406
+ */
407
+ protected function isCustomRatingField()
408
+ {
409
+ return 'rating' !== $this->args['rating_field'] && !empty($this->args['rating_field']);
410
+ }
411
+
412
+ /**
413
+ * Used to determine the join method used in review assignments
414
+ * @return string
415
+ */
416
+ protected function joinMethod()
417
+ {
418
+ $joins = ['loose' => 'LEFT JOIN', 'strict' => 'INNER JOIN'];
419
+ return Arr::get($joins, glsr_get_option('reviews.assignment', 'strict'), 'INNER JOIN');
420
+ }
421
+
422
  /**
423
  * @return array
424
  */
440
  return $and;
441
  }
442
 
443
+ /**
444
+ * @return string
445
+ */
446
+ protected function ratingColumn()
447
+ {
448
+ return Helper::ifTrue($this->isCustomRatingField(), 'pm.meta_value', 'r.rating');
449
+ }
450
+
451
  /**
452
  * @param int $depth
453
  * @return string
plugin/Database/SqlSchema.php CHANGED
@@ -35,6 +35,7 @@ class SqlSchema
35
  */
36
  public function addAssignedPostsForeignConstraints()
37
  {
 
38
  $this->addForeignConstraint(
39
  $table = $this->table('assigned_posts'),
40
  $constraint = $this->foreignConstraint('assigned_posts_rating_id'),
@@ -56,6 +57,7 @@ class SqlSchema
56
  */
57
  public function addAssignedTermsForeignConstraints()
58
  {
 
59
  $this->addForeignConstraint(
60
  $table = $this->table('assigned_terms'),
61
  $constraint = $this->foreignConstraint('assigned_terms_rating_id'),
@@ -77,6 +79,7 @@ class SqlSchema
77
  */
78
  public function addAssignedUsersForeignConstraints()
79
  {
 
80
  $this->addForeignConstraint(
81
  $table = $this->table('assigned_users'),
82
  $constraint = $this->foreignConstraint('assigned_users_rating_id'),
@@ -98,6 +101,7 @@ class SqlSchema
98
  */
99
  public function addReviewsForeignConstraints()
100
  {
 
101
  $this->addForeignConstraint(
102
  $table = $this->table('ratings'),
103
  $constraint = $this->foreignConstraint('assigned_posts_review_id'),
35
  */
36
  public function addAssignedPostsForeignConstraints()
37
  {
38
+ glsr(Database::class)->deleteInvalidPostAssignments();
39
  $this->addForeignConstraint(
40
  $table = $this->table('assigned_posts'),
41
  $constraint = $this->foreignConstraint('assigned_posts_rating_id'),
57
  */
58
  public function addAssignedTermsForeignConstraints()
59
  {
60
+ glsr(Database::class)->deleteInvalidTermAssignments();
61
  $this->addForeignConstraint(
62
  $table = $this->table('assigned_terms'),
63
  $constraint = $this->foreignConstraint('assigned_terms_rating_id'),
79
  */
80
  public function addAssignedUsersForeignConstraints()
81
  {
82
+ glsr(Database::class)->deleteInvalidUserAssignments();
83
  $this->addForeignConstraint(
84
  $table = $this->table('assigned_users'),
85
  $constraint = $this->foreignConstraint('assigned_users_rating_id'),
101
  */
102
  public function addReviewsForeignConstraints()
103
  {
104
+ glsr(Database::class)->deleteInvalidReviews();
105
  $this->addForeignConstraint(
106
  $table = $this->table('ratings'),
107
  $constraint = $this->foreignConstraint('assigned_posts_review_id'),
plugin/Defaults/ReviewsDefaults.php CHANGED
@@ -18,6 +18,7 @@ class ReviewsDefaults extends Defaults
18
  'pagination' => 'string',
19
  'per_page' => 'int',
20
  'rating' => 'int',
 
21
  'status' => 'string',
22
  ];
23
 
@@ -42,6 +43,7 @@ class ReviewsDefaults extends Defaults
42
  'email' => 'email',
43
  'post__in' => 'array-int',
44
  'post__not_in' => 'array-int',
 
45
  'type' => 'key',
46
  'user__in' => 'array-int',
47
  'user__not_in' => 'array-int',
@@ -68,6 +70,7 @@ class ReviewsDefaults extends Defaults
68
  'post__in' => [],
69
  'post__not_in' => [],
70
  'rating' => '',
 
71
  'status' => 'approved',
72
  'terms' => '',
73
  'type' => '',
18
  'pagination' => 'string',
19
  'per_page' => 'int',
20
  'rating' => 'int',
21
+ 'rating_field' => 'string',
22
  'status' => 'string',
23
  ];
24
 
43
  'email' => 'email',
44
  'post__in' => 'array-int',
45
  'post__not_in' => 'array-int',
46
+ 'rating_field' => 'name',
47
  'type' => 'key',
48
  'user__in' => 'array-int',
49
  'user__not_in' => 'array-int',
70
  'post__in' => [],
71
  'post__not_in' => [],
72
  'rating' => '',
73
+ 'rating_field' => 'rating', // used for custom rating fields
74
  'status' => 'approved',
75
  'terms' => '',
76
  'type' => '',
plugin/Defaults/SiteReviewsDefaults.php CHANGED
@@ -63,6 +63,7 @@ class SiteReviewsDefaults extends Defaults
63
  'page' => 1,
64
  'pagination' => false,
65
  'rating' => 0,
 
66
  'schema' => false,
67
  'terms' => '',
68
  'title' => '',
63
  'page' => 1,
64
  'pagination' => false,
65
  'rating' => 0,
66
+ 'rating_field' => 'rating', // used for custom rating fields
67
  'schema' => false,
68
  'terms' => '',
69
  'title' => '',
plugin/Defaults/SiteReviewsSummaryDefaults.php CHANGED
@@ -55,6 +55,7 @@ class SiteReviewsSummaryDefaults extends Defaults
55
  'id' => '',
56
  'labels' => '',
57
  'rating' => 1,
 
58
  'schema' => false,
59
  'terms' => '',
60
  'text' => '',
55
  'id' => '',
56
  'labels' => '',
57
  'rating' => 1,
58
+ 'rating_field' => 'rating', // used for custom rating fields
59
  'schema' => false,
60
  'terms' => '',
61
  'text' => '',
plugin/Hooks.php CHANGED
@@ -173,6 +173,8 @@ class Hooks implements HooksContract
173
  add_action('site-reviews/route/admin/export-settings', [$this->tools, 'exportSettings']);
174
  add_action('site-reviews/route/admin/import-reviews', [$this->tools, 'importReviews']);
175
  add_action('site-reviews/route/admin/import-settings', [$this->tools, 'importSettings']);
 
 
176
  add_action('site-reviews/route/admin/reset-assigned-meta', [$this->tools, 'resetAssignedMeta']);
177
  add_action('site-reviews/route/ajax/reset-assigned-meta', [$this->tools, 'resetAssignedMetaAjax']);
178
  add_action('site-reviews/route/admin/reset-permissions', [$this->tools, 'resetPermissions']);
173
  add_action('site-reviews/route/admin/export-settings', [$this->tools, 'exportSettings']);
174
  add_action('site-reviews/route/admin/import-reviews', [$this->tools, 'importReviews']);
175
  add_action('site-reviews/route/admin/import-settings', [$this->tools, 'importSettings']);
176
+ add_action('site-reviews/route/admin/repair-review-relations', [$this->tools, 'repairReviewRelations']);
177
+ add_action('site-reviews/route/ajax/repair-review-relations', [$this->tools, 'repairReviewRelationsAjax']);
178
  add_action('site-reviews/route/admin/reset-assigned-meta', [$this->tools, 'resetAssignedMeta']);
179
  add_action('site-reviews/route/ajax/reset-assigned-meta', [$this->tools, 'resetAssignedMetaAjax']);
180
  add_action('site-reviews/route/admin/reset-permissions', [$this->tools, 'resetPermissions']);
plugin/Install.php CHANGED
@@ -129,6 +129,7 @@ class Install
129
  glsr(Database::class)->deleteInvalidPostAssignments();
130
  glsr(Database::class)->deleteInvalidTermAssignments();
131
  glsr(Database::class)->deleteInvalidUserAssignments();
 
132
  }
133
 
134
  /**
129
  glsr(Database::class)->deleteInvalidPostAssignments();
130
  glsr(Database::class)->deleteInvalidTermAssignments();
131
  glsr(Database::class)->deleteInvalidUserAssignments();
132
+ glsr(Database::class)->deleteInvalidReviews();
133
  }
134
 
135
  /**
plugin/Modules/Migrations/Migrate_5_0_0/MigrateSidebars.php CHANGED
@@ -182,7 +182,9 @@ class MigrateSidebars
182
  protected function widgetsExist(array $sidebars)
183
  {
184
  $sidebars = array_filter($sidebars, 'is_array');
185
- $widgets = call_user_func_array('array_merge', array_values($sidebars));
 
 
186
  foreach ($widgets as $widget) {
187
  if (Str::startsWith(glsr()->id.'_', $widget)) {
188
  return true;
182
  protected function widgetsExist(array $sidebars)
183
  {
184
  $sidebars = array_filter($sidebars, 'is_array');
185
+ $sidebars = array_values($sidebars);
186
+ $widgets = call_user_func_array('array_merge', $sidebars);
187
+ $widgets = Arr::consolidate($widgets); // ensure this is an array in case call_user_func_array() errors
188
  foreach ($widgets as $widget) {
189
  if (Str::startsWith(glsr()->id.'_', $widget)) {
190
  return true;
plugin/Modules/Migrations/Migrate_5_9_0.php CHANGED
@@ -68,6 +68,7 @@ class Migrate_5_9_0
68
  glsr(Database::class)->deleteInvalidPostAssignments();
69
  glsr(Database::class)->deleteInvalidTermAssignments();
70
  glsr(Database::class)->deleteInvalidUserAssignments();
 
71
  }
72
 
73
  /**
68
  glsr(Database::class)->deleteInvalidPostAssignments();
69
  glsr(Database::class)->deleteInvalidTermAssignments();
70
  glsr(Database::class)->deleteInvalidUserAssignments();
71
+ glsr(Database::class)->deleteInvalidReviews();
72
  }
73
 
74
  /**
plugin/Modules/Sanitizer.php CHANGED
@@ -83,7 +83,7 @@ class Sanitizer
83
  */
84
  public function sanitizeDate($value, $fallback = '')
85
  {
86
- $date = strtotime(Cast::toString($value));
87
  if (false !== $date) {
88
  return wp_date('Y-m-d H:i:s', $date);
89
  }
@@ -96,7 +96,7 @@ class Sanitizer
96
  */
97
  public function sanitizeEmail($value)
98
  {
99
- return sanitize_email(Cast::toString($value));
100
  }
101
 
102
  /**
@@ -123,7 +123,7 @@ class Sanitizer
123
  }
124
 
125
  /**
126
- * This allows lowercase alphannumeric, dash, and underscore characters
127
  * @param mixed $value
128
  * @return string
129
  */
@@ -158,7 +158,7 @@ class Sanitizer
158
  */
159
  public function sanitizeText($value)
160
  {
161
- return sanitize_text_field(Cast::toString($value));
162
  }
163
 
164
  /**
@@ -174,7 +174,7 @@ class Sanitizer
174
  'strong' => glsr_get($allowedposttags, 'strong'),
175
  ];
176
  $allowedHtml = glsr()->filterString('sanitize/allowed-html-tags', $allowedHtml, $allowedposttags);
177
- return trim(wp_kses(Cast::toString($value), $allowedHtml));
178
  }
179
 
180
  /**
@@ -183,7 +183,7 @@ class Sanitizer
183
  */
184
  public function sanitizeTextMultiline($value)
185
  {
186
- return sanitize_textarea_field(Cast::toString($value));
187
  }
188
 
189
  /**
@@ -192,7 +192,7 @@ class Sanitizer
192
  */
193
  public function sanitizeUrl($value)
194
  {
195
- $url = Cast::toString($value);
196
  if (!Str::startsWith('http://, https://', $url)) {
197
  $url = Str::prefix($url, 'https://');
198
  }
@@ -207,7 +207,7 @@ class Sanitizer
207
  public function sanitizeUserEmail($value)
208
  {
209
  $user = wp_get_current_user();
210
- $value = Cast::toString($value);
211
  if ($user->exists() && !glsr()->retrieveAs('bool', 'import', false)) {
212
  return Helper::ifEmpty($value, $user->user_email);
213
  }
@@ -221,7 +221,7 @@ class Sanitizer
221
  public function sanitizeUserName($value)
222
  {
223
  $user = wp_get_current_user();
224
- $value = Cast::toString($value);
225
  if ($user->exists() && !glsr()->retrieveAs('bool', 'import', false)) {
226
  return Helper::ifEmpty($value, $user->display_name);
227
  }
83
  */
84
  public function sanitizeDate($value, $fallback = '')
85
  {
86
+ $date = strtotime(trim(Cast::toString($value)));
87
  if (false !== $date) {
88
  return wp_date('Y-m-d H:i:s', $date);
89
  }
96
  */
97
  public function sanitizeEmail($value)
98
  {
99
+ return sanitize_email(trim(Cast::toString($value)));
100
  }
101
 
102
  /**
123
  }
124
 
125
  /**
126
+ * This allows lowercase alphannumeric and underscore characters
127
  * @param mixed $value
128
  * @return string
129
  */
158
  */
159
  public function sanitizeText($value)
160
  {
161
+ return sanitize_text_field(trim(Cast::toString($value)));
162
  }
163
 
164
  /**
174
  'strong' => glsr_get($allowedposttags, 'strong'),
175
  ];
176
  $allowedHtml = glsr()->filterString('sanitize/allowed-html-tags', $allowedHtml, $allowedposttags);
177
+ return wp_kses(trim(Cast::toString($value)), $allowedHtml);
178
  }
179
 
180
  /**
183
  */
184
  public function sanitizeTextMultiline($value)
185
  {
186
+ return sanitize_textarea_field(trim(Cast::toString($value)));
187
  }
188
 
189
  /**
192
  */
193
  public function sanitizeUrl($value)
194
  {
195
+ $url = trim(Cast::toString($value));
196
  if (!Str::startsWith('http://, https://', $url)) {
197
  $url = Str::prefix($url, 'https://');
198
  }
207
  public function sanitizeUserEmail($value)
208
  {
209
  $user = wp_get_current_user();
210
+ $value = trim(Cast::toString($value));
211
  if ($user->exists() && !glsr()->retrieveAs('bool', 'import', false)) {
212
  return Helper::ifEmpty($value, $user->user_email);
213
  }
221
  public function sanitizeUserName($value)
222
  {
223
  $user = wp_get_current_user();
224
+ $value = trim(Cast::toString($value));
225
  if ($user->exists() && !glsr()->retrieveAs('bool', 'import', false)) {
226
  return Helper::ifEmpty($value, $user->display_name);
227
  }
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: reviews, ratings, testimonials, woocommerce, product reviews
5
  Tested up to: 5.7
6
  Requires at least: 5.5
7
  Requires PHP: 5.6
8
- Stable tag: 5.11.1
9
  License: GPLv3
10
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -137,6 +137,12 @@ All documentation can be found in the "Help" page of the plugin. If your questio
137
 
138
  ## Changelog
139
 
 
 
 
 
 
 
140
  = 5.11.1 (2021-06-03) =
141
 
142
  - Fixed pagination
5
  Tested up to: 5.7
6
  Requires at least: 5.5
7
  Requires PHP: 5.6
8
+ Stable tag: 5.12.0
9
  License: GPLv3
10
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
11
 
137
 
138
  ## Changelog
139
 
140
+ = 5.12.0-beta2 (2021-06-05) =
141
+
142
+ - Added the "rating_field" shortcode option which allows you to use a custom rating field with the summary
143
+ - Added the "Repair Review Relations" tool
144
+ - Fixed "loose assignment" database queries
145
+
146
  = 5.11.1 (2021-06-03) =
147
 
148
  - Fixed pagination
site-reviews.php CHANGED
@@ -7,7 +7,7 @@
7
  * Plugin Name: Site Reviews
8
  * Plugin URI: https://wordpress.org/plugins/site-reviews
9
  * Description: Receive and display reviews on your website
10
- * Version: 5.11.1
11
  * Author: Paul Ryley
12
  * Author URI: https://geminilabs.io
13
  * License: GPL2
7
  * Plugin Name: Site Reviews
8
  * Plugin URI: https://wordpress.org/plugins/site-reviews
9
  * Description: Receive and display reviews on your website
10
+ * Version: 5.12.0
11
  * Author: Paul Ryley
12
  * Author URI: https://geminilabs.io
13
  * License: GPL2
views/pages/documentation/functions/glsr_get_ratings.php CHANGED
@@ -22,6 +22,7 @@ glsr_get_ratings(array $args = []);</code></pre>
22
  'email' => '',
23
  'ip_address' => '',
24
  'rating' => '',
 
25
  'status' => 'approved', // accepted values are "all", "approved", and "unapproved"
26
  'type' => '',
27
  ];</code></pre>
22
  'email' => '',
23
  'ip_address' => '',
24
  'rating' => '',
25
+ 'rating_field' => '',
26
  'status' => 'approved', // accepted values are "all", "approved", and "unapproved"
27
  'type' => '',
28
  ];</code></pre>
views/pages/documentation/functions/glsr_get_reviews.php CHANGED
@@ -30,6 +30,7 @@ glsr_get_reviews(array $args = []);</code></pre>
30
  'post__in' => [],
31
  'post__not_in' => [],
32
  'rating' => '',
 
33
  'status' => 'approved', // value can be "all", "approved", or "unapproved"
34
  'type' => '',
35
  'user__in' => [],
30
  'post__in' => [],
31
  'post__not_in' => [],
32
  'rating' => '',
33
+ 'rating_field' => '',
34
  'status' => 'approved', // value can be "all", "approved", or "unapproved"
35
  'type' => '',
36
  'user__in' => [],
views/pages/documentation/shortcodes/site_reviews.php CHANGED
@@ -24,6 +24,7 @@
24
  trailingslashit(__DIR__).'site_reviews/offset.php',
25
  trailingslashit(__DIR__).'site_reviews/pagination.php',
26
  trailingslashit(__DIR__).'site_reviews/rating.php',
 
27
  trailingslashit(__DIR__).'site_reviews/schema.php',
28
  trailingslashit(__DIR__).'site_reviews/terms.php',
29
  ];
24
  trailingslashit(__DIR__).'site_reviews/offset.php',
25
  trailingslashit(__DIR__).'site_reviews/pagination.php',
26
  trailingslashit(__DIR__).'site_reviews/rating.php',
27
+ trailingslashit(__DIR__).'site_reviews/rating_field.php',
28
  trailingslashit(__DIR__).'site_reviews/schema.php',
29
  trailingslashit(__DIR__).'site_reviews/terms.php',
30
  ];
views/pages/documentation/shortcodes/site_reviews/rating_field.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <p class="glsr-heading">rating_field</p>
2
+ <div class="components-notice is-info">
3
+ <p class="components-notice__content">Custom rating fields can be added with the <a href="<?= admin_url('edit.php?post_type='.glsr()->post_type.'&page=addons'); ?>">Review Forms</a> add-on.</p>
4
+ </div>
5
+ <p>Include the "rating_field" option to make the "rating" option apply to the value of a custom rating field. Use the custom rating Field Name as the value.</p>
6
+ <p>The default rating_field value is: <code>""</code></p>
7
+ <div class="shortcode-example">
8
+ <input type="text" readonly class="code" value='[site_reviews rating_field="sound_rating"]'>
9
+ <pre><code class="syntax-shortcode"><span class="tag">[site_reviews</span> <span class="attr-name">rating_field</span>=<span class="attr-value">"sound_rating"</span><span class="tag">]</span></code></pre>
10
+ </div>
views/pages/documentation/shortcodes/site_reviews_summary.php CHANGED
@@ -21,6 +21,7 @@
21
  trailingslashit(__DIR__).'site_reviews_summary/id.php',
22
  trailingslashit(__DIR__).'site_reviews_summary/labels.php',
23
  trailingslashit(__DIR__).'site_reviews_summary/rating.php',
 
24
  trailingslashit(__DIR__).'site_reviews_summary/schema.php',
25
  trailingslashit(__DIR__).'site_reviews_summary/terms.php',
26
  trailingslashit(__DIR__).'site_reviews_summary/text.php',
21
  trailingslashit(__DIR__).'site_reviews_summary/id.php',
22
  trailingslashit(__DIR__).'site_reviews_summary/labels.php',
23
  trailingslashit(__DIR__).'site_reviews_summary/rating.php',
24
+ trailingslashit(__DIR__).'site_reviews_summary/rating_field.php',
25
  trailingslashit(__DIR__).'site_reviews_summary/schema.php',
26
  trailingslashit(__DIR__).'site_reviews_summary/terms.php',
27
  trailingslashit(__DIR__).'site_reviews_summary/text.php',
views/pages/documentation/shortcodes/site_reviews_summary/rating_field.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <p class="glsr-heading">rating_field</p>
2
+ <div class="components-notice is-info">
3
+ <p class="components-notice__content">Custom rating fields can be added with the <a href="<?= admin_url('edit.php?post_type='.glsr()->post_type.'&page=addons'); ?>">Review Forms</a> add-on.</p>
4
+ </div>
5
+ <p>Include the "rating_field" option to use the values of a custom rating field for the ratings in the summary. Use the custom rating Field Name as the value.</p>
6
+ <p>The default rating_field value is: <code>""</code></p>
7
+ <div class="shortcode-example">
8
+ <input type="text" readonly class="code" value='[site_reviews_summary rating_field="sound_rating"]'>
9
+ <pre><code class="syntax-shortcode"><span class="tag">[site_reviews_summary</span> <span class="attr-name">rating_field</span>=<span class="attr-value">"sound_rating"</span><span class="tag">]</span></code></pre>
10
+ </div>
views/pages/tools/general.php CHANGED
@@ -6,6 +6,7 @@ $sections = [
6
  trailingslashit(__DIR__).'general/import-reviews.php',
7
  trailingslashit(__DIR__).'general/migrate-plugin.php',
8
  trailingslashit(__DIR__).'general/optimise-db-tables.php',
 
9
  trailingslashit(__DIR__).'general/reset-assigned-meta.php',
10
  trailingslashit(__DIR__).'general/reset-permissions.php',
11
  trailingslashit(__DIR__).'general/test-ip-detection.php',
6
  trailingslashit(__DIR__).'general/import-reviews.php',
7
  trailingslashit(__DIR__).'general/migrate-plugin.php',
8
  trailingslashit(__DIR__).'general/optimise-db-tables.php',
9
+ trailingslashit(__DIR__).'general/repair-review-relations.php',
10
  trailingslashit(__DIR__).'general/reset-assigned-meta.php',
11
  trailingslashit(__DIR__).'general/reset-permissions.php',
12
  trailingslashit(__DIR__).'general/test-ip-detection.php',
views/pages/tools/general/repair-review-relations.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php glsr()->hasPermission('settings') || die; ?>
2
+
3
+ <div class="glsr-card postbox">
4
+ <h3 class="glsr-card-heading">
5
+ <button type="button" class="glsr-accordion-trigger" aria-expanded="false" aria-controls="tools-repair-review-relations">
6
+ <span class="title dashicons-before dashicons-admin-tools"><?= _x('Repair Review Relations', 'admin-text', 'site-reviews'); ?></span>
7
+ <span class="icon"></span>
8
+ </button>
9
+ </h3>
10
+ <div id="tools-repair-review-relations" class="inside">
11
+ <?php if (!empty($myisam_tables)) { ?>
12
+ <div class="components-notice is-info" style="margin-bottom:1em;">
13
+ <p class="components-notice__content"><?= sprintf(
14
+ _x('Once you have repaired the review relationships, it is recommended that you run the %s tool to prevent the problem from happening again.', 'admin-text', 'site-reviews'),
15
+ sprintf('<a data-expand="#tools-optimise-db-tables" href="%s">%s</a>',
16
+ admin_url('edit.php?post_type='.glsr()->post_type.'&page=tools#tab-general'),
17
+ _x('Optimise Your Database Tables', 'admin-text', 'site-reviews')
18
+ )
19
+ ); ?>
20
+ </p>
21
+ </div>
22
+ <p><?= _x('Site Reviews stores review details in a custom database table, these entries are linked to the review post type in the WordPress posts table using the review\'s Post ID.', 'admin-text', 'site-reviews'); ?></p>
23
+ <p><?= _x('If your database tables use the standard InnoDB engine, foreign indexes are used to maintain the relationship between the two so that when a review is deleted from the WordPress posts table, the review details are also removed from the custom table.', 'admin-text', 'site-reviews'); ?></p>
24
+ <p><?= _x('However, if your database tables use the outdated MyISAM engine, then foreign indexes cannot be used so Site Reviews depends on the built-in WordPress hooks (which are triggered when a review is deleted) in order to remove the relationship and delete the review details from the custom database table.', 'admin-text', 'site-reviews'); ?></p>
25
+ <p><?= _x('Depending on the WordPress hooks is problematic because there are many unknown factors involved which may prevent these hooks from being triggered (i.e. manually deleting a review from the database, using a third-party plugin which overrides the hooks, etc.), this can mess up the relationships between the review in the WordPress posts table and the review details in the custom database table and when this happens, it may cause your reviews to incorrectly use the content of a non-review post type.', 'admin-text', 'site-reviews'); ?></p>
26
+ <p><?= _x('This tool will repair the review relationships in your database by removing any review details in the custom database table that do not point to a valid review.', 'admin-text', 'site-reviews'); ?></p>
27
+ <form method="post">
28
+ <?php wp_nonce_field('repair-review-relations'); ?>
29
+ <input type="hidden" name="{{ id }}[_action]" value="repair-review-relations">
30
+ <button type="submit" class="glsr-button components-button is-secondary" id="repair-review-relations" data-ajax-click data-ajax-scroll>
31
+ <span data-loading="<?= esc_attr_x('Repairing relations, please wait...', 'admin-text', 'site-reviews'); ?>"><?= _x('Repair Relations', 'admin-text', 'site-reviews'); ?></span>
32
+ </button>
33
+ </form>
34
+ <?php } else { ?>
35
+ <div class="components-notice is-success" style="margin-bottom:1em;">
36
+ <p class="components-notice__content"><?= _x('Repair is unnecessary because your database tables use the InnoDB engine!', 'admin-text', 'site-reviews'); ?> ✨</p>
37
+ </div>
38
+ <?php } ?>
39
+ </div>
40
+ </div>
views/pages/welcome/whatsnew.php CHANGED
@@ -4,6 +4,7 @@
4
  <div class="is-fullwidth">
5
  <div class="glsr-flex-row">
6
  <div class="glsr-column">
 
7
  <?php include trailingslashit(__DIR__).'whatsnew/v511.php'; ?>
8
  <?php include trailingslashit(__DIR__).'whatsnew/v510.php'; ?>
9
  <?php include trailingslashit(__DIR__).'whatsnew/v59.php'; ?>
4
  <div class="is-fullwidth">
5
  <div class="glsr-flex-row">
6
  <div class="glsr-column">
7
+ <?php include trailingslashit(__DIR__).'whatsnew/v512.php'; ?>
8
  <?php include trailingslashit(__DIR__).'whatsnew/v511.php'; ?>
9
  <?php include trailingslashit(__DIR__).'whatsnew/v510.php'; ?>
10
  <?php include trailingslashit(__DIR__).'whatsnew/v59.php'; ?>
views/pages/welcome/whatsnew/v511.php CHANGED
@@ -1,6 +1,6 @@
1
- <div class="glsr-card postbox is-fullwidth open">
2
  <h3 class="glsr-card-heading">
3
- <button type="button" class="glsr-accordion-trigger" aria-expanded="true" aria-controls="welcome-v5_11_0">
4
  <span class="title">Version 5.11</span>
5
  <span class="icon"></span>
6
  </button>
1
+ <div class="glsr-card postbox is-fullwidth">
2
  <h3 class="glsr-card-heading">
3
+ <button type="button" class="glsr-accordion-trigger" aria-expanded="false" aria-controls="welcome-v5_11_0">
4
  <span class="title">Version 5.11</span>
5
  <span class="icon"></span>
6
  </button>
views/pages/welcome/whatsnew/v512.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="glsr-card postbox is-fullwidth open">
2
+ <h3 class="glsr-card-heading">
3
+ <button type="button" class="glsr-accordion-trigger" aria-expanded="true" aria-controls="welcome-v5_12_0">
4
+ <span class="title">Version 5.12</span>
5
+ <span class="icon"></span>
6
+ </button>
7
+ </h3>
8
+ <div id="welcome-v5_12_0" class="inside">
9
+ <p><em>Initial Release Date &mdash; June 7th, 2021</em></p>
10
+ <h4>✨ New Features</h4>
11
+ <ul>
12
+ <li>Added the "rating_field" shortcode option which allows you to use a custom rating field with the summary (the <a href="<?= admin_url('edit.php?post_type='.glsr()->post_type.'&page=addons'); ?>">Review Forms</a> add-on is required to add custom fields)</li>
13
+ <li>Added the "Repair Review Relations" tool</li>
14
+ </ul>
15
+ <h4>🐞 Bugs Fixed</h4>
16
+ <ul>
17
+ <li>Fixed "loose assignment" database queries</li>
18
+ </ul>
19
+ </div>
20
+ </div>