PluginProbe
wpForo Forum / 2.2.9
wpForo Forum v2.2.9
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.2.9, at includes/functions.php

3,815 lines 152.4 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-voice-posting' => [
356 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-voice-posting/wpforoVoicePosting.php' ),
357 'base' => false,
358 'version' => '3.0.0',
359 'requires' => '2.2.8',
360 'class' => 'wpforoVoicePosting',
361 'deps' => [],
362 'title' => 'Voice Posting',
363 'thumb' => WPFORO_URL . '/assets/addons/wpforo-voice-posting/header.png',
364 'desc' => __( 'Allows to record and attach voice messages to forum posts and private messages.', 'wpforo' ),
365 'url' => 'https://gvectors.com/product/wpforo-voice-posting/',
366 ],
367 'wpforo-cross-posting' => [
368 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-cross-posting/wpForoCrossPosting.php' ),
369 'base' => false,
370 'version' => '3.0.0',
371 'requires' => '2.0.0',
372 'class' => 'wpForoCrossPosting',
373 'deps' => [],
374 'title' => '"Forum - Blog" Cross Posting',
375 'thumb' => WPFORO_URL . '/assets/addons/cross/header.png',
376 'desc' => __( 'Blog to Forum and Forum to Blog content synchronization. Blog posts with Forum topics and Blog comments with Forum replies.', 'wpforo' ),
377 'url' => 'https://gvectors.com/product/wpforo-cross-posting/',
378 ],
379 'wpforo-ad-manager' => [
380 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-ad-manager/wpforoad.php' ),
381 'base' => false,
382 'version' => '3.0.1',
383 'requires' => '2.0.0',
384 'class' => 'wpForoAD',
385 'deps' => [],
386 'install_func' => function () {
387 if (
388 class_exists( 'wpForoAD' )
389 && function_exists( 'wpforoad_activation' )
390 && version_compare( WPFORO_VERSION, WPFOROAD_WPFORO_REQUIRED_VERSION, '>=' )
391 ) {
392 wpforoad_activation();
393 }
394 },
395 'title' => 'Ads Manager',
396 'thumb' => WPFORO_URL . '/assets/addons/ad-manager/header.png',
397 'desc' => __( 'Ads Manager is a powerful yet simple advertisement management system. Allows you to add adverting banners between forums, topics and posts.', 'wpforo' ),
398 'url' => 'https://gvectors.com/product/wpforo-ad-manager/',
399 ],
400 'wpforo-polls' => [
401 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-polls/wpforopoll.php' ),
402 'base' => false,
403 'version' => '3.0.0',
404 'requires' => '2.0.0',
405 'class' => 'wpForoPoll',
406 'deps' => [],
407 'install_func' => function () {
408 if (
409 class_exists( 'wpForoPoll' )
410 && function_exists( 'wpforopoll_activation' )
411 && version_compare( WPFORO_VERSION, WPFOROPOLL_WPFORO_REQUIRED_VERSION, '>=' )
412 ) {
413 wpforopoll_activation();
414 }
415 },
416 'title' => 'Polls',
417 'thumb' => WPFORO_URL . '/assets/addons/polls/header.png',
418 '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' ),
419 'url' => 'https://gvectors.com/product/wpforo-polls/',
420 ],
421 'wpforo-emoticons' => [
422 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-emoticons/wpforosmile.php' ),
423 'base' => false,
424 'version' => '3.0.0',
425 'requires' => '2.0.0',
426 'class' => 'wpForoSmiles',
427 'deps' => [],
428 'install_func' => function () {
429 if (
430 class_exists( 'wpForoSmiles' )
431 && function_exists( 'wpforosmile_activation' )
432 && version_compare( WPFORO_VERSION, WPFOROSMILE_WPFORO_REQUIRED_VERSION, '>=' )
433 ) {
434 wpforosmile_activation();
435 }
436 },
437 'title' => 'wpForo Emoticons',
438 'thumb' => WPFORO_URL . '/assets/addons/wpforo-emoticons/header.png',
439 'desc' => __( 'Adds awesome Sticker and Emoticons packs to editor. Allows to create new custom emoticons packs.', 'wpforo' ),
440 'url' => 'https://gvectors.com/product/wpforo-emoticons/',
441 ],
442 'wpforo-tenor' => [
443 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-tenor/wpforotenor.php' ),
444 'base' => false,
445 'version' => '3.0.0',
446 'requires' => '2.0.0',
447 'class' => 'wpForoTenor',
448 'deps' => [],
449 'title' => 'Tenor GIFs Integration',
450 'thumb' => WPFORO_URL . '/assets/addons/tenor/header.png',
451 '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' ),
452 'url' => 'https://gvectors.com/product/wpforo-tenor/',
453 ],
454 'wpforo-giphy' => [
455 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-giphy/wpforogiphy.php' ),
456 'base' => false,
457 'version' => '3.0.0',
458 'requires' => '2.0.0',
459 'class' => 'wpForoGiphy',
460 'deps' => [],
461 'title' => 'GIPHY Integration',
462 'thumb' => WPFORO_URL . '/assets/addons/giphy/header.png',
463 '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' ),
464 'url' => 'https://gvectors.com/product/wpforo-giphy/',
465 ],
466 'wpforo-mycred' => [
467 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-mycred/wpforo-mc.php' ),
468 'base' => true,
469 'version' => '3.0.0',
470 'requires' => '2.0.0',
471 'class' => 'myCRED_Hook_wpForo',
472 'deps' => [],
473 'title' => 'MyCRED Integration',
474 'thumb' => WPFORO_URL . '/assets/addons/mycred/header.png',
475 'desc' => __( 'Awards myCRED points for forum activity. Integrates myCRED Badges and Ranks. Converts wpForo topic and posts, likes to myCRED points.', 'wpforo' ),
476 'url' => 'https://gvectors.com/product/wpforo-mycred/',
477 ],
478 'wpforo-syntax-highlighter' => [
479 'ABSPATH' => wpforo_fix_dir_sep( WP_PLUGIN_DIR . '/wpforo-syntax-highlighter/wpForoSyntaxHighlighter.php' ),
480 'base' => false,
481 'version' => '3.0.0',
482 'requires' => '2.0.0',
483 'class' => 'wpForoSyntaxHighlighter',
484 'deps' => [],
485 'install_func' => function () {
486 if (
487 class_exists( 'wpForoSyntaxHighlighter' )
488 && function_exists( 'wpForoSyntaxActivation' )
489 && version_compare( WPFORO_VERSION, WPFOROSYNTAX_WPFORO_REQUIRED_VERSION, '>=' )
490 ) {
491 wpForoSyntaxActivation();
492 }
493 },
494 'title' => 'Syntax Highlighter',
495 'thumb' => WPFORO_URL . '/assets/addons/syntax/header.png',
496 'desc' => __( 'Syntax highlighting for forum posts, automatic language detection and multi-language code highlighting.', 'wpforo' ),
497 'url' => 'https://gvectors.com/product/wpforo-syntax-highlighter/',
498 ],
499
500 ];
501
502 if( !is_null( $base ) ){
503 $addons = array_filter($addons, function( $addon ) use ( $base ){
504 return $addon['base'] === $base;
505 });
506 }
507
508 return $addons;
509 }
510
511 function wpforo_get_ajax_actions_list(){
512 $nonces = [];
513 global $wp_filter;
514 if( WPF()->current_userid ){
515 foreach ( $wp_filter as $key => $value ){
516 if( strpos( (string) $key, 'wp_ajax_wpf' ) === 0 ) $nonces[] = preg_replace( '#^wp_ajax_#iu', '', (string) $key, 1 );
517 }
518 }else{
519 foreach ( $wp_filter as $key => $value ){
520 if( strpos( (string) $key, 'wp_ajax_nopriv_wpf' ) === 0 ) $nonces[] = preg_replace( '#^wp_ajax_nopriv_#iu', '', (string) $key, 1 );
521 }
522 }
523
524 return array_unique( (array) apply_filters( 'wpforo_get_ajax_actions_list', $nonces ) );
525 }
526
527 function wpforo_generate_ajax_nonces(){
528 $nonces = [];
529 foreach ( wpforo_get_ajax_actions_list() as $action ){
530 $nonces[ $action ] = wp_create_nonce( $action );
531 }
532
533 return $nonces;
534 }
535
536 function wpforo_verify_form( $action = 'wpforo_verify_form', $query_arg = '_wpfnonce', $mode = 'full' ) {
537 wpforo_verify_nonce( $action, $mode, $query_arg );
538 do_action( 'wpforo_verify_form_end' );
539 }
540
541 function wpforo_verify_nonce( $action = 'wpforo_verify_form', $mode = 'ajax', $query_arg = '_wpfnonce' ) {
542 if( $mode === 'full' ) {
543 if( ! wp_verify_nonce( wpfval( $_REQUEST, $query_arg ), $action ) ) {
544 wpforo_phrase( 'Sorry, something is wrong with your data.' );
545 exit();
546 }
547 }elseif( $mode === 'ajax' ){
548 check_ajax_referer( $action, $query_arg );
549 }
550 if( apply_filters( 'wpforo_check_referer', true )
551 && in_array( $mode, [ 'ajax', 'full', 'ref' ] ) ) {
552 if( ! wpfval( $_SERVER, 'HTTP_REFERER' ) ) {
553 exit( 'Error 2252 | Please contact the forum administrator.' );
554 }
555 if( wpforo_is_url_external( $_SERVER['HTTP_REFERER'] ) ) {
556 exit( 'Error 2253 | Please contact the forum administrator.' );
557 }
558 }
559 do_action( 'wpforo_verify_nonce_end' );
560 }
561
562 /**
563 * @param string $path
564 * @param string|null $route
565 * @param string|null $scheme
566 *
567 * @return string
568 */
569 function wpforo_url( $path = '', $route = null, $scheme = null ) {
570 if( $route ) $route = wpforo_settings_get_slug( $route );
571 if( is_null( $route ) ) $route = WPF()->board->full_route;
572 $path = trim( trim( (string) $route, '/\\' ) . '/' . trim( (string) $path, '/\\' ), '/\\' );
573
574 if( function_exists( 'pll_default_language' ) && function_exists( 'PLL' ) ){
575 if( ! PLL()->options['hide_default'] && $lng = pll_default_language() ) $path = $lng . '/' . $path;
576 }
577
578 $url = home_url( $path, $scheme );
579 //-START- check is url maybe WordPress home
580 $maybe_home_url = trim( preg_replace( '#/?index\.php/?(\?.*)?$#isu', '', (string) $url ), '/\\' );
581 $home_url = trim( home_url( '', $scheme ), '/\\' );
582 if( $maybe_home_url === $home_url ) $url = preg_replace( '#index\.php/?#isu', '', (string) $url, 1 );
583
584 //-END- check is url maybe WordPress home
585
586 return WPF()->user_trailingslashit( $url );
587 }
588
589 /**
590 * @param string $path
591 * @param string|null $scheme
592 *
593 * @return string
594 */
595 function wpforo_home_url( $path = '', $scheme = null ) {
596 if( strpos( (string) $path, 'http' ) === 0 ) {
597 $path = wpforo_get_url_query_vars_str( $path );
598 $path = preg_replace( '#index\.php/?#isu', '', (string) $path, 1 );
599 $path = preg_replace( '#^/?' . preg_quote( trim( (string) WPF()->board->route, '/\\' ) ) . '#isu', '', (string) $path, 1 );
600 }
601
602 return wpforo_url( $path, null, $scheme );
603 }
604
605 function wpforo_is_ajax() {
606 return defined( 'DOING_AJAX' ) && DOING_AJAX;
607 }
608
609 function wpforo_is_admin( $url = '' ) {
610 $url = trim( (string) $url );
611 if( $url ) return strpos( (string) $url, trim( admin_url(), '/' ) ) === 0 || strpos( $url, trim( self_admin_url(), '/' ) ) === 0;
612
613 return is_admin() && ! wpforo_is_ajax();
614 }
615
616 function _is_wpforo_page( $url = '' ) {
617 if( ! $url ) $url = wpforo_get_request_uri();
618 $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 ) ) );
619
620 return apply_filters( 'is_wpforo_page', $result, $url );
621 }
622
623 function is_wpforo_page( $url = '' ) {
624 return wpforo_ram_get( '_is_wpforo_page', $url );
625 }
626
627 function _is_wpforo_exclude_url( $url = '' ) {
628 $result = false;
629 if( ! $url ) $url = wpforo_get_request_uri();
630 $url = urldecode( (string) $url );
631 $url = preg_replace( '#/page/\d*/?$#iu', '', (string) $url );
632 if( $current_url = wpforo_get_url_query_vars_str( $url ) ) {
633 if( preg_match( '#^/?(?:([^\s/?&=<>:\'\"*\\\|]*/)(?1)*)?[^\s/?&=<>:\'\"*\\\|]+\.(?:php|js|css|jpe?g|png|gif|webp|svg|bmp|tiff)/?(?:\?[^/]*)?$#iu', $current_url ) ) {
634 $result = true;
635 } elseif( WPF()->board->get_current( 'is_standalone' ) && ( $excld_urls = WPF()->board->get_current( 'excld_urls' ) ) ) {
636 foreach( $excld_urls as $excld_url ) {
637 $excld_url = urldecode( (string) $excld_url );
638 if( wpforo_is_url_internal( $excld_url ) ) {
639 $excld_url = wpforo_get_url_query_vars_str( $excld_url );
640 $pattern = preg_quote( $excld_url );
641 $pattern = str_replace( [ '/\*', '\*' ], [ '/?[^\r\n\t\s\0]*?', '[^\r\n\t\s\0]*?' ], $pattern );
642 if( preg_match( '#^' . $pattern . '$#iu', $current_url ) ) {
643 $result = true;
644 break;
645 }
646 }
647 }
648 }
649 }
650
651 return $result;
652 }
653
654 function is_wpforo_exclude_url( $url = '' ) {
655 return wpforo_ram_get( '_is_wpforo_exclude_url', $url );
656 }
657
658 function _is_wpforo_url( $url = '' ) {
659 $result = false;
660 if( ! wpforo_is_admin( $url ) ) {
661 if( WPF()->board->get_current( 'is_standalone' ) && ! is_wpforo_exclude_url( $url ) ) {
662 $result = true;
663 } else {
664 $current_url = wpforo_get_url_query_vars_str( $url );
665 $current_url = preg_replace( '#/?\?.*$#isu', '', $current_url );
666 foreach( WPF()->board->routes as $route ) {
667 if( $route && ( $current_url === $route || strpos( (string) $current_url, $route . '/' ) === 0 ) ) {
668 $result = true;
669 break;
670 }
671 }
672 }
673 }
674
675 return $result;
676 }
677
678 function is_wpforo_url( $url = '' ) {
679 return wpforo_ram_get( '_is_wpforo_url', $url );
680 }
681
682 /**
683 * @param string $url
684 *
685 * @return bool
686 */
687 function _is_wpforo_shortcode_page( $url = '' ) {
688 return ! wpforo_is_admin( $url ) && ! is_wpforo_url( $url ) && has_shortcode( wpforo_get_wp_post_content( $url ), 'wpforo' );
689 }
690
691 function is_wpforo_shortcode_page( $url = '' ) {
692 return wpforo_ram_get( '_is_wpforo_shortcode_page', $url );
693 }
694
695 function _wpforo_get_wp_post_content( $url = '' ) {
696 if( ! $url ) $url = wpforo_get_request_uri();
697 $post_content = '';
698 global $post;
699 if( $url === wpforo_get_request_uri() && is_a( $post, 'WP_Post' ) ) {
700 $post_content = $post->post_content;
701 } elseif( $postid = wpforo_wp_url_to_postid( $url ) ) {
702 $post_content = get_post_field( 'post_content', $postid );
703 }
704
705 return $post_content;
706 }
707
708 function wpforo_get_wp_post_content( $url = '' ) {
709 return wpforo_ram_get( '_wpforo_get_wp_post_content', $url );
710 }
711
712
713 /**
714 * @param string $text
715 * @param string $url
716 *
717 * @return array|string
718 */
719 function get_wpforo_shortcode_atts( $text = '', $url = '' ) {
720 if( ! $text ) $text = wpforo_get_wp_post_content( $url );
721 if( preg_match( '#\[[\r\n\t\s\0]*wpforo[\r\n\t\s\0]*([^\[\]]*?)]#iu', $text, $match ) ) {
722 return shortcode_parse_atts( $match[1] );
723 }
724
725 return '';
726 }
727
728 function wpforo_get_url_query_vars_str( $url = '' ) {
729 if( ! $url ) $url = wpforo_get_request_uri();
730 $home_url = preg_replace( '#/?\?.*$#isu', '', home_url( '/' ) );
731 $current_url = preg_replace( '#https?://[^/\?]+/?#isu', '', $url );
732 $site_url = preg_replace( '#https?://[^/\?]+/?#isu', '', $home_url );
733 if( strpos( (string) $current_url, '/' ) === false && $current_url === trim( (string) $site_url, '/' ) ) return '';
734 $current_url = preg_replace( '#^/?' . preg_quote( $site_url ) . '(?:/?index\.php/?)?#isu', '', $current_url, 1 );
735 $current_url = preg_replace( '#^/?' . preg_quote( wpforo_get_query_var_lang() ) . '/#isu', '', $current_url, 1 );
736 $current_url = preg_replace( '#^[\s\0/]*(.*?)[\s\0/]*$#isu', '$1', $current_url );
737
738 return wpforo_fix_url( $current_url );
739 }
740
741 function wpforo_get_url_route( $url = '' ) {
742 $route = '';
743 $query_vars_str = preg_replace( '#/?\?.*$#isu', '', wpforo_get_url_query_vars_str( (string) $url ) );
744 $query_vars = array_values( array_filter( explode( '/', trim( (string) $query_vars_str, '/' ) ) ) );
745 if( array_key_exists( 0, $query_vars ) && in_array( $query_vars[0], WPF()->board->routes ) ) $route = $query_vars[0];
746
747 return $route;
748 }
749
750 function wpforo_dir_size( $directory ) {
751 $size = 0;
752 if( is_dir( $directory ) && class_exists( 'RecursiveIteratorIterator' ) && class_exists( 'RecursiveDirectoryIterator' ) ) {
753 foreach( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $directory ) ) as $file ) $size += $file->getSize();
754 }
755
756 return $size;
757 }
758
759 function wpforo_make_hidden_fields_from_url( $url = '', $echo = true ) {
760 if( ! $url ) $url = wpforo_get_request_uri();
761 $return = '';
762 if( $url_query = parse_url( $url, PHP_URL_QUERY ) ) {
763 parse_str( $url_query, $url_query_arr );
764 if( ! empty( $url_query_arr ) ) {
765 foreach( $url_query_arr as $key => $value ) {
766 $return .= '<input type="hidden" name="' . $key . '" value="' . $value . '">';
767 }
768 }
769 }
770 if( ! $echo ) return $return;
771 echo $return;
772 }
773
774 /**
775 * Returns merged arguments array from defined and default arguments.
776 *
777 * @param mixed $args
778 * @param array $default
779 *
780 * @return array
781 */
782 function wpforo_parse_args( $args, $default = [] ) {
783 if( is_object( $args ) ) {
784 $defined = get_object_vars( $args );
785 } elseif( is_array( $args ) ) {
786 $defined = $args;
787 } elseif( preg_match( '|^[\d,\s]+$|', (string) $args ) ) {
788 $defined = explode( ',', trim( (string) $args ) );
789 } elseif( is_integer( $args ) || is_float( $args ) ) {
790 $defined[0] = $args;
791 } elseif( is_serialized( $args ) ) {
792 $defined = unserialize( $args );
793 } elseif( strpos( (string) $args, '=' ) !== false ) {
794 parse_str( $args, $defined );
795 } else {
796 $defined = (array) $args;
797 }
798 if( ! empty( $default ) ) {
799 return array_merge( $default, $defined );
800 } else {
801 return $defined;
802 }
803 }
804
805 /**
806 * Detects serialized data
807 *
808 * @param string
809 *
810 * @return boolean
811 * @since 1.0.0
812 *
813 */
814 if( ! function_exists( 'is_serialized' ) ) {
815 function is_serialized( $value ) {
816 if( $value == '' ) return false;
817 $value = trim( (string) $value );
818 $chsd = @unserialize( $value );
819 if( $chsd !== false || $value === 'b:0;' ) {
820 return true;
821 } else {
822 return false;
823 }
824 }
825 }
826
827 function wpforo_get_request_uri( $with_port = false, $get_referer_when_ajax = true ) {
828 if( $get_referer_when_ajax && wpforo_is_ajax() ) {
829 if( $referer = wpfval( $_REQUEST, 'referer' ) ) {
830 $referer = preg_replace( '#\#[^/?&]*$#iu', '', (string) $referer );
831
832 return esc_url_raw( $referer );
833 }
834 if( $referer = wpfval( $_SERVER, 'HTTP_REFERER' ) ) {
835 $url = preg_replace( '#\#[^/?&]*$#iu', '', (string) $referer );
836
837 return esc_url_raw( $url );
838 }
839 }
840 $s = is_ssl() ? 's' : '';
841 $sp = strtolower( (string) wpfval( $_SERVER, 'SERVER_PROTOCOL' ) );
842 $protocol = substr( $sp, 0, strpos( (string) $sp, "/" ) ) . $s;
843 $server_port = intval( wpfval( $_SERVER, 'SERVER_PORT' ) );
844 $port = $server_port === 80 ? "" : ":" . $server_port;
845 $host = preg_replace( '#^www\.#iu', '', (string) wpfval( $_SERVER, 'HTTP_HOST' ) );
846 if( strpos( home_url(), 'www.' ) !== false ) $host = 'www.' . $host;
847 $requri = wpfval( $_SERVER, 'UNENCODED_URL' ) ?: wpfval( $_SERVER, 'REQUEST_URI' );
848 $url = $protocol . "://" . $host . ( $with_port && $server_port ? $port : '' ) . $requri;
849 $url = wpforo_fix_url( $url );
850
851 return esc_url_raw( $url );
852 }
853
854 function wpforo_arr_group_by( $array, $key_by ) {
855 if( ! empty( $array ) ) {
856 $fltrd = [];
857 foreach( $array as $key => $arr ) {
858 if( is_numeric( $key ) ) $fltrd[] = $arr[ $key_by ];
859 }
860 $uniq_arr = array_unique( $fltrd );
861 asort( $uniq_arr );
862
863 return $uniq_arr;
864 }
865 }
866
867 function wpforo_phrase( $key, $echo = true, $format = 'first-upper' ) {
868 $locale = WPF()->locale;
869 $phrase = ( isset( WPF()->phrase->phrases[ addslashes( strtolower( (string) $key ) ) ] ) ) ? WPF()->phrase->phrases[ addslashes( strtolower( (string) $key ) ) ] : $key;
870 if( 'en_US' !== $locale ) {
871 $native = $phrase;
872 $backtrace = wp_debug_backtrace_summary();
873 $mopo_domain = 'wpforo';
874 if( strpos( (string) $backtrace, '\plugins\wpforo-private-messages\\' ) !== false || strpos( (string) $backtrace, '/plugins/wpforo-private-messages/' ) !== false ) $mopo_domain = 'wpforo_pm';
875 if( strpos( (string) $backtrace, 'wpForoPolls' ) !== false ) $mopo_domain = 'wpforo_poll';
876 $key = preg_replace( "/(^\s+)|(\s+$)/u", "", (string) $key );
877 $phrase = preg_replace( "/(^\s+)|(\s+$)/u", "", (string) $phrase );
878 if( strtolower( (string) $key ) === strtolower( (string) $phrase ) ) {
879 $phrase = __( $key, $mopo_domain );
880 if( strtolower( (string) $key ) === strtolower( (string) $phrase ) ) {
881 $key = strtolower( (string) $key );
882 $phrase = __( $key, $mopo_domain );
883 if( strtolower( (string) $key ) === strtolower( (string) $phrase ) ) {
884 $phrase = __( ucfirst( (string) $key ), $mopo_domain );
885 if( strtolower( (string) $key ) === strtolower( (string) $phrase ) ) {
886 $phrase = __( $native, $mopo_domain ); //Try all, if no result pass the original text to translation again.
887 }
888 }
889 }
890 }
891 }
892
893 if( $format === 'first-upper' ) {
894 if( 'en_US' !== $locale && function_exists( 'mb_strlen' ) && mb_strlen( (string) $phrase ) !== strlen( (string) $phrase ) && function_exists( 'mb_strtoupper' ) ) {
895 $phrase = mb_strtoupper( mb_substr( (string) $phrase, 0, 1 ) ) . mb_substr( (string) $phrase, 1 );
896 } else {
897 $phrase = ucfirst( (string) $phrase );
898 }
899 } elseif( $format === 'upper' ) {
900 if( function_exists( 'mb_strtoupper' ) ) {
901 $phrase = mb_strtoupper( (string) $phrase );
902 } else {
903 $phrase = strtoupper( (string) $phrase );
904 }
905 } elseif( $format === 'lower' ) {
906 if( function_exists( 'mb_strtolower' ) ) {
907 $phrase = mb_strtolower( (string) $phrase );
908 } else {
909 $phrase = strtolower( (string) $phrase );
910 }
911 }
912
913 $phrase = str_replace( '{number}', '', $phrase );
914
915 if( $echo ) echo $phrase;
916 return $phrase;
917 }
918
919 function wpforo_screen_option() { ?>
920 <div id="screen-meta" class="metabox-prefs" style="display: none; ">
921 <div id="screen-options-wrap" class="hidden" tabindex="-1" aria-label="Screen Options Tab" style="display: none; ">
922 <form id="adv-settings" action="" method="POST">
923 <input type="hidden" name="wpfaction" value="dashboard_options_save">
924 <h5><?php _e( 'Show on screen', 'wpforo' ) ?></h5>
925
926 <div class="screen-options">
927 <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 ) ?>">
928 <label for="edit_post_per_page"><?php _e( 'Items', 'wpforo' ) ?></label>
929 <input type="submit" id="screen-options-apply" class="button" value="<?php _e( 'Apply', 'wpforo' ) ?>">
930 </div>
931 </form>
932 </div>
933 </div>
934
935 <div id="screen-meta-links">
936 <div id="screen-options-link-wrap" class="hide-if-no-js screen-meta-toggle" style="">
937 <a href="#screen-options-wrap" id="show-settings-link" class="show-settings screen-meta-active" aria-controls="screen-options-wrap" aria-expanded="true">
938 <?php _e( 'Screen Options', 'wpforo' ) ?>
939 </a>
940 </div>
941 </div>
942
943 <?php
944 }
945
946 function wpforo_strip_shortcodes( $text, $only_wpforo_shortcodes = false ) {
947 $text = (string) apply_filters( 'wpforo_strip_shortcodes', (string) $text, $only_wpforo_shortcodes );
948 $text = preg_replace( '#\[attach[^\[\]]*][^\[\]]*\[/attach]#iu', '', $text );
949 $text = preg_replace( '#\[spoiler[^\[\]]*][^\[\]]*\[/spoiler]#iu', '', $text );
950 $text = preg_replace( '#\[quote[^\[\]]*][^\[\]]*\[/quote]#iu', '', $text );
951 $text = preg_replace( '#\[wpfgiphy[^\[\]]+?]#iu', '', $text );
952 $text = preg_replace( '#\[wpftenor[^\[\]]+?]#iu', '', $text );
953 // $text = preg_replace('#(?:{\w+})?:[^/.\s]+:#iu', '', $text);
954 if( ! $only_wpforo_shortcodes ) $text = strip_shortcodes( $text );
955
956 return $text;
957 }
958
959 function wpforo_strip_quotes( $text ) {
960 $text = preg_replace( '#<(blockquote)[^<>]*?data-userid[^<>]*?>(?:.*?(?R)*.*?)*?</\1>#isu', '', (string) $text );
961 return preg_replace( '#\[(quote)[^\[\]]*?data-userid[^\[\]]*?](?:.*?(?R)*.*?)*?\[/\1]#isu', '', (string) $text );
962 }
963
964 function wpforo_strip_urls( $text ) {
965 return preg_replace( '#(?:[^\'\"]|^)(https?://[^\s\'\"<>]+)(?:[^\'\"]|$)#iu', '', (string) $text );
966 }
967
968 function wpforo_text( $text, $length = 0, $echo = true, $strip_tags = true, $strip_urls = true, $strip_shortcodes = true, $strip_quotes = true ) {
969 $text = (string) $text;
970 $text = str_replace( '</p>', '</p> ', $text );
971 $text = str_replace( '</div>', '</div> ', $text );
972
973 if( $strip_quotes ) $text = wpforo_strip_quotes( $text );
974 if( $strip_urls ) $text = wpforo_strip_urls( $text );
975 if( $strip_tags ) $text = strip_tags( (string) $text );
976 if( $strip_shortcodes ) $text = wpforo_strip_shortcodes( $text );
977
978 $text = apply_filters( 'wpforo_text', $text, $length, $echo, $strip_tags, $strip_urls, $strip_shortcodes, $strip_quotes );
979
980 $text = trim( str_replace( "\xc2\xa0", ' ', (string) $text ) );
981
982 if( ! $length ) {
983 $text = trim( (string) $text );
984 }elseif( function_exists( 'mb_substr' ) ) {
985 $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 ? '...' : '' ) );
986 } else {
987 $text = trim( substr( (string) $text, 0, $length ) . ( strlen( (string) $text ) > $length ? '...' : '' ) );
988 }
989
990 if( $echo ) echo $text;
991 return $text;
992 }
993
994 function wpforo_admin_options_tabs( $tabs, $current = 'general', $subtab = false, $sub_current = 'general' ) {
995 if( ! empty( $tabs ) ) {
996 $class_attr = $subtab ? 'vert_tab' : '';
997 echo '<h2 class="nav-tab-wrapper ' . esc_attr( $class_attr ) . '">';
998 foreach( $tabs as $tab => $name ) {
999 $class = ( $tab === $current || ( $subtab && $tab === $sub_current ) ) ? ' nav-tab-active' : '';
1000 $sub = $subtab ? '&subtab=' . esc_attr( $tab ) : '';
1001 $class = esc_attr( $class );
1002 $current = esc_attr( $current );
1003 $tab = esc_attr( $tab );
1004 $sub = esc_attr( $sub );
1005 echo "<a class='nav-tab $class $class_attr' href='?page=" . (string) wpfval( $_GET, 'page' ) . "&tab=" . ( $subtab ? $current : $tab ) . "$sub'>$name</a>";
1006 }
1007 echo '</h2>';
1008 }
1009 }
1010
1011 function wpforo_admin_tools_tabs( $tabs, $current = 'antispam', $subtab = false, $sub_current = 'antispam' ) {
1012 if( ! empty( $tabs ) ) {
1013 $class_attr = $subtab ? 'vert_tab' : '';
1014 echo '<h2 class="nav-tab-wrapper ' . esc_attr( $class_attr ) . '">';
1015 foreach( $tabs as $tab => $name ) {
1016 $class = ( $tab == $current || ( $subtab && $tab == $sub_current ) ) ? ' nav-tab-active' : '';
1017 $sub = $subtab ? '&subtab=' . esc_attr( $tab ) : '';
1018 $class = esc_attr( $class );
1019 $current = esc_attr( $current );
1020 $tab = esc_attr( $tab );
1021 $sub = esc_attr( $sub );
1022 $name = esc_html( $name );
1023 echo "<a class='nav-tab$class $class_attr' href='?page=" . wpforo_prefix_slug( 'tools' ) . "&tab=" . ( $subtab ? $current : $tab ) . "$sub' style='float:left'>$name</a>";
1024 }
1025 echo '</h2>';
1026 }
1027 }
1028
1029 function wpforo_content_filter( $content, $post = [] ) {
1030 $content = (string) $content;
1031 if( strpos( (string) $content, '../' ) !== false ) {
1032 $home_url = trim( preg_replace( [ '#/?\?.*$#isu', '#index\.php/?#isu' ], '', home_url() ), '/\\' ) . '/';
1033 $content = preg_replace( '#((?:href|src)=[\'\"])(?:https?://)?(?:\.+/)+wp-content/#i', "$1" . $home_url . "wp-content/", (string) $content );
1034 }
1035 $content = apply_filters( 'wpforo_body_text_filter', $content, $post );
1036 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 );
1037 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 );
1038 if( preg_match_all( '#<pre([^<>]*)>(.*?class=[\'"]wpforo-auto-embeded[^\'"]*[\'"].*?)</pre>#isu', (string) $content, $matches, PREG_SET_ORDER ) ) {
1039 foreach( $matches as $match ) {
1040 $match[2] = preg_replace( '#<img[^<>]*class=[\'"]wpforo-auto-embeded-image[\'"][^<>]*src=[\'"]([^\'"]*)[\'"][^<>]*>#isu', '$1', $match[2] );
1041 $match[2] = preg_replace( '#<a[^<>]*class=[\'"]wpforo-auto-embeded-link[\'"][^<>]*href=[\'"]([^\'"]*)[\'"][^<>]*>.*?</a>#isu', '$1', $match[2] );
1042 $content = str_replace( $match[0], '<pre' . $match[1] . '>' . $match[2] . '</pre>', $content );
1043 }
1044 }
1045 $content = preg_replace( '#(<a[^<>]*>[^<>]*)<a[^<>]*class=[\'"]wpforo-auto-embeded-link[\'"][^<>]*href=[\'"]([^\'"]*)[\'"][^<>]*>[^<>]*</a>([^<>]*</a>)#iu', '$1$2$3', (string) $content );
1046 $content = apply_filters( 'wpforo_content_filter', $content, $post );
1047
1048 return wpautop( $content );
1049 }
1050
1051 function wpforo_remove_links( $content ) {
1052 return preg_replace( '#([^\'\"]|^)(https?://[^\s\'\"<>]+)([^\'\"]|$)#isu', '$1 [' . wpforo_phrase( 'removed link', false ) . '] $3', (string) $content );
1053 }
1054
1055 add_filter( 'wpforo_content_filter', 'wpforo_nofollow_tag', 20 );
1056 function wpforo_nofollow_tag( $content ) {
1057 return preg_replace_callback( '#<a[^><]*?href=[\'\"]([^\'\"]+)[\'\"][^><]*?>#isu', 'wpforo_nofollow', (string) $content );
1058 }
1059
1060 function wpforo_nofollow( $match ) {
1061 $ret = $match[0];
1062 if( apply_filters( 'wpforo_external_link_nofollow', true ) ) {
1063 $dofollow = wpforo_setting( 'seo', 'dofollow' );
1064 $request_uri_parse = parse_url( wpforo_get_request_uri() );
1065 $main_host = preg_replace( '#^.*?([^.]+?\.[^.]+?)$#isu', '$1', (string) $request_uri_parse['host'] );
1066 $link_url = parse_url( $match[1] );
1067 if( strpos( (string) $match[1], $main_host ) === false && ! ( ! empty( $dofollow ) && ! empty( $link_url['host'] ) && in_array( $link_url['host'], $dofollow ) ) ) {
1068 $ret = preg_replace_callback(
1069 '#\srel=[\'\"]([^\'\"]*?)[\'\"]#iu',
1070 function( $m ) {
1071 $rels = array_filter( preg_split( '#\s#u', $m[1] ) );
1072 $rels = array_merge( [ 'nofollow' ], $rels );
1073
1074 return sprintf( 'rel="%1$s"', implode( ' ', $rels ) );
1075 },
1076 (string) $match[0],
1077 1,
1078 $count
1079 );
1080 if( ! $count ) $ret = str_replace( '>', ' rel="nofollow">', $match[0] );
1081 }
1082 }
1083
1084 return $ret;
1085 }
1086
1087 add_action( 'wpforo_actions_end', 'wpforo_logs', 10 );
1088 function wpforo_logs() {
1089 if( ! WPF()->current_object['is_404'] ) {
1090 WPF()->log->read();
1091 WPF()->log->visit();
1092 }
1093 }
1094
1095 add_action( 'wpforo_bottom_hook', 'wpforo_user_logging' );
1096 function wpforo_user_logging() {
1097 $data = WPF()->current_object;
1098 $filter_views = apply_filters( 'wpforo_filter_topic_views', true );
1099 if( wpfval( $data, 'template' ) && $data['template'] == 'post' && wpfval( $data, 'topicid' ) ) {
1100 if( $filter_views ) {
1101 //to-do: don't increase views before all read point.
1102 if( wpforo_setting( 'legal', 'cookies' ) ) {
1103 $viwed_ids = wpforo_getcookie( wpforo_prefix( 'read_topics' ), false );
1104 if( empty( $viwed_ids ) || ! wpfval( $viwed_ids, $data['topicid'] ) ) {
1105 WPF()->db->query( "UPDATE `" . WPF()->tables->topics . "` SET `views` = `views` + 1 WHERE `topicid` = " . intval( $data['topicid'] ) );
1106 }
1107 } elseif( is_user_logged_in() ) {
1108 if( wpfval( WPF()->current_usermeta, wpforo_prefix( 'read_topics' ) ) ) {
1109 $viwed_db_ids = wpforo_current_usermeta( wpforo_prefix( 'read_topics' ) );
1110 if( empty( $viwed_db_ids ) || ! wpfval( $viwed_db_ids, $data['topicid'] ) ) {
1111 WPF()->db->query( "UPDATE `" . WPF()->tables->topics . "` SET `views` = `views` + 1 WHERE `topicid` = " . intval( $data['topicid'] ) );
1112 }
1113 } else {
1114 WPF()->db->query( "UPDATE `" . WPF()->tables->topics . "` SET `views` = `views` + 1 WHERE `topicid` = " . intval( $data['topicid'] ) );
1115 }
1116 }
1117 } else {
1118 WPF()->db->query( "UPDATE `" . WPF()->tables->topics . "` SET `views` = `views` + 1 WHERE `topicid` = " . intval( $data['topicid'] ) );
1119 }
1120 }
1121 }
1122
1123 function wpforo_setcookie( $key, $args = [], $implode = false ) {
1124 if( ! wpforo_setting( 'legal', 'cookies' ) ) return;
1125 if( ! empty( $args ) && is_array( $args ) ) {
1126 $num = count( $args );
1127 if( $num > 3 ) {
1128 $max = apply_filters( 'wpforo_cookie_max_logged_topics', 10 );
1129 $delta = $num - $max;
1130 if( $delta > 0 ) $args = array_slice( $args, $delta, null, true );
1131 }
1132 }
1133 if( ! empty( $args ) && is_array( $args ) && $implode ) {
1134 $value = trim( implode( ',', $args ), ',' );
1135 } elseif( ! empty( $args ) && is_array( $args ) && ! $implode ) {
1136 $value = json_encode( $args );
1137 }
1138 if( ! isset( $value ) ) $value = '';
1139 if( $key ) {
1140 $secure = is_ssl();
1141 $secure_logged_in_cookie = $secure && 'https' === parse_url( get_option( 'home' ), PHP_URL_SCHEME );
1142 if( COOKIEPATH != SITECOOKIEPATH ) {
1143 @setcookie( $key, $value, time() + 7776000, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true );
1144 } else {
1145 @setcookie( $key, $value, time() + 7776000, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true );
1146 }
1147 }
1148 }
1149
1150 function wpforo_getcookie( $key, $explode = false ) {
1151 if( ! wpforo_setting( 'legal', 'cookies' ) ) return false;
1152 if( $cookie = wpfval( $_COOKIE, $key ) ) {
1153 if( $explode ) {
1154 return explode( ',', $cookie );
1155 } else {
1156 $cookie = wp_unslash( $cookie );
1157 if( ! $data = json_decode( $cookie, true ) ) return $cookie;
1158
1159 return $data;
1160 }
1161 }
1162
1163 return false;
1164 }
1165
1166 function wpforo_is_bot() {
1167 if( ! $http_user_agent = wpfval( $_SERVER, 'HTTP_USER_AGENT' ) ) $http_user_agent = wpfval( $_SERVER, 'http_user_agent' );
1168 if( $http_user_agent ) {
1169 return preg_match(
1170 '#(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|\.\.\.\.)#i',
1171 (string) $http_user_agent
1172 );
1173 }
1174
1175 return true;
1176 }
1177
1178 //Option value filter and output
1179 function wpfo( $option = '', $echo = true, $esc = 'esc_attr' ) {
1180 if( is_string( $option ) ) {
1181 $option = stripslashes( (string) $option );
1182 if( $esc === 'esc_attr' ) {
1183 $option = esc_attr( $option );
1184 } elseif( $esc === 'esc_html' ) {
1185 $option = esc_html( $option );
1186 } elseif( $esc === 'esc_url' ) {
1187 $option = esc_url( (string) $option );
1188 } elseif( $esc === 'esc_textarea' ) {
1189 $option = esc_textarea( (string) $option );
1190 }
1191 }
1192
1193 if( $echo ) {
1194 echo $option;
1195 } else {
1196 return $option;
1197 }
1198 }
1199
1200 //Option maker for checkbox, radio and select
1201 function wpfo_check( $option = '', $value = '', $type = 'checked', $echo = true ) {
1202 $option = ( isset( $option ) && isset( $value ) && $option == $value ) ? $type : '';
1203 if( $echo ) {
1204 echo $option;
1205 } else {
1206 return $option;
1207 }
1208 }
1209
1210 /**
1211 * Validates keys of requested array.
1212 *
1213 * @param array $array
1214 * @param string|int $key
1215 * @param string|int ... $_, ... [optional] more keys
1216 *
1217 * @return bool
1218 */
1219 function wpfkey( &$array, $key ) {
1220 $a = $array;
1221 foreach( func_get_args() as $arg_num => $key ) {
1222 if( $arg_num === 0 ) continue;
1223 if( is_array( $a ) && ( is_string( $key ) || is_int( $key ) ) && array_key_exists( $key, $a ) ) {
1224 $a = $a[ $key ];
1225 } else {
1226 return false;
1227 }
1228 }
1229
1230 return true;
1231 }
1232
1233 /**
1234 * get values of requested array keys if found otherwise returns null to allow you cast to your desired type of variable.
1235 *
1236 * @param mixed $array
1237 * @param string|int ... $_, ... [optional] more keys
1238 *
1239 * @return mixed|null
1240 */
1241 function wpfval( &$array ) {
1242 if( func_num_args() === 1 ) return $array;
1243 $a = $array;
1244 foreach( func_get_args() as $arg_num => $key ) {
1245 if( $arg_num === 0 ) continue;
1246 if( is_array( $a ) && ( is_string( $key ) || is_int( $key ) ) && array_key_exists( $key, $a ) ) {
1247 $a = $a[ $key ];
1248 } else {
1249 return null;
1250 }
1251 }
1252
1253 return $a;
1254 }
1255
1256 /**
1257 * Always return the preferred default value if the array, or array index is undefined.
1258 *
1259 * @param mixed the default value if current index is undefined in the array
1260 * @param mixed the $array to be checked
1261 * @param string|int ... $_, ... [optional] more keys
1262 *
1263 * @return mixed|null
1264 */
1265 function wpffix( $return, $array, $a = null, $b = null, $c = null ) {
1266 $value = wpfval( $array, $a, $b, $c );
1267 if( ! is_null( $value ) ) {
1268 return $value;
1269 }
1270
1271 return $return;
1272 }
1273
1274 function wpforo_human_filesize( $bytes, $decimals = 2 ) {
1275 $size = [ 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' ];
1276 $factor = floor( ( strlen( (string) $bytes ) - 1 ) / 3 );
1277
1278 return sprintf( "%.{$decimals}f", $bytes / pow( 1024, $factor ) ) . '&nbsp;' . @$size[ $factor ];
1279 }
1280
1281 function wpforo_human_time_diff( int $from, bool $ago = true, int $to = 0 ): string{
1282 if ( ! $to ) $to = time();
1283 $d = human_time_diff( $from, $to );
1284 if( $ago ) $d = sprintf( wpforo_phrase( '%s ago', false, false ), $d );
1285
1286 return $d;
1287 }
1288
1289 /**
1290 * @param int | string $date
1291 *
1292 * @return int
1293 */
1294 function wpforo_gmtimestamp( $date = 0 ): int{
1295 if( ! $date ) $date = time();
1296 return intval( is_numeric( $date ) ? $date : strtotime( $date . ' GMT' ) );
1297 }
1298
1299 function wpforo_timezone( string $timezone = '' ): array{
1300 $timezone_string = '';
1301 $current_offset = null;
1302 if( $timezone ) {
1303 if( $timezone === 'wp' ){
1304 $timezone_string = get_option( 'timezone_string', '' );
1305 $current_offset = get_option( 'gmt_offset', null );
1306 }else{
1307 if( preg_match( '#UTC\s*([-+])\s*([\d.]+)#i', (string) $timezone, $timezone_array ) ) {
1308 $current_offset = $timezone_array[1] . $timezone_array[2];
1309 } elseif( in_array( $timezone, timezone_identifiers_list() ) ) {
1310 $timezone_string = $timezone;
1311 }
1312 }
1313 }
1314
1315 return compact( 'timezone_string', 'current_offset' );
1316 }
1317
1318 /**
1319 * @param int | string $date
1320 * @param string $timezone
1321 *
1322 * @return int
1323 */
1324 function wpforo_timestamp( $date = 0, string $timezone = '' ): int{
1325 $timestamp = wpforo_gmtimestamp( $date );
1326 $tmz = wpforo_timezone( $timezone );
1327
1328 if( is_numeric( $tmz['current_offset'] ) ) {
1329 $timestamp += $tmz['current_offset'] * 3600;
1330 } elseif( $tmz['timezone_string'] && class_exists( 'DateTime' ) && class_exists( 'DateTimeZone' ) ) {
1331 try {
1332 $dt = new DateTime( gmdate( 'Y-m-d H:i:s', $timestamp ), new DateTimeZone( $tmz['timezone_string'] ) );
1333 $timestamp += $dt->getOffset();
1334 } catch ( Exception $e ) {
1335 }
1336 }
1337
1338 return (int) $timestamp;
1339 }
1340
1341 /**
1342 * @param int $gmttime
1343 * @param string $format
1344 * @param bool $wp_date_format
1345 *
1346 * @return string
1347 */
1348 function wpforo_date_format( int $gmttime, string $format, bool $wp_date_format = true ): string {
1349 if ( $format === 'mysql' ){
1350 return 'Y-m-d H:i:s';
1351 } elseif( in_array( $format, ['human','ago','timestamp'], true ) ){
1352 return $format;
1353 }
1354
1355 if( $format === 'ago-date' ) {
1356 $diff = time() - $gmttime;
1357 if( $diff > 31536000 ) {
1358 return apply_filters( 'wpforo_date_format_ago_date_year', 'M d, y' );
1359 } elseif( $diff > 518400 ) {
1360 return apply_filters( 'wpforo_date_format_ago_date_month', 'M d' );
1361 } else {
1362 return 'ago';
1363 }
1364 }
1365
1366 $sep = ' ';
1367 if( $wp_date_format ) {
1368 $date_format = get_option( 'date_format' );
1369 $time_format = get_option( 'time_format' );
1370 } else {
1371 $date_format = 'Y-m-d';
1372 $time_format = 'H:i:s';
1373 }
1374 if( $format === 'datetime' ) {
1375 $format = $date_format . $sep . $time_format;
1376 } elseif( $format === 'date' ) {
1377 $format = $date_format;
1378 } elseif( $format === 'time' ) {
1379 $format = $time_format;
1380 } elseif( $wp_date_format ) {
1381 $format = $date_format . $sep . $time_format;
1382 }
1383
1384 return $format;
1385 }
1386
1387 /**
1388 * @param $date string | int
1389 * @param $format string
1390 * @param $timezone string
1391 *
1392 * @return string | int
1393 */
1394 function _wpforo_date( $date = 0, string $format = 'Y-m-d H:i:s', string $timezone = '' ) {
1395 if( in_array( $format, ['human','ago'], true ) ){
1396 $d = wpforo_human_time_diff( wpforo_gmtimestamp( $date ), $format === 'ago' );
1397 } else {
1398 $timestamp = wpforo_timestamp( $date, $timezone );
1399 if( $format === 'timestamp' ){
1400 $d = $timestamp;
1401 }else{
1402 if( $format === 'mysql' ) $format = 'Y-m-d H:i:s';
1403 $d = date_i18n( $format, $timestamp );
1404 }
1405 }
1406
1407 return $d;
1408 }
1409
1410 /**
1411 * @param int | string $date
1412 * @param string $format
1413 * @param bool $echo
1414 * @param bool $wp_date_format
1415 * @param string|null $timezone
1416 *
1417 * @return string | int
1418 */
1419 function wpforo_date( $date, $format = 'ago', $echo = true, $wp_date_format = true, $timezone = null ) {
1420 $wp_date_format = $wp_date_format && wpforo_setting( 'general', 'wp_date_format' );
1421 $format = wpforo_date_format( wpforo_gmtimestamp( $date ), $format, $wp_date_format );
1422
1423 if( is_null( $timezone ) ) $timezone = ( (string) wpfval( WPF()->current_user, 'timezone' ) ?: ( (string) wpfval($_COOKIE, 'wpforo_browser_timezone') ?: 'wp') );
1424 $timezone = apply_filters( 'wpforo_date_timezone', $timezone );
1425
1426 $d = _wpforo_date( $date, $format, $timezone );
1427 $d = apply_filters( 'wpforo_date', $d, $date, $format, $timezone );
1428
1429 if( $echo ) echo $d;
1430 return $d;
1431 }
1432
1433 if( ! function_exists( 'wpforo_date_raw' ) ) {
1434 /**
1435 * @param string|int $date
1436 * @param string $format
1437 * @param bool $echo
1438 * @param string|null $timezone
1439 *
1440 * @return string | int
1441 */
1442 function wpforo_date_raw( $date, $format = 'ago', $echo = true, $timezone = null ) {
1443 return wpforo_date( $date, $format, $echo, false, $timezone );
1444 }
1445 }
1446
1447 function wpforo_write_file( string $new_file, $content ): array {
1448 $return = [ 'error' => '', 'file' => '' ];
1449 $new_file = wpforo_fix_dir_sep( $new_file );
1450 $dir = dirname( $new_file );
1451 if( ! is_dir( $dir ) ) wp_mkdir_p( $dir );
1452 $ifp = @fopen( $new_file, 'wb' );
1453 if( ! $ifp ) {
1454 if( is_resource( $ifp ) ) {
1455 @fclose( $ifp );
1456 }
1457 $return = [ 'error' => sprintf( __( 'Could not write file %s' ), $new_file ) ];
1458 } else {
1459 @fwrite( $ifp, $content );
1460 fclose( $ifp );
1461 clearstatcache();
1462 // Set correct file permissions
1463 $stat = @stat( dirname( $new_file ) );
1464 $perms = $stat['mode'] & 0007777;
1465 $perms = $perms & 0000666;
1466 @chmod( $new_file, $perms );
1467 clearstatcache();
1468 $return['file'] = $new_file;
1469 }
1470
1471 return $return;
1472 }
1473
1474 function wpforo_get_file_content( $file ) {
1475 if( is_file( $file ) ) {
1476 $fp = @fopen( $file, 'r' );
1477 if( ! $fp ) {
1478 if( is_resource( $fp ) ) {
1479 @fclose( $fp );
1480 }
1481 } else {
1482 $size = @filesize( $file );
1483 if( $size > 0 ) {
1484 $file_data = fread( $fp, $size );
1485 fclose( $fp );
1486
1487 return $file_data;
1488 }
1489 @fclose( $fp );
1490 }
1491 }
1492
1493 return false;
1494 }
1495
1496 ################################################################################
1497 /**
1498 * Clears file basename and removes trailing slash
1499 *
1500 * @param string filename
1501 *
1502 * @return string
1503 * @since 1.0.0
1504 *
1505 */
1506 function wpforo_clear_basename( $file ) {
1507 $file = (string) $file;
1508 $file = str_replace( '\\', '/', $file );
1509 $file = preg_replace( '|/+|', '/', $file );
1510 $file = trim( (string) $file, '/' );
1511
1512 return $file;
1513 }
1514
1515 #################################################################################
1516 /**
1517 * Removes directory with all files and folders
1518 *
1519 * @param string directory name
1520 *
1521 * @since 1.0.0
1522 *
1523 */
1524 function _wpforo_remove_directory( $directory ) {
1525 $directory = (string) $directory;
1526 $directory = wpforo_fix_dir_sep( $directory );
1527 $directory_ns = trim( $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;
1528 $directory_ws = DIRECTORY_SEPARATOR . trim( $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;
1529 $glob = glob( $directory_ns . '*' );
1530 if( empty( $glob ) ) $glob = glob( $directory_ws . '*' );
1531 foreach( $glob as $item ) {
1532 if( is_dir( $item ) ) {
1533 _wpforo_remove_directory( $item );
1534 } else {
1535 unlink( $item );
1536 }
1537 }
1538
1539 return rmdir( $directory );
1540 }
1541
1542 #################################################################################
1543 /**
1544 * Removes directory with all files and folders
1545 *
1546 * @param string directory name
1547 *
1548 * @since 1.0.0
1549 *
1550 */
1551 function wpforo_remove_directory( $file, $recursive = true ) {
1552 if( ! class_exists( 'WP_Filesystem_Direct' ) ) {
1553 require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
1554 require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
1555 }
1556 $file = wpforo_fix_dir_sep( $file );
1557 $WP_Filesystem_Direct = new WP_Filesystem_Direct( null );
1558 return $WP_Filesystem_Direct->delete( $file, $recursive );
1559 }
1560
1561 #################################################################################
1562 /**
1563 * Converts bytes to KB, MB, GB
1564 *
1565 * @param integer Bytes
1566 *
1567 * @return string
1568 * @since 1.0.0
1569 *
1570 */
1571 function wpforo_print_size( $value, $points = true ) {
1572 if( $value < 1024 ) {
1573 return $value . ( ( $points ) ? "B" : '' );
1574 } elseif( $value >= 1024 && $value < ( 1024 * 1024 ) ) {
1575 $value = round( ( $value / 1024 ) * 10 ) / 10;
1576
1577 return $value . ( ( $points ) ? "KB" : '' );
1578 } elseif( $value >= 1024 * 1024 && $value < 1024 * 1024 * 1024 ) {
1579 $value = round( ( $value / ( 1024 * 1024 ) ) * 10 ) / 10;
1580
1581 return $value . ( ( $points ) ? "MB" : '' );
1582 } elseif( $value >= 1024 * 1024 * 1024 && $value <= 1024 * 1024 * 1024 * 1024 ) {
1583 $value = round( ( $value / ( 1024 * 1024 * 1024 ) ) * 10 ) / 10;
1584
1585 return $value . ( ( $points ) ? "GB" : '' );
1586 } else {
1587 $value = round( ( $value / ( 1024 * 1024 * 1024 * 1024 ) ) * 10 ) / 10;
1588
1589 return $value . ( ( $points ) ? "TB" : '' );
1590 }
1591 }
1592
1593 function wpforo_human_size_to_bytes( $sSize ) {
1594 if( is_numeric( $sSize ) ) return $sSize;
1595
1596 $sSuffix = substr( (string) $sSize, - 1 );
1597 $iValue = substr( (string) $sSize, 0, - 1 );
1598 switch( strtoupper( (string) $sSuffix ) ) {
1599 case 'M':
1600 $iValue *= 1024 * 1024;
1601 break;
1602 case 'K':
1603 $iValue *= 1024;
1604 break;
1605 case 'G':
1606 $iValue *= 1024 * 1024 * 1024;
1607 break;
1608 case 'T':
1609 $iValue *= 1024 * 1024 * 1024 * 1024;
1610 break;
1611 case 'P':
1612 $iValue *= 1024 * 1024 * 1024 * 1024 * 1024;
1613 break;
1614 }
1615
1616 return $iValue;
1617 }
1618
1619 function wpforo_print_number( $n, $echo = false ) {
1620 $x = str_replace( ",", "", $n );
1621 $x = intval( $x );
1622 $n = 0 + $x;
1623 $number = 0;
1624 if( ! is_numeric( $n ) ) return false;
1625 if( $n > 1000000000000 ) {
1626 $number = round( ( $n / 1000000000000 ), 1 ) . ' ' . str_replace( '{number}', '', wpforo_phrase( '{number}T', false ) );
1627 } else if( $n > 1000000000 ) {
1628 $number = round( ( $n / 1000000000 ), 1 ) . ' ' . str_replace( '{number}', '', wpforo_phrase( '{number}B', false ) );
1629 } else if( $n > 1000000 ) {
1630 $number = round( ( $n / 1000000 ), 1 ) . ' ' . str_replace( '{number}', '', wpforo_phrase( '{number}M', false ) );
1631 } else if( $n > 10000 ) $number = round( ( $n / 1000 ), 1 ) . ' ' . str_replace( '{number}', '', wpforo_phrase( '{number}K', false ) );
1632
1633 $number = ( $number ) ? $number : number_format( $n );
1634
1635 if( $echo ) {
1636 echo $number;
1637 } else {
1638 return $number;
1639 }
1640 }
1641
1642 function wpforo_bigintval( $value ) {
1643 $value = wpforo_settype( $value, 'string' );
1644 if( is_string( $value ) ) {
1645 $value = trim( $value );
1646 if( ! is_numeric( $value ) ) {
1647 $value = preg_replace( "#[^0-9].*#s", '', (string) $value );
1648 if( ! is_numeric( $value ) ) $value = 0;
1649 }
1650 } else {
1651 $value = 0;
1652 }
1653
1654 return ( strlen( (string) $value ) < strlen( (string) PHP_INT_MAX ) ) ? (int) $value : $value;
1655 }
1656
1657 function wpforo_removebb( $string ) {
1658 if( isset( $string ) && $string ) {
1659 $string = preg_replace( '|\[/*[^]\[]+]|i', '', (string) $string );
1660 }
1661
1662 return $string;
1663 }
1664
1665 function wpforo_file_upload_error( $code ): string {
1666 switch( $code ) {
1667 case UPLOAD_ERR_INI_SIZE:
1668 $message = wpforo_phrase( "The uploaded file exceeds the upload_max_filesize directive in php.ini", false );
1669 break;
1670 case UPLOAD_ERR_FORM_SIZE:
1671 $message = wpforo_phrase( "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form", false );
1672 break;
1673 case UPLOAD_ERR_PARTIAL:
1674 $message = wpforo_phrase( "The uploaded file was only partially uploaded", false );
1675 break;
1676 case UPLOAD_ERR_NO_FILE:
1677 $message = wpforo_phrase( "No file was uploaded", false );
1678 break;
1679 case UPLOAD_ERR_NO_TMP_DIR:
1680 $message = wpforo_phrase( "Missing a temporary folder", false );
1681 break;
1682 case UPLOAD_ERR_CANT_WRITE:
1683 $message = wpforo_phrase( "Failed to write file to disk", false );
1684 break;
1685 case UPLOAD_ERR_EXTENSION:
1686 $message = wpforo_phrase( "File upload stopped by extension", false );
1687 break;
1688 default:
1689 $message = wpforo_phrase( "Unknown upload error", false ) . ": " . $code;
1690 break;
1691 }
1692
1693 return $message;
1694 }
1695
1696 //$key allowed values are post, strip, data, user_description entities or the name of a field filter such as pre_user_description.
1697 //More info https://core.trac.wordpress.org/browser/tags/4.5.2/src/wp-includes/kses.php#L624
1698 function wpforo_kses( $string = '', $key = 'post' ) {
1699 if( ! $string || ! $key ) return $string;
1700 if( $key === 'email' ) {
1701 if( ! preg_match( '#<p[^<>]*?>#iu', (string) $string ) ) $string = wpautop( $string );
1702 $allowed_html = [
1703 'a' => [ 'href' => true, 'title' => true, 'style' => true ],
1704 'img' => [
1705 'src' => true,
1706 'width' => true,
1707 'height' => true,
1708 'align' => true,
1709 'alt' => true,
1710 'style' => true,
1711 ],
1712 'blockquote' => [ 'style' => true ],
1713 'h1' => [ 'style' => true ],
1714 'h2' => [ 'style' => true ],
1715 'h3' => [ 'style' => true ],
1716 'h4' => [ 'style' => true ],
1717 'h5' => [ 'style' => true ],
1718 'h6' => [ 'style' => true ],
1719 'hr' => [],
1720 'br' => [],
1721 'p' => [ 'style' => true ],
1722 'strong' => [ 'style' => true ],
1723 'em' => [ 'style' => true ],
1724 'del' => [ 'style' => true ],
1725 'style' => [],
1726 ];
1727 $allowed_html = apply_filters( 'wpforo_kses_allowed_html_email', $allowed_html );
1728 } elseif( $key === 'user_description' ) {
1729 $allowed_html = wp_kses_allowed_html( $key );
1730 $allowed_html['img'] = [ 'alt' => true, 'align' => true, 'border' => true, 'height' => true, 'hspace' => true, 'longdesc' => true, 'vspace' => true, 'src' => true, 'usemap' => true, 'width' => true ];
1731 $allowed_html['br'] = [];
1732 $allowed_html = apply_filters( 'wpforo_kses_allowed_html_user_description', $allowed_html );
1733 } elseif( $key === 'svg' ){
1734 $allowed_tags = [
1735 // HTML
1736 'a',
1737 'font',
1738 'image',
1739 'style',
1740
1741 // SVG
1742 'svg',
1743 'altglyph',
1744 'altglyphdef',
1745 'altglyphitem',
1746 'animatecolor',
1747 'animatemotion',
1748 'animatetransform',
1749 'circle',
1750 'clippath',
1751 'defs',
1752 'desc',
1753 'ellipse',
1754 'filter',
1755 'font',
1756 'g',
1757 'glyph',
1758 'glyphref',
1759 'hkern',
1760 'image',
1761 'line',
1762 'lineargradient',
1763 'marker',
1764 'mask',
1765 'metadata',
1766 'mpath',
1767 'path',
1768 'pattern',
1769 'polygon',
1770 'polyline',
1771 'radialgradient',
1772 'rect',
1773 'stop',
1774 'switch',
1775 'symbol',
1776 'text',
1777 'textpath',
1778 'title',
1779 'tref',
1780 'tspan',
1781 'use',
1782 'view',
1783 'vkern',
1784
1785 // SVG Filters
1786 'feBlend',
1787 'feColorMatrix',
1788 'feComponentTransfer',
1789 'feComposite',
1790 'feConvolveMatrix',
1791 'feDiffuseLighting',
1792 'feDisplacementMap',
1793 'feDistantLight',
1794 'feFlood',
1795 'feFuncA',
1796 'feFuncB',
1797 'feFuncG',
1798 'feFuncR',
1799 'feGaussianBlur',
1800 'feMerge',
1801 'feMergeNode',
1802 'feMorphology',
1803 'feOffset',
1804 'fePointLight',
1805 'feSpecularLighting',
1806 'feSpotLight',
1807 'feTile',
1808 'feTurbulence',
1809
1810 //text
1811 '#text'
1812 ];
1813 $allowed_attrs = [
1814 // HTML
1815 'about',
1816 'accept',
1817 'action',
1818 'align',
1819 'alt',
1820 'autocomplete',
1821 'background',
1822 'bgcolor',
1823 'border',
1824 'cellpadding',
1825 'cellspacing',
1826 'checked',
1827 'cite',
1828 'class',
1829 'clear',
1830 'color',
1831 'cols',
1832 'colspan',
1833 'coords',
1834 'crossorigin',
1835 'datetime',
1836 'default',
1837 'dir',
1838 'disabled',
1839 'download',
1840 'enctype',
1841 'encoding',
1842 'face',
1843 'for',
1844 'headers',
1845 'height',
1846 'hidden',
1847 'high',
1848 'href',
1849 'hreflang',
1850 'id',
1851 'integrity',
1852 'ismap',
1853 'label',
1854 'lang',
1855 'list',
1856 'loop',
1857 'low',
1858 'max',
1859 'maxlength',
1860 'media',
1861 'method',
1862 'min',
1863 'multiple',
1864 'name',
1865 'noshade',
1866 'novalidate',
1867 'nowrap',
1868 'open',
1869 'optimum',
1870 'pattern',
1871 'placeholder',
1872 'poster',
1873 'preload',
1874 'pubdate',
1875 'radiogroup',
1876 'readonly',
1877 'rel',
1878 'required',
1879 'rev',
1880 'reversed',
1881 'role',
1882 'rows',
1883 'rowspan',
1884 'spellcheck',
1885 'scope',
1886 'selected',
1887 'shape',
1888 'size',
1889 'sizes',
1890 'span',
1891 'srclang',
1892 'start',
1893 'src',
1894 'srcset',
1895 'step',
1896 'style',
1897 'summary',
1898 'tabindex',
1899 'title',
1900 'type',
1901 'usemap',
1902 'valign',
1903 'value',
1904 'version',
1905 'width',
1906 'xmlns',
1907
1908 // SVG
1909 'accent-height',
1910 'accumulate',
1911 'additivive',
1912 'alignment-baseline',
1913 'ascent',
1914 'attributename',
1915 'attributetype',
1916 'azimuth',
1917 'basefrequency',
1918 'baseline-shift',
1919 'begin',
1920 'bias',
1921 'by',
1922 'class',
1923 'clip',
1924 'clip-path',
1925 'clip-rule',
1926 'color',
1927 'color-interpolation',
1928 'color-interpolation-filters',
1929 'color-profile',
1930 'color-rendering',
1931 'cx',
1932 'cy',
1933 'd',
1934 'dx',
1935 'dy',
1936 'diffuseconstant',
1937 'direction',
1938 'display',
1939 'divisor',
1940 'dur',
1941 'edgemode',
1942 'elevation',
1943 'end',
1944 'fill',
1945 'fill-opacity',
1946 'fill-rule',
1947 'filter',
1948 'filterUnits',
1949 'flood-color',
1950 'flood-opacity',
1951 'font-family',
1952 'font-size',
1953 'font-size-adjust',
1954 'font-stretch',
1955 'font-style',
1956 'font-variant',
1957 'font-weight',
1958 'fx',
1959 'fy',
1960 'g1',
1961 'g2',
1962 'glyph-name',
1963 'glyphref',
1964 'gradientunits',
1965 'gradienttransform',
1966 'height',
1967 'href',
1968 'id',
1969 'image-rendering',
1970 'in',
1971 'in2',
1972 'k',
1973 'k1',
1974 'k2',
1975 'k3',
1976 'k4',
1977 'kerning',
1978 'keypoints',
1979 'keysplines',
1980 'keytimes',
1981 'lang',
1982 'lengthadjust',
1983 'letter-spacing',
1984 'kernelmatrix',
1985 'kernelunitlength',
1986 'lighting-color',
1987 'local',
1988 'marker-end',
1989 'marker-mid',
1990 'marker-start',
1991 'markerheight',
1992 'markerunits',
1993 'markerwidth',
1994 'maskcontentunits',
1995 'maskunits',
1996 'max',
1997 'mask',
1998 'media',
1999 'method',
2000 'mode',
2001 'min',
2002 'name',
2003 'numoctaves',
2004 'offset',
2005 'operator',
2006 'opacity',
2007 'order',
2008 'orient',
2009 'orientation',
2010 'origin',
2011 'overflow',
2012 'paint-order',
2013 'path',
2014 'pathlength',
2015 'patterncontentunits',
2016 'patterntransform',
2017 'patternunits',
2018 'points',
2019 'preservealpha',
2020 'preserveaspectratio',
2021 'r',
2022 'rx',
2023 'ry',
2024 'radius',
2025 'refx',
2026 'refy',
2027 'repeatcount',
2028 'repeatdur',
2029 'restart',
2030 'result',
2031 'rotate',
2032 'scale',
2033 'seed',
2034 'shape-rendering',
2035 'specularconstant',
2036 'specularexponent',
2037 'spreadmethod',
2038 'stddeviation',
2039 'stitchtiles',
2040 'stop-color',
2041 'stop-opacity',
2042 'stroke-dasharray',
2043 'stroke-dashoffset',
2044 'stroke-linecap',
2045 'stroke-linejoin',
2046 'stroke-miterlimit',
2047 'stroke-opacity',
2048 'stroke',
2049 'stroke-width',
2050 'style',
2051 'surfacescale',
2052 'tabindex',
2053 'targetx',
2054 'targety',
2055 'transform',
2056 'text-anchor',
2057 'text-decoration',
2058 'text-rendering',
2059 'textlength',
2060 'type',
2061 'u1',
2062 'u2',
2063 'unicode',
2064 'values',
2065 'viewbox',
2066 'visibility',
2067 'vector-effect',
2068 'vert-adv-y',
2069 'vert-origin-x',
2070 'vert-origin-y',
2071 'width',
2072 'word-spacing',
2073 'wrap',
2074 'writing-mode',
2075 'xchannelselector',
2076 'ychannelselector',
2077 'x',
2078 'x1',
2079 'x2',
2080 'xmlns',
2081 'y',
2082 'y1',
2083 'y2',
2084 'z',
2085 'zoomandpan',
2086
2087 // MathML
2088 'accent',
2089 'accentunder',
2090 'align',
2091 'bevelled',
2092 'close',
2093 'columnsalign',
2094 'columnlines',
2095 'columnspan',
2096 'denomalign',
2097 'depth',
2098 'dir',
2099 'display',
2100 'displaystyle',
2101 'fence',
2102 'frame',
2103 'height',
2104 'href',
2105 'id',
2106 'largeop',
2107 'length',
2108 'linethickness',
2109 'lspace',
2110 'lquote',
2111 'mathbackground',
2112 'mathcolor',
2113 'mathsize',
2114 'mathvariant',
2115 'maxsize',
2116 'minsize',
2117 'movablelimits',
2118 'notation',
2119 'numalign',
2120 'open',
2121 'rowalign',
2122 'rowlines',
2123 'rowspacing',
2124 'rowspan',
2125 'rspace',
2126 'rquote',
2127 'scriptlevel',
2128 'scriptminsize',
2129 'scriptsizemultiplier',
2130 'selection',
2131 'separator',
2132 'separators',
2133 'slope',
2134 'stretchy',
2135 'subscriptshift',
2136 'supscriptshift',
2137 'symmetric',
2138 'voffset',
2139 'width',
2140 'xmlns',
2141
2142 // XML
2143 'xlink:href',
2144 'xml:id',
2145 'xlink:title',
2146 'xml:space',
2147 'xmlns:xlink',
2148 ];
2149 $attrs = array_map( '__return_true', array_flip( $allowed_attrs ) );
2150 $allowed_html = array_map( function() use ($attrs){
2151 return $attrs;
2152 }, array_flip( $allowed_tags ) );
2153 } else {
2154 global $allowedposttags;
2155 $allowed_html = $allowedposttags;
2156 if( wpforo_setting( 'posting', 'content_do_shortcode' ) ) {
2157 $allowed_html = wp_kses_allowed_html( $key );
2158 }
2159 $extra_html = wpforo_setting( 'posting', 'extra_html_tags' );
2160 $allowed_html = wpforo_extra_html_parser( $extra_html, $allowed_html );
2161 $allowed_html['a']['data-gallery'] = true;
2162 $allowed_html['a']['download'] = true;
2163 $allowed_html['blockquote']['class'] = true;
2164 $allowed_html['blockquote']['data-width'] = true;
2165 $allowed_html['blockquote']['data-userid'] = true;
2166 $allowed_html['blockquote']['data-postid'] = true;
2167 $allowed_html['p']['lang'] = true;
2168 $allowed_html['p']['dir'] = true;
2169 $allowed_html['pre']['contenteditable'] = true;
2170 if( ! wpfval( $allowed_html, 'iframe' ) && class_exists( 'wpForoEmbeds' ) ) {
2171 $allowed_html['iframe'] = [ 'width' => true, 'height' => true, 'src' => true, 'frameborder' => true, 'allowfullscreen' => true ];
2172 }
2173 $allowed_html = apply_filters( 'wpforo_kses_allowed_html', $allowed_html );
2174 }
2175
2176 return wp_kses( $string, $allowed_html );
2177 }
2178
2179 function wpforo_deep_merge( $default, $current = [] ) {
2180 foreach( $default as $k => $v ) {
2181 if( ! empty( $v ) && is_array( $v ) ) {
2182 foreach( $v as $kk => $vv ) {
2183 if( ! empty( $vv ) && is_array( $vv ) ) {
2184 foreach( $vv as $kkk => $vvv ) {
2185 if( ! empty( $vvv ) && is_array( $vvv ) ) {
2186 foreach( $vvv as $kkkk => $vvvv ) {
2187 if( ! empty( $vvv ) && is_array( $vvv ) ) {
2188 //Stop on 5th level
2189 } else {
2190 if( isset( $current[ $k ][ $kk ][ $kkk ][ $kkkk ] ) ) $default[ $k ][ $kk ][ $kkk ][ $kkkk ] = $current[ $k ][ $kk ][ $kkk ][ $kkkk ];
2191 }
2192 }
2193 } else {
2194 if( isset( $current[ $k ][ $kk ][ $kkk ] ) ) $default[ $k ][ $kk ][ $kkk ] = $current[ $k ][ $kk ][ $kkk ];
2195 }
2196 }
2197 } else {
2198 if( isset( $current[ $k ][ $kk ] ) ) $default[ $k ][ $kk ] = $current[ $k ][ $kk ];
2199 }
2200 }
2201 } else {
2202 if( isset( $current[ $k ] ) ) $default[ $k ] = $current[ $k ];
2203 }
2204 }
2205
2206 return $default;
2207 }
2208
2209 function wpforo_is_image( $e ) {
2210 return (bool) preg_match( '#^(jpe?g|png|gif|bmp|webp|svg|tiff?)$#i', (string) $e );
2211 }
2212
2213 function wpforo_is_video( $e ) {
2214 return (bool) preg_match( '#^(mp4|webm|ogg)$#i', (string) $e );
2215 }
2216
2217 function wpforo_is_audio( $e ) {
2218 return (bool) preg_match( '#^(mp3|wav|ogg)$#i', (string) $e );
2219 }
2220
2221 /**
2222 * @param string $option
2223 * @param mixed $default
2224 * @param bool $cache
2225 *
2226 * @return mixed
2227 */
2228 function wpforo_get_option( $option, $default = null, $cache = true ) {
2229 if( strpos( (string) $option, 'wpforo_' ) !== 0 ) {
2230 $option = wpforo_prefix( $option );
2231 }
2232
2233 $from_cache = false;
2234 $value = null;
2235
2236 $cache = ( $cache && apply_filters( 'wpforo_options_cache', true ) );
2237
2238 if( $cache ) {
2239 $option_file = WPF()->folders['cache']['dir'] . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'option' . DIRECTORY_SEPARATOR . ( defined( 'AUTH_KEY' ) ? md5( $option . AUTH_KEY ) : md5( $option ) );
2240 $value = maybe_unserialize( wpforo_get_file_content( $option_file ) );
2241 if( ! empty( $value ) ) $from_cache = true;
2242 }
2243
2244 if( ! $from_cache ) $value = get_option( $option );
2245
2246 if( $value === false ) {
2247 $value = $default;
2248 } else {
2249 if( preg_match( '#_(?:general|profiles|rating|tools_misc)$#iu', (string) $option ) ) {
2250 foreach( [ 'title_groupids', 'title_secondary_groupids', 'rating_title_ug', 'rating_badge_ug', 'admin_bar', 'admin_note_groups', 'admin_note_pages' ] as $k ) {
2251 if( wpfkey( $default, $k ) ) $default[ $k ] = [];
2252 }
2253 }
2254 $default = apply_filters( 'wpforo_get_option_default_arg_before_cast', $default, $option );
2255
2256 $value = wpforo_settype( $value, gettype( $default ) );
2257 if( $default && is_array( $default ) && is_array( $value ) ) $value = wpforo_array_args_cast_and_merge( $value, $default );
2258 }
2259
2260 if( $cache && ! $from_cache && ! empty( $value ) ) wpforo_write_file( $option_file, maybe_serialize( $value ) );
2261
2262 return apply_filters( 'wpforo_get_option', $value, $option, $default, $cache );
2263 }
2264
2265 /**
2266 * calls the WordPress core update_option() function
2267 *
2268 * @param string $option Name of the option to update. Expected to not be SQL-escaped.
2269 * @param mixed $value Option value. Must be serializable if non-scalar. Expected to not be SQL-escaped.
2270 * @param string|bool $autoload Optional. Whether to load the option when WordPress starts up. For existing options,
2271 * `$autoload` can only be updated using `update_option()` if `$value` is also changed.
2272 * Accepts 'yes'|true to enable or 'no'|false to disable. For non-existent options,
2273 * the default value is 'yes'. Default null.
2274 *
2275 * @return bool True if the value was updated, false otherwise.
2276 * @since 2.0.0
2277 */
2278 function wpforo_update_option( $option, $value, $autoload = null ) {
2279 if( strpos( (string) $option, 'wpforo_' ) !== 0 ) {
2280 $option = wpforo_prefix( $option );
2281 }
2282 $ret = update_option( $option, $value, $autoload );
2283 do_action( 'wpforo_update_option', $option, $value, $autoload );
2284 wpforo_clean_cache( 'option' );
2285
2286 return $ret;
2287 }
2288
2289 /**
2290 * calls the WordPress core delete_option() function
2291 *
2292 * @param string $option Name of the option to update. Expected to not be SQL-escaped.
2293 *
2294 * @return bool True if the value was updated, false otherwise.
2295 * @since 2.0.0
2296 */
2297 function wpforo_delete_option( $option ) {
2298 if( strpos( (string) $option, 'wpforo_' ) !== 0 ) {
2299 $option = wpforo_prefix( $option );
2300 }
2301 $ret = delete_option( $option );
2302 do_action( 'wpforo_delete_option', $option );
2303 wpforo_clean_cache( 'option' );
2304
2305 return $ret;
2306 }
2307
2308 /**
2309 * Extract what remains from an unintentionally truncated serialized string
2310 * $data contains your original array (or what remains of it).
2311 *
2312 * @param string The serialized array
2313 */
2314 function wpforo_fixSerializedArray( $serialized ) {
2315 $tmp = preg_replace( '/^a:\d+:\{/', '', (string) $serialized );
2316
2317 return wpforo_fixSerializedArray_R( $tmp );
2318 }
2319
2320 /**
2321 * The recursive function that does all the heavy lifing. Do not call directly.
2322 *
2323 * @param string The broken serialzized array
2324 *
2325 * @return array Returns the repaired string
2326 */
2327 function wpforo_fixSerializedArray_R( &$broken ) {
2328 $data = [];
2329 $index = null;
2330 $len = strlen( (string) $broken );
2331 $i = 0;
2332 while( strlen( (string) $broken ) ) {
2333 $i ++;
2334 if( $i > $len ) {
2335 break;
2336 }
2337 if( substr( (string) $broken, 0, 1 ) == '}' ) {
2338 $broken = substr( (string) $broken, 1 );
2339
2340 return $data;
2341 } else {
2342 $bite = substr( (string) $broken, 0, 2 );
2343 switch( $bite ) {
2344 case 's:':
2345 $re = '/^s:\d+:"([^\"]*)";/';
2346 if( preg_match( $re, (string) $broken, $m ) ) {
2347 if( $index === null ) {
2348 $index = $m[1];
2349 } else {
2350 $data[ $index ] = $m[1];
2351 $index = null;
2352 }
2353 $broken = preg_replace( $re, '', (string) $broken );
2354 }
2355 break;
2356 case 'i:':
2357 $re = '/^i:(\d+);/';
2358 if( preg_match( $re, (string) $broken, $m ) ) {
2359 if( $index === null ) {
2360 $index = (int) $m[1];
2361 } else {
2362 $data[ $index ] = (int) $m[1];
2363 $index = null;
2364 }
2365 $broken = preg_replace( $re, '', (string) $broken );
2366 }
2367 break;
2368 case 'b:':
2369 $re = '/^b:[01];/';
2370 if( preg_match( $re, (string) $broken, $m ) ) {
2371 $data[ $index ] = (bool) $m[1];
2372 $index = null;
2373 $broken = preg_replace( $re, '', (string) $broken );
2374 }
2375 break;
2376 case 'a:':
2377 $re = '/^a:\d+:\{/';
2378 if( preg_match( $re, (string) $broken, $m ) ) {
2379 $broken = preg_replace( '/^a:\d+:\{/', '', (string) $broken );
2380 $data[ $index ] = wpforo_fixSerializedArray_R( $broken );
2381 $index = null;
2382 }
2383 break;
2384 case 'N;':
2385 $broken = substr( (string) $broken, 2 );
2386 $data[ $index ] = null;
2387 $index = null;
2388 break;
2389 }
2390 }
2391 }
2392
2393 return $data;
2394 }
2395
2396 function wpforo_insert_to_media_library( $attach_path, $title = '' ) {
2397 if( wpforo_setting( 'posting', 'attachs_to_medialib' ) ) {
2398 if( ! $attach_path ) return 0;
2399 $attach_fname = basename( (string) $attach_path );
2400 if( ! $title ) $title = $attach_fname;
2401 require_once( ABSPATH . 'wp-admin/includes/media.php' );
2402 require_once( ABSPATH . 'wp-admin/includes/file.php' );
2403 require_once( ABSPATH . 'wp-admin/includes/image.php' );
2404 $filetype = wp_check_filetype( $attach_fname, null );
2405 $attachment = [ 'guid' => $attach_path, 'post_mime_type' => $filetype['type'], 'post_title' => $title, 'post_content' => '', 'post_status' => 'inherit' ];
2406 $attach_id = wp_insert_attachment( $attachment, $attach_path );
2407 add_filter( 'intermediate_image_sizes', 'wpforo_attachment_sizes' );
2408 $attach_data = wp_generate_attachment_metadata( $attach_id, $attach_path );
2409 wp_update_attachment_metadata( $attach_id, $attach_data );
2410 remove_filter( 'intermediate_image_sizes', 'wpforo_attachment_sizes' );
2411
2412 return $attach_id;
2413 }
2414
2415 return 0;
2416 }
2417
2418 function wpforo_attachment_sizes( $sizes ) {
2419 return [ 'thumbnail' ];
2420 }
2421
2422 #################################################################################
2423 /**
2424 * Cleans forum cache
2425 *
2426 * @param string Item View / Template (e.g.: 'forum', 'topic', 'post', 'user', 'widget', etc...)
2427 * @param integer Item ID (e.g.: $topicid or $postid) | (!) ID is 0 on dome actions (e.g.: delete actions)
2428 * @param array Item data as array
2429 *
2430 * @since 1.2.1
2431 *
2432 */
2433 function wpforo_clean_cache( $template = 'all', $id = 0, $item = [] ) {
2434 do_action( 'wpforo_clean_cache_start', $id, $template );
2435
2436 if( ! $pageid = WPF()->board->get_current( 'pageid' ) ) $pageid = wpforo_wp_url_to_postid( $_SERVER['REQUEST_URI'] );
2437 if( $pageid ) clean_post_cache( $pageid );
2438
2439 do_action( 'wpforo_clean_cache', $id, $template );
2440 WPF()->cache->clean( $id, $template, $item );
2441 if( $template !== 'option' ) WPF()->statistic_cache_clean();
2442 do_action( 'wpforo_clean_cache_end', $id, $template );
2443 }
2444
2445 function wpforo_is_owner( $userid, $email = '' ) {
2446 return wpforo_is_users_same( ['userid' => $userid, 'user_email' => $email] );
2447 }
2448
2449 function wpforo_is_users_same( $user1, $user2 = null ){
2450 if( is_null( $user2 ) ) {
2451 $user2 = WPF()->current_user;
2452 }elseif( is_scalar( $user2 ) ){
2453 if( is_numeric( $user2 ) ){
2454 $user2 = [ 'userid' => wpforo_bigintval( $user2 ), 'user_email' => wpforo_member( $user2, 'user_email' ) ];
2455 }else{
2456 $user2 = [ 'userid' => 0, 'user_email' => sanitize_email( $user2 ) ];
2457 }
2458 }
2459
2460 if( is_scalar( $user1 ) ){
2461 if( is_numeric( $user1 ) ){
2462 $user1 = [ 'userid' => wpforo_bigintval( $user1 ), 'user_email' => wpforo_member( $user1, 'user_email' ) ];
2463 }else{
2464 $user1 = [ 'userid' => 0, 'user_email' => sanitize_email( $user1 ) ];
2465 }
2466 }
2467
2468 if( wpforo_bigintval( wpfval($user2, 'userid') ) && ($userid = wpforo_bigintval( wpfval( $user1, 'userid' ) )) ){
2469 return $userid === $user2['userid'];
2470 }elseif( wpfval($user2, 'user_email') && ($user_email = sanitize_email( wpfval( $user1, 'user_email' ) )) ){
2471 return $user_email === $user2['user_email'];
2472 }
2473
2474 return false;
2475 }
2476
2477 function wpforo_dashboard_is_owner(){
2478 return (
2479 preg_match( '#/profile\.php$#iu', $_SERVER['REQUEST_URI'])
2480 || (
2481 preg_match( '#/user-edit\.php#iu', $_SERVER['REQUEST_URI'] )
2482 && (int)wpfval($_GET, 'user_id' ) === WPF()->current_userid
2483 )
2484 );
2485 }
2486
2487 /**
2488 * @param array $user wpforo user array
2489 * @param bool $echo
2490 *
2491 * @return string
2492 */
2493 function wpforo_user_dname( $user, $echo = false ) {
2494 $display_name = trim( (string) wpfval( $user, 'display_name' ) );
2495 $user_nicename = trim( (string) wpfval( $user, 'user_nicename' ) );
2496 $dname = esc_html( $display_name ) ?: ( esc_html( urldecode( $user_nicename ) ) ?: wpforo_phrase( 'Anonymous', false ) );
2497 if( $echo ) echo $dname;
2498 return $dname;
2499 }
2500
2501 function wpforo_strlen( $string ) {
2502 if( ! $string ) return 0;
2503 if( function_exists( 'mb_strlen' ) ) {
2504 return mb_strlen( (string) $string );
2505 } else {
2506 return strlen( (string) $string );
2507 }
2508 }
2509
2510 function wpforo_string2array( $string, $regexp = '' ) {
2511 if( ! $regexp ) $regexp = '#' . preg_quote( PHP_EOL ) . '#isu';
2512 $array = preg_split( $regexp, $string );
2513
2514 return array_filter( $array );
2515 }
2516
2517 function wpforo_array_ordered_intersect_key( $array1, $array2 ) {
2518 $new_array = [];
2519 foreach( $array2 as $key => $value ) if( wpfkey( $array1, $key ) ) $new_array[ $key ] = $array1[ $key ];
2520
2521 return $new_array;
2522 }
2523
2524 function wpforo_get_upload_dir_folders() {
2525 return (array) array_diff( scandir( WPF()->folders['upload']['dir'] ), [ '.', '..' ] );
2526 }
2527
2528 function wpforo_fix_upload_dir( $upload_dir ) {
2529 $folders = wpforo_ram_get( 'wpforo_get_upload_dir_folders' );
2530 $folders = array_map( 'preg_quote', $folders );
2531 if( $folders && preg_match( '#[/\\\]wpforo(?:_\d+)?[/\\\](?:' . implode( '|', $folders ) . ')[/\\\].+?$#iu', (string) $upload_dir, $match ) ) {
2532 $upload_dir = wpforo_fix_dir_sep( WPF()->folders['wp_upload']['dir'] . $match[0] );
2533 $upload_dir = urldecode( (string) $upload_dir );
2534 }
2535
2536 return $upload_dir;
2537 }
2538
2539 function wpforo_fix_upload_url( $upload_url ) {
2540 $folders = wpforo_ram_get( 'wpforo_get_upload_dir_folders' );
2541 $folders = array_map( 'preg_quote', $folders );
2542 if( $folders && preg_match( '#[/\\\]wpforo(?:_\d+)?[/\\\](?:' . implode( '|', $folders ) . ')[/\\\].+?$#iu', (string) $upload_url, $match ) ) {
2543 $upload_url = wpforo_fix_url_sep( WPF()->folders['wp_upload']['url'] . $match[0] );
2544 }
2545
2546 return $upload_url;
2547 }
2548
2549 function wpforo_xcopy( $source, $dest ) {
2550 // Check for symlinks
2551 if( is_link( $source ) ) {
2552 return symlink( readlink( $source ), $dest );
2553 }
2554
2555 // Simple copy for a file
2556 if( is_file( $source ) ) {
2557 return copy( $source, $dest );
2558 }
2559
2560 // Make destination directory
2561 if( ! is_dir( $dest ) ) {
2562 wp_mkdir_p( $dest );
2563 }
2564
2565 // Loop through the folder
2566 $dir = dir( $source );
2567 while( false !== $entry = $dir->read() ) {
2568 // Skip pointers
2569 if( $entry === '.' || $entry === '..' ) {
2570 continue;
2571 }
2572
2573 // Deep copy directories
2574 wpforo_xcopy( rtrim( (string) $source, '/' ) . "/$entry", rtrim( (string) $dest, '/' ) . "/$entry" );
2575 }
2576
2577 // Clean up
2578 $dir->close();
2579
2580 return true;
2581 }
2582
2583 function wpforo_printf_array( $format, $arr ) {
2584 return call_user_func_array( 'printf', array_merge( (array) $format, (array) $arr ) );
2585 }
2586
2587 function wpforo_sprintf_array( $format, $arr ) {
2588 return call_user_func_array( 'sprintf', array_merge( (array) $format, (array) $arr ) );
2589 }
2590
2591 function wpforo_avatar_url( $avatar_html ) {
2592 if( preg_match( '#src=[\'"]([^\'"]+?)[\'"]#iu', (string) $avatar_html, $matches ) ) {
2593 return $matches[1];
2594 }
2595
2596 return '';
2597 }
2598
2599 /**
2600 * @param string $content
2601 * @param bool $first
2602 * @param string $type
2603 *
2604 * @return string|array
2605 */
2606 function wpforo_find_image_urls( $content, $first = true, $type = 'general' ) {
2607 $images = [];
2608 $content = trim( (string) $content );
2609
2610 if( $content ) {
2611 if( preg_match_all( '#<img[^<>]*?src=[\'\"]([^\'\"]+\.(?:jpe?g|png|gif|bmp|webp|svg|tiff))[\'\"][^<>]*?>#iu', (string) $content, $matches, PREG_SET_ORDER ) ) {
2612 foreach( $matches as $match ) {
2613 if( preg_match( '#class=[\'\"]wpfem[^\'\"]*[\'\"][^<>]*?data-code=#isu', $match[0] ) ) continue;
2614 if( strpos( (string) $match[1], 'http' ) === 0 ) {
2615 $images[] = $match[1];
2616 } else {
2617 $images[] = 'http' . ( is_ssl() ? 's' : '' ) . ':' . $match[1];
2618 }
2619 }
2620 } elseif( preg_match_all( '#https?://[^\r\n\t\s\'\"<>]+?\.(?:jpe?g|png|gif|bmp|webp|svg|tiff)#iu', (string) $content, $matches, PREG_SET_ORDER ) ) {
2621 foreach( $matches as $match ) {
2622 $images[] = $match[0];
2623 }
2624 } elseif( preg_match_all( '#//[^\r\n\t\s\'\"<>]+?\.(?:jpe?g|png|gif|bmp|webp|svg|tiff)#iu', (string) $content, $matches, PREG_SET_ORDER ) ) {
2625 foreach( $matches as $match ) {
2626 $images[] = 'http' . ( is_ssl() ? 's' : '' ) . ':' . $match[0];
2627 }
2628 }
2629 }
2630
2631 if( $first && $images ) $images = wpfval( $images, 0 );
2632
2633 return apply_filters( 'wpforo_find_image_url', $images, $type, $first );
2634 }
2635
2636 function wpforo_is_json( $string ) {
2637 if( is_scalar( $string ) ) {
2638 json_decode( $string );
2639
2640 return json_last_error() === JSON_ERROR_NONE;
2641 }
2642
2643 return false;
2644 }
2645
2646 function wpforo_ajax_response( $message ) {
2647 wp_send_json( $message );
2648 die();
2649 }
2650
2651 function wpforo_get_fb_user( $user ) {
2652 if( is_user_logged_in() ) return wp_get_current_user();
2653 $user_data = get_user_by( 'email', $user['user_email'] );
2654 if( ! $user_data ) {
2655 $users = get_users( [ 'meta_key' => '_fb_user_id', 'meta_value' => $user['fb_user_id'], 'number' => 1, 'count_total' => false ] );
2656 if( is_array( $users ) ) $user_data = reset( $users );
2657 }
2658
2659 return $user_data;
2660 }
2661
2662 function wpforo_unique_username( $username ) {
2663 static $i;
2664 if( ! $username ) $username = 'user_' . uniqid();
2665 if( strpos( (string) $username, '@' ) !== false ) {
2666 $parts = explode( "@", $username );
2667 if( ! empty( $parts ) && isset( $parts[0] ) && $parts[0] ) {
2668 $username = $parts[0];
2669 } else {
2670 $username = str_replace( '@', '', $username );
2671 }
2672 }
2673 if( null === $i ) {
2674 $i = 1;
2675 } else {
2676 $i ++;
2677 }
2678 if( ! username_exists( $username ) ) {
2679 return $username;
2680 }
2681 $new_username = sprintf( '%s-%s', $username, $i );
2682 if( ! username_exists( $new_username ) ) {
2683 return $new_username;
2684 } else {
2685 return call_user_func( __FUNCTION__, $username );
2686 }
2687 }
2688
2689 function wpforo_is_session_started() {
2690 if( php_sapi_name() !== 'cli' ) {
2691 if( version_compare( phpversion(), '5.4.0', '>=' ) ) {
2692 return session_status() === PHP_SESSION_ACTIVE ? true : false;
2693 } else {
2694 return session_id() === '' ? false : true;
2695 }
2696 }
2697
2698 return false;
2699 }
2700
2701 function wpforo_current_guest( $email ) {
2702 $guest = WPF()->member->get_guest_cookies();
2703 if( ! wpfval( $guest, 'email' ) || ! $guest['email'] ) return false;
2704 if( $email == $guest['email'] ) {
2705 return true;
2706 } else {
2707 return false;
2708 }
2709 }
2710
2711 function wpforo_extra_html_parser( $extra_html = '', $allowed_html = [] ) {
2712 if( $extra_html ) {
2713 $extra_html = explode( ',', $extra_html );
2714 $extra_html = array_filter( $extra_html );
2715 if( ! empty( $extra_html ) ) {
2716 foreach( $extra_html as $html ) {
2717 $html = trim( (string) $html );
2718 if( preg_match( '|([^()]+)\((.+)\)|', $html, $item ) ) {
2719 if( wpfval( $item, 1 ) && wpfval( $item, 2 ) ) {
2720 $attrs = explode( ' ', $item[2] );
2721 $attrs = array_map( 'trim', $attrs );
2722 foreach( $attrs as $attr ) {
2723 $allowed_html[ $item[1] ][ $attr ] = [];
2724 }
2725 }
2726 } else {
2727 $allowed_html[ $html ] = [];
2728 }
2729 }
2730 }
2731 }
2732
2733 return $allowed_html;
2734 }
2735
2736 function wpforo_clear_array( $array, $clear = [], $by = 'value' ) {
2737 if( is_array( $clear ) && ! empty( $clear ) ) {
2738 foreach( $clear as $ext ) {
2739 if( $by == 'value' ) {
2740 if( ( $key = array_search( $ext, $array ) ) !== false ) {
2741 unset( $array[ $key ] );
2742 }
2743 } elseif( $by == 'key' ) {
2744 if( wpfkey( $array, $ext ) ) unset( $array[ $ext ] );
2745 }
2746 }
2747 } elseif( is_string( $clear ) || is_numeric( $clear ) ) {
2748 if( wpfval( $array, $clear ) ) unset( $array[ $clear ] );
2749 }
2750
2751 return $array;
2752 }
2753
2754 function wpforo_key( $array = [], $value = '', $type = 'default' ) {
2755 $keys = [];
2756 if( is_array( $array ) && ! empty( $array ) ) {
2757 foreach( $array as $k => $v ) {
2758 if( $v === $value ) {
2759 $keys[] = $k;
2760 }
2761 }
2762 }
2763 if( $type === 'sort' ) {
2764 sort( $keys );
2765
2766 return $keys;
2767 } else {
2768 return $keys;
2769 }
2770 }
2771
2772 function wpforo_unslashe( $data ) {
2773 $data = is_array( $data ) ? array_map( 'wpforo_unslashe', $data ) : stripslashes( (string) $data );
2774
2775 return $data;
2776 }
2777
2778 function wpforo_encode( $data ) {
2779 $data = is_array( $data ) ? array_map( 'wpforo_encode', $data ) : htmlspecialchars( $data, ENT_QUOTES );
2780
2781 return $data;
2782 }
2783
2784 function wpforo_decode( $data ) {
2785 $data = is_array( $data ) ? array_map( 'wpforo_decode', $data ) : htmlspecialchars_decode( $data, ENT_QUOTES );
2786
2787 return $data;
2788 }
2789
2790 function wpforo_trim( $data ) {
2791 $data = is_array( $data ) ? array_map( 'wpforo_trim', $data ) : trim( (string) $data );
2792
2793 return $data;
2794 }
2795
2796 function wpforo_sanitize_int( $data ) {
2797 $data = is_array( $data ) ? array_map( 'wpforo_sanitize_int', $data ) : intval( $data );
2798
2799 return $data;
2800 }
2801
2802 function wpforo_sanitize_text( $data ) {
2803 $data = is_array( $data ) ? array_map( 'wpforo_sanitize_text', $data ) : sanitize_text_field( $data );
2804
2805 return $data;
2806 }
2807
2808
2809 if( ! function_exists( 'sanitize_textarea_field' ) && ! function_exists( '_sanitize_text_fields' ) ) {
2810 function sanitize_textarea_field( $str ) {
2811 $filtered = _sanitize_text_fields( $str, true );
2812
2813 return apply_filters( 'sanitize_textarea_field', $filtered, $str );
2814 }
2815
2816 function _sanitize_text_fields( $str, $keep_newlines = false ) {
2817 $filtered = wp_check_invalid_utf8( $str );
2818 if( strpos( (string) $filtered, '<' ) !== false ) {
2819 $filtered = wp_pre_kses_less_than( $filtered );
2820 $filtered = wp_strip_all_tags( $filtered, false );
2821 $filtered = str_replace( "<\n", "&lt;\n", $filtered );
2822 }
2823 if( ! $keep_newlines ) {
2824 $filtered = preg_replace( '/[\r\n\t ]+/', ' ', (string) $filtered );
2825 }
2826 $filtered = trim( (string) $filtered );
2827 $found = false;
2828 while( preg_match( '/%[a-f0-9]{2}/i', (string) $filtered, $match ) ) {
2829 $filtered = str_replace( $match[0], '', $filtered );
2830 $found = true;
2831 }
2832 if( $found ) {
2833 $filtered = trim( preg_replace( '/ +/', ' ', (string) $filtered ) );
2834 }
2835
2836 return $filtered;
2837 }
2838 }
2839
2840 /**
2841 * @param string $role
2842 *
2843 * @return bool
2844 */
2845 function wpforo_current_user_is( $role ) {
2846 $role = strtolower( (string) $role );
2847
2848 $filter_result = apply_filters( 'wpforo_current_user_is', null, $role );
2849 if( ! is_null( $filter_result ) ) return (bool) $filter_result;
2850
2851 switch( $role ) {
2852 case 'admin':
2853 if( current_user_can( 'activate_plugins' ) ) {
2854 return true;
2855 }
2856 if( current_user_can( 'install_plugins' ) ) {
2857 return true;
2858 }
2859 if( current_user_can( 'create_sites' ) ) {
2860 return true;
2861 }
2862 break;
2863 case 'moderator':
2864 if( WPF()->usergroup->can( 'aum' ) ) {
2865 return true;
2866 }
2867 if( current_user_can( 'moderate_comments' ) ) {
2868 return true;
2869 }
2870 if( current_user_can( 'edit_published_posts' ) ) {
2871 return true;
2872 }
2873 if( current_user_can( 'manage_categories' ) ) {
2874 return true;
2875 }
2876 break;
2877 }
2878
2879 return false;
2880 }
2881
2882 /**
2883 * @param int $userid
2884 * @param string $role
2885 *
2886 * @return bool
2887 */
2888 function wpforo_user_is( $userid, $role ) {
2889 $userid = wpforo_bigintval( $userid );
2890 $role = strtolower( (string) $role );
2891
2892 $filter_result = apply_filters( 'wpforo_user_is', null, $userid, $role );
2893 if( ! is_null( $filter_result ) ) return (bool) $filter_result;
2894
2895 switch( $role ) {
2896 case 'admin':
2897 if( user_can( $userid, 'activate_plugins' ) ) {
2898 return true;
2899 }
2900 if( user_can( $userid, 'install_plugins' ) ) {
2901 return true;
2902 }
2903 if( user_can( $userid, 'create_sites' ) ) {
2904 return true;
2905 }
2906 if( $user = WPF()->member->get_member( $userid ) ) {
2907 if( WPF()->usergroup->can( 'mf', $user['groupids'] ) || WPF()->usergroup->can( 'ms', $user['groupids'] ) ) {
2908 return true;
2909 }
2910 }
2911 break;
2912 case 'moderator':
2913 if( user_can( $userid, 'moderate_comments' ) ) {
2914 return true;
2915 }
2916 if( user_can( $userid, 'edit_published_posts' ) ) {
2917 return true;
2918 }
2919 if( user_can( $userid, 'manage_categories' ) ) {
2920 return true;
2921 }
2922 if( $user = WPF()->member->get_member( $userid ) ) {
2923 if( WPF()->usergroup->can( 'aum', $user['groupids'] ) ) {
2924 return true;
2925 }
2926 }
2927 break;
2928 }
2929
2930 return false;
2931 }
2932
2933 function wpforo_random_colors() {
2934 mt_srand( (double) microtime() * 1000000 );
2935 $color = '';
2936 while( strlen( (string) $color ) < 6 ) {
2937 $color .= sprintf( "%02X", mt_rand( 0, 255 ) );
2938 }
2939
2940 return '#' . $color;
2941 }
2942
2943 /**
2944 * @param string $dir
2945 *
2946 * @return string
2947 */
2948 function wpforo_fix_dir_sep( string $dir ): string {
2949 return WPF()->fix_dir_sep( $dir );
2950 }
2951
2952 /**
2953 * @param string $url
2954 *
2955 * @return string
2956 */
2957 function wpforo_fix_url_sep( string $url ): string {
2958 return WPF()->fix_url_sep( $url );
2959 }
2960
2961 function wpforo_root_exist() {
2962 $args = [ 'table' => WPF()->tables->posts, 'col' => 'root', 'check' => 'col_exists' ];
2963
2964 return wpforo_db_check( $args );
2965 }
2966
2967 function wpforo_urlencode( $str ): string {
2968 $str = (string) $str;
2969 if( ! preg_match( '#^(\#post-\d+|https?:|s?ftp:)$#iu', (string) $str )
2970 && ! preg_match( '#([?&][^?&/=\r\n]*=?[^?&/=\r\n]*)(?1)*$#iu', (string) $str )
2971 && strpos( (string) $str, '~' ) === false
2972 && strpos( (string) $str, '*' ) === false
2973 && $str === urldecode( (string) $str )
2974 ) {
2975 $str = urlencode( (string) $str );
2976 }
2977
2978 // Use wpforo_url_strtolower() instead of strtolower() to keep the exact values
2979 // of GET variables in pagination buttons. Otherwise, the GET values will differ
2980 // and the field value will be changed on the next page.
2981 // Example: on members search page the selected "field=Test" will be changed to "field=test"
2982 // on the next page. And the select field will loos current selected value.
2983 return $str !== urldecode( (string) $str ) ? wpforo_url_strtolower( (string) $str ) : $str;
2984 }
2985
2986 /**
2987 * Converts the URL to lowercase.
2988 *
2989 * This function converts the URL to lowercase. If the $include_query parameter is set to true,
2990 * the entire URL including the query string is converted to lowercase. Otherwise, only the part of the URL
2991 * before the query string is converted to lowercase.
2992 *
2993 * @param string $url The URL to be converted to lowercase.
2994 * @param bool $include_query Optional. Whether to include the query string in the conversion. Default is false.
2995 *
2996 * @return string The URL converted to lowercase.
2997 */
2998 function wpforo_url_strtolower( $url, $include_query = false ): string {
2999 // If $include_query is true, convert the entire URL to lowercase and return it.
3000 if( $include_query ) return strtolower( $url );
3001 // If the URL does not contain a query string, convert the entire URL to lowercase and return it.
3002 if( strpos($url, '?') === false ){
3003 $url = strtolower( $url );
3004 } else {
3005 // If the URL contains a query string, split the URL into two parts at the '?' character.
3006 $parts = explode( '?', $url );
3007 // Remove the last element from the $parts array and store it in $query.
3008 $query = array_pop( $parts );
3009 $parts = array_map( 'strtolower', $parts );
3010 $url = implode( '?', $parts ) . '?' . $query;
3011 }
3012 return $url;
3013 }
3014
3015 function wpforo_fix_url( string $url ): string {
3016 $hash = '';
3017 $url = preg_replace_callback( '#(\#[^/]*)$#iu', function($m) use (&$hash){
3018 $hash = $m[1];
3019 return '';
3020 }, $url, 1 );
3021
3022 if( preg_match( '#^((?:https?:|s?ftp:)//)([^?&/=\s:]+)((?::\d+)?)([^\r\n]*)#iu', (string) $url, $match ) ){
3023 $match[2] = wpforo_urlencode( $match[2] );
3024 $url = $match[1] . $match[2] . $match[3] . implode( '/', array_map( 'wpforo_urlencode', explode( '/', $match[4] ) ) );
3025 }else{
3026 $url = implode( '/', array_map( 'wpforo_urlencode', explode( '/', $url ) ) );
3027 }
3028
3029 return $url . $hash;
3030 }
3031
3032 function wpforo_is_domains_equal( $url1, $url2 ) {
3033 $domain1 = strtolower( str_replace( 'www.', '', parse_url( $url1, PHP_URL_HOST ) ) );
3034 $domain2 = strtolower( str_replace( 'www.', '', parse_url( $url2, PHP_URL_HOST ) ) );
3035
3036 return $domain1 === $domain2;
3037 }
3038
3039 function wpforo_is_url_internal( $url, $home_url = null ) {
3040 $url = trim( (string) $url );
3041 if( ! preg_match( '#^(?:https?:)?//#iu', (string) $url ) ) return true;
3042 if( ! $home_url ) $home_url = home_url();
3043 $home_url = trim( (string) $home_url );
3044 $url = preg_replace( '#^(https?://)?(www\.)?#iu', '', (string) $url );
3045 $home_url = preg_replace( [ '#^(https?://)?(www\.)?#iu', '#/?\?.*$#isu', '#index\.php/?#iu' ], '', (string) $home_url );
3046
3047 return strpos( (string) $url, $home_url ) === 0;
3048 }
3049
3050 function wpforo_is_url_external( $url, $home_url = null ) {
3051 return ! wpforo_is_url_internal( $url, $home_url );
3052 }
3053
3054 function wpforo_settype( $var, $type ) {
3055 $var_type = strtolower( gettype( $var ) );
3056 $type = strtolower( (string) $type );
3057 $allowed_types = [ 'bool', 'boolean', 'int', 'integer', 'double', 'real', 'float', 'string', 'array', 'object', 'null' ];
3058 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' ) ) {
3059 settype( $var, $type );
3060 }
3061
3062 return $var;
3063 }
3064
3065 function wpforo_array_args_cast( $array, $type ) {
3066 return array_map( function( $var ) use ( $type ) {
3067 return wpforo_settype( $var, $type );
3068 }, $array );
3069 }
3070
3071 function wpforo_array_args_cast_and_merge( $array, $default ) {
3072 $array = (array) $array;
3073 $default = (array) $default;
3074 foreach( $array as $key => $value ) {
3075 if( array_key_exists( $key, $default ) ) {
3076 $array[ $key ] = wpforo_settype( $value, gettype( $default[ $key ] ) );
3077 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
3078 }
3079 }
3080 $array += $default;
3081
3082 return $array;
3083 }
3084
3085 function wpforo_db_data_format( $var ){
3086 switch( gettype( $var ) ){
3087 case 'bool': case 'boolean': case 'int': case 'integer': return '%d';
3088 case 'double': case 'real': case 'float': return '%f';
3089 default: return '%s';
3090 }
3091 }
3092
3093 /**
3094 * @param int $seconds execution time in seconds , by default 0 (unlimited)
3095 */
3096 function wpforo_set_max_execution_time( $seconds = 0 ) {
3097 if( function_exists( 'set_time_limit' ) ) @set_time_limit( $seconds );
3098 if( function_exists( 'ini_set' ) ) @ini_set( 'max_execution_time', $seconds );
3099 }
3100
3101 /**
3102 * @param string $pattern
3103 * @param array $array
3104 *
3105 * @return array
3106 */
3107 function wpforo_preg_grep_recursive( $pattern, $array ) {
3108 $m = [];
3109 if( is_array( $array ) ) {
3110 foreach( $array as $key => $value ) {
3111 if( is_string( $value ) || is_numeric( $value ) ) {
3112 if( preg_match( $pattern, (string) $value ) ) $m[ $key ] = $value;
3113 } elseif( is_array( $value = wpforo_settype( $value, 'array' ) ) ) {
3114 if( $_m = wpforo_preg_grep_recursive( $pattern, $value ) ) $m[ $key ] = $_m;
3115 }
3116 }
3117 }
3118
3119 return $m;
3120 }
3121
3122 function wpforo_send_new_user_notifications( $userid, $notify = 'both' ) {
3123 if( wpforo_setting( 'email', 'disable_new_user_admin_notification' ) ) {
3124 if( $notify !== 'admin' ) wp_send_new_user_notifications( $userid, 'user' );
3125 } else {
3126 wp_send_new_user_notifications( $userid, $notify );
3127 }
3128 }
3129
3130 function wpforo_get_callbacks_for_action( $hook = '' ) {
3131 global $wp_filter;
3132 if( empty( $hook ) || ! isset( $wp_filter[ $hook ] ) ) return [];
3133
3134 return $wp_filter[ $hook ]->callbacks;
3135 }
3136
3137 /**
3138 * The optimized version of wordpress url_to_postid($url) function
3139 *
3140 * Examine a URL and try to determine the post ID it represents.
3141 *
3142 * Checks are supposedly from the hosted site blog.
3143 *
3144 * @param string $url Permalink to check.
3145 *
3146 * @return int Post ID, or 0 on failure.
3147 * @global WP $wp Current WordPress environment instance.
3148 *
3149 * @since 1.7.4
3150 *
3151 * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
3152 */
3153 function _wpforo_wp_url_to_postid( $url ) {
3154 if( strpos( (string) $url, admin_url() ) !== false ) return 0;
3155
3156 $url_host = str_replace( 'www.', '', parse_url( $url, PHP_URL_HOST ) );
3157 $home_url_host = str_replace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) );
3158
3159 // Bail early if the URL does not belong to this site.
3160 if( $url_host && $url_host !== $home_url_host ) return 0;
3161
3162 // First, check to see if there is a 'p=N' or 'page_id=N' to match against.
3163 if( preg_match( '#[?&](p|page_id|post_id|post|attachment_id)=(\d+)#', (string) $url, $values ) ) {
3164 if( $id = absint( $values[2] ) ) return $id;
3165 }
3166
3167 global $wp_rewrite;
3168 if( $wp_rewrite ) {
3169 // Get rid of the #anchor.
3170 $url_split = explode( '#', $url );
3171 $url = $url_split[0];
3172
3173 // Get rid of URL ?query=string.
3174 $url_split = explode( '?', $url );
3175 $url = $url_split[0];
3176
3177 // Set the correct URL scheme.
3178 $scheme = parse_url( home_url(), PHP_URL_SCHEME );
3179 $url = set_url_scheme( $url, $scheme );
3180
3181 // Add 'www.' if it is absent and should be there.
3182 if( false !== strpos( home_url(), '://www.' ) && false === strpos( $url, '://www.' ) ) {
3183 $url = str_replace( '://', '://www.', $url );
3184 }
3185
3186 // Strip 'www.' if it is present and shouldn't be.
3187 if( false === strpos( home_url(), '://www.' ) ) {
3188 $url = str_replace( '://www.', '://', $url );
3189 }
3190
3191 if( trim( (string) $url, '/' ) === home_url() && 'page' == get_option( 'show_on_front' ) ) {
3192 $page_on_front = get_option( 'page_on_front' );
3193 if( $page_on_front && get_post( $page_on_front ) instanceof WP_Post ) return (int) $page_on_front;
3194 }
3195
3196 // Check to see if we are using rewrite rules.
3197 $rewrite = $wp_rewrite->wp_rewrite_rules();
3198
3199 // Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options.
3200 if( empty( $rewrite ) ) return 0;
3201
3202 // Strip 'index.php/' if we're not using path info permalinks.
3203 if( ! $wp_rewrite->using_index_permalinks() ) {
3204 $url = str_replace( $wp_rewrite->index . '/', '', $url );
3205 }
3206
3207 if( false !== strpos( trailingslashit( (string) $url ), home_url( '/' ) ) ) {
3208 // Chop off http://domain.com/[path].
3209 $url = str_replace( home_url(), '', $url );
3210 } else {
3211 // Chop off /path/to/blog.
3212 $home_path = parse_url( home_url( '/' ) );
3213 $home_path = isset( $home_path['path'] ) ? $home_path['path'] : '';
3214 $url = preg_replace( sprintf( '#^%s#', preg_quote( $home_path ) ), '', trailingslashit( (string) $url ) );
3215 }
3216
3217 // Trim leading and lagging slashes.
3218 $url = trim( (string) $url, '/' );
3219
3220 $request = $url;
3221 $post_type_query_vars = [];
3222
3223 foreach( get_post_types( [], 'objects' ) as $post_type => $t ) {
3224 if( ! empty( $t->query_var ) ) {
3225 $post_type_query_vars[ $t->query_var ] = $post_type;
3226 }
3227 }
3228
3229 // Look for matches.
3230 $request_match = $request;
3231 foreach( (array) $rewrite as $match => $query ) {
3232 if( preg_match( "#^$match#", (string) $request_match, $matches ) ) {
3233
3234 if( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)]/', (string) $query, $varmatch ) ) {
3235 // This is a verbose page match, let's check to be sure about it.
3236 $page = get_page_by_path( $matches[ $varmatch[1] ] );
3237 if( ! $page ) {
3238 continue;
3239 }
3240
3241 $post_status_obj = get_post_status_object( $page->post_status );
3242 if( ! $post_status_obj->public && ! $post_status_obj->protected && ! $post_status_obj->private && $post_status_obj->exclude_from_search ) {
3243 continue;
3244 }
3245 }
3246
3247 // Got a match.
3248 // Trim the query of everything up to the '?'.
3249 $query = preg_replace( '!^.+\?!', '', (string) $query );
3250
3251 // Substitute the substring matches into the query.
3252 $query = addslashes( WP_MatchesMapRegex::apply( $query, $matches ) );
3253
3254 // Filter out non-public query vars.
3255 global $wp;
3256 parse_str( $query, $query_vars );
3257 $query = [];
3258 foreach( (array) $query_vars as $key => $value ) {
3259 if( in_array( $key, $wp->public_query_vars ) ) {
3260 $query[ $key ] = $value;
3261 if( isset( $post_type_query_vars[ $key ] ) ) {
3262 $query['post_type'] = $post_type_query_vars[ $key ];
3263 $query['name'] = $value;
3264 }
3265 }
3266 }
3267
3268 // Resolve conflicts between posts with numeric slugs and date archive queries.
3269 $query = wp_resolve_numeric_slug_conflicts( $query );
3270
3271 // Do the query.
3272 if( $p = wpforo_bigintval( wpfval( $query, 'p' ) ) ) return $p;
3273 if( $page_id = wpforo_bigintval( wpfval( $query, 'page_id' ) ) ) return $page_id;
3274
3275 $pagename = wpfval( $query, 'pagename' );
3276 $name = wpfval( $query, 'name' );
3277 if( $pagename || $name ) {
3278 if( ! $slug = $pagename ) $slug = $name;
3279 $sql = "SELECT `ID` FROM `" . WPF()->db->posts . "`
3280 WHERE `post_status` = 'publish'
3281 AND `post_name` = %s";
3282 $post_types = (array) apply_filters( 'wpforo_wp_url_to_postid_post_types', [ 'page', 'post' ] );
3283 if( $post_types ) $sql .= " AND `post_type` IN('" . implode( "','", $post_types ) . "')";
3284 $sql = WPF()->db->prepare( $sql, $slug );
3285 $postid = WPF()->db->get_var( $sql );
3286 if( $postid = wpforo_bigintval( $postid ) ) {
3287 return $postid;
3288 } else {
3289 return 0;
3290 }
3291 } else {
3292 return 0;
3293 }
3294
3295 }
3296 }
3297 }
3298
3299 return 0;
3300 }
3301
3302 function wpforo_wp_url_to_postid( $url ) {
3303 return wpforo_ram_get( '_wpforo_wp_url_to_postid', $url );
3304 }
3305
3306 function wpforo_get_blog_content_types() {
3307 $post_types = get_post_types( [
3308 'public' => true,
3309 'publicly_queryable' => true,
3310 'show_ui' => true,
3311 'capability_type' => 'post',
3312 ] );
3313 $post_types['post'] = 'post';
3314 $post_types['page'] = 'page';
3315 unset( $post_types['attachment'] );
3316 $post_types = array_filter( array_keys( $post_types ) );
3317
3318 return apply_filters( 'wpforo_get_blog_content_types', $post_types );
3319 }
3320
3321 /**
3322 * @param string $css
3323 *
3324 * @return string
3325 */
3326 function wpforo_add_wrapper( $css ) {
3327 $css = preg_replace( '@(#(?:wpforo-wrap|wpfa_dialog|wpfa_dialog_wrap)[\s.#{:>+\[~,])@um', '#wpforo $1', (string) $css );
3328
3329 return preg_replace( '@(#(?:wpforo|wpforo-wrap|wpfa_dialog|wpfa_dialog_wrap)\s*)#wpforo[\s.#{:>+\[~,]@um', '$1', (string) $css );
3330 }
3331
3332 /**
3333 * @param string $css
3334 *
3335 * @return string
3336 */
3337 function wpforo_wrap_fix_in_css( $css ) {
3338 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 ) ) {
3339 $css = wpforo_add_wrapper( $css );
3340 }
3341
3342 return $css;
3343 }
3344
3345 /**
3346 * @param string $csspath
3347 */
3348 function wpforo_wrap_fix_in_cssfile( $csspath ) {
3349 $csspath = wpforo_fix_dir_sep( $csspath );
3350 if( is_file( $csspath ) ) {
3351 $css = wpforo_get_file_content( $csspath );
3352 $css = wpforo_wrap_fix_in_css( $css );
3353 if( md5( $css ) !== md5_file( $csspath ) ) wpforo_write_file( $csspath, $css );
3354 }
3355 }
3356
3357 function wpforo_wrap_in_all_addons_css() {
3358 $csspaths = [
3359 '/wpforo-ad-manager/assets/css/style.css',
3360 '/wpforo-advanced-attachments/assets/css/style.css',
3361 '/wpforo-cross-posting/assets/css/wpdiscuz-uploader.css',
3362 '/wpforo-cross-posting/assets/css/wpforo-cross-rtl.css',
3363 '/wpforo-cross-posting/assets/css/wpforo-cross.css',
3364 '/wpforo-embeds/assets/css/embed.css',
3365 '/wpforo-emoticons/assets/emoticons.css',
3366 '/wpforo-mycred/css/mycread.css',
3367 '/wpforo-polls/assets/css/poll.css',
3368 '/wpforo-private-messages/assets/css/style-rtl.css',
3369 '/wpforo-private-messages/assets/css/style.css',
3370 '/wpforo-user-custom-fields/assets/css/frontend.css',
3371 ];
3372 foreach( $csspaths as $csspath ) wpforo_wrap_fix_in_cssfile( WP_PLUGIN_DIR . $csspath );
3373
3374 WPF()->dissmissed['addons_css_update'] = 1;
3375 wpforo_update_option( 'dissmissed', WPF()->dissmissed );
3376 }
3377
3378 /*add_action( 'admin_notices', function() {
3379 if( ! (int) wpfval( WPF()->dissmissed, 'addons_css_update' ) ) {
3380 $has_addon = false;
3381 foreach( wpforo_get_addons_info() as $addon ) {
3382 if( class_exists( $addon['class'] ) ) {
3383 $has_addon = true;
3384 break;
3385 }
3386 }
3387 if( ! $has_addon ) return;
3388 $class = 'notice notice-warning';
3389 $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>';
3390 $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>';
3391 printf( '<div class="%1$s"><p>%2$s</p></div>', $class, $message );
3392 }
3393 } );*/
3394
3395 /**
3396 * @param string $url
3397 *
3398 * @return string|null
3399 */
3400 function wpforo_get_topic_slug_from_url( $url = '' ) {
3401 $url = trim( (string) $url );
3402 if( ! $url ) $url = wpforo_get_request_uri();
3403
3404 if( is_wpforo_url( $url ) ) {
3405 if( preg_match( '#/' . preg_quote( wpforo_settings_get_slug( 'postid' ) ) . '/(\d+)/?$#isu', strtok( $url, '?' ), $match ) ) {
3406 $url = WPF()->post->get_url( $match[1] );
3407 }
3408 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 ) ) {
3409 return $match[1];
3410 }
3411 }
3412
3413 return null;
3414 }
3415
3416 function wpforo_clean_folder( $directory ) {
3417 $directory = wpforo_fix_dir_sep( $directory );
3418 $directory_ns = trim( (string) $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR . '*';
3419 $directory_ws = DIRECTORY_SEPARATOR . trim( (string) $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR . '*';
3420 $glob = glob( $directory_ns );
3421 if( empty( $glob ) ) $glob = glob( $directory_ws );
3422 foreach( $glob as $item ) {
3423 if( strpos( (string) $item, 'index.html' ) !== false || strpos( (string) $item, '.htaccess' ) !== false ) continue;
3424 if( ! is_dir( $item ) && file_exists( $item ) ) {
3425 @unlink( $item );
3426 }
3427 }
3428 }
3429
3430 /**
3431 * @param string $basename
3432 *
3433 * @return string
3434 */
3435 function wpforo_fix_table_name( $basename ) {
3436 return WPF()->fix_table_name( $basename );
3437 }
3438
3439 /**
3440 * @param callable $func
3441 * @param mixed ... $_, ... [optional] call_user_func parameters
3442 *
3443 * @return mixed
3444 */
3445 function wpforo_ram_get( $func ) {
3446 return call_user_func_array( [ WPF()->ram_cache, 'call_user_func' ], func_get_args() );
3447 }
3448
3449 function wpforo_prefix( $str = '', $wp_prefix = false ) {
3450 return ( $wp_prefix ? WPF()->blog_prefix : '' ) . WPF()->prefix . $str;
3451 }
3452
3453 function wpforo_prefix_slug( $str = '' ) {
3454 if( $str === 'dashboard' && ! is_wpforo_multiboard() ) $str = 'overview';
3455 return str_replace( '_', '-', wpforo_prefix( $str ) );
3456 }
3457
3458 function wpforo_get_ajax_url() {
3459 return admin_url(
3460 sprintf(
3461 'admin-ajax.php?lang=%1$s&page_id=%2$d&wpforo_boardid=%3$d',
3462 strtok( WPF()->locale, '_' ),
3463 WPF()->board->get_current( 'pageid' ),
3464 WPF()->board->get_current( 'boardid' )
3465 )
3466 );
3467 }
3468
3469 function wpforo_get_query_var_lang() {
3470 if( ! $lang = wpfval( $_GET, 'lang' ) ) $lang = get_query_var( 'lang' );
3471
3472 return $lang;
3473 }
3474
3475 function wpforo_is_db_mysql8() {
3476 return wpforo_ram_get( [ WPF(), 'is_db_mysql8' ] );
3477 }
3478
3479 function wpforo_is_module_enabled( $module_key, $board = [] ) {
3480 return WPF()->board->is_module_enabled( $module_key, $board );
3481 }
3482
3483 function is_wpforo_multiboard() {
3484 return wpforo_ram_get( [ WPF()->board, 'is_multi' ] );
3485 }
3486
3487 function wpforo_get_redirect_to() {
3488 if( ! ( ( $redirect_to = urldecode( (string) wpfval( $_GET, 'redirect_to' ) ) ) && wpforo_is_url_internal( $redirect_to ) ) ) {
3489 $redirect_to = preg_replace( '#/?\?.*$#isu', '', wpforo_get_request_uri() );
3490 }
3491
3492 return $redirect_to;
3493 }
3494
3495 function wpforo_get_redirect_to_url_path( $redirect_to = null ) {
3496 if( is_null( $redirect_to ) ) $redirect_to = wpforo_get_redirect_to();
3497
3498 return $redirect_to ? '?redirect_to=' . urlencode( (string) $redirect_to ) : '';
3499 }
3500
3501 function wpforo_login_url( $redirect_to = null ) {
3502 if( wpforo_setting( 'authorization', 'login_url' ) ) {
3503 $login_url = trim( (string) get_bloginfo( 'url' ), '/' ) . '/' . ltrim( (string) wpforo_setting( 'authorization', 'login_url' ), '/' );
3504 } else {
3505 $login_url = wpforo_url( wpforo_get_redirect_to_url_path( $redirect_to ), 'login' );
3506 }
3507 $login_url = apply_filters( 'wpforo_login_url', $login_url );
3508
3509 return esc_url_raw( $login_url );
3510 }
3511
3512 function wpforo_register_url( $redirect_to = null ) {
3513 if( wpforo_setting( 'authorization', 'register_url' ) ) {
3514 $register_url = trim( (string) get_bloginfo( 'url' ), '/' ) . '/' . ltrim( (string) wpforo_setting( 'authorization', 'register_url' ), '/' );
3515 } else {
3516 $register_url = wpforo_url( wpforo_get_redirect_to_url_path( $redirect_to ), 'register' );
3517 }
3518 $register_url = apply_filters( 'wpforo_register_url', $register_url );
3519
3520 return esc_url_raw( $register_url );
3521 }
3522
3523 function wpforo_logout_url( $redirect_to = null ) {
3524 $logout_url = wpforo_url( wpforo_get_redirect_to_url_path( $redirect_to ), 'logout' );
3525 $logout_url = wp_nonce_url( $logout_url, 'wpforo_logout', '_wpfnonce' );
3526 $logout_url = apply_filters( 'wpforo_logout_url', $logout_url );
3527
3528 return esc_url_raw( $logout_url );
3529 }
3530
3531 function wpforo_lostpassword_url( $redirect_to = null ) {
3532 if( wpforo_setting( 'authorization', 'lost_password_url' ) ) {
3533 $lostpassword_url = trim( (string) get_bloginfo( 'url' ), '/' ) . '/' . ltrim( (string) wpforo_setting( 'authorization', 'lost_password_url' ), '/' );
3534 } else {
3535 if( wpforo_setting( 'authorization', 'use_our_lostpassword_url' ) ) {
3536 $lostpassword_url = wpforo_url( wpforo_get_redirect_to_url_path( $redirect_to ), 'lostpassword' );
3537 } else {
3538 if( is_null( $redirect_to ) ) $redirect_to = wpforo_get_redirect_to();
3539 $lostpassword_url = wp_lostpassword_url( $redirect_to );
3540 }
3541 }
3542 $lostpassword_url = apply_filters( 'wpforo_lostpassword_url', $lostpassword_url );
3543
3544 return esc_url_raw( $lostpassword_url );
3545 }
3546
3547 function wpforo_resetpassword_url( $rp_key, $rp_login ) {
3548 $rp_key = rawurlencode( rawurldecode( $rp_key ) );
3549 $rp_login = rawurlencode( rawurldecode( $rp_login ) );
3550 $resetpassword_url = wpforo_url( "?wpfaction=resetpassword_form&rp_key=$rp_key&rp_login=$rp_login", 'lostpassword' );
3551 $resetpassword_url = apply_filters( 'wpforo_resetpassword_url', $resetpassword_url );
3552
3553 return esc_url_raw( $resetpassword_url );
3554 }
3555
3556 function wpforo_members_url() {
3557 return wpforo_url( '', 'members' );
3558 }
3559
3560 function wpforo_redirect_to() {
3561 $redirect_url = wpforo_home_url();
3562 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 ) ) ) {
3563 $redirect_url = $redirect_to;
3564 }
3565 wp_safe_redirect( $redirect_url );
3566 exit();
3567 }
3568
3569 function wpforo_settings_get_slug( $key ) {
3570 $slug = wpfval( WPF()->settings->slugs, $key );
3571 if( ! $slug ) $slug = $key;
3572
3573 return strtolower( (string) $slug );
3574 }
3575
3576 function wpforo_settings_get_slug_key( $slug ) {
3577 $ret = array_search( $slug, WPF()->settings->slugs );
3578 if( $ret === false ) $ret = $slug;
3579
3580 return $ret;
3581 }
3582
3583 function wpforo_is_slug_base( $slug ) {
3584 return ! wpfkey( WPF()->tpl->templates, wpforo_settings_get_slug_key( $slug ) );
3585 }
3586
3587 /**
3588 * @param string $group
3589 * @param string|int ...$name ... $_, ... [optional] more keys
3590 *
3591 * @return mixed|null
3592 */
3593 function wpforo_setting( $group, $name ) {
3594 if( ! is_null( WPF()->settings ) && property_exists( WPF()->settings, $group ) ) {
3595 $args = func_get_args();
3596 $args[0] = &WPF()->settings->$group;
3597
3598 return call_user_func_array( 'wpfval', $args );
3599 }
3600
3601 return null;
3602 }
3603
3604 function _wpforo_apply_email_shortcodes( $txt, $args ){
3605 $forumid = (int) wpfval( $args, 'forumid' );
3606 $topicid = wpforo_bigintval( wpfval( $args, 'topicid' ) );
3607 $postid = wpforo_bigintval( wpfval( $args, 'postid' ) );
3608 $user = (array) wpfval( $args, 'user' );
3609 $owner = (array) wpfval( $args, 'owner' );
3610 $unsubscribe_link = (string) wpfval( $args, 'unsubscribe_link' );
3611
3612 $txt = do_shortcode( $txt );
3613
3614 return preg_replace_callback( '#\[[^\[\]]+?]#iu', function ( $match ) use ( $forumid, $topicid, $postid, $user, $owner, $unsubscribe_link ) {
3615 $body_length = apply_filters( 'wpforo_email_notification_post_body_length', 1000 );
3616 $value = '';
3617 $shortcode = $match[0];
3618
3619 if ( $shortcode === '[unsubscribe_link]' ) {
3620 if ( $unsubscribe_link ) {
3621 $value = sprintf( '<br><a target="_blank" href="%1$s">%2$s</a>', esc_url( (string) $unsubscribe_link ), wpforo_phrase( 'Unsubscribe', false ) );
3622 $value = stripslashes( $value );
3623 }
3624 } elseif ( strpos( (string) $shortcode, '[user_' ) === 0 ) {
3625 if ( wpforo_bigintval( wpfval( $user, 'userid' ) ) ) {
3626 $value = wpforo_user_field_shortcode_to_value( $shortcode, $user['userid'] );
3627 if ( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) {
3628 $value = '';
3629 }
3630 } else {
3631 if ( $shortcode === '[user_display_name]' ) {
3632 $value = wpfval( $user, 'display_name' ) ?: wpforo_phrase( 'Anonymous', false );
3633 } elseif ( $shortcode === '[user_user_email]' ) {
3634 $value = (string) wpfval( $user, 'user_email' );
3635 }
3636 }
3637 } elseif ( strpos( (string) $shortcode, '[owner_' ) === 0 ) {
3638 if ( wpforo_bigintval( wpfval( $owner, 'userid' ) ) ) {
3639 $value = wpforo_user_field_shortcode_to_value( $shortcode, $owner['userid'] );
3640 if ( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) {
3641 $value = '';
3642 }
3643 } else {
3644 if ( $shortcode === '[owner_display_name]' ) {
3645 $value = wpfval( $owner, 'display_name' ) ?: wpforo_phrase( 'Anonymous', false );
3646 } elseif ( $shortcode === '[owner_user_email]' ) {
3647 $value = (string) wpfval( $owner, 'user_email' );
3648 }
3649 }
3650 } elseif ( strpos( (string) $shortcode, '[forum_' ) === 0 ) {
3651 if ( $forumid ) {
3652 $value = wpforo_forum_field_shortcode_to_value( $shortcode, $forumid );
3653 if ( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) {
3654 $value = '';
3655 }
3656 }
3657 } elseif ( strpos( (string) $shortcode, '[topic_' ) === 0 ) {
3658 if ( $topicid ) {
3659 $value = wpforo_topic_field_shortcode_to_value( $shortcode, $topicid );
3660 if ( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) {
3661 $value = '';
3662 }elseif ( $shortcode === '[topic_body]' ) {
3663 $value = wpforo_text( wpforo_kses( wpforo_content_filter( $value ), 'email' ), $body_length, false, false, false, true, false );
3664 }
3665 }
3666 } elseif ( strpos( (string) $shortcode, '[post_' ) === 0 ) {
3667 if ( $postid ) {
3668 $value = wpforo_post_field_shortcode_to_value( $shortcode, $postid );
3669 if ( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) {
3670 $value = '';
3671 }elseif ( $shortcode === '[post_body]' ) {
3672 $value = wpforo_text( wpforo_kses( wpforo_content_filter( $value ), 'email' ), $body_length, false, false, false, true, false );
3673 }
3674 }
3675 }
3676
3677 return $value;
3678 }, (string) $txt );
3679 }
3680
3681 function wpforo_apply_email_shortcodes( $txt, $pitem, $item, $owner, $user, $unsubscribe_link ) {
3682 $forumid = ( intval( wpfval( $pitem, 'forumid' ) ) ?: intval( wpfval( $item, 'forumid' ) ) );
3683 $topicid = ( wpforo_bigintval( wpfval( $pitem, 'topicid' ) ) ?: wpforo_bigintval( wpfval( $item, 'topicid' ) ) );
3684 $postid = ( wpforo_bigintval( wpfval( $item, 'postid' ) ) ?: wpforo_bigintval( wpfval( $item, 'first_postid' ) ) );
3685
3686 return _wpforo_apply_email_shortcodes( $txt, compact('forumid', 'topicid', 'postid', 'owner', 'user', 'unsubscribe_link') );
3687 }
3688
3689 function wpforo_send_email( $email, $sbj, $msg, $headers = '' ){
3690 if( defined( 'IS_GO2WPFORO' ) && IS_GO2WPFORO ) return false;
3691 if( apply_filters( 'break_wpforo_send_email', false, $email, $sbj, $msg, $headers ) ) return false;
3692 $key = func_get_args();
3693 if( WPF()->ram_cache->exists( $key ) ) return false;
3694 add_filter( 'wp_mail_content_type', 'wpforo_set_html_content_type', 999 );
3695 if( wp_mail( $email, $sbj, $msg, ( $headers ?: wpforo_mail_headers() ) ) ){
3696 WPF()->ram_cache->set( $key, true );
3697 return true;
3698 }
3699 remove_filter( 'wp_mail_content_type', 'wpforo_set_html_content_type' );
3700
3701 return false;
3702 }
3703
3704 function wpforo_get_site_default_locale(){
3705 $locale = trim( (string) get_option( 'WPLANG', 'en_US' ) );
3706 if( ! $locale ) $locale = 'en_US';
3707 return $locale;
3708 }
3709
3710 function wpforo_sanitize( $action, $variable_name, $filter, $default = '' ) {
3711 if( $filter === "FILTER_SANITIZE_STRING" ) {
3712 $glob = INPUT_POST === $action ? $_POST : $_GET;
3713 if( wpfkey( $glob, $variable_name ) ) {
3714 return sanitize_text_field( $glob[ $variable_name ] );
3715 } else {
3716 return $default;
3717 }
3718 }
3719 $variable = isset( $variable_name ) ? filter_input( $action, $variable_name, $filter ) : '';
3720
3721 return $variable ?: $default;
3722 }
3723
3724 function wpforo_length( $str, $filtered = true ){
3725 $str = $filtered ? strip_tags( trim( (string) $str ) ) : $str;
3726 if( function_exists( 'mb_substr' ) ) {
3727 $length = mb_strlen( (string) $str, get_option( 'blog_charset' ) );
3728 } else {
3729 $length = strlen( (string) $str );
3730 }
3731 return (int) $length;
3732 }
3733
3734 if( ! function_exists( 'wpforo_remote_retrieve_header' ) ){
3735 /**
3736 *
3737 * @param array|WP_Error $response HTTP response.
3738 * @param string $header Header name to retrieve value from.
3739 * @return string string The header value. Empty string on if incorrect parameter given, or if the header doesn't exist.
3740 */
3741 function wpforo_remote_retrieve_header( $response, $header ){
3742 $header = wp_remote_retrieve_header( $response, $header );
3743 if( is_array( $header ) ) $header = (string) array_pop( $header );
3744
3745 return $header;
3746 }
3747 }
3748
3749 function wpforo_is_array_of_scalars($array) {
3750 if (!is_array($array)) return false;
3751 foreach ($array as $element) if (!is_scalar($element)) return false;
3752 return true;
3753 }
3754
3755 function wpforo_is_gzipped( $contents ): bool {
3756 // phpcs:disable Generic.Strings.UnnecessaryStringConcat.Found
3757 if ( function_exists( 'mb_strpos' ) ) {
3758 return 0 === mb_strpos( $contents, "\x1f" . "\x8b" . "\x08" );
3759 } else {
3760 return 0 === strpos( $contents, "\x1f" . "\x8b" . "\x08" );
3761 }
3762 // phpcs:enable
3763 }
3764
3765 function wpforo_sanitize_svg( $file ): bool {
3766 $dirty = file_get_contents( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
3767
3768 // Is the SVG gzipped? If so we try and decode the string
3769 $is_zipped = wpforo_is_gzipped( $dirty );
3770 if ( $is_zipped ) {
3771 $dirty = gzdecode( $dirty );
3772
3773 // If decoding fails, bail as we're not secure
3774 if ( false === $dirty ) {
3775 return false;
3776 }
3777 }
3778
3779 $clean = trim( wpforo_kses( $dirty, 'svg' ) );
3780
3781 if ( !$clean ) {
3782 return false;
3783 }
3784
3785 // If we were gzipped, we need to re-zip
3786 if ( $is_zipped ) {
3787 $clean = gzencode( $clean );
3788 }
3789
3790 file_put_contents( $file, $clean ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
3791
3792 return true;
3793 }
3794
3795 function wpforo_check_for_svg( $file ){
3796 // Ensure we have a proper file path before processing
3797 if ( ! isset( $file['tmp_name'] ) ) {
3798 return $file;
3799 }
3800
3801 $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], ( $file['name'] ?? '' ), [] );
3802 $type = ! empty( $wp_filetype['type'] ) ? $wp_filetype['type'] : (string) wpfval( $file, 'type' );
3803
3804 if ( in_array( $type, [ 'image/svg+xml', 'image/svg' ], true ) ) {
3805 if ( ! wpforo_sanitize_svg( $file['tmp_name'] ) ) {
3806 $file['error'] = __(
3807 "Sorry, this file couldn't be sanitized so for security reasons wasn't uploaded",
3808 'wpforo'
3809 );
3810 }
3811 }
3812
3813 return $file;
3814 }
3815