PluginProbe
wpForo Forum / 2.3.0
wpForo Forum v2.3.0
3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 1.4.13 1.4.2 All 137 releases
wpforo / includes / functions.php

functions.php in wpForo Forum 2.3.0, at includes/functions.php

3,832 lines 153.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // Exit if accessed directly
3 if( ! defined( 'ABSPATH' ) ) exit;
4
5 function wpforo_get_modules_info( $base = null ) {
6 $modules = [
7 'tags' => [
8 'base' => false,
9 'version' => '1.0.0',
10 'requires' => '1.5.2',
11 'class' => 'wpforo\classes\Topics',
12 'deps' => [],
13 'title' => 'Topic Tags',
14 'thumb' => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="122.879px" height="50px" viewBox="0 0 122.879 106.533" enable-background="new 0 0 122.879 106.533" xml:space="preserve"><g><path d="M79.704,105.239c-1.2,1.072-3.042,0.971-4.115-0.229c-1.073-1.2-0.971-3.042,0.229-4.115l39.432-35.204l0.012-0.01v-0.001 c0.569-0.503,0.995-1.045,1.281-1.629c0.281-0.574,0.443-1.229,0.487-1.963c0.044-0.732-0.041-1.404-0.255-2.016 c-0.213-0.605-0.572-1.192-1.077-1.761l-0.048-0.055L70.895,9.569c-1.092-1.187-1.015-3.035,0.172-4.126s3.034-1.015,4.126,0.172 l44.758,48.693c0.038,0.039,0.075,0.078,0.112,0.119c1.004,1.127,1.744,2.37,2.22,3.727c0.474,1.352,0.665,2.777,0.575,4.277 c-0.09,1.497-0.448,2.89-1.081,4.182c-0.629,1.283-1.512,2.427-2.653,3.435l-0.001-0.001L79.704,105.239L79.704,105.239z M54.383,3.131h0.738v0.006c0.788,0,1.574,0.317,2.149,0.942l44.758,48.694c0.038,0.039,0.075,0.078,0.111,0.119 c1.005,1.128,1.745,2.37,2.221,3.727c0.474,1.352,0.665,2.777,0.575,4.278c-0.091,1.496-0.449,2.89-1.082,4.181 c-0.628,1.283-1.511,2.427-2.653,3.435v-0.001l-39.291,35.077c-0.026,0.028-0.055,0.055-0.083,0.082 c-1.113,1.032-2.346,1.787-3.698,2.266c-1.34,0.474-2.774,0.666-4.302,0.575c-1.53-0.091-2.936-0.451-4.219-1.076 c-1.287-0.628-2.435-1.511-3.447-2.647l-42.083-47.2l0.001-0.001c-0.505-0.567-0.79-1.33-0.732-2.147l0.086-1.181L0.006,3.114h0 c-0.008-0.124-0.009-0.25,0-0.377c0.101-1.609,1.487-2.833,3.097-2.731L53.451,3.22c0.063-0.01,0.127-0.02,0.192-0.028 C53.934,3.151,54.182,3.131,54.383,3.131L54.383,3.131z M53.922,9.068c-0.195,0.019-0.389,0.018-0.579-0.002L6.066,6.047 l3.208,46.027c0.009,0.135,0.009,0.269-0.001,0.4h0.001l-0.011,0.148l41.261,46.28c0.498,0.559,1.045,0.984,1.642,1.275 c0.6,0.292,1.267,0.462,2.001,0.505c0.738,0.044,1.41-0.042,2.016-0.256c0.594-0.21,1.149-0.554,1.663-1.031 c0.059-0.055,0.12-0.106,0.182-0.155l39.296-35.082l0.012-0.011l-0.001-0.001c0.569-0.502,0.996-1.045,1.281-1.628 c0.282-0.575,0.443-1.229,0.488-1.964c0.044-0.732-0.042-1.404-0.256-2.016c-0.213-0.605-0.571-1.192-1.077-1.761l-0.048-0.055 L53.922,9.068L53.922,9.068z M24.268,12.234c1.474,0.04,2.833,0.335,4.078,0.885c1.256,0.555,2.392,1.36,3.406,2.413l0.013,0.015 c1.006,1.042,1.766,2.204,2.278,3.485c0.519,1.298,0.779,2.68,0.779,4.145c0,0.127-0.008,0.252-0.024,0.375 c-0.071,1.347-0.37,2.605-0.895,3.772l-0.005,0.01l0.005,0.002c-0.563,1.251-1.38,2.386-2.446,3.404 c-0.063,0.06-0.128,0.117-0.195,0.17c-1.023,0.923-2.145,1.621-3.363,2.09c-1.271,0.489-2.614,0.72-4.031,0.69v0.01 c-1.463,0-2.838-0.278-4.123-0.832c-1.265-0.544-2.419-1.354-3.459-2.424l-0.012-0.012v0c-1.028-1.067-1.796-2.246-2.304-3.537 c-0.504-1.285-0.738-2.663-0.698-4.133c0.039-1.495,0.334-2.873,0.892-4.135c0.564-1.278,1.387-2.43,2.473-3.457 c1.058-1,2.232-1.75,3.521-2.247S22.818,12.195,24.268,12.234L24.268,12.234z M25.991,18.468c-0.57-0.251-1.19-0.387-1.86-0.405 c-0.681-0.019-1.305,0.082-1.87,0.3c-0.568,0.22-1.11,0.573-1.625,1.059c-0.5,0.472-0.874,0.992-1.124,1.56 c-0.257,0.582-0.394,1.222-0.412,1.918c-0.019,0.674,0.085,1.297,0.31,1.869c0.224,0.571,0.582,1.11,1.069,1.617l0,0 c0.485,0.497,1.01,0.867,1.574,1.109c0.543,0.234,1.148,0.351,1.814,0.351v0.011l0.069,0.001c0.688,0.019,1.315-0.082,1.882-0.3 c0.528-0.203,1.032-0.518,1.512-0.943c0.033-0.036,0.068-0.07,0.104-0.104c0.506-0.483,0.888-1.009,1.143-1.576l0.004,0.002 c0.224-0.502,0.355-1.047,0.392-1.636c-0.002-0.041-0.003-0.083-0.003-0.125c0-0.74-0.115-1.398-0.345-1.973 c-0.231-0.579-0.586-1.115-1.063-1.607l-0.018-0.018C27.072,19.087,26.554,18.717,25.991,18.468L25.991,18.468z M52.422,31.36 c-1.092-1.187-1.015-3.035,0.171-4.126c1.187-1.092,3.034-1.015,4.126,0.172l24.931,27.04c1.092,1.187,1.015,3.034-0.172,4.126 s-3.034,1.016-4.126-0.172L52.422,31.36L52.422,31.36z M40.49,42.661c-1.092-1.187-1.015-3.034,0.171-4.126 c1.187-1.092,3.034-1.016,4.126,0.171l24.931,27.04c1.092,1.187,1.015,3.034-0.172,4.126c-1.188,1.092-3.035,1.016-4.127-0.171 L40.49,42.661L40.49,42.661z M28.074,53.962c-1.092-1.187-1.015-3.035,0.171-4.126c1.187-1.092,3.035-1.015,4.126,0.171 l24.931,27.04c1.092,1.188,1.016,3.034-0.171,4.126s-3.034,1.016-4.126-0.171L28.074,53.962L28.074,53.962z"></path></g></svg>',
15 'desc' => __( 'Adds Tags Feature For Topics', 'wpforo' ),
16 'url' => 'https://wpforo.com',
17 ],
18 'subscriptions' => [
19 'base' => true,
20 'version' => '1.0.0',
21 'requires' => '1.0.2',
22 'class' => 'wpforo\modules\subscriptions\Subscriptions',
23 'deps' => [],
24 'title' => 'Subscriptions',
25 'thumb' => '<svg height="54px" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 117.88 122.88"><path d="M102.31,30.86,78,5.74V5.49H12.44V46h0L46.66,76.6l.15.14L58.36,87.06,105,46.36v-10l-2.64-5.49ZM64.07,28.44l.09-.41.56-2.47h6.83L67.62,44.1c-.07.33-.14.63-.19.9h0a9.34,9.34,0,0,0-.18,1.42,1.43,1.43,0,0,0,.29.94,1,1,0,0,0,.72.33c.93,0,2.14-.61,3.61-1.82a16.05,16.05,0,0,0,3.85-5,15.19,15.19,0,0,0,.47-12.71,13.86,13.86,0,0,0-3.4-4.76,16,16,0,0,0-5.5-3.15,22.48,22.48,0,0,0-7.41-1.14,25.87,25.87,0,0,0-7.73,1.11A19.49,19.49,0,0,0,46,23.41a17.46,17.46,0,0,0-4.45,5.08,22.14,22.14,0,0,0-2.4,5.84,25.72,25.72,0,0,0-.83,6.54A18.36,18.36,0,0,0,40.07,49a16.3,16.3,0,0,0,5.14,6.18,21,21,0,0,0,8.16,3.55,34.53,34.53,0,0,0,10.8.42A25.52,25.52,0,0,0,72.27,57a15.69,15.69,0,0,0,5.63-4.5h5.51a18.14,18.14,0,0,1-3.5,5,20.31,20.31,0,0,1-5.06,3.66,26.25,26.25,0,0,1-6.49,2.26,36.89,36.89,0,0,1-7.84.77,36.4,36.4,0,0,1-11.44-1.67,23.41,23.41,0,0,1-8.58-4.81,20.32,20.32,0,0,1-5.27-7.5,25.24,25.24,0,0,1-1.77-9.62A28.31,28.31,0,0,1,35.3,30.22,23.54,23.54,0,0,1,49,16.39a29.27,29.27,0,0,1,11-2,27,27,0,0,1,9,1.44A19.93,19.93,0,0,1,76,20a18.12,18.12,0,0,1,4.47,6.38A21,21,0,0,1,82,34.46a19.13,19.13,0,0,1-1.24,6.78,19.37,19.37,0,0,1-3.56,6,17.25,17.25,0,0,1-5.44,4.19A15.06,15.06,0,0,1,65,53a6.17,6.17,0,0,1-3.25-.72A3.48,3.48,0,0,1,60.22,50,12,12,0,0,1,59,51a10.61,10.61,0,0,1-1.06.68,9.49,9.49,0,0,1-2.89,1.07,9.65,9.65,0,0,1-5.67-.55A9.38,9.38,0,0,1,45,48.44a12,12,0,0,1-1.69-6.56,19.55,19.55,0,0,1,1.74-8,16.59,16.59,0,0,1,5-6.44,11.56,11.56,0,0,1,7.29-2.51A7.46,7.46,0,0,1,64,28.25l.12.19ZM49.93,41.83a7.62,7.62,0,0,0,1.29,4.76,4.1,4.1,0,0,0,3.38,1.66,5.75,5.75,0,0,0,4.28-2,10,10,0,0,0,1-1.23,10.61,10.61,0,0,0,.6-.93,13.67,13.67,0,0,0,1.2-2.74,18.71,18.71,0,0,0,1-5.66v-.07a10.84,10.84,0,0,0-.06-1.16,7.48,7.48,0,0,0-.26-1.3,5.92,5.92,0,0,0-1-1.93A4.18,4.18,0,0,0,59.84,30a4.76,4.76,0,0,0-2-.42,5.74,5.74,0,0,0-4.1,1.78,11.34,11.34,0,0,0-2.78,4.54h0l0,0a17.92,17.92,0,0,0-1,5.86Zm60.51-.26a3.59,3.59,0,0,1,2.43-.93,4,4,0,0,1,2.06.6,5.09,5.09,0,0,1,1.26,1.07,7.06,7.06,0,0,1,1.69,4.26v70.64a5.71,5.71,0,0,1-1.66,4h0a5.67,5.67,0,0,1-4,1.66H5.67a5.71,5.71,0,0,1-4-1.66h0a5.62,5.62,0,0,1-1.66-4V46.57a7.1,7.1,0,0,1,1.73-4.32,5.5,5.5,0,0,1,1.26-1,4,4,0,0,1,2-.58,3.59,3.59,0,0,1,2,.57V2.74A2.74,2.74,0,0,1,9.7,0H78.9A2.71,2.71,0,0,1,81,1l28.65,29.59a2.71,2.71,0,0,1,.78,1.9h0v.79c0,.11,0,.22,0,.34s0,.22,0,.33v7.63ZM56.52,92.74,44.9,82.36,5.49,116.9v.31a.17.17,0,0,0,0,.13h0a.22.22,0,0,0,.13,0H112.21a.22.22,0,0,0,.13,0h0a.17.17,0,0,0,0-.13V117L72.45,82,60.15,92.76h0a2.73,2.73,0,0,1-3.62,0Zm-15.73-14L5.49,47.15v62.48l35.3-30.94Zm71.6,31.06V47.12L76.6,78.39l35.79,31.36Z"></path></svg>',
26 'desc' => __( 'Adds Subscriptions & Mentioning Feature For Topics', 'wpforo' ),
27 'url' => 'https://wpforo.com',
28 ],
29 'mentioning' => [
30 'base' => true,
31 'version' => '1.0.0',
32 'requires' => '2.0.0',
33 'class' => 'wpforo\modules\mentioning\Mentioning',
34 'deps' => [ 'wpforo\modules\subscriptions\Subscriptions' ],
35 'title' => 'User Mentioning System',
36 'thumb' => '<svg height="54px" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 117.88 122.88"><path d="M102.31,30.86,78,5.74V5.49H12.44V46h0L46.66,76.6l.15.14L58.36,87.06,105,46.36v-10l-2.64-5.49ZM64.07,28.44l.09-.41.56-2.47h6.83L67.62,44.1c-.07.33-.14.63-.19.9h0a9.34,9.34,0,0,0-.18,1.42,1.43,1.43,0,0,0,.29.94,1,1,0,0,0,.72.33c.93,0,2.14-.61,3.61-1.82a16.05,16.05,0,0,0,3.85-5,15.19,15.19,0,0,0,.47-12.71,13.86,13.86,0,0,0-3.4-4.76,16,16,0,0,0-5.5-3.15,22.48,22.48,0,0,0-7.41-1.14,25.87,25.87,0,0,0-7.73,1.11A19.49,19.49,0,0,0,46,23.41a17.46,17.46,0,0,0-4.45,5.08,22.14,22.14,0,0,0-2.4,5.84,25.72,25.72,0,0,0-.83,6.54A18.36,18.36,0,0,0,40.07,49a16.3,16.3,0,0,0,5.14,6.18,21,21,0,0,0,8.16,3.55,34.53,34.53,0,0,0,10.8.42A25.52,25.52,0,0,0,72.27,57a15.69,15.69,0,0,0,5.63-4.5h5.51a18.14,18.14,0,0,1-3.5,5,20.31,20.31,0,0,1-5.06,3.66,26.25,26.25,0,0,1-6.49,2.26,36.89,36.89,0,0,1-7.84.77,36.4,36.4,0,0,1-11.44-1.67,23.41,23.41,0,0,1-8.58-4.81,20.32,20.32,0,0,1-5.27-7.5,25.24,25.24,0,0,1-1.77-9.62A28.31,28.31,0,0,1,35.3,30.22,23.54,23.54,0,0,1,49,16.39a29.27,29.27,0,0,1,11-2,27,27,0,0,1,9,1.44A19.93,19.93,0,0,1,76,20a18.12,18.12,0,0,1,4.47,6.38A21,21,0,0,1,82,34.46a19.13,19.13,0,0,1-1.24,6.78,19.37,19.37,0,0,1-3.56,6,17.25,17.25,0,0,1-5.44,4.19A15.06,15.06,0,0,1,65,53a6.17,6.17,0,0,1-3.25-.72A3.48,3.48,0,0,1,60.22,50,12,12,0,0,1,59,51a10.61,10.61,0,0,1-1.06.68,9.49,9.49,0,0,1-2.89,1.07,9.65,9.65,0,0,1-5.67-.55A9.38,9.38,0,0,1,45,48.44a12,12,0,0,1-1.69-6.56,19.55,19.55,0,0,1,1.74-8,16.59,16.59,0,0,1,5-6.44,11.56,11.56,0,0,1,7.29-2.51A7.46,7.46,0,0,1,64,28.25l.12.19ZM49.93,41.83a7.62,7.62,0,0,0,1.29,4.76,4.1,4.1,0,0,0,3.38,1.66,5.75,5.75,0,0,0,4.28-2,10,10,0,0,0,1-1.23,10.61,10.61,0,0,0,.6-.93,13.67,13.67,0,0,0,1.2-2.74,18.71,18.71,0,0,0,1-5.66v-.07a10.84,10.84,0,0,0-.06-1.16,7.48,7.48,0,0,0-.26-1.3,5.92,5.92,0,0,0-1-1.93A4.18,4.18,0,0,0,59.84,30a4.76,4.76,0,0,0-2-.42,5.74,5.74,0,0,0-4.1,1.78,11.34,11.34,0,0,0-2.78,4.54h0l0,0a17.92,17.92,0,0,0-1,5.86Zm60.51-.26a3.59,3.59,0,0,1,2.43-.93,4,4,0,0,1,2.06.6,5.09,5.09,0,0,1,1.26,1.07,7.06,7.06,0,0,1,1.69,4.26v70.64a5.71,5.71,0,0,1-1.66,4h0a5.67,5.67,0,0,1-4,1.66H5.67a5.71,5.71,0,0,1-4-1.66h0a5.62,5.62,0,0,1-1.66-4V46.57a7.1,7.1,0,0,1,1.73-4.32,5.5,5.5,0,0,1,1.26-1,4,4,0,0,1,2-.58,3.59,3.59,0,0,1,2,.57V2.74A2.74,2.74,0,0,1,9.7,0H78.9A2.71,2.71,0,0,1,81,1l28.65,29.59a2.71,2.71,0,0,1,.78,1.9h0v.79c0,.11,0,.22,0,.34s0,.22,0,.33v7.63ZM56.52,92.74,44.9,82.36,5.49,116.9v.31a.17.17,0,0,0,0,.13h0a.22.22,0,0,0,.13,0H112.21a.22.22,0,0,0,.13,0h0a.17.17,0,0,0,0-.13V117L72.45,82,60.15,92.76h0a2.73,2.73,0,0,1-3.62,0Zm-15.73-14L5.49,47.15v62.48l35.3-30.94Zm71.6,31.06V47.12L76.6,78.39l35.79,31.36Z"></path></svg>',
37 'desc' => __( 'Adds User Following System Feature For Users', 'wpforo' ),
38 'url' => 'https://wpforo.com',
39 ],
40 'follows' => [
41 'base' => true,
42 'version' => '1.0.0',
43 'requires' => '2.0.0',
44 'class' => 'wpforo\modules\follows\Follows',
45 'deps' => [ 'wpforo\modules\subscriptions\Subscriptions' ],
46 'title' => 'User Following System',
47 'thumb' => '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 116.42 122.88" style="enable-background:new 0 0 116.42 122.88" xml:space="preserve"><style type="text/css">.st0{fill-rule:evenodd;clip-rule:evenodd;} .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#3AAF3C;}</style><g><path class="st0" d="M5.95,112.26c-5.1-0.39-6.33-4.06-5.86-8.29c2.79-24.96,30.78-17.73,42.03-27.86l0,0 c5.61,16.5,29.05,17.11,34.31,0c1.21,1.09,2.89,2.01,4.87,2.82c-1.81,1.1-3.52,2.43-5.09,4c-7.93,7.92-9.88,19.57-5.86,29.33H5.95 L5.95,112.26z M41.97,59.56c2.13,3.37,4.36,6.83,7.12,9.37c2.66,2.43,5.9,4.09,10.16,4.1c4.64,0.01,8.01-1.7,10.76-4.28 c2.86-2.67,5.11-6.34,7.34-10l5.98-9.84c1.11-2.55,1.52-4.25,1.26-5.25c-0.16-0.59-0.81-0.88-1.92-0.93 c-0.23-0.01-0.48-0.01-0.72-0.01c-0.26,0.01-0.54,0.03-0.84,0.05c-0.17,0.01-0.31,0-0.46-0.03c-0.52,0.03-1.08-0.01-1.63-0.09 l2.04-9.06c-15.19,2.39-26.55-8.88-42.59-2.25l1.16,10.67c-0.63,0.04-1.25,0.01-1.82-0.07C28.6,42.24,40.16,56.67,41.97,59.56 L41.97,59.56L41.97,59.56L41.97,59.56z M84.74,40.01c1.47,0.45,2.41,1.38,2.8,2.89c0.43,1.67-0.04,4.03-1.46,7.25l0,0 c-0.03,0.06-0.05,0.12-0.09,0.17l-6.04,9.95c-2.33,3.84-4.69,7.69-7.85,10.63c-3.26,3.06-7.3,5.1-12.81,5.08 c-5.14-0.01-9.02-1.97-12.2-4.89c-3.84-3.52-21.52-25.66-13.62-30.99c0.39-0.25,0.82-0.48,1.28-0.65 c-0.35-4.58-0.47-10.34-0.25-15.17c0.12-1.14,0.34-2.28,0.65-3.43c1.35-4.85,4.76-8.75,8.96-11.43c2.32-1.48,4.87-2.59,7.51-3.33 c1.68-0.48-1.43-5.87,0.3-6.03c8.41-0.87,22.05,6.82,27.93,13.19c2.93,3.18,4.8,7.41,5.2,13L84.74,40.01L84.74,40.01L84.74,40.01 L84.74,40.01L84.74,40.01L84.74,40.01z"/><path class="st1" d="M95.32,80.66c11.66,0,21.11,9.45,21.11,21.11c0,11.66-9.45,21.11-21.11,21.11c-11.66,0-21.11-9.45-21.11-21.11 C74.21,90.11,83.66,80.66,95.32,80.66L95.32,80.66L95.32,80.66L95.32,80.66z M87.77,100.17c1.58,0.91,2.61,1.67,3.83,3.02 c3.17-5.11,6.62-7.94,11.1-11.97l0.44-0.17h4.91c-6.58,7.3-11.68,13.33-16.24,22.13c-2.38-5.08-4.5-8.59-9.23-11.84L87.77,100.17 L87.77,100.17L87.77,100.17z"/></g></svg>',
48 'desc' => __( 'Adds User Following System Feature For Users', 'wpforo' ),
49 'url' => 'https://wpforo.com',
50 ],
51 'notifications' => [
52 'base' => false,
53 'version' => '1.0.0',
54 'requires' => '1.7.0',
55 'class' => 'wpforo\classes\Activity',
56 'deps' => [],
57 'title' => 'Forum Notifications',
58 'thumb' => '<svg version="1.1" height="50px" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 116.11 122.88" style="enable-background:new 0 0 116.11 122.88" xml:space="preserve"><g><path d="M74.82,109.04c-0.37,1.94-1.02,3.72-1.96,5.35c-0.97,1.67-2.24,3.18-3.8,4.5c-1.57,1.32-3.27,2.32-5.12,2.99 c-1.85,0.67-3.81,1-5.88,1c-2.08,0-4.04-0.34-5.88-1c-1.84-0.67-3.55-1.66-5.11-2.99c-1.57-1.33-2.83-2.83-3.8-4.5 c-0.97-1.67-1.63-3.51-1.99-5.53c-0.18-0.98,0.48-1.92,1.46-2.1c0.03,0,0.32-0.03,0.32-0.03h30.02c1,0,1.82,0.81,1.82,1.82 C74.89,108.72,74.86,108.88,74.82,109.04L74.82,109.04L74.82,109.04z M20.21,0.25c1.83-0.73,3.9,0.17,4.63,2 c0.73,1.83-0.17,3.9-2,4.63c-3.96,1.58-7.28,3.77-9.93,6.61c-2.64,2.84-4.63,6.36-5.93,10.59c-0.58,1.88-2.58,2.94-4.46,2.36 c-1.88-0.58-2.94-2.58-2.36-4.46c1.63-5.3,4.15-9.74,7.52-13.36C11.05,5.01,15.24,2.23,20.21,0.25L20.21,0.25z M93.27,6.88 c-1.83-0.73-2.73-2.8-2-4.63c0.73-1.83,2.8-2.73,4.63-2c4.97,1.98,9.16,4.76,12.53,8.38c3.37,3.63,5.9,8.07,7.52,13.36 c0.58,1.88-0.48,3.88-2.36,4.46c-1.88,0.58-3.88-0.48-4.46-2.36c-1.3-4.24-3.29-7.76-5.93-10.59 C100.55,10.65,97.23,8.46,93.27,6.88L93.27,6.88z M67.62,10.54c1.47,0.38,2.9,0.85,4.29,1.4c2.04,0.81,4,1.78,5.88,2.91 c0.07,0.05,0.15,0.09,0.22,0.14c1.8,1.11,3.48,2.33,5.02,3.65c1.62,1.39,3.12,2.92,4.52,4.6l0.01,0.01h0 c1.37,1.65,2.59,3.42,3.67,5.29c1.08,1.88,2.01,3.84,2.78,5.86l0,0c0.79,2.09,1.38,4.22,1.78,6.41c0.39,2.2,0.59,4.45,0.59,6.76 c0,4.56,0,7.03,0,7.33c0.01,2.34,0.02,4.63,0.04,6.86v0.02l0,0c0.01,2.02,0.14,4.05,0.39,6.08c0.25,2.01,0.61,3.95,1.08,5.82l0,0 c0.47,1.84,1.11,3.62,1.9,5.32c0.82,1.75,1.82,3.47,2.99,5.14l0.01,0c1.16,1.64,2.61,3.27,4.35,4.87c1.8,1.65,3.88,3.28,6.26,4.86 c1.36,0.91,1.73,2.76,0.81,4.12c-0.57,0.85-1.51,1.32-2.47,1.32v0.01l-26.85,0H58.06H31.21H4.37c-1.65,0-2.98-1.33-2.98-2.98 c0-1.08,0.58-2.03,1.44-2.55c2.41-1.63,4.48-3.25,6.21-4.85c1.72-1.59,3.16-3.22,4.32-4.9c0.03-0.05,0.07-0.1,0.11-0.14 c1.12-1.64,2.08-3.31,2.87-5.01c0.81-1.73,1.46-3.51,1.94-5.34c0.01-0.04,0.02-0.08,0.03-0.11c0.46-1.78,0.81-3.66,1.05-5.63 c0.24-1.98,0.37-4.03,0.37-6.14v-14.1c0-2.27,0.2-4.52,0.61-6.77c0.41-2.24,1-4.39,1.79-6.44c0.78-2.05,1.72-4.02,2.81-5.9 c1.08-1.87,2.32-3.64,3.71-5.32l0.02-0.02l0,0c1.38-1.65,2.9-3.19,4.55-4.6c1.63-1.39,3.39-2.66,5.28-3.79 c1.91-1.14,3.89-2.1,5.93-2.88c1.42-0.54,2.89-0.99,4.39-1.36c0.51-1.79,1.39-3.24,2.64-4.35c1.72-1.53,3.98-2.29,6.79-2.26 c2.78,0.02,5.03,0.79,6.73,2.32C66.22,7.32,67.11,8.76,67.62,10.54L67.62,10.54L67.62,10.54z M69.75,17.47 c-1.65-0.65-3.33-1.16-5.04-1.53c-1.32-0.17-2.4-1.21-2.57-2.59c-0.16-1.3-0.53-2.21-1.12-2.73c-0.59-0.52-1.53-0.79-2.82-0.8 c-1.29-0.01-2.22,0.24-2.79,0.75c-0.58,0.52-0.95,1.44-1.1,2.76h0c-0.14,1.26-1.09,2.34-2.41,2.58c-1.85,0.35-3.64,0.85-5.37,1.51 c-1.73,0.65-3.38,1.46-4.98,2.41c-1.59,0.95-3.08,2.02-4.46,3.21c-1.38,1.18-2.67,2.48-3.85,3.9l0,0c-1.16,1.4-2.2,2.91-3.13,4.51 c-0.91,1.58-1.71,3.26-2.39,5.04c-0.68,1.77-1.18,3.57-1.51,5.37c-0.33,1.81-0.49,3.72-0.49,5.72v14.1c0,2.34-0.14,4.62-0.41,6.86 c-0.27,2.15-0.67,4.29-1.22,6.4c-0.01,0.05-0.02,0.09-0.03,0.14c-0.57,2.15-1.34,4.26-2.31,6.34c-0.94,2.01-2.06,3.96-3.35,5.85 c-0.04,0.06-0.08,0.12-0.12,0.18c-1.36,1.96-3.09,3.91-5.18,5.85l-0.08,0.07h18.22h26.85H84.9h18.19c-2.04-1.88-3.76-3.82-5.16-5.8 l0,0l0-0.01c-1.37-1.96-2.54-3.97-3.51-6.03c-0.99-2.1-1.75-4.23-2.3-6.37l0,0l0-0.01c-0.54-2.13-0.95-4.32-1.22-6.56 c-0.26-2.14-0.4-4.4-0.41-6.77v-0.01c-0.02-2.21-0.03-4.51-0.04-6.91c-0.02-4.44-0.02-6.86-0.02-7.33c0-1.96-0.16-3.87-0.5-5.72 c-0.33-1.84-0.82-3.62-1.47-5.34l0,0l0-0.01c-0.67-1.77-1.46-3.44-2.36-5.01c-0.9-1.57-1.94-3.06-3.11-4.48l0,0 c-1.16-1.39-2.43-2.68-3.81-3.87c-1.34-1.15-2.76-2.18-4.25-3.11c-0.07-0.03-0.13-0.07-0.2-0.11 C73.11,18.97,71.45,18.15,69.75,17.47L69.75,17.47L69.75,17.47z"></path></g></svg>',
59 'desc' => __( 'Adds Forum Notifications Feature For Topics', 'wpforo' ),
60 'url' => 'https://wpforo.com',
61 ],
62 'logging' => [
63 'base' => false,
64 'version' => '1.0.0',
65 'requires' => '1.0.0',
66 'class' => 'wpforo\classes\Logs',
67 'deps' => [],
68 'title' => 'Action Logging & Views',
69 'thumb' => '<svg height="55px" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 109.21 122.88"><title>mouse-click</title><path d="M86,122.31a5.57,5.57,0,0,1-.9.35,5.09,5.09,0,0,1-1,.18,5.46,5.46,0,0,1-1,0,6.77,6.77,0,0,1-1-.15,6,6,0,0,1-1-.36l0,0a5.51,5.51,0,0,1-.92-.53l0,0a6.41,6.41,0,0,1-.78-.69,5.19,5.19,0,0,1-.65-.87l-9.08-14.88-7.69,9a15.49,15.49,0,0,1-1.1,1.18c-.39.37-.78.71-1.18,1l-.08.06a12.19,12.19,0,0,1-1.2.82,9.66,9.66,0,0,1-1.24.63,6.91,6.91,0,0,1-1,.37,6.21,6.21,0,0,1-1,.22,7.55,7.55,0,0,1-1.06.07,7.19,7.19,0,0,1-1-.11,6.14,6.14,0,0,1-1.18-.35,5.42,5.42,0,0,1-1.06-.57,6.22,6.22,0,0,1-.92-.78l0,0a7.31,7.31,0,0,1-.75-1l-.11-.2-.09-.21L47.72,112l0-.17L40.91,43.26a4.52,4.52,0,0,1,0-1.33,4.3,4.3,0,0,1,.43-1.25,4.31,4.31,0,0,1,1.39-1.55l0,0a3.82,3.82,0,0,1,.9-.46,4.25,4.25,0,0,1,1-.24h0a4.31,4.31,0,0,1,1.29.05,4.67,4.67,0,0,1,1.25.44l.3.16c13.51,8.84,26.1,17.06,38.64,25.25l19,12.39a11.72,11.72,0,0,1,1,.72l0,0a8.78,8.78,0,0,1,.82.73l.06.07a7.41,7.41,0,0,1,.71.82,5.91,5.91,0,0,1,.57.87,6.42,6.42,0,0,1,.51,1.14,5.6,5.6,0,0,1,.26,1.17,5.44,5.44,0,0,1,0,1.21h0a6.59,6.59,0,0,1-.23,1.19,6.54,6.54,0,0,1-.94,1.88,6.41,6.41,0,0,1-.67.83,7.45,7.45,0,0,1-.82.76,10.42,10.42,0,0,1-1.16.83,12.92,12.92,0,0,1-1.34.7c-.47.21-1,.41-1.46.58a14.27,14.27,0,0,1-1.55.43h0c-2.77.54-5.53,1.21-8.27,1.87l-3.25.77,9,14.94a5.84,5.84,0,0,1,.46,1,5.59,5.59,0,0,1,.15,3.21l0,.1a5.53,5.53,0,0,1-.33.94,6.43,6.43,0,0,1-.51.89,5.62,5.62,0,0,1-.68.81,6,6,0,0,1-.82.67l-2,1.29A83,83,0,0,1,86,122.31ZM37.63,19.46a4,4,0,0,1-6.92,4l-8-14a4,4,0,0,1,6.91-4l8.06,14Zm-15,46.77a4,4,0,0,1,4,6.91l-14,8.06a4,4,0,0,1-4-6.91l14-8.06ZM20.56,39.84a4,4,0,0,1-2.07,7.72L3,43.36A4,4,0,0,1,5,35.64l15.53,4.2ZM82,41.17a4,4,0,0,1-4-6.91L92,26.2a4,4,0,0,1,4,6.91L82,41.17ZM63.46,20.57a4,4,0,1,1-7.71-2.06L59.87,3A4,4,0,0,1,67.59,5L63.46,20.57Zm20.17,96.36,9.67-5.86c-3.38-5.62-8.85-13.55-11.51-19.17a2.17,2.17,0,0,1-.12-.36,2.4,2.4,0,0,1,1.81-2.87c5.38-1.23,10.88-2.39,16.22-3.73a10.28,10.28,0,0,0,1.8-.58,6.11,6.11,0,0,0,1.3-.77,3.38,3.38,0,0,0,.38-.38.9.9,0,0,0,.14-.24l-.06-.18a2.15,2.15,0,0,0-.44-.53,5.75,5.75,0,0,0-.83-.63L47.06,45.75c2.11,21.36,5.2,44.1,6.45,65.31a6.28,6.28,0,0,0,.18,1,2.89,2.89,0,0,0,.26.62l.13.14a1,1,0,0,0,.29,0,2.76,2.76,0,0,0,.51-.17,5.71,5.71,0,0,0,1.28-.79,11.22,11.22,0,0,0,1.35-1.33c1.93-2.27,9.6-12.14,11.4-13.18a2.4,2.4,0,0,1,3.28.82l11.44,18.75Z"></path></svg>',
70 'desc' => __( 'Adds Action Logging & Views Feature For Topics', 'wpforo' ),
71 'url' => 'https://wpforo.com',
72 ],
73 'seo' => [
74 'base' => false,
75 'version' => '1.0.0',
76 'requires' => '1.0.0',
77 'class' => 'wpforo\classes\SEO',
78 'deps' => [],
79 'title' => 'wpForo SEO',
80 'thumb' => '<svg height="55px" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 111.56 122.88"><defs><style>.cls-5514{fill-rule:evenodd;}</style></defs><title>testing</title><path class="cls-5514" d="M79.86,65.67a25,25,0,0,1,20.89,38.62l10.81,11.78-7.46,6.81L93.68,111.42A25,25,0,1,1,79.86,65.67Zm-42.65.26a2.74,2.74,0,0,1-2.6-2.84,2.71,2.71,0,0,1,2.6-2.84h15.4a2.76,2.76,0,0,1,2.6,2.84,2.71,2.71,0,0,1-2.6,2.84ZM22.44,57.22a5.67,5.67,0,1,1-5.67,5.67,5.67,5.67,0,0,1,5.67-5.67Zm2-18.58a2,2,0,0,1,2.85,0,2.07,2.07,0,0,1,0,2.89l-2,2,2,2a2,2,0,0,1,0,2.87,2,2,0,0,1-2.84,0l-2-2-2,2a2,2,0,0,1-2.86,0,2.07,2.07,0,0,1,0-2.89l2-2-2-2.05a2,2,0,0,1,2.87-2.86l2,2,2-2ZM16.85,21.52a2.29,2.29,0,0,1,3.16.63l1.13,1.36,4-5.05a2.27,2.27,0,1,1,3.51,2.88l-5.86,7.34a2.48,2.48,0,0,1-.55.52,2.28,2.28,0,0,1-3.16-.63l-2.84-3.89a2.28,2.28,0,0,1,.63-3.16Zm66.51-4.25h9.32a6.69,6.69,0,0,1,6.66,6.65v30.9c-.2,2.09-5.31,2.11-5.75,0V23.92a.93.93,0,0,0-.27-.67.91.91,0,0,0-.67-.27H83.32V54.82c-.49,1.89-4.75,2.18-5.71,0V6.66A1,1,0,0,0,77.34,6a.93.93,0,0,0-.67-.27h-70A.93.93,0,0,0,6,6a1,1,0,0,0-.27.68V85.79a1,1,0,0,0,.27.68.93.93,0,0,0,.67.27H44.74c2.88.29,3,5.27,0,5.71H21.66v10.61a.92.92,0,0,0,.94.94H44.74c2.09.24,2.76,5,0,5.71H22.64a6.54,6.54,0,0,1-4.7-2,6.63,6.63,0,0,1-2-4.7V92.45H6.66A6.69,6.69,0,0,1,0,85.79V6.66A6.54,6.54,0,0,1,2,2a6.61,6.61,0,0,1,4.7-2h70a6.55,6.55,0,0,1,4.7,2,6.65,6.65,0,0,1,2,4.7V17.27ZM37.18,26.44a2.75,2.75,0,0,1-2.6-2.84,2.71,2.71,0,0,1,2.6-2.84H63.86a2.74,2.74,0,0,1,2.6,2.84,2.71,2.71,0,0,1-2.6,2.84Zm0,19.74a2.74,2.74,0,0,1-2.6-2.83,2.71,2.71,0,0,1,2.6-2.84H63.86a2.74,2.74,0,0,1,2.6,2.84,2.7,2.7,0,0,1-2.6,2.83ZM70.45,93a3.46,3.46,0,0,1-.34-.44,3.4,3.4,0,0,1-.26-.5,3.18,3.18,0,0,1,4.57-4,2.93,2.93,0,0,1,.49.38h0c.87.83,1.15,1,2.11,1.87l.84.74,6.79-7.29c2.87-3,7.45,1.37,4.58,4.4l-8.47,9.06-.43.45a3.19,3.19,0,0,1-4.43.19l0,0c-.22-.19-.44-.4-.66-.6-.52-.46-1.06-.94-1.61-1.41-1.26-1.09-2-1.69-3.17-2.87Zm9.43-22.09A19.86,19.86,0,1,1,60,90.74,19.86,19.86,0,0,1,79.88,70.88Z"></path></svg>',
81 'desc' => __( 'Adds wpForo SEO Feature For Topics', 'wpforo' ),
82 'url' => 'https://wpforo.com',
83 ],
84 'antispam' => [
85 'base' => false,
86 'version' => '1.0.0',
87 'requires' => '1.0.0',
88 'class' => 'wpforo\classes\Moderation',
89 'deps' => [],
90 'title' => 'Spam Protection',
91 'thumb' => '<svg width="55px" version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 111.811 122.88" enable-background="new 0 0 111.811 122.88" xml:space="preserve"><g><path fill-rule="evenodd" clip-rule="evenodd" d="M55.713,0c20.848,13.215,39.682,19.467,55.846,17.989 c2.823,57.098-18.263,90.818-55.63,104.891C19.844,109.708-1.5,77.439,0.083,17.123C19.058,18.116,37.674,14.014,55.713,0L55.713,0 z M56.163,19.543c14.217,9.011,27.061,13.274,38.083,12.268c1.925,38.936-12.454,61.93-37.935,71.526 c-0.161-0.059-0.319-0.12-0.479-0.18V19.796L56.163,19.543L56.163,19.543z M55.735,7.055 c18.454,11.697,35.126,17.232,49.434,15.923c2.498,50.541-16.166,80.39-49.241,92.846C23.986,104.165,5.091,75.603,6.493,22.211 C23.29,23.091,39.768,19.46,55.735,7.055L55.735,7.055z"></path></g></svg>',
92 'desc' => __( 'Adds Spam Protection Feature For Topics', 'wpforo' ),
93 'url' => 'https://wpforo.com',
94 ],
95 'akismet' => [
96 'base' => false,
97 'version' => '1.0.0',
98 'requires' => '1.0.0',
99 'class' => 'wpforo\classes\Moderation',
100 'deps' => [],
101 'title' => 'Akismet Antispam Integration',
102 'thumb' => WPFORO_URL . '/assets/images/dashboard/akismet.png',
103 'desc' => __( 'Adds Akismet Antispam Integration Feature For Topics', 'wpforo' ),
104 'url' => 'https://wpforo.com',
105 ],
106 'rss' => [
107 'base' => false,
108 'version' => '1.0.0',
109 'requires' => '1.0.0',
110 'class' => '',
111 'deps' => [],
112 'title' => 'Forum Feed / RSS',
113 'thumb' => '<svg height="65px" style="enable-background:new 0 0 30.3 29.9;" version="1.1" viewBox="0 0 30.3 29.9" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><style type="text/css">.st33453{fill:none;stroke:#43a6df;stroke-width:1.25;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}.st22013{fill:none;stroke:#43a6df;stroke-width:1.1713;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}</style><g><circle class="st33453" cx="8.1" cy="20" r="2.4"></circle><path class="st33453" d="M5.4,13.4c0,0,9.7,0.6,9.3,9h3.4c0,0,0.5-11-12.7-12.1V13.4z"></path><path class="st33453" d="M5.7,7.3c0,0,15.3,1.7,14.8,14.9h3.4c0,0,0.8-16.4-18.2-18.1V7.3z"></path></g></svg>',
114 'desc' => __( 'Adds Forum Feed / RSS Feature For Topics', 'wpforo' ),
115 'url' => 'https://wpforo.com',
116 ],
117 'social' => [
118 'base' => false,
119 'version' => '1.0.0',
120 'requires' => '1.0.0',
121 'class' => 'wpforo\classes\API',
122 'deps' => [],
123 'title' => 'Social Share',
124 'thumb' => '<svg height="60px" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 121.28 122.88"><path d="M50.51,46,64,37a27.93,27.93,0,1,1,6.68,10.72c-.43-.44-.85-.89-1.25-1.35L55.1,55.93a27.8,27.8,0,0,1,.76,6.5,28.37,28.37,0,0,1-.34,4.35L71.67,77.34A27.77,27.77,0,1,1,66.58,87L51.64,77.19a27.93,27.93,0,1,1-4-34.51A29.78,29.78,0,0,1,50.51,46ZM41.4,74.84a19,19,0,0,1-26.93,0L12.8,72.65,14,71.71c2.5-1.48,6.27-1.09,8.73-2.81A7.08,7.08,0,0,0,23.2,68c.23-.53.44-1.1.57-1.49a18.85,18.85,0,0,1-1.49-2.13L20.77,62a4.38,4.38,0,0,1-.86-2.2,1.69,1.69,0,0,1,.15-.79,1.42,1.42,0,0,1,.52-.6,1.55,1.55,0,0,1,.36-.19,38.65,38.65,0,0,1-.07-4.32,5.47,5.47,0,0,1,.19-1,5.78,5.78,0,0,1,2.55-3.26,8.37,8.37,0,0,1,2.14-.95c.48-.13-.41-1.67.09-1.72,2.4-.25,6.29,1.94,8,3.76a5.91,5.91,0,0,1,1.49,3.71l-.1,3.92h0a1.09,1.09,0,0,1,.8.82,3.4,3.4,0,0,1-.42,2.07h0l0,0-1.72,2.84a14.89,14.89,0,0,1-2.12,2.91l.23.33a10.22,10.22,0,0,0,1.12,1.45l0,0c2,1.41,6.81,1.75,8.67,2.78l.07,0,1.22,1a22.07,22.07,0,0,1-1.66,2.16ZM44,46.37a22.72,22.72,0,1,0,6.65,16.06A22.64,22.64,0,0,0,44,46.37Zm63.09,60.25c-5.76,6.39-21.5,6.83-27.43.52l0,0V105.8c0-3.44,5.72-3.5,8.77-5.33,2-1.21,1.68-2.43,1.67-4.44H86.85c-8.32,0-2.84.66-1.71-8.39,1.69-12.77,14.56-12.78,16.46,0C102.82,96.35,108,96,99.89,96H96.64c0,2.22-.36,3.35,1.94,4.6s8.5,1.88,8.5,5v1Zm2.33-27.73A22.72,22.72,0,1,0,116.06,95a22.64,22.64,0,0,0-6.65-16.06Zm-5.47-39.82a19,19,0,0,1-26.93,0l-1.67-2.18L76.56,36c2.5-1.48,6.27-1.09,8.73-2.81a7,7,0,0,0,.46-.88c.23-.53.43-1.1.57-1.49a18.56,18.56,0,0,1-1.5-2.13l-1.51-2.4a4.47,4.47,0,0,1-.86-2.2,1.69,1.69,0,0,1,.15-.79,1.37,1.37,0,0,1,.52-.6,1.43,1.43,0,0,1,.37-.19,36.43,36.43,0,0,1-.07-4.32,5.45,5.45,0,0,1,.18-1,5.76,5.76,0,0,1,2.56-3.26,7.9,7.9,0,0,1,2.14-1c.48-.14-.41-1.67.08-1.72,2.41-.25,6.29,1.94,8,3.76a5.82,5.82,0,0,1,1.48,3.7l-.09,3.93h0a1.1,1.1,0,0,1,.79.82,3.46,3.46,0,0,1-.41,2.07h0l0,0L96.37,28.4a14.49,14.49,0,0,1-2.12,2.91l.23.33a10.3,10.3,0,0,0,1.13,1.45s0,0,0,0c2,1.4,6.82,1.74,8.67,2.78l.08,0,1.22,1a22,22,0,0,1-1.67,2.15Zm2.59-27.2a22.72,22.72,0,1,0,6.65,16.06,22.64,22.64,0,0,0-6.65-16.06Z"></path></svg>',
125 'desc' => __( 'Adds Social Share Feature For Topics', 'wpforo' ),
126 'url' => 'https://wpforo.com',
127 ],
128 'revisions' => [
129 'base' => false,
130 'version' => '1.0.0',
131 'requires' => '1.0.0',
132 'class' => 'wpforo\modules\revisions\Revisions',
133 'deps' => [],
134 'title' => 'Post Preview and Auto Drafting',
135 'thumb' => '<svg version="1.1" height="53px" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 122.88 122.88" style="enable-background:new 0 0 122.88 122.88" xml:space="preserve"><style type="text/css">.st0{fill-rule:evenodd;clip-rule:evenodd;}</style><g><path class="st0" d="M79.7,31.87c-0.7-0.65-1.5-1-2.4-0.95c-0.9,0-1.7,0.35-2.35,1.05l-5.29,5.49L84.49,51.8l5.34-5.59 c0.65-0.65,0.9-1.5,0.9-2.4c0-0.9-0.35-1.75-1-2.35L79.7,31.87L79.7,31.87L79.7,31.87z M12.51,0h97.85c3.44,0,6.57,1.41,8.84,3.67 c2.27,2.27,3.67,5.4,3.67,8.84v97.85c0,3.44-1.41,6.57-3.67,8.84c-2.27,2.27-5.4,3.67-8.84,3.67H12.51c-3.44,0-6.57-1.41-8.84-3.67 c-2.27-2.27-3.67-5.4-3.67-8.84V12.51c0-3.44,1.41-6.57,3.67-8.84C5.94,1.41,9.07,0,12.51,0L12.51,0z M110.37,5.39H12.51 c-1.96,0-3.74,0.8-5.03,2.1c-1.29,1.29-2.1,3.08-2.1,5.03v97.85c0,1.96,0.8,3.74,2.1,5.03c1.29,1.29,3.08,2.1,5.03,2.1h97.85 c1.96,0,3.74-0.8,5.03-2.1c1.29-1.29,2.1-3.08,2.1-5.03V12.51c0-1.96-0.8-3.74-2.1-5.03C114.1,6.19,112.32,5.39,110.37,5.39 L110.37,5.39z M51.93,85.61c-1.95,0.65-3.95,1.25-5.89,1.9c-1.95,0.65-3.9,1.3-5.89,1.95c-4.64,1.5-7.19,2.35-7.74,2.5 c-0.55,0.15-0.2-2,0.95-6.49l3.7-14.13l0.3-0.32L51.93,85.61L51.93,85.61L51.93,85.61L51.93,85.61z M42.74,65.41l22.9-23.78 l14.83,14.28L57.33,79.99L42.74,65.41L42.74,65.41z"/></g></svg>',
136 'desc' => __( 'Adds Post Preview and Auto Drafting Feature For Topics And Posts', 'wpforo' ),
137 'url' => 'https://wpforo.com',
138 ],
139 'bookmarks' => [
140 'base' => true,
141 'version' => '1.0.0',
142 'requires' => '2.0.0',
143 'class' => 'wpforo\modules\bookmarks\Bookmarks',
144 'deps' => [],
145 'title' => 'Post Bookmark',
146 'thumb' => '<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="43.226px" height="43.226px" viewBox="0 0 43.226 43.226" style="enable-background:new 0 0 43.226 43.226;" xml:space="preserve"> <g> <g> <path d="M37.119,28.389c0-8.208,0-16.417,0-24.624c0-5.556-12.92-3.32-16.004-3.32c-3.796,0-11.869-1.572-14.403,2.354 c-0.429,0.663-0.566,1.33-0.575,1.363C6.13,4.187,6.12,4.202,6.12,4.222c0,6.907,0,13.817,0,20.727 c0,2.296-1.792,17.566,1.371,18.212c0.472,0.098,0.975,0.081,1.498-0.021c0.774-0.152,1.328-0.399,1.288-0.361 c-0.041,0.037-0.037,0.045,0.008,0.017c0.024-0.016,0.052-0.029,0.078-0.051c0,0,0.026-0.02,0.057-0.043 c0.032-0.024,0.648-0.3,1.33-0.699c3.325-1.95,6.837-5.704,8.679-7.102c3.335-2.534,10.157,10.289,14.973,7.901 C39.006,41.012,37.119,31.631,37.119,28.389z M32.341,38.072c-0.556,0.561-1.275,0.811-1.607,0.557 c-0.331-0.253-0.149-0.91,0.405-1.473c0.278-0.281,0.557-0.561,0.834-0.842c0.557-0.562,0.956-1.042,0.892-1.071 c-0.065-0.029-0.561,0.412-1.104,0.985c-0.258,0.271-0.515,0.542-0.771,0.811c-0.543,0.572-1.112,0.939-1.271,0.818 c-0.097-0.072-0.191-0.146-0.286-0.218c-0.187-0.144-0.372-0.283-0.558-0.426c-0.307-0.233-0.184-0.818,0.271-1.307 c0.456-0.487,0.556-0.81,0.248-0.66c-0.188,0.092-0.365,0.199-0.529,0.323c0,0-0.241,0.184-0.537,0.409 c-0.298,0.227-1.046,0.02-1.699-0.424c-2.529-1.725-5.066-2.683-8.339-0.69c-2.322,1.412-4.44,3.364-6.604,5.006 c-2.647,2.012-2.805,2.818-2.805-0.311c0-0.388,0-0.773,0-1.16c0-0.641,0.449-1.615,1.004-2.178 c1.479-1.5,2.963-3.004,4.455-4.516c0.556-0.562,0.533-0.583-0.047-0.05c-0.385,0.353-0.773,0.71-1.173,1.076 c-1.064,0.986-2.127,1.969-3.189,2.953c-0.579,0.536-1.05,0.37-1.05-0.373c0-0.742,0.473-1.777,1.056-2.312 c2.015-1.845,4.029-3.688,6.036-5.539c0.58-0.534,1.051-1.526,1.051-2.218c0-0.688-0.469-0.812-1.048-0.273 c-2.017,1.872-4.033,3.744-6.049,5.617c-0.578,0.538-1.046,0.385-1.046-0.343s0.468-1.754,1.044-2.292 c1.998-1.863,4.022-3.753,6.055-5.649c0.576-0.539,1.044-1.116,1.044-1.289s-0.453,0.142-1.012,0.698 c-1.746,1.747-3.775,3.781-6.123,6.136c-0.557,0.56-1.008,0.371-1.008-0.419c0-0.634,0-1.265,0-1.897 c0-0.79,0.445-1.89,0.994-2.457c2.066-2.133,4.119-4.248,6.151-6.341c0.551-0.565,0.997-1.39,0.997-1.839 c0-0.451-0.18-0.644-0.403-0.431c-0.131,0.126-0.264,0.252-0.403,0.386c-2.167,2.158-4.27,4.25-6.322,6.293 c-0.56,0.558-1.014,0.367-1.014-0.422c0-0.94,0-1.878,0-2.816c0-0.79,0.455-1.881,1.017-2.435c2.036-2.011,4.07-4.02,6.108-6.026 c0.562-0.555,1.017-1.604,1.017-2.348c0-0.742-0.445-0.888-0.995-0.319c-2.056,2.115-4.102,4.232-6.153,6.348 c-0.549,0.566-0.994,0.548-0.994-0.045c0-0.591,0.453-1.521,1.008-2.085c0.789-0.8,2.07-2.104,3.973-4.042 c0.814-0.838,1.599-1.645,2.365-2.433c0.55-0.565,1.017-1.072,1.042-1.129c0.026-0.059-0.398,0.354-0.945,0.923 c-2.484,2.561-5.072,5.228-6.454,6.657C9.32,11.54,8.881,11.92,8.881,11.816s0.448-0.646,0.999-1.211 c3.111-3.181,5.81-5.928,7.886-8.019c0.556-0.56,0.428-1.013-0.287-1.013c-0.715,0-1.749,0.448-2.314,1.002 c-1.738,1.707-3.496,3.436-5.264,5.173c-0.562,0.556-1.02,0.364-1.02-0.426c0-0.516,0-1.032,0-1.547 c0-0.791,0.451-1.887,1.008-2.446c0.246-0.246,0.491-0.493,0.737-0.739c0.556-0.561,0.854-1.017,0.662-1.017h-0.345 c-0.105,0-0.21,0.007-0.312,0.02c-0.172,0.021-0.573,0.317-0.896,0.646C9.412,2.566,9.4,2.181,10.073,1.77 c1.258-0.77,3.174-0.236,5.225-0.236c4.017,0,8.034,0,12.052,0c2.18,0,7.008-0.868,7.008,2.966c0,2.308,0,4.613,0,6.92 c0,7.59,0,15.18,0,22.769c0,0.348,0,0.697,0,1.046c0,0.578-0.031,1.021-0.07,0.994c-0.037-0.029-0.518,0.401-1.076,0.964 C32.921,37.488,32.63,37.78,32.341,38.072z M34.353,41.38c-0.056-0.043-0.118-0.094-0.196-0.151 c-0.185-0.141-0.367-0.279-0.552-0.419c-0.304-0.23-0.261-0.72,0.101-1.092c0.36-0.371,0.653-0.209,0.653,0.359 c0,0.345,0,0.69,0,1.034c0,0.081,0.022,0.158,0.056,0.229C34.469,41.459,34.459,41.468,34.353,41.38z"/> <path d="M30.369,1.578c-0.242,0-0.46,0.021-0.487,0.048c-0.026,0.025,0.148,0.046,0.394,0.046c0.244,0,0.464-0.02,0.488-0.047 C30.788,1.6,30.612,1.578,30.369,1.578z"/> <path d="M22.525,1.578c-0.791,0-1.451,0.021-1.477,0.047c-0.024,0.026,0.596,0.047,1.386,0.047h0.026 c0.79,0,1.451-0.02,1.478-0.047c0.025-0.024-0.594-0.047-1.383-0.047H22.525z"/> <path d="M35.924,42.904c0.212-0.234,0.104-0.323-0.105-0.107C35.607,43.015,35.71,43.14,35.924,42.904z"/> <path d="M18.671,1.95c0.063-0.037,0.186-0.13,0.269-0.215c0.084-0.087,0.126-0.157,0.094-0.157c-0.032,0-0.15,0.099-0.268,0.218 C18.65,1.915,18.607,1.984,18.671,1.95z"/> </g> </g> <g> </g> <g> </g> <g> </g> <g> </g> <g> </g> <g> </g> <g> </g> <g> </g> <g> </g> <g> </g> <g> </g> <g> </g> <g> </g> <g> </g> <g> </g> </svg>',
147 'desc' => __( 'Adds Post Bookmark Feature For Posts', 'wpforo' ),
148 'url' => 'https://wpforo.com',
149 ],
150 ];
151
152 if( !is_null( $base ) ){
153 $modules = array_filter($modules, function( $module ) use ( $base ){
154 return $module['base'] === $base;
155 });
156 }
157
158 return $modules;
159 }
160
161 function wpforo_get_addons_info( $base = null ) {
162 $addons = [
163 'wpforo-user-custom-fields' => [
164 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-user-custom-fields/wpforo-ucf.php' ),
165 'base' => true,
166 'version' => '3.0.0',
167 'requires' => '2.0.0',
168 'class' => 'WpforoUcf',
169 'deps' => [],
170 'title' => 'User Custom Fields',
171 'thumb' => WPFORO_URL . '/assets/addons/ucf/header.png',
172 'desc' => __( 'Advanced user profile builder system. Allows to add new fields and manage profile. Creates custom Registration, Account, Member Search forms.', 'wpforo' ),
173 'url' => 'https://gvectors.com/product/wpforo-user-custom-fields/',
174 ],
175 'wpforo-topic-custom-fields' => [
176 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-topic-custom-fields/wpforotcf.php' ),
177 'base' => false,
178 'version' => '3.0.1',
179 'requires' => '2.0.0',
180 'class' => 'wpForoTcf',
181 'deps' => [],
182 'install_func' => function () {
183 if (
184 class_exists( 'wpForoTcf' )
185 && function_exists( 'wpforotcf_activation' )
186 && version_compare( WPFORO_VERSION, WPFOROTCF_WPFORO_REQUIRED_VERSION, '>=' )
187 ) {
188 wpforotcf_activation();
189 }
190 },
191 'title' => 'Topic Custom Fields',
192 'thumb' => WPFORO_URL . '/assets/addons/tcf/header.png',
193 'desc' => __( 'Allows to create topic custom fields and manage topic form layout with a form builder. Adds topic search options by custom fields', 'wpforo' ),
194 'url' => 'https://gvectors.com/product/wpforo-topic-custom-fields/',
195 ],
196 'wpforo-private-messages' => [
197 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-private-messages/wpforopm.php' ),
198 'base' => true,
199 'version' => '3.0.0',
200 'requires' => '2.0.0',
201 'class' => 'wpForoPMs',
202 'deps' => [],
203 'install_func' => function () {
204 if (
205 class_exists( 'wpForoPMs' )
206 && function_exists( 'wpforopm_activation' )
207 && version_compare( WPFORO_VERSION, WPFOROPM_WPFORO_REQUIRED_VERSION, '>=' )
208 ) {
209 wpforopm_activation();
210 }
211 },
212 'title' => 'Private Messages',
213 'thumb' => WPFORO_URL . '/assets/addons/pm/header.png',
214 'desc' => __( 'Provides a safe way to communicate directly with other members. Messages are private and can only be viewed by conversation participants.', 'wpforo' ),
215 'url' => 'https://gvectors.com/product/wpforo-private-messages/',
216 ],
217 'wpforo-topic-prefix' => [
218 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-topic-prefix/wpforotpx.php' ),
219 'base' => false,
220 'version' => '3.0.0',
221 'requires' => '2.0.0',
222 'class' => 'wpForoTopicPrefix',
223 'deps' => [],
224 'install_func' => function () {
225 if (
226 class_exists( 'wpForoTopicPrefix' )
227 && function_exists( 'wpforotpx_activation' )
228 && version_compare( WPFORO_VERSION, WPFOROTPX_WPFORO_REQUIRED_VERSION, '>=' )
229 ) {
230 wpforotpx_activation();
231 }
232 },
233 'title' => 'Topic Prefix & Tag Manager',
234 'thumb' => WPFORO_URL . '/assets/addons/prefix/header.png',
235 'desc' => __( 'Allows you to create topic prefixes and prefix groups to categorize topics. Also, it allows you to add, edit, delete topic tags and convert them to prefixes.', 'wpforo' ),
236 'url' => 'https://gvectors.com/product/wpforo-topic-prefix/',
237 ],
238 'wpforo-advanced-attachments' => [
239 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-advanced-attachments/wpforoattach.php' ),
240 'base' => false,
241 'version' => '3.0.1',
242 'requires' => '2.0.0',
243 'class' => 'wpForoAttachments',
244 'deps' => [],
245 'install_func' => function () {
246 if (
247 class_exists( 'wpForoAttachments' )
248 && function_exists( 'wpforoattach_activation' )
249 && version_compare( WPFORO_VERSION, WPFOROATTACH_WPFORO_REQUIRED_VERSION, '>=' )
250 ) {
251 wpforoattach_activation();
252 }
253 },
254 'title' => 'Advanced Attachments',
255 'thumb' => WPFORO_URL . '/assets/addons/attachments/header.png',
256 'desc' => __( 'Adds an advanced file attachment system to forum. AJAX powered media uploading and displaying system with user specific library.', 'wpforo' ),
257 'url' => 'https://gvectors.com/product/wpforo-advanced-attachments/',
258 ],
259 'wpforo-embeds' => [
260 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-embeds/wpforoembeds.php' ),
261 'base' => false,
262 'version' => '3.0.0',
263 'requires' => '2.0.0',
264 'class' => 'wpForoEmbeds',
265 'deps' => [],
266 'title' => 'Embeds',
267 'thumb' => WPFORO_URL . '/assets/addons/embeds/header.png',
268 'desc' => __( 'Allows to embed hundreds of video, social network, audio and photo content providers in forum topics and posts.', 'wpforo' ),
269 'url' => 'https://gvectors.com/product/wpforo-embeds/',
270 ],
271 'wpforo-memberpress' => [
272 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-memberpress/wpforo_mempress.php' ),
273 'base' => true,
274 'version' => '1.0.0',
275 'requires' => '2.2.2',
276 'class' => 'wpForoMemberPress',
277 'deps' => [],
278 'install_func' => function () {
279 if (
280 class_exists( 'wpForoMemberPress' )
281 && function_exists( 'wpforomp_activation' )
282 && version_compare( WPFORO_VERSION, WPFOROMP_WPFORO_REQUIRED_VERSION, '>=' )
283 ) {
284 wpforomp_activation();
285 }
286 },
287 'title' => 'MemberPress Integration',
288 'thumb' => WPFORO_URL . '/assets/addons/wpforo-memberpress/header.png',
289 'desc' => __( 'Allows to setup membership access to your forums through MemberPress membership plugin.', 'wpforo' ),
290 'url' => 'https://gvectors.com/product/wpforo-memberpress/',
291 ],
292 'wpforo-suremembers' => [
293 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-suremembers/wpforo_surmem.php' ),
294 'base' => true,
295 'version' => '1.0.0',
296 'requires' => '2.2.2',
297 'class' => 'wpForoSureMembers',
298 'deps' => [],
299 'install_func' => function () {
300 if (
301 class_exists( 'wpForoSureMembers' )
302 && function_exists( 'wpforosm_activation' )
303 && version_compare( WPFORO_VERSION, WPFOROSM_WPFORO_REQUIRED_VERSION, '>=' )
304 ) {
305 wpforosm_activation();
306 }
307 },
308 'title' => 'SureMembers Integration',
309 'thumb' => WPFORO_URL . '/assets/addons/wpforo-suremembers/header.png',
310 'desc' => __( 'Allows to setup membership access to your forums through SureMembers membership plugin.', 'wpforo' ),
311 'url' => 'https://gvectors.com/product/wpforo-suremembers/',
312 ],
313 'wpforo-paid-membership-pro' => [
314 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-paid-membership-pro/wpforo_mempro.php' ),
315 'base' => true,
316 'version' => '1.0.0',
317 'requires' => '2.2.2',
318 'class' => 'wpForoPaidMembershipPro',
319 'deps' => [],
320 'install_func' => function () {
321 if (
322 class_exists( 'wpForoPaidMembershipPro' )
323 && function_exists( 'wpforopmp_activation' )
324 && version_compare( WPFORO_VERSION, WPFOROPMP_WPFORO_REQUIRED_VERSION, '>=' )
325 ) {
326 wpforopmp_activation();
327 }
328 },
329 'title' => 'Paid Membership Pro Integration',
330 'thumb' => WPFORO_URL . '/assets/addons/wpforo-paid-membership-pro/header.png',
331 'desc' => __( 'Allows to setup membership access to your forums through Paid Membership Pro plugin.', 'wpforo' ),
332 'url' => 'https://gvectors.com/product/wpforo-paid-membership-pro/',
333 ],
334 'wpforo-woocommerce-memberships' => [
335 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-woocommerce-memberships/wpforo_woomem.php' ),
336 'base' => true,
337 'version' => '3.0.0',
338 'requires' => '2.0.0',
339 'class' => 'wpForoWooMembers',
340 'deps' => [],
341 'install_func' => function () {
342 if (
343 class_exists( 'wpForoWooMembers' )
344 && function_exists( 'wpforowm_activation' )
345 && version_compare( WPFORO_VERSION, WPFOROWM_WPFORO_REQUIRED_VERSION, '>=' )
346 ) {
347 wpforowm_activation();
348 }
349 },
350 'title' => 'WooCommerce Memberships Integration',
351 'thumb' => WPFORO_URL . '/assets/addons/wpforo-woocomerce-memberships/header.png',
352 'desc' => __( 'Allows to setup membership access to your forums and topics through WooCommerce Memberships plugin.', 'wpforo' ),
353 'url' => 'https://gvectors.com/product/wpforo-woocommerce-memberships/',
354 ],
355 'wpforo-mentioning' => [
356 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo_mentioning/Main.php' ),
357 'base' => false,
358 'version' => '3.0.0',
359 'requires' => '2.2.9',
360 'class' => '\wpforo_mentioning\Main',
361 'deps' => [],
362 'title' => 'User Mentioning',
363 'thumb' => WPFORO_URL . '/assets/addons/wpforo-mentioning/header.png',
364 'desc' => __( 'Opens pop-up window to select users for mentioning by @nickname.', 'wpforo' ),
365 'url' => 'https://gvectors.com/product/wpforo_mentioning/',
366 ],
367 'wpforo-voice-posting' => [
368 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-voice-posting/wpforoVoicePosting.php' ),
369 'base' => false,
370 'version' => '3.0.0',
371 'requires' => '2.2.8',
372 'class' => 'wpforoVoicePosting',
373 'deps' => [],
374 'title' => 'Voice Posting',
375 'thumb' => WPFORO_URL . '/assets/addons/wpforo-voice-posting/header.png',
376 'desc' => __( 'Allows to record and attach voice messages to forum posts and private messages.', 'wpforo' ),
377 'url' => 'https://gvectors.com/product/wpforo-voice-posting/',
378 ],
379 'wpforo-cross-posting' => [
380 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-cross-posting/wpForoCrossPosting.php' ),
381 'base' => false,
382 'version' => '3.0.0',
383 'requires' => '2.0.0',
384 'class' => 'wpForoCrossPosting',
385 'deps' => [],
386 'title' => '"Forum - Blog" Cross Posting',
387 'thumb' => WPFORO_URL . '/assets/addons/cross/header.png',
388 'desc' => __( 'Blog to Forum and Forum to Blog content synchronization. Blog posts with Forum topics and Blog comments with Forum replies.', 'wpforo' ),
389 'url' => 'https://gvectors.com/product/wpforo-cross-posting/',
390 ],
391 'wpforo-ad-manager' => [
392 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-ad-manager/wpforoad.php' ),
393 'base' => false,
394 'version' => '3.0.1',
395 'requires' => '2.0.0',
396 'class' => 'wpForoAD',
397 'deps' => [],
398 'install_func' => function () {
399 if (
400 class_exists( 'wpForoAD' )
401 && function_exists( 'wpforoad_activation' )
402 && version_compare( WPFORO_VERSION, WPFOROAD_WPFORO_REQUIRED_VERSION, '>=' )
403 ) {
404 wpforoad_activation();
405 }
406 },
407 'title' => 'Ads Manager',
408 'thumb' => WPFORO_URL . '/assets/addons/ad-manager/header.png',
409 'desc' => __( 'Ads Manager is a powerful yet simple advertisement management system. Allows you to add adverting banners between forums, topics and posts.', 'wpforo' ),
410 'url' => 'https://gvectors.com/product/wpforo-ad-manager/',
411 ],
412 'wpforo-polls' => [
413 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-polls/wpforopoll.php' ),
414 'base' => false,
415 'version' => '3.0.0',
416 'requires' => '2.0.0',
417 'class' => 'wpForoPoll',
418 'deps' => [],
419 'install_func' => function () {
420 if (
421 class_exists( 'wpForoPoll' )
422 && function_exists( 'wpforopoll_activation' )
423 && version_compare( WPFORO_VERSION, WPFOROPOLL_WPFORO_REQUIRED_VERSION, '>=' )
424 ) {
425 wpforopoll_activation();
426 }
427 },
428 'title' => 'Polls',
429 'thumb' => WPFORO_URL . '/assets/addons/polls/header.png',
430 'desc' => __( 'wpForo Polls is a complete addon to help forum members create, vote and manage polls effectively. Comes with poll specific permissions and settings.', 'wpforo' ),
431 'url' => 'https://gvectors.com/product/wpforo-polls/',
432 ],
433 'wpforo-emoticons' => [
434 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-emoticons/wpforosmile.php' ),
435 'base' => false,
436 'version' => '3.0.0',
437 'requires' => '2.0.0',
438 'class' => 'wpForoSmiles',
439 'deps' => [],
440 'install_func' => function () {
441 if (
442 class_exists( 'wpForoSmiles' )
443 && function_exists( 'wpforosmile_activation' )
444 && version_compare( WPFORO_VERSION, WPFOROSMILE_WPFORO_REQUIRED_VERSION, '>=' )
445 ) {
446 wpforosmile_activation();
447 }
448 },
449 'title' => 'wpForo Emoticons',
450 'thumb' => WPFORO_URL . '/assets/addons/wpforo-emoticons/header.png',
451 'desc' => __( 'Adds awesome Sticker and Emoticons packs to editor. Allows to create new custom emoticons packs.', 'wpforo' ),
452 'url' => 'https://gvectors.com/product/wpforo-emoticons/',
453 ],
454 'wpforo-tenor' => [
455 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-tenor/wpforotenor.php' ),
456 'base' => false,
457 'version' => '3.0.0',
458 'requires' => '2.0.0',
459 'class' => 'wpForoTenor',
460 'deps' => [],
461 'title' => 'Tenor GIFs Integration',
462 'thumb' => WPFORO_URL . '/assets/addons/tenor/header.png',
463 'desc' => __( 'Adds Tenor [GIF] button and opens popup where you can search for gifs and insert them in topic, post and private message content.', 'wpforo' ),
464 'url' => 'https://gvectors.com/product/wpforo-tenor/',
465 ],
466 'wpforo-giphy' => [
467 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-giphy/wpforogiphy.php' ),
468 'base' => false,
469 'version' => '3.0.0',
470 'requires' => '2.0.0',
471 'class' => 'wpForoGiphy',
472 'deps' => [],
473 'title' => 'GIPHY Integration',
474 'thumb' => WPFORO_URL . '/assets/addons/giphy/header.png',
475 'desc' => __( 'Adds GIPHY [GIF] button and opens popup where you can search for gifs and insert them in topic, post and private message content.', 'wpforo' ),
476 'url' => 'https://gvectors.com/product/wpforo-giphy/',
477 ],
478 'wpforo-mycred' => [
479 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-mycred/wpforo-mc.php' ),
480 'base' => true,
481 'version' => '3.0.0',
482 'requires' => '2.0.0',
483 'class' => 'myCRED_Hook_wpForo',
484 'deps' => [],
485 'title' => 'MyCRED Integration',
486 'thumb' => WPFORO_URL . '/assets/addons/mycred/header.png',
487 'desc' => __( 'Awards myCRED points for forum activity. Integrates myCRED Badges and Ranks. Converts wpForo topic and posts, likes to myCRED points.', 'wpforo' ),
488 'url' => 'https://gvectors.com/product/wpforo-mycred/',
489 ],
490 'wpforo-syntax-highlighter' => [
491 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-syntax-highlighter/wpForoSyntaxHighlighter.php' ),
492 'base' => false,
493 'version' => '3.0.0',
494 'requires' => '2.0.0',
495 'class' => 'wpForoSyntaxHighlighter',
496 'deps' => [],
497 'install_func' => function () {
498 if (
499 class_exists( 'wpForoSyntaxHighlighter' )
500 && function_exists( 'wpForoSyntaxActivation' )
501 && version_compare( WPFORO_VERSION, WPFOROSYNTAX_WPFORO_REQUIRED_VERSION, '>=' )
502 ) {
503 wpForoSyntaxActivation();
504 }
505 },
506 'title' => 'Syntax Highlighter',
507 'thumb' => WPFORO_URL . '/assets/addons/syntax/header.png',
508 'desc' => __( 'Syntax highlighting for forum posts, automatic language detection and multi-language code highlighting.', 'wpforo' ),
509 'url' => 'https://gvectors.com/product/wpforo-syntax-highlighter/',
510 ],
511
512 ];
513
514 if( !is_null( $base ) ){
515 $addons = array_filter($addons, function( $addon ) use ( $base ){
516 return $addon['base'] === $base;
517 });
518 }
519
520 return $addons;
521 }
522
523 function wpforo_get_ajax_actions_list(){
524 $nonces = [];
525 global $wp_filter;
526 if( WPF()->current_userid ){
527 foreach ( $wp_filter as $key => $value ){
528 if( strpos( (string) $key, 'wp_ajax_wpf' ) === 0 ) $nonces[] = preg_replace( '#^wp_ajax_#iu', '', (string) $key, 1 );
529 }
530 }else{
531 foreach ( $wp_filter as $key => $value ){
532 if( strpos( (string) $key, 'wp_ajax_nopriv_wpf' ) === 0 ) $nonces[] = preg_replace( '#^wp_ajax_nopriv_#iu', '', (string) $key, 1 );
533 }
534 }
535
536 return array_unique( (array) apply_filters( 'wpforo_get_ajax_actions_list', $nonces ) );
537 }
538
539 function wpforo_generate_ajax_nonces(){
540 $nonces = [];
541 foreach ( wpforo_get_ajax_actions_list() as $action ){
542 $nonces[ $action ] = wp_create_nonce( $action );
543 }
544
545 return $nonces;
546 }
547
548 function wpforo_verify_form( $action = 'wpforo_verify_form', $query_arg = '_wpfnonce', $mode = 'full' ) {
549 wpforo_verify_nonce( $action, $mode, $query_arg );
550 do_action( 'wpforo_verify_form_end' );
551 }
552
553 function wpforo_verify_nonce( $action = 'wpforo_verify_form', $mode = 'ajax', $query_arg = '_wpfnonce' ) {
554 if( $mode === 'full' ) {
555 if( ! wp_verify_nonce( wpfval( $_REQUEST, $query_arg ), $action ) ) {
556 wpforo_phrase( 'Sorry, something is wrong with your data.' );
557 exit();
558 }
559 }elseif( $mode === 'ajax' ){
560 check_ajax_referer( $action, $query_arg );
561 }
562 if( apply_filters( 'wpforo_check_referer', true )
563 && in_array( $mode, [ 'ajax', 'full', 'ref' ] ) ) {
564 if( ! wpfval( $_SERVER, 'HTTP_REFERER' ) ) {
565 exit( 'Error 2252 | Please contact the forum administrator.' );
566 }
567 if( wpforo_is_url_external( $_SERVER['HTTP_REFERER'] ) ) {
568 exit( 'Error 2253 | Please contact the forum administrator.' );
569 }
570 }
571 do_action( 'wpforo_verify_nonce_end' );
572 }
573
574 /**
575 * @param string $path
576 * @param string|null $route
577 * @param string|null $scheme
578 *
579 * @return string
580 */
581 function wpforo_url( $path = '', $route = null, $scheme = null ) {
582 if( $route ) $route = wpforo_settings_get_slug( $route );
583 if( is_null( $route ) ) $route = WPF()->board->full_route;
584 $path = trim( trim( (string) $route, '/\\' ) . '/' . trim( (string) $path, '/\\' ), '/\\' );
585
586 if( function_exists( 'pll_default_language' ) && function_exists( 'PLL' ) ){
587 if( ! PLL()->options['hide_default'] && $lng = pll_default_language() ) $path = $lng . '/' . $path;
588 }
589
590 $url = home_url( $path, $scheme );
591 //-START- check is url maybe WordPress home
592 $maybe_home_url = trim( preg_replace( '#/?index\.php/?(\?.*)?$#isu', '', (string) $url ), '/\\' );
593 $home_url = trim( home_url( '', $scheme ), '/\\' );
594 if( $maybe_home_url === $home_url ) $url = preg_replace( '#index\.php/?#isu', '', (string) $url, 1 );
595
596 //-END- check is url maybe WordPress home
597
598 $url = apply_filters( 'wpforo_url', $url, $path, $route, $scheme );
599
600 return WPF()->user_trailingslashit( $url );
601 }
602
603 /**
604 * @param string $path
605 * @param string|null $scheme
606 *
607 * @return string
608 */
609 function wpforo_home_url( $path = '', $scheme = null ) {
610 if( strpos( (string) $path, 'http' ) === 0 ) {
611 $path = wpforo_get_url_query_vars_str( $path );
612 $path = preg_replace( '#index\.php/?#isu', '', (string) $path, 1 );
613 $path = preg_replace( '#^/?' . preg_quote( trim( (string) WPF()->board->route, '/\\' ) ) . '#isu', '', (string) $path, 1 );
614 }
615
616 return wpforo_url( $path, null, $scheme );
617 }
618
619 function wpforo_is_ajax() {
620 return defined( 'DOING_AJAX' ) && DOING_AJAX;
621 }
622
623 function wpforo_is_admin( $url = '' ) {
624 $url = trim( (string) $url );
625 if( $url ) return strpos( (string) $url, trim( admin_url(), '/' ) ) === 0 || strpos( $url, trim( self_admin_url(), '/' ) ) === 0;
626
627 return is_admin() && ! wpforo_is_ajax();
628 }
629
630 function _is_wpforo_page( $url = '' ) {
631 if( ! $url ) $url = wpforo_get_request_uri();
632 $result = ( ! ( wpforo_is_admin( $url ) || ( is_wpforo_exclude_url( $url ) && !is_wpforo_url( $url ) && ! is_wpforo_shortcode_page( $url ) ) ) && ( is_wpforo_url( $url ) || is_wpforo_shortcode_page( $url ) ) );
633
634 return apply_filters( 'is_wpforo_page', $result, $url );
635 }
636
637 function is_wpforo_page( $url = '' ) {
638 return wpforo_ram_get( '_is_wpforo_page', $url );
639 }
640
641 function _is_wpforo_exclude_url( $url = '' ) {
642 $result = false;
643 if( ! $url ) $url = wpforo_get_request_uri();
644 $url = urldecode( (string) $url );
645 $url = preg_replace( '#/page/\d*/?$#iu', '', (string) $url );
646 if( $current_url = wpforo_get_url_query_vars_str( $url ) ) {
647 if( preg_match( '#^/?(?:([^\s/?&=<>:\'\"*\\\|]*/)(?1)*)?[^\s/?&=<>:\'\"*\\\|]+\.(?:php|js|css|jpe?g|png|gif|webp|svg|bmp|tiff)/?(?:\?[^/]*)?$#iu', $current_url ) ) {
648 $result = true;
649 } elseif( WPF()->board->get_current( 'is_standalone' ) && ( $excld_urls = WPF()->board->get_current( 'excld_urls' ) ) ) {
650 foreach( $excld_urls as $excld_url ) {
651 $excld_url = urldecode( (string) $excld_url );
652 if( wpforo_is_url_internal( $excld_url ) ) {
653 $excld_url = wpforo_get_url_query_vars_str( $excld_url );
654 $pattern = preg_quote( $excld_url );
655 $pattern = str_replace( [ '/\*', '\*' ], [ '/?[^\r\n\t\s\0]*?', '[^\r\n\t\s\0]*?' ], $pattern );
656 if( preg_match( '#^' . $pattern . '$#iu', $current_url ) ) {
657 $result = true;
658 break;
659 }
660 }
661 }
662 }
663 }
664
665 return $result;
666 }
667
668 function is_wpforo_exclude_url( $url = '' ) {
669 return wpforo_ram_get( '_is_wpforo_exclude_url', $url );
670 }
671
672 function _is_wpforo_url( $url = '' ) {
673 $result = false;
674 if( ! wpforo_is_admin( $url ) ) {
675 if( WPF()->board->get_current( 'is_standalone' ) && ! is_wpforo_exclude_url( $url ) ) {
676 $result = true;
677 } else {
678 $current_url = wpforo_get_url_query_vars_str( $url );
679 $current_url = preg_replace( '#/?\?.*$#isu', '', $current_url );
680 foreach( WPF()->board->routes as $route ) {
681 if( $route && ( $current_url === $route || strpos( (string) $current_url, $route . '/' ) === 0 ) ) {
682 $result = true;
683 break;
684 }
685 }
686 }
687 }
688
689 return $result;
690 }
691
692 function is_wpforo_url( $url = '' ) {
693 return wpforo_ram_get( '_is_wpforo_url', $url );
694 }
695
696 /**
697 * @param string $url
698 *
699 * @return bool
700 */
701 function _is_wpforo_shortcode_page( $url = '' ) {
702 $result = ! wpforo_is_admin( $url ) && ! is_wpforo_url( $url ) && has_shortcode( wpforo_get_wp_post_content( $url ), 'wpforo' );
703 return apply_filters( 'wpforo_is_shortcode_page', $result, $url );
704 }
705
706 function is_wpforo_shortcode_page( $url = '' ) {
707 return wpforo_ram_get( '_is_wpforo_shortcode_page', $url );
708 }
709
710 function _wpforo_get_wp_post_content( $url = '' ) {
711 if( ! $url ) $url = wpforo_get_request_uri();
712 $post_content = '';
713 global $post;
714 if( $url === wpforo_get_request_uri() && is_a( $post, 'WP_Post' ) ) {
715 $post_content = $post->post_content;
716 } elseif( $postid = wpforo_wp_url_to_postid( $url ) ) {
717 $post_content = get_post_field( 'post_content', $postid );
718 }
719
720 return $post_content;
721 }
722
723 function wpforo_get_wp_post_content( $url = '' ) {
724 return wpforo_ram_get( '_wpforo_get_wp_post_content', $url );
725 }
726
727
728 /**
729 * @param string $text
730 * @param string $url
731 *
732 * @return array|string
733 */
734 function get_wpforo_shortcode_atts( $text = '', $url = '' ) {
735 if( ! $text ) $text = wpforo_get_wp_post_content( $url );
736 if( preg_match( '#\[[\r\n\t\s\0]*wpforo[\r\n\t\s\0]*([^\[\]]*?)]#iu', $text, $match ) ) {
737 return shortcode_parse_atts( $match[1] );
738 }
739
740 return '';
741 }
742
743 function wpforo_get_url_query_vars_str( $url = '' ) {
744 if( ! $url ) $url = wpforo_get_request_uri();
745 $home_url = preg_replace( '#/?\?.*$#isu', '', home_url( '/' ) );
746 $current_url = preg_replace( '#https?://[^/\?]+/?#isu', '', $url );
747 $site_url = preg_replace( '#https?://[^/\?]+/?#isu', '', $home_url );
748 if( strpos( (string) $current_url, '/' ) === false && $current_url === trim( (string) $site_url, '/' ) ) return '';
749 $current_url = preg_replace( '#^/?' . preg_quote( $site_url ) . '(?:/?index\.php/?)?#isu', '', $current_url, 1 );
750 $current_url = preg_replace( '#^/?' . preg_quote( wpforo_get_query_var_lang() ) . '/#isu', '', $current_url, 1 );
751 $current_url = preg_replace( '#^[\s\0/]*(.*?)[\s\0/]*$#isu', '$1', $current_url );
752
753 return wpforo_fix_url( $current_url );
754 }
755
756 function wpforo_get_url_route( $url = '' ) {
757 $route = '';
758 $query_vars_str = preg_replace( '#/?\?.*$#isu', '', wpforo_get_url_query_vars_str( (string) $url ) );
759 $query_vars = array_values( array_filter( explode( '/', trim( (string) $query_vars_str, '/' ) ) ) );
760 if( array_key_exists( 0, $query_vars ) && in_array( $query_vars[0], WPF()->board->routes ) ) $route = $query_vars[0];
761
762 return $route;
763 }
764
765 function wpforo_dir_size( $directory ) {
766 $size = 0;
767 if( is_dir( $directory ) && class_exists( 'RecursiveIteratorIterator' ) && class_exists( 'RecursiveDirectoryIterator' ) ) {
768 foreach( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $directory ) ) as $file ) $size += $file->getSize();
769 }
770
771 return $size;
772 }
773
774 function wpforo_make_hidden_fields_from_url( $url = '', $echo = true ) {
775 if( ! $url ) $url = wpforo_get_request_uri();
776 $return = '';
777 if( $url_query = parse_url( $url, PHP_URL_QUERY ) ) {
778 parse_str( $url_query, $url_query_arr );
779 if( ! empty( $url_query_arr ) ) {
780 foreach( $url_query_arr as $key => $value ) {
781 $return .= '<input type="hidden" name="' . $key . '" value="' . $value . '">';
782 }
783 }
784 }
785 if( ! $echo ) return $return;
786 echo $return;
787 }
788
789 /**
790 * Returns merged arguments array from defined and default arguments.
791 *
792 * @param mixed $args
793 * @param array $default
794 *
795 * @return array
796 */
797 function wpforo_parse_args( $args, $default = [] ) {
798 if( is_object( $args ) ) {
799 $defined = get_object_vars( $args );
800 } elseif( is_array( $args ) ) {
801 $defined = $args;
802 } elseif( preg_match( '|^[\d,\s]+$|', (string) $args ) ) {
803 $defined = explode( ',', trim( (string) $args ) );
804 } elseif( is_integer( $args ) || is_float( $args ) ) {
805 $defined[0] = $args;
806 } elseif( is_serialized( $args ) ) {
807 $defined = unserialize( $args );
808 } elseif( strpos( (string) $args, '=' ) !== false ) {
809 parse_str( $args, $defined );
810 } else {
811 $defined = (array) $args;
812 }
813 if( ! empty( $default ) ) {
814 return array_merge( $default, $defined );
815 } else {
816 return $defined;
817 }
818 }
819
820 /**
821 * Detects serialized data
822 *
823 * @param string
824 *
825 * @return boolean
826 * @since 1.0.0
827 *
828 */
829 if( ! function_exists( 'is_serialized' ) ) {
830 function is_serialized( $value ) {
831 if( $value == '' ) return false;
832 $value = trim( (string) $value );
833 $chsd = @unserialize( $value );
834 if( $chsd !== false || $value === 'b:0;' ) {
835 return true;
836 } else {
837 return false;
838 }
839 }
840 }
841
842 function wpforo_get_request_uri( $with_port = false, $get_referer_when_ajax = true ) {
843 if( $get_referer_when_ajax && wpforo_is_ajax() ) {
844 if( $referer = wpfval( $_REQUEST, 'referer' ) ) {
845 $referer = preg_replace( '#\#[^/?&]*$#iu', '', (string) $referer );
846
847 return esc_url_raw( $referer );
848 }
849 if( $referer = wpfval( $_SERVER, 'HTTP_REFERER' ) ) {
850 $url = preg_replace( '#\#[^/?&]*$#iu', '', (string) $referer );
851
852 return esc_url_raw( $url );
853 }
854 }
855 $s = is_ssl() ? 's' : '';
856 $sp = strtolower( (string) wpfval( $_SERVER, 'SERVER_PROTOCOL' ) );
857 $protocol = substr( $sp, 0, strpos( (string) $sp, "/" ) ) . $s;
858 $server_port = intval( wpfval( $_SERVER, 'SERVER_PORT' ) );
859 $port = $server_port === 80 ? "" : ":" . $server_port;
860 $host = preg_replace( '#^www\.#iu', '', (string) wpfval( $_SERVER, 'HTTP_HOST' ) );
861 if( strpos( home_url(), 'www.' ) !== false ) $host = 'www.' . $host;
862 $requri = wpfval( $_SERVER, 'UNENCODED_URL' ) ?: wpfval( $_SERVER, 'REQUEST_URI' );
863 $url = $protocol . "://" . $host . ( $with_port && $server_port ? $port : '' ) . $requri;
864 $url = wpforo_fix_url( $url );
865
866 return esc_url_raw( $url );
867 }
868
869 function wpforo_arr_group_by( $array, $key_by ) {
870 if( ! empty( $array ) ) {
871 $fltrd = [];
872 foreach( $array as $key => $arr ) {
873 if( is_numeric( $key ) ) $fltrd[] = $arr[ $key_by ];
874 }
875 $uniq_arr = array_unique( $fltrd );
876 asort( $uniq_arr );
877
878 return $uniq_arr;
879 }
880 }
881
882 function wpforo_phrase( $key, $echo = true, $format = 'first-upper' ) {
883 $locale = WPF()->locale;
884 $phrase = ( isset( WPF()->phrase->phrases[ addslashes( strtolower( (string) $key ) ) ] ) ) ? WPF()->phrase->phrases[ addslashes( strtolower( (string) $key ) ) ] : $key;
885 if( 'en_US' !== $locale ) {
886 $native = $phrase;
887 $backtrace = wp_debug_backtrace_summary();
888 $mopo_domain = 'wpforo';
889 if( strpos( (string) $backtrace, '\plugins\wpforo-private-messages\\' ) !== false || strpos( (string) $backtrace, '/plugins/wpforo-private-messages/' ) !== false ) $mopo_domain = 'wpforo_pm';
890 if( strpos( (string) $backtrace, 'wpForoPolls' ) !== false ) $mopo_domain = 'wpforo_poll';
891 $key = preg_replace( "/(^\s+)|(\s+$)/u", "", (string) $key );
892 $phrase = preg_replace( "/(^\s+)|(\s+$)/u", "", (string) $phrase );
893 if( strtolower( (string) $key ) === strtolower( (string) $phrase ) ) {
894 $phrase = __( $key, $mopo_domain );
895 if( strtolower( (string) $key ) === strtolower( (string) $phrase ) ) {
896 $key = strtolower( (string) $key );
897 $phrase = __( $key, $mopo_domain );
898 if( strtolower( (string) $key ) === strtolower( (string) $phrase ) ) {
899 $phrase = __( ucfirst( (string) $key ), $mopo_domain );
900 if( strtolower( (string) $key ) === strtolower( (string) $phrase ) ) {
901 $phrase = __( $native, $mopo_domain ); //Try all, if no result pass the original text to translation again.
902 }
903 }
904 }
905 }
906 }
907
908 if( $format === 'first-upper' ) {
909 if( 'en_US' !== $locale && function_exists( 'mb_strlen' ) && mb_strlen( (string) $phrase ) !== strlen( (string) $phrase ) && function_exists( 'mb_strtoupper' ) ) {
910 $phrase = mb_strtoupper( mb_substr( (string) $phrase, 0, 1 ) ) . mb_substr( (string) $phrase, 1 );
911 } else {
912 $phrase = ucfirst( (string) $phrase );
913 }
914 } elseif( $format === 'upper' ) {
915 if( function_exists( 'mb_strtoupper' ) ) {
916 $phrase = mb_strtoupper( (string) $phrase );
917 } else {
918 $phrase = strtoupper( (string) $phrase );
919 }
920 } elseif( $format === 'lower' ) {
921 if( function_exists( 'mb_strtolower' ) ) {
922 $phrase = mb_strtolower( (string) $phrase );
923 } else {
924 $phrase = strtolower( (string) $phrase );
925 }
926 }
927
928 $phrase = str_replace( '{number}', '', $phrase );
929
930 if( $echo ) echo $phrase;
931 return $phrase;
932 }
933
934 function wpforo_screen_option() { ?>
935 <div id="screen-meta" class="metabox-prefs" style="display: none; ">
936 <div id="screen-options-wrap" class="hidden" tabindex="-1" aria-label="Screen Options Tab" style="display: none; ">
937 <form id="adv-settings" action="" method="POST">
938 <input type="hidden" name="wpfaction" value="dashboard_options_save">
939 <h5><?php _e( 'Show on screen', 'wpforo' ) ?></h5>
940
941 <div class="screen-options">
942 <input type="number" step="1" min="1" max="999" class="screen-per-page" name="wpforo_dashboard_count_per_page" id="edit_post_per_page" maxlength="3" value="<?php echo wpforo_get_option( 'count_per_page', 10 ) ?>">
943 <label for="edit_post_per_page"><?php _e( 'Items', 'wpforo' ) ?></label>
944 <input type="submit" id="screen-options-apply" class="button" value="<?php _e( 'Apply', 'wpforo' ) ?>">
945 </div>
946 </form>
947 </div>
948 </div>
949
950 <div id="screen-meta-links">
951 <div id="screen-options-link-wrap" class="hide-if-no-js screen-meta-toggle" style="">
952 <a href="#screen-options-wrap" id="show-settings-link" class="show-settings screen-meta-active" aria-controls="screen-options-wrap" aria-expanded="true">
953 <?php _e( 'Screen Options', 'wpforo' ) ?>
954 </a>
955 </div>
956 </div>
957
958 <?php
959 }
960
961 function wpforo_strip_shortcodes( $text, $only_wpforo_shortcodes = false ) {
962 $text = (string) apply_filters( 'wpforo_strip_shortcodes', (string) $text, $only_wpforo_shortcodes );
963 $text = preg_replace( '#\[attach[^\[\]]*][^\[\]]*\[/attach]#iu', '', $text );
964 $text = preg_replace( '#\[spoiler[^\[\]]*][^\[\]]*\[/spoiler]#iu', '', $text );
965 $text = preg_replace( '#\[quote[^\[\]]*][^\[\]]*\[/quote]#iu', '', $text );
966 $text = preg_replace( '#\[wpfgiphy[^\[\]]+?]#iu', '', $text );
967 $text = preg_replace( '#\[wpftenor[^\[\]]+?]#iu', '', $text );
968 // $text = preg_replace('#(?:{\w+})?:[^/.\s]+:#iu', '', $text);
969 if( ! $only_wpforo_shortcodes ) $text = strip_shortcodes( $text );
970
971 return $text;
972 }
973
974 function wpforo_strip_quotes( $text ) {
975 $text = preg_replace( '#<(blockquote)[^<>]*?data-userid[^<>]*?>(?:.*?(?R)*.*?)*?</\1>#isu', '', (string) $text );
976 return preg_replace( '#\[(quote)[^\[\]]*?data-userid[^\[\]]*?](?:.*?(?R)*.*?)*?\[/\1]#isu', '', (string) $text );
977 }
978
979 function wpforo_strip_urls( $text ) {
980 return preg_replace( '#(?:[^\'\"]|^)(https?://[^\s\'\"<>]+)(?:[^\'\"]|$)#iu', '', (string) $text );
981 }
982
983 function wpforo_text( $text, $length = 0, $echo = true, $strip_tags = true, $strip_urls = true, $strip_shortcodes = true, $strip_quotes = true ) {
984 $text = (string) $text;
985 $text = str_replace( '</p>', '</p> ', $text );
986 $text = str_replace( '</div>', '</div> ', $text );
987
988 if( $strip_quotes ) $text = wpforo_strip_quotes( $text );
989 if( $strip_urls ) $text = wpforo_strip_urls( $text );
990 if( $strip_tags ) $text = strip_tags( (string) $text );
991 if( $strip_shortcodes ) $text = wpforo_strip_shortcodes( $text );
992
993 $text = apply_filters( 'wpforo_text', $text, $length, $echo, $strip_tags, $strip_urls, $strip_shortcodes, $strip_quotes );
994
995 $text = trim( str_replace( "\xc2\xa0", ' ', (string) $text ) );
996
997 if( ! $length ) {
998 $text = trim( (string) $text );
999 }elseif( function_exists( 'mb_substr' ) ) {
1000 $text = trim( mb_substr( (string) $text, 0, $length, get_option( 'blog_charset' ) ) . ( ( function_exists( 'mb_strlen' ) ? mb_strlen( (string) $text, get_option( 'blog_charset' ) ) : strlen( (string) $text ) ) > $length ? '...' : '' ) );
1001 } else {
1002 $text = trim( substr( (string) $text, 0, $length ) . ( strlen( (string) $text ) > $length ? '...' : '' ) );
1003 }
1004
1005 if( $echo ) echo $text;
1006 return $text;
1007 }
1008
1009 function wpforo_admin_options_tabs( $tabs, $current = 'general', $subtab = false, $sub_current = 'general' ) {
1010 if( ! empty( $tabs ) ) {
1011 $class_attr = $subtab ? 'vert_tab' : '';
1012 echo '<h2 class="nav-tab-wrapper ' . esc_attr( $class_attr ) . '">';
1013 foreach( $tabs as $tab => $name ) {
1014 $class = ( $tab === $current || ( $subtab && $tab === $sub_current ) ) ? ' nav-tab-active' : '';
1015 $sub = $subtab ? '&subtab=' . esc_attr( $tab ) : '';
1016 $class = esc_attr( $class );
1017 $current = esc_attr( $current );
1018 $tab = esc_attr( $tab );
1019 $sub = esc_attr( $sub );
1020 echo "<a class='nav-tab $class $class_attr' href='?page=" . (string) wpfval( $_GET, 'page' ) . "&tab=" . ( $subtab ? $current : $tab ) . "$sub'>$name</a>";
1021 }
1022 echo '</h2>';
1023 }
1024 }
1025
1026 function wpforo_admin_tools_tabs( $tabs, $current = 'antispam', $subtab = false, $sub_current = 'antispam' ) {
1027 if( ! empty( $tabs ) ) {
1028 $class_attr = $subtab ? 'vert_tab' : '';
1029 echo '<h2 class="nav-tab-wrapper ' . esc_attr( $class_attr ) . '">';
1030 foreach( $tabs as $tab => $name ) {
1031 $class = ( $tab == $current || ( $subtab && $tab == $sub_current ) ) ? ' nav-tab-active' : '';
1032 $sub = $subtab ? '&subtab=' . esc_attr( $tab ) : '';
1033 $class = esc_attr( $class );
1034 $current = esc_attr( $current );
1035 $tab = esc_attr( $tab );
1036 $sub = esc_attr( $sub );
1037 $name = esc_html( $name );
1038 echo "<a class='nav-tab$class $class_attr' href='?page=" . wpforo_prefix_slug( 'tools' ) . "&tab=" . ( $subtab ? $current : $tab ) . "$sub' style='float:left'>$name</a>";
1039 }
1040 echo '</h2>';
1041 }
1042 }
1043
1044 function wpforo_content_filter( $content, $post = [] ) {
1045 $content = (string) $content;
1046 if( strpos( (string) $content, '../' ) !== false ) {
1047 $home_url = trim( preg_replace( [ '#/?\?.*$#isu', '#index\.php/?#isu' ], '', home_url() ), '/\\' ) . '/';
1048 $content = preg_replace( '#((?:href|src)=[\'\"])(?:https?://)?(?:\.+/)+wp-content/#i', "$1" . $home_url . "wp-content/", (string) $content );
1049 }
1050 $content = apply_filters( 'wpforo_body_text_filter', $content, $post );
1051 if( apply_filters( 'wpforo_auto_embed_image', true, $post ) ) $content = preg_replace( '#([^\'\"]|^)(https?://[^\s\'\"<>]+\.(?:jpg|jpeg|png|webp|gif|svg|bmp|tiff))([^\'\"]|$)#iu', '$1 <a class="wpforo-auto-embeded-link" href="$2" target="_blank"><img class="wpforo-auto-embeded-image" src="$2"/></a> $3', (string) $content );
1052 if( apply_filters( 'wpforo_auto_embed_link', true, $post ) ) $content = preg_replace( '#([^\'\"]|^)(https?://[^\s\'\"<>\[\]]+)([^\'\"]|$)#iu', '$1 <a class="wpforo-auto-embeded-link" href="$2" target="_blank">$2</a> $3', (string) $content );
1053 if( preg_match_all( '#<pre([^<>]*)>(.*?class=[\'"]wpforo-auto-embeded[^\'"]*[\'"].*?)</pre>#isu', (string) $content, $matches, PREG_SET_ORDER ) ) {
1054 foreach( $matches as $match ) {
1055 $match[2] = preg_replace( '#<img[^<>]*class=[\'"]wpforo-auto-embeded-image[\'"][^<>]*src=[\'"]([^\'"]*)[\'"][^<>]*>#isu', '$1', $match[2] );
1056 $match[2] = preg_replace( '#<a[^<>]*class=[\'"]wpforo-auto-embeded-link[\'"][^<>]*href=[\'"]([^\'"]*)[\'"][^<>]*>.*?</a>#isu', '$1', $match[2] );
1057 $content = str_replace( $match[0], '<pre' . $match[1] . '>' . $match[2] . '</pre>', $content );
1058 }
1059 }
1060 $content = preg_replace( '#(<a[^<>]*>[^<>]*)<a[^<>]*class=[\'"]wpforo-auto-embeded-link[\'"][^<>]*href=[\'"]([^\'"]*)[\'"][^<>]*>[^<>]*</a>([^<>]*</a>)#iu', '$1$2$3', (string) $content );
1061 $content = apply_filters( 'wpforo_content_filter', $content, $post );
1062
1063 return wpautop( $content );
1064 }
1065
1066 function wpforo_remove_links( $content ) {
1067 return preg_replace( '#([^\'\"]|^)(https?://[^\s\'\"<>]+)([^\'\"]|$)#isu', '$1 [' . wpforo_phrase( 'removed link', false ) . '] $3', (string) $content );
1068 }
1069
1070 add_filter( 'wpforo_content_filter', 'wpforo_nofollow_tag', 20 );
1071 function wpforo_nofollow_tag( $content ) {
1072 return preg_replace_callback( '#<a[^><]*?href=[\'\"]([^\'\"]+)[\'\"][^><]*?>#isu', 'wpforo_nofollow', (string) $content );
1073 }
1074
1075 function wpforo_nofollow( $match ) {
1076 $ret = $match[0];
1077 if( apply_filters( 'wpforo_external_link_nofollow', true ) ) {
1078 $dofollow = wpforo_setting( 'seo', 'dofollow' );
1079 $request_uri_parse = parse_url( wpforo_get_request_uri() );
1080 $main_host = preg_replace( '#^.*?([^.]+?\.[^.]+?)$#isu', '$1', (string) $request_uri_parse['host'] );
1081 $link_url = parse_url( $match[1] );
1082 if( strpos( (string) $match[1], $main_host ) === false && ! ( ! empty( $dofollow ) && ! empty( $link_url['host'] ) && in_array( $link_url['host'], $dofollow ) ) ) {
1083 $ret = preg_replace_callback(
1084 '#\srel=[\'\"]([^\'\"]*?)[\'\"]#iu',
1085 function( $m ) {
1086 $rels = array_filter( preg_split( '#\s#u', $m[1] ) );
1087 $rels = array_merge( [ 'nofollow' ], $rels );
1088
1089 return sprintf( 'rel="%1$s"', implode( ' ', $rels ) );
1090 },
1091 (string) $match[0],
1092 1,
1093 $count
1094 );
1095 if( ! $count ) $ret = str_replace( '>', ' rel="nofollow">', $match[0] );
1096 }
1097 }
1098
1099 return $ret;
1100 }
1101
1102 add_action( 'wpforo_actions_end', 'wpforo_logs', 10 );
1103 function wpforo_logs() {
1104 if( ! WPF()->current_object['is_404'] ) {
1105 WPF()->log->read();
1106 WPF()->log->visit();
1107 }
1108 }
1109
1110 add_action( 'wpforo_bottom_hook', 'wpforo_user_logging' );
1111 function wpforo_user_logging() {
1112 $data = WPF()->current_object;
1113 $filter_views = apply_filters( 'wpforo_filter_topic_views', true );
1114 if( wpfval( $data, 'template' ) && $data['template'] == 'post' && wpfval( $data, 'topicid' ) ) {
1115 if( $filter_views ) {
1116 //to-do: don't increase views before all read point.
1117 if( wpforo_setting( 'legal', 'cookies' ) ) {
1118 $viwed_ids = wpforo_getcookie( wpforo_prefix( 'read_topics' ), false );
1119 if( empty( $viwed_ids ) || ! wpfval( $viwed_ids, $data['topicid'] ) ) {
1120 WPF()->db->query( "UPDATE `" . WPF()->tables->topics . "` SET `views` = `views` + 1 WHERE `topicid` = " . intval( $data['topicid'] ) );
1121 }
1122 } elseif( is_user_logged_in() ) {
1123 if( wpfval( WPF()->current_usermeta, wpforo_prefix( 'read_topics' ) ) ) {
1124 $viwed_db_ids = wpforo_current_usermeta( wpforo_prefix( 'read_topics' ) );
1125 if( empty( $viwed_db_ids ) || ! wpfval( $viwed_db_ids, $data['topicid'] ) ) {
1126 WPF()->db->query( "UPDATE `" . WPF()->tables->topics . "` SET `views` = `views` + 1 WHERE `topicid` = " . intval( $data['topicid'] ) );
1127 }
1128 } else {
1129 WPF()->db->query( "UPDATE `" . WPF()->tables->topics . "` SET `views` = `views` + 1 WHERE `topicid` = " . intval( $data['topicid'] ) );
1130 }
1131 }
1132 } else {
1133 WPF()->db->query( "UPDATE `" . WPF()->tables->topics . "` SET `views` = `views` + 1 WHERE `topicid` = " . intval( $data['topicid'] ) );
1134 }
1135 }
1136 }
1137
1138 function wpforo_setcookie( $key, $args = [], $implode = false ) {
1139 if( ! wpforo_setting( 'legal', 'cookies' ) ) return;
1140 if( ! empty( $args ) && is_array( $args ) ) {
1141 $num = count( $args );
1142 if( $num > 3 ) {
1143 $max = apply_filters( 'wpforo_cookie_max_logged_topics', 10 );
1144 $delta = $num - $max;
1145 if( $delta > 0 ) $args = array_slice( $args, $delta, null, true );
1146 }
1147 }
1148 if( ! empty( $args ) && is_array( $args ) && $implode ) {
1149 $value = trim( implode( ',', $args ), ',' );
1150 } elseif( ! empty( $args ) && is_array( $args ) && ! $implode ) {
1151 $value = json_encode( $args );
1152 }
1153 if( ! isset( $value ) ) $value = '';
1154 if( $key ) {
1155 $secure = is_ssl();
1156 $secure_logged_in_cookie = $secure && 'https' === parse_url( get_option( 'home' ), PHP_URL_SCHEME );
1157 if( COOKIEPATH != SITECOOKIEPATH ) {
1158 @setcookie( $key, $value, time() + 7776000, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true );
1159 } else {
1160 @setcookie( $key, $value, time() + 7776000, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true );
1161 }
1162 }
1163 }
1164
1165 function wpforo_getcookie( $key, $explode = false ) {
1166 if( ! wpforo_setting( 'legal', 'cookies' ) ) return false;
1167 if( $cookie = wpfval( $_COOKIE, $key ) ) {
1168 if( $explode ) {
1169 return explode( ',', $cookie );
1170 } else {
1171 $cookie = wp_unslash( $cookie );
1172 if( ! $data = json_decode( $cookie, true ) ) return $cookie;
1173
1174 return $data;
1175 }
1176 }
1177
1178 return false;
1179 }
1180
1181 function wpforo_is_bot() {
1182 if( ! $http_user_agent = wpfval( $_SERVER, 'HTTP_USER_AGENT' ) ) $http_user_agent = wpfval( $_SERVER, 'http_user_agent' );
1183 if( $http_user_agent ) {
1184 return preg_match(
1185 '#(abot|dbot|ebot|hbot|kbot|lbot|mbot|nbot|obot|pbot|rbot|sbot|tbot|vbot|ybot|zbot|bot\.|bot\/|_bot|\.bot|\/bot|\-bot|\:bot|\(bot|crawl|slurp|spider|seek|accoona|acoon|adressendeutschland|ah\-ha\.com|ahoy|altavista|ananzi|anthill|appie|arachnophilia|arale|araneo|aranha|architext|aretha|arks|asterias|atlocal|atn|atomz|augurfind|backrub|bannana_bot|baypup|bdfetch|big brother|biglotron|bjaaland|blackwidow|blaiz|blog|blo\.|bloodhound|boitho|booch|bradley|butterfly|calif|cassandra|ccubee|cfetch|charlotte|churl|cienciaficcion|cmc|collective|comagent|combine|computingsite|csci|curl|cusco|daumoa|deepindex|delorie|depspid|deweb|die blinde kuh|digger|ditto|dmoz|docomo|download express|dtaagent|dwcp|ebiness|ebingbong|e\-collector|ejupiter|emacs\-w3 search engine|esther|evliya celebi|ezresult|falcon|felix ide|ferret|fetchrover|fido|findlinks|fireball|fish search|fouineur|funnelweb|gazz|gcreep|genieknows|getterroboplus|geturl|glx|goforit|golem|grabber|grapnel|gralon|griffon|gromit|grub|gulliver|hamahakki|harvest|havindex|helix|heritrix|hku www octopus|homerweb|htdig|html index|html_analyzer|htmlgobble|hubater|hyper\-decontextualizer|ia_archiver|ibm_planetwide|ichiro|iconsurf|iltrovatore|image\.kapsi\.net|imagelock|incywincy|indexer|infobee|informant|ingrid|inktomisearch\.com|inspector web|intelliagent|internet shinchakubin|ip3000|iron33|israeli\-search|ivia|jack|jakarta|javabee|jetbot|jumpstation|katipo|kdd\-explorer|kilroy|knowledge|kototoi|kretrieve|labelgrabber|lachesis|larbin|legs|libwww|linkalarm|link validator|linkscan|lockon|lwp|lycos|magpie|mantraagent|mapoftheinternet|marvin\/|mattie|mediafox|mediapartners|mercator|merzscope|microsoft url control|minirank|miva|mj12|mnogosearch|moget|monster|moose|motor|multitext|muncher|muscatferret|mwd\.search|myweb|najdi|nameprotect|nationaldirectory|nazilla|ncsa beta|nec\-meshexplorer|nederland\.zoek|netcarta webmap engine|netmechanic|netresearchserver|netscoop|newscan\-online|nhse|nokia6682\/|nomad|noyona|nutch|nzexplorer|objectssearch|occam|omni|open text|openfind|openintelligencedata|orb search|osis\-project|pack rat|pageboy|pagebull|page_verifier|panscient|parasite|partnersite|patric|pear\.|pegasus|peregrinator|pgp key agent|phantom|phpdig|picosearch|piltdownman|pimptrain|pinpoint|pioneer|piranha|plumtreewebaccessor|pogodak|poirot|pompos|poppelsdorf|poppi|popular iconoclast|psycheclone|publisher|python|rambler|raven search|roach|road runner|roadhouse|robbie|robofox|robozilla|rules|salty|sbider|scooter|scoutjet|scrubby|search\.|searchprocess|semanticdiscovery|senrigan|sg\-scout|shai\'hulud|shark|shopwiki|sidewinder|sift|silk|simmany|site searcher|site valet|sitetech\-rover|skymob\.com|sleek|smartwit|sna\-|snappy|snooper|sohu|speedfind|sphere|sphider|spinner|spyder|steeler\/|suke|suntek|supersnooper|surfnomore|sven|sygol|szukacz|tach black widow|tarantula|templeton|\/teoma|t\-h\-u\-n\-d\-e\-r\-s\-t\-o\-n\-e|theophrastus|titan|titin|tkwww|toutatis|t\-rex|tutorgig|twiceler|twisted|ucsd|udmsearch|url check|updated|vagabondo|valkyrie|verticrawl|victoria|vision\-search|volcano|voyager\/|voyager\-hc|w3c_validator|w3m2|w3mir|walker|wallpaper|wanderer|wauuu|wavefire|web core|web hopper|web wombat|webbandit|webcatcher|webcopy|webfoot|weblayers|weblinker|weblog monitor|webmirror|webmonkey|webquest|webreaper|websitepulse|websnarf|webstolperer|webvac|webwalk|webwatch|webwombat|webzinger|wget|whizbang|whowhere|wild ferret|wordpress|worldlight|wwwc|wwwster|xenu|xget|xift|xirq|yandex|yanga|yeti|yodao|zao\/|zippp|zyborg|\.\.\.\.|InspectionTool)#i',
1186 (string) $http_user_agent
1187 );
1188 }
1189
1190 return true;
1191 }
1192
1193 //Option value filter and output
1194 function wpfo( $option = '', $echo = true, $esc = 'esc_attr' ) {
1195 if( is_string( $option ) ) {
1196 $option = stripslashes( (string) $option );
1197 if( $esc === 'esc_attr' ) {
1198 $option = esc_attr( $option );
1199 } elseif( $esc === 'esc_html' ) {
1200 $option = esc_html( $option );
1201 } elseif( $esc === 'esc_url' ) {
1202 $option = esc_url( (string) $option );
1203 } elseif( $esc === 'esc_textarea' ) {
1204 $option = esc_textarea( (string) $option );
1205 }
1206 }
1207
1208 if( $echo ) {
1209 echo $option;
1210 } else {
1211 return $option;
1212 }
1213 }
1214
1215 //Option maker for checkbox, radio and select
1216 function wpfo_check( $option = '', $value = '', $type = 'checked', $echo = true ) {
1217 $option = ( isset( $option ) && isset( $value ) && $option == $value ) ? $type : '';
1218 if( $echo ) {
1219 echo $option;
1220 } else {
1221 return $option;
1222 }
1223 }
1224
1225 /**
1226 * Validates keys of requested array.
1227 *
1228 * @param array $array
1229 * @param string|int $key
1230 * @param string|int ... $_, ... [optional] more keys
1231 *
1232 * @return bool
1233 */
1234 function wpfkey( &$array, $key ) {
1235 $a = $array;
1236 foreach( func_get_args() as $arg_num => $key ) {
1237 if( $arg_num === 0 ) continue;
1238 if( is_array( $a ) && ( is_string( $key ) || is_int( $key ) ) && array_key_exists( $key, $a ) ) {
1239 $a = $a[ $key ];
1240 } else {
1241 return false;
1242 }
1243 }
1244
1245 return true;
1246 }
1247
1248 /**
1249 * get values of requested array keys if found otherwise returns null to allow you cast to your desired type of variable.
1250 *
1251 * @param mixed $array
1252 * @param string|int ... $_, ... [optional] more keys
1253 *
1254 * @return mixed|null
1255 */
1256 function wpfval( &$array ) {
1257 if( func_num_args() === 1 ) return $array;
1258 $a = $array;
1259 foreach( func_get_args() as $arg_num => $key ) {
1260 if( $arg_num === 0 ) continue;
1261 if( is_array( $a ) && ( is_string( $key ) || is_int( $key ) ) && array_key_exists( $key, $a ) ) {
1262 $a = $a[ $key ];
1263 } else {
1264 return null;
1265 }
1266 }
1267
1268 return $a;
1269 }
1270
1271 /**
1272 * Always return the preferred default value if the array, or array index is undefined.
1273 *
1274 * @param mixed the default value if current index is undefined in the array
1275 * @param mixed the $array to be checked
1276 * @param string|int ... $_, ... [optional] more keys
1277 *
1278 * @return mixed|null
1279 */
1280 function wpffix( $return, $array, $a = null, $b = null, $c = null ) {
1281 $value = wpfval( $array, $a, $b, $c );
1282 if( ! is_null( $value ) ) {
1283 return $value;
1284 }
1285
1286 return $return;
1287 }
1288
1289 function wpforo_human_filesize( $bytes, $decimals = 2 ) {
1290 $size = [ 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' ];
1291 $factor = floor( ( strlen( (string) $bytes ) - 1 ) / 3 );
1292
1293 return sprintf( "%.{$decimals}f", $bytes / pow( 1024, $factor ) ) . '&nbsp;' . @$size[ $factor ];
1294 }
1295
1296 function wpforo_human_time_diff( int $from, bool $ago = true, int $to = 0 ): string{
1297 if ( ! $to ) $to = time();
1298 $d = human_time_diff( $from, $to );
1299 if( $ago ) $d = sprintf( wpforo_phrase( '%s ago', false, false ), $d );
1300
1301 return $d;
1302 }
1303
1304 /**
1305 * @param int | string $date
1306 *
1307 * @return int
1308 */
1309 function wpforo_gmtimestamp( $date = 0 ): int{
1310 if( ! $date ) $date = time();
1311 return intval( is_numeric( $date ) ? $date : strtotime( $date . ' GMT' ) );
1312 }
1313
1314 function wpforo_timezone( string $timezone = '' ): array{
1315 $timezone_string = '';
1316 $current_offset = null;
1317 if( $timezone ) {
1318 if( $timezone === 'wp' ){
1319 $timezone_string = get_option( 'timezone_string', '' );
1320 $current_offset = get_option( 'gmt_offset', null );
1321 }else{
1322 if( preg_match( '#UTC\s*([-+])\s*([\d.]+)#i', (string) $timezone, $timezone_array ) ) {
1323 $current_offset = $timezone_array[1] . $timezone_array[2];
1324 } elseif( in_array( $timezone, timezone_identifiers_list() ) ) {
1325 $timezone_string = $timezone;
1326 }
1327 }
1328 }
1329
1330 return compact( 'timezone_string', 'current_offset' );
1331 }
1332
1333 /**
1334 * @param int | string $date
1335 * @param string $timezone
1336 *
1337 * @return int
1338 */
1339 function wpforo_timestamp( $date = 0, string $timezone = '' ): int{
1340 $timestamp = wpforo_gmtimestamp( $date );
1341 $tmz = wpforo_timezone( $timezone );
1342
1343 if( is_numeric( $tmz['current_offset'] ) ) {
1344 $timestamp += $tmz['current_offset'] * 3600;
1345 } elseif( $tmz['timezone_string'] && class_exists( 'DateTime' ) && class_exists( 'DateTimeZone' ) ) {
1346 try {
1347 $dt = new DateTime( gmdate( 'Y-m-d H:i:s', $timestamp ), new DateTimeZone( $tmz['timezone_string'] ) );
1348 $timestamp += $dt->getOffset();
1349 } catch ( Exception $e ) {
1350 }
1351 }
1352
1353 return (int) $timestamp;
1354 }
1355
1356 /**
1357 * @param int $gmttime
1358 * @param string $format
1359 * @param bool $wp_date_format
1360 *
1361 * @return string
1362 */
1363 function wpforo_date_format( int $gmttime, string $format, bool $wp_date_format = true ): string {
1364 if ( $format === 'mysql' ){
1365 return 'Y-m-d H:i:s';
1366 } elseif( in_array( $format, ['human','ago','timestamp'], true ) ){
1367 return $format;
1368 }
1369
1370 if( $format === 'ago-date' ) {
1371 $diff = time() - $gmttime;
1372 if( $diff > 31536000 ) {
1373 return apply_filters( 'wpforo_date_format_ago_date_year', 'M d, y' );
1374 } elseif( $diff > 518400 ) {
1375 return apply_filters( 'wpforo_date_format_ago_date_month', 'M d' );
1376 } else {
1377 return 'ago';
1378 }
1379 }
1380
1381 $sep = ' ';
1382 if( $wp_date_format ) {
1383 $date_format = get_option( 'date_format' );
1384 $time_format = get_option( 'time_format' );
1385 } else {
1386 $date_format = 'Y-m-d';
1387 $time_format = 'H:i:s';
1388 }
1389 if( $format === 'datetime' ) {
1390 $format = $date_format . $sep . $time_format;
1391 } elseif( $format === 'date' ) {
1392 $format = $date_format;
1393 } elseif( $format === 'time' ) {
1394 $format = $time_format;
1395 } elseif( $wp_date_format ) {
1396 $format = $date_format . $sep . $time_format;
1397 }
1398
1399 return $format;
1400 }
1401
1402 /**
1403 * @param $date string | int
1404 * @param $format string
1405 * @param $timezone string
1406 *
1407 * @return string | int
1408 */
1409 function _wpforo_date( $date = 0, string $format = 'Y-m-d H:i:s', string $timezone = '' ) {
1410 if( in_array( $format, ['human','ago'], true ) ){
1411 $d = wpforo_human_time_diff( wpforo_gmtimestamp( $date ), $format === 'ago' );
1412 } else {
1413 $timestamp = wpforo_timestamp( $date, $timezone );
1414 if( $format === 'timestamp' ){
1415 $d = $timestamp;
1416 }else{
1417 if( $format === 'mysql' ) $format = 'Y-m-d H:i:s';
1418 $d = date_i18n( $format, $timestamp );
1419 }
1420 }
1421
1422 return $d;
1423 }
1424
1425 /**
1426 * @param int | string $date
1427 * @param string $format
1428 * @param bool $echo
1429 * @param bool $wp_date_format
1430 * @param string|null $timezone
1431 *
1432 * @return string | int
1433 */
1434 function wpforo_date( $date, $format = 'ago', $echo = true, $wp_date_format = true, $timezone = null ) {
1435 $wp_date_format = $wp_date_format && wpforo_setting( 'general', 'wp_date_format' );
1436 $format = wpforo_date_format( wpforo_gmtimestamp( $date ), $format, $wp_date_format );
1437
1438 if( is_null( $timezone ) ) $timezone = ( (string) wpfval( WPF()->current_user, 'timezone' ) ?: ( (string) wpfval($_COOKIE, 'wpforo_browser_timezone') ?: 'wp') );
1439 $timezone = apply_filters( 'wpforo_date_timezone', $timezone );
1440
1441 $d = _wpforo_date( $date, $format, $timezone );
1442 $d = apply_filters( 'wpforo_date', $d, $date, $format, $timezone );
1443
1444 if( $echo ) echo $d;
1445 return $d;
1446 }
1447
1448 if( ! function_exists( 'wpforo_date_raw' ) ) {
1449 /**
1450 * @param string|int $date
1451 * @param string $format
1452 * @param bool $echo
1453 * @param string|null $timezone
1454 *
1455 * @return string | int
1456 */
1457 function wpforo_date_raw( $date, $format = 'ago', $echo = true, $timezone = null ) {
1458 return wpforo_date( $date, $format, $echo, false, $timezone );
1459 }
1460 }
1461
1462 function wpforo_write_file( string $new_file, $content ): array {
1463 $return = [ 'error' => '', 'file' => '' ];
1464 $new_file = wpforo_fix_dir_sep( $new_file );
1465 $dir = dirname( $new_file );
1466 if( ! is_dir( $dir ) ) wp_mkdir_p( $dir );
1467 $ifp = @fopen( $new_file, 'wb' );
1468 if( ! $ifp ) {
1469 if( is_resource( $ifp ) ) {
1470 @fclose( $ifp );
1471 }
1472 $return = [ 'error' => sprintf( __( 'Could not write file %s' ), $new_file ) ];
1473 } else {
1474 @fwrite( $ifp, $content );
1475 fclose( $ifp );
1476 clearstatcache();
1477 // Set correct file permissions
1478 $stat = @stat( dirname( $new_file ) );
1479 $perms = $stat['mode'] & 0007777;
1480 $perms = $perms & 0000666;
1481 @chmod( $new_file, $perms );
1482 clearstatcache();
1483 $return['file'] = $new_file;
1484 }
1485
1486 return $return;
1487 }
1488
1489 function wpforo_get_file_content( $file ) {
1490 if( is_file( $file ) ) {
1491 $fp = @fopen( $file, 'r' );
1492 if( ! $fp ) {
1493 if( is_resource( $fp ) ) {
1494 @fclose( $fp );
1495 }
1496 } else {
1497 $size = @filesize( $file );
1498 if( $size > 0 ) {
1499 $file_data = fread( $fp, $size );
1500 fclose( $fp );
1501
1502 return $file_data;
1503 }
1504 @fclose( $fp );
1505 }
1506 }
1507
1508 return false;
1509 }
1510
1511 ################################################################################
1512 /**
1513 * Clears file basename and removes trailing slash
1514 *
1515 * @param string filename
1516 *
1517 * @return string
1518 * @since 1.0.0
1519 *
1520 */
1521 function wpforo_clear_basename( $file ) {
1522 $file = (string) $file;
1523 $file = str_replace( '\\', '/', $file );
1524 $file = preg_replace( '|/+|', '/', $file );
1525 $file = trim( (string) $file, '/' );
1526
1527 return $file;
1528 }
1529
1530 #################################################################################
1531 /**
1532 * Removes directory with all files and folders
1533 *
1534 * @param string directory name
1535 *
1536 * @since 1.0.0
1537 *
1538 */
1539 function _wpforo_remove_directory( $directory ) {
1540 $directory = (string) $directory;
1541 $directory = wpforo_fix_dir_sep( $directory );
1542 $directory_ns = trim( $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;
1543 $directory_ws = DIRECTORY_SEPARATOR . trim( $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;
1544 $glob = glob( $directory_ns . '*' );
1545 if( empty( $glob ) ) $glob = glob( $directory_ws . '*' );
1546 foreach( $glob as $item ) {
1547 if( is_dir( $item ) ) {
1548 _wpforo_remove_directory( $item );
1549 } else {
1550 unlink( $item );
1551 }
1552 }
1553
1554 return rmdir( $directory );
1555 }
1556
1557 #################################################################################
1558 /**
1559 * Removes directory with all files and folders
1560 *
1561 * @param string directory name
1562 *
1563 * @since 1.0.0
1564 *
1565 */
1566 function wpforo_remove_directory( $file, $recursive = true ) {
1567 if( ! class_exists( 'WP_Filesystem_Direct' ) ) {
1568 require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
1569 require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
1570 }
1571 $file = wpforo_fix_dir_sep( $file );
1572 $WP_Filesystem_Direct = new WP_Filesystem_Direct( null );
1573 return $WP_Filesystem_Direct->delete( $file, $recursive );
1574 }
1575
1576 #################################################################################
1577 /**
1578 * Converts bytes to KB, MB, GB
1579 *
1580 * @param integer Bytes
1581 *
1582 * @return string
1583 * @since 1.0.0
1584 *
1585 */
1586 function wpforo_print_size( $value, $points = true ) {
1587 if( $value < 1024 ) {
1588 return $value . ( ( $points ) ? "B" : '' );
1589 } elseif( $value >= 1024 && $value < ( 1024 * 1024 ) ) {
1590 $value = round( ( $value / 1024 ) * 10 ) / 10;
1591
1592 return $value . ( ( $points ) ? "KB" : '' );
1593 } elseif( $value >= 1024 * 1024 && $value < 1024 * 1024 * 1024 ) {
1594 $value = round( ( $value / ( 1024 * 1024 ) ) * 10 ) / 10;
1595
1596 return $value . ( ( $points ) ? "MB" : '' );
1597 } elseif( $value >= 1024 * 1024 * 1024 && $value <= 1024 * 1024 * 1024 * 1024 ) {
1598 $value = round( ( $value / ( 1024 * 1024 * 1024 ) ) * 10 ) / 10;
1599
1600 return $value . ( ( $points ) ? "GB" : '' );
1601 } else {
1602 $value = round( ( $value / ( 1024 * 1024 * 1024 * 1024 ) ) * 10 ) / 10;
1603
1604 return $value . ( ( $points ) ? "TB" : '' );
1605 }
1606 }
1607
1608 function wpforo_human_size_to_bytes( $sSize ) {
1609 if( is_numeric( $sSize ) ) return $sSize;
1610
1611 $sSuffix = substr( (string) $sSize, - 1 );
1612 $iValue = substr( (string) $sSize, 0, - 1 );
1613 switch( strtoupper( (string) $sSuffix ) ) {
1614 case 'M':
1615 $iValue *= 1024 * 1024;
1616 break;
1617 case 'K':
1618 $iValue *= 1024;
1619 break;
1620 case 'G':
1621 $iValue *= 1024 * 1024 * 1024;
1622 break;
1623 case 'T':
1624 $iValue *= 1024 * 1024 * 1024 * 1024;
1625 break;
1626 case 'P':
1627 $iValue *= 1024 * 1024 * 1024 * 1024 * 1024;
1628 break;
1629 }
1630
1631 return $iValue;
1632 }
1633
1634 function wpforo_print_number( $n, $echo = false ) {
1635 $x = str_replace( ",", "", $n );
1636 $x = intval( $x );
1637 $n = 0 + $x;
1638 $number = 0;
1639 if( ! is_numeric( $n ) ) return false;
1640 if( $n > 1000000000000 ) {
1641 $number = round( ( $n / 1000000000000 ), 1 ) . ' ' . str_replace( '{number}', '', wpforo_phrase( '{number}T', false ) );
1642 } else if( $n > 1000000000 ) {
1643 $number = round( ( $n / 1000000000 ), 1 ) . ' ' . str_replace( '{number}', '', wpforo_phrase( '{number}B', false ) );
1644 } else if( $n > 1000000 ) {
1645 $number = round( ( $n / 1000000 ), 1 ) . ' ' . str_replace( '{number}', '', wpforo_phrase( '{number}M', false ) );
1646 } else if( $n > 10000 ) $number = round( ( $n / 1000 ), 1 ) . ' ' . str_replace( '{number}', '', wpforo_phrase( '{number}K', false ) );
1647
1648 $number = ( $number ) ? $number : number_format( $n );
1649
1650 if( $echo ) {
1651 echo $number;
1652 } else {
1653 return $number;
1654 }
1655 }
1656
1657 function wpforo_bigintval( $value ) {
1658 $value = wpforo_settype( $value, 'string' );
1659 if( is_string( $value ) ) {
1660 $value = trim( $value );
1661 if( ! is_numeric( $value ) ) {
1662 $value = preg_replace( "#[^0-9].*#s", '', (string) $value );
1663 if( ! is_numeric( $value ) ) $value = 0;
1664 }
1665 } else {
1666 $value = 0;
1667 }
1668
1669 return ( strlen( (string) $value ) < strlen( (string) PHP_INT_MAX ) ) ? (int) $value : $value;
1670 }
1671
1672 function wpforo_removebb( $string ) {
1673 if( isset( $string ) && $string ) {
1674 $string = preg_replace( '|\[/*[^]\[]+]|i', '', (string) $string );
1675 }
1676
1677 return $string;
1678 }
1679
1680 function wpforo_file_upload_error( $code ): string {
1681 switch( $code ) {
1682 case UPLOAD_ERR_INI_SIZE:
1683 $message = wpforo_phrase( "The uploaded file exceeds the upload_max_filesize directive in php.ini", false );
1684 break;
1685 case UPLOAD_ERR_FORM_SIZE:
1686 $message = wpforo_phrase( "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form", false );
1687 break;
1688 case UPLOAD_ERR_PARTIAL:
1689 $message = wpforo_phrase( "The uploaded file was only partially uploaded", false );
1690 break;
1691 case UPLOAD_ERR_NO_FILE:
1692 $message = wpforo_phrase( "No file was uploaded", false );
1693 break;
1694 case UPLOAD_ERR_NO_TMP_DIR:
1695 $message = wpforo_phrase( "Missing a temporary folder", false );
1696 break;
1697 case UPLOAD_ERR_CANT_WRITE:
1698 $message = wpforo_phrase( "Failed to write file to disk", false );
1699 break;
1700 case UPLOAD_ERR_EXTENSION:
1701 $message = wpforo_phrase( "File upload stopped by extension", false );
1702 break;
1703 default:
1704 $message = wpforo_phrase( "Unknown upload error", false ) . ": " . $code;
1705 break;
1706 }
1707
1708 return $message;
1709 }
1710
1711 //$key allowed values are post, strip, data, user_description entities or the name of a field filter such as pre_user_description.
1712 //More info https://core.trac.wordpress.org/browser/tags/4.5.2/src/wp-includes/kses.php#L624
1713 function wpforo_kses( $string = '', $key = 'post' ) {
1714 if( ! $string || ! $key ) return $string;
1715 if( $key === 'email' ) {
1716 if( ! preg_match( '#<p[^<>]*?>#iu', (string) $string ) ) $string = wpautop( $string );
1717 $allowed_html = [
1718 'a' => [ 'href' => true, 'title' => true, 'style' => true ],
1719 'img' => [
1720 'src' => true,
1721 'width' => true,
1722 'height' => true,
1723 'align' => true,
1724 'alt' => true,
1725 'style' => true,
1726 ],
1727 'blockquote' => [ 'style' => true ],
1728 'h1' => [ 'style' => true ],
1729 'h2' => [ 'style' => true ],
1730 'h3' => [ 'style' => true ],
1731 'h4' => [ 'style' => true ],
1732 'h5' => [ 'style' => true ],
1733 'h6' => [ 'style' => true ],
1734 'hr' => [],
1735 'br' => [],
1736 'p' => [ 'style' => true ],
1737 'strong' => [ 'style' => true ],
1738 'em' => [ 'style' => true ],
1739 'del' => [ 'style' => true ],
1740 'style' => [],
1741 ];
1742 $allowed_html = apply_filters( 'wpforo_kses_allowed_html_email', $allowed_html );
1743 } elseif( $key === 'user_description' ) {
1744 $allowed_html = wp_kses_allowed_html( $key );
1745 $allowed_html['img'] = [ 'alt' => true, 'align' => true, 'border' => true, 'height' => true, 'hspace' => true, 'longdesc' => true, 'vspace' => true, 'src' => true, 'usemap' => true, 'width' => true ];
1746 $allowed_html['br'] = [];
1747 $allowed_html = apply_filters( 'wpforo_kses_allowed_html_user_description', $allowed_html );
1748 } elseif( $key === 'svg' ){
1749 $allowed_tags = [
1750 // HTML
1751 'a',
1752 'font',
1753 'image',
1754 'style',
1755
1756 // SVG
1757 'svg',
1758 'altglyph',
1759 'altglyphdef',
1760 'altglyphitem',
1761 'animatecolor',
1762 'animatemotion',
1763 'animatetransform',
1764 'circle',
1765 'clippath',
1766 'defs',
1767 'desc',
1768 'ellipse',
1769 'filter',
1770 'font',
1771 'g',
1772 'glyph',
1773 'glyphref',
1774 'hkern',
1775 'image',
1776 'line',
1777 'lineargradient',
1778 'marker',
1779 'mask',
1780 'metadata',
1781 'mpath',
1782 'path',
1783 'pattern',
1784 'polygon',
1785 'polyline',
1786 'radialgradient',
1787 'rect',
1788 'stop',
1789 'switch',
1790 'symbol',
1791 'text',
1792 'textpath',
1793 'title',
1794 'tref',
1795 'tspan',
1796 'use',
1797 'view',
1798 'vkern',
1799
1800 // SVG Filters
1801 'feBlend',
1802 'feColorMatrix',
1803 'feComponentTransfer',
1804 'feComposite',
1805 'feConvolveMatrix',
1806 'feDiffuseLighting',
1807 'feDisplacementMap',
1808 'feDistantLight',
1809 'feFlood',
1810 'feFuncA',
1811 'feFuncB',
1812 'feFuncG',
1813 'feFuncR',
1814 'feGaussianBlur',
1815 'feMerge',
1816 'feMergeNode',
1817 'feMorphology',
1818 'feOffset',
1819 'fePointLight',
1820 'feSpecularLighting',
1821 'feSpotLight',
1822 'feTile',
1823 'feTurbulence',
1824
1825 //text
1826 '#text'
1827 ];
1828 $allowed_attrs = [
1829 // HTML
1830 'about',
1831 'accept',
1832 'action',
1833 'align',
1834 'alt',
1835 'autocomplete',
1836 'background',
1837 'bgcolor',
1838 'border',
1839 'cellpadding',
1840 'cellspacing',
1841 'checked',
1842 'cite',
1843 'class',
1844 'clear',
1845 'color',
1846 'cols',
1847 'colspan',
1848 'coords',
1849 'crossorigin',
1850 'datetime',
1851 'default',
1852 'dir',
1853 'disabled',
1854 'download',
1855 'enctype',
1856 'encoding',
1857 'face',
1858 'for',
1859 'headers',
1860 'height',
1861 'hidden',
1862 'high',
1863 'href',
1864 'hreflang',
1865 'id',
1866 'integrity',
1867 'ismap',
1868 'label',
1869 'lang',
1870 'list',
1871 'loop',
1872 'low',
1873 'max',
1874 'maxlength',
1875 'media',
1876 'method',
1877 'min',
1878 'multiple',
1879 'name',
1880 'noshade',
1881 'novalidate',
1882 'nowrap',
1883 'open',
1884 'optimum',
1885 'pattern',
1886 'placeholder',
1887 'poster',
1888 'preload',
1889 'pubdate',
1890 'radiogroup',
1891 'readonly',
1892 'rel',
1893 'required',
1894 'rev',
1895 'reversed',
1896 'role',
1897 'rows',
1898 'rowspan',
1899 'spellcheck',
1900 'scope',
1901 'selected',
1902 'shape',
1903 'size',
1904 'sizes',
1905 'span',
1906 'srclang',
1907 'start',
1908 'src',
1909 'srcset',
1910 'step',
1911 'style',
1912 'summary',
1913 'tabindex',
1914 'title',
1915 'type',
1916 'usemap',
1917 'valign',
1918 'value',
1919 'version',
1920 'width',
1921 'xmlns',
1922
1923 // SVG
1924 'accent-height',
1925 'accumulate',
1926 'additivive',
1927 'alignment-baseline',
1928 'ascent',
1929 'attributename',
1930 'attributetype',
1931 'azimuth',
1932 'basefrequency',
1933 'baseline-shift',
1934 'begin',
1935 'bias',
1936 'by',
1937 'class',
1938 'clip',
1939 'clip-path',
1940 'clip-rule',
1941 'color',
1942 'color-interpolation',
1943 'color-interpolation-filters',
1944 'color-profile',
1945 'color-rendering',
1946 'cx',
1947 'cy',
1948 'd',
1949 'dx',
1950 'dy',
1951 'diffuseconstant',
1952 'direction',
1953 'display',
1954 'divisor',
1955 'dur',
1956 'edgemode',
1957 'elevation',
1958 'end',
1959 'fill',
1960 'fill-opacity',
1961 'fill-rule',
1962 'filter',
1963 'filterUnits',
1964 'flood-color',
1965 'flood-opacity',
1966 'font-family',
1967 'font-size',
1968 'font-size-adjust',
1969 'font-stretch',
1970 'font-style',
1971 'font-variant',
1972 'font-weight',
1973 'fx',
1974 'fy',
1975 'g1',
1976 'g2',
1977 'glyph-name',
1978 'glyphref',
1979 'gradientunits',
1980 'gradienttransform',
1981 'height',
1982 'href',
1983 'id',
1984 'image-rendering',
1985 'in',
1986 'in2',
1987 'k',
1988 'k1',
1989 'k2',
1990 'k3',
1991 'k4',
1992 'kerning',
1993 'keypoints',
1994 'keysplines',
1995 'keytimes',
1996 'lang',
1997 'lengthadjust',
1998 'letter-spacing',
1999 'kernelmatrix',
2000 'kernelunitlength',
2001 'lighting-color',
2002 'local',
2003 'marker-end',
2004 'marker-mid',
2005 'marker-start',
2006 'markerheight',
2007 'markerunits',
2008 'markerwidth',
2009 'maskcontentunits',
2010 'maskunits',
2011 'max',
2012 'mask',
2013 'media',
2014 'method',
2015 'mode',
2016 'min',
2017 'name',
2018 'numoctaves',
2019 'offset',
2020 'operator',
2021 'opacity',
2022 'order',
2023 'orient',
2024 'orientation',
2025 'origin',
2026 'overflow',
2027 'paint-order',
2028 'path',
2029 'pathlength',
2030 'patterncontentunits',
2031 'patterntransform',
2032 'patternunits',
2033 'points',
2034 'preservealpha',
2035 'preserveaspectratio',
2036 'r',
2037 'rx',
2038 'ry',
2039 'radius',
2040 'refx',
2041 'refy',
2042 'repeatcount',
2043 'repeatdur',
2044 'restart',
2045 'result',
2046 'rotate',
2047 'scale',
2048 'seed',
2049 'shape-rendering',
2050 'specularconstant',
2051 'specularexponent',
2052 'spreadmethod',
2053 'stddeviation',
2054 'stitchtiles',
2055 'stop-color',
2056 'stop-opacity',
2057 'stroke-dasharray',
2058 'stroke-dashoffset',
2059 'stroke-linecap',
2060 'stroke-linejoin',
2061 'stroke-miterlimit',
2062 'stroke-opacity',
2063 'stroke',
2064 'stroke-width',
2065 'style',
2066 'surfacescale',
2067 'tabindex',
2068 'targetx',
2069 'targety',
2070 'transform',
2071 'text-anchor',
2072 'text-decoration',
2073 'text-rendering',
2074 'textlength',
2075 'type',
2076 'u1',
2077 'u2',
2078 'unicode',
2079 'values',
2080 'viewbox',
2081 'visibility',
2082 'vector-effect',
2083 'vert-adv-y',
2084 'vert-origin-x',
2085 'vert-origin-y',
2086 'width',
2087 'word-spacing',
2088 'wrap',
2089 'writing-mode',
2090 'xchannelselector',
2091 'ychannelselector',
2092 'x',
2093 'x1',
2094 'x2',
2095 'xmlns',
2096 'y',
2097 'y1',
2098 'y2',
2099 'z',
2100 'zoomandpan',
2101
2102 // MathML
2103 'accent',
2104 'accentunder',
2105 'align',
2106 'bevelled',
2107 'close',
2108 'columnsalign',
2109 'columnlines',
2110 'columnspan',
2111 'denomalign',
2112 'depth',
2113 'dir',
2114 'display',
2115 'displaystyle',
2116 'fence',
2117 'frame',
2118 'height',
2119 'href',
2120 'id',
2121 'largeop',
2122 'length',
2123 'linethickness',
2124 'lspace',
2125 'lquote',
2126 'mathbackground',
2127 'mathcolor',
2128 'mathsize',
2129 'mathvariant',
2130 'maxsize',
2131 'minsize',
2132 'movablelimits',
2133 'notation',
2134 'numalign',
2135 'open',
2136 'rowalign',
2137 'rowlines',
2138 'rowspacing',
2139 'rowspan',
2140 'rspace',
2141 'rquote',
2142 'scriptlevel',
2143 'scriptminsize',
2144 'scriptsizemultiplier',
2145 'selection',
2146 'separator',
2147 'separators',
2148 'slope',
2149 'stretchy',
2150 'subscriptshift',
2151 'supscriptshift',
2152 'symmetric',
2153 'voffset',
2154 'width',
2155 'xmlns',
2156
2157 // XML
2158 'xlink:href',
2159 'xml:id',
2160 'xlink:title',
2161 'xml:space',
2162 'xmlns:xlink',
2163 ];
2164 $attrs = array_map( '__return_true', array_flip( $allowed_attrs ) );
2165 $allowed_html = array_map( function() use ($attrs){
2166 return $attrs;
2167 }, array_flip( $allowed_tags ) );
2168 } else {
2169 global $allowedposttags;
2170 $allowed_html = $allowedposttags;
2171 if( wpforo_setting( 'posting', 'content_do_shortcode' ) ) {
2172 $allowed_html = wp_kses_allowed_html( $key );
2173 }
2174 $extra_html = wpforo_setting( 'posting', 'extra_html_tags' );
2175 $allowed_html = wpforo_extra_html_parser( $extra_html, $allowed_html );
2176 $allowed_html['a']['data-gallery'] = true;
2177 $allowed_html['a']['download'] = true;
2178 $allowed_html['blockquote']['class'] = true;
2179 $allowed_html['blockquote']['data-width'] = true;
2180 $allowed_html['blockquote']['data-userid'] = true;
2181 $allowed_html['blockquote']['data-postid'] = true;
2182 $allowed_html['p']['lang'] = true;
2183 $allowed_html['p']['dir'] = true;
2184 $allowed_html['pre']['contenteditable'] = true;
2185 if( ! wpfval( $allowed_html, 'iframe' ) && class_exists( 'wpForoEmbeds' ) ) {
2186 $allowed_html['iframe'] = [ 'width' => true, 'height' => true, 'src' => true, 'frameborder' => true, 'allowfullscreen' => true ];
2187 }
2188 $allowed_html = apply_filters( 'wpforo_kses_allowed_html', $allowed_html );
2189 }
2190
2191 return wp_kses( $string, $allowed_html );
2192 }
2193
2194 function wpforo_deep_merge( $default, $current = [] ) {
2195 foreach( $default as $k => $v ) {
2196 if( ! empty( $v ) && is_array( $v ) ) {
2197 foreach( $v as $kk => $vv ) {
2198 if( ! empty( $vv ) && is_array( $vv ) ) {
2199 foreach( $vv as $kkk => $vvv ) {
2200 if( ! empty( $vvv ) && is_array( $vvv ) ) {
2201 foreach( $vvv as $kkkk => $vvvv ) {
2202 if( ! empty( $vvv ) && is_array( $vvv ) ) {
2203 //Stop on 5th level
2204 } else {
2205 if( isset( $current[ $k ][ $kk ][ $kkk ][ $kkkk ] ) ) $default[ $k ][ $kk ][ $kkk ][ $kkkk ] = $current[ $k ][ $kk ][ $kkk ][ $kkkk ];
2206 }
2207 }
2208 } else {
2209 if( isset( $current[ $k ][ $kk ][ $kkk ] ) ) $default[ $k ][ $kk ][ $kkk ] = $current[ $k ][ $kk ][ $kkk ];
2210 }
2211 }
2212 } else {
2213 if( isset( $current[ $k ][ $kk ] ) ) $default[ $k ][ $kk ] = $current[ $k ][ $kk ];
2214 }
2215 }
2216 } else {
2217 if( isset( $current[ $k ] ) ) $default[ $k ] = $current[ $k ];
2218 }
2219 }
2220
2221 return $default;
2222 }
2223
2224 function wpforo_is_image( $e ) {
2225 return (bool) preg_match( '#^(jpe?g|png|gif|bmp|webp|svg|tiff?)$#i', (string) $e );
2226 }
2227
2228 function wpforo_is_video( $e ) {
2229 return (bool) preg_match( '#^(mp4|webm|ogg)$#i', (string) $e );
2230 }
2231
2232 function wpforo_is_audio( $e ) {
2233 return (bool) preg_match( '#^(mp3|wav|ogg)$#i', (string) $e );
2234 }
2235
2236 /**
2237 * @param string $option
2238 * @param mixed $default
2239 * @param bool $cache
2240 *
2241 * @return mixed
2242 */
2243 function wpforo_get_option( $option, $default = null, $cache = true ) {
2244 if( strpos( (string) $option, 'wpforo_' ) !== 0 ) {
2245 $option = wpforo_prefix( $option );
2246 }
2247
2248 $from_cache = false;
2249 $value = null;
2250
2251 $cache = ( $cache && apply_filters( 'wpforo_options_cache', true ) );
2252
2253 if( $cache ) {
2254 $option_file = WPF()->folders['cache']['dir'] . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'option' . DIRECTORY_SEPARATOR . ( defined( 'AUTH_KEY' ) ? md5( $option . AUTH_KEY ) : md5( $option ) );
2255 $value = maybe_unserialize( wpforo_get_file_content( $option_file ) );
2256 if( ! empty( $value ) ) $from_cache = true;
2257 }
2258
2259 if( ! $from_cache ) $value = get_option( $option );
2260
2261 if( $value === false ) {
2262 $value = $default;
2263 } else {
2264 if( preg_match( '#_(?:general|profiles|rating|tools_misc)$#iu', (string) $option ) ) {
2265 foreach( [ 'title_groupids', 'title_secondary_groupids', 'rating_title_ug', 'rating_badge_ug', 'admin_bar', 'admin_note_groups', 'admin_note_pages' ] as $k ) {
2266 if( wpfkey( $default, $k ) ) $default[ $k ] = [];
2267 }
2268 }
2269 $default = apply_filters( 'wpforo_get_option_default_arg_before_cast', $default, $option );
2270
2271 $value = wpforo_settype( $value, gettype( $default ) );
2272 if( $default && is_array( $default ) && is_array( $value ) ) $value = wpforo_array_args_cast_and_merge( $value, $default );
2273 }
2274
2275 if( $cache && ! $from_cache && ! empty( $value ) ) wpforo_write_file( $option_file, maybe_serialize( $value ) );
2276
2277 return apply_filters( 'wpforo_get_option', $value, $option, $default, $cache );
2278 }
2279
2280 /**
2281 * calls the WordPress core update_option() function
2282 *
2283 * @param string $option Name of the option to update. Expected to not be SQL-escaped.
2284 * @param mixed $value Option value. Must be serializable if non-scalar. Expected to not be SQL-escaped.
2285 * @param string|bool $autoload Optional. Whether to load the option when WordPress starts up. For existing options,
2286 * `$autoload` can only be updated using `update_option()` if `$value` is also changed.
2287 * Accepts 'yes'|true to enable or 'no'|false to disable. For non-existent options,
2288 * the default value is 'yes'. Default null.
2289 *
2290 * @return bool True if the value was updated, false otherwise.
2291 * @since 2.0.0
2292 */
2293 function wpforo_update_option( $option, $value, $autoload = null ) {
2294 if( strpos( (string) $option, 'wpforo_' ) !== 0 ) {
2295 $option = wpforo_prefix( $option );
2296 }
2297 $ret = update_option( $option, $value, $autoload );
2298 do_action( 'wpforo_update_option', $option, $value, $autoload );
2299 wpforo_clean_cache( 'option' );
2300
2301 return $ret;
2302 }
2303
2304 /**
2305 * calls the WordPress core delete_option() function
2306 *
2307 * @param string $option Name of the option to update. Expected to not be SQL-escaped.
2308 *
2309 * @return bool True if the value was updated, false otherwise.
2310 * @since 2.0.0
2311 */
2312 function wpforo_delete_option( $option ) {
2313 if( strpos( (string) $option, 'wpforo_' ) !== 0 ) {
2314 $option = wpforo_prefix( $option );
2315 }
2316 $ret = delete_option( $option );
2317 do_action( 'wpforo_delete_option', $option );
2318 wpforo_clean_cache( 'option' );
2319
2320 return $ret;
2321 }
2322
2323 /**
2324 * Extract what remains from an unintentionally truncated serialized string
2325 * $data contains your original array (or what remains of it).
2326 *
2327 * @param string The serialized array
2328 */
2329 function wpforo_fixSerializedArray( $serialized ) {
2330 $tmp = preg_replace( '/^a:\d+:\{/', '', (string) $serialized );
2331
2332 return wpforo_fixSerializedArray_R( $tmp );
2333 }
2334
2335 /**
2336 * The recursive function that does all the heavy lifing. Do not call directly.
2337 *
2338 * @param string The broken serialzized array
2339 *
2340 * @return array Returns the repaired string
2341 */
2342 function wpforo_fixSerializedArray_R( &$broken ) {
2343 $data = [];
2344 $index = null;
2345 $len = strlen( (string) $broken );
2346 $i = 0;
2347 while( strlen( (string) $broken ) ) {
2348 $i ++;
2349 if( $i > $len ) {
2350 break;
2351 }
2352 if( substr( (string) $broken, 0, 1 ) == '}' ) {
2353 $broken = substr( (string) $broken, 1 );
2354
2355 return $data;
2356 } else {
2357 $bite = substr( (string) $broken, 0, 2 );
2358 switch( $bite ) {
2359 case 's:':
2360 $re = '/^s:\d+:"([^\"]*)";/';
2361 if( preg_match( $re, (string) $broken, $m ) ) {
2362 if( $index === null ) {
2363 $index = $m[1];
2364 } else {
2365 $data[ $index ] = $m[1];
2366 $index = null;
2367 }
2368 $broken = preg_replace( $re, '', (string) $broken );
2369 }
2370 break;
2371 case 'i:':
2372 $re = '/^i:(\d+);/';
2373 if( preg_match( $re, (string) $broken, $m ) ) {
2374 if( $index === null ) {
2375 $index = (int) $m[1];
2376 } else {
2377 $data[ $index ] = (int) $m[1];
2378 $index = null;
2379 }
2380 $broken = preg_replace( $re, '', (string) $broken );
2381 }
2382 break;
2383 case 'b:':
2384 $re = '/^b:[01];/';
2385 if( preg_match( $re, (string) $broken, $m ) ) {
2386 $data[ $index ] = (bool) $m[1];
2387 $index = null;
2388 $broken = preg_replace( $re, '', (string) $broken );
2389 }
2390 break;
2391 case 'a:':
2392 $re = '/^a:\d+:\{/';
2393 if( preg_match( $re, (string) $broken, $m ) ) {
2394 $broken = preg_replace( '/^a:\d+:\{/', '', (string) $broken );
2395 $data[ $index ] = wpforo_fixSerializedArray_R( $broken );
2396 $index = null;
2397 }
2398 break;
2399 case 'N;':
2400 $broken = substr( (string) $broken, 2 );
2401 $data[ $index ] = null;
2402 $index = null;
2403 break;
2404 }
2405 }
2406 }
2407
2408 return $data;
2409 }
2410
2411 function wpforo_insert_to_media_library( $attach_path, $title = '' ) {
2412 if( wpforo_setting( 'posting', 'attachs_to_medialib' ) ) {
2413 if( ! $attach_path ) return 0;
2414 $attach_fname = basename( (string) $attach_path );
2415 if( ! $title ) $title = $attach_fname;
2416 require_once( ABSPATH . 'wp-admin/includes/media.php' );
2417 require_once( ABSPATH . 'wp-admin/includes/file.php' );
2418 require_once( ABSPATH . 'wp-admin/includes/image.php' );
2419 $filetype = wp_check_filetype( $attach_fname, null );
2420 $attachment = [ 'guid' => $attach_path, 'post_mime_type' => $filetype['type'], 'post_title' => $title, 'post_content' => '', 'post_status' => 'inherit' ];
2421 $attach_id = wp_insert_attachment( $attachment, $attach_path );
2422 add_filter( 'intermediate_image_sizes', 'wpforo_attachment_sizes' );
2423 $attach_data = wp_generate_attachment_metadata( $attach_id, $attach_path );
2424 wp_update_attachment_metadata( $attach_id, $attach_data );
2425 remove_filter( 'intermediate_image_sizes', 'wpforo_attachment_sizes' );
2426
2427 return $attach_id;
2428 }
2429
2430 return 0;
2431 }
2432
2433 function wpforo_attachment_sizes( $sizes ) {
2434 return [ 'thumbnail' ];
2435 }
2436
2437 #################################################################################
2438 /**
2439 * Cleans forum cache
2440 *
2441 * @param string Item View / Template (e.g.: 'forum', 'topic', 'post', 'user', 'widget', etc...)
2442 * @param integer Item ID (e.g.: $topicid or $postid) | (!) ID is 0 on dome actions (e.g.: delete actions)
2443 * @param array Item data as array
2444 *
2445 * @since 1.2.1
2446 *
2447 */
2448 function wpforo_clean_cache( $template = 'all', $id = 0, $item = [] ) {
2449 if( is_null(WPF()->board) ) return;
2450 do_action( 'wpforo_clean_cache_start', $id, $template );
2451
2452 if( ! $pageid = WPF()->board->get_current( 'pageid' ) ) $pageid = wpforo_wp_url_to_postid( $_SERVER['REQUEST_URI'] );
2453 if( $pageid ) clean_post_cache( $pageid );
2454
2455 do_action( 'wpforo_clean_cache', $id, $template );
2456 WPF()->cache->clean( $id, $template, $item );
2457 if( $template !== 'option' ) WPF()->statistic_cache_clean();
2458 do_action( 'wpforo_clean_cache_end', $id, $template );
2459 }
2460
2461 function wpforo_is_owner( $userid, $email = '' ) {
2462 return wpforo_is_users_same( ['userid' => $userid, 'user_email' => $email] );
2463 }
2464
2465 function wpforo_is_users_same( $user1, $user2 = null ){
2466 if( is_null( $user2 ) ) {
2467 $user2 = WPF()->current_user;
2468 }elseif( is_scalar( $user2 ) ){
2469 if( is_numeric( $user2 ) ){
2470 $user2 = [ 'userid' => wpforo_bigintval( $user2 ), 'user_email' => wpforo_member( $user2, 'user_email' ) ];
2471 }else{
2472 $user2 = [ 'userid' => 0, 'user_email' => sanitize_email( $user2 ) ];
2473 }
2474 }
2475
2476 if( is_scalar( $user1 ) ){
2477 if( is_numeric( $user1 ) ){
2478 $user1 = [ 'userid' => wpforo_bigintval( $user1 ), 'user_email' => wpforo_member( $user1, 'user_email' ) ];
2479 }else{
2480 $user1 = [ 'userid' => 0, 'user_email' => sanitize_email( $user1 ) ];
2481 }
2482 }
2483
2484 if( wpforo_bigintval( wpfval($user2, 'userid') ) && ($userid = wpforo_bigintval( wpfval( $user1, 'userid' ) )) ){
2485 return $userid === $user2['userid'];
2486 }elseif( wpfval($user2, 'user_email') && ($user_email = sanitize_email( wpfval( $user1, 'user_email' ) )) ){
2487 return $user_email === $user2['user_email'];
2488 }
2489
2490 return false;
2491 }
2492
2493 function wpforo_dashboard_is_owner(){
2494 return (
2495 preg_match( '#/profile\.php$#iu', $_SERVER['REQUEST_URI'])
2496 || (
2497 preg_match( '#/user-edit\.php#iu', $_SERVER['REQUEST_URI'] )
2498 && (int)wpfval($_GET, 'user_id' ) === WPF()->current_userid
2499 )
2500 );
2501 }
2502
2503 /**
2504 * @param array $user wpforo user array
2505 * @param bool $echo
2506 *
2507 * @return string
2508 */
2509 function wpforo_user_dname( $user, $echo = false ) {
2510 $display_name = trim( (string) wpfval( $user, 'display_name' ) );
2511 $user_nicename = trim( (string) wpfval( $user, 'user_nicename' ) );
2512 $dname = esc_html( $display_name ) ?: ( esc_html( urldecode( $user_nicename ) ) ?: wpforo_phrase( 'Anonymous', false ) );
2513 $dname = apply_filters( 'wpforo_user_display_name', $dname, $user );
2514 if( $echo ) echo $dname;
2515 return $dname;
2516 }
2517
2518 function wpforo_strlen( $string ) {
2519 if( ! $string ) return 0;
2520 if( function_exists( 'mb_strlen' ) ) {
2521 return mb_strlen( (string) $string );
2522 } else {
2523 return strlen( (string) $string );
2524 }
2525 }
2526
2527 function wpforo_string2array( $string, $regexp = '' ) {
2528 if( ! $regexp ) $regexp = '#' . preg_quote( PHP_EOL ) . '#isu';
2529 $array = preg_split( $regexp, $string );
2530
2531 return array_filter( $array );
2532 }
2533
2534 function wpforo_array_ordered_intersect_key( $array1, $array2 ) {
2535 $new_array = [];
2536 foreach( $array2 as $key => $value ) if( wpfkey( $array1, $key ) ) $new_array[ $key ] = $array1[ $key ];
2537
2538 return $new_array;
2539 }
2540
2541 function wpforo_get_upload_dir_folders() {
2542 return (array) array_diff( scandir( WPF()->folders['upload']['dir'] ), [ '.', '..' ] );
2543 }
2544
2545 function wpforo_fix_upload_dir( $upload_dir ) {
2546 $folders = wpforo_ram_get( 'wpforo_get_upload_dir_folders' );
2547 $folders = array_map( 'preg_quote', $folders );
2548 if( $folders && preg_match( '#[/\\\]wpforo(?:_\d+)?[/\\\](?:' . implode( '|', $folders ) . ')[/\\\].+?$#iu', (string) $upload_dir, $match ) ) {
2549 $upload_dir = wpforo_fix_dir_sep( WPF()->folders['wp_upload']['dir'] . $match[0] );
2550 $upload_dir = urldecode( (string) $upload_dir );
2551 }
2552
2553 return $upload_dir;
2554 }
2555
2556 function wpforo_fix_upload_url( $upload_url ) {
2557 $folders = wpforo_ram_get( 'wpforo_get_upload_dir_folders' );
2558 $folders = array_map( 'preg_quote', $folders );
2559 if( $folders && preg_match( '#[/\\\]wpforo(?:_\d+)?[/\\\](?:' . implode( '|', $folders ) . ')[/\\\].+?$#iu', (string) $upload_url, $match ) ) {
2560 $upload_url = wpforo_fix_url_sep( WPF()->folders['wp_upload']['url'] . $match[0] );
2561 }
2562
2563 return $upload_url;
2564 }
2565
2566 function wpforo_xcopy( $source, $dest ) {
2567 // Check for symlinks
2568 if( is_link( $source ) ) {
2569 return symlink( readlink( $source ), $dest );
2570 }
2571
2572 // Simple copy for a file
2573 if( is_file( $source ) ) {
2574 return copy( $source, $dest );
2575 }
2576
2577 // Make destination directory
2578 if( ! is_dir( $dest ) ) {
2579 wp_mkdir_p( $dest );
2580 }
2581
2582 // Loop through the folder
2583 $dir = dir( $source );
2584 while( false !== $entry = $dir->read() ) {
2585 // Skip pointers
2586 if( $entry === '.' || $entry === '..' ) {
2587 continue;
2588 }
2589
2590 // Deep copy directories
2591 wpforo_xcopy( rtrim( (string) $source, '/' ) . "/$entry", rtrim( (string) $dest, '/' ) . "/$entry" );
2592 }
2593
2594 // Clean up
2595 $dir->close();
2596
2597 return true;
2598 }
2599
2600 function wpforo_printf_array( $format, $arr ) {
2601 return call_user_func_array( 'printf', array_merge( (array) $format, (array) $arr ) );
2602 }
2603
2604 function wpforo_sprintf_array( $format, $arr ) {
2605 return call_user_func_array( 'sprintf', array_merge( (array) $format, (array) $arr ) );
2606 }
2607
2608 function wpforo_avatar_url( $avatar_html ) {
2609 if( preg_match( '#src=[\'"]([^\'"]+?)[\'"]#iu', (string) $avatar_html, $matches ) ) {
2610 return $matches[1];
2611 }
2612
2613 return '';
2614 }
2615
2616 /**
2617 * @param string $content
2618 * @param bool $first
2619 * @param string $type
2620 *
2621 * @return string|array
2622 */
2623 function wpforo_find_image_urls( $content, $first = true, $type = 'general' ) {
2624 $images = [];
2625 $content = trim( (string) $content );
2626
2627 if( $content ) {
2628 if( preg_match_all( '#<img[^<>]*?src=[\'\"]([^\'\"]+\.(?:jpe?g|png|gif|bmp|webp|svg|tiff))[\'\"][^<>]*?>#iu', (string) $content, $matches, PREG_SET_ORDER ) ) {
2629 foreach( $matches as $match ) {
2630 if( preg_match( '#class=[\'\"]wpfem[^\'\"]*[\'\"][^<>]*?data-code=#isu', $match[0] ) ) continue;
2631 if( strpos( (string) $match[1], 'http' ) === 0 ) {
2632 $images[] = $match[1];
2633 } else {
2634 $images[] = 'http' . ( is_ssl() ? 's' : '' ) . ':' . $match[1];
2635 }
2636 }
2637 } elseif( preg_match_all( '#https?://[^\r\n\t\s\'\"<>]+?\.(?:jpe?g|png|gif|bmp|webp|svg|tiff)#iu', (string) $content, $matches, PREG_SET_ORDER ) ) {
2638 foreach( $matches as $match ) {
2639 $images[] = $match[0];
2640 }
2641 } elseif( preg_match_all( '#//[^\r\n\t\s\'\"<>]+?\.(?:jpe?g|png|gif|bmp|webp|svg|tiff)#iu', (string) $content, $matches, PREG_SET_ORDER ) ) {
2642 foreach( $matches as $match ) {
2643 $images[] = 'http' . ( is_ssl() ? 's' : '' ) . ':' . $match[0];
2644 }
2645 }
2646 }
2647
2648 if( $first && $images ) $images = wpfval( $images, 0 );
2649
2650 return apply_filters( 'wpforo_find_image_url', $images, $type, $first );
2651 }
2652
2653 function wpforo_is_json( $string ) {
2654 if( is_scalar( $string ) ) {
2655 json_decode( $string );
2656
2657 return json_last_error() === JSON_ERROR_NONE;
2658 }
2659
2660 return false;
2661 }
2662
2663 function wpforo_ajax_response( $message ) {
2664 wp_send_json( $message );
2665 die();
2666 }
2667
2668 function wpforo_get_fb_user( $user ) {
2669 if( is_user_logged_in() ) return wp_get_current_user();
2670 $user_data = get_user_by( 'email', $user['user_email'] );
2671 if( ! $user_data ) {
2672 $users = get_users( [ 'meta_key' => '_fb_user_id', 'meta_value' => $user['fb_user_id'], 'number' => 1, 'count_total' => false ] );
2673 if( is_array( $users ) ) $user_data = reset( $users );
2674 }
2675
2676 return $user_data;
2677 }
2678
2679 function wpforo_unique_username( $username ) {
2680 static $i;
2681 if( ! $username ) $username = 'user_' . uniqid();
2682 if( strpos( (string) $username, '@' ) !== false ) {
2683 $parts = explode( "@", $username );
2684 if( ! empty( $parts ) && isset( $parts[0] ) && $parts[0] ) {
2685 $username = $parts[0];
2686 } else {
2687 $username = str_replace( '@', '', $username );
2688 }
2689 }
2690 if( null === $i ) {
2691 $i = 1;
2692 } else {
2693 $i ++;
2694 }
2695 if( ! username_exists( $username ) ) {
2696 return $username;
2697 }
2698 $new_username = sprintf( '%s-%s', $username, $i );
2699 if( ! username_exists( $new_username ) ) {
2700 return $new_username;
2701 } else {
2702 return call_user_func( __FUNCTION__, $username );
2703 }
2704 }
2705
2706 function wpforo_is_session_started() {
2707 if( php_sapi_name() !== 'cli' ) {
2708 if( version_compare( phpversion(), '5.4.0', '>=' ) ) {
2709 return session_status() === PHP_SESSION_ACTIVE ? true : false;
2710 } else {
2711 return session_id() === '' ? false : true;
2712 }
2713 }
2714
2715 return false;
2716 }
2717
2718 function wpforo_current_guest( $email ) {
2719 $guest = WPF()->member->get_guest_cookies();
2720 if( ! wpfval( $guest, 'email' ) || ! $guest['email'] ) return false;
2721 if( $email == $guest['email'] ) {
2722 return true;
2723 } else {
2724 return false;
2725 }
2726 }
2727
2728 function wpforo_extra_html_parser( $extra_html = '', $allowed_html = [] ) {
2729 if( $extra_html ) {
2730 $extra_html = explode( ',', $extra_html );
2731 $extra_html = array_filter( $extra_html );
2732 if( ! empty( $extra_html ) ) {
2733 foreach( $extra_html as $html ) {
2734 $html = trim( (string) $html );
2735 if( preg_match( '|([^()]+)\((.+)\)|', $html, $item ) ) {
2736 if( wpfval( $item, 1 ) && wpfval( $item, 2 ) ) {
2737 $attrs = explode( ' ', $item[2] );
2738 $attrs = array_map( 'trim', $attrs );
2739 foreach( $attrs as $attr ) {
2740 $allowed_html[ $item[1] ][ $attr ] = [];
2741 }
2742 }
2743 } else {
2744 $allowed_html[ $html ] = [];
2745 }
2746 }
2747 }
2748 }
2749
2750 return $allowed_html;
2751 }
2752
2753 function wpforo_clear_array( $array, $clear = [], $by = 'value' ) {
2754 if( is_array( $clear ) && ! empty( $clear ) ) {
2755 foreach( $clear as $ext ) {
2756 if( $by == 'value' ) {
2757 if( ( $key = array_search( $ext, $array ) ) !== false ) {
2758 unset( $array[ $key ] );
2759 }
2760 } elseif( $by == 'key' ) {
2761 if( wpfkey( $array, $ext ) ) unset( $array[ $ext ] );
2762 }
2763 }
2764 } elseif( is_string( $clear ) || is_numeric( $clear ) ) {
2765 if( wpfval( $array, $clear ) ) unset( $array[ $clear ] );
2766 }
2767
2768 return $array;
2769 }
2770
2771 function wpforo_key( $array = [], $value = '', $type = 'default' ) {
2772 $keys = [];
2773 if( is_array( $array ) && ! empty( $array ) ) {
2774 foreach( $array as $k => $v ) {
2775 if( $v === $value ) {
2776 $keys[] = $k;
2777 }
2778 }
2779 }
2780 if( $type === 'sort' ) {
2781 sort( $keys );
2782
2783 return $keys;
2784 } else {
2785 return $keys;
2786 }
2787 }
2788
2789 function wpforo_unslashe( $data ) {
2790 $data = is_array( $data ) ? array_map( 'wpforo_unslashe', $data ) : stripslashes( (string) $data );
2791
2792 return $data;
2793 }
2794
2795 function wpforo_encode( $data ) {
2796 $data = is_array( $data ) ? array_map( 'wpforo_encode', $data ) : htmlspecialchars( $data, ENT_QUOTES );
2797
2798 return $data;
2799 }
2800
2801 function wpforo_decode( $data ) {
2802 $data = is_array( $data ) ? array_map( 'wpforo_decode', $data ) : htmlspecialchars_decode( $data, ENT_QUOTES );
2803
2804 return $data;
2805 }
2806
2807 function wpforo_trim( $data ) {
2808 $data = is_array( $data ) ? array_map( 'wpforo_trim', $data ) : trim( (string) $data );
2809
2810 return $data;
2811 }
2812
2813 function wpforo_sanitize_int( $data ) {
2814 $data = is_array( $data ) ? array_map( 'wpforo_sanitize_int', $data ) : intval( $data );
2815
2816 return $data;
2817 }
2818
2819 function wpforo_sanitize_text( $data ) {
2820 $data = is_array( $data ) ? array_map( 'wpforo_sanitize_text', $data ) : sanitize_text_field( $data );
2821
2822 return $data;
2823 }
2824
2825
2826 if( ! function_exists( 'sanitize_textarea_field' ) && ! function_exists( '_sanitize_text_fields' ) ) {
2827 function sanitize_textarea_field( $str ) {
2828 $filtered = _sanitize_text_fields( $str, true );
2829
2830 return apply_filters( 'sanitize_textarea_field', $filtered, $str );
2831 }
2832
2833 function _sanitize_text_fields( $str, $keep_newlines = false ) {
2834 $filtered = wp_check_invalid_utf8( $str );
2835 if( strpos( (string) $filtered, '<' ) !== false ) {
2836 $filtered = wp_pre_kses_less_than( $filtered );
2837 $filtered = wp_strip_all_tags( $filtered, false );
2838 $filtered = str_replace( "<\n", "&lt;\n", $filtered );
2839 }
2840 if( ! $keep_newlines ) {
2841 $filtered = preg_replace( '/[\r\n\t ]+/', ' ', (string) $filtered );
2842 }
2843 $filtered = trim( (string) $filtered );
2844 $found = false;
2845 while( preg_match( '/%[a-f0-9]{2}/i', (string) $filtered, $match ) ) {
2846 $filtered = str_replace( $match[0], '', $filtered );
2847 $found = true;
2848 }
2849 if( $found ) {
2850 $filtered = trim( preg_replace( '/ +/', ' ', (string) $filtered ) );
2851 }
2852
2853 return $filtered;
2854 }
2855 }
2856
2857 /**
2858 * @param string $role
2859 *
2860 * @return bool
2861 */
2862 function wpforo_current_user_is( $role ) {
2863 $role = strtolower( (string) $role );
2864
2865 $filter_result = apply_filters( 'wpforo_current_user_is', null, $role );
2866 if( ! is_null( $filter_result ) ) return (bool) $filter_result;
2867
2868 switch( $role ) {
2869 case 'admin':
2870 if( current_user_can( 'activate_plugins' ) ) {
2871 return true;
2872 }
2873 if( current_user_can( 'install_plugins' ) ) {
2874 return true;
2875 }
2876 if( current_user_can( 'create_sites' ) ) {
2877 return true;
2878 }
2879 break;
2880 case 'moderator':
2881 if( WPF()->usergroup->can( 'aum' ) ) {
2882 return true;
2883 }
2884 if( current_user_can( 'moderate_comments' ) ) {
2885 return true;
2886 }
2887 if( current_user_can( 'edit_published_posts' ) ) {
2888 return true;
2889 }
2890 if( current_user_can( 'manage_categories' ) ) {
2891 return true;
2892 }
2893 break;
2894 }
2895
2896 return false;
2897 }
2898
2899 /**
2900 * @param int $userid
2901 * @param string $role
2902 *
2903 * @return bool
2904 */
2905 function wpforo_user_is( $userid, $role ) {
2906 $userid = wpforo_bigintval( $userid );
2907 $role = strtolower( (string) $role );
2908
2909 $filter_result = apply_filters( 'wpforo_user_is', null, $userid, $role );
2910 if( ! is_null( $filter_result ) ) return (bool) $filter_result;
2911
2912 switch( $role ) {
2913 case 'admin':
2914 if( user_can( $userid, 'activate_plugins' ) ) {
2915 return true;
2916 }
2917 if( user_can( $userid, 'install_plugins' ) ) {
2918 return true;
2919 }
2920 if( user_can( $userid, 'create_sites' ) ) {
2921 return true;
2922 }
2923 if( $user = WPF()->member->get_member( $userid ) ) {
2924 if( WPF()->usergroup->can( 'mf', $user['groupids'] ) || WPF()->usergroup->can( 'ms', $user['groupids'] ) ) {
2925 return true;
2926 }
2927 }
2928 break;
2929 case 'moderator':
2930 if( user_can( $userid, 'moderate_comments' ) ) {
2931 return true;
2932 }
2933 if( user_can( $userid, 'edit_published_posts' ) ) {
2934 return true;
2935 }
2936 if( user_can( $userid, 'manage_categories' ) ) {
2937 return true;
2938 }
2939 if( $user = WPF()->member->get_member( $userid ) ) {
2940 if( WPF()->usergroup->can( 'aum', $user['groupids'] ) ) {
2941 return true;
2942 }
2943 }
2944 break;
2945 }
2946
2947 return false;
2948 }
2949
2950 function wpforo_random_colors() {
2951 mt_srand( (double) microtime() * 1000000 );
2952 $color = '';
2953 while( strlen( (string) $color ) < 6 ) {
2954 $color .= sprintf( "%02X", mt_rand( 0, 255 ) );
2955 }
2956
2957 return '#' . $color;
2958 }
2959
2960 /**
2961 * @param string $dir
2962 *
2963 * @return string
2964 */
2965 function wpforo_fix_dir_sep( string $dir ): string {
2966 return WPF()->fix_dir_sep( $dir );
2967 }
2968
2969 /**
2970 * @param string $url
2971 *
2972 * @return string
2973 */
2974 function wpforo_fix_url_sep( string $url ): string {
2975 return WPF()->fix_url_sep( $url );
2976 }
2977
2978 function wpforo_root_exist() {
2979 $args = [ 'table' => WPF()->tables->posts, 'col' => 'root', 'check' => 'col_exists' ];
2980
2981 return wpforo_db_check( $args );
2982 }
2983
2984 function wpforo_urlencode( $str ): string {
2985 $str = (string) $str;
2986 if( ! preg_match( '#^(\#post-\d+|https?:|s?ftp:)$#iu', (string) $str )
2987 && ! preg_match( '#([?&][^?&/=\r\n]*=?[^?&/=\r\n]*)(?1)*$#iu', (string) $str )
2988 && strpos( (string) $str, '~' ) === false
2989 && strpos( (string) $str, '*' ) === false
2990 && $str === urldecode( (string) $str )
2991 ) {
2992 $str = urlencode( (string) $str );
2993 }
2994
2995 // Use wpforo_url_strtolower() instead of strtolower() to keep the exact values
2996 // of GET variables in pagination buttons. Otherwise, the GET values will differ
2997 // and the field value will be changed on the next page.
2998 // Example: on members search page the selected "field=Test" will be changed to "field=test"
2999 // on the next page. And the select field will loos current selected value.
3000 return $str !== urldecode( (string) $str ) ? wpforo_url_strtolower( (string) $str ) : $str;
3001 }
3002
3003 /**
3004 * Converts the URL to lowercase.
3005 *
3006 * This function converts the URL to lowercase. If the $include_query parameter is set to true,
3007 * the entire URL including the query string is converted to lowercase. Otherwise, only the part of the URL
3008 * before the query string is converted to lowercase.
3009 *
3010 * @param string $url The URL to be converted to lowercase.
3011 * @param bool $include_query Optional. Whether to include the query string in the conversion. Default is false.
3012 *
3013 * @return string The URL converted to lowercase.
3014 */
3015 function wpforo_url_strtolower( $url, $include_query = false ): string {
3016 // If $include_query is true, convert the entire URL to lowercase and return it.
3017 if( $include_query ) return strtolower( $url );
3018 // If the URL does not contain a query string, convert the entire URL to lowercase and return it.
3019 if( strpos($url, '?') === false ){
3020 $url = strtolower( $url );
3021 } else {
3022 // If the URL contains a query string, split the URL into two parts at the '?' character.
3023 $parts = explode( '?', $url );
3024 // Remove the last element from the $parts array and store it in $query.
3025 $query = array_pop( $parts );
3026 $parts = array_map( 'strtolower', $parts );
3027 $url = implode( '?', $parts ) . '?' . $query;
3028 }
3029 return $url;
3030 }
3031
3032 function wpforo_fix_url( string $url ): string {
3033 $hash = '';
3034 $url = preg_replace_callback( '#(\#[^/]*)$#iu', function($m) use (&$hash){
3035 $hash = $m[1];
3036 return '';
3037 }, $url, 1 );
3038
3039 if( preg_match( '#^((?:https?:|s?ftp:)//)([^?&/=\s:]+)((?::\d+)?)([^\r\n]*)#iu', (string) $url, $match ) ){
3040 $match[2] = wpforo_urlencode( $match[2] );
3041 $url = $match[1] . $match[2] . $match[3] . implode( '/', array_map( 'wpforo_urlencode', explode( '/', $match[4] ) ) );
3042 }else{
3043 $url = implode( '/', array_map( 'wpforo_urlencode', explode( '/', $url ) ) );
3044 }
3045
3046 return $url . $hash;
3047 }
3048
3049 function wpforo_is_domains_equal( $url1, $url2 ) {
3050 $domain1 = strtolower( str_replace( 'www.', '', parse_url( $url1, PHP_URL_HOST ) ) );
3051 $domain2 = strtolower( str_replace( 'www.', '', parse_url( $url2, PHP_URL_HOST ) ) );
3052
3053 return $domain1 === $domain2;
3054 }
3055
3056 function wpforo_is_url_internal( $url, $home_url = null ) {
3057 $url = trim( (string) $url );
3058 if( ! preg_match( '#^(?:https?:)?//#iu', (string) $url ) ) return true;
3059 if( ! $home_url ) $home_url = home_url();
3060 $home_url = trim( (string) $home_url );
3061 $url = preg_replace( '#^(https?://)?(www\.)?#iu', '', (string) $url );
3062 $home_url = preg_replace( [ '#^(https?://)?(www\.)?#iu', '#/?\?.*$#isu', '#index\.php/?#iu' ], '', (string) $home_url );
3063
3064 return strpos( (string) $url, $home_url ) === 0;
3065 }
3066
3067 function wpforo_is_url_external( $url, $home_url = null ) {
3068 return ! wpforo_is_url_internal( $url, $home_url );
3069 }
3070
3071 function wpforo_settype( $var, $type ) {
3072 $var_type = strtolower( gettype( $var ) );
3073 $type = strtolower( (string) $type );
3074 $allowed_types = [ 'bool', 'boolean', 'int', 'integer', 'double', 'real', 'float', 'string', 'array', 'object', 'null' ];
3075 if( $var_type !== $type && in_array( $var_type, $allowed_types ) && in_array( $type, $allowed_types ) && $type !== 'null' && ! ( $var_type === 'object' && ! in_array( $type, [ 'boolean', 'array' ] ) ) && ! ( $var_type === 'array' && $type === 'string' ) ) {
3076 settype( $var, $type );
3077 }
3078
3079 return $var;
3080 }
3081
3082 function wpforo_array_args_cast( $array, $type ) {
3083 return array_map( function( $var ) use ( $type ) {
3084 return wpforo_settype( $var, $type );
3085 }, $array );
3086 }
3087
3088 function wpforo_array_args_cast_and_merge( $array, $default ) {
3089 $array = (array) $array;
3090 $default = (array) $default;
3091 foreach( $array as $key => $value ) {
3092 if( array_key_exists( $key, $default ) ) {
3093 $array[ $key ] = wpforo_settype( $value, gettype( $default[ $key ] ) );
3094 if( is_array( $array[ $key ] ) ) $array[ $key ] = wpforo_array_args_cast_and_merge( $array[ $key ], $default[ $key ] ); #### do not open this comment until you have checkboxes on options settings pages
3095 }
3096 }
3097 $array += $default;
3098
3099 return $array;
3100 }
3101
3102 function wpforo_db_data_format( $var ){
3103 switch( gettype( $var ) ){
3104 case 'bool': case 'boolean': case 'int': case 'integer': return '%d';
3105 case 'double': case 'real': case 'float': return '%f';
3106 default: return '%s';
3107 }
3108 }
3109
3110 /**
3111 * @param int $seconds execution time in seconds , by default 0 (unlimited)
3112 */
3113 function wpforo_set_max_execution_time( $seconds = 0 ) {
3114 if( function_exists( 'set_time_limit' ) ) @set_time_limit( $seconds );
3115 if( function_exists( 'ini_set' ) ) @ini_set( 'max_execution_time', $seconds );
3116 }
3117
3118 /**
3119 * @param string $pattern
3120 * @param array $array
3121 *
3122 * @return array
3123 */
3124 function wpforo_preg_grep_recursive( $pattern, $array ) {
3125 $m = [];
3126 if( is_array( $array ) ) {
3127 foreach( $array as $key => $value ) {
3128 if( is_string( $value ) || is_numeric( $value ) ) {
3129 if( preg_match( $pattern, (string) $value ) ) $m[ $key ] = $value;
3130 } elseif( is_array( $value = wpforo_settype( $value, 'array' ) ) ) {
3131 if( $_m = wpforo_preg_grep_recursive( $pattern, $value ) ) $m[ $key ] = $_m;
3132 }
3133 }
3134 }
3135
3136 return $m;
3137 }
3138
3139 function wpforo_send_new_user_notifications( $userid, $notify = 'both' ) {
3140 if( wpforo_setting( 'email', 'disable_new_user_admin_notification' ) ) {
3141 if( $notify !== 'admin' ) wp_send_new_user_notifications( $userid, 'user' );
3142 } else {
3143 wp_send_new_user_notifications( $userid, $notify );
3144 }
3145 }
3146
3147 function wpforo_get_callbacks_for_action( $hook = '' ) {
3148 global $wp_filter;
3149 if( empty( $hook ) || ! isset( $wp_filter[ $hook ] ) ) return [];
3150
3151 return $wp_filter[ $hook ]->callbacks;
3152 }
3153
3154 /**
3155 * The optimized version of wordpress url_to_postid($url) function
3156 *
3157 * Examine a URL and try to determine the post ID it represents.
3158 *
3159 * Checks are supposedly from the hosted site blog.
3160 *
3161 * @param string $url Permalink to check.
3162 *
3163 * @return int Post ID, or 0 on failure.
3164 * @global WP $wp Current WordPress environment instance.
3165 *
3166 * @since 1.7.4
3167 *
3168 * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
3169 */
3170 function _wpforo_wp_url_to_postid( $url ) {
3171 if( strpos( (string) $url, admin_url() ) !== false ) return 0;
3172
3173 $url_host = str_replace( 'www.', '', (string) parse_url( $url, PHP_URL_HOST ) );
3174 $home_url_host = str_replace( 'www.', '', (string) parse_url( home_url(), PHP_URL_HOST ) );
3175
3176 // Bail early if the URL does not belong to this site.
3177 if( $url_host && $url_host !== $home_url_host ) return 0;
3178
3179 // First, check to see if there is a 'p=N' or 'page_id=N' to match against.
3180 if( preg_match( '#[?&](p|page_id|post_id|post|attachment_id)=(\d+)#', (string) $url, $values ) ) {
3181 if( $id = absint( $values[2] ) ) return $id;
3182 }
3183
3184 global $wp_rewrite;
3185 if( $wp_rewrite ) {
3186 // Get rid of the #anchor.
3187 $url_split = explode( '#', $url );
3188 $url = $url_split[0];
3189
3190 // Get rid of URL ?query=string.
3191 $url_split = explode( '?', $url );
3192 $url = $url_split[0];
3193
3194 // Set the correct URL scheme.
3195 $scheme = parse_url( home_url(), PHP_URL_SCHEME );
3196 $url = set_url_scheme( $url, $scheme );
3197
3198 // Add 'www.' if it is absent and should be there.
3199 if( false !== strpos( home_url(), '://www.' ) && false === strpos( $url, '://www.' ) ) {
3200 $url = str_replace( '://', '://www.', $url );
3201 }
3202
3203 // Strip 'www.' if it is present and shouldn't be.
3204 if( false === strpos( home_url(), '://www.' ) ) {
3205 $url = str_replace( '://www.', '://', $url );
3206 }
3207
3208 if( trim( (string) $url, '/' ) === home_url() && 'page' == get_option( 'show_on_front' ) ) {
3209 $page_on_front = get_option( 'page_on_front' );
3210 if( $page_on_front && get_post( $page_on_front ) instanceof WP_Post ) return (int) $page_on_front;
3211 }
3212
3213 // Check to see if we are using rewrite rules.
3214 $rewrite = $wp_rewrite->wp_rewrite_rules();
3215
3216 // Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options.
3217 if( empty( $rewrite ) ) return 0;
3218
3219 // Strip 'index.php/' if we're not using path info permalinks.
3220 if( ! $wp_rewrite->using_index_permalinks() ) {
3221 $url = str_replace( $wp_rewrite->index . '/', '', $url );
3222 }
3223
3224 if( false !== strpos( trailingslashit( (string) $url ), home_url( '/' ) ) ) {
3225 // Chop off http://domain.com/[path].
3226 $url = str_replace( home_url(), '', $url );
3227 } else {
3228 // Chop off /path/to/blog.
3229 $home_path = parse_url( home_url( '/' ) );
3230 $home_path = isset( $home_path['path'] ) ? $home_path['path'] : '';
3231 $url = preg_replace( sprintf( '#^%s#', preg_quote( $home_path ) ), '', trailingslashit( (string) $url ) );
3232 }
3233
3234 // Trim leading and lagging slashes.
3235 $url = trim( (string) $url, '/' );
3236
3237 $request = $url;
3238 $post_type_query_vars = [];
3239
3240 foreach( get_post_types( [], 'objects' ) as $post_type => $t ) {
3241 if( ! empty( $t->query_var ) ) {
3242 $post_type_query_vars[ $t->query_var ] = $post_type;
3243 }
3244 }
3245
3246 // Look for matches.
3247 $request_match = $request;
3248 foreach( (array) $rewrite as $match => $query ) {
3249 if( preg_match( "#^$match#", (string) $request_match, $matches ) ) {
3250
3251 if( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)]/', (string) $query, $varmatch ) ) {
3252 // This is a verbose page match, let's check to be sure about it.
3253 $page = get_page_by_path( $matches[ $varmatch[1] ] );
3254 if( ! $page ) {
3255 continue;
3256 }
3257
3258 $post_status_obj = get_post_status_object( $page->post_status );
3259 if( ! $post_status_obj->public && ! $post_status_obj->protected && ! $post_status_obj->private && $post_status_obj->exclude_from_search ) {
3260 continue;
3261 }
3262 }
3263
3264 // Got a match.
3265 // Trim the query of everything up to the '?'.
3266 $query = preg_replace( '!^.+\?!', '', (string) $query );
3267
3268 // Substitute the substring matches into the query.
3269 $query = addslashes( WP_MatchesMapRegex::apply( $query, $matches ) );
3270
3271 // Filter out non-public query vars.
3272 global $wp;
3273 parse_str( $query, $query_vars );
3274 $query = [];
3275 foreach( (array) $query_vars as $key => $value ) {
3276 if( in_array( $key, $wp->public_query_vars ) ) {
3277 $query[ $key ] = $value;
3278 if( isset( $post_type_query_vars[ $key ] ) ) {
3279 $query['post_type'] = $post_type_query_vars[ $key ];
3280 $query['name'] = $value;
3281 }
3282 }
3283 }
3284
3285 // Resolve conflicts between posts with numeric slugs and date archive queries.
3286 $query = wp_resolve_numeric_slug_conflicts( $query );
3287
3288 // Do the query.
3289 if( $p = wpforo_bigintval( wpfval( $query, 'p' ) ) ) return $p;
3290 if( $page_id = wpforo_bigintval( wpfval( $query, 'page_id' ) ) ) return $page_id;
3291
3292 $pagename = wpfval( $query, 'pagename' );
3293 $name = wpfval( $query, 'name' );
3294 if( $pagename || $name ) {
3295 if( ! $slug = $pagename ) $slug = $name;
3296 $sql = "SELECT `ID` FROM `" . WPF()->db->posts . "`
3297 WHERE `post_status` = 'publish'
3298 AND `post_name` = %s";
3299 $post_types = (array) apply_filters( 'wpforo_wp_url_to_postid_post_types', [ 'page', 'post' ] );
3300 if( $post_types ) $sql .= " AND `post_type` IN('" . implode( "','", $post_types ) . "')";
3301 $sql = WPF()->db->prepare( $sql, $slug );
3302 $postid = WPF()->db->get_var( $sql );
3303 if( $postid = wpforo_bigintval( $postid ) ) {
3304 return $postid;
3305 } else {
3306 return 0;
3307 }
3308 } else {
3309 return 0;
3310 }
3311
3312 }
3313 }
3314 }
3315
3316 return 0;
3317 }
3318
3319 function wpforo_wp_url_to_postid( $url ) {
3320 return wpforo_ram_get( '_wpforo_wp_url_to_postid', $url );
3321 }
3322
3323 function wpforo_get_blog_content_types() {
3324 $post_types = get_post_types( [
3325 'public' => true,
3326 'publicly_queryable' => true,
3327 'show_ui' => true,
3328 'capability_type' => 'post',
3329 ] );
3330 $post_types['post'] = 'post';
3331 $post_types['page'] = 'page';
3332 unset( $post_types['attachment'] );
3333 $post_types = array_filter( array_keys( $post_types ) );
3334
3335 return apply_filters( 'wpforo_get_blog_content_types', $post_types );
3336 }
3337
3338 /**
3339 * @param string $css
3340 *
3341 * @return string
3342 */
3343 function wpforo_add_wrapper( $css ) {
3344 $css = preg_replace( '@(#(?:wpforo-wrap|wpfa_dialog|wpfa_dialog_wrap)[\s.#{:>+\[~,])@um', '#wpforo $1', (string) $css );
3345
3346 return preg_replace( '@(#(?:wpforo|wpforo-wrap|wpfa_dialog|wpfa_dialog_wrap)\s*)#wpforo[\s.#{:>+\[~,]@um', '$1', (string) $css );
3347 }
3348
3349 /**
3350 * @param string $css
3351 *
3352 * @return string
3353 */
3354 function wpforo_wrap_fix_in_css( $css ) {
3355 if( ( strpos( (string) $css, '#wpforo-wrap' ) !== false && strpos( (string) $css, '#wpforo #wpforo-wrap' ) === false ) || ( strpos( (string) $css, '#wpfa_dialog' ) !== false && strpos( (string) $css, '#wpforo #wpfa_dialog' ) === false ) ) {
3356 $css = wpforo_add_wrapper( $css );
3357 }
3358
3359 return $css;
3360 }
3361
3362 /**
3363 * @param string $csspath
3364 */
3365 function wpforo_wrap_fix_in_cssfile( $csspath ) {
3366 $csspath = wpforo_fix_dir_sep( $csspath );
3367 if( is_file( $csspath ) ) {
3368 $css = wpforo_get_file_content( $csspath );
3369 $css = wpforo_wrap_fix_in_css( $css );
3370 if( md5( $css ) !== md5_file( $csspath ) ) wpforo_write_file( $csspath, $css );
3371 }
3372 }
3373
3374 function wpforo_wrap_in_all_addons_css() {
3375 $csspaths = [
3376 '/wpforo-ad-manager/assets/css/style.css',
3377 '/wpforo-advanced-attachments/assets/css/style.css',
3378 '/wpforo-cross-posting/assets/css/wpdiscuz-uploader.css',
3379 '/wpforo-cross-posting/assets/css/wpforo-cross-rtl.css',
3380 '/wpforo-cross-posting/assets/css/wpforo-cross.css',
3381 '/wpforo-embeds/assets/css/embed.css',
3382 '/wpforo-emoticons/assets/emoticons.css',
3383 '/wpforo-mycred/css/mycread.css',
3384 '/wpforo-polls/assets/css/poll.css',
3385 '/wpforo-private-messages/assets/css/style-rtl.css',
3386 '/wpforo-private-messages/assets/css/style.css',
3387 '/wpforo-user-custom-fields/assets/css/frontend.css',
3388 ];
3389 foreach( $csspaths as $csspath ) wpforo_wrap_fix_in_cssfile( WP_PLUGIN_DIR . $csspath );
3390
3391 WPF()->dissmissed['addons_css_update'] = 1;
3392 wpforo_update_option( 'dissmissed', WPF()->dissmissed );
3393 }
3394
3395 /*add_action( 'admin_notices', function() {
3396 if( ! (int) wpfval( WPF()->dissmissed, 'addons_css_update' ) ) {
3397 $has_addon = false;
3398 foreach( wpforo_get_addons_info() as $addon ) {
3399 if( class_exists( $addon['class'] ) ) {
3400 $has_addon = true;
3401 break;
3402 }
3403 }
3404 if( ! $has_addon ) return;
3405 $class = 'notice notice-warning';
3406 $message = '<h3>' . __( 'Action Required!', 'wpforo' ) . '<span style="display: inline-block;font-size: 14px; padding: 0 7px; font-weight: normal;">' . __( 'Please update wpForo addons CSS style to make compatible with the current version of wpForo.', 'wpforo' ) . '</span>' . '</h3>';
3407 $message .= '<a href="' . admin_url( wp_nonce_url( 'admin.php?page=' . wpforo_prefix_slug( 'settings' ) . '&wpfaction=update_addons_css', 'wpforo-update-addons-css' ) ) . '" class="button button-primary">' . __( 'Update CSS >>', 'wpforo' ) . '</a>';
3408 printf( '<div class="%1$s"><p>%2$s</p></div>', $class, $message );
3409 }
3410 } );*/
3411
3412 /**
3413 * @param string $url
3414 *
3415 * @return string|null
3416 */
3417 function wpforo_get_topic_slug_from_url( $url = '' ) {
3418 $url = trim( (string) $url );
3419 if( ! $url ) $url = wpforo_get_request_uri();
3420
3421 if( is_wpforo_url( $url ) ) {
3422 if( preg_match( '#/' . preg_quote( wpforo_settings_get_slug( 'postid' ) ) . '/(\d+)/?$#isu', strtok( $url, '?' ), $match ) ) {
3423 $url = WPF()->post->get_url( $match[1] );
3424 }
3425 if( preg_match( '#^[\r\n\t\s]*https?://[^\r\n\t\s]+?/[^/]+?/([^/]+?)(?:/' . wpforo_settings_get_slug( 'paged' ) . '/\d+/?)?(?:/?\#post-\d+)?/?[\r\n\t\s]*$#isu', (string) $url, $match ) ) {
3426 return $match[1];
3427 }
3428 }
3429
3430 return null;
3431 }
3432
3433 function wpforo_clean_folder( $directory ) {
3434 $directory = wpforo_fix_dir_sep( $directory );
3435 $directory_ns = trim( (string) $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR . '*';
3436 $directory_ws = DIRECTORY_SEPARATOR . trim( (string) $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR . '*';
3437 $glob = glob( $directory_ns );
3438 if( empty( $glob ) ) $glob = glob( $directory_ws );
3439 foreach( $glob as $item ) {
3440 if( strpos( (string) $item, 'index.html' ) !== false || strpos( (string) $item, '.htaccess' ) !== false ) continue;
3441 if( ! is_dir( $item ) && file_exists( $item ) ) {
3442 @unlink( $item );
3443 }
3444 }
3445 }
3446
3447 /**
3448 * @param string $basename
3449 *
3450 * @return string
3451 */
3452 function wpforo_fix_table_name( $basename ) {
3453 return WPF()->fix_table_name( $basename );
3454 }
3455
3456 /**
3457 * @param callable $func
3458 * @param mixed ... $_, ... [optional] call_user_func parameters
3459 *
3460 * @return mixed
3461 */
3462 function wpforo_ram_get( $func ) {
3463 return call_user_func_array( [ WPF()->ram_cache, 'call_user_func' ], func_get_args() );
3464 }
3465
3466 function wpforo_prefix( $str = '', $wp_prefix = false ) {
3467 return ( $wp_prefix ? WPF()->blog_prefix : '' ) . WPF()->prefix . $str;
3468 }
3469
3470 function wpforo_prefix_slug( $str = '' ) {
3471 if( $str === 'dashboard' && ! is_wpforo_multiboard() ) $str = 'overview';
3472 return str_replace( '_', '-', wpforo_prefix( $str ) );
3473 }
3474
3475 function wpforo_get_ajax_url() {
3476 return admin_url(
3477 sprintf(
3478 'admin-ajax.php?lang=%1$s&page_id=%2$d&wpforo_boardid=%3$d',
3479 strtok( WPF()->locale, '_' ),
3480 WPF()->board->get_current( 'pageid' ),
3481 WPF()->board->get_current( 'boardid' )
3482 )
3483 );
3484 }
3485
3486 function wpforo_get_query_var_lang() {
3487 if( ! $lang = wpfval( $_GET, 'lang' ) ) $lang = get_query_var( 'lang' );
3488
3489 return $lang;
3490 }
3491
3492 function wpforo_is_db_mysql8() {
3493 return wpforo_ram_get( [ WPF(), 'is_db_mysql8' ] );
3494 }
3495
3496 function wpforo_is_module_enabled( $module_key, $board = [] ) {
3497 return WPF()->board->is_module_enabled( $module_key, $board );
3498 }
3499
3500 function is_wpforo_multiboard() {
3501 return wpforo_ram_get( [ WPF()->board, 'is_multi' ] );
3502 }
3503
3504 function wpforo_get_redirect_to() {
3505 if( ! ( ( $redirect_to = urldecode( (string) wpfval( $_GET, 'redirect_to' ) ) ) && wpforo_is_url_internal( $redirect_to ) ) ) {
3506 $redirect_to = preg_replace( '#/?\?.*$#isu', '', wpforo_get_request_uri() );
3507 }
3508
3509 return $redirect_to;
3510 }
3511
3512 function wpforo_get_redirect_to_url_path( $redirect_to = null ) {
3513 if( is_null( $redirect_to ) ) $redirect_to = wpforo_get_redirect_to();
3514
3515 return $redirect_to ? '?redirect_to=' . urlencode( (string) $redirect_to ) : '';
3516 }
3517
3518 function wpforo_login_url( $redirect_to = null ) {
3519 if( wpforo_setting( 'authorization', 'login_url' ) ) {
3520 $login_url = trim( (string) get_bloginfo( 'url' ), '/' ) . '/' . ltrim( (string) wpforo_setting( 'authorization', 'login_url' ), '/' );
3521 } else {
3522 $login_url = wpforo_url( wpforo_get_redirect_to_url_path( $redirect_to ), 'login' );
3523 }
3524 $login_url = apply_filters( 'wpforo_login_url', $login_url );
3525
3526 return esc_url_raw( $login_url );
3527 }
3528
3529 function wpforo_register_url( $redirect_to = null ) {
3530 if( wpforo_setting( 'authorization', 'register_url' ) ) {
3531 $register_url = trim( (string) get_bloginfo( 'url' ), '/' ) . '/' . ltrim( (string) wpforo_setting( 'authorization', 'register_url' ), '/' );
3532 } else {
3533 $register_url = wpforo_url( wpforo_get_redirect_to_url_path( $redirect_to ), 'register' );
3534 }
3535 $register_url = apply_filters( 'wpforo_register_url', $register_url );
3536
3537 return esc_url_raw( $register_url );
3538 }
3539
3540 function wpforo_logout_url( $redirect_to = null ) {
3541 $logout_url = wpforo_url( wpforo_get_redirect_to_url_path( $redirect_to ), 'logout' );
3542 $logout_url = wp_nonce_url( $logout_url, 'wpforo_logout', '_wpfnonce' );
3543 $logout_url = apply_filters( 'wpforo_logout_url', $logout_url );
3544
3545 return esc_url_raw( $logout_url );
3546 }
3547
3548 function wpforo_lostpassword_url( $redirect_to = null ) {
3549 if( wpforo_setting( 'authorization', 'lost_password_url' ) ) {
3550 $lostpassword_url = trim( (string) get_bloginfo( 'url' ), '/' ) . '/' . ltrim( (string) wpforo_setting( 'authorization', 'lost_password_url' ), '/' );
3551 } else {
3552 if( wpforo_setting( 'authorization', 'use_our_lostpassword_url' ) ) {
3553 $lostpassword_url = wpforo_url( wpforo_get_redirect_to_url_path( $redirect_to ), 'lostpassword' );
3554 } else {
3555 if( is_null( $redirect_to ) ) $redirect_to = wpforo_get_redirect_to();
3556 $lostpassword_url = wp_lostpassword_url( $redirect_to );
3557 }
3558 }
3559 $lostpassword_url = apply_filters( 'wpforo_lostpassword_url', $lostpassword_url );
3560
3561 return esc_url_raw( $lostpassword_url );
3562 }
3563
3564 function wpforo_resetpassword_url( $rp_key, $rp_login ) {
3565 $rp_key = rawurlencode( rawurldecode( $rp_key ) );
3566 $rp_login = rawurlencode( rawurldecode( $rp_login ) );
3567 $resetpassword_url = wpforo_url( "?wpfaction=resetpassword_form&rp_key=$rp_key&rp_login=$rp_login", 'lostpassword' );
3568 $resetpassword_url = apply_filters( 'wpforo_resetpassword_url', $resetpassword_url );
3569
3570 return esc_url_raw( $resetpassword_url );
3571 }
3572
3573 function wpforo_members_url() {
3574 return wpforo_url( '', 'members' );
3575 }
3576
3577 function wpforo_redirect_to() {
3578 $redirect_url = wpforo_home_url();
3579 if( ( $redirect_to = urldecode( (string) wpfval( $_GET, 'redirect_to' ) ) ) && wpforo_is_url_internal( $redirect_to ) && ( ! WPF()->current_userid || ( strpos( $redirect_to, wpforo_settings_get_slug( 'login' ) ) === false && strpos( $redirect_to, wpforo_settings_get_slug( 'register' ) ) === false && strpos( $redirect_to, wpforo_settings_get_slug( 'lostpassword' ) ) === false ) ) ) {
3580 $redirect_url = $redirect_to;
3581 }
3582 wp_safe_redirect( $redirect_url );
3583 exit();
3584 }
3585
3586 function wpforo_settings_get_slug( $key ) {
3587 $slug = wpfval( WPF()->settings->slugs, $key );
3588 if( ! $slug ) $slug = $key;
3589
3590 return strtolower( (string) $slug );
3591 }
3592
3593 function wpforo_settings_get_slug_key( $slug ) {
3594 $ret = array_search( $slug, WPF()->settings->slugs );
3595 if( $ret === false ) $ret = $slug;
3596
3597 return $ret;
3598 }
3599
3600 function wpforo_is_slug_base( $slug ) {
3601 return ! wpfkey( WPF()->tpl->templates, wpforo_settings_get_slug_key( $slug ) );
3602 }
3603
3604 /**
3605 * @param string $group
3606 * @param string|int ...$name ... $_, ... [optional] more keys
3607 *
3608 * @return mixed|null
3609 */
3610 function wpforo_setting( $group, $name ) {
3611 if( ! is_null( WPF()->settings ) && property_exists( WPF()->settings, $group ) ) {
3612 $args = func_get_args();
3613 $args[0] = &WPF()->settings->$group;
3614
3615 return call_user_func_array( 'wpfval', $args );
3616 }
3617
3618 return null;
3619 }
3620
3621 function _wpforo_apply_email_shortcodes( $txt, $args ){
3622 $forumid = (int) wpfval( $args, 'forumid' );
3623 $topicid = wpforo_bigintval( wpfval( $args, 'topicid' ) );
3624 $postid = wpforo_bigintval( wpfval( $args, 'postid' ) );
3625 $user = (array) wpfval( $args, 'user' );
3626 $owner = (array) wpfval( $args, 'owner' );
3627 $unsubscribe_link = (string) wpfval( $args, 'unsubscribe_link' );
3628
3629 $txt = do_shortcode( $txt );
3630
3631 return preg_replace_callback( '#\[[^\[\]]+?]#iu', function ( $match ) use ( $forumid, $topicid, $postid, $user, $owner, $unsubscribe_link ) {
3632 $body_length = apply_filters( 'wpforo_email_notification_post_body_length', 1000 );
3633 $value = '';
3634 $shortcode = $match[0];
3635
3636 if ( $shortcode === '[unsubscribe_link]' ) {
3637 if ( $unsubscribe_link ) {
3638 $value = sprintf( '<br><a target="_blank" href="%1$s">%2$s</a>', esc_url( (string) $unsubscribe_link ), wpforo_phrase( 'Unsubscribe', false ) );
3639 $value = stripslashes( $value );
3640 }
3641 } elseif ( strpos( (string) $shortcode, '[user_' ) === 0 ) {
3642 if ( wpforo_bigintval( wpfval( $user, 'userid' ) ) ) {
3643 $value = wpforo_user_field_shortcode_to_value( $shortcode, $user['userid'] );
3644 if ( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) {
3645 $value = '';
3646 }
3647 } else {
3648 if ( $shortcode === '[user_display_name]' ) {
3649 $value = wpfval( $user, 'display_name' ) ?: wpforo_phrase( 'Anonymous', false );
3650 } elseif ( $shortcode === '[user_user_email]' ) {
3651 $value = (string) wpfval( $user, 'user_email' );
3652 }
3653 }
3654 } elseif ( strpos( (string) $shortcode, '[owner_' ) === 0 ) {
3655 if ( wpforo_bigintval( wpfval( $owner, 'userid' ) ) ) {
3656 $value = wpforo_user_field_shortcode_to_value( $shortcode, $owner['userid'] );
3657 if ( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) {
3658 $value = '';
3659 }
3660 } else {
3661 if ( $shortcode === '[owner_display_name]' ) {
3662 $value = wpfval( $owner, 'display_name' ) ?: wpforo_phrase( 'Anonymous', false );
3663 } elseif ( $shortcode === '[owner_user_email]' ) {
3664 $value = (string) wpfval( $owner, 'user_email' );
3665 }
3666 }
3667 } elseif ( strpos( (string) $shortcode, '[forum_' ) === 0 ) {
3668 if ( $forumid ) {
3669 $value = wpforo_forum_field_shortcode_to_value( $shortcode, $forumid );
3670 if ( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) {
3671 $value = '';
3672 }
3673 }
3674 } elseif ( strpos( (string) $shortcode, '[topic_' ) === 0 ) {
3675 if ( $topicid ) {
3676 $value = wpforo_topic_field_shortcode_to_value( $shortcode, $topicid );
3677 if ( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) {
3678 $value = '';
3679 }elseif ( $shortcode === '[topic_body]' ) {
3680 $value = wpforo_text( wpforo_kses( wpforo_content_filter( $value ), 'email' ), $body_length, false, false, false, true, false );
3681 }
3682 }
3683 } elseif ( strpos( (string) $shortcode, '[post_' ) === 0 ) {
3684 if ( $postid ) {
3685 $value = wpforo_post_field_shortcode_to_value( $shortcode, $postid );
3686 if ( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) {
3687 $value = '';
3688 }elseif ( $shortcode === '[post_body]' ) {
3689 $value = wpforo_text( wpforo_kses( wpforo_content_filter( $value ), 'email' ), $body_length, false, false, false, true, false );
3690 }
3691 }
3692 }
3693
3694 return $value;
3695 }, (string) $txt );
3696 }
3697
3698 function wpforo_apply_email_shortcodes( $txt, $pitem, $item, $owner, $user, $unsubscribe_link ) {
3699 $forumid = ( intval( wpfval( $pitem, 'forumid' ) ) ?: intval( wpfval( $item, 'forumid' ) ) );
3700 $topicid = ( wpforo_bigintval( wpfval( $pitem, 'topicid' ) ) ?: wpforo_bigintval( wpfval( $item, 'topicid' ) ) );
3701 $postid = ( wpforo_bigintval( wpfval( $item, 'postid' ) ) ?: wpforo_bigintval( wpfval( $item, 'first_postid' ) ) );
3702
3703 return _wpforo_apply_email_shortcodes( $txt, compact('forumid', 'topicid', 'postid', 'owner', 'user', 'unsubscribe_link') );
3704 }
3705
3706 function wpforo_send_email( $email, $sbj, $msg, $headers = '' ){
3707 if( defined( 'IS_GO2WPFORO' ) && IS_GO2WPFORO ) return false;
3708 if( apply_filters( 'break_wpforo_send_email', false, $email, $sbj, $msg, $headers ) ) return false;
3709 $key = func_get_args();
3710 if( WPF()->ram_cache->exists( $key ) ) return false;
3711 add_filter( 'wp_mail_content_type', 'wpforo_set_html_content_type', 999 );
3712 if( wp_mail( $email, $sbj, $msg, ( $headers ?: wpforo_mail_headers() ) ) ){
3713 WPF()->ram_cache->set( $key, true );
3714 return true;
3715 }
3716 remove_filter( 'wp_mail_content_type', 'wpforo_set_html_content_type' );
3717
3718 return false;
3719 }
3720
3721 function wpforo_get_site_default_locale(){
3722 $locale = trim( (string) get_option( 'WPLANG', 'en_US' ) );
3723 if( ! $locale ) $locale = 'en_US';
3724 return $locale;
3725 }
3726
3727 function wpforo_sanitize( $action, $variable_name, $filter, $default = '' ) {
3728 if( $filter === "FILTER_SANITIZE_STRING" ) {
3729 $glob = INPUT_POST === $action ? $_POST : $_GET;
3730 if( wpfkey( $glob, $variable_name ) ) {
3731 return sanitize_text_field( $glob[ $variable_name ] );
3732 } else {
3733 return $default;
3734 }
3735 }
3736 $variable = isset( $variable_name ) ? filter_input( $action, $variable_name, $filter ) : '';
3737
3738 return $variable ?: $default;
3739 }
3740
3741 function wpforo_length( $str, $filtered = true ){
3742 $str = $filtered ? strip_tags( trim( (string) $str ) ) : $str;
3743 if( function_exists( 'mb_substr' ) ) {
3744 $length = mb_strlen( (string) $str, get_option( 'blog_charset' ) );
3745 } else {
3746 $length = strlen( (string) $str );
3747 }
3748 return (int) $length;
3749 }
3750
3751 if( ! function_exists( 'wpforo_remote_retrieve_header' ) ){
3752 /**
3753 *
3754 * @param array|WP_Error $response HTTP response.
3755 * @param string $header Header name to retrieve value from.
3756 * @return string string The header value. Empty string on if incorrect parameter given, or if the header doesn't exist.
3757 */
3758 function wpforo_remote_retrieve_header( $response, $header ){
3759 $header = wp_remote_retrieve_header( $response, $header );
3760 if( is_array( $header ) ) $header = (string) array_pop( $header );
3761
3762 return $header;
3763 }
3764 }
3765
3766 function wpforo_is_array_of_scalars($array) {
3767 if (!is_array($array)) return false;
3768 foreach ($array as $element) if (!is_scalar($element)) return false;
3769 return true;
3770 }
3771
3772 function wpforo_is_gzipped( $contents ): bool {
3773 // phpcs:disable Generic.Strings.UnnecessaryStringConcat.Found
3774 if ( function_exists( 'mb_strpos' ) ) {
3775 return 0 === mb_strpos( $contents, "\x1f" . "\x8b" . "\x08" );
3776 } else {
3777 return 0 === strpos( $contents, "\x1f" . "\x8b" . "\x08" );
3778 }
3779 // phpcs:enable
3780 }
3781
3782 function wpforo_sanitize_svg( $file ): bool {
3783 $dirty = file_get_contents( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
3784
3785 // Is the SVG gzipped? If so we try and decode the string
3786 $is_zipped = wpforo_is_gzipped( $dirty );
3787 if ( $is_zipped ) {
3788 $dirty = gzdecode( $dirty );
3789
3790 // If decoding fails, bail as we're not secure
3791 if ( false === $dirty ) {
3792 return false;
3793 }
3794 }
3795
3796 $clean = trim( wpforo_kses( $dirty, 'svg' ) );
3797
3798 if ( !$clean ) {
3799 return false;
3800 }
3801
3802 // If we were gzipped, we need to re-zip
3803 if ( $is_zipped ) {
3804 $clean = gzencode( $clean );
3805 }
3806
3807 file_put_contents( $file, $clean ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
3808
3809 return true;
3810 }
3811
3812 function wpforo_check_for_svg( $file ){
3813 // Ensure we have a proper file path before processing
3814 if ( ! isset( $file['tmp_name'] ) ) {
3815 return $file;
3816 }
3817
3818 $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], ( $file['name'] ?? '' ), [] );
3819 $type = ! empty( $wp_filetype['type'] ) ? $wp_filetype['type'] : (string) wpfval( $file, 'type' );
3820
3821 if ( in_array( $type, [ 'image/svg+xml', 'image/svg' ], true ) ) {
3822 if ( ! wpforo_sanitize_svg( $file['tmp_name'] ) ) {
3823 $file['error'] = __(
3824 "Sorry, this file couldn't be sanitized so for security reasons wasn't uploaded",
3825 'wpforo'
3826 );
3827 }
3828 }
3829
3830 return $file;
3831 }
3832